mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-17 18:30:45 +00:00
core: remaining spec fixes for glamsterdam (#35279)
This PR fixes five remaining differences between master and glamsterdam-devnet-6: - RegularCost for Auths has been increased, since 8037 increases the WARM_ACCESS cost - Floor is anchored to the transaction base cost, not the intrinsic cost - Auth destinations need to be recorded in the BAL before the call is executed - Change the place where intrinsic gas is verified in calls to delegated addresses - Refund state gas directly to reservoir in outer tx frame --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
This commit is contained in:
parent
415fe376a3
commit
2919267b64
4 changed files with 34 additions and 20 deletions
|
|
@ -242,12 +242,10 @@ func FloorDataGas(rules params.Rules, from common.Address, to *common.Address, v
|
||||||
tokenCost = params.TxCostFloorPerToken
|
tokenCost = params.TxCostFloorPerToken
|
||||||
}
|
}
|
||||||
|
|
||||||
// The floor is anchored to the transaction base cost. Under EIP-2780 that
|
// The floor is anchored to the transaction base cost.
|
||||||
// base is the per-resource decomposition (the same one used by the intrinsic
|
|
||||||
// gas), so the floor never undercuts the transaction's own base.
|
|
||||||
floorBase := params.TxGas
|
floorBase := params.TxGas
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
floorBase = intrinsicBaseGasEIP2780(from, to, value)
|
floorBase = params.TxBaseCost2780
|
||||||
}
|
}
|
||||||
// Check for overflow
|
// Check for overflow
|
||||||
if (math.MaxUint64-floorBase)/tokenCost < tokens {
|
if (math.MaxUint64-floorBase)/tokenCost < tokens {
|
||||||
|
|
@ -739,7 +737,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// If the contract creation failed, or the destination was pre-existing,
|
// If the contract creation failed, or the destination was pre-existing,
|
||||||
// refund the account-creation state gas pre-charged in IntrinsicGas.
|
// refund the account-creation state gas pre-charged in IntrinsicGas.
|
||||||
if rules.IsAmsterdam && !creation {
|
if rules.IsAmsterdam && !creation {
|
||||||
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
st.gasRemaining.RefundStateToReservoir(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Increment the nonce for the next transaction.
|
// Increment the nonce for the next transaction.
|
||||||
|
|
@ -755,6 +753,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// performing the resolution and warming.
|
// performing the resolution and warming.
|
||||||
if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok {
|
if addr, ok := types.ParseDelegation(st.state.GetCode(*msg.To)); ok {
|
||||||
st.state.AddAddressToAccessList(addr)
|
st.state.AddAddressToAccessList(addr)
|
||||||
|
// Record in BAL
|
||||||
|
if rules.IsAmsterdam {
|
||||||
|
st.state.GetCode(addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// EIP-2780: charge the transaction's top-level recipient costs. If the
|
// EIP-2780: charge the transaction's top-level recipient costs. If the
|
||||||
// budget cannot cover the charge, the top frame halts out of gas.
|
// budget cannot cover the charge, the top frame halts out of gas.
|
||||||
|
|
@ -968,7 +970,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
||||||
authority, err := st.validateAuthorization(auth)
|
authority, err := st.validateAuthorization(auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
st.gasRemaining.RefundState((params.AccountCreationSize + params.AuthorizationCreationSize) * st.evm.Context.CostPerStateByte)
|
st.gasRemaining.RefundStateToReservoir((params.AccountCreationSize + params.AuthorizationCreationSize) * st.evm.Context.CostPerStateByte)
|
||||||
st.state.AddRefund(params.AccountWriteAmsterdam)
|
st.state.AddRefund(params.AccountWriteAmsterdam)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
@ -981,7 +983,7 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if st.state.Exist(authority) {
|
if st.state.Exist(authority) {
|
||||||
st.gasRemaining.RefundState(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
st.gasRemaining.RefundStateToReservoir(params.AccountCreationSize * st.evm.Context.CostPerStateByte)
|
||||||
st.state.AddRefund(params.AccountWriteAmsterdam)
|
st.state.AddRefund(params.AccountWriteAmsterdam)
|
||||||
}
|
}
|
||||||
authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
|
authBase := params.AuthorizationCreationSize * st.evm.Context.CostPerStateByte
|
||||||
|
|
@ -993,17 +995,17 @@ func (st *stateTransition) applyAuthorization(rules params.Rules, auth *types.Se
|
||||||
}
|
}
|
||||||
if auth.Address == (common.Address{}) {
|
if auth.Address == (common.Address{}) {
|
||||||
// Clearing writes no indicator, refill this auth's state charge.
|
// Clearing writes no indicator, refill this auth's state charge.
|
||||||
st.gasRemaining.RefundState(authBase)
|
st.gasRemaining.RefundStateToReservoir(authBase)
|
||||||
|
|
||||||
// The indicator was created by an earlier auth within the same
|
// The indicator was created by an earlier auth within the same
|
||||||
// transaction, refill the state charge as it's no longer justified.
|
// transaction, refill the state charge as it's no longer justified.
|
||||||
if curDelegated && !preDelegated {
|
if curDelegated && !preDelegated {
|
||||||
st.gasRemaining.RefundState(authBase)
|
st.gasRemaining.RefundStateToReservoir(authBase)
|
||||||
}
|
}
|
||||||
} else if curDelegated || preDelegated {
|
} else if curDelegated || preDelegated {
|
||||||
// The 23-byte slot is already occupied, overwriting it writes no
|
// The 23-byte slot is already occupied, overwriting it writes no
|
||||||
// new bytes, refill the state charge.
|
// new bytes, refill the state charge.
|
||||||
st.gasRemaining.RefundState(authBase)
|
st.gasRemaining.RefundStateToReservoir(authBase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,22 @@ func (g *GasBudget) RefundState(s uint64) {
|
||||||
g.UsedStateGas -= int64(s)
|
g.UsedStateGas -= int64(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefundStateToReservoir credits a state-gas refund directly to the
|
||||||
|
// reservoir, without repaying spilled regular gas first.
|
||||||
|
//
|
||||||
|
// Per the spec's set_delegation, authorization refunds (and the post-create
|
||||||
|
// new-account refund) are added to message.state_gas_reservoir directly, in
|
||||||
|
// contrast to the LIFO inline refunds handled by RefundState. The usage
|
||||||
|
// counter is decremented by the full amount, matching the spec's
|
||||||
|
// tx_state_gas = intrinsic_state + state_gas_used - state_refund and
|
||||||
|
// preserving the per-frame invariant:
|
||||||
|
//
|
||||||
|
// StateGas + UsedStateGas == initialStateGas + Spilled
|
||||||
|
func (g *GasBudget) RefundStateToReservoir(s uint64) {
|
||||||
|
g.StateGas += s
|
||||||
|
g.UsedStateGas -= int64(s)
|
||||||
|
}
|
||||||
|
|
||||||
// Forward drains `regular` regular gas and the entire state reservoir from
|
// Forward drains `regular` regular gas and the entire state reservoir from
|
||||||
// the parent's running budget and returns the initial GasBudget for a child
|
// the parent's running budget and returns the initial GasBudget for a child
|
||||||
// frame. The parent's UsedRegularGas is bumped by the forwarded amount so
|
// frame. The parent's UsedRegularGas is bumped by the forwarded amount so
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
|
||||||
// Terminate the gas measurement if the leftover gas is not sufficient,
|
// Terminate the gas measurement if the leftover gas is not sufficient,
|
||||||
// it can effectively prevent accessing the states in the following steps.
|
// it can effectively prevent accessing the states in the following steps.
|
||||||
// It's an essential safeguard before any stateful check.
|
// It's an essential safeguard before any stateful check.
|
||||||
if contract.Gas.RegularGas < intrinsicCost {
|
if !contract.chargeRegular(intrinsicCost, evm.Config.Tracer, tracing.GasChangeIgnored) {
|
||||||
return GasCosts{}, ErrOutOfGas
|
return GasCosts{}, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -422,7 +422,7 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
|
||||||
}
|
}
|
||||||
// Calculate the gas budget for the nested call. The costs defined by
|
// Calculate the gas budget for the nested call. The costs defined by
|
||||||
// EIP-2929 and EIP-7702 have already been applied.
|
// EIP-2929 and EIP-7702 have already been applied.
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, intrinsicCost, stack.back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, 0, stack.back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -430,11 +430,13 @@ func makeCallVariantGasCallEIP7702(intrinsicFunc intrinsicGasFunc, coldCost uint
|
||||||
// adding it to the return, it will be charged outside of this function, as
|
// 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
|
// part of the dynamic gas. This will ensure it is correctly reported to
|
||||||
// tracers.
|
// tracers.
|
||||||
contract.Gas.RegularGas += eip2929Cost + eip7702Cost
|
contract.Gas.RegularGas += eip2929Cost + eip7702Cost + intrinsicCost
|
||||||
|
|
||||||
// Undo the RegularGasUsed increments from the direct UseGas charges,
|
// Undo the RegularGasUsed increments from the direct UseGas charges,
|
||||||
// since this gas will be re-charged via the returned cost.
|
// since this gas will be re-charged via the returned cost.
|
||||||
contract.Gas.UsedRegularGas -= eip2929Cost
|
contract.Gas.UsedRegularGas -= eip2929Cost
|
||||||
contract.Gas.UsedRegularGas -= eip7702Cost
|
contract.Gas.UsedRegularGas -= eip7702Cost
|
||||||
|
contract.Gas.UsedRegularGas -= intrinsicCost
|
||||||
|
|
||||||
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
// Aggregate the gas costs from all components, including EIP-2929, EIP-7702,
|
||||||
// the CALL opcode itself, and the cost incurred by nested calls.
|
// the CALL opcode itself, and the cost incurred by nested calls.
|
||||||
|
|
|
||||||
|
|
@ -101,13 +101,7 @@ const (
|
||||||
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
TxAccessListStorageKeyGas uint64 = 1900 // Per storage key specified in EIP 2930 access list
|
||||||
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
TxAuthTupleGas uint64 = 12500 // Per auth tuple code specified in EIP-7702
|
||||||
|
|
||||||
// RegularPerAuthBaseCost is defined in EIP-8037 as the sum of:
|
RegularPerAuthBaseCost uint64 = 7816 // As defined by EIP-8037 and EIP-8038
|
||||||
//
|
|
||||||
// - Calldata cost: 1,616 (101 bytes × 16)
|
|
||||||
// - Recovering authority address (ecRecover)
|
|
||||||
// - Reading nonce and code of authority (cold access)
|
|
||||||
// - Storing values in already warm account: 2 x WARM_ACCESS
|
|
||||||
RegularPerAuthBaseCost uint64 = 7500
|
|
||||||
|
|
||||||
// EIP-2780: resource-based intrinsic transaction gas.
|
// EIP-2780: resource-based intrinsic transaction gas.
|
||||||
TxBaseCost2780 uint64 = 12000
|
TxBaseCost2780 uint64 = 12000
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue