diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index ca0c5ce15b..49630096ce 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -420,6 +420,8 @@ pub struct BeaconChain { pub slasher: Option>>, /// Provides monitoring of a set of explicitly defined validators. pub validator_monitor: RwLock>, + /// The slot at which blocks are downloaded back to. + pub genesis_backfill_slot: Slot, } type BeaconBlockAndState = (BeaconBlock, BeaconState); diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index 56006b4d62..ca377635d6 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -772,6 +772,29 @@ where let canonical_head = CanonicalHead::new(fork_choice, Arc::new(head_snapshot)); let shuffling_cache_size = self.chain_config.shuffling_cache_size; + // Calculate the weak subjectivity point in which to backfill blocks to. + let genesis_backfill_slot = if self.chain_config.genesis_backfill { + Slot::new(0) + } else { + let backfill_epoch_range = (self.spec.min_validator_withdrawability_delay + + self.spec.churn_limit_quotient) + .as_u64() + / 2; + match slot_clock.now() { + Some(current_slot) => { + let genesis_backfill_epoch = current_slot + .epoch(TEthSpec::slots_per_epoch()) + .saturating_sub(backfill_epoch_range); + genesis_backfill_epoch.start_slot(TEthSpec::slots_per_epoch()) + } + None => { + // The slot clock cannot derive the current slot. We therefore assume we are + // at or prior to genesis and backfill should sync all the way to genesis. + Slot::new(0) + } + } + }; + let beacon_chain = BeaconChain { spec: self.spec, config: self.chain_config, @@ -839,6 +862,7 @@ where graffiti: self.graffiti, slasher: self.slasher.clone(), validator_monitor: RwLock::new(validator_monitor), + genesis_backfill_slot, }; let head = beacon_chain.head_snapshot(); diff --git a/beacon_node/beacon_chain/src/chain_config.rs b/beacon_node/beacon_chain/src/chain_config.rs index 9921435313..a74fdced1f 100644 --- a/beacon_node/beacon_chain/src/chain_config.rs +++ b/beacon_node/beacon_chain/src/chain_config.rs @@ -73,6 +73,9 @@ pub struct ChainConfig { pub optimistic_finalized_sync: bool, /// The size of the shuffling cache, pub shuffling_cache_size: usize, + /// If using a weak-subjectivity sync, whether we should download blocks all the way back to + /// genesis. + pub genesis_backfill: bool, /// Whether to send payload attributes every slot, regardless of connected proposers. /// /// This is useful for block builders and testing. @@ -106,6 +109,7 @@ impl Default for ChainConfig { // This value isn't actually read except in tests. optimistic_finalized_sync: true, shuffling_cache_size: crate::shuffling_cache::DEFAULT_CACHE_SIZE, + genesis_backfill: false, always_prepare_payload: false, enable_backfill_rate_limiting: true, } diff --git a/beacon_node/beacon_chain/src/historical_blocks.rs b/beacon_node/beacon_chain/src/historical_blocks.rs index cc45a6bb9a..5f59073500 100644 --- a/beacon_node/beacon_chain/src/historical_blocks.rs +++ b/beacon_node/beacon_chain/src/historical_blocks.rs @@ -189,13 +189,17 @@ impl BeaconChain { oldest_block_parent: expected_block_root, ..anchor_info }; - let backfill_complete = new_anchor.block_backfill_complete(); + let backfill_complete = new_anchor.block_backfill_complete(self.genesis_backfill_slot); self.store .compare_and_set_anchor_info_with_write(Some(anchor_info), Some(new_anchor))?; // If backfill has completed and the chain is configured to reconstruct historic states, // send a message to the background migrator instructing it to begin reconstruction. - if backfill_complete && self.config.reconstruct_historic_states { + // This can only happen if we have backfilled all the way to genesis. + if backfill_complete + && self.genesis_backfill_slot == Slot::new(0) + && self.config.reconstruct_historic_states + { self.store_migrator.process_reconstruction(); } diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index d4b785cb11..5ef1f28fb4 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -250,6 +250,12 @@ where genesis_state_bytes, } => { info!(context.log(), "Starting checkpoint sync"); + if config.chain.genesis_backfill { + info!( + context.log(), + "Blocks will downloaded all the way back to genesis" + ); + } let anchor_state = BeaconState::from_ssz_bytes(&anchor_state_bytes, &spec) .map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?; @@ -271,6 +277,12 @@ where "Starting checkpoint sync"; "remote_url" => %url, ); + if config.chain.genesis_backfill { + info!( + context.log(), + "Blocks will be downloaded all the way back to genesis" + ); + } let remote = BeaconNodeHttpClient::new( url, diff --git a/beacon_node/client/src/notifier.rs b/beacon_node/client/src/notifier.rs index 1105bc41f6..1ff469fe30 100644 --- a/beacon_node/client/src/notifier.rs +++ b/beacon_node/client/src/notifier.rs @@ -142,7 +142,8 @@ pub fn spawn_notifier( .get_anchor_info() .map(|ai| ai.oldest_block_slot) { - sync_distance = current_anchor_slot; + sync_distance = current_anchor_slot + .saturating_sub(beacon_chain.genesis_backfill_slot); speedo // For backfill sync use a fake slot which is the distance we've progressed from the starting `oldest_block_slot`. .observe( @@ -207,14 +208,14 @@ pub fn spawn_notifier( "Downloading historical blocks"; "distance" => distance, "speed" => sync_speed_pretty(speed), - "est_time" => estimated_time_pretty(speedo.estimated_time_till_slot(original_anchor_slot.unwrap_or(current_slot))), + "est_time" => estimated_time_pretty(speedo.estimated_time_till_slot(original_anchor_slot.unwrap_or(current_slot).saturating_sub(beacon_chain.genesis_backfill_slot))), ); } else { info!( log, "Downloading historical blocks"; "distance" => distance, - "est_time" => estimated_time_pretty(speedo.estimated_time_till_slot(original_anchor_slot.unwrap_or(current_slot))), + "est_time" => estimated_time_pretty(speedo.estimated_time_till_slot(original_anchor_slot.unwrap_or(current_slot).saturating_sub(beacon_chain.genesis_backfill_slot))), ); } } else if !is_backfilling && last_backfill_log_slot.is_some() { diff --git a/beacon_node/lighthouse_network/src/types/sync_state.rs b/beacon_node/lighthouse_network/src/types/sync_state.rs index 5f09aec27a..b82e63bd9c 100644 --- a/beacon_node/lighthouse_network/src/types/sync_state.rs +++ b/beacon_node/lighthouse_network/src/types/sync_state.rs @@ -13,7 +13,7 @@ pub enum SyncState { /// The node is undertaking a backfill sync. This occurs when a user has specified a trusted /// state. The node first syncs "forward" by downloading blocks up to the current head as /// specified by its peers. Once completed, the node enters this sync state and attempts to - /// download all required historical blocks to complete its chain. + /// download all required historical blocks. BackFillSyncing { completed: usize, remaining: usize }, /// The node has completed syncing a finalized chain and is in the process of re-evaluating /// which sync state to progress to. diff --git a/beacon_node/network/src/sync/backfill_sync/mod.rs b/beacon_node/network/src/sync/backfill_sync/mod.rs index d36bbbc79b..460c8b1ee9 100644 --- a/beacon_node/network/src/sync/backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/backfill_sync/mod.rs @@ -159,20 +159,20 @@ impl BackFillSync { // If, for some reason a backfill has already been completed (or we've used a trusted // genesis root) then backfill has been completed. - let (state, current_start) = if let Some(anchor_info) = beacon_chain.store.get_anchor_info() - { - if anchor_info.block_backfill_complete() { - (BackFillState::Completed, Epoch::new(0)) - } else { - ( - BackFillState::Paused, - anchor_info - .oldest_block_slot - .epoch(T::EthSpec::slots_per_epoch()), - ) + let (state, current_start) = match beacon_chain.store.get_anchor_info() { + Some(anchor_info) => { + if anchor_info.block_backfill_complete(beacon_chain.genesis_backfill_slot) { + (BackFillState::Completed, Epoch::new(0)) + } else { + ( + BackFillState::Paused, + anchor_info + .oldest_block_slot + .epoch(T::EthSpec::slots_per_epoch()), + ) + } } - } else { - (BackFillState::NotRequired, Epoch::new(0)) + None => (BackFillState::NotRequired, Epoch::new(0)), }; let bfs = BackFillSync { @@ -287,6 +287,7 @@ impl BackFillSync { remaining: self .current_start .start_slot(T::EthSpec::slots_per_epoch()) + .saturating_sub(self.beacon_chain.genesis_backfill_slot) .as_usize(), }) } @@ -1097,7 +1098,12 @@ impl BackFillSync { match self.batches.entry(batch_id) { Entry::Occupied(_) => { // this batch doesn't need downloading, let this same function decide the next batch - if batch_id == 0 { + if batch_id + == self + .beacon_chain + .genesis_backfill_slot + .epoch(T::EthSpec::slots_per_epoch()) + { self.last_batch_downloaded = true; } @@ -1108,7 +1114,12 @@ impl BackFillSync { } Entry::Vacant(entry) => { entry.insert(BatchInfo::new(&batch_id, BACKFILL_EPOCHS_PER_BATCH)); - if batch_id == 0 { + if batch_id + == self + .beacon_chain + .genesis_backfill_slot + .epoch(T::EthSpec::slots_per_epoch()) + { self.last_batch_downloaded = true; } self.to_be_downloaded = self @@ -1125,7 +1136,7 @@ impl BackFillSync { /// not required. fn reset_start_epoch(&mut self) -> Result<(), ResetEpochError> { if let Some(anchor_info) = self.beacon_chain.store.get_anchor_info() { - if anchor_info.block_backfill_complete() { + if anchor_info.block_backfill_complete(self.beacon_chain.genesis_backfill_slot) { Err(ResetEpochError::SyncCompleted) } else { self.current_start = anchor_info @@ -1140,12 +1151,17 @@ impl BackFillSync { /// Checks with the beacon chain if backfill sync has completed. fn check_completed(&mut self) -> bool { - if self.current_start == 0 { + if self.current_start + == self + .beacon_chain + .genesis_backfill_slot + .epoch(T::EthSpec::slots_per_epoch()) + { // Check that the beacon chain agrees if let Some(anchor_info) = self.beacon_chain.store.get_anchor_info() { // Conditions that we have completed a backfill sync - if anchor_info.block_backfill_complete() { + if anchor_info.block_backfill_complete(self.beacon_chain.genesis_backfill_slot) { return true; } else { error!(self.log, "Backfill out of sync with beacon chain"); diff --git a/beacon_node/src/cli.rs b/beacon_node/src/cli.rs index 61a11f88e6..633cbf0438 100644 --- a/beacon_node/src/cli.rs +++ b/beacon_node/src/cli.rs @@ -247,6 +247,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .help("One or more comma-delimited trusted peer ids which always have the highest score according to the peer scoring system.") .takes_value(true), ) + .arg( + Arg::with_name("genesis-backfill") + .long("genesis-backfill") + .help("Attempts to download blocks all the way back to genesis when checkpoint syncing.") + .takes_value(false), + ) .arg( Arg::with_name("enable-private-discovery") .long("enable-private-discovery") @@ -831,7 +837,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> { .arg( Arg::with_name("reconstruct-historic-states") .long("reconstruct-historic-states") - .help("After a checkpoint sync, reconstruct historic states in the database.") + .help("After a checkpoint sync, reconstruct historic states in the database. This requires syncing all the way back to genesis.") .takes_value(false) ) .arg( diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 64cfb0fedf..f05fea2db1 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -508,6 +508,7 @@ pub fn get_config( if cli_args.is_present("reconstruct-historic-states") { client_config.chain.reconstruct_historic_states = true; + client_config.chain.genesis_backfill = true; } let raw_graffiti = if let Some(graffiti) = cli_args.value_of("graffiti") { @@ -780,6 +781,9 @@ pub fn get_config( client_config.chain.optimistic_finalized_sync = !cli_args.is_present("disable-optimistic-finalized-sync"); + if cli_args.is_present("genesis-backfill") { + client_config.chain.genesis_backfill = true; + } // Payload selection configs if cli_args.is_present("always-prefer-builder-payload") { client_config.always_prefer_builder_payload = true; diff --git a/beacon_node/store/src/metadata.rs b/beacon_node/store/src/metadata.rs index 8e9b3599b1..eca8fc834f 100644 --- a/beacon_node/store/src/metadata.rs +++ b/beacon_node/store/src/metadata.rs @@ -99,8 +99,10 @@ pub struct AnchorInfo { impl AnchorInfo { /// Returns true if the block backfill has completed. - pub fn block_backfill_complete(&self) -> bool { - self.oldest_block_slot == 0 + /// This is a comparison between the oldest block slot and the target backfill slot (which is + /// likely to be the closest WSP). + pub fn block_backfill_complete(&self, target_slot: Slot) -> bool { + self.oldest_block_slot <= target_slot } } diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index 0876061117..7e647c904d 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -345,6 +345,23 @@ fn trusted_peers_flag() { }); } +#[test] +fn genesis_backfill_flag() { + CommandLineTest::new() + .flag("genesis-backfill", None) + .run_with_zero_port() + .with_config(|config| assert_eq!(config.chain.genesis_backfill, true)); +} + +/// The genesis backfill flag should be enabled if historic states flag is set. +#[test] +fn genesis_backfill_with_historic_flag() { + CommandLineTest::new() + .flag("reconstruct-historic-states", None) + .run_with_zero_port() + .with_config(|config| assert_eq!(config.chain.genesis_backfill, true)); +} + #[test] fn always_prefer_builder_payload_flag() { CommandLineTest::new()