Skip to content

Commit e09f142

Browse files
committed
gas dimension tracing: Expose L1GasUsed, CallExecutionGas explicitly
This PR exposes the GasUsed, L1GasUsed, and L2GasUsed from the transaction reciept directly in the tracer response. It also changes the tracers for CALL, CALLCODE, STATICCALL, DELEGATECALL, CREATE and CREATE2 to explicitly return the gas consumed by the child execution of the call when the call stack depth is increased by those opcodes.
1 parent f0e39db commit e09f142

7 files changed

+203
-52
lines changed

eth/tracers/live/tx_gas_dimension_by_opcode.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/ethereum/go-ethereum/core/tracing"
1010
"github.com/ethereum/go-ethereum/eth/tracers"
1111
"github.com/ethereum/go-ethereum/eth/tracers/native"
12+
"github.com/ethereum/go-ethereum/params"
1213

1314
"github.com/ethereum/go-ethereum/common"
1415
"github.com/ethereum/go-ethereum/core/types"
@@ -21,12 +22,14 @@ func init() {
2122
}
2223

2324
type txGasDimensionByOpcodeLiveTraceConfig struct {
24-
Path string `json:"path"` // Path to directory for output
25+
Path string `json:"path"` // Path to directory for output
26+
ChainConfig *params.ChainConfig `json:"chainConfig"`
2527
}
2628

2729
// gasDimensionTracer struct
2830
type TxGasDimensionByOpcodeLiveTracer struct {
2931
Path string `json:"path"` // Path to directory for output
32+
ChainConfig *params.ChainConfig
3033
GasDimensionTracer *native.TxGasDimensionByOpcodeTracer
3134
}
3235

@@ -45,8 +48,16 @@ func NewTxGasDimensionByOpcodeLogger(
4548
return nil, fmt.Errorf("tx gas dimension live tracer path for output is required: %v", config)
4649
}
4750

51+
// if you get stuck here, look at
52+
// cmd/chaininfo/arbitrum_chain_info.json
53+
// for a sample chain config
54+
if config.ChainConfig == nil {
55+
return nil, fmt.Errorf("tx gas dimension live tracer chain config is required: %v", config)
56+
}
57+
4858
t := &TxGasDimensionByOpcodeLiveTracer{
4959
Path: config.Path,
60+
ChainConfig: config.ChainConfig,
5061
GasDimensionTracer: nil,
5162
}
5263

@@ -69,7 +80,7 @@ func (t *TxGasDimensionByOpcodeLiveTracer) OnTxStart(
6980
}
7081

7182
t.GasDimensionTracer = &native.TxGasDimensionByOpcodeTracer{
72-
BaseGasDimensionTracer: native.NewBaseGasDimensionTracer(),
83+
BaseGasDimensionTracer: native.NewBaseGasDimensionTracer(t.ChainConfig),
7384
OpcodeToDimensions: make(map[_vm.OpCode]native.GasesByDimension),
7485
}
7586
t.GasDimensionTracer.OnTxStart(vm, tx, from)

eth/tracers/native/base_gas_dimension_tracer.go

+36-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import (
66
"sync/atomic"
77

88
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/core"
910
"github.com/ethereum/go-ethereum/core/tracing"
1011
"github.com/ethereum/go-ethereum/core/types"
1112
"github.com/ethereum/go-ethereum/core/vm"
13+
"github.com/ethereum/go-ethereum/params"
1214
)
1315

1416
// BaseGasDimensionTracer contains the shared functionality between different gas dimension tracers
@@ -18,7 +20,13 @@ type BaseGasDimensionTracer struct {
1820
// the hash of the transactionh
1921
txHash common.Hash
2022
// the amount of gas used in the transaction
21-
usedGas uint64
23+
gasUsed uint64
24+
// the amount of gas used for L1 in the transaction
25+
gasUsedForL1 uint64
26+
// the amount of gas used for L2 in the transaction
27+
gasUsedForL2 uint64
28+
// the intrinsic gas of the transaction, the static cost + calldata bytes cost
29+
intrinsicGas uint64
2230
// the call stack for the transaction
2331
callStack CallGasDimensionStack
2432
// the depth at the current step of execution of the call stack
@@ -34,10 +42,13 @@ type BaseGasDimensionTracer struct {
3442
interrupt atomic.Bool
3543
// reason or error for the interruption in the tracer itself (as opposed to the transaction)
3644
reason error
45+
// cached chain config for use in hooks
46+
chainConfig *params.ChainConfig
3747
}
3848

39-
func NewBaseGasDimensionTracer() BaseGasDimensionTracer {
49+
func NewBaseGasDimensionTracer(chainConfig *params.ChainConfig) BaseGasDimensionTracer {
4050
return BaseGasDimensionTracer{
51+
chainConfig: chainConfig,
4152
depth: 1,
4253
refundAccumulated: 0,
4354
prevAccessListAddresses: map[common.Address]int{},
@@ -216,6 +227,18 @@ func (t *BaseGasDimensionTracer) updatePrevAccessList(addresses map[common.Addre
216227
// OnTxStart handles transaction start
217228
func (t *BaseGasDimensionTracer) OnTxStart(env *tracing.VMContext, tx *types.Transaction, from common.Address) {
218229
t.env = env
230+
isContractCreation := tx.To() == nil
231+
rules := t.chainConfig.Rules(env.BlockNumber, env.Random != nil, env.Time, env.ArbOSVersion)
232+
intrinsicGas, _ := core.IntrinsicGas(
233+
tx.Data(),
234+
tx.AccessList(),
235+
tx.SetCodeAuthorizations(),
236+
isContractCreation,
237+
rules.IsHomestead,
238+
rules.IsIstanbul,
239+
rules.IsShanghai,
240+
)
241+
t.intrinsicGas = intrinsicGas
219242
}
220243

221244
// OnTxEnd handles transaction end
@@ -227,7 +250,9 @@ func (t *BaseGasDimensionTracer) OnTxEnd(receipt *types.Receipt, err error) {
227250
}
228251
return
229252
}
230-
t.usedGas = receipt.GasUsed
253+
t.gasUsed = receipt.GasUsed
254+
t.gasUsedForL1 = receipt.GasUsedForL1
255+
t.gasUsedForL2 = receipt.GasUsedForL2()
231256
t.txHash = receipt.TxHash
232257
}
233258

@@ -281,7 +306,10 @@ func (t *BaseGasDimensionTracer) Error() error { return t.err }
281306

282307
// BaseExecutionResult has shared fields for execution results
283308
type BaseExecutionResult struct {
284-
Gas uint64 `json:"gas"`
309+
GasUsed uint64 `json:"gasUsed"`
310+
GasUsedForL1 uint64 `json:"gasUsedForL1"`
311+
GasUsedForL2 uint64 `json:"gasUsedForL2"`
312+
IntrinsicGas uint64 `json:"intrinsicGas"`
285313
Failed bool `json:"failed"`
286314
TxHash string `json:"txHash"`
287315
BlockTimestamp uint64 `json:"blockTimestamp"`
@@ -297,7 +325,10 @@ func (t *BaseGasDimensionTracer) GetBaseExecutionResult() (BaseExecutionResult,
297325
failed := t.err != nil
298326

299327
return BaseExecutionResult{
300-
Gas: t.usedGas,
328+
GasUsed: t.gasUsed,
329+
GasUsedForL1: t.gasUsedForL1,
330+
GasUsedForL2: t.gasUsedForL2,
331+
IntrinsicGas: t.intrinsicGas,
301332
Failed: failed,
302333
TxHash: t.txHash.Hex(),
303334
BlockTimestamp: t.env.Time,

eth/tracers/native/gas_dimension_calc.go

+38-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type GasesByDimension struct {
1919
StateGrowth uint64 `json:"gr,omitempty"`
2020
HistoryGrowth uint64 `json:"h,omitempty"`
2121
StateGrowthRefund int64 `json:"rf,omitempty"`
22+
ChildExecutionCost uint64 `json:"exc,omitempty"`
2223
}
2324

2425
// in the case of opcodes like CALL, STATICCALL, DELEGATECALL, etc,
@@ -202,6 +203,7 @@ func calcSimpleSingleDimensionGas(
202203
StateGrowth: 0,
203204
HistoryGrowth: 0,
204205
StateGrowthRefund: 0,
206+
ChildExecutionCost: 0,
205207
}, nil, nil
206208
}
207209

@@ -237,6 +239,7 @@ func calcSimpleAddressAccessSetGas(
237239
StateGrowth: 0,
238240
HistoryGrowth: 0,
239241
StateGrowthRefund: 0,
242+
ChildExecutionCost: 0,
240243
}
241244
if err := checkGasDimensionsEqualOneDimensionalGas(pc, op, depth, gas, cost, ret); err != nil {
242245
return GasesByDimension{}, nil, err
@@ -270,6 +273,7 @@ func calcSLOADGas(
270273
StateGrowth: 0,
271274
HistoryGrowth: 0,
272275
StateGrowthRefund: 0,
276+
ChildExecutionCost: 0,
273277
}
274278
if err := checkGasDimensionsEqualOneDimensionalGas(pc, op, depth, gas, cost, ret); err != nil {
275279
return GasesByDimension{}, nil, err
@@ -324,6 +328,7 @@ func calcExtCodeCopyGas(
324328
StateGrowth: 0,
325329
HistoryGrowth: 0,
326330
StateGrowthRefund: 0,
331+
ChildExecutionCost: 0,
327332
}
328333
if err := checkGasDimensionsEqualOneDimensionalGas(pc, op, depth, gas, cost, ret); err != nil {
329334
return GasesByDimension{}, nil, err
@@ -405,6 +410,7 @@ func calcStateReadCallGas(
405410
StateGrowth: 0,
406411
HistoryGrowth: 0,
407412
StateGrowthRefund: 0,
413+
ChildExecutionCost: 0,
408414
}, &CallGasDimensionInfo{
409415
Pc: pc,
410416
Op: vm.OpCode(op),
@@ -430,15 +436,17 @@ func finishCalcStateReadCallGas(
430436
codeExecutionCost uint64,
431437
callGasDimensionInfo CallGasDimensionInfo,
432438
) (GasesByDimension, error) {
439+
oneDimensionalGas := totalGasUsed - codeExecutionCost
433440
computation := callGasDimensionInfo.AccessListComputationCost + callGasDimensionInfo.MemoryExpansionCost
434441
stateAccess := callGasDimensionInfo.AccessListStateAccessCost
435442
ret := GasesByDimension{
436-
OneDimensionalGasCost: totalGasUsed,
443+
OneDimensionalGasCost: oneDimensionalGas,
437444
Computation: computation,
438445
StateAccess: stateAccess,
439446
StateGrowth: 0,
440447
HistoryGrowth: 0,
441448
StateGrowthRefund: 0,
449+
ChildExecutionCost: codeExecutionCost,
442450
}
443451
err := checkGasDimensionsEqualCallGas(
444452
callGasDimensionInfo.Pc,
@@ -504,6 +512,7 @@ func calcLogGas(
504512
StateGrowth: 0,
505513
HistoryGrowth: historyGrowthCost,
506514
StateGrowthRefund: 0,
515+
ChildExecutionCost: 0,
507516
}, nil, nil
508517
}
509518

@@ -557,6 +566,7 @@ func calcCreateGas(
557566
StateGrowth: 0,
558567
HistoryGrowth: 0,
559568
StateGrowthRefund: 0,
569+
ChildExecutionCost: 0,
560570
}, &CallGasDimensionInfo{
561571
Pc: pc,
562572
Op: vm.OpCode(op),
@@ -577,6 +587,7 @@ func finishCalcCreateGas(
577587
codeExecutionCost uint64,
578588
callGasDimensionInfo CallGasDimensionInfo,
579589
) (GasesByDimension, error) {
590+
oneDimensionalGas := totalGasUsed - codeExecutionCost
580591
// totalGasUsed = init_code_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost
581592
codeDepositCost := totalGasUsed - params.CreateGas - callGasDimensionInfo.InitCodeCost -
582593
callGasDimensionInfo.MemoryExpansionCost - callGasDimensionInfo.HashCost - codeExecutionCost
@@ -588,12 +599,13 @@ func finishCalcCreateGas(
588599
computeNonNewAccountCost := staticNonNewAccountCost / 2
589600
growthNonNewAccountCost := staticNonNewAccountCost - computeNonNewAccountCost
590601
ret := GasesByDimension{
591-
OneDimensionalGasCost: totalGasUsed,
602+
OneDimensionalGasCost: oneDimensionalGas,
592603
Computation: callGasDimensionInfo.InitCodeCost + callGasDimensionInfo.MemoryExpansionCost + callGasDimensionInfo.HashCost + computeNonNewAccountCost,
593604
StateAccess: 0,
594605
StateGrowth: growthNonNewAccountCost + params.CallNewAccountGas + codeDepositCost,
595606
HistoryGrowth: 0,
596607
StateGrowthRefund: 0,
608+
ChildExecutionCost: codeExecutionCost,
597609
}
598610
err := checkGasDimensionsEqualCallGas(
599611
callGasDimensionInfo.Pc,
@@ -677,6 +689,7 @@ func calcReadAndStoreCallGas(
677689
StateGrowth: 0,
678690
HistoryGrowth: 0,
679691
StateGrowthRefund: 0,
692+
ChildExecutionCost: 0,
680693
}, &CallGasDimensionInfo{
681694
Pc: pc,
682695
Op: vm.OpCode(op),
@@ -702,6 +715,8 @@ func finishCalcStateReadAndStoreCallGas(
702715
codeExecutionCost uint64,
703716
callGasDimensionInfo CallGasDimensionInfo,
704717
) (GasesByDimension, error) {
718+
oneDimensionalGas := totalGasUsed - codeExecutionCost
719+
fmt.Println("finishCalcStateReadAndStoreCallGas is called")
705720
// the stipend is 2300 and it is not charged to the call itself but used in the execution cost
706721
var positiveValueCostLessStipend uint64 = 0
707722
if callGasDimensionInfo.IsValueSentWithCall {
@@ -716,44 +731,53 @@ func finishCalcStateReadAndStoreCallGas(
716731
// and whatever was left over after that was address_access_cost
717732
// callcode is the same as call except does not have value_to_empty_account_cost,
718733
// so this code properly handles it coincidentally, too
734+
fmt.Println("finishCalcStateReadAndStoreCallGas is continuing")
719735
ret := GasesByDimension{
720-
OneDimensionalGasCost: totalGasUsed,
736+
OneDimensionalGasCost: oneDimensionalGas,
721737
Computation: callGasDimensionInfo.MemoryExpansionCost + params.WarmStorageReadCostEIP2929,
722738
StateAccess: positiveValueCostLessStipend,
723739
StateGrowth: 0,
724740
HistoryGrowth: 0,
725741
StateGrowthRefund: 0,
742+
ChildExecutionCost: codeExecutionCost,
726743
}
727744
if leftOver > params.ColdAccountAccessCostEIP2929 { // there is a value being sent to an empty account
745+
fmt.Println("leftOver > params.ColdAccountAccessCostEIP2929")
728746
var coldCost uint64 = 0
729747
if leftOver-params.CallNewAccountGas == params.ColdAccountAccessCostEIP2929 {
730748
coldCost = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
731749
}
732750
ret = GasesByDimension{
733-
OneDimensionalGasCost: totalGasUsed,
751+
OneDimensionalGasCost: oneDimensionalGas,
734752
Computation: callGasDimensionInfo.MemoryExpansionCost + params.WarmStorageReadCostEIP2929,
735753
StateAccess: coldCost + positiveValueCostLessStipend,
736754
StateGrowth: params.CallNewAccountGas,
737755
HistoryGrowth: 0,
738756
StateGrowthRefund: 0,
757+
ChildExecutionCost: codeExecutionCost,
739758
}
740759
} else if leftOver == params.ColdAccountAccessCostEIP2929 {
760+
fmt.Println("leftOver == params.ColdAccountAccessCostEIP2929")
741761
var coldCost uint64 = params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
742762
ret = GasesByDimension{
743-
OneDimensionalGasCost: totalGasUsed,
763+
OneDimensionalGasCost: oneDimensionalGas,
744764
Computation: callGasDimensionInfo.MemoryExpansionCost + params.WarmStorageReadCostEIP2929,
745765
StateAccess: coldCost + positiveValueCostLessStipend,
746766
StateGrowth: 0,
747767
HistoryGrowth: 0,
748768
StateGrowthRefund: 0,
769+
ChildExecutionCost: codeExecutionCost,
749770
}
750771
}
772+
fmt.Println("finishCalcStateReadAndStoreCallGas is returning")
751773
err := checkGasDimensionsEqualCallGas(
752774
callGasDimensionInfo.Pc,
753775
byte(callGasDimensionInfo.Op),
754776
codeExecutionCost,
755777
ret,
756778
)
779+
fmt.Println("End result: ", ret)
780+
fmt.Println("End error: ", err)
757781
return ret, err
758782
}
759783

@@ -824,6 +848,7 @@ func calcSStoreGas(
824848
StateGrowth: params.SstoreSetGas,
825849
HistoryGrowth: 0,
826850
StateGrowthRefund: diff,
851+
ChildExecutionCost: 0,
827852
}
828853
} else if cost > 0 {
829854
ret = GasesByDimension{
@@ -833,6 +858,7 @@ func calcSStoreGas(
833858
StateGrowth: 0,
834859
HistoryGrowth: 0,
835860
StateGrowthRefund: diff,
861+
ChildExecutionCost: 0,
836862
}
837863
}
838864
if err := checkGasDimensionsEqualOneDimensionalGas(pc, op, depth, gas, cost, ret); err != nil {
@@ -881,6 +907,7 @@ func calcSelfDestructGas(
881907
StateGrowth: params.CreateBySelfdestructGas,
882908
HistoryGrowth: 0,
883909
StateGrowthRefund: 0,
910+
ChildExecutionCost: 0,
884911
}, nil, nil
885912
} else if cost == params.CreateBySelfdestructGas+params.SelfdestructGasEIP150+params.ColdAccountAccessCostEIP2929 {
886913
// cold and funds target empty
@@ -895,6 +922,7 @@ func calcSelfDestructGas(
895922
StateGrowth: params.CreateBySelfdestructGas,
896923
HistoryGrowth: 0,
897924
StateGrowthRefund: 0,
925+
ChildExecutionCost: 0,
898926
}, nil, nil
899927
} else if cost == params.SelfdestructGasEIP150+params.ColdAccountAccessCostEIP2929 {
900928
// address lookup was cold but funds target has money already. Cost is 7600
@@ -908,6 +936,7 @@ func calcSelfDestructGas(
908936
StateGrowth: 0,
909937
HistoryGrowth: 0,
910938
StateGrowthRefund: 0,
939+
ChildExecutionCost: 0,
911940
}, nil, nil
912941
}
913942
// if you reach here, then the cost was 5000
@@ -920,6 +949,7 @@ func calcSelfDestructGas(
920949
StateGrowth: 0,
921950
HistoryGrowth: 0,
922951
StateGrowthRefund: 0,
952+
ChildExecutionCost: 0,
923953
}, nil, nil
924954
}
925955

@@ -1086,12 +1116,13 @@ func checkGasDimensionsEqualCallGas(
10861116
codeExecutionCost uint64,
10871117
dim GasesByDimension,
10881118
) error {
1089-
if dim.OneDimensionalGasCost != codeExecutionCost+dim.Computation+dim.StateAccess+dim.StateGrowth+dim.HistoryGrowth {
1119+
if dim.OneDimensionalGasCost != dim.Computation+dim.StateAccess+dim.StateGrowth+dim.HistoryGrowth {
10901120
return fmt.Errorf(
1091-
"unexpected gas cost mismatch: pc %d, op %d, codeExecutionCost %d != %v",
1121+
"unexpected gas cost mismatch: pc %d, op %d, with codeExecutionCost %d, expected %d == %v",
10921122
pc,
10931123
op,
10941124
codeExecutionCost,
1125+
dim.OneDimensionalGasCost,
10951126
dim,
10961127
)
10971128
}

0 commit comments

Comments
 (0)