Fork choice modifications and cleanup (#3962)

## Issue Addressed

NA

## Proposed Changes

- Implements https://github.com/ethereum/consensus-specs/pull/3290/
- Bumps `ef-tests` to [v1.3.0-rc.4](https://github.com/ethereum/consensus-spec-tests/releases/tag/v1.3.0-rc.4).

The `CountRealizedFull` concept has been removed and the `--count-unrealized-full` and `--count-unrealized` BN flags now do nothing but log a `WARN` when used.

## Database Migration Debt

This PR removes the `best_justified_checkpoint` from fork choice. This field is persisted on-disk and the correct way to go about this would be to make a DB migration to remove the field. However, in this PR I've simply stubbed out the value with a junk value. I've taken this approach because if we're going to do a DB migration I'd love to remove the `Option`s around the justified and finalized checkpoints on `ProtoNode` whilst we're at it. Those options were added in #2822 which was included in Lighthouse v2.1.0. The options were only put there to handle the migration and they've been set to `Some` ever since v2.1.0. There's no reason to keep them as options anymore.

I started adding the DB migration to this branch but I started to feel like I was bloating this rather critical PR with nice-to-haves. I've kept the partially-complete migration [over in my repo](https://github.com/paulhauner/lighthouse/tree/fc-pr-18-migration) so we can pick it up after this PR is merged.
This commit is contained in:
Paul Hauner 2023-03-21 07:34:41 +00:00
parent 3ac5583cf9
commit 1f8c17b530
29 changed files with 217 additions and 414 deletions

View File

@ -73,7 +73,7 @@ use itertools::process_results;
use itertools::Itertools;
use operation_pool::{AttestationRef, OperationPool, PersistedOperationPool, ReceivedPreCapella};
use parking_lot::{Mutex, RwLock};
use proto_array::{CountUnrealizedFull, DoNotReOrg, ProposerHeadError};
use proto_array::{DoNotReOrg, ProposerHeadError};
use safe_arith::SafeArith;
use slasher::Slasher;
use slog::{crit, debug, error, info, trace, warn, Logger};
@ -479,7 +479,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn load_fork_choice(
store: BeaconStore<T>,
reset_payload_statuses: ResetPayloadStatuses,
count_unrealized_full: CountUnrealizedFull,
spec: &ChainSpec,
log: &Logger,
) -> Result<Option<BeaconForkChoice<T>>, Error> {
@ -496,7 +495,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
persisted_fork_choice.fork_choice,
reset_payload_statuses,
fc_store,
count_unrealized_full,
spec,
log,
)?))
@ -1900,7 +1898,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
self.slot()?,
verified.indexed_attestation(),
AttestationFromBlock::False,
&self.spec,
)
.map_err(Into::into)
}
@ -2868,7 +2865,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
&state,
payload_verification_status,
&self.spec,
count_unrealized.and(self.config.count_unrealized.into()),
count_unrealized,
)
.map_err(|e| BlockError::BeaconChainError(e.into()))?;
}
@ -2987,7 +2984,6 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
ResetPayloadStatuses::always_reset_conditionally(
self.config.always_reset_payload_statuses,
),
self.config.count_unrealized_full,
&self.store,
&self.spec,
&self.log,

View File

@ -20,6 +20,14 @@ use types::{
Hash256, Slot,
};
/// Ensure this justified checkpoint has an epoch of 0 so that it is never
/// greater than the justified checkpoint and enshrined as the actual justified
/// checkpoint.
const JUNK_BEST_JUSTIFIED_CHECKPOINT: Checkpoint = Checkpoint {
epoch: Epoch::new(0),
root: Hash256::repeat_byte(0),
};
#[derive(Debug)]
pub enum Error {
UnableToReadSlot,
@ -144,7 +152,6 @@ pub struct BeaconForkChoiceStore<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<
finalized_checkpoint: Checkpoint,
justified_checkpoint: Checkpoint,
justified_balances: JustifiedBalances,
best_justified_checkpoint: Checkpoint,
unrealized_justified_checkpoint: Checkpoint,
unrealized_finalized_checkpoint: Checkpoint,
proposer_boost_root: Hash256,
@ -194,7 +201,6 @@ where
justified_checkpoint,
justified_balances,
finalized_checkpoint,
best_justified_checkpoint: justified_checkpoint,
unrealized_justified_checkpoint: justified_checkpoint,
unrealized_finalized_checkpoint: finalized_checkpoint,
proposer_boost_root: Hash256::zero(),
@ -212,7 +218,7 @@ where
finalized_checkpoint: self.finalized_checkpoint,
justified_checkpoint: self.justified_checkpoint,
justified_balances: self.justified_balances.effective_balances.clone(),
best_justified_checkpoint: self.best_justified_checkpoint,
best_justified_checkpoint: JUNK_BEST_JUSTIFIED_CHECKPOINT,
unrealized_justified_checkpoint: self.unrealized_justified_checkpoint,
unrealized_finalized_checkpoint: self.unrealized_finalized_checkpoint,
proposer_boost_root: self.proposer_boost_root,
@ -234,7 +240,6 @@ where
finalized_checkpoint: persisted.finalized_checkpoint,
justified_checkpoint: persisted.justified_checkpoint,
justified_balances,
best_justified_checkpoint: persisted.best_justified_checkpoint,
unrealized_justified_checkpoint: persisted.unrealized_justified_checkpoint,
unrealized_finalized_checkpoint: persisted.unrealized_finalized_checkpoint,
proposer_boost_root: persisted.proposer_boost_root,
@ -277,10 +282,6 @@ where
&self.justified_balances
}
fn best_justified_checkpoint(&self) -> &Checkpoint {
&self.best_justified_checkpoint
}
fn finalized_checkpoint(&self) -> &Checkpoint {
&self.finalized_checkpoint
}
@ -333,10 +334,6 @@ where
Ok(())
}
fn set_best_justified_checkpoint(&mut self, checkpoint: Checkpoint) {
self.best_justified_checkpoint = checkpoint
}
fn set_unrealized_justified_checkpoint(&mut self, checkpoint: Checkpoint) {
self.unrealized_justified_checkpoint = checkpoint;
}

View File

@ -1468,7 +1468,6 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
current_slot,
indexed_attestation,
AttestationFromBlock::True,
&chain.spec,
) {
Ok(()) => Ok(()),
// Ignore invalid attestations whilst importing attestations from a block. The

View File

@ -18,7 +18,7 @@ use crate::{
};
use eth1::Config as Eth1Config;
use execution_layer::ExecutionLayer;
use fork_choice::{ForkChoice, ResetPayloadStatuses};
use fork_choice::{CountUnrealized, ForkChoice, ResetPayloadStatuses};
use futures::channel::mpsc::Sender;
use operation_pool::{OperationPool, PersistedOperationPool};
use parking_lot::RwLock;
@ -265,7 +265,6 @@ where
ResetPayloadStatuses::always_reset_conditionally(
self.chain_config.always_reset_payload_statuses,
),
self.chain_config.count_unrealized_full,
&self.spec,
log,
)
@ -384,7 +383,6 @@ where
&genesis.beacon_block,
&genesis.beacon_state,
current_slot,
self.chain_config.count_unrealized_full,
&self.spec,
)
.map_err(|e| format!("Unable to initialize ForkChoice: {:?}", e))?;
@ -503,7 +501,6 @@ where
&snapshot.beacon_block,
&snapshot.beacon_state,
current_slot,
self.chain_config.count_unrealized_full,
&self.spec,
)
.map_err(|e| format!("Unable to initialize ForkChoice: {:?}", e))?;
@ -681,8 +678,7 @@ where
store.clone(),
Some(current_slot),
&self.spec,
self.chain_config.count_unrealized.into(),
self.chain_config.count_unrealized_full,
CountUnrealized::True,
)?;
}

View File

@ -45,8 +45,7 @@ use crate::{
};
use eth2::types::{EventKind, SseChainReorg, SseFinalizedCheckpoint, SseHead, SseLateHead};
use fork_choice::{
CountUnrealizedFull, ExecutionStatus, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock,
ResetPayloadStatuses,
ExecutionStatus, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock, ResetPayloadStatuses,
};
use itertools::process_results;
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
@ -285,19 +284,13 @@ impl<T: BeaconChainTypes> CanonicalHead<T> {
// defensive programming.
mut fork_choice_write_lock: RwLockWriteGuard<BeaconForkChoice<T>>,
reset_payload_statuses: ResetPayloadStatuses,
count_unrealized_full: CountUnrealizedFull,
store: &BeaconStore<T>,
spec: &ChainSpec,
log: &Logger,
) -> Result<(), Error> {
let fork_choice = <BeaconChain<T>>::load_fork_choice(
store.clone(),
reset_payload_statuses,
count_unrealized_full,
spec,
log,
)?
.ok_or(Error::MissingPersistedForkChoice)?;
let fork_choice =
<BeaconChain<T>>::load_fork_choice(store.clone(), reset_payload_statuses, spec, log)?
.ok_or(Error::MissingPersistedForkChoice)?;
let fork_choice_view = fork_choice.cached_fork_choice_view();
let beacon_block_root = fork_choice_view.head_block_root;
let beacon_block = store

View File

@ -1,4 +1,4 @@
pub use proto_array::{CountUnrealizedFull, ReOrgThreshold};
pub use proto_array::ReOrgThreshold;
use serde_derive::{Deserialize, Serialize};
use std::time::Duration;
use types::{Checkpoint, Epoch};
@ -48,16 +48,11 @@ pub struct ChainConfig {
pub builder_fallback_epochs_since_finalization: usize,
/// Whether any chain health checks should be considered when deciding whether to use the builder API.
pub builder_fallback_disable_checks: bool,
/// When set to `true`, weigh the "unrealized" FFG progression when choosing a head in fork
/// choice.
pub count_unrealized: bool,
/// When set to `true`, forget any valid/invalid/optimistic statuses in fork choice during start
/// up.
pub always_reset_payload_statuses: bool,
/// Whether to apply paranoid checks to blocks proposed by this beacon node.
pub paranoid_block_proposal: bool,
/// Whether to strictly count unrealized justified votes.
pub count_unrealized_full: CountUnrealizedFull,
/// Optionally set timeout for calls to checkpoint sync endpoint.
pub checkpoint_sync_url_timeout: u64,
/// The offset before the start of a proposal slot at which payload attributes should be sent.
@ -91,10 +86,8 @@ impl Default for ChainConfig {
builder_fallback_skips_per_epoch: 8,
builder_fallback_epochs_since_finalization: 3,
builder_fallback_disable_checks: false,
count_unrealized: true,
always_reset_payload_statuses: false,
paranoid_block_proposal: false,
count_unrealized_full: CountUnrealizedFull::default(),
checkpoint_sync_url_timeout: 60,
prepare_payload_lookahead: Duration::from_secs(4),
// This value isn't actually read except in tests.

View File

@ -1,7 +1,6 @@
use crate::{BeaconForkChoiceStore, BeaconSnapshot};
use fork_choice::{CountUnrealized, ForkChoice, PayloadVerificationStatus};
use itertools::process_results;
use proto_array::CountUnrealizedFull;
use slog::{info, warn, Logger};
use state_processing::state_advance::complete_state_advance;
use state_processing::{
@ -102,7 +101,6 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
current_slot: Option<Slot>,
spec: &ChainSpec,
count_unrealized_config: CountUnrealized,
count_unrealized_full_config: CountUnrealizedFull,
) -> Result<ForkChoice<BeaconForkChoiceStore<E, Hot, Cold>, E>, String> {
// Fetch finalized block.
let finalized_checkpoint = head_state.finalized_checkpoint();
@ -156,7 +154,6 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
&finalized_snapshot.beacon_block,
&finalized_snapshot.beacon_state,
current_slot,
count_unrealized_full_config,
spec,
)
.map_err(|e| format!("Unable to reset fork choice for revert: {:?}", e))?;

View File

@ -57,7 +57,7 @@ pub use self::beacon_chain::{
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
};
pub use self::beacon_snapshot::BeaconSnapshot;
pub use self::chain_config::{ChainConfig, CountUnrealizedFull};
pub use self::chain_config::ChainConfig;
pub use self::errors::{BeaconChainError, BlockProductionError};
pub use self::historical_blocks::HistoricalBlockError;
pub use attestation_verification::Error as AttestationError;

View File

@ -3,6 +3,7 @@ mod migration_schema_v12;
mod migration_schema_v13;
mod migration_schema_v14;
mod migration_schema_v15;
mod migration_schema_v16;
use crate::beacon_chain::{BeaconChainTypes, ETH1_CACHE_DB_KEY};
use crate::eth1_chain::SszEth1;
@ -132,6 +133,14 @@ pub fn migrate_schema<T: BeaconChainTypes>(
let ops = migration_schema_v15::downgrade_from_v15::<T>(db.clone(), log)?;
db.store_schema_version_atomically(to, ops)
}
(SchemaVersion(15), SchemaVersion(16)) => {
let ops = migration_schema_v16::upgrade_to_v16::<T>(db.clone(), log)?;
db.store_schema_version_atomically(to, ops)
}
(SchemaVersion(16), SchemaVersion(15)) => {
let ops = migration_schema_v16::downgrade_from_v16::<T>(db.clone(), log)?;
db.store_schema_version_atomically(to, ops)
}
// Anything else is an error.
(_, _) => Err(HotColdDBError::UnsupportedSchemaVersion {
target_version: to,

View File

@ -0,0 +1,46 @@
use crate::beacon_chain::{BeaconChainTypes, FORK_CHOICE_DB_KEY};
use crate::persisted_fork_choice::PersistedForkChoiceV11;
use slog::{debug, Logger};
use std::sync::Arc;
use store::{Error, HotColdDB, KeyValueStoreOp, StoreItem};
pub fn upgrade_to_v16<T: BeaconChainTypes>(
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
log: Logger,
) -> Result<Vec<KeyValueStoreOp>, Error> {
drop_balances_cache::<T>(db, log)
}
pub fn downgrade_from_v16<T: BeaconChainTypes>(
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
log: Logger,
) -> Result<Vec<KeyValueStoreOp>, Error> {
drop_balances_cache::<T>(db, log)
}
/// Drop the balances cache from the fork choice store.
///
/// There aren't any type-level changes in this schema migration, however the
/// way that we compute the `JustifiedBalances` has changed due to:
/// https://github.com/sigp/lighthouse/pull/3962
pub fn drop_balances_cache<T: BeaconChainTypes>(
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
log: Logger,
) -> Result<Vec<KeyValueStoreOp>, Error> {
let mut persisted_fork_choice = db
.get_item::<PersistedForkChoiceV11>(&FORK_CHOICE_DB_KEY)?
.ok_or_else(|| Error::SchemaMigrationError("fork choice missing from database".into()))?;
debug!(
log,
"Dropping fork choice balances cache";
"item_count" => persisted_fork_choice.fork_choice_store.balances_cache.items.len()
);
// Drop all items in the balances cache.
persisted_fork_choice.fork_choice_store.balances_cache = <_>::default();
let kv_op = persisted_fork_choice.as_kv_store_op(FORK_CHOICE_DB_KEY);
Ok(vec![kv_op])
}

View File

@ -500,7 +500,7 @@ async fn unaggregated_attestations_added_to_fork_choice_some_none() {
// Move forward a slot so all queued attestations can be processed.
harness.advance_slot();
fork_choice
.update_time(harness.chain.slot().unwrap(), &harness.chain.spec)
.update_time(harness.chain.slot().unwrap())
.unwrap();
let validator_slots: Vec<(usize, Slot)> = (0..VALIDATOR_COUNT)
@ -614,7 +614,7 @@ async fn unaggregated_attestations_added_to_fork_choice_all_updated() {
// Move forward a slot so all queued attestations can be processed.
harness.advance_slot();
fork_choice
.update_time(harness.chain.slot().unwrap(), &harness.chain.spec)
.update_time(harness.chain.slot().unwrap())
.unwrap();
let validators: Vec<usize> = (0..VALIDATOR_COUNT).collect();

View File

@ -966,8 +966,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("count-unrealized")
.long("count-unrealized")
.hidden(true)
.help("Enables an alternative, potentially more performant FFG \
vote tracking method.")
.help("This flag is deprecated and has no effect.")
.takes_value(true)
.default_value("true")
)
@ -975,7 +974,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
Arg::with_name("count-unrealized-full")
.long("count-unrealized-full")
.hidden(true)
.help("Stricter version of `count-unrealized`.")
.help("This flag is deprecated and has no effect.")
.takes_value(true)
.default_value("false")
)

View File

@ -709,10 +709,21 @@ pub fn get_config<E: EthSpec>(
client_config.chain.fork_choice_before_proposal_timeout_ms = timeout;
}
client_config.chain.count_unrealized =
clap_utils::parse_required(cli_args, "count-unrealized")?;
client_config.chain.count_unrealized_full =
clap_utils::parse_required::<bool>(cli_args, "count-unrealized-full")?.into();
if !clap_utils::parse_required::<bool>(cli_args, "count-unrealized")? {
warn!(
log,
"The flag --count-unrealized is deprecated and will be removed";
"info" => "any use of the flag will have no effect"
);
}
if clap_utils::parse_required::<bool>(cli_args, "count-unrealized-full")? {
warn!(
log,
"The flag --count-unrealized-full is deprecated and will be removed";
"info" => "setting it to `true` has no effect"
);
}
client_config.chain.always_reset_payload_statuses =
cli_args.is_present("reset-payload-statuses");

View File

@ -4,7 +4,7 @@ use ssz::{Decode, Encode};
use ssz_derive::{Decode, Encode};
use types::{Checkpoint, Hash256, Slot};
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(15);
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(16);
// All the keys that get stored under the `BeaconMeta` column.
//

View File

@ -1,6 +1,6 @@
use crate::{ForkChoiceStore, InvalidationOperation};
use proto_array::{
Block as ProtoBlock, CountUnrealizedFull, ExecutionStatus, ProposerHeadError, ProposerHeadInfo,
Block as ProtoBlock, ExecutionStatus, ProposerHeadError, ProposerHeadInfo,
ProtoArrayForkChoice, ReOrgThreshold,
};
use slog::{crit, debug, warn, Logger};
@ -187,51 +187,6 @@ impl CountUnrealized {
pub fn is_true(&self) -> bool {
matches!(self, CountUnrealized::True)
}
pub fn and(&self, other: CountUnrealized) -> CountUnrealized {
if self.is_true() && other.is_true() {
CountUnrealized::True
} else {
CountUnrealized::False
}
}
}
impl From<bool> for CountUnrealized {
fn from(count_unrealized: bool) -> Self {
if count_unrealized {
CountUnrealized::True
} else {
CountUnrealized::False
}
}
}
#[derive(Copy, Clone)]
enum UpdateJustifiedCheckpointSlots {
OnTick {
current_slot: Slot,
},
OnBlock {
state_slot: Slot,
current_slot: Slot,
},
}
impl UpdateJustifiedCheckpointSlots {
fn current_slot(&self) -> Slot {
match self {
UpdateJustifiedCheckpointSlots::OnTick { current_slot } => *current_slot,
UpdateJustifiedCheckpointSlots::OnBlock { current_slot, .. } => *current_slot,
}
}
fn state_slot(&self) -> Option<Slot> {
match self {
UpdateJustifiedCheckpointSlots::OnTick { .. } => None,
UpdateJustifiedCheckpointSlots::OnBlock { state_slot, .. } => Some(*state_slot),
}
}
}
/// Indicates if a block has been verified by an execution payload.
@ -393,7 +348,6 @@ where
anchor_block: &SignedBeaconBlock<E>,
anchor_state: &BeaconState<E>,
current_slot: Option<Slot>,
count_unrealized_full_config: CountUnrealizedFull,
spec: &ChainSpec,
) -> Result<Self, Error<T::Error>> {
// Sanity check: the anchor must lie on an epoch boundary.
@ -440,7 +394,6 @@ where
current_epoch_shuffling_id,
next_epoch_shuffling_id,
execution_status,
count_unrealized_full_config,
)?;
let mut fork_choice = Self {
@ -533,7 +486,7 @@ where
// 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.
let current_slot = self.update_time(system_time_current_slot, spec)?;
let current_slot = self.update_time(system_time_current_slot)?;
let store = &mut self.fc_store;
@ -654,58 +607,6 @@ where
}
}
/// Returns `true` if the given `store` should be updated to set
/// `state.current_justified_checkpoint` its `justified_checkpoint`.
///
/// ## Specification
///
/// Is equivalent to:
///
/// https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/fork-choice.md#should_update_justified_checkpoint
fn should_update_justified_checkpoint(
&mut self,
new_justified_checkpoint: Checkpoint,
slots: UpdateJustifiedCheckpointSlots,
spec: &ChainSpec,
) -> Result<bool, Error<T::Error>> {
self.update_time(slots.current_slot(), spec)?;
if compute_slots_since_epoch_start::<E>(self.fc_store.get_current_slot())
< spec.safe_slots_to_update_justified
{
return Ok(true);
}
let justified_slot =
compute_start_slot_at_epoch::<E>(self.fc_store.justified_checkpoint().epoch);
// This sanity check is not in the spec, but the invariant is implied.
if let Some(state_slot) = slots.state_slot() {
if justified_slot >= state_slot {
return Err(Error::AttemptToRevertJustification {
store: justified_slot,
state: state_slot,
});
}
}
// We know that the slot for `new_justified_checkpoint.root` is not greater than
// `state.slot`, since a state cannot justify its own slot.
//
// We know that `new_justified_checkpoint.root` is an ancestor of `state`, since a `state`
// only ever justifies ancestors.
//
// A prior `if` statement protects against a justified_slot that is greater than
// `state.slot`
let justified_ancestor =
self.get_ancestor(new_justified_checkpoint.root, justified_slot)?;
if justified_ancestor != Some(self.fc_store.justified_checkpoint().root) {
return Ok(false);
}
Ok(true)
}
/// See `ProtoArrayForkChoice::process_execution_payload_validation` for documentation.
pub fn on_valid_execution_payload(
&mut self,
@ -759,7 +660,7 @@ where
// 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.
let current_slot = self.update_time(system_time_current_slot, spec)?;
let current_slot = self.update_time(system_time_current_slot)?;
// Parent block must be known.
let parent_block = self
@ -814,17 +715,10 @@ where
self.fc_store.set_proposer_boost_root(block_root);
}
let update_justified_checkpoint_slots = UpdateJustifiedCheckpointSlots::OnBlock {
state_slot: state.slot(),
current_slot,
};
// Update store with checkpoints if necessary
self.update_checkpoints(
state.current_justified_checkpoint(),
state.finalized_checkpoint(),
update_justified_checkpoint_slots,
spec,
)?;
// Update unrealized justified/finalized checkpoints.
@ -905,12 +799,7 @@ where
// If block is from past epochs, try to update store's justified & finalized checkpoints right away
if block.slot().epoch(E::slots_per_epoch()) < current_slot.epoch(E::slots_per_epoch()) {
self.update_checkpoints(
unrealized_justified_checkpoint,
unrealized_finalized_checkpoint,
update_justified_checkpoint_slots,
spec,
)?;
self.pull_up_store_checkpoints()?;
}
(
@ -1004,29 +893,19 @@ where
&mut self,
justified_checkpoint: Checkpoint,
finalized_checkpoint: Checkpoint,
slots: UpdateJustifiedCheckpointSlots,
spec: &ChainSpec,
) -> Result<(), Error<T::Error>> {
// Update justified checkpoint.
if justified_checkpoint.epoch > self.fc_store.justified_checkpoint().epoch {
if justified_checkpoint.epoch > self.fc_store.best_justified_checkpoint().epoch {
self.fc_store
.set_best_justified_checkpoint(justified_checkpoint);
}
if self.should_update_justified_checkpoint(justified_checkpoint, slots, spec)? {
self.fc_store
.set_justified_checkpoint(justified_checkpoint)
.map_err(Error::UnableToSetJustifiedCheckpoint)?;
}
self.fc_store
.set_justified_checkpoint(justified_checkpoint)
.map_err(Error::UnableToSetJustifiedCheckpoint)?;
}
// Update finalized checkpoint.
if finalized_checkpoint.epoch > self.fc_store.finalized_checkpoint().epoch {
self.fc_store.set_finalized_checkpoint(finalized_checkpoint);
self.fc_store
.set_justified_checkpoint(justified_checkpoint)
.map_err(Error::UnableToSetJustifiedCheckpoint)?;
}
Ok(())
}
@ -1167,9 +1046,8 @@ where
system_time_current_slot: Slot,
attestation: &IndexedAttestation<E>,
is_from_block: AttestationFromBlock,
spec: &ChainSpec,
) -> Result<(), Error<T::Error>> {
self.update_time(system_time_current_slot, spec)?;
self.update_time(system_time_current_slot)?;
// Ignore any attestations to the zero hash.
//
@ -1230,16 +1108,12 @@ where
/// Call `on_tick` for all slots between `fc_store.get_current_slot()` and the provided
/// `current_slot`. Returns the value of `self.fc_store.get_current_slot`.
pub fn update_time(
&mut self,
current_slot: Slot,
spec: &ChainSpec,
) -> Result<Slot, Error<T::Error>> {
pub fn update_time(&mut self, current_slot: Slot) -> Result<Slot, Error<T::Error>> {
while self.fc_store.get_current_slot() < current_slot {
let previous_slot = self.fc_store.get_current_slot();
// Note: we are relying upon `on_tick` to update `fc_store.time` to ensure we don't
// get stuck in a loop.
self.on_tick(previous_slot + 1, spec)?
self.on_tick(previous_slot + 1)?
}
// Process any attestations that might now be eligible.
@ -1255,7 +1129,7 @@ where
/// Equivalent to:
///
/// https://github.com/ethereum/eth2.0-specs/blob/v0.12.1/specs/phase0/fork-choice.md#on_tick
fn on_tick(&mut self, time: Slot, spec: &ChainSpec) -> Result<(), Error<T::Error>> {
fn on_tick(&mut self, time: Slot) -> Result<(), Error<T::Error>> {
let store = &mut self.fc_store;
let previous_slot = store.get_current_slot();
@ -1283,26 +1157,21 @@ where
return Ok(());
}
if store.best_justified_checkpoint().epoch > store.justified_checkpoint().epoch {
let store = &self.fc_store;
if self.is_finalized_checkpoint_or_descendant(store.best_justified_checkpoint().root) {
let store = &mut self.fc_store;
store
.set_justified_checkpoint(*store.best_justified_checkpoint())
.map_err(Error::ForkChoiceStoreError)?;
}
}
// Update the justified/finalized checkpoints based upon the
// best-observed unrealized justification/finality.
self.pull_up_store_checkpoints()?;
Ok(())
}
fn pull_up_store_checkpoints(&mut self) -> Result<(), Error<T::Error>> {
// Update store.justified_checkpoint if a better unrealized justified checkpoint is known
let unrealized_justified_checkpoint = *self.fc_store.unrealized_justified_checkpoint();
let unrealized_finalized_checkpoint = *self.fc_store.unrealized_finalized_checkpoint();
self.update_checkpoints(
unrealized_justified_checkpoint,
unrealized_finalized_checkpoint,
UpdateJustifiedCheckpointSlots::OnTick { current_slot },
spec,
)?;
Ok(())
)
}
/// Processes and removes from the queue any queued attestations which may now be eligible for
@ -1468,16 +1337,6 @@ where
*self.fc_store.justified_checkpoint()
}
/// Return the best justified checkpoint.
///
/// ## Warning
///
/// This is distinct to the "justified checkpoint" or the "current justified checkpoint". This
/// "best justified checkpoint" value should only be used internally or for testing.
pub fn best_justified_checkpoint(&self) -> Checkpoint {
*self.fc_store.best_justified_checkpoint()
}
pub fn unrealized_justified_checkpoint(&self) -> Checkpoint {
*self.fc_store.unrealized_justified_checkpoint()
}
@ -1538,13 +1397,11 @@ where
pub fn proto_array_from_persisted(
persisted: &PersistedForkChoice,
reset_payload_statuses: ResetPayloadStatuses,
count_unrealized_full: CountUnrealizedFull,
spec: &ChainSpec,
log: &Logger,
) -> Result<ProtoArrayForkChoice, Error<T::Error>> {
let mut proto_array =
ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes, count_unrealized_full)
.map_err(Error::InvalidProtoArrayBytes)?;
let mut proto_array = ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes)
.map_err(Error::InvalidProtoArrayBytes)?;
let contains_invalid_payloads = proto_array.contains_invalid_payloads();
debug!(
@ -1575,7 +1432,7 @@ where
"error" => e,
"info" => "please report this error",
);
ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes, count_unrealized_full)
ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes)
.map_err(Error::InvalidProtoArrayBytes)
} else {
debug!(
@ -1592,17 +1449,11 @@ where
persisted: PersistedForkChoice,
reset_payload_statuses: ResetPayloadStatuses,
fc_store: T,
count_unrealized_full: CountUnrealizedFull,
spec: &ChainSpec,
log: &Logger,
) -> Result<Self, Error<T::Error>> {
let proto_array = Self::proto_array_from_persisted(
&persisted,
reset_payload_statuses,
count_unrealized_full,
spec,
log,
)?;
let proto_array =
Self::proto_array_from_persisted(&persisted, reset_payload_statuses, spec, log)?;
let current_slot = fc_store.get_current_slot();

View File

@ -47,9 +47,6 @@ pub trait ForkChoiceStore<T: EthSpec>: Sized {
/// Returns balances from the `state` identified by `justified_checkpoint.root`.
fn justified_balances(&self) -> &JustifiedBalances;
/// Returns the `best_justified_checkpoint`.
fn best_justified_checkpoint(&self) -> &Checkpoint;
/// Returns the `finalized_checkpoint`.
fn finalized_checkpoint(&self) -> &Checkpoint;
@ -68,9 +65,6 @@ pub trait ForkChoiceStore<T: EthSpec>: Sized {
/// Sets the `justified_checkpoint`.
fn set_justified_checkpoint(&mut self, checkpoint: Checkpoint) -> Result<(), Self::Error>;
/// Sets the `best_justified_checkpoint`.
fn set_best_justified_checkpoint(&mut self, checkpoint: Checkpoint);
/// Sets the `unrealized_justified_checkpoint`.
fn set_unrealized_justified_checkpoint(&mut self, checkpoint: Checkpoint);

View File

@ -7,6 +7,4 @@ pub use crate::fork_choice::{
PersistedForkChoice, QueuedAttestation, ResetPayloadStatuses,
};
pub use fork_choice_store::ForkChoiceStore;
pub use proto_array::{
Block as ProtoBlock, CountUnrealizedFull, ExecutionStatus, InvalidationOperation,
};
pub use proto_array::{Block as ProtoBlock, ExecutionStatus, InvalidationOperation};

View File

@ -104,16 +104,6 @@ impl ForkChoiceTest {
self
}
/// Assert the epochs match.
pub fn assert_best_justified_epoch(self, epoch: u64) -> Self {
assert_eq!(
self.get(|fc_store| fc_store.best_justified_checkpoint().epoch),
Epoch::new(epoch),
"best_justified_epoch"
);
self
}
/// Assert the given slot is greater than the head slot.
pub fn assert_finalized_epoch_is_less_than(self, epoch: Epoch) -> Self {
assert!(self.harness.finalized_checkpoint().epoch < epoch);
@ -151,7 +141,7 @@ impl ForkChoiceTest {
.chain
.canonical_head
.fork_choice_write_lock()
.update_time(self.harness.chain.slot().unwrap(), &self.harness.spec)
.update_time(self.harness.chain.slot().unwrap())
.unwrap();
func(
self.harness
@ -241,6 +231,11 @@ impl ForkChoiceTest {
///
/// If the chain is presently in an unsafe period, transition through it and the following safe
/// period.
///
/// Note: the `SAFE_SLOTS_TO_UPDATE_JUSTIFIED` variable has been removed
/// from the fork choice spec in Q1 2023. We're still leaving references to
/// it in our tests because (a) it's easier and (b) it allows us to easily
/// test for the absence of that parameter.
pub fn move_to_next_unsafe_period(self) -> Self {
self.move_inside_safe_to_update()
.move_outside_safe_to_update()
@ -534,7 +529,6 @@ async fn justified_checkpoint_updates_with_descendent_outside_safe_slots() {
.unwrap()
.move_outside_safe_to_update()
.assert_justified_epoch(2)
.assert_best_justified_epoch(2)
.apply_blocks(1)
.await
.assert_justified_epoch(3);
@ -551,11 +545,9 @@ async fn justified_checkpoint_updates_first_justification_outside_safe_to_update
.unwrap()
.move_to_next_unsafe_period()
.assert_justified_epoch(0)
.assert_best_justified_epoch(0)
.apply_blocks(1)
.await
.assert_justified_epoch(2)
.assert_best_justified_epoch(2);
.assert_justified_epoch(2);
}
/// - The new justified checkpoint **does not** descend from the current.
@ -583,8 +575,7 @@ async fn justified_checkpoint_updates_with_non_descendent_inside_safe_slots_with
.unwrap();
})
.await
.assert_justified_epoch(3)
.assert_best_justified_epoch(3);
.assert_justified_epoch(3);
}
/// - The new justified checkpoint **does not** descend from the current.
@ -612,8 +603,9 @@ async fn justified_checkpoint_updates_with_non_descendent_outside_safe_slots_wit
.unwrap();
})
.await
.assert_justified_epoch(2)
.assert_best_justified_epoch(3);
// Now that `SAFE_SLOTS_TO_UPDATE_JUSTIFIED` has been removed, the new
// block should have updated the justified checkpoint.
.assert_justified_epoch(3);
}
/// - The new justified checkpoint **does not** descend from the current.
@ -641,8 +633,7 @@ async fn justified_checkpoint_updates_with_non_descendent_outside_safe_slots_wit
.unwrap();
})
.await
.assert_justified_epoch(3)
.assert_best_justified_epoch(3);
.assert_justified_epoch(3);
}
/// Check that the balances are obtained correctly.

View File

@ -3,7 +3,6 @@ mod ffg_updates;
mod no_votes;
mod votes;
use crate::proto_array::CountUnrealizedFull;
use crate::proto_array_fork_choice::{Block, ExecutionStatus, ProtoArrayForkChoice};
use crate::{InvalidationOperation, JustifiedBalances};
use serde_derive::{Deserialize, Serialize};
@ -88,7 +87,6 @@ impl ForkChoiceTestDefinition {
junk_shuffling_id.clone(),
junk_shuffling_id,
ExecutionStatus::Optimistic(ExecutionBlockHash::zero()),
CountUnrealizedFull::default(),
)
.expect("should create fork choice struct");
let equivocating_indices = BTreeSet::new();
@ -307,8 +305,8 @@ fn get_checkpoint(i: u64) -> Checkpoint {
fn check_bytes_round_trip(original: &ProtoArrayForkChoice) {
let bytes = original.as_bytes();
let decoded = ProtoArrayForkChoice::from_bytes(&bytes, CountUnrealizedFull::default())
.expect("fork choice should decode from bytes");
let decoded =
ProtoArrayForkChoice::from_bytes(&bytes).expect("fork choice should decode from bytes");
assert!(
*original == decoded,
"fork choice should encode and decode without change"

View File

@ -24,7 +24,7 @@ impl JustifiedBalances {
.validators()
.iter()
.map(|validator| {
if validator.is_active_at(current_epoch) {
if !validator.slashed && validator.is_active_at(current_epoch) {
total_effective_balance.safe_add_assign(validator.effective_balance)?;
num_active_validators.safe_add_assign(1)?;

View File

@ -6,9 +6,7 @@ mod proto_array_fork_choice;
mod ssz_container;
pub use crate::justified_balances::JustifiedBalances;
pub use crate::proto_array::{
calculate_committee_fraction, CountUnrealizedFull, InvalidationOperation,
};
pub use crate::proto_array::{calculate_committee_fraction, InvalidationOperation};
pub use crate::proto_array_fork_choice::{
Block, DoNotReOrg, ExecutionStatus, ProposerHeadError, ProposerHeadInfo, ProtoArrayForkChoice,
ReOrgThreshold,

View File

@ -118,24 +118,6 @@ impl Default for ProposerBoost {
}
}
/// Indicate whether we should strictly count unrealized justification/finalization votes.
#[derive(Default, PartialEq, Eq, Debug, Serialize, Deserialize, Copy, Clone)]
pub enum CountUnrealizedFull {
True,
#[default]
False,
}
impl From<bool> for CountUnrealizedFull {
fn from(b: bool) -> Self {
if b {
CountUnrealizedFull::True
} else {
CountUnrealizedFull::False
}
}
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
pub struct ProtoArray {
/// Do not attempt to prune the tree unless it has at least this many nodes. Small prunes
@ -146,7 +128,6 @@ pub struct ProtoArray {
pub nodes: Vec<ProtoNode>,
pub indices: HashMap<Hash256, usize>,
pub previous_proposer_boost: ProposerBoost,
pub count_unrealized_full: CountUnrealizedFull,
}
impl ProtoArray {
@ -900,55 +881,44 @@ impl ProtoArray {
}
let genesis_epoch = Epoch::new(0);
let checkpoint_match_predicate =
|node_justified_checkpoint: Checkpoint, node_finalized_checkpoint: Checkpoint| {
let correct_justified = node_justified_checkpoint == self.justified_checkpoint
|| self.justified_checkpoint.epoch == genesis_epoch;
let correct_finalized = node_finalized_checkpoint == self.finalized_checkpoint
|| self.finalized_checkpoint.epoch == genesis_epoch;
correct_justified && correct_finalized
let current_epoch = current_slot.epoch(E::slots_per_epoch());
let node_epoch = node.slot.epoch(E::slots_per_epoch());
let node_justified_checkpoint =
if let Some(justified_checkpoint) = node.justified_checkpoint {
justified_checkpoint
} else {
// The node does not have any information about the justified
// checkpoint. This indicates an inconsistent proto-array.
return false;
};
if let (
Some(unrealized_justified_checkpoint),
Some(unrealized_finalized_checkpoint),
Some(justified_checkpoint),
Some(finalized_checkpoint),
) = (
node.unrealized_justified_checkpoint,
node.unrealized_finalized_checkpoint,
node.justified_checkpoint,
node.finalized_checkpoint,
) {
let current_epoch = current_slot.epoch(E::slots_per_epoch());
// If previous epoch is justified, pull up all tips to at least the previous epoch
if CountUnrealizedFull::True == self.count_unrealized_full
&& (current_epoch > genesis_epoch
&& self.justified_checkpoint.epoch + 1 == current_epoch)
{
unrealized_justified_checkpoint.epoch + 1 >= current_epoch
// If previous epoch is not justified, pull up only tips from past epochs up to the current epoch
} else {
// If block is from a previous epoch, filter using unrealized justification & finalization information
if node.slot.epoch(E::slots_per_epoch()) < current_epoch {
checkpoint_match_predicate(
unrealized_justified_checkpoint,
unrealized_finalized_checkpoint,
)
// If block is from the current epoch, filter using the head state's justification & finalization information
} else {
checkpoint_match_predicate(justified_checkpoint, finalized_checkpoint)
}
}
} else if let (Some(justified_checkpoint), Some(finalized_checkpoint)) =
(node.justified_checkpoint, node.finalized_checkpoint)
{
checkpoint_match_predicate(justified_checkpoint, finalized_checkpoint)
let voting_source = if current_epoch > node_epoch {
// The block is from a prior epoch, the voting source will be pulled-up.
node.unrealized_justified_checkpoint
// Sometimes we don't track the unrealized justification. In
// that case, just use the fully-realized justified checkpoint.
.unwrap_or(node_justified_checkpoint)
} else {
false
// The block is not from a prior epoch, therefore the voting source
// is not pulled up.
node_justified_checkpoint
};
let mut correct_justified = self.justified_checkpoint.epoch == genesis_epoch
|| voting_source.epoch == self.justified_checkpoint.epoch;
if let Some(node_unrealized_justified_checkpoint) = node.unrealized_justified_checkpoint {
if !correct_justified && self.justified_checkpoint.epoch + 1 == current_epoch {
correct_justified = node_unrealized_justified_checkpoint.epoch
>= self.justified_checkpoint.epoch
&& voting_source.epoch + 2 >= current_epoch;
}
}
let correct_finalized = self.finalized_checkpoint.epoch == genesis_epoch
|| self.is_finalized_checkpoint_or_descendant::<E>(node.root);
correct_justified && correct_finalized
}
/// Return a reverse iterator over the nodes which comprise the chain ending at `block_root`.

View File

@ -1,8 +1,8 @@
use crate::{
error::Error,
proto_array::{
calculate_committee_fraction, CountUnrealizedFull, InvalidationOperation, Iter,
ProposerBoost, ProtoArray, ProtoNode,
calculate_committee_fraction, InvalidationOperation, Iter, ProposerBoost, ProtoArray,
ProtoNode,
},
ssz_container::SszContainer,
JustifiedBalances,
@ -307,7 +307,6 @@ impl ProtoArrayForkChoice {
current_epoch_shuffling_id: AttestationShufflingId,
next_epoch_shuffling_id: AttestationShufflingId,
execution_status: ExecutionStatus,
count_unrealized_full: CountUnrealizedFull,
) -> Result<Self, String> {
let mut proto_array = ProtoArray {
prune_threshold: DEFAULT_PRUNE_THRESHOLD,
@ -316,7 +315,6 @@ impl ProtoArrayForkChoice {
nodes: Vec::with_capacity(1),
indices: HashMap::with_capacity(1),
previous_proposer_boost: ProposerBoost::default(),
count_unrealized_full,
};
let block = Block {
@ -780,13 +778,10 @@ impl ProtoArrayForkChoice {
SszContainer::from(self).as_ssz_bytes()
}
pub fn from_bytes(
bytes: &[u8],
count_unrealized_full: CountUnrealizedFull,
) -> Result<Self, String> {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
let container = SszContainer::from_ssz_bytes(bytes)
.map_err(|e| format!("Failed to decode ProtoArrayForkChoice: {:?}", e))?;
(container, count_unrealized_full)
container
.try_into()
.map_err(|e| format!("Failed to initialize ProtoArrayForkChoice: {e:?}"))
}
@ -950,7 +945,6 @@ mod test_compute_deltas {
junk_shuffling_id.clone(),
junk_shuffling_id.clone(),
execution_status,
CountUnrealizedFull::default(),
)
.unwrap();
@ -1076,7 +1070,6 @@ mod test_compute_deltas {
junk_shuffling_id.clone(),
junk_shuffling_id.clone(),
execution_status,
CountUnrealizedFull::default(),
)
.unwrap();

View File

@ -1,6 +1,6 @@
use crate::proto_array::ProposerBoost;
use crate::{
proto_array::{CountUnrealizedFull, ProtoArray, ProtoNode},
proto_array::{ProtoArray, ProtoNode},
proto_array_fork_choice::{ElasticList, ProtoArrayForkChoice, VoteTracker},
Error, JustifiedBalances,
};
@ -43,12 +43,10 @@ impl From<&ProtoArrayForkChoice> for SszContainer {
}
}
impl TryFrom<(SszContainer, CountUnrealizedFull)> for ProtoArrayForkChoice {
impl TryFrom<SszContainer> for ProtoArrayForkChoice {
type Error = Error;
fn try_from(
(from, count_unrealized_full): (SszContainer, CountUnrealizedFull),
) -> Result<Self, Error> {
fn try_from(from: SszContainer) -> Result<Self, Error> {
let proto_array = ProtoArray {
prune_threshold: from.prune_threshold,
justified_checkpoint: from.justified_checkpoint,
@ -56,7 +54,6 @@ impl TryFrom<(SszContainer, CountUnrealizedFull)> for ProtoArrayForkChoice {
nodes: from.nodes,
indices: from.indices.into_iter().collect::<HashMap<_, _>>(),
previous_proposer_boost: from.previous_proposer_boost,
count_unrealized_full,
};
Ok(Self {

View File

@ -1,4 +1,4 @@
use beacon_node::{beacon_chain::CountUnrealizedFull, ClientConfig as Config};
use beacon_node::ClientConfig as Config;
use crate::exec::{CommandLineTestExec, CompletedTest};
use beacon_node::beacon_chain::chain_config::{
@ -232,74 +232,58 @@ fn paranoid_block_proposal_on() {
.with_config(|config| assert!(config.chain.paranoid_block_proposal));
}
#[test]
fn count_unrealized_default() {
CommandLineTest::new()
.run_with_zero_port()
.with_config(|config| assert!(config.chain.count_unrealized));
}
#[test]
fn count_unrealized_no_arg() {
CommandLineTest::new()
.flag("count-unrealized", None)
.run_with_zero_port()
.with_config(|config| assert!(config.chain.count_unrealized));
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]
fn count_unrealized_false() {
CommandLineTest::new()
.flag("count-unrealized", Some("false"))
.run_with_zero_port()
.with_config(|config| assert!(!config.chain.count_unrealized));
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]
fn count_unrealized_true() {
CommandLineTest::new()
.flag("count-unrealized", Some("true"))
.run_with_zero_port()
.with_config(|config| assert!(config.chain.count_unrealized));
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]
fn count_unrealized_full_no_arg() {
CommandLineTest::new()
.flag("count-unrealized-full", None)
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.chain.count_unrealized_full,
CountUnrealizedFull::False
)
});
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]
fn count_unrealized_full_false() {
CommandLineTest::new()
.flag("count-unrealized-full", Some("false"))
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.chain.count_unrealized_full,
CountUnrealizedFull::False
)
});
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]
fn count_unrealized_full_true() {
CommandLineTest::new()
.flag("count-unrealized-full", Some("true"))
.run_with_zero_port()
.with_config(|config| {
assert_eq!(
config.chain.count_unrealized_full,
CountUnrealizedFull::True
)
});
// This flag should be ignored, so there's nothing to test but that the
// client starts with the flag present.
.run_with_zero_port();
}
#[test]

View File

@ -1,4 +1,4 @@
TESTS_TAG := v1.3.0-rc.3
TESTS_TAG := v1.3.0-rc.4
TESTS = general minimal mainnet
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))

View File

@ -45,7 +45,6 @@ pub struct Checks {
justified_checkpoint: Option<Checkpoint>,
justified_checkpoint_root: Option<Hash256>,
finalized_checkpoint: Option<Checkpoint>,
best_justified_checkpoint: Option<Checkpoint>,
u_justified_checkpoint: Option<Checkpoint>,
u_finalized_checkpoint: Option<Checkpoint>,
proposer_boost_root: Option<Hash256>,
@ -229,7 +228,6 @@ impl<E: EthSpec> Case for ForkChoiceTest<E> {
justified_checkpoint,
justified_checkpoint_root,
finalized_checkpoint,
best_justified_checkpoint,
u_justified_checkpoint,
u_finalized_checkpoint,
proposer_boost_root,
@ -260,11 +258,6 @@ impl<E: EthSpec> Case for ForkChoiceTest<E> {
tester.check_finalized_checkpoint(*expected_finalized_checkpoint)?;
}
if let Some(expected_best_justified_checkpoint) = best_justified_checkpoint {
tester
.check_best_justified_checkpoint(*expected_best_justified_checkpoint)?;
}
if let Some(expected_u_justified_checkpoint) = u_justified_checkpoint {
tester.check_u_justified_checkpoint(*expected_u_justified_checkpoint)?;
}
@ -378,7 +371,7 @@ impl<E: EthSpec> Tester<E> {
.chain
.canonical_head
.fork_choice_write_lock()
.update_time(slot, &self.spec)
.update_time(slot)
.unwrap();
}
@ -388,7 +381,7 @@ impl<E: EthSpec> Tester<E> {
let result = self.block_on_dangerous(self.harness.chain.process_block(
block_root,
block.clone(),
CountUnrealized::False,
CountUnrealized::True,
NotifyExecutionLayer::Yes,
))?;
if result.is_ok() != valid {
@ -448,7 +441,7 @@ impl<E: EthSpec> Tester<E> {
&state,
PayloadVerificationStatus::Irrelevant,
&self.harness.chain.spec,
self.harness.chain.config.count_unrealized.into(),
CountUnrealized::True,
);
if result.is_ok() {
@ -576,23 +569,6 @@ impl<E: EthSpec> Tester<E> {
check_equal("finalized_checkpoint", fc_checkpoint, expected_checkpoint)
}
pub fn check_best_justified_checkpoint(
&self,
expected_checkpoint: Checkpoint,
) -> Result<(), Error> {
let best_justified_checkpoint = self
.harness
.chain
.canonical_head
.fork_choice_read_lock()
.best_justified_checkpoint();
check_equal(
"best_justified_checkpoint",
best_justified_checkpoint,
expected_checkpoint,
)
}
pub fn check_u_justified_checkpoint(
&self,
expected_checkpoint: Checkpoint,

View File

@ -547,6 +547,11 @@ impl<E: EthSpec + TypeName> Handler for ForkChoiceHandler<E> {
return false;
}
// Tests are no longer generated for the base/phase0 specification.
if fork_name == ForkName::Base {
return false;
}
// These tests check block validity (which may include signatures) and there is no need to
// run them with fake crypto.
cfg!(not(feature = "fake_crypto"))

View File

@ -503,6 +503,18 @@ fn fork_choice_ex_ante() {
ForkChoiceHandler::<MainnetEthSpec>::new("ex_ante").run();
}
#[test]
fn fork_choice_reorg() {
ForkChoiceHandler::<MinimalEthSpec>::new("reorg").run();
// There is no mainnet variant for this test.
}
#[test]
fn fork_choice_withholding() {
ForkChoiceHandler::<MinimalEthSpec>::new("withholding").run();
// There is no mainnet variant for this test.
}
#[test]
fn optimistic_sync() {
OptimisticSyncHandler::<MinimalEthSpec>::default().run();