Gloas envelope consensus and more operations tests (#8781)

- Implement new `process_execution_payload` (as `process_execution_payload_envelope`).
- Implement new processing for deposit requests, including logic for adding new builders to the registry with index reuse.
- Enable a bunch more operations EF tests (most of them except bid processing/payload attestations/etc which we don't have code for yet).


Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Michael Sproul
2026-02-10 14:59:25 +11:00
committed by GitHub
parent 286b67f048
commit 7e275f8dc2
12 changed files with 643 additions and 34 deletions

View File

@@ -5,6 +5,7 @@ use crate::common::{
slash_validator,
};
use crate::per_block_processing::errors::{BlockProcessingError, IntoWithIndex};
use bls::{PublicKeyBytes, SignatureBytes};
use ssz_types::FixedVector;
use typenum::U33;
use types::consts::altair::{PARTICIPATION_FLAG_WEIGHTS, PROPOSER_WEIGHT, WEIGHT_DENOMINATOR};
@@ -38,9 +39,14 @@ pub fn process_operations<E: EthSpec, Payload: AbstractExecPayload<E>>(
process_bls_to_execution_changes(state, bls_to_execution_changes, verify_signatures, spec)?;
}
if state.fork_name_unchecked().electra_enabled() {
if state.fork_name_unchecked().electra_enabled() && !state.fork_name_unchecked().gloas_enabled()
{
state.update_pubkey_cache()?;
process_deposit_requests(state, &block_body.execution_requests()?.deposits, spec)?;
process_deposit_requests_pre_gloas(
state,
&block_body.execution_requests()?.deposits,
spec,
)?;
process_withdrawal_requests(state, &block_body.execution_requests()?.withdrawals, spec)?;
process_consolidation_requests(
state,
@@ -377,6 +383,31 @@ pub fn process_proposer_slashings<E: EthSpec>(
verify_proposer_slashing(proposer_slashing, state, verify_signatures, spec)
.map_err(|e| e.into_with_index(i))?;
// [New in Gloas:EIP7732]
// Remove the BuilderPendingPayment corresponding to this proposal
// if it is still in the 2-epoch window.
if state.fork_name_unchecked().gloas_enabled() {
let slot = proposer_slashing.signed_header_1.message.slot;
let proposal_epoch = slot.epoch(E::slots_per_epoch());
let slot_in_epoch = slot.as_usize().safe_rem(E::SlotsPerEpoch::to_usize())?;
let payment_index = if proposal_epoch == state.current_epoch() {
Some(E::SlotsPerEpoch::to_usize().safe_add(slot_in_epoch)?)
} else if proposal_epoch == state.previous_epoch() {
Some(slot_in_epoch)
} else {
None
};
if let Some(index) = payment_index {
let payment = state
.builder_pending_payments_mut()?
.get_mut(index)
.ok_or(BlockProcessingError::BuilderPaymentIndexOutOfBounds(index))?;
*payment = BuilderPendingPayment::default();
}
}
slash_validator(
state,
proposer_slashing.signed_header_1.message.proposer_index as usize,
@@ -736,7 +767,7 @@ pub fn process_withdrawal_requests<E: EthSpec>(
Ok(())
}
pub fn process_deposit_requests<E: EthSpec>(
pub fn process_deposit_requests_pre_gloas<E: EthSpec>(
state: &mut BeaconState<E>,
deposit_requests: &[DepositRequest],
spec: &ChainSpec,
@@ -763,6 +794,112 @@ pub fn process_deposit_requests<E: EthSpec>(
Ok(())
}
pub fn process_deposit_requests_post_gloas<E: EthSpec>(
state: &mut BeaconState<E>,
deposit_requests: &[DepositRequest],
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
for request in deposit_requests {
process_deposit_request_post_gloas(state, request, spec)?;
}
Ok(())
}
pub fn process_deposit_request_post_gloas<E: EthSpec>(
state: &mut BeaconState<E>,
deposit_request: &DepositRequest,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
// [New in Gloas:EIP7732]
// Regardless of the withdrawal credentials prefix, if a builder/validator
// already exists with this pubkey, apply the deposit to their balance
// TODO(gloas): this could be more efficient in the builder case, see:
// https://github.com/sigp/lighthouse/issues/8783
let builder_index = state
.builders()?
.iter()
.enumerate()
.find(|(_, builder)| builder.pubkey == deposit_request.pubkey)
.map(|(i, _)| i as u64);
let is_builder = builder_index.is_some();
let validator_index = state.get_validator_index(&deposit_request.pubkey)?;
let is_validator = validator_index.is_some();
let is_builder_prefix =
is_builder_withdrawal_credential(deposit_request.withdrawal_credentials, spec);
if is_builder || (is_builder_prefix && !is_validator) {
// Apply builder deposits immediately
apply_deposit_for_builder(
state,
builder_index,
deposit_request.pubkey,
deposit_request.withdrawal_credentials,
deposit_request.amount,
deposit_request.signature.clone(),
state.slot(),
spec,
)?;
return Ok(());
}
// Add validator deposits to the queue
let slot = state.slot();
state.pending_deposits_mut()?.push(PendingDeposit {
pubkey: deposit_request.pubkey,
withdrawal_credentials: deposit_request.withdrawal_credentials,
amount: deposit_request.amount,
signature: deposit_request.signature.clone(),
slot,
})?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn apply_deposit_for_builder<E: EthSpec>(
state: &mut BeaconState<E>,
builder_index_opt: Option<BuilderIndex>,
pubkey: PublicKeyBytes,
withdrawal_credentials: Hash256,
amount: u64,
signature: SignatureBytes,
slot: Slot,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
match builder_index_opt {
None => {
// Verify the deposit signature (proof of possession) which is not checked by the deposit contract
let deposit_data = DepositData {
pubkey,
withdrawal_credentials,
amount,
signature,
};
if is_valid_deposit_signature(&deposit_data, spec).is_ok() {
state.add_builder_to_registry(
pubkey,
withdrawal_credentials,
amount,
slot,
spec,
)?;
}
}
Some(builder_index) => {
state
.builders_mut()?
.get_mut(builder_index as usize)
.ok_or(BeaconStateError::UnknownBuilder(builder_index))?
.balance
.safe_add_assign(amount)?;
}
}
Ok(())
}
// Make sure to build the pubkey cache before calling this function
pub fn process_consolidation_requests<E: EthSpec>(
state: &mut BeaconState<E>,

View File

@@ -52,8 +52,6 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, E: EthSpec>(
///
/// Returns a descriptive `Err` if the attestation is malformed or does not accurately reflect the
/// prior blocks in `state`.
///
/// Spec v0.12.1
pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
state: &BeaconState<E>,
attestation: AttestationRef<'ctxt, E>,
@@ -94,8 +92,6 @@ pub fn verify_attestation_for_state<'ctxt, E: EthSpec>(
}
/// Check target epoch and source checkpoint.
///
/// Spec v0.12.1
fn verify_casper_ffg_vote<E: EthSpec>(
attestation: AttestationRef<E>,
state: &BeaconState<E>,