Skip to content

Commit 3fd16af

Browse files
authored
core,eth: implement tx-level hooks for tracers (#24510)
* core,eth: add empty tx logger hooks * core,eth: add initial and remaining gas to tx hooks * store tx gasLimit in js tracer * use gasLimit to compute intrinsic cost for js tracer * re-use rules in transitiondb * rm logs * rm logs * Mv some fields from Start to TxStart * simplify sender lookup in prestate tracer * mv env to TxStart * Revert "mv env to TxStart" This reverts commit 656939634b9aff19f55a1cd167345faf8b1ec310. * Revert "simplify sender lookup in prestate tracer" This reverts commit ab65bce48007cab99e68232e7aac2fe008338d50. * Revert "Mv some fields from Start to TxStart" This reverts commit aa50d3d9b2559addc80df966111ef5fb5d0c1b6b. * fix intrinsic gas for prestate tracer * add comments * refactor * fix test case * simplify consumedGas calc in prestate tracer
1 parent da16d08 commit 3fd16af

File tree

11 files changed

+83
-40
lines changed

11 files changed

+83
-40
lines changed

core/state_transition.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,23 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
287287
if err := st.preCheck(); err != nil {
288288
return nil, err
289289
}
290-
msg := st.msg
291-
sender := vm.AccountRef(msg.From())
292-
homestead := st.evm.ChainConfig().IsHomestead(st.evm.Context.BlockNumber)
293-
istanbul := st.evm.ChainConfig().IsIstanbul(st.evm.Context.BlockNumber)
294-
london := st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber)
295-
contractCreation := msg.To() == nil
290+
291+
if st.evm.Config.Debug {
292+
st.evm.Config.Tracer.CaptureTxStart(st.initialGas)
293+
defer func() {
294+
st.evm.Config.Tracer.CaptureTxEnd(st.gas)
295+
}()
296+
}
297+
298+
var (
299+
msg = st.msg
300+
sender = vm.AccountRef(msg.From())
301+
rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil)
302+
contractCreation = msg.To() == nil
303+
)
296304

297305
// Check clauses 4-5, subtract intrinsic gas if everything is correct
298-
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, homestead, istanbul)
306+
gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul)
299307
if err != nil {
300308
return nil, err
301309
}
@@ -310,7 +318,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
310318
}
311319

312320
// Set up the initial access list.
313-
if rules := st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil); rules.IsBerlin {
321+
if rules.IsBerlin {
314322
st.state.PrepareAccessList(msg.From(), msg.To(), vm.ActivePrecompiles(rules), msg.AccessList())
315323
}
316324
var (
@@ -325,15 +333,15 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
325333
ret, st.gas, vmerr = st.evm.Call(sender, st.to(), st.data, st.gas, st.value)
326334
}
327335

328-
if !london {
336+
if !rules.IsLondon {
329337
// Before EIP-3529: refunds were capped to gasUsed / 2
330338
st.refundGas(params.RefundQuotient)
331339
} else {
332340
// After EIP-3529: refunds are capped to gasUsed / 5
333341
st.refundGas(params.RefundQuotientEIP3529)
334342
}
335343
effectiveTip := st.gasPrice
336-
if london {
344+
if rules.IsLondon {
337345
effectiveTip = cmath.BigMin(st.gasTipCap, new(big.Int).Sub(st.gasFeeCap, st.evm.Context.BaseFee))
338346
}
339347
st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), effectiveTip))

core/vm/logger.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,16 @@ import (
2929
// Note that reference types are actual VM data structures; make copies
3030
// if you need to retain them beyond the current call.
3131
type EVMLogger interface {
32+
// Transaction level
33+
CaptureTxStart(gasLimit uint64)
34+
CaptureTxEnd(restGas uint64)
35+
// Top call frame
3236
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
33-
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
37+
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
38+
// Rest of call frames
3439
CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
3540
CaptureExit(output []byte, gasUsed uint64, err error)
41+
// Opcode level
42+
CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
3643
CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
37-
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
3844
}

eth/tracers/js/tracer.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030

3131
"github.com/ethereum/go-ethereum/common"
3232
"github.com/ethereum/go-ethereum/common/hexutil"
33-
"github.com/ethereum/go-ethereum/core"
3433
"github.com/ethereum/go-ethereum/core/vm"
3534
"github.com/ethereum/go-ethereum/crypto"
3635
tracers2 "github.com/ethereum/go-ethereum/eth/tracers"
@@ -419,6 +418,7 @@ type jsTracer struct {
419418
activePrecompiles []common.Address // Updated on CaptureStart based on given rules
420419
traceSteps bool // When true, will invoke step() on each opcode
421420
traceCallFrames bool // When true, will invoke enter() and exit() js funcs
421+
gasLimit uint64 // Amount of gas bought for the whole tx
422422
}
423423

424424
// New instantiates a new tracer instance. code specifies a Javascript snippet,
@@ -679,7 +679,18 @@ func wrapError(context string, err error) error {
679679
return fmt.Errorf("%v in server-side tracer function '%v'", err, context)
680680
}
681681

682-
// CaptureStart implements the Tracer interface to initialize the tracing operation.
682+
// CaptureTxStart implements the Tracer interface and is invoked at the beginning of
683+
// transaction processing.
684+
func (jst *jsTracer) CaptureTxStart(gasLimit uint64) {
685+
jst.gasLimit = gasLimit
686+
}
687+
688+
// CaptureTxStart implements the Tracer interface and is invoked at the end of
689+
// transaction processing.
690+
func (*jsTracer) CaptureTxEnd(restGas uint64) {}
691+
692+
// CaptureStart implements the Tracer interface and is invoked before executing the
693+
// top-level call frame of a transaction.
683694
func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
684695
jst.env = env
685696
jst.ctx["type"] = "CALL"
@@ -700,14 +711,8 @@ func (jst *jsTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Ad
700711
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil)
701712
jst.activePrecompiles = vm.ActivePrecompiles(rules)
702713

703-
// Compute intrinsic gas
704-
isHomestead := env.ChainConfig().IsHomestead(env.Context.BlockNumber)
705-
isIstanbul := env.ChainConfig().IsIstanbul(env.Context.BlockNumber)
706-
intrinsicGas, err := core.IntrinsicGas(input, nil, jst.ctx["type"] == "CREATE", isHomestead, isIstanbul)
707-
if err != nil {
708-
return
709-
}
710-
jst.ctx["intrinsicGas"] = intrinsicGas
714+
// Intrinsic costs are the only things reduced from initial gas to this point
715+
jst.ctx["intrinsicGas"] = jst.gasLimit - gas
711716
}
712717

713718
// CaptureState implements the Tracer interface to trace a single step of VM execution.
@@ -760,7 +765,7 @@ func (jst *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, sco
760765
}
761766
}
762767

763-
// CaptureEnd is called after the call finishes to finalize the tracing.
768+
// CaptureEnd is called after the top-level call finishes.
764769
func (jst *jsTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {
765770
jst.ctx["output"] = output
766771
jst.ctx["time"] = t.String()

eth/tracers/js/tracer_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,19 @@ func testCtx() *vmContext {
6262
func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainConfig) (json.RawMessage, error) {
6363
var (
6464
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Debug: true, Tracer: tracer})
65+
gasLimit uint64 = 31000
6566
startGas uint64 = 10000
6667
value = big.NewInt(0)
6768
contract = vm.NewContract(account{}, account{}, value, startGas)
6869
)
6970
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
7071

72+
tracer.CaptureTxStart(gasLimit)
7173
tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
7274
ret, err := env.Interpreter().Run(contract, []byte{}, false)
7375
tracer.CaptureEnd(ret, startGas-contract.Gas, 1, err)
76+
// Rest gas assumes no refund
77+
tracer.CaptureTxEnd(startGas - contract.Gas)
7478
if err != nil {
7579
return nil, err
7680
}

eth/tracers/logger/access_list_tracer.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ func (*AccessListTracer) CaptureEnter(typ vm.OpCode, from common.Address, to com
174174

175175
func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
176176

177+
func (*AccessListTracer) CaptureTxStart(gasLimit uint64) {}
178+
179+
func (*AccessListTracer) CaptureTxEnd(restGas uint64) {}
180+
177181
// AccessList returns the current accesslist maintained by the tracer.
178182
func (a *AccessListTracer) AccessList() types.AccessList {
179183
return a.list.accessList()

eth/tracers/logger/logger.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,10 @@ func (l *StructLogger) CaptureEnter(typ vm.OpCode, from common.Address, to commo
223223

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

226+
func (*StructLogger) CaptureTxStart(gasLimit uint64) {}
227+
228+
func (*StructLogger) CaptureTxEnd(restGas uint64) {}
229+
226230
// StructLogs returns the captured log entries.
227231
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
228232

@@ -347,3 +351,7 @@ func (t *mdLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.Ad
347351
}
348352

349353
func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
354+
355+
func (*mdLogger) CaptureTxStart(gasLimit uint64) {}
356+
357+
func (*mdLogger) CaptureTxEnd(restGas uint64) {}

eth/tracers/logger/logger_json.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,7 @@ func (l *JSONLogger) CaptureEnter(typ vm.OpCode, from common.Address, to common.
9898
}
9999

100100
func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
101+
102+
func (l *JSONLogger) CaptureTxStart(gasLimit uint64) {}
103+
104+
func (l *JSONLogger) CaptureTxEnd(restGas uint64) {}

eth/tracers/native/4byte.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ func (t *fourByteTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64,
131131
func (t *fourByteTracer) CaptureEnd(output []byte, gasUsed uint64, _ time.Duration, err error) {
132132
}
133133

134+
func (*fourByteTracer) CaptureTxStart(gasLimit uint64) {}
135+
136+
func (*fourByteTracer) CaptureTxEnd(restGas uint64) {}
137+
134138
// GetResult returns the json-encoded nested list of call traces, and any
135139
// error arising from the encoding or forceful termination (via `Stop`).
136140
func (t *fourByteTracer) GetResult() (json.RawMessage, error) {

eth/tracers/native/call.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
142142
t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
143143
}
144144

145+
func (*callTracer) CaptureTxStart(gasLimit uint64) {}
146+
147+
func (*callTracer) CaptureTxEnd(restGas uint64) {}
148+
145149
// GetResult returns the json-encoded nested list of call traces, and any
146150
// error arising from the encoding or forceful termination (via `Stop`).
147151
func (t *callTracer) GetResult() (json.RawMessage, error) {

eth/tracers/native/noop.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ func (t *noopTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.
6464
func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
6565
}
6666

67+
func (*noopTracer) CaptureTxStart(gasLimit uint64) {}
68+
69+
func (*noopTracer) CaptureTxEnd(restGas uint64) {}
70+
6771
// GetResult returns an empty json object.
6872
func (t *noopTracer) GetResult() (json.RawMessage, error) {
6973
return json.RawMessage(`{}`), nil

0 commit comments

Comments
 (0)