Skip to content

Commit 0061de6

Browse files
gzliudanholimans1na
authored
eth/tracers: use non-threaded tracechain ethereum#24283 (#1315)
This makes non-JS tracers execute all block txs on a single goroutine. In the previous implementation, we used to prepare every tx pre-state on one goroutine, and then run the transactions again with tracing enabled. Native tracers are usually faster, so it is faster overall to use their output as the pre-state for tracing the next transaction. Co-authored-by: Martin Holst Swende <[email protected]> Co-authored-by: Sina Mahmoodi <[email protected]>
1 parent afc2028 commit 0061de6

File tree

12 files changed

+130
-134
lines changed

12 files changed

+130
-134
lines changed

eth/tracers/api.go

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
574574
if block.NumberU64() == 0 {
575575
return nil, errors.New("genesis is not traceable")
576576
}
577+
// Prepare base state
577578
parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
578579
if err != nil {
579580
return nil, err
@@ -588,21 +589,72 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
588589
}
589590
defer release()
590591

592+
// JS tracers have high overhead. In this case run a parallel
593+
// process that generates states in one thread and traces txes
594+
// in separate worker threads.
595+
if config != nil && config.Tracer != nil && *config.Tracer != "" {
596+
if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
597+
return api.traceBlockParallel(ctx, block, statedb, config)
598+
}
599+
}
600+
// Native tracers have low overhead
601+
var (
602+
txs = block.Transactions()
603+
blockHash = block.Hash()
604+
is158 = api.backend.ChainConfig().IsEIP158(block.Number())
605+
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
606+
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
607+
results = make([]*txTraceResult, len(txs))
608+
)
609+
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
610+
for i, tx := range txs {
611+
var balance *big.Int
612+
if tx.To() != nil {
613+
if tx.IsSkipNonceTransaction() {
614+
continue
615+
}
616+
if value, ok := feeCapacity[*tx.To()]; ok {
617+
balance = value
618+
}
619+
}
620+
// Generate the next state snapshot fast without tracing
621+
msg, _ := tx.AsMessage(signer, balance, block.Number(), block.BaseFee())
622+
txctx := &Context{
623+
BlockHash: blockHash,
624+
TxIndex: i,
625+
TxHash: tx.Hash(),
626+
}
627+
res, err := api.traceTx(ctx, msg, txctx, blockCtx, statedb, config)
628+
if err != nil {
629+
return nil, err
630+
}
631+
results[i] = &txTraceResult{Result: res}
632+
// Finalize the state so any modifications are written to the trie
633+
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
634+
statedb.Finalise(is158)
635+
}
636+
return results, nil
637+
}
638+
639+
// traceBlockParallel is for tracers that have a high overhead (read JS tracers). One thread
640+
// runs along and executes txes without tracing enabled to generate their prestate.
641+
// Worker threads take the tasks and the prestate and trace them.
642+
func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, statedb *state.StateDB, config *TraceConfig) ([]*txTraceResult, error) {
591643
// Execute all the transaction contained within the block concurrently
592644
var (
593-
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
594-
txs = block.Transactions()
595-
results = make([]*txTraceResult, len(txs))
596-
pend sync.WaitGroup
645+
txs = block.Transactions()
646+
blockHash = block.Hash()
647+
blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
648+
signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
649+
results = make([]*txTraceResult, len(txs))
650+
pend sync.WaitGroup
597651
)
598652
threads := runtime.NumCPU()
599653
if threads > len(txs) {
600654
threads = len(txs)
601655
}
602656
jobs := make(chan *txTraceTask, threads)
603-
blockHash := block.Hash()
604657
for th := 0; th < threads; th++ {
605-
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
606658
pend.Add(1)
607659
go func() {
608660
defer pend.Done()
@@ -635,7 +687,6 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
635687
// Feed the transactions into the tracers and return
636688
feeCapacity := state.GetTRC21FeeCapacityFromState(statedb)
637689
var failed error
638-
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
639690
txloop:
640691
for i, tx := range txs {
641692
// Send the trace task over for execution
@@ -795,7 +846,7 @@ func (api *API) traceTx(ctx context.Context, message core.Message, txctx *Contex
795846
// Default tracer is the struct logger
796847
tracer = logger.NewStructLogger(config.Config)
797848
if config.Tracer != nil {
798-
tracer, err = New(*config.Tracer, txctx, config.TracerConfig)
849+
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
799850
if err != nil {
800851
return nil, err
801852
}

eth/tracers/internal/tracetest/calltrace_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
140140
}
141141
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
142142
)
143-
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
143+
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
144144
if err != nil {
145145
t.Fatalf("failed to create call tracer: %v", err)
146146
}
@@ -242,7 +242,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
242242
b.ReportAllocs()
243243
b.ResetTimer()
244244
for i := 0; i < b.N; i++ {
245-
tracer, err := tracers.New(tracerName, new(tracers.Context), nil)
245+
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
246246
if err != nil {
247247
b.Fatalf("failed to create call tracer: %v", err)
248248
}
@@ -308,7 +308,7 @@ func TestZeroValueToNotExitCall(t *testing.T) {
308308
}
309309
statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc)
310310
// Create the tracer, the EVM environment and run it
311-
tracer, err := tracers.New("callTracer", nil, nil)
311+
tracer, err := tracers.DefaultDirectory.New("callTracer", nil, nil)
312312
if err != nil {
313313
t.Fatalf("failed to create call tracer: %v", err)
314314
}
@@ -384,7 +384,7 @@ func testContractTracer(tracerName string, dirPath string, t *testing.T) {
384384
}
385385
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
386386
)
387-
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
387+
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
388388
if err != nil {
389389
t.Fatalf("failed to create call tracer: %v", err)
390390
}

eth/tracers/internal/tracetest/prestate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
110110
}
111111
statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc)
112112
)
113-
tracer, err := tracers.New(tracerName, new(tracers.Context), test.TracerConfig)
113+
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
114114
if err != nil {
115115
t.Fatalf("failed to create call tracer: %v", err)
116116
}

eth/tracers/js/goja.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,16 @@ func init() {
4444
if err != nil {
4545
panic(err)
4646
}
47-
tracers.RegisterLookup(true, newJsTracer)
47+
type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
48+
lookup := func(code string) ctorFn {
49+
return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
50+
return newJsTracer(code, ctx, cfg)
51+
}
52+
}
53+
for name, code := range assetTracers {
54+
tracers.DefaultDirectory.Register(name, lookup(code), true)
55+
}
56+
tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
4857
}
4958

5059
// bigIntProgram is compiled once and the exported function mostly invoked to convert
@@ -121,16 +130,14 @@ type jsTracer struct {
121130
frameResultValue goja.Value
122131
}
123132

124-
// newJsTracer instantiates a new JS tracer instance. code is either
125-
// the name of a built-in JS tracer or a Javascript snippet which
126-
// evaluates to an expression returning an object with certain methods.
133+
// newJsTracer instantiates a new JS tracer instance. code is a
134+
// Javascript snippet which evaluates to an expression returning
135+
// an object with certain methods:
136+
//
127137
// The methods `result` and `fault` are required to be present.
128138
// The methods `step`, `enter`, and `exit` are optional, but note that
129139
// `enter` and `exit` always go together.
130140
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
131-
if c, ok := assetTracers[code]; ok {
132-
code = c
133-
}
134141
vm := goja.New()
135142
// By default field names are exported to JS as is, i.e. capitalized.
136143
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())

eth/tracers/native/4byte.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
)
2929

3030
func init() {
31-
register("4byteTracer", newFourByteTracer)
31+
tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
3232
}
3333

3434
// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.

eth/tracers/native/call.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
3333

3434
func init() {
35-
register("callTracer", newCallTracer)
35+
tracers.DefaultDirectory.Register("callTracer", newCallTracer, false)
3636
}
3737

3838
type callLog struct {

eth/tracers/native/contract.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313
)
1414

1515
func init() {
16-
register("contractTracer", NewContractTracer)
16+
tracers.DefaultDirectory.Register("contractTracer", NewContractTracer, false)
1717
}
1818

1919
type contractTracer struct {

eth/tracers/native/mux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
func init() {
29-
register("muxTracer", newMuxTracer)
29+
tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
3030
}
3131

3232
// muxTracer is a go implementation of the Tracer interface which
@@ -47,7 +47,7 @@ func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, er
4747
objects := make([]tracers.Tracer, 0, len(config))
4848
names := make([]string, 0, len(config))
4949
for k, v := range config {
50-
t, err := tracers.New(k, ctx, v)
50+
t, err := tracers.DefaultDirectory.New(k, ctx, v)
5151
if err != nil {
5252
return nil, err
5353
}

eth/tracers/native/noop.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
func init() {
29-
register("noopTracer", newNoopTracer)
29+
tracers.DefaultDirectory.Register("noopTracer", newNoopTracer, false)
3030
}
3131

3232
type noopTracer struct{}

eth/tracers/native/prestate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
3333

3434
func init() {
35-
register("prestateTracer", newPrestateTracer)
35+
tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
3636
}
3737

3838
type state = map[common.Address]*account

0 commit comments

Comments
 (0)