mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-14 16:03:45 +00:00
Merge remote-tracking branch 's1na/extended-tracer' into feature/gas-full-cycle-and-reason
# Conflicts: # eth/tracers/logger/access_list_tracer.go # eth/tracers/logger/logger.go # eth/tracers/logger/logger_json.go
This commit is contained in:
commit
daf3f63421
28 changed files with 393 additions and 407 deletions
|
|
@ -54,6 +54,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/filters"
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/remotedb"
|
"github.com/ethereum/go-ethereum/ethdb/remotedb"
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
|
|
@ -518,9 +519,9 @@ var (
|
||||||
Usage: "Record information useful for VM and contract debugging",
|
Usage: "Record information useful for VM and contract debugging",
|
||||||
Category: flags.VMCategory,
|
Category: flags.VMCategory,
|
||||||
}
|
}
|
||||||
VMTraceFlag = &cli.BoolFlag{
|
VMTraceFlag = &cli.StringFlag{
|
||||||
Name: "vmtrace",
|
Name: "vmtrace",
|
||||||
Usage: "Record internal VM operations (costly)",
|
Usage: "Name of tracer which should record internal VM operations (costly)",
|
||||||
Category: flags.VMCategory,
|
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
|
// TODO(fjl): force-enable this in --dev mode
|
||||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMTraceFlag.Name) {
|
|
||||||
cfg.LiveTrace = ctx.Bool(VMTraceFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
|
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
|
||||||
cfg.RPCGasCap = ctx.Uint64(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)}
|
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
|
||||||
if ctx.IsSet(VMTraceFlag.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.
|
// Disable transaction indexing/unindexing by default.
|
||||||
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
|
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
|
||||||
|
|
|
||||||
|
|
@ -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
|
// Load any existing snapshot, regenerating it if loading failed
|
||||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||||
// If the chain was rewound past the snapshot persistent layer (causing
|
// 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
|
// Process block using the parent state as reference point
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
|
|
||||||
if bc.logger != nil {
|
// The traced section of block import.
|
||||||
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
err, stop := func() (blockEndErr error, _ bool) {
|
||||||
bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
|
if bc.logger != nil {
|
||||||
}
|
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
|
||||||
if err != nil {
|
defer func() {
|
||||||
bc.reportBlock(block, receipts, err)
|
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)
|
followupInterrupt.Store(true)
|
||||||
if bc.logger != nil {
|
if err != nil {
|
||||||
bc.logger.OnBlockEnd(err)
|
return err, true
|
||||||
}
|
}
|
||||||
return it.index, err
|
// Update the metrics touched during block commit
|
||||||
}
|
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
|
||||||
ptime := time.Since(pstart)
|
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()
|
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||||
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
|
blockInsertTimer.UpdateSince(start)
|
||||||
bc.reportBlock(block, receipts, err)
|
|
||||||
followupInterrupt.Store(true)
|
// Report the import stats before returning the various results
|
||||||
if bc.logger != nil {
|
stats.processed++
|
||||||
bc.logger.OnBlockEnd(err)
|
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
|
switch status {
|
||||||
}
|
case CanonStatTy:
|
||||||
vtime := time.Since(vstart)
|
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
|
||||||
proctime := time.Since(start) // processing + validation
|
"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
|
lastCanon = block
|
||||||
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.
|
// Only count canonical blocks for GC processing time
|
||||||
var (
|
bc.gcproc += proctime
|
||||||
wstart = time.Now()
|
|
||||||
status WriteStatus
|
case SideStatTy:
|
||||||
)
|
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
|
||||||
if !setHead {
|
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
// Don't set the head, only insert the block
|
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
|
||||||
err = bc.writeBlockWithState(block, receipts, statedb)
|
"root", block.Root())
|
||||||
} else {
|
|
||||||
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
|
default:
|
||||||
}
|
// This in theory is impossible, but lets be nice to our future selves and leave
|
||||||
followupInterrupt.Store(true)
|
// a log, instead of trying to track down blocks imports that don't emit logs.
|
||||||
if err != nil {
|
log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
|
||||||
if bc.logger != nil {
|
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
bc.logger.OnBlockEnd(err)
|
"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
|
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
|
// Any blocks remaining here? The only ones we care about are the future ones
|
||||||
|
|
|
||||||
|
|
@ -176,36 +176,49 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitGenesisState loads the stored genesis state with the given block
|
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc GenesisAlloc, err error) {
|
||||||
// hash and commits it into the provided trie database.
|
|
||||||
func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
|
|
||||||
var alloc GenesisAlloc
|
|
||||||
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
|
blob := rawdb.ReadGenesisStateSpec(db, blockhash)
|
||||||
if len(blob) != 0 {
|
if len(blob) != 0 {
|
||||||
if err := alloc.UnmarshalJSON(blob); err != nil {
|
if err := alloc.UnmarshalJSON(blob); err != nil {
|
||||||
return err
|
return nil, 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 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)
|
return alloc.flush(db, triedb, blockhash, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ func (n *proofList) Delete(key []byte) error {
|
||||||
|
|
||||||
// StateLogger is used to collect state update traces from EVM transaction
|
// StateLogger is used to collect state update traces from EVM transaction
|
||||||
// execution.
|
// 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
|
// Note that reference types are actual VM data structures; make copies
|
||||||
// if you need to retain them beyond the current call.
|
// if you need to retain them beyond the current call.
|
||||||
type StateLogger interface {
|
type StateLogger interface {
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
"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/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
|
|
@ -194,9 +193,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
||||||
Preimages: config.Preimages,
|
Preimages: config.Preimages,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if config.LiveTrace {
|
|
||||||
vmConfig.Tracer = tracers.NewPrinter()
|
|
||||||
}
|
|
||||||
// Override the chain config with provided settings.
|
// Override the chain config with provided settings.
|
||||||
var overrides core.ChainOverrides
|
var overrides core.ChainOverrides
|
||||||
if config.OverrideCancun != nil {
|
if config.OverrideCancun != nil {
|
||||||
|
|
|
||||||
|
|
@ -140,9 +140,6 @@ type Config struct {
|
||||||
// Enables tracking of SHA3 preimages in the VM
|
// Enables tracking of SHA3 preimages in the VM
|
||||||
EnablePreimageRecording bool
|
EnablePreimageRecording bool
|
||||||
|
|
||||||
// LiveTrace will enable tracing during normal chain processing.
|
|
||||||
LiveTrace bool
|
|
||||||
|
|
||||||
// Miscellaneous options
|
// Miscellaneous options
|
||||||
DocRoot string `toml:"-"`
|
DocRoot string `toml:"-"`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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/eth/tracers/logger"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"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
|
// Trace all the transactions contained within
|
||||||
for i, tx := range task.block.Transactions() {
|
for i, tx := range task.block.Transactions() {
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
|
||||||
txctx := &Context{
|
txctx := &directory.Context{
|
||||||
BlockHash: task.block.Hash(),
|
BlockHash: task.block.Hash(),
|
||||||
BlockNumber: task.block.Number(),
|
BlockNumber: task.block.Number(),
|
||||||
TxIndex: i,
|
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
|
// process that generates states in one thread and traces txes
|
||||||
// in separate worker threads.
|
// in separate worker threads.
|
||||||
if config != nil && config.Tracer != nil && *config.Tracer != "" {
|
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)
|
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 {
|
for i, tx := range txs {
|
||||||
// Generate the next state snapshot fast without tracing
|
// Generate the next state snapshot fast without tracing
|
||||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
txctx := &Context{
|
txctx := &directory.Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
BlockNumber: block.Number(),
|
BlockNumber: block.Number(),
|
||||||
TxIndex: i,
|
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
|
// Fetch and execute the next transaction trace tasks
|
||||||
for task := range jobs {
|
for task := range jobs {
|
||||||
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
|
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
|
||||||
txctx := &Context{
|
txctx := &directory.Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
BlockNumber: block.Number(),
|
BlockNumber: block.Number(),
|
||||||
TxIndex: task.index,
|
TxIndex: task.index,
|
||||||
|
|
@ -859,7 +860,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txctx := &Context{
|
txctx := &directory.Context{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
BlockNumber: block.Number(),
|
BlockNumber: block.Number(),
|
||||||
TxIndex: int(index),
|
TxIndex: int(index),
|
||||||
|
|
@ -927,15 +928,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
|
||||||
if config != nil {
|
if config != nil {
|
||||||
traceConfig = &config.TraceConfig
|
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
|
// traceTx configures a new tracer according to the provided configuration, and
|
||||||
// executes the given message in the provided environment. The return value will
|
// executes the given message in the provided environment. The return value will
|
||||||
// be tracer dependent.
|
// 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 (
|
var (
|
||||||
tracer Tracer
|
tracer directory.Tracer
|
||||||
err error
|
err error
|
||||||
timeout = defaultTraceTimeout
|
timeout = defaultTraceTimeout
|
||||||
txContext = core.NewEVMTxContext(message)
|
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
|
// Default tracer is the struct logger
|
||||||
tracer = logger.NewStructLogger(config.Config)
|
tracer = logger.NewStructLogger(config.Config)
|
||||||
if config.Tracer != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
eth/tracers/directory/live.go
Normal file
30
eth/tracers/directory/live.go
Normal file
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package tracers
|
package directory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -14,13 +14,13 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Package tracers is a manager for transaction tracing engines.
|
// Package directory provides functionality to lookup tracers by name.
|
||||||
package tracers
|
// It also includes utility functions that are imported by the other
|
||||||
|
// tracing packages.
|
||||||
|
package directory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -101,27 +101,3 @@ func (d *directory) IsJS(name string) bool {
|
||||||
// JS eval will execute JS code
|
// JS eval will execute JS code
|
||||||
return true
|
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
|
|
||||||
}
|
|
||||||
47
eth/tracers/directory/util.go
Normal file
47
eth/tracers/directory/util.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
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
|
||||||
|
}
|
||||||
60
eth/tracers/directory/util_test.go
Normal file
60
eth/tracers/directory/util_test.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"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/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"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)
|
_, 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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
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.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
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 {
|
if err != nil {
|
||||||
b.Fatalf("failed to create call tracer: %v", err)
|
b.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -283,8 +283,8 @@ func TestInternals(t *testing.T) {
|
||||||
BaseFee: new(big.Int),
|
BaseFee: new(big.Int),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer {
|
mkTracer := func(name string, cfg json.RawMessage) directory.Tracer {
|
||||||
tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
|
tr, err := directory.DefaultDirectory.New(name, nil, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +294,7 @@ func TestInternals(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
code []byte
|
code []byte
|
||||||
tracer tracers.Tracer
|
tracer directory.Tracer
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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/rlp"
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
"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.
|
// 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)
|
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
|
||||||
|
|
||||||
// Create the tracer, the EVM environment and run it
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create call tracer: %v", err)
|
return fmt.Errorf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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"
|
"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)
|
_, 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 {
|
if err != nil {
|
||||||
t.Fatalf("failed to create call tracer: %v", err)
|
t.Fatalf("failed to create call tracer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,12 @@ import (
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
|
||||||
jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
|
jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -42,16 +42,16 @@ func init() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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 {
|
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)
|
return newJsTracer(code, ctx, cfg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for name, code := range assetTracers {
|
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
|
// 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
|
// jsTracer is an implementation of the Tracer interface which evaluates
|
||||||
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
|
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
|
||||||
type jsTracer struct {
|
type jsTracer struct {
|
||||||
tracers.NoopTracer
|
directory.NoopTracer
|
||||||
|
|
||||||
vm *goja.Runtime
|
vm *goja.Runtime
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
|
|
@ -136,7 +136,7 @@ type jsTracer struct {
|
||||||
// The methods `result` and `fault` are required to be present.
|
// The methods `result` and `fault` are required to be present.
|
||||||
// The methods `step`, `enter`, and `exit` are optional, but note that
|
// The methods `step`, `enter`, and `exit` are optional, but note that
|
||||||
// `enter` and `exit` always go together.
|
// `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()
|
vm := goja.New()
|
||||||
// By default field names are exported to JS as is, i.e. capitalized.
|
// By default field names are exported to JS as is, i.e. capitalized.
|
||||||
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
|
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),
|
ctx: make(map[string]goja.Value),
|
||||||
}
|
}
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = new(tracers.Context)
|
ctx = new(directory.Context)
|
||||||
}
|
}
|
||||||
if ctx.BlockHash != (common.Hash{}) {
|
if ctx.BlockHash != (common.Hash{}) {
|
||||||
t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
|
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 {
|
if end < begin || begin < 0 {
|
||||||
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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"
|
"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)}}
|
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 (
|
var (
|
||||||
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
|
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
|
||||||
gasLimit uint64 = 31000
|
gasLimit uint64 = 31000
|
||||||
|
|
@ -264,14 +264,14 @@ func TestIsPrecompile(t *testing.T) {
|
||||||
|
|
||||||
func TestEnterExit(t *testing.T) {
|
func TestEnterExit(t *testing.T) {
|
||||||
// test that either both or none of enter() and exit() are defined
|
// 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")
|
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)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// test that the enter and exit method are correctly invoked and the values passed
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +293,7 @@ func TestEnterExit(t *testing.T) {
|
||||||
|
|
||||||
func TestSetup(t *testing.T) {
|
func TestSetup(t *testing.T) {
|
||||||
// Test empty config
|
// 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 {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
@ -303,12 +303,12 @@ func TestSetup(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Test no setup func
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Test config value
|
// 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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package tracers
|
package live
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -8,14 +8,20 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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{}
|
type Printer struct{}
|
||||||
|
|
||||||
func NewPrinter() *Printer {
|
func newPrinter() (core.BlockchainLogger, error) {
|
||||||
return &Printer{}
|
return &Printer{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
|
// 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))
|
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)
|
fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,11 +17,10 @@
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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
|
// 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
|
// AccessListTracer is a tracer that accumulates touched accounts and storage
|
||||||
// slots into an internal set.
|
// slots into an internal set.
|
||||||
type AccessListTracer struct {
|
type AccessListTracer struct {
|
||||||
|
directory.NoopTracer
|
||||||
excl map[common.Address]struct{} // Set of account to exclude from the list
|
excl map[common.Address]struct{} // Set of account to exclude from the list
|
||||||
list accessList // Set of accounts and storage slots touched
|
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.
|
// 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) {
|
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
|
||||||
stack := scope.Stack
|
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.
|
// AccessList returns the current accesslist maintained by the tracer.
|
||||||
func (a *AccessListTracer) AccessList() types.AccessList {
|
func (a *AccessListTracer) AccessList() types.AccessList {
|
||||||
return a.list.accessList()
|
return a.list.accessList()
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"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/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/holiman/uint256"
|
"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
|
// a track record of modified storage which is used in reporting snapshots of the
|
||||||
// contract their storage.
|
// contract their storage.
|
||||||
type StructLogger struct {
|
type StructLogger struct {
|
||||||
|
directory.NoopTracer
|
||||||
cfg Config
|
cfg Config
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
|
|
||||||
|
|
@ -139,10 +140,6 @@ func (l *StructLogger) Reset() {
|
||||||
l.err = nil
|
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 logs a new structured log message and pushes it out to the environment
|
||||||
//
|
//
|
||||||
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
|
// 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)
|
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.
|
// CaptureEnd is called after the call finishes to finalize the tracing.
|
||||||
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
l.output = output
|
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) {
|
func (l *StructLogger) GetResult() (json.RawMessage, error) {
|
||||||
// Tracing aborted
|
// Tracing aborted
|
||||||
if l.reason != nil {
|
if l.reason != nil {
|
||||||
|
|
@ -280,20 +261,6 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) {
|
||||||
l.usedGas = receipt.GasUsed
|
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.
|
// StructLogs returns the captured log entries.
|
||||||
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
|
||||||
|
|
||||||
|
|
@ -351,6 +318,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type mdLogger struct {
|
type mdLogger struct {
|
||||||
|
directory.NoopTracer
|
||||||
out io.Writer
|
out io.Writer
|
||||||
cfg *Config
|
cfg *Config
|
||||||
env *vm.EVM
|
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)
|
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) {
|
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
|
||||||
output, gasUsed, err)
|
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
|
// ExecutionResult groups all structured logs emitted by the EVM
|
||||||
// while replaying a transaction in debug mode as well as transaction
|
// while replaying a transaction in debug mode as well as transaction
|
||||||
// execution status, the amount of gas used and the return value
|
// execution status, the amount of gas used and the return value
|
||||||
|
|
|
||||||
|
|
@ -19,15 +19,16 @@ package logger
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
type JSONLogger struct {
|
type JSONLogger struct {
|
||||||
|
directory.NoopTracer
|
||||||
encoder *json.Encoder
|
encoder *json.Encoder
|
||||||
cfg *Config
|
cfg *Config
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
|
|
@ -43,9 +44,6 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
|
||||||
return l
|
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) {
|
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
|
// TODO: Add rData to this interface as well
|
||||||
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
|
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)
|
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.
|
// CaptureEnd is triggered at end of execution.
|
||||||
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
|
||||||
type endLog struct {
|
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})
|
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) {
|
func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {
|
||||||
l.env = env
|
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) {}
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ func TestStoreCapture(t *testing.T) {
|
||||||
)
|
)
|
||||||
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
|
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
|
||||||
var index common.Hash
|
var index common.Hash
|
||||||
|
logger.CaptureTxStart(env, nil)
|
||||||
logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil)
|
logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil)
|
||||||
_, err := env.Interpreter().Run(contract, []byte{}, false)
|
_, err := env.Interpreter().Run(contract, []byte{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,11 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
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.
|
// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
|
||||||
|
|
@ -47,7 +47,7 @@ func init() {
|
||||||
// 0xc281d19e-0: 1
|
// 0xc281d19e-0: 1
|
||||||
// }
|
// }
|
||||||
type fourByteTracer struct {
|
type fourByteTracer struct {
|
||||||
tracers.NoopTracer
|
directory.NoopTracer
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
ids map[string]int // ids aggregates the 4byte ids found
|
ids map[string]int // ids aggregates the 4byte ids found
|
||||||
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
interrupt atomic.Bool // Atomic flag to signal execution interruption
|
||||||
|
|
@ -57,7 +57,7 @@ type fourByteTracer struct {
|
||||||
|
|
||||||
// newFourByteTracer returns a native go tracer which collects
|
// newFourByteTracer returns a native go tracer which collects
|
||||||
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
|
// 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{
|
t := &fourByteTracer{
|
||||||
ids: make(map[string]int),
|
ids: make(map[string]int),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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
|
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tracers.DefaultDirectory.Register("callTracer", newCallTracer, false)
|
directory.DefaultDirectory.Register("callTracer", newCallTracer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type callLog struct {
|
type callLog struct {
|
||||||
|
|
@ -99,7 +99,7 @@ type callFrameMarshaling struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type callTracer struct {
|
type callTracer struct {
|
||||||
tracers.NoopTracer
|
directory.NoopTracer
|
||||||
callstack []callFrame
|
callstack []callFrame
|
||||||
config callTracerConfig
|
config callTracerConfig
|
||||||
gasLimit uint64
|
gasLimit uint64
|
||||||
|
|
@ -115,7 +115,7 @@ type callTracerConfig struct {
|
||||||
|
|
||||||
// newCallTracer returns a native go tracer which tracks
|
// newCallTracer returns a native go tracer which tracks
|
||||||
// call frames of a tx, and implements vm.EVMLogger.
|
// 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
|
var config callTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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 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
|
//go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
|
directory.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var parityErrorMapping = map[string]string{
|
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.
|
// flatCallTracer reports call frame information of a tx in a flat format, i.e.
|
||||||
// as opposed to the nested format of `callTracer`.
|
// as opposed to the nested format of `callTracer`.
|
||||||
type flatCallTracer struct {
|
type flatCallTracer struct {
|
||||||
tracers.NoopTracer
|
directory.NoopTracer
|
||||||
tracer *callTracer
|
tracer *callTracer
|
||||||
config flatCallTracerConfig
|
config flatCallTracerConfig
|
||||||
ctx *tracers.Context // Holds tracer context data
|
ctx *directory.Context // Holds tracer context data
|
||||||
reason error // Textual reason for the interruption
|
reason error // Textual reason for the interruption
|
||||||
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
|
||||||
}
|
}
|
||||||
|
|
||||||
type flatCallTracerConfig struct {
|
type flatCallTracerConfig struct {
|
||||||
|
|
@ -123,7 +123,7 @@ type flatCallTracerConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newFlatCallTracer returns a new flatCallTracer.
|
// 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
|
var config flatCallTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != 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
|
// Create inner call tracer with default configuration, don't forward
|
||||||
// the OnlyTopCall or WithLog to inner for now
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +244,7 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
|
||||||
return false
|
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
|
var frame *flatCallFrame
|
||||||
switch input.Type {
|
switch input.Type {
|
||||||
case vm.CREATE, vm.CREATE2:
|
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 {
|
if ctx == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,32 +24,32 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
"github.com/ethereum/go-ethereum/eth/tracers/directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
|
directory.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// muxTracer is a go implementation of the Tracer interface which
|
// muxTracer is a go implementation of the Tracer interface which
|
||||||
// runs multiple tracers in one go.
|
// runs multiple tracers in one go.
|
||||||
type muxTracer struct {
|
type muxTracer struct {
|
||||||
names []string
|
names []string
|
||||||
tracers []tracers.Tracer
|
tracers []directory.Tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
// newMuxTracer returns a new mux 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
|
var config map[string]json.RawMessage
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != nil {
|
if err := json.Unmarshal(cfg, &config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objects := make([]tracers.Tracer, 0, len(config))
|
objects := make([]directory.Tracer, 0, len(config))
|
||||||
names := make([]string, 0, len(config))
|
names := make([]string, 0, len(config))
|
||||||
for k, v := range 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"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"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
|
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
|
directory.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
type stateMap = map[common.Address]*account
|
type stateMap = map[common.Address]*account
|
||||||
|
|
@ -57,7 +57,7 @@ type accountMarshaling struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type prestateTracer struct {
|
type prestateTracer struct {
|
||||||
tracers.NoopTracer
|
directory.NoopTracer
|
||||||
env *vm.EVM
|
env *vm.EVM
|
||||||
pre stateMap
|
pre stateMap
|
||||||
post stateMap
|
post stateMap
|
||||||
|
|
@ -74,7 +74,7 @@ type prestateTracerConfig struct {
|
||||||
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
|
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
|
var config prestateTracerConfig
|
||||||
if cfg != nil {
|
if cfg != nil {
|
||||||
if err := json.Unmarshal(cfg, &config); err != 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:
|
case stackLen >= 4 && op == vm.CREATE2:
|
||||||
offset := stackData[stackLen-2]
|
offset := stackData[stackLen-2]
|
||||||
size := stackData[stackLen-3]
|
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 {
|
if err != nil {
|
||||||
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
|
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -109,41 +109,3 @@ func BenchmarkTransactionTrace(b *testing.B) {
|
||||||
tracer.Reset()
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue