mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 20:57:10 +00:00
initial straightforward merge changes
This commit is contained in:
@@ -27,7 +27,12 @@ use crate::data_availability_checker_v2::DataColumnReconstructionResult as DataC
|
||||
use crate::data_availability_router::{
|
||||
AvailabilityOutcome, DataAvailabilityRouter, ReconstructionOutcome,
|
||||
};
|
||||
use crate::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn};
|
||||
use crate::data_column_verification::{
|
||||
GossipDataColumnError, GossipPartialDataColumnError, GossipVerifiedDataColumn,
|
||||
GossipVerifiedPartialDataColumnHeader, KzgVerifiedCustodyPartialDataColumn,
|
||||
KzgVerifiedPartialDataColumn, PartialColumnVerificationResult,
|
||||
validate_partial_data_column_sidecar_for_gossip,
|
||||
};
|
||||
use crate::early_attester_cache::EarlyAttesterCache;
|
||||
use crate::envelope_times_cache::EnvelopeTimesCache;
|
||||
use crate::errors::{BeaconChainError as Error, BlockProductionError};
|
||||
@@ -59,6 +64,8 @@ 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::partial_data_column_assembler::PartialMergeResult;
|
||||
use crate::payload_bid_verification::payload_bid_cache::GossipVerifiedPayloadBidCache;
|
||||
#[cfg(not(test))]
|
||||
use crate::payload_envelope_streamer::{EnvelopeRequestSource, launch_payload_envelope_stream};
|
||||
use crate::pending_payload_envelopes::PendingPayloadEnvelopes;
|
||||
@@ -66,6 +73,7 @@ use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||
use crate::persisted_custody::persist_custody_context;
|
||||
use crate::persisted_fork_choice::PersistedForkChoice;
|
||||
use crate::pre_finalization_cache::PreFinalizationBlockCache;
|
||||
use crate::proposer_preferences_verification::proposer_preference_cache::GossipVerifiedProposerPreferenceCache;
|
||||
use crate::shuffling_cache::{BlockShufflingIds, ShufflingCache};
|
||||
use crate::sync_committee_verification::{
|
||||
Error as SyncCommitteeError, VerifiedSyncCommitteeMessage, VerifiedSyncContribution,
|
||||
@@ -81,8 +89,8 @@ use crate::{
|
||||
use bls::{PublicKey, PublicKeyBytes, Signature};
|
||||
use eth2::beacon_response::ForkVersionedResponse;
|
||||
use eth2::types::{
|
||||
EventKind, SseBlobSidecar, SseBlock, SseDataColumnSidecar, SseExtendedPayloadAttributes,
|
||||
SseHead,
|
||||
EventKind, PtcDuty, SseBlobSidecar, SseBlock, SseDataColumnSidecar,
|
||||
SseExtendedPayloadAttributes, SseHead,
|
||||
};
|
||||
use execution_layer::{
|
||||
BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer,
|
||||
@@ -114,8 +122,8 @@ use state_processing::{
|
||||
epoch_cache::initialize_epoch_cache,
|
||||
per_block_processing,
|
||||
per_block_processing::{
|
||||
VerifySignatures, errors::AttestationValidationError, get_expected_withdrawals,
|
||||
verify_attestation_for_block_inclusion,
|
||||
VerifySignatures, apply_parent_execution_payload, errors::AttestationValidationError,
|
||||
get_expected_withdrawals, verify_attestation_for_block_inclusion,
|
||||
},
|
||||
per_slot_processing,
|
||||
state_advance::{complete_state_advance, partial_state_advance},
|
||||
@@ -471,6 +479,10 @@ pub struct BeaconChain<T: BeaconChainTypes> {
|
||||
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 store gossip verified payload bids.
|
||||
pub gossip_verified_payload_bid_cache: GossipVerifiedPayloadBidCache<T>,
|
||||
/// A cache used to store gossip verified proposer preferences.
|
||||
pub gossip_verified_proposer_preferences_cache: GossipVerifiedProposerPreferenceCache,
|
||||
/// A cache used to produce light_client server messages
|
||||
pub light_client_server_cache: LightClientServerCache<T>,
|
||||
/// Sender to signal the light_client server to produce new updates
|
||||
@@ -551,6 +563,9 @@ impl FinalizationAndCanonicity {
|
||||
}
|
||||
}
|
||||
|
||||
type ProcessedPartialColumnStatus<E> =
|
||||
Option<(AvailabilityProcessingStatus, PartialMergeResult<E>)>;
|
||||
|
||||
impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// Checks if a block is finalized.
|
||||
/// The finalization check is done with the block slot. The block root is used to verify that
|
||||
@@ -1710,6 +1725,46 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
Ok((duties, dependent_root, execution_status))
|
||||
}
|
||||
|
||||
/// Get PTC duties for validators at a given epoch.
|
||||
///
|
||||
/// TODO(gloas): per-validator `get_ptc_assignment` makes this O(N * slots_per_epoch * PTCSize).
|
||||
/// A future ptc cache (or a single-pass `ptc_window` walk) can drop this to
|
||||
/// O(slots_per_epoch * PTCSize + N).
|
||||
pub fn compute_ptc_duties(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
epoch: Epoch,
|
||||
validator_indices: &[u64],
|
||||
dependent_block_root: Hash256,
|
||||
) -> Result<(Vec<Option<PtcDuty>>, Hash256), Error> {
|
||||
// The ptc_window only covers previous, current, and next epochs.
|
||||
let relative_epoch = RelativeEpoch::from_epoch(state.current_epoch(), epoch)
|
||||
.map_err(Error::IncorrectStateForAttestation)?;
|
||||
|
||||
let dependent_root =
|
||||
state.attester_shuffling_decision_root(dependent_block_root, relative_epoch)?;
|
||||
|
||||
let pubkey_cache = self.validator_pubkey_cache.read();
|
||||
|
||||
let duties = validator_indices
|
||||
.iter()
|
||||
.map(|&validator_index| -> Result<Option<PtcDuty>, Error> {
|
||||
let Some(&pubkey) = pubkey_cache.get_pubkey_bytes(validator_index as usize) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let slot_opt =
|
||||
state.get_ptc_assignment(validator_index as usize, epoch, &self.spec)?;
|
||||
Ok(slot_opt.map(|slot| PtcDuty {
|
||||
validator_index,
|
||||
slot,
|
||||
pubkey,
|
||||
}))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
Ok((duties, dependent_root))
|
||||
}
|
||||
|
||||
pub fn get_aggregated_attestation(
|
||||
&self,
|
||||
attestation: AttestationRef<T::EthSpec>,
|
||||
@@ -1947,6 +2002,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let beacon_block_root;
|
||||
let beacon_state_root;
|
||||
let target;
|
||||
let is_same_slot_attestation;
|
||||
let current_epoch_attesting_info: Option<(Checkpoint, usize)>;
|
||||
let head_timer = metrics::start_timer(&metrics::ATTESTATION_PRODUCTION_HEAD_SCRAPE_SECONDS);
|
||||
let head_span = debug_span!("attestation_production_head_scrape").entered();
|
||||
@@ -1987,11 +2043,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// When attesting to the head slot or later, always use the head of the chain.
|
||||
beacon_block_root = head.beacon_block_root;
|
||||
beacon_state_root = head.beacon_state_root();
|
||||
is_same_slot_attestation = request_slot == head.beacon_block.slot();
|
||||
} else {
|
||||
// Permit attesting to slots *prior* to the current head. This is desirable when
|
||||
// the VC and BN are out-of-sync due to time issues or overloading.
|
||||
beacon_block_root = *head_state.get_block_root(request_slot)?;
|
||||
beacon_state_root = *head_state.get_state_root(request_slot)?;
|
||||
|
||||
// Fetch the previous block root. If the previous block root equals
|
||||
// the block root being attested to, the `request_slot` is a skipped slot
|
||||
// and this is not a same slot attestation.
|
||||
let prior_slot_root = head_state
|
||||
.get_block_root(request_slot.saturating_sub(1u64))
|
||||
.ok();
|
||||
is_same_slot_attestation = prior_slot_root != Some(&beacon_block_root);
|
||||
};
|
||||
|
||||
let target_slot = request_epoch.start_slot(T::EthSpec::slots_per_epoch());
|
||||
@@ -2058,12 +2123,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// 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,
|
||||
StatePayloadStatus::Pending,
|
||||
request_slot,
|
||||
beacon_state_root,
|
||||
)?
|
||||
.get_advanced_hot_state(beacon_block_root, request_slot, beacon_state_root)?
|
||||
.ok_or(Error::MissingBeaconState(beacon_state_root))?;
|
||||
if state.current_epoch() < request_epoch {
|
||||
partial_state_advance(
|
||||
@@ -2086,6 +2146,21 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
)
|
||||
};
|
||||
|
||||
// For gloas the attestation data index indicates payload presence:
|
||||
// `payload_present=false` for same-slot attestations or when payload not received.
|
||||
// `payload_present=true` when attesting to a prior slot whose payload has been received.
|
||||
let payload_present = if self
|
||||
.spec
|
||||
.fork_name_at_slot::<T::EthSpec>(request_slot)
|
||||
.gloas_enabled()
|
||||
&& !is_same_slot_attestation
|
||||
{
|
||||
self.canonical_head
|
||||
.block_has_canonical_payload(&beacon_block_root, &self.spec)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(Attestation::<T::EthSpec>::empty_for_signing(
|
||||
request_index,
|
||||
committee_len,
|
||||
@@ -2093,10 +2168,55 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
beacon_block_root,
|
||||
justified_checkpoint,
|
||||
target,
|
||||
payload_present,
|
||||
&self.spec,
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Produce a `PayloadAttestationData` for a PTC validator to sign.
|
||||
///
|
||||
/// This is used by PTC (Payload Timeliness Committee) validators to attest to the
|
||||
/// presence/absence of an execution payload and blobs for a given slot.
|
||||
pub fn produce_payload_attestation_data(
|
||||
&self,
|
||||
request_slot: Slot,
|
||||
) -> Result<PayloadAttestationData, Error> {
|
||||
let _timer = metrics::start_timer(&metrics::PAYLOAD_ATTESTATION_PRODUCTION_SECONDS);
|
||||
|
||||
// Payload attestations are only valid for the current slot
|
||||
let current_slot = self.slot()?;
|
||||
if request_slot != current_slot {
|
||||
return Err(Error::InvalidSlot(request_slot));
|
||||
}
|
||||
|
||||
// Check if we've seen a block for this slot from the canonical head
|
||||
let head = self.head_snapshot();
|
||||
if head.beacon_block.slot() != request_slot {
|
||||
return Err(Error::NoBlockForSlot(request_slot));
|
||||
}
|
||||
|
||||
let beacon_block_root = head.beacon_block_root;
|
||||
|
||||
// TODO(gloas) do we want to use a dedicated envelope cache instead?
|
||||
// Maybe the new gloas DA cache? (Or should the gloas DA cache use
|
||||
// the envelopes_times_cache internally?)
|
||||
let payload_present = self
|
||||
.envelope_times_cache
|
||||
.read()
|
||||
.cache
|
||||
.contains_key(&beacon_block_root);
|
||||
|
||||
// TODO(EIP-7732): Check blob data availability. For now, default to true.
|
||||
let blob_data_available = true;
|
||||
|
||||
Ok(PayloadAttestationData {
|
||||
beacon_block_root,
|
||||
slot: head.beacon_block.slot(),
|
||||
payload_present,
|
||||
blob_data_available,
|
||||
})
|
||||
}
|
||||
|
||||
/// Performs the same validation as `Self::verify_unaggregated_attestation_for_gossip`, but for
|
||||
/// multiple attestations using batch BLS verification. Batch verification can provide
|
||||
/// significant CPU-time savings compared to individual verification.
|
||||
@@ -2258,6 +2378,59 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_partial_data_column_header_for_gossip(
|
||||
&self,
|
||||
block_root: Hash256,
|
||||
data_column_header: PartialDataColumnHeader<T::EthSpec>,
|
||||
) -> Result<GossipVerifiedPartialDataColumnHeader<T::EthSpec>, GossipPartialDataColumnError>
|
||||
{
|
||||
metrics::inc_counter(&metrics::PARTIAL_DATA_COLUMN_SIDECAR_HEADER_PROCESSING_REQUESTS);
|
||||
let _timer = metrics::start_timer(
|
||||
&metrics::PARTIAL_DATA_COLUMN_SIDECAR_HEADER_GOSSIP_VERIFICATION_TIMES,
|
||||
);
|
||||
let Some(assembler) = self.data_availability_checker.partial_assembler() else {
|
||||
return Err(GossipPartialDataColumnError::PartialColumnsDisabled);
|
||||
};
|
||||
if let Some(cached_header) = assembler.get_header(&block_root) {
|
||||
return if *cached_header == data_column_header {
|
||||
metrics::inc_counter(&metrics::PARTIAL_DATA_COLUMN_SIDECAR_HEADER_PROCESSING_DUPES);
|
||||
Ok(GossipVerifiedPartialDataColumnHeader::new_from_cached(
|
||||
cached_header,
|
||||
))
|
||||
} else {
|
||||
Err(GossipPartialDataColumnError::HeaderMismatches)
|
||||
};
|
||||
}
|
||||
|
||||
GossipVerifiedPartialDataColumnHeader::new(block_root, data_column_header, self).inspect(
|
||||
|_| {
|
||||
metrics::inc_counter(
|
||||
&metrics::PARTIAL_DATA_COLUMN_SIDECAR_HEADER_PROCESSING_SUCCESSES,
|
||||
);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "trace")]
|
||||
pub fn verify_partial_data_column_sidecar_for_gossip(
|
||||
self: &Arc<Self>,
|
||||
data_column_sidecar: Box<PartialDataColumn<T::EthSpec>>,
|
||||
seen_timestamp: Duration,
|
||||
) -> PartialColumnVerificationResult<T::EthSpec> {
|
||||
metrics::inc_counter(&metrics::PARTIAL_DATA_COLUMN_SIDECAR_PROCESSING_REQUESTS);
|
||||
let _timer =
|
||||
metrics::start_timer(&metrics::PARTIAL_DATA_COLUMN_SIDECAR_GOSSIP_VERIFICATION_TIMES);
|
||||
let ret = validate_partial_data_column_sidecar_for_gossip(
|
||||
data_column_sidecar,
|
||||
self,
|
||||
seen_timestamp,
|
||||
);
|
||||
if matches!(ret, PartialColumnVerificationResult::Ok { .. }) {
|
||||
metrics::inc_counter(&metrics::PARTIAL_DATA_COLUMN_SIDECAR_PROCESSING_SUCCESSES);
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[instrument(skip_all, level = "trace")]
|
||||
pub fn verify_blob_sidecar_for_gossip(
|
||||
self: &Arc<Self>,
|
||||
@@ -3089,6 +3262,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Cache the data columns in the processing cache, process it, then evict it from the cache if it was
|
||||
/// imported or errors.
|
||||
/// Only accepts full columns. Partials are handled via PartialDataColumnAssembler.
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
pub async fn process_gossip_data_columns(
|
||||
self: &Arc<Self>,
|
||||
@@ -3131,6 +3305,93 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Process a gossip-verified partial data column by attempting to merge it in the assembler.
|
||||
/// Returns the merge result which indicates if a column was completed.
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
pub async fn process_gossip_partial_data_column(
|
||||
self: &Arc<Self>,
|
||||
verified_partial: KzgVerifiedPartialDataColumn<T::EthSpec>,
|
||||
verified_header: GossipVerifiedPartialDataColumnHeader<T::EthSpec>,
|
||||
slot: Slot,
|
||||
) -> Result<ProcessedPartialColumnStatus<T::EthSpec>, BlockError> {
|
||||
let block_root = verified_partial.block_root();
|
||||
let partial = verified_partial.as_data_column();
|
||||
let index_str = partial.index.to_string();
|
||||
metrics::inc_counter_vec_by(
|
||||
&metrics::BEACON_PARTIAL_MESSAGE_CELLS_RECEIVED_TOTAL,
|
||||
&[index_str.as_str()],
|
||||
partial.sidecar.column.len() as u64,
|
||||
);
|
||||
|
||||
// Check if we have custody of this column
|
||||
let sampling_columns =
|
||||
self.sampling_columns_for_epoch(slot.epoch(T::EthSpec::slots_per_epoch()));
|
||||
let verified_partial = if sampling_columns.contains(&partial.index) {
|
||||
KzgVerifiedCustodyPartialDataColumn::from_asserted_custody(verified_partial)
|
||||
} else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// If this block has already been imported to forkchoice it must have been available
|
||||
if self
|
||||
.canonical_head
|
||||
.fork_choice_read_lock()
|
||||
.contains_block(&block_root)
|
||||
{
|
||||
return Err(BlockError::DuplicateFullyImported(block_root));
|
||||
}
|
||||
|
||||
let Some(assembler) = self.data_availability_checker.partial_assembler() else {
|
||||
// Partial messages are apparently not activated
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
// Merge the partial into the assembler
|
||||
let merge_result = assembler
|
||||
.merge_partials(
|
||||
block_root,
|
||||
vec![verified_partial],
|
||||
verified_header.into_header(),
|
||||
)
|
||||
.ok_or_else(|| BlockError::InternalError("No assembly found for block".to_string()))?;
|
||||
|
||||
metrics::inc_counter_vec_by(
|
||||
&metrics::BEACON_PARTIAL_MESSAGE_USEFUL_CELLS_TOTAL,
|
||||
&[index_str.as_str()],
|
||||
merge_result.added_cells as u64,
|
||||
);
|
||||
|
||||
let availability = if !merge_result.full_columns.is_empty() {
|
||||
metrics::inc_counter_vec_by(
|
||||
&metrics::BEACON_PARTIAL_MESSAGE_COLUMN_COMPLETIONS_TOTAL,
|
||||
&[index_str.as_str()],
|
||||
merge_result.full_columns.len() as u64,
|
||||
);
|
||||
|
||||
self.emit_sse_data_column_sidecar_events(
|
||||
&block_root,
|
||||
merge_result
|
||||
.full_columns
|
||||
.iter()
|
||||
.map(|column| column.as_data_column()),
|
||||
);
|
||||
|
||||
let availability = self
|
||||
.data_availability_checker
|
||||
.put_kzg_verified_custody_data_columns(
|
||||
block_root,
|
||||
merge_result.full_columns.clone(),
|
||||
)?;
|
||||
|
||||
self.process_availability(slot, availability, || Ok(()))
|
||||
.await?
|
||||
} else {
|
||||
AvailabilityProcessingStatus::MissingComponents(slot, block_root)
|
||||
};
|
||||
|
||||
Ok(Some((availability, merge_result)))
|
||||
}
|
||||
|
||||
/// Cache the blobs in the processing cache, process it, then evict it from the cache if it was
|
||||
/// imported or errors.
|
||||
#[instrument(skip_all, level = "debug")]
|
||||
@@ -3636,6 +3897,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
/// Checks if the provided data column can make any cached blocks available, and imports immediately
|
||||
/// if so, otherwise caches the data column in the data availability checker.
|
||||
/// Check gossip data columns for availability and import. Only accepts full columns.
|
||||
/// Partials are handled separately via PartialDataColumnAssembler.
|
||||
async fn check_gossip_data_columns_availability_and_import(
|
||||
self: &Arc<Self>,
|
||||
slot: Slot,
|
||||
@@ -3790,13 +4053,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// from RPC.
|
||||
for header in custody_columns
|
||||
.into_iter()
|
||||
.map(|c| c.signed_block_header.clone())
|
||||
.map(|c| &c.signed_block_header)
|
||||
.unique()
|
||||
{
|
||||
// Return an error if *any* header signature is invalid, we do not want to import this
|
||||
// list of blobs into the DA checker. However, we will process any valid headers prior
|
||||
// to the first invalid header in the slashable cache & slasher.
|
||||
verify_header_signature::<T, BlockError>(self, &header)?;
|
||||
verify_header_signature::<T, BlockError>(self, header)?;
|
||||
|
||||
slashable_cache
|
||||
.observe_slashable(
|
||||
@@ -3806,7 +4069,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
)
|
||||
.map_err(|e| BlockError::BeaconChainError(Box::new(e.into())))?;
|
||||
if let Some(slasher) = self.slasher.as_ref() {
|
||||
slasher.accept_block_header(header);
|
||||
slasher.accept_block_header(header.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -4582,7 +4845,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
//
|
||||
// Load the parent state from disk.
|
||||
let chain = self.clone();
|
||||
let (state, state_root_opt) = self
|
||||
let block_production_state = self
|
||||
.task_executor
|
||||
.spawn_blocking_handle(
|
||||
move || chain.load_state_for_block_production(slot),
|
||||
@@ -4591,6 +4854,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.ok_or(BlockProductionError::ShuttingDown)?
|
||||
.await
|
||||
.map_err(BlockProductionError::TokioJoin)??;
|
||||
let (state, state_root_opt) = (
|
||||
block_production_state.state,
|
||||
block_production_state.state_root,
|
||||
);
|
||||
|
||||
// Part 2/2 (async, with some blocking components)
|
||||
//
|
||||
@@ -4725,42 +4992,48 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
proposal_slot: Slot,
|
||||
) -> Result<Withdrawals<T::EthSpec>, Error> {
|
||||
let cached_head = self.canonical_head.cached_head();
|
||||
let head_block = &cached_head.snapshot.beacon_block;
|
||||
let head_block_root = cached_head.head_block_root();
|
||||
let head_state = &cached_head.snapshot.beacon_state;
|
||||
|
||||
let parent_block_root = forkchoice_update_params.head_root;
|
||||
|
||||
let (unadvanced_state, unadvanced_state_root) =
|
||||
if cached_head.head_block_root() == parent_block_root {
|
||||
(Cow::Borrowed(head_state), cached_head.head_state_root())
|
||||
let (unadvanced_state, unadvanced_state_root, parent_bid_block_hash) =
|
||||
if parent_block_root == head_block_root {
|
||||
(
|
||||
Cow::Borrowed(head_state),
|
||||
cached_head.head_state_root(),
|
||||
head_block.payload_bid_block_hash().ok(),
|
||||
)
|
||||
} 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,
|
||||
StatePayloadStatus::Pending,
|
||||
proposal_slot,
|
||||
block.state_root(),
|
||||
)?
|
||||
.get_advanced_hot_state(parent_block_root, proposal_slot, block.state_root())?
|
||||
.ok_or(Error::MissingBeaconState(block.state_root()))?;
|
||||
(Cow::Owned(state), state_root)
|
||||
(
|
||||
Cow::Owned(state),
|
||||
state_root,
|
||||
block.payload_bid_block_hash().ok(),
|
||||
)
|
||||
};
|
||||
|
||||
// Parent state epoch is the same as the proposal, we don't need to advance because the
|
||||
// list of expected withdrawals can only change after an epoch advance or a
|
||||
// block application.
|
||||
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
if head_state.current_epoch() == proposal_epoch {
|
||||
return get_expected_withdrawals(&unadvanced_state, &self.spec)
|
||||
.map(Into::into)
|
||||
.map_err(Error::PrepareProposerFailed);
|
||||
}
|
||||
let parent_payload_status = if let Some(block_hash) = parent_bid_block_hash
|
||||
&& block_hash != ExecutionBlockHash::default()
|
||||
&& forkchoice_update_params.head_hash == Some(block_hash)
|
||||
{
|
||||
fork_choice::PayloadStatus::Full
|
||||
} else {
|
||||
fork_choice::PayloadStatus::Empty
|
||||
};
|
||||
|
||||
// Advance the state using the partial method.
|
||||
// TODO(gloas): we might want to optimise this further by using:
|
||||
// - `get_advanced_hot_state` instead of the cached head
|
||||
// - restoring the pre-Gloas optimisation to avoid advancing further than the epoch
|
||||
// boundary
|
||||
debug!(
|
||||
%proposal_slot,
|
||||
?parent_block_root,
|
||||
@@ -4770,9 +5043,33 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
partial_state_advance(
|
||||
&mut advanced_state,
|
||||
Some(unadvanced_state_root),
|
||||
proposal_epoch.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
proposal_slot,
|
||||
&self.spec,
|
||||
)?;
|
||||
|
||||
// For Gloas, when the head payload is Full, we need to apply the parent's
|
||||
// execution requests to the state to get the correct withdrawals.
|
||||
if parent_payload_status == fork_choice::PayloadStatus::Full {
|
||||
let envelope = if parent_block_root == head_block_root {
|
||||
cached_head.snapshot.execution_envelope.clone()
|
||||
} else {
|
||||
self.store
|
||||
.get_payload_envelope(&parent_block_root)?
|
||||
.map(Arc::new)
|
||||
}
|
||||
.ok_or(Error::MissingExecutionPayloadEnvelope(parent_block_root))?;
|
||||
|
||||
let parent_bid = advanced_state.latest_execution_payload_bid()?.clone();
|
||||
|
||||
apply_parent_execution_payload(
|
||||
&mut advanced_state,
|
||||
&parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(Error::PrepareProposerFailed)?;
|
||||
}
|
||||
|
||||
get_expected_withdrawals(&advanced_state, &self.spec)
|
||||
.map(Into::into)
|
||||
.map_err(Error::PrepareProposerFailed)
|
||||
@@ -5984,13 +6281,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
fcu_params.head_root,
|
||||
&cached_head,
|
||||
)?;
|
||||
Ok::<_, Error>(Some((fcu_params, pre_payload_attributes)))
|
||||
let head_payload_status = cached_head.head_payload_status();
|
||||
Ok::<_, Error>(Some((
|
||||
fcu_params,
|
||||
pre_payload_attributes,
|
||||
head_payload_status,
|
||||
)))
|
||||
},
|
||||
"prepare_beacon_proposer_head_read",
|
||||
)
|
||||
.await??;
|
||||
|
||||
let Some((forkchoice_update_params, Some(pre_payload_attributes))) = maybe_prep_data else {
|
||||
let Some((forkchoice_update_params, Some(pre_payload_attributes), head_payload_status)) =
|
||||
maybe_prep_data
|
||||
else {
|
||||
// Appropriate log messages have already been logged above and in
|
||||
// `get_pre_payload_attributes`.
|
||||
return Ok(None);
|
||||
@@ -6012,7 +6316,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// considerable time to compute if a state load is required.
|
||||
let head_root = forkchoice_update_params.head_root;
|
||||
let payload_attributes = if let Some(payload_attributes) = execution_layer
|
||||
.payload_attributes(prepare_slot, head_root)
|
||||
.payload_attributes(prepare_slot, head_root, head_payload_status)
|
||||
.await
|
||||
{
|
||||
payload_attributes
|
||||
@@ -6037,6 +6341,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
None
|
||||
};
|
||||
|
||||
let slot_number = if prepare_slot_fork.gloas_enabled() {
|
||||
Some(prepare_slot.as_u64())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
self.slot_clock
|
||||
.start_of(prepare_slot)
|
||||
@@ -6046,12 +6356,14 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
execution_layer.get_suggested_fee_recipient(proposer).await,
|
||||
withdrawals.map(Into::into),
|
||||
parent_beacon_block_root,
|
||||
slot_number,
|
||||
);
|
||||
|
||||
execution_layer
|
||||
.insert_proposer(
|
||||
prepare_slot,
|
||||
head_root,
|
||||
head_payload_status,
|
||||
proposer,
|
||||
payload_attributes.clone(),
|
||||
)
|
||||
@@ -6063,6 +6375,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
%prepare_slot,
|
||||
validator = proposer,
|
||||
parent_root = ?head_root,
|
||||
payload_status = ?head_payload_status,
|
||||
"Prepared beacon proposer"
|
||||
);
|
||||
payload_attributes
|
||||
@@ -6115,6 +6428,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.update_execution_engine_forkchoice(
|
||||
current_slot,
|
||||
forkchoice_update_params,
|
||||
head_payload_status,
|
||||
OverrideForkchoiceUpdate::AlreadyApplied,
|
||||
)
|
||||
.await?;
|
||||
@@ -6127,6 +6441,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self: &Arc<Self>,
|
||||
current_slot: Slot,
|
||||
input_params: ForkchoiceUpdateParameters,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
override_forkchoice_update: OverrideForkchoiceUpdate,
|
||||
) -> Result<(), Error> {
|
||||
let execution_layer = self
|
||||
@@ -6187,6 +6502,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
finalized_hash,
|
||||
current_slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
)
|
||||
.await
|
||||
.map_err(Error::ExecutionForkChoiceUpdateFailed);
|
||||
@@ -6471,6 +6787,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.naive_aggregation_pool.write().prune(slot);
|
||||
self.block_times_cache.write().prune(slot);
|
||||
self.envelope_times_cache.write().prune(slot);
|
||||
self.gossip_verified_payload_bid_cache.prune(slot);
|
||||
self.gossip_verified_proposer_preferences_cache.prune(slot);
|
||||
|
||||
// Don't run heavy-weight tasks during sync.
|
||||
if self.best_slot() + MAX_PER_SLOT_FORK_CHOICE_DISTANCE < slot {
|
||||
@@ -6679,12 +6997,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// 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,
|
||||
StatePayloadStatus::Pending,
|
||||
target_slot,
|
||||
head_block.state_root,
|
||||
)?
|
||||
.get_advanced_hot_state(head_block_root, target_slot, head_block.state_root)?
|
||||
.ok_or(Error::MissingBeaconState(head_block.state_root))?;
|
||||
(state, state_root)
|
||||
};
|
||||
@@ -6772,10 +7085,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
blocks.push((beacon_block_root, Arc::new(beacon_block)));
|
||||
}
|
||||
|
||||
// Collect states, using the next blocks to determine if states are full (have Gloas
|
||||
// payloads).
|
||||
// Collect envelopes, using the next blocks to determine if payloads are canonical
|
||||
// (the parent block was full).
|
||||
for (i, (block_root, block)) in blocks.iter().enumerate() {
|
||||
let (opt_envelope, state_root) = if block.fork_name_unchecked().gloas_enabled() {
|
||||
let opt_envelope = if block.fork_name_unchecked().gloas_enabled() {
|
||||
let opt_envelope = self.store.get_payload_envelope(block_root)?.map(Arc::new);
|
||||
|
||||
if let Some((_, next_block)) = blocks.get(i + 1) {
|
||||
@@ -6784,22 +7097,30 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let envelope = opt_envelope.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing envelope {block_root:?}"))
|
||||
})?;
|
||||
let state_root = envelope.message.state_root;
|
||||
(Some(envelope), state_root)
|
||||
Some(envelope)
|
||||
} else {
|
||||
(None, block.state_root())
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// TODO(gloas): should use fork choice/cached head for last block in sequence
|
||||
opt_envelope
|
||||
.as_ref()
|
||||
.map_or((None, block.state_root()), |envelope| {
|
||||
(Some(envelope.clone()), envelope.message.state_root)
|
||||
})
|
||||
// Last block in the sequence: use canonical head to determine
|
||||
// whether the payload is canonical.
|
||||
let head = self.canonical_head.cached_head();
|
||||
assert_eq!(head.head_block_root(), *block_root);
|
||||
let payload_received =
|
||||
head.head_payload_status() == fork_choice::PayloadStatus::Full;
|
||||
if payload_received {
|
||||
let envelope = opt_envelope.ok_or_else(|| {
|
||||
Error::DBInconsistent(format!("Missing envelope {block_root:?}"))
|
||||
})?;
|
||||
Some(envelope)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(None, block.state_root())
|
||||
None
|
||||
};
|
||||
let state_root = block.state_root();
|
||||
|
||||
let mut beacon_state = self
|
||||
.store
|
||||
|
||||
Reference in New Issue
Block a user