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 {
|
func makeCallVariantGasCallEIP7702(oldCalculator gasFunc) gasFunc {
|
||||||
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
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
|
// Check slot presence in the access list
|
||||||
warmAccess := evm.StateDB.AddressInAccessList(addr)
|
if !evm.StateDB.AddressInAccessList(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)
|
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
|
||||||
// Charge the remaining difference here already, to correctly calculate available
|
// Charge the remaining difference here already, to correctly calculate available
|
||||||
// gas for call
|
// gas for call
|
||||||
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
if !contract.UseGas(coldCost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
total += coldCost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if code is a delegation and if so, charge for resolution.
|
// 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
|
var cost uint64
|
||||||
if evm.StateDB.AddressInAccessList(addr) {
|
if evm.StateDB.AddressInAccessList(target) {
|
||||||
cost += params.WarmStorageReadCostEIP2929
|
cost = params.WarmStorageReadCostEIP2929
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.AddAddressToAccessList(addr)
|
evm.StateDB.AddAddressToAccessList(target)
|
||||||
cost += params.ColdAccountAccessCostEIP2929
|
cost = params.ColdAccountAccessCostEIP2929
|
||||||
}
|
}
|
||||||
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
if !contract.UseGas(cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||||
return 0, ErrOutOfGas
|
return 0, ErrOutOfGas
|
||||||
}
|
}
|
||||||
coldCost += cost
|
total += cost
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now call the old calculator, which takes into account
|
// Now call the old calculator, which takes into account
|
||||||
// - create new account
|
// - create new account
|
||||||
// - transfer value
|
// - transfer value
|
||||||
// - memory expansion
|
// - memory expansion
|
||||||
// - 63/64ths rule
|
// - 63/64ths rule
|
||||||
gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
old, err := oldCalculator(evm, contract, stack, mem, memorySize)
|
||||||
if warmAccess || err != nil {
|
if err != nil {
|
||||||
return gas, err
|
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
|
// Temporarily add the gas charge back to the contract and return value. By
|
||||||
// outside of this function, as part of the dynamic gas, and that will make it
|
// adding it to the return, it will be charged outside of this function, as
|
||||||
// also become correctly reported to tracers.
|
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||||
contract.Gas += coldCost
|
// tracers.
|
||||||
|
contract.Gas += total
|
||||||
|
|
||||||
var overflow bool
|
var overflow bool
|
||||||
if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
|
if total, overflow = math.SafeAdd(old, total); overflow {
|
||||||
return 0, ErrGasUintOverflow
|
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-step-10M", stepTracer, b)
|
||||||
benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, 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