eth/tracers: support for golang tracers + add golang callTracer #23708 (#1264)

This commit is contained in:
Daniel Liu 2025-08-28 18:53:13 +08:00 committed by GitHub
parent 9425af8b7c
commit d138eb22a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 15 additions and 18 deletions

View file

@ -363,10 +363,9 @@ func (r *frameResult) pushObject(vm *duktape.Context) {
vm.PutPropString(obj, "getError") vm.PutPropString(obj, "getError")
} }
// JsTracer provides an implementation of JsTracer that evaluates a Javascript // jsTracer provides an implementation of jsTracer that evaluates a Javascript
// function for each VM execution step. // function for each VM execution step.
// TODO gerui rename to private func type jsTracer struct {
type JsTracer struct {
vm *duktape.Context // Javascript VM instance vm *duktape.Context // Javascript VM instance
tracerObject int // Stack index of the tracer JavaScript object tracerObject int // Stack index of the tracer JavaScript object
@ -410,9 +409,8 @@ type Context struct {
// New instantiates a new tracer instance. code specifies a Javascript snippet, // New instantiates a new tracer instance. code specifies a Javascript snippet,
// which must evaluate to an expression returning an object with 'step', 'fault' // which must evaluate to an expression returning an object with 'step', 'fault'
// and 'result' functions. // and 'result' functions.
// TODO gerui rename to private func func newJsTracer(code string, ctx *Context) (*jsTracer, error) {
func NewJsTracer(code string, ctx *Context) (*JsTracer, error) { tracer := &jsTracer{
tracer := &JsTracer{
vm: duktape.New(), vm: duktape.New(),
ctx: make(map[string]interface{}), ctx: make(map[string]interface{}),
opWrapper: new(opWrapper), opWrapper: new(opWrapper),
@ -619,14 +617,14 @@ func NewJsTracer(code string, ctx *Context) (*JsTracer, error) {
} }
// Stop terminates execution of the tracer at the first opportune moment. // Stop terminates execution of the tracer at the first opportune moment.
func (jst *JsTracer) Stop(err error) { func (jst *jsTracer) Stop(err error) {
jst.reason = err jst.reason = err
atomic.StoreUint32(&jst.interrupt, 1) atomic.StoreUint32(&jst.interrupt, 1)
} }
// call executes a method on a JS object, catching any errors, formatting and // call executes a method on a JS object, catching any errors, formatting and
// returning them as error objects. // returning them as error objects.
func (jst *JsTracer) 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 // Execute the JavaScript call and return any error
jst.vm.PushString(method) jst.vm.PushString(method)
for _, arg := range args { for _, arg := range args {
@ -662,7 +660,7 @@ func wrapError(context string, err error) error {
} }
// CaptureStart implements the Tracer interface to initialize the tracing operation. // CaptureStart implements the Tracer interface to initialize the tracing operation.
func (jst *JsTracer) 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" jst.ctx["type"] = "CALL"
if create { if create {
jst.ctx["type"] = "CREATE" jst.ctx["type"] = "CREATE"
@ -695,7 +693,7 @@ func (jst *JsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
} }
// CaptureState implements the Tracer interface to trace a single step of VM execution. // CaptureState implements the Tracer interface to trace a single step of VM execution.
func (jst *JsTracer) 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 { if !jst.traceSteps {
return return
} }
@ -731,7 +729,7 @@ func (jst *JsTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
} }
// CaptureFault implements the Tracer interface to trace an execution fault // CaptureFault implements the Tracer interface to trace an execution fault
func (jst *JsTracer) 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 { if jst.err != nil {
return return
} }
@ -745,7 +743,7 @@ func (jst *JsTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
} }
// CaptureEnd is called after the call finishes to finalize the tracing. // CaptureEnd is called after the call finishes to finalize the tracing.
func (jst *JsTracer) 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["output"] = output
jst.ctx["time"] = t.String() jst.ctx["time"] = t.String()
jst.ctx["gasUsed"] = gasUsed jst.ctx["gasUsed"] = gasUsed
@ -756,7 +754,7 @@ func (jst *JsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
} }
// 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 (jst *JsTracer) 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 { if !jst.traceCallFrames {
return return
} }
@ -786,7 +784,7 @@ func (jst *JsTracer) 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 (jst *JsTracer) CaptureExit(output []byte, gasUsed uint64, err error) { func (jst *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
if !jst.traceCallFrames { if !jst.traceCallFrames {
return return
} }
@ -810,7 +808,7 @@ func (jst *JsTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
} }
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error // GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
func (jst *JsTracer) GetResult() (json.RawMessage, error) { func (jst *jsTracer) GetResult() (json.RawMessage, error) {
// Transform the context into a JavaScript object and inject into the state // Transform the context into a JavaScript object and inject into the state
obj := jst.vm.PushObject() obj := jst.vm.PushObject()
@ -832,7 +830,7 @@ func (jst *JsTracer) GetResult() (json.RawMessage, error) {
} }
// addToObj pushes a field to a JS object. // addToObj pushes a field to a JS object.
func (jst *JsTracer) addToObj(obj int, key string, val interface{}) { func (jst *jsTracer) addToObj(obj int, key string, val interface{}) {
pushValue(jst.vm, val) pushValue(jst.vm, val)
jst.vm.PutPropString(obj, key) jst.vm.PutPropString(obj, key)
} }

View file

@ -66,7 +66,7 @@ func New(code string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
if tracer, ok := jsTracers[code]; ok { if tracer, ok := jsTracers[code]; ok {
code = tracer code = tracer
} }
return NewJsTracer(code, ctx) return newJsTracer(code, ctx)
} }
// camel converts a snake cased input string into a camel cased output. // camel converts a snake cased input string into a camel cased output.
@ -83,6 +83,5 @@ func init() {
for _, file := range tracers.AssetNames() { for _, file := range tracers.AssetNames() {
name := camel(strings.TrimSuffix(file, ".js")) name := camel(strings.TrimSuffix(file, ".js"))
jsTracers[name] = string(tracers.MustAsset(file)) jsTracers[name] = string(tracers.MustAsset(file))
} }
} }