Skip to content

Commit 27aabe8

Browse files
authored
Pseudo finalization endpoint (#7103)
This is a backport of: - #7059 - #7071 For: - #7039 Introduce a new lighthouse endpoint that allows a user to force a pseudo finalization. This migrates data to the freezer db and prunes sidechains which may help reduce disk space issues on non finalized networks like Holesky We also ban peers that send us blocks that conflict with the manually finalized checkpoint. There were some CI fixes in #7071 that I tried including here Co-authored with: @jimmygchen @pawanjay176 @michaelsproul
1 parent 5848258 commit 27aabe8

File tree

9 files changed

+353
-23
lines changed

9 files changed

+353
-23
lines changed

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use crate::light_client_optimistic_update_verification::{
4242
Error as LightClientOptimisticUpdateError, VerifiedLightClientOptimisticUpdate,
4343
};
4444
use crate::light_client_server_cache::LightClientServerCache;
45-
use crate::migrate::BackgroundMigrator;
45+
use crate::migrate::{BackgroundMigrator, ManualFinalizationNotification};
4646
use crate::naive_aggregation_pool::{
4747
AggregatedAttestationMap, Error as NaiveAggregationError, NaiveAggregationPool,
4848
SyncContributionAggregateMap,
@@ -118,8 +118,8 @@ use std::sync::Arc;
118118
use std::time::Duration;
119119
use store::iter::{BlockRootsIterator, ParentRootBlockIterator, StateRootsIterator};
120120
use store::{
121-
BlobSidecarListFromRoot, DatabaseBlock, Error as DBError, HotColdDB, KeyValueStore,
122-
KeyValueStoreOp, StoreItem, StoreOp,
121+
BlobSidecarListFromRoot, DatabaseBlock, Error as DBError, HotColdDB, HotStateSummary,
122+
KeyValueStore, KeyValueStoreOp, StoreItem, StoreOp,
123123
};
124124
use task_executor::{ShutdownReason, TaskExecutor};
125125
use tokio::sync::oneshot;
@@ -1711,6 +1711,41 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
17111711
}
17121712
}
17131713

1714+
pub fn manually_finalize_state(
1715+
&self,
1716+
state_root: Hash256,
1717+
checkpoint: Checkpoint,
1718+
) -> Result<(), Error> {
1719+
let HotStateSummary {
1720+
slot,
1721+
latest_block_root,
1722+
..
1723+
} = self
1724+
.store
1725+
.load_hot_state_summary(&state_root)
1726+
.map_err(BeaconChainError::DBError)?
1727+
.ok_or(BeaconChainError::MissingHotStateSummary(state_root))?;
1728+
1729+
if slot != checkpoint.epoch.start_slot(T::EthSpec::slots_per_epoch())
1730+
|| latest_block_root != *checkpoint.root
1731+
{
1732+
return Err(BeaconChainError::InvalidCheckpoint {
1733+
state_root,
1734+
checkpoint,
1735+
});
1736+
}
1737+
1738+
let notif = ManualFinalizationNotification {
1739+
state_root: state_root.into(),
1740+
checkpoint,
1741+
head_tracker: self.head_tracker.clone(),
1742+
genesis_block_root: self.genesis_block_root,
1743+
};
1744+
1745+
self.store_migrator.process_manual_finalization(notif);
1746+
Ok(())
1747+
}
1748+
17141749
/// Returns an aggregated `Attestation`, if any, that has a matching `attestation.data`.
17151750
///
17161751
/// The attestation will be obtained from `self.naive_aggregation_pool`.

beacon_node/beacon_chain/src/block_verification.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,22 @@ pub fn check_block_is_finalized_checkpoint_or_descendant<
17671767
fork_choice: &BeaconForkChoice<T>,
17681768
block: B,
17691769
) -> Result<B, BlockError> {
1770-
if fork_choice.is_finalized_checkpoint_or_descendant(block.parent_root()) {
1770+
// If we have a split block newer than finalization then we also ban blocks which are not
1771+
// descended from that split block. It's important not to try checking `is_descendant` if
1772+
// finality is ahead of the split and the split block has been pruned, as `is_descendant` will
1773+
// return `false` in this case.
1774+
let finalized_slot = fork_choice
1775+
.finalized_checkpoint()
1776+
.epoch
1777+
.start_slot(T::EthSpec::slots_per_epoch());
1778+
let split = chain.store.get_split_info();
1779+
let is_descendant_from_split_block = split.slot == 0
1780+
|| split.slot <= finalized_slot
1781+
|| fork_choice.is_descendant(split.block_root, block.parent_root());
1782+
1783+
if fork_choice.is_finalized_checkpoint_or_descendant(block.parent_root())
1784+
&& is_descendant_from_split_block
1785+
{
17711786
Ok(block)
17721787
} else {
17731788
// If fork choice does *not* consider the parent to be a descendant of the finalized block,

beacon_node/beacon_chain/src/errors.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub enum BeaconChainError {
6161
ForkChoiceStoreError(ForkChoiceStoreError),
6262
MissingBeaconBlock(Hash256),
6363
MissingBeaconState(Hash256),
64+
MissingHotStateSummary(Hash256),
6465
SlotProcessingError(SlotProcessingError),
6566
EpochProcessingError(EpochProcessingError),
6667
StateAdvanceError(StateAdvanceError),
@@ -181,9 +182,9 @@ pub enum BeaconChainError {
181182
execution_block_hash: Option<ExecutionBlockHash>,
182183
},
183184
ForkchoiceUpdate(execution_layer::Error),
184-
FinalizedCheckpointMismatch {
185-
head_state: Checkpoint,
186-
fork_choice: Hash256,
185+
InvalidCheckpoint {
186+
state_root: Hash256,
187+
checkpoint: Checkpoint,
187188
},
188189
InvalidSlot(Slot),
189190
HeadBlockNotFullyVerified {

beacon_node/beacon_chain/src/migrate.rs

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,22 @@ pub enum Notification {
124124
Finalization(FinalizationNotification),
125125
Reconstruction,
126126
PruneBlobs(Epoch),
127+
ManualFinalization(ManualFinalizationNotification),
128+
}
129+
130+
pub struct ManualFinalizationNotification {
131+
pub state_root: BeaconStateHash,
132+
pub checkpoint: Checkpoint,
133+
pub head_tracker: Arc<HeadTracker>,
134+
pub genesis_block_root: Hash256,
127135
}
128136

129137
pub struct FinalizationNotification {
130-
finalized_state_root: BeaconStateHash,
131-
finalized_checkpoint: Checkpoint,
132-
head_tracker: Arc<HeadTracker>,
133-
prev_migration: Arc<Mutex<PrevMigration>>,
134-
genesis_block_root: Hash256,
138+
pub finalized_state_root: BeaconStateHash,
139+
pub finalized_checkpoint: Checkpoint,
140+
pub head_tracker: Arc<HeadTracker>,
141+
pub prev_migration: Arc<Mutex<PrevMigration>>,
142+
pub genesis_block_root: Hash256,
135143
}
136144

137145
impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Hot, Cold> {
@@ -190,6 +198,14 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
190198
Ok(())
191199
}
192200

201+
pub fn process_manual_finalization(&self, notif: ManualFinalizationNotification) {
202+
if let Some(Notification::ManualFinalization(notif)) =
203+
self.send_background_notification(Notification::ManualFinalization(notif))
204+
{
205+
Self::run_manual_migration(self.db.clone(), notif, &self.log);
206+
}
207+
}
208+
193209
pub fn process_reconstruction(&self) {
194210
if let Some(Notification::Reconstruction) =
195211
self.send_background_notification(Notification::Reconstruction)
@@ -289,6 +305,26 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
289305
}
290306
}
291307

308+
fn run_manual_migration(
309+
db: Arc<HotColdDB<E, Hot, Cold>>,
310+
notif: ManualFinalizationNotification,
311+
log: &Logger,
312+
) {
313+
// We create a "dummy" prev migration
314+
let prev_migration = PrevMigration {
315+
epoch: Epoch::new(1),
316+
epochs_per_migration: 2,
317+
};
318+
let notif = FinalizationNotification {
319+
finalized_state_root: notif.state_root,
320+
finalized_checkpoint: notif.checkpoint,
321+
head_tracker: notif.head_tracker,
322+
prev_migration: Arc::new(prev_migration.into()),
323+
genesis_block_root: notif.genesis_block_root,
324+
};
325+
Self::run_migration(db, notif, log);
326+
}
327+
292328
/// Perform the actual work of `process_finalization`.
293329
fn run_migration(
294330
db: Arc<HotColdDB<E, Hot, Cold>>,
@@ -423,16 +459,27 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
423459
while let Ok(notif) = rx.recv() {
424460
let mut reconstruction_notif = None;
425461
let mut finalization_notif = None;
462+
let mut manual_finalization_notif = None;
426463
let mut prune_blobs_notif = None;
427464
match notif {
428465
Notification::Reconstruction => reconstruction_notif = Some(notif),
429466
Notification::Finalization(fin) => finalization_notif = Some(fin),
467+
Notification::ManualFinalization(fin) => manual_finalization_notif = Some(fin),
430468
Notification::PruneBlobs(dab) => prune_blobs_notif = Some(dab),
431469
}
432470
// Read the rest of the messages in the channel, taking the best of each type.
433471
for notif in rx.try_iter() {
434472
match notif {
435473
Notification::Reconstruction => reconstruction_notif = Some(notif),
474+
Notification::ManualFinalization(fin) => {
475+
if let Some(current) = manual_finalization_notif.as_mut() {
476+
if fin.checkpoint.epoch > current.checkpoint.epoch {
477+
*current = fin;
478+
}
479+
} else {
480+
manual_finalization_notif = Some(fin);
481+
}
482+
}
436483
Notification::Finalization(fin) => {
437484
if let Some(current) = finalization_notif.as_mut() {
438485
if fin.finalized_checkpoint.epoch
@@ -455,6 +502,9 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> BackgroundMigrator<E, Ho
455502
if let Some(fin) = finalization_notif {
456503
Self::run_migration(db.clone(), fin, &log);
457504
}
505+
if let Some(fin) = manual_finalization_notif {
506+
Self::run_manual_migration(db.clone(), fin, &log);
507+
}
458508
if let Some(dab) = prune_blobs_notif {
459509
Self::run_prune_blobs(db.clone(), dab, &log);
460510
}

0 commit comments

Comments
 (0)