@@ -24,8 +24,9 @@ use operation_pool::{OperationPool, PersistedOperationPool};
24
24
use parking_lot:: RwLock ;
25
25
use proto_array:: { DisallowedReOrgOffsets , ReOrgThreshold } ;
26
26
use slasher:: Slasher ;
27
- use slog:: { crit, error, info, Logger } ;
27
+ use slog:: { crit, debug , error, info, Logger } ;
28
28
use slot_clock:: { SlotClock , TestingSlotClock } ;
29
+ use state_processing:: per_slot_processing;
29
30
use std:: marker:: PhantomData ;
30
31
use std:: sync:: Arc ;
31
32
use std:: time:: Duration ;
@@ -287,7 +288,7 @@ where
287
288
let genesis_state = store
288
289
. get_state ( & genesis_block. state_root ( ) , Some ( genesis_block. slot ( ) ) )
289
290
. map_err ( |e| descriptive_db_error ( "genesis state" , & e) ) ?
290
- . ok_or ( "Genesis block not found in store" ) ?;
291
+ . ok_or ( "Genesis state not found in store" ) ?;
291
292
292
293
self . genesis_time = Some ( genesis_state. genesis_time ( ) ) ;
293
294
@@ -382,6 +383,16 @@ where
382
383
let ( genesis, updated_builder) = self . set_genesis_state ( beacon_state) ?;
383
384
self = updated_builder;
384
385
386
+ // Stage the database's metadata fields for atomic storage when `build` is called.
387
+ // Since v4.4.0 we will set the anchor with a dummy state upper limit in order to prevent
388
+ // historic states from being retained (unless `--reconstruct-historic-states` is set).
389
+ let retain_historic_states = self . chain_config . reconstruct_historic_states ;
390
+ self . pending_io_batch . push (
391
+ store
392
+ . init_anchor_info ( genesis. beacon_block . message ( ) , retain_historic_states)
393
+ . map_err ( |e| format ! ( "Failed to initialize genesis anchor: {:?}" , e) ) ?,
394
+ ) ;
395
+
385
396
let fc_store = BeaconForkChoiceStore :: get_forkchoice_store ( store, & genesis)
386
397
. map_err ( |e| format ! ( "Unable to initialize fork choice store: {e:?}" ) ) ?;
387
398
let current_slot = None ;
@@ -408,46 +419,48 @@ where
408
419
weak_subj_block : SignedBeaconBlock < TEthSpec > ,
409
420
genesis_state : BeaconState < TEthSpec > ,
410
421
) -> Result < Self , String > {
411
- let store = self . store . clone ( ) . ok_or ( "genesis_state requires a store" ) ?;
412
-
413
- let weak_subj_slot = weak_subj_state. slot ( ) ;
414
- let weak_subj_block_root = weak_subj_block. canonical_root ( ) ;
415
- let weak_subj_state_root = weak_subj_block. state_root ( ) ;
416
-
417
- // Check that the given block lies on an epoch boundary. Due to the database only storing
418
- // full states on epoch boundaries and at restore points it would be difficult to support
419
- // starting from a mid-epoch state.
420
- if weak_subj_slot % TEthSpec :: slots_per_epoch ( ) != 0 {
421
- return Err ( format ! (
422
- "Checkpoint block at slot {} is not aligned to epoch start. \
423
- Please supply an aligned checkpoint with block.slot % 32 == 0",
424
- weak_subj_block. slot( ) ,
425
- ) ) ;
426
- }
422
+ let store = self
423
+ . store
424
+ . clone ( )
425
+ . ok_or ( "weak_subjectivity_state requires a store" ) ?;
426
+ let log = self
427
+ . log
428
+ . as_ref ( )
429
+ . ok_or ( "weak_subjectivity_state requires a log" ) ?;
427
430
428
- // Check that the block and state have consistent slots and state roots.
429
- if weak_subj_state. slot ( ) != weak_subj_block. slot ( ) {
430
- return Err ( format ! (
431
- "Slot of snapshot block ({}) does not match snapshot state ({})" ,
432
- weak_subj_block. slot( ) ,
433
- weak_subj_state. slot( ) ,
434
- ) ) ;
431
+ // Ensure the state is advanced to an epoch boundary.
432
+ let slots_per_epoch = TEthSpec :: slots_per_epoch ( ) ;
433
+ if weak_subj_state. slot ( ) % slots_per_epoch != 0 {
434
+ debug ! (
435
+ log,
436
+ "Advancing checkpoint state to boundary" ;
437
+ "state_slot" => weak_subj_state. slot( ) ,
438
+ "block_slot" => weak_subj_block. slot( ) ,
439
+ ) ;
440
+ while weak_subj_state. slot ( ) % slots_per_epoch != 0 {
441
+ per_slot_processing ( & mut weak_subj_state, None , & self . spec )
442
+ . map_err ( |e| format ! ( "Error advancing state: {e:?}" ) ) ?;
443
+ }
435
444
}
436
445
437
446
// Prime all caches before storing the state in the database and computing the tree hash
438
447
// root.
439
448
weak_subj_state
440
449
. build_caches ( & self . spec )
441
450
. map_err ( |e| format ! ( "Error building caches on checkpoint state: {e:?}" ) ) ?;
442
-
443
- let computed_state_root = weak_subj_state
451
+ let weak_subj_state_root = weak_subj_state
444
452
. update_tree_hash_cache ( )
445
453
. map_err ( |e| format ! ( "Error computing checkpoint state root: {:?}" , e) ) ?;
446
454
447
- if weak_subj_state_root != computed_state_root {
455
+ let weak_subj_slot = weak_subj_state. slot ( ) ;
456
+ let weak_subj_block_root = weak_subj_block. canonical_root ( ) ;
457
+
458
+ // Validate the state's `latest_block_header` against the checkpoint block.
459
+ let state_latest_block_root = weak_subj_state. get_latest_block_root ( weak_subj_state_root) ;
460
+ if weak_subj_block_root != state_latest_block_root {
448
461
return Err ( format ! (
449
- "Snapshot state root does not match block, expected: {:?}, got: {:?}" ,
450
- weak_subj_state_root , computed_state_root
462
+ "Snapshot state's most recent block root does not match block, expected: {:?}, got: {:?}" ,
463
+ weak_subj_block_root , state_latest_block_root
451
464
) ) ;
452
465
}
453
466
@@ -464,7 +477,7 @@ where
464
477
465
478
// Set the store's split point *before* storing genesis so that genesis is stored
466
479
// immediately in the freezer DB.
467
- store. set_split ( weak_subj_slot, weak_subj_state_root) ;
480
+ store. set_split ( weak_subj_slot, weak_subj_state_root, weak_subj_block_root ) ;
468
481
let ( _, updated_builder) = self . set_genesis_state ( genesis_state) ?;
469
482
self = updated_builder;
470
483
@@ -480,10 +493,11 @@ where
480
493
// Stage the database's metadata fields for atomic storage when `build` is called.
481
494
// This prevents the database from restarting in an inconsistent state if the anchor
482
495
// info or split point is written before the `PersistedBeaconChain`.
496
+ let retain_historic_states = self . chain_config . reconstruct_historic_states ;
483
497
self . pending_io_batch . push ( store. store_split_in_batch ( ) ) ;
484
498
self . pending_io_batch . push (
485
499
store
486
- . init_anchor_info ( weak_subj_block. message ( ) )
500
+ . init_anchor_info ( weak_subj_block. message ( ) , retain_historic_states )
487
501
. map_err ( |e| format ! ( "Failed to initialize anchor info: {:?}" , e) ) ?,
488
502
) ;
489
503
@@ -503,13 +517,12 @@ where
503
517
let fc_store = BeaconForkChoiceStore :: get_forkchoice_store ( store, & snapshot)
504
518
. map_err ( |e| format ! ( "Unable to initialize fork choice store: {e:?}" ) ) ?;
505
519
506
- let current_slot = Some ( snapshot. beacon_block . slot ( ) ) ;
507
520
let fork_choice = ForkChoice :: from_anchor (
508
521
fc_store,
509
522
snapshot. beacon_block_root ,
510
523
& snapshot. beacon_block ,
511
524
& snapshot. beacon_state ,
512
- current_slot ,
525
+ Some ( weak_subj_slot ) ,
513
526
& self . spec ,
514
527
)
515
528
. map_err ( |e| format ! ( "Unable to initialize ForkChoice: {:?}" , e) ) ?;
@@ -672,9 +685,8 @@ where
672
685
Err ( e) => return Err ( descriptive_db_error ( "head block" , & e) ) ,
673
686
} ;
674
687
675
- let head_state_root = head_block. state_root ( ) ;
676
- let head_state = store
677
- . get_state ( & head_state_root, Some ( head_block. slot ( ) ) )
688
+ let ( _head_state_root, head_state) = store
689
+ . get_advanced_hot_state ( head_block_root, current_slot, head_block. state_root ( ) )
678
690
. map_err ( |e| descriptive_db_error ( "head state" , & e) ) ?
679
691
. ok_or ( "Head state not found in store" ) ?;
680
692
0 commit comments