Fix compilation, clear best_child/best_descendant in migration

- Fix leaf detection in heads_descended_from_finalization (parent()
  method call, map away enumerate index)
- Clear best_child and best_descendant in v28->v29 migration (no
  longer used, replaced by virtual tree walk)
- Migration now rewrites fork choice data instead of being a no-op
This commit is contained in:
dapplion
2026-03-31 00:18:24 -05:00
parent b6728c2030
commit 5353710e0a
5 changed files with 49 additions and 73 deletions

View File

@@ -2,17 +2,18 @@ use crate::beacon_chain::BeaconChainTypes;
use crate::persisted_fork_choice::{PersistedForkChoiceV28, PersistedForkChoiceV29};
use ssz::Decode;
use store::hot_cold_store::HotColdDB;
use store::{DBColumn, Error as StoreError, KeyValueStore};
use store::{DBColumn, Error as StoreError, KeyValueStore, KeyValueStoreOp};
use types::{EthSpec, Hash256};
/// The key used to store the fork choice in the database.
const FORK_CHOICE_DB_KEY: Hash256 = Hash256::ZERO;
/// Upgrade from schema v28 to v29 (no-op).
/// Upgrade from schema v28 to v29.
///
/// Fails if the persisted fork choice contains any V17 (pre-Gloas) proto nodes at or after the
/// Gloas fork slot. Such nodes indicate the node synced a broken sidechain with Gloas disabled
/// and would not be able to sync the v29 chain.
/// - Clears `best_child` and `best_descendant` on all nodes (replaced by
/// virtual tree walk).
/// - Fails if the persisted fork choice contains any V17 (pre-Gloas) proto
/// nodes at or after the Gloas fork slot.
pub fn upgrade_to_v29<T: BeaconChainTypes>(
db: &HotColdDB<T::EthSpec, T::HotStore, T::ColdStore>,
) -> Result<(), StoreError> {
@@ -21,11 +22,6 @@ pub fn upgrade_to_v29<T: BeaconChainTypes>(
.gloas_fork_epoch
.map(|epoch| epoch.start_slot(T::EthSpec::slots_per_epoch()));
// If Gloas is not configured, the upgrade is a safe no-op.
let Some(gloas_fork_slot) = gloas_fork_slot else {
return Ok(());
};
// Load the persisted fork choice (v28 format, uncompressed SSZ).
let Some(fc_bytes) = db
.hot_db
@@ -34,26 +30,45 @@ pub fn upgrade_to_v29<T: BeaconChainTypes>(
return Ok(());
};
let persisted_v28 =
let mut persisted_v28 =
PersistedForkChoiceV28::from_ssz_bytes(&fc_bytes).map_err(StoreError::SszDecodeError)?;
// In v28 format, all nodes are ProtoNodeV17. Check if any are at/after the Gloas fork slot.
let bad_node = persisted_v28
.fork_choice_v28
.proto_array_v28
.nodes
.iter()
.find(|node| node.slot >= gloas_fork_slot);
// Check for V17 nodes at/after the Gloas fork slot.
if let Some(gloas_fork_slot) = gloas_fork_slot {
let bad_node = persisted_v28
.fork_choice_v28
.proto_array_v28
.nodes
.iter()
.find(|node| node.slot >= gloas_fork_slot);
if let Some(node) = bad_node {
return Err(StoreError::MigrationError(format!(
"cannot upgrade from v28 to v29: found V17 proto node at slot {} (root: {:?}) \
which is at or after the Gloas fork slot {}. This node has synced a chain with \
Gloas disabled and cannot be upgraded. Please resync from scratch.",
node.slot, node.root, gloas_fork_slot,
)));
if let Some(node) = bad_node {
return Err(StoreError::MigrationError(format!(
"cannot upgrade from v28 to v29: found V17 proto node at slot {} (root: {:?}) \
which is at or after the Gloas fork slot {}. This node has synced a chain with \
Gloas disabled and cannot be upgraded. Please resync from scratch.",
node.slot, node.root, gloas_fork_slot,
)));
}
}
// Clear best_child/best_descendant — replaced by the virtual tree walk.
for node in &mut persisted_v28.fork_choice_v28.proto_array_v28.nodes {
node.best_child = None;
node.best_descendant = None;
}
// Convert to v29 and write back.
let persisted_v29 = PersistedForkChoiceV29::from(persisted_v28);
let fc_bytes = persisted_v29
.as_bytes(db.get_config())
.map_err(|e| StoreError::MigrationError(format!("failed to encode v29: {:?}", e)))?;
db.hot_db.do_atomically(vec![KeyValueStoreOp::PutKeyValue(
DBColumn::ForkChoice,
FORK_CHOICE_DB_KEY.as_slice().to_vec(),
fc_bytes,
)])?;
Ok(())
}