diff --git a/internal/ethapi/tracer.go b/internal/ethapi/tracer.go index fc742e6c44..71cafc6e97 100644 --- a/internal/ethapi/tracer.go +++ b/internal/ethapi/tracer.go @@ -200,19 +200,18 @@ func (c *contractWrapper) toValue(vm *otto.Otto) otto.Value { // JavascriptTracer provides an implementation of Tracer that evaluates a // Javascript function for each VM execution step. type JavascriptTracer struct { - vm *otto.Otto // Javascript VM instance - traceobj *otto.Object // User-supplied object to call - log map[string]interface{} // (Reusable) map for the `log` arg to `step` - logvalue otto.Value // JS view of `log` - memory *memoryWrapper // Wrapper around the VM memory - memvalue otto.Value // JS view of `memory` - stack *stackWrapper // Wrapper around the VM stack - stackvalue otto.Value // JS view of `stack` - db *dbWrapper // Wrapper around the VM environment - dbvalue otto.Value // JS view of `db` - contract *contractWrapper // Wrapper around the contract object - contractvalue otto.Value // JS view of `contract` - err error // Error, if one has occurred + vm *otto.Otto // Javascript VM instance + traceobj *otto.Object // User-supplied object to call + op *opCodeWrapper // Wrapper around the VM opcode + log map[string]interface{} // (Reusable) map for the `log` arg to `step` + logvalue otto.Value // JS view of `log` + memory *memoryWrapper // Wrapper around the VM memory + stack *stackWrapper // Wrapper around the VM stack + db *dbWrapper // Wrapper around the VM environment + dbvalue otto.Value // JS view of `db` + contract *contractWrapper // Wrapper around the contract object + err error // Error, if one has occurred + result interface{} // Final result to return to the user } // NewJavascriptTracer instantiates a new JavascriptTracer instance. @@ -230,7 +229,6 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) { if err != nil { return nil, err } - // Check the required functions exist step, err := jstracer.Get("step") if err != nil { @@ -247,31 +245,34 @@ func NewJavascriptTracer(code string) (*JavascriptTracer, error) { if !result.IsFunction() { return nil, fmt.Errorf("Trace object must expose a function result()") } - // Create the persistent log object - log := make(map[string]interface{}) + var ( + op = new(opCodeWrapper) + mem = new(memoryWrapper) + stack = new(stackWrapper) + db = new(dbWrapper) + contract = new(contractWrapper) + ) + log := map[string]interface{}{ + "op": op.toValue(vm), + "memory": mem.toValue(vm), + "stack": stack.toValue(vm), + "contract": contract.toValue(vm), + } logvalue, _ := vm.ToValue(log) - // Create persistent wrappers for memory and stack - mem := &memoryWrapper{} - stack := &stackWrapper{} - db := &dbWrapper{} - contract := &contractWrapper{} - return &JavascriptTracer{ - vm: vm, - traceobj: jstracer, - log: log, - logvalue: logvalue, - memory: mem, - memvalue: mem.toValue(vm), - stack: stack, - stackvalue: stack.toValue(vm), - db: db, - dbvalue: db.toValue(vm), - contract: contract, - contractvalue: contract.toValue(vm), - err: nil, + vm: vm, + traceobj: jstracer, + op: op, + log: log, + logvalue: logvalue, + memory: mem, + stack: stack, + db: db, + dbvalue: db.toValue(vm), + contract: contract, + err: nil, }, nil } @@ -319,24 +320,22 @@ func wrapError(context string, err error) error { // CaptureState implements the Tracer interface to trace a single step of VM execution func (jst *JavascriptTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error { if jst.err == nil { + jst.op.op = op jst.memory.memory = memory jst.stack.stack = stack jst.db.db = env.StateDB jst.contract.contract = contract - ocw := &opCodeWrapper{op} - jst.log["pc"] = pc - jst.log["op"] = ocw.toValue(jst.vm) jst.log["gas"] = gas - jst.log["gasPrice"] = cost - jst.log["memory"] = jst.memvalue - jst.log["stack"] = jst.stackvalue - jst.log["contract"] = jst.contractvalue + jst.log["cost"] = cost jst.log["depth"] = depth jst.log["account"] = contract.Address() - jst.log["err"] = err + delete(jst.log, "error") + if err != nil { + jst.log["error"] = err + } _, err := jst.callSafely("step", jst.logvalue, jst.dbvalue) if err != nil { jst.err = wrapError("step", err)