From 2bafd6783caa9cf4a709d20e27e7083ed636227b Mon Sep 17 00:00:00 2001 From: ylsGit Date: Tue, 14 Nov 2023 14:55:10 +0800 Subject: [PATCH] support flatCallTracer and result limit for trace transaction (#40) * support flatCallTracer * trace result limit --- jsonrpc/endpoints_debug.go | 9 ++++++++- .../tracers/native/call_flat.go | 20 ++++++++++++++++--- state/transaction.go | 7 +++++++ state/types.go | 6 ++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/jsonrpc/endpoints_debug.go b/jsonrpc/endpoints_debug.go index b61c397d9a..add5274420 100644 --- a/jsonrpc/endpoints_debug.go +++ b/jsonrpc/endpoints_debug.go @@ -28,6 +28,7 @@ var defaultTraceConfig = &traceConfig{ EnableMemory: false, EnableReturnData: false, Tracer: nil, + Limit: 0, } // DebugEndpoints is the debug jsonrpc endpoint @@ -54,6 +55,7 @@ type traceConfig struct { EnableReturnData bool `json:"enableReturnData"` Tracer *string `json:"tracer"` TracerConfig json.RawMessage `json:"tracerConfig"` + Limit int `json:"limit"` } // StructLogRes represents the debug trace information for each opcode @@ -313,6 +315,7 @@ func (d *DebugEndpoints) buildTraceTransaction(ctx context.Context, hash common. EnableReturnData: traceCfg.EnableReturnData, Tracer: traceCfg.Tracer, TracerConfig: traceCfg.TracerConfig, + Limit: traceCfg.Limit, } result, err := d.state.DebugTransaction(ctx, hash, stateTraceConfig, dbTx) if errors.Is(err, state.ErrNotFound) { @@ -431,6 +434,10 @@ func (d *DebugEndpoints) buildStructLogs(stateStructLogs []instrumentation.Struc structLogs = append(structLogs, structLogRes) } + + if cfg.Limit > 0 && len(structLogs) > cfg.Limit { + structLogs = structLogs[:cfg.Limit] + } return structLogs } @@ -439,7 +446,7 @@ func (d *DebugEndpoints) buildStructLogs(stateStructLogs []instrumentation.Struc func isBuiltInTracer(tracer string) bool { // built-in tracers switch tracer { - case "callTracer", "4byteTracer", "prestateTracer", "noopTracer": + case "callTracer", "flatCallTracer", "4byteTracer", "prestateTracer", "noopTracer": return true default: return false diff --git a/state/runtime/instrumentation/tracers/native/call_flat.go b/state/runtime/instrumentation/tracers/native/call_flat.go index c91077f9de..667b094a02 100644 --- a/state/runtime/instrumentation/tracers/native/call_flat.go +++ b/state/runtime/instrumentation/tracers/native/call_flat.go @@ -33,7 +33,7 @@ import ( //go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go func init() { - tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false) + tracers.DefaultDirectory.Register("flatCallTracer", NewFlatCallTracer, false) } var parityErrorMapping = map[string]string{ @@ -113,6 +113,7 @@ type flatCallTracer struct { ctx *tracers.Context // Holds tracer context data reason error // Textual reason for the interruption activePrecompiles []common.Address // Updated on CaptureStart based on given rules + limit int } type flatCallTracerConfig struct { @@ -120,8 +121,8 @@ type flatCallTracerConfig struct { IncludePrecompiles bool `json:"includePrecompiles"` // If true, call tracer includes calls to precompiled contracts } -// newFlatCallTracer returns a new flatCallTracer. -func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { +// NewFlatCallTracer returns a new flatCallTracer. +func NewFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) { var config flatCallTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { @@ -141,6 +142,15 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil } +// SetFlatCallTracerLimit set the limit for flatCallFrame. +func SetFlatCallTracerLimit(t tracers.Tracer, l int) tracers.Tracer { + if flatTracer, ok := t.(*flatCallTracer); ok { + flatTracer.limit = l + return flatTracer + } + return t +} + // CaptureStart implements the EVMLogger interface to initialize the tracing operation. func (t *flatCallTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { t.tracer.CaptureStart(env, from, to, create, input, gas, value) @@ -218,6 +228,10 @@ func (t *flatCallTracer) GetResult() (json.RawMessage, error) { return nil, err } + if t.limit > 0 && len(flat) > t.limit { + flat = flat[:t.limit] + } + res, err := json.Marshal(flat) if err != nil { return nil, err diff --git a/state/transaction.go b/state/transaction.go index 1528888713..50d9c0a82d 100644 --- a/state/transaction.go +++ b/state/transaction.go @@ -403,6 +403,13 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has log.Errorf("debug transaction: failed to create callTracer, err: %v", err) return nil, fmt.Errorf("failed to create callTracer, err: %v", err) } + } else if traceConfig.IsFlatCallTracer() { + customTracer, err = native.NewFlatCallTracer(tracerContext, traceConfig.TracerConfig) + if err != nil { + log.Errorf("debug transaction: failed to create flatCallTracer, err: %v", err) + return nil, fmt.Errorf("failed to create flatCallTracer, err: %v", err) + } + customTracer = native.SetFlatCallTracerLimit(customTracer, traceConfig.Limit) } else if traceConfig.IsNoopTracer() { customTracer, err = native.NewNoopTracer(tracerContext, traceConfig.TracerConfig) if err != nil { diff --git a/state/types.go b/state/types.go index d3a7759e56..0eb2b83a75 100644 --- a/state/types.go +++ b/state/types.go @@ -183,6 +183,7 @@ type TraceConfig struct { EnableReturnData bool Tracer *string TracerConfig json.RawMessage + Limit int } // IsDefaultTracer returns true when no custom tracer is set @@ -200,6 +201,11 @@ func (t *TraceConfig) IsCallTracer() bool { return t.Tracer != nil && *t.Tracer == "callTracer" } +// IsFlatCallTracer returns true when should use flatCallTracer +func (t *TraceConfig) IsFlatCallTracer() bool { + return t.Tracer != nil && *t.Tracer == "flatCallTracer" +} + // IsNoopTracer returns true when should use noopTracer func (t *TraceConfig) IsNoopTracer() bool { return t.Tracer != nil && *t.Tracer == "noopTracer"