Skip to content

Commit e98e686

Browse files
committed
core/vm: report gas used warming 7702 delegation targets correctly in tracer
1 parent fdfc159 commit e98e686

File tree

2 files changed

+108
-22
lines changed

2 files changed

+108
-22
lines changed

core/vm/operations_acl.go

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -253,55 +253,61 @@ var (
253253

254254
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
255255
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
256-
addr := common.Address(stack.Back(1).Bytes20())
256+
var (
257+
total uint64 // total dynamic gas used
258+
addr = common.Address(stack.Back(1).Bytes20())
259+
)
260+
257261
// Check slot presence in the access list
258-
warmAccess := evm.StateDB.AddressInAccessList(addr)
259-
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
260-
// the cost to charge for cold access, if any, is Cold - Warm
261-
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
262-
if !warmAccess {
262+
if !evm.StateDB.AddressInAccessList(addr) {
263263
evm.StateDB.AddAddressToAccessList(addr)
264+
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
265+
// the cost to charge for cold access, if any, is Cold - Warm
266+
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
264267
// Charge the remaining difference here already, to correctly calculate available
265268
// gas for call
266269
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
267270
return 0, ErrOutOfGas
268271
}
272+
total += coldCost
269273
}
270274

271275
// Check if code is a delegation and if so, charge for resolution.
272-
if addr, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
276+
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
273277
var cost uint64
274-
if evm.StateDB.AddressInAccessList(addr) {
275-
cost += params.WarmStorageReadCostEIP2929
278+
if evm.StateDB.AddressInAccessList(target) {
279+
cost = params.WarmStorageReadCostEIP2929
276280
} else {
277-
evm.StateDB.AddAddressToAccessList(addr)
278-
cost += params.ColdAccountAccessCostEIP2929
281+
evm.StateDB.AddAddressToAccessList(target)
282+
cost = params.ColdAccountAccessCostEIP2929
279283
}
280284
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
281285
return 0, ErrOutOfGas
282286
}
283-
coldCost += cost
287+
total += cost
284288
}
289+
285290
// Now call the old calculator, which takes into account
286291
// - create new account
287292
// - transfer value
288293
// - memory expansion
289294
// - 63/64ths rule
290-
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
291-
if warmAccess || err != nil {
292-
return gas, err
295+
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
296+
if err != nil {
297+
return old, err
293298
}
294-
// In case of a cold access, we temporarily add the cold charge back, and also
295-
// add it to the returned gas. By adding it to the return, it will be charged
296-
// outside of this function, as part of the dynamic gas, and that will make it
297-
// also become correctly reported to tracers.
298-
contract.Gas += coldCost
299+
300+
// Temporarily add the gas charge back to the contract and return value. By
301+
// adding it to the return, it will be charged outside of this function, as
302+
// part of the dynamic gas. This will ensure it is correctly reported to
303+
// tracers.
304+
contract.Gas += total
299305

300306
var overflow bool
301-
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
307+
if total, overflow = math.SafeAdd(old, total); overflow {
302308
return 0, ErrGasUintOverflow
303309
}
304-
return gas, nil
310+
return total, nil
305311
}
306312
}
307313

core/vm/runtime/runtime_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,3 +866,83 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) {
866866
benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b)
867867
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b)
868868
}
869+
870+
// TestDelegatedAccountAccessCost tests that calling an account with an EIP-7702
871+
// delegation designator incurs the correct amount of gas based on the tracer.
872+
func TestDelegatedAccountAccessCost(t *testing.T) {
873+
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
874+
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")))
875+
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes())
876+
877+
for i, tc := range []struct {
878+
code []byte
879+
step int
880+
want uint64
881+
}{
882+
{ // CALL(0xff)
883+
code: []byte{
884+
byte(vm.PUSH1), 0x0,
885+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
886+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
887+
},
888+
step: 7,
889+
want: 5455,
890+
},
891+
{ // CALLCODE(0xff)
892+
code: []byte{
893+
byte(vm.PUSH1), 0x0,
894+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
895+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
896+
},
897+
step: 7,
898+
want: 5455,
899+
},
900+
{ // DELEGATECALL(0xff)
901+
code: []byte{
902+
byte(vm.PUSH1), 0x0,
903+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
904+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
905+
},
906+
step: 6,
907+
want: 5455,
908+
},
909+
{ // STATICCALL(0xff)
910+
code: []byte{
911+
byte(vm.PUSH1), 0x0,
912+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
913+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
914+
},
915+
step: 6,
916+
want: 5455,
917+
},
918+
{ // SELFDESTRUCT(0xff): should not be affected by resolution
919+
code: []byte{
920+
byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
921+
},
922+
step: 1,
923+
want: 7600,
924+
},
925+
} {
926+
var step = 0
927+
var have = uint64(0)
928+
Execute(tc.code, nil, &Config{
929+
ChainConfig: params.MergedTestChainConfig,
930+
State: statedb,
931+
EVMConfig: vm.Config{
932+
Tracer: &tracing.Hooks{
933+
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
934+
// Uncomment to investigate failures:
935+
t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
936+
if step == tc.step {
937+
have = cost
938+
}
939+
step++
940+
},
941+
},
942+
},
943+
})
944+
if want := tc.want; have != want {
945+
t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
946+
}
947+
}
948+
}

0 commit comments

Comments
 (0)