From e111830bc653116100d3052dfbd85cd606cbba89 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Thu, 15 Dec 2022 15:07:10 +0100 Subject: [PATCH] eth/tracers: exec block serially for native tracer Co-authored-by: Martin Holst Swende --- eth/tracers/api.go | 58 +++++++++++++++++++++++++++++++++++++----- eth/tracers/js/goja.go | 3 +++ eth/tracers/tracers.go | 6 +++++ 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 4436d13961f63..a15ec4366de9c 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -593,6 +593,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac if block.NumberU64() == 0 { return nil, errors.New("genesis is not traceable") } + // Prepare base state parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash()) if err != nil { return nil, err @@ -606,24 +607,68 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac return nil, err } defer release() + // JS tracers have high overhead. In this case run a parallel + // process that generates states in one thread and traces txes + // in separate worker threads. + if config.Tracer != nil && *config.Tracer != "" { + t, err := New(*config.Tracer, nil, nil) + if err != nil { + return nil, err + } + if _, ok := t.(JSTracer); ok { + return api.traceBlockParallel(ctx, block, statedb, config) + } + } + // Native tracers have low overhead + var ( + txs = block.Transactions() + blockHash = block.Hash() + is158 = api.backend.ChainConfig().IsEIP158(block.Number()) + blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) + results = make([]*txTraceResult, len(txs)) + ) + for i, tx := range txs { + // Generate the next state snapshot fast without tracing + msg, _ := tx.AsMessage(signer, block.BaseFee()) + txctx := &Context{ + BlockHash: blockHash, + TxIndex: i, + TxHash: tx.Hash(), + } + res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config) + if err != nil { + return nil, err + } + results[i] = &txTraceResult{Result: res} + // Finalize 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 + statedb.Finalise(is158) + } + return results, nil +} +// traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread +// runs along and executes txes without tracing enabled to generate their prestate. +// Worker threads take the tasks and the prestate and trace them. +func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) { // Execute all the transaction contained within the block concurrently var ( - signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) - txs = block.Transactions() - results = make([]*txTraceResult, len(txs)) - pend sync.WaitGroup + txs = block.Transactions() + blockHash = block.Hash() + blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + signer = types.MakeSigner(api.backend.ChainConfig(), block.Number()) + results = make([]*txTraceResult, len(txs)) + pend sync.WaitGroup ) threads := runtime.NumCPU() if threads > len(txs) { threads = len(txs) } jobs := make(chan *txTraceTask, threads) - blockHash := block.Hash() for th := 0; th < threads; th++ { pend.Add(1) go func() { - blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) defer pend.Done() // Fetch and execute the next transaction trace tasks for task := range jobs { @@ -645,7 +690,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac // Feed the transactions into the tracers and return var failed error - blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) txloop: for i, tx := range txs { // Send the trace task over for execution diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go index 9adfca9fb62a6..ba7f97914e042 100644 --- a/eth/tracers/js/goja.go +++ b/eth/tracers/js/goja.go @@ -350,6 +350,9 @@ func (t *jsTracer) Stop(err error) { t.vm.Interrupt(err) } +// IsJS returns whether this tracer evaluates JS code. +func (t *jsTracer) IsJS() bool { return true } + // onError is called anytime the running JS code is interrupted // and returns an error. It in turn pings the EVM to cancel its // execution. diff --git a/eth/tracers/tracers.go b/eth/tracers/tracers.go index 3d2d1256c0914..c84dc758556ba 100644 --- a/eth/tracers/tracers.go +++ b/eth/tracers/tracers.go @@ -42,6 +42,12 @@ type Tracer interface { Stop(err error) } +// JSTracer is implemented by tracers evaluating JS code. +type JSTracer interface { + Tracer + IsJS() bool +} + type lookupFunc func(string, *Context, json.RawMessage) (Tracer, error) var (