solana_core/
commitment_service.rs

1use {
2    crate::consensus::{tower_vote_state::TowerVoteState, Stake},
3    crossbeam_channel::{unbounded, Receiver, RecvTimeoutError, Sender},
4    solana_measure::measure::Measure,
5    solana_metrics::datapoint_info,
6    solana_rpc::rpc_subscriptions::RpcSubscriptions,
7    solana_runtime::{
8        bank::Bank,
9        commitment::{BlockCommitment, BlockCommitmentCache, CommitmentSlots, VOTE_THRESHOLD_SIZE},
10    },
11    solana_sdk::{clock::Slot, pubkey::Pubkey},
12    std::{
13        cmp::max,
14        collections::HashMap,
15        sync::{
16            atomic::{AtomicBool, Ordering},
17            Arc, RwLock,
18        },
19        thread::{self, Builder, JoinHandle},
20        time::Duration,
21    },
22};
23
24pub struct CommitmentAggregationData {
25    bank: Arc<Bank>,
26    root: Slot,
27    total_stake: Stake,
28    // The latest local vote state of the node running this service.
29    // Used for commitment aggregation if the node's vote account is staked.
30    node_vote_state: (Pubkey, TowerVoteState),
31}
32
33impl CommitmentAggregationData {
34    pub fn new(
35        bank: Arc<Bank>,
36        root: Slot,
37        total_stake: Stake,
38        node_vote_state: (Pubkey, TowerVoteState),
39    ) -> Self {
40        Self {
41            bank,
42            root,
43            total_stake,
44            node_vote_state,
45        }
46    }
47}
48
49fn get_highest_super_majority_root(mut rooted_stake: Vec<(Slot, u64)>, total_stake: u64) -> Slot {
50    rooted_stake.sort_by(|a, b| a.0.cmp(&b.0).reverse());
51    let mut stake_sum = 0;
52    for (root, stake) in rooted_stake {
53        stake_sum += stake;
54        if (stake_sum as f64 / total_stake as f64) > VOTE_THRESHOLD_SIZE {
55            return root;
56        }
57    }
58    0
59}
60
61pub struct AggregateCommitmentService {
62    t_commitment: JoinHandle<()>,
63}
64
65impl AggregateCommitmentService {
66    pub fn new(
67        exit: Arc<AtomicBool>,
68        block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
69        subscriptions: Arc<RpcSubscriptions>,
70    ) -> (Sender<CommitmentAggregationData>, Self) {
71        let (sender, receiver): (
72            Sender<CommitmentAggregationData>,
73            Receiver<CommitmentAggregationData>,
74        ) = unbounded();
75        (
76            sender,
77            Self {
78                t_commitment: Builder::new()
79                    .name("solAggCommitSvc".to_string())
80                    .spawn(move || loop {
81                        if exit.load(Ordering::Relaxed) {
82                            break;
83                        }
84
85                        if let Err(RecvTimeoutError::Disconnected) =
86                            Self::run(&receiver, &block_commitment_cache, &subscriptions, &exit)
87                        {
88                            break;
89                        }
90                    })
91                    .unwrap(),
92            },
93        )
94    }
95
96    fn run(
97        receiver: &Receiver<CommitmentAggregationData>,
98        block_commitment_cache: &RwLock<BlockCommitmentCache>,
99        subscriptions: &Arc<RpcSubscriptions>,
100        exit: &AtomicBool,
101    ) -> Result<(), RecvTimeoutError> {
102        loop {
103            if exit.load(Ordering::Relaxed) {
104                return Ok(());
105            }
106
107            let aggregation_data = receiver.recv_timeout(Duration::from_secs(1))?;
108            let aggregation_data = receiver.try_iter().last().unwrap_or(aggregation_data);
109
110            let ancestors = aggregation_data.bank.status_cache_ancestors();
111            if ancestors.is_empty() {
112                continue;
113            }
114
115            let mut aggregate_commitment_time = Measure::start("aggregate-commitment-ms");
116            let update_commitment_slots =
117                Self::update_commitment_cache(block_commitment_cache, aggregation_data, ancestors);
118            aggregate_commitment_time.stop();
119            datapoint_info!(
120                "block-commitment-cache",
121                (
122                    "aggregate-commitment-ms",
123                    aggregate_commitment_time.as_ms() as i64,
124                    i64
125                ),
126                (
127                    "highest-super-majority-root",
128                    update_commitment_slots.highest_super_majority_root as i64,
129                    i64
130                ),
131                (
132                    "highest-confirmed-slot",
133                    update_commitment_slots.highest_confirmed_slot as i64,
134                    i64
135                ),
136            );
137
138            // Triggers rpc_subscription notifications as soon as new commitment data is available,
139            // sending just the commitment cache slot information that the notifications thread
140            // needs
141            subscriptions.notify_subscribers(update_commitment_slots);
142        }
143    }
144
145    fn update_commitment_cache(
146        block_commitment_cache: &RwLock<BlockCommitmentCache>,
147        aggregation_data: CommitmentAggregationData,
148        ancestors: Vec<u64>,
149    ) -> CommitmentSlots {
150        let (block_commitment, rooted_stake) = Self::aggregate_commitment(
151            &ancestors,
152            &aggregation_data.bank,
153            &aggregation_data.node_vote_state,
154        );
155
156        let highest_super_majority_root =
157            get_highest_super_majority_root(rooted_stake, aggregation_data.total_stake);
158
159        let mut new_block_commitment = BlockCommitmentCache::new(
160            block_commitment,
161            aggregation_data.total_stake,
162            CommitmentSlots {
163                slot: aggregation_data.bank.slot(),
164                root: aggregation_data.root,
165                highest_confirmed_slot: aggregation_data.root,
166                highest_super_majority_root,
167            },
168        );
169        let highest_confirmed_slot = new_block_commitment.calculate_highest_confirmed_slot();
170        new_block_commitment.set_highest_confirmed_slot(highest_confirmed_slot);
171
172        let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
173
174        let highest_super_majority_root = max(
175            new_block_commitment.highest_super_majority_root(),
176            w_block_commitment_cache.highest_super_majority_root(),
177        );
178        new_block_commitment.set_highest_super_majority_root(highest_super_majority_root);
179
180        *w_block_commitment_cache = new_block_commitment;
181        w_block_commitment_cache.commitment_slots()
182    }
183
184    pub fn aggregate_commitment(
185        ancestors: &[Slot],
186        bank: &Bank,
187        (node_vote_pubkey, node_vote_state): &(Pubkey, TowerVoteState),
188    ) -> (HashMap<Slot, BlockCommitment>, Vec<(Slot, u64)>) {
189        assert!(!ancestors.is_empty());
190
191        // Check ancestors is sorted
192        for a in ancestors.windows(2) {
193            assert!(a[0] < a[1]);
194        }
195
196        let mut commitment = HashMap::new();
197        let mut rooted_stake: Vec<(Slot, u64)> = Vec::new();
198        for (pubkey, (lamports, account)) in bank.vote_accounts().iter() {
199            if *lamports == 0 {
200                continue;
201            }
202            let vote_state = if pubkey == node_vote_pubkey {
203                // Override old vote_state in bank with latest one for my own vote pubkey
204                node_vote_state.clone()
205            } else {
206                TowerVoteState::from(account.vote_state().clone())
207            };
208            Self::aggregate_commitment_for_vote_account(
209                &mut commitment,
210                &mut rooted_stake,
211                &vote_state,
212                ancestors,
213                *lamports,
214            );
215        }
216
217        (commitment, rooted_stake)
218    }
219
220    fn aggregate_commitment_for_vote_account(
221        commitment: &mut HashMap<Slot, BlockCommitment>,
222        rooted_stake: &mut Vec<(Slot, u64)>,
223        vote_state: &TowerVoteState,
224        ancestors: &[Slot],
225        lamports: u64,
226    ) {
227        assert!(!ancestors.is_empty());
228        let mut ancestors_index = 0;
229        if let Some(root) = vote_state.root_slot {
230            for (i, a) in ancestors.iter().enumerate() {
231                if *a <= root {
232                    commitment
233                        .entry(*a)
234                        .or_default()
235                        .increase_rooted_stake(lamports);
236                } else {
237                    ancestors_index = i;
238                    break;
239                }
240            }
241            rooted_stake.push((root, lamports));
242        }
243
244        for vote in &vote_state.votes {
245            while ancestors[ancestors_index] <= vote.slot() {
246                commitment
247                    .entry(ancestors[ancestors_index])
248                    .or_default()
249                    .increase_confirmation_stake(vote.confirmation_count() as usize, lamports);
250                ancestors_index += 1;
251
252                if ancestors_index == ancestors.len() {
253                    return;
254                }
255            }
256        }
257    }
258
259    pub fn join(self) -> thread::Result<()> {
260        self.t_commitment.join()
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use {
267        super::*,
268        solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo},
269        solana_runtime::{
270            accounts_background_service::AbsRequestSender,
271            bank_forks::BankForks,
272            genesis_utils::{create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs},
273        },
274        solana_sdk::{account::Account, pubkey::Pubkey, signature::Signer},
275        solana_stake_program::stake_state,
276        solana_vote_program::{
277            vote_state::{
278                self, process_slot_vote_unchecked, TowerSync, VoteStateVersions,
279                MAX_LOCKOUT_HISTORY,
280            },
281            vote_transaction,
282        },
283    };
284
285    fn new_bank_from_parent_with_bank_forks(
286        bank_forks: &RwLock<BankForks>,
287        parent: Arc<Bank>,
288        collector_id: &Pubkey,
289        slot: Slot,
290    ) -> Arc<Bank> {
291        let bank = Bank::new_from_parent(parent, collector_id, slot);
292        bank_forks
293            .write()
294            .unwrap()
295            .insert(bank)
296            .clone_without_scheduler()
297    }
298
299    #[test]
300    fn test_get_highest_super_majority_root() {
301        assert_eq!(get_highest_super_majority_root(vec![], 10), 0);
302        let rooted_stake = vec![(0, 5), (1, 5)];
303        assert_eq!(get_highest_super_majority_root(rooted_stake, 10), 0);
304        let rooted_stake = vec![(1, 5), (0, 10), (2, 5), (1, 4)];
305        assert_eq!(get_highest_super_majority_root(rooted_stake, 10), 1);
306    }
307
308    #[test]
309    fn test_aggregate_commitment_for_vote_account_1() {
310        let ancestors = vec![3, 4, 5, 7, 9, 11];
311        let mut commitment = HashMap::new();
312        let mut rooted_stake = vec![];
313        let lamports = 5;
314        let mut vote_state = TowerVoteState::default();
315
316        let root = *ancestors.last().unwrap();
317        vote_state.root_slot = Some(root);
318        AggregateCommitmentService::aggregate_commitment_for_vote_account(
319            &mut commitment,
320            &mut rooted_stake,
321            &vote_state,
322            &ancestors,
323            lamports,
324        );
325
326        for a in ancestors {
327            let mut expected = BlockCommitment::default();
328            expected.increase_rooted_stake(lamports);
329            assert_eq!(*commitment.get(&a).unwrap(), expected);
330        }
331        assert_eq!(rooted_stake[0], (root, lamports));
332    }
333
334    #[test]
335    fn test_aggregate_commitment_for_vote_account_2() {
336        let ancestors = vec![3, 4, 5, 7, 9, 11];
337        let mut commitment = HashMap::new();
338        let mut rooted_stake = vec![];
339        let lamports = 5;
340        let mut vote_state = TowerVoteState::default();
341
342        let root = ancestors[2];
343        vote_state.root_slot = Some(root);
344        vote_state.process_next_vote_slot(*ancestors.last().unwrap());
345        AggregateCommitmentService::aggregate_commitment_for_vote_account(
346            &mut commitment,
347            &mut rooted_stake,
348            &vote_state,
349            &ancestors,
350            lamports,
351        );
352
353        for a in ancestors {
354            let mut expected = BlockCommitment::default();
355            if a <= root {
356                expected.increase_rooted_stake(lamports);
357            } else {
358                expected.increase_confirmation_stake(1, lamports);
359            }
360            assert_eq!(*commitment.get(&a).unwrap(), expected);
361        }
362        assert_eq!(rooted_stake[0], (root, lamports));
363    }
364
365    #[test]
366    fn test_aggregate_commitment_for_vote_account_3() {
367        let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
368        let mut commitment = HashMap::new();
369        let mut rooted_stake = vec![];
370        let lamports = 5;
371        let mut vote_state = TowerVoteState::default();
372
373        let root = ancestors[2];
374        vote_state.root_slot = Some(root);
375        assert!(ancestors[4] + 2 >= ancestors[6]);
376        vote_state.process_next_vote_slot(ancestors[4]);
377        vote_state.process_next_vote_slot(ancestors[6]);
378        AggregateCommitmentService::aggregate_commitment_for_vote_account(
379            &mut commitment,
380            &mut rooted_stake,
381            &vote_state,
382            &ancestors,
383            lamports,
384        );
385
386        for (i, a) in ancestors.iter().enumerate() {
387            if *a <= root {
388                let mut expected = BlockCommitment::default();
389                expected.increase_rooted_stake(lamports);
390                assert_eq!(*commitment.get(a).unwrap(), expected);
391            } else if i <= 4 {
392                let mut expected = BlockCommitment::default();
393                expected.increase_confirmation_stake(2, lamports);
394                assert_eq!(*commitment.get(a).unwrap(), expected);
395            } else if i <= 6 {
396                let mut expected = BlockCommitment::default();
397                expected.increase_confirmation_stake(1, lamports);
398                assert_eq!(*commitment.get(a).unwrap(), expected);
399            }
400        }
401        assert_eq!(rooted_stake[0], (root, lamports));
402    }
403
404    fn do_test_aggregate_commitment_validity(with_node_vote_state: bool) {
405        let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
406        let GenesisConfigInfo {
407            mut genesis_config, ..
408        } = create_genesis_config(10_000);
409
410        let rooted_stake_amount = 40;
411
412        let sk1 = solana_pubkey::new_rand();
413        let pk1 = solana_pubkey::new_rand();
414        let mut vote_account1 =
415            vote_state::create_account(&pk1, &solana_pubkey::new_rand(), 0, 100);
416        let stake_account1 =
417            stake_state::create_account(&sk1, &pk1, &vote_account1, &genesis_config.rent, 100);
418        let sk2 = solana_pubkey::new_rand();
419        let pk2 = solana_pubkey::new_rand();
420        let mut vote_account2 = vote_state::create_account(&pk2, &solana_pubkey::new_rand(), 0, 50);
421        let stake_account2 =
422            stake_state::create_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
423        let sk3 = solana_pubkey::new_rand();
424        let pk3 = solana_pubkey::new_rand();
425        let mut vote_account3 = vote_state::create_account(&pk3, &solana_pubkey::new_rand(), 0, 1);
426        let stake_account3 = stake_state::create_account(
427            &sk3,
428            &pk3,
429            &vote_account3,
430            &genesis_config.rent,
431            rooted_stake_amount,
432        );
433        let sk4 = solana_pubkey::new_rand();
434        let pk4 = solana_pubkey::new_rand();
435        let mut vote_account4 = vote_state::create_account(&pk4, &solana_pubkey::new_rand(), 0, 1);
436        let stake_account4 = stake_state::create_account(
437            &sk4,
438            &pk4,
439            &vote_account4,
440            &genesis_config.rent,
441            rooted_stake_amount,
442        );
443
444        genesis_config.accounts.extend(
445            vec![
446                (pk1, vote_account1.clone()),
447                (sk1, stake_account1),
448                (pk2, vote_account2.clone()),
449                (sk2, stake_account2),
450                (pk3, vote_account3.clone()),
451                (sk3, stake_account3),
452                (pk4, vote_account4.clone()),
453                (sk4, stake_account4),
454            ]
455            .into_iter()
456            .map(|(key, account)| (key, Account::from(account))),
457        );
458
459        // Create bank
460        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
461
462        let mut vote_state1 = vote_state::from(&vote_account1).unwrap();
463        process_slot_vote_unchecked(&mut vote_state1, 3);
464        process_slot_vote_unchecked(&mut vote_state1, 5);
465        if !with_node_vote_state {
466            let versioned = VoteStateVersions::new_current(vote_state1.clone());
467            vote_state::to(&versioned, &mut vote_account1).unwrap();
468            bank.store_account(&pk1, &vote_account1);
469        }
470
471        let mut vote_state2 = vote_state::from(&vote_account2).unwrap();
472        process_slot_vote_unchecked(&mut vote_state2, 9);
473        process_slot_vote_unchecked(&mut vote_state2, 10);
474        let versioned = VoteStateVersions::new_current(vote_state2);
475        vote_state::to(&versioned, &mut vote_account2).unwrap();
476        bank.store_account(&pk2, &vote_account2);
477
478        let mut vote_state3 = vote_state::from(&vote_account3).unwrap();
479        vote_state3.root_slot = Some(1);
480        let versioned = VoteStateVersions::new_current(vote_state3);
481        vote_state::to(&versioned, &mut vote_account3).unwrap();
482        bank.store_account(&pk3, &vote_account3);
483
484        let mut vote_state4 = vote_state::from(&vote_account4).unwrap();
485        vote_state4.root_slot = Some(2);
486        let versioned = VoteStateVersions::new_current(vote_state4);
487        vote_state::to(&versioned, &mut vote_account4).unwrap();
488        bank.store_account(&pk4, &vote_account4);
489
490        let node_vote_pubkey = if with_node_vote_state {
491            pk1
492        } else {
493            // Use some random pubkey as dummy to suppress the override.
494            solana_pubkey::new_rand()
495        };
496
497        let (commitment, rooted_stake) = AggregateCommitmentService::aggregate_commitment(
498            &ancestors,
499            &bank,
500            &(node_vote_pubkey, TowerVoteState::from(vote_state1)),
501        );
502
503        for a in ancestors {
504            if a <= 3 {
505                let mut expected = BlockCommitment::default();
506                expected.increase_confirmation_stake(2, 150);
507                assert_eq!(*commitment.get(&a).unwrap(), expected);
508            } else if a <= 5 {
509                let mut expected = BlockCommitment::default();
510                expected.increase_confirmation_stake(1, 100);
511                expected.increase_confirmation_stake(2, 50);
512                assert_eq!(*commitment.get(&a).unwrap(), expected);
513            } else if a <= 9 {
514                let mut expected = BlockCommitment::default();
515                expected.increase_confirmation_stake(2, 50);
516                assert_eq!(*commitment.get(&a).unwrap(), expected);
517            } else if a <= 10 {
518                let mut expected = BlockCommitment::default();
519                expected.increase_confirmation_stake(1, 50);
520                assert_eq!(*commitment.get(&a).unwrap(), expected);
521            } else {
522                assert!(!commitment.contains_key(&a));
523            }
524        }
525        assert_eq!(rooted_stake.len(), 2);
526        assert_eq!(get_highest_super_majority_root(rooted_stake, 100), 1)
527    }
528
529    #[test]
530    fn test_aggregate_commitment_validity_with_node_vote_state() {
531        do_test_aggregate_commitment_validity(true)
532    }
533
534    #[test]
535    fn test_aggregate_commitment_validity_without_node_vote_state() {
536        do_test_aggregate_commitment_validity(false);
537    }
538
539    #[test]
540    fn test_highest_super_majority_root_advance() {
541        fn get_vote_state(vote_pubkey: Pubkey, bank: &Bank) -> TowerVoteState {
542            let vote_account = bank.get_vote_account(&vote_pubkey).unwrap();
543            TowerVoteState::from(vote_account.vote_state().clone())
544        }
545
546        let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
547
548        let validator_vote_keypairs = ValidatorVoteKeypairs::new_rand();
549        let validator_keypairs = vec![&validator_vote_keypairs];
550        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config_with_vote_accounts(
551            1_000_000_000,
552            &validator_keypairs,
553            vec![100; 1],
554        );
555
556        let (_bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
557
558        // Fill bank_forks with banks with votes landing in the next slot
559        // Create enough banks such that vote account will root slots 0 and 1
560        for x in 0..33 {
561            let previous_bank = bank_forks.read().unwrap().get(x).unwrap();
562            let bank = new_bank_from_parent_with_bank_forks(
563                bank_forks.as_ref(),
564                previous_bank.clone(),
565                &Pubkey::default(),
566                x + 1,
567            );
568            let tower_sync = TowerSync::new_from_slot(x, previous_bank.hash());
569            let vote = vote_transaction::new_tower_sync_transaction(
570                tower_sync,
571                previous_bank.last_blockhash(),
572                &validator_vote_keypairs.node_keypair,
573                &validator_vote_keypairs.vote_keypair,
574                &validator_vote_keypairs.vote_keypair,
575                None,
576            );
577            bank.process_transaction(&vote).unwrap();
578        }
579
580        let working_bank = bank_forks.read().unwrap().working_bank();
581        let vote_pubkey = validator_vote_keypairs.vote_keypair.pubkey();
582        let root = get_vote_state(vote_pubkey, &working_bank)
583            .root_slot
584            .unwrap();
585        for x in 0..root {
586            bank_forks
587                .write()
588                .unwrap()
589                .set_root(x, &AbsRequestSender::default(), None)
590                .unwrap();
591        }
592
593        // Add an additional bank/vote that will root slot 2
594        let bank33 = bank_forks.read().unwrap().get(33).unwrap();
595        let bank34 = new_bank_from_parent_with_bank_forks(
596            bank_forks.as_ref(),
597            bank33.clone(),
598            &Pubkey::default(),
599            34,
600        );
601        let tower_sync = TowerSync::new_from_slot(33, bank33.hash());
602        let vote33 = vote_transaction::new_tower_sync_transaction(
603            tower_sync,
604            bank33.last_blockhash(),
605            &validator_vote_keypairs.node_keypair,
606            &validator_vote_keypairs.vote_keypair,
607            &validator_vote_keypairs.vote_keypair,
608            None,
609        );
610        bank34.process_transaction(&vote33).unwrap();
611
612        let working_bank = bank_forks.read().unwrap().working_bank();
613        let vote_state = get_vote_state(vote_pubkey, &working_bank);
614        let root = vote_state.root_slot.unwrap();
615        let ancestors = working_bank.status_cache_ancestors();
616        let _ = AggregateCommitmentService::update_commitment_cache(
617            &block_commitment_cache,
618            CommitmentAggregationData {
619                bank: working_bank,
620                root: 0,
621                total_stake: 100,
622                node_vote_state: (vote_pubkey, vote_state.clone()),
623            },
624            ancestors,
625        );
626        let highest_super_majority_root = block_commitment_cache
627            .read()
628            .unwrap()
629            .highest_super_majority_root();
630        bank_forks
631            .write()
632            .unwrap()
633            .set_root(
634                root,
635                &AbsRequestSender::default(),
636                Some(highest_super_majority_root),
637            )
638            .unwrap();
639        let highest_super_majority_root_bank =
640            bank_forks.read().unwrap().get(highest_super_majority_root);
641        assert!(highest_super_majority_root_bank.is_some());
642
643        // Add a forked bank. Because the vote for bank 33 landed in the non-ancestor, the vote
644        // account's root (and thus the highest_super_majority_root) rolls back to slot 1
645        let bank33 = bank_forks.read().unwrap().get(33).unwrap();
646        let _bank35 = new_bank_from_parent_with_bank_forks(
647            bank_forks.as_ref(),
648            bank33,
649            &Pubkey::default(),
650            35,
651        );
652
653        let working_bank = bank_forks.read().unwrap().working_bank();
654        let ancestors = working_bank.status_cache_ancestors();
655        let _ = AggregateCommitmentService::update_commitment_cache(
656            &block_commitment_cache,
657            CommitmentAggregationData {
658                bank: working_bank,
659                root: 1,
660                total_stake: 100,
661                node_vote_state: (vote_pubkey, vote_state),
662            },
663            ancestors,
664        );
665        let highest_super_majority_root = block_commitment_cache
666            .read()
667            .unwrap()
668            .highest_super_majority_root();
669        let highest_super_majority_root_bank =
670            bank_forks.read().unwrap().get(highest_super_majority_root);
671        assert!(highest_super_majority_root_bank.is_some());
672
673        // Add additional banks beyond lockout built on the new fork to ensure that behavior
674        // continues normally
675        for x in 35..=37 {
676            let previous_bank = bank_forks.read().unwrap().get(x).unwrap();
677            let bank = new_bank_from_parent_with_bank_forks(
678                bank_forks.as_ref(),
679                previous_bank.clone(),
680                &Pubkey::default(),
681                x + 1,
682            );
683            // Skip 34 as it is not part of this fork.
684            let lowest_slot = x - MAX_LOCKOUT_HISTORY as u64;
685            let slots: Vec<_> = (lowest_slot..(x + 1)).filter(|s| *s != 34).collect();
686            let tower_sync =
687                TowerSync::new_from_slots(slots, previous_bank.hash(), Some(lowest_slot - 1));
688            let vote = vote_transaction::new_tower_sync_transaction(
689                tower_sync,
690                previous_bank.last_blockhash(),
691                &validator_vote_keypairs.node_keypair,
692                &validator_vote_keypairs.vote_keypair,
693                &validator_vote_keypairs.vote_keypair,
694                None,
695            );
696            bank.process_transaction(&vote).unwrap();
697        }
698
699        let working_bank = bank_forks.read().unwrap().working_bank();
700        let vote_state =
701            get_vote_state(validator_vote_keypairs.vote_keypair.pubkey(), &working_bank);
702        let root = vote_state.root_slot.unwrap();
703        let ancestors = working_bank.status_cache_ancestors();
704        let _ = AggregateCommitmentService::update_commitment_cache(
705            &block_commitment_cache,
706            CommitmentAggregationData {
707                bank: working_bank,
708                root: 0,
709                total_stake: 100,
710                node_vote_state: (vote_pubkey, vote_state),
711            },
712            ancestors,
713        );
714        let highest_super_majority_root = block_commitment_cache
715            .read()
716            .unwrap()
717            .highest_super_majority_root();
718        bank_forks
719            .write()
720            .unwrap()
721            .set_root(
722                root,
723                &AbsRequestSender::default(),
724                Some(highest_super_majority_root),
725            )
726            .unwrap();
727        let highest_super_majority_root_bank =
728            bank_forks.read().unwrap().get(highest_super_majority_root);
729        assert!(highest_super_majority_root_bank.is_some());
730    }
731}