rm OnStart and onEnd

This commit is contained in:
Sina Mahmoodi 2024-03-06 15:49:49 +01:00
parent 923c180058
commit 37ce159500
13 changed files with 109 additions and 161 deletions

View file

@ -91,15 +91,8 @@ type (
// TxEndHook is called after the execution of a transaction ends.
TxEndHook = func(receipt *types.Receipt, err error)
// StartHook is invoked when the processing of the root call starts.
StartHook = func(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
// EndHook is invoked when the processing of the top call ends.
// See docs for `ExitHook` for info on the `reverted` parameter.
EndHook = func(output []byte, gasUsed uint64, err error, reverted bool)
// EnterHook is invoked when the processing of a message starts.
EnterHook = func(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
EnterHook = func(depth int, typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
// ExitHook is invoked when the processing of a message ends.
// `revert` is true when there was an error during the execution.
@ -107,7 +100,7 @@ type (
// ran out of gas when attempting to persist the code to database did not
// count as a call failure and did not cause a revert of the call. This will
// be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`.
ExitHook = func(output []byte, gasUsed uint64, err error, reverted bool)
ExitHook = func(depth int, output []byte, gasUsed uint64, err error, reverted bool)
// OpcodeHook is invoked just prior to the execution of an opcode.
OpcodeHook = func(pc uint64, op OpCode, gas, cost uint64, scope OpContext, rData []byte, depth int, err error)
@ -167,8 +160,6 @@ type Hooks struct {
// VM events
OnTxStart TxStartHook
OnTxEnd TxEndHook
OnStart StartHook
OnEnd EndHook
OnEnter EnterHook
OnExit ExitHook
OnOpcode OpcodeHook

View file

@ -181,9 +181,9 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth == 0, CALL, caller.Address(), addr, input, gas, value.ToBig())
evm.captureBegin(evm.depth, CALL, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err)
evm.captureEnd(evm.depth, CALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -253,9 +253,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(false, CALLCODE, caller.Address(), addr, input, gas, value.ToBig())
evm.captureBegin(evm.depth, CALLCODE, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err)
evm.captureEnd(evm.depth, CALLCODE, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -308,9 +308,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// that caller is something other than a Contract.
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
evm.captureBegin(false, DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig())
evm.captureBegin(evm.depth, DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err)
evm.captureEnd(evm.depth, DELEGATECALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -349,9 +349,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
evm.captureBegin(false, STATICCALL, caller.Address(), addr, input, gas, nil)
evm.captureBegin(evm.depth, STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err)
evm.captureEnd(evm.depth, STATICCALL, startGas, leftOverGas, ret, err)
}(gas)
}
// Fail if we're trying to execute above the call depth limit
@ -416,9 +416,9 @@ func (c *codeAndHash) Hash() common.Hash {
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) {
if evm.Config.Tracer != nil {
evm.captureBegin(evm.depth == 0, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err)
evm.captureEnd(evm.depth, typ, startGas, leftOverGas, ret, err)
}(gas)
}
// Depth check execution. Fail if we're trying to execute above the
@ -518,25 +518,18 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
// ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
func (evm *EVM) captureBegin(isRoot bool, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) {
tracer := evm.Config.Tracer
if isRoot {
if tracer.OnStart != nil {
tracer.OnStart(from, to, typ == CREATE || typ == CREATE2, input, startGas, value)
}
} else if tracer.OnEnter != nil {
tracer.OnEnter(tracing.OpCode(typ), from, to, input, startGas, value)
if tracer.OnEnter != nil {
tracer.OnEnter(depth, tracing.OpCode(typ), from, to, input, startGas, value)
}
if tracer.OnGasChange != nil {
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance)
}
}
func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
func (evm *EVM) captureEnd(depth int, typ OpCode, startGas uint64, leftOverGas uint64, ret []byte, err error) {
tracer := evm.Config.Tracer
if leftOverGas != 0 && tracer.OnGasChange != nil {
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned)
}
@ -547,12 +540,8 @@ func (evm *EVM) captureEnd(isRoot bool, typ OpCode, startGas uint64, leftOverGas
if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) {
reverted = false
}
if isRoot {
if tracer.OnEnd != nil {
tracer.OnEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
}
} else if tracer.OnExit != nil {
tracer.OnExit(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
if tracer.OnExit != nil {
tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
}
}

View file

@ -837,10 +837,10 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {
tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.OnEnter(interpreter.evm.depth, tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if tracer.OnExit != nil {
tracer.OnExit([]byte{}, 0, nil, false)
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
}
}
return nil, errStopToken
@ -857,10 +857,10 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != nil {
tracer.OnEnter(tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.OnEnter(interpreter.evm.depth, tracing.OpCode(SELFDESTRUCT), scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
}
if tracer.OnExit != nil {
tracer.OnExit([]byte{}, 0, nil, false)
tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
}
}
return nil, errStopToken

View file

@ -40,8 +40,6 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) {
Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
@ -82,12 +80,12 @@ func (t *NoopTracer) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *NoopTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (t *NoopTracer) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *NoopTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
func (t *NoopTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
}
func (*NoopTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {

View file

@ -226,8 +226,6 @@ func newJsTracer(code string, ctx *directory.Context, cfg json.RawMessage) (*dir
Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
@ -273,7 +271,7 @@ func (t *jsTracer) CaptureTxEnd(receipt *types.Receipt, err error) {
}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (t *jsTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (t *jsTracer) captureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
cancel := func(err error) {
t.err = err
t.env.VM.Cancel()
@ -347,7 +345,7 @@ func (t *jsTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64,
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
func (t *jsTracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
@ -360,7 +358,11 @@ func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *jsTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (t *jsTracer) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if depth == 0 {
t.captureStart(from, to, vm.OpCode(typ) == vm.CREATE, input, gas, value)
return
}
if !t.traceFrame {
return
}
@ -385,7 +387,11 @@ func (t *jsTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to comm
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *jsTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
func (t *jsTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth == 0 {
t.captureEnd(output, gasUsed, err, reverted)
return
}
if !t.traceFrame {
return
}

View file

@ -77,9 +77,9 @@ func runTrace(tracer *directory.Tracer, vmctx *vmContext, chaincfg *params.Chain
}
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{Gas: gasLimit}), contract.Caller())
tracer.OnStart(contract.Caller(), contract.Address(), false, []byte{}, startGas, value.ToBig())
tracer.OnEnter(0, tracing.OpCode(vm.CALL), contract.Caller(), contract.Address(), []byte{}, startGas, value.ToBig())
ret, err := env.Interpreter().Run(contract, []byte{}, false)
tracer.OnEnd(ret, startGas-contract.Gas, err, true)
tracer.OnExit(0, ret, startGas-contract.Gas, err, true)
// Rest gas assumes no refund
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
if err != nil {
@ -188,7 +188,7 @@ func TestHaltBetweenSteps(t *testing.T) {
}
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
tracer.OnEnter(0, tracing.OpCode(vm.CALL), common.Address{}, common.Address{}, []byte{}, 0, big.NewInt(0))
tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp")
tracer.Stop(timeout)
@ -210,8 +210,8 @@ func TestNoStepExec(t *testing.T) {
}
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(100)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer.Hooks})
tracer.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0))
tracer.OnEnd(nil, 0, nil, false)
tracer.OnEnter(0, tracing.OpCode(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
tracer.OnExit(0, nil, 0, nil, false)
ret, err := tracer.GetResult()
if err != nil {
t.Fatal(err)
@ -280,8 +280,8 @@ func TestEnterExit(t *testing.T) {
scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
}
tracer.OnEnter(tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit([]byte{}, 400, nil, false)
tracer.OnEnter(1, tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit(1, []byte{}, 400, nil, false)
have, err := tracer.GetResult()
if err != nil {

View file

@ -26,8 +26,6 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
return &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
@ -67,12 +65,12 @@ func (t *noop) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64, _ tr
func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *noop) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (t *noop) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *noop) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
func (t *noop) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
}
func (t *noop) CaptureTxStart(vm *tracing.VMContext, tx *types.Transaction, from common.Address) {

View file

@ -138,7 +138,7 @@ func (l *StructLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{
OnTxStart: l.CaptureTxStart,
OnTxEnd: l.CaptureTxEnd,
OnEnd: l.CaptureEnd,
OnExit: l.CaptureExit,
OnOpcode: l.CaptureState,
}
}
@ -227,8 +227,11 @@ func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost
l.logs = append(l.logs, log)
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
// CaptureExit is called a call frame finishes processing.
func (l *StructLogger) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth != 0 {
return
}
l.output = output
l.err = err
if l.cfg.Debug {
@ -356,10 +359,10 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
func (t *mdLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnStart: t.CaptureStart,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
OnFault: t.CaptureFault,
OnEnd: t.CaptureEnd,
}
}
@ -367,7 +370,11 @@ func (t *mdLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction,
t.env = env
}
func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (t *mdLogger) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if depth != 0 {
return
}
create := vm.OpCode(typ) == vm.CREATE
if !create {
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
@ -384,6 +391,13 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
`)
}
func (t *mdLogger) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth == 0 {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
}
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
stack := scope.StackData()
@ -409,11 +423,6 @@ func (t *mdLogger) CaptureFault(pc uint64, op tracing.OpCode, gas, cost uint64,
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
}
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
fmt.Fprintf(t.out, "\nOutput: `%#x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
// 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

@ -48,7 +48,7 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
func (l *JSONLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{
OnTxStart: l.CaptureTxStart,
OnEnd: l.CaptureEnd,
OnExit: l.CaptureExit,
OnOpcode: l.CaptureState,
OnFault: l.CaptureFault,
}
@ -87,7 +87,10 @@ func (l *JSONLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64
}
// CaptureEnd is triggered at end of execution.
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
func (l *JSONLogger) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth > 0 {
return
}
type endLog struct {
Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"`

View file

@ -64,7 +64,6 @@ func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tr
return &directory.Tracer{
Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnStart: t.CaptureStart,
OnEnter: t.CaptureEnter,
},
GetResult: t.GetResult,
@ -94,16 +93,8 @@ func (t *fourByteTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transa
t.activePrecompiles = vm.ActivePrecompiles(rules)
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *fourByteTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
// Save the outer calldata
if len(input) >= 4 {
t.store(input[0:4], len(input)-4)
}
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *fourByteTracer) CaptureEnter(opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (t *fourByteTracer) CaptureEnter(depth int, opcode tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
// Skip if tracing was interrupted
if t.interrupt.Load() {
return

View file

@ -130,11 +130,8 @@ func newCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trac
Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
OnLog: t.OnLog,
},
GetResult: t.GetResult,
@ -151,38 +148,13 @@ func newCallTracerObject(ctx *directory.Context, cfg json.RawMessage) (*callTrac
}
// First callframe contains tx context info
// and is populated on start and end.
return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *callTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
toCopy := to
t.callstack[0] = callFrame{
Type: vm.CALL,
From: from,
To: &toCopy,
Input: common.CopyBytes(input),
Gas: t.gasLimit,
Value: value,
}
if create {
t.callstack[0].Type = vm.CREATE
}
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
t.callstack[0].processOutput(output, err, reverted)
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
func (t *callTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
return &callTracer{callstack: make([]callFrame, 0, 1), config: config}, nil
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *callTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
t.depth++
if t.config.OnlyTopCall {
func (t *callTracer) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
t.depth = depth
if t.config.OnlyTopCall && depth > 0 {
return
}
// Skip if tracing was interrupted
@ -199,30 +171,47 @@ func (t *callTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to co
Gas: gas,
Value: value,
}
if depth == 0 {
call.Gas = t.gasLimit
}
t.callstack = append(t.callstack, call)
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
t.depth--
func (t *callTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth == 0 {
t.captureEnd(output, gasUsed, err, reverted)
return
}
t.depth = depth - 1
if t.config.OnlyTopCall {
return
}
size := len(t.callstack)
if size <= 1 {
return
}
// pop call
// Pop call.
call := t.callstack[size-1]
t.callstack = t.callstack[:size-1]
size -= 1
call.GasUsed = gasUsed
call.processOutput(output, err, reverted)
// Nest call into parent.
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
}
func (t *callTracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
if len(t.callstack) != 1 {
return
}
t.callstack[0].processOutput(output, err, reverted)
}
func (t *callTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
t.gasLimit = tx.Gas()
}

View file

@ -144,8 +144,6 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.
Hooks: &tracing.Hooks{
OnTxStart: ft.CaptureTxStart,
OnTxEnd: ft.CaptureTxEnd,
OnStart: ft.CaptureStart,
OnEnd: ft.CaptureEnd,
OnEnter: ft.CaptureEnter,
OnExit: ft.CaptureExit,
OnOpcode: ft.CaptureState,
@ -156,16 +154,6 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.
}, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *flatCallTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
t.tracer.CaptureStart(from, to, create, input, gas, value)
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
t.tracer.CaptureEnd(output, gasUsed, err, reverted)
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
func (t *flatCallTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
@ -177,9 +165,12 @@ func (t *flatCallTracer) CaptureFault(pc uint64, op tracing.OpCode, gas, cost ui
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *flatCallTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
t.tracer.CaptureEnter(typ, from, to, input, gas, value)
func (t *flatCallTracer) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
t.tracer.CaptureEnter(depth, typ, from, to, input, gas, value)
if depth == 0 {
return
}
// Child calls must have a value, even if it's zero.
// Practically speaking, only STATICCALL has nil value. Set it to zero.
if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
@ -189,9 +180,12 @@ func (t *flatCallTracer) CaptureEnter(typ tracing.OpCode, from common.Address, t
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
t.tracer.CaptureExit(output, gasUsed, err, reverted)
func (t *flatCallTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
t.tracer.CaptureExit(depth, output, gasUsed, err, reverted)
if depth == 0 {
return
}
// Parity traces don't include CALL/STATICCALLs to precompiles.
// By default we remove them from the callstack.
if t.config.IncludePrecompiles {

View file

@ -61,8 +61,6 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace
Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState,
@ -80,24 +78,6 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace
}, nil
}
// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
func (t *muxTracer) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
for _, t := range t.tracers {
if t.OnStart != nil {
t.OnStart(from, to, create, input, gas, value)
}
}
}
// CaptureEnd is called after the call finishes to finalize the tracing.
func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) {
for _, t := range t.tracers {
if t.OnEnd != nil {
t.OnEnd(output, gasUsed, err, reverted)
}
}
}
// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
func (t *muxTracer) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
for _, t := range t.tracers {
@ -135,20 +115,20 @@ func (t *muxTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason)
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *muxTracer) CaptureEnter(typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
func (t *muxTracer) CaptureEnter(depth int, typ tracing.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
for _, t := range t.tracers {
if t.OnEnter != nil {
t.OnEnter(typ, from, to, input, gas, value)
t.OnEnter(depth, typ, from, to, input, gas, value)
}
}
}
// CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code.
func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) {
func (t *muxTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
for _, t := range t.tracers {
if t.OnExit != nil {
t.OnExit(output, gasUsed, err, reverted)
t.OnExit(depth, output, gasUsed, err, reverted)
}
}
}