@@ -569,22 +569,18 @@ impl Tower {
569569 return SwitchForkDecision :: SameFork ;
570570 }
571571
572- // Generally, should never consider switching to an ancestor of your last vote
573572 if last_vote_ancestors. contains ( & switch_slot) {
574- let restored_stray_exception = self . is_stray_last_vote ( ) && switch_slot < last_voted_slot;
575- if !restored_stray_exception {
573+ if !self . is_stray_last_vote ( ) {
576574 panic ! (
577575 "Should never consider switching to slot ({}), which is ancestors({:?}) of last vote: {}" ,
578576 switch_slot,
579577 last_vote_ancestors,
580578 last_voted_slot
581579 ) ;
582580 } else {
583- // If last vote is stray, it's possible to reach here, because we don't have complete view of voting history.
584- // In that case, pretend we're always failing to switch, regarless switch_slot is actually on same frok or not.
585- // this is for safety; otherwise we accidentally might doubl#e-vte on ancestors.
586- // siwtch_slot must be `< last_voted_slot` because switch_slot must be one of last_vote_ancestors in this if clause.
587- return SwitchForkDecision :: FailedSwitchThreshold ( 0 , 1 ) ;
581+ // If last vote is stray, it's possible to reach here, because bank fork might not have the slot which (???)
582+ // lockout check should prevent double vote and slashing.
583+ return SwitchForkDecision :: SameFork ;
588584 }
589585 }
590586
@@ -857,14 +853,9 @@ impl Tower {
857853 ) ;
858854 assert_eq ! ( slot_history. check( replayed_root) , Check :: Found ) ;
859855
860- // reconcile_blockstore_roots_with_tower() should already have aligned these.
861- assert ! (
862- tower_root <= replayed_root,
863- format!(
864- "tower root: {:?} >= replayed root slot: {}" ,
865- tower_root, replayed_root
866- )
867- ) ;
856+ if tower_root > replayed_root {
857+ return Err ( TowerError :: TooOldBlockstore ( tower_root, replayed_root) ) ;
858+ }
868859 assert ! (
869860 self . last_vote == Vote :: default ( ) && self . lockouts. votes. is_empty( )
870861 || self . last_vote != Vote :: default ( ) && !self . lockouts. votes. is_empty( ) ,
@@ -1126,6 +1117,12 @@ pub enum TowerError {
11261117 ) ]
11271118 TooOldTower ( Slot , Slot ) ,
11281119
1120+ #[ error(
1121+ "The blockstore is too old: \
1122+ root in tower ({0}) > root in blockstore ({1})"
1123+ ) ]
1124+ TooOldBlockstore ( Slot , Slot ) ,
1125+
11291126 #[ error( "The tower is fatally inconsistent with blockstore: {0}" ) ]
11301127 FatallyInconsistent ( & ' static str ) ,
11311128}
@@ -1184,14 +1181,24 @@ pub fn reconcile_blockstore_roots_with_tower(
11841181 ) ,
11851182 } )
11861183 . collect ( ) ;
1187- // adjust this assertion as well; this can be triggered by being empty if tower is moved to new ledger
1188- // which has older max slot.
1189- assert ! (
1190- !new_roots. is_empty( ) ,
1191- "at least 1 parent slot must be found"
1192- ) ;
1193-
1194- blockstore. set_roots ( & new_roots) ?;
1184+ if !new_roots. is_empty ( ) {
1185+ info ! (
1186+ "marking slots as root based on tower root: {:?} ({}..{}) " ,
1187+ new_roots, tower_root, last_blockstore_root
1188+ ) ;
1189+ blockstore. set_roots ( & new_roots) ?;
1190+ } else {
1191+ // This indicates we're in bad state; but still don't panic here.
1192+ // That's because we might have a chance of recovering properly with
1193+ // newer snapshot. Ultimately TooOldBlockstore should handle
1194+ // this bad condition.
1195+ warn ! (
1196+ "Couldn't find any ancestor slots from tower root ({}) \
1197+ towards ({}) in blockstore; blockstore pruned or only \
1198+ tower moved?",
1199+ tower_root, last_blockstore_root,
1200+ ) ;
1201+ }
11951202 }
11961203 Ok ( ( ) )
11971204}
@@ -2713,8 +2720,7 @@ pub mod test {
27132720 }
27142721
27152722 #[ test]
2716- #[ should_panic( expected = "at least 1 parent slot must be found" ) ]
2717- fn test_reconcile_blockstore_roots_with_tower_panic_no_parent ( ) {
2723+ fn test_reconcile_blockstore_roots_with_tower_nop_no_parent ( ) {
27182724 solana_logger:: setup ( ) ;
27192725 let blockstore_path = get_tmp_ledger_path ! ( ) ;
27202726 {
@@ -2730,7 +2736,9 @@ pub mod test {
27302736
27312737 let mut tower = Tower :: new_with_key ( & Pubkey :: default ( ) ) ;
27322738 tower. lockouts . root_slot = Some ( 4 ) ;
2739+ assert_eq ! ( blockstore. last_root( ) , 0 ) ;
27332740 reconcile_blockstore_roots_with_tower ( & tower, & blockstore) . unwrap ( ) ;
2741+ assert_eq ! ( blockstore. last_root( ) , 0 ) ;
27342742 }
27352743 Blockstore :: destroy ( & blockstore_path) . expect ( "Expected successful database destruction" ) ;
27362744 }
@@ -3052,6 +3060,23 @@ pub mod test {
30523060 . unwrap ( ) ;
30533061 }
30543062
3063+ #[ test]
3064+ fn test_adjust_lockouts_after_replay_vote_on_root ( ) {
3065+ let mut tower = Tower :: new_for_tests ( 10 , 0.9 ) ;
3066+ tower. lockouts . root_slot = Some ( 42 ) ;
3067+ tower. lockouts . votes . push_back ( Lockout :: new ( 42 ) ) ;
3068+ tower. lockouts . votes . push_back ( Lockout :: new ( 43 ) ) ;
3069+ tower. lockouts . votes . push_back ( Lockout :: new ( 44 ) ) ;
3070+ let vote = Vote :: new ( vec ! [ 44 ] , Hash :: default ( ) ) ;
3071+ tower. last_vote = vote;
3072+
3073+ let mut slot_history = SlotHistory :: default ( ) ;
3074+ slot_history. add ( 42 ) ;
3075+
3076+ let tower = tower. adjust_lockouts_after_replay ( 42 , & slot_history) ;
3077+ assert_eq ! ( tower. unwrap( ) . voted_slots( ) , [ 43 , 44 ] ) ;
3078+ }
3079+
30553080 #[ test]
30563081 fn test_adjust_lockouts_after_replay_vote_on_genesis ( ) {
30573082 let mut tower = Tower :: new_for_tests ( 10 , 0.9 ) ;
0 commit comments