Skip to content

Commit

Permalink
add call tracer function
Browse files Browse the repository at this point in the history
  • Loading branch information
laizy committed Dec 11, 2023
1 parent 12ac120 commit fe94594
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 21 deletions.
77 changes: 61 additions & 16 deletions vm/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,15 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas

if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
// Calling a non-existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
} else {
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
evm.vmConfig.Tracer.CaptureExit(ret, 0, nil)
}
}
return nil, gas, nil
}
Expand All @@ -213,11 +218,19 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)

// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
if evm.vmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
} else {
// Handle tracer events for entering and exiting a call frame
evm.vmConfig.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}
}

if isPrecompile {
Expand Down Expand Up @@ -276,6 +289,14 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
}
var snapshot = evm.StateDB.Snapshot()

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
Expand Down Expand Up @@ -314,6 +335,14 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
}
var snapshot = evm.StateDB.Snapshot()

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
Expand Down Expand Up @@ -361,6 +390,14 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// future scenarios
evm.StateDB.AddBalance(addr, big0)

// Invoke tracer hooks that signal entering/exiting a call frame
if evm.vmConfig.Debug {
evm.vmConfig.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
defer func(startGas uint64) {
evm.vmConfig.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
}

if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
Expand Down Expand Up @@ -402,7 +439,7 @@ 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 *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
Expand Down Expand Up @@ -435,8 +472,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
return nil, address, gas, nil
}

if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
if evm.vmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
evm.vmConfig.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}
start := time.Now()

Expand Down Expand Up @@ -472,8 +513,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if maxCodeSizeExceeded && err == nil {
err = errors.ErrMaxCodeSizeExceeded
}
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
if evm.vmConfig.Debug {
if evm.depth == 0 {
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
} else {
evm.vmConfig.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
return ret, address, contract.Gas, err

Expand All @@ -482,7 +527,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}

// Create2 creates a new contract using code as deployment code.
Expand All @@ -492,7 +537,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}

// ChainConfig returns the environment's chain configuration
Expand Down
4 changes: 4 additions & 0 deletions vm/evm/instructions.go
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,10 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
MakeOngTransferLog(interpreter.evm.StateDB, callContext.contract.Address(), beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.Suicide(callContext.contract.Address())
if interpreter.cfg.Debug {
interpreter.cfg.Tracer.CaptureEnter(SELFDESTRUCT, callContext.contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
interpreter.cfg.Tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, nil
}

Expand Down
18 changes: 15 additions & 3 deletions vm/evm/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,11 @@ func (s *StructLog) ErrorString() string {
// Note that reference types are actual VM data structures; make copies
// if you need to retain them beyond the current call.
type Tracer interface {
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack,
rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error)
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
CaptureExit(output []byte, gasUsed uint64, err error)
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack,
rStack *ReturnStack, contract *Contract, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
Expand Down Expand Up @@ -144,7 +146,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
}

// CaptureStart implements the Tracer 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) {
func (l *StructLogger) CaptureStart(env *EVM, 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
Expand Down Expand Up @@ -229,6 +231,11 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
}
}

func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}

func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}

// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }

Expand Down Expand Up @@ -306,7 +313,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
return l
}

func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
if !create {
_, _ = fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
from.String(), to.String(),
Expand Down Expand Up @@ -361,3 +368,8 @@ func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, e
_, _ = fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}

func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}

func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
7 changes: 6 additions & 1 deletion vm/evm/logger_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
return l
}

func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte,
func (l *JSONLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte,
gas uint64, value *big.Int) {
}

Expand Down Expand Up @@ -99,3 +99,8 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
_ = l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
return
}

func (l *JSONLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}

func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
7 changes: 6 additions & 1 deletion vm/evm/runtime/runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ type stepCounter struct {
steps int
}

func (s *stepCounter) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
func (s *stepCounter) CaptureStart(env *evm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
}

func (s *stepCounter) CaptureState(env *evm.EVM, pc uint64, op evm.OpCode, gas, cost uint64,
Expand All @@ -354,6 +354,11 @@ func (s *stepCounter) CaptureFault(env *evm.EVM, pc uint64, op evm.OpCode, gas,
func (s *stepCounter) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
}

func (s *stepCounter) CaptureEnter(typ evm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
}

func (s *stepCounter) CaptureExit(output []byte, gasUsed uint64, err error) {}

func TestJumpSub1024Limit(t *testing.T) {
db := storage.NewCacheDB(overlaydb.NewOverlayDB(leveldbstore.NewMemLevelDBStore()))
statedb := storage.NewStateDB(db, common.Hash{}, common.Hash{}, ong.OngBalanceHandle{})
Expand Down
Loading

0 comments on commit fe94594

Please sign in to comment.