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,18 +1774,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// Process block using the parent state as reference point
pstart := time.Now()
// 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)
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
}
return it.index, err
return err, true
}
ptime := time.Since(pstart)
@ -1793,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 {
bc.reportBlock(block, receipts, err)
followupInterrupt.Store(true)
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
}
return it.index, err
return err, true
}
vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation
@ -1830,12 +1829,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
}
followupInterrupt.Store(true)
if err != nil {
if bc.logger != nil {
bc.logger.OnBlockEnd(err)
return err, true
}
return it.index, err
}
// Update the metrics touched during block commit
accountCommitTimer.Update(statedb.AccountCommits) // Account commits are complete, we can mark them
storageCommitTimer.Update(statedb.StorageCommits) // Storage commits are complete, we can mark them
@ -1852,16 +1847,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
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
return nil, true // Direct block insertion of a single block
}
switch status {
case CanonStatTy:
@ -1889,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()),
"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

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.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.GasChangeFailedExecution: pbeth.GasChange_REASON_FAILED_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,10 +109,10 @@ 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
ctx *directory.Context // Holds tracer context data
reason error // Textual reason for the interruption
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
}
@ -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)
}
}
}