From 907fee2d3e566839900514ad0ba7cb2d4240a2c5 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 17 Sep 2021 23:14:21 +0200 Subject: [PATCH 1/3] eth/tracers: implement debug.intermediateRoots --- eth/tracers/api.go | 56 +++++++++++++++++++++++++++++++++++++ internal/web3ext/web3ext.go | 6 ++++ 2 files changed, 62 insertions(+) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 68265932854c..116a8d300e46 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -464,6 +464,62 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, return api.standardTraceBlockToFile(ctx, block, config) } +// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list +// of intermediate roots: the stateroot after each transaction. +func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) { + block, _ := api.blockByHash(ctx, hash) + if block == nil { + // Check if the block is one of the bad ones + for _, b := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { + if b.Hash() == hash { + block = b + } + } + } + if block == nil { + return nil, fmt.Errorf("block %#x not found", hash) + } + if block.NumberU64() == 0 { + return nil, errors.New("genesis is not traceable") + } + parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) + if err != nil { + return nil, err + } + reexec := defaultTraceReexec + if config != nil && config.Reexec != nil { + reexec = *config.Reexec + } + statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) + if err != nil { + return nil, err + } + var ( + roots []common.Hash + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) + chainConfig = api.backend.ChainConfig() + vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + vmConf vm.Config + ) + for i, tx := range block.Transactions() { + var ( + msg, _ = tx.AsMessage(signer, block.BaseFee()) + txContext = core.NewEVMTxContext(msg) + vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) + ) + statedb.Prepare(tx.Hash(), i) + if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { + return roots, err + } + // calling IntermediateRoot will internally call Finalize on the state + // so any modifications are written to the trie + // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect + root := statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number())) + roots = append(roots, root) + } + return roots, nil +} + // StandardTraceBadBlockToFile dumps the structured logs created during the // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 927dba1897b3..2b8b48db6706 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -390,6 +390,12 @@ web3._extend({ params: 2, inputFormatter: [null, null] }), + new web3._extend.Method({ + name: 'intermediateRoots', + call: 'debug_intermediateRoots', + params: 2, + inputFormatter: [null, null] + }), new web3._extend.Method({ name: 'standardTraceBlockToFile', call: 'debug_standardTraceBlockToFile', From c09de4b36d77af1c1d1c69de8b922e482789c17e Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 17 Sep 2021 23:37:54 +0200 Subject: [PATCH 2/3] eth/tracers: use better db accessor method --- eth/tracers/api.go | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 116a8d300e46..6b83c15080ad 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -445,12 +445,11 @@ func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *Tra // EVM against a block pulled from the pool of bad ones and returns them as a JSON // object. func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) { - for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { - if block.Hash() == hash { - return api.traceBlock(ctx, block, config) - } + block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash) + if block == nil { + return nil, fmt.Errorf("bad block %#x not found", hash) } - return nil, fmt.Errorf("bad block %#x not found", hash) + return api.traceBlock(ctx, block, config) } // StandardTraceBlockToFile dumps the structured logs created during the @@ -469,12 +468,8 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash, func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) { block, _ := api.blockByHash(ctx, hash) if block == nil { - // Check if the block is one of the bad ones - for _, b := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { - if b.Hash() == hash { - block = b - } - } + // Check in the bad blocks + block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash) } if block == nil { return nil, fmt.Errorf("block %#x not found", hash) @@ -524,12 +519,11 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config // execution of EVM against a block pulled from the pool of bad ones to the // local file system and returns a list of files to the caller. func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) { - for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) { - if block.Hash() == hash { - return api.standardTraceBlockToFile(ctx, block, config) - } + block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash) + if block == nil { + return nil, fmt.Errorf("bad block %#x not found", hash) } - return nil, fmt.Errorf("bad block %#x not found", hash) + return api.standardTraceBlockToFile(ctx, block, config) } // traceBlock configures a new tracer according to the provided configuration, and From 1442233b187428bf24313ff25b09eee717661d69 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sun, 19 Sep 2021 00:23:59 +0200 Subject: [PATCH 3/3] eth/tracers: return intermediate roots even on tx error --- eth/tracers/api.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 6b83c15080ad..39b95264d762 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -490,27 +490,32 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config return nil, err } var ( - roots []common.Hash - signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) - chainConfig = api.backend.ChainConfig() - vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - vmConf vm.Config + roots []common.Hash + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) + chainConfig = api.backend.ChainConfig() + vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) ) for i, tx := range block.Transactions() { var ( msg, _ = tx.AsMessage(signer, block.BaseFee()) txContext = core.NewEVMTxContext(msg) - vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vmConf) + vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{}) ) statedb.Prepare(tx.Hash(), i) if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil { - return roots, err + log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) + // We intentionally don't return the error here: if we do, then the RPC server will not + // return the roots. Most likely, the caller already knows that a certain transaction fails to + // be included, but still want the intermediate roots that led to that point. + // It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be + // executable. + // N.B: This should never happen while tracing canon blocks, only when tracing bad blocks. + return roots, nil } // calling IntermediateRoot will internally call Finalize on the state // so any modifications are written to the trie - // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect - root := statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number())) - roots = append(roots, root) + roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects)) } return roots, nil }