@@ -2900,12 +2900,28 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
29002900 Ok ( ops)
29012901 }
29022902
2903- /// Returns a single block root from the cold DB
2903+ /// Return a single block root from the cold DB.
2904+ ///
2905+ /// If the slot is unavailable due to partial block history, `Ok(None)` will be returned.
29042906 pub fn get_cold_block_root ( & self , slot : Slot ) -> Result < Option < Hash256 > , Error > {
29052907 Ok ( self
29062908 . cold_db
29072909 . get_bytes ( DBColumn :: BeaconBlockRoots , & slot. as_u64 ( ) . to_be_bytes ( ) ) ?
2908- . map ( |bytes| Hash256 :: from_ssz_bytes ( & bytes) . unwrap ( ) ) )
2910+ . map ( |bytes| Hash256 :: from_ssz_bytes ( & bytes) )
2911+ . transpose ( ) ?)
2912+ }
2913+
2914+ /// Return a single state root from the cold DB.
2915+ ///
2916+ /// If the slot is unavailable due to partial state history, `Ok(None)` will be returned.
2917+ ///
2918+ /// This function will usually only work on an archive node.
2919+ pub fn get_cold_state_root ( & self , slot : Slot ) -> Result < Option < Hash256 > , Error > {
2920+ Ok ( self
2921+ . cold_db
2922+ . get_bytes ( DBColumn :: BeaconStateRoots , & slot. as_u64 ( ) . to_be_bytes ( ) ) ?
2923+ . map ( |bytes| Hash256 :: from_ssz_bytes ( & bytes) )
2924+ . transpose ( ) ?)
29092925 }
29102926
29112927 /// Try to prune all execution payloads, returning early if there is no need to prune.
@@ -3448,6 +3464,11 @@ pub enum StateSummaryIteratorError {
34483464 } ,
34493465 BelowTarget ( Slot ) ,
34503466 LoadSummaryError ( Box < Error > ) ,
3467+ LoadStateRootError ( Box < Error > ) ,
3468+ MissingStateRoot {
3469+ target_slot : Slot ,
3470+ state_upper_limit : Slot ,
3471+ } ,
34513472 OutOfBoundsInitialSlot ,
34523473}
34533474
@@ -3463,10 +3484,35 @@ fn get_ancestor_state_root<'a, E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>
34633484 return Ok ( * target_state_root) ;
34643485 }
34653486
3487+ // Fetch the anchor info prior to obtaining the split lock. We don't need to hold a lock because
3488+ // the `state_upper_limit` can't increase (and rug us) unless state pruning runs, and it never
3489+ // runs concurrently.
3490+ let state_upper_limit = store. get_anchor_info ( ) . state_upper_limit ;
3491+
34663492 // Hold the split lock so that state summaries are not pruned concurrently with this function
34673493 // running.
34683494 let split = store. split . read_recursive ( ) ;
34693495
3496+ // If the state root is in range of the freezer DB's linear state root storage, fetch it
3497+ // directly from there. This is useful on archive nodes to avoid some of the complexity of
3498+ // traversing the sparse portion of the hdiff grid (prior to the split slot). It is also
3499+ // necessary for the v24 schema migration on archive nodes, where there isn't yet any grid
3500+ // to traverse.
3501+ //
3502+ // FIXME(tree-states): still need to add logic below to somehow traverse the grid on non-archive
3503+ // nodes.
3504+ if target_slot < split. slot && target_slot >= state_upper_limit {
3505+ drop ( split) ;
3506+ return store
3507+ . get_cold_state_root ( target_slot)
3508+ . map_err ( Box :: new)
3509+ . map_err ( StateSummaryIteratorError :: LoadStateRootError ) ?
3510+ . ok_or_else ( || StateSummaryIteratorError :: MissingStateRoot {
3511+ target_slot,
3512+ state_upper_limit,
3513+ } ) ;
3514+ }
3515+
34703516 let mut state_root = {
34713517 // We can not start loading summaries from `state_root` since its summary has not yet been
34723518 // imported. This code path is called during block import.
0 commit comments