mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-17 21:08:32 +00:00
Gloas fork choice redux (#9025)
Co-Authored-By: hopinheimer <knmanas6@gmail.com> Co-Authored-By: Michael Sproul <michael@sigmaprime.io> Co-Authored-By: hopinheimer <48147533+hopinheimer@users.noreply.github.com> Co-Authored-By: Eitan Seri- Levi <eserilev@gmail.com> Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com> Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com> Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com> Co-Authored-By: Daniel Knopik <107140945+dknopik@users.noreply.github.com>
This commit is contained in:
@@ -3,8 +3,8 @@ use crate::{ForkChoiceStore, InvalidationOperation};
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use logging::crit;
|
||||
use proto_array::{
|
||||
Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, JustifiedBalances,
|
||||
ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
|
||||
Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, JustifiedBalances, LatestMessage,
|
||||
PayloadStatus, ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
|
||||
};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use state_processing::{
|
||||
@@ -19,12 +19,14 @@ use tracing::{debug, instrument, warn};
|
||||
use types::{
|
||||
AbstractExecPayload, AttestationShufflingId, AttesterSlashingRef, BeaconBlockRef, BeaconState,
|
||||
BeaconStateError, ChainSpec, Checkpoint, Epoch, EthSpec, ExecPayload, ExecutionBlockHash,
|
||||
Hash256, IndexedAttestationRef, RelativeEpoch, SignedBeaconBlock, Slot,
|
||||
Hash256, IndexedAttestationRef, IndexedPayloadAttestation, RelativeEpoch, SignedBeaconBlock,
|
||||
Slot,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error<T> {
|
||||
InvalidAttestation(InvalidAttestation),
|
||||
InvalidPayloadAttestation(InvalidPayloadAttestation),
|
||||
InvalidAttesterSlashing(AttesterSlashingValidationError),
|
||||
InvalidBlock(InvalidBlock),
|
||||
ProtoArrayStringError(String),
|
||||
@@ -84,6 +86,12 @@ impl<T> From<InvalidAttestation> for Error<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<InvalidPayloadAttestation> for Error<T> {
|
||||
fn from(e: InvalidPayloadAttestation) -> Self {
|
||||
Error::InvalidPayloadAttestation(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<AttesterSlashingValidationError> for Error<T> {
|
||||
fn from(e: AttesterSlashingValidationError) -> Self {
|
||||
Error::InvalidAttesterSlashing(e)
|
||||
@@ -169,6 +177,33 @@ pub enum InvalidAttestation {
|
||||
/// The attestation is attesting to a state that is later than itself. (Viz., attesting to the
|
||||
/// future).
|
||||
AttestsToFutureBlock { block: Slot, attestation: Slot },
|
||||
/// Post-Gloas: attestation index must be 0 or 1.
|
||||
InvalidAttestationIndex { index: u64 },
|
||||
/// A same-slot attestation has a non-zero index, which is invalid post-Gloas.
|
||||
InvalidSameSlotAttestationIndex { slot: Slot },
|
||||
/// Post-Gloas: attestation with index == 1 (payload_present) requires the block's
|
||||
/// payload to have been received (`root in store.payload_states`).
|
||||
PayloadNotReceived { beacon_block_root: Hash256 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InvalidPayloadAttestation {
|
||||
/// The payload attestation's attesting indices were empty.
|
||||
EmptyAggregationBitfield,
|
||||
/// The `payload_attestation.data.beacon_block_root` block is unknown.
|
||||
UnknownHeadBlock { beacon_block_root: Hash256 },
|
||||
/// The payload attestation is attesting to a block that is later than itself.
|
||||
AttestsToFutureBlock { block: Slot, attestation: Slot },
|
||||
/// A gossip payload attestation must be for the current slot.
|
||||
PayloadAttestationNotCurrentSlot {
|
||||
attestation_slot: Slot,
|
||||
current_slot: Slot,
|
||||
},
|
||||
/// One or more payload attesters are not part of the PTC.
|
||||
PayloadAttestationAttestersNotInPtc {
|
||||
attesting_indices_len: usize,
|
||||
attesting_indices_in_ptc: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> From<String> for Error<T> {
|
||||
@@ -240,6 +275,17 @@ pub struct QueuedAttestation {
|
||||
attesting_indices: Vec<u64>,
|
||||
block_root: Hash256,
|
||||
target_epoch: Epoch,
|
||||
/// Per Gloas spec: `payload_present = attestation.data.index == 1`.
|
||||
payload_present: bool,
|
||||
}
|
||||
|
||||
/// Legacy queued attestation without payload_present (pre-Gloas, schema V28).
|
||||
#[derive(Clone, PartialEq, Encode, Decode)]
|
||||
pub struct QueuedAttestationV28 {
|
||||
slot: Slot,
|
||||
attesting_indices: Vec<u64>,
|
||||
block_root: Hash256,
|
||||
target_epoch: Epoch,
|
||||
}
|
||||
|
||||
impl<'a, E: EthSpec> From<IndexedAttestationRef<'a, E>> for QueuedAttestation {
|
||||
@@ -249,6 +295,7 @@ impl<'a, E: EthSpec> From<IndexedAttestationRef<'a, E>> for QueuedAttestation {
|
||||
attesting_indices: a.attesting_indices_to_vec(),
|
||||
block_root: a.data().beacon_block_root,
|
||||
target_epoch: a.data().target.epoch,
|
||||
payload_present: a.data().index == 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -366,21 +413,32 @@ where
|
||||
AttestationShufflingId::new(anchor_block_root, anchor_state, RelativeEpoch::Next)
|
||||
.map_err(Error::BeaconStateError)?;
|
||||
|
||||
let execution_status = anchor_block.message().execution_payload().map_or_else(
|
||||
// If the block doesn't have an execution payload then it can't have
|
||||
// execution enabled.
|
||||
|_| ExecutionStatus::irrelevant(),
|
||||
|execution_payload| {
|
||||
let (execution_status, execution_payload_parent_hash, execution_payload_block_hash) =
|
||||
if let Ok(signed_bid) = anchor_block.message().body().signed_execution_payload_bid() {
|
||||
// Gloas: execution status is irrelevant post-Gloas; payload validation
|
||||
// is decoupled from beacon blocks.
|
||||
(
|
||||
ExecutionStatus::irrelevant(),
|
||||
Some(signed_bid.message.parent_block_hash),
|
||||
Some(signed_bid.message.block_hash),
|
||||
)
|
||||
} else if let Ok(execution_payload) = anchor_block.message().execution_payload() {
|
||||
// Pre-Gloas forks: do not set payload hashes, they are only used post-Gloas.
|
||||
if execution_payload.is_default_with_empty_roots() {
|
||||
// A default payload does not have execution enabled.
|
||||
ExecutionStatus::irrelevant()
|
||||
(ExecutionStatus::irrelevant(), None, None)
|
||||
} else {
|
||||
// Assume that this payload is valid, since the anchor should be a trusted block and
|
||||
// state.
|
||||
ExecutionStatus::Valid(execution_payload.block_hash())
|
||||
// Assume that this payload is valid, since the anchor should be a
|
||||
// trusted block and state.
|
||||
(
|
||||
ExecutionStatus::Valid(execution_payload.block_hash()),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// Pre-merge: no execution payload at all.
|
||||
(ExecutionStatus::irrelevant(), None, None)
|
||||
};
|
||||
|
||||
// If the current slot is not provided, use the value that was last provided to the store.
|
||||
let current_slot = current_slot.unwrap_or_else(|| fc_store.get_current_slot());
|
||||
@@ -394,6 +452,10 @@ where
|
||||
current_epoch_shuffling_id,
|
||||
next_epoch_shuffling_id,
|
||||
execution_status,
|
||||
execution_payload_parent_hash,
|
||||
execution_payload_block_hash,
|
||||
anchor_block.message().proposer_index(),
|
||||
spec,
|
||||
)?;
|
||||
|
||||
let mut fork_choice = Self {
|
||||
@@ -479,7 +541,7 @@ where
|
||||
&mut self,
|
||||
system_time_current_slot: Slot,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Hash256, Error<T::Error>> {
|
||||
) -> Result<(Hash256, PayloadStatus), Error<T::Error>> {
|
||||
// Provide the slot (as per the system clock) to the `fc_store` and then return its view of
|
||||
// the current slot. The `fc_store` will ensure that the `current_slot` is never
|
||||
// decreasing, a property which we must maintain.
|
||||
@@ -487,7 +549,7 @@ where
|
||||
|
||||
let store = &mut self.fc_store;
|
||||
|
||||
let head_root = self.proto_array.find_head::<E>(
|
||||
let (head_root, head_payload_status) = self.proto_array.find_head::<E>(
|
||||
*store.justified_checkpoint(),
|
||||
*store.finalized_checkpoint(),
|
||||
store.justified_balances(),
|
||||
@@ -516,7 +578,7 @@ where
|
||||
finalized_hash,
|
||||
};
|
||||
|
||||
Ok(head_root)
|
||||
Ok((head_root, head_payload_status))
|
||||
}
|
||||
|
||||
/// Get the block to build on as proposer, taking into account proposer re-orgs.
|
||||
@@ -611,6 +673,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a Gloas payload envelope as valid and received.
|
||||
///
|
||||
/// This must only be called for valid Gloas payloads.
|
||||
pub fn on_valid_payload_envelope_received(
|
||||
&mut self,
|
||||
block_root: Hash256,
|
||||
) -> Result<(), Error<T::Error>> {
|
||||
self.proto_array
|
||||
.on_valid_payload_envelope_received(block_root)
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
}
|
||||
|
||||
/// Pre-Gloas only.
|
||||
///
|
||||
/// See `ProtoArrayForkChoice::process_execution_payload_validation` for documentation.
|
||||
pub fn on_valid_execution_payload(
|
||||
&mut self,
|
||||
@@ -621,6 +697,8 @@ where
|
||||
.map_err(Error::FailedToProcessValidExecutionPayload)
|
||||
}
|
||||
|
||||
/// Pre-Gloas only.
|
||||
///
|
||||
/// See `ProtoArrayForkChoice::process_execution_payload_invalidation` for documentation.
|
||||
pub fn on_invalid_execution_payload(
|
||||
&mut self,
|
||||
@@ -729,6 +807,11 @@ where
|
||||
let attestation_threshold = spec.get_unaggregated_attestation_due();
|
||||
|
||||
// Add proposer score boost if the block is timely.
|
||||
// TODO(gloas): the spec's `update_proposer_boost_root` additionally checks that
|
||||
// `block.proposer_index == get_beacon_proposer_index(head_state)` — i.e. that
|
||||
// the block's proposer matches the expected proposer on the canonical chain.
|
||||
// This requires calling `get_head` and advancing the head state to the current
|
||||
// slot, which is expensive. Implement once we have a cached proposer index.
|
||||
let is_before_attesting_interval = block_delay < attestation_threshold;
|
||||
|
||||
let is_first_block = self.fc_store.proposer_boost_root().is_zero();
|
||||
@@ -881,6 +964,16 @@ where
|
||||
ExecutionStatus::irrelevant()
|
||||
};
|
||||
|
||||
let (execution_payload_parent_hash, execution_payload_block_hash) =
|
||||
if let Ok(signed_bid) = block.body().signed_execution_payload_bid() {
|
||||
(
|
||||
Some(signed_bid.message.parent_block_hash),
|
||||
Some(signed_bid.message.block_hash),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
// This does not apply a vote to the block, it just makes fork choice aware of the block so
|
||||
// it can still be identified as the head even if it doesn't have any votes.
|
||||
self.proto_array.process_block::<E>(
|
||||
@@ -907,10 +1000,13 @@ where
|
||||
execution_status,
|
||||
unrealized_justified_checkpoint: Some(unrealized_justified_checkpoint),
|
||||
unrealized_finalized_checkpoint: Some(unrealized_finalized_checkpoint),
|
||||
execution_payload_parent_hash,
|
||||
execution_payload_block_hash,
|
||||
proposer_index: Some(block.proposer_index()),
|
||||
},
|
||||
current_slot,
|
||||
self.justified_checkpoint(),
|
||||
self.finalized_checkpoint(),
|
||||
spec,
|
||||
block_delay,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
@@ -979,6 +1075,7 @@ where
|
||||
&self,
|
||||
indexed_attestation: IndexedAttestationRef<E>,
|
||||
is_from_block: AttestationFromBlock,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), InvalidAttestation> {
|
||||
// There is no point in processing an attestation with an empty bitfield. Reject
|
||||
// it immediately.
|
||||
@@ -1051,6 +1148,89 @@ where
|
||||
});
|
||||
}
|
||||
|
||||
if spec
|
||||
.fork_name_at_slot::<E>(indexed_attestation.data().slot)
|
||||
.gloas_enabled()
|
||||
{
|
||||
let index = indexed_attestation.data().index;
|
||||
|
||||
// Post-Gloas: attestation index must be 0 or 1.
|
||||
if index > 1 {
|
||||
return Err(InvalidAttestation::InvalidAttestationIndex { index });
|
||||
}
|
||||
|
||||
// Same-slot attestations must have index == 0.
|
||||
if indexed_attestation.data().slot == block.slot && index != 0 {
|
||||
return Err(InvalidAttestation::InvalidSameSlotAttestationIndex {
|
||||
slot: block.slot,
|
||||
});
|
||||
}
|
||||
|
||||
// index == 1 (payload_present) requires the block's payload to have been received.
|
||||
// TODO(gloas): could optimise by adding `payload_received` to `Block`
|
||||
if index == 1
|
||||
&& !self
|
||||
.proto_array
|
||||
.is_payload_received(&indexed_attestation.data().beacon_block_root)
|
||||
{
|
||||
return Err(InvalidAttestation::PayloadNotReceived {
|
||||
beacon_block_root: indexed_attestation.data().beacon_block_root,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates a payload attestation for application to fork choice.
|
||||
fn validate_on_payload_attestation(
|
||||
&self,
|
||||
indexed_payload_attestation: &IndexedPayloadAttestation<E>,
|
||||
is_from_block: AttestationFromBlock,
|
||||
) -> Result<(), InvalidPayloadAttestation> {
|
||||
// This check is from `is_valid_indexed_payload_attestation`, but we do it immediately to
|
||||
// avoid wasting time on junk attestations.
|
||||
if indexed_payload_attestation.attesting_indices.is_empty() {
|
||||
return Err(InvalidPayloadAttestation::EmptyAggregationBitfield);
|
||||
}
|
||||
|
||||
// PTC attestation must be for a known block. If block is unknown, delay consideration until
|
||||
// the block is found (responsibility of caller).
|
||||
let block = self
|
||||
.proto_array
|
||||
.get_block(&indexed_payload_attestation.data.beacon_block_root)
|
||||
.ok_or(InvalidPayloadAttestation::UnknownHeadBlock {
|
||||
beacon_block_root: indexed_payload_attestation.data.beacon_block_root,
|
||||
})?;
|
||||
|
||||
// Not strictly part of the spec, but payload attestations to future slots are MORE INVALID
|
||||
// than payload attestations to blocks at previous slots.
|
||||
if block.slot > indexed_payload_attestation.data.slot {
|
||||
return Err(InvalidPayloadAttestation::AttestsToFutureBlock {
|
||||
block: block.slot,
|
||||
attestation: indexed_payload_attestation.data.slot,
|
||||
});
|
||||
}
|
||||
|
||||
// PTC votes can only change the vote for their assigned beacon block, return early otherwise
|
||||
if block.slot != indexed_payload_attestation.data.slot {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Gossip payload attestations must be for the current slot.
|
||||
// NOTE: signature is assumed to have been verified by caller.
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/fork-choice.md
|
||||
if matches!(is_from_block, AttestationFromBlock::False)
|
||||
&& indexed_payload_attestation.data.slot != self.fc_store.get_current_slot()
|
||||
{
|
||||
return Err(
|
||||
InvalidPayloadAttestation::PayloadAttestationNotCurrentSlot {
|
||||
attestation_slot: indexed_payload_attestation.data.slot,
|
||||
current_slot: self.fc_store.get_current_slot(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1076,6 +1256,7 @@ where
|
||||
system_time_current_slot: Slot,
|
||||
attestation: IndexedAttestationRef<E>,
|
||||
is_from_block: AttestationFromBlock,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), Error<T::Error>> {
|
||||
let _timer = metrics::start_timer(&metrics::FORK_CHOICE_ON_ATTESTATION_TIMES);
|
||||
|
||||
@@ -1098,14 +1279,21 @@ where
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.validate_on_attestation(attestation, is_from_block)?;
|
||||
self.validate_on_attestation(attestation, is_from_block, spec)?;
|
||||
|
||||
// Per Gloas spec: `payload_present = attestation.data.index == 1`.
|
||||
let payload_present = spec
|
||||
.fork_name_at_slot::<E>(attestation.data().slot)
|
||||
.gloas_enabled()
|
||||
&& attestation.data().index == 1;
|
||||
|
||||
if attestation.data().slot < self.fc_store.get_current_slot() {
|
||||
for validator_index in attestation.attesting_indices_iter() {
|
||||
self.proto_array.process_attestation(
|
||||
*validator_index as usize,
|
||||
attestation.data().beacon_block_root,
|
||||
attestation.data().target.epoch,
|
||||
attestation.data().slot,
|
||||
payload_present,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
@@ -1122,6 +1310,59 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register a payload attestation with the fork choice DAG.
|
||||
///
|
||||
/// `ptc` is the PTC committee for the attestation's slot: a list of validator indices
|
||||
/// ordered by committee position. Each attesting validator index is resolved to its
|
||||
/// position within `ptc` (its `ptc_index`) before being applied to the proto-array.
|
||||
pub fn on_payload_attestation(
|
||||
&mut self,
|
||||
system_time_current_slot: Slot,
|
||||
attestation: &IndexedPayloadAttestation<E>,
|
||||
is_from_block: AttestationFromBlock,
|
||||
ptc: &[usize],
|
||||
) -> Result<(), Error<T::Error>> {
|
||||
self.update_time(system_time_current_slot)?;
|
||||
|
||||
if attestation.data.beacon_block_root.is_zero() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO(gloas): Should ignore wrong-slot payload attestations at the caller, they could
|
||||
// have been processed at the correct slot when received on gossip, but then have the
|
||||
// wrong-slot by the time they make it to here (TOCTOU).
|
||||
self.validate_on_payload_attestation(attestation, is_from_block)?;
|
||||
|
||||
// Resolve validator indices to PTC committee positions.
|
||||
let ptc_indices: Vec<usize> = attestation
|
||||
.attesting_indices
|
||||
.iter()
|
||||
.filter_map(|vi| ptc.iter().position(|&p| p == *vi as usize))
|
||||
.collect();
|
||||
|
||||
// Check that all the attesters are in the PTC
|
||||
if ptc_indices.len() != attestation.attesting_indices.len() {
|
||||
return Err(
|
||||
InvalidPayloadAttestation::PayloadAttestationAttestersNotInPtc {
|
||||
attesting_indices_len: attestation.attesting_indices.len(),
|
||||
attesting_indices_in_ptc: ptc_indices.len(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
for &ptc_index in &ptc_indices {
|
||||
self.proto_array.process_payload_attestation(
|
||||
attestation.data.beacon_block_root,
|
||||
ptc_index,
|
||||
attestation.data.payload_present,
|
||||
attestation.data.blob_data_available,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply an attester slashing to fork choice.
|
||||
///
|
||||
/// We assume that the attester slashing provided to this function has already been verified.
|
||||
@@ -1228,7 +1469,8 @@ where
|
||||
self.proto_array.process_attestation(
|
||||
*validator_index as usize,
|
||||
attestation.block_root,
|
||||
attestation.target_epoch,
|
||||
attestation.slot,
|
||||
attestation.payload_present,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -1358,13 +1600,15 @@ where
|
||||
|
||||
/// Returns the latest message for a given validator, if any.
|
||||
///
|
||||
/// Returns `(block_root, block_slot)`.
|
||||
/// Returns `block_root, block_slot, payload_present`.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// It may be prudent to call `Self::update_time` before calling this function,
|
||||
/// since some attestations might be queued and awaiting processing.
|
||||
pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Epoch)> {
|
||||
///
|
||||
/// This function is only used in tests.
|
||||
pub fn latest_message(&self, validator_index: usize) -> Option<LatestMessage> {
|
||||
self.proto_array.latest_message(validator_index)
|
||||
}
|
||||
|
||||
@@ -1409,7 +1653,6 @@ where
|
||||
persisted_proto_array: proto_array::core::SszContainer,
|
||||
justified_balances: JustifiedBalances,
|
||||
reset_payload_statuses: ResetPayloadStatuses,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<ProtoArrayForkChoice, Error<T::Error>> {
|
||||
let mut proto_array = ProtoArrayForkChoice::from_container(
|
||||
persisted_proto_array.clone(),
|
||||
@@ -1434,7 +1677,7 @@ where
|
||||
|
||||
// Reset all blocks back to being "optimistic". This helps recover from an EL consensus
|
||||
// fault where an invalid payload becomes valid.
|
||||
if let Err(e) = proto_array.set_all_blocks_to_optimistic::<E>(spec) {
|
||||
if let Err(e) = proto_array.set_all_blocks_to_optimistic::<E>() {
|
||||
// If there is an error resetting the optimistic status then log loudly and revert
|
||||
// back to a proto-array which does not have the reset applied. This indicates a
|
||||
// significant error in Lighthouse and warrants detailed investigation.
|
||||
@@ -1464,7 +1707,6 @@ where
|
||||
persisted.proto_array,
|
||||
justified_balances,
|
||||
reset_payload_statuses,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
let current_slot = fc_store.get_current_slot();
|
||||
@@ -1472,7 +1714,7 @@ where
|
||||
let mut fork_choice = Self {
|
||||
fc_store,
|
||||
proto_array,
|
||||
queued_attestations: persisted.queued_attestations,
|
||||
queued_attestations: vec![],
|
||||
// Will be updated in the following call to `Self::get_head`.
|
||||
forkchoice_update_parameters: ForkchoiceUpdateParameters {
|
||||
head_hash: None,
|
||||
@@ -1498,7 +1740,7 @@ where
|
||||
// get a different result.
|
||||
fork_choice
|
||||
.proto_array
|
||||
.set_all_blocks_to_optimistic::<E>(spec)?;
|
||||
.set_all_blocks_to_optimistic::<E>()?;
|
||||
// If the second attempt at finding a head fails, return an error since we do not
|
||||
// expect this scenario.
|
||||
fork_choice.get_head(current_slot, spec)?;
|
||||
@@ -1511,10 +1753,7 @@ where
|
||||
/// be instantiated again later.
|
||||
pub fn to_persisted(&self) -> PersistedForkChoice {
|
||||
PersistedForkChoice {
|
||||
proto_array: self
|
||||
.proto_array()
|
||||
.as_ssz_container(self.justified_checkpoint(), self.finalized_checkpoint()),
|
||||
queued_attestations: self.queued_attestations().to_vec(),
|
||||
proto_array: self.proto_array().as_ssz_container(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1528,16 +1767,37 @@ where
|
||||
///
|
||||
/// This is used when persisting the state of the fork choice to disk.
|
||||
#[superstruct(
|
||||
variants(V28),
|
||||
variants(V28, V29),
|
||||
variant_attributes(derive(Encode, Decode, Clone)),
|
||||
no_enum
|
||||
)]
|
||||
pub struct PersistedForkChoice {
|
||||
pub proto_array: proto_array::core::SszContainerV28,
|
||||
pub queued_attestations: Vec<QueuedAttestation>,
|
||||
#[superstruct(only(V28))]
|
||||
pub proto_array_v28: proto_array::core::SszContainerV28,
|
||||
#[superstruct(only(V29))]
|
||||
pub proto_array: proto_array::core::SszContainerV29,
|
||||
#[superstruct(only(V28))]
|
||||
pub queued_attestations_v28: Vec<QueuedAttestationV28>,
|
||||
}
|
||||
|
||||
pub type PersistedForkChoice = PersistedForkChoiceV28;
|
||||
pub type PersistedForkChoice = PersistedForkChoiceV29;
|
||||
|
||||
impl From<PersistedForkChoiceV28> for PersistedForkChoiceV29 {
|
||||
fn from(v28: PersistedForkChoiceV28) -> Self {
|
||||
Self {
|
||||
proto_array: v28.proto_array_v28.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PersistedForkChoiceV29> for PersistedForkChoiceV28 {
|
||||
fn from(v29: PersistedForkChoiceV29) -> Self {
|
||||
Self {
|
||||
proto_array_v28: v29.proto_array.into(),
|
||||
queued_attestations_v28: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@@ -1574,6 +1834,7 @@ mod tests {
|
||||
attesting_indices: vec![],
|
||||
block_root: Hash256::zero(),
|
||||
target_epoch: Epoch::new(0),
|
||||
payload_present: false,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,11 @@ mod metrics;
|
||||
|
||||
pub use crate::fork_choice::{
|
||||
AttestationFromBlock, Error, ForkChoice, ForkChoiceView, ForkchoiceUpdateParameters,
|
||||
InvalidAttestation, InvalidBlock, PayloadVerificationStatus, PersistedForkChoice,
|
||||
PersistedForkChoiceV28, QueuedAttestation, ResetPayloadStatuses,
|
||||
InvalidAttestation, InvalidBlock, InvalidPayloadAttestation, PayloadVerificationStatus,
|
||||
PersistedForkChoice, PersistedForkChoiceV28, PersistedForkChoiceV29, QueuedAttestation,
|
||||
ResetPayloadStatuses,
|
||||
};
|
||||
pub use fork_choice_store::ForkChoiceStore;
|
||||
pub use proto_array::{
|
||||
Block as ProtoBlock, ExecutionStatus, InvalidationOperation, ProposerHeadError,
|
||||
Block as ProtoBlock, ExecutionStatus, InvalidationOperation, PayloadStatus, ProposerHeadError,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user