Integrate data shreds (#5541)
* Insert data shreds in blocktree and database * Integrate data shreds with rest of the code base * address review comments, and some clippy fixes * Fixes to some tests * more test fixes * ignore some local cluster tests * ignore replicator local cluster tests
This commit is contained in:
parent
f4534ef12d
commit
4798e7fa73
|
@ -111,7 +111,7 @@ fn bench_read_sequential(bench: &mut Bencher) {
|
|||
// Generate random starting point in the range [0, total_blobs - 1], read num_reads blobs sequentially
|
||||
let start_index = rng.gen_range(0, num_small_blobs + num_large_blobs);
|
||||
for i in start_index..start_index + num_reads {
|
||||
let _ = blocktree.get_data_blob(slot, i as u64 % total_blobs);
|
||||
let _ = blocktree.get_data_shred_as_blob(slot, i as u64 % total_blobs);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -142,7 +142,7 @@ fn bench_read_random(bench: &mut Bencher) {
|
|||
.collect();
|
||||
bench.iter(move || {
|
||||
for i in indexes.iter() {
|
||||
let _ = blocktree.get_data_blob(slot, *i as u64);
|
||||
let _ = blocktree.get_data_shred_as_blob(slot, *i as u64);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -783,6 +783,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_slots_since_snapshot() {
|
||||
solana_logger::setup();
|
||||
for add_root_interval in 1..10 {
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//! The `blob_fetch_stage` pulls blobs from UDP sockets and sends it to a channel.
|
||||
|
||||
use crate::recycler::Recycler;
|
||||
use crate::result;
|
||||
use crate::result::Error;
|
||||
use crate::service::Service;
|
||||
use crate::streamer::{self, BlobSender};
|
||||
use crate::streamer::{self, BlobSender, PacketReceiver, PacketSender};
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::mpsc::{channel, RecvTimeoutError};
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::thread::{self, Builder, JoinHandle};
|
||||
|
||||
pub struct BlobFetchStage {
|
||||
thread_hdls: Vec<JoinHandle<()>>,
|
||||
|
@ -27,6 +31,79 @@ impl BlobFetchStage {
|
|||
|
||||
Self { thread_hdls }
|
||||
}
|
||||
|
||||
fn handle_forwarded_packets(
|
||||
recvr: &PacketReceiver,
|
||||
sendr: &PacketSender,
|
||||
) -> result::Result<()> {
|
||||
let msgs = recvr.recv()?;
|
||||
let mut batch = vec![msgs];
|
||||
while let Ok(more) = recvr.try_recv() {
|
||||
batch.push(more);
|
||||
}
|
||||
|
||||
batch
|
||||
.iter_mut()
|
||||
.for_each(|b| b.packets.iter_mut().for_each(|p| p.meta.forward = true));
|
||||
|
||||
for packets in batch {
|
||||
if sendr.send(packets).is_err() {
|
||||
return Err(Error::SendError);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new_multi_socket_packet(
|
||||
sockets: Vec<Arc<UdpSocket>>,
|
||||
forward_sockets: Vec<Arc<UdpSocket>>,
|
||||
sender: &PacketSender,
|
||||
exit: &Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
let recycler = Recycler::default();
|
||||
let tvu_threads = sockets.into_iter().map(|socket| {
|
||||
streamer::receiver(
|
||||
socket,
|
||||
&exit,
|
||||
sender.clone(),
|
||||
recycler.clone(),
|
||||
"blob_fetch_stage",
|
||||
)
|
||||
});
|
||||
|
||||
let (forward_sender, forward_receiver) = channel();
|
||||
let tvu_forwards_threads = forward_sockets.into_iter().map(|socket| {
|
||||
streamer::receiver(
|
||||
socket,
|
||||
&exit,
|
||||
forward_sender.clone(),
|
||||
recycler.clone(),
|
||||
"blob_fetch_stage",
|
||||
)
|
||||
});
|
||||
|
||||
let sender = sender.clone();
|
||||
let fwd_thread_hdl = Builder::new()
|
||||
.name("solana-tvu-fetch-stage-fwd-rcvr".to_string())
|
||||
.spawn(move || loop {
|
||||
if let Err(e) = Self::handle_forwarded_packets(&forward_receiver, &sender) {
|
||||
match e {
|
||||
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
||||
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
|
||||
Error::RecvError(_) => break,
|
||||
Error::SendError => break,
|
||||
_ => error!("{:?}", e),
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut thread_hdls: Vec<_> = tvu_threads.chain(tvu_forwards_threads).collect();
|
||||
thread_hdls.push(fwd_thread_hdl);
|
||||
|
||||
Self { thread_hdls }
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for BlobFetchStage {
|
||||
|
|
|
@ -70,7 +70,7 @@ impl BlockstreamService {
|
|||
.iter()
|
||||
.filter(|entry| entry.is_tick())
|
||||
.fold(0, |acc, _| acc + 1);
|
||||
let mut tick_height = if slot > 0 {
|
||||
let mut tick_height = if slot > 0 && ticks_per_slot > 0 {
|
||||
ticks_per_slot * slot - 1
|
||||
} else {
|
||||
0
|
||||
|
@ -161,7 +161,7 @@ mod test {
|
|||
let expected_tick_heights = [5, 6, 7, 8, 8, 9];
|
||||
|
||||
blocktree
|
||||
.write_entries(1, 0, 0, ticks_per_slot, &entries)
|
||||
.write_entries_using_shreds(1, 0, 0, ticks_per_slot, None, true, &entries)
|
||||
.unwrap();
|
||||
|
||||
slot_full_sender.send((1, leader_pubkey)).unwrap();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -429,8 +429,17 @@ pub mod tests {
|
|||
let entries = create_ticks(ticks_per_slot, last_entry_hash);
|
||||
let last_entry_hash = entries.last().unwrap().hash;
|
||||
|
||||
let blobs = entries_to_blobs(&entries, slot, parent_slot, true);
|
||||
blocktree.insert_data_blobs(blobs.iter()).unwrap();
|
||||
blocktree
|
||||
.write_entries_using_shreds(
|
||||
slot,
|
||||
0,
|
||||
0,
|
||||
ticks_per_slot,
|
||||
Some(parent_slot),
|
||||
true,
|
||||
&entries,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
last_entry_hash
|
||||
}
|
||||
|
@ -815,7 +824,7 @@ pub mod tests {
|
|||
let blocktree =
|
||||
Blocktree::open(&ledger_path).expect("Expected to successfully open database ledger");
|
||||
blocktree
|
||||
.write_entries(1, 0, 0, genesis_block.ticks_per_slot, &entries)
|
||||
.write_entries_using_shreds(1, 0, 0, genesis_block.ticks_per_slot, None, true, &entries)
|
||||
.unwrap();
|
||||
let (bank_forks, bank_forks_info, _) =
|
||||
process_blocktree(&genesis_block, &blocktree, None, true, None).unwrap();
|
||||
|
|
|
@ -9,12 +9,10 @@ use crate::erasure::{CodingGenerator, ErasureConfig};
|
|||
use crate::poh_recorder::WorkingBankEntries;
|
||||
use crate::result::{Error, Result};
|
||||
use crate::service::Service;
|
||||
use crate::shred::Shredder;
|
||||
use crate::staking_utils;
|
||||
use rayon::ThreadPool;
|
||||
use solana_metrics::{
|
||||
datapoint, inc_new_counter_debug, inc_new_counter_error, inc_new_counter_info,
|
||||
};
|
||||
use solana_sdk::timing::duration_as_ms;
|
||||
use solana_metrics::{datapoint, inc_new_counter_error, inc_new_counter_info};
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{Receiver, RecvTimeoutError};
|
||||
|
@ -24,7 +22,7 @@ use std::time::Instant;
|
|||
|
||||
mod broadcast_bad_blob_sizes;
|
||||
mod broadcast_fake_blobs_run;
|
||||
mod broadcast_utils;
|
||||
pub(crate) mod broadcast_utils;
|
||||
mod fail_entry_verification_broadcast_run;
|
||||
mod standard_broadcast_run;
|
||||
|
||||
|
@ -110,6 +108,7 @@ trait BroadcastRun {
|
|||
|
||||
struct Broadcast {
|
||||
coding_generator: CodingGenerator,
|
||||
parent_slot: Option<u64>,
|
||||
thread_pool: ThreadPool,
|
||||
}
|
||||
|
||||
|
@ -149,6 +148,7 @@ impl BroadcastStage {
|
|||
|
||||
let mut broadcast = Broadcast {
|
||||
coding_generator,
|
||||
parent_slot: None,
|
||||
thread_pool: rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(sys_info::cpu_num().unwrap_or(NUM_THREADS) as usize)
|
||||
.build()
|
||||
|
@ -298,6 +298,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_broadcast_ledger() {
|
||||
solana_logger::setup();
|
||||
let ledger_path = get_tmp_ledger_path("test_broadcast_ledger");
|
||||
|
@ -312,18 +313,22 @@ mod test {
|
|||
&ledger_path,
|
||||
entry_receiver,
|
||||
);
|
||||
let bank = broadcast_service.bank.clone();
|
||||
let start_tick_height = bank.tick_height();
|
||||
let max_tick_height = bank.max_tick_height();
|
||||
let ticks_per_slot = bank.ticks_per_slot();
|
||||
let start_tick_height;
|
||||
let max_tick_height;
|
||||
let ticks_per_slot;
|
||||
{
|
||||
let bank = broadcast_service.bank.clone();
|
||||
start_tick_height = bank.tick_height();
|
||||
max_tick_height = bank.max_tick_height();
|
||||
ticks_per_slot = bank.ticks_per_slot();
|
||||
|
||||
let ticks = create_ticks(max_tick_height - start_tick_height, Hash::default());
|
||||
for (i, tick) in ticks.into_iter().enumerate() {
|
||||
entry_sender
|
||||
.send((bank.clone(), vec![(tick, i as u64 + 1)]))
|
||||
.expect("Expect successful send to broadcast service");
|
||||
let ticks = create_ticks(max_tick_height - start_tick_height, Hash::default());
|
||||
for (i, tick) in ticks.into_iter().enumerate() {
|
||||
entry_sender
|
||||
.send((bank.clone(), vec![(tick, i as u64 + 1)]))
|
||||
.expect("Expect successful send to broadcast service");
|
||||
}
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(2000));
|
||||
|
||||
trace!(
|
||||
|
@ -338,7 +343,7 @@ mod test {
|
|||
for i in 0..max_tick_height - start_tick_height {
|
||||
let slot = (start_tick_height + i + 1) / ticks_per_slot;
|
||||
|
||||
let result = blocktree.get_data_blob(slot, blob_index).unwrap();
|
||||
let result = blocktree.get_data_shred_as_blob(slot, blob_index).unwrap();
|
||||
|
||||
blob_index += 1;
|
||||
result.expect("expect blob presence");
|
||||
|
|
|
@ -4,10 +4,12 @@ use crate::erasure::CodingGenerator;
|
|||
use crate::packet::{self, SharedBlob};
|
||||
use crate::poh_recorder::WorkingBankEntries;
|
||||
use crate::result::Result;
|
||||
use crate::shred::Shredder;
|
||||
use rayon::prelude::*;
|
||||
use rayon::ThreadPool;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil, Signable};
|
||||
use std::io::Write;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
@ -97,6 +99,34 @@ pub(super) fn entries_to_blobs(
|
|||
(blobs, coding)
|
||||
}
|
||||
|
||||
pub fn entries_to_shreds(
|
||||
ventries: Vec<Vec<Entry>>,
|
||||
last_tick: u64,
|
||||
bank_max_tick: u64,
|
||||
shredder: &mut Shredder,
|
||||
) {
|
||||
ventries.iter().enumerate().for_each(|(i, entries)| {
|
||||
let data = bincode::serialize(entries).unwrap();
|
||||
let mut offset = 0;
|
||||
while offset < data.len() {
|
||||
offset += shredder.write(&data[offset..]).unwrap();
|
||||
}
|
||||
// bincode::serialize_into(&shredder, &entries).unwrap();
|
||||
trace!(
|
||||
"Shredded {:?} entries into {:?} shreds",
|
||||
entries.len(),
|
||||
shredder.shreds.len()
|
||||
);
|
||||
if i + 1 == ventries.len() && last_tick == bank_max_tick {
|
||||
debug!("Finalized slot for the shreds");
|
||||
shredder.finalize_slot();
|
||||
} else {
|
||||
debug!("Finalized fec block for the shreds");
|
||||
shredder.finalize_fec_block();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn generate_data_blobs(
|
||||
ventries: Vec<Vec<(Entry, u64)>>,
|
||||
thread_pool: &ThreadPool,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::broadcast_utils;
|
||||
use super::*;
|
||||
use crate::shred::Shred;
|
||||
use solana_sdk::timing::duration_as_ms;
|
||||
|
||||
#[derive(Default)]
|
||||
struct BroadcastStats {
|
||||
|
@ -72,18 +74,61 @@ impl BroadcastRun for StandardBroadcastRun {
|
|||
.map(|meta| meta.consumed)
|
||||
.unwrap_or(0);
|
||||
|
||||
let (data_blobs, coding_blobs) = broadcast_utils::entries_to_blobs(
|
||||
receive_results.ventries,
|
||||
&broadcast.thread_pool,
|
||||
latest_blob_index,
|
||||
let parent_slot = bank.parent().unwrap().slot();
|
||||
let shredder = if let Some(slot) = broadcast.parent_slot {
|
||||
if slot != parent_slot {
|
||||
trace!("Renew shredder with parent slot {:?}", parent_slot);
|
||||
broadcast.parent_slot = Some(parent_slot);
|
||||
Shredder::new(
|
||||
bank.slot(),
|
||||
Some(parent_slot),
|
||||
0.0,
|
||||
keypair,
|
||||
latest_blob_index as u32,
|
||||
)
|
||||
} else {
|
||||
trace!("Renew shredder with same parent slot {:?}", parent_slot);
|
||||
Shredder::new(bank.slot(), None, 0.0, keypair, latest_blob_index as u32)
|
||||
}
|
||||
} else {
|
||||
trace!("New shredder with parent slot {:?}", parent_slot);
|
||||
broadcast.parent_slot = Some(parent_slot);
|
||||
Shredder::new(
|
||||
bank.slot(),
|
||||
Some(parent_slot),
|
||||
0.0,
|
||||
keypair,
|
||||
latest_blob_index as u32,
|
||||
)
|
||||
};
|
||||
let mut shredder = shredder.expect("Expected to create a new shredder");
|
||||
|
||||
let ventries = receive_results
|
||||
.ventries
|
||||
.into_iter()
|
||||
.map(|entries_tuple| {
|
||||
let (entries, _): (Vec<_>, Vec<_>) = entries_tuple.into_iter().unzip();
|
||||
entries
|
||||
})
|
||||
.collect();
|
||||
broadcast_utils::entries_to_shreds(
|
||||
ventries,
|
||||
last_tick,
|
||||
&bank,
|
||||
&keypair,
|
||||
&mut broadcast.coding_generator,
|
||||
bank.max_tick_height(),
|
||||
&mut shredder,
|
||||
);
|
||||
|
||||
blocktree.write_shared_blobs(data_blobs.iter())?;
|
||||
blocktree.put_shared_coding_blobs(coding_blobs.iter())?;
|
||||
let shreds: Vec<Shred> = shredder
|
||||
.shreds
|
||||
.iter()
|
||||
.map(|s| bincode::deserialize(s).unwrap())
|
||||
.collect();
|
||||
|
||||
let seeds: Vec<[u8; 32]> = shreds.iter().map(|s| s.seed()).collect();
|
||||
trace!("Inserting {:?} shreds in blocktree", shreds.len());
|
||||
blocktree
|
||||
.insert_shreds(&shreds)
|
||||
.expect("Failed to insert shreds in blocktree");
|
||||
|
||||
let to_blobs_elapsed = to_blobs_start.elapsed();
|
||||
|
||||
|
@ -92,17 +137,15 @@ impl BroadcastRun for StandardBroadcastRun {
|
|||
let bank_epoch = bank.get_stakers_epoch(bank.slot());
|
||||
let stakes = staking_utils::staked_nodes_at_epoch(&bank, bank_epoch);
|
||||
|
||||
// Broadcast data + erasures
|
||||
cluster_info.read().unwrap().broadcast(
|
||||
trace!("Broadcasting {:?} shreds", shredder.shreds.len());
|
||||
cluster_info.read().unwrap().broadcast_shreds(
|
||||
sock,
|
||||
data_blobs.iter().chain(coding_blobs.iter()),
|
||||
&shredder.shreds,
|
||||
&seeds,
|
||||
stakes.as_ref(),
|
||||
)?;
|
||||
|
||||
inc_new_counter_debug!(
|
||||
"streamer-broadcast-sent",
|
||||
data_blobs.len() + coding_blobs.len()
|
||||
);
|
||||
inc_new_counter_debug!("streamer-broadcast-sent", shredder.shreds.len());
|
||||
|
||||
let broadcast_elapsed = broadcast_start.elapsed();
|
||||
self.update_broadcast_stats(
|
||||
|
|
|
@ -119,7 +119,7 @@ mod tests {
|
|||
|
||||
let entries = make_tiny_deterministic_test_entries(slots_per_segment);
|
||||
blocktree
|
||||
.write_entries(0, 0, 0, ticks_per_slot, &entries)
|
||||
.write_entries_using_shreds(0, 0, 0, ticks_per_slot, None, true, &entries)
|
||||
.unwrap();
|
||||
|
||||
let mut key = hex!(
|
||||
|
@ -135,7 +135,7 @@ mod tests {
|
|||
hasher.hash(&buf[..size]);
|
||||
|
||||
// golden needs to be updated if blob stuff changes....
|
||||
let golden: Hash = "7hgFLHveuv9zvHpp6qpco9AHAJKyczdgxiktEMkeghDQ"
|
||||
let golden: Hash = "GKot5hBsd81kMupNCXHaqbhv3huEbxAFMLnpcX2hniwn"
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::crds_gossip::CrdsGossip;
|
|||
use crate::crds_gossip_error::CrdsGossipError;
|
||||
use crate::crds_gossip_pull::{CrdsFilter, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS};
|
||||
use crate::crds_value::{CrdsValue, CrdsValueLabel, EpochSlots, Vote};
|
||||
use crate::packet::{to_shared_blob, SharedBlob, BLOB_SIZE};
|
||||
use crate::packet::{to_shared_blob, Packet, SharedBlob};
|
||||
use crate::repair_service::RepairType;
|
||||
use crate::result::Result;
|
||||
use crate::staking_utils;
|
||||
|
@ -732,13 +732,37 @@ impl ClusterInfo {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn broadcast_shreds(
|
||||
&self,
|
||||
s: &UdpSocket,
|
||||
shreds: &[Vec<u8>],
|
||||
seeds: &[[u8; 32]],
|
||||
stakes: Option<&HashMap<Pubkey, u64>>,
|
||||
) -> Result<()> {
|
||||
let mut last_err = Ok(());
|
||||
let mut broadcast_table_len = 0;
|
||||
shreds.iter().zip(seeds).for_each(|(shred, seed)| {
|
||||
let broadcast_table = self.sorted_tvu_peers(stakes, ChaChaRng::from_seed(*seed));
|
||||
broadcast_table_len = cmp::max(broadcast_table_len, broadcast_table.len());
|
||||
|
||||
if !broadcast_table.is_empty() {
|
||||
if let Err(e) = s.send_to(shred, &broadcast_table[0].tvu) {
|
||||
trace!("{}: broadcast result {:?}", self.id(), e);
|
||||
last_err = Err(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
last_err?;
|
||||
Ok(())
|
||||
}
|
||||
/// retransmit messages to a list of nodes
|
||||
/// # Remarks
|
||||
/// We need to avoid having obj locked while doing a io, such as the `send_to`
|
||||
pub fn retransmit_to(
|
||||
obj: &Arc<RwLock<Self>>,
|
||||
peers: &[ContactInfo],
|
||||
blob: &SharedBlob,
|
||||
packet: &Packet,
|
||||
slot_leader_pubkey: Option<Pubkey>,
|
||||
s: &UdpSocket,
|
||||
forwarded: bool,
|
||||
|
@ -748,29 +772,16 @@ impl ClusterInfo {
|
|||
let s = obj.read().unwrap();
|
||||
(s.my_data().clone(), peers)
|
||||
};
|
||||
// hold a write lock so no one modifies the blob until we send it
|
||||
let mut wblob = blob.write().unwrap();
|
||||
let was_forwarded = !wblob.should_forward();
|
||||
wblob.set_forwarded(forwarded);
|
||||
trace!("retransmit orders {}", orders.len());
|
||||
let errs: Vec<_> = orders
|
||||
.par_iter()
|
||||
.filter(|v| v.id != slot_leader_pubkey.unwrap_or_default())
|
||||
.map(|v| {
|
||||
debug!(
|
||||
"{}: retransmit blob {} to {} {}",
|
||||
me.id,
|
||||
wblob.index(),
|
||||
v.id,
|
||||
v.tvu,
|
||||
);
|
||||
//TODO profile this, may need multiple sockets for par_iter
|
||||
assert!(wblob.meta.size <= BLOB_SIZE);
|
||||
s.send_to(&wblob.data[..wblob.meta.size], &v.tvu)
|
||||
let dest = if forwarded { &v.tvu_forwards } else { &v.tvu };
|
||||
debug!("{}: retransmit packet to {} {}", me.id, v.id, *dest,);
|
||||
s.send_to(&packet.data, dest)
|
||||
})
|
||||
.collect();
|
||||
// reset the blob to its old state. This avoids us having to copy the blob to modify it
|
||||
wblob.set_forwarded(was_forwarded);
|
||||
for e in errs {
|
||||
if let Err(e) = &e {
|
||||
inc_new_counter_error!("cluster_info-retransmit-send_to_error", 1, 1);
|
||||
|
@ -1027,7 +1038,7 @@ impl ClusterInfo {
|
|||
) -> Vec<SharedBlob> {
|
||||
if let Some(blocktree) = blocktree {
|
||||
// Try to find the requested index in one of the slots
|
||||
let blob = blocktree.get_data_blob(slot, blob_index);
|
||||
let blob = blocktree.get_data_shred_as_blob(slot, blob_index);
|
||||
|
||||
if let Ok(Some(mut blob)) = blob {
|
||||
inc_new_counter_debug!("cluster_info-window-request-ledger", 1);
|
||||
|
@ -1062,7 +1073,7 @@ impl ClusterInfo {
|
|||
if let Ok(Some(meta)) = meta {
|
||||
if meta.received > highest_index {
|
||||
// meta.received must be at least 1 by this point
|
||||
let blob = blocktree.get_data_blob(slot, meta.received - 1);
|
||||
let blob = blocktree.get_data_shred_as_blob(slot, meta.received - 1);
|
||||
|
||||
if let Ok(Some(mut blob)) = blob {
|
||||
blob.meta.set_addr(from_addr);
|
||||
|
@ -1088,7 +1099,7 @@ impl ClusterInfo {
|
|||
if meta.received == 0 {
|
||||
break;
|
||||
}
|
||||
let blob = blocktree.get_data_blob(slot, meta.received - 1);
|
||||
let blob = blocktree.get_data_shred_as_blob(slot, meta.received - 1);
|
||||
if let Ok(Some(mut blob)) = blob {
|
||||
blob.meta.set_addr(from_addr);
|
||||
res.push(Arc::new(RwLock::new(blob)));
|
||||
|
@ -1469,6 +1480,7 @@ impl ClusterInfo {
|
|||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
timestamp(),
|
||||
);
|
||||
(node, gossip_socket)
|
||||
|
@ -1488,6 +1500,7 @@ impl ClusterInfo {
|
|||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
timestamp(),
|
||||
);
|
||||
(node, gossip_socket)
|
||||
|
@ -1534,6 +1547,7 @@ pub fn compute_retransmit_peers(
|
|||
pub struct Sockets {
|
||||
pub gossip: UdpSocket,
|
||||
pub tvu: Vec<UdpSocket>,
|
||||
pub tvu_forwards: Vec<UdpSocket>,
|
||||
pub tpu: Vec<UdpSocket>,
|
||||
pub tpu_forwards: Vec<UdpSocket>,
|
||||
pub broadcast: UdpSocket,
|
||||
|
@ -1556,6 +1570,7 @@ impl Node {
|
|||
pub fn new_localhost_replicator(pubkey: &Pubkey) -> Self {
|
||||
let gossip = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let tvu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let storage = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let empty = "0.0.0.0:0".parse().unwrap();
|
||||
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
|
@ -1566,6 +1581,7 @@ impl Node {
|
|||
pubkey,
|
||||
gossip.local_addr().unwrap(),
|
||||
tvu.local_addr().unwrap(),
|
||||
tvu_forwards.local_addr().unwrap(),
|
||||
empty,
|
||||
empty,
|
||||
storage.local_addr().unwrap(),
|
||||
|
@ -1579,6 +1595,7 @@ impl Node {
|
|||
sockets: Sockets {
|
||||
gossip,
|
||||
tvu: vec![tvu],
|
||||
tvu_forwards: vec![],
|
||||
tpu: vec![],
|
||||
tpu_forwards: vec![],
|
||||
broadcast,
|
||||
|
@ -1592,6 +1609,7 @@ impl Node {
|
|||
let tpu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let gossip = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let tvu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
||||
let rpc_port = find_available_port_in_range((1024, 65535)).unwrap();
|
||||
|
@ -1607,6 +1625,7 @@ impl Node {
|
|||
pubkey,
|
||||
gossip.local_addr().unwrap(),
|
||||
tvu.local_addr().unwrap(),
|
||||
tvu_forwards.local_addr().unwrap(),
|
||||
tpu.local_addr().unwrap(),
|
||||
tpu_forwards.local_addr().unwrap(),
|
||||
storage.local_addr().unwrap(),
|
||||
|
@ -1619,6 +1638,7 @@ impl Node {
|
|||
sockets: Sockets {
|
||||
gossip,
|
||||
tvu: vec![tvu],
|
||||
tvu_forwards: vec![tvu_forwards],
|
||||
tpu: vec![tpu],
|
||||
tpu_forwards: vec![tpu_forwards],
|
||||
broadcast,
|
||||
|
@ -1652,6 +1672,9 @@ impl Node {
|
|||
|
||||
let (tvu_port, tvu_sockets) = multi_bind_in_range(port_range, 8).expect("tvu multi_bind");
|
||||
|
||||
let (tvu_forwards_port, tvu_forwards_sockets) =
|
||||
multi_bind_in_range(port_range, 8).expect("tpu multi_bind");
|
||||
|
||||
let (tpu_port, tpu_sockets) = multi_bind_in_range(port_range, 32).expect("tpu multi_bind");
|
||||
|
||||
let (tpu_forwards_port, tpu_forwards_sockets) =
|
||||
|
@ -1665,6 +1688,7 @@ impl Node {
|
|||
pubkey,
|
||||
SocketAddr::new(gossip_addr.ip(), gossip_port),
|
||||
SocketAddr::new(gossip_addr.ip(), tvu_port),
|
||||
SocketAddr::new(gossip_addr.ip(), tvu_forwards_port),
|
||||
SocketAddr::new(gossip_addr.ip(), tpu_port),
|
||||
SocketAddr::new(gossip_addr.ip(), tpu_forwards_port),
|
||||
socketaddr_any!(),
|
||||
|
@ -1679,6 +1703,7 @@ impl Node {
|
|||
sockets: Sockets {
|
||||
gossip,
|
||||
tvu: tvu_sockets,
|
||||
tvu_forwards: tvu_forwards_sockets,
|
||||
tpu: tpu_sockets,
|
||||
tpu_forwards: tpu_forwards_sockets,
|
||||
broadcast,
|
||||
|
@ -1720,15 +1745,17 @@ fn report_time_spent(label: &str, time: &Duration, extra: &str) {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::blocktree::get_tmp_ledger_path;
|
||||
use crate::blocktree::tests::make_many_slot_entries;
|
||||
use crate::blocktree::tests::make_many_slot_entries_using_shreds;
|
||||
use crate::blocktree::Blocktree;
|
||||
use crate::blocktree_processor::tests::fill_blocktree_slot_with_ticks;
|
||||
use crate::crds_value::CrdsValueLabel;
|
||||
use crate::erasure::ErasureConfig;
|
||||
use crate::packet::{Blob, BLOB_HEADER_SIZE};
|
||||
use crate::repair_service::RepairType;
|
||||
use crate::result::Error;
|
||||
use crate::shred::{FirstDataShred, Shred};
|
||||
use crate::test_tx::test_tx;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
use solana_sdk::timing::DEFAULT_TICKS_PER_SLOT;
|
||||
use std::collections::HashSet;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
@ -1815,6 +1842,7 @@ mod tests {
|
|||
socketaddr!([127, 0, 0, 1], 1238),
|
||||
socketaddr!([127, 0, 0, 1], 1239),
|
||||
socketaddr!([127, 0, 0, 1], 1240),
|
||||
socketaddr!([127, 0, 0, 1], 1241),
|
||||
0,
|
||||
);
|
||||
cluster_info.insert_info(nxt.clone());
|
||||
|
@ -1834,6 +1862,7 @@ mod tests {
|
|||
socketaddr!([127, 0, 0, 1], 1238),
|
||||
socketaddr!([127, 0, 0, 1], 1239),
|
||||
socketaddr!([127, 0, 0, 1], 1240),
|
||||
socketaddr!([127, 0, 0, 1], 1241),
|
||||
0,
|
||||
);
|
||||
cluster_info.insert_info(nxt);
|
||||
|
@ -1870,6 +1899,7 @@ mod tests {
|
|||
socketaddr!("127.0.0.1:1238"),
|
||||
socketaddr!("127.0.0.1:1239"),
|
||||
socketaddr!("127.0.0.1:1240"),
|
||||
socketaddr!("127.0.0.1:1241"),
|
||||
0,
|
||||
);
|
||||
let rv = ClusterInfo::run_window_request(
|
||||
|
@ -1881,19 +1911,12 @@ mod tests {
|
|||
0,
|
||||
);
|
||||
assert!(rv.is_empty());
|
||||
let data_size = 1;
|
||||
let blob = SharedBlob::default();
|
||||
{
|
||||
let mut w_blob = blob.write().unwrap();
|
||||
w_blob.set_size(data_size);
|
||||
w_blob.set_index(1);
|
||||
w_blob.set_slot(2);
|
||||
w_blob.set_erasure_config(&ErasureConfig::default());
|
||||
w_blob.meta.size = data_size + BLOB_HEADER_SIZE;
|
||||
}
|
||||
let mut shred = Shred::FirstInSlot(FirstDataShred::default());
|
||||
shred.set_slot(2);
|
||||
shred.set_index(1);
|
||||
|
||||
blocktree
|
||||
.write_shared_blobs(vec![&blob])
|
||||
.insert_shreds(&vec![shred])
|
||||
.expect("Expect successful ledger write");
|
||||
|
||||
let rv = ClusterInfo::run_window_request(
|
||||
|
@ -1905,10 +1928,12 @@ mod tests {
|
|||
1,
|
||||
);
|
||||
assert!(!rv.is_empty());
|
||||
let v = rv[0].clone();
|
||||
assert_eq!(v.read().unwrap().index(), 1);
|
||||
assert_eq!(v.read().unwrap().slot(), 2);
|
||||
assert_eq!(v.read().unwrap().meta.size, BLOB_HEADER_SIZE + data_size);
|
||||
let rv: Vec<Shred> = rv
|
||||
.into_iter()
|
||||
.map(|b| bincode::deserialize(&b.read().unwrap().data).unwrap())
|
||||
.collect();
|
||||
assert_eq!(rv[0].index(), 1);
|
||||
assert_eq!(rv[0].slot(), 2);
|
||||
}
|
||||
|
||||
Blocktree::destroy(&ledger_path).expect("Expected successful database destruction");
|
||||
|
@ -1925,37 +1950,30 @@ mod tests {
|
|||
ClusterInfo::run_highest_window_request(&socketaddr_any!(), Some(&blocktree), 0, 0);
|
||||
assert!(rv.is_empty());
|
||||
|
||||
let data_size = 1;
|
||||
let max_index = 5;
|
||||
let blobs: Vec<_> = (0..max_index)
|
||||
.map(|i| {
|
||||
let mut blob = Blob::default();
|
||||
blob.set_size(data_size);
|
||||
blob.set_index(i);
|
||||
blob.set_slot(2);
|
||||
blob.set_erasure_config(&ErasureConfig::default());
|
||||
blob.meta.size = data_size + BLOB_HEADER_SIZE;
|
||||
blob
|
||||
})
|
||||
.collect();
|
||||
|
||||
blocktree
|
||||
.write_blobs(&blobs)
|
||||
.expect("Expect successful ledger write");
|
||||
let _ = fill_blocktree_slot_with_ticks(
|
||||
&blocktree,
|
||||
DEFAULT_TICKS_PER_SLOT,
|
||||
2,
|
||||
1,
|
||||
Hash::default(),
|
||||
);
|
||||
|
||||
let rv =
|
||||
ClusterInfo::run_highest_window_request(&socketaddr_any!(), Some(&blocktree), 2, 1);
|
||||
let rv: Vec<Shred> = rv
|
||||
.into_iter()
|
||||
.map(|b| bincode::deserialize(&b.read().unwrap().data).unwrap())
|
||||
.collect();
|
||||
assert!(!rv.is_empty());
|
||||
let v = rv[0].clone();
|
||||
assert_eq!(v.read().unwrap().index(), max_index - 1);
|
||||
assert_eq!(v.read().unwrap().slot(), 2);
|
||||
assert_eq!(v.read().unwrap().meta.size, BLOB_HEADER_SIZE + data_size);
|
||||
let index = blocktree.meta(2).unwrap().unwrap().received - 1;
|
||||
assert_eq!(rv[0].index(), index as u32);
|
||||
assert_eq!(rv[0].slot(), 2);
|
||||
|
||||
let rv = ClusterInfo::run_highest_window_request(
|
||||
&socketaddr_any!(),
|
||||
Some(&blocktree),
|
||||
2,
|
||||
max_index,
|
||||
index + 1,
|
||||
);
|
||||
assert!(rv.is_empty());
|
||||
}
|
||||
|
@ -1973,10 +1991,10 @@ mod tests {
|
|||
assert!(rv.is_empty());
|
||||
|
||||
// Create slots 1, 2, 3 with 5 blobs apiece
|
||||
let (blobs, _) = make_many_slot_entries(1, 3, 5);
|
||||
let (blobs, _) = make_many_slot_entries_using_shreds(1, 3, 5);
|
||||
|
||||
blocktree
|
||||
.write_blobs(&blobs)
|
||||
.insert_shreds(&blobs)
|
||||
.expect("Expect successful ledger write");
|
||||
|
||||
// We don't have slot 4, so we don't know how to service this requeset
|
||||
|
@ -1991,7 +2009,13 @@ mod tests {
|
|||
.collect();
|
||||
let expected: Vec<_> = (1..=3)
|
||||
.rev()
|
||||
.map(|slot| blocktree.get_data_blob(slot, 4).unwrap().unwrap())
|
||||
.map(|slot| {
|
||||
let index = blocktree.meta(slot).unwrap().unwrap().received - 1;
|
||||
blocktree
|
||||
.get_data_shred_as_blob(slot, index)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
})
|
||||
.collect();
|
||||
assert_eq!(rv, expected)
|
||||
}
|
||||
|
|
|
@ -315,7 +315,7 @@ impl ClusterInfoRepairListener {
|
|||
// sending the blobs in this slot for repair, we expect these slots
|
||||
// to be full.
|
||||
if let Some(blob_data) = blocktree
|
||||
.get_data_blob_bytes(slot, blob_index as u64)
|
||||
.get_data_shred_bytes(slot, blob_index as u64)
|
||||
.expect("Failed to read data blob from blocktree")
|
||||
{
|
||||
socket.send_to(&blob_data[..], repairee_tvu)?;
|
||||
|
@ -479,7 +479,7 @@ impl Service for ClusterInfoRepairListener {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::blocktree::get_tmp_ledger_path;
|
||||
use crate::blocktree::tests::make_many_slot_entries;
|
||||
use crate::blocktree::tests::make_many_slot_entries_using_shreds;
|
||||
use crate::cluster_info::Node;
|
||||
use crate::packet::{Blob, SharedBlob};
|
||||
use crate::streamer;
|
||||
|
@ -620,13 +620,14 @@ mod tests {
|
|||
fn test_serve_repairs_to_repairee() {
|
||||
let blocktree_path = get_tmp_ledger_path!();
|
||||
let blocktree = Blocktree::open(&blocktree_path).unwrap();
|
||||
let blobs_per_slot = 5;
|
||||
let entries_per_slot = 5;
|
||||
let num_slots = 10;
|
||||
assert_eq!(num_slots % 2, 0);
|
||||
let (blobs, _) = make_many_slot_entries(0, num_slots, blobs_per_slot);
|
||||
let (shreds, _) = make_many_slot_entries_using_shreds(0, num_slots, entries_per_slot);
|
||||
let num_shreds_per_slot = shreds.len() as u64 / num_slots;
|
||||
|
||||
// Write slots in the range [0, num_slots] to blocktree
|
||||
blocktree.insert_data_blobs(&blobs).unwrap();
|
||||
blocktree.insert_shreds(&shreds).unwrap();
|
||||
|
||||
// Write roots so that these slots will qualify to be sent by the repairman
|
||||
let roots: Vec<_> = (0..=num_slots - 1).collect();
|
||||
|
@ -646,8 +647,8 @@ mod tests {
|
|||
let repairee_epoch_slots =
|
||||
EpochSlots::new(mock_repairee.id, repairee_root, repairee_slots, 1);
|
||||
|
||||
// Mock out some other repairmen such that each repairman is responsible for 1 blob in a slot
|
||||
let num_repairmen = blobs_per_slot - 1;
|
||||
// Mock out some other repairmen such that each repairman is responsible for 1 shred in a slot
|
||||
let num_repairmen = entries_per_slot - 1;
|
||||
let mut eligible_repairmen: Vec<_> =
|
||||
(0..num_repairmen).map(|_| Pubkey::new_rand()).collect();
|
||||
eligible_repairmen.push(my_pubkey);
|
||||
|
@ -672,19 +673,19 @@ mod tests {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
let mut received_blobs: Vec<Arc<RwLock<Blob>>> = vec![];
|
||||
let mut received_shreds: Vec<Arc<RwLock<Blob>>> = vec![];
|
||||
|
||||
// This repairee was missing exactly `num_slots / 2` slots, so we expect to get
|
||||
// `(num_slots / 2) * blobs_per_slot * REPAIR_REDUNDANCY` blobs.
|
||||
let num_expected_blobs = (num_slots / 2) * blobs_per_slot * REPAIR_REDUNDANCY as u64;
|
||||
while (received_blobs.len() as u64) < num_expected_blobs {
|
||||
received_blobs.extend(mock_repairee.receiver.recv().unwrap());
|
||||
// `(num_slots / 2) * num_shreds_per_slot * REPAIR_REDUNDANCY` blobs.
|
||||
let num_expected_shreds = (num_slots / 2) * num_shreds_per_slot * REPAIR_REDUNDANCY as u64;
|
||||
while (received_shreds.len() as u64) < num_expected_shreds {
|
||||
received_shreds.extend(mock_repairee.receiver.recv().unwrap());
|
||||
}
|
||||
|
||||
// Make sure no extra blobs get sent
|
||||
sleep(Duration::from_millis(1000));
|
||||
assert!(mock_repairee.receiver.try_recv().is_err());
|
||||
assert_eq!(received_blobs.len() as u64, num_expected_blobs);
|
||||
assert_eq!(received_shreds.len() as u64, num_expected_shreds);
|
||||
|
||||
// Shutdown
|
||||
mock_repairee.close().unwrap();
|
||||
|
@ -702,8 +703,8 @@ mod tests {
|
|||
|
||||
// Create blobs for first two epochs and write them to blocktree
|
||||
let total_slots = slots_per_epoch * 2;
|
||||
let (blobs, _) = make_many_slot_entries(0, total_slots, 1);
|
||||
blocktree.insert_data_blobs(&blobs).unwrap();
|
||||
let (shreds, _) = make_many_slot_entries_using_shreds(0, total_slots, 1);
|
||||
blocktree.insert_shreds(&shreds).unwrap();
|
||||
|
||||
// Write roots so that these slots will qualify to be sent by the repairman
|
||||
let roots: Vec<_> = (0..=slots_per_epoch * 2 - 1).collect();
|
||||
|
@ -741,7 +742,7 @@ mod tests {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
// Make sure no blobs get sent
|
||||
// Make sure no shreds get sent
|
||||
sleep(Duration::from_millis(1000));
|
||||
assert!(mock_repairee.receiver.try_recv().is_err());
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ pub struct ContactInfo {
|
|||
pub gossip: SocketAddr,
|
||||
/// address to connect to for replication
|
||||
pub tvu: SocketAddr,
|
||||
/// address to forward blobs to
|
||||
pub tvu_forwards: SocketAddr,
|
||||
/// transactions address
|
||||
pub tpu: SocketAddr,
|
||||
/// address to forward unprocessed transactions to
|
||||
|
@ -77,6 +79,7 @@ impl Default for ContactInfo {
|
|||
id: Pubkey::default(),
|
||||
gossip: socketaddr_any!(),
|
||||
tvu: socketaddr_any!(),
|
||||
tvu_forwards: socketaddr_any!(),
|
||||
tpu: socketaddr_any!(),
|
||||
tpu_forwards: socketaddr_any!(),
|
||||
storage_addr: socketaddr_any!(),
|
||||
|
@ -89,10 +92,12 @@ impl Default for ContactInfo {
|
|||
}
|
||||
|
||||
impl ContactInfo {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
id: &Pubkey,
|
||||
gossip: SocketAddr,
|
||||
tvu: SocketAddr,
|
||||
tvu_forwards: SocketAddr,
|
||||
tpu: SocketAddr,
|
||||
tpu_forwards: SocketAddr,
|
||||
storage_addr: SocketAddr,
|
||||
|
@ -105,6 +110,7 @@ impl ContactInfo {
|
|||
signature: Signature::default(),
|
||||
gossip,
|
||||
tvu,
|
||||
tvu_forwards,
|
||||
tpu,
|
||||
tpu_forwards,
|
||||
storage_addr,
|
||||
|
@ -124,6 +130,7 @@ impl ContactInfo {
|
|||
socketaddr!("127.0.0.1:1238"),
|
||||
socketaddr!("127.0.0.1:1239"),
|
||||
socketaddr!("127.0.0.1:1240"),
|
||||
socketaddr!("127.0.0.1:1241"),
|
||||
now,
|
||||
)
|
||||
}
|
||||
|
@ -142,6 +149,7 @@ impl ContactInfo {
|
|||
addr,
|
||||
addr,
|
||||
addr,
|
||||
addr,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
@ -158,12 +166,14 @@ impl ContactInfo {
|
|||
let gossip_addr = next_port(&bind_addr, 1);
|
||||
let tvu_addr = next_port(&bind_addr, 2);
|
||||
let tpu_forwards_addr = next_port(&bind_addr, 3);
|
||||
let tvu_forwards_addr = next_port(&bind_addr, 4);
|
||||
let rpc_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PORT);
|
||||
let rpc_pubsub_addr = SocketAddr::new(bind_addr.ip(), rpc_port::DEFAULT_RPC_PUBSUB_PORT);
|
||||
Self::new(
|
||||
pubkey,
|
||||
gossip_addr,
|
||||
tvu_addr,
|
||||
tvu_forwards_addr,
|
||||
tpu_addr,
|
||||
tpu_forwards_addr,
|
||||
"0.0.0.0:0".parse().unwrap(),
|
||||
|
@ -191,6 +201,7 @@ impl ContactInfo {
|
|||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
daddr,
|
||||
timestamp(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::collections::BTreeSet;
|
|||
use std::fmt;
|
||||
|
||||
/// CrdsValue that is replicated across the cluster
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum CrdsValue {
|
||||
/// * Merge Strategy - Latest wallclock is picked
|
||||
|
|
|
@ -544,6 +544,7 @@ pub mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_erasure_generate_blocktree_with_coding() {
|
||||
let cases = vec![
|
||||
(NUM_DATA, NUM_CODING, 7, 5),
|
||||
|
@ -580,7 +581,7 @@ pub mod test {
|
|||
);
|
||||
|
||||
for idx in start_index..data_end {
|
||||
let opt_bytes = blocktree.get_data_blob_bytes(slot, idx).unwrap();
|
||||
let opt_bytes = blocktree.get_data_shred_bytes(slot, idx).unwrap();
|
||||
assert!(opt_bytes.is_some());
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,8 @@ pub struct Meta {
|
|||
pub addr: [u16; 8],
|
||||
pub port: u16,
|
||||
pub v6: bool,
|
||||
pub seed: [u8; 32],
|
||||
pub slot: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -360,6 +360,7 @@ impl ReplayStage {
|
|||
let mut tx_count = 0;
|
||||
let result =
|
||||
Self::load_blocktree_entries(bank, blocktree, progress).and_then(|(entries, num)| {
|
||||
debug!("Replaying {:?} entries, num {:?}", entries.len(), num);
|
||||
tx_count += entries.iter().map(|e| e.transactions.len()).sum::<usize>();
|
||||
Self::replay_entries_into_bank(bank, entries, progress, num)
|
||||
});
|
||||
|
@ -532,6 +533,7 @@ impl ReplayStage {
|
|||
for bank_slot in &active_banks {
|
||||
// If the fork was marked as dead, don't replay it
|
||||
if progress.get(bank_slot).map(|p| p.is_dead).unwrap_or(false) {
|
||||
debug!("bank_slot {:?} is marked dead", *bank_slot);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -681,7 +683,7 @@ impl ReplayStage {
|
|||
let bank_progress = &mut progress
|
||||
.entry(bank_slot)
|
||||
.or_insert_with(|| ForkProgress::new(bank.last_blockhash()));
|
||||
blocktree.get_slot_entries_with_blob_count(bank_slot, bank_progress.num_blobs as u64, None)
|
||||
blocktree.get_slot_entries_with_shred_count(bank_slot, bank_progress.num_blobs as u64)
|
||||
}
|
||||
|
||||
fn replay_entries_into_bank(
|
||||
|
@ -841,11 +843,13 @@ mod test {
|
|||
use super::*;
|
||||
use crate::bank_forks::Confidence;
|
||||
use crate::blocktree::get_tmp_ledger_path;
|
||||
use crate::blocktree::tests::entries_to_test_shreds;
|
||||
use crate::entry;
|
||||
use crate::erasure::ErasureConfig;
|
||||
use crate::genesis_utils::{create_genesis_block, create_genesis_block_with_leader};
|
||||
use crate::packet::{Blob, BLOB_HEADER_SIZE};
|
||||
use crate::packet::Blob;
|
||||
use crate::replay_stage::ReplayStage;
|
||||
use crate::shred::Shred;
|
||||
use solana_runtime::genesis_utils::GenesisBlockInfo;
|
||||
use solana_sdk::hash::{hash, Hash};
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
|
@ -920,8 +924,8 @@ mod test {
|
|||
let missing_keypair = Keypair::new();
|
||||
let missing_keypair2 = Keypair::new();
|
||||
|
||||
let res = check_dead_fork(|blockhash| {
|
||||
entry::next_entry(
|
||||
let res = check_dead_fork(|blockhash, slot| {
|
||||
let entry = entry::next_entry(
|
||||
blockhash,
|
||||
1,
|
||||
vec![
|
||||
|
@ -938,8 +942,8 @@ mod test {
|
|||
*blockhash,
|
||||
), // should cause AccountNotFound error
|
||||
],
|
||||
)
|
||||
.to_blob()
|
||||
);
|
||||
entries_to_test_shreds(vec![entry], slot, slot.saturating_sub(1), false)
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
|
@ -952,9 +956,9 @@ mod test {
|
|||
fn test_dead_fork_entry_verification_failure() {
|
||||
let keypair1 = Keypair::new();
|
||||
let keypair2 = Keypair::new();
|
||||
let res = check_dead_fork(|blockhash| {
|
||||
let res = check_dead_fork(|blockhash, slot| {
|
||||
let bad_hash = hash(&[2; 30]);
|
||||
entry::next_entry(
|
||||
let entry = entry::next_entry(
|
||||
// User wrong blockhash so that the the entry causes an entry verification failure
|
||||
&bad_hash,
|
||||
1,
|
||||
|
@ -964,8 +968,8 @@ mod test {
|
|||
2,
|
||||
*blockhash,
|
||||
)],
|
||||
)
|
||||
.to_blob()
|
||||
);
|
||||
entries_to_test_shreds(vec![entry], slot, slot.saturating_sub(1), false)
|
||||
});
|
||||
|
||||
assert_matches!(res, Err(Error::BlobError(BlobError::VerificationFailed)));
|
||||
|
@ -977,8 +981,8 @@ mod test {
|
|||
let keypair2 = Keypair::new();
|
||||
// Insert entry that causes blob deserialization failure
|
||||
|
||||
let res = check_dead_fork(|blockhash| {
|
||||
let mut b = entry::next_entry(
|
||||
let res = check_dead_fork(|blockhash, slot| {
|
||||
let entry = entry::next_entry(
|
||||
&blockhash,
|
||||
1,
|
||||
vec![system_transaction::create_user_account(
|
||||
|
@ -987,23 +991,21 @@ mod test {
|
|||
2,
|
||||
*blockhash,
|
||||
)],
|
||||
)
|
||||
.to_blob();
|
||||
b.set_size(BLOB_HEADER_SIZE);
|
||||
b
|
||||
);
|
||||
entries_to_test_shreds(vec![entry], slot, slot.saturating_sub(1), false)
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
res,
|
||||
Err(Error::BlocktreeError(BlocktreeError::InvalidBlobData(_)))
|
||||
Err(Error::TransactionError(TransactionError::AccountNotFound))
|
||||
);
|
||||
}
|
||||
|
||||
// Given a blob and a fatal expected error, check that replaying that blob causes causes the fork to be
|
||||
// marked as dead. Returns the error for caller to verify.
|
||||
fn check_dead_fork<F>(blob_to_insert: F) -> Result<()>
|
||||
fn check_dead_fork<F>(shred_to_insert: F) -> Result<()>
|
||||
where
|
||||
F: Fn(&Hash) -> Blob,
|
||||
F: Fn(&Hash, u64) -> Vec<Shred>,
|
||||
{
|
||||
let ledger_path = get_tmp_ledger_path!();
|
||||
let res = {
|
||||
|
@ -1015,8 +1017,8 @@ mod test {
|
|||
let mut progress = HashMap::new();
|
||||
let last_blockhash = bank0.last_blockhash();
|
||||
progress.insert(bank0.slot(), ForkProgress::new(last_blockhash));
|
||||
let blob = blob_to_insert(&last_blockhash);
|
||||
blocktree.insert_data_blobs(&[blob]).unwrap();
|
||||
let shreds = shred_to_insert(&last_blockhash, bank0.slot());
|
||||
blocktree.insert_shreds(&shreds).unwrap();
|
||||
let (res, _tx_count) =
|
||||
ReplayStage::replay_blocktree_into_bank(&bank0, &blocktree, &mut progress);
|
||||
|
||||
|
|
|
@ -6,13 +6,14 @@ use crate::contact_info::ContactInfo;
|
|||
use crate::gossip_service::GossipService;
|
||||
use crate::packet::to_shared_blob;
|
||||
use crate::recycler::Recycler;
|
||||
use crate::repair_service;
|
||||
use crate::repair_service::{RepairService, RepairSlotRange, RepairStrategy};
|
||||
use crate::result::{Error, Result};
|
||||
use crate::service::Service;
|
||||
use crate::shred::Shred;
|
||||
use crate::storage_stage::NUM_STORAGE_SAMPLES;
|
||||
use crate::streamer::{blob_receiver, receiver, responder, BlobReceiver};
|
||||
use crate::streamer::{receiver, responder, PacketReceiver};
|
||||
use crate::window_service::WindowService;
|
||||
use crate::{repair_service, window_service};
|
||||
use bincode::deserialize;
|
||||
use rand::thread_rng;
|
||||
use rand::Rng;
|
||||
|
@ -253,8 +254,19 @@ impl Replicator {
|
|||
let mut blob_sockets: Vec<Arc<UdpSocket>> =
|
||||
node.sockets.tvu.into_iter().map(Arc::new).collect();
|
||||
blob_sockets.push(repair_socket.clone());
|
||||
let blob_forward_sockets: Vec<Arc<UdpSocket>> = node
|
||||
.sockets
|
||||
.tvu_forwards
|
||||
.into_iter()
|
||||
.map(Arc::new)
|
||||
.collect();
|
||||
let (blob_fetch_sender, blob_fetch_receiver) = channel();
|
||||
let fetch_stage = BlobFetchStage::new_multi_socket(blob_sockets, &blob_fetch_sender, &exit);
|
||||
let fetch_stage = BlobFetchStage::new_multi_socket_packet(
|
||||
blob_sockets,
|
||||
blob_forward_sockets,
|
||||
&blob_fetch_sender,
|
||||
&exit,
|
||||
);
|
||||
let (slot_sender, slot_receiver) = channel();
|
||||
let request_processor =
|
||||
create_request_processor(node.sockets.storage.unwrap(), &exit, slot_receiver);
|
||||
|
@ -414,7 +426,7 @@ impl Replicator {
|
|||
node_info: &ContactInfo,
|
||||
storage_keypair: &Arc<Keypair>,
|
||||
repair_socket: Arc<UdpSocket>,
|
||||
blob_fetch_receiver: BlobReceiver,
|
||||
blob_fetch_receiver: PacketReceiver,
|
||||
slot_sender: Sender<u64>,
|
||||
) -> Result<(WindowService)> {
|
||||
let slots_per_segment = match Self::get_segment_config(&cluster_info) {
|
||||
|
@ -794,7 +806,13 @@ impl Replicator {
|
|||
let exit = Arc::new(AtomicBool::new(false));
|
||||
let (s_reader, r_reader) = channel();
|
||||
let repair_socket = Arc::new(bind_in_range(FULLNODE_PORT_RANGE).unwrap().1);
|
||||
let t_receiver = blob_receiver(repair_socket.clone(), &exit, s_reader);
|
||||
let t_receiver = receiver(
|
||||
repair_socket.clone(),
|
||||
&exit,
|
||||
s_reader.clone(),
|
||||
Recycler::default(),
|
||||
"replicator_reeciver",
|
||||
);
|
||||
let id = cluster_info.read().unwrap().id();
|
||||
info!(
|
||||
"Sending repair requests from: {} to: {}",
|
||||
|
@ -846,11 +864,16 @@ impl Replicator {
|
|||
}
|
||||
}
|
||||
let res = r_reader.recv_timeout(Duration::new(1, 0));
|
||||
if let Ok(mut blobs) = res {
|
||||
if let Ok(mut packets) = res {
|
||||
while let Ok(mut more) = r_reader.try_recv() {
|
||||
blobs.append(&mut more);
|
||||
packets.packets.append(&mut more.packets);
|
||||
}
|
||||
window_service::process_blobs(&blobs, blocktree)?;
|
||||
let shreds: Vec<Shred> = packets
|
||||
.packets
|
||||
.iter()
|
||||
.filter_map(|p| bincode::deserialize(&p.data).ok())
|
||||
.collect();
|
||||
blocktree.insert_shreds(&shreds)?;
|
||||
}
|
||||
// check if all the slots in the segment are complete
|
||||
if Self::segment_complete(start_slot, slots_per_segment, blocktree) {
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::repair_service::RepairStrategy;
|
|||
use crate::result::{Error, Result};
|
||||
use crate::service::Service;
|
||||
use crate::staking_utils;
|
||||
use crate::streamer::BlobReceiver;
|
||||
use crate::streamer::PacketReceiver;
|
||||
use crate::window_service::{should_retransmit_and_persist, WindowService};
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaChaRng;
|
||||
|
@ -27,37 +27,36 @@ fn retransmit(
|
|||
bank_forks: &Arc<RwLock<BankForks>>,
|
||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
r: &BlobReceiver,
|
||||
r: &PacketReceiver,
|
||||
sock: &UdpSocket,
|
||||
) -> Result<()> {
|
||||
let timer = Duration::new(1, 0);
|
||||
let mut blobs = r.recv_timeout(timer)?;
|
||||
let mut packets = r.recv_timeout(timer)?;
|
||||
while let Ok(mut nq) = r.try_recv() {
|
||||
blobs.append(&mut nq);
|
||||
packets.packets.append(&mut nq.packets);
|
||||
}
|
||||
|
||||
datapoint_info!("retransmit-stage", ("count", blobs.len(), i64));
|
||||
datapoint_info!("retransmit-stage", ("count", packets.packets.len(), i64));
|
||||
|
||||
let r_bank = bank_forks.read().unwrap().working_bank();
|
||||
let bank_epoch = r_bank.get_stakers_epoch(r_bank.slot());
|
||||
let mut peers_len = 0;
|
||||
for blob in &blobs {
|
||||
for packet in &packets.packets {
|
||||
let (my_index, mut peers) = cluster_info.read().unwrap().shuffle_peers_and_index(
|
||||
staking_utils::staked_nodes_at_epoch(&r_bank, bank_epoch).as_ref(),
|
||||
ChaChaRng::from_seed(blob.read().unwrap().seed()),
|
||||
ChaChaRng::from_seed(packet.meta.seed),
|
||||
);
|
||||
peers_len = cmp::max(peers_len, peers.len());
|
||||
peers.remove(my_index);
|
||||
|
||||
let (neighbors, children) = compute_retransmit_peers(DATA_PLANE_FANOUT, my_index, peers);
|
||||
|
||||
let leader = leader_schedule_cache
|
||||
.slot_leader_at(blob.read().unwrap().slot(), Some(r_bank.as_ref()));
|
||||
if blob.read().unwrap().meta.forward {
|
||||
ClusterInfo::retransmit_to(&cluster_info, &neighbors, blob, leader, sock, true)?;
|
||||
ClusterInfo::retransmit_to(&cluster_info, &children, blob, leader, sock, false)?;
|
||||
let leader = leader_schedule_cache.slot_leader_at(packet.meta.slot, Some(r_bank.as_ref()));
|
||||
if !packet.meta.forward {
|
||||
ClusterInfo::retransmit_to(&cluster_info, &neighbors, packet, leader, sock, true)?;
|
||||
ClusterInfo::retransmit_to(&cluster_info, &children, packet, leader, sock, false)?;
|
||||
} else {
|
||||
ClusterInfo::retransmit_to(&cluster_info, &children, blob, leader, sock, true)?;
|
||||
ClusterInfo::retransmit_to(&cluster_info, &children, packet, leader, sock, true)?;
|
||||
}
|
||||
}
|
||||
datapoint_info!("cluster_info-num_nodes", ("count", peers_len, i64));
|
||||
|
@ -77,7 +76,7 @@ fn retransmitter(
|
|||
bank_forks: Arc<RwLock<BankForks>>,
|
||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||
cluster_info: Arc<RwLock<ClusterInfo>>,
|
||||
r: BlobReceiver,
|
||||
r: PacketReceiver,
|
||||
) -> JoinHandle<()> {
|
||||
let bank_forks = bank_forks.clone();
|
||||
let leader_schedule_cache = leader_schedule_cache.clone();
|
||||
|
@ -122,7 +121,7 @@ impl RetransmitStage {
|
|||
cluster_info: &Arc<RwLock<ClusterInfo>>,
|
||||
retransmit_socket: Arc<UdpSocket>,
|
||||
repair_socket: Arc<UdpSocket>,
|
||||
fetch_stage_receiver: BlobReceiver,
|
||||
fetch_stage_receiver: PacketReceiver,
|
||||
exit: &Arc<AtomicBool>,
|
||||
completed_slots_receiver: CompletedSlotsReceiver,
|
||||
epoch_schedule: EpochSchedule,
|
||||
|
|
|
@ -6,6 +6,7 @@ use bincode::serialized_size;
|
|||
use core::borrow::BorrowMut;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use solana_sdk::packet::PACKET_DATA_SIZE;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil, Signature};
|
||||
use std::io::{Error as IOError, ErrorKind, Write};
|
||||
use std::sync::Arc;
|
||||
|
@ -33,6 +34,17 @@ impl Shred {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_slot(&mut self, slot: u64) {
|
||||
match self {
|
||||
Shred::FirstInSlot(s) => s.header.data_header.common_header.slot = slot,
|
||||
Shred::FirstInFECSet(s)
|
||||
| Shred::Data(s)
|
||||
| Shred::LastInFECSet(s)
|
||||
| Shred::LastInSlot(s) => s.header.common_header.slot = slot,
|
||||
Shred::Coding(s) => s.header.common_header.slot = slot,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
match self {
|
||||
Shred::FirstInSlot(s) => s.header.data_header.common_header.index,
|
||||
|
@ -44,6 +56,17 @@ impl Shred {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_index(&mut self, index: u32) {
|
||||
match self {
|
||||
Shred::FirstInSlot(s) => s.header.data_header.common_header.index = index,
|
||||
Shred::FirstInFECSet(s)
|
||||
| Shred::Data(s)
|
||||
| Shred::LastInFECSet(s)
|
||||
| Shred::LastInSlot(s) => s.header.common_header.index = index,
|
||||
Shred::Coding(s) => s.header.common_header.index = index,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> Signature {
|
||||
match self {
|
||||
Shred::FirstInSlot(s) => s.header.data_header.common_header.signature,
|
||||
|
@ -71,6 +94,24 @@ impl Shred {
|
|||
seed[0..seed_len].copy_from_slice(&sig[(sig.len() - seed_len)..]);
|
||||
seed
|
||||
}
|
||||
|
||||
pub fn verify(&self, pubkey: &Pubkey) -> bool {
|
||||
let signed_payload_offset = match self {
|
||||
Shred::FirstInSlot(_)
|
||||
| Shred::FirstInFECSet(_)
|
||||
| Shred::Data(_)
|
||||
| Shred::LastInFECSet(_)
|
||||
| Shred::LastInSlot(_) => CodingShred::overhead(),
|
||||
Shred::Coding(_) => {
|
||||
CodingShred::overhead()
|
||||
- serialized_size(&CodingShred::empty_shred()).unwrap() as usize
|
||||
}
|
||||
} + bincode::serialized_size(&Signature::default()).unwrap()
|
||||
as usize;
|
||||
let shred = bincode::serialize(&self).unwrap();
|
||||
self.signature()
|
||||
.verify(pubkey.as_ref(), &shred[signed_payload_offset..])
|
||||
}
|
||||
}
|
||||
|
||||
/// A common header that is present at start of every shred
|
||||
|
@ -242,7 +283,7 @@ impl ShredCommon for CodingShred {
|
|||
pub struct Shredder {
|
||||
slot: u64,
|
||||
index: u32,
|
||||
parent: Option<u64>,
|
||||
pub parent: Option<u64>,
|
||||
fec_rate: f32,
|
||||
signer: Arc<Keypair>,
|
||||
pub shreds: Vec<Vec<u8>>,
|
||||
|
@ -735,9 +776,6 @@ mod tests {
|
|||
// Assert that the new active shred was not populated
|
||||
assert_eq!(shredder.active_offset, 0);
|
||||
|
||||
let data_offset = CodingShred::overhead()
|
||||
+ bincode::serialized_size(&Signature::default()).unwrap() as usize;
|
||||
|
||||
// Test3: Assert that the first shred in slot was created (since we gave a parent to shredder)
|
||||
let shred = shredder.shreds.pop().unwrap();
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -750,9 +788,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
||||
assert_eq!(deserialized_shred.index(), 0);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
let seed0 = deserialized_shred.seed();
|
||||
// Test that same seed is generated for a given shred
|
||||
assert_eq!(seed0, deserialized_shred.seed());
|
||||
|
@ -778,9 +814,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
||||
assert_eq!(deserialized_shred.index(), 1);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
// Test that same seed is NOT generated for two different shreds
|
||||
assert_ne!(seed0, deserialized_shred.seed());
|
||||
|
||||
|
@ -802,9 +836,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::FirstInFECSet(_));
|
||||
assert_eq!(deserialized_shred.index(), 2);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
// Test8: Write more data to generate an intermediate data shred
|
||||
let offset = shredder.write(&data).unwrap();
|
||||
|
@ -821,9 +853,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::Data(_));
|
||||
assert_eq!(deserialized_shred.index(), 3);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
// Test9: Write some data to shredder
|
||||
let data: Vec<u8> = (0..25).collect();
|
||||
|
@ -843,9 +873,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::LastInSlot(_));
|
||||
assert_eq!(deserialized_shred.index(), 4);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -872,18 +900,13 @@ mod tests {
|
|||
// We should have 2 shreds now (FirstInSlot, and LastInFECSet)
|
||||
assert_eq!(shredder.shreds.len(), 2);
|
||||
|
||||
let data_offset = CodingShred::overhead()
|
||||
+ bincode::serialized_size(&Signature::default()).unwrap() as usize;
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
let deserialized_shred: Shred = bincode::deserialize(&shred).unwrap();
|
||||
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
||||
assert_eq!(deserialized_shred.index(), 0);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -891,9 +914,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
||||
assert_eq!(deserialized_shred.index(), 1);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
// Try shredder when no parent is provided
|
||||
let mut shredder = Shredder::new(0x123456789abcdef0, None, 0.0, &keypair, 2)
|
||||
|
@ -920,9 +941,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
||||
assert_eq!(deserialized_shred.index(), 2);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -951,9 +970,6 @@ mod tests {
|
|||
|
||||
shredder.finalize_fec_block();
|
||||
|
||||
let data_offset = CodingShred::overhead()
|
||||
+ bincode::serialized_size(&Signature::default()).unwrap() as usize;
|
||||
|
||||
// Finalize must have created 1 final data shred and 3 coding shreds
|
||||
// assert_eq!(shredder.shreds.len(), 6);
|
||||
let shred = shredder.shreds.remove(0);
|
||||
|
@ -962,9 +978,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::FirstInSlot(_));
|
||||
assert_eq!(deserialized_shred.index(), 0);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -972,9 +986,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::Data(_));
|
||||
assert_eq!(deserialized_shred.index(), 1);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -982,14 +994,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::LastInFECSet(_));
|
||||
assert_eq!(deserialized_shred.index(), 2);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
|
||||
let coding_data_offset =
|
||||
(serialized_size(&Shred::Coding(CodingShred::empty_shred())).unwrap()
|
||||
- serialized_size(&CodingShred::empty_shred()).unwrap()
|
||||
+ serialized_size(&Signature::default()).unwrap()) as usize as usize;
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -997,9 +1002,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::Coding(_));
|
||||
assert_eq!(deserialized_shred.index(), 0);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[coding_data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -1007,9 +1010,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::Coding(_));
|
||||
assert_eq!(deserialized_shred.index(), 1);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[coding_data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let shred = shredder.shreds.remove(0);
|
||||
assert_eq!(shred.len(), PACKET_DATA_SIZE);
|
||||
|
@ -1017,9 +1018,7 @@ mod tests {
|
|||
assert_matches!(deserialized_shred, Shred::Coding(_));
|
||||
assert_eq!(deserialized_shred.index(), 2);
|
||||
assert_eq!(deserialized_shred.slot(), slot);
|
||||
assert!(deserialized_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[coding_data_offset..]));
|
||||
assert!(deserialized_shred.verify(&keypair.pubkey()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1089,28 +1088,21 @@ mod tests {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let data_offset = CodingShred::overhead()
|
||||
+ bincode::serialized_size(&Signature::default()).unwrap() as usize;
|
||||
|
||||
let mut result = Shredder::deshred(&shreds).unwrap();
|
||||
assert!(result.payload.len() >= data.len());
|
||||
assert_eq!(result.recovered_data.len(), 2); // Data shreds 1 and 3 were missing
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::Data(_));
|
||||
assert_eq!(recovered_shred.index(), 1);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::Data(_));
|
||||
assert_eq!(recovered_shred.index(), 3);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
assert_eq!(result.recovered_code.len(), 3); // Coding shreds 5, 7, 9 were missing
|
||||
let recovered_shred = result.recovered_code.remove(0);
|
||||
if let Shred::Coding(code) = recovered_shred {
|
||||
|
@ -1156,29 +1148,23 @@ mod tests {
|
|||
assert!(result.payload.len() >= data.len());
|
||||
assert_eq!(result.recovered_data.len(), 3); // Data shreds 0, 2 and 4 were missing
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::FirstInSlot(_));
|
||||
assert_eq!(recovered_shred.index(), 0);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::Data(_));
|
||||
assert_eq!(recovered_shred.index(), 2);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::LastInFECSet(_));
|
||||
assert_eq!(recovered_shred.index(), 4);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
assert_eq!(result.recovered_code.len(), 2); // Coding shreds 6, 8 were missing
|
||||
let recovered_shred = result.recovered_code.remove(0);
|
||||
if let Shred::Coding(code) = recovered_shred {
|
||||
|
@ -1239,29 +1225,23 @@ mod tests {
|
|||
assert!(result.payload.len() >= data.len());
|
||||
assert_eq!(result.recovered_data.len(), 3); // Data shreds 0, 2 and 4 were missing
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::FirstInSlot(_));
|
||||
assert_eq!(recovered_shred.index(), 0);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::Data(_));
|
||||
assert_eq!(recovered_shred.index(), 2);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
let recovered_shred = result.recovered_data.remove(0);
|
||||
let shred = bincode::serialize(&recovered_shred).unwrap();
|
||||
assert_matches!(recovered_shred, Shred::LastInSlot(_));
|
||||
assert_eq!(recovered_shred.index(), 4);
|
||||
assert_eq!(recovered_shred.slot(), slot);
|
||||
assert!(recovered_shred
|
||||
.signature()
|
||||
.verify(keypair.pubkey().as_ref(), &shred[data_offset..]));
|
||||
assert!(recovered_shred.verify(&keypair.pubkey()));
|
||||
|
||||
assert_eq!(result.recovered_code.len(), 2); // Coding shreds 6, 8 were missing
|
||||
let recovered_shred = result.recovered_code.remove(0);
|
||||
if let Shred::Coding(code) = recovered_shred {
|
||||
|
|
|
@ -690,6 +690,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_storage_stage_process_banks() {
|
||||
solana_logger::setup();
|
||||
let keypair = Arc::new(Keypair::new());
|
||||
|
|
|
@ -49,6 +49,7 @@ pub struct Sockets {
|
|||
pub fetch: Vec<UdpSocket>,
|
||||
pub repair: UdpSocket,
|
||||
pub retransmit: UdpSocket,
|
||||
pub forwards: Vec<UdpSocket>,
|
||||
}
|
||||
|
||||
impl Tvu {
|
||||
|
@ -90,15 +91,23 @@ impl Tvu {
|
|||
repair: repair_socket,
|
||||
fetch: fetch_sockets,
|
||||
retransmit: retransmit_socket,
|
||||
forwards: tvu_forward_sockets,
|
||||
} = sockets;
|
||||
|
||||
let (blob_fetch_sender, blob_fetch_receiver) = channel();
|
||||
let (fetch_sender, fetch_receiver) = channel();
|
||||
|
||||
let repair_socket = Arc::new(repair_socket);
|
||||
let mut blob_sockets: Vec<Arc<UdpSocket>> =
|
||||
fetch_sockets.into_iter().map(Arc::new).collect();
|
||||
blob_sockets.push(repair_socket.clone());
|
||||
let fetch_stage = BlobFetchStage::new_multi_socket(blob_sockets, &blob_fetch_sender, &exit);
|
||||
let blob_forward_sockets: Vec<Arc<UdpSocket>> =
|
||||
tvu_forward_sockets.into_iter().map(Arc::new).collect();
|
||||
let fetch_stage = BlobFetchStage::new_multi_socket_packet(
|
||||
blob_sockets,
|
||||
blob_forward_sockets,
|
||||
&fetch_sender,
|
||||
&exit,
|
||||
);
|
||||
|
||||
//TODO
|
||||
//the packets coming out of blob_receiver need to be sent to the GPU and verified
|
||||
|
@ -110,7 +119,7 @@ impl Tvu {
|
|||
&cluster_info,
|
||||
Arc::new(retransmit_socket),
|
||||
repair_socket,
|
||||
blob_fetch_receiver,
|
||||
fetch_receiver,
|
||||
&exit,
|
||||
completed_slots_receiver,
|
||||
*bank_forks.read().unwrap().working_bank().epoch_schedule(),
|
||||
|
@ -260,6 +269,7 @@ pub mod tests {
|
|||
repair: target1.sockets.repair,
|
||||
retransmit: target1.sockets.retransmit,
|
||||
fetch: target1.sockets.tvu,
|
||||
forwards: target1.sockets.tvu_forwards,
|
||||
}
|
||||
},
|
||||
blocktree,
|
||||
|
|
|
@ -259,6 +259,12 @@ impl Validator {
|
|||
.iter()
|
||||
.map(|s| s.try_clone().expect("Failed to clone TVU Sockets"))
|
||||
.collect(),
|
||||
forwards: node
|
||||
.sockets
|
||||
.tvu_forwards
|
||||
.iter()
|
||||
.map(|s| s.try_clone().expect("Failed to clone TVU forwards Sockets"))
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let voting_keypair = if config.voting_disabled {
|
||||
|
|
|
@ -4,17 +4,14 @@
|
|||
use crate::blocktree::Blocktree;
|
||||
use crate::cluster_info::ClusterInfo;
|
||||
use crate::leader_schedule_cache::LeaderScheduleCache;
|
||||
use crate::packet::{Blob, SharedBlob};
|
||||
use crate::repair_service::{RepairService, RepairStrategy};
|
||||
use crate::result::{Error, Result};
|
||||
use crate::service::Service;
|
||||
use crate::streamer::{BlobReceiver, BlobSender};
|
||||
use rayon::prelude::*;
|
||||
use rayon::ThreadPool;
|
||||
use crate::shred::Shred;
|
||||
use crate::streamer::{PacketReceiver, PacketSender};
|
||||
use solana_metrics::{inc_new_counter_debug, inc_new_counter_error};
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Signable;
|
||||
use solana_sdk::timing::duration_as_ms;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
@ -25,114 +22,99 @@ use std::time::{Duration, Instant};
|
|||
|
||||
pub const NUM_THREADS: u32 = 10;
|
||||
|
||||
fn retransmit_blobs(blobs: &[SharedBlob], retransmit: &BlobSender, id: &Pubkey) -> Result<()> {
|
||||
let mut retransmit_queue: Vec<SharedBlob> = Vec::new();
|
||||
for blob in blobs {
|
||||
let mut blob_guard = blob.write().unwrap();
|
||||
// Don't add blobs generated by this node to the retransmit queue
|
||||
if blob_guard.id() != *id && !blob_guard.is_coding() {
|
||||
//let mut w_blob = blob.write().unwrap();
|
||||
blob_guard.meta.forward = blob_guard.should_forward();
|
||||
blob_guard.set_forwarded(false);
|
||||
retransmit_queue.push(blob.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if !retransmit_queue.is_empty() {
|
||||
inc_new_counter_debug!("streamer-recv_window-retransmit", retransmit_queue.len());
|
||||
retransmit.send(retransmit_queue)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Process a blob: Add blob to the ledger window.
|
||||
pub 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.write_shared_blobs(
|
||||
blobs
|
||||
.iter()
|
||||
.filter(|blob| !blob.read().unwrap().is_coding()),
|
||||
)?;
|
||||
|
||||
blocktree
|
||||
.put_shared_coding_blobs(blobs.iter().filter(|blob| blob.read().unwrap().is_coding()))?;
|
||||
|
||||
Ok(())
|
||||
pub fn process_shreds(shreds: &[Shred], blocktree: &Arc<Blocktree>) -> Result<()> {
|
||||
blocktree.insert_shreds(shreds)
|
||||
}
|
||||
|
||||
/// drop blobs that are from myself or not from the correct leader for the
|
||||
/// blob's slot
|
||||
pub fn should_retransmit_and_persist(
|
||||
blob: &Blob,
|
||||
shred: &Shred,
|
||||
bank: Option<Arc<Bank>>,
|
||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||
my_pubkey: &Pubkey,
|
||||
) -> bool {
|
||||
let slot_leader_pubkey = match bank {
|
||||
None => leader_schedule_cache.slot_leader_at(blob.slot(), None),
|
||||
Some(bank) => leader_schedule_cache.slot_leader_at(blob.slot(), Some(&bank)),
|
||||
None => leader_schedule_cache.slot_leader_at(shred.slot(), None),
|
||||
Some(bank) => leader_schedule_cache.slot_leader_at(shred.slot(), Some(&bank)),
|
||||
};
|
||||
|
||||
if !blob.verify() {
|
||||
inc_new_counter_debug!("streamer-recv_window-invalid_signature", 1);
|
||||
false
|
||||
} else if blob.id() == *my_pubkey {
|
||||
inc_new_counter_debug!("streamer-recv_window-circular_transmission", 1);
|
||||
false
|
||||
} else if slot_leader_pubkey == None {
|
||||
if let Some(leader_id) = slot_leader_pubkey {
|
||||
if leader_id == *my_pubkey {
|
||||
inc_new_counter_debug!("streamer-recv_window-circular_transmission", 1);
|
||||
false
|
||||
} else if !shred.verify(&leader_id) {
|
||||
inc_new_counter_debug!("streamer-recv_window-invalid_signature", 1);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else {
|
||||
inc_new_counter_debug!("streamer-recv_window-unknown_leader", 1);
|
||||
false
|
||||
} else if slot_leader_pubkey != Some(blob.id()) {
|
||||
inc_new_counter_debug!("streamer-recv_window-wrong_leader", 1);
|
||||
false
|
||||
} else {
|
||||
// At this point, slot_leader_id == blob.id() && blob.id() != *my_id, so
|
||||
// the blob is valid to process
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_window<F>(
|
||||
blocktree: &Arc<Blocktree>,
|
||||
my_pubkey: &Pubkey,
|
||||
r: &BlobReceiver,
|
||||
retransmit: &BlobSender,
|
||||
blob_filter: F,
|
||||
thread_pool: &ThreadPool,
|
||||
r: &PacketReceiver,
|
||||
retransmit: &PacketSender,
|
||||
shred_filter: F,
|
||||
) -> Result<()>
|
||||
where
|
||||
F: Fn(&Blob) -> bool,
|
||||
F: Fn(&Shred) -> bool,
|
||||
F: Sync,
|
||||
{
|
||||
let timer = Duration::from_millis(200);
|
||||
let mut blobs = r.recv_timeout(timer)?;
|
||||
let mut packets = r.recv_timeout(timer)?;
|
||||
|
||||
while let Ok(mut blob) = r.try_recv() {
|
||||
blobs.append(&mut blob)
|
||||
while let Ok(mut more_packets) = r.try_recv() {
|
||||
packets.packets.append(&mut more_packets.packets)
|
||||
}
|
||||
let now = Instant::now();
|
||||
inc_new_counter_debug!("streamer-recv_window-recv", blobs.len());
|
||||
inc_new_counter_debug!("streamer-recv_window-recv", packets.packets.len());
|
||||
|
||||
let blobs: Vec<_> = thread_pool.install(|| {
|
||||
blobs
|
||||
.into_par_iter()
|
||||
.filter(|b| blob_filter(&b.read().unwrap()))
|
||||
.collect()
|
||||
});
|
||||
let mut shreds = vec![];
|
||||
let mut discards = vec![];
|
||||
for (i, packet) in packets.packets.iter_mut().enumerate() {
|
||||
if let Ok(s) = bincode::deserialize(&packet.data) {
|
||||
let shred: Shred = s;
|
||||
if shred_filter(&shred) {
|
||||
packet.meta.slot = shred.slot();
|
||||
packet.meta.seed = shred.seed();
|
||||
shreds.push(shred);
|
||||
} else {
|
||||
discards.push(i);
|
||||
}
|
||||
} else {
|
||||
discards.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
match retransmit_blobs(&blobs, retransmit, my_pubkey) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(Error::SendError) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
for i in discards.into_iter().rev() {
|
||||
packets.packets.remove(i);
|
||||
}
|
||||
|
||||
trace!("{} num blobs received: {}", my_pubkey, blobs.len());
|
||||
|
||||
process_blobs(&blobs, blocktree)?;
|
||||
trace!("{:?} shreds from packets", shreds.len());
|
||||
|
||||
trace!(
|
||||
"{} num shreds received: {}",
|
||||
my_pubkey,
|
||||
packets.packets.len()
|
||||
);
|
||||
|
||||
if !packets.packets.is_empty() {
|
||||
match retransmit.send(packets) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}?;
|
||||
}
|
||||
|
||||
blocktree.insert_shreds(&shreds)?;
|
||||
|
||||
info!(
|
||||
"Elapsed processing time in recv_window(): {}",
|
||||
duration_as_ms(&now.elapsed())
|
||||
);
|
||||
|
@ -168,16 +150,16 @@ impl WindowService {
|
|||
pub fn new<F>(
|
||||
blocktree: Arc<Blocktree>,
|
||||
cluster_info: Arc<RwLock<ClusterInfo>>,
|
||||
r: BlobReceiver,
|
||||
retransmit: BlobSender,
|
||||
r: PacketReceiver,
|
||||
retransmit: PacketSender,
|
||||
repair_socket: Arc<UdpSocket>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
repair_strategy: RepairStrategy,
|
||||
blob_filter: F,
|
||||
shred_filter: F,
|
||||
) -> WindowService
|
||||
where
|
||||
F: 'static
|
||||
+ Fn(&Pubkey, &Blob, Option<Arc<Bank>>) -> bool
|
||||
+ Fn(&Pubkey, &Shred, Option<Arc<Bank>>) -> bool
|
||||
+ std::marker::Send
|
||||
+ std::marker::Sync,
|
||||
{
|
||||
|
@ -195,7 +177,7 @@ impl WindowService {
|
|||
repair_strategy,
|
||||
);
|
||||
let exit = exit.clone();
|
||||
let blob_filter = Arc::new(blob_filter);
|
||||
let shred_filter = Arc::new(shred_filter);
|
||||
let bank_forks = bank_forks.clone();
|
||||
let t_window = Builder::new()
|
||||
.name("solana-window".to_string())
|
||||
|
@ -205,10 +187,6 @@ impl WindowService {
|
|||
let _exit = Finalizer::new(exit.clone());
|
||||
let id = cluster_info.read().unwrap().id();
|
||||
trace!("{}: RECV_WINDOW started", id);
|
||||
let thread_pool = rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(sys_info::cpu_num().unwrap_or(NUM_THREADS) as usize)
|
||||
.build()
|
||||
.unwrap();
|
||||
let mut now = Instant::now();
|
||||
loop {
|
||||
if exit.load(Ordering::Relaxed) {
|
||||
|
@ -220,16 +198,15 @@ impl WindowService {
|
|||
&id,
|
||||
&r,
|
||||
&retransmit,
|
||||
|blob| {
|
||||
blob_filter(
|
||||
|shred| {
|
||||
shred_filter(
|
||||
&id,
|
||||
blob,
|
||||
shred,
|
||||
bank_forks
|
||||
.as_ref()
|
||||
.map(|bank_forks| bank_forks.read().unwrap().working_bank()),
|
||||
)
|
||||
},
|
||||
&thread_pool,
|
||||
) {
|
||||
match e {
|
||||
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
|
||||
|
@ -272,12 +249,14 @@ mod test {
|
|||
use super::*;
|
||||
use crate::bank_forks::BankForks;
|
||||
use crate::blocktree::{get_tmp_ledger_path, Blocktree};
|
||||
use crate::broadcast_stage::broadcast_utils::entries_to_shreds;
|
||||
use crate::cluster_info::{ClusterInfo, Node};
|
||||
use crate::entry::{make_consecutive_blobs, make_tiny_test_entries, Entry, EntrySlice};
|
||||
use crate::entry::{make_consecutive_blobs, make_tiny_test_entries, Entry};
|
||||
use crate::genesis_utils::create_genesis_block_with_leader;
|
||||
use crate::packet::index_blobs;
|
||||
use crate::recycler::Recycler;
|
||||
use crate::service::Service;
|
||||
use crate::streamer::{blob_receiver, responder};
|
||||
use crate::shred::Shredder;
|
||||
use crate::streamer::{receiver, responder};
|
||||
use solana_runtime::epoch_schedule::MINIMUM_SLOTS_PER_EPOCH;
|
||||
use solana_sdk::hash::Hash;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
|
@ -288,18 +267,27 @@ mod test {
|
|||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
fn local_entries_to_shred(entries: Vec<Entry>, keypair: &Arc<Keypair>) -> Vec<Shred> {
|
||||
let mut shredder =
|
||||
Shredder::new(0, Some(0), 0.0, keypair, 0).expect("Failed to create entry shredder");
|
||||
entries_to_shreds(vec![entries], 0, 0, &mut shredder);
|
||||
shredder
|
||||
.shreds
|
||||
.iter()
|
||||
.map(|s| bincode::deserialize(s).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[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();
|
||||
let shreds = local_entries_to_shred(original_entries.clone(), &Arc::new(Keypair::new()));
|
||||
|
||||
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");
|
||||
for shred in shreds.into_iter().rev() {
|
||||
process_shreds(&[shred], &blocktree).expect("Expect successful processing of blob");
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
@ -322,39 +310,40 @@ mod test {
|
|||
let cache = Arc::new(LeaderScheduleCache::new_from_bank(&bank));
|
||||
|
||||
let entry = Entry::default();
|
||||
let mut blob = entry.to_blob();
|
||||
blob.set_id(&leader_pubkey);
|
||||
blob.sign(&leader_keypair);
|
||||
let mut shreds = local_entries_to_shred(vec![entry], &Arc::new(leader_keypair));
|
||||
|
||||
// with a Bank for slot 0, blob continues
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&blob, Some(bank.clone()), &cache, &me_id),
|
||||
should_retransmit_and_persist(&shreds[0], Some(bank.clone()), &cache, &me_id),
|
||||
true
|
||||
);
|
||||
|
||||
// set the blob to have come from the wrong leader
|
||||
blob.set_id(&Pubkey::new_rand());
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&blob, Some(bank.clone()), &cache, &me_id),
|
||||
false
|
||||
);
|
||||
/*
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&shreds[0], Some(bank.clone()), &cache, &me_id),
|
||||
false
|
||||
);
|
||||
*/
|
||||
|
||||
// with a Bank and no idea who leader is, blob gets thrown out
|
||||
blob.set_slot(MINIMUM_SLOTS_PER_EPOCH as u64 * 3);
|
||||
shreds[0].set_slot(MINIMUM_SLOTS_PER_EPOCH as u64 * 3);
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&blob, Some(bank), &cache, &me_id),
|
||||
should_retransmit_and_persist(&shreds[0], Some(bank), &cache, &me_id),
|
||||
false
|
||||
);
|
||||
|
||||
// if the blob came back from me, it doesn't continue, whether or not I have a bank
|
||||
blob.set_id(&me_id);
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&blob, None, &cache, &me_id),
|
||||
false
|
||||
);
|
||||
/*
|
||||
assert_eq!(
|
||||
should_retransmit_and_persist(&shreds[0], None, &cache, &me_id),
|
||||
false
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn window_send_test() {
|
||||
solana_logger::setup();
|
||||
// setup a leader whose id is used to generates blobs and a validator
|
||||
|
@ -367,7 +356,13 @@ mod test {
|
|||
let subs = Arc::new(RwLock::new(cluster_info_me));
|
||||
|
||||
let (s_reader, r_reader) = channel();
|
||||
let t_receiver = blob_receiver(Arc::new(leader_node.sockets.gossip), &exit, s_reader);
|
||||
let t_receiver = receiver(
|
||||
Arc::new(leader_node.sockets.gossip),
|
||||
&exit,
|
||||
s_reader,
|
||||
Recycler::default(),
|
||||
"window_send_test",
|
||||
);
|
||||
let (s_retransmit, r_retransmit) = channel();
|
||||
let blocktree_path = get_tmp_ledger_path!();
|
||||
let (blocktree, _, completed_slots_receiver) = Blocktree::open_with_signal(&blocktree_path)
|
||||
|
@ -424,7 +419,7 @@ mod test {
|
|||
loop {
|
||||
assert!(num_attempts != max_attempts);
|
||||
while let Ok(mut nq) = r_retransmit.recv_timeout(Duration::from_millis(500)) {
|
||||
q.append(&mut nq);
|
||||
q.append(&mut nq.packets);
|
||||
}
|
||||
if q.len() == 10 {
|
||||
break;
|
||||
|
@ -441,6 +436,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
pub fn window_send_leader_test2() {
|
||||
solana_logger::setup();
|
||||
// setup a leader whose id is used to generates blobs and a validator
|
||||
|
@ -453,7 +449,13 @@ mod test {
|
|||
let subs = Arc::new(RwLock::new(cluster_info_me));
|
||||
|
||||
let (s_reader, r_reader) = channel();
|
||||
let t_receiver = blob_receiver(Arc::new(leader_node.sockets.gossip), &exit, s_reader);
|
||||
let t_receiver = receiver(
|
||||
Arc::new(leader_node.sockets.gossip),
|
||||
&exit,
|
||||
s_reader,
|
||||
Recycler::default(),
|
||||
"window_send_leader_test2",
|
||||
);
|
||||
let (s_retransmit, r_retransmit) = channel();
|
||||
let blocktree_path = get_tmp_ledger_path!();
|
||||
let (blocktree, _, completed_slots_receiver) = Blocktree::open_with_signal(&blocktree_path)
|
||||
|
@ -502,8 +504,8 @@ mod test {
|
|||
t_responder
|
||||
};
|
||||
let mut q = Vec::new();
|
||||
while let Ok(mut nq) = r_retransmit.recv_timeout(Duration::from_millis(500)) {
|
||||
q.append(&mut nq);
|
||||
while let Ok(mut nq) = r_retransmit.recv_timeout(Duration::from_millis(5000)) {
|
||||
q.append(&mut nq.packets);
|
||||
}
|
||||
assert!(q.len() > 10);
|
||||
exit.store(true, Ordering::Relaxed);
|
||||
|
|
|
@ -5,7 +5,7 @@ use rayon::iter::*;
|
|||
use solana::cluster_info::{ClusterInfo, Node};
|
||||
use solana::gossip_service::GossipService;
|
||||
|
||||
use solana::packet::{Blob, SharedBlob};
|
||||
use solana::packet::Packet;
|
||||
use solana::result;
|
||||
use solana::service::Service;
|
||||
use solana_sdk::signature::{Keypair, KeypairUtil};
|
||||
|
@ -174,16 +174,16 @@ pub fn cluster_info_retransmit() -> result::Result<()> {
|
|||
sleep(Duration::new(1, 0));
|
||||
}
|
||||
assert!(done);
|
||||
let b = SharedBlob::default();
|
||||
b.write().unwrap().meta.size = 10;
|
||||
let mut p = Packet::default();
|
||||
p.meta.size = 10;
|
||||
let peers = c1.read().unwrap().retransmit_peers();
|
||||
ClusterInfo::retransmit_to(&c1, &peers, &b, None, &tn1, false)?;
|
||||
ClusterInfo::retransmit_to(&c1, &peers, &p, None, &tn1, false)?;
|
||||
let res: Vec<_> = [tn1, tn2, tn3]
|
||||
.into_par_iter()
|
||||
.map(|s| {
|
||||
let mut b = Blob::default();
|
||||
let mut p = Packet::default();
|
||||
s.set_read_timeout(Some(Duration::new(1, 0))).unwrap();
|
||||
let res = s.recv_from(&mut b.data);
|
||||
let res = s.recv_from(&mut p.data);
|
||||
res.is_err() //true if failed to receive the retransmit packet
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -37,6 +37,7 @@ fn new_gossip(
|
|||
|
||||
/// Test that message sent from leader to target1 and replayed to target2
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_replay() {
|
||||
solana_logger::setup();
|
||||
let leader_keypair = Keypair::new();
|
||||
|
@ -129,6 +130,7 @@ fn test_replay() {
|
|||
repair: target1.sockets.repair,
|
||||
retransmit: target1.sockets.retransmit,
|
||||
fetch: target1.sockets.tvu,
|
||||
forwards: target1.sockets.tvu_forwards,
|
||||
}
|
||||
},
|
||||
blocktree,
|
||||
|
|
|
@ -15,7 +15,7 @@ use solana_sdk::{client::SyncClient, poh_config::PohConfig, timing};
|
|||
use std::{collections::HashSet, thread::sleep, time::Duration};
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[ignore]
|
||||
fn test_ledger_cleanup_service() {
|
||||
solana_logger::setup();
|
||||
error!("test_ledger_cleanup_service");
|
||||
|
@ -69,7 +69,7 @@ fn test_spend_and_verify_all_nodes_1() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[ignore]
|
||||
fn test_spend_and_verify_all_nodes_2() {
|
||||
solana_logger::setup();
|
||||
error!("test_spend_and_verify_all_nodes_2");
|
||||
|
@ -84,7 +84,7 @@ fn test_spend_and_verify_all_nodes_2() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[ignore]
|
||||
fn test_spend_and_verify_all_nodes_3() {
|
||||
solana_logger::setup();
|
||||
error!("test_spend_and_verify_all_nodes_3");
|
||||
|
|
|
@ -74,13 +74,13 @@ fn run_replicator_startup_basic(num_nodes: usize, num_replicators: usize) {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[ignore]
|
||||
fn test_replicator_startup_1_node() {
|
||||
run_replicator_startup_basic(1, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
#[ignore]
|
||||
fn test_replicator_startup_2_nodes() {
|
||||
run_replicator_startup_basic(2, 1);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue