|
|
|
@ -53,6 +53,13 @@ const ( |
|
|
|
|
// and reexecute to produce missing historical state necessary to run a specific
|
|
|
|
|
// trace.
|
|
|
|
|
defaultTraceReexec = uint64(128) |
|
|
|
|
|
|
|
|
|
// defaultTracechainMemLimit is the size of the triedb, at which traceChain
|
|
|
|
|
// switches over and tries to use a disk-backed database instead of building
|
|
|
|
|
// on top of memory.
|
|
|
|
|
// For non-archive nodes, this limit _will_ be overblown, as disk-backed tries
|
|
|
|
|
// will only be found every ~15K blocks or so.
|
|
|
|
|
defaultTracechainMemLimit = common.StorageSize(500 * 1024 * 1024) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Backend interface provides the common API services (that are provided by
|
|
|
|
@ -67,7 +74,10 @@ type Backend interface { |
|
|
|
|
ChainConfig() *params.ChainConfig |
|
|
|
|
Engine() consensus.Engine |
|
|
|
|
ChainDb() ethdb.Database |
|
|
|
|
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive bool) (*state.StateDB, error) |
|
|
|
|
// StateAtBlock returns the state corresponding to the stateroot of the block.
|
|
|
|
|
// N.B: For executing transactions on block N, the required stateRoot is block N-1,
|
|
|
|
|
// so this method should be called with the parent.
|
|
|
|
|
StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, error) |
|
|
|
|
StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (core.Message, vm.BlockContext, *state.StateDB, error) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -320,6 +330,7 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config |
|
|
|
|
} |
|
|
|
|
close(results) |
|
|
|
|
}() |
|
|
|
|
var preferDisk bool |
|
|
|
|
// Feed all the blocks both into the tracer, as well as fast process concurrently
|
|
|
|
|
for number = start.NumberU64(); number < end.NumberU64(); number++ { |
|
|
|
|
// Stop tracing if interruption was requested
|
|
|
|
@ -349,18 +360,24 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config |
|
|
|
|
} |
|
|
|
|
// Prepare the statedb for tracing. Don't use the live database for
|
|
|
|
|
// tracing to avoid persisting state junks into the database.
|
|
|
|
|
statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false) |
|
|
|
|
statedb, err = api.backend.StateAtBlock(localctx, block, reexec, statedb, false, preferDisk) |
|
|
|
|
if err != nil { |
|
|
|
|
failed = err |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
if statedb.Database().TrieDB() != nil { |
|
|
|
|
if trieDb := statedb.Database().TrieDB(); trieDb != nil { |
|
|
|
|
// Hold the reference for tracer, will be released at the final stage
|
|
|
|
|
statedb.Database().TrieDB().Reference(block.Root(), common.Hash{}) |
|
|
|
|
trieDb.Reference(block.Root(), common.Hash{}) |
|
|
|
|
|
|
|
|
|
// Release the parent state because it's already held by the tracer
|
|
|
|
|
if parent != (common.Hash{}) { |
|
|
|
|
statedb.Database().TrieDB().Dereference(parent) |
|
|
|
|
trieDb.Dereference(parent) |
|
|
|
|
} |
|
|
|
|
// Prefer disk if the trie db memory grows too much
|
|
|
|
|
s1, s2 := trieDb.Size() |
|
|
|
|
if !preferDisk && (s1+s2) > defaultTracechainMemLimit { |
|
|
|
|
log.Info("Switching to prefer-disk mode for tracing", "size", s1+s2) |
|
|
|
|
preferDisk = true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
parent = block.Root() |
|
|
|
@ -496,7 +513,7 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config |
|
|
|
|
if config != nil && config.Reexec != nil { |
|
|
|
|
reexec = *config.Reexec |
|
|
|
|
} |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
@ -557,7 +574,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac |
|
|
|
|
if config != nil && config.Reexec != nil { |
|
|
|
|
reexec = *config.Reexec |
|
|
|
|
} |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
@ -646,7 +663,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block |
|
|
|
|
if config != nil && config.Reexec != nil { |
|
|
|
|
reexec = *config.Reexec |
|
|
|
|
} |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true) |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true, false) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
@ -810,7 +827,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc |
|
|
|
|
if config != nil && config.Reexec != nil { |
|
|
|
|
reexec = *config.Reexec |
|
|
|
|
} |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true) |
|
|
|
|
statedb, err := api.backend.StateAtBlock(ctx, block, reexec, nil, true, false) |
|
|
|
|
if err != nil { |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|