diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index da2814855a..5c4eafcf4d 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2192,7 +2192,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh if ctx.IsSet(VMTraceJsonConfigFlag.Name) { config = json.RawMessage(ctx.String(VMTraceJsonConfigFlag.Name)) } - t, err := tracers.LiveDirectory.New(name, config) + t, _, err := tracers.LiveDirectory.New(name, config) if err != nil { Fatalf("Failed to create tracer %q: %v", name, err) } diff --git a/core/tracing/hooks.go b/core/tracing/hooks.go index 882eb52837..abab70864c 100644 --- a/core/tracing/hooks.go +++ b/core/tracing/hooks.go @@ -17,11 +17,13 @@ package tracing import ( + "context" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" ) @@ -68,6 +70,16 @@ type BlockEvent struct { Safe *types.Header } +// Backend interface provides the common API services (that are provided by +// both full and light clients) with access to necessary functions. +type Backend interface { + HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) + HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) + BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) + BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) + GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) +} + type ( /* - VM events - @@ -171,6 +183,7 @@ type ( ) type Hooks struct { + Backend Backend // VM events OnTxStart TxStartHook OnTxEnd TxEndHook @@ -196,6 +209,11 @@ type Hooks struct { OnLog LogHook } +// SetBackend sets a backend to the hooks, use this backend to read chain data. +func (h *Hooks) SetBackend(backend Backend) { + h.Backend = backend +} + // BalanceChangeReason is used to indicate the reason for a balance change, useful // for tracing and reporting. type BalanceChangeReason byte diff --git a/eth/backend.go b/eth/backend.go index f10d99c3a7..2e0cb48ad8 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,6 +32,7 @@ import ( "github.com/ethereum/go-ethereum/core/bloombits" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state/pruner" + "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/txpool/blobpool" "github.com/ethereum/go-ethereum/core/txpool/legacypool" @@ -195,17 +196,20 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { StateHistory: config.StateHistory, StateScheme: scheme, } + liveTracer *tracing.Hooks + liveAPIs []rpc.API ) if config.VMTrace != "" { var traceConfig json.RawMessage if config.VMTraceJsonConfig != "" { traceConfig = json.RawMessage(config.VMTraceJsonConfig) } - t, err := tracers.LiveDirectory.New(config.VMTrace, traceConfig) + var err error + liveTracer, liveAPIs, err = tracers.LiveDirectory.New(config.VMTrace, traceConfig) if err != nil { return nil, fmt.Errorf("failed to create tracer %s: %v", config.VMTrace, err) } - vmConfig.Tracer = t + vmConfig.Tracer = liveTracer } // Override the chain config with provided settings. var overrides core.ChainOverrides @@ -268,6 +272,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { stack.RegisterProtocols(eth.Protocols()) stack.RegisterLifecycle(eth) + // Set live tracer's backend and register the live tracer APIs + if liveTracer != nil { + liveTracer.SetBackend(eth.APIBackend) + if liveAPIs != nil { + stack.RegisterAPIs(liveAPIs) + } + } + // Successful startup; push a marker and check previous unclean shutdowns. eth.shutdownTracker.MarkStartup() diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 5c11b5e472..86ea3357ee 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -552,7 +552,7 @@ func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockG traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") // Load supply tracer - tracer, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) + tracer, _, err := tracers.LiveDirectory.New("supply", json.RawMessage(fmt.Sprintf(`{"path":"%s"}`, traceOutputPath))) if err != nil { return nil, nil, fmt.Errorf("failed to create call tracer: %v", err) } diff --git a/eth/tracers/live.go b/eth/tracers/live.go index ffb2303af4..91a42f42fb 100644 --- a/eth/tracers/live.go +++ b/eth/tracers/live.go @@ -5,9 +5,10 @@ import ( "errors" "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/rpc" ) -type ctorFunc func(config json.RawMessage) (*tracing.Hooks, error) +type ctorFunc func(config json.RawMessage) (*tracing.Hooks, []rpc.API, error) // LiveDirectory is the collection of tracers which can be used // during normal block import operations. @@ -23,9 +24,9 @@ func (d *liveDirectory) Register(name string, f ctorFunc) { } // New instantiates a tracer by name. -func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, error) { +func (d *liveDirectory) New(name string, config json.RawMessage) (*tracing.Hooks, []rpc.API, error) { if f, ok := d.elems[name]; ok { return f(config) } - return nil, errors.New("not found") + return nil, nil, errors.New("not found") } diff --git a/eth/tracers/live/noop.go b/eth/tracers/live/noop.go index 7433c28840..6a5b29fcc4 100644 --- a/eth/tracers/live/noop.go +++ b/eth/tracers/live/noop.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" ) func init() { @@ -21,7 +22,7 @@ func init() { // as soon as we have a real live tracer. type noop struct{} -func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { +func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, []rpc.API, error) { t := &noop{} return &tracing.Hooks{ OnTxStart: t.OnTxStart, @@ -41,7 +42,7 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) { OnCodeChange: t.OnCodeChange, OnStorageChange: t.OnStorageChange, OnLog: t.OnLog, - }, nil + }, nil, nil } func (t *noop) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { diff --git a/eth/tracers/live/supply.go b/eth/tracers/live/supply.go index 96f7059454..b99c0271d8 100644 --- a/eth/tracers/live/supply.go +++ b/eth/tracers/live/supply.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" "gopkg.in/natefinch/lumberjack.v2" ) @@ -74,15 +75,15 @@ type supplyTracerConfig struct { MaxSize int `json:"maxSize"` // MaxSize is the maximum size in megabytes of the tracer log file before it gets rotated. It defaults to 100 megabytes. } -func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { +func newSupply(cfg json.RawMessage) (*tracing.Hooks, []rpc.API, error) { var config supplyTracerConfig if cfg != nil { if err := json.Unmarshal(cfg, &config); err != nil { - return nil, fmt.Errorf("failed to parse config: %v", err) + return nil, nil, fmt.Errorf("failed to parse config: %v", err) } } if config.Path == "" { - return nil, errors.New("supply tracer output path is required") + return nil, nil, errors.New("supply tracer output path is required") } // Store traces in a rotating file @@ -106,7 +107,7 @@ func newSupply(cfg json.RawMessage) (*tracing.Hooks, error) { OnEnter: t.OnEnter, OnExit: t.OnExit, OnClose: t.OnClose, - }, nil + }, nil, nil } func newSupplyInfo() supplyInfo {