Skip to content

Commit 5e726c9

Browse files
holimanatif-konasl
authored andcommitted
eth/tracers, core: use scopecontext in tracers, provide statedb in capturestart (ethereum#22333)
Fixes the CaptureStart api to include the EVM, thus being able to set the statedb early on. This pr also exposes the struct we used internally in the interpreter to encapsulate the contract, mem, stack, rstack, so we pass it as a single struct to the tracer, and removes the error returns on the capture methods.
1 parent 52ba774 commit 5e726c9

File tree

12 files changed

+384
-365
lines changed

12 files changed

+384
-365
lines changed

core/vm/eips.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ func enable1884(jt *JumpTable) {
7676
}
7777
}
7878

79-
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
80-
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(callContext.contract.Address()))
81-
callContext.stack.push(balance)
79+
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
80+
balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
81+
scope.Stack.push(balance)
8282
return nil, nil
8383
}
8484

@@ -95,9 +95,9 @@ func enable1344(jt *JumpTable) {
9595
}
9696

9797
// opChainID implements CHAINID opcode
98-
func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
98+
func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
9999
chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
100-
callContext.stack.push(chainId)
100+
scope.Stack.push(chainId)
101101
return nil, nil
102102
}
103103

core/vm/evm.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
239239
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
240240
// Calling a non existing account, don't do anything, but ping the tracer
241241
if evm.vmConfig.Debug && evm.depth == 0 {
242-
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
242+
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
243243
evm.vmConfig.Tracer.CaptureEnd(ret, 0, 0, nil)
244244
}
245245
return nil, gas, nil
@@ -250,7 +250,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
250250

251251
// Capture the tracer start/end events in debug mode
252252
if evm.vmConfig.Debug && evm.depth == 0 {
253-
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
253+
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
254254
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
255255
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
256256
}(gas, time.Now())
@@ -472,7 +472,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
472472
}
473473

474474
if evm.vmConfig.Debug && evm.depth == 0 {
475-
evm.vmConfig.Tracer.CaptureStart(caller.Address(), address, true, codeAndHash.code, gas, value)
475+
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
476476
}
477477
start := time.Now()
478478

core/vm/instructions.go

Lines changed: 212 additions & 212 deletions
Large diffs are not rendered by default.

core/vm/instructions_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
104104
expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
105105
stack.push(x)
106106
stack.push(y)
107-
opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
107+
opFn(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
108108
if len(stack.data) != 1 {
109109
t.Errorf("Expected one item on stack after %v, got %d: ", name, len(stack.data))
110110
}
@@ -219,7 +219,7 @@ func TestAddMod(t *testing.T) {
219219
stack.push(z)
220220
stack.push(y)
221221
stack.push(x)
222-
opAddmod(&pc, evmInterpreter, &callCtx{nil, stack, nil})
222+
opAddmod(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
223223
actual := stack.pop()
224224
if actual.Cmp(expected) != 0 {
225225
t.Errorf("Testcase %d, expected %x, got %x", i, expected, actual)
@@ -241,7 +241,7 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
241241
y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
242242
stack.push(x)
243243
stack.push(y)
244-
opFn(&pc, interpreter, &callCtx{nil, stack, nil})
244+
opFn(&pc, interpreter, &ScopeContext{nil, stack, nil})
245245
actual := stack.pop()
246246
result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
247247
}
@@ -299,7 +299,7 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
299299
a.SetBytes(arg)
300300
stack.push(a)
301301
}
302-
op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
302+
op(&pc, evmInterpreter, &ScopeContext{nil, stack, nil})
303303
stack.pop()
304304
}
305305
}
@@ -525,12 +525,12 @@ func TestOpMstore(t *testing.T) {
525525
pc := uint64(0)
526526
v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
527527
stack.pushN(*new(uint256.Int).SetBytes(common.Hex2Bytes(v)), *new(uint256.Int))
528-
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
528+
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
529529
if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
530530
t.Fatalf("Mstore fail, got %v, expected %v", got, v)
531531
}
532532
stack.pushN(*new(uint256.Int).SetUint64(0x1), *new(uint256.Int))
533-
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
533+
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
534534
if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
535535
t.Fatalf("Mstore failed to overwrite previous value")
536536
}
@@ -553,7 +553,7 @@ func BenchmarkOpMstore(bench *testing.B) {
553553
bench.ResetTimer()
554554
for i := 0; i < bench.N; i++ {
555555
stack.pushN(*value, *memStart)
556-
opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
556+
opMstore(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
557557
}
558558
}
559559

@@ -572,7 +572,7 @@ func BenchmarkOpSHA3(bench *testing.B) {
572572
bench.ResetTimer()
573573
for i := 0; i < bench.N; i++ {
574574
stack.pushN(*uint256.NewInt().SetUint64(32), *start)
575-
opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
575+
opSha3(&pc, evmInterpreter, &ScopeContext{mem, stack, nil})
576576
}
577577
}
578578

core/vm/interpreter.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ type Interpreter interface {
6262
CanRun([]byte) bool
6363
}
6464

65-
// callCtx contains the things that are per-call, such as stack and memory,
65+
// ScopeContext contains the things that are per-call, such as stack and memory,
6666
// but not transients like pc and gas
67-
type callCtx struct {
68-
memory *Memory
69-
stack *Stack
70-
contract *Contract
67+
type ScopeContext struct {
68+
Memory *Memory
69+
Stack *Stack
70+
Contract *Contract
7171
}
7272

7373
// keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
@@ -163,10 +163,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
163163
op OpCode // current opcode
164164
mem = NewMemory() // bound memory
165165
stack = newstack() // local stack
166-
callContext = &callCtx{
167-
memory: mem,
168-
stack: stack,
169-
contract: contract,
166+
callContext = &ScopeContext{
167+
Memory: mem,
168+
Stack: stack,
169+
Contract: contract,
170170
}
171171
// For optimisation reason we're using uint64 as the program counter.
172172
// It's theoretically possible to go above 2^64. The YP defines the PC
@@ -191,9 +191,9 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
191191
defer func() {
192192
if err != nil {
193193
if !logged {
194-
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err)
194+
in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
195195
} else {
196-
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
196+
in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
197197
}
198198
}
199199
}()
@@ -275,7 +275,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
275275
}
276276

277277
if in.cfg.Debug {
278-
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, in.returnData, contract, in.evm.depth, err)
278+
in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
279279
logged = true
280280
}
281281

core/vm/jump_table.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
)
2222

2323
type (
24-
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
24+
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
2525
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
2626
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
2727
memorySizeFunc func(*Stack) (size uint64, overflow bool)

core/vm/logger.go

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package vm
1818

1919
import (
2020
"encoding/hex"
21-
"errors"
2221
"fmt"
2322
"io"
2423
"math/big"
@@ -32,8 +31,6 @@ import (
3231
"github.com/ethereum/go-ethereum/params"
3332
)
3433

35-
var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
36-
3734
// Storage represents a contract's storage.
3835
type Storage map[common.Hash]common.Hash
3936

@@ -107,10 +104,10 @@ func (s *StructLog) ErrorString() string {
107104
// Note that reference types are actual VM data structures; make copies
108105
// if you need to retain them beyond the current call.
109106
type Tracer interface {
110-
CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
111-
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error
112-
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
113-
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
107+
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
108+
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
109+
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
110+
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
114111
}
115112

116113
// StructLogger is an EVM state logger and implements Tracer.
@@ -139,17 +136,19 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
139136
}
140137

141138
// CaptureStart implements the Tracer interface to initialize the tracing operation.
142-
func (l *StructLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
143-
return nil
139+
func (l *StructLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
144140
}
145141

146142
// CaptureState logs a new structured log message and pushes it out to the environment
147143
//
148144
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
149-
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
145+
func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
146+
memory := scope.Memory
147+
stack := scope.Stack
148+
contract := scope.Contract
150149
// check if already accumulated the specified number of logs
151150
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
152-
return errTraceLimitReached
151+
return
153152
}
154153
// Copy a snapshot of the current memory state to a new buffer
155154
var mem []byte
@@ -199,17 +198,15 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
199198
// create a new snapshot of the EVM.
200199
log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rdata, storage, depth, env.StateDB.GetRefund(), err}
201200
l.logs = append(l.logs, log)
202-
return nil
203201
}
204202

205203
// CaptureFault implements the Tracer interface to trace an execution fault
206204
// while running an opcode.
207-
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
208-
return nil
205+
func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
209206
}
210207

211208
// CaptureEnd is called after the call finishes to finalize the tracing.
212-
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
209+
func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
213210
l.output = output
214211
l.err = err
215212
if l.cfg.Debug {
@@ -218,7 +215,6 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
218215
fmt.Printf(" error: %v\n", err)
219216
}
220217
}
221-
return nil
222218
}
223219

224220
// StructLogs returns the captured log entries.
@@ -292,7 +288,7 @@ func NewMarkdownLogger(cfg *LogConfig, writer io.Writer) *mdLogger {
292288
return l
293289
}
294290

295-
func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
291+
func (t *mdLogger) CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
296292
if !create {
297293
fmt.Fprintf(t.out, "From: `%v`\nTo: `%v`\nData: `0x%x`\nGas: `%d`\nValue `%v` wei\n",
298294
from.String(), to.String(),
@@ -307,10 +303,11 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
307303
| Pc | Op | Cost | Stack | RStack | Refund |
308304
|-------|-------------|------|-----------|-----------|---------|
309305
`)
310-
return nil
311306
}
312307

313-
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
308+
// CaptureState also tracks SLOAD/SSTORE ops to track storage change.
309+
func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
310+
stack := scope.Stack
314311
fmt.Fprintf(t.out, "| %4d | %10v | %3d |", pc, op, cost)
315312

316313
if !t.cfg.DisableStack {
@@ -327,18 +324,13 @@ func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64
327324
if err != nil {
328325
fmt.Fprintf(t.out, "Error: %v\n", err)
329326
}
330-
return nil
331327
}
332328

333-
func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
334-
329+
func (t *mdLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error) {
335330
fmt.Fprintf(t.out, "\nError: at pc=%d, op=%v: %v\n", pc, op, err)
336-
337-
return nil
338331
}
339332

340-
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) error {
333+
func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, err error) {
341334
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
342335
output, gasUsed, err)
343-
return nil
344336
}

core/vm/logger_json.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,16 @@ func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
4141
return l
4242
}
4343

44-
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
45-
return nil
44+
func (l *JSONLogger) CaptureStart(env *EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
4645
}
4746

47+
func (l *JSONLogger) CaptureFault(*EVM, uint64, OpCode, uint64, uint64, *ScopeContext, int, error) {}
48+
4849
// CaptureState outputs state information on the logger.
49-
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rData []byte, contract *Contract, depth int, err error) error {
50+
func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error) {
51+
memory := scope.Memory
52+
stack := scope.Stack
53+
5054
log := StructLog{
5155
Pc: pc,
5256
Op: op,
@@ -72,24 +76,19 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
7276
if !l.cfg.DisableReturnData {
7377
log.ReturnData = rData
7478
}
75-
return l.encoder.Encode(log)
76-
}
77-
78-
// CaptureFault outputs state information on the logger.
79-
func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
80-
return nil
79+
l.encoder.Encode(log)
8180
}
8281

8382
// CaptureEnd is triggered at end of execution.
84-
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error {
83+
func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
8584
type endLog struct {
8685
Output string `json:"output"`
8786
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
8887
Time time.Duration `json:"time"`
8988
Err string `json:"error,omitempty"`
9089
}
9190
if err != nil {
92-
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
91+
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, err.Error()})
9392
}
94-
return l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
93+
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, ""})
9594
}

core/vm/logger_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,20 @@ func TestStoreCapture(t *testing.T) {
5353
var (
5454
env = NewEVM(BlockContext{}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{})
5555
logger = NewStructLogger(nil)
56-
mem = NewMemory()
57-
stack = newstack()
5856
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 0)
57+
scope = &ScopeContext{
58+
Memory: NewMemory(),
59+
Stack: newstack(),
60+
Contract: contract,
61+
}
5962
)
60-
stack.push(uint256.NewInt().SetUint64(1))
61-
stack.push(uint256.NewInt())
63+
scope.Stack.push(uint256.NewInt().SetUint64(1))
64+
scope.Stack.push(uint256.NewInt())
6265
var index common.Hash
63-
logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, nil, contract, 0, nil)
66+
logger.CaptureState(env, 0, SSTORE, 0, 0, scope, nil, 0, nil)
6467
if len(logger.storage[contract.Address()]) == 0 {
65-
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
68+
t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(),
69+
len(logger.storage[contract.Address()]))
6670
}
6771
exp := common.BigToHash(big.NewInt(1))
6872
if logger.storage[contract.Address()][index] != exp {

0 commit comments

Comments
 (0)