Gloas bid and preference verification (#9036)

Gossip verify and cache bids and proposer preferences. This PR also ensures we subscribe to new fork topics one epoch early instead of two slots early. This is required for proposer preferences.


  


Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com>
This commit is contained in:
Eitan Seri-Levi
2026-04-15 01:39:59 +09:00
committed by GitHub
parent 8c8facd0cd
commit b40a178111
19 changed files with 2267 additions and 79 deletions

View File

@@ -531,26 +531,6 @@ pub fn compute_timestamp_at_slot<E: EthSpec>(
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
}
pub fn can_builder_cover_bid<E: EthSpec>(
state: &BeaconState<E>,
builder_index: BuilderIndex,
builder: &Builder,
bid_amount: u64,
spec: &ChainSpec,
) -> Result<bool, BlockProcessingError> {
let builder_balance = builder.balance;
let pending_withdrawals_amount =
state.get_pending_balance_to_withdraw_for_builder(builder_index)?;
let min_balance = spec
.min_deposit_amount
.safe_add(pending_withdrawals_amount)?;
if builder_balance < min_balance {
Ok(false)
} else {
Ok(builder_balance.safe_sub(min_balance)? >= bid_amount)
}
}
pub fn process_execution_payload_bid<E: EthSpec, Payload: AbstractExecPayload<E>>(
state: &mut BeaconState<E>,
block: BeaconBlockRef<'_, E, Payload>,
@@ -579,13 +559,13 @@ pub fn process_execution_payload_bid<E: EthSpec, Payload: AbstractExecPayload<E>
// Verify that the builder is active
block_verify!(
builder.is_active_at_finalized_epoch(state.finalized_checkpoint().epoch, spec),
state.is_active_builder(builder_index, spec)?,
ExecutionPayloadBidInvalid::BuilderNotActive(builder_index).into()
);
// Verify that the builder has funds to cover the bid
block_verify!(
can_builder_cover_bid(state, builder_index, builder, amount, spec)?,
state.can_builder_cover_bid(builder_index, amount, spec)?,
ExecutionPayloadBidInvalid::InsufficientBalance {
builder_index,
builder_balance: builder.balance,

View File

@@ -556,8 +556,7 @@ fn process_builder_voluntary_exit<E: EthSpec>(
)))?;
// Verify the builder is active
let finalized_epoch = state.finalized_checkpoint().epoch;
if !builder.is_active_at_finalized_epoch(finalized_epoch, spec) {
if !state.is_active_builder(builder_index, spec)? {
return Err(BlockOperationError::invalid(ExitInvalid::NotActive(
signed_exit.message.validator_index,
)));

View File

@@ -12,9 +12,9 @@ use types::{
BuilderIndex, ChainSpec, DepositData, Domain, Epoch, EthSpec, Fork, Hash256, InconsistentFork,
IndexedAttestation, IndexedAttestationRef, IndexedPayloadAttestation, ProposerSlashing,
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader,
SignedBlsToExecutionChange, SignedContributionAndProof, SignedExecutionPayloadBid, SignedRoot,
SignedVoluntaryExit, SigningData, Slot, SyncAggregate, SyncAggregatorSelectionData,
consts::gloas::BUILDER_INDEX_SELF_BUILD,
SignedBlsToExecutionChange, SignedContributionAndProof, SignedExecutionPayloadBid,
SignedProposerPreferences, SignedRoot, SignedVoluntaryExit, SigningData, Slot, SyncAggregate,
SyncAggregatorSelectionData, consts::gloas::BUILDER_INDEX_SELF_BUILD,
};
pub type Result<T> = std::result::Result<T, Error>;
@@ -389,6 +389,37 @@ where
Ok(SignatureSet::multiple_pubkeys(signature, pubkeys, message))
}
pub fn proposer_preferences_signature_set<'a, E, F>(
state: &'a BeaconState<E>,
get_pubkey: F,
signed_proposer_preferences: &'a SignedProposerPreferences,
spec: &'a ChainSpec,
) -> Result<SignatureSet<'a>>
where
E: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let preferences = &signed_proposer_preferences.message;
let validator_index = preferences.validator_index as usize;
let proposal_epoch = preferences.proposal_slot.epoch(E::slots_per_epoch());
let proposal_fork = spec.fork_at_epoch(proposal_epoch);
let domain = spec.get_domain(
proposal_epoch,
Domain::ProposerPreferences,
&proposal_fork,
state.genesis_validators_root(),
);
let message = preferences.signing_root(domain);
Ok(SignatureSet::single_pubkey(
&signed_proposer_preferences.signature,
get_pubkey(validator_index).ok_or(Error::ValidatorUnknown(validator_index as u64))?,
message,
))
}
pub fn execution_payload_bid_signature_set<'a, E, F>(
state: &'a BeaconState<E>,
get_builder_pubkey: F,
@@ -407,10 +438,16 @@ where
// See `process_execution_payload_bid`.
return Ok(None);
}
let bid_epoch = signed_execution_payload_bid
.message
.slot
.epoch(E::slots_per_epoch());
let bid_fork = spec.fork_at_epoch(bid_epoch);
let domain = spec.get_domain(
state.current_epoch(),
bid_epoch,
Domain::BeaconBuilder,
&state.fork(),
&bid_fork,
state.genesis_validators_root(),
);