From 5db19c5922c4850bc2bdd6d50e5fbd46c8bd5e36 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 26 Oct 2021 14:34:59 +0200 Subject: [PATCH] eth/tracers: rework the model a bit --- core/vm/runtime/runtime_test.go | 2 +- eth/tracers/api.go | 28 +++++++---------- eth/tracers/native/call.go | 27 +++++++++------- eth/tracers/native/native.go | 51 ------------------------------ eth/tracers/tracer.go | 42 ++++++++++++------------- eth/tracers/tracer_test.go | 8 ++--- eth/tracers/tracers.go | 55 ++++++++++++++++++++++++++------- eth/tracers/tracers_test.go | 21 ++++++------- 8 files changed, 105 insertions(+), 129 deletions(-) delete mode 100644 eth/tracers/native/native.go diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 9f4bafbc7f..1e54c40f40 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -494,7 +494,7 @@ func BenchmarkSimpleLoop(b *testing.B) { //Execute(loopingCode, nil, &Config{ // EVMConfig: vm.Config{ // Debug: true, - // Tracer: tracer, + // JSTracer: tracer, // }}) // 100M gas benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", "", b) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 07764bc4ac..41bae92a52 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -36,7 +36,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/tracers/native" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/log" @@ -860,22 +859,17 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex } } // Native tracers take precedence - var ok bool - if tracer, ok = native.New(*config.Tracer); !ok { - if tracer, err = New(*config.Tracer, txctx); err != nil { - return nil, err - } - // TODO(s1na): do we need timeout for native tracers? - // Handle timeouts and RPC cancellations - deadlineCtx, cancel := context.WithTimeout(ctx, timeout) - go func() { - <-deadlineCtx.Done() - if deadlineCtx.Err() == context.DeadlineExceeded { - tracer.(*Tracer).Stop(errors.New("execution timeout")) - } - }() - defer cancel() + if tracer, err = New(*config.Tracer, txctx); err != nil { + return nil, err } + deadlineCtx, cancel := context.WithTimeout(ctx, timeout) + go func() { + <-deadlineCtx.Done() + if deadlineCtx.Err() == context.DeadlineExceeded { + tracer.(*JSTracer).Stop(errors.New("execution timeout")) + } + }() + defer cancel() case config == nil: tracer = vm.NewStructLogger(nil) @@ -909,7 +903,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex StructLogs: ethapi.FormatLogs(tracer.StructLogs()), }, nil - case native.Tracer: + case Tracer: return tracer.GetResult() default: diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 98e1b35373..fb7b0a1982 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -26,10 +26,11 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/eth/tracers" ) func init() { - register("callTracerNative", NewCallTracer) + tracers.RegisterNativeTracer("callTracerNative", NewCallTracer) } type callFrame struct { @@ -45,20 +46,20 @@ type callFrame struct { Calls []callFrame `json:"calls,omitempty"` } -type CallTracer struct { +type callTracer struct { callstack []callFrame } // NewCallTracer returns a native go tracer which tracks // call frames of a tx, and implements vm.Tracer. -func NewCallTracer() Tracer { +func NewCallTracer() tracers.Tracer { // First callframe contains tx context info // and is populated on start and end. - t := &CallTracer{callstack: make([]callFrame, 1)} + t := &callTracer{callstack: make([]callFrame, 1)} return t } -func (t *CallTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (t *callTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { t.callstack[0] = callFrame{ Type: "CALL", From: addrToHex(from), @@ -72,7 +73,7 @@ func (t *CallTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad } } -func (t *CallTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { +func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) { t.callstack[0].Output = bytesToHex(output) t.callstack[0].GasUsed = uintToHex(gasUsed) if err != nil { @@ -80,13 +81,13 @@ func (t *CallTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, } } -func (t *CallTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (t *callTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { } -func (t *CallTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { +func (t *callTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, _ *vm.ScopeContext, depth int, err error) { } -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) { call := callFrame{ Type: typ.String(), From: addrToHex(from), @@ -98,7 +99,7 @@ func (t *CallTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common. t.callstack = append(t.callstack, call) } -func (t *CallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) { size := len(t.callstack) if size <= 1 { return @@ -120,7 +121,7 @@ func (t *CallTracer) CaptureExit(output []byte, gasUsed uint64, err error) { t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call) } -func (t *CallTracer) GetResult() (json.RawMessage, error) { +func (t *callTracer) GetResult() (json.RawMessage, error) { if len(t.callstack) != 1 { return nil, errors.New("incorrect number of top-level calls") } @@ -131,6 +132,10 @@ func (t *CallTracer) GetResult() (json.RawMessage, error) { return json.RawMessage(res), nil } +func (t *callTracer) Stop(err error) { + // TODO +} + func bytesToHex(s []byte) string { return "0x" + common.Bytes2Hex(s) } diff --git a/eth/tracers/native/native.go b/eth/tracers/native/native.go deleted file mode 100644 index 4148c52c7c..0000000000 --- a/eth/tracers/native/native.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2021 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package native - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/core/vm" -) - -// Tracer interface extends vm.Tracer and additionally -// allows collecting the tracing result. -type Tracer interface { - vm.Tracer - GetResult() (json.RawMessage, error) -} - -// constructor creates a new instance of a Tracer. -type constructor func() Tracer - -var tracers map[string]constructor = make(map[string]constructor) - -// register makes native tracers in this directory which adhere -// to the `Tracer` interface available to the rest of the codebase. -// It is typically invoked in the `init()` function. -func register(name string, fn constructor) { - tracers[name] = fn -} - -// New returns a new instance of a tracer, if one was -// registered under the given name. -func New(name string) (Tracer, bool) { - if fn, ok := tracers[name]; ok { - return fn(), true - } - return nil, false -} diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index ed56004533..23a0221d10 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -100,13 +100,13 @@ func (mw *memoryWrapper) slice(begin, end int64) []byte { if end < begin || begin < 0 { // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "offset", begin, "end", end) + log.Warn("JSTracer accessed out of bound memory", "offset", begin, "end", end) return nil } if mw.memory.Len() < int(end) { // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) + log.Warn("JSTracer accessed out of bound memory", "available", mw.memory.Len(), "offset", begin, "size", end-begin) return nil } return mw.memory.GetCopy(begin, end-begin) @@ -117,7 +117,7 @@ func (mw *memoryWrapper) getUint(addr int64) *big.Int { if mw.memory.Len() < int(addr)+32 || addr < 0 { // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32) + log.Warn("JSTracer accessed out of bound memory", "available", mw.memory.Len(), "offset", addr, "size", 32) return new(big.Int) } return new(big.Int).SetBytes(mw.memory.GetPtr(addr, 32)) @@ -160,7 +160,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int { if len(sw.stack.Data()) <= idx || idx < 0 { // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) + log.Warn("JSTracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx) return new(big.Int) } return sw.stack.Back(idx).ToBig() @@ -365,7 +365,7 @@ func (r *frameResult) pushObject(vm *duktape.Context) { // Tracer provides an implementation of Tracer that evaluates a Javascript // function for each VM execution step. -type Tracer struct { +type JSTracer struct { vm *duktape.Context // Javascript VM instance tracerObject int // Stack index of the tracer JavaScript object @@ -409,12 +409,8 @@ type Context struct { // New instantiates a new tracer instance. code specifies a Javascript snippet, // which must evaluate to an expression returning an object with 'step', 'fault' // and 'result' functions. -func New(code string, ctx *Context) (*Tracer, error) { - // Resolve any tracers by name and assemble the tracer object - if tracer, ok := tracer(code); ok { - code = tracer - } - tracer := &Tracer{ +func newJsTracer(code string, ctx *Context) (*JSTracer, error) { + tracer := &JSTracer{ vm: duktape.New(), ctx: make(map[string]interface{}), opWrapper: new(opWrapper), @@ -522,7 +518,7 @@ func New(code string, ctx *Context) (*Tracer, error) { if start < 0 || start > end || end > len(blob) { // TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go // runtime goes belly up https://github.com/golang/go/issues/15639. - log.Warn("Tracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size) + log.Warn("JSTracer accessed out of bound memory", "available", len(blob), "offset", start, "size", size) ctx.PushFixedBuffer(0) return 1 } @@ -566,7 +562,7 @@ func New(code string, ctx *Context) (*Tracer, error) { tracer.traceCallFrames = hasEnter tracer.traceSteps = hasStep - // Tracer is valid, inject the big int library to access large numbers + // JSTracer is valid, inject the big int library to access large numbers tracer.vm.EvalString(bigIntegerJS) tracer.vm.PutGlobalString("bigInt") @@ -627,14 +623,14 @@ func New(code string, ctx *Context) (*Tracer, error) { } // Stop terminates execution of the tracer at the first opportune moment. -func (jst *Tracer) Stop(err error) { +func (jst *JSTracer) Stop(err error) { jst.reason = err atomic.StoreUint32(&jst.interrupt, 1) } // call executes a method on a JS object, catching any errors, formatting and // returning them as error objects. -func (jst *Tracer) call(noret bool, method string, args ...string) (json.RawMessage, error) { +func (jst *JSTracer) call(noret bool, method string, args ...string) (json.RawMessage, error) { // Execute the JavaScript call and return any error jst.vm.PushString(method) for _, arg := range args { @@ -670,7 +666,7 @@ func wrapError(context string, err error) error { } // CaptureStart implements the Tracer interface to initialize the tracing operation. -func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (jst *JSTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { jst.ctx["type"] = "CALL" if create { jst.ctx["type"] = "CREATE" @@ -700,7 +696,7 @@ func (jst *Tracer) CaptureStart(env *vm.EVM, from common.Address, to common.Addr } // CaptureState implements the Tracer interface to trace a single step of VM execution. -func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (jst *JSTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { if !jst.traceSteps { return } @@ -736,7 +732,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost } // CaptureFault implements the Tracer interface to trace an execution fault -func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (jst *JSTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { if jst.err != nil { return } @@ -750,7 +746,7 @@ func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost } // CaptureEnd is called after the call finishes to finalize the tracing. -func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { +func (jst *JSTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) { jst.ctx["output"] = output jst.ctx["time"] = t.String() jst.ctx["gasUsed"] = gasUsed @@ -761,7 +757,7 @@ func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, er } // CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). -func (jst *Tracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (jst *JSTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { if !jst.traceCallFrames { return } @@ -791,7 +787,7 @@ func (jst *Tracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad // CaptureExit is called when EVM exits a scope, even if the scope didn't // execute any code. -func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) { +func (jst *JSTracer) CaptureExit(output []byte, gasUsed uint64, err error) { if !jst.traceCallFrames { return } @@ -815,7 +811,7 @@ func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) { } // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error -func (jst *Tracer) GetResult() (json.RawMessage, error) { +func (jst *JSTracer) GetResult() (json.RawMessage, error) { // Transform the context into a JavaScript object and inject into the state obj := jst.vm.PushObject() @@ -837,7 +833,7 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) { } // addToObj pushes a field to a JS object. -func (jst *Tracer) addToObj(obj int, key string, val interface{}) { +func (jst *JSTracer) addToObj(obj int, key string, val interface{}) { pushValue(jst.vm, val) jst.vm.PutPropString(obj, key) } diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go index 3decca225d..6834947f05 100644 --- a/eth/tracers/tracer_test.go +++ b/eth/tracers/tracer_test.go @@ -58,7 +58,7 @@ func testCtx() *vmContext { return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}} } -func runTrace(tracer *Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { +func runTrace(tracer Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) { env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer}) var ( startGas uint64 = 10000 @@ -168,7 +168,7 @@ func TestHaltBetweenSteps(t *testing.T) { // TestNoStepExec tests a regular value transfer (no exec), and accessing the statedb // in 'result' func TestNoStepExec(t *testing.T) { - runEmptyTrace := func(tracer *Tracer, vmctx *vmContext) (json.RawMessage, error) { + runEmptyTrace := func(tracer Tracer, vmctx *vmContext) (json.RawMessage, error) { env := vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}) startGas := uint64(10000) contract := vm.NewContract(account{}, account{}, big.NewInt(0), startGas) @@ -223,7 +223,7 @@ func TestIsPrecompile(t *testing.T) { t.Error(err) } if string(res) != "false" { - t.Errorf("Tracer should not consider blake2f as precompile in byzantium") + t.Errorf("JSTracer should not consider blake2f as precompile in byzantium") } tracer, _ = New("{addr: toAddress('0000000000000000000000000000000000000009'), res: null, step: function() { this.res = isPrecompiled(this.addr); }, fault: function() {}, result: function() { return this.res; }}", new(Context)) @@ -233,7 +233,7 @@ func TestIsPrecompile(t *testing.T) { t.Error(err) } if string(res) != "true" { - t.Errorf("Tracer should consider blake2f as precompile in istanbul") + t.Errorf("JSTracer should consider blake2f as precompile in istanbul") } } diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go index 4e1ef23ad2..9eef6bfb10 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/tracers.go @@ -18,14 +18,55 @@ package tracers import ( + "encoding/json" + "fmt" "strings" "unicode" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers/internal/tracers" ) -// all contains all the built in JavaScript tracers by name. -var all = make(map[string]string) +// Tracer interface extends vm.JSTracer and additionally +// allows collecting the tracing result. +type Tracer interface { + vm.Tracer + GetResult() (json.RawMessage, error) + // Stop terminates execution of the tracer at the first opportune moment. + Stop(err error) +} + +var ( + nativeTracers map[string]func() Tracer = make(map[string]func() Tracer) + jsTracers = make(map[string]string) +) + +// RegisterNativeTracer makes native tracers in this directory which adhere +// to the `JSTracer` interface available to the rest of the codebase. +// It is typically invoked in the `init()` function. +func RegisterNativeTracer(name string, ctor func() Tracer) { + nativeTracers[name] = ctor +} + +// New returns a new instance of a tracer, +// 1. If 'code' is the name of a registered native tracer, then that tracer +// is instantiated and returned +// 2. If 'code' is the name of a registered js-tracer, then that tracer is +// instantiated and returned +// 3. Otherwise, the code is interpreted as the js code of a js-tracer, and +// is evaluated and returned. +func New(code string, ctx *Context) (Tracer, error) { + // Resolve native tracer + if fn, ok := nativeTracers[code]; ok { + return fn(), nil + } + panic(fmt.Sprintf("no native tracer %v found", code)) + // Resolve js-tracers by name and assemble the tracer object + if tracer, ok := jsTracers[code]; ok { + code = tracer + } + return newJsTracer(code, ctx) +} // camel converts a snake cased input string into a camel cased output. func camel(str string) string { @@ -40,14 +81,6 @@ func camel(str string) string { func init() { for _, file := range tracers.AssetNames() { name := camel(strings.TrimSuffix(file, ".js")) - all[name] = string(tracers.MustAsset(file)) + jsTracers[name] = string(tracers.MustAsset(file)) } } - -// tracer retrieves a specific JavaScript tracer by name. -func tracer(name string) (string, bool) { - if tracer, ok := all[name]; ok { - return tracer, true - } - return "", false -} diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 7f5f19f1eb..c0b4302944 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -35,7 +35,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/tracers/native" + //"github.com/ethereum/go-ethereum/eth/tracers/native" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/tests" @@ -205,7 +205,7 @@ func TestPrestateTracerCreate2(t *testing.T) { // Iterates over all the input-output datasets in the tracer test harness and // runs the JavaScript tracers against them. func TestCallTracerLegacy(t *testing.T) { - newTracer := func() native.Tracer { + newTracer := func() Tracer { tracer, err := New("callTracerLegacy", new(Context)) if err != nil { t.Fatalf("failed to create call tracer: %v", err) @@ -217,7 +217,7 @@ func TestCallTracerLegacy(t *testing.T) { } func TestCallTracer(t *testing.T) { - newTracer := func() native.Tracer { + newTracer := func() Tracer { tracer, err := New("callTracer", new(Context)) if err != nil { t.Fatalf("failed to create call tracer: %v", err) @@ -229,18 +229,17 @@ func TestCallTracer(t *testing.T) { } func TestCallTracerNative(t *testing.T) { - newTracer := func() native.Tracer { - tracer, ok := native.New("callTracerNative") - if !ok { - t.Fatal("failed to create native call tracer") + newTracer := func() Tracer { + tracer, err := New("callTracerNative", nil /* TODO? */) + if err != nil { + t.Fatalf("failed to create native call tracer: %v", err) } return tracer } - testCallTracer(newTracer, "call_tracer", t) } -func testCallTracer(newTracer func() native.Tracer, dirPath string, t *testing.T) { +func testCallTracer(newTracer func() Tracer, dirPath string, t *testing.T) { files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath)) if err != nil { t.Fatalf("failed to retrieve tracer test suite: %v", err) @@ -431,7 +430,7 @@ func BenchmarkTracers(b *testing.B) { if err := json.Unmarshal(blob, test); err != nil { b.Fatalf("failed to parse testcase: %v", err) } - newTracer := func() native.Tracer { + newTracer := func() Tracer { tracer, err := New("callTracer", new(Context)) if err != nil { b.Fatalf("failed to create call tracer: %v", err) @@ -443,7 +442,7 @@ func BenchmarkTracers(b *testing.B) { } } -func benchTracer(newTracer func() native.Tracer, test *callTracerTest, b *testing.B) { +func benchTracer(newTracer func() Tracer, test *callTracerTest, b *testing.B) { // Configure a blockchain with the given prestate tx := new(types.Transaction) if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {