Skip to content

Commit 347a2b2

Browse files
authored
Merge pull request #469 from OffchainLabs/relyt-gas-dim-precompiles-tracing
Gas Dimension Tracer modifications to handle Arbitrum precompiles and stylus
2 parents 23597ca + dc99e11 commit 347a2b2

File tree

5 files changed

+451
-71
lines changed

5 files changed

+451
-71
lines changed

eth/tracers/native/base_gas_dimension_tracer.go

Lines changed: 111 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package native
33
import (
44
"fmt"
55
"math/big"
6+
"slices"
67
"sync/atomic"
78

89
"github.com/ethereum/go-ethereum/common"
@@ -31,6 +32,16 @@ type BaseGasDimensionTracer struct {
3132
callStack CallGasDimensionStack
3233
// the depth at the current step of execution of the call stack
3334
depth int
35+
// whether the root of the call stack is a precompile
36+
rootIsPrecompile bool
37+
// an adjustment must be made to the gas value of the transaction
38+
// if the root is a precompile
39+
rootIsPrecompileAdjustment uint64
40+
// whether the root of the call stack is a stylus contract
41+
rootIsStylus bool
42+
// an adjustment must be made to the gas value of the transaction
43+
// if the root is a stylus contract
44+
rootIsStylusAdjustment uint64
3445
// maintain an access list tracer to check previous access list statuses.
3546
prevAccessListAddresses map[common.Address]int
3647
prevAccessListSlots []map[common.Hash]struct{}
@@ -52,27 +63,35 @@ type BaseGasDimensionTracer struct {
5263
reason error
5364
// cached chain config for use in hooks
5465
chainConfig *params.ChainConfig
66+
// for debugging it's sometimes useful to know the order in which opcodes were seen / count
67+
opCount uint64
5568
}
5669

5770
func NewBaseGasDimensionTracer(chainConfig *params.ChainConfig) BaseGasDimensionTracer {
5871
return BaseGasDimensionTracer{
59-
chainConfig: chainConfig,
60-
depth: 1,
61-
refundAccumulated: 0,
62-
prevAccessListAddresses: map[common.Address]int{},
63-
prevAccessListSlots: []map[common.Hash]struct{}{},
64-
env: nil,
65-
txHash: common.Hash{},
66-
gasUsed: 0,
67-
gasUsedForL1: 0,
68-
gasUsedForL2: 0,
69-
intrinsicGas: 0,
70-
callStack: CallGasDimensionStack{},
71-
executionGasAccumulated: 0,
72-
refundAdjusted: 0,
73-
err: nil,
74-
interrupt: atomic.Bool{},
75-
reason: nil,
72+
chainConfig: chainConfig,
73+
depth: 1,
74+
refundAccumulated: 0,
75+
prevAccessListAddresses: map[common.Address]int{},
76+
prevAccessListSlots: []map[common.Hash]struct{}{},
77+
env: nil,
78+
txHash: common.Hash{},
79+
gasUsed: 0,
80+
gasUsedForL1: 0,
81+
gasUsedForL2: 0,
82+
intrinsicGas: 0,
83+
callStack: CallGasDimensionStack{},
84+
rootIsPrecompile: false,
85+
rootIsPrecompileAdjustment: 0,
86+
rootIsStylus: false,
87+
rootIsStylusAdjustment: 0,
88+
executionGasAccumulated: 0,
89+
refundAdjusted: 0,
90+
err: nil,
91+
status: 0,
92+
interrupt: atomic.Bool{},
93+
reason: nil,
94+
opCount: 0,
7695
}
7796
}
7897

@@ -110,6 +129,7 @@ func (t *BaseGasDimensionTracer) onOpcodeStart(
110129
callStackInfo *CallGasDimensionInfo,
111130
opcode vm.OpCode,
112131
) {
132+
t.opCount++
113133
// First check if tracer has been interrupted
114134
if t.interrupt.Load() {
115135
return true, zeroGasesByDimension(), nil, vm.OpCode(op)
@@ -219,6 +239,7 @@ func (t *BaseGasDimensionTracer) callFinishFunction(
219239
// you can't trust the `gas` field on the call itself. I wonder if the gas field is an estimation
220240
gasUsedByCall = stackInfo.GasDimensionInfo.GasCounterAtTimeOfCall - gas
221241
var finishErr error
242+
222243
finishGasesByDimension, finishErr = finishFunction(gasUsedByCall, stackInfo.ExecutionCost, stackInfo.GasDimensionInfo)
223244
if finishErr != nil {
224245
t.interrupt.Store(true)
@@ -265,6 +286,15 @@ func (t *BaseGasDimensionTracer) OnTxStart(env *tracing.VMContext, tx *types.Tra
265286
rules.IsShanghai,
266287
)
267288
t.intrinsicGas = intrinsicGas
289+
t.rootIsPrecompile = false
290+
t.rootIsPrecompileAdjustment = 0
291+
t.rootIsStylus = false
292+
t.rootIsStylusAdjustment = 0
293+
precompileAddressList := t.GetPrecompileAddressList()
294+
if tx.To() != nil {
295+
t.rootIsPrecompile = slices.Contains(precompileAddressList, *tx.To())
296+
t.rootIsStylus = isStylusContract(t, *tx.To())
297+
}
268298
}
269299

270300
// OnTxEnd handles transaction end
@@ -282,6 +312,14 @@ func (t *BaseGasDimensionTracer) OnTxEnd(receipt *types.Receipt, err error) {
282312
t.txHash = receipt.TxHash
283313
t.status = receipt.Status
284314
t.refundAdjusted = t.adjustRefund(t.executionGasAccumulated+t.intrinsicGas, t.GetRefundAccumulated())
315+
// if the root is a precompile then we need to separately account for the gas used by the
316+
// precompile itself, which is not exposed to the opcode tracing
317+
if t.rootIsPrecompile {
318+
t.rootIsPrecompileAdjustment = t.gasUsedForL2 - (t.executionGasAccumulated + t.intrinsicGas)
319+
}
320+
if t.rootIsStylus {
321+
t.rootIsStylusAdjustment = t.gasUsedForL2 - (t.executionGasAccumulated + t.intrinsicGas)
322+
}
285323
}
286324

287325
// Stop signals the tracer to stop tracing
@@ -320,11 +358,37 @@ func (t *BaseGasDimensionTracer) GetStateDB() tracing.StateDB {
320358
return t.env.StateDB
321359
}
322360

361+
// GetCallStack returns the call stack
362+
func (t *BaseGasDimensionTracer) GetCallStack() CallGasDimensionStack {
363+
return t.callStack
364+
}
365+
366+
// GetOpCount returns the op count
367+
func (t *BaseGasDimensionTracer) GetOpCount() uint64 {
368+
return t.opCount
369+
}
370+
371+
// GetRootIsPrecompile returns whether the root of the call stack is a precompile
372+
func (t *BaseGasDimensionTracer) GetRootIsPrecompile() bool {
373+
return t.rootIsPrecompile
374+
}
375+
376+
// GetRootIsStylus returns whether the root of the call stack is a stylus contract
377+
func (t *BaseGasDimensionTracer) GetRootIsStylus() bool {
378+
return t.rootIsStylus
379+
}
380+
323381
// GetPrevAccessList returns the previous access list
324382
func (t *BaseGasDimensionTracer) GetPrevAccessList() (addresses map[common.Address]int, slots []map[common.Hash]struct{}) {
325383
return t.prevAccessListAddresses, t.prevAccessListSlots
326384
}
327385

386+
// GetPrecompileAddressList returns the list of precompile addresses
387+
func (t *BaseGasDimensionTracer) GetPrecompileAddressList() []common.Address {
388+
rules := t.chainConfig.Rules(t.env.BlockNumber, t.env.Random != nil, t.env.Time, t.env.ArbOSVersion)
389+
return vm.ActivePrecompiles(rules)
390+
}
391+
328392
// Error returns the EVM execution error captured by the trace
329393
func (t *BaseGasDimensionTracer) Error() error { return t.err }
330394

@@ -380,6 +444,8 @@ func zeroCallGasDimensionInfo() CallGasDimensionInfo {
380444
IsValueSentWithCall: false,
381445
InitCodeCost: 0,
382446
HashCost: 0,
447+
isTargetPrecompile: false,
448+
isTargetStylusContract: false,
383449
}
384450
}
385451

@@ -398,16 +464,20 @@ func zeroCallGasDimensionStackInfo() CallGasDimensionStackInfo {
398464

399465
// BaseExecutionResult has shared fields for execution results
400466
type BaseExecutionResult struct {
401-
GasUsed uint64 `json:"gasUsed"`
402-
GasUsedForL1 uint64 `json:"gasUsedForL1"`
403-
GasUsedForL2 uint64 `json:"gasUsedForL2"`
404-
IntrinsicGas uint64 `json:"intrinsicGas"`
405-
AdjustedRefund uint64 `json:"adjustedRefund"`
406-
Failed bool `json:"failed"`
407-
TxHash string `json:"txHash"`
408-
BlockTimestamp uint64 `json:"blockTimestamp"`
409-
BlockNumber *big.Int `json:"blockNumber"`
410-
Status uint64 `json:"status"`
467+
GasUsed uint64 `json:"gasUsed"`
468+
GasUsedForL1 uint64 `json:"gasUsedForL1"`
469+
GasUsedForL2 uint64 `json:"gasUsedForL2"`
470+
IntrinsicGas uint64 `json:"intrinsicGas"`
471+
AdjustedRefund uint64 `json:"adjustedRefund"`
472+
RootIsPrecompile bool `json:"rootIsPrecompile"`
473+
RootIsPrecompileAdjustment uint64 `json:"rootIsPrecompileAdjustment"`
474+
RootIsStylus bool `json:"rootIsStylus"`
475+
RootIsStylusAdjustment uint64 `json:"rootIsStylusAdjustment"`
476+
Failed bool `json:"failed"`
477+
TxHash string `json:"txHash"`
478+
BlockTimestamp uint64 `json:"blockTimestamp"`
479+
BlockNumber *big.Int `json:"blockNumber"`
480+
Status uint64 `json:"status"`
411481
}
412482

413483
// get the result of the transaction execution that we will hand to the json output
@@ -419,15 +489,19 @@ func (t *BaseGasDimensionTracer) GetBaseExecutionResult() (BaseExecutionResult,
419489
failed := t.err != nil
420490

421491
return BaseExecutionResult{
422-
GasUsed: t.gasUsed,
423-
GasUsedForL1: t.gasUsedForL1,
424-
GasUsedForL2: t.gasUsedForL2,
425-
IntrinsicGas: t.intrinsicGas,
426-
AdjustedRefund: t.refundAdjusted,
427-
Failed: failed,
428-
TxHash: t.txHash.Hex(),
429-
BlockTimestamp: t.env.Time,
430-
BlockNumber: t.env.BlockNumber,
431-
Status: t.status,
492+
GasUsed: t.gasUsed,
493+
GasUsedForL1: t.gasUsedForL1,
494+
GasUsedForL2: t.gasUsedForL2,
495+
IntrinsicGas: t.intrinsicGas,
496+
AdjustedRefund: t.refundAdjusted,
497+
RootIsPrecompile: t.rootIsPrecompile,
498+
RootIsPrecompileAdjustment: t.rootIsPrecompileAdjustment,
499+
RootIsStylus: t.rootIsStylus,
500+
RootIsStylusAdjustment: t.rootIsStylusAdjustment,
501+
Failed: failed,
502+
TxHash: t.txHash.Hex(),
503+
BlockTimestamp: t.env.Time,
504+
BlockNumber: t.env.BlockNumber,
505+
Status: t.status,
432506
}, nil
433507
}

0 commit comments

Comments
 (0)