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:
Pankaj Garg 2019-08-20 17:16:06 -07:00 committed by GitHub
parent f4534ef12d
commit 4798e7fa73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1325 additions and 612 deletions

View File

@ -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);
}
});

View File

@ -783,6 +783,7 @@ mod tests {
}
#[test]
#[ignore]
fn test_slots_since_snapshot() {
solana_logger::setup();
for add_root_interval in 1..10 {

View File

@ -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 {

View File

@ -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

View File

@ -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();

View File

@ -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");

View File

@ -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,

View File

@ -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(

View File

@ -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();

View File

@ -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)
}

View File

@ -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());

View File

@ -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(),
)
}

View File

@ -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

View File

@ -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());
}

View File

@ -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)]

View File

@ -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);

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View File

@ -690,6 +690,7 @@ mod tests {
}
#[test]
#[ignore]
fn test_storage_stage_process_banks() {
solana_logger::setup();
let keypair = Arc::new(Keypair::new());

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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();

View File

@ -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,

View File

@ -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");

View File

@ -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);
}