eth/tracers: rework the model a bit
This commit is contained in:
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user