Patch for concurrent iterator & others (onto v1.11.6) #386
@ -333,7 +333,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
|
|||||||
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
cfg.GasLimit = gas
|
cfg.GasLimit = gas
|
||||||
if len(tracerCode) > 0 {
|
if len(tracerCode) > 0 {
|
||||||
tracer, err := tracers.New(tracerCode, new(tracers.Context))
|
tracer, err := tracers.New(tracerCode, new(tracers.Context), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -832,7 +832,7 @@ func TestRuntimeJSTracer(t *testing.T) {
|
|||||||
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
|
||||||
statedb.SetCode(common.HexToAddress("0xff"), depressedCode)
|
statedb.SetCode(common.HexToAddress("0xff"), depressedCode)
|
||||||
|
|
||||||
tracer, err := tracers.New(jsTracer, new(tracers.Context))
|
tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -868,7 +868,7 @@ func TestJSTracerCreateTx(t *testing.T) {
|
|||||||
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
|
||||||
|
|
||||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
||||||
tracer, err := tracers.New(jsTracer, new(tracers.Context))
|
tracer, err := tracers.New(jsTracer, new(tracers.Context), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -169,15 +170,15 @@ type TraceConfig struct {
|
|||||||
Tracer *string
|
Tracer *string
|
||||||
Timeout *string
|
Timeout *string
|
||||||
Reexec *uint64
|
Reexec *uint64
|
||||||
|
// Config specific to given tracer. Note struct logger
|
||||||
|
// config are historically embedded in main object.
|
||||||
|
TracerConfig json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceCallConfig is the config for traceCall API. It holds one more
|
// TraceCallConfig is the config for traceCall API. It holds one more
|
||||||
// field to override the state for tracing.
|
// field to override the state for tracing.
|
||||||
type TraceCallConfig struct {
|
type TraceCallConfig struct {
|
||||||
*logger.Config
|
TraceConfig
|
||||||
Tracer *string
|
|
||||||
Timeout *string
|
|
||||||
Reexec *uint64
|
|
||||||
StateOverrides *ethapi.StateOverride
|
StateOverrides *ethapi.StateOverride
|
||||||
BlockOverrides *ethapi.BlockOverrides
|
BlockOverrides *ethapi.BlockOverrides
|
||||||
}
|
}
|
||||||
@ -882,7 +883,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
|
|||||||
// Default tracer is the struct logger
|
// Default tracer is the struct logger
|
||||||
tracer = logger.NewStructLogger(config.Config)
|
tracer = logger.NewStructLogger(config.Config)
|
||||||
if config.Tracer != nil {
|
if config.Tracer != nil {
|
||||||
tracer, err = New(*config.Tracer, txctx)
|
tracer, err = New(*config.Tracer, txctx, config.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,11 @@ type callTrace struct {
|
|||||||
|
|
||||||
// callTracerTest defines a single test to check the call tracer against.
|
// callTracerTest defines a single test to check the call tracer against.
|
||||||
type callTracerTest struct {
|
type callTracerTest struct {
|
||||||
Genesis *core.Genesis `json:"genesis"`
|
Genesis *core.Genesis `json:"genesis"`
|
||||||
Context *callContext `json:"context"`
|
Context *callContext `json:"context"`
|
||||||
Input string `json:"input"`
|
Input string `json:"input"`
|
||||||
Result *callTrace `json:"result"`
|
TracerConfig json.RawMessage `json:"tracerConfig"`
|
||||||
|
Result *callTrace `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterates over all the input-output datasets in the tracer test harness and
|
// Iterates over all the input-output datasets in the tracer test harness and
|
||||||
@ -179,7 +180,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
|
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
|
||||||
)
|
)
|
||||||
tracer, err := tracers.New(tracerName, new(tracers.Context))
|
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
@ -293,7 +294,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
|
|||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
tracer, err := tracers.New(tracerName, new(tracers.Context))
|
tracer, err := tracers.New(tracerName, new(tracers.Context), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("failed to create call tracer: %v", err)
|
b.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
@ -359,7 +360,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
|
|||||||
}
|
}
|
||||||
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
|
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
|
||||||
// Create the tracer, the EVM environment and run it
|
// Create the tracer, the EVM environment and run it
|
||||||
tracer, err := tracers.New("callTracer", nil)
|
tracer, err := tracers.New("callTracer", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
72
eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json
vendored
Normal file
72
eth/tracers/internal/tracetest/testdata/call_tracer/simple_onlytop.json
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"difficulty": "3502894804",
|
||||||
|
"gasLimit": "4722976",
|
||||||
|
"miner": "0x1585936b53834b021f68cc13eeefdec2efc8e724",
|
||||||
|
"number": "2289806",
|
||||||
|
"timestamp": "1513601314"
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"alloc": {
|
||||||
|
"0x0024f658a46fbb89d8ac105e98d7ac7cbbaf27c5": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "22",
|
||||||
|
"storage": {}
|
||||||
|
},
|
||||||
|
"0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe": {
|
||||||
|
"balance": "0x4d87094125a369d9bd5",
|
||||||
|
"code": "0x606060405236156100935763ffffffff60e060020a60003504166311ee8382811461009c57806313af4035146100be5780631f5e8f4c146100ee57806324daddc5146101125780634921a91a1461013b57806363e4bff414610157578063764978f91461017f578063893d20e8146101a1578063ba40aaa1146101cd578063cebc9a82146101f4578063e177246e14610216575b61009a5b5b565b005b34156100a457fe5b6100ac61023d565b60408051918252519081900360200190f35b34156100c657fe5b6100da600160a060020a0360043516610244565b604080519115158252519081900360200190f35b34156100f657fe5b6100da610307565b604080519115158252519081900360200190f35b341561011a57fe5b6100da6004351515610318565b604080519115158252519081900360200190f35b6100da6103d6565b604080519115158252519081900360200190f35b6100da600160a060020a0360043516610420565b604080519115158252519081900360200190f35b341561018757fe5b6100ac61046c565b60408051918252519081900360200190f35b34156101a957fe5b6101b1610473565b60408051600160a060020a039092168252519081900360200190f35b34156101d557fe5b6100da600435610483565b604080519115158252519081900360200190f35b34156101fc57fe5b6100ac61050d565b60408051918252519081900360200190f35b341561021e57fe5b6100da600435610514565b604080519115158252519081900360200190f35b6003545b90565b60006000610250610473565b600160a060020a031633600160a060020a03161415156102705760006000fd5b600160a060020a03831615156102865760006000fd5b50600054600160a060020a0390811690831681146102fb57604051600160a060020a0380851691908316907ffcf23a92150d56e85e3a3d33b357493246e55783095eb6a733eb8439ffc752c890600090a360008054600160a060020a031916600160a060020a03851617905560019150610300565b600091505b5b50919050565b60005460a060020a900460ff165b90565b60006000610324610473565b600160a060020a031633600160a060020a03161415156103445760006000fd5b5060005460a060020a900460ff16801515831515146102fb576000546040805160a060020a90920460ff1615158252841515602083015280517fe6cd46a119083b86efc6884b970bfa30c1708f53ba57b86716f15b2f4551a9539281900390910190a16000805460a060020a60ff02191660a060020a8515150217905560019150610300565b600091505b5b50919050565b60006103e0610307565b801561040557506103ef610473565b600160a060020a031633600160a060020a031614155b156104105760006000fd5b610419336105a0565b90505b5b90565b600061042a610307565b801561044f5750610439610473565b600160a060020a031633600160a060020a031614155b1561045a5760006000fd5b610463826105a0565b90505b5b919050565b6001545b90565b600054600160a060020a03165b90565b6000600061048f610473565b600160a060020a031633600160a060020a03161415156104af5760006000fd5b506001548281146102fb57604080518281526020810185905281517f79a3746dde45672c9e8ab3644b8bb9c399a103da2dc94b56ba09777330a83509929181900390910190a160018381559150610300565b600091505b5b50919050565b6002545b90565b60006000610520610473565b600160a060020a031633600160a060020a03161415156105405760006000fd5b506002548281146102fb57604080518281526020810185905281517ff6991a728965fedd6e927fdf16bdad42d8995970b4b31b8a2bf88767516e2494929181900390910190a1600283905560019150610300565b600091505b5b50919050565b60006000426105ad61023d565b116102fb576105c46105bd61050d565b4201610652565b6105cc61046c565b604051909150600160a060020a038416908290600081818185876187965a03f1925050501561063d57604080518281529051600160a060020a038516917f9bca65ce52fdef8a470977b51f247a2295123a4807dfa9e502edf0d30722da3b919081900360200190a260019150610300565b6102fb42610652565b5b600091505b50919050565b60038190555b505600a165627a7a72305820f3c973c8b7ed1f62000b6701bd5b708469e19d0f1d73fde378a56c07fd0b19090029",
|
||||||
|
"nonce": "1",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000001b436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x00000000000000000000000000000000000000000000000006f05b59d3b20000",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000003c",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000003": "0x000000000000000000000000000000000000000000000000000000005a37b834"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0xb436ba50d378d4bbc8660d312a13df6af6e89dfb": {
|
||||||
|
"balance": "0x1780d77678137ac1b775",
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "29072",
|
||||||
|
"storage": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"byzantiumBlock": 1700000,
|
||||||
|
"chainId": 3,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d",
|
||||||
|
"eip155Block": 10,
|
||||||
|
"eip158Block": 10,
|
||||||
|
"ethash": {},
|
||||||
|
"homesteadBlock": 0
|
||||||
|
},
|
||||||
|
"difficulty": "3509749784",
|
||||||
|
"extraData": "0x4554482e45544846414e532e4f52472d4641313738394444",
|
||||||
|
"gasLimit": "4727564",
|
||||||
|
"hash": "0x609948ac3bd3c00b7736b933248891d6c901ee28f066241bddb28f4e00a9f440",
|
||||||
|
"miner": "0xbbf5029fd710d227630c8b7d338051b8e76d50b3",
|
||||||
|
"mixHash": "0xb131e4507c93c7377de00e7c271bf409ec7492767142ff0f45c882f8068c2ada",
|
||||||
|
"nonce": "0x4eb12e19c16d43da",
|
||||||
|
"number": "2289805",
|
||||||
|
"stateRoot": "0xc7f10f352bff82fac3c2999d3085093d12652e19c7fd32591de49dc5d91b4f1f",
|
||||||
|
"timestamp": "1513601261",
|
||||||
|
"totalDifficulty": "7143276353481064"
|
||||||
|
},
|
||||||
|
"input": "0xf88b8271908506fc23ac0083015f90943b873a919aa0512d5a0f09e6dcceaa4a6727fafe80a463e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c52aa0bdce0b59e8761854e857fe64015f06dd08a4fbb7624f6094893a79a72e6ad6bea01d9dde033cff7bb235a3163f348a6d7ab8d6b52bc0963a95b91612e40ca766a4",
|
||||||
|
"tracerConfig": {
|
||||||
|
"onlyTopCall": true
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"from": "0xb436ba50d378d4bbc8660d312a13df6af6e89dfb",
|
||||||
|
"gas": "0x10738",
|
||||||
|
"gasUsed": "0x3ef9",
|
||||||
|
"input": "0x63e4bff40000000000000000000000000024f658a46fbb89d8ac105e98d7ac7cbbaf27c5",
|
||||||
|
"output": "0x0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"to": "0x3b873a919aa0512d5a0f09e6dcceaa4a6727fafe",
|
||||||
|
"type": "CALL",
|
||||||
|
"value": "0x0"
|
||||||
|
}
|
||||||
|
}
|
@ -125,7 +125,7 @@ type jsTracer struct {
|
|||||||
// The methods `result` and `fault` are required to be present.
|
// The methods `result` and `fault` are required to be present.
|
||||||
// The methods `step`, `enter`, and `exit` are optional, but note that
|
// The methods `step`, `enter`, and `exit` are optional, but note that
|
||||||
// `enter` and `exit` always go together.
|
// `enter` and `exit` always go together.
|
||||||
func newJsTracer(code string, ctx *tracers.Context) (tracers.Tracer, error) {
|
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
||||||
if c, ok := assetTracers[code]; ok {
|
if c, ok := assetTracers[code]; ok {
|
||||||
code = c
|
code = c
|
||||||
}
|
}
|
||||||
@ -177,6 +177,17 @@ func newJsTracer(code string, ctx *tracers.Context) (tracers.Tracer, error) {
|
|||||||
t.exit = exit
|
t.exit = exit
|
||||||
t.result = result
|
t.result = result
|
||||||
t.fault = fault
|
t.fault = fault
|
||||||
|
|
||||||
|
// Pass in config
|
||||||
|
if setup, ok := goja.AssertFunction(obj.Get("setup")); ok {
|
||||||
|
cfgStr := "{}"
|
||||||
|
if cfg != nil {
|
||||||
|
cfgStr = string(cfg)
|
||||||
|
}
|
||||||
|
if _, err := setup(obj, vm.ToValue(cfgStr)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
// Setup objects carrying data to JS. These are created once and re-used.
|
// Setup objects carrying data to JS. These are created once and re-used.
|
||||||
t.log = &steplog{
|
t.log = &steplog{
|
||||||
vm: vm,
|
vm: vm,
|
||||||
|
@ -85,7 +85,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
|
|||||||
func TestTracer(t *testing.T) {
|
func TestTracer(t *testing.T) {
|
||||||
execTracer := func(code string) ([]byte, string) {
|
execTracer := func(code string) ([]byte, string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tracer, err := newJsTracer(code, nil)
|
tracer, err := newJsTracer(code, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -149,7 +149,7 @@ func TestTracer(t *testing.T) {
|
|||||||
|
|
||||||
func TestHalt(t *testing.T) {
|
func TestHalt(t *testing.T) {
|
||||||
timeout := errors.New("stahp")
|
timeout := errors.New("stahp")
|
||||||
tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil)
|
tracer, err := newJsTracer("{step: function() { while(1); }, result: function() { return null; }, fault: function(){}}", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ func TestHalt(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHaltBetweenSteps(t *testing.T) {
|
func TestHaltBetweenSteps(t *testing.T) {
|
||||||
tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil)
|
tracer, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }}", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ func TestHaltBetweenSteps(t *testing.T) {
|
|||||||
func TestNoStepExec(t *testing.T) {
|
func TestNoStepExec(t *testing.T) {
|
||||||
execTracer := func(code string) []byte {
|
execTracer := func(code string) []byte {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tracer, err := newJsTracer(code, nil)
|
tracer, err := newJsTracer(code, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -221,7 +221,7 @@ func TestIsPrecompile(t *testing.T) {
|
|||||||
chaincfg.IstanbulBlock = big.NewInt(200)
|
chaincfg.IstanbulBlock = big.NewInt(200)
|
||||||
chaincfg.BerlinBlock = big.NewInt(300)
|
chaincfg.BerlinBlock = big.NewInt(300)
|
||||||
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
|
txCtx := vm.TxContext{GasPrice: big.NewInt(100000)}
|
||||||
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
|
tracer, err := newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -235,7 +235,7 @@ func TestIsPrecompile(t *testing.T) {
|
|||||||
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
|
t.Errorf("tracer should not consider blake2f as precompile in byzantium")
|
||||||
}
|
}
|
||||||
|
|
||||||
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil)
|
tracer, _ = newJsTracer("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", nil, nil)
|
||||||
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
|
blockCtx = vm.BlockContext{BlockNumber: big.NewInt(250)}
|
||||||
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
|
res, err = runTrace(tracer, &vmContext{blockCtx, txCtx}, chaincfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -248,14 +248,14 @@ func TestIsPrecompile(t *testing.T) {
|
|||||||
|
|
||||||
func TestEnterExit(t *testing.T) {
|
func TestEnterExit(t *testing.T) {
|
||||||
// test that either both or none of enter() and exit() are defined
|
// test that either both or none of enter() and exit() are defined
|
||||||
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context)); err == nil {
|
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil {
|
||||||
t.Fatal("tracer creation should've failed without exit() definition")
|
t.Fatal("tracer creation should've failed without exit() definition")
|
||||||
}
|
}
|
||||||
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context)); err != nil {
|
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// test that the enter and exit method are correctly invoked and the values passed
|
// test that the enter and exit method are correctly invoked and the values passed
|
||||||
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context))
|
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -274,3 +274,33 @@ func TestEnterExit(t *testing.T) {
|
|||||||
t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
|
t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetup(t *testing.T) {
|
||||||
|
// Test empty config
|
||||||
|
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := json.Marshal(map[string]string{"foo": "bar"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Test no setup func
|
||||||
|
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Test config value
|
||||||
|
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
have, err := tracer.GetResult()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(have) != `"bar"` {
|
||||||
|
t.Errorf("tracer returned wrong result. have: %s, want: \"bar\"\n", string(have))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -55,11 +55,11 @@ type fourByteTracer struct {
|
|||||||
|
|
||||||
// newFourByteTracer returns a native go tracer which collects
|
// newFourByteTracer returns a native go tracer which collects
|
||||||
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
||||||
func newFourByteTracer(ctx *tracers.Context) tracers.Tracer {
|
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
||||||
t := &fourByteTracer{
|
t := &fourByteTracer{
|
||||||
ids: make(map[string]int),
|
ids: make(map[string]int),
|
||||||
}
|
}
|
||||||
return t
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
|
// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
|
||||||
|
@ -50,16 +50,27 @@ type callFrame struct {
|
|||||||
type callTracer struct {
|
type callTracer struct {
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
callstack []callFrame
|
callstack []callFrame
|
||||||
|
config callTracerConfig
|
||||||
interrupt uint32 // Atomic flag to signal execution interruption
|
interrupt uint32 // Atomic flag to signal execution interruption
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type callTracerConfig struct {
|
||||||
|
OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls
|
||||||
|
}
|
||||||
|
|
||||||
// newCallTracer returns a native go tracer which tracks
|
// newCallTracer returns a native go tracer which tracks
|
||||||
// call frames of a tx, and implements vm.EVMLogger.
|
// call frames of a tx, and implements vm.EVMLogger.
|
||||||
func newCallTracer(ctx *tracers.Context) tracers.Tracer {
|
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
||||||
|
var config callTracerConfig
|
||||||
|
if cfg != nil {
|
||||||
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
// First callframe contains tx context info
|
// First callframe contains tx context info
|
||||||
// and is populated on start and end.
|
// and is populated on start and end.
|
||||||
return &callTracer{callstack: make([]callFrame, 1)}
|
return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
@ -101,6 +112,9 @@ func (t *callTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, _ *
|
|||||||
|
|
||||||
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
|
||||||
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
|
||||||
|
if t.config.OnlyTopCall {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Skip if tracing was interrupted
|
// Skip if tracing was interrupted
|
||||||
if atomic.LoadUint32(&t.interrupt) > 0 {
|
if atomic.LoadUint32(&t.interrupt) > 0 {
|
||||||
t.env.Cancel()
|
t.env.Cancel()
|
||||||
@ -121,6 +135,9 @@ func (t *callTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
|
|||||||
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
// CaptureExit is called when EVM exits a scope, even if the scope didn't
|
||||||
// execute any code.
|
// execute any code.
|
||||||
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
|
||||||
|
if t.config.OnlyTopCall {
|
||||||
|
return
|
||||||
|
}
|
||||||
size := len(t.callstack)
|
size := len(t.callstack)
|
||||||
if size <= 1 {
|
if size <= 1 {
|
||||||
return
|
return
|
||||||
|
@ -35,8 +35,8 @@ func init() {
|
|||||||
type noopTracer struct{}
|
type noopTracer struct{}
|
||||||
|
|
||||||
// newNoopTracer returns a new noop tracer.
|
// newNoopTracer returns a new noop tracer.
|
||||||
func newNoopTracer(ctx *tracers.Context) tracers.Tracer {
|
func newNoopTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
||||||
return &noopTracer{}
|
return &noopTracer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
|
@ -51,10 +51,10 @@ type prestateTracer struct {
|
|||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPrestateTracer(ctx *tracers.Context) tracers.Tracer {
|
func newPrestateTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
||||||
// First callframe contains tx context info
|
// First callframe contains tx context info
|
||||||
// and is populated on start and end.
|
// and is populated on start and end.
|
||||||
return &prestateTracer{prestate: prestate{}}
|
return &prestateTracer{prestate: prestate{}}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
|
@ -46,8 +46,8 @@ type revertReasonTracer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// newRevertReasonTracer returns a new revert reason tracer.
|
// newRevertReasonTracer returns a new revert reason tracer.
|
||||||
func newRevertReasonTracer(_ *tracers.Context) tracers.Tracer {
|
func newRevertReasonTracer(_ *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
|
||||||
return &revertReasonTracer{}
|
return &revertReasonTracer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
||||||
|
@ -35,6 +35,7 @@ func init() {
|
|||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||||
@ -46,7 +47,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ctorFn is the constructor signature of a native tracer.
|
// ctorFn is the constructor signature of a native tracer.
|
||||||
type ctorFn = func(*tracers.Context) tracers.Tracer
|
type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ctors is a map of package-local tracer constructors.
|
ctors is a map of package-local tracer constructors.
|
||||||
@ -71,12 +72,12 @@ func register(name string, ctor ctorFn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// lookup returns a tracer, if one can be matched to the given name.
|
// lookup returns a tracer, if one can be matched to the given name.
|
||||||
func lookup(name string, ctx *tracers.Context) (tracers.Tracer, error) {
|
func lookup(name string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
|
||||||
if ctors == nil {
|
if ctors == nil {
|
||||||
ctors = make(map[string]ctorFn)
|
ctors = make(map[string]ctorFn)
|
||||||
}
|
}
|
||||||
if ctor, ok := ctors[name]; ok {
|
if ctor, ok := ctors[name]; ok {
|
||||||
return ctor(ctx), nil
|
return ctor(ctx, cfg)
|
||||||
}
|
}
|
||||||
return nil, errors.New("no tracer found")
|
return nil, errors.New("no tracer found")
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ type Tracer interface {
|
|||||||
Stop(err error)
|
Stop(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type lookupFunc func(string, *Context) (Tracer, error)
|
type lookupFunc func(string, *Context, json.RawMessage) (Tracer, error)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lookups []lookupFunc
|
lookups []lookupFunc
|
||||||
@ -62,9 +62,9 @@ func RegisterLookup(wildcard bool, lookup lookupFunc) {
|
|||||||
|
|
||||||
// New returns a new instance of a tracer, by iterating through the
|
// New returns a new instance of a tracer, by iterating through the
|
||||||
// registered lookups.
|
// registered lookups.
|
||||||
func New(code string, ctx *Context) (Tracer, error) {
|
func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
|
||||||
for _, lookup := range lookups {
|
for _, lookup := range lookups {
|
||||||
if tracer, err := lookup(code, ctx); err == nil {
|
if tracer, err := lookup(code, ctx, cfg); err == nil {
|
||||||
return tracer, nil
|
return tracer, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user