Skip to content

Commit b5e12e2

Browse files
holimangzliudan
authored andcommitted
core/vm: make gas cost reporting to tracers correct (ethereum#22702)
Previously, the makeCallVariantGasCallEIP2929 charged the cold account access cost directly, leading to an incorrect gas cost passed to the tracer from the main execution loop. This change still temporarily charges the cost (to allow for an accurate calculation of the available gas for the call), but then afterwards refunds it and instead returns the correct total gas cost to be then properly charged in the main loop.
1 parent 4ff3e15 commit b5e12e2

File tree

2 files changed

+98
-4
lines changed

2 files changed

+98
-4
lines changed

core/vm/operations_acl.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,15 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
177177
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
178178
addr := common.Address(stack.Back(1).Bytes20())
179179
// Check slot presence in the access list
180-
if !evm.StateDB.AddressInAccessList(addr) {
180+
warmAccess := evm.StateDB.AddressInAccessList(addr)
181+
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
182+
// the cost to charge for cold access, if any, is Cold - Warm
183+
coldCost := ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929
184+
if !warmAccess {
181185
evm.StateDB.AddAddressToAccessList(addr)
182-
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost
183-
if !contract.UseGas(ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929) {
186+
// Charge the remaining difference here already, to correctly calculate available
187+
// gas for call
188+
if !contract.UseGas(coldCost) {
184189
return 0, ErrOutOfGas
185190
}
186191
}
@@ -189,7 +194,16 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
189194
// - transfer value
190195
// - memory expansion
191196
// - 63/64ths rule
192-
return oldCalculator(evm, contract, stack, mem, memorySize)
197+
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
198+
if warmAccess || err != nil {
199+
return gas, err
200+
}
201+
// In case of a cold access, we temporarily add the cold charge back, and also
202+
// add it to the returned gas. By adding it to the return, it will be charged
203+
// outside of this function, as part of the dynamic gas, and that will make it
204+
// also become correctly reported to tracers.
205+
contract.Gas += coldCost
206+
return gas + coldCost, nil
193207
}
194208
}
195209

core/vm/runtime/runtime_test.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,83 @@ func TestEip2929Cases(t *testing.T) {
598598
"account (cheap)", code)
599599
}
600600
}
601+
602+
// TestColdAccountAccessCost test that the cold account access cost is reported
603+
// correctly
604+
// see: https://github.com/ethereum/go-ethereum/issues/22649
605+
func TestColdAccountAccessCost(t *testing.T) {
606+
for i, tc := range []struct {
607+
code []byte
608+
step int
609+
want uint64
610+
}{
611+
{ // EXTCODEHASH(0xff)
612+
code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.EXTCODEHASH), byte(vm.POP)},
613+
step: 1,
614+
want: 2600,
615+
},
616+
{ // BALANCE(0xff)
617+
code: []byte{byte(vm.PUSH1), 0xFF, byte(vm.BALANCE), byte(vm.POP)},
618+
step: 1,
619+
want: 2600,
620+
},
621+
{ // CALL(0xff)
622+
code: []byte{
623+
byte(vm.PUSH1), 0x0,
624+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
625+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
626+
},
627+
step: 7,
628+
want: 2855,
629+
},
630+
{ // CALLCODE(0xff)
631+
code: []byte{
632+
byte(vm.PUSH1), 0x0,
633+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
634+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
635+
},
636+
step: 7,
637+
want: 2855,
638+
},
639+
{ // DELEGATECALL(0xff)
640+
code: []byte{
641+
byte(vm.PUSH1), 0x0,
642+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
643+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
644+
},
645+
step: 6,
646+
want: 2855,
647+
},
648+
{ // STATICCALL(0xff)
649+
code: []byte{
650+
byte(vm.PUSH1), 0x0,
651+
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
652+
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
653+
},
654+
step: 6,
655+
want: 2855,
656+
},
657+
{ // SELFDESTRUCT(0xff)
658+
code: []byte{
659+
byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
660+
},
661+
step: 1,
662+
want: 7600,
663+
},
664+
} {
665+
tracer := vm.NewStructLogger(nil)
666+
Execute(tc.code, nil, &Config{
667+
EVMConfig: vm.Config{
668+
Debug: true,
669+
Tracer: tracer,
670+
},
671+
})
672+
have := tracer.StructLogs()[tc.step].GasCost
673+
if want := tc.want; have != want {
674+
for ii, op := range tracer.StructLogs() {
675+
t.Logf("%d: %v %d", ii, op.OpName(), op.GasCost)
676+
}
677+
t.Fatalf("tescase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
678+
}
679+
}
680+
}

0 commit comments

Comments
 (0)