Skip to content

Commit

Permalink
eth/tracers: execute block serially when tracer is native
Browse files Browse the repository at this point in the history
  • Loading branch information
s1na committed Dec 15, 2022
1 parent f53ff0f commit 26799d9
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 7 deletions.
58 changes: 51 additions & 7 deletions eth/tracers/api.go
Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions eth/tracers/js/goja.go
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions eth/tracers/tracers.go
Expand Up @@ -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 (
Expand Down

0 comments on commit 26799d9

Please sign in to comment.