mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-03 03:31:14 +00:00
core: implement EIP-7928 spec change (#35260)
https://github.com/ethereum/EIPs/pull/11838/changes
This commit is contained in:
parent
00a773dad7
commit
ea242145cf
2 changed files with 83 additions and 0 deletions
|
|
@ -518,6 +518,73 @@ func TestBALStaticCallTargetIncluded(t *testing.T) {
|
|||
assertEmpty(t, assertPresent(t, b, target))
|
||||
}
|
||||
|
||||
// makeValueCaller emits a single value-transferring CALL-family op (CALL 0xf1
|
||||
// or CALLCODE 0xf2) against `target` with value=1, then STOPs. Used together
|
||||
// with a zero-balance caller to make the value transfer fail CanTransfer.
|
||||
func makeValueCaller(op byte, target common.Address) []byte {
|
||||
code := []byte{
|
||||
0x60, 0x00, // retSize
|
||||
0x60, 0x00, // retOff
|
||||
0x60, 0x00, // argsSize
|
||||
0x60, 0x00, // argsOff
|
||||
0x60, 0x01, // value = 1
|
||||
0x73, // PUSH20 target
|
||||
}
|
||||
code = append(code, target.Bytes()...)
|
||||
return append(code, 0x5a, op, 0x50, 0x00) // GAS, op, POP, STOP
|
||||
}
|
||||
|
||||
// TestBALCallToDelegatedTargetBalanceFail asserts the EIP-7928 rule revised in
|
||||
// ethereum/EIPs#11838: when a CALL targets an EIP-7702 delegated account and the
|
||||
// delegated address passes its access_cost gas check, the delegated
|
||||
// (implementation) address MUST appear in the BAL even when the call then fails
|
||||
// its sender-balance check, because the delegation is resolved before that
|
||||
// check. CALL routes through the EIP-8037 gas path.
|
||||
func TestBALCallToDelegatedTargetBalanceFail(t *testing.T) {
|
||||
delegated := common.HexToAddress("0xde1e9a7ed") // EOA carrying a 7702 designator
|
||||
impl := common.HexToAddress("0x111111") // delegation target (implementation)
|
||||
caller := common.HexToAddress("0xca11") // zero-balance contract issuing the CALL
|
||||
|
||||
env := newBALTestEnv(types.GenesisAlloc{
|
||||
caller: {Code: makeValueCaller(0xf1 /* CALL */, delegated), Balance: common.Big0},
|
||||
delegated: {Code: types.AddressToDelegation(impl), Balance: common.Big0},
|
||||
impl: {Code: []byte{0x00}, Balance: common.Big0}, // STOP
|
||||
})
|
||||
|
||||
b, _ := env.run(t, func(g *BlockGen) {
|
||||
g.AddTx(env.tx(0, &caller, big.NewInt(0), 1_000_000, 0, nil))
|
||||
})
|
||||
|
||||
assertPresent(t, b, caller)
|
||||
assertPresent(t, b, delegated)
|
||||
// The call failed its sender-balance check, so the implementation never
|
||||
// executed: it is recorded with an empty change set, but it MUST be present.
|
||||
assertEmpty(t, assertPresent(t, b, impl))
|
||||
}
|
||||
|
||||
// TestBALCallCodeToDelegatedTargetBalanceFail is the CALLCODE analogue of
|
||||
// TestBALCallToDelegatedTargetBalanceFail, exercising the EIP-7702 gas path
|
||||
// (CALLCODE/STATICCALL/DELEGATECALL) rather than the EIP-8037 one.
|
||||
func TestBALCallCodeToDelegatedTargetBalanceFail(t *testing.T) {
|
||||
delegated := common.HexToAddress("0xde1e9a7ed")
|
||||
impl := common.HexToAddress("0x111111")
|
||||
caller := common.HexToAddress("0xca11")
|
||||
|
||||
env := newBALTestEnv(types.GenesisAlloc{
|
||||
caller: {Code: makeValueCaller(0xf2 /* CALLCODE */, delegated), Balance: common.Big0},
|
||||
delegated: {Code: types.AddressToDelegation(impl), Balance: common.Big0},
|
||||
impl: {Code: []byte{0x00}, Balance: common.Big0}, // STOP
|
||||
})
|
||||
|
||||
b, _ := env.run(t, func(g *BlockGen) {
|
||||
g.AddTx(env.tx(0, &caller, big.NewInt(0), 1_000_000, 0, nil))
|
||||
})
|
||||
|
||||
assertPresent(t, b, caller)
|
||||
assertPresent(t, b, delegated)
|
||||
assertEmpty(t, assertPresent(t, b, impl))
|
||||
}
|
||||
|
||||
// ============================== Revert behaviour ==============================
|
||||
|
||||
// TestBALRevertedTxStillIncluded: a tx whose top-level call REVERTs still
|
||||
|
|
|
|||
|
|
@ -257,6 +257,14 @@ func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
|
|||
return gasFunc
|
||||
}
|
||||
|
||||
// recordDelegationAccess records the EIP-7702 delegated target in the block
|
||||
// access list (EIP-7928).
|
||||
func recordDelegationAccess(evm *EVM, target common.Address) {
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
evm.StateDB.GetCode(target)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
innerGasCallEIP7702 = makeCallVariantGasCallEIP7702(gasCallIntrinsic)
|
||||
gasDelegateCallEIP7702 = makeCallVariantGasCallEIP7702(gasDelegateCallIntrinsic)
|
||||
|
|
@ -336,6 +344,10 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc) gasFunc {
|
|||
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
// The delegated address has passed its gas check; record it in the
|
||||
// block access list now, before the call's sender-balance and
|
||||
// call-stack-depth checks.
|
||||
recordDelegationAccess(evm, target)
|
||||
}
|
||||
// Calculate the gas budget for the nested call. The costs defined by
|
||||
// EIP-2929 and EIP-7702 have already been applied.
|
||||
|
|
@ -416,6 +428,10 @@ func makeCallVariantGasCallEIP8037(regularFunc regularGasFunc, stateGasFunc stat
|
|||
if !contract.chargeRegular(eip7702Cost, evm.Config.Tracer, tracing.GasChangeCallStorageColdAccess) {
|
||||
return GasCosts{}, ErrOutOfGas
|
||||
}
|
||||
// The delegated address has passed its gas check; record it in the
|
||||
// block access list now, before the call's sender-balance and
|
||||
// call-stack-depth checks.
|
||||
recordDelegationAccess(evm, target)
|
||||
}
|
||||
|
||||
// Compute and charge state gas (new account creation) AFTER regular gas.
|
||||
|
|
|
|||
Loading…
Reference in a new issue