From b4ac2eefda54aba07344253e8ab21128a2259dea Mon Sep 17 00:00:00 2001 From: Eitan Seri-Levi Date: Thu, 30 Apr 2026 12:53:51 +0200 Subject: [PATCH] il inclusion list inclusive --- .../src/block_production/gloas.rs | 17 +++--- .../types/src/state/inclusion_list_cache.rs | 54 ++++++++++++++++++- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/beacon_node/beacon_chain/src/block_production/gloas.rs b/beacon_node/beacon_chain/src/block_production/gloas.rs index bc4f9bfd51..d03e2345fc 100644 --- a/beacon_node/beacon_chain/src/block_production/gloas.rs +++ b/beacon_node/beacon_chain/src/block_production/gloas.rs @@ -545,11 +545,6 @@ impl BeaconChain { | BeaconState::Deneb(_) | BeaconState::Electra(_) | BeaconState::Fulu(_) - | BeaconState::Heze(_) => { - return Err(BlockProductionError::InvalidBlockVariant( - "Cannot construct a block pre-Gloas".to_owned(), - )); - } BeaconState::Gloas(_) => BeaconBlock::Gloas(BeaconBlockGloas { slot, proposer_index, @@ -588,6 +583,16 @@ impl BeaconChain { }), BeaconState::Heze(_) => { let gloas_bid = signed_execution_payload_bid; + // Compute inclusion_list_bits for the previous slot's ILs + let il_slot = slot.saturating_sub(1_u64); + let inclusion_list_bits = state + .get_inclusion_list_committee(il_slot, &self.spec) + .map(|committee| { + self.inclusion_list_cache + .read() + .get_inclusion_list_bits(il_slot, &committee, false) + }) + .unwrap_or_default(); let heze_bid = SignedExecutionPayloadBidHeze { message: ExecutionPayloadBidHeze { parent_block_hash: gloas_bid.message.parent_block_hash, @@ -602,7 +607,7 @@ impl BeaconChain { execution_payment: gloas_bid.message.execution_payment, blob_kzg_commitments: gloas_bid.message.blob_kzg_commitments, execution_requests_root: gloas_bid.message.execution_requests_root, - inclusion_list_bits: Default::default(), + inclusion_list_bits, }, signature: gloas_bid.signature, }; diff --git a/consensus/types/src/state/inclusion_list_cache.rs b/consensus/types/src/state/inclusion_list_cache.rs index 1bb3ffa5a6..5a20f08bbf 100644 --- a/consensus/types/src/state/inclusion_list_cache.rs +++ b/consensus/types/src/state/inclusion_list_cache.rs @@ -1,4 +1,5 @@ -use crate::{EthSpec, SignedInclusionList, Slot, Transaction, Transactions}; +use crate::{EthSpec, InclusionListCommittee, SignedInclusionList, Slot, Transaction, Transactions}; +use ssz_types::BitVector; use std::collections::{HashMap, HashSet}; use tracing::info; @@ -123,4 +124,55 @@ impl InclusionListCache { let il: Vec<_> = txs.iter().cloned().collect(); il.try_into().ok() } + + /// Returns a bitvector with bits set for each committee member who submitted a valid, + /// non-equivocating inclusion list. + pub fn get_inclusion_list_bits( + &self, + slot: Slot, + committee: &InclusionListCommittee, + only_timely: bool, + ) -> BitVector { + let mut bits = BitVector::new(); + let Some(inner) = self.inner_map.get(&slot) else { + return bits; + }; + + for (i, validator_index) in committee.iter().enumerate() { + if inner.inclusion_list_equivocators.contains(validator_index) { + continue; + } + if !inner.inclusion_lists_seen.contains(validator_index) { + continue; + } + if only_timely { + if inner.inclusion_list_timeliness.get(validator_index) == Some(&true) { + bits.set(i, true).ok(); + } + } else { + bits.set(i, true).ok(); + } + } + + bits + } + + /// Checks that `inclusion_list_bits` is a superset of the locally observed bits. + pub fn is_inclusion_list_bits_inclusive( + &self, + slot: Slot, + committee: &InclusionListCommittee, + inclusion_list_bits: &BitVector, + only_timely: bool, + ) -> bool { + let local_bits = self.get_inclusion_list_bits(slot, committee, only_timely); + // Check that inclusion_list_bits is a superset of local_bits: + // every bit set in local_bits must also be set in inclusion_list_bits. + for i in 0..local_bits.len() { + if local_bits.get(i).unwrap_or(false) && !inclusion_list_bits.get(i).unwrap_or(false) { + return false; + } + } + true + } }