Resolve merge conflicts

This commit is contained in:
Eitan Seri- Levi
2026-03-16 02:24:50 -07:00
270 changed files with 16594 additions and 8798 deletions

View File

@@ -4,19 +4,16 @@ use crate::attestation_verification::{
batch_verify_unaggregated_attestations,
};
use crate::beacon_block_streamer::{BeaconBlockStreamer, CheckCaches};
use crate::beacon_proposer_cache::{
BeaconProposerCache, EpochBlockProposers, ensure_state_can_determine_proposers_for_epoch,
};
use crate::beacon_proposer_cache::{BeaconProposerCache, EpochBlockProposers};
use crate::blob_verification::{GossipBlobError, GossipVerifiedBlob};
use crate::block_times_cache::BlockTimesCache;
use crate::block_verification::POS_PANDA_BANNER;
use crate::block_verification::{
BlockError, ExecutionPendingBlock, GossipVerifiedBlock, IntoExecutionPendingBlock,
check_block_is_finalized_checkpoint_or_descendant, check_block_relevancy,
signature_verify_chain_segment, verify_header_signature,
};
use crate::block_verification_types::{
AsBlock, AvailableExecutedBlock, BlockImportData, ExecutedBlock, RpcBlock,
AsBlock, AvailableExecutedBlock, BlockImportData, ExecutedBlock, RangeSyncBlock,
};
pub use crate::canonical_head::CanonicalHead;
use crate::chain_config::ChainConfig;
@@ -31,11 +28,12 @@ use crate::data_availability_router::{
};
use crate::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn};
use crate::early_attester_cache::EarlyAttesterCache;
use crate::envelope_times_cache::EnvelopeTimesCache;
use crate::errors::{BeaconChainError as Error, BlockProductionError};
use crate::events::ServerSentEventHandler;
use crate::execution_payload::{NotifyExecutionLayer, PreparePayloadHandle, get_execution_payload};
use crate::fetch_blobs::EngineGetBlobsOutput;
use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx, ForkChoiceWaitResult};
use crate::fork_choice_signal::{ForkChoiceSignalRx, ForkChoiceSignalTx};
use crate::graffiti_calculator::{GraffitiCalculator, GraffitiSettings};
use crate::kzg_utils::reconstruct_blobs;
use crate::light_client_finality_update_verification::{
@@ -60,6 +58,7 @@ use crate::observed_block_producers::ObservedBlockProducers;
use crate::observed_data_sidecars::ObservedDataSidecars;
use crate::observed_operations::{ObservationOutcome, ObservedOperations};
use crate::observed_slashable::ObservedSlashable;
use crate::pending_payload_envelopes::PendingPayloadEnvelopes;
use crate::persisted_beacon_chain::PersistedBeaconChain;
use crate::persisted_custody::persist_custody_context;
use crate::persisted_fork_choice::PersistedForkChoice;
@@ -70,7 +69,6 @@ use crate::sync_committee_verification::{
};
use crate::validator_monitor::{
HISTORIC_EPOCHS as VALIDATOR_MONITOR_HISTORIC_EPOCHS, ValidatorMonitor, get_slot_delay_ms,
timestamp_now,
};
use crate::validator_pubkey_cache::ValidatorPubkeyCache;
use crate::{
@@ -143,7 +141,7 @@ use types::*;
pub type ForkChoiceError = fork_choice::Error<crate::ForkChoiceStoreError>;
/// Alias to appease clippy.
type HashBlockTuple<E> = (Hash256, RpcBlock<E>);
type HashBlockTuple<E> = (Hash256, RangeSyncBlock<E>);
// These keys are all zero because they get stored in different columns, see `DBColumn` type.
pub const BEACON_CHAIN_DB_KEY: Hash256 = Hash256::ZERO;
@@ -239,7 +237,7 @@ pub struct PrePayloadAttributes {
///
/// The parent block number is not part of the payload attributes sent to the EL, but *is*
/// sent to builders via SSE.
pub parent_block_number: u64,
pub parent_block_number: Option<u64>,
/// The block root of the block being built upon (same block as fcU `headBlockHash`).
pub parent_beacon_block_root: Hash256,
}
@@ -423,6 +421,9 @@ pub struct BeaconChain<T: BeaconChainTypes> {
RwLock<ObservedDataSidecars<DataColumnSidecar<T::EthSpec>, T::EthSpec>>,
/// Maintains a record of slashable message seen over the gossip network or RPC.
pub observed_slashable: RwLock<ObservedSlashable<T::EthSpec>>,
/// Cache of pending execution payload envelopes for local block building.
/// Envelopes are stored here during block production and eventually published.
pub pending_payload_envelopes: RwLock<PendingPayloadEnvelopes<T::EthSpec>>,
/// Maintains a record of which validators have submitted voluntary exits.
pub observed_voluntary_exits: Mutex<ObservedOperations<SignedVoluntaryExit, T::EthSpec>>,
/// Maintains a record of which validators we've seen proposer slashings for.
@@ -463,6 +464,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub early_attester_cache: EarlyAttesterCache<T::EthSpec>,
/// A cache used to keep track of various block timings.
pub block_times_cache: Arc<RwLock<BlockTimesCache>>,
/// A cache used to keep track of various envelope timings.
pub envelope_times_cache: Arc<RwLock<EnvelopeTimesCache>>,
/// A cache used to track pre-finalization block roots for quick rejection.
pub pre_finalization_block_cache: PreFinalizationBlockCache,
/// A cache used to produce light_client server messages
@@ -663,7 +666,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.custody_context()
.as_ref()
.into();
debug!(?custody_context, "Persisting custody context to store");
// Pattern match to avoid accidentally missing fields and to ignore deprecated fields.
let CustodyContextSsz {
validator_custody_at_head,
epoch_validator_custody_requirements,
persisted_is_supernode: _,
} = &custody_context;
debug!(
validator_custody_at_head,
?epoch_validator_custody_requirements,
"Persisting custody context to store"
);
persist_custody_context::<T::EthSpec, T::HotStore, T::ColdStore>(
self.store.clone(),
@@ -1670,7 +1684,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let validator_index = *validator_index as usize;
committee_cache.get_attestation_duties(validator_index)
})
.collect();
.collect::<Result<Vec<_>, _>>()?;
Ok((duties, dependent_root))
},
@@ -2022,9 +2036,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// required information.
(justified_checkpoint, committee_len)
} else {
// We assume that the `Pending` state has the same shufflings as a `Full` state
// for the same block. Analysis: https://hackmd.io/@dapplion/gloas_dependant_root
let (advanced_state_root, mut state) = self
.store
.get_advanced_hot_state(beacon_block_root, request_slot, beacon_state_root)?
.get_advanced_hot_state(
beacon_block_root,
StatePayloadStatus::Pending,
request_slot,
beacon_state_root,
)?
.ok_or(Error::MissingBeaconState(beacon_state_root))?;
if state.current_epoch() < request_epoch {
partial_state_advance(
@@ -2730,7 +2751,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// This method is potentially long-running and should not run on the core executor.
pub fn filter_chain_segment(
self: &Arc<Self>,
chain_segment: Vec<RpcBlock<T::EthSpec>>,
chain_segment: Vec<RangeSyncBlock<T::EthSpec>>,
) -> Result<Vec<HashBlockTuple<T::EthSpec>>, Box<ChainSegmentResult>> {
// This function will never import any blocks.
let imported_blocks = vec![];
@@ -2839,7 +2860,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
/// `Self::process_block`.
pub async fn process_chain_segment(
self: &Arc<Self>,
chain_segment: Vec<RpcBlock<T::EthSpec>>,
chain_segment: Vec<RangeSyncBlock<T::EthSpec>>,
notify_execution_layer: NotifyExecutionLayer,
) -> ChainSegmentResult {
for block in chain_segment.iter() {
@@ -3399,11 +3420,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
);
}
self.data_availability_checker.put_pre_execution_block(
block_root,
unverified_block.block_cloned(),
block_source,
)?;
// Gloas blocks dont need to be inserted into the DA cache
// they are always available.
if !unverified_block
.block()
.fork_name_unchecked()
.gloas_enabled()
{
self.data_availability_checker.put_pre_execution_block(
block_root,
unverified_block.block_cloned(),
block_source,
)?;
}
// Start the Prometheus timer.
let _full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES);
@@ -3526,28 +3555,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.map_err(BeaconChainError::TokioJoin)?
.ok_or(BeaconChainError::RuntimeShutdown)??;
// Log the PoS pandas if a merge transition just occurred.
if payload_verification_outcome.is_valid_merge_transition_block {
info!("{}", POS_PANDA_BANNER);
info!(slot = %block.slot(), "Proof of Stake Activated");
info!(
terminal_pow_block_hash = ?block
.message()
.execution_payload()?
.parent_hash()
.into_root(),
);
info!(
merge_transition_block_root = ?block.message().tree_hash_root(),
);
info!(
merge_transition_execution_hash = ?block
.message()
.execution_payload()?
.block_hash()
.into_root(),
);
}
Ok(ExecutedBlock::new(
block,
import_data,
@@ -4102,23 +4109,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// See https://github.com/sigp/lighthouse/issues/2028
let (_, signed_block, block_data) = signed_block.deconstruct();
match self.get_blobs_or_columns_store_op(block_root, signed_block.slot(), block_data) {
Ok(Some(blobs_or_columns_store_op)) => {
ops.push(blobs_or_columns_store_op);
}
Ok(None) => {}
Err(e) => {
error!(
msg = "Restoring fork choice from disk",
error = &e,
?block_root,
"Failed to store data columns into the database"
);
return Err(self
.handle_import_block_db_write_error(fork_choice)
.err()
.unwrap_or(BlockError::InternalError(e)));
}
if let Some(blobs_or_columns_store_op) =
self.get_blobs_or_columns_store_op(block_root, signed_block.slot(), block_data)
{
ops.push(blobs_or_columns_store_op);
}
let block = signed_block.message();
@@ -4148,7 +4142,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// We're declaring the block "imported" at this point, since fork choice and the DB know
// about it.
let block_time_imported = timestamp_now();
let block_time_imported = self.slot_clock.now_duration().unwrap_or(Duration::MAX);
// compute state proofs for light client updates before inserting the state into the
// snapshot cache.
@@ -4217,7 +4211,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
/// Check block's consistentency with any configured weak subjectivity checkpoint.
fn check_block_against_weak_subjectivity_checkpoint(
pub(crate) fn check_block_against_weak_subjectivity_checkpoint(
&self,
block: BeaconBlockRef<T::EthSpec>,
block_root: Hash256,
@@ -4564,55 +4558,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(())
}
/// If configured, wait for the fork choice run at the start of the slot to complete.
#[instrument(level = "debug", skip_all)]
fn wait_for_fork_choice_before_block_production(
self: &Arc<Self>,
slot: Slot,
) -> Result<(), BlockProductionError> {
if let Some(rx) = &self.fork_choice_signal_rx {
let current_slot = self
.slot()
.map_err(|_| BlockProductionError::UnableToReadSlot)?;
let timeout = Duration::from_millis(self.config.fork_choice_before_proposal_timeout_ms);
if slot == current_slot || slot == current_slot + 1 {
match rx.wait_for_fork_choice(slot, timeout) {
ForkChoiceWaitResult::Success(fc_slot) => {
debug!(
%slot,
fork_choice_slot = %fc_slot,
"Fork choice successfully updated before block production"
);
}
ForkChoiceWaitResult::Behind(fc_slot) => {
warn!(
fork_choice_slot = %fc_slot,
%slot,
message = "this block may be orphaned",
"Fork choice notifier out of sync with block production"
);
}
ForkChoiceWaitResult::TimeOut => {
warn!(
message = "this block may be orphaned",
"Timed out waiting for fork choice before proposal"
);
}
}
} else {
error!(
%slot,
%current_slot,
message = "check clock sync, this block may be orphaned",
"Producing block at incorrect slot"
);
}
}
Ok(())
}
pub async fn produce_block_with_verification(
self: &Arc<Self>,
randao_reveal: Signature,
@@ -4659,165 +4604,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.await
}
/// Load a beacon state from the database for block production. This is a long-running process
/// that should not be performed in an `async` context.
fn load_state_for_block_production(
self: &Arc<Self>,
slot: Slot,
) -> Result<(BeaconState<T::EthSpec>, Option<Hash256>), BlockProductionError> {
let fork_choice_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_FORK_CHOICE_TIMES);
self.wait_for_fork_choice_before_block_production(slot)?;
drop(fork_choice_timer);
let state_load_timer = metrics::start_timer(&metrics::BLOCK_PRODUCTION_STATE_LOAD_TIMES);
// Atomically read some values from the head whilst avoiding holding cached head `Arc` any
// longer than necessary.
let (head_slot, head_block_root, head_state_root) = {
let head = self.canonical_head.cached_head();
(
head.head_slot(),
head.head_block_root(),
head.head_state_root(),
)
};
let (state, state_root_opt) = if head_slot < slot {
// Attempt an aggressive re-org if configured and the conditions are right.
if let Some((re_org_state, re_org_state_root)) =
self.get_state_for_re_org(slot, head_slot, head_block_root)
{
info!(
%slot,
head_to_reorg = %head_block_root,
"Proposing block to re-org current head"
);
(re_org_state, Some(re_org_state_root))
} else {
// Fetch the head state advanced through to `slot`, which should be present in the
// state cache thanks to the state advance timer.
let (state_root, state) = self
.store
.get_advanced_hot_state(head_block_root, slot, head_state_root)
.map_err(BlockProductionError::FailedToLoadState)?
.ok_or(BlockProductionError::UnableToProduceAtSlot(slot))?;
(state, Some(state_root))
}
} else {
warn!(
message = "this block is more likely to be orphaned",
%slot,
"Producing block that conflicts with head"
);
let state = self
.state_at_slot(slot - 1, StateSkipConfig::WithStateRoots)
.map_err(|_| BlockProductionError::UnableToProduceAtSlot(slot))?;
(state, None)
};
drop(state_load_timer);
Ok((state, state_root_opt))
}
/// Fetch the beacon state to use for producing a block if a 1-slot proposer re-org is viable.
///
/// This function will return `None` if proposer re-orgs are disabled.
#[instrument(skip_all, level = "debug")]
fn get_state_for_re_org(
&self,
slot: Slot,
head_slot: Slot,
canonical_head: Hash256,
) -> Option<(BeaconState<T::EthSpec>, Hash256)> {
let re_org_head_threshold = self.config.re_org_head_threshold?;
let re_org_parent_threshold = self.config.re_org_parent_threshold?;
if self.spec.proposer_score_boost.is_none() {
warn!(
reason = "this network does not have proposer boost enabled",
"Ignoring proposer re-org configuration"
);
return None;
}
let slot_delay = self
.slot_clock
.seconds_from_current_slot_start()
.or_else(|| {
warn!(error = "unable to read slot clock", "Not attempting re-org");
None
})?;
// Attempt a proposer re-org if:
//
// 1. It seems we have time to propagate and still receive the proposer boost.
// 2. The current head block was seen late.
// 3. The `get_proposer_head` conditions from fork choice pass.
let proposing_on_time =
slot_delay < self.config.re_org_cutoff(self.spec.get_slot_duration());
if !proposing_on_time {
debug!(reason = "not proposing on time", "Not attempting re-org");
return None;
}
let head_late = self.block_observed_after_attestation_deadline(canonical_head, head_slot);
if !head_late {
debug!(reason = "head not late", "Not attempting re-org");
return None;
}
// Is the current head weak and appropriate for re-orging?
let proposer_head_timer =
metrics::start_timer(&metrics::BLOCK_PRODUCTION_GET_PROPOSER_HEAD_TIMES);
let proposer_head = self
.canonical_head
.fork_choice_read_lock()
.get_proposer_head(
slot,
canonical_head,
re_org_head_threshold,
re_org_parent_threshold,
&self.config.re_org_disallowed_offsets,
self.config.re_org_max_epochs_since_finalization,
)
.map_err(|e| match e {
ProposerHeadError::DoNotReOrg(reason) => {
debug!(
%reason,
"Not attempting re-org"
);
}
ProposerHeadError::Error(e) => {
warn!(
error = ?e,
"Not attempting re-org"
);
}
})
.ok()?;
drop(proposer_head_timer);
let re_org_parent_block = proposer_head.parent_node.root;
let (state_root, state) = self
.store
.get_advanced_hot_state_from_cache(re_org_parent_block, slot)
.or_else(|| {
warn!(reason = "no state in cache", "Not attempting re-org");
None
})?;
info!(
weak_head = ?canonical_head,
parent = ?re_org_parent_block,
head_weight = proposer_head.head_node.weight,
threshold_weight = proposer_head.re_org_head_weight_threshold,
"Attempting re-org due to weak head"
);
Some((state, state_root))
}
/// Get the proposer index and `prev_randao` value for a proposal at slot `proposal_slot`.
///
/// The `proposer_head` may be the head block of `cached_head` or its parent. An error will
@@ -4900,15 +4686,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
return Ok(None);
};
// Get the `prev_randao` and parent block number.
let head_block_number = cached_head.head_block_number()?;
let (prev_randao, parent_block_number) = if proposer_head == head_parent_block_root {
(
cached_head.parent_random()?,
head_block_number.saturating_sub(1),
)
// TODO(gloas) not sure what to do here see this issue
// https://github.com/sigp/lighthouse/issues/8817
let (prev_randao, parent_block_number) = if self
.spec
.fork_name_at_slot::<T::EthSpec>(proposal_slot)
.gloas_enabled()
{
(cached_head.head_random()?, None)
} else {
(cached_head.head_random()?, head_block_number)
// Get the `prev_randao` and parent block number.
let head_block_number = cached_head.head_block_number()?;
if proposer_head == head_parent_block_root {
(
cached_head.parent_random()?,
Some(head_block_number.saturating_sub(1)),
)
} else {
(cached_head.head_random()?, Some(head_block_number))
}
};
Ok(Some(PrePayloadAttributes {
@@ -4933,12 +4729,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
if cached_head.head_block_root() == parent_block_root {
(Cow::Borrowed(head_state), cached_head.head_state_root())
} else {
// TODO(gloas): this function needs updating to be envelope-aware
// See: https://github.com/sigp/lighthouse/issues/8957
let block = self
.get_blinded_block(&parent_block_root)?
.ok_or(Error::MissingBeaconBlock(parent_block_root))?;
let (state_root, state) = self
.store
.get_advanced_hot_state(parent_block_root, proposal_slot, block.state_root())?
.get_advanced_hot_state(
parent_block_root,
StatePayloadStatus::Pending,
proposal_slot,
block.state_root(),
)?
.ok_or(Error::MissingBeaconState(block.state_root()))?;
(Cow::Owned(state), state_root)
};
@@ -5153,7 +4956,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
/// Check if the block with `block_root` was observed after the attestation deadline of `slot`.
fn block_observed_after_attestation_deadline(&self, block_root: Hash256, slot: Slot) -> bool {
pub(crate) fn block_observed_after_attestation_deadline(
&self,
block_root: Hash256,
slot: Slot,
) -> bool {
let block_delays = self.block_times_cache.read().get_block_delays(
block_root,
self.slot_clock
@@ -5512,7 +5319,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
err = ?e,
block_slot = %state.slot(),
?exit,
"Attempted to include an invalid proposer slashing"
"Attempted to include an invalid voluntary exit"
);
})
.is_ok()
@@ -5920,7 +5727,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
execution_payload_value,
)
}
BeaconState::Gloas(_) => return Err(BlockProductionError::GloasNotImplemented),
BeaconState::Gloas(_) => {
return Err(BlockProductionError::GloasNotImplemented(
"Attempting to produce gloas beacon block via non gloas code path".to_owned(),
));
}
};
let block = SignedBeaconBlock::from_block(
@@ -6258,13 +6069,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// Push a server-sent event (probably to a block builder or relay).
if let Some(event_handler) = &self.event_handler
&& event_handler.has_payload_attributes_subscribers()
&& let Some(parent_block_number) = pre_payload_attributes.parent_block_number
{
event_handler.register(EventKind::PayloadAttributes(ForkVersionedResponse {
data: SseExtendedPayloadAttributes {
proposal_slot: prepare_slot,
proposer_index: proposer,
parent_block_root: head_root,
parent_block_number: pre_payload_attributes.parent_block_number,
parent_block_number,
parent_block_hash: forkchoice_update_params.head_hash.unwrap_or_default(),
payload_attributes: payload_attributes.into(),
},
@@ -6315,21 +6127,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
input_params: ForkchoiceUpdateParameters,
override_forkchoice_update: OverrideForkchoiceUpdate,
) -> Result<(), Error> {
let next_slot = current_slot + 1;
// There is no need to issue a `forkchoiceUpdated` (fcU) message unless the Bellatrix fork
// has:
//
// 1. Already happened.
// 2. Will happen in the next slot.
//
// The reason for a fcU message in the slot prior to the Bellatrix fork is in case the
// terminal difficulty has already been reached and a payload preparation message needs to
// be issued.
if self.slot_is_prior_to_bellatrix(next_slot) {
return Ok(());
}
let execution_layer = self
.execution_layer
.as_ref()
@@ -6377,50 +6174,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
.unwrap_or_else(ExecutionBlockHash::zero),
)
} else {
// The head block does not have an execution block hash. We must check to see if we
// happen to be the proposer of the transition block, in which case we still need to
// send forkchoice_updated.
if self
.spec
.fork_name_at_slot::<T::EthSpec>(next_slot)
.bellatrix_enabled()
{
// We are post-bellatrix
if let Some(payload_attributes) = execution_layer
.payload_attributes(next_slot, params.head_root)
.await
{
// We are a proposer, check for terminal_pow_block_hash
if let Some(terminal_pow_block_hash) = execution_layer
.get_terminal_pow_block_hash(&self.spec, payload_attributes.timestamp())
.await
.map_err(Error::ForkchoiceUpdate)?
{
info!(
slot = %next_slot,
"Prepared POS transition block proposer"
);
(
params.head_root,
terminal_pow_block_hash,
params
.justified_hash
.unwrap_or_else(ExecutionBlockHash::zero),
params
.finalized_hash
.unwrap_or_else(ExecutionBlockHash::zero),
)
} else {
// TTD hasn't been reached yet, no need to update the EL.
return Ok(());
}
} else {
// We are not a proposer, no need to update the EL.
return Ok(());
}
} else {
return Ok(());
}
// Proposing the block for the merge is no longer supported.
return Ok(());
};
let forkchoice_updated_response = execution_layer
@@ -6713,6 +6468,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// sync anyway).
self.naive_aggregation_pool.write().prune(slot);
self.block_times_cache.write().prune(slot);
self.envelope_times_cache.write().prune(slot);
// Don't run heavy-weight tasks during sync.
if self.best_slot() + MAX_PER_SLOT_FORK_CHOICE_DISTANCE < slot {
@@ -6772,62 +6528,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
accessor: impl Fn(&EpochBlockProposers) -> Result<V, BeaconChainError>,
state_provider: impl FnOnce() -> Result<(Hash256, BeaconState<T::EthSpec>), E>,
) -> Result<V, E> {
let cache_entry = self
.beacon_proposer_cache
.lock()
.get_or_insert_key(proposal_epoch, shuffling_decision_block);
// If the cache entry is not initialised, run the code to initialise it inside a OnceCell.
// This prevents duplication of work across multiple threads.
//
// If it is already initialised, then `get_or_try_init` will return immediately without
// executing the initialisation code at all.
let epoch_block_proposers = cache_entry.get_or_try_init(|| {
// Fetch the state on-demand if the required epoch was missing from the cache.
// If the caller wants to not compute the state they must return an error here and then
// catch it at the call site.
let (state_root, mut state) = state_provider()?;
// Ensure the state can compute proposer duties for `epoch`.
ensure_state_can_determine_proposers_for_epoch(
&mut state,
state_root,
proposal_epoch,
&self.spec,
)?;
// Sanity check the state.
let latest_block_root = state.get_latest_block_root(state_root);
let state_decision_block_root = state.proposer_shuffling_decision_root_at_epoch(
proposal_epoch,
latest_block_root,
&self.spec,
)?;
if state_decision_block_root != shuffling_decision_block {
return Err(Error::ProposerCacheIncorrectState {
state_decision_block_root,
requested_decision_block_root: shuffling_decision_block,
}
.into());
}
let proposers = state.get_beacon_proposer_indices(proposal_epoch, &self.spec)?;
// Use fork_at_epoch rather than the state's fork, because post-Fulu we may not have
// advanced the state completely into the new epoch.
let fork = self.spec.fork_at_epoch(proposal_epoch);
debug!(
?shuffling_decision_block,
epoch = %proposal_epoch,
"Priming proposer shuffling cache"
);
Ok::<_, E>(EpochBlockProposers::new(proposal_epoch, fork, proposers))
})?;
// Run the accessor function on the computed epoch proposers.
accessor(epoch_block_proposers).map_err(Into::into)
crate::beacon_proposer_cache::with_proposer_cache(
&self.beacon_proposer_cache,
shuffling_decision_block,
proposal_epoch,
accessor,
state_provider,
&self.spec,
)
}
/// Runs the `map_fn` with the committee cache for `shuffling_epoch` from the chain with head
@@ -6965,9 +6673,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let (mut state, state_root) = if let Some((state, state_root)) = head_state_opt {
(state, state_root)
} else {
// We assume that the `Pending` state has the same shufflings as a `Full` state
// for the same block. Analysis: https://hackmd.io/@dapplion/gloas_dependant_root
let (state_root, state) = self
.store
.get_advanced_hot_state(head_block_root, target_slot, head_block.state_root)?
.get_advanced_hot_state(
head_block_root,
StatePayloadStatus::Pending,
target_slot,
head_block.state_root,
)?
.ok_or(Error::MissingBeaconState(head_block.state_root))?;
(state, state_root)
};
@@ -7503,16 +7218,16 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
block_root: Hash256,
block_slot: Slot,
block_data: AvailableBlockData<T::EthSpec>,
) -> Result<Option<StoreOp<'_, T::EthSpec>>, String> {
) -> Option<StoreOp<'_, T::EthSpec>> {
match block_data {
AvailableBlockData::NoData => Ok(None),
AvailableBlockData::NoData => None,
AvailableBlockData::Blobs(blobs) => {
debug!(
%block_root,
count = blobs.len(),
"Writing blobs to store"
);
Ok(Some(StoreOp::PutBlobs(block_root, blobs)))
Some(StoreOp::PutBlobs(block_root, blobs))
}
AvailableBlockData::DataColumns(mut data_columns) => {
let columns_to_custody = self.custody_columns_for_epoch(Some(
@@ -7528,7 +7243,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
count = data_columns.len(),
"Writing data columns to store"
);
Ok(Some(StoreOp::PutDataColumns(block_root, data_columns)))
Some(StoreOp::PutDataColumns(block_root, data_columns))
}
}
}