@@ -3,6 +3,7 @@ package native
3
3
import (
4
4
"fmt"
5
5
"math/big"
6
+ "slices"
6
7
"sync/atomic"
7
8
8
9
"github.com/ethereum/go-ethereum/common"
@@ -31,6 +32,16 @@ type BaseGasDimensionTracer struct {
31
32
callStack CallGasDimensionStack
32
33
// the depth at the current step of execution of the call stack
33
34
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
34
45
// maintain an access list tracer to check previous access list statuses.
35
46
prevAccessListAddresses map [common.Address ]int
36
47
prevAccessListSlots []map [common.Hash ]struct {}
@@ -52,27 +63,35 @@ type BaseGasDimensionTracer struct {
52
63
reason error
53
64
// cached chain config for use in hooks
54
65
chainConfig * params.ChainConfig
66
+ // for debugging it's sometimes useful to know the order in which opcodes were seen / count
67
+ opCount uint64
55
68
}
56
69
57
70
func NewBaseGasDimensionTracer (chainConfig * params.ChainConfig ) BaseGasDimensionTracer {
58
71
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 ,
76
95
}
77
96
}
78
97
@@ -110,6 +129,7 @@ func (t *BaseGasDimensionTracer) onOpcodeStart(
110
129
callStackInfo * CallGasDimensionInfo ,
111
130
opcode vm.OpCode ,
112
131
) {
132
+ t .opCount ++
113
133
// First check if tracer has been interrupted
114
134
if t .interrupt .Load () {
115
135
return true , zeroGasesByDimension (), nil , vm .OpCode (op )
@@ -219,6 +239,7 @@ func (t *BaseGasDimensionTracer) callFinishFunction(
219
239
// you can't trust the `gas` field on the call itself. I wonder if the gas field is an estimation
220
240
gasUsedByCall = stackInfo .GasDimensionInfo .GasCounterAtTimeOfCall - gas
221
241
var finishErr error
242
+
222
243
finishGasesByDimension , finishErr = finishFunction (gasUsedByCall , stackInfo .ExecutionCost , stackInfo .GasDimensionInfo )
223
244
if finishErr != nil {
224
245
t .interrupt .Store (true )
@@ -265,6 +286,15 @@ func (t *BaseGasDimensionTracer) OnTxStart(env *tracing.VMContext, tx *types.Tra
265
286
rules .IsShanghai ,
266
287
)
267
288
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
+ }
268
298
}
269
299
270
300
// OnTxEnd handles transaction end
@@ -282,6 +312,14 @@ func (t *BaseGasDimensionTracer) OnTxEnd(receipt *types.Receipt, err error) {
282
312
t .txHash = receipt .TxHash
283
313
t .status = receipt .Status
284
314
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
+ }
285
323
}
286
324
287
325
// Stop signals the tracer to stop tracing
@@ -320,11 +358,37 @@ func (t *BaseGasDimensionTracer) GetStateDB() tracing.StateDB {
320
358
return t .env .StateDB
321
359
}
322
360
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
+
323
381
// GetPrevAccessList returns the previous access list
324
382
func (t * BaseGasDimensionTracer ) GetPrevAccessList () (addresses map [common.Address ]int , slots []map [common.Hash ]struct {}) {
325
383
return t .prevAccessListAddresses , t .prevAccessListSlots
326
384
}
327
385
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
+
328
392
// Error returns the EVM execution error captured by the trace
329
393
func (t * BaseGasDimensionTracer ) Error () error { return t .err }
330
394
@@ -380,6 +444,8 @@ func zeroCallGasDimensionInfo() CallGasDimensionInfo {
380
444
IsValueSentWithCall : false ,
381
445
InitCodeCost : 0 ,
382
446
HashCost : 0 ,
447
+ isTargetPrecompile : false ,
448
+ isTargetStylusContract : false ,
383
449
}
384
450
}
385
451
@@ -398,16 +464,20 @@ func zeroCallGasDimensionStackInfo() CallGasDimensionStackInfo {
398
464
399
465
// BaseExecutionResult has shared fields for execution results
400
466
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"`
411
481
}
412
482
413
483
// get the result of the transaction execution that we will hand to the json output
@@ -419,15 +489,19 @@ func (t *BaseGasDimensionTracer) GetBaseExecutionResult() (BaseExecutionResult,
419
489
failed := t .err != nil
420
490
421
491
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 ,
432
506
}, nil
433
507
}
0 commit comments