forked from cerc-io/plugeth
eth/tracers: implement debug.intermediateRoots (#23594)
This PR implements a new debug method, which I've talked briefly about to some other client developers. It allows the caller to obtain the intermediate state roots for a block (which might be either a canon block or a 'bad' block).
This commit is contained in:
parent
443afc975c
commit
ab2caaee11
@ -445,13 +445,12 @@ 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
|
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
|
||||||
// object.
|
// object.
|
||||||
func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
|
||||||
for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
|
block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||||
if block.Hash() == hash {
|
if block == nil {
|
||||||
return api.traceBlock(ctx, block, config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
// StandardTraceBlockToFile dumps the structured logs created during the
|
||||||
// execution of EVM to the local file system and returns a list of files
|
// execution of EVM to the local file system and returns a list of files
|
||||||
@ -464,17 +463,73 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash,
|
|||||||
return api.standardTraceBlockToFile(ctx, block, config)
|
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 in the bad blocks
|
||||||
|
block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
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, vm.Config{})
|
||||||
|
)
|
||||||
|
statedb.Prepare(tx.Hash(), i)
|
||||||
|
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
|
||||||
|
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
|
||||||
|
roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
|
||||||
|
}
|
||||||
|
return roots, nil
|
||||||
|
}
|
||||||
|
|
||||||
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
// StandardTraceBadBlockToFile dumps the structured logs created during the
|
||||||
// execution of EVM against a block pulled from the pool of bad ones to 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.
|
// 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) {
|
func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
|
||||||
for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
|
block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
|
||||||
if block.Hash() == hash {
|
if block == nil {
|
||||||
return api.standardTraceBlockToFile(ctx, block, config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
// traceBlock configures a new tracer according to the provided configuration, and
|
||||||
// executes all the transactions contained within. The return value will be one item
|
// executes all the transactions contained within. The return value will be one item
|
||||||
|
@ -391,6 +391,12 @@ web3._extend({
|
|||||||
params: 2,
|
params: 2,
|
||||||
inputFormatter: [null, null]
|
inputFormatter: [null, null]
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'intermediateRoots',
|
||||||
|
call: 'debug_intermediateRoots',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [null, null]
|
||||||
|
}),
|
||||||
new web3._extend.Method({
|
new web3._extend.Method({
|
||||||
name: 'standardTraceBlockToFile',
|
name: 'standardTraceBlockToFile',
|
||||||
call: 'debug_standardTraceBlockToFile',
|
call: 'debug_standardTraceBlockToFile',
|
||||||
|
Loading…
Reference in New Issue
Block a user