@@ -289,15 +289,26 @@ impl<E: EthSpec> HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>> {
289289 // stop and restart correctly. This needs to occur *before* running any migrations
290290 // because some migrations load states and depend on the split.
291291 //
292- // V24: `load_split` needs to load a hot state summary, which need to be migrated from V22
293- // to V24. Attempting to `load_split` here before the migration will trigger an SSZ decode
294- // error. Instead we load the partial split, and load the full split after the migration.
295- if let Some ( split) = db. load_split_partial ( ) ? {
292+ // We use a method that is ambivalent to the state summaries being V22 or V24, because
293+ // we need to support several scenarios:
294+ //
295+ // - Migrating from V22 to V24: initially summaries are V22 , and we need
296+ // to be able to load a block root from them. Loading the split partially at first
297+ // (without reading a V24 summary) and then completing the full load after the migration
298+ // runs is possible in this case, but not in the next case.
299+ // - Migrating from V24 to V22: initially summaries are V24, but after the migration runs
300+ // they will be V22. If we used the "load full split after migration" approach with strict
301+ // V24 summaries, it would break when trying to read V22 summaries after the migration.
302+ //
303+ // Therefore we take the most flexible approach of reading _either_ a V22 or V24 summary and
304+ // using this to load the split correctly the first time.
305+ if let Some ( split) = db. load_split ( ) ? {
296306 * db. split . write ( ) = split;
297307
298308 info ! (
299309 %split. slot,
300- split_state = ?split. state_root,
310+ ?split. state_root,
311+ ?split. block_root,
301312 "Hot-Cold DB initialized"
302313 ) ;
303314 }
@@ -395,14 +406,6 @@ impl<E: EthSpec> HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>> {
395406 db. store_schema_version ( CURRENT_SCHEMA_VERSION ) ?;
396407 }
397408
398- // Load the full split after the migration to set the `split.block_root` to the correct
399- // value.
400- if let Some ( split) = db. load_split ( ) ? {
401- * db. split . write ( ) = split;
402-
403- debug ! ( ?split, "Hot-Cold DB split initialized" ) ;
404- }
405-
406409 // TODO(tree-states): Here we can choose to prune advanced states to reclaim disk space. As
407410 // it's a foreground task there's no risk of race condition that can corrupt the DB.
408411 // Advanced states for invalid blocks that were never written to the DB, or descendants of
@@ -2764,14 +2767,14 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
27642767 Some ( mut split) => {
27652768 debug ! ( ?split, "Loaded split partial" ) ;
27662769 // Load the hot state summary to get the block root.
2767- let summary = self
2768- . load_hot_state_summary ( & split. state_root )
2770+ let latest_block_root = self
2771+ . load_block_root_from_summary_any_version ( & split. state_root )
27692772 . map_err ( |e| Error :: LoadHotStateSummaryForSplit ( e. into ( ) ) ) ?
27702773 . ok_or ( HotColdDBError :: MissingSplitState (
27712774 split. state_root ,
27722775 split. slot ,
27732776 ) ) ?;
2774- split. block_root = summary . latest_block_root ;
2777+ split. block_root = latest_block_root;
27752778 Ok ( Some ( split) )
27762779 }
27772780 None => Ok ( None ) ,
@@ -2801,6 +2804,31 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
28012804 . map_err ( |e| Error :: LoadHotStateSummary ( * state_root, e. into ( ) ) )
28022805 }
28032806
2807+ /// Load a hot state's summary in V22 format, given its root.
2808+ pub fn load_hot_state_summary_v22 (
2809+ & self ,
2810+ state_root : & Hash256 ,
2811+ ) -> Result < Option < HotStateSummaryV22 > , Error > {
2812+ self . hot_db
2813+ . get ( state_root)
2814+ . map_err ( |e| Error :: LoadHotStateSummary ( * state_root, e. into ( ) ) )
2815+ }
2816+
2817+ /// Load the latest block root for a hot state summary either in modern form, or V22 form.
2818+ ///
2819+ /// This function is required to open a V22 database for migration to V24, or vice versa.
2820+ pub fn load_block_root_from_summary_any_version (
2821+ & self ,
2822+ state_root : & Hash256 ,
2823+ ) -> Result < Option < Hash256 > , Error > {
2824+ self . load_hot_state_summary ( state_root)
2825+ . map ( |opt_summary| opt_summary. map ( |summary| summary. latest_block_root ) )
2826+ . or_else ( |_| {
2827+ self . load_hot_state_summary_v22 ( state_root)
2828+ . map ( |opt_summary| opt_summary. map ( |summary| summary. latest_block_root ) )
2829+ } )
2830+ }
2831+
28042832 /// Load all hot state summaries present in the hot DB
28052833 pub fn load_hot_state_summaries ( & self ) -> Result < Vec < ( Hash256 , HotStateSummary ) > , Error > {
28062834 self . hot_db
@@ -3689,6 +3717,30 @@ impl HotStateSummary {
36893717 }
36903718}
36913719
3720+ /// Legacy hot state summary used in schema V22 and before.
3721+ ///
3722+ /// This can be deleted when we remove V22 support.
3723+ #[ derive( Debug , Clone , Copy , Encode , Decode ) ]
3724+ pub struct HotStateSummaryV22 {
3725+ pub slot : Slot ,
3726+ pub latest_block_root : Hash256 ,
3727+ pub epoch_boundary_state_root : Hash256 ,
3728+ }
3729+
3730+ impl StoreItem for HotStateSummaryV22 {
3731+ fn db_column ( ) -> DBColumn {
3732+ DBColumn :: BeaconStateSummary
3733+ }
3734+
3735+ fn as_store_bytes ( & self ) -> Vec < u8 > {
3736+ self . as_ssz_bytes ( )
3737+ }
3738+
3739+ fn from_store_bytes ( bytes : & [ u8 ] ) -> Result < Self , Error > {
3740+ Ok ( Self :: from_ssz_bytes ( bytes) ?)
3741+ }
3742+ }
3743+
36923744/// Struct for summarising a state in the freezer database.
36933745#[ derive( Debug , Clone , Copy , Default , Encode , Decode ) ]
36943746pub ( crate ) struct ColdStateSummary {
0 commit comments