mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-20 21:34:46 +00:00
Shrink persisted fork choice data (#7805)
Closes: - https://github.com/sigp/lighthouse/issues/7760 - [x] Remove `balances_cache` from `PersistedForkChoiceStore` (~65 MB saving on mainnet) - [x] Remove `justified_balances` from `PersistedForkChoiceStore` (~16 MB saving on mainnet) - [x] Remove `balances` from `ProtoArray`/`SszContainer`. - [x] Implement zstd compression for votes - [x] Fix bug in justified state usage - [x] Bump schema version to V28 and implement migration.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::BeaconForkChoiceStore;
|
||||
use crate::beacon_chain::BeaconChainTypes;
|
||||
use crate::persisted_fork_choice::PersistedForkChoice;
|
||||
use crate::persisted_fork_choice::PersistedForkChoiceV17;
|
||||
use crate::schema_change::StoreError;
|
||||
use crate::test_utils::{BEACON_CHAIN_DB_KEY, FORK_CHOICE_DB_KEY, PersistedBeaconChain};
|
||||
use fork_choice::{ForkChoice, ResetPayloadStatuses};
|
||||
@@ -80,7 +80,7 @@ pub fn downgrade_from_v23<T: BeaconChainTypes>(
|
||||
};
|
||||
|
||||
// Recreate head-tracker from fork choice.
|
||||
let Some(persisted_fork_choice) = db.get_item::<PersistedForkChoice>(&FORK_CHOICE_DB_KEY)?
|
||||
let Some(persisted_fork_choice) = db.get_item::<PersistedForkChoiceV17>(&FORK_CHOICE_DB_KEY)?
|
||||
else {
|
||||
// Fork choice should exist if the database exists.
|
||||
return Err(Error::MigrationError(
|
||||
@@ -88,19 +88,30 @@ pub fn downgrade_from_v23<T: BeaconChainTypes>(
|
||||
));
|
||||
};
|
||||
|
||||
let fc_store =
|
||||
BeaconForkChoiceStore::from_persisted(persisted_fork_choice.fork_choice_store, db.clone())
|
||||
.map_err(|e| {
|
||||
Error::MigrationError(format!(
|
||||
"Error loading fork choise store from persisted: {e:?}"
|
||||
))
|
||||
})?;
|
||||
// We use dummy roots for the justified states because we can source the balances from the v17
|
||||
// persited fork choice. The justified state root isn't required to look up the justified state's
|
||||
// balances (as it would be in V28). This fork choice object with corrupt state roots SHOULD NOT
|
||||
// be written to disk.
|
||||
let dummy_justified_state_root = Hash256::repeat_byte(0x66);
|
||||
let dummy_unrealized_justified_state_root = Hash256::repeat_byte(0x77);
|
||||
|
||||
let fc_store = BeaconForkChoiceStore::from_persisted_v17(
|
||||
persisted_fork_choice.fork_choice_store_v17,
|
||||
dummy_justified_state_root,
|
||||
dummy_unrealized_justified_state_root,
|
||||
db.clone(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::MigrationError(format!(
|
||||
"Error loading fork choice store from persisted: {e:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
// Doesn't matter what policy we use for invalid payloads, as our head calculation just
|
||||
// considers descent from finalization.
|
||||
let reset_payload_statuses = ResetPayloadStatuses::OnlyWithInvalidPayload;
|
||||
let fork_choice = ForkChoice::from_persisted(
|
||||
persisted_fork_choice.fork_choice,
|
||||
persisted_fork_choice.fork_choice_v17.try_into()?,
|
||||
reset_payload_statuses,
|
||||
fc_store,
|
||||
&db.spec,
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
use crate::{
|
||||
BeaconChain, BeaconChainTypes, BeaconForkChoiceStore, PersistedForkChoiceStoreV17,
|
||||
beacon_chain::FORK_CHOICE_DB_KEY,
|
||||
persisted_fork_choice::{PersistedForkChoiceV17, PersistedForkChoiceV28},
|
||||
summaries_dag::{DAGStateSummary, StateSummariesDAG},
|
||||
};
|
||||
use fork_choice::{ForkChoice, ForkChoiceStore, ResetPayloadStatuses};
|
||||
use std::sync::Arc;
|
||||
use store::{Error, HotColdDB, KeyValueStoreOp, StoreItem};
|
||||
use tracing::{info, warn};
|
||||
use types::{EthSpec, Hash256};
|
||||
|
||||
/// Upgrade `PersistedForkChoice` from V17 to V28.
|
||||
pub fn upgrade_to_v28<T: BeaconChainTypes>(
|
||||
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
|
||||
) -> Result<Vec<KeyValueStoreOp>, Error> {
|
||||
let Some(persisted_fork_choice_v17) =
|
||||
db.get_item::<PersistedForkChoiceV17>(&FORK_CHOICE_DB_KEY)?
|
||||
else {
|
||||
warn!("No fork choice found to upgrade to v28");
|
||||
return Ok(vec![]);
|
||||
};
|
||||
|
||||
// Load state DAG in order to compute justified checkpoint roots.
|
||||
let state_summaries_dag = {
|
||||
let state_summaries = db
|
||||
.load_hot_state_summaries()?
|
||||
.into_iter()
|
||||
.map(|(state_root, summary)| (state_root, summary.into()))
|
||||
.collect::<Vec<(Hash256, DAGStateSummary)>>();
|
||||
|
||||
StateSummariesDAG::new(state_summaries).map_err(|e| {
|
||||
Error::MigrationError(format!("Error loading state summaries DAG: {e:?}"))
|
||||
})?
|
||||
};
|
||||
|
||||
// Determine the justified state roots.
|
||||
let justified_checkpoint = persisted_fork_choice_v17
|
||||
.fork_choice_store_v17
|
||||
.justified_checkpoint;
|
||||
let justified_block_root = justified_checkpoint.root;
|
||||
let justified_slot = justified_checkpoint
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let justified_state_root = state_summaries_dag
|
||||
.state_root_at_slot(justified_block_root, justified_slot)
|
||||
.ok_or_else(|| {
|
||||
Error::MigrationError(format!(
|
||||
"Missing state root for justified slot {justified_slot} with latest_block_root \
|
||||
{justified_block_root:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let unrealized_justified_checkpoint = persisted_fork_choice_v17
|
||||
.fork_choice_store_v17
|
||||
.unrealized_justified_checkpoint;
|
||||
let unrealized_justified_block_root = unrealized_justified_checkpoint.root;
|
||||
let unrealized_justified_slot = unrealized_justified_checkpoint
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch());
|
||||
let unrealized_justified_state_root = state_summaries_dag
|
||||
.state_root_at_slot(unrealized_justified_block_root, unrealized_justified_slot)
|
||||
.ok_or_else(|| {
|
||||
Error::MigrationError(format!(
|
||||
"Missing state root for unrealized justified slot {unrealized_justified_slot} \
|
||||
with latest_block_root {unrealized_justified_block_root:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
let fc_store = BeaconForkChoiceStore::from_persisted_v17(
|
||||
persisted_fork_choice_v17.fork_choice_store_v17,
|
||||
justified_state_root,
|
||||
unrealized_justified_state_root,
|
||||
db.clone(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
Error::MigrationError(format!(
|
||||
"Error loading fork choice store from persisted: {e:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
info!(
|
||||
?justified_state_root,
|
||||
%justified_slot,
|
||||
"Added justified state root to fork choice"
|
||||
);
|
||||
|
||||
// Construct top-level ForkChoice struct using the patched fork choice store, and the converted
|
||||
// proto array.
|
||||
let reset_payload_statuses = ResetPayloadStatuses::OnlyWithInvalidPayload;
|
||||
let fork_choice = ForkChoice::from_persisted(
|
||||
persisted_fork_choice_v17.fork_choice_v17.try_into()?,
|
||||
reset_payload_statuses,
|
||||
fc_store,
|
||||
db.get_chain_spec(),
|
||||
)
|
||||
.map_err(|e| Error::MigrationError(format!("Unable to build ForkChoice: {e:?}")))?;
|
||||
|
||||
let ops = vec![BeaconChain::<T>::persist_fork_choice_in_batch_standalone(
|
||||
&fork_choice,
|
||||
db.get_config(),
|
||||
)?];
|
||||
|
||||
info!("Upgraded fork choice for DB schema v28");
|
||||
|
||||
Ok(ops)
|
||||
}
|
||||
|
||||
pub fn downgrade_from_v28<T: BeaconChainTypes>(
|
||||
db: Arc<HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>>,
|
||||
) -> Result<Vec<KeyValueStoreOp>, Error> {
|
||||
let reset_payload_statuses = ResetPayloadStatuses::OnlyWithInvalidPayload;
|
||||
let Some(fork_choice) =
|
||||
BeaconChain::<T>::load_fork_choice(db.clone(), reset_payload_statuses, db.get_chain_spec())
|
||||
.map_err(|e| Error::MigrationError(format!("Unable to load fork choice: {e:?}")))?
|
||||
else {
|
||||
warn!("No fork choice to downgrade");
|
||||
return Ok(vec![]);
|
||||
};
|
||||
|
||||
// Recreate V28 persisted fork choice, then convert each field back to its V17 version.
|
||||
let persisted_fork_choice = PersistedForkChoiceV28 {
|
||||
fork_choice: fork_choice.to_persisted(),
|
||||
fork_choice_store: fork_choice.fc_store().to_persisted(),
|
||||
};
|
||||
|
||||
let justified_balances = fork_choice.fc_store().justified_balances();
|
||||
|
||||
// 1. Create `proto_array::PersistedForkChoiceV17`.
|
||||
let fork_choice_v17: fork_choice::PersistedForkChoiceV17 = (
|
||||
persisted_fork_choice.fork_choice,
|
||||
justified_balances.clone(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let fork_choice_store_v17: PersistedForkChoiceStoreV17 = (
|
||||
persisted_fork_choice.fork_choice_store,
|
||||
justified_balances.clone(),
|
||||
)
|
||||
.into();
|
||||
|
||||
let persisted_fork_choice_v17 = PersistedForkChoiceV17 {
|
||||
fork_choice_v17,
|
||||
fork_choice_store_v17,
|
||||
};
|
||||
|
||||
let ops = vec![persisted_fork_choice_v17.as_kv_store_op(FORK_CHOICE_DB_KEY)];
|
||||
|
||||
info!("Downgraded fork choice for DB schema v28");
|
||||
|
||||
Ok(ops)
|
||||
}
|
||||
Reference in New Issue
Block a user