| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | use rayon::iter::ParallelIterator;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | use rayon::prelude::*;
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:35:03 -07:00
										 |  |  | use serial_test_derive::serial;
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:23:33 -07:00
										 |  |  | use solana_core::cluster_info::{compute_retransmit_peers, ClusterInfo};
 | 
					
						
							|  |  |  | use solana_core::contact_info::ContactInfo;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | use solana_sdk::pubkey::Pubkey;
 | 
					
						
							| 
									
										
										
										
											2019-05-30 21:31:35 -07:00
										 |  |  | use std::collections::{HashMap, HashSet};
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | use std::sync::mpsc::channel;
 | 
					
						
							|  |  |  | use std::sync::mpsc::TryRecvError;
 | 
					
						
							|  |  |  | use std::sync::mpsc::{Receiver, Sender};
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | use std::sync::Arc;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | use std::sync::Mutex;
 | 
					
						
							|  |  |  | use std::time::Instant;
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | type Nodes = HashMap<Pubkey, (bool, HashSet<i32>, Receiver<(i32, bool)>)>;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | fn num_threads() -> usize {
 | 
					
						
							| 
									
										
										
										
											2020-03-16 12:53:13 -07:00
										 |  |  |     num_cpus::get()
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Search for the a node with the given balance
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  | fn find_insert_shred(id: &Pubkey, shred: i32, batches: &mut [Nodes]) {
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     batches.par_iter_mut().for_each(|batch| {
 | 
					
						
							|  |  |  |         if batch.contains_key(id) {
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |             let _ = batch.get_mut(id).unwrap().1.insert(shred);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |         }
 | 
					
						
							|  |  |  |     });
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | fn retransmit(
 | 
					
						
							|  |  |  |     mut shuffled_nodes: Vec<ContactInfo>,
 | 
					
						
							|  |  |  |     senders: &HashMap<Pubkey, Sender<(i32, bool)>>,
 | 
					
						
							|  |  |  |     cluster: &ClusterInfo,
 | 
					
						
							|  |  |  |     fanout: usize,
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |     shred: i32,
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |     retransmit: bool,
 | 
					
						
							|  |  |  | ) -> i32 {
 | 
					
						
							|  |  |  |     let mut seed = [0; 32];
 | 
					
						
							|  |  |  |     let mut my_index = 0;
 | 
					
						
							|  |  |  |     let mut index = 0;
 | 
					
						
							|  |  |  |     shuffled_nodes.retain(|c| {
 | 
					
						
							|  |  |  |         if c.id == cluster.id() {
 | 
					
						
							|  |  |  |             my_index = index;
 | 
					
						
							|  |  |  |             false
 | 
					
						
							|  |  |  |         } else {
 | 
					
						
							|  |  |  |             index += 1;
 | 
					
						
							|  |  |  |             true
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     });
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |     seed[0..4].copy_from_slice(&shred.to_le_bytes());
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |     let shuffled_indices = (0..shuffled_nodes.len()).collect();
 | 
					
						
							|  |  |  |     let (neighbors, children) = compute_retransmit_peers(fanout, my_index, shuffled_indices);
 | 
					
						
							|  |  |  |     children.into_iter().for_each(|i| {
 | 
					
						
							|  |  |  |         let s = senders.get(&shuffled_nodes[i].id).unwrap();
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |         let _ = s.send((shred, retransmit));
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |     });
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if retransmit {
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |         neighbors.into_iter().for_each(|i| {
 | 
					
						
							|  |  |  |             let s = senders.get(&shuffled_nodes[i].id).unwrap();
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |             let _ = s.send((shred, false));
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         });
 | 
					
						
							|  |  |  |     }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |     shred
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 17:35:43 +01:00
										 |  |  | #[allow(clippy::type_complexity)]
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  | fn run_simulation(stakes: &[u64], fanout: usize) {
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     let num_threads = num_threads();
 | 
					
						
							|  |  |  |     // set timeout to 5 minutes
 | 
					
						
							|  |  |  |     let timeout = 60 * 5;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // describe the leader
 | 
					
						
							| 
									
										
										
										
											2019-03-30 21:37:33 -06:00
										 |  |  |     let leader_info = ContactInfo::new_localhost(&Pubkey::new_rand(), 0);
 | 
					
						
							| 
									
										
										
										
											2020-04-21 12:54:45 -07:00
										 |  |  |     let cluster_info = ClusterInfo::new_with_invalid_keypair(leader_info.clone());
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  |     // setup staked nodes
 | 
					
						
							|  |  |  |     let mut staked_nodes = HashMap::new();
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // setup accounts for all nodes (leader has 0 bal)
 | 
					
						
							|  |  |  |     let (s, r) = channel();
 | 
					
						
							|  |  |  |     let senders: Arc<Mutex<HashMap<Pubkey, Sender<(i32, bool)>>>> =
 | 
					
						
							|  |  |  |         Arc::new(Mutex::new(HashMap::new()));
 | 
					
						
							|  |  |  |     senders.lock().unwrap().insert(leader_info.id, s);
 | 
					
						
							|  |  |  |     let mut batches: Vec<Nodes> = Vec::with_capacity(num_threads);
 | 
					
						
							|  |  |  |     (0..num_threads).for_each(|_| batches.push(HashMap::new()));
 | 
					
						
							|  |  |  |     batches
 | 
					
						
							|  |  |  |         .get_mut(0)
 | 
					
						
							|  |  |  |         .unwrap()
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         .insert(leader_info.id, (false, HashSet::new(), r));
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  |     let range: Vec<_> = (1..=stakes.len()).collect();
 | 
					
						
							|  |  |  |     let chunk_size = (stakes.len() + num_threads - 1) / num_threads;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     range.chunks(chunk_size).for_each(|chunk| {
 | 
					
						
							| 
									
										
										
										
											2020-05-15 17:35:43 +01:00
										 |  |  |         chunk.iter().for_each(|i| {
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |             //distribute neighbors across threads to maximize parallel compute
 | 
					
						
							|  |  |  |             let batch_ix = *i as usize % batches.len();
 | 
					
						
							| 
									
										
										
										
											2019-03-30 21:37:33 -06:00
										 |  |  |             let node = ContactInfo::new_localhost(&Pubkey::new_rand(), 0);
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  |             staked_nodes.insert(node.id, stakes[*i - 1]);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |             cluster_info.insert_info(node.clone());
 | 
					
						
							|  |  |  |             let (s, r) = channel();
 | 
					
						
							|  |  |  |             batches
 | 
					
						
							|  |  |  |                 .get_mut(batch_ix)
 | 
					
						
							|  |  |  |                 .unwrap()
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |                 .insert(node.id, (false, HashSet::new(), r));
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |             senders.lock().unwrap().insert(node.id, s);
 | 
					
						
							|  |  |  |         })
 | 
					
						
							|  |  |  |     });
 | 
					
						
							| 
									
										
										
										
											2020-04-21 12:54:45 -07:00
										 |  |  |     let c_info = cluster_info.clone_with_id(&cluster_info.id());
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 17:11:18 -08:00
										 |  |  |     let staked_nodes = Arc::new(staked_nodes);
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |     let shreds_len = 100;
 | 
					
						
							|  |  |  |     let shuffled_peers: Vec<Vec<ContactInfo>> = (0..shreds_len as i32)
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         .map(|i| {
 | 
					
						
							|  |  |  |             let mut seed = [0; 32];
 | 
					
						
							|  |  |  |             seed[0..4].copy_from_slice(&i.to_le_bytes());
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |             let (peers, stakes_and_index) =
 | 
					
						
							| 
									
										
										
										
											2019-12-16 17:11:18 -08:00
										 |  |  |                 cluster_info.sorted_retransmit_peers_and_stakes(Some(staked_nodes.clone()));
 | 
					
						
							| 
									
										
										
										
											2019-10-08 14:41:16 -07:00
										 |  |  |             let (_, shuffled_stakes_and_indexes) = ClusterInfo::shuffle_peers_and_index(
 | 
					
						
							|  |  |  |                 &cluster_info.id(),
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |                 &peers,
 | 
					
						
							|  |  |  |                 &stakes_and_index,
 | 
					
						
							| 
									
										
										
										
											2019-10-30 13:41:11 -07:00
										 |  |  |                 seed,
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |             );
 | 
					
						
							| 
									
										
										
										
											2020-05-15 17:35:43 +01:00
										 |  |  |             shuffled_stakes_and_indexes
 | 
					
						
							| 
									
										
										
										
											2019-10-04 11:52:02 -07:00
										 |  |  |                 .into_iter()
 | 
					
						
							|  |  |  |                 .map(|(_, i)| peers[i].clone())
 | 
					
						
							| 
									
										
										
										
											2020-05-15 17:35:43 +01:00
										 |  |  |                 .collect()
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |         })
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         .collect();
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |     // create some "shreds".
 | 
					
						
							| 
									
										
										
										
											2020-05-15 17:35:43 +01:00
										 |  |  |     (0..shreds_len).for_each(|i| {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         let broadcast_table = &shuffled_peers[i];
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |         find_insert_shred(&broadcast_table[0].id, i as i32, &mut batches);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     });
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     assert!(!batches.is_empty());
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-06 12:48:40 -07:00
										 |  |  |     // start turbine simulation
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |     let now = Instant::now();
 | 
					
						
							|  |  |  |     batches.par_iter_mut().for_each(|batch| {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |         let mut remaining = batch.len();
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |         let senders: HashMap<_, _> = senders.lock().unwrap().clone();
 | 
					
						
							|  |  |  |         while remaining > 0 {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |             for (id, (layer1_done, recv, r)) in batch.iter_mut() {
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |                 assert!(
 | 
					
						
							|  |  |  |                     now.elapsed().as_secs() < timeout,
 | 
					
						
							|  |  |  |                     "Timed out with {:?} remaining nodes",
 | 
					
						
							|  |  |  |                     remaining
 | 
					
						
							|  |  |  |                 );
 | 
					
						
							| 
									
										
										
										
											2020-04-21 12:54:45 -07:00
										 |  |  |                 let cluster = c_info.clone_with_id(id);
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |                 if !*layer1_done {
 | 
					
						
							|  |  |  |                     recv.iter().for_each(|i| {
 | 
					
						
							|  |  |  |                         retransmit(
 | 
					
						
							|  |  |  |                             shuffled_peers[*i as usize].clone(),
 | 
					
						
							|  |  |  |                             &senders,
 | 
					
						
							|  |  |  |                             &cluster,
 | 
					
						
							|  |  |  |                             fanout,
 | 
					
						
							|  |  |  |                             *i,
 | 
					
						
							|  |  |  |                             true,
 | 
					
						
							|  |  |  |                         );
 | 
					
						
							|  |  |  |                     });
 | 
					
						
							|  |  |  |                     *layer1_done = true;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |                 }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 //send and recv
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |                 if recv.len() < shreds_len {
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |                     loop {
 | 
					
						
							|  |  |  |                         match r.try_recv() {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |                             Ok((data, retx)) => {
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |                                 if recv.insert(data) {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |                                     let _ = retransmit(
 | 
					
						
							|  |  |  |                                         shuffled_peers[data as usize].clone(),
 | 
					
						
							|  |  |  |                                         &senders,
 | 
					
						
							|  |  |  |                                         &cluster,
 | 
					
						
							|  |  |  |                                         fanout,
 | 
					
						
							|  |  |  |                                         data,
 | 
					
						
							|  |  |  |                                         retx,
 | 
					
						
							|  |  |  |                                     );
 | 
					
						
							|  |  |  |                                 }
 | 
					
						
							| 
									
										
										
										
											2019-11-14 11:49:31 -08:00
										 |  |  |                                 if recv.len() == shreds_len {
 | 
					
						
							| 
									
										
										
										
											2019-06-03 20:38:05 -07:00
										 |  |  |                                     remaining -= 1;
 | 
					
						
							|  |  |  |                                     break;
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  |                                 }
 | 
					
						
							|  |  |  |                             }
 | 
					
						
							|  |  |  |                             Err(TryRecvError::Disconnected) => break,
 | 
					
						
							|  |  |  |                             Err(TryRecvError::Empty) => break,
 | 
					
						
							|  |  |  |                         };
 | 
					
						
							|  |  |  |                     }
 | 
					
						
							|  |  |  |                 }
 | 
					
						
							|  |  |  |             }
 | 
					
						
							|  |  |  |         }
 | 
					
						
							|  |  |  |     });
 | 
					
						
							|  |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Recommended to not run these tests in parallel (they are resource heavy and want all the compute)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //todo add tests with network failures
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Run with a single layer
 | 
					
						
							|  |  |  | #[test]
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:35:03 -07:00
										 |  |  | #[serial]
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | fn test_retransmit_small() {
 | 
					
						
							| 
									
										
										
										
											2020-08-01 08:44:32 -07:00
										 |  |  |     let stakes: Vec<_> = (0..200).collect();
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     run_simulation(&stakes, 200);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Make sure at least 2 layers are used
 | 
					
						
							|  |  |  | #[test]
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:35:03 -07:00
										 |  |  | #[serial]
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | fn test_retransmit_medium() {
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     let num_nodes = 2000;
 | 
					
						
							| 
									
										
										
										
											2020-08-01 08:44:32 -07:00
										 |  |  |     let stakes: Vec<_> = (0..num_nodes).collect();
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     run_simulation(&stakes, 200);
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Make sure at least 2 layers are used but with equal stakes
 | 
					
						
							|  |  |  | #[test]
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:35:03 -07:00
										 |  |  | #[serial]
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  | fn test_retransmit_medium_equal_stakes() {
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     let num_nodes = 2000;
 | 
					
						
							| 
									
										
										
										
											2019-04-19 21:07:21 -07:00
										 |  |  |     let stakes: Vec<_> = (0..num_nodes).map(|_| 10).collect();
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     run_simulation(&stakes, 200);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | }
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  | // Scale down the network and make sure many layers are used
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | #[test]
 | 
					
						
							| 
									
										
										
										
											2019-07-02 17:35:03 -07:00
										 |  |  | #[serial]
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | fn test_retransmit_large() {
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     let num_nodes = 4000;
 | 
					
						
							| 
									
										
										
										
											2020-08-01 08:44:32 -07:00
										 |  |  |     let stakes: Vec<_> = (0..num_nodes).collect();
 | 
					
						
							| 
									
										
										
										
											2019-05-07 13:24:58 -07:00
										 |  |  |     run_simulation(&stakes, 2);
 | 
					
						
							| 
									
										
										
										
											2019-02-18 09:46:30 -07:00
										 |  |  | }
 |