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 is called after the execution of a transaction ends.
TxEndHook = func(receipt *types.Receipt, err error) 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 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. // ExitHook is invoked when the processing of a message ends.
// `revert` is true when there was an error during the execution. // `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 // 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 // count as a call failure and did not cause a revert of the call. This will
// be indicated by `reverted == false` and `err == ErrCodeStoreOutOfGas`. // 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 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) 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 // VM events
OnTxStart TxStartHook OnTxStart TxStartHook
OnTxEnd TxEndHook OnTxEnd TxEndHook
OnStart StartHook
OnEnd EndHook
OnEnter EnterHook OnEnter EnterHook
OnExit ExitHook OnExit ExitHook
OnOpcode OpcodeHook 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) { 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 // Capture the tracer start/end events in debug mode
if evm.Config.Tracer != nil { 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) { defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, CALL, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, CALL, startGas, leftOverGas, ret, err)
}(gas) }(gas)
} }
// Fail if we're trying to execute above the call depth limit // 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) { 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { 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) { defer func(startGas uint64) {
evm.captureEnd(false, CALLCODE, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, CALLCODE, startGas, leftOverGas, ret, err)
}(gas) }(gas)
} }
// Fail if we're trying to execute above the call depth limit // 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. // that caller is something other than a Contract.
parent := caller.(*Contract) parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call // 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) { defer func(startGas uint64) {
evm.captureEnd(false, DELEGATECALL, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, DELEGATECALL, startGas, leftOverGas, ret, err)
}(gas) }(gas)
} }
// Fail if we're trying to execute above the call depth limit // 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) { 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 // Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil { 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) { defer func(startGas uint64) {
evm.captureEnd(false, STATICCALL, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, STATICCALL, startGas, leftOverGas, ret, err)
}(gas) }(gas)
} }
// Fail if we're trying to execute above the call depth limit // 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. // 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) { 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 { 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) { defer func(startGas uint64) {
evm.captureEnd(evm.depth == 0, typ, startGas, leftOverGas, ret, err) evm.captureEnd(evm.depth, typ, startGas, leftOverGas, ret, err)
}(gas) }(gas)
} }
// Depth check execution. Fail if we're trying to execute above the // 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 // ChainConfig returns the environment's chain configuration
func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } 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 tracer := evm.Config.Tracer
if tracer.OnEnter != nil {
if isRoot { tracer.OnEnter(depth, tracing.OpCode(typ), from, to, input, startGas, value)
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.OnGasChange != nil { if tracer.OnGasChange != nil {
tracer.OnGasChange(0, startGas, tracing.GasChangeCallInitialBalance) 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 tracer := evm.Config.Tracer
if leftOverGas != 0 && tracer.OnGasChange != nil { if leftOverGas != 0 && tracer.OnGasChange != nil {
tracer.OnGasChange(leftOverGas, 0, tracing.GasChangeCallLeftOverReturned) 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) { if !evm.chainRules.IsHomestead && errors.Is(err, ErrCodeStoreOutOfGas) {
reverted = false reverted = false
} }
if isRoot { if tracer.OnExit != nil {
if tracer.OnEnd != nil { tracer.OnExit(depth, ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
tracer.OnEnd(ret, startGas-leftOverGas, VMErrorFromErr(err), reverted)
}
} else if tracer.OnExit != nil {
tracer.OnExit(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()) interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != 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 { if tracer.OnExit != nil {
tracer.OnExit([]byte{}, 0, nil, false) tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
} }
} }
return nil, errStopToken return nil, errStopToken
@ -857,10 +857,10 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address()) interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil { if tracer := interpreter.evm.Config.Tracer; tracer != nil {
if tracer.OnEnter != 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 { if tracer.OnExit != nil {
tracer.OnExit([]byte{}, 0, nil, false) tracer.OnExit(interpreter.evm.depth, []byte{}, 0, nil, false)
} }
} }
return nil, errStopToken return nil, errStopToken

View file

@ -40,8 +40,6 @@ func newNoopTracer(ctx *Context, _ json.RawMessage) (*Tracer, error) {
Hooks: &tracing.Hooks{ Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd, OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter, OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit, OnExit: t.CaptureExit,
OnOpcode: t.CaptureState, 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) {} func (t *NoopTracer) OnGasChange(old, new uint64, reason tracing.GasChangeReason) {}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). // 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 // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // 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) { 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{ Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd, OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter, OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit, OnExit: t.CaptureExit,
OnOpcode: t.CaptureState, 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. // 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) { cancel := func(err error) {
t.err = err t.err = err
t.env.VM.Cancel() 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. // 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 { if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error()) 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). // 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 { if !t.traceFrame {
return 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 // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // 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 { if !t.traceFrame {
return 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.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) 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 // Rest gas assumes no refund
tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil) tracer.OnTxEnd(&types.Receipt{GasUsed: gasLimit - contract.Gas}, nil)
if err != 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}) 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.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) tracer.OnOpcode(0, 0, 0, 0, scope, nil, 0, nil)
timeout := errors.New("stahp") timeout := errors.New("stahp")
tracer.Stop(timeout) 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}) 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.OnTxStart(env.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{})
tracer.OnStart(common.Address{}, common.Address{}, false, []byte{}, 1000, big.NewInt(0)) tracer.OnEnter(0, tracing.OpCode(vm.CALL), common.Address{}, common.Address{}, []byte{}, 1000, big.NewInt(0))
tracer.OnEnd(nil, 0, nil, false) tracer.OnExit(0, nil, 0, nil, false)
ret, err := tracer.GetResult() ret, err := tracer.GetResult()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -280,8 +280,8 @@ func TestEnterExit(t *testing.T) {
scope := &vm.ScopeContext{ scope := &vm.ScopeContext{
Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0), 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.OnEnter(1, tracing.OpCode(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.OnExit([]byte{}, 400, nil, false) tracer.OnExit(1, []byte{}, 400, nil, false)
have, err := tracer.GetResult() have, err := tracer.GetResult()
if err != nil { if err != nil {

View file

@ -26,8 +26,6 @@ func newNoopTracer(_ json.RawMessage) (*tracing.Hooks, error) {
return &tracing.Hooks{ return &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd, OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter, OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit, OnExit: t.CaptureExit,
OnOpcode: t.CaptureState, 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) {} func (t *noop) CaptureKeccakPreimage(hash common.Hash, data []byte) {}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct). // 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 // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // 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) { 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{ return &tracing.Hooks{
OnTxStart: l.CaptureTxStart, OnTxStart: l.CaptureTxStart,
OnTxEnd: l.CaptureTxEnd, OnTxEnd: l.CaptureTxEnd,
OnEnd: l.CaptureEnd, OnExit: l.CaptureExit,
OnOpcode: l.CaptureState, OnOpcode: l.CaptureState,
} }
} }
@ -227,8 +227,11 @@ func (l *StructLogger) CaptureState(pc uint64, opcode tracing.OpCode, gas, cost
l.logs = append(l.logs, log) l.logs = append(l.logs, log)
} }
// CaptureEnd is called after the call finishes to finalize the tracing. // CaptureExit is called a call frame finishes processing.
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, err error, reverted bool) { func (l *StructLogger) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
if depth != 0 {
return
}
l.output = output l.output = output
l.err = err l.err = err
if l.cfg.Debug { if l.cfg.Debug {
@ -356,10 +359,10 @@ func NewMarkdownLogger(cfg *Config, writer io.Writer) *mdLogger {
func (t *mdLogger) Hooks() *tracing.Hooks { func (t *mdLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{ return &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnStart: t.CaptureStart, OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit,
OnOpcode: t.CaptureState, OnOpcode: t.CaptureState,
OnFault: t.CaptureFault, OnFault: t.CaptureFault,
OnEnd: t.CaptureEnd,
} }
} }
@ -367,7 +370,11 @@ func (t *mdLogger) CaptureTxStart(env *tracing.VMContext, tx *types.Transaction,
t.env = env 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 { if !create {
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n", fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `%#x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(), 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. // 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) { func (t *mdLogger) CaptureState(pc uint64, op tracing.OpCode, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
stack := scope.StackData() 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) 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 // ExecutionResult groups all structured logs emitted by the EVM
// while replaying a transaction in debug mode as well as transaction // while replaying a transaction in debug mode as well as transaction
// execution status, the amount of gas used and the return value // execution status, the amount of gas used and the return value

View file

@ -48,7 +48,7 @@ func NewJSONLogger(cfg *Config, writer io.Writer) *JSONLogger {
func (l *JSONLogger) Hooks() *tracing.Hooks { func (l *JSONLogger) Hooks() *tracing.Hooks {
return &tracing.Hooks{ return &tracing.Hooks{
OnTxStart: l.CaptureTxStart, OnTxStart: l.CaptureTxStart,
OnEnd: l.CaptureEnd, OnExit: l.CaptureExit,
OnOpcode: l.CaptureState, OnOpcode: l.CaptureState,
OnFault: l.CaptureFault, 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. // 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 { type endLog struct {
Output string `json:"output"` Output string `json:"output"`
GasUsed math.HexOrDecimal64 `json:"gasUsed"` GasUsed math.HexOrDecimal64 `json:"gasUsed"`

View file

@ -64,7 +64,6 @@ func newFourByteTracer(ctx *directory.Context, _ json.RawMessage) (*directory.Tr
return &directory.Tracer{ return &directory.Tracer{
Hooks: &tracing.Hooks{ Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnStart: t.CaptureStart,
OnEnter: t.CaptureEnter, OnEnter: t.CaptureEnter,
}, },
GetResult: t.GetResult, GetResult: t.GetResult,
@ -94,16 +93,8 @@ func (t *fourByteTracer) CaptureTxStart(env *tracing.VMContext, tx *types.Transa
t.activePrecompiles = vm.ActivePrecompiles(rules) 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). // 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 // Skip if tracing was interrupted
if t.interrupt.Load() { if t.interrupt.Load() {
return return

View file

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

View file

@ -144,8 +144,6 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.
Hooks: &tracing.Hooks{ Hooks: &tracing.Hooks{
OnTxStart: ft.CaptureTxStart, OnTxStart: ft.CaptureTxStart,
OnTxEnd: ft.CaptureTxEnd, OnTxEnd: ft.CaptureTxEnd,
OnStart: ft.CaptureStart,
OnEnd: ft.CaptureEnd,
OnEnter: ft.CaptureEnter, OnEnter: ft.CaptureEnter,
OnExit: ft.CaptureExit, OnExit: ft.CaptureExit,
OnOpcode: ft.CaptureState, OnOpcode: ft.CaptureState,
@ -156,16 +154,6 @@ func newFlatCallTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.
}, nil }, 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. // 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) { 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) 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). // 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) { 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(typ, from, to, input, gas, value) 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. // Child calls must have a value, even if it's zero.
// Practically speaking, only STATICCALL has nil value. Set it to 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 { 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 // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // execute any code.
func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error, reverted bool) { func (t *flatCallTracer) CaptureExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
t.tracer.CaptureExit(output, gasUsed, err, reverted) t.tracer.CaptureExit(depth, output, gasUsed, err, reverted)
if depth == 0 {
return
}
// Parity traces don't include CALL/STATICCALLs to precompiles. // Parity traces don't include CALL/STATICCALLs to precompiles.
// By default we remove them from the callstack. // By default we remove them from the callstack.
if t.config.IncludePrecompiles { if t.config.IncludePrecompiles {

View file

@ -61,8 +61,6 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace
Hooks: &tracing.Hooks{ Hooks: &tracing.Hooks{
OnTxStart: t.CaptureTxStart, OnTxStart: t.CaptureTxStart,
OnTxEnd: t.CaptureTxEnd, OnTxEnd: t.CaptureTxEnd,
OnStart: t.CaptureStart,
OnEnd: t.CaptureEnd,
OnEnter: t.CaptureEnter, OnEnter: t.CaptureEnter,
OnExit: t.CaptureExit, OnExit: t.CaptureExit,
OnOpcode: t.CaptureState, OnOpcode: t.CaptureState,
@ -80,24 +78,6 @@ func newMuxTracer(ctx *directory.Context, cfg json.RawMessage) (*directory.Trace
}, nil }, 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. // 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) { 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 { 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). // 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 { for _, t := range t.tracers {
if t.OnEnter != nil { 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 // CaptureExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // 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 { for _, t := range t.tracers {
if t.OnExit != nil { if t.OnExit != nil {
t.OnExit(output, gasUsed, err, reverted) t.OnExit(depth, output, gasUsed, err, reverted)
} }
} }
} }