Merge branch 'feature/gas-full-cycle-and-reason' into feature/firehose-extended-tracer

# Conflicts:
#	core/blockchain.go
#	core/state_transition.go
#	core/vm/contracts.go
#	core/vm/evm.go
#	core/vm/instructions.go
#	core/vm/interpreter.go
#	core/vm/logger.go
#	core/vm/operations_acl.go
#	eth/backend.go
#	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-09 13:21:10 -04:00
commit 31f1a01ec5
38 changed files with 457 additions and 479 deletions

View file

@ -54,6 +54,7 @@ import (
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/remotedb"
"github.com/ethereum/go-ethereum/ethstats"
@ -518,9 +519,9 @@ var (
Usage: "Record information useful for VM and contract debugging",
Category: flags.VMCategory,
}
VMTraceFlag = &cli.BoolFlag{
VMTraceFlag = &cli.StringFlag{
Name: "vmtrace",
Usage: "Record internal VM operations (costly)",
Usage: "Name of tracer which should record internal VM operations (costly)",
Category: flags.VMCategory,
}
@ -1723,9 +1724,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// TODO(fjl): force-enable this in --dev mode
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
}
if ctx.IsSet(VMTraceFlag.Name) {
cfg.LiveTrace = ctx.Bool(VMTraceFlag.Name)
}
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
@ -2143,7 +2141,13 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}
if ctx.IsSet(VMTraceFlag.Name) {
vmcfg.Tracer = tracers.NewPrinter()
if name := ctx.String(VMTraceFlag.Name); name != "" {
t, err := directory.LiveDirectory.New(name)
if err != nil {
Fatalf("Failed to create tracer %q: %v", name, err)
}
vmcfg.Tracer = t
}
}
// Disable transaction indexing/unindexing by default.
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)

View file

@ -1774,121 +1774,117 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// Process block using the parent state as reference point
pstart := time.Now()
if bc.logger != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
}
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil {
bc.reportBlock(block, receipts, err)
// The traced section of block import.
err, stop := func() (blockEndErr error, _ bool) {
if bc.logger != nil {
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
bc.logger.OnBlockStart(block, td, bc.CurrentFinalBlock(), bc.CurrentSafeBlock())
defer func() {
bc.logger.OnBlockEnd(blockEndErr)
}()
}
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
if err != nil {
bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true)
return err, true
}
ptime := time.Since(pstart)
vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true)
return err, true
}
vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation
// Update the metrics touched during block processing and validation
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
// Write the block to the chain and get the status.
var (
wstart = time.Now()
status WriteStatus
)
if !setHead {
// Don't set the head, only insert the block
err = bc.writeBlockWithState(block, receipts, statedb)
} else {
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
}
followupInterrupt.Store(true)
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
if err != nil {
return err, true
}
return it.index, err
}
ptime := time.Since(pstart)
// Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true)
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
blockInsertTimer.UpdateSince(start)
// Report the import stats before returning the various results
stats.processed++
stats.usedGas += usedGas
dirty, _ := bc.triedb.Size()
stats.report(chain, it.index, dirty, setHead)
if !setHead {
// After merge we expect few side chains. Simply count
// all blocks the CL gives us for GC processing time
bc.gcproc += proctime
return nil, true // Direct block insertion of a single block
}
return it.index, err
}
vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation
switch status {
case CanonStatTy:
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
"elapsed", common.PrettyDuration(time.Since(start)),
"root", block.Root())
// Update the metrics touched during block processing and validation
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
storageHashTimer.Update(statedb.StorageHashes) // Storage hashes are complete(in validation)
triehash := statedb.AccountHashes + statedb.StorageHashes // The time spent on tries hashing
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
lastCanon = block
// Write the block to the chain and get the status.
var (
wstart = time.Now()
status WriteStatus
)
if !setHead {
// Don't set the head, only insert the block
err = bc.writeBlockWithState(block, receipts, statedb)
} else {
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
}
followupInterrupt.Store(true)
if err != nil {
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
// Only count canonical blocks for GC processing time
bc.gcproc += proctime
case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
default:
// This in theory is impossible, but lets be nice to our future selves and leave
// a log, instead of trying to track down blocks imports that don't emit logs.
log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
}
return nil, false
}()
if err != nil || stop {
return it.index, err
}
// Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
snapshotCommitTimer.Update(statedb.SnapshotCommits) // Snapshot commits are complete, we can mark them
triedbCommitTimer.Update(statedb.TrieDBCommits) // Trie database commits are complete, we can mark them
blockWriteTimer.Update(time.Since(wstart) - statedb.AccountCommits - statedb.StorageCommits - statedb.SnapshotCommits - statedb.TrieDBCommits)
blockInsertTimer.UpdateSince(start)
// Report the import stats before returning the various results
stats.processed++
stats.usedGas += usedGas
dirty, _ := bc.triedb.Size()
stats.report(chain, it.index, dirty, setHead)
if bc.logger != nil {
bc.logger.OnBlockEnd(nil)
}
if !setHead {
// After merge we expect few side chains. Simply count
// all blocks the CL gives us for GC processing time
bc.gcproc += proctime
return it.index, nil // Direct block insertion of a single block
}
switch status {
case CanonStatTy:
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
"uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
"elapsed", common.PrettyDuration(time.Since(start)),
"root", block.Root())
lastCanon = block
// Only count canonical blocks for GC processing time
bc.gcproc += proctime
case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
default:
// This in theory is impossible, but lets be nice to our future selves and leave
// a log, instead of trying to track down blocks imports that don't emit logs.
log.Warn("Inserted block with unknown status", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
}
}
// Any blocks remaining here? The only ones we care about are the future ones

View file

@ -36,7 +36,6 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
@ -3024,7 +3023,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
})
// Import the canonical chain
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{
Tracer: logger.NewJSONLogger(nil, os.Stdout),
//Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
@ -3101,7 +3100,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
})
// Import the canonical chain
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{
Tracer: logger.NewJSONLogger(nil, os.Stdout),
//Tracer: logger.NewJSONLogger(nil, os.Stdout),
}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
@ -4307,7 +4306,7 @@ func TestEIP3651(t *testing.T) {
b.AddTx(tx)
})
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)}, nil, nil)
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{ /*Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr)*/ }, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}

View file

@ -317,6 +317,7 @@ func NewLevelDBDatabase(file string, cache int, handles int, namespace string, r
if err != nil {
return nil, err
}
log.Info("Using LevelDB as the backing database")
return NewDatabase(db), 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
// execution.
// The following hooks are invoked post execution. I.e. looking up state
// after the hook should reflect the new value.
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type StateLogger interface {

View file

@ -265,7 +265,7 @@ func (st *StateTransition) buyGas() error {
}
if st.evm.Config.Tracer != nil {
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasInitialBalance)
st.evm.Config.Tracer.OnGasChange(0, st.msg.GasLimit, vm.GasChangeTxInitialBalance)
}
st.gasRemaining += st.msg.GasLimit
@ -391,7 +391,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas)
}
if t := st.evm.Config.Tracer; t != nil {
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeIntrinsicGas)
t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, vm.GasChangeTxIntrinsicGas)
}
st.gasRemaining -= gas
@ -458,8 +458,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
refund = st.state.GetRefund()
}
if st.evm.Config.Tracer != nil {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasRefunded)
if st.evm.Config.Tracer != nil && refund > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, st.gasRemaining+refund, vm.GasChangeTxRefunds)
}
st.gasRemaining += refund
@ -468,8 +468,8 @@ func (st *StateTransition) refundGas(refundQuotient uint64) {
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining, state.BalanceChangeGasRefund)
if st.evm.Config.Tracer != nil {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasBuyBack)
if st.evm.Config.Tracer != nil && st.gasRemaining > 0 {
st.evm.Config.Tracer.OnGasChange(st.gasRemaining, 0, vm.GasChangeTxBuyBack)
}
// Also return remaining gas to the block gas counter so it is

View file

@ -174,7 +174,7 @@ func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uin
return nil, 0, ErrOutOfGas
}
if logger != nil {
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangePrecompiledContract)
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, GasChangeCallPrecompiledContract)
}
suppliedGas -= gasCost
output, err := p.Run(input)

View file

@ -189,9 +189,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
} else {
// Handle tracer events for entering and exiting a call frame
evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack)
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
@ -241,7 +241,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution)
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
}
gas = 0
@ -264,9 +264,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack)
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
@ -299,7 +299,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution)
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
}
gas = 0
@ -321,9 +321,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack)
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
@ -348,7 +348,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution)
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
}
gas = 0
@ -365,9 +365,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func(startGas uint64) {
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasBuyBack)
evm.Config.Tracer.OnGasChange(leftOverGas, 0, GasChangeCallLeftOverRefunded)
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
@ -409,7 +409,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution)
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
}
gas = 0
@ -440,9 +440,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}()
} else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
evm.Config.Tracer.OnGasChange(0, gas, GasInitialBalance)
evm.Config.Tracer.OnGasChange(0, gas, GasChangeCallInitialBalance)
defer func() {
evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasBuyBack)
evm.Config.Tracer.OnGasChange(leftoverGas, 0, GasChangeCallLeftOverRefunded)
evm.Config.Tracer.CaptureExit(ret, gas-leftoverGas, err)
}()
}
@ -469,7 +469,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) {
if evm.Config.Tracer != nil {
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeFailedExecution)
evm.Config.Tracer.OnGasChange(gas, 0, GasChangeCallFailedExecution)
}
return nil, common.Address{}, 0, ErrContractAddressCollision
@ -505,7 +505,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// by the error checking condition below.
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCodeStorage) {
if contract.UseGas(createDataGas, evm.Config.Tracer, GasChangeCallCodeStorage) {
evm.StateDB.SetCode(address, ret)
} else {
err = ErrCodeStoreOutOfGas
@ -518,7 +518,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeFailedExecution)
contract.UseGas(contract.Gas, evm.Config.Tracer, GasChangeCallFailedExecution)
}
}

View file

@ -592,7 +592,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// reuse size int for stackvalue
stackvalue := size
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation)
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation)
//TODO: use uint256.Int instead of converting with toBig()
var bigVal = big0
if !value.IsZero() {
@ -614,7 +614,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
scope.Stack.push(&stackvalue)
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas
@ -640,7 +640,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
)
// Apply EIP150
gas -= gas / 64
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeContractCreation2)
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, GasChangeCallContractCreation2)
// reuse size int for stackvalue
stackvalue := size
//TODO: use uint256.Int instead of converting with toBig()
@ -659,7 +659,7 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
scope.Stack.push(&stackvalue)
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas
@ -709,7 +709,7 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
}
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas
@ -749,7 +749,7 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
}
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas
@ -782,7 +782,7 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
}
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas
@ -815,7 +815,7 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverRefunded)
interpreter.evm.Config.Tracer.OnGasChange(scope.Contract.Gas, scope.Contract.Gas+returnGas, GasChangeCallLeftOverReturned)
}
scope.Contract.Gas += returnGas

View file

@ -185,7 +185,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
} else if sLen > operation.maxStack {
return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
}
if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeOpCode) {
if !contract.UseGas(cost, in.evm.Config.Tracer, GasChangeCallOpCode) {
return nil, ErrOutOfGas
}
if operation.dynamicGas != nil {
@ -211,7 +211,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
var dynamicCost uint64
dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
cost += dynamicCost // for tracing
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeOpCode) {
if err != nil || !contract.UseGas(dynamicCost, in.evm.Config.Tracer, GasChangeCallOpCode) {
return nil, ErrOutOfGas
}
// Do tracing before memory expansion

View file

@ -48,47 +48,52 @@ type EVMLogger interface {
// GasChangeReason is used to indicate the reason for a gas change, useful
// for tracing and reporting.
//
// There is essentially two types of gas changes, those that can be emitted once per transaction
// and those that can be emitted on a call basis, so possibly multiple times per transaction.
//
// They can be recognized easily by their name, those that start with `GasChangeTx` are emitted
// once per transaction, while those that start with `GasChangeCall` are emitted on a call basis.
type GasChangeReason byte
const (
GasChangeUnspecified GasChangeReason = iota
// GasInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call
GasInitialBalance
// GasRefunded is the amount of gas that will be refunded to the caller for data returned to the chain
// PR Review: Is that the right description? Called in core/state_transition.go#StateTransition.refundGas
GasRefunded
// GasBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller
GasBuyBack
// GasChangeIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
// GasChangeTxInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
// one such gas change per transaction.
GasChangeTxInitialBalance
// GasChangeTxIntrinsicGas is the amount of gas that will be charged for the intrinsic cost of the transaction, there is
// always exactly one of those per transaction
GasChangeIntrinsicGas
GasChangeTxIntrinsicGas
// GasChangeTxRefunds is the sum of all refunds which happened during the tx execution (e.g. storage slot being cleared)
// this generates an increase in gas. There is only one such gas change per transaction.
GasChangeTxRefunds
// GasChangeTxBuyBack is the amount of gas that will be bought back by the chain and returned in Wei to the caller at the very
// end of the transaction's execution. There is only one such gas change per transaction.
GasChangeTxBuyBack
// PR Review: `GasChangeContractCreation/2` are actually the EIP150 burn cost of CREATE/CREATE2 respectively.
// I think our old name `GasChangeContractCreation/2` is not really accurate. I don't
// think that using EIP150 as a name is a good idea as rules can change in the future. So
// maybe we can just call them `GasChangeCreateBurn/GasChangeCreate2Burn`? Burn might
// feels like the wrong term, `Stipend` came to mind also but I'm unsure.
//
// I would like also to keep the distinction between CREATE and CREATE2 however, it's an
// important thing IMO as they are different and could use different rules in the future.
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules
GasChangeContractCreation
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules
GasChangeContractCreation2
// GasChangeCodeStorage is the amount of gas that will be charged for code storage
GasChangeCodeStorage
// GasChangeOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
// performed can be check by `CaptureState` handling
GasChangeOpCode
// GasChangePrecompiledContract is the amount of gas that will be charged for a precompiled contract execution
GasChangePrecompiledContract
// GasChangeStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules
GasChangeStorageColdAccess
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the caller after the execution of the call, if there is left over at the end of execution
// GasChangeCallInitialBalance is the initial balance for the call which will be equal to the gasLimit of the call. There is only
// one such gas change per call.
GasChangeCallInitialBalance
// GasChangeCallLeftOverReturned is the amount of gas left over that will be returned to the caller, this change will always
// be a negative change as we "drain" left over gas towards 0.
GasChangeCallLeftOverReturned
// GasChangeCallLeftOverRefunded is the amount of gas that will be refunded to the call after the child call execution it
// executed completed. This value is always positive as we are giving gas back to the you, the left over gas of the child.
GasChangeCallLeftOverRefunded
// GasChangeFailedExecution is the burning of the remaining gas when the execution failed without a revert
GasChangeFailedExecution
// GasChangeCallContractCreation is the amount of gas that will be burned for a CREATE, today controlled by EIP150 rules.
GasChangeCallContractCreation
// GasChangeContractCreation is the amount of gas that will be burned for a CREATE2, today controlled by EIP150 rules.
GasChangeCallContractCreation2
// GasChangeCallCodeStorage is the amount of gas that will be charged for code storage.
GasChangeCallCodeStorage
// GasChangeCallOpCode is the amount of gas that will be charged for an opcode executed by the EVM, exact opcode that was
// performed can be check by `CaptureState` handling.
GasChangeCallOpCode
// GasChangeCallPrecompiledContract is the amount of gas that will be charged for a precompiled contract execution.
GasChangeCallPrecompiledContract
// GasChangeCallStorageColdAccess is the amount of gas that will be charged for a cold storage access as controlled by EIP2929 rules.
GasChangeCallStorageColdAccess
// GasChangeCallFailedExecution is the burning of the remaining gas when the execution failed without a revert.
GasChangeCallFailedExecution
)

View file

@ -169,7 +169,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
evm.StateDB.AddAddressToAccessList(addr)
// Charge the remaining difference here already, to correctly calculate available
// gas for call
if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeStorageColdAccess) {
if !contract.UseGas(coldCost, evm.Config.Tracer, GasChangeCallStorageColdAccess) {
return 0, ErrOutOfGas
}
}

View file

@ -32,7 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/params"
@ -330,7 +330,7 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
cfg.State, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
cfg.GasLimit = gas
if len(tracerCode) > 0 {
tracer, err := tracers.DefaultDirectory.New(tracerCode, new(tracers.Context), nil)
tracer, err := directory.DefaultDirectory.New(tracerCode, new(directory.Context), nil)
if err != nil {
b.Fatal(err)
}
@ -826,7 +826,7 @@ func TestRuntimeJSTracer(t *testing.T) {
statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
statedb.SetCode(common.HexToAddress("0xff"), suicideCode)
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil)
if err != nil {
t.Fatal(err)
}
@ -861,7 +861,7 @@ func TestJSTracerCreateTx(t *testing.T) {
code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
tracer, err := tracers.DefaultDirectory.New(jsTracer, new(tracers.Context), nil)
tracer, err := directory.DefaultDirectory.New(jsTracer, new(directory.Context), nil)
if err != nil {
t.Fatal(err)
}

View file

@ -44,7 +44,6 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
@ -194,10 +193,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
Preimages: config.Preimages,
}
)
if config.LiveTrace {
// vmConfig.Tracer = tracers.NewPrinter()
vmConfig.Tracer = tracers.NewFirehoseLogger()
}
// Override the chain config with provided settings.
var overrides core.ChainOverrides
if config.OverrideCancun != nil {

View file

@ -140,9 +140,6 @@ type Config struct {
// Enables tracking of SHA3 preimages in the VM
EnablePreimageRecording bool
// LiveTrace will enable tracing during normal chain processing.
LiveTrace bool
// Miscellaneous options
DocRoot string `toml:"-"`

View file

@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/internal/ethapi"
@ -272,7 +273,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
// Trace all the transactions contained within
for i, tx := range task.block.Transactions() {
msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee())
txctx := &Context{
txctx := &directory.Context{
BlockHash: task.block.Hash(),
BlockNumber: task.block.Number(),
TxIndex: i,
@ -591,7 +592,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
// process that generates states in one thread and traces txes
// in separate worker threads.
if config != nil && config.Tracer != nil && *config.Tracer != "" {
if isJS := DefaultDirectory.IsJS(*config.Tracer); isJS {
if isJS := directory.DefaultDirectory.IsJS(*config.Tracer); isJS {
return api.traceBlockParallel(ctx, block, statedb, config)
}
}
@ -607,7 +608,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
for i, tx := range txs {
// Generate the next state snapshot fast without tracing
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
txctx := &Context{
txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: i,
@ -650,7 +651,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// Fetch and execute the next transaction trace tasks
for task := range jobs {
msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee())
txctx := &Context{
txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: task.index,
@ -859,7 +860,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
return nil, err
}
txctx := &Context{
txctx := &directory.Context{
BlockHash: blockHash,
BlockNumber: block.Number(),
TxIndex: int(index),
@ -927,15 +928,15 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil {
traceConfig = &config.TraceConfig
}
return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig)
return api.traceTx(ctx, tx, msg, new(directory.Context), vmctx, statedb, traceConfig)
}
// traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will
// be tracer dependent.
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *directory.Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) {
var (
tracer Tracer
tracer directory.Tracer
err error
timeout = defaultTraceTimeout
txContext = core.NewEVMTxContext(message)
@ -946,7 +947,7 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
// Default tracer is the struct logger
tracer = logger.NewStructLogger(config.Config)
if config.Tracer != nil {
tracer, err = DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
tracer, err = directory.DefaultDirectory.New(*config.Tracer, txctx, config.TracerConfig)
if err != nil {
return nil, err
}

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
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package tracers
package directory
import (
"encoding/json"

View file

@ -14,13 +14,13 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package tracers is a manager for transaction tracing engines.
package tracers
// Package directory provides functionality to lookup tracers by name.
// It also includes utility functions that are imported by the other
// tracing packages.
package directory
import (
"encoding/json"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
@ -101,27 +101,3 @@ func (d *directory) IsJS(name string) bool {
// JS eval will execute JS code
return true
}
const (
memoryPadLimit = 1024 * 1024
)
// GetMemoryCopyPadded returns offset + size as a new slice.
// It zero-pads the slice if it extends beyond memory bounds.
func GetMemoryCopyPadded(m *vm.Memory, offset, size int64) ([]byte, error) {
if offset < 0 || size < 0 {
return nil, errors.New("offset or size must not be negative")
}
if int(offset+size) < m.Len() { // slice fully inside memory
return m.GetCopy(offset, size), nil
}
paddingNeeded := int(offset+size) - m.Len()
if paddingNeeded > memoryPadLimit {
return nil, fmt.Errorf("reached limit for padding memory slice: %d", paddingNeeded)
}
cpy := make([]byte, size)
if overlap := int64(m.Len()) - offset; overlap > 0 {
copy(cpy, m.GetPtr(offset, overlap))
}
return cpy, nil
}

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

@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/rlp"
pbeth "github.com/streamingfast/firehose-ethereum/types/pb/sf/ethereum/type/v2"
"golang.org/x/exp/maps"
@ -30,6 +31,8 @@ import (
"google.golang.org/protobuf/types/known/timestamppb"
)
var _ core.BlockchainLogger = (*Firehose)(nil)
var firehoseTracerLogLevel = strings.ToLower(os.Getenv("GETH_FIREHOSE_TRACER_LOG_LEVEL"))
var isFirehoseDebugEnabled = firehoseTracerLogLevel == "debug" || firehoseTracerLogLevel == "trace"
var isFirehoseTracerEnabled = firehoseTracerLogLevel == "trace"
@ -41,6 +44,13 @@ var emptyCommonHash = common.Hash{}
func init() {
staticFirehoseChainValidationOnInit()
directory.LiveDirectory.Register("firehose", newFirehoseTracer)
}
func newFirehoseTracer() (core.BlockchainLogger, error) {
firehoseDebug("New firehose tracer")
return NewFirehoseLogger(), nil
}
type Firehose struct {
@ -759,7 +769,7 @@ func (f *Firehose) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
return
}
if reason == vm.GasChangeOpCode {
if reason == vm.GasChangeCallOpCode {
// We ignore those because we track OpCode gas consumption manually by tracking the gas value at `CaptureState` call
return
}
@ -770,7 +780,7 @@ func (f *Firehose) OnGasChange(old, new uint64, reason vm.GasChangeReason) {
// For new chain, this code should be remove so that they are included and useful to user.
//
// Ref eb1916a67d9bea03df16a7a3e2cfac72
if reason == vm.GasInitialBalance || reason == vm.GasRefunded || reason == vm.GasBuyBack {
if reason == vm.GasChangeTxInitialBalance || reason == vm.GasChangeTxRefunds || reason == vm.GasChangeTxBuyBack {
return
}
@ -1111,24 +1121,26 @@ var gasChangeReasonToPb = map[vm.GasChangeReason]pbeth.GasChange_Reason{
//
// New chain should uncomment the code below and remove the same assigments to UNKNOWN
//
// vm.GasInitialBalance: pbeth.GasChange_REASON_INITIAL_BALANCE,
// vm.GasRefunded: pbeth.GasChange_REASON_REFUND,
// vm.GasBuyBack: pbeth.GasChange_REASON_BUYBACK,
vm.GasInitialBalance: pbeth.GasChange_REASON_UNKNOWN,
vm.GasRefunded: pbeth.GasChange_REASON_UNKNOWN,
vm.GasBuyBack: pbeth.GasChange_REASON_UNKNOWN,
// vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_INITIAL_BALANCE,
// vm.GasChangeTxRefunds: pbeth.GasChange_REASON_REFUND,
// vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_BUYBACK,
// vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_BUYBACK,
vm.GasChangeTxInitialBalance: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeTxRefunds: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeTxBuyBack: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeCallInitialBalance: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeIntrinsicGas: pbeth.GasChange_REASON_INTRINSIC_GAS,
vm.GasChangeContractCreation: pbeth.GasChange_REASON_CONTRACT_CREATION,
vm.GasChangeContractCreation2: pbeth.GasChange_REASON_CONTRACT_CREATION2,
vm.GasChangeCodeStorage: pbeth.GasChange_REASON_CODE_STORAGE,
vm.GasChangePrecompiledContract: pbeth.GasChange_REASON_PRECOMPILED_CONTRACT,
vm.GasChangeStorageColdAccess: pbeth.GasChange_REASON_STATE_COLD_ACCESS,
vm.GasChangeCallLeftOverRefunded: pbeth.GasChange_REASON_REFUND_AFTER_EXECUTION,
vm.GasChangeFailedExecution: pbeth.GasChange_REASON_FAILED_EXECUTION,
vm.GasChangeTxIntrinsicGas: pbeth.GasChange_REASON_INTRINSIC_GAS,
vm.GasChangeCallContractCreation: pbeth.GasChange_REASON_CONTRACT_CREATION,
vm.GasChangeCallContractCreation2: pbeth.GasChange_REASON_CONTRACT_CREATION2,
vm.GasChangeCallCodeStorage: pbeth.GasChange_REASON_CODE_STORAGE,
vm.GasChangeCallPrecompiledContract: pbeth.GasChange_REASON_PRECOMPILED_CONTRACT,
vm.GasChangeCallStorageColdAccess: pbeth.GasChange_REASON_STATE_COLD_ACCESS,
vm.GasChangeCallLeftOverRefunded: pbeth.GasChange_REASON_REFUND_AFTER_EXECUTION,
vm.GasChangeCallFailedExecution: pbeth.GasChange_REASON_FAILED_EXECUTION,
// Ignored, we track them manually, newGasChange ensure that we panic if we see Unknown
vm.GasChangeOpCode: pbeth.GasChange_REASON_UNKNOWN,
vm.GasChangeCallOpCode: pbeth.GasChange_REASON_UNKNOWN,
}
func gasChangeReasonFromChain(reason vm.GasChangeReason) pbeth.GasChange_Reason {
@ -1180,6 +1192,10 @@ func gasPrice(tx *types.Transaction, baseFee *big.Int) *pbeth.BigInt {
panic(errUnhandledTransactionType("gasPrice", tx.Type()))
}
func FirehoseDebug(msg string, args ...interface{}) {
firehoseDebug(msg, args...)
}
func firehoseDebug(msg string, args ...interface{}) {
if isFirehoseDebugEnabled {
fmt.Fprintf(os.Stderr, "[Firehose] "+msg+"\n", args...)

View file

@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
@ -141,7 +141,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
}
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
)
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@ -247,7 +247,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil)
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), nil)
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
@ -283,8 +283,8 @@ func TestInternals(t *testing.T) {
BaseFee: new(big.Int),
}
)
mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer {
tr, err := tracers.DefaultDirectory.New(name, nil, cfg)
mkTracer := func(name string, cfg json.RawMessage) directory.Tracer {
tr, err := directory.DefaultDirectory.New(name, nil, cfg)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@ -294,7 +294,7 @@ func TestInternals(t *testing.T) {
for _, tc := range []struct {
name string
code []byte
tracer tracers.Tracer
tracer directory.Tracer
want string
}{
{

View file

@ -16,11 +16,9 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/tests"
// Force-load the native, to trigger registration
"github.com/ethereum/go-ethereum/eth/tracers"
)
// flatCallTrace is the result of a callTracerParity run.
@ -103,7 +101,7 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
// Create the tracer, the EVM environment and run it
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
return fmt.Errorf("failed to create call tracer: %v", err)
}

View file

@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/tests"
)
@ -110,7 +110,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
}
_, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
)
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
tracer, err := directory.DefaultDirectory.New(tracerName, new(directory.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}

View file

@ -24,12 +24,12 @@ import (
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
jsassets "github.com/ethereum/go-ethereum/eth/tracers/js/internal/tracers"
)
@ -42,16 +42,16 @@ func init() {
if err != nil {
panic(err)
}
type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
type ctorFn = func(*directory.Context, json.RawMessage) (directory.Tracer, error)
lookup := func(code string) ctorFn {
return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
return func(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
return newJsTracer(code, ctx, cfg)
}
}
for name, code := range assetTracers {
tracers.DefaultDirectory.Register(name, lookup(code), true)
directory.DefaultDirectory.Register(name, lookup(code), true)
}
tracers.DefaultDirectory.RegisterJSEval(newJsTracer)
directory.DefaultDirectory.RegisterJSEval(newJsTracer)
}
// bigIntProgram is compiled once and the exported function mostly invoked to convert
@ -96,7 +96,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
// jsTracer is an implementation of the Tracer interface which evaluates
// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
type jsTracer struct {
tracers.NoopTracer
directory.NoopTracer
vm *goja.Runtime
env *vm.EVM
@ -136,7 +136,7 @@ type jsTracer struct {
// The methods `result` and `fault` are required to be present.
// The methods `step`, `enter`, and `exit` are optional, but note that
// `enter` and `exit` always go together.
func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
vm := goja.New()
// By default field names are exported to JS as is, i.e. capitalized.
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
@ -145,7 +145,7 @@ func newJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracer
ctx: make(map[string]goja.Value),
}
if ctx == nil {
ctx = new(tracers.Context)
ctx = new(directory.Context)
}
if ctx.BlockHash != (common.Hash{}) {
t.ctx["blockHash"] = vm.ToValue(ctx.BlockHash.Bytes())
@ -576,7 +576,7 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
if end < begin || begin < 0 {
return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
slice, err := tracers.GetMemoryCopyPadded(mo.memory, begin, end-begin)
slice, err := directory.GetMemoryCopyPadded(mo.memory, begin, end-begin)
if err != nil {
return nil, err
}

View file

@ -28,7 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
)
@ -61,7 +61,7 @@ func testCtx() *vmContext {
return &vmContext{blockCtx: vm.BlockContext{BlockNumber: big.NewInt(1)}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
}
func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
func runTrace(tracer directory.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig, contractCode []byte) (json.RawMessage, error) {
var (
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
gasLimit uint64 = 31000
@ -264,14 +264,14 @@ func TestIsPrecompile(t *testing.T) {
func TestEnterExit(t *testing.T) {
// test that either both or none of enter() and exit() are defined
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(tracers.Context), nil); err == nil {
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(directory.Context), nil); err == nil {
t.Fatal("tracer creation should've failed without exit() definition")
}
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(tracers.Context), nil); err != nil {
if _, err := newJsTracer("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(directory.Context), nil); err != nil {
t.Fatal(err)
}
// test that the enter and exit method are correctly invoked and the values passed
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(tracers.Context), nil)
tracer, err := newJsTracer("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(directory.Context), nil)
if err != nil {
t.Fatal(err)
}
@ -293,7 +293,7 @@ func TestEnterExit(t *testing.T) {
func TestSetup(t *testing.T) {
// Test empty config
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(tracers.Context), nil)
_, err := newJsTracer(`{setup: function(cfg) { if (cfg !== "{}") { throw("invalid empty config") } }, fault: function() {}, result: function() {}}`, new(directory.Context), nil)
if err != nil {
t.Error(err)
}
@ -303,12 +303,12 @@ func TestSetup(t *testing.T) {
t.Fatal(err)
}
// Test no setup func
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(tracers.Context), cfg)
_, err = newJsTracer(`{fault: function() {}, result: function() {}}`, new(directory.Context), cfg)
if err != nil {
t.Fatal(err)
}
// Test config value
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(tracers.Context), cfg)
tracer, err := newJsTracer("{config: null, setup: function(cfg) { this.config = JSON.parse(cfg) }, step: function() {}, fault: function() {}, result: function() { return this.config.foo }}", new(directory.Context), cfg)
if err != nil {
t.Fatal(err)
}

View file

@ -1,4 +1,4 @@
package tracers
package live
import (
"encoding/json"
@ -8,14 +8,20 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
func init() {
directory.LiveDirectory.Register("printer", newPrinter)
}
type Printer struct{}
func NewPrinter() *Printer {
return &Printer{}
func newPrinter() (core.BlockchainLogger, error) {
return &Printer{}, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
@ -91,7 +97,7 @@ func (p *Printer) OnGenesisBlock(b *types.Block, alloc core.GenesisAlloc) {
fmt.Printf("OnGenesisBlock: b=%v, allocLength=%d\n", b.NumberU64(), len(alloc))
}
func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int) {
func (p *Printer) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
fmt.Printf("OnBalanceChange: a=%v, prev=%v, new=%v\n", a, prev, new)
}

View file

@ -17,11 +17,10 @@
package logger
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
// accessList is an accumulator for the set of accounts and storage slots an EVM
@ -103,6 +102,7 @@ func (al accessList) accessList() types.AccessList {
// AccessListTracer is a tracer that accumulates touched accounts and storage
// slots into an internal set.
type AccessListTracer struct {
directory.NoopTracer
excl map[common.Address]struct{} // Set of account to exclude from the list
list accessList // Set of accounts and storage slots touched
}
@ -132,9 +132,6 @@ func NewAccessListTracer(acl types.AccessList, from, to common.Address, precompi
}
}
func (a *AccessListTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
// CaptureState captures all opcodes that touch storage or addresses and adds them to the accesslist.
func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
stack := scope.Stack
@ -158,37 +155,6 @@ func (a *AccessListTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint6
}
}
func (*AccessListTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
func (*AccessListTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
func (*AccessListTracer) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {}
func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (*AccessListTracer) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {}
func (*AccessListTracer) CaptureTxEnd(receipt *types.Receipt, err error) {}
func (*AccessListTracer) OnBalanceChange(a common.Address, prev, new *big.Int) {}
func (*AccessListTracer) OnNonceChange(a common.Address, prev, new uint64) {}
func (*AccessListTracer) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
}
func (*AccessListTracer) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
func (*AccessListTracer) OnLog(log *types.Log) {}
func (*AccessListTracer) OnNewAccount(a common.Address) {}
// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {
return a.list.accessList()

View file

@ -28,9 +28,9 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
@ -107,6 +107,7 @@ func (s *StructLog) ErrorString() string {
// a track record of modified storage which is used in reporting snapshots of the
// contract their storage.
type StructLogger struct {
directory.NoopTracer
cfg Config
env *vm.EVM
@ -139,10 +140,6 @@ func (l *StructLogger) Reset() {
l.err = nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
// CaptureState logs a new structured log message and pushes it out to the environment
//
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
@ -211,16 +208,6 @@ func (l *StructLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, s
l.logs = append(l.logs, log)
}
// CaptureFault implements the EVMLogger interface to trace an execution fault
// while running an opcode.
func (l *StructLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
}
// CaptureKeccakPreimage is called during the KECCAK256 opcode.
func (l *StructLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
func (l *StructLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
l.output = output
@ -233,12 +220,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
}
}
func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {
}
func (l *StructLogger) GetResult() (json.RawMessage, error) {
// Tracing aborted
if l.reason != nil {
@ -280,20 +261,6 @@ func (l *StructLogger) CaptureTxEnd(receipt *types.Receipt, err error) {
l.usedGas = receipt.GasUsed
}
func (l *StructLogger) OnBalanceChange(a common.Address, prev, new *big.Int, reason state.BalanceChangeReason) {
}
func (l *StructLogger) OnNonceChange(a common.Address, prev, new uint64) {}
func (l *StructLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
}
func (l *StructLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
func (l *StructLogger) OnLog(log *types.Log) {}
func (l *StructLogger) OnNewAccount(a common.Address) {}
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
@ -351,6 +318,7 @@ func WriteLogs(writer io.Writer, logs []*types.Log) {
}
type mdLogger struct {
directory.NoopTracer
out io.Writer
cfg *Config
env *vm.EVM
@ -408,37 +376,11 @@ func (t *mdLogger) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
func (t *mdLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
func (t *mdLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (*mdLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {}
func (*mdLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
func (*mdLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
func (*mdLogger) OnNonceChange(a common.Address, prev, new uint64) {}
func (*mdLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
}
func (*mdLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
func (*mdLogger) OnLog(log *types.Log) {}
func (*mdLogger) OnNewAccount(a common.Address) {}
// ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value

View file

@ -19,15 +19,16 @@ package logger
import (
"encoding/json"
"io"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
type JSONLogger struct {
directory.NoopTracer
encoder *json.Encoder
cfg *Config
env *vm.EVM
@ -43,9 +44,6 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
return l
}
func (l *JSONLogger) CaptureStart(from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}
func (l *JSONLogger) CaptureFault(pc uint64, op vm.OpCode, gas uint64, cost uint64, scope *vm.ScopeContext, depth int, err error) {
// TODO: Add rData to this interface as well
l.CaptureState(pc, op, gas, cost, scope, nil, depth, err)
@ -78,11 +76,6 @@ func (l *JSONLogger) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
l.encoder.Encode(log)
}
// CaptureKeccakPreimage is called during the KECCAK256 opcode.
func (l *JSONLogger) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
func (l *JSONLogger) OnGasChange(old, new uint64, reason vm.GasChangeReason) {}
// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
type endLog struct {
@ -97,26 +90,6 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error) {
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), errMsg})
}
func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
func (l *JSONLogger) CaptureTxStart(env *vm.EVM, tx *types.Transaction) {
l.env = env
}
func (l *JSONLogger) CaptureTxEnd(receipt *types.Receipt, err error) {}
func (*JSONLogger) OnBalanceChange(a common.Address, prev, new *big.Int) {}
func (*JSONLogger) OnNonceChange(a common.Address, prev, new uint64) {}
func (*JSONLogger) OnCodeChange(a common.Address, prevCodeHash common.Hash, prev []byte, codeHash common.Hash, code []byte) {
}
func (*JSONLogger) OnStorageChange(a common.Address, k, prev, new common.Hash) {}
func (*JSONLogger) OnLog(log *types.Log) {}
func (*JSONLogger) OnNewAccount(a common.Address) {}

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)}
var index common.Hash
logger.CaptureTxStart(env, nil)
logger.CaptureStart(common.Address{}, contract.Address(), false, nil, 0, nil)
_, err := env.Interpreter().Run(contract, []byte{}, false)
if err != nil {

View file

@ -25,11 +25,11 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
func init() {
tracers.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
directory.DefaultDirectory.Register("4byteTracer", newFourByteTracer, false)
}
// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
@ -47,7 +47,7 @@ func init() {
// 0xc281d19e-0: 1
// }
type fourByteTracer struct {
tracers.NoopTracer
directory.NoopTracer
env *vm.EVM
ids map[string]int // ids aggregates the 4byte ids found
interrupt atomic.Bool // Atomic flag to signal execution interruption
@ -57,7 +57,7 @@ type fourByteTracer struct {
// newFourByteTracer returns a native go tracer which collects
// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
func newFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (directory.Tracer, error) {
t := &fourByteTracer{
ids: make(map[string]int),
}

View file

@ -27,13 +27,13 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
func init() {
tracers.DefaultDirectory.Register("callTracer", newCallTracer, false)
directory.DefaultDirectory.Register("callTracer", newCallTracer, false)
}
type callLog struct {
@ -99,7 +99,7 @@ type callFrameMarshaling struct {
}
type callTracer struct {
tracers.NoopTracer
directory.NoopTracer
callstack []callFrame
config callTracerConfig
gasLimit uint64
@ -115,7 +115,7 @@ type callTracerConfig struct {
// newCallTracer returns a native go tracer which tracks
// call frames of a tx, and implements vm.EVMLogger.
func newCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config callTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {

View file

@ -27,14 +27,14 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
//go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go
func init() {
tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
directory.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
}
var parityErrorMapping = map[string]string{
@ -109,12 +109,12 @@ type flatCallResultMarshaling struct {
// flatCallTracer reports call frame information of a tx in a flat format, i.e.
// as opposed to the nested format of `callTracer`.
type flatCallTracer struct {
tracers.NoopTracer
directory.NoopTracer
tracer *callTracer
config flatCallTracerConfig
ctx *tracers.Context // Holds tracer context data
reason error // Textual reason for the interruption
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
ctx *directory.Context // Holds tracer context data
reason error // Textual reason for the interruption
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
}
type flatCallTracerConfig struct {
@ -123,7 +123,7 @@ type flatCallTracerConfig struct {
}
// newFlatCallTracer returns a new flatCallTracer.
func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config flatCallTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
@ -133,7 +133,7 @@ func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Trace
// Create inner call tracer with default configuration, don't forward
// the OnlyTopCall or WithLog to inner for now
tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, nil)
tracer, err := directory.DefaultDirectory.New("callTracer", ctx, nil)
if err != nil {
return nil, err
}
@ -244,7 +244,7 @@ func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
return false
}
func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) {
func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *directory.Context) (output []flatCallFrame, err error) {
var frame *flatCallFrame
switch input.Type {
case vm.CREATE, vm.CREATE2:
@ -343,7 +343,7 @@ func newFlatSelfdestruct(input *callFrame) *flatCallFrame {
}
}
func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) {
func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *directory.Context) {
if ctx == nil {
return
}

View file

@ -24,32 +24,32 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
)
func init() {
tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
directory.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
}
// muxTracer is a go implementation of the Tracer interface which
// runs multiple tracers in one go.
type muxTracer struct {
names []string
tracers []tracers.Tracer
tracers []directory.Tracer
}
// newMuxTracer returns a new mux tracer.
func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config map[string]json.RawMessage
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
return nil, err
}
}
objects := make([]tracers.Tracer, 0, len(config))
objects := make([]directory.Tracer, 0, len(config))
names := make([]string, 0, len(config))
for k, v := range config {
t, err := tracers.DefaultDirectory.New(k, ctx, v)
t, err := directory.DefaultDirectory.New(k, ctx, v)
if err != nil {
return nil, err
}

View file

@ -28,14 +28,14 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/eth/tracers/directory"
"github.com/ethereum/go-ethereum/log"
)
//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
func init() {
tracers.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
directory.DefaultDirectory.Register("prestateTracer", newPrestateTracer, false)
}
type stateMap = map[common.Address]*account
@ -57,7 +57,7 @@ type accountMarshaling struct {
}
type prestateTracer struct {
tracers.NoopTracer
directory.NoopTracer
env *vm.EVM
pre stateMap
post stateMap
@ -74,7 +74,7 @@ type prestateTracerConfig struct {
DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
}
func newPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
func newPrestateTracer(ctx *directory.Context, cfg json.RawMessage) (directory.Tracer, error) {
var config prestateTracerConfig
if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil {
@ -143,7 +143,7 @@ func (t *prestateTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64,
case stackLen >= 4 && op == vm.CREATE2:
offset := stackData[stackLen-2]
size := stackData[stackLen-3]
init, err := tracers.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
init, err := directory.GetMemoryCopyPadded(scope.Memory, int64(offset.Uint64()), int64(size.Uint64()))
if err != nil {
log.Warn("failed to copy CREATE2 input", "err", err, "tracer", "prestateTracer", "offset", offset, "size", size)
return

View file

@ -109,41 +109,3 @@ func BenchmarkTransactionTrace(b *testing.B) {
tracer.Reset()
}
}
func TestMemCopying(t *testing.T) {
for i, tc := range []struct {
memsize int64
offset int64
size int64
wantErr string
wantSize int
}{
{0, 0, 100, "", 100}, // Should pad up to 100
{0, 100, 0, "", 0}, // No need to pad (0 size)
{100, 50, 100, "", 100}, // Should pad 100-150
{100, 50, 5, "", 5}, // Wanted range fully within memory
{100, -50, 0, "offset or size must not be negative", 0}, // Errror
{0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
{10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
} {
mem := vm.NewMemory()
mem.Resize(uint64(tc.memsize))
cpy, err := GetMemoryCopyPadded(mem, tc.offset, tc.size)
if want := tc.wantErr; want != "" {
if err == nil {
t.Fatalf("test %d: want '%v' have no error", i, want)
}
if have := err.Error(); want != have {
t.Fatalf("test %d: want '%v' have '%v'", i, want, have)
}
continue
}
if err != nil {
t.Fatalf("test %d: unexpected error: %v", i, err)
}
if want, have := tc.wantSize, len(cpy); have != want {
t.Fatalf("test %d: want %v have %v", i, want, have)
}
}
}