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