Realized unrealized experimentation (#3322)
## Issue Addressed Add a flag that optionally enables unrealized vote tracking. Would like to test out on testnets and benchmark differences in methods of vote tracking. This PR includes a DB schema upgrade to enable to new vote tracking style. Co-authored-by: realbigsean <sean@sigmaprime.io> Co-authored-by: Paul Hauner <paul@paulhauner.com> Co-authored-by: sean <seananderson33@gmail.com> Co-authored-by: Mac L <mjladson@pm.me>
This commit is contained in:
co-authored by
realbigsean
Paul Hauner
Mac L
parent
bb5a6d2cca
commit
20ebf1f3c1
@@ -93,6 +93,7 @@ use types::beacon_state::CloneConfig;
|
||||
use types::*;
|
||||
|
||||
pub use crate::canonical_head::{CanonicalHead, CanonicalHeadRwLock};
|
||||
pub use fork_choice::CountUnrealized;
|
||||
|
||||
pub type ForkChoiceError = fork_choice::Error<crate::ForkChoiceStoreError>;
|
||||
|
||||
@@ -1740,6 +1741,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.slot()?,
|
||||
verified.indexed_attestation(),
|
||||
AttestationFromBlock::False,
|
||||
&self.spec,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@@ -2220,6 +2222,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub async fn process_chain_segment(
|
||||
self: &Arc<Self>,
|
||||
chain_segment: Vec<Arc<SignedBeaconBlock<T::EthSpec>>>,
|
||||
count_unrealized: CountUnrealized,
|
||||
) -> ChainSegmentResult<T::EthSpec> {
|
||||
let mut imported_blocks = 0;
|
||||
|
||||
@@ -2284,7 +2287,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// Import the blocks into the chain.
|
||||
for signature_verified_block in signature_verified_blocks {
|
||||
match self.process_block(signature_verified_block).await {
|
||||
match self
|
||||
.process_block(signature_verified_block, count_unrealized)
|
||||
.await
|
||||
{
|
||||
Ok(_) => imported_blocks += 1,
|
||||
Err(error) => {
|
||||
return ChainSegmentResult::Failed {
|
||||
@@ -2368,6 +2374,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
pub async fn process_block<B: IntoExecutionPendingBlock<T>>(
|
||||
self: &Arc<Self>,
|
||||
unverified_block: B,
|
||||
count_unrealized: CountUnrealized,
|
||||
) -> Result<Hash256, BlockError<T::EthSpec>> {
|
||||
// Start the Prometheus timer.
|
||||
let _full_timer = metrics::start_timer(&metrics::BLOCK_PROCESSING_TIMES);
|
||||
@@ -2383,7 +2390,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
let import_block = async move {
|
||||
let execution_pending = unverified_block.into_execution_pending_block(&chain)?;
|
||||
chain
|
||||
.import_execution_pending_block(execution_pending)
|
||||
.import_execution_pending_block(execution_pending, count_unrealized)
|
||||
.await
|
||||
};
|
||||
|
||||
@@ -2441,6 +2448,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
async fn import_execution_pending_block(
|
||||
self: Arc<Self>,
|
||||
execution_pending_block: ExecutionPendingBlock<T>,
|
||||
count_unrealized: CountUnrealized,
|
||||
) -> Result<Hash256, BlockError<T::EthSpec>> {
|
||||
let ExecutionPendingBlock {
|
||||
block,
|
||||
@@ -2499,6 +2507,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
state,
|
||||
confirmed_state_roots,
|
||||
payload_verification_status,
|
||||
count_unrealized,
|
||||
)
|
||||
},
|
||||
"payload_verification_handle",
|
||||
@@ -2520,6 +2529,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
mut state: BeaconState<T::EthSpec>,
|
||||
confirmed_state_roots: Vec<Hash256>,
|
||||
payload_verification_status: PayloadVerificationStatus,
|
||||
count_unrealized: CountUnrealized,
|
||||
) -> Result<Hash256, BlockError<T::EthSpec>> {
|
||||
let current_slot = self.slot()?;
|
||||
let current_epoch = current_slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
@@ -2665,6 +2675,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
&state,
|
||||
payload_verification_status,
|
||||
&self.spec,
|
||||
count_unrealized.and(self.config.count_unrealized.into()),
|
||||
)
|
||||
.map_err(|e| BlockError::BeaconChainError(e.into()))?;
|
||||
}
|
||||
@@ -2690,6 +2701,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
current_slot,
|
||||
&indexed_attestation,
|
||||
AttestationFromBlock::True,
|
||||
&self.spec,
|
||||
) {
|
||||
Ok(()) => Ok(()),
|
||||
// Ignore invalid attestations whilst importing attestations from a block. The
|
||||
|
||||
@@ -155,6 +155,8 @@ pub struct BeaconForkChoiceStore<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<
|
||||
justified_checkpoint: Checkpoint,
|
||||
justified_balances: Vec<u64>,
|
||||
best_justified_checkpoint: Checkpoint,
|
||||
unrealized_justified_checkpoint: Checkpoint,
|
||||
unrealized_finalized_checkpoint: Checkpoint,
|
||||
proposer_boost_root: Hash256,
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
@@ -201,6 +203,8 @@ where
|
||||
justified_balances: anchor_state.balances().clone().into(),
|
||||
finalized_checkpoint,
|
||||
best_justified_checkpoint: justified_checkpoint,
|
||||
unrealized_justified_checkpoint: justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: finalized_checkpoint,
|
||||
proposer_boost_root: Hash256::zero(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
@@ -216,6 +220,8 @@ where
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
justified_balances: self.justified_balances.clone(),
|
||||
best_justified_checkpoint: self.best_justified_checkpoint,
|
||||
unrealized_justified_checkpoint: self.unrealized_justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: self.unrealized_finalized_checkpoint,
|
||||
proposer_boost_root: self.proposer_boost_root,
|
||||
}
|
||||
}
|
||||
@@ -233,6 +239,8 @@ where
|
||||
justified_checkpoint: persisted.justified_checkpoint,
|
||||
justified_balances: persisted.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,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
@@ -280,6 +288,14 @@ where
|
||||
&self.finalized_checkpoint
|
||||
}
|
||||
|
||||
fn unrealized_justified_checkpoint(&self) -> &Checkpoint {
|
||||
&self.unrealized_justified_checkpoint
|
||||
}
|
||||
|
||||
fn unrealized_finalized_checkpoint(&self) -> &Checkpoint {
|
||||
&self.unrealized_finalized_checkpoint
|
||||
}
|
||||
|
||||
fn proposer_boost_root(&self) -> Hash256 {
|
||||
self.proposer_boost_root
|
||||
}
|
||||
@@ -323,6 +339,14 @@ where
|
||||
self.best_justified_checkpoint = checkpoint
|
||||
}
|
||||
|
||||
fn set_unrealized_justified_checkpoint(&mut self, checkpoint: Checkpoint) {
|
||||
self.unrealized_justified_checkpoint = checkpoint;
|
||||
}
|
||||
|
||||
fn set_unrealized_finalized_checkpoint(&mut self, checkpoint: Checkpoint) {
|
||||
self.unrealized_finalized_checkpoint = checkpoint;
|
||||
}
|
||||
|
||||
fn set_proposer_boost_root(&mut self, proposer_boost_root: Hash256) {
|
||||
self.proposer_boost_root = proposer_boost_root;
|
||||
}
|
||||
@@ -330,22 +354,26 @@ where
|
||||
|
||||
/// A container which allows persisting the `BeaconForkChoiceStore` to the on-disk database.
|
||||
#[superstruct(
|
||||
variants(V1, V7, V8),
|
||||
variants(V1, V7, V8, V10),
|
||||
variant_attributes(derive(Encode, Decode)),
|
||||
no_enum
|
||||
)]
|
||||
pub struct PersistedForkChoiceStore {
|
||||
#[superstruct(only(V1, V7))]
|
||||
pub balances_cache: BalancesCacheV1,
|
||||
#[superstruct(only(V8))]
|
||||
#[superstruct(only(V8, V10))]
|
||||
pub balances_cache: BalancesCacheV8,
|
||||
pub time: Slot,
|
||||
pub finalized_checkpoint: Checkpoint,
|
||||
pub justified_checkpoint: Checkpoint,
|
||||
pub justified_balances: Vec<u64>,
|
||||
pub best_justified_checkpoint: Checkpoint,
|
||||
#[superstruct(only(V7, V8))]
|
||||
#[superstruct(only(V10))]
|
||||
pub unrealized_justified_checkpoint: Checkpoint,
|
||||
#[superstruct(only(V10))]
|
||||
pub unrealized_finalized_checkpoint: Checkpoint,
|
||||
#[superstruct(only(V7, V8, V10))]
|
||||
pub proposer_boost_root: Hash256,
|
||||
}
|
||||
|
||||
pub type PersistedForkChoiceStore = PersistedForkChoiceStoreV8;
|
||||
pub type PersistedForkChoiceStore = PersistedForkChoiceStoreV10;
|
||||
|
||||
@@ -1416,6 +1416,10 @@ fn check_block_against_finalized_slot<T: BeaconChainTypes>(
|
||||
block_root: Hash256,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<(), BlockError<T::EthSpec>> {
|
||||
// The finalized checkpoint is being read from fork choice, rather than the cached head.
|
||||
//
|
||||
// Fork choice has the most up-to-date view of finalization and there's no point importing a
|
||||
// block which conflicts with the fork-choice view of finalization.
|
||||
let finalized_slot = chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
|
||||
@@ -647,6 +647,7 @@ where
|
||||
store.clone(),
|
||||
Some(current_slot),
|
||||
&self.spec,
|
||||
self.chain_config.count_unrealized.into(),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct ChainConfig {
|
||||
///
|
||||
/// If set to 0 then block proposal will not wait for fork choice at all.
|
||||
pub fork_choice_before_proposal_timeout_ms: u64,
|
||||
pub count_unrealized: bool,
|
||||
}
|
||||
|
||||
impl Default for ChainConfig {
|
||||
@@ -35,6 +36,7 @@ impl Default for ChainConfig {
|
||||
enable_lock_timeouts: true,
|
||||
max_network_size: 10 * 1_048_576, // 10M
|
||||
fork_choice_before_proposal_timeout_ms: DEFAULT_FORK_CHOICE_BEFORE_PROPOSAL_TIMEOUT,
|
||||
count_unrealized: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{BeaconForkChoiceStore, BeaconSnapshot};
|
||||
use fork_choice::{ForkChoice, PayloadVerificationStatus};
|
||||
use fork_choice::{CountUnrealized, ForkChoice, PayloadVerificationStatus};
|
||||
use itertools::process_results;
|
||||
use slog::{info, warn, Logger};
|
||||
use state_processing::state_advance::complete_state_advance;
|
||||
@@ -99,6 +99,7 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
|
||||
store: Arc<HotColdDB<E, Hot, Cold>>,
|
||||
current_slot: Option<Slot>,
|
||||
spec: &ChainSpec,
|
||||
count_unrealized_config: CountUnrealized,
|
||||
) -> Result<ForkChoice<BeaconForkChoiceStore<E, Hot, Cold>, E>, String> {
|
||||
// Fetch finalized block.
|
||||
let finalized_checkpoint = head_state.finalized_checkpoint();
|
||||
@@ -163,7 +164,8 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
|
||||
.map_err(|e| format!("Error loading blocks to replay for fork choice: {:?}", e))?;
|
||||
|
||||
let mut state = finalized_snapshot.beacon_state;
|
||||
for block in blocks {
|
||||
let blocks_len = blocks.len();
|
||||
for (i, block) in blocks.into_iter().enumerate() {
|
||||
complete_state_advance(&mut state, None, block.slot(), spec)
|
||||
.map_err(|e| format!("State advance failed: {:?}", e))?;
|
||||
|
||||
@@ -183,6 +185,15 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
|
||||
// This scenario is so rare that it seems OK to double-verify some blocks.
|
||||
let payload_verification_status = PayloadVerificationStatus::Optimistic;
|
||||
|
||||
// Because we are replaying a single chain of blocks, we only need to calculate unrealized
|
||||
// justification for the last block in the chain.
|
||||
let is_last_block = i + 1 == blocks_len;
|
||||
let count_unrealized = if is_last_block {
|
||||
count_unrealized_config
|
||||
} else {
|
||||
CountUnrealized::False
|
||||
};
|
||||
|
||||
fork_choice
|
||||
.on_block(
|
||||
block.slot(),
|
||||
@@ -193,6 +204,7 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
|
||||
&state,
|
||||
payload_verification_status,
|
||||
spec,
|
||||
count_unrealized,
|
||||
)
|
||||
.map_err(|e| format!("Error applying replayed block to fork choice: {:?}", e))?;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ mod validator_pubkey_cache;
|
||||
|
||||
pub use self::beacon_chain::{
|
||||
AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BeaconStore, ChainSegmentResult,
|
||||
ForkChoiceError, ProduceBlockVerification, StateSkipConfig, WhenSlotSkipped,
|
||||
CountUnrealized, ForkChoiceError, ProduceBlockVerification, StateSkipConfig, WhenSlotSkipped,
|
||||
INVALID_JUSTIFIED_PAYLOAD_SHUTDOWN_REASON, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||
};
|
||||
pub use self::beacon_snapshot::BeaconSnapshot;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::beacon_fork_choice_store::{
|
||||
PersistedForkChoiceStoreV1, PersistedForkChoiceStoreV7, PersistedForkChoiceStoreV8,
|
||||
PersistedForkChoiceStoreV1, PersistedForkChoiceStoreV10, PersistedForkChoiceStoreV7,
|
||||
PersistedForkChoiceStoreV8,
|
||||
};
|
||||
use ssz::{Decode, Encode};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
@@ -7,10 +8,10 @@ use store::{DBColumn, Error, StoreItem};
|
||||
use superstruct::superstruct;
|
||||
|
||||
// If adding a new version you should update this type alias and fix the breakages.
|
||||
pub type PersistedForkChoice = PersistedForkChoiceV8;
|
||||
pub type PersistedForkChoice = PersistedForkChoiceV10;
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V7, V8),
|
||||
variants(V1, V7, V8, V10),
|
||||
variant_attributes(derive(Encode, Decode)),
|
||||
no_enum
|
||||
)]
|
||||
@@ -22,6 +23,8 @@ pub struct PersistedForkChoice {
|
||||
pub fork_choice_store: PersistedForkChoiceStoreV7,
|
||||
#[superstruct(only(V8))]
|
||||
pub fork_choice_store: PersistedForkChoiceStoreV8,
|
||||
#[superstruct(only(V10))]
|
||||
pub fork_choice_store: PersistedForkChoiceStoreV10,
|
||||
}
|
||||
|
||||
macro_rules! impl_store_item {
|
||||
@@ -45,3 +48,4 @@ macro_rules! impl_store_item {
|
||||
impl_store_item!(PersistedForkChoiceV1);
|
||||
impl_store_item!(PersistedForkChoiceV7);
|
||||
impl_store_item!(PersistedForkChoiceV8);
|
||||
impl_store_item!(PersistedForkChoiceV10);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! Utilities for managing database schema changes.
|
||||
mod migration_schema_v10;
|
||||
mod migration_schema_v6;
|
||||
mod migration_schema_v7;
|
||||
mod migration_schema_v8;
|
||||
@@ -6,7 +7,9 @@ mod migration_schema_v9;
|
||||
mod types;
|
||||
|
||||
use crate::beacon_chain::{BeaconChainTypes, FORK_CHOICE_DB_KEY};
|
||||
use crate::persisted_fork_choice::{PersistedForkChoiceV1, PersistedForkChoiceV7};
|
||||
use crate::persisted_fork_choice::{
|
||||
PersistedForkChoiceV1, PersistedForkChoiceV10, PersistedForkChoiceV7, PersistedForkChoiceV8,
|
||||
};
|
||||
use crate::types::ChainSpec;
|
||||
use slog::{warn, Logger};
|
||||
use std::path::Path;
|
||||
@@ -130,6 +133,32 @@ pub fn migrate_schema<T: BeaconChainTypes>(
|
||||
migration_schema_v9::downgrade_from_v9::<T>(db.clone(), log)?;
|
||||
db.store_schema_version(to)
|
||||
}
|
||||
(SchemaVersion(9), SchemaVersion(10)) => {
|
||||
let mut ops = vec![];
|
||||
let fork_choice_opt = db.get_item::<PersistedForkChoiceV8>(&FORK_CHOICE_DB_KEY)?;
|
||||
if let Some(fork_choice) = fork_choice_opt {
|
||||
let updated_fork_choice = migration_schema_v10::update_fork_choice(fork_choice)?;
|
||||
|
||||
ops.push(updated_fork_choice.as_kv_store_op(FORK_CHOICE_DB_KEY));
|
||||
}
|
||||
|
||||
db.store_schema_version_atomically(to, ops)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
(SchemaVersion(10), SchemaVersion(9)) => {
|
||||
let mut ops = vec![];
|
||||
let fork_choice_opt = db.get_item::<PersistedForkChoiceV10>(&FORK_CHOICE_DB_KEY)?;
|
||||
if let Some(fork_choice) = fork_choice_opt {
|
||||
let updated_fork_choice = migration_schema_v10::downgrade_fork_choice(fork_choice)?;
|
||||
|
||||
ops.push(updated_fork_choice.as_kv_store_op(FORK_CHOICE_DB_KEY));
|
||||
}
|
||||
|
||||
db.store_schema_version_atomically(to, ops)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Anything else is an error.
|
||||
(_, _) => Err(HotColdDBError::UnsupportedSchemaVersion {
|
||||
target_version: to,
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
use crate::beacon_fork_choice_store::{PersistedForkChoiceStoreV10, PersistedForkChoiceStoreV8};
|
||||
use crate::persisted_fork_choice::{PersistedForkChoiceV10, PersistedForkChoiceV8};
|
||||
use crate::schema_change::{
|
||||
types::{SszContainerV10, SszContainerV7},
|
||||
StoreError,
|
||||
};
|
||||
use proto_array::core::SszContainer;
|
||||
use ssz::{Decode, Encode};
|
||||
|
||||
pub fn update_fork_choice(
|
||||
mut fork_choice: PersistedForkChoiceV8,
|
||||
) -> Result<PersistedForkChoiceV10, StoreError> {
|
||||
let ssz_container_v7 = SszContainerV7::from_ssz_bytes(
|
||||
&fork_choice.fork_choice.proto_array_bytes,
|
||||
)
|
||||
.map_err(|e| {
|
||||
StoreError::SchemaMigrationError(format!(
|
||||
"Failed to decode ProtoArrayForkChoice during schema migration: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
// These transformations instantiate `node.unrealized_justified_checkpoint` and
|
||||
// `node.unrealized_finalized_checkpoint` to `None`.
|
||||
let ssz_container_v10: SszContainerV10 = ssz_container_v7.into();
|
||||
let ssz_container: SszContainer = ssz_container_v10.into();
|
||||
fork_choice.fork_choice.proto_array_bytes = ssz_container.as_ssz_bytes();
|
||||
|
||||
Ok(fork_choice.into())
|
||||
}
|
||||
|
||||
pub fn downgrade_fork_choice(
|
||||
mut fork_choice: PersistedForkChoiceV10,
|
||||
) -> Result<PersistedForkChoiceV8, StoreError> {
|
||||
let ssz_container_v10 = SszContainerV10::from_ssz_bytes(
|
||||
&fork_choice.fork_choice.proto_array_bytes,
|
||||
)
|
||||
.map_err(|e| {
|
||||
StoreError::SchemaMigrationError(format!(
|
||||
"Failed to decode ProtoArrayForkChoice during schema migration: {:?}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let ssz_container_v7: SszContainerV7 = ssz_container_v10.into();
|
||||
fork_choice.fork_choice.proto_array_bytes = ssz_container_v7.as_ssz_bytes();
|
||||
|
||||
Ok(fork_choice.into())
|
||||
}
|
||||
|
||||
impl From<PersistedForkChoiceStoreV8> for PersistedForkChoiceStoreV10 {
|
||||
fn from(other: PersistedForkChoiceStoreV8) -> Self {
|
||||
Self {
|
||||
balances_cache: other.balances_cache,
|
||||
time: other.time,
|
||||
finalized_checkpoint: other.finalized_checkpoint,
|
||||
justified_checkpoint: other.justified_checkpoint,
|
||||
justified_balances: other.justified_balances,
|
||||
best_justified_checkpoint: other.best_justified_checkpoint,
|
||||
unrealized_justified_checkpoint: other.best_justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: other.finalized_checkpoint,
|
||||
proposer_boost_root: other.proposer_boost_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PersistedForkChoiceV8> for PersistedForkChoiceV10 {
|
||||
fn from(other: PersistedForkChoiceV8) -> Self {
|
||||
Self {
|
||||
fork_choice: other.fork_choice,
|
||||
fork_choice_store: other.fork_choice_store.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PersistedForkChoiceStoreV10> for PersistedForkChoiceStoreV8 {
|
||||
fn from(other: PersistedForkChoiceStoreV10) -> Self {
|
||||
Self {
|
||||
balances_cache: other.balances_cache,
|
||||
time: other.time,
|
||||
finalized_checkpoint: other.finalized_checkpoint,
|
||||
justified_checkpoint: other.justified_checkpoint,
|
||||
justified_balances: other.justified_balances,
|
||||
best_justified_checkpoint: other.best_justified_checkpoint,
|
||||
proposer_boost_root: other.proposer_boost_root,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PersistedForkChoiceV10> for PersistedForkChoiceV8 {
|
||||
fn from(other: PersistedForkChoiceV10) -> Self {
|
||||
Self {
|
||||
fork_choice: other.fork_choice,
|
||||
fork_choice_store: other.fork_choice_store.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
use crate::beacon_chain::BeaconChainTypes;
|
||||
use crate::beacon_fork_choice_store::{PersistedForkChoiceStoreV1, PersistedForkChoiceStoreV7};
|
||||
use crate::persisted_fork_choice::{PersistedForkChoiceV1, PersistedForkChoiceV7};
|
||||
use crate::schema_change::types::{ProtoNodeV6, SszContainerV6, SszContainerV7};
|
||||
use crate::schema_change::types::{ProtoNodeV6, SszContainerV10, SszContainerV6, SszContainerV7};
|
||||
use crate::types::{ChainSpec, Checkpoint, Epoch, EthSpec, Hash256, Slot};
|
||||
use crate::{BeaconForkChoiceStore, BeaconSnapshot};
|
||||
use fork_choice::ForkChoice;
|
||||
@@ -86,7 +86,8 @@ pub(crate) fn update_fork_choice<T: BeaconChainTypes>(
|
||||
// to `None`.
|
||||
let ssz_container_v7: SszContainerV7 =
|
||||
ssz_container_v6.into_ssz_container_v7(justified_checkpoint, finalized_checkpoint);
|
||||
let ssz_container: SszContainer = ssz_container_v7.into();
|
||||
let ssz_container_v10: SszContainerV10 = ssz_container_v7.into();
|
||||
let ssz_container: SszContainer = ssz_container_v10.into();
|
||||
let mut fork_choice: ProtoArrayForkChoice = ssz_container.into();
|
||||
|
||||
update_checkpoints::<T>(finalized_checkpoint.root, &nodes_v6, &mut fork_choice, db)
|
||||
@@ -97,6 +98,13 @@ pub(crate) fn update_fork_choice<T: BeaconChainTypes>(
|
||||
update_store_justified_checkpoint(persisted_fork_choice, &mut fork_choice)
|
||||
.map_err(StoreError::SchemaMigrationError)?;
|
||||
|
||||
// Need to downgrade the SSZ container to V7 so that all migrations can be applied in sequence.
|
||||
let ssz_container = SszContainer::from(&fork_choice);
|
||||
let ssz_container_v7 = SszContainerV7::from(ssz_container);
|
||||
|
||||
persisted_fork_choice.fork_choice.proto_array_bytes = ssz_container_v7.as_ssz_bytes();
|
||||
persisted_fork_choice.fork_choice_store.justified_checkpoint = justified_checkpoint;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -301,8 +309,6 @@ fn update_store_justified_checkpoint(
|
||||
.ok_or("Proto node with current finalized checkpoint not found")?;
|
||||
|
||||
fork_choice.core_proto_array_mut().justified_checkpoint = justified_checkpoint;
|
||||
persisted_fork_choice.fork_choice.proto_array_bytes = fork_choice.as_bytes();
|
||||
persisted_fork_choice.fork_choice_store.justified_checkpoint = justified_checkpoint;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ four_byte_option_impl!(four_byte_option_usize, usize);
|
||||
four_byte_option_impl!(four_byte_option_checkpoint, Checkpoint);
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V6, V7),
|
||||
variants(V1, V6, V7, V10),
|
||||
variant_attributes(derive(Clone, PartialEq, Debug, Encode, Decode)),
|
||||
no_enum
|
||||
)]
|
||||
@@ -30,18 +30,24 @@ pub struct ProtoNode {
|
||||
#[superstruct(only(V1, V6))]
|
||||
pub finalized_epoch: Epoch,
|
||||
#[ssz(with = "four_byte_option_checkpoint")]
|
||||
#[superstruct(only(V7))]
|
||||
#[superstruct(only(V7, V10))]
|
||||
pub justified_checkpoint: Option<Checkpoint>,
|
||||
#[ssz(with = "four_byte_option_checkpoint")]
|
||||
#[superstruct(only(V7))]
|
||||
#[superstruct(only(V7, V10))]
|
||||
pub finalized_checkpoint: Option<Checkpoint>,
|
||||
pub weight: u64,
|
||||
#[ssz(with = "four_byte_option_usize")]
|
||||
pub best_child: Option<usize>,
|
||||
#[ssz(with = "four_byte_option_usize")]
|
||||
pub best_descendant: Option<usize>,
|
||||
#[superstruct(only(V6, V7))]
|
||||
#[superstruct(only(V6, V7, V10))]
|
||||
pub execution_status: ExecutionStatus,
|
||||
#[ssz(with = "four_byte_option_checkpoint")]
|
||||
#[superstruct(only(V10))]
|
||||
pub unrealized_justified_checkpoint: Option<Checkpoint>,
|
||||
#[ssz(with = "four_byte_option_checkpoint")]
|
||||
#[superstruct(only(V10))]
|
||||
pub unrealized_finalized_checkpoint: Option<Checkpoint>,
|
||||
}
|
||||
|
||||
impl Into<ProtoNodeV6> for ProtoNodeV1 {
|
||||
@@ -88,9 +94,31 @@ impl Into<ProtoNodeV7> for ProtoNodeV6 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ProtoNode> for ProtoNodeV7 {
|
||||
fn into(self) -> ProtoNode {
|
||||
ProtoNode {
|
||||
impl Into<ProtoNodeV10> for ProtoNodeV7 {
|
||||
fn into(self) -> ProtoNodeV10 {
|
||||
ProtoNodeV10 {
|
||||
slot: self.slot,
|
||||
state_root: self.state_root,
|
||||
target_root: self.target_root,
|
||||
current_epoch_shuffling_id: self.current_epoch_shuffling_id,
|
||||
next_epoch_shuffling_id: self.next_epoch_shuffling_id,
|
||||
root: self.root,
|
||||
parent: self.parent,
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
finalized_checkpoint: self.finalized_checkpoint,
|
||||
weight: self.weight,
|
||||
best_child: self.best_child,
|
||||
best_descendant: self.best_descendant,
|
||||
execution_status: self.execution_status,
|
||||
unrealized_justified_checkpoint: None,
|
||||
unrealized_finalized_checkpoint: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ProtoNodeV7> for ProtoNodeV10 {
|
||||
fn into(self) -> ProtoNodeV7 {
|
||||
ProtoNodeV7 {
|
||||
slot: self.slot,
|
||||
state_root: self.state_root,
|
||||
target_root: self.target_root,
|
||||
@@ -108,8 +136,50 @@ impl Into<ProtoNode> for ProtoNodeV7 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ProtoNode> for ProtoNodeV10 {
|
||||
fn into(self) -> ProtoNode {
|
||||
ProtoNode {
|
||||
slot: self.slot,
|
||||
state_root: self.state_root,
|
||||
target_root: self.target_root,
|
||||
current_epoch_shuffling_id: self.current_epoch_shuffling_id,
|
||||
next_epoch_shuffling_id: self.next_epoch_shuffling_id,
|
||||
root: self.root,
|
||||
parent: self.parent,
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
finalized_checkpoint: self.finalized_checkpoint,
|
||||
weight: self.weight,
|
||||
best_child: self.best_child,
|
||||
best_descendant: self.best_descendant,
|
||||
execution_status: self.execution_status,
|
||||
unrealized_justified_checkpoint: self.unrealized_justified_checkpoint,
|
||||
unrealized_finalized_checkpoint: self.unrealized_finalized_checkpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ProtoNode> for ProtoNodeV7 {
|
||||
fn from(container: ProtoNode) -> Self {
|
||||
Self {
|
||||
slot: container.slot,
|
||||
state_root: container.state_root,
|
||||
target_root: container.target_root,
|
||||
current_epoch_shuffling_id: container.current_epoch_shuffling_id,
|
||||
next_epoch_shuffling_id: container.next_epoch_shuffling_id,
|
||||
root: container.root,
|
||||
parent: container.parent,
|
||||
justified_checkpoint: container.justified_checkpoint,
|
||||
finalized_checkpoint: container.finalized_checkpoint,
|
||||
weight: container.weight,
|
||||
best_child: container.best_child,
|
||||
best_descendant: container.best_descendant,
|
||||
execution_status: container.execution_status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[superstruct(
|
||||
variants(V1, V6, V7),
|
||||
variants(V1, V6, V7, V10),
|
||||
variant_attributes(derive(Encode, Decode)),
|
||||
no_enum
|
||||
)]
|
||||
@@ -122,9 +192,9 @@ pub struct SszContainer {
|
||||
pub justified_epoch: Epoch,
|
||||
#[superstruct(only(V1, V6))]
|
||||
pub finalized_epoch: Epoch,
|
||||
#[superstruct(only(V7))]
|
||||
#[superstruct(only(V7, V10))]
|
||||
pub justified_checkpoint: Checkpoint,
|
||||
#[superstruct(only(V7))]
|
||||
#[superstruct(only(V7, V10))]
|
||||
pub finalized_checkpoint: Checkpoint,
|
||||
#[superstruct(only(V1))]
|
||||
pub nodes: Vec<ProtoNodeV1>,
|
||||
@@ -132,8 +202,10 @@ pub struct SszContainer {
|
||||
pub nodes: Vec<ProtoNodeV6>,
|
||||
#[superstruct(only(V7))]
|
||||
pub nodes: Vec<ProtoNodeV7>,
|
||||
#[superstruct(only(V10))]
|
||||
pub nodes: Vec<ProtoNodeV10>,
|
||||
pub indices: Vec<(Hash256, usize)>,
|
||||
#[superstruct(only(V7))]
|
||||
#[superstruct(only(V7, V10))]
|
||||
pub previous_proposer_boost: ProposerBoost,
|
||||
}
|
||||
|
||||
@@ -174,7 +246,41 @@ impl SszContainerV6 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SszContainer> for SszContainerV7 {
|
||||
impl Into<SszContainerV10> for SszContainerV7 {
|
||||
fn into(self) -> SszContainerV10 {
|
||||
let nodes = self.nodes.into_iter().map(Into::into).collect();
|
||||
|
||||
SszContainerV10 {
|
||||
votes: self.votes,
|
||||
balances: self.balances,
|
||||
prune_threshold: self.prune_threshold,
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
finalized_checkpoint: self.finalized_checkpoint,
|
||||
nodes,
|
||||
indices: self.indices,
|
||||
previous_proposer_boost: self.previous_proposer_boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SszContainerV7> for SszContainerV10 {
|
||||
fn into(self) -> SszContainerV7 {
|
||||
let nodes = self.nodes.into_iter().map(Into::into).collect();
|
||||
|
||||
SszContainerV7 {
|
||||
votes: self.votes,
|
||||
balances: self.balances,
|
||||
prune_threshold: self.prune_threshold,
|
||||
justified_checkpoint: self.justified_checkpoint,
|
||||
finalized_checkpoint: self.finalized_checkpoint,
|
||||
nodes,
|
||||
indices: self.indices,
|
||||
previous_proposer_boost: self.previous_proposer_boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<SszContainer> for SszContainerV10 {
|
||||
fn into(self) -> SszContainer {
|
||||
let nodes = self.nodes.into_iter().map(Into::into).collect();
|
||||
|
||||
@@ -190,3 +296,20 @@ impl Into<SszContainer> for SszContainerV7 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SszContainer> for SszContainerV7 {
|
||||
fn from(container: SszContainer) -> Self {
|
||||
let nodes = container.nodes.into_iter().map(Into::into).collect();
|
||||
|
||||
Self {
|
||||
votes: container.votes,
|
||||
balances: container.balances,
|
||||
prune_threshold: container.prune_threshold,
|
||||
justified_checkpoint: container.justified_checkpoint,
|
||||
finalized_checkpoint: container.finalized_checkpoint,
|
||||
nodes,
|
||||
indices: container.indices,
|
||||
previous_proposer_boost: container.previous_proposer_boost,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use execution_layer::{
|
||||
test_utils::{ExecutionBlockGenerator, MockExecutionLayer, DEFAULT_TERMINAL_BLOCK},
|
||||
ExecutionLayer,
|
||||
};
|
||||
use fork_choice::CountUnrealized;
|
||||
use futures::channel::mpsc::Receiver;
|
||||
pub use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
|
||||
use int_to_bytes::int_to_bytes32;
|
||||
@@ -1370,8 +1371,11 @@ where
|
||||
block: SignedBeaconBlock<E>,
|
||||
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
|
||||
self.set_current_slot(slot);
|
||||
let block_hash: SignedBeaconBlockHash =
|
||||
self.chain.process_block(Arc::new(block)).await?.into();
|
||||
let block_hash: SignedBeaconBlockHash = self
|
||||
.chain
|
||||
.process_block(Arc::new(block), CountUnrealized::True)
|
||||
.await?
|
||||
.into();
|
||||
self.chain.recompute_head_at_current_slot().await?;
|
||||
Ok(block_hash)
|
||||
}
|
||||
@@ -1380,8 +1384,11 @@ where
|
||||
&self,
|
||||
block: SignedBeaconBlock<E>,
|
||||
) -> Result<SignedBeaconBlockHash, BlockError<E>> {
|
||||
let block_hash: SignedBeaconBlockHash =
|
||||
self.chain.process_block(Arc::new(block)).await?.into();
|
||||
let block_hash: SignedBeaconBlockHash = self
|
||||
.chain
|
||||
.process_block(Arc::new(block), CountUnrealized::True)
|
||||
.await?
|
||||
.into();
|
||||
self.chain.recompute_head_at_current_slot().await?;
|
||||
Ok(block_hash)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use beacon_chain::test_utils::{
|
||||
AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType,
|
||||
};
|
||||
use beacon_chain::{BeaconSnapshot, BlockError, ChainSegmentResult};
|
||||
use fork_choice::CountUnrealized;
|
||||
use lazy_static::lazy_static;
|
||||
use logging::test_logger;
|
||||
use slasher::{Config as SlasherConfig, Slasher};
|
||||
@@ -147,14 +148,14 @@ async fn chain_segment_full_segment() {
|
||||
// Sneak in a little check to ensure we can process empty chain segments.
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(vec![])
|
||||
.process_chain_segment(vec![], CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error()
|
||||
.expect("should import empty chain segment");
|
||||
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks.clone())
|
||||
.process_chain_segment(blocks.clone(), CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error()
|
||||
.expect("should import chain segment");
|
||||
@@ -187,7 +188,7 @@ async fn chain_segment_varying_chunk_size() {
|
||||
for chunk in blocks.chunks(*chunk_size) {
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(chunk.to_vec())
|
||||
.process_chain_segment(chunk.to_vec(), CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error()
|
||||
.unwrap_or_else(|_| panic!("should import chain segment of len {}", chunk_size));
|
||||
@@ -227,7 +228,7 @@ async fn chain_segment_non_linear_parent_roots() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::NonLinearParentRoots)
|
||||
@@ -247,7 +248,7 @@ async fn chain_segment_non_linear_parent_roots() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::NonLinearParentRoots)
|
||||
@@ -278,7 +279,7 @@ async fn chain_segment_non_linear_slots() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::NonLinearSlots)
|
||||
@@ -299,7 +300,7 @@ async fn chain_segment_non_linear_slots() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::NonLinearSlots)
|
||||
@@ -325,7 +326,7 @@ async fn assert_invalid_signature(
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::InvalidSignature)
|
||||
@@ -342,12 +343,18 @@ async fn assert_invalid_signature(
|
||||
.collect();
|
||||
// We don't care if this fails, we just call this to ensure that all prior blocks have been
|
||||
// imported prior to this test.
|
||||
let _ = harness.chain.process_chain_segment(ancestor_blocks).await;
|
||||
let _ = harness
|
||||
.chain
|
||||
.process_chain_segment(ancestor_blocks, CountUnrealized::True)
|
||||
.await;
|
||||
assert!(
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(snapshots[block_index].beacon_block.clone())
|
||||
.process_block(
|
||||
snapshots[block_index].beacon_block.clone(),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await,
|
||||
Err(BlockError::InvalidSignature)
|
||||
),
|
||||
@@ -397,7 +404,7 @@ async fn invalid_signature_gossip_block() {
|
||||
.collect();
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(ancestor_blocks)
|
||||
.process_chain_segment(ancestor_blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error()
|
||||
.expect("should import all blocks prior to the one being tested");
|
||||
@@ -405,10 +412,10 @@ async fn invalid_signature_gossip_block() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(Arc::new(SignedBeaconBlock::from_block(
|
||||
block,
|
||||
junk_signature()
|
||||
)))
|
||||
.process_block(
|
||||
Arc::new(SignedBeaconBlock::from_block(block, junk_signature())),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await,
|
||||
Err(BlockError::InvalidSignature)
|
||||
),
|
||||
@@ -441,7 +448,7 @@ async fn invalid_signature_block_proposal() {
|
||||
matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::InvalidSignature)
|
||||
@@ -639,7 +646,7 @@ async fn invalid_signature_deposit() {
|
||||
!matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(blocks)
|
||||
.process_chain_segment(blocks, CountUnrealized::True)
|
||||
.await
|
||||
.into_block_error(),
|
||||
Err(BlockError::InvalidSignature)
|
||||
@@ -716,11 +723,18 @@ async fn block_gossip_verification() {
|
||||
|
||||
harness
|
||||
.chain
|
||||
.process_block(gossip_verified)
|
||||
.process_block(gossip_verified, CountUnrealized::True)
|
||||
.await
|
||||
.expect("should import valid gossip verified block");
|
||||
}
|
||||
|
||||
// Recompute the head to ensure we cache the latest view of fork choice.
|
||||
harness
|
||||
.chain
|
||||
.recompute_head_at_current_slot()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
/*
|
||||
* This test ensures that:
|
||||
*
|
||||
@@ -978,7 +992,11 @@ async fn verify_block_for_gossip_slashing_detection() {
|
||||
.verify_block_for_gossip(Arc::new(block1))
|
||||
.await
|
||||
.unwrap();
|
||||
harness.chain.process_block(verified_block).await.unwrap();
|
||||
harness
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
unwrap_err(
|
||||
harness
|
||||
.chain
|
||||
@@ -1009,7 +1027,11 @@ async fn verify_block_for_gossip_doppelganger_detection() {
|
||||
.await
|
||||
.unwrap();
|
||||
let attestations = verified_block.block.message().body().attestations().clone();
|
||||
harness.chain.process_block(verified_block).await.unwrap();
|
||||
harness
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
for att in attestations.iter() {
|
||||
let epoch = att.data.target.epoch;
|
||||
@@ -1148,7 +1170,7 @@ async fn add_base_block_to_altair_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(Arc::new(base_block.clone()))
|
||||
.process_block(Arc::new(base_block.clone()), CountUnrealized::True)
|
||||
.await
|
||||
.err()
|
||||
.expect("should error when processing base block"),
|
||||
@@ -1162,7 +1184,7 @@ async fn add_base_block_to_altair_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(vec![Arc::new(base_block)])
|
||||
.process_chain_segment(vec![Arc::new(base_block)], CountUnrealized::True)
|
||||
.await,
|
||||
ChainSegmentResult::Failed {
|
||||
imported_blocks: 0,
|
||||
@@ -1276,7 +1298,7 @@ async fn add_altair_block_to_base_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_block(Arc::new(altair_block.clone()))
|
||||
.process_block(Arc::new(altair_block.clone()), CountUnrealized::True)
|
||||
.await
|
||||
.err()
|
||||
.expect("should error when processing altair block"),
|
||||
@@ -1290,7 +1312,7 @@ async fn add_altair_block_to_base_chain() {
|
||||
assert!(matches!(
|
||||
harness
|
||||
.chain
|
||||
.process_chain_segment(vec![Arc::new(altair_block)])
|
||||
.process_chain_segment(vec![Arc::new(altair_block)], CountUnrealized::True)
|
||||
.await,
|
||||
ChainSegmentResult::Failed {
|
||||
imported_blocks: 0,
|
||||
|
||||
@@ -9,7 +9,9 @@ use execution_layer::{
|
||||
json_structures::{JsonForkChoiceStateV1, JsonPayloadAttributesV1},
|
||||
ExecutionLayer, ForkChoiceState, PayloadAttributes,
|
||||
};
|
||||
use fork_choice::{Error as ForkChoiceError, InvalidationOperation, PayloadVerificationStatus};
|
||||
use fork_choice::{
|
||||
CountUnrealized, Error as ForkChoiceError, InvalidationOperation, PayloadVerificationStatus,
|
||||
};
|
||||
use proto_array::{Error as ProtoArrayError, ExecutionStatus};
|
||||
use slot_clock::SlotClock;
|
||||
use std::sync::Arc;
|
||||
@@ -648,7 +650,7 @@ async fn invalidates_all_descendants() {
|
||||
let fork_block_root = rig
|
||||
.harness
|
||||
.chain
|
||||
.process_block(Arc::new(fork_block))
|
||||
.process_block(Arc::new(fork_block), CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
rig.recompute_head().await;
|
||||
@@ -740,7 +742,7 @@ async fn switches_heads() {
|
||||
let fork_block_root = rig
|
||||
.harness
|
||||
.chain
|
||||
.process_block(Arc::new(fork_block))
|
||||
.process_block(Arc::new(fork_block), CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
rig.recompute_head().await;
|
||||
@@ -984,7 +986,7 @@ async fn invalid_parent() {
|
||||
|
||||
// Ensure the block built atop an invalid payload is invalid for import.
|
||||
assert!(matches!(
|
||||
rig.harness.chain.process_block(block.clone()).await,
|
||||
rig.harness.chain.process_block(block.clone(), CountUnrealized::True).await,
|
||||
Err(BlockError::ParentExecutionPayloadInvalid { parent_root: invalid_root })
|
||||
if invalid_root == parent_root
|
||||
));
|
||||
@@ -998,7 +1000,8 @@ async fn invalid_parent() {
|
||||
Duration::from_secs(0),
|
||||
&state,
|
||||
PayloadVerificationStatus::Optimistic,
|
||||
&rig.harness.chain.spec
|
||||
&rig.harness.chain.spec,
|
||||
CountUnrealized::True,
|
||||
),
|
||||
Err(ForkChoiceError::ProtoArrayError(message))
|
||||
if message.contains(&format!(
|
||||
|
||||
@@ -10,6 +10,7 @@ use beacon_chain::{
|
||||
BeaconChainError, BeaconChainTypes, BeaconSnapshot, ChainConfig, ServerSentEventHandler,
|
||||
WhenSlotSkipped,
|
||||
};
|
||||
use fork_choice::CountUnrealized;
|
||||
use lazy_static::lazy_static;
|
||||
use logging::test_logger;
|
||||
use maplit::hashset;
|
||||
@@ -2124,7 +2125,7 @@ async fn weak_subjectivity_sync() {
|
||||
|
||||
beacon_chain.slot_clock.set_slot(block.slot().as_u64());
|
||||
beacon_chain
|
||||
.process_block(Arc::new(full_block))
|
||||
.process_block(Arc::new(full_block), CountUnrealized::True)
|
||||
.await
|
||||
.unwrap();
|
||||
beacon_chain.recompute_head_at_current_slot().await.unwrap();
|
||||
|
||||
@@ -8,6 +8,7 @@ use beacon_chain::{
|
||||
},
|
||||
BeaconChain, StateSkipConfig, WhenSlotSkipped,
|
||||
};
|
||||
use fork_choice::CountUnrealized;
|
||||
use lazy_static::lazy_static;
|
||||
use operation_pool::PersistedOperationPool;
|
||||
use state_processing::{
|
||||
@@ -499,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())
|
||||
.update_time(harness.chain.slot().unwrap(), &harness.chain.spec)
|
||||
.unwrap();
|
||||
|
||||
let validator_slots: Vec<(usize, Slot)> = (0..VALIDATOR_COUNT)
|
||||
@@ -613,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())
|
||||
.update_time(harness.chain.slot().unwrap(), &harness.chain.spec)
|
||||
.unwrap();
|
||||
|
||||
let validators: Vec<usize> = (0..VALIDATOR_COUNT).collect();
|
||||
@@ -683,7 +684,10 @@ async fn run_skip_slot_test(skip_slots: u64) {
|
||||
assert_eq!(
|
||||
harness_b
|
||||
.chain
|
||||
.process_block(harness_a.chain.head_snapshot().beacon_block.clone())
|
||||
.process_block(
|
||||
harness_a.chain.head_snapshot().beacon_block.clone(),
|
||||
CountUnrealized::True
|
||||
)
|
||||
.await
|
||||
.unwrap(),
|
||||
harness_a.chain.head_snapshot().beacon_block_root
|
||||
|
||||
@@ -23,7 +23,7 @@ use beacon_chain::{
|
||||
observed_operations::ObservationOutcome,
|
||||
validator_monitor::{get_block_delay_ms, timestamp_now},
|
||||
AttestationError as AttnError, BeaconChain, BeaconChainError, BeaconChainTypes,
|
||||
ProduceBlockVerification, WhenSlotSkipped,
|
||||
CountUnrealized, ProduceBlockVerification, WhenSlotSkipped,
|
||||
};
|
||||
pub use block_id::BlockId;
|
||||
use eth2::types::{self as api_types, EndpointVersion, ValidatorId};
|
||||
@@ -1035,7 +1035,10 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
let delay = get_block_delay_ms(seen_timestamp, block.message(), &chain.slot_clock);
|
||||
metrics::observe_duration(&metrics::HTTP_API_BLOCK_BROADCAST_DELAY_TIMES, delay);
|
||||
|
||||
match chain.process_block(block.clone()).await {
|
||||
match chain
|
||||
.process_block(block.clone(), CountUnrealized::True)
|
||||
.await
|
||||
{
|
||||
Ok(root) => {
|
||||
info!(
|
||||
log,
|
||||
@@ -1179,7 +1182,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
PubsubMessage::BeaconBlock(new_block.clone()),
|
||||
)?;
|
||||
|
||||
match chain.process_block(new_block).await {
|
||||
match chain.process_block(new_block, CountUnrealized::True).await {
|
||||
Ok(_) => {
|
||||
// Update the head since it's likely this block will become the new
|
||||
// head.
|
||||
|
||||
@@ -6,7 +6,8 @@ use beacon_chain::{
|
||||
observed_operations::ObservationOutcome,
|
||||
sync_committee_verification::{self, Error as SyncCommitteeError},
|
||||
validator_monitor::get_block_delay_ms,
|
||||
BeaconChainError, BeaconChainTypes, BlockError, ForkChoiceError, GossipVerifiedBlock,
|
||||
BeaconChainError, BeaconChainTypes, BlockError, CountUnrealized, ForkChoiceError,
|
||||
GossipVerifiedBlock,
|
||||
};
|
||||
use lighthouse_network::{Client, MessageAcceptance, MessageId, PeerAction, PeerId, ReportSource};
|
||||
use slog::{crit, debug, error, info, trace, warn};
|
||||
@@ -899,7 +900,11 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
) {
|
||||
let block: Arc<_> = verified_block.block.clone();
|
||||
|
||||
match self.chain.process_block(verified_block).await {
|
||||
match self
|
||||
.chain
|
||||
.process_block(verified_block, CountUnrealized::True)
|
||||
.await
|
||||
{
|
||||
Ok(block_root) => {
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_GOSSIP_BLOCK_IMPORTED_TOTAL);
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ use crate::beacon_processor::DuplicateCache;
|
||||
use crate::metrics;
|
||||
use crate::sync::manager::{BlockProcessType, SyncMessage};
|
||||
use crate::sync::{BatchProcessResult, ChainId};
|
||||
use beacon_chain::ExecutionPayloadError;
|
||||
use beacon_chain::{
|
||||
BeaconChainError, BeaconChainTypes, BlockError, ChainSegmentResult, HistoricalBlockError,
|
||||
};
|
||||
use beacon_chain::{CountUnrealized, ExecutionPayloadError};
|
||||
use lighthouse_network::PeerAction;
|
||||
use slog::{debug, error, info, warn};
|
||||
use std::sync::Arc;
|
||||
@@ -21,7 +21,7 @@ use types::{Epoch, Hash256, SignedBeaconBlock};
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ChainSegmentProcessId {
|
||||
/// Processing Id of a range syncing batch.
|
||||
RangeBatchId(ChainId, Epoch),
|
||||
RangeBatchId(ChainId, Epoch, CountUnrealized),
|
||||
/// Processing ID for a backfill syncing batch.
|
||||
BackSyncBatchId(Epoch),
|
||||
/// Processing Id of the parent lookup of a block.
|
||||
@@ -89,7 +89,7 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
}
|
||||
};
|
||||
let slot = block.slot();
|
||||
let result = self.chain.process_block(block).await;
|
||||
let result = self.chain.process_block(block, CountUnrealized::True).await;
|
||||
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_RPC_BLOCK_IMPORTED_TOTAL);
|
||||
|
||||
@@ -133,12 +133,15 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
) {
|
||||
let result = match sync_type {
|
||||
// this a request from the range sync
|
||||
ChainSegmentProcessId::RangeBatchId(chain_id, epoch) => {
|
||||
ChainSegmentProcessId::RangeBatchId(chain_id, epoch, count_unrealized) => {
|
||||
let start_slot = downloaded_blocks.first().map(|b| b.slot().as_u64());
|
||||
let end_slot = downloaded_blocks.last().map(|b| b.slot().as_u64());
|
||||
let sent_blocks = downloaded_blocks.len();
|
||||
|
||||
match self.process_blocks(downloaded_blocks.iter()).await {
|
||||
match self
|
||||
.process_blocks(downloaded_blocks.iter(), count_unrealized)
|
||||
.await
|
||||
{
|
||||
(_, Ok(_)) => {
|
||||
debug!(self.log, "Batch processed";
|
||||
"batch_epoch" => epoch,
|
||||
@@ -207,7 +210,10 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
);
|
||||
// parent blocks are ordered from highest slot to lowest, so we need to process in
|
||||
// reverse
|
||||
match self.process_blocks(downloaded_blocks.iter().rev()).await {
|
||||
match self
|
||||
.process_blocks(downloaded_blocks.iter().rev(), CountUnrealized::True)
|
||||
.await
|
||||
{
|
||||
(imported_blocks, Err(e)) => {
|
||||
debug!(self.log, "Parent lookup failed"; "error" => %e.message);
|
||||
BatchProcessResult::Failed {
|
||||
@@ -231,9 +237,14 @@ impl<T: BeaconChainTypes> Worker<T> {
|
||||
async fn process_blocks<'a>(
|
||||
&self,
|
||||
downloaded_blocks: impl Iterator<Item = &'a Arc<SignedBeaconBlock<T::EthSpec>>>,
|
||||
count_unrealized: CountUnrealized,
|
||||
) -> (usize, Result<(), ChainSegmentFailed>) {
|
||||
let blocks: Vec<Arc<_>> = downloaded_blocks.cloned().collect();
|
||||
match self.chain.process_chain_segment(blocks).await {
|
||||
match self
|
||||
.chain
|
||||
.process_chain_segment(blocks, count_unrealized)
|
||||
.await
|
||||
{
|
||||
ChainSegmentResult::Successful { imported_blocks } => {
|
||||
metrics::inc_counter(&metrics::BEACON_PROCESSOR_CHAIN_SEGMENT_SUCCESS_TOTAL);
|
||||
if imported_blocks > 0 {
|
||||
|
||||
@@ -532,7 +532,7 @@ impl<T: BeaconChainTypes> SyncManager<T> {
|
||||
.parent_block_processed(chain_hash, result, &mut self.network),
|
||||
},
|
||||
SyncMessage::BatchProcessed { sync_type, result } => match sync_type {
|
||||
ChainSegmentProcessId::RangeBatchId(chain_id, epoch) => {
|
||||
ChainSegmentProcessId::RangeBatchId(chain_id, epoch, _) => {
|
||||
self.range_sync.handle_block_process_result(
|
||||
&mut self.network,
|
||||
chain_id,
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::batch::{BatchInfo, BatchProcessingResult, BatchState};
|
||||
use crate::beacon_processor::WorkEvent as BeaconWorkEvent;
|
||||
use crate::beacon_processor::{ChainSegmentProcessId, FailureMode};
|
||||
use crate::sync::{manager::Id, network_context::SyncNetworkContext, BatchProcessResult};
|
||||
use beacon_chain::BeaconChainTypes;
|
||||
use beacon_chain::{BeaconChainTypes, CountUnrealized};
|
||||
use fnv::FnvHashMap;
|
||||
use lighthouse_network::{PeerAction, PeerId};
|
||||
use rand::seq::SliceRandom;
|
||||
@@ -100,6 +100,8 @@ pub struct SyncingChain<T: BeaconChainTypes> {
|
||||
/// A multi-threaded, non-blocking processor for applying messages to the beacon chain.
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T>>,
|
||||
|
||||
is_finalized_segment: bool,
|
||||
|
||||
/// The chain's log.
|
||||
log: slog::Logger,
|
||||
}
|
||||
@@ -126,6 +128,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
target_head_root: Hash256,
|
||||
peer_id: PeerId,
|
||||
beacon_processor_send: Sender<BeaconWorkEvent<T>>,
|
||||
is_finalized_segment: bool,
|
||||
log: &slog::Logger,
|
||||
) -> Self {
|
||||
let mut peers = FnvHashMap::default();
|
||||
@@ -148,6 +151,7 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
current_processing_batch: None,
|
||||
validated_batches: 0,
|
||||
beacon_processor_send,
|
||||
is_finalized_segment,
|
||||
log: log.new(o!("chain" => id)),
|
||||
}
|
||||
}
|
||||
@@ -302,7 +306,12 @@ impl<T: BeaconChainTypes> SyncingChain<T> {
|
||||
// for removing chains and checking completion is in the callback.
|
||||
|
||||
let blocks = batch.start_processing()?;
|
||||
let process_id = ChainSegmentProcessId::RangeBatchId(self.id, batch_id);
|
||||
let count_unrealized = if self.is_finalized_segment {
|
||||
CountUnrealized::False
|
||||
} else {
|
||||
CountUnrealized::True
|
||||
};
|
||||
let process_id = ChainSegmentProcessId::RangeBatchId(self.id, batch_id, count_unrealized);
|
||||
self.current_processing_batch = Some(batch_id);
|
||||
|
||||
if let Err(e) = self
|
||||
|
||||
@@ -472,10 +472,10 @@ impl<T: BeaconChainTypes, C: BlockStorage> ChainCollection<T, C> {
|
||||
network: &mut SyncNetworkContext<T::EthSpec>,
|
||||
) {
|
||||
let id = SyncingChain::<T>::id(&target_head_root, &target_head_slot);
|
||||
let collection = if let RangeSyncType::Finalized = sync_type {
|
||||
&mut self.finalized_chains
|
||||
let (collection, is_finalized) = if let RangeSyncType::Finalized = sync_type {
|
||||
(&mut self.finalized_chains, true)
|
||||
} else {
|
||||
&mut self.head_chains
|
||||
(&mut self.head_chains, false)
|
||||
};
|
||||
match collection.entry(id) {
|
||||
Entry::Occupied(mut entry) => {
|
||||
@@ -501,6 +501,7 @@ impl<T: BeaconChainTypes, C: BlockStorage> ChainCollection<T, C> {
|
||||
target_head_root,
|
||||
peer,
|
||||
beacon_processor_send.clone(),
|
||||
is_finalized,
|
||||
&self.log,
|
||||
);
|
||||
debug_assert_eq!(new_chain.get_id(), id);
|
||||
|
||||
@@ -708,4 +708,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
.default_value("250")
|
||||
.takes_value(true)
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("count-unrealized")
|
||||
.long("count-unrealized")
|
||||
.hidden(true)
|
||||
.help("**EXPERIMENTAL** Enables an alternative, potentially more performant FFG \
|
||||
vote tracking method.")
|
||||
.takes_value(false)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -630,6 +630,10 @@ pub fn get_config<E: EthSpec>(
|
||||
client_config.chain.fork_choice_before_proposal_timeout_ms = timeout;
|
||||
}
|
||||
|
||||
if cli_args.is_present("count-unrealized") {
|
||||
client_config.chain.count_unrealized = true;
|
||||
}
|
||||
|
||||
Ok(client_config)
|
||||
}
|
||||
|
||||
|
||||
@@ -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(9);
|
||||
pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(10);
|
||||
|
||||
// All the keys that get stored under the `BeaconMeta` column.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user