diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index c05e76e1a5..6a2d788a4a 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -54,6 +54,7 @@ import (
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/remotedb"
"github.com/ethereum/go-ethereum/ethstats"
@@ -518,9 +519,9 @@ var (
Usage: "Record information useful for VM and contract debugging",
Category: flags.VMCategory,
}
- VMTraceFlag = &cli.BoolFlag{
+ VMTraceFlag = &cli.StringFlag{
Name: "vmtrace",
- Usage: "Record internal VM operations (costly)",
+ Usage: "Name of tracer which should record internal VM operations (costly)",
Category: flags.VMCategory,
}
@@ -1723,9 +1724,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// TODO(fjl): force-enable this in --dev mode
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
}
- if ctx.IsSet(VMTraceFlag.Name) {
- cfg.LiveTrace = ctx.Bool(VMTraceFlag.Name)
- }
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
@@ -2143,7 +2141,13 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
if ctx.IsSet(VMTraceFlag.Name) {
- vmcfg.Tracer = tracers.NewPrinter()
+ if name := ctx.String(VMTraceFlag.Name); name != "" {
+ t, err := directory.LiveDirectory.New(name)
+ if err != nil {
+ Fatalf("Failed to create tracer %q: %v", name, err)
+ }
+ vmcfg.Tracer = t
+ }
}
// Disable transaction indexing/unindexing by default.
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
diff --git a/core/blockchain.go b/core/blockchain.go
index 3ee20a9aa3..75c4e3f9bf 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -406,6 +406,21 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
}
}
+ if bc.logger != nil {
+ if block := bc.CurrentBlock(); block.Number.Uint64() == 0 {
+ alloc, err := getGenesisState(bc.db, block.Hash())
+ if err != nil {
+ return nil, fmt.Errorf("failed to get genesis state: %w", err)
+ }
+
+ if alloc == nil {
+ return nil, fmt.Errorf("live blockchain tracer requires genesis alloc to be set")
+ }
+
+ bc.logger.OnGenesisBlock(bc.genesisBlock, alloc)
+ }
+ }
+
// Load any existing snapshot, regenerating it if loading failed
if bc.cacheConfig.SnapshotLimit > 0 {
// If the chain was rewound past the snapshot persistent layer (causing
@@ -1759,120 +1774,117 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// Process block using the parent state as reference point
pstart := time.Now()
- if bc.logger != nil {
- td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
- bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
- }
- receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
- if err != nil {
- bc.reportBlock(block, receipts, err)
+ // The traced section of block import.
+ err, stop := func() (blockEndErr error, _ bool) {
+ if bc.logger != nil {
+ td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
+ bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
+ defer func() {
+ bc.logger.OnBlockEnd(blockEndErr)
+ }()
+ }
+ receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
+ if err != nil {
+ bc.reportBlock(block, receipts, err)
+ followupInterrupt.Store(true)
+ return err, true
+ }
+ ptime := time.Since(pstart)
+
+ vstart := time.Now()
+ if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
+ bc.reportBlock(block, receipts, err)
+ followupInterrupt.Store(true)
+ return err, true
+ }
+ vtime := time.Since(vstart)
+ proctime := time.Since(start) // processing + validation
+
+ // Update the metrics touched during block processing and validation
+ accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
+ storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
+ snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
+ snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
+ accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
+ storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
+ accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
+ storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
+ triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
+ trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
+ trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
+ trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
+ blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
+ blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
+
+ // Write the block to the chain and get the status.
+ var (
+ wstart = time.Now()
+ status WriteStatus
+ )
+ if !setHead {
+ // Don't set the head, only insert the block
+ err = bc.writeBlockWithState(block, receipts, statedb)
+ } else {
+ status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
+ }
followupInterrupt.Store(true)
- if bc.logger != nil {
- bc.logger.OnBlockEnd(err)
+ if err != nil {
+ return err, true
}
- return it.index, err
- }
- ptime := time.Since(pstart)
+ // Update the metrics touched during block commit
+ accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
+ storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
+ snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
+ triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
- vstart := time.Now()
- if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
- bc.reportBlock(block, receipts, err)
- followupInterrupt.Store(true)
- if bc.logger != nil {
- bc.logger.OnBlockEnd(err)
+ blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
+ blockInsertTimer.UpdateSince(start)
+
+ // Report the import stats before returning the various results
+ stats.processed++
+ stats.usedGas += usedGas
+
+ dirty, _ := bc.triedb.Size()
+ stats.report(chain, it.index, dirty, setHead)
+
+ if !setHead {
+ // After merge we expect few side chains. Simply count
+ // all blocks the CL gives us for GC processing time
+ bc.gcproc += proctime
+
+ return nil, true // Direct block insertion of a single block
}
- return it.index, err
- }
- vtime := time.Since(vstart)
- proctime := time.Since(start) // processing + validation
+ switch status {
+ case CanonStatTy:
+ log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
+ "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
+ "elapsed", common.PrettyDuration(time.Since(start)),
+ "root", block.Root())
- // Update the metrics touched during block processing and validation
- accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
- storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
- snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
- snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
- accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
- storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
- accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
- storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
- triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
- trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
- trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
- trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
- blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
- blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
+ lastCanon = block
- // Write the block to the chain and get the status.
- var (
- wstart = time.Now()
- status WriteStatus
- )
- if !setHead {
- // Don't set the head, only insert the block
- err = bc.writeBlockWithState(block, receipts, statedb)
- } else {
- status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
- }
- followupInterrupt.Store(true)
- if err != nil {
- if bc.logger != nil {
- bc.logger.OnBlockEnd(err)
+ // Only count canonical blocks for GC processing time
+ bc.gcproc += proctime
+
+ case SideStatTy:
+ log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
+ "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
+ "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
+ "root", block.Root())
+
+ default:
+ // This in theory is impossible, but lets be nice to our future selves and leave
+ // a log, instead of trying to track down blocks imports that don't emit logs.
+ log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
+ "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
+ "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
+ "root", block.Root())
}
+ return nil, false
+ }()
+ if err != nil || stop {
return it.index, err
}
- // Update the metrics touched during block commit
- accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
- storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
- snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
- triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
-
- blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
- blockInsertTimer.UpdateSince(start)
-
- // Report the import stats before returning the various results
- stats.processed++
- stats.usedGas += usedGas
-
- dirty, _ := bc.triedb.Size()
- stats.report(chain, it.index, dirty, setHead)
-
- if bc.logger != nil {
- bc.logger.OnBlockEnd(nil)
- }
-
- if !setHead {
- // After merge we expect few side chains. Simply count
- // all blocks the CL gives us for GC processing time
- bc.gcproc += proctime
-
- return it.index, nil // Direct block insertion of a single block
- }
- switch status {
- case CanonStatTy:
- log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
- "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
- "elapsed", common.PrettyDuration(time.Since(start)),
- "root", block.Root())
-
- lastCanon = block
-
- // Only count canonical blocks for GC processing time
- bc.gcproc += proctime
-
- case SideStatTy:
- log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
- "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
- "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
- "root", block.Root())
-
- default:
- // This in theory is impossible, but lets be nice to our future selves and leave
- // a log, instead of trying to track down blocks imports that don't emit logs.
- log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
- "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
- "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
- "root", block.Root())
- }
}
// Any blocks remaining here? The only ones we care about are the future ones
diff --git a/core/genesis.go b/core/genesis.go
index e274f5a79b..54ec15cba4 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -176,36 +176,49 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
return nil
}
-// CommitGenesisState loads the stored genesis state with the given block
-// hash and commits it into the provided trie database.
-func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
- var alloc GenesisAlloc
+func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc GenesisAlloc, err error) {
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
if len(blob) != 0 {
if err := alloc.UnmarshalJSON(blob); err != nil {
- return err
- }
- } else {
- // Genesis allocation is missing and there are several possibilities:
- // the node is legacy which doesn't persist the genesis allocation or
- // the persisted allocation is just lost.
- // - supported networks(mainnet, testnets), recover with defined allocations
- // - private network, can't recover
- var genesis *Genesis
- switch blockhash {
- case params.MainnetGenesisHash:
- genesis = DefaultGenesisBlock()
- case params.GoerliGenesisHash:
- genesis = DefaultGoerliGenesisBlock()
- case params.SepoliaGenesisHash:
- genesis = DefaultSepoliaGenesisBlock()
- }
- if genesis != nil {
- alloc = genesis.Alloc
- } else {
- return errors.New("not found")
+ return nil, err
}
+
+ return alloc, nil
}
+
+ // Genesis allocation is missing and there are several possibilities:
+ // the node is legacy which doesn't persist the genesis allocation or
+ // the persisted allocation is just lost.
+ // - supported networks(mainnet, testnets), recover with defined allocations
+ // - private network, can't recover
+ var genesis *Genesis
+ switch blockhash {
+ case params.MainnetGenesisHash:
+ genesis = DefaultGenesisBlock()
+ case params.GoerliGenesisHash:
+ genesis = DefaultGoerliGenesisBlock()
+ case params.SepoliaGenesisHash:
+ genesis = DefaultSepoliaGenesisBlock()
+ }
+ if genesis != nil {
+ return genesis.Alloc, nil
+ }
+
+ return nil, nil
+}
+
+// CommitGenesisState loads the stored genesis state with the given block
+// hash and commits it into the provided trie database.
+func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
+ alloc, err := getGenesisState(db, blockhash)
+ if err != nil {
+ return err
+ }
+
+ if alloc == nil {
+ return errors.New("not found")
+ }
+
return alloc.flush(db, triedb, blockhash, nil)
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 3f52b9f736..b3ee524fc1 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -56,6 +56,8 @@ func (n *proofList) Delete(key []byte) error {
// StateLogger is used to collect state update traces from EVM transaction
// execution.
+// The following hooks are invoked post execution. I.e. looking up state
+// after the hook should reflect the new value.
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type StateLogger interface {
diff --git a/eth/backend.go b/eth/backend.go
index 6cb5611d32..667200bced 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -44,7 +44,6 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
- "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
@@ -194,9 +193,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Preimages: config.Preimages,
}
)
- if config.LiveTrace {
- vmConfig.Tracer = tracers.NewPrinter()
- }
// Override the chain config with provided settings.
var overrides core.ChainOverrides
if config.OverrideCancun != nil {
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 55dd56316e..4bc8b8dc6c 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -140,9 +140,6 @@ type Config struct {
// Enables tracking of SHA3 preimages in the VM
EnablePreimageRecording bool
- // LiveTrace will enable tracing during normal chain processing.
- LiveTrace bool
-
// Miscellaneous options
DocRoot string `toml:"-"`
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 1d3c57dc7c..69fe38ba20 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
@@ -272,7 +273,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
- txctx := &Context{
+ txctx := &directory.Context{
BlockHash: task.block.Hash(),
BlockNumber: task.block.Number(),
TxIndex: i,
@@ -591,7 +592,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// process that generates states in one thread and traces txes
// in separate worker threads.
if config != nil && config.Tracer != nil && *config.Tracer != "" {
- if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
+ if isJS := directory.DefaultDirectory.IsJS(*config.Tracer); isJS {
return api.traceBlockParallel(ctx, block, statedb, config)
}
}
@@ -607,7 +608,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
for i, tx := range txs {
// Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
- txctx := &Context{
+ txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: i,
@@ -650,7 +651,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// Fetch and execute the next transaction trace tasks
for task := range jobs {
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
- txctx := &Context{
+ txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: task.index,
@@ -859,7 +860,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
return nil, err
}
- txctx := &Context{
+ txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: int(index),
@@ -927,15 +928,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil {
traceConfig = &config.TraceConfig
}
- return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig)
+ return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
-func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
+func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
var (
- tracer Tracer
+ tracer directory.Tracer
err error
timeout = defaultTraceTimeout
txContext = core.NewEVMTxContext(message)
@@ -946,7 +947,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
// Default tracer is the struct logger
tracer = logger.NewStructLogger(config.Config)
if config.Tracer != nil {
- tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
+ tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
if err != nil {
return nil, err
}
diff --git a/eth/tracers/directory/live.go b/eth/tracers/directory/live.go
new file mode 100644
index 0000000000..dc6927b142
--- /dev/null
+++ b/eth/tracers/directory/live.go
@@ -0,0 +1,30 @@
+package directory
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/core"
+)
+
+type ctorFunc func() (core.BlockchainLogger, error)
+
+// LiveDirectory is the collection of tracers which can be used
+// during normal block import operations.
+var LiveDirectory = liveDirectory{elems: make(map[string]ctorFunc)}
+
+type liveDirectory struct {
+ elems map[string]ctorFunc
+}
+
+// Register registers a tracer constructor by name.
+func (d *liveDirectory) Register(name string, f ctorFunc) {
+ d.elems[name] = f
+}
+
+// New instantiates a tracer by name.
+func (d *liveDirectory) New(name string) (core.BlockchainLogger, error) {
+ if f, ok := d.elems[name]; ok {
+ return f()
+ }
+ return nil, errors.New("not found")
+}
diff --git a/eth/tracers/noop.go b/eth/tracers/directory/noop.go
similarity index 99%
rename from eth/tracers/noop.go
rename to eth/tracers/directory/noop.go
index 428c13fef4..973b1abb47 100644
--- a/eth/tracers/noop.go
+++ b/eth/tracers/directory/noop.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package tracers
+package directory
import (
"encoding/json"
diff --git a/eth/tracers/tracers.go b/eth/tracers/directory/tracers.go
similarity index 80%
rename from eth/tracers/tracers.go
rename to eth/tracers/directory/tracers.go
index c82fcf92ac..b16d9d2fe6 100644
--- a/eth/tracers/tracers.go
+++ b/eth/tracers/directory/tracers.go
@@ -14,13 +14,13 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// Package tracers is a manager for transaction tracing engines.
-package tracers
+// Package directory provides functionality to lookup tracers by name.
+// It also includes utility functions that are imported by the other
+// tracing packages.
+package directory
import (
"encoding/json"
- "errors"
- "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
@@ -101,27 +101,3 @@ func (d *directory) IsJS(name string) bool {
// JS eval will execute JS code
return true
}
-
-const (
- memoryPadLimit = 1024 * 1024
-)
-
-// GetMemoryCopyPadded returns offset + size as a new slice.
-// It zero-pads the slice if it extends beyond memory bounds.
-func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) {
- if offset < 0 || size < 0 {
- return nil, errors.New("offset or size must not be negative")
- }
- if int(offset+size) < m.Len() { // slice fully inside memory
- return m.GetCopy(offset, size), nil
- }
- paddingNeeded := int(offset+size) - m.Len()
- if paddingNeeded > memoryPadLimit {
- return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
- }
- cpy := make([]byte, size)
- if overlap := int64(m.Len()) - offset; overlap > 0 {
- copy(cpy, m.GetPtr(offset, overlap))
- }
- return cpy, nil
-}
diff --git a/eth/tracers/directory/util.go b/eth/tracers/directory/util.go
new file mode 100644
index 0000000000..92de735291
--- /dev/null
+++ b/eth/tracers/directory/util.go
@@ -0,0 +1,47 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+package directory
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+const (
+ memoryPadLimit = 1024 * 1024
+)
+
+// GetMemoryCopyPadded returns offset + size as a new slice.
+// It zero-pads the slice if it extends beyond memory bounds.
+func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) {
+ if offset < 0 || size < 0 {
+ return nil, errors.New("offset or size must not be negative")
+ }
+ if int(offset+size) < m.Len() { // slice fully inside memory
+ return m.GetCopy(offset, size), nil
+ }
+ paddingNeeded := int(offset+size) - m.Len()
+ if paddingNeeded > memoryPadLimit {
+ return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
+ }
+ cpy := make([]byte, size)
+ if overlap := int64(m.Len()) - offset; overlap > 0 {
+ copy(cpy, m.GetPtr(offset, overlap))
+ }
+ return cpy, nil
+}
diff --git a/eth/tracers/directory/util_test.go b/eth/tracers/directory/util_test.go
new file mode 100644
index 0000000000..c72096708d
--- /dev/null
+++ b/eth/tracers/directory/util_test.go
@@ -0,0 +1,60 @@
+// Copyright 2023 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+package directory
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+func TestMemCopying(t *testing.T) {
+ for i, tc := range []struct {
+ memsize int64
+ offset int64
+ size int64
+ wantErr string
+ wantSize int
+ }{
+ {0, 0, 100, "", 100}, // Should pad up to 100
+ {0, 100, 0, "", 0}, // No need to pad (0 size)
+ {100, 50, 100, "", 100}, // Should pad 100-150
+ {100, 50, 5, "", 5}, // Wanted range fully within memory
+ {100, -50, 0, "offset or size must not be negative", 0}, // Errror
+ {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
+ {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
+
+ } {
+ mem := vm.NewMemory()
+ mem.Resize(uint64(tc.memsize))
+ cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size)
+ if want := tc.wantErr; want != "" {
+ if err == nil {
+ t.Fatalf("test %d: want '%v' have no error", i, want)
+ }
+ if have := err.Error(); want != have {
+ t.Fatalf("test %d: want '%v' have '%v'", i, want, have)
+ }
+ continue
+ }
+ if err != nil {
+ t.Fatalf("test %d: unexpected error: %v", i, err)
+ }
+ if want, have := tc.wantSize, len(cpy); have != want {
+ t.Fatalf("test %d: want %v have %v", i, want, have)
+ }
+ }
+}
diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go
index a0001118a8..d342f2109d 100644
--- a/eth/tracers/internal/tracetest/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
@@ -141,7 +141,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
}
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
)
- tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
+ tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -247,7 +247,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
+ tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), nil)
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
@@ -283,8 +283,8 @@ func TestInternals(t *testing.T) {
BaseFee: new(big.Int),
}
)
- mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer {
- tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
+ mkTracer := func(name string, cfg json.RawMessage) directory.Tracer {
+ tr, err := directory.DefaultDirectory.New(name, nil, cfg)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -294,7 +294,7 @@ func TestInternals(t *testing.T) {
for _, tc := range []struct {
name string
code []byte
- tracer tracers.Tracer
+ tracer directory.Tracer
want string
}{
{
diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go
index f8ca103314..be552ccfd5 100644
--- a/eth/tracers/internal/tracetest/flat_calltrace_test.go
+++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go
@@ -16,11 +16,9 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
-
- // Force-load the native, to trigger registration
- "github.com/ethereum/go-ethereum/eth/tracers"
)
// flatCallTrace is the result of a callTracerParity run.
@@ -103,7 +101,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
// Create the tracer, the EVM environment and run it
- tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
+ tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
return fmt.Errorf("failed to create call tracer: %v", err)
}
diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go
index 74d5492d52..dc2e7293b9 100644
--- a/eth/tracers/internal/tracetest/prestate_test.go
+++ b/eth/tracers/internal/tracetest/prestate_test.go
@@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/tests"
)
@@ -110,7 +110,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
}
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
)
- tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
+ tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
diff --git a/eth/tracers/js/goja.go b/eth/tracers/js/goja.go
index 8ed55ef1c6..c21b01525a 100644
--- a/eth/tracers/js/goja.go
+++ b/eth/tracers/js/goja.go
@@ -24,12 +24,12 @@ import (
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/tracers"
jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
)
@@ -42,16 +42,16 @@ func init() {
if err != nil {
panic(err)
}
- type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
+ type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error)
lookup := func(code string) ctorFn {
- return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
return newJsTracer(code, ctx, cfg)
}
}
for name, code := range assetTracers {
- tracers.DefaultDirectory.Register(name, lookup(code), true)
+ directory.DefaultDirectory.Register(name, lookup(code), true)
}
- tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
+ directory.DefaultDirectory.RegisterJSEval(newJsTracer)
}
// bigIntProgram is compiled once and the exported function mostly invoked to convert
@@ -96,7 +96,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
// jsTracer is an implementation of the Tracer interface which evaluates
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
type jsTracer struct {
- tracers.NoopTracer
+ directory.NoopTracer
vm *goja.Runtime
env *vm.EVM
@@ -136,7 +136,7 @@ type jsTracer struct {
// The methods `result` and `fault` are required to be present.
// The methods `step`, `enter`, and `exit` are optional, but note that
// `enter` and `exit` always go together.
-func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
vm := goja.New()
// By default field names are exported to JS as is, i.e. capitalized.
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
@@ -145,7 +145,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
ctx: make(map[string]goja.Value),
}
if ctx == nil {
- ctx = new(tracers.Context)
+ ctx = new(directory.Context)
}
if ctx.BlockHash != (common.Hash{}) {
t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
@@ -576,7 +576,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
if end < begin || begin < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
- slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin)
+ slice, err := directory.GetMemoryCopyPadded(mo.memory, begin, end-begin)
if err != nil {
return nil, err
}
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
index 4a7d6c5bcd..0706690c89 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -28,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
)
@@ -61,7 +61,7 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
}
-func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
+func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
var (
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
gasLimit uint64 = 31000
@@ -264,14 +264,14 @@ func TestIsPrecompile(t *testing.T) {
func TestEnterExit(t *testing.T) {
// test that either both or none of enter() and exit() are defined
- if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil {
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(directory.Context), nil); err == nil {
t.Fatal("tracer creation should've failed without exit() definition")
}
- if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil {
+ if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(directory.Context), nil); err != nil {
t.Fatal(err)
}
// test that the enter and exit method are correctly invoked and the values passed
- tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil)
+ tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(directory.Context), nil)
if err != nil {
t.Fatal(err)
}
@@ -293,7 +293,7 @@ func TestEnterExit(t *testing.T) {
func TestSetup(t *testing.T) {
// Test empty config
- _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil)
+ _, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(directory.Context), nil)
if err != nil {
t.Error(err)
}
@@ -303,12 +303,12 @@ func TestSetup(t *testing.T) {
t.Fatal(err)
}
// Test no setup func
- _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg)
+ _, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(directory.Context), cfg)
if err != nil {
t.Fatal(err)
}
// Test config value
- tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg)
+ tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(directory.Context), cfg)
if err != nil {
t.Fatal(err)
}
diff --git a/eth/tracers/printer.go b/eth/tracers/live/printer.go
similarity index 93%
rename from eth/tracers/printer.go
rename to eth/tracers/live/printer.go
index 3f82107dad..1f51a6fc3f 100644
--- a/eth/tracers/printer.go
+++ b/eth/tracers/live/printer.go
@@ -1,4 +1,4 @@
-package tracers
+package live
import (
"encoding/json"
@@ -8,14 +8,20 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
+func init() {
+ directory.LiveDirectory.Register("printer", newPrinter)
+}
+
type Printer struct{}
-func NewPrinter() *Printer {
- return &Printer{}
+func newPrinter() (core.BlockchainLogger, error) {
+ return &Printer{}, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
@@ -91,7 +97,7 @@ func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) {
fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc))
}
-func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int) {
+func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new)
}
diff --git a/eth/tracers/logger/access_list_tracer.go b/eth/tracers/logger/access_list_tracer.go
index 0c246bef4c..7df66066b5 100644
--- a/eth/tracers/logger/access_list_tracer.go
+++ b/eth/tracers/logger/access_list_tracer.go
@@ -17,11 +17,10 @@
package logger
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
// accessList is an accumulator for the set of accounts and storage slots an EVM
@@ -103,6 +102,7 @@ func (al accessList) accessList() types.AccessList {
// AccessListTracer is a tracer that accumulates touched accounts and storage
// slots into an internal set.
type AccessListTracer struct {
+ directory.NoopTracer
excl map[common.Address]struct{} // Set of account to exclude from the list
list accessList // Set of accounts and storage slots touched
}
@@ -132,9 +132,6 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
}
}
-func (a *AccessListTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
-}
-
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
@@ -158,37 +155,6 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
}
}
-func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-func (*AccessListTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
-
-func (*AccessListTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
-func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {}
-
-func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
-}
-
-func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
-
-func (*AccessListTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {}
-
-func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (*AccessListTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {}
-
-func (*AccessListTracer) OnNonceChange(a common.Address, prev, new uint64) {}
-
-func (*AccessListTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
-}
-
-func (*AccessListTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
-
-func (*AccessListTracer) OnLog(log *types.Log) {}
-
-func (*AccessListTracer) OnNewAccount(a common.Address) {}
-
// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {
return a.list.accessList()
diff --git a/eth/tracers/logger/logger.go b/eth/tracers/logger/logger.go
index 3858bbd8f7..22294252af 100644
--- a/eth/tracers/logger/logger.go
+++ b/eth/tracers/logger/logger.go
@@ -28,9 +28,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@@ -107,6 +107,7 @@ func (s *StructLog) ErrorString() string {
// a track record of modified storage which is used in reporting snapshots of the
// contract their storage.
type StructLogger struct {
+ directory.NoopTracer
cfg Config
env *vm.EVM
@@ -139,10 +140,6 @@ func (l *StructLogger) Reset() {
l.err = nil
}
-// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
-func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
-}
-
// CaptureState logs a new structured log message and pushes it out to the environment
//
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
@@ -211,16 +208,6 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
l.logs = append(l.logs, log)
}
-// CaptureFault implements the EVMLogger interface to trace an execution fault
-// while running an opcode.
-func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
-}
-
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
-
-func (l *StructLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
// CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
l.output = output
@@ -233,12 +220,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
}
}
-func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
-}
-
-func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
-}
-
func (l *StructLogger) GetResult() (json.RawMessage, error) {
// Tracing aborted
if l.reason != nil {
@@ -280,20 +261,6 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) {
l.usedGas = receipt.GasUsed
}
-func (l *StructLogger) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
-}
-
-func (l *StructLogger) OnNonceChange(a common.Address, prev, new uint64) {}
-
-func (l *StructLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
-}
-
-func (l *StructLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
-
-func (l *StructLogger) OnLog(log *types.Log) {}
-
-func (l *StructLogger) OnNewAccount(a common.Address) {}
-
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
@@ -351,6 +318,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
}
type mdLogger struct {
+ directory.NoopTracer
out io.Writer
cfg *Config
env *vm.EVM
@@ -408,37 +376,11 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
-func (t *mdLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
-
-func (t *mdLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
-func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
-}
-
-func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
-
-func (*mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {}
-
-func (*mdLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (*mdLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
-
-func (*mdLogger) OnNonceChange(a common.Address, prev, new uint64) {}
-
-func (*mdLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
-}
-
-func (*mdLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
-
-func (*mdLogger) OnLog(log *types.Log) {}
-
-func (*mdLogger) OnNewAccount(a common.Address) {}
-
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value
diff --git a/eth/tracers/logger/logger_json.go b/eth/tracers/logger/logger_json.go
index 6e687c7e64..1ea014a0d6 100644
--- a/eth/tracers/logger/logger_json.go
+++ b/eth/tracers/logger/logger_json.go
@@ -19,15 +19,16 @@ package logger
import (
"encoding/json"
"io"
- "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
type JSONLogger struct {
+ directory.NoopTracer
encoder *json.Encoder
cfg *Config
env *vm.EVM
@@ -43,9 +44,6 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
return l
}
-func (l *JSONLogger) CaptureStart(from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
-}
-
func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) {
// TODO: Add rData to this interface as well
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
@@ -78,11 +76,6 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
l.encoder.Encode(log)
}
-// CaptureKeccakPreimage is called during the KECCAK256 opcode.
-func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
-
-func (l *JSONLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
-
// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
type endLog struct {
@@ -97,26 +90,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
}
-func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
-}
-
-func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
-
func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {
l.env = env
}
-
-func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
-
-func (*JSONLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
-
-func (*JSONLogger) OnNonceChange(a common.Address, prev, new uint64) {}
-
-func (*JSONLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
-}
-
-func (*JSONLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
-
-func (*JSONLogger) OnLog(log *types.Log) {}
-
-func (*JSONLogger) OnNewAccount(a common.Address) {}
diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go
index 4a7db6e72f..e6c3be41cd 100644
--- a/eth/tracers/logger/logger_test.go
+++ b/eth/tracers/logger/logger_test.go
@@ -60,6 +60,7 @@ func TestStoreCapture(t *testing.T) {
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash
+ logger.CaptureTxStart(env, nil)
logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil)
_, err := env.Interpreter().Run(contract, []byte{}, false)
if err != nil {
diff --git a/eth/tracers/native/4byte.go b/eth/tracers/native/4byte.go
index 25152a098e..b25ff06ae2 100644
--- a/eth/tracers/native/4byte.go
+++ b/eth/tracers/native/4byte.go
@@ -25,11 +25,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
func init() {
- tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
+ directory.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
}
// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
@@ -47,7 +47,7 @@ func init() {
// 0xc281d19e-0: 1
// }
type fourByteTracer struct {
- tracers.NoopTracer
+ directory.NoopTracer
env *vm.EVM
ids map[string]int // ids aggregates the 4byte ids found
interrupt atomic.Bool // Atomic flag to signal execution interruption
@@ -57,7 +57,7 @@ type fourByteTracer struct {
// newFourByteTracer returns a native go tracer which collects
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
-func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
+func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) {
t := &fourByteTracer{
ids: make(map[string]int),
}
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 3af18cd3ce..bb2ea19b54 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -27,13 +27,13 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
func init() {
- tracers.DefaultDirectory.Register("callTracer", newCallTracer, false)
+ directory.DefaultDirectory.Register("callTracer", newCallTracer, false)
}
type callLog struct {
@@ -99,7 +99,7 @@ type callFrameMarshaling struct {
}
type callTracer struct {
- tracers.NoopTracer
+ directory.NoopTracer
callstack []callFrame
config callTracerConfig
gasLimit uint64
@@ -115,7 +115,7 @@ type callTracerConfig struct {
// newCallTracer returns a native go tracer which tracks
// call frames of a tx, and implements vm.EVMLogger.
-func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config callTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
diff --git a/eth/tracers/native/call_flat.go b/eth/tracers/native/call_flat.go
index 084df593a8..d5aebc00fd 100644
--- a/eth/tracers/native/call_flat.go
+++ b/eth/tracers/native/call_flat.go
@@ -27,14 +27,14 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
//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)
+ directory.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
}
var parityErrorMapping = map[string]string{
@@ -109,12 +109,12 @@ type flatCallResultMarshaling struct {
// flatCallTracer reports call frame information of a tx in a flat format, i.e.
// as opposed to the nested format of `callTracer`.
type flatCallTracer struct {
- tracers.NoopTracer
+ directory.NoopTracer
tracer *callTracer
config flatCallTracerConfig
- ctx *tracers.Context // Holds tracer context data
- reason error // Textual reason for the interruption
- activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+ ctx *directory.Context // Holds tracer context data
+ reason error // Textual reason for the interruption
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
}
type flatCallTracerConfig struct {
@@ -123,7 +123,7 @@ type flatCallTracerConfig struct {
}
// newFlatCallTracer returns a new flatCallTracer.
-func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config flatCallTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
@@ -133,7 +133,7 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace
// Create inner call tracer with default configuration, don't forward
// the OnlyTopCall or WithLog to inner for now
- tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil)
+ tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil)
if err != nil {
return nil, err
}
@@ -244,7 +244,7 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
return false
}
-func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) {
+func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *directory.Context) (output []flatCallFrame, err error) {
var frame *flatCallFrame
switch input.Type {
case vm.CREATE, vm.CREATE2:
@@ -343,7 +343,7 @@ func newFlatSelfdestruct(input *callFrame) *flatCallFrame {
}
}
-func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) {
+func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *directory.Context) {
if ctx == nil {
return
}
diff --git a/eth/tracers/native/mux.go b/eth/tracers/native/mux.go
index da2381b880..dab284a3a3 100644
--- a/eth/tracers/native/mux.go
+++ b/eth/tracers/native/mux.go
@@ -24,32 +24,32 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
)
func init() {
- tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
+ directory.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
}
// muxTracer is a go implementation of the Tracer interface which
// runs multiple tracers in one go.
type muxTracer struct {
names []string
- tracers []tracers.Tracer
+ tracers []directory.Tracer
}
// newMuxTracer returns a new mux tracer.
-func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config map[string]json.RawMessage
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
- objects := make([]tracers.Tracer, 0, len(config))
+ objects := make([]directory.Tracer, 0, len(config))
names := make([]string, 0, len(config))
for k, v := range config {
- t, err := tracers.DefaultDirectory.New(k, ctx, v)
+ t, err := directory.DefaultDirectory.New(k, ctx, v)
if err != nil {
return nil, err
}
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index 9375279cb4..71cd7ce791 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -28,14 +28,14 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/tracers"
+ "github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/log"
)
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
func init() {
- tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
+ directory.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
}
type stateMap = map[common.Address]*account
@@ -57,7 +57,7 @@ type accountMarshaling struct {
}
type prestateTracer struct {
- tracers.NoopTracer
+ directory.NoopTracer
env *vm.EVM
pre stateMap
post stateMap
@@ -74,7 +74,7 @@ type prestateTracerConfig struct {
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
}
-func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config prestateTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
@@ -143,7 +143,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
case stackLen >= 4 && op == vm.CREATE2:
offset := stackData[stackLen-2]
size := stackData[stackLen-3]
- init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
+ init, err := directory.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
if err != nil {
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
return
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 759e3a4dd3..b9c0ac526d 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -109,41 +109,3 @@ func BenchmarkTransactionTrace(b *testing.B) {
tracer.Reset()
}
}
-
-func TestMemCopying(t *testing.T) {
- for i, tc := range []struct {
- memsize int64
- offset int64
- size int64
- wantErr string
- wantSize int
- }{
- {0, 0, 100, "", 100}, // Should pad up to 100
- {0, 100, 0, "", 0}, // No need to pad (0 size)
- {100, 50, 100, "", 100}, // Should pad 100-150
- {100, 50, 5, "", 5}, // Wanted range fully within memory
- {100, -50, 0, "offset or size must not be negative", 0}, // Errror
- {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
- {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
-
- } {
- mem := vm.NewMemory()
- mem.Resize(uint64(tc.memsize))
- cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size)
- if want := tc.wantErr; want != "" {
- if err == nil {
- t.Fatalf("test %d: want '%v' have no error", i, want)
- }
- if have := err.Error(); want != have {
- t.Fatalf("test %d: want '%v' have '%v'", i, want, have)
- }
- continue
- }
- if err != nil {
- t.Fatalf("test %d: unexpected error: %v", i, err)
- }
- if want, have := tc.wantSize, len(cpy); have != want {
- t.Fatalf("test %d: want %v have %v", i, want, have)
- }
- }
-}