2019-04-14 18:52:05 -07:00
|
|
|
//! `window_service` handles the data plane incoming blobs, storing them in
|
|
|
|
//! blocktree and retransmitting where required
|
2018-09-07 16:00:26 -06:00
|
|
|
//!
|
2019-02-07 20:52:39 -08:00
|
|
|
use crate::blocktree::Blocktree;
|
2018-12-07 20:16:27 -07:00
|
|
|
use crate::cluster_info::ClusterInfo;
|
2019-04-22 18:41:01 -07:00
|
|
|
use crate::leader_schedule_cache::LeaderScheduleCache;
|
2019-04-22 15:21:10 -07:00
|
|
|
use crate::packet::{Blob, SharedBlob, BLOB_HEADER_SIZE};
|
2019-05-09 14:10:04 -07:00
|
|
|
use crate::repair_service::{RepairService, RepairStrategy};
|
2018-12-07 20:16:27 -07:00
|
|
|
use crate::result::{Error, Result};
|
2019-02-07 15:10:54 -08:00
|
|
|
use crate::service::Service;
|
2018-12-07 20:16:27 -07:00
|
|
|
use crate::streamer::{BlobReceiver, BlobSender};
|
2019-06-12 16:43:05 -07:00
|
|
|
use rayon::prelude::*;
|
|
|
|
use rayon::ThreadPool;
|
2019-05-17 07:00:06 -07:00
|
|
|
use solana_metrics::{inc_new_counter_debug, inc_new_counter_error};
|
2019-04-22 15:21:10 -07:00
|
|
|
use solana_runtime::bank::Bank;
|
2018-10-25 11:13:08 -07:00
|
|
|
use solana_sdk::pubkey::Pubkey;
|
2019-06-12 16:43:05 -07:00
|
|
|
use solana_sdk::signature::Signable;
|
2018-11-16 08:45:59 -08:00
|
|
|
use solana_sdk::timing::duration_as_ms;
|
2018-09-07 16:00:26 -06:00
|
|
|
use std::net::UdpSocket;
|
2019-02-13 20:04:20 -08:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2018-09-07 16:00:26 -06:00
|
|
|
use std::sync::mpsc::RecvTimeoutError;
|
|
|
|
use std::sync::{Arc, RwLock};
|
2019-02-07 15:10:54 -08:00
|
|
|
use std::thread::{self, Builder, JoinHandle};
|
2018-09-07 16:00:26 -06:00
|
|
|
use std::time::{Duration, Instant};
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
pub const NUM_THREADS: u32 = 10;
|
|
|
|
|
2019-04-14 18:52:05 -07:00
|
|
|
fn retransmit_blobs(blobs: &[SharedBlob], retransmit: &BlobSender, id: &Pubkey) -> Result<()> {
|
|
|
|
let mut retransmit_queue: Vec<SharedBlob> = Vec::new();
|
|
|
|
for blob in blobs {
|
|
|
|
// Don't add blobs generated by this node to the retransmit queue
|
|
|
|
if blob.read().unwrap().id() != *id {
|
2019-04-20 16:44:06 -07:00
|
|
|
let mut w_blob = blob.write().unwrap();
|
|
|
|
w_blob.meta.forward = w_blob.should_forward();
|
|
|
|
w_blob.set_forwarded(false);
|
2019-04-14 18:52:05 -07:00
|
|
|
retransmit_queue.push(blob.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !retransmit_queue.is_empty() {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_debug!(
|
2019-04-26 20:37:40 -07:00
|
|
|
"streamer-recv_window-retransmit",
|
|
|
|
retransmit_queue.len(),
|
|
|
|
0,
|
|
|
|
1000
|
|
|
|
);
|
2019-04-14 18:52:05 -07:00
|
|
|
retransmit.send(retransmit_queue)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-09-07 16:00:26 -06:00
|
|
|
|
2019-04-14 18:52:05 -07:00
|
|
|
/// Process a blob: Add blob to the ledger window.
|
|
|
|
fn process_blobs(blobs: &[SharedBlob], blocktree: &Arc<Blocktree>) -> Result<()> {
|
|
|
|
// make an iterator for insert_data_blobs()
|
|
|
|
let blobs: Vec<_> = blobs.iter().map(move |blob| blob.read().unwrap()).collect();
|
|
|
|
|
|
|
|
blocktree.insert_data_blobs(blobs.iter().filter_map(|blob| {
|
|
|
|
if !blob.is_coding() {
|
|
|
|
Some(&(**blob))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}))?;
|
|
|
|
|
|
|
|
for blob in blobs {
|
|
|
|
// TODO: Once the original leader signature is added to the blob, make sure that
|
|
|
|
// the blob was originally generated by the expected leader for this slot
|
|
|
|
|
|
|
|
// Insert the new blob into block tree
|
|
|
|
if blob.is_coding() {
|
|
|
|
blocktree.put_coding_blob_bytes(
|
|
|
|
blob.slot(),
|
|
|
|
blob.index(),
|
|
|
|
&blob.data[..BLOB_HEADER_SIZE + blob.size()],
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
2018-09-25 15:41:29 -07:00
|
|
|
}
|
|
|
|
|
2019-04-22 15:21:10 -07:00
|
|
|
/// drop blobs that are from myself or not from the correct leader for the
|
2019-05-13 21:19:51 -07:00
|
|
|
/// blob's slot
|
|
|
|
pub fn should_retransmit_and_persist(
|
2019-04-22 18:41:01 -07:00
|
|
|
blob: &Blob,
|
2019-05-13 21:19:51 -07:00
|
|
|
bank: Option<Arc<Bank>>,
|
|
|
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
2019-05-23 23:20:04 -07:00
|
|
|
my_pubkey: &Pubkey,
|
2019-04-22 18:41:01 -07:00
|
|
|
) -> bool {
|
2019-05-23 23:20:04 -07:00
|
|
|
let slot_leader_pubkey = match bank {
|
2019-05-13 21:19:51 -07:00
|
|
|
None => leader_schedule_cache.slot_leader_at(blob.slot(), None),
|
|
|
|
Some(bank) => leader_schedule_cache.slot_leader_at(blob.slot(), Some(&bank)),
|
2019-04-22 18:41:01 -07:00
|
|
|
};
|
2019-04-22 15:21:10 -07:00
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
if !blob.verify() {
|
|
|
|
inc_new_counter_debug!("streamer-recv_window-invalid_signature", 1);
|
|
|
|
false
|
|
|
|
} else if blob.id() == *my_pubkey {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_debug!("streamer-recv_window-circular_transmission", 1);
|
2019-04-22 15:21:10 -07:00
|
|
|
false
|
2019-05-23 23:20:04 -07:00
|
|
|
} else if slot_leader_pubkey == None {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_debug!("streamer-recv_window-unknown_leader", 1);
|
2019-05-24 19:20:09 -07:00
|
|
|
false
|
2019-05-23 23:20:04 -07:00
|
|
|
} else if slot_leader_pubkey != Some(blob.id()) {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_debug!("streamer-recv_window-wrong_leader", 1);
|
2019-04-22 15:21:10 -07:00
|
|
|
false
|
|
|
|
} else {
|
2019-05-24 19:20:09 -07:00
|
|
|
// At this point, slot_leader_id == blob.id() && blob.id() != *my_id, so
|
|
|
|
// the blob is valid to process
|
2019-04-22 15:21:10 -07:00
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-13 21:19:51 -07:00
|
|
|
fn recv_window<F>(
|
2019-02-07 20:52:39 -08:00
|
|
|
blocktree: &Arc<Blocktree>,
|
2019-05-23 23:20:04 -07:00
|
|
|
my_pubkey: &Pubkey,
|
2018-09-07 16:00:26 -06:00
|
|
|
r: &BlobReceiver,
|
|
|
|
retransmit: &BlobSender,
|
2019-05-13 21:19:51 -07:00
|
|
|
blob_filter: F,
|
2019-06-12 16:43:05 -07:00
|
|
|
thread_pool: &ThreadPool,
|
2019-05-13 21:19:51 -07:00
|
|
|
) -> Result<()>
|
|
|
|
where
|
|
|
|
F: Fn(&Blob) -> bool,
|
2019-06-12 16:43:05 -07:00
|
|
|
F: Sync,
|
2019-05-13 21:19:51 -07:00
|
|
|
{
|
2018-09-07 16:00:26 -06:00
|
|
|
let timer = Duration::from_millis(200);
|
2019-04-22 15:21:10 -07:00
|
|
|
let mut blobs = r.recv_timeout(timer)?;
|
2018-11-24 19:32:33 -08:00
|
|
|
|
2019-04-22 15:21:10 -07:00
|
|
|
while let Ok(mut blob) = r.try_recv() {
|
|
|
|
blobs.append(&mut blob)
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
let now = Instant::now();
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_debug!("streamer-recv_window-recv", blobs.len(), 0, 1000);
|
2018-10-16 12:54:23 -07:00
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
let blobs: Vec<_> = thread_pool.install(|| {
|
|
|
|
blobs
|
|
|
|
.into_par_iter()
|
|
|
|
.filter(|b| blob_filter(&b.read().unwrap()))
|
|
|
|
.collect()
|
|
|
|
});
|
2018-10-16 12:54:23 -07:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
retransmit_blobs(&blobs, retransmit, my_pubkey)?;
|
2018-09-07 16:00:26 -06:00
|
|
|
|
2019-05-23 23:20:04 -07:00
|
|
|
trace!("{} num blobs received: {}", my_pubkey, blobs.len());
|
2018-11-24 19:32:33 -08:00
|
|
|
|
2019-04-22 15:21:10 -07:00
|
|
|
process_blobs(&blobs, blocktree)?;
|
2018-11-24 19:32:33 -08:00
|
|
|
|
|
|
|
trace!(
|
|
|
|
"Elapsed processing time in recv_window(): {}",
|
|
|
|
duration_as_ms(&now.elapsed())
|
|
|
|
);
|
|
|
|
|
2018-09-07 16:00:26 -06:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-02-07 15:10:54 -08:00
|
|
|
// Implement a destructor for the window_service thread to signal it exited
|
|
|
|
// even on panics
|
|
|
|
struct Finalizer {
|
|
|
|
exit_sender: Arc<AtomicBool>,
|
|
|
|
}
|
2018-11-24 19:32:33 -08:00
|
|
|
|
2019-02-07 15:10:54 -08:00
|
|
|
impl Finalizer {
|
|
|
|
fn new(exit_sender: Arc<AtomicBool>) -> Self {
|
|
|
|
Finalizer { exit_sender }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Implement a destructor for Finalizer.
|
|
|
|
impl Drop for Finalizer {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.exit_sender.clone().store(true, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
}
|
2018-11-24 19:32:33 -08:00
|
|
|
|
2019-02-07 15:10:54 -08:00
|
|
|
pub struct WindowService {
|
|
|
|
t_window: JoinHandle<()>,
|
|
|
|
repair_service: RepairService,
|
|
|
|
}
|
2018-09-07 16:00:26 -06:00
|
|
|
|
2019-02-07 15:10:54 -08:00
|
|
|
impl WindowService {
|
2019-04-23 16:24:44 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-05-13 21:19:51 -07:00
|
|
|
pub fn new<F>(
|
2019-02-07 20:52:39 -08:00
|
|
|
blocktree: Arc<Blocktree>,
|
2019-02-07 15:10:54 -08:00
|
|
|
cluster_info: Arc<RwLock<ClusterInfo>>,
|
|
|
|
r: BlobReceiver,
|
|
|
|
retransmit: BlobSender,
|
|
|
|
repair_socket: Arc<UdpSocket>,
|
2019-03-04 20:50:02 -08:00
|
|
|
exit: &Arc<AtomicBool>,
|
2019-05-09 14:10:04 -07:00
|
|
|
repair_strategy: RepairStrategy,
|
2019-05-13 21:19:51 -07:00
|
|
|
blob_filter: F,
|
|
|
|
) -> WindowService
|
|
|
|
where
|
|
|
|
F: 'static
|
|
|
|
+ Fn(&Pubkey, &Blob, Option<Arc<Bank>>) -> bool
|
|
|
|
+ std::marker::Send
|
|
|
|
+ std::marker::Sync,
|
|
|
|
{
|
2019-05-09 14:10:04 -07:00
|
|
|
let bank_forks = match repair_strategy {
|
|
|
|
RepairStrategy::RepairRange(_) => None,
|
|
|
|
|
|
|
|
RepairStrategy::RepairAll { ref bank_forks, .. } => Some(bank_forks.clone()),
|
|
|
|
};
|
|
|
|
|
2019-02-12 17:43:45 -08:00
|
|
|
let repair_service = RepairService::new(
|
|
|
|
blocktree.clone(),
|
2019-05-24 19:20:09 -07:00
|
|
|
exit.clone(),
|
2019-02-12 17:43:45 -08:00
|
|
|
repair_socket,
|
|
|
|
cluster_info.clone(),
|
2019-05-09 14:10:04 -07:00
|
|
|
repair_strategy,
|
2019-02-12 17:43:45 -08:00
|
|
|
);
|
2019-03-04 20:50:02 -08:00
|
|
|
let exit = exit.clone();
|
2019-05-13 21:19:51 -07:00
|
|
|
let blob_filter = Arc::new(blob_filter);
|
|
|
|
let bank_forks = bank_forks.clone();
|
2019-02-07 15:10:54 -08:00
|
|
|
let t_window = Builder::new()
|
|
|
|
.name("solana-window".to_string())
|
|
|
|
.spawn(move || {
|
2019-03-04 20:50:02 -08:00
|
|
|
let _exit = Finalizer::new(exit.clone());
|
2019-02-07 15:10:54 -08:00
|
|
|
let id = cluster_info.read().unwrap().id();
|
|
|
|
trace!("{}: RECV_WINDOW started", id);
|
2019-06-12 16:43:05 -07:00
|
|
|
let thread_pool = rayon::ThreadPoolBuilder::new()
|
|
|
|
.num_threads(sys_info::cpu_num().unwrap_or(NUM_THREADS) as usize)
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
2019-02-07 15:10:54 -08:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
2018-11-24 19:32:33 -08:00
|
|
|
}
|
2019-05-09 14:10:04 -07:00
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
if let Err(e) = recv_window(
|
|
|
|
&blocktree,
|
|
|
|
&id,
|
|
|
|
&r,
|
|
|
|
&retransmit,
|
|
|
|
|blob| {
|
|
|
|
blob_filter(
|
|
|
|
&id,
|
|
|
|
blob,
|
|
|
|
bank_forks
|
|
|
|
.as_ref()
|
|
|
|
.map(|bank_forks| bank_forks.read().unwrap().working_bank()),
|
|
|
|
)
|
|
|
|
},
|
|
|
|
&thread_pool,
|
|
|
|
) {
|
2019-02-07 15:10:54 -08:00
|
|
|
match e {
|
|
|
|
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
|
|
|
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
|
|
|
_ => {
|
2019-05-17 07:00:06 -07:00
|
|
|
inc_new_counter_error!("streamer-window-error", 1, 1);
|
2019-02-07 15:10:54 -08:00
|
|
|
error!("window error: {:?}", e);
|
|
|
|
}
|
2018-11-24 19:32:33 -08:00
|
|
|
}
|
|
|
|
}
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
2019-02-07 15:10:54 -08:00
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
WindowService {
|
|
|
|
t_window,
|
|
|
|
repair_service,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Service for WindowService {
|
|
|
|
type JoinReturnType = ();
|
|
|
|
|
|
|
|
fn join(self) -> thread::Result<()> {
|
|
|
|
self.t_window.join()?;
|
|
|
|
self.repair_service.join()
|
|
|
|
}
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2019-04-14 18:52:05 -07:00
|
|
|
use super::*;
|
2019-04-22 15:21:10 -07:00
|
|
|
use crate::bank_forks::BankForks;
|
2019-04-14 18:52:05 -07:00
|
|
|
use crate::blocktree::{get_tmp_ledger_path, Blocktree};
|
2018-12-07 20:16:27 -07:00
|
|
|
use crate::cluster_info::{ClusterInfo, Node};
|
2019-06-12 16:43:05 -07:00
|
|
|
use crate::entry::{make_consecutive_blobs, make_tiny_test_entries, Entry, EntrySlice};
|
2019-05-07 11:16:22 -07:00
|
|
|
use crate::genesis_utils::create_genesis_block_with_leader;
|
2019-06-12 16:43:05 -07:00
|
|
|
use crate::packet::index_blobs;
|
2019-02-07 15:10:54 -08:00
|
|
|
use crate::service::Service;
|
2018-12-07 20:16:27 -07:00
|
|
|
use crate::streamer::{blob_receiver, responder};
|
2019-06-14 11:38:37 -07:00
|
|
|
use solana_runtime::epoch_schedule::MINIMUM_SLOTS_PER_EPOCH;
|
2018-11-16 08:04:46 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-06-12 16:43:05 -07:00
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
2018-11-24 19:32:33 -08:00
|
|
|
use std::fs::remove_dir_all;
|
2018-09-25 15:41:29 -07:00
|
|
|
use std::net::UdpSocket;
|
2018-09-07 16:00:26 -06:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2019-02-04 15:33:43 -08:00
|
|
|
use std::sync::mpsc::channel;
|
2018-09-07 16:00:26 -06:00
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
use std::time::Duration;
|
2018-09-21 16:01:24 -07:00
|
|
|
|
2019-04-14 18:52:05 -07:00
|
|
|
#[test]
|
|
|
|
fn test_process_blob() {
|
|
|
|
let blocktree_path = get_tmp_ledger_path!();
|
|
|
|
let blocktree = Arc::new(Blocktree::open(&blocktree_path).unwrap());
|
|
|
|
let num_entries = 10;
|
|
|
|
let original_entries = make_tiny_test_entries(num_entries);
|
|
|
|
let shared_blobs = original_entries.clone().to_shared_blobs();
|
|
|
|
|
|
|
|
index_blobs(&shared_blobs, &Pubkey::new_rand(), 0, 0, 0);
|
|
|
|
|
|
|
|
for blob in shared_blobs.into_iter().rev() {
|
|
|
|
process_blobs(&[blob], &blocktree).expect("Expect successful processing of blob");
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
blocktree.get_slot_entries(0, 0, None).unwrap(),
|
|
|
|
original_entries
|
|
|
|
);
|
|
|
|
|
|
|
|
drop(blocktree);
|
|
|
|
Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction");
|
|
|
|
}
|
|
|
|
|
2019-04-22 15:21:10 -07:00
|
|
|
#[test]
|
|
|
|
fn test_should_retransmit_and_persist() {
|
|
|
|
let me_id = Pubkey::new_rand();
|
2019-06-12 16:43:05 -07:00
|
|
|
let leader_keypair = Keypair::new();
|
|
|
|
let leader_pubkey = leader_keypair.pubkey();
|
2019-04-22 15:21:10 -07:00
|
|
|
let bank = Arc::new(Bank::new(
|
2019-05-23 23:20:04 -07:00
|
|
|
&create_genesis_block_with_leader(100, &leader_pubkey, 10).genesis_block,
|
2019-04-22 15:21:10 -07:00
|
|
|
));
|
2019-04-22 18:41:01 -07:00
|
|
|
let cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
2019-04-22 15:21:10 -07:00
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
let entry = Entry::default();
|
|
|
|
let mut blob = entry.to_blob();
|
2019-05-23 23:20:04 -07:00
|
|
|
blob.set_id(&leader_pubkey);
|
2019-06-12 16:43:05 -07:00
|
|
|
blob.sign(&leader_keypair);
|
2019-04-22 15:21:10 -07:00
|
|
|
|
2019-05-24 19:20:09 -07:00
|
|
|
// without a Bank and blobs not from me, blob gets thrown out
|
2019-04-22 18:41:01 -07:00
|
|
|
assert_eq!(
|
2019-05-13 21:19:51 -07:00
|
|
|
should_retransmit_and_persist(&blob, None, &cache, &me_id),
|
2019-05-24 19:20:09 -07:00
|
|
|
false
|
2019-04-22 18:41:01 -07:00
|
|
|
);
|
2019-04-22 15:21:10 -07:00
|
|
|
|
|
|
|
// with a Bank for slot 0, blob continues
|
|
|
|
assert_eq!(
|
2019-05-13 21:19:51 -07:00
|
|
|
should_retransmit_and_persist(&blob, Some(bank.clone()), &cache, &me_id),
|
2019-04-22 15:21:10 -07:00
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
// set the blob to have come from the wrong leader
|
|
|
|
blob.set_id(&Pubkey::new_rand());
|
|
|
|
assert_eq!(
|
2019-05-13 21:19:51 -07:00
|
|
|
should_retransmit_and_persist(&blob, Some(bank.clone()), &cache, &me_id),
|
2019-04-22 15:21:10 -07:00
|
|
|
false
|
|
|
|
);
|
|
|
|
|
2019-05-24 19:20:09 -07:00
|
|
|
// with a Bank and no idea who leader is, blob gets thrown out
|
2019-06-14 11:38:37 -07:00
|
|
|
blob.set_slot(MINIMUM_SLOTS_PER_EPOCH as u64 * 3);
|
2019-04-22 15:21:10 -07:00
|
|
|
assert_eq!(
|
2019-05-13 21:19:51 -07:00
|
|
|
should_retransmit_and_persist(&blob, Some(bank), &cache, &me_id),
|
2019-05-24 19:20:09 -07:00
|
|
|
false
|
2019-04-22 15:21:10 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
// if the blob came back from me, it doesn't continue, whether or not I have a bank
|
|
|
|
blob.set_id(&me_id);
|
2019-04-22 18:41:01 -07:00
|
|
|
assert_eq!(
|
2019-05-13 21:19:51 -07:00
|
|
|
should_retransmit_and_persist(&blob, None, &cache, &me_id),
|
2019-04-22 18:41:01 -07:00
|
|
|
false
|
|
|
|
);
|
2019-04-22 15:21:10 -07:00
|
|
|
}
|
|
|
|
|
2018-09-07 16:00:26 -06:00
|
|
|
#[test]
|
|
|
|
pub fn window_send_test() {
|
2018-12-14 12:36:50 -08:00
|
|
|
solana_logger::setup();
|
2019-02-01 14:30:26 -08:00
|
|
|
// setup a leader whose id is used to generates blobs and a validator
|
|
|
|
// node whose window service will retransmit leader blobs.
|
|
|
|
let leader_node = Node::new_localhost();
|
|
|
|
let validator_node = Node::new_localhost();
|
2018-09-07 16:00:26 -06:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2019-03-08 19:39:26 -08:00
|
|
|
let cluster_info_me = ClusterInfo::new_with_invalid_keypair(validator_node.info.clone());
|
2019-02-01 14:30:26 -08:00
|
|
|
let me_id = leader_node.info.id;
|
2018-10-08 20:55:54 -06:00
|
|
|
let subs = Arc::new(RwLock::new(cluster_info_me));
|
2018-09-07 16:00:26 -06:00
|
|
|
|
|
|
|
let (s_reader, r_reader) = channel();
|
2019-03-04 20:50:02 -08:00
|
|
|
let t_receiver = blob_receiver(Arc::new(leader_node.sockets.gossip), &exit, s_reader);
|
2018-09-07 16:00:26 -06:00
|
|
|
let (s_retransmit, r_retransmit) = channel();
|
2019-02-26 17:11:26 -08:00
|
|
|
let blocktree_path = get_tmp_ledger_path!();
|
2019-05-09 14:10:04 -07:00
|
|
|
let (blocktree, _, completed_slots_receiver) = Blocktree::open_with_signal(&blocktree_path)
|
|
|
|
.expect("Expected to be able to open database ledger");
|
|
|
|
let blocktree = Arc::new(blocktree);
|
2019-04-22 18:41:01 -07:00
|
|
|
|
2019-05-22 20:39:00 -07:00
|
|
|
let bank = Bank::new(&create_genesis_block_with_leader(100, &me_id, 10).genesis_block);
|
2019-05-09 14:10:04 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
|
|
|
let repair_strategy = RepairStrategy::RepairAll {
|
2019-05-13 15:37:50 -07:00
|
|
|
bank_forks: bank_forks.clone(),
|
2019-05-09 14:10:04 -07:00
|
|
|
completed_slots_receiver,
|
2019-05-13 15:37:50 -07:00
|
|
|
epoch_schedule: bank_forks
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.working_bank()
|
|
|
|
.epoch_schedule()
|
|
|
|
.clone(),
|
2019-05-09 14:10:04 -07:00
|
|
|
};
|
|
|
|
let t_window = WindowService::new(
|
2019-02-07 20:52:39 -08:00
|
|
|
blocktree,
|
2018-09-07 16:00:26 -06:00
|
|
|
subs,
|
|
|
|
r_reader,
|
|
|
|
s_retransmit,
|
2019-02-01 14:30:26 -08:00
|
|
|
Arc::new(leader_node.sockets.repair),
|
2019-03-04 20:50:02 -08:00
|
|
|
&exit,
|
2019-05-09 14:10:04 -07:00
|
|
|
repair_strategy,
|
2019-05-13 21:19:51 -07:00
|
|
|
|_, _, _| true,
|
2018-09-07 16:00:26 -06:00
|
|
|
);
|
|
|
|
let t_responder = {
|
|
|
|
let (s_responder, r_responder) = channel();
|
2018-09-14 16:56:06 -07:00
|
|
|
let blob_sockets: Vec<Arc<UdpSocket>> =
|
2019-02-01 14:30:26 -08:00
|
|
|
leader_node.sockets.tvu.into_iter().map(Arc::new).collect();
|
2018-09-14 16:56:06 -07:00
|
|
|
|
2018-09-18 08:02:57 -07:00
|
|
|
let t_responder = responder("window_send_test", blob_sockets[0].clone(), r_responder);
|
2018-12-08 22:44:20 -07:00
|
|
|
let num_blobs_to_make = 10;
|
2019-02-01 14:30:26 -08:00
|
|
|
let gossip_address = &leader_node.info.gossip;
|
2019-02-27 13:37:08 -08:00
|
|
|
let msgs = make_consecutive_blobs(
|
|
|
|
&me_id,
|
|
|
|
num_blobs_to_make,
|
|
|
|
0,
|
|
|
|
Hash::default(),
|
|
|
|
&gossip_address,
|
|
|
|
)
|
|
|
|
.into_iter()
|
|
|
|
.rev()
|
|
|
|
.collect();;
|
2018-09-07 16:00:26 -06:00
|
|
|
s_responder.send(msgs).expect("send");
|
|
|
|
t_responder
|
|
|
|
};
|
|
|
|
|
2019-02-04 15:33:43 -08:00
|
|
|
let max_attempts = 10;
|
|
|
|
let mut num_attempts = 0;
|
2019-02-13 10:19:10 -08:00
|
|
|
let mut q = Vec::new();
|
2019-02-04 15:33:43 -08:00
|
|
|
loop {
|
|
|
|
assert!(num_attempts != max_attempts);
|
2019-02-13 10:19:10 -08:00
|
|
|
while let Ok(mut nq) = r_retransmit.recv_timeout(Duration::from_millis(500)) {
|
2019-02-04 15:33:43 -08:00
|
|
|
q.append(&mut nq);
|
|
|
|
}
|
2019-02-13 10:19:10 -08:00
|
|
|
if q.len() == 10 {
|
2019-02-04 15:33:43 -08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
num_attempts += 1;
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
2019-02-04 15:33:43 -08:00
|
|
|
|
2018-09-07 16:00:26 -06:00
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
t_receiver.join().expect("join");
|
|
|
|
t_responder.join().expect("join");
|
|
|
|
t_window.join().expect("join");
|
2019-02-07 20:52:39 -08:00
|
|
|
Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction");
|
|
|
|
let _ignored = remove_dir_all(&blocktree_path);
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2018-11-24 19:32:33 -08:00
|
|
|
pub fn window_send_leader_test2() {
|
2018-12-14 12:36:50 -08:00
|
|
|
solana_logger::setup();
|
2019-02-01 14:30:26 -08:00
|
|
|
// setup a leader whose id is used to generates blobs and a validator
|
|
|
|
// node whose window service will retransmit leader blobs.
|
|
|
|
let leader_node = Node::new_localhost();
|
|
|
|
let validator_node = Node::new_localhost();
|
2018-09-07 16:00:26 -06:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2019-03-06 13:47:18 -08:00
|
|
|
let cluster_info_me = ClusterInfo::new_with_invalid_keypair(validator_node.info.clone());
|
2019-02-01 14:30:26 -08:00
|
|
|
let me_id = leader_node.info.id;
|
2018-10-08 20:55:54 -06:00
|
|
|
let subs = Arc::new(RwLock::new(cluster_info_me));
|
2018-09-07 16:00:26 -06:00
|
|
|
|
|
|
|
let (s_reader, r_reader) = channel();
|
2019-03-04 20:50:02 -08:00
|
|
|
let t_receiver = blob_receiver(Arc::new(leader_node.sockets.gossip), &exit, s_reader);
|
2018-09-07 16:00:26 -06:00
|
|
|
let (s_retransmit, r_retransmit) = channel();
|
2019-02-26 17:11:26 -08:00
|
|
|
let blocktree_path = get_tmp_ledger_path!();
|
2019-05-09 14:10:04 -07:00
|
|
|
let (blocktree, _, completed_slots_receiver) = Blocktree::open_with_signal(&blocktree_path)
|
|
|
|
.expect("Expected to be able to open database ledger");
|
|
|
|
|
|
|
|
let blocktree = Arc::new(blocktree);
|
2019-05-22 20:39:00 -07:00
|
|
|
let bank = Bank::new(&create_genesis_block_with_leader(100, &me_id, 10).genesis_block);
|
2019-05-09 14:10:04 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(0, bank)));
|
2019-05-13 15:37:50 -07:00
|
|
|
let epoch_schedule = *bank_forks.read().unwrap().working_bank().epoch_schedule();
|
2019-05-09 14:10:04 -07:00
|
|
|
let repair_strategy = RepairStrategy::RepairAll {
|
2019-04-22 18:41:01 -07:00
|
|
|
bank_forks,
|
2019-05-09 14:10:04 -07:00
|
|
|
completed_slots_receiver,
|
2019-05-13 15:37:50 -07:00
|
|
|
epoch_schedule,
|
2019-05-09 14:10:04 -07:00
|
|
|
};
|
|
|
|
let t_window = WindowService::new(
|
2019-02-07 20:52:39 -08:00
|
|
|
blocktree,
|
2018-09-07 16:00:26 -06:00
|
|
|
subs.clone(),
|
|
|
|
r_reader,
|
|
|
|
s_retransmit,
|
2019-02-01 14:30:26 -08:00
|
|
|
Arc::new(leader_node.sockets.repair),
|
2019-03-04 20:50:02 -08:00
|
|
|
&exit,
|
2019-05-09 14:10:04 -07:00
|
|
|
repair_strategy,
|
2019-05-13 21:19:51 -07:00
|
|
|
|_, _, _| true,
|
2018-09-07 16:00:26 -06:00
|
|
|
);
|
|
|
|
let t_responder = {
|
|
|
|
let (s_responder, r_responder) = channel();
|
2018-09-14 16:56:06 -07:00
|
|
|
let blob_sockets: Vec<Arc<UdpSocket>> =
|
2019-02-01 14:30:26 -08:00
|
|
|
leader_node.sockets.tvu.into_iter().map(Arc::new).collect();
|
2018-09-18 08:02:57 -07:00
|
|
|
let t_responder = responder("window_send_test", blob_sockets[0].clone(), r_responder);
|
2018-09-07 16:00:26 -06:00
|
|
|
let mut msgs = Vec::new();
|
2019-02-27 13:37:08 -08:00
|
|
|
let blobs =
|
|
|
|
make_consecutive_blobs(&me_id, 14u64, 0, Hash::default(), &leader_node.info.gossip);
|
2019-01-30 20:18:28 -08:00
|
|
|
|
2018-09-07 16:00:26 -06:00
|
|
|
for v in 0..10 {
|
|
|
|
let i = 9 - v;
|
2019-01-30 20:18:28 -08:00
|
|
|
msgs.push(blobs[i].clone());
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
s_responder.send(msgs).expect("send");
|
|
|
|
|
|
|
|
let mut msgs1 = Vec::new();
|
|
|
|
for v in 1..5 {
|
|
|
|
let i = 9 + v;
|
2019-01-30 20:18:28 -08:00
|
|
|
msgs1.push(blobs[i].clone());
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
s_responder.send(msgs1).expect("send");
|
|
|
|
t_responder
|
|
|
|
};
|
2019-02-13 10:19:10 -08:00
|
|
|
let mut q = Vec::new();
|
|
|
|
while let Ok(mut nq) = r_retransmit.recv_timeout(Duration::from_millis(500)) {
|
2018-09-07 16:00:26 -06:00
|
|
|
q.append(&mut nq);
|
|
|
|
}
|
|
|
|
assert!(q.len() > 10);
|
|
|
|
exit.store(true, Ordering::Relaxed);
|
|
|
|
t_receiver.join().expect("join");
|
|
|
|
t_responder.join().expect("join");
|
|
|
|
t_window.join().expect("join");
|
2019-02-07 20:52:39 -08:00
|
|
|
Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction");
|
|
|
|
let _ignored = remove_dir_all(&blocktree_path);
|
2018-09-07 16:00:26 -06:00
|
|
|
}
|
|
|
|
}
|