Gloas alpha spec 8 (#9315)

https://github.com/ethereum/consensus-specs/releases/tag/v1.7.0-alpha.8


  


Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu>

Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Eitan Seri-Levi
2026-05-21 23:21:20 -07:00
committed by GitHub
parent b5d5644eeb
commit 60abd4b5b9
36 changed files with 863 additions and 243 deletions

View File

@@ -43,9 +43,6 @@ pub(crate) fn verify_bid_consistency<E: EthSpec>(
if bid.fee_recipient != proposer_preferences.message.fee_recipient {
return Err(PayloadBidError::InvalidFeeRecipient);
}
if bid.gas_limit != proposer_preferences.message.gas_limit {
return Err(PayloadBidError::InvalidGasLimit);
}
let max_blobs_per_block =
spec.max_blobs_per_block(bid_slot.epoch(E::slots_per_epoch())) as usize;
@@ -161,7 +158,23 @@ impl<T: BeaconChainTypes> GossipVerifiedPayloadBid<T> {
});
}
// TODO(gloas) [IGNORE] bid.parent_block_hash is the block hash of a known execution payload in fork choice.
// TODO(gloas): [IGNORE] bid.parent_block_hash is the block hash of a known execution
// payload in fork choice.
// TODO(gloas): This uses head state's bid gas_limit as parent_gas_limit, which is only
// correct when the bid's parent is the head. If the parent is an ancestor further back
// this check may be inaccurate. Fixing this requires storing
// gas_limit in fork choice or looking it up from the store by parent_block_hash. Taking the above
// TODO into consideration maybe should persist parent block hash and gas limit in fork choice?
if let Ok(parent_bid) = head_state.latest_execution_payload_bid()
&& !is_gas_limit_target_compatible(
parent_bid.gas_limit,
signed_bid.message.gas_limit,
proposer_preferences.message.target_gas_limit,
)?
{
return Err(PayloadBidError::InvalidGasLimit);
}
drop(fork_choice);
@@ -263,8 +276,36 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}
/// Check if `gas_limit` is compatible with `target_gas_limit` under the
/// EIP-1559 transition rule from `parent_gas_limit`.
pub fn is_gas_limit_target_compatible(
parent_gas_limit: u64,
gas_limit: u64,
target_gas_limit: u64,
) -> Result<bool, PayloadBidError> {
let max_gas_limit_difference = (parent_gas_limit / 1024)
.max(1)
.checked_sub(1)
.ok_or(PayloadBidError::InvalidGasLimit)?;
let min_gas_limit = parent_gas_limit
.checked_sub(max_gas_limit_difference)
.ok_or(PayloadBidError::InvalidGasLimit)?;
let max_gas_limit = parent_gas_limit
.checked_add(max_gas_limit_difference)
.ok_or(PayloadBidError::InvalidGasLimit)?;
if target_gas_limit >= min_gas_limit && target_gas_limit <= max_gas_limit {
Ok(gas_limit == target_gas_limit)
} else if target_gas_limit > max_gas_limit {
Ok(gas_limit == max_gas_limit)
} else {
Ok(gas_limit == min_gas_limit)
}
}
#[cfg(test)]
mod tests {
use super::is_gas_limit_target_compatible;
use bls::Signature;
use kzg::KzgCommitment;
use ssz_types::VariableList;
@@ -288,11 +329,14 @@ mod tests {
}
}
fn make_preferences(fee_recipient: Address, gas_limit: u64) -> SignedProposerPreferences {
fn make_preferences(
fee_recipient: Address,
target_gas_limit: u64,
) -> SignedProposerPreferences {
SignedProposerPreferences {
message: ProposerPreferences {
fee_recipient,
gas_limit,
target_gas_limit,
..ProposerPreferences::default()
},
signature: Signature::empty(),
@@ -382,13 +426,41 @@ mod tests {
}
#[test]
fn test_gas_limit_mismatch() {
let (state, spec) = state_and_spec();
let current_slot = Slot::new(10);
let bid = make_bid(current_slot, Address::ZERO, 30_000_000);
let prefs = make_preferences(Address::ZERO, 50_000_000);
fn test_is_gas_limit_target_compatible_increase_within_limit() {
assert!(is_gas_limit_target_compatible(60_000_000, 60_000_100, 60_000_100).unwrap());
}
let result = verify_bid_consistency::<E>(&bid, current_slot, &prefs, &state, &spec);
assert!(matches!(result, Err(PayloadBidError::InvalidGasLimit)));
#[test]
fn test_is_gas_limit_target_compatible_increase_exceeding_limit() {
// max_diff = 60_000_000 / 1024 - 1 = 58_592
// max_gas_limit = 60_000_000 + 58_592 = 60_058_592
assert!(is_gas_limit_target_compatible(60_000_000, 60_058_592, 100_000_000).unwrap());
}
#[test]
fn test_is_gas_limit_target_compatible_increase_exceeding_off_by_one() {
assert!(!is_gas_limit_target_compatible(60_000_000, 60_058_593, 100_000_000).unwrap());
}
#[test]
fn test_is_gas_limit_target_compatible_decrease_within_limit() {
assert!(is_gas_limit_target_compatible(60_000_000, 59_999_990, 59_999_990).unwrap());
}
#[test]
fn test_is_gas_limit_target_compatible_decrease_exceeding_limit() {
// min_gas_limit = 60_000_000 - 58_592 = 59_941_408
assert!(is_gas_limit_target_compatible(60_000_000, 59_941_408, 30_000_000).unwrap());
}
#[test]
fn test_is_gas_limit_target_compatible_target_equals_parent() {
assert!(is_gas_limit_target_compatible(60_000_000, 60_000_000, 60_000_000).unwrap());
}
#[test]
fn test_is_gas_limit_target_compatible_parent_underflows() {
// parent=1023: max(1023/1024, 1) - 1 = max(0, 1) - 1 = 0, no change allowed
assert!(is_gas_limit_target_compatible(1023, 1023, 60_000_000).unwrap());
}
}

View File

@@ -48,7 +48,7 @@ pub enum PayloadBidError {
},
/// The bids fee recipient doesn't match the proposer preferences fee recipient.
InvalidFeeRecipient,
/// The bids gas limit doesn't match the proposer preferences gas limit.
/// The bid's gas limit is not compatible with the proposer's target gas limit.
InvalidGasLimit,
/// The bids execution payment is non-zero
ExecutionPaymentNonZero { execution_payment: u64 },

View File

@@ -101,6 +101,17 @@ impl TestContext {
root: Hash256::ZERO,
};
// Set a non-zero gas_limit on latest_execution_payload_bid so the gas limit
// compatibility check doesn't reject all bids at genesis.
if let Ok(bid) = state.latest_execution_payload_bid_mut() {
bid.gas_limit = 30_000_000;
}
// Update body_root to reflect the modified bid (genesis block embeds it).
let genesis_body_root = genesis_block(&state, &spec)
.expect("should build genesis block")
.body_root();
state.latest_block_header_mut().body_root = genesis_body_root;
let inactive_keypair = &keypairs[NUM_BUILDERS];
let inactive_creds = builder_withdrawal_credentials(&inactive_keypair.pk, &spec);
let inactive_builder_index = state
@@ -248,7 +259,7 @@ fn make_signed_preferences(
proposal_slot: Slot,
validator_index: u64,
fee_recipient: Address,
gas_limit: u64,
target_gas_limit: u64,
) -> Arc<SignedProposerPreferences> {
Arc::new(SignedProposerPreferences {
message: ProposerPreferences {
@@ -256,7 +267,7 @@ fn make_signed_preferences(
proposal_slot,
validator_index,
fee_recipient,
gas_limit,
target_gas_limit,
},
signature: Signature::empty(),
})