diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 396e760fd0..ebdbff8958 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -191,6 +191,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) gaspool.SetGas(prevGas) + tracer.CaptureTxEnd(nil, err) continue } includedTxs = append(includedTxs, tx) @@ -232,7 +233,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, //receipt.BlockNumber receipt.TransactionIndex = uint(txIndex) receipts = append(receipts, receipt) - tracer.CaptureTxEnd(receipt) + tracer.CaptureTxEnd(receipt, nil) } txIndex++ diff --git a/core/state_processor.go b/core/state_processor.go index a5972f371b..75df98e71b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -108,11 +108,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg } func applyTransaction(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { - var receipt *types.Receipt + var ( + receipt *types.Receipt + err error + ) if evm.Config.Tracer != nil { evm.Config.Tracer.CaptureTxStart(evm, tx) defer func() { - evm.Config.Tracer.CaptureTxEnd(receipt) + evm.Config.Tracer.CaptureTxEnd(receipt, err) }() } // Create a new context to be used in the EVM environment. diff --git a/core/vm/logger.go b/core/vm/logger.go index b7b0fc4d8e..0408411500 100644 --- a/core/vm/logger.go +++ b/core/vm/logger.go @@ -31,7 +31,7 @@ import ( type EVMLogger interface { // Transaction level CaptureTxStart(evm *EVM, tx *types.Transaction) - CaptureTxEnd(receipt *types.Receipt) + CaptureTxEnd(receipt *types.Receipt, err error) // Top call frame CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) CaptureEnd(output []byte, gasUsed uint64, err error) diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index a3e75c6721..d6a3a5508a 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -116,6 +116,9 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { sender = vm.AccountRef(cfg.Origin) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit})) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) @@ -149,6 +152,9 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { sender = vm.AccountRef(cfg.Origin) rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{Data: input, Value: cfg.Value, Gas: cfg.GasLimit})) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) @@ -177,6 +183,9 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er statedb = cfg.State rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time) ) + if cfg.EVMConfig.Tracer != nil { + cfg.EVMConfig.Tracer.CaptureTxStart(vmenv, types.NewTx(&types.LegacyTx{To: &address, Data: input, Value: cfg.Value, Gas: cfg.GasLimit})) + } // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go index 7b521a2ead..f411a67f8d 100644 --- a/core/vm/runtime/runtime_test.go +++ b/core/vm/runtime/runtime_test.go @@ -811,7 +811,7 @@ func TestRuntimeJSTracer(t *testing.T) { byte(vm.PUSH1), 0, byte(vm.RETURN), } - depressedCode := []byte{ + suicideCode := []byte{ byte(vm.PUSH1), 0xaa, byte(vm.SELFDESTRUCT), } @@ -824,7 +824,7 @@ func TestRuntimeJSTracer(t *testing.T) { statedb.SetCode(common.HexToAddress("0xcc"), calleeCode) statedb.SetCode(common.HexToAddress("0xdd"), calleeCode) statedb.SetCode(common.HexToAddress("0xee"), calleeCode) - statedb.SetCode(common.HexToAddress("0xff"), depressedCode) + statedb.SetCode(common.HexToAddress("0xff"), suicideCode) tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil) if err != nil { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 7df7e7526a..fd786231c0 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -792,7 +792,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block statedb.SetTxContext(tx.Hash(), i) vmConf.Tracer.CaptureTxStart(vmenv, tx) vmRet, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.GasLimit)) - vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}) + vmConf.Tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err) if writer != nil { writer.Flush() } @@ -976,10 +976,11 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor tracer.CaptureTxStart(vmenv, tx) res, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.GasLimit)) if err != nil { + tracer.CaptureTxEnd(nil, err) return nil, fmt.Errorf("tracing failed: %w", err) } r := &types.Receipt{GasUsed: res.UsedGas} - tracer.CaptureTxEnd(r) + tracer.CaptureTxEnd(r, nil) return tracer.GetResult() } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index dd33469edf..6f32be31bf 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -156,7 +156,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}) + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected. res, err := tracer.GetResult() if err != nil { @@ -382,7 +382,7 @@ func TestInternals(t *testing.T) { if err != nil { t.Fatalf("test %v: failed to execute transaction: %v", tc.name, err) } - tc.tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}) + tc.tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tc.tracer.GetResult() if err != nil { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index dac3ef06cf..9d339dcf52 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -119,7 +119,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string if err != nil { return fmt.Errorf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}) + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the etalon res, err := tracer.GetResult() diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 9366bc418f..74d5492d52 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -125,7 +125,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { if err != nil { t.Fatalf("failed to execute transaction: %v", err) } - tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}) + tracer.CaptureTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, nil) // Retrieve the trace result and compare against the expected res, err := tracer.GetResult() if err != nil { diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index e4a06195f8..8ed55ef1c6 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -229,7 +229,14 @@ func (t *jsTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { // CaptureTxEnd implements the Tracer interface and is invoked at the end of // transaction processing. -func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt) { +func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + // Don't override vm error + if _, ok := t.ctx["error"]; !ok { + t.ctx["error"] = t.vm.ToValue(err.Error()) + } + return + } t.ctx["gasUsed"] = t.vm.ToValue(receipt.GasUsed) } diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index c2273b5f08..4a7d6c5bcd 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -79,7 +79,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon ret, err := env.Interpreter().Run(contract, []byte{}, false) tracer.CaptureEnd(ret, startGas-contract.Gas, err) // Rest gas assumes no refund - tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}) + tracer.CaptureTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) if err != nil { return nil, err } @@ -184,6 +184,8 @@ func TestHaltBetweenSteps(t *testing.T) { scope := &vm.ScopeContext{ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0), } + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) + tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{})) tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0)) tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil) timeout := errors.New("stahp") @@ -204,6 +206,8 @@ func TestNoStepExec(t *testing.T) { if err != nil { t.Fatal(err) } + env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer}) + tracer.CaptureTxStart(env, types.NewTx(&types.LegacyTx{})) tracer.CaptureStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) tracer.CaptureEnd(nil, 0, nil) ret, err := tracer.GetResult() diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go index c5d3ec3299..ff7715f2ee 100644 --- a/eth/tracers/logger/access_list_tracer.go +++ b/eth/tracers/logger/access_list_tracer.go @@ -174,7 +174,7 @@ func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) { func (*AccessListTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {} -func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt) {} +func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} func (*AccessListTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {} diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go index c0cb5e1638..2a9e1926bb 100644 --- a/eth/tracers/logger/logger.go +++ b/eth/tracers/logger/logger.go @@ -269,7 +269,14 @@ func (l *StructLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { l.env = env } -func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt) { +func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + // Don't override vm error + if l.err == nil { + l.err = err + } + return + } l.usedGas = receipt.GasUsed } @@ -417,7 +424,7 @@ func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {} func (*mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {} -func (*mdLogger) CaptureTxEnd(receipt *types.Receipt) {} +func (*mdLogger) CaptureTxEnd(receipt *types.Receipt, err error) {} func (*mdLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {} diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go index 845549b63b..c1e25a1c3a 100644 --- a/eth/tracers/logger/logger_json.go +++ b/eth/tracers/logger/logger_json.go @@ -106,7 +106,7 @@ func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { l.env = env } -func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt) {} +func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt, err error) {} func (*JSONLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {} diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go index 87a7bbf958..3af18cd3ce 100644 --- a/eth/tracers/native/call.go +++ b/eth/tracers/native/call.go @@ -200,7 +200,11 @@ func (t *callTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { t.gasLimit = tx.Gas() } -func (t *callTracer) CaptureTxEnd(receipt *types.Receipt) { +func (t *callTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + // Error happened during tx validation. + if err != nil { + return + } t.callstack[0].GasUsed = receipt.GasUsed if t.config.WithLog { // Logs are not emitted when the call fails diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go index 9e07c25155..d63ca85362 100644 --- a/eth/tracers/native/call_flat.go +++ b/eth/tracers/native/call_flat.go @@ -207,8 +207,8 @@ func (t *flatCallTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { t.activePrecompiles = vm.ActivePrecompiles(rules) } -func (t *flatCallTracer) CaptureTxEnd(receipt *types.Receipt) { - t.tracer.CaptureTxEnd(receipt) +func (t *flatCallTracer) CaptureTxEnd(receipt *types.Receipt, err error) { + t.tracer.CaptureTxEnd(receipt, err) } // GetResult returns an empty json object. diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go index d85a3f2c99..2dcb8f2b2d 100644 --- a/eth/tracers/native/mux.go +++ b/eth/tracers/native/mux.go @@ -123,9 +123,9 @@ func (t *muxTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { } } -func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt) { +func (t *muxTracer) CaptureTxEnd(receipt *types.Receipt, err error) { for _, t := range t.tracers { - t.CaptureTxEnd(receipt) + t.CaptureTxEnd(receipt, err) } } diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go index 79bfa337a1..52c975ce66 100644 --- a/eth/tracers/native/prestate.go +++ b/eth/tracers/native/prestate.go @@ -176,10 +176,13 @@ func (t *prestateTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { } } -func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt) { +func (t *prestateTracer) CaptureTxEnd(receipt *types.Receipt, err error) { if !t.config.DiffMode { return } + if err != nil { + return + } for addr, state := range t.pre { // The deleted account's state is pruned from `post` but kept in `pre` diff --git a/eth/tracers/noop.go b/eth/tracers/noop.go index fc68ad8b80..695c99c84b 100644 --- a/eth/tracers/noop.go +++ b/eth/tracers/noop.go @@ -72,7 +72,7 @@ func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error) { func (*NoopTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {} -func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt) {} +func (*NoopTracer) CaptureTxEnd(receipt *types.Receipt, err error) {} func (*NoopTracer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) { } diff --git a/eth/tracers/printer.go b/eth/tracers/printer.go index 3dcf2c1098..a07a7ea02b 100644 --- a/eth/tracers/printer.go +++ b/eth/tracers/printer.go @@ -62,7 +62,11 @@ func (p *Printer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) { } -func (p *Printer) CaptureTxEnd(receipt *types.Receipt) { +func (p *Printer) CaptureTxEnd(receipt *types.Receipt, err error) { + if err != nil { + fmt.Printf("CaptureTxEnd err: %v\n", err) + return + } buf, err := json.Marshal(receipt) if err != nil { fmt.Printf("err: %v\n", err)