PR review fixes

- Removing unused code
- Adding test cases
This commit is contained in:
shahafn 2025-02-03 11:51:25 +02:00
parent d0755a687e
commit 9d5a50277c
4 changed files with 742 additions and 66 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Copyright 2021 The go-ethereum Authors // Copyright 2025 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
@ -20,8 +20,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"math/big" "math/big"
"runtime"
"runtime/debug"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
@ -31,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256" "github.com/holiman/uint256"
) )
@ -48,19 +45,17 @@ type contractSizeWithOpcode struct {
} }
type callFrameWithOpcodes struct { type callFrameWithOpcodes struct {
Type vm.OpCode `json:"-"` Type vm.OpCode `json:"-"`
From common.Address `json:"from"` From common.Address `json:"from"`
Gas uint64 `json:"gas"` Gas uint64 `json:"gas"`
GasUsed uint64 `json:"gasUsed"` GasUsed uint64 `json:"gasUsed"`
To *common.Address `json:"to,omitempty" rlp:"optional"` To *common.Address `json:"to,omitempty" rlp:"optional"`
Input []byte `json:"input" rlp:"optional"` Input []byte `json:"input" rlp:"optional"`
Output []byte `json:"output,omitempty" rlp:"optional"` Output []byte `json:"output,omitempty" rlp:"optional"`
Error string `json:"error,omitempty" rlp:"optional"` Error string `json:"error,omitempty" rlp:"optional"`
RevertReason string `json:"revertReason,omitempty"` RevertReason string `json:"revertReason,omitempty"`
Logs []callLog `json:"logs,omitempty" rlp:"optional"` Logs []callLog `json:"logs,omitempty" rlp:"optional"`
// Placed at end on purpose. The RLP will be decoded to 0 instead of Value *big.Int `json:"value,omitempty" rlp:"optional"`
// nil if there are non-empty elements after in the struct.
Value *big.Int `json:"value,omitempty" rlp:"optional"`
revertedSnapshot bool revertedSnapshot bool
AccessedSlots accessedSlots `json:"accessedSlots"` AccessedSlots accessedSlots `json:"accessedSlots"`
@ -108,12 +103,18 @@ func (f *callFrameWithOpcodes) processOutput(output []byte, err error, reverted
} }
type callFrameWithOpcodesMarshaling struct { type callFrameWithOpcodesMarshaling struct {
TypeString string `json:"type"` TypeString string `json:"type"`
Gas hexutil.Uint64 Gas hexutil.Uint64
GasUsed hexutil.Uint64 GasUsed hexutil.Uint64
Value *hexutil.Big Value *hexutil.Big
Input hexutil.Bytes Input hexutil.Bytes
Output hexutil.Bytes Output hexutil.Bytes
AccessedSlots accessedSlots `json:"accessedSlots"`
ExtCodeAccessInfo []common.Address `json:"extCodeAccessInfo"`
UsedOpcodes map[vm.OpCode]uint64 `json:"usedOpcodes"`
ContractSize map[common.Address]*contractSizeWithOpcode `json:"contractSize"`
OutOfGas bool `json:"outOfGas"`
Calls []callFrameWithOpcodes `json:"calls,omitempty"`
} }
type accessedSlots struct { type accessedSlots struct {
@ -140,25 +141,13 @@ type erc7562Tracer struct {
callstackWithOpcodes []callFrameWithOpcodes callstackWithOpcodes []callFrameWithOpcodes
lastOpWithStack *opcodeWithPartialStack lastOpWithStack *opcodeWithPartialStack
Keccak map[string]struct{} `json:"keccak"` Keccak map[string]struct{} `json:"keccak"`
} transactionType uint8
// catchPanic handles panic recovery and logs the panic and stack trace.
func catchPanic() {
if r := recover(); r != nil {
// Retrieve the function name
pc, _, _, _ := runtime.Caller(1)
funcName := runtime.FuncForPC(pc).Name()
// Log the panic and function name
log.Error("Panic in", funcName, r)
debug.PrintStack()
}
} }
// newErc7562Tracer returns a native go tracer which tracks // newErc7562Tracer returns a native go tracer which tracks
// call frames of a tx, and implements vm.EVMLogger. // call frames of a tx, and implements vm.EVMLogger.
func newErc7562Tracer(ctx *tracers.Context, cfg json.RawMessage, chainConfig *params.ChainConfig) (*tracers.Tracer, error) { func newErc7562Tracer(ctx *tracers.Context, cfg json.RawMessage, _ *params.ChainConfig) (*tracers.Tracer, error) {
t, err := newErc7562TracerObject(ctx, cfg) t, err := newErc7562TracerObject(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,7 +184,7 @@ func getFullConfiguration(partial erc7562TracerConfig) erc7562TracerConfig {
return config return config
} }
func newErc7562TracerObject(ctx *tracers.Context, cfg json.RawMessage) (*erc7562Tracer, error) { func newErc7562TracerObject(cfg json.RawMessage) (*erc7562Tracer, error) {
var config erc7562TracerConfig var config erc7562TracerConfig
if cfg != nil { if cfg != nil {
if err := json.Unmarshal(cfg, &config); err != nil { if err := json.Unmarshal(cfg, &config); err != nil {
@ -212,14 +201,13 @@ func newErc7562TracerObject(ctx *tracers.Context, cfg json.RawMessage) (*erc7562
} }
func (t *erc7562Tracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) { func (t *erc7562Tracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
defer catchPanic()
t.env = env t.env = env
t.gasLimit = tx.Gas() t.gasLimit = tx.Gas()
t.transactionType = tx.Type()
} }
// OnEnter is called when EVM enters a new scope (via call, create or selfdestruct). // OnEnter is called when EVM enters a new scope (via call, create or selfdestruct).
func (t *erc7562Tracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { func (t *erc7562Tracer) OnEnter(depth int, typ byte, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
defer catchPanic()
t.depth = depth t.depth = depth
// Skip if tracing was interrupted // Skip if tracing was interrupted
if t.interrupt.Load() { if t.interrupt.Load() {
@ -250,7 +238,7 @@ func (t *erc7562Tracer) OnEnter(depth int, typ byte, from common.Address, to com
t.callstackWithOpcodes = append(t.callstackWithOpcodes, call) t.callstackWithOpcodes = append(t.callstackWithOpcodes, call)
} }
func (t *erc7562Tracer) captureEnd(output []byte, gasUsed uint64, err error, reverted bool) { func (t *erc7562Tracer) captureEnd(output []byte, err error, reverted bool) {
if len(t.callstackWithOpcodes) != 1 { if len(t.callstackWithOpcodes) != 1 {
return return
} }
@ -260,9 +248,8 @@ func (t *erc7562Tracer) captureEnd(output []byte, gasUsed uint64, err error, rev
// OnExit is called when EVM exits a scope, even if the scope didn't // OnExit is called when EVM exits a scope, even if the scope didn't
// execute any code. // execute any code.
func (t *erc7562Tracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) { func (t *erc7562Tracer) OnExit(depth int, output []byte, gasUsed uint64, err error, reverted bool) {
defer catchPanic()
if depth == 0 { if depth == 0 {
t.captureEnd(output, gasUsed, err, reverted) t.captureEnd(output, err, reverted)
return return
} }
@ -287,7 +274,6 @@ func (t *erc7562Tracer) OnExit(depth int, output []byte, gasUsed uint64, err err
} }
func (t *erc7562Tracer) OnTxEnd(receipt *types.Receipt, err error) { func (t *erc7562Tracer) OnTxEnd(receipt *types.Receipt, err error) {
defer catchPanic()
// Error happened during tx validation. // Error happened during tx validation.
if err != nil { if err != nil {
return return
@ -300,7 +286,6 @@ func (t *erc7562Tracer) OnTxEnd(receipt *types.Receipt, err error) {
} }
func (t *erc7562Tracer) OnLog(log1 *types.Log) { func (t *erc7562Tracer) OnLog(log1 *types.Log) {
defer catchPanic()
// Only logs need to be captured via opcode processing // Only logs need to be captured via opcode processing
if !t.config.WithLog { if !t.config.WithLog {
return return
@ -321,7 +306,6 @@ func (t *erc7562Tracer) OnLog(log1 *types.Log) {
// GetResult returns the json-encoded nested list of call traces, and any // GetResult returns the json-encoded nested list of call traces, and any
// error arising from the encoding or forceful termination (via `Stop`). // error arising from the encoding or forceful termination (via `Stop`).
func (t *erc7562Tracer) GetResult() (json.RawMessage, error) { func (t *erc7562Tracer) GetResult() (json.RawMessage, error) {
defer catchPanic()
if len(t.callstackWithOpcodes) != 1 { if len(t.callstackWithOpcodes) != 1 {
return nil, errors.New("incorrect number of top-level calls") return nil, errors.New("incorrect number of top-level calls")
} }
@ -356,7 +340,6 @@ func (t *erc7562Tracer) GetResult() (json.RawMessage, error) {
// Stop terminates execution of the tracer at the first opportune moment. // Stop terminates execution of the tracer at the first opportune moment.
func (t *erc7562Tracer) Stop(err error) { func (t *erc7562Tracer) Stop(err error) {
defer catchPanic()
t.reason = err t.reason = err
t.interrupt.Store(true) t.interrupt.Store(true)
} }
@ -375,7 +358,6 @@ func (t *erc7562Tracer) clearFailedLogs(cf *callFrameWithOpcodes, parentFailed b
} }
func (t *erc7562Tracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) { func (t *erc7562Tracer) OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
defer catchPanic()
opcode := vm.OpCode(op) opcode := vm.OpCode(op)
var opcodeWithStack *opcodeWithPartialStack var opcodeWithStack *opcodeWithPartialStack
stackSize := len(scope.StackData()) stackSize := len(scope.StackData())
@ -413,14 +395,14 @@ func (t *erc7562Tracer) handleGasObserved(opcode vm.OpCode, currentCallFrame *ca
// [OP-012] // [OP-012]
pendingGasObserved := t.lastOpWithStack.Opcode == vm.GAS && !isCall(opcode) pendingGasObserved := t.lastOpWithStack.Opcode == vm.GAS && !isCall(opcode)
if pendingGasObserved { if pendingGasObserved {
incrementCount(currentCallFrame.UsedOpcodes, vm.GAS) currentCallFrame.UsedOpcodes[vm.GAS]++
} }
} }
func (t *erc7562Tracer) storeUsedOpcode(opcode vm.OpCode, currentCallFrame *callFrameWithOpcodes) { func (t *erc7562Tracer) storeUsedOpcode(opcode vm.OpCode, currentCallFrame *callFrameWithOpcodes) {
// ignore "unimportant" opcodes // ignore "unimportant" opcodes
if opcode != vm.GAS && !t.isIgnoredOpcode(opcode) { if opcode != vm.GAS && !t.isIgnoredOpcode(opcode) {
incrementCount(currentCallFrame.UsedOpcodes, opcode) currentCallFrame.UsedOpcodes[opcode]++
} }
} }
@ -439,11 +421,11 @@ func (t *erc7562Tracer) handleStorageAccess(opcode vm.OpCode, scope tracing.OpCo
currentCallFrame.AccessedSlots.Reads[slotHex] = append(currentCallFrame.AccessedSlots.Reads[slotHex], t.env.StateDB.GetState(addr, slot).Hex()) currentCallFrame.AccessedSlots.Reads[slotHex] = append(currentCallFrame.AccessedSlots.Reads[slotHex], t.env.StateDB.GetState(addr, slot).Hex())
} }
} else if opcode == vm.SSTORE { } else if opcode == vm.SSTORE {
incrementCount(currentCallFrame.AccessedSlots.Writes, slotHex) currentCallFrame.AccessedSlots.Writes[slotHex]++
} else if opcode == vm.TLOAD { } else if opcode == vm.TLOAD {
incrementCount(currentCallFrame.AccessedSlots.TransientReads, slotHex) currentCallFrame.AccessedSlots.TransientReads[slotHex]++
} else { } else {
incrementCount(currentCallFrame.AccessedSlots.TransientWrites, slotHex) currentCallFrame.AccessedSlots.TransientWrites[slotHex]++
} }
} }
} }
@ -480,7 +462,7 @@ func (t *erc7562Tracer) handleAccessedContractSize(opcode vm.OpCode, scope traci
n = 1 n = 1
} }
addr := common.BytesToAddress(peepStack(scope.StackData(), n).Bytes()) addr := common.BytesToAddress(peepStack(scope.StackData(), n).Bytes())
if _, ok := currentCallFrame.ContractSize[addr]; !ok && !isAllowedPrecompile(addr) { if _, ok := currentCallFrame.ContractSize[addr]; !ok {
currentCallFrame.ContractSize[addr] = &contractSizeWithOpcode{ currentCallFrame.ContractSize[addr] = &contractSizeWithOpcode{
ContractSize: len(t.env.StateDB.GetCode(addr)), ContractSize: len(t.env.StateDB.GetCode(addr)),
Opcode: opcode, Opcode: opcode,
@ -537,14 +519,3 @@ func defaultIgnoredOpcodes() map[vm.OpCode]struct{} {
return ignored return ignored
} }
// not using 'isPrecompiled' to only allow the ones defined by the ERC-7562 as stateless precompiles
// [OP-062]
func isAllowedPrecompile(addr common.Address) bool {
addrInt := addr.Big()
return addrInt.Cmp(big.NewInt(0)) == 1 && addrInt.Cmp(big.NewInt(10)) == -1
}
func incrementCount[K comparable](m map[K]uint64, k K) {
m[k] = m[k] + 1
}