mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
core/vm: report gas used warming 7702 delegation targets correctly in tracer
This commit is contained in:
parent
fdfc159213
commit
e98e68662b
2 changed files with 108 additions and 22 deletions
|
|
@ -253,55 +253,61 @@ var (
|
|||
|
||||
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
addr := common.Address(stack.Back(1).Bytes20())
|
||||
var (
|
||||
total uint64 // total dynamic gas used
|
||||
addr = common.Address(stack.Back(1).Bytes20())
|
||||
)
|
||||
|
||||
// Check slot presence in the access list
|
||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
||||
if !evm.StateDB.AddressInAccessList(addr) {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
|
||||
// the cost to charge for cold access, if any, is Cold - Warm
|
||||
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
|
||||
if !warmAccess {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
// Charge the remaining difference here already, to correctly calculate available
|
||||
// gas for call
|
||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
total += coldCost
|
||||
}
|
||||
|
||||
// Check if code is a delegation and if so, charge for resolution.
|
||||
if addr, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
if target, ok := types.ParseDelegation(evm.StateDB.GetCode(addr)); ok {
|
||||
var cost uint64
|
||||
if evm.StateDB.AddressInAccessList(addr) {
|
||||
cost += params.WarmStorageReadCostEIP2929
|
||||
if evm.StateDB.AddressInAccessList(target) {
|
||||
cost = params.WarmStorageReadCostEIP2929
|
||||
} else {
|
||||
evm.StateDB.AddAddressToAccessList(addr)
|
||||
cost += params.ColdAccountAccessCostEIP2929
|
||||
evm.StateDB.AddAddressToAccessList(target)
|
||||
cost = params.ColdAccountAccessCostEIP2929
|
||||
}
|
||||
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
coldCost += cost
|
||||
total += cost
|
||||
}
|
||||
|
||||
// Now call the old calculator, which takes into account
|
||||
// - create new account
|
||||
// - transfer value
|
||||
// - memory expansion
|
||||
// - 63/64ths rule
|
||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if warmAccess || err != nil {
|
||||
return gas, err
|
||||
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||
if err != nil {
|
||||
return old, err
|
||||
}
|
||||
// In case of a cold access, we temporarily add the cold charge back, and also
|
||||
// add it to the returned gas. By adding it to the return, it will be charged
|
||||
// outside of this function, as part of the dynamic gas, and that will make it
|
||||
// also become correctly reported to tracers.
|
||||
contract.Gas += coldCost
|
||||
|
||||
// Temporarily add the gas charge back to the contract and return value. By
|
||||
// adding it to the return, it will be charged outside of this function, as
|
||||
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||
// tracers.
|
||||
contract.Gas += total
|
||||
|
||||
var overflow bool
|
||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
||||
if total, overflow = math.SafeAdd(old, total); overflow {
|
||||
return 0, ErrGasUintOverflow
|
||||
}
|
||||
return gas, nil
|
||||
return total, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -866,3 +866,83 @@ func BenchmarkTracerStepVsCallFrame(b *testing.B) {
|
|||
benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b)
|
||||
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b)
|
||||
}
|
||||
|
||||
// TestDelegatedAccountAccessCost tests that calling an account with an EIP-7702
|
||||
// delegation designator incurs the correct amount of gas based on the tracer.
|
||||
func TestDelegatedAccountAccessCost(t *testing.T) {
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||
statedb.SetCode(common.HexToAddress("0xff"), types.AddressToDelegation(common.HexToAddress("0xaa")))
|
||||
statedb.SetCode(common.HexToAddress("0xaa"), program.New().Return(0, 0).Bytes())
|
||||
|
||||
for i, tc := range []struct {
|
||||
code []byte
|
||||
step int
|
||||
want uint64
|
||||
}{
|
||||
{ // CALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALL), byte(vm.POP),
|
||||
},
|
||||
step: 7,
|
||||
want: 5455,
|
||||
},
|
||||
{ // CALLCODE(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.CALLCODE), byte(vm.POP),
|
||||
},
|
||||
step: 7,
|
||||
want: 5455,
|
||||
},
|
||||
{ // DELEGATECALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.DELEGATECALL), byte(vm.POP),
|
||||
},
|
||||
step: 6,
|
||||
want: 5455,
|
||||
},
|
||||
{ // STATICCALL(0xff)
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0x0,
|
||||
byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1),
|
||||
byte(vm.PUSH1), 0xff, byte(vm.DUP1), byte(vm.STATICCALL), byte(vm.POP),
|
||||
},
|
||||
step: 6,
|
||||
want: 5455,
|
||||
},
|
||||
{ // SELFDESTRUCT(0xff): should not be affected by resolution
|
||||
code: []byte{
|
||||
byte(vm.PUSH1), 0xff, byte(vm.SELFDESTRUCT),
|
||||
},
|
||||
step: 1,
|
||||
want: 7600,
|
||||
},
|
||||
} {
|
||||
var step = 0
|
||||
var have = uint64(0)
|
||||
Execute(tc.code, nil, &Config{
|
||||
ChainConfig: params.MergedTestChainConfig,
|
||||
State: statedb,
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: &tracing.Hooks{
|
||||
OnOpcode: func(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error) {
|
||||
// Uncomment to investigate failures:
|
||||
t.Logf("%d: %v %d", step, vm.OpCode(op).String(), cost)
|
||||
if step == tc.step {
|
||||
have = cost
|
||||
}
|
||||
step++
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if want := tc.want; have != want {
|
||||
t.Fatalf("testcase %d, gas report wrong, step %d, have %d want %d", i, tc.step, have, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue