Skip to main content

solana_core/
vote_simulator.rs

1#![cfg(feature = "dev-context-only-utils")]
2use {
3    crate::{
4        cluster_info_vote_listener::VoteTracker,
5        cluster_slots_service::cluster_slots::ClusterSlots,
6        consensus::{
7            Tower,
8            fork_choice::{SelectVoteAndResetForkResult, select_vote_and_reset_forks},
9            heaviest_subtree_fork_choice::HeaviestSubtreeForkChoice,
10            latest_validator_votes_for_frozen_banks::LatestValidatorVotesForFrozenBanks,
11            progress_map::{ForkProgress, LockoutInterval, ProgressMap},
12            tower_vote_state::TowerVoteState,
13        },
14        repair::cluster_slot_state_verifier::{
15            DuplicateConfirmedSlots, DuplicateSlotsTracker, EpochSlotsFrozenSlots,
16        },
17        replay_stage::{HeaviestForkFailures, ReplayStage, TowerBFTStructures},
18        unfrozen_gossip_verified_vote_hashes::UnfrozenGossipVerifiedVoteHashes,
19    },
20    crossbeam_channel::bounded,
21    solana_clock::Slot,
22    solana_epoch_schedule::EpochSchedule,
23    solana_hash::Hash,
24    solana_leader_schedule::SlotLeader,
25    solana_pubkey::Pubkey,
26    solana_runtime::{
27        bank::{Bank, BankTestConfig},
28        bank_forks::BankForks,
29        genesis_utils::{
30            GenesisConfigInfo, ValidatorVoteKeypairs, create_genesis_config_with_vote_accounts,
31        },
32    },
33    solana_signer::Signer,
34    solana_vote::vote_transaction,
35    solana_vote_program::vote_state::{Lockout, TowerSync},
36    std::{
37        collections::{HashMap, HashSet, VecDeque},
38        sync::{Arc, RwLock},
39    },
40    trees::{Tree, TreeWalk, tr},
41};
42
43pub struct VoteSimulator {
44    pub validator_keypairs: HashMap<Pubkey, ValidatorVoteKeypairs>,
45    pub node_pubkeys: Vec<Pubkey>,
46    pub vote_pubkeys: Vec<Pubkey>,
47    pub bank_forks: Arc<RwLock<BankForks>>,
48    pub progress: ProgressMap,
49    pub latest_validator_votes_for_frozen_banks: LatestValidatorVotesForFrozenBanks,
50    pub tbft_structs: TowerBFTStructures,
51}
52
53impl VoteSimulator {
54    pub fn new(num_keypairs: usize) -> Self {
55        Self::new_with(num_keypairs, BankTestConfig::default())
56    }
57
58    pub fn new_with(num_keypairs: usize, bank_test_config: BankTestConfig) -> Self {
59        let (
60            validator_keypairs,
61            node_pubkeys,
62            vote_pubkeys,
63            bank_forks,
64            progress,
65            heaviest_subtree_fork_choice,
66        ) = Self::init_state(num_keypairs, bank_test_config);
67        Self {
68            validator_keypairs,
69            node_pubkeys,
70            vote_pubkeys,
71            bank_forks,
72            progress,
73            latest_validator_votes_for_frozen_banks: LatestValidatorVotesForFrozenBanks::default(),
74            tbft_structs: TowerBFTStructures {
75                heaviest_subtree_fork_choice,
76                duplicate_slots_tracker: DuplicateSlotsTracker::default(),
77                duplicate_confirmed_slots: DuplicateConfirmedSlots::default(),
78                unfrozen_gossip_verified_vote_hashes: UnfrozenGossipVerifiedVoteHashes::default(),
79                epoch_slots_frozen_slots: EpochSlotsFrozenSlots::default(),
80            },
81        }
82    }
83
84    pub fn fill_bank_forks(
85        &mut self,
86        forks: Tree<u64>,
87        cluster_votes: &HashMap<Pubkey, Vec<u64>>,
88        is_frozen: bool,
89    ) {
90        let root = *forks.root().data();
91        assert!(self.bank_forks.read().unwrap().get(root).is_some());
92
93        let mut walk = TreeWalk::from(forks);
94
95        while let Some(visit) = walk.get() {
96            let slot = *visit.node().data();
97            if self.bank_forks.read().unwrap().get(slot).is_some() {
98                walk.forward();
99                continue;
100            }
101            let parent = *walk.get_parent().unwrap().data();
102            let parent_bank = self.bank_forks.read().unwrap().get(parent).unwrap();
103            let new_bank = Bank::new_from_parent(parent_bank.clone(), SlotLeader::default(), slot);
104            let new_bank = self
105                .bank_forks
106                .write()
107                .unwrap()
108                .insert(new_bank)
109                .clone_without_scheduler();
110            self.progress
111                .entry(slot)
112                .or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0, None));
113            for (pubkey, vote) in cluster_votes.iter() {
114                if vote.contains(&parent) {
115                    let keypairs = self.validator_keypairs.get(pubkey).unwrap();
116                    let latest_blockhash = parent_bank.last_blockhash();
117                    let tower_sync = if let Some(vote_account) =
118                        parent_bank.get_vote_account(&keypairs.vote_keypair.pubkey())
119                    {
120                        let mut vote_state = TowerVoteState::from(vote_account.vote_state_view());
121                        vote_state.process_next_vote_slot(parent);
122                        TowerSync::new(
123                            vote_state.votes,
124                            vote_state.root_slot,
125                            parent_bank.hash(),
126                            Hash::default(),
127                        )
128                    } else {
129                        TowerSync::new(
130                            VecDeque::from([Lockout::new(parent)]),
131                            Some(root),
132                            parent_bank.hash(),
133                            Hash::default(),
134                        )
135                    };
136                    let vote_tx = vote_transaction::new_tower_sync_transaction(
137                        tower_sync,
138                        latest_blockhash,
139                        &keypairs.node_keypair,
140                        &keypairs.vote_keypair,
141                        &keypairs.vote_keypair,
142                        None,
143                    );
144                    info!("voting {} {}", parent_bank.slot(), parent_bank.hash());
145                    new_bank.process_transaction(&vote_tx).unwrap();
146
147                    // Check the vote landed
148                    let vote_account = new_bank
149                        .get_vote_account(&keypairs.vote_keypair.pubkey())
150                        .unwrap();
151                    let vote_state_view = vote_account.vote_state_view();
152                    assert!(
153                        vote_state_view
154                            .votes_iter()
155                            .any(|lockout| lockout.slot() == parent)
156                    );
157                }
158            }
159
160            new_bank.fill_bank_with_ticks_for_tests();
161            if !visit.node().has_no_child() || is_frozen {
162                new_bank.set_block_id(Some(Hash::new_unique()));
163                new_bank.freeze();
164                self.progress
165                    .get_fork_stats_mut(new_bank.slot())
166                    .expect("All frozen banks must exist in the Progress map")
167                    .bank_hash = Some(new_bank.hash());
168                self.tbft_structs
169                    .heaviest_subtree_fork_choice
170                    .add_new_leaf_slot(
171                        (new_bank.slot(), new_bank.hash()),
172                        Some((new_bank.parent_slot(), new_bank.parent_hash())),
173                    );
174            }
175
176            walk.forward();
177        }
178    }
179
180    pub fn simulate_vote(
181        &mut self,
182        vote_slot: Slot,
183        my_pubkey: &Pubkey,
184        tower: &mut Tower,
185    ) -> Vec<HeaviestForkFailures> {
186        // Try to simulate the vote
187        let ancestors = self.bank_forks.read().unwrap().ancestors();
188        let mut frozen_banks: Vec<_> = self
189            .bank_forks
190            .read()
191            .unwrap()
192            .frozen_banks()
193            .map(|(_slot, bank)| bank)
194            .collect();
195        let mut vote_slots = HashSet::default();
196        let migration_status = self.bank_forks.read().unwrap().migration_status();
197        let _ = ReplayStage::compute_bank_stats(
198            my_pubkey,
199            &ancestors,
200            &mut frozen_banks,
201            tower,
202            &mut self.progress,
203            &VoteTracker::default(),
204            &ClusterSlots::default_for_tests(),
205            &self.bank_forks,
206            &mut self.tbft_structs.heaviest_subtree_fork_choice,
207            &mut self.latest_validator_votes_for_frozen_banks,
208            &mut vote_slots,
209            migration_status.as_ref(),
210        );
211
212        let vote_bank = self
213            .bank_forks
214            .read()
215            .unwrap()
216            .get(vote_slot)
217            .expect("Bank must have been created before vote simulation");
218
219        // Try to vote on the given slot
220        let descendants = self.bank_forks.read().unwrap().descendants();
221        let SelectVoteAndResetForkResult {
222            heaviest_fork_failures,
223            ..
224        } = select_vote_and_reset_forks(
225            &vote_bank,
226            None,
227            &ancestors,
228            &descendants,
229            &self.progress,
230            tower,
231            &self.latest_validator_votes_for_frozen_banks,
232            &self.tbft_structs.heaviest_subtree_fork_choice,
233        );
234
235        // Make sure this slot isn't locked out or failing threshold
236        info!("Checking vote: {}", vote_bank.slot());
237        if !heaviest_fork_failures.is_empty() {
238            return heaviest_fork_failures;
239        }
240
241        let new_root = tower.record_bank_vote(&vote_bank);
242        if let Some(new_root) = new_root {
243            self.set_root(new_root);
244        }
245
246        vec![]
247    }
248
249    pub fn set_root(&mut self, new_root: Slot) {
250        let (drop_bank_sender, _drop_bank_receiver) = bounded(1024);
251        ReplayStage::handle_new_root(
252            new_root,
253            &self.bank_forks,
254            &mut self.progress,
255            None, // snapshot_controller
256            None,
257            &mut true,
258            &mut Vec::new(),
259            &drop_bank_sender,
260            &mut self.tbft_structs,
261        )
262    }
263
264    pub fn create_and_vote_new_branch(
265        &mut self,
266        start_slot: Slot,
267        end_slot: Slot,
268        cluster_votes: &HashMap<Pubkey, Vec<u64>>,
269        votes_to_simulate: &HashSet<Slot>,
270        my_pubkey: &Pubkey,
271        tower: &mut Tower,
272    ) -> HashMap<Slot, Vec<HeaviestForkFailures>> {
273        (start_slot + 1..=end_slot)
274            .filter_map(|slot| {
275                let mut fork_tip_parent = tr(slot - 1);
276                fork_tip_parent.push_front(tr(slot));
277                self.fill_bank_forks(fork_tip_parent, cluster_votes, true);
278                if votes_to_simulate.contains(&slot) {
279                    Some((slot, self.simulate_vote(slot, my_pubkey, tower)))
280                } else {
281                    None
282                }
283            })
284            .collect()
285    }
286
287    pub fn simulate_lockout_interval(
288        &mut self,
289        slot: Slot,
290        lockout_interval: (u64, u64),
291        vote_account_pubkey: &Pubkey,
292    ) {
293        self.progress
294            .entry(slot)
295            .or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0, None))
296            .fork_stats
297            .lockout_intervals
298            .push(LockoutInterval {
299                start: lockout_interval.0,
300                end: lockout_interval.1,
301                voter: *vote_account_pubkey,
302            });
303    }
304
305    pub fn clear_lockout_intervals(&mut self, slot: Slot) {
306        self.progress
307            .entry(slot)
308            .or_insert_with(|| ForkProgress::new(Hash::default(), None, None, 0, 0, None))
309            .fork_stats
310            .lockout_intervals
311            .clear()
312    }
313
314    pub fn can_progress_on_fork(
315        &mut self,
316        my_pubkey: &Pubkey,
317        tower: &mut Tower,
318        start_slot: u64,
319        num_slots: u64,
320        cluster_votes: &mut HashMap<Pubkey, Vec<u64>>,
321    ) -> bool {
322        // Check that within some reasonable time, validator can make a new
323        // root on this fork
324        let old_root = tower.root();
325
326        for i in 1..num_slots {
327            // The parent of the tip of the fork
328            let mut fork_tip_parent = tr(start_slot + i - 1);
329            // The tip of the fork
330            fork_tip_parent.push_front(tr(start_slot + i));
331            self.fill_bank_forks(fork_tip_parent, cluster_votes, true);
332            if self
333                .simulate_vote(i + start_slot, my_pubkey, tower)
334                .is_empty()
335            {
336                cluster_votes
337                    .entry(*my_pubkey)
338                    .or_default()
339                    .push(start_slot + i);
340            }
341            if old_root != tower.root() {
342                return true;
343            }
344        }
345
346        false
347    }
348
349    #[allow(clippy::type_complexity)]
350    fn init_state(
351        num_keypairs: usize,
352        bank_test_config: BankTestConfig,
353    ) -> (
354        HashMap<Pubkey, ValidatorVoteKeypairs>,
355        Vec<Pubkey>,
356        Vec<Pubkey>,
357        Arc<RwLock<BankForks>>,
358        ProgressMap,
359        HeaviestSubtreeForkChoice,
360    ) {
361        let keypairs: HashMap<_, _> = std::iter::repeat_with(|| {
362            let vote_keypairs = ValidatorVoteKeypairs::new_rand();
363            (vote_keypairs.node_keypair.pubkey(), vote_keypairs)
364        })
365        .take(num_keypairs)
366        .collect();
367        let node_pubkeys: Vec<_> = keypairs
368            .values()
369            .map(|keys| keys.node_keypair.pubkey())
370            .collect();
371        let vote_pubkeys: Vec<_> = keypairs
372            .values()
373            .map(|keys| keys.vote_keypair.pubkey())
374            .collect();
375
376        let (bank_forks, progress, heaviest_subtree_fork_choice) =
377            initialize_state_with(&keypairs, 10_000, bank_test_config);
378        (
379            keypairs,
380            node_pubkeys,
381            vote_pubkeys,
382            bank_forks,
383            progress,
384            heaviest_subtree_fork_choice,
385        )
386    }
387}
388
389// Setup BankForks with bank 0 and all the validator accounts
390pub fn initialize_state(
391    validator_keypairs_map: &HashMap<Pubkey, ValidatorVoteKeypairs>,
392    stake: u64,
393) -> (
394    Arc<RwLock<BankForks>>,
395    ProgressMap,
396    HeaviestSubtreeForkChoice,
397) {
398    initialize_state_with(validator_keypairs_map, stake, BankTestConfig::default())
399}
400
401pub fn initialize_state_with(
402    validator_keypairs_map: &HashMap<Pubkey, ValidatorVoteKeypairs>,
403    stake: u64,
404    bank_test_config: BankTestConfig,
405) -> (
406    Arc<RwLock<BankForks>>,
407    ProgressMap,
408    HeaviestSubtreeForkChoice,
409) {
410    let validator_keypairs: Vec<_> = validator_keypairs_map.values().collect();
411    let GenesisConfigInfo {
412        mut genesis_config,
413        mint_keypair,
414        ..
415    } = create_genesis_config_with_vote_accounts(
416        1_000_000_000,
417        &validator_keypairs,
418        vec![stake; validator_keypairs.len()],
419    );
420
421    genesis_config.epoch_schedule = EpochSchedule::without_warmup();
422    genesis_config.poh_config.hashes_per_tick = Some(4);
423    let (bank0, bank_forks) =
424        Bank::new_with_paths_for_tests(&genesis_config, Some(bank_test_config), vec![], None)
425            .wrap_with_bank_forks_for_tests();
426    bank0.set_block_id(Some(Hash::new_unique()));
427
428    for pubkey in validator_keypairs_map.keys() {
429        bank0.transfer(10_000, &mint_keypair, pubkey).unwrap();
430    }
431
432    bank0.fill_bank_with_ticks_for_tests();
433    bank0.freeze();
434    let mut progress = ProgressMap::default();
435    progress.insert(
436        0,
437        ForkProgress::new_from_bank(
438            &bank0,
439            bank0.leader_id(),
440            &Pubkey::default(),
441            None,
442            0,
443            0,
444            None,
445        ),
446    );
447    let heaviest_subtree_fork_choice =
448        HeaviestSubtreeForkChoice::new_from_bank_forks(bank_forks.clone());
449    (bank_forks, progress, heaviest_subtree_fork_choice)
450}