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:
Matthieu Vachon 2023-09-01 09:34:29 -04:00
commit daf3f63421
28 changed files with 393 additions and 407 deletions

View file

@ -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)

View file

@ -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,18 +1774,20 @@ 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()
// The traced section of block import.
err, stop := func() (blockEndErr error, _ bool) {
if bc.logger != nil { if bc.logger != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1) td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock()) 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) receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true) followupInterrupt.Store(true)
if bc.logger != nil { return err, true
bc.logger.OnBlockEnd(err)
}
return it.index, err
} }
ptime := time.Since(pstart) ptime := time.Since(pstart)
@ -1778,10 +1795,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true) followupInterrupt.Store(true)
if bc.logger != nil { return err, true
bc.logger.OnBlockEnd(err)
}
return it.index, err
} }
vtime := time.Since(vstart) vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation proctime := time.Since(start) // processing + validation
@ -1815,10 +1829,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
} }
followupInterrupt.Store(true) followupInterrupt.Store(true)
if err != nil { if err != nil {
if bc.logger != nil { return err, true
bc.logger.OnBlockEnd(err)
}
return it.index, err
} }
// Update the metrics touched during block commit // Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
@ -1836,16 +1847,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
dirty, _ := bc.triedb.Size() dirty, _ := bc.triedb.Size()
stats.report(chain, it.index, dirty, setHead) stats.report(chain, it.index, dirty, setHead)
if bc.logger != nil {
bc.logger.OnBlockEnd(nil)
}
if !setHead { if !setHead {
// After merge we expect few side chains. Simply count // After merge we expect few side chains. Simply count
// all blocks the CL gives us for GC processing time // all blocks the CL gives us for GC processing time
bc.gcproc += proctime bc.gcproc += proctime
return it.index, nil // Direct block insertion of a single block return nil, true // Direct block insertion of a single block
} }
switch status { switch status {
case CanonStatTy: case CanonStatTy:
@ -1873,6 +1880,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root()) "root", block.Root())
} }
return nil, false
}()
if err != nil || stop {
return it.index, err
}
} }
// 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

View file

@ -176,16 +176,16 @@ 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 {
return alloc, nil
}
// Genesis allocation is missing and there are several possibilities: // Genesis allocation is missing and there are several possibilities:
// the node is legacy which doesn't persist the genesis allocation or // the node is legacy which doesn't persist the genesis allocation or
// the persisted allocation is just lost. // the persisted allocation is just lost.
@ -201,11 +201,24 @@ func CommitGenesisState(db ethdb.Database, triedb *trie.Database, blockhash comm
genesis = DefaultSepoliaGenesisBlock() genesis = DefaultSepoliaGenesisBlock()
} }
if genesis != nil { if genesis != nil {
alloc = genesis.Alloc return genesis.Alloc, nil
} else { }
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 errors.New("not found")
} }
}
return alloc.flush(db, triedb, blockhash, nil) return alloc.flush(db, triedb, blockhash, nil)
} }

View file

@ -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 {

View file

@ -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 {

View file

@ -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:"-"`

View file

@ -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
} }

View 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")
}

View file

@ -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"

View file

@ -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
}

View 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
}

View 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)
}
}
}

View file

@ -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
}{ }{
{ {

View file

@ -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)
} }

View file

@ -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)
} }

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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)
} }

View file

@ -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()

View file

@ -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

View file

@ -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) {}

View file

@ -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 {

View file

@ -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),
} }

View file

@ -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 {

View file

@ -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,10 +109,10 @@ 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
} }
@ -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
} }

View file

@ -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
} }

View file

@ -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

View file

@ -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)
}
}
}