solana_core/
commitment_service.rs

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