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::{state_traits::StateMut, Account, ReadableAccount},
275        solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo},
276        solana_pubkey::Pubkey,
277        solana_runtime::{
278            genesis_utils::{create_genesis_config_with_vote_accounts, ValidatorVoteKeypairs},
279            stake_utils,
280        },
281        solana_signer::Signer,
282        solana_vote::vote_transaction,
283        solana_vote_program::vote_state::{
284            self, process_slot_vote_unchecked, TowerSync, VoteStateV4, VoteStateVersions,
285            MAX_LOCKOUT_HISTORY,
286        },
287    };
288
289    #[test]
290    fn test_get_highest_super_majority_root() {
291        assert_eq!(get_highest_super_majority_root(vec![], 10), 0);
292        let rooted_stake = vec![(0, 5), (1, 5)];
293        assert_eq!(get_highest_super_majority_root(rooted_stake, 10), 0);
294        let rooted_stake = vec![(1, 5), (0, 10), (2, 5), (1, 4)];
295        assert_eq!(get_highest_super_majority_root(rooted_stake, 10), 1);
296    }
297
298    #[test]
299    fn test_aggregate_commitment_for_vote_account_1() {
300        let ancestors = vec![3, 4, 5, 7, 9, 11];
301        let mut commitment = HashMap::new();
302        let mut rooted_stake = vec![];
303        let lamports = 5;
304        let mut vote_state = TowerVoteState::default();
305
306        let root = *ancestors.last().unwrap();
307        vote_state.root_slot = Some(root);
308        AggregateCommitmentService::aggregate_commitment_for_vote_account(
309            &mut commitment,
310            &mut rooted_stake,
311            &vote_state,
312            &ancestors,
313            lamports,
314        );
315
316        for a in ancestors {
317            let mut expected = BlockCommitment::default();
318            expected.increase_rooted_stake(lamports);
319            assert_eq!(*commitment.get(&a).unwrap(), expected);
320        }
321        assert_eq!(rooted_stake[0], (root, lamports));
322    }
323
324    #[test]
325    fn test_aggregate_commitment_for_vote_account_2() {
326        let ancestors = vec![3, 4, 5, 7, 9, 11];
327        let mut commitment = HashMap::new();
328        let mut rooted_stake = vec![];
329        let lamports = 5;
330        let mut vote_state = TowerVoteState::default();
331
332        let root = ancestors[2];
333        vote_state.root_slot = Some(root);
334        vote_state.process_next_vote_slot(*ancestors.last().unwrap());
335        AggregateCommitmentService::aggregate_commitment_for_vote_account(
336            &mut commitment,
337            &mut rooted_stake,
338            &vote_state,
339            &ancestors,
340            lamports,
341        );
342
343        for a in ancestors {
344            let mut expected = BlockCommitment::default();
345            if a <= root {
346                expected.increase_rooted_stake(lamports);
347            } else {
348                expected.increase_confirmation_stake(1, lamports);
349            }
350            assert_eq!(*commitment.get(&a).unwrap(), expected);
351        }
352        assert_eq!(rooted_stake[0], (root, lamports));
353    }
354
355    #[test]
356    fn test_aggregate_commitment_for_vote_account_3() {
357        let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
358        let mut commitment = HashMap::new();
359        let mut rooted_stake = vec![];
360        let lamports = 5;
361        let mut vote_state = TowerVoteState::default();
362
363        let root = ancestors[2];
364        vote_state.root_slot = Some(root);
365        assert!(ancestors[4] + 2 >= ancestors[6]);
366        vote_state.process_next_vote_slot(ancestors[4]);
367        vote_state.process_next_vote_slot(ancestors[6]);
368        AggregateCommitmentService::aggregate_commitment_for_vote_account(
369            &mut commitment,
370            &mut rooted_stake,
371            &vote_state,
372            &ancestors,
373            lamports,
374        );
375
376        for (i, a) in ancestors.iter().enumerate() {
377            if *a <= root {
378                let mut expected = BlockCommitment::default();
379                expected.increase_rooted_stake(lamports);
380                assert_eq!(*commitment.get(a).unwrap(), expected);
381            } else if i <= 4 {
382                let mut expected = BlockCommitment::default();
383                expected.increase_confirmation_stake(2, lamports);
384                assert_eq!(*commitment.get(a).unwrap(), expected);
385            } else if i <= 6 {
386                let mut expected = BlockCommitment::default();
387                expected.increase_confirmation_stake(1, lamports);
388                assert_eq!(*commitment.get(a).unwrap(), expected);
389            }
390        }
391        assert_eq!(rooted_stake[0], (root, lamports));
392    }
393
394    fn do_test_aggregate_commitment_validity(with_node_vote_state: bool) {
395        let ancestors = vec![3, 4, 5, 7, 9, 10, 11];
396        let GenesisConfigInfo {
397            mut genesis_config, ..
398        } = create_genesis_config(10_000);
399
400        let rooted_stake_amount = 40;
401
402        let sk1 = solana_pubkey::new_rand();
403        let pk1 = solana_pubkey::new_rand();
404        let mut vote_account1 = vote_state::create_v4_account_with_authorized(
405            &solana_pubkey::new_rand(),
406            &pk1,
407            &pk1,
408            None,
409            0,
410            100,
411        );
412        let stake_account1 = stake_utils::create_stake_account(
413            &sk1,
414            &pk1,
415            &vote_account1,
416            &genesis_config.rent,
417            100,
418        );
419        let sk2 = solana_pubkey::new_rand();
420        let pk2 = solana_pubkey::new_rand();
421        let mut vote_account2 = vote_state::create_v4_account_with_authorized(
422            &solana_pubkey::new_rand(),
423            &pk2,
424            &pk2,
425            None,
426            0,
427            50,
428        );
429        let stake_account2 =
430            stake_utils::create_stake_account(&sk2, &pk2, &vote_account2, &genesis_config.rent, 50);
431        let sk3 = solana_pubkey::new_rand();
432        let pk3 = solana_pubkey::new_rand();
433        let mut vote_account3 = vote_state::create_v4_account_with_authorized(
434            &solana_pubkey::new_rand(),
435            &pk3,
436            &pk3,
437            None,
438            0,
439            1,
440        );
441        let stake_account3 = stake_utils::create_stake_account(
442            &sk3,
443            &pk3,
444            &vote_account3,
445            &genesis_config.rent,
446            rooted_stake_amount,
447        );
448        let sk4 = solana_pubkey::new_rand();
449        let pk4 = solana_pubkey::new_rand();
450        let mut vote_account4 = vote_state::create_v4_account_with_authorized(
451            &solana_pubkey::new_rand(),
452            &pk4,
453            &pk4,
454            None,
455            0,
456            1,
457        );
458        let stake_account4 = stake_utils::create_stake_account(
459            &sk4,
460            &pk4,
461            &vote_account4,
462            &genesis_config.rent,
463            rooted_stake_amount,
464        );
465
466        genesis_config.accounts.extend(
467            vec![
468                (pk1, vote_account1.clone()),
469                (sk1, stake_account1),
470                (pk2, vote_account2.clone()),
471                (sk2, stake_account2),
472                (pk3, vote_account3.clone()),
473                (sk3, stake_account3),
474                (pk4, vote_account4.clone()),
475                (sk4, stake_account4),
476            ]
477            .into_iter()
478            .map(|(key, account)| (key, Account::from(account))),
479        );
480
481        // Create bank
482        let bank = Arc::new(Bank::new_for_tests(&genesis_config));
483
484        let mut vote_state1 = VoteStateV4::deserialize(vote_account1.data(), &pk1).unwrap();
485        process_slot_vote_unchecked(&mut vote_state1, 3);
486        process_slot_vote_unchecked(&mut vote_state1, 5);
487        if !with_node_vote_state {
488            let versioned = VoteStateVersions::new_v4(vote_state1.clone());
489            vote_account1.set_state(&versioned).unwrap();
490            bank.store_account(&pk1, &vote_account1);
491        }
492
493        let mut vote_state2 = VoteStateV4::deserialize(vote_account2.data(), &pk2).unwrap();
494        process_slot_vote_unchecked(&mut vote_state2, 9);
495        process_slot_vote_unchecked(&mut vote_state2, 10);
496        let versioned = VoteStateVersions::new_v4(vote_state2);
497        vote_account2.set_state(&versioned).unwrap();
498        bank.store_account(&pk2, &vote_account2);
499
500        let mut vote_state3 = VoteStateV4::deserialize(vote_account3.data(), &pk3).unwrap();
501        vote_state3.root_slot = Some(1);
502        let versioned = VoteStateVersions::new_v4(vote_state3);
503        vote_account3.set_state(&versioned).unwrap();
504        bank.store_account(&pk3, &vote_account3);
505
506        let mut vote_state4 = VoteStateV4::deserialize(vote_account4.data(), &pk4).unwrap();
507        vote_state4.root_slot = Some(2);
508        let versioned = VoteStateVersions::new_v4(vote_state4);
509        vote_account4.set_state(&versioned).unwrap();
510        bank.store_account(&pk4, &vote_account4);
511
512        let node_vote_pubkey = if with_node_vote_state {
513            pk1
514        } else {
515            // Use some random pubkey as dummy to suppress the override.
516            solana_pubkey::new_rand()
517        };
518
519        let (commitment, rooted_stake) = AggregateCommitmentService::aggregate_commitment(
520            &ancestors,
521            &bank,
522            &(node_vote_pubkey, TowerVoteState::from(vote_state1)),
523        );
524
525        for a in ancestors {
526            if a <= 3 {
527                let mut expected = BlockCommitment::default();
528                expected.increase_confirmation_stake(2, 150);
529                assert_eq!(*commitment.get(&a).unwrap(), expected);
530            } else if a <= 5 {
531                let mut expected = BlockCommitment::default();
532                expected.increase_confirmation_stake(1, 100);
533                expected.increase_confirmation_stake(2, 50);
534                assert_eq!(*commitment.get(&a).unwrap(), expected);
535            } else if a <= 9 {
536                let mut expected = BlockCommitment::default();
537                expected.increase_confirmation_stake(2, 50);
538                assert_eq!(*commitment.get(&a).unwrap(), expected);
539            } else if a <= 10 {
540                let mut expected = BlockCommitment::default();
541                expected.increase_confirmation_stake(1, 50);
542                assert_eq!(*commitment.get(&a).unwrap(), expected);
543            } else {
544                assert!(!commitment.contains_key(&a));
545            }
546        }
547        assert_eq!(rooted_stake.len(), 2);
548        assert_eq!(get_highest_super_majority_root(rooted_stake, 100), 1)
549    }
550
551    #[test]
552    fn test_aggregate_commitment_validity_with_node_vote_state() {
553        do_test_aggregate_commitment_validity(true)
554    }
555
556    #[test]
557    fn test_aggregate_commitment_validity_without_node_vote_state() {
558        do_test_aggregate_commitment_validity(false);
559    }
560
561    #[test]
562    fn test_highest_super_majority_root_advance() {
563        fn get_vote_state(vote_pubkey: Pubkey, bank: &Bank) -> TowerVoteState {
564            let vote_account = bank.get_vote_account(&vote_pubkey).unwrap();
565            TowerVoteState::from(vote_account.vote_state_view())
566        }
567
568        let block_commitment_cache = RwLock::new(BlockCommitmentCache::new_for_tests());
569
570        let validator_vote_keypairs = ValidatorVoteKeypairs::new_rand();
571        let validator_keypairs = vec![&validator_vote_keypairs];
572        let GenesisConfigInfo { genesis_config, .. } = create_genesis_config_with_vote_accounts(
573            1_000_000_000,
574            &validator_keypairs,
575            vec![100; 1],
576        );
577
578        let (_bank0, bank_forks) = Bank::new_with_bank_forks_for_tests(&genesis_config);
579
580        // Fill bank_forks with banks with votes landing in the next slot
581        // Create enough banks such that vote account will root slots 0 and 1
582        for x in 0..33 {
583            let previous_bank = bank_forks.read().unwrap().get(x).unwrap();
584            let bank = Bank::new_from_parent_with_bank_forks(
585                bank_forks.as_ref(),
586                previous_bank.clone(),
587                &Pubkey::default(),
588                x + 1,
589            );
590            let tower_sync = TowerSync::new_from_slot(x, previous_bank.hash());
591            let vote = vote_transaction::new_tower_sync_transaction(
592                tower_sync,
593                previous_bank.last_blockhash(),
594                &validator_vote_keypairs.node_keypair,
595                &validator_vote_keypairs.vote_keypair,
596                &validator_vote_keypairs.vote_keypair,
597                None,
598            );
599            bank.process_transaction(&vote).unwrap();
600        }
601
602        let working_bank = bank_forks.read().unwrap().working_bank();
603        let vote_pubkey = validator_vote_keypairs.vote_keypair.pubkey();
604        let root = get_vote_state(vote_pubkey, &working_bank)
605            .root_slot
606            .unwrap();
607        for x in 0..root {
608            bank_forks.write().unwrap().set_root(x, None, None);
609        }
610
611        // Add an additional bank/vote that will root slot 2
612        let bank33 = bank_forks.read().unwrap().get(33).unwrap();
613        let bank34 = Bank::new_from_parent_with_bank_forks(
614            bank_forks.as_ref(),
615            bank33.clone(),
616            &Pubkey::default(),
617            34,
618        );
619        let tower_sync = TowerSync::new_from_slot(33, bank33.hash());
620        let vote33 = vote_transaction::new_tower_sync_transaction(
621            tower_sync,
622            bank33.last_blockhash(),
623            &validator_vote_keypairs.node_keypair,
624            &validator_vote_keypairs.vote_keypair,
625            &validator_vote_keypairs.vote_keypair,
626            None,
627        );
628        bank34.process_transaction(&vote33).unwrap();
629
630        let working_bank = bank_forks.read().unwrap().working_bank();
631        let vote_state = get_vote_state(vote_pubkey, &working_bank);
632        let root = vote_state.root_slot.unwrap();
633        let ancestors = working_bank.status_cache_ancestors();
634        let _ = AggregateCommitmentService::update_commitment_cache(
635            &block_commitment_cache,
636            CommitmentAggregationData {
637                bank: working_bank,
638                root: 0,
639                total_stake: 100,
640                node_vote_state: (vote_pubkey, vote_state.clone()),
641            },
642            ancestors,
643        );
644        let highest_super_majority_root = block_commitment_cache
645            .read()
646            .unwrap()
647            .highest_super_majority_root();
648        bank_forks
649            .write()
650            .unwrap()
651            .set_root(root, None, Some(highest_super_majority_root));
652        let highest_super_majority_root_bank =
653            bank_forks.read().unwrap().get(highest_super_majority_root);
654        assert!(highest_super_majority_root_bank.is_some());
655
656        // Add a forked bank. Because the vote for bank 33 landed in the non-ancestor, the vote
657        // account's root (and thus the highest_super_majority_root) rolls back to slot 1
658        let bank33 = bank_forks.read().unwrap().get(33).unwrap();
659        let _bank35 = Bank::new_from_parent_with_bank_forks(
660            bank_forks.as_ref(),
661            bank33,
662            &Pubkey::default(),
663            35,
664        );
665
666        let working_bank = bank_forks.read().unwrap().working_bank();
667        let ancestors = working_bank.status_cache_ancestors();
668        let _ = AggregateCommitmentService::update_commitment_cache(
669            &block_commitment_cache,
670            CommitmentAggregationData {
671                bank: working_bank,
672                root: 1,
673                total_stake: 100,
674                node_vote_state: (vote_pubkey, vote_state),
675            },
676            ancestors,
677        );
678        let highest_super_majority_root = block_commitment_cache
679            .read()
680            .unwrap()
681            .highest_super_majority_root();
682        let highest_super_majority_root_bank =
683            bank_forks.read().unwrap().get(highest_super_majority_root);
684        assert!(highest_super_majority_root_bank.is_some());
685
686        // Add additional banks beyond lockout built on the new fork to ensure that behavior
687        // continues normally
688        for x in 35..=37 {
689            let previous_bank = bank_forks.read().unwrap().get(x).unwrap();
690            let bank = Bank::new_from_parent_with_bank_forks(
691                bank_forks.as_ref(),
692                previous_bank.clone(),
693                &Pubkey::default(),
694                x + 1,
695            );
696            // Skip 34 as it is not part of this fork.
697            let lowest_slot = x - MAX_LOCKOUT_HISTORY as u64;
698            let slots: Vec<_> = (lowest_slot..(x + 1)).filter(|s| *s != 34).collect();
699            let tower_sync =
700                TowerSync::new_from_slots(slots, previous_bank.hash(), Some(lowest_slot - 1));
701            let vote = vote_transaction::new_tower_sync_transaction(
702                tower_sync,
703                previous_bank.last_blockhash(),
704                &validator_vote_keypairs.node_keypair,
705                &validator_vote_keypairs.vote_keypair,
706                &validator_vote_keypairs.vote_keypair,
707                None,
708            );
709            bank.process_transaction(&vote).unwrap();
710        }
711
712        let working_bank = bank_forks.read().unwrap().working_bank();
713        let vote_state =
714            get_vote_state(validator_vote_keypairs.vote_keypair.pubkey(), &working_bank);
715        let root = vote_state.root_slot.unwrap();
716        let ancestors = working_bank.status_cache_ancestors();
717        let _ = AggregateCommitmentService::update_commitment_cache(
718            &block_commitment_cache,
719            CommitmentAggregationData {
720                bank: working_bank,
721                root: 0,
722                total_stake: 100,
723                node_vote_state: (vote_pubkey, vote_state),
724            },
725            ancestors,
726        );
727        let highest_super_majority_root = block_commitment_cache
728            .read()
729            .unwrap()
730            .highest_super_majority_root();
731        bank_forks
732            .write()
733            .unwrap()
734            .set_root(root, None, Some(highest_super_majority_root));
735        let highest_super_majority_root_bank =
736            bank_forks.read().unwrap().get(highest_super_majority_root);
737        assert!(highest_super_majority_root_bank.is_some());
738    }
739}