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 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 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 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 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, 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 let old_root = tower.root();
325
326 for i in 1..num_slots {
327 let mut fork_tip_parent = tr(start_slot + i - 1);
329 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
389pub 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}