2018-10-08 19:55:54 -07:00
|
|
|
//! The `cluster_info` module defines a data structure that is shared by all the nodes in the network over
|
2018-04-28 00:31:20 -07:00
|
|
|
//! a gossip control plane. The goal is to share small bits of off-chain information and detect and
|
2018-04-21 11:02:49 -07:00
|
|
|
//! repair partitions.
|
|
|
|
//!
|
2018-08-09 08:13:57 -07:00
|
|
|
//! This CRDT only supports a very limited set of types. A map of Pubkey -> Versioned Struct.
|
2018-05-15 04:35:41 -07:00
|
|
|
//! The last version is always picked during an update.
|
2018-04-28 00:31:20 -07:00
|
|
|
//!
|
|
|
|
//! The network is arranged in layers:
|
|
|
|
//!
|
|
|
|
//! * layer 0 - Leader.
|
|
|
|
//! * layer 1 - As many nodes as we can fit
|
|
|
|
//! * layer 2 - Everyone else, if layer 1 is `2^10`, layer 2 should be able to fit `2^20` number of nodes.
|
|
|
|
//!
|
2018-05-14 14:33:11 -07:00
|
|
|
//! Bank needs to provide an interface for us to query the stake weight
|
2019-11-02 00:38:30 -07:00
|
|
|
use crate::{
|
|
|
|
contact_info::ContactInfo,
|
|
|
|
crds_gossip::CrdsGossip,
|
|
|
|
crds_gossip_error::CrdsGossipError,
|
|
|
|
crds_gossip_pull::{CrdsFilter, CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS},
|
2020-03-11 21:31:50 -07:00
|
|
|
crds_value::{
|
2020-05-11 15:02:01 -07:00
|
|
|
self, CrdsData, CrdsValue, CrdsValueLabel, EpochSlotsIndex, LowestSlot, SnapshotHash,
|
|
|
|
Version, Vote, MAX_WALLCLOCK,
|
2020-03-11 21:31:50 -07:00
|
|
|
},
|
|
|
|
epoch_slots::EpochSlots,
|
2019-11-02 00:38:30 -07:00
|
|
|
result::{Error, Result},
|
2020-04-06 17:36:22 -07:00
|
|
|
weighted_shuffle::weighted_shuffle,
|
2019-11-02 00:38:30 -07:00
|
|
|
};
|
2020-04-18 22:11:17 -07:00
|
|
|
|
|
|
|
use rand::distributions::{Distribution, WeightedIndex};
|
|
|
|
use rand::SeedableRng;
|
|
|
|
use rand_chacha::ChaChaRng;
|
2020-04-27 11:06:00 -07:00
|
|
|
use solana_sdk::sanitize::{Sanitize, SanitizeError};
|
2020-04-18 22:11:17 -07:00
|
|
|
|
2019-11-06 00:07:57 -08:00
|
|
|
use bincode::{serialize, serialized_size};
|
2019-02-18 08:51:00 -08:00
|
|
|
use core::cmp;
|
2019-06-01 07:55:43 -07:00
|
|
|
use itertools::Itertools;
|
2020-02-19 19:31:55 -08:00
|
|
|
use rayon::iter::IntoParallelIterator;
|
|
|
|
use rayon::iter::ParallelIterator;
|
|
|
|
use rayon::ThreadPool;
|
2020-01-31 14:23:51 -08:00
|
|
|
use solana_ledger::{bank_forks::BankForks, staking_utils};
|
2020-04-18 22:11:17 -07:00
|
|
|
use solana_measure::measure::Measure;
|
2020-01-01 09:51:51 -08:00
|
|
|
use solana_measure::thread_mem_usage;
|
2019-07-30 13:18:33 -07:00
|
|
|
use solana_metrics::{datapoint_debug, inc_new_counter_debug, inc_new_counter_error};
|
2019-11-12 12:37:13 -08:00
|
|
|
use solana_net_utils::{
|
2019-09-19 17:16:22 -07:00
|
|
|
bind_common, bind_common_in_range, bind_in_range, find_available_port_in_range,
|
|
|
|
multi_bind_in_range, PortRange,
|
2019-04-12 18:17:34 -07:00
|
|
|
};
|
2020-03-17 23:30:23 -07:00
|
|
|
use solana_perf::packet::{
|
|
|
|
limited_deserialize, to_packets_with_destination, Packet, Packets, PacketsRecycler,
|
|
|
|
PACKET_DATA_SIZE,
|
|
|
|
};
|
2020-02-19 19:31:55 -08:00
|
|
|
use solana_rayon_threadlimit::get_thread_count;
|
2020-02-20 11:46:13 -08:00
|
|
|
use solana_sdk::hash::Hash;
|
2019-11-02 00:38:30 -07:00
|
|
|
use solana_sdk::{
|
2020-03-11 21:31:50 -07:00
|
|
|
clock::{Slot, DEFAULT_MS_PER_SLOT, DEFAULT_SLOTS_PER_EPOCH},
|
2019-11-02 00:38:30 -07:00
|
|
|
pubkey::Pubkey,
|
2020-03-16 08:37:31 -07:00
|
|
|
signature::{Keypair, Signable, Signature, Signer},
|
2020-05-25 15:03:34 -07:00
|
|
|
timing::timestamp,
|
2019-11-02 00:38:30 -07:00
|
|
|
transaction::Transaction,
|
|
|
|
};
|
2020-04-06 17:36:22 -07:00
|
|
|
use solana_streamer::sendmmsg::multicast;
|
2020-03-17 23:30:23 -07:00
|
|
|
use solana_streamer::streamer::{PacketReceiver, PacketSender};
|
2019-11-02 00:38:30 -07:00
|
|
|
use std::{
|
|
|
|
borrow::Cow,
|
|
|
|
cmp::min,
|
2020-03-11 21:31:50 -07:00
|
|
|
collections::{HashMap, HashSet},
|
2019-11-02 00:38:30 -07:00
|
|
|
fmt,
|
|
|
|
net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, UdpSocket},
|
2020-05-25 15:03:34 -07:00
|
|
|
ops::{Deref, DerefMut},
|
|
|
|
sync::atomic::{AtomicBool, AtomicU64, Ordering},
|
|
|
|
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
2019-11-02 00:38:30 -07:00
|
|
|
thread::{sleep, Builder, JoinHandle},
|
|
|
|
time::{Duration, Instant},
|
|
|
|
};
|
2018-06-14 12:31:52 -07:00
|
|
|
|
2019-10-10 16:33:00 -07:00
|
|
|
pub const VALIDATOR_PORT_RANGE: PortRange = (8000, 10_000);
|
2020-04-14 12:00:36 -07:00
|
|
|
pub const MINIMUM_VALIDATOR_PORT_RANGE_WIDTH: u16 = 10; // VALIDATOR_PORT_RANGE must be at least this wide
|
2018-09-11 18:40:38 -07:00
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
/// The Data plane fanout size, also used as the neighborhood size
|
|
|
|
pub const DATA_PLANE_FANOUT: usize = 200;
|
2018-06-14 22:03:49 -07:00
|
|
|
/// milliseconds we sleep for between gossip requests
|
2019-01-31 15:51:29 -08:00
|
|
|
pub const GOSSIP_SLEEP_MILLIS: u64 = 100;
|
2019-11-14 10:24:53 -08:00
|
|
|
/// The maximum size of a bloom filter
|
2020-03-11 21:31:50 -07:00
|
|
|
pub const MAX_BLOOM_SIZE: usize = MAX_CRDS_OBJECT_SIZE;
|
|
|
|
pub const MAX_CRDS_OBJECT_SIZE: usize = 928;
|
2019-08-13 18:04:14 -07:00
|
|
|
/// The maximum size of a protocol payload
|
2019-11-14 10:24:53 -08:00
|
|
|
const MAX_PROTOCOL_PAYLOAD_SIZE: u64 = PACKET_DATA_SIZE as u64 - MAX_PROTOCOL_HEADER_SIZE;
|
|
|
|
/// The largest protocol header size
|
2020-01-31 14:23:51 -08:00
|
|
|
const MAX_PROTOCOL_HEADER_SIZE: u64 = 214;
|
2020-02-19 19:31:55 -08:00
|
|
|
/// A hard limit on incoming gossip messages
|
|
|
|
/// Chosen to be able to handle 1Gbps of pure gossip traffic
|
|
|
|
/// 128MB/PACKET_DATA_SIZE
|
|
|
|
const MAX_GOSSIP_TRAFFIC: usize = 128_000_000 / PACKET_DATA_SIZE;
|
2019-07-30 15:43:17 -07:00
|
|
|
|
2020-02-29 00:11:20 -08:00
|
|
|
/// Keep the number of snapshot hashes a node publishes under MAX_PROTOCOL_PAYLOAD_SIZE
|
|
|
|
pub const MAX_SNAPSHOT_HASHES: usize = 16;
|
|
|
|
|
2018-07-05 13:37:13 -07:00
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2018-10-08 19:55:54 -07:00
|
|
|
pub enum ClusterInfoError {
|
2018-07-17 09:44:48 -07:00
|
|
|
NoPeers,
|
2018-07-09 15:53:49 -07:00
|
|
|
NoLeader,
|
2018-07-16 19:31:52 -07:00
|
|
|
BadContactInfo,
|
2018-07-17 16:27:46 -07:00
|
|
|
BadGossipAddress,
|
2018-07-05 13:37:13 -07:00
|
|
|
}
|
2019-02-11 16:20:31 -08:00
|
|
|
#[derive(Clone)]
|
2020-04-18 22:11:17 -07:00
|
|
|
pub struct DataBudget {
|
|
|
|
bytes: usize, // amount of bytes we have in the budget to send
|
|
|
|
last_timestamp_ms: u64, // Last time that we upped the bytes count,
|
|
|
|
// used to detect when to up the bytes budget again
|
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
|
2020-05-25 15:03:34 -07:00
|
|
|
struct GossipWriteLock<'a> {
|
|
|
|
gossip: RwLockWriteGuard<'a, CrdsGossip>,
|
|
|
|
timer: Measure,
|
|
|
|
counter: &'a Counter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> GossipWriteLock<'a> {
|
|
|
|
fn new(
|
|
|
|
gossip: RwLockWriteGuard<'a, CrdsGossip>,
|
|
|
|
label: &'static str,
|
|
|
|
counter: &'a Counter,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
gossip,
|
|
|
|
timer: Measure::start(label),
|
|
|
|
counter,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Deref for GossipWriteLock<'a> {
|
|
|
|
type Target = RwLockWriteGuard<'a, CrdsGossip>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.gossip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> DerefMut for GossipWriteLock<'a> {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.gossip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Drop for GossipWriteLock<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.timer.stop();
|
|
|
|
self.counter.add_measure(&mut self.timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct GossipReadLock<'a> {
|
|
|
|
gossip: RwLockReadGuard<'a, CrdsGossip>,
|
|
|
|
timer: Measure,
|
|
|
|
counter: &'a Counter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> GossipReadLock<'a> {
|
|
|
|
fn new(
|
|
|
|
gossip: RwLockReadGuard<'a, CrdsGossip>,
|
|
|
|
label: &'static str,
|
|
|
|
counter: &'a Counter,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
gossip,
|
|
|
|
timer: Measure::start(label),
|
|
|
|
counter,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Deref for GossipReadLock<'a> {
|
|
|
|
type Target = RwLockReadGuard<'a, CrdsGossip>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.gossip
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Drop for GossipReadLock<'a> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.timer.stop();
|
|
|
|
self.counter.add_measure(&mut self.timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct Counter(AtomicU64);
|
|
|
|
|
|
|
|
impl Counter {
|
|
|
|
fn add_measure(&self, x: &mut Measure) {
|
|
|
|
x.stop();
|
|
|
|
self.0.fetch_add(x.as_us(), Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
fn add_relaxed(&self, x: u64) {
|
|
|
|
self.0.fetch_add(x, Ordering::Relaxed);
|
|
|
|
}
|
|
|
|
fn clear(&self) -> u64 {
|
|
|
|
self.0.swap(0, Ordering::Relaxed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct GossipStats {
|
|
|
|
entrypoint: Counter,
|
|
|
|
entrypoint2: Counter,
|
|
|
|
push_vote_read: Counter,
|
|
|
|
vote_process_push: Counter,
|
|
|
|
get_votes: Counter,
|
|
|
|
get_accounts_hash: Counter,
|
|
|
|
get_snapshot_hash: Counter,
|
|
|
|
all_tvu_peers: Counter,
|
|
|
|
tvu_peers: Counter,
|
|
|
|
retransmit_peers: Counter,
|
|
|
|
repair_peers: Counter,
|
|
|
|
new_push_requests: Counter,
|
|
|
|
new_push_requests2: Counter,
|
2020-06-01 08:37:54 -07:00
|
|
|
new_push_requests_num: Counter,
|
2020-05-25 15:03:34 -07:00
|
|
|
process_pull_response: Counter,
|
|
|
|
process_pull_response_count: Counter,
|
|
|
|
process_pull_response_len: Counter,
|
|
|
|
process_pull_response_timeout: Counter,
|
2020-05-28 11:38:13 -07:00
|
|
|
process_pull_response_fail: Counter,
|
|
|
|
process_pull_response_success: Counter,
|
2020-05-25 15:03:34 -07:00
|
|
|
process_pull_requests: Counter,
|
2020-05-28 11:38:13 -07:00
|
|
|
generate_pull_responses: Counter,
|
2020-05-25 15:03:34 -07:00
|
|
|
process_prune: Counter,
|
|
|
|
process_push_message: Counter,
|
|
|
|
prune_received_cache: Counter,
|
|
|
|
purge: Counter,
|
|
|
|
epoch_slots_lookup: Counter,
|
|
|
|
epoch_slots_push: Counter,
|
|
|
|
push_message: Counter,
|
|
|
|
new_pull_requests: Counter,
|
|
|
|
mark_pull_request: Counter,
|
2020-05-28 11:38:13 -07:00
|
|
|
skip_pull_response_shred_version: Counter,
|
|
|
|
skip_pull_shred_version: Counter,
|
|
|
|
skip_push_message_shred_version: Counter,
|
|
|
|
push_message_count: Counter,
|
|
|
|
push_message_value_count: Counter,
|
2020-06-01 08:37:54 -07:00
|
|
|
push_response_count: Counter,
|
2020-05-25 15:03:34 -07:00
|
|
|
}
|
|
|
|
|
2018-10-08 19:55:54 -07:00
|
|
|
pub struct ClusterInfo {
|
2018-11-15 13:23:26 -08:00
|
|
|
/// The network
|
2020-04-21 12:54:45 -07:00
|
|
|
pub gossip: RwLock<CrdsGossip>,
|
2018-12-01 12:00:30 -08:00
|
|
|
/// set the keypair that will be used to sign crds values generated. It is unset only in tests.
|
2018-12-12 18:57:48 -08:00
|
|
|
pub(crate) keypair: Arc<Keypair>,
|
2019-03-08 18:08:24 -08:00
|
|
|
/// The network entrypoint
|
2020-04-21 12:54:45 -07:00
|
|
|
entrypoint: RwLock<Option<ContactInfo>>,
|
|
|
|
outbound_budget: RwLock<DataBudget>,
|
|
|
|
my_contact_info: RwLock<ContactInfo>,
|
|
|
|
id: Pubkey,
|
2020-05-25 15:03:34 -07:00
|
|
|
stats: GossipStats,
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2018-09-17 15:43:23 -07:00
|
|
|
|
2019-01-02 00:46:15 -08:00
|
|
|
#[derive(Default, Clone)]
|
|
|
|
pub struct Locality {
|
|
|
|
/// The bounds of the neighborhood represented by this locality
|
|
|
|
pub neighbor_bounds: (usize, usize),
|
2019-06-06 12:48:40 -07:00
|
|
|
/// The `turbine` layer this locality is in
|
2019-01-02 00:46:15 -08:00
|
|
|
pub layer_ix: usize,
|
|
|
|
/// The bounds of the current layer
|
|
|
|
pub layer_bounds: (usize, usize),
|
|
|
|
/// The bounds of the next layer
|
2019-05-07 13:24:58 -07:00
|
|
|
pub next_layer_bounds: Option<(usize, usize)>,
|
2019-01-02 00:46:15 -08:00
|
|
|
/// The indices of the nodes that should be contacted in next layer
|
2019-05-07 13:24:58 -07:00
|
|
|
pub next_layer_peers: Vec<usize>,
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
|
2019-02-11 16:20:31 -08:00
|
|
|
impl fmt::Debug for Locality {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(
|
|
|
|
f,
|
2019-05-07 13:24:58 -07:00
|
|
|
"Locality {{ neighborhood_bounds: {:?}, current_layer: {:?}, child_layer_bounds: {:?} child_layer_peers: {:?} }}",
|
|
|
|
self.neighbor_bounds, self.layer_ix, self.next_layer_bounds, self.next_layer_peers
|
2019-02-11 16:20:31 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-14 10:24:53 -08:00
|
|
|
#[derive(Debug, Default, Deserialize, Serialize)]
|
2018-12-01 12:00:30 -08:00
|
|
|
pub struct PruneData {
|
|
|
|
/// Pubkey of the node that sent this prune data
|
|
|
|
pub pubkey: Pubkey,
|
|
|
|
/// Pubkeys of nodes that should be pruned
|
|
|
|
pub prunes: Vec<Pubkey>,
|
|
|
|
/// Signature of this Prune Message
|
|
|
|
pub signature: Signature,
|
|
|
|
/// The Pubkey of the intended node/destination for this message
|
|
|
|
pub destination: Pubkey,
|
|
|
|
/// Wallclock of the node that generated this message
|
|
|
|
pub wallclock: u64,
|
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for PruneData {
|
|
|
|
fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
|
|
|
|
if self.wallclock >= MAX_WALLCLOCK {
|
2020-04-29 18:12:51 -07:00
|
|
|
return Err(SanitizeError::ValueOutOfBounds);
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-01 12:00:30 -08:00
|
|
|
impl Signable for PruneData {
|
|
|
|
fn pubkey(&self) -> Pubkey {
|
|
|
|
self.pubkey
|
|
|
|
}
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
fn signable_data(&self) -> Cow<[u8]> {
|
2018-12-01 12:00:30 -08:00
|
|
|
#[derive(Serialize)]
|
|
|
|
struct SignData {
|
|
|
|
pubkey: Pubkey,
|
|
|
|
prunes: Vec<Pubkey>,
|
|
|
|
destination: Pubkey,
|
|
|
|
wallclock: u64,
|
|
|
|
}
|
|
|
|
let data = SignData {
|
|
|
|
pubkey: self.pubkey,
|
|
|
|
prunes: self.prunes.clone(),
|
|
|
|
destination: self.destination,
|
|
|
|
wallclock: self.wallclock,
|
|
|
|
};
|
2019-06-12 16:43:05 -07:00
|
|
|
Cow::Owned(serialize(&data).expect("serialize PruneData"))
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_signature(&self) -> Signature {
|
|
|
|
self.signature
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_signature(&mut self, signature: Signature) {
|
|
|
|
self.signature = signature
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 17:04:45 -07:00
|
|
|
struct PullData {
|
|
|
|
pub from_addr: SocketAddr,
|
|
|
|
pub caller: CrdsValue,
|
|
|
|
pub filter: CrdsFilter,
|
|
|
|
}
|
|
|
|
|
2020-03-16 08:37:31 -07:00
|
|
|
pub fn make_accounts_hashes_message(
|
|
|
|
keypair: &Keypair,
|
|
|
|
accounts_hashes: Vec<(Slot, Hash)>,
|
|
|
|
) -> Option<CrdsValue> {
|
|
|
|
let message = CrdsData::AccountsHashes(SnapshotHash::new(keypair.pubkey(), accounts_hashes));
|
|
|
|
Some(CrdsValue::new_signed(message, keypair))
|
|
|
|
}
|
|
|
|
|
2018-12-01 12:00:30 -08:00
|
|
|
// TODO These messages should go through the gpu pipeline for spam filtering
|
2018-06-03 19:59:17 -07:00
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
2018-12-07 19:01:28 -08:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2018-04-21 11:02:49 -07:00
|
|
|
enum Protocol {
|
2018-12-01 12:00:30 -08:00
|
|
|
/// Gossip protocol messages
|
2019-08-13 18:04:14 -07:00
|
|
|
PullRequest(CrdsFilter, CrdsValue),
|
2018-11-15 13:23:26 -08:00
|
|
|
PullResponse(Pubkey, Vec<CrdsValue>),
|
|
|
|
PushMessage(Pubkey, Vec<CrdsValue>),
|
2018-12-01 12:00:30 -08:00
|
|
|
PruneMessage(Pubkey, PruneData),
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl Sanitize for Protocol {
|
|
|
|
fn sanitize(&self) -> std::result::Result<(), SanitizeError> {
|
|
|
|
match self {
|
|
|
|
Protocol::PullRequest(filter, val) => {
|
|
|
|
filter.sanitize()?;
|
|
|
|
val.sanitize()
|
|
|
|
}
|
|
|
|
Protocol::PullResponse(_, val) => val.sanitize(),
|
|
|
|
Protocol::PushMessage(_, val) => val.sanitize(),
|
|
|
|
Protocol::PruneMessage(_, val) => val.sanitize(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 22:11:17 -07:00
|
|
|
// Rating for pull requests
|
|
|
|
// A response table is generated as a
|
|
|
|
// 2-d table arranged by target nodes and a
|
|
|
|
// list of responses for that node,
|
|
|
|
// to/responses_index is a location in that table.
|
|
|
|
struct ResponseScore {
|
|
|
|
to: usize, // to, index of who the response is to
|
|
|
|
responses_index: usize, // index into the list of responses for a given to
|
|
|
|
score: u64, // Relative score of the response
|
|
|
|
}
|
|
|
|
|
2018-10-08 19:55:54 -07:00
|
|
|
impl ClusterInfo {
|
2019-03-06 13:47:18 -08:00
|
|
|
/// Without a valid keypair gossip will not function. Only useful for tests.
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn new_with_invalid_keypair(contact_info: ContactInfo) -> Self {
|
|
|
|
Self::new(contact_info, Arc::new(Keypair::new()))
|
2018-12-01 12:00:30 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn new(contact_info: ContactInfo, keypair: Arc<Keypair>) -> Self {
|
2020-04-21 12:54:45 -07:00
|
|
|
let id = contact_info.id;
|
|
|
|
let me = Self {
|
|
|
|
gossip: RwLock::new(CrdsGossip::default()),
|
2018-12-01 12:00:30 -08:00
|
|
|
keypair,
|
2020-04-21 12:54:45 -07:00
|
|
|
entrypoint: RwLock::new(None),
|
|
|
|
outbound_budget: RwLock::new(DataBudget {
|
2020-04-18 22:11:17 -07:00
|
|
|
bytes: 0,
|
|
|
|
last_timestamp_ms: 0,
|
2020-04-21 12:54:45 -07:00
|
|
|
}),
|
|
|
|
my_contact_info: RwLock::new(contact_info),
|
|
|
|
id,
|
2020-05-25 15:03:34 -07:00
|
|
|
stats: GossipStats::default(),
|
2018-04-21 11:02:49 -07:00
|
|
|
};
|
2020-05-05 20:15:19 -07:00
|
|
|
{
|
|
|
|
let mut gossip = me.gossip.write().unwrap();
|
|
|
|
gossip.set_self(&id);
|
|
|
|
gossip.set_shred_version(me.my_shred_version());
|
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
me.insert_self();
|
2019-02-20 20:02:47 -08:00
|
|
|
me.push_self(&HashMap::new());
|
2018-11-19 11:25:14 -08:00
|
|
|
me
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
// Should only be used by tests and simulations
|
|
|
|
pub fn clone_with_id(&self, new_id: &Pubkey) -> Self {
|
|
|
|
let mut gossip = self.gossip.read().unwrap().clone();
|
|
|
|
gossip.id = *new_id;
|
|
|
|
let mut my_contact_info = self.my_contact_info.read().unwrap().clone();
|
|
|
|
my_contact_info.id = *new_id;
|
|
|
|
ClusterInfo {
|
|
|
|
gossip: RwLock::new(gossip),
|
|
|
|
keypair: self.keypair.clone(),
|
|
|
|
entrypoint: RwLock::new(self.entrypoint.read().unwrap().clone()),
|
|
|
|
outbound_budget: RwLock::new(self.outbound_budget.read().unwrap().clone()),
|
|
|
|
my_contact_info: RwLock::new(my_contact_info),
|
|
|
|
id: *new_id,
|
2020-05-25 15:03:34 -07:00
|
|
|
stats: GossipStats::default(),
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn update_contact_info<F>(&self, modify: F)
|
|
|
|
where
|
|
|
|
F: FnOnce(&mut ContactInfo) -> (),
|
|
|
|
{
|
|
|
|
let my_id = self.id();
|
|
|
|
modify(&mut self.my_contact_info.write().unwrap());
|
|
|
|
assert_eq!(self.my_contact_info.read().unwrap().id, my_id);
|
|
|
|
self.insert_self()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_self(&self, stakes: &HashMap<Pubkey, u64>) {
|
2018-11-15 13:23:26 -08:00
|
|
|
let now = timestamp();
|
2020-04-21 12:54:45 -07:00
|
|
|
self.my_contact_info.write().unwrap().wallclock = now;
|
|
|
|
let entry =
|
|
|
|
CrdsValue::new_signed(CrdsData::ContactInfo(self.my_contact_info()), &self.keypair);
|
|
|
|
let mut w_gossip = self.gossip.write().unwrap();
|
|
|
|
w_gossip.refresh_push_active_set(stakes);
|
|
|
|
w_gossip.process_push_message(&self.id(), vec![entry], now);
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2019-03-08 18:08:24 -08:00
|
|
|
// TODO kill insert_info, only used by tests
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn insert_info(&self, contact_info: ContactInfo) {
|
2019-11-03 10:07:51 -08:00
|
|
|
let value = CrdsValue::new_signed(CrdsData::ContactInfo(contact_info), &self.keypair);
|
2020-04-21 12:54:45 -07:00
|
|
|
let _ = self.gossip.write().unwrap().crds.insert(value, timestamp());
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn set_entrypoint(&self, entrypoint: ContactInfo) {
|
|
|
|
*self.entrypoint.write().unwrap() = Some(entrypoint)
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
pub fn id(&self) -> Pubkey {
|
2020-04-21 12:54:45 -07:00
|
|
|
self.id
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn lookup_contact_info<F, Y>(&self, id: &Pubkey, map: F) -> Option<Y>
|
|
|
|
where
|
|
|
|
F: FnOnce(&ContactInfo) -> Y,
|
|
|
|
{
|
2019-03-09 19:28:43 -08:00
|
|
|
let entry = CrdsValueLabel::ContactInfo(*id);
|
2018-11-15 13:23:26 -08:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.lookup(&entry)
|
2019-04-15 14:56:08 -07:00
|
|
|
.and_then(CrdsValue::contact_info)
|
2020-04-21 12:54:45 -07:00
|
|
|
.map(map)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn my_contact_info(&self) -> ContactInfo {
|
|
|
|
self.my_contact_info.read().unwrap().clone()
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn my_shred_version(&self) -> u16 {
|
|
|
|
self.my_contact_info.read().unwrap().shred_version
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-03-11 14:42:24 -07:00
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
pub fn lookup_epoch_slots(&self, ix: EpochSlotsIndex) -> EpochSlots {
|
|
|
|
let entry = CrdsValueLabel::EpochSlots(ix, self.id());
|
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2020-03-11 21:31:50 -07:00
|
|
|
.crds
|
|
|
|
.lookup(&entry)
|
|
|
|
.and_then(CrdsValue::epoch_slots)
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_else(|| EpochSlots::new(self.id(), timestamp()))
|
|
|
|
}
|
|
|
|
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn contact_info_trace(&self) -> String {
|
2019-04-18 09:48:21 -07:00
|
|
|
let now = timestamp();
|
|
|
|
let mut spy_nodes = 0;
|
2020-05-05 20:15:19 -07:00
|
|
|
let mut different_shred_nodes = 0;
|
2020-04-21 12:54:45 -07:00
|
|
|
let my_pubkey = self.id();
|
2020-05-05 20:15:19 -07:00
|
|
|
let my_shred_version = self.my_shred_version();
|
2018-09-10 12:06:14 -07:00
|
|
|
let nodes: Vec<_> = self
|
2019-04-18 09:48:21 -07:00
|
|
|
.all_peers()
|
2018-11-15 13:23:26 -08:00
|
|
|
.into_iter()
|
2020-05-05 20:15:19 -07:00
|
|
|
.filter_map(|(node, last_updated)| {
|
2019-04-30 16:42:56 -07:00
|
|
|
if Self::is_spy_node(&node) {
|
2019-04-18 09:48:21 -07:00
|
|
|
spy_nodes += 1;
|
2019-03-07 10:13:54 -08:00
|
|
|
}
|
2020-05-05 20:15:19 -07:00
|
|
|
|
2020-05-11 15:02:01 -07:00
|
|
|
let node_version = self.get_node_version(&node.id);
|
2020-05-05 20:15:19 -07:00
|
|
|
if my_shred_version != 0 && (node.shred_version != 0 && node.shred_version != my_shred_version) {
|
|
|
|
different_shred_nodes += 1;
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
fn addr_to_string(default_ip: &IpAddr, addr: &SocketAddr) -> String {
|
|
|
|
if ContactInfo::is_valid_address(addr) {
|
|
|
|
if &addr.ip() == default_ip {
|
|
|
|
addr.port().to_string()
|
|
|
|
} else {
|
|
|
|
addr.to_string()
|
|
|
|
}
|
2019-11-04 15:05:08 -08:00
|
|
|
} else {
|
2020-05-05 20:15:19 -07:00
|
|
|
"none".to_string()
|
2019-11-04 15:05:08 -08:00
|
|
|
}
|
2019-04-15 13:25:09 -07:00
|
|
|
}
|
2020-05-05 20:15:19 -07:00
|
|
|
let ip_addr = node.gossip.ip();
|
|
|
|
Some(format!(
|
2020-05-11 15:02:01 -07:00
|
|
|
"{:15} {:2}| {:5} | {:44} |{:^15}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {:5}| {}\n",
|
2020-05-05 20:15:19 -07:00
|
|
|
if ContactInfo::is_valid_address(&node.gossip) {
|
|
|
|
ip_addr.to_string()
|
|
|
|
} else {
|
|
|
|
"none".to_string()
|
|
|
|
},
|
|
|
|
if node.id == my_pubkey { "me" } else { "" }.to_string(),
|
|
|
|
now.saturating_sub(last_updated),
|
|
|
|
node.id.to_string(),
|
2020-05-11 15:02:01 -07:00
|
|
|
if let Some(node_version) = node_version {
|
|
|
|
node_version.to_string()
|
|
|
|
} else {
|
|
|
|
"-".to_string()
|
|
|
|
},
|
2020-05-05 20:15:19 -07:00
|
|
|
addr_to_string(&ip_addr, &node.gossip),
|
|
|
|
addr_to_string(&ip_addr, &node.tpu),
|
|
|
|
addr_to_string(&ip_addr, &node.tpu_forwards),
|
|
|
|
addr_to_string(&ip_addr, &node.tvu),
|
|
|
|
addr_to_string(&ip_addr, &node.tvu_forwards),
|
|
|
|
addr_to_string(&ip_addr, &node.repair),
|
|
|
|
addr_to_string(&ip_addr, &node.serve_repair),
|
|
|
|
addr_to_string(&ip_addr, &node.rpc),
|
|
|
|
addr_to_string(&ip_addr, &node.rpc_pubsub),
|
|
|
|
node.shred_version,
|
|
|
|
))
|
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect();
|
2018-09-10 12:06:14 -07:00
|
|
|
|
|
|
|
format!(
|
2019-11-09 00:12:18 -08:00
|
|
|
"IP Address |Age(ms)| Node identifier \
|
2020-05-11 15:02:01 -07:00
|
|
|
| Version |Gossip| TPU |TPUfwd| TVU |TVUfwd|Repair|ServeR| RPC |PubSub|ShredVer\n\
|
|
|
|
------------------+-------+----------------------------------------------+---------------+\
|
|
|
|
------+------+------+------+------+------+------+------+------+--------\n\
|
2019-03-07 10:13:54 -08:00
|
|
|
{}\
|
2020-05-14 18:22:47 -07:00
|
|
|
Nodes: {}{}{}",
|
2018-09-10 12:06:14 -07:00
|
|
|
nodes.join(""),
|
2020-05-14 18:22:47 -07:00
|
|
|
nodes.len() - spy_nodes,
|
2019-04-18 09:48:21 -07:00
|
|
|
if spy_nodes > 0 {
|
|
|
|
format!("\nSpies: {}", spy_nodes)
|
|
|
|
} else {
|
|
|
|
"".to_string()
|
2020-05-05 20:15:19 -07:00
|
|
|
},
|
2020-05-11 15:02:01 -07:00
|
|
|
if different_shred_nodes > 0 {
|
2020-05-05 20:15:19 -07:00
|
|
|
format!(
|
|
|
|
"\nNodes with different shred version: {}",
|
|
|
|
different_shred_nodes
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
"".to_string()
|
2019-04-18 09:48:21 -07:00
|
|
|
}
|
2018-09-10 12:06:14 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_lowest_slot(&self, id: Pubkey, min: Slot) {
|
2020-03-11 21:31:50 -07:00
|
|
|
let now = timestamp();
|
|
|
|
let last = self
|
|
|
|
.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2020-03-11 21:31:50 -07:00
|
|
|
.crds
|
|
|
|
.lookup(&CrdsValueLabel::LowestSlot(self.id()))
|
|
|
|
.and_then(|x| x.lowest_slot())
|
|
|
|
.map(|x| x.lowest)
|
|
|
|
.unwrap_or(0);
|
|
|
|
if min > last {
|
|
|
|
let entry = CrdsValue::new_signed(
|
|
|
|
CrdsData::LowestSlot(0, LowestSlot::new(id, min, now)),
|
|
|
|
&self.keypair,
|
|
|
|
);
|
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-03-11 21:31:50 -07:00
|
|
|
.process_push_message(&self.id(), vec![entry], now);
|
2020-02-17 12:39:30 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_epoch_slots(&self, update: &[Slot]) {
|
2020-03-11 21:31:50 -07:00
|
|
|
let mut num = 0;
|
|
|
|
let mut current_slots: Vec<_> = (0..crds_value::MAX_EPOCH_SLOTS)
|
|
|
|
.filter_map(|ix| {
|
|
|
|
Some((
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock(
|
|
|
|
"lookup_epoch_slots",
|
|
|
|
&self.stats.epoch_slots_lookup,
|
|
|
|
)
|
|
|
|
.crds
|
|
|
|
.lookup(&CrdsValueLabel::EpochSlots(ix, self.id()))
|
|
|
|
.and_then(CrdsValue::epoch_slots)
|
|
|
|
.and_then(|x| Some((x.wallclock, x.first_slot()?)))?,
|
2020-03-11 21:31:50 -07:00
|
|
|
ix,
|
|
|
|
))
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
current_slots.sort();
|
|
|
|
let min_slot: Slot = current_slots
|
|
|
|
.iter()
|
|
|
|
.map(|((_, s), _)| *s)
|
|
|
|
.min()
|
|
|
|
.unwrap_or(0);
|
|
|
|
let max_slot: Slot = update.iter().max().cloned().unwrap_or(0);
|
|
|
|
let total_slots = max_slot as isize - min_slot as isize;
|
|
|
|
// WARN if CRDS is not storing at least a full epoch worth of slots
|
|
|
|
if DEFAULT_SLOTS_PER_EPOCH as isize > total_slots
|
|
|
|
&& crds_value::MAX_EPOCH_SLOTS as usize <= current_slots.len()
|
|
|
|
{
|
|
|
|
inc_new_counter_warn!("cluster_info-epoch_slots-filled", 1);
|
|
|
|
warn!(
|
|
|
|
"EPOCH_SLOTS are filling up FAST {}/{}",
|
|
|
|
total_slots,
|
|
|
|
current_slots.len()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let mut reset = false;
|
|
|
|
let mut epoch_slot_index = current_slots.last().map(|(_, x)| *x).unwrap_or(0);
|
|
|
|
while num < update.len() {
|
|
|
|
let ix = (epoch_slot_index % crds_value::MAX_EPOCH_SLOTS) as u8;
|
|
|
|
let now = timestamp();
|
|
|
|
let mut slots = if !reset {
|
|
|
|
self.lookup_epoch_slots(ix)
|
|
|
|
} else {
|
|
|
|
EpochSlots::new(self.id(), now)
|
|
|
|
};
|
|
|
|
let n = slots.fill(&update[num..], now);
|
|
|
|
if n > 0 {
|
|
|
|
let entry = CrdsValue::new_signed(CrdsData::EpochSlots(ix, slots), &self.keypair);
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_write_lock("epcoh_slots_push", &self.stats.epoch_slots_push)
|
2020-03-11 21:31:50 -07:00
|
|
|
.process_push_message(&self.id(), vec![entry], now);
|
2020-02-20 09:48:39 -08:00
|
|
|
}
|
2020-03-11 21:31:50 -07:00
|
|
|
num += n;
|
|
|
|
if num < update.len() {
|
|
|
|
epoch_slot_index += 1;
|
|
|
|
reset = true;
|
2020-02-20 09:48:39 -08:00
|
|
|
}
|
2020-02-17 12:39:30 -08:00
|
|
|
}
|
|
|
|
}
|
2020-03-16 08:37:31 -07:00
|
|
|
|
2020-05-25 15:03:34 -07:00
|
|
|
fn time_gossip_read_lock<'a>(
|
|
|
|
&'a self,
|
|
|
|
label: &'static str,
|
|
|
|
counter: &'a Counter,
|
|
|
|
) -> GossipReadLock<'a> {
|
|
|
|
GossipReadLock::new(self.gossip.read().unwrap(), label, counter)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn time_gossip_write_lock<'a>(
|
|
|
|
&'a self,
|
|
|
|
label: &'static str,
|
|
|
|
counter: &'a Counter,
|
|
|
|
) -> GossipWriteLock<'a> {
|
|
|
|
GossipWriteLock::new(self.gossip.write().unwrap(), label, counter)
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_message(&self, message: CrdsValue) {
|
2020-03-16 08:37:31 -07:00
|
|
|
let now = message.wallclock();
|
|
|
|
let id = message.pubkey();
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_write_lock("process_push_message", &self.stats.push_message)
|
2020-04-21 12:54:45 -07:00
|
|
|
.process_push_message(&id, vec![message], now);
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_accounts_hashes(&self, accounts_hashes: Vec<(Slot, Hash)>) {
|
2020-03-16 08:37:31 -07:00
|
|
|
if accounts_hashes.len() > MAX_SNAPSHOT_HASHES {
|
|
|
|
warn!(
|
|
|
|
"accounts hashes too large, ignored: {}",
|
|
|
|
accounts_hashes.len(),
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let message = CrdsData::AccountsHashes(SnapshotHash::new(self.id(), accounts_hashes));
|
|
|
|
self.push_message(CrdsValue::new_signed(message, &self.keypair));
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_snapshot_hashes(&self, snapshot_hashes: Vec<(Slot, Hash)>) {
|
2020-02-29 00:11:20 -08:00
|
|
|
if snapshot_hashes.len() > MAX_SNAPSHOT_HASHES {
|
|
|
|
warn!(
|
2020-03-16 08:37:31 -07:00
|
|
|
"snapshot hashes too large, ignored: {}",
|
|
|
|
snapshot_hashes.len(),
|
2020-02-29 00:11:20 -08:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-16 08:37:31 -07:00
|
|
|
let message = CrdsData::SnapshotHashes(SnapshotHash::new(self.id(), snapshot_hashes));
|
|
|
|
self.push_message(CrdsValue::new_signed(message, &self.keypair));
|
2020-02-20 11:46:13 -08:00
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn push_vote(&self, tower_index: usize, vote: Transaction) {
|
2019-01-31 15:51:29 -08:00
|
|
|
let now = timestamp();
|
2019-03-11 16:43:30 -07:00
|
|
|
let vote = Vote::new(&self.id(), vote, now);
|
2020-04-21 12:54:45 -07:00
|
|
|
let vote_ix = {
|
2020-05-25 15:03:34 -07:00
|
|
|
let r_gossip =
|
|
|
|
self.time_gossip_read_lock("gossip_read_push_vote", &self.stats.push_vote_read);
|
2020-04-21 12:54:45 -07:00
|
|
|
let current_votes: Vec<_> = (0..crds_value::MAX_VOTES)
|
|
|
|
.filter_map(|ix| r_gossip.crds.lookup(&CrdsValueLabel::Vote(ix, self.id())))
|
|
|
|
.collect();
|
|
|
|
CrdsValue::compute_vote_index(tower_index, current_votes)
|
|
|
|
};
|
2019-11-04 16:19:54 -08:00
|
|
|
let entry = CrdsValue::new_signed(CrdsData::Vote(vote_ix, vote), &self.keypair);
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_write_lock("push_vote_process_push", &self.stats.vote_process_push)
|
2019-06-26 00:30:16 -07:00
|
|
|
.process_push_message(&self.id(), vec![entry], now);
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Get votes in the crds
|
2019-04-10 17:16:08 -07:00
|
|
|
/// * since - The timestamp of when the vote inserted must be greater than
|
2019-01-31 15:51:29 -08:00
|
|
|
/// since. This allows the bank to query for new votes only.
|
|
|
|
///
|
2019-04-10 17:16:08 -07:00
|
|
|
/// * return - The votes, and the max timestamp from the new set.
|
2020-03-15 20:31:05 -07:00
|
|
|
pub fn get_votes(&self, since: u64) -> (Vec<CrdsValueLabel>, Vec<Transaction>, u64) {
|
|
|
|
let mut max_ts = since;
|
|
|
|
let (labels, txs): (Vec<CrdsValueLabel>, Vec<Transaction>) = self
|
2020-05-25 15:03:34 -07:00
|
|
|
.time_gossip_read_lock("get_votes", &self.stats.get_votes)
|
2019-01-31 15:51:29 -08:00
|
|
|
.crds
|
|
|
|
.table
|
2020-03-15 20:31:05 -07:00
|
|
|
.iter()
|
|
|
|
.filter(|(_, x)| x.insert_timestamp > since)
|
|
|
|
.filter_map(|(label, x)| {
|
|
|
|
max_ts = std::cmp::max(x.insert_timestamp, max_ts);
|
2019-01-31 15:51:29 -08:00
|
|
|
x.value
|
|
|
|
.vote()
|
2020-03-15 20:31:05 -07:00
|
|
|
.map(|v| (label.clone(), v.transaction.clone()))
|
2019-01-31 15:51:29 -08:00
|
|
|
})
|
2020-03-15 20:31:05 -07:00
|
|
|
.unzip();
|
2019-11-20 11:25:18 -08:00
|
|
|
inc_new_counter_info!("cluster_info-get_votes-count", txs.len());
|
2020-03-15 20:31:05 -07:00
|
|
|
(labels, txs, max_ts)
|
2019-01-31 15:51:29 -08:00
|
|
|
}
|
|
|
|
|
2020-02-20 11:46:13 -08:00
|
|
|
pub fn get_snapshot_hash(&self, slot: Slot) -> Vec<(Pubkey, Hash)> {
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("get_snapshot_hash", &self.stats.get_snapshot_hash)
|
2020-02-20 11:46:13 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.snapshot_hash().map(|v| v))
|
|
|
|
.filter_map(|x| {
|
|
|
|
for (table_slot, hash) in &x.hashes {
|
|
|
|
if *table_slot == slot {
|
|
|
|
return Some((x.from, *hash));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn get_accounts_hash_for_node<F, Y>(&self, pubkey: &Pubkey, map: F) -> Option<Y>
|
|
|
|
where
|
|
|
|
F: FnOnce(&Vec<(Slot, Hash)>) -> Y,
|
|
|
|
{
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("get_accounts_hash", &self.stats.get_accounts_hash)
|
2020-03-16 08:37:31 -07:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.get(&CrdsValueLabel::AccountsHashes(*pubkey))
|
|
|
|
.map(|x| &x.value.accounts_hash().unwrap().hashes)
|
2020-04-21 12:54:45 -07:00
|
|
|
.map(map)
|
2020-03-16 08:37:31 -07:00
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn get_snapshot_hash_for_node<F, Y>(&self, pubkey: &Pubkey, map: F) -> Option<Y>
|
|
|
|
where
|
|
|
|
F: FnOnce(&Vec<(Slot, Hash)>) -> Y,
|
|
|
|
{
|
2020-02-20 11:39:53 -08:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2020-02-20 11:39:53 -08:00
|
|
|
.crds
|
|
|
|
.table
|
2020-03-16 08:37:31 -07:00
|
|
|
.get(&CrdsValueLabel::SnapshotHashes(*pubkey))
|
2020-02-20 11:39:53 -08:00
|
|
|
.map(|x| &x.value.snapshot_hash().unwrap().hashes)
|
2020-04-21 12:54:45 -07:00
|
|
|
.map(map)
|
2020-02-20 11:39:53 -08:00
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
pub fn get_lowest_slot_for_node<F, Y>(
|
2019-05-23 03:10:16 -07:00
|
|
|
&self,
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
since: Option<u64>,
|
2020-04-21 12:54:45 -07:00
|
|
|
map: F,
|
|
|
|
) -> Option<Y>
|
|
|
|
where
|
|
|
|
F: FnOnce(&LowestSlot, u64) -> Y,
|
|
|
|
{
|
2019-05-23 03:10:16 -07:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-05-23 03:10:16 -07:00
|
|
|
.crds
|
|
|
|
.table
|
2020-03-11 21:31:50 -07:00
|
|
|
.get(&CrdsValueLabel::LowestSlot(*pubkey))
|
|
|
|
.filter(|x| {
|
|
|
|
since
|
|
|
|
.map(|since| x.insert_timestamp > since)
|
|
|
|
.unwrap_or(true)
|
|
|
|
})
|
2020-04-21 12:54:45 -07:00
|
|
|
.map(|x| map(x.value.lowest_slot().unwrap(), x.insert_timestamp))
|
2020-03-11 21:31:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_epoch_slots_since(&self, since: Option<u64>) -> (Vec<EpochSlots>, Option<u64>) {
|
|
|
|
let vals: Vec<_> = self
|
|
|
|
.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2020-03-11 21:31:50 -07:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
2019-05-23 03:10:16 -07:00
|
|
|
.filter(|x| {
|
|
|
|
since
|
|
|
|
.map(|since| x.insert_timestamp > since)
|
|
|
|
.unwrap_or(true)
|
|
|
|
})
|
2020-04-21 12:54:45 -07:00
|
|
|
.filter_map(|x| Some((x.value.epoch_slots()?.clone(), x.insert_timestamp)))
|
2020-03-11 21:31:50 -07:00
|
|
|
.collect();
|
|
|
|
let max = vals.iter().map(|x| x.1).max().or(since);
|
2020-04-21 12:54:45 -07:00
|
|
|
let vec = vals.into_iter().map(|x| x.0).collect();
|
2020-03-11 21:31:50 -07:00
|
|
|
(vec, max)
|
2019-05-23 03:10:16 -07:00
|
|
|
}
|
|
|
|
|
2020-05-11 15:02:01 -07:00
|
|
|
pub fn get_node_version(&self, pubkey: &Pubkey) -> Option<solana_version::Version> {
|
|
|
|
self.gossip
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.get(&CrdsValueLabel::Version(*pubkey))
|
|
|
|
.map(|x| x.value.version())
|
|
|
|
.flatten()
|
|
|
|
.map(|version| version.version.clone())
|
|
|
|
}
|
|
|
|
|
2020-01-30 13:22:05 -08:00
|
|
|
/// all validators that have a valid rpc port regardless of `shred_version`.
|
|
|
|
pub fn all_rpc_peers(&self) -> Vec<ContactInfo> {
|
2018-11-15 13:23:26 -08:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.table
|
2018-09-15 23:46:16 -07:00
|
|
|
.values()
|
2018-11-15 13:23:26 -08:00
|
|
|
.filter_map(|x| x.value.contact_info())
|
2020-04-21 12:54:45 -07:00
|
|
|
.filter(|x| x.id != self.id() && ContactInfo::is_valid_address(&x.rpc))
|
2018-09-15 23:46:16 -07:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-04-29 13:19:24 -07:00
|
|
|
// All nodes in gossip (including spy nodes) and the last time we heard about them
|
|
|
|
pub(crate) fn all_peers(&self) -> Vec<(ContactInfo, u64)> {
|
2019-04-18 09:48:21 -07:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-04-18 09:48:21 -07:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
2019-04-29 13:19:24 -07:00
|
|
|
.filter_map(|x| {
|
|
|
|
x.value
|
|
|
|
.contact_info()
|
|
|
|
.map(|ci| (ci.clone(), x.local_timestamp))
|
|
|
|
})
|
2019-04-18 09:48:21 -07:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn gossip_peers(&self) -> Vec<ContactInfo> {
|
2020-04-21 12:54:45 -07:00
|
|
|
let me = self.id();
|
2018-11-15 13:23:26 -08:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.contact_info())
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
// shred_version not considered for gossip peers (ie, spy nodes do not set shred_version)
|
|
|
|
.filter(|x| x.id != me && ContactInfo::is_valid_address(&x.gossip))
|
2018-11-15 13:23:26 -08:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
2018-06-14 12:31:52 -07:00
|
|
|
}
|
2018-06-18 23:50:41 -07:00
|
|
|
|
2020-01-30 00:15:37 -08:00
|
|
|
/// all validators that have a valid tvu port regardless of `shred_version`.
|
|
|
|
pub fn all_tvu_peers(&self) -> Vec<ContactInfo> {
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("all_tvu_peers", &self.stats.all_tvu_peers)
|
2020-01-30 00:15:37 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.contact_info())
|
2020-05-14 18:22:47 -07:00
|
|
|
.filter(|x| ContactInfo::is_valid_address(&x.tvu) && x.id != self.id())
|
2020-01-30 00:15:37 -08:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// all validators that have a valid tvu port and are on the same `shred_version`.
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn tvu_peers(&self) -> Vec<ContactInfo> {
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("tvu_peers", &self.stats.tvu_peers)
|
2018-12-11 15:51:47 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.contact_info())
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
.filter(|x| {
|
|
|
|
ContactInfo::is_valid_address(&x.tvu)
|
2020-04-21 12:54:45 -07:00
|
|
|
&& x.id != self.id()
|
|
|
|
&& x.shred_version == self.my_shred_version()
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
})
|
2019-05-16 07:14:58 -07:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2019-01-26 00:28:08 -08:00
|
|
|
/// all peers that have a valid tvu
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn retransmit_peers(&self) -> Vec<ContactInfo> {
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("retransmit_peers", &self.stats.retransmit_peers)
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.contact_info())
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
.filter(|x| {
|
2020-04-21 12:54:45 -07:00
|
|
|
x.id != self.id()
|
|
|
|
&& x.shred_version == self.my_shred_version()
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
&& ContactInfo::is_valid_address(&x.tvu)
|
|
|
|
&& ContactInfo::is_valid_address(&x.tvu_forwards)
|
|
|
|
})
|
2018-11-15 13:23:26 -08:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2018-04-28 00:31:20 -07:00
|
|
|
|
2019-12-05 11:25:13 -08:00
|
|
|
/// all tvu peers with valid gossip addrs that likely have the slot being requested
|
2020-01-31 14:23:51 -08:00
|
|
|
pub fn repair_peers(&self, slot: Slot) -> Vec<ContactInfo> {
|
2020-05-25 15:03:34 -07:00
|
|
|
let mut time = Measure::start("repair_peers");
|
|
|
|
let ret = ClusterInfo::tvu_peers(self)
|
2018-12-11 15:51:47 -08:00
|
|
|
.into_iter()
|
2019-12-05 11:25:13 -08:00
|
|
|
.filter(|x| {
|
2020-04-21 12:54:45 -07:00
|
|
|
x.id != self.id()
|
|
|
|
&& x.shred_version == self.my_shred_version()
|
2020-02-12 09:47:54 -08:00
|
|
|
&& ContactInfo::is_valid_address(&x.serve_repair)
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
&& {
|
2020-04-21 12:54:45 -07:00
|
|
|
self.get_lowest_slot_for_node(&x.id, None, |lowest_slot, _| {
|
|
|
|
lowest_slot.lowest <= slot
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| /* fallback to legacy behavior */ true)
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
}
|
2019-12-05 11:25:13 -08:00
|
|
|
})
|
2020-05-25 15:03:34 -07:00
|
|
|
.collect();
|
|
|
|
self.stats.repair_peers.add_measure(&mut time);
|
|
|
|
ret
|
2018-12-11 15:51:47 -08:00
|
|
|
}
|
|
|
|
|
2019-04-30 16:42:56 -07:00
|
|
|
fn is_spy_node(contact_info: &ContactInfo) -> bool {
|
2020-05-14 18:22:47 -07:00
|
|
|
!ContactInfo::is_valid_address(&contact_info.tpu)
|
2019-04-30 16:42:56 -07:00
|
|
|
|| !ContactInfo::is_valid_address(&contact_info.gossip)
|
2020-05-14 18:22:47 -07:00
|
|
|
|| !ContactInfo::is_valid_address(&contact_info.tvu)
|
2019-04-30 16:42:56 -07:00
|
|
|
}
|
|
|
|
|
2019-10-04 11:52:02 -07:00
|
|
|
fn sorted_stakes_with_index<S: std::hash::BuildHasher>(
|
2019-10-01 09:38:29 -07:00
|
|
|
peers: &[ContactInfo],
|
2019-12-16 17:11:18 -08:00
|
|
|
stakes: Option<Arc<HashMap<Pubkey, u64, S>>>,
|
2019-10-01 09:38:29 -07:00
|
|
|
) -> Vec<(u64, usize)> {
|
2019-10-04 11:52:02 -07:00
|
|
|
let stakes_and_index: Vec<_> = peers
|
2019-10-01 09:38:29 -07:00
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, c)| {
|
2019-10-04 11:52:02 -07:00
|
|
|
// For stake weighted shuffle a valid weight is atleast 1. Weight 0 is
|
|
|
|
// assumed to be missing entry. So let's make sure stake weights are atleast 1
|
2019-12-16 17:11:18 -08:00
|
|
|
let stake = 1.max(
|
|
|
|
stakes
|
|
|
|
.as_ref()
|
|
|
|
.map_or(1, |stakes| *stakes.get(&c.id).unwrap_or(&1)),
|
|
|
|
);
|
2019-10-01 09:38:29 -07:00
|
|
|
(stake, i)
|
|
|
|
})
|
|
|
|
.sorted_by(|(l_stake, l_info), (r_stake, r_info)| {
|
|
|
|
if r_stake == l_stake {
|
|
|
|
peers[*r_info].id.cmp(&peers[*l_info].id)
|
|
|
|
} else {
|
|
|
|
r_stake.cmp(&l_stake)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
stakes_and_index
|
2019-10-04 11:52:02 -07:00
|
|
|
}
|
2019-10-01 09:38:29 -07:00
|
|
|
|
2019-10-04 11:52:02 -07:00
|
|
|
fn stake_weighted_shuffle(
|
|
|
|
stakes_and_index: &[(u64, usize)],
|
2019-10-29 21:02:11 -07:00
|
|
|
seed: [u8; 32],
|
2019-10-04 11:52:02 -07:00
|
|
|
) -> Vec<(u64, usize)> {
|
|
|
|
let stake_weights = stakes_and_index.iter().map(|(w, _)| *w).collect();
|
|
|
|
|
2019-10-29 21:02:11 -07:00
|
|
|
let shuffle = weighted_shuffle(stake_weights, seed);
|
2019-10-04 11:52:02 -07:00
|
|
|
|
|
|
|
shuffle.iter().map(|x| stakes_and_index[*x]).collect()
|
2019-10-01 09:38:29 -07:00
|
|
|
}
|
|
|
|
|
2019-10-04 11:52:02 -07:00
|
|
|
// Return sorted_retransmit_peers(including self) and their stakes
|
|
|
|
pub fn sorted_retransmit_peers_and_stakes(
|
2019-02-20 21:38:16 -08:00
|
|
|
&self,
|
2019-12-16 17:11:18 -08:00
|
|
|
stakes: Option<Arc<HashMap<Pubkey, u64>>>,
|
2019-10-04 11:52:02 -07:00
|
|
|
) -> (Vec<ContactInfo>, Vec<(u64, usize)>) {
|
2019-04-19 21:07:21 -07:00
|
|
|
let mut peers = self.retransmit_peers();
|
2019-10-04 11:52:02 -07:00
|
|
|
// insert "self" into this list for the layer and neighborhood computation
|
2020-04-21 12:54:45 -07:00
|
|
|
peers.push(self.my_contact_info());
|
2019-10-04 11:52:02 -07:00
|
|
|
let stakes_and_index = ClusterInfo::sorted_stakes_with_index(&peers, stakes);
|
|
|
|
(peers, stakes_and_index)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return sorted Retransmit peers and index of `Self.id()` as if it were in that list
|
|
|
|
pub fn shuffle_peers_and_index(
|
2019-10-08 14:41:16 -07:00
|
|
|
id: &Pubkey,
|
2019-10-04 11:52:02 -07:00
|
|
|
peers: &[ContactInfo],
|
|
|
|
stakes_and_index: &[(u64, usize)],
|
2019-10-29 21:02:11 -07:00
|
|
|
seed: [u8; 32],
|
2019-10-04 11:52:02 -07:00
|
|
|
) -> (usize, Vec<(u64, usize)>) {
|
2019-10-29 21:02:11 -07:00
|
|
|
let shuffled_stakes_and_index = ClusterInfo::stake_weighted_shuffle(stakes_and_index, seed);
|
2019-10-04 11:52:02 -07:00
|
|
|
let mut self_index = 0;
|
|
|
|
shuffled_stakes_and_index
|
|
|
|
.iter()
|
2019-04-19 21:07:21 -07:00
|
|
|
.enumerate()
|
2019-10-04 11:52:02 -07:00
|
|
|
.for_each(|(i, (_stake, index))| {
|
2019-10-08 14:41:16 -07:00
|
|
|
if &peers[*index].id == id {
|
2019-10-04 11:52:02 -07:00
|
|
|
self_index = i;
|
2019-04-19 21:07:21 -07:00
|
|
|
}
|
2019-10-04 11:52:02 -07:00
|
|
|
});
|
|
|
|
(self_index, shuffled_stakes_and_index)
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
|
2018-07-09 15:53:49 -07:00
|
|
|
/// compute broadcast table
|
2019-03-08 17:23:07 -08:00
|
|
|
pub fn tpu_peers(&self) -> Vec<ContactInfo> {
|
2018-11-15 13:23:26 -08:00
|
|
|
self.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.values()
|
|
|
|
.filter_map(|x| x.value.contact_info())
|
2020-04-21 12:54:45 -07:00
|
|
|
.filter(|x| x.id != self.id() && ContactInfo::is_valid_address(&x.tpu))
|
2018-11-15 13:23:26 -08:00
|
|
|
.cloned()
|
|
|
|
.collect()
|
2018-07-09 15:53:49 -07:00
|
|
|
}
|
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
/// Given a node count and fanout, it calculates how many layers are needed and at what index each layer begins.
|
|
|
|
pub fn describe_data_plane(nodes: usize, fanout: usize) -> (usize, Vec<usize>) {
|
2019-01-02 00:46:15 -08:00
|
|
|
let mut layer_indices: Vec<usize> = vec![0];
|
|
|
|
if nodes == 0 {
|
|
|
|
(0, vec![])
|
|
|
|
} else if nodes <= fanout {
|
|
|
|
// single layer data plane
|
|
|
|
(1, layer_indices)
|
|
|
|
} else {
|
|
|
|
//layer 1 is going to be the first num fanout nodes, so exclude those
|
|
|
|
let mut remaining_nodes = nodes - fanout;
|
|
|
|
layer_indices.push(fanout);
|
|
|
|
let mut num_layers = 2;
|
2019-05-07 13:24:58 -07:00
|
|
|
// fanout * num_nodes in a neighborhood, which is also fanout.
|
|
|
|
let mut layer_capacity = fanout * fanout;
|
2019-01-02 00:46:15 -08:00
|
|
|
while remaining_nodes > 0 {
|
|
|
|
if remaining_nodes > layer_capacity {
|
|
|
|
// Needs more layers.
|
|
|
|
num_layers += 1;
|
|
|
|
remaining_nodes -= layer_capacity;
|
|
|
|
let end = *layer_indices.last().unwrap();
|
|
|
|
layer_indices.push(layer_capacity + end);
|
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
// Next layer's capacity
|
|
|
|
layer_capacity *= fanout;
|
2019-01-02 00:46:15 -08:00
|
|
|
} else {
|
|
|
|
//everything will now fit in the layers we have
|
|
|
|
let end = *layer_indices.last().unwrap();
|
|
|
|
layer_indices.push(layer_capacity + end);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(num_layers, layer_indices.len() - 1);
|
|
|
|
(num_layers, layer_indices)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn localize_item(
|
|
|
|
layer_indices: &[usize],
|
2019-05-07 13:24:58 -07:00
|
|
|
fanout: usize,
|
2019-01-02 00:46:15 -08:00
|
|
|
select_index: usize,
|
|
|
|
curr_index: usize,
|
2019-11-19 20:15:37 -08:00
|
|
|
) -> Option<Locality> {
|
2019-01-02 00:46:15 -08:00
|
|
|
let end = layer_indices.len() - 1;
|
|
|
|
let next = min(end, curr_index + 1);
|
2019-05-07 13:24:58 -07:00
|
|
|
let layer_start = layer_indices[curr_index];
|
|
|
|
// localized if selected index lies within the current layer's bounds
|
|
|
|
let localized = select_index >= layer_start && select_index < layer_indices[next];
|
2019-01-02 00:46:15 -08:00
|
|
|
if localized {
|
2019-05-07 13:24:58 -07:00
|
|
|
let mut locality = Locality::default();
|
|
|
|
let hood_ix = (select_index - layer_start) / fanout;
|
2019-01-02 00:46:15 -08:00
|
|
|
match curr_index {
|
|
|
|
_ if curr_index == 0 => {
|
|
|
|
locality.layer_ix = 0;
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.layer_bounds = (0, fanout);
|
2019-01-02 00:46:15 -08:00
|
|
|
locality.neighbor_bounds = locality.layer_bounds;
|
2019-05-07 13:24:58 -07:00
|
|
|
|
2019-01-02 00:46:15 -08:00
|
|
|
if next == end {
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds = None;
|
|
|
|
locality.next_layer_peers = vec![];
|
2019-01-02 00:46:15 -08:00
|
|
|
} else {
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds =
|
2019-01-02 00:46:15 -08:00
|
|
|
Some((layer_indices[next], layer_indices[next + 1]));
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_peers = ClusterInfo::next_layer_peers(
|
2019-01-02 00:46:15 -08:00
|
|
|
select_index,
|
2019-05-07 13:24:58 -07:00
|
|
|
hood_ix,
|
2019-01-02 00:46:15 -08:00
|
|
|
layer_indices[next],
|
2019-05-07 13:24:58 -07:00
|
|
|
fanout,
|
2019-01-02 00:46:15 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ if curr_index == end => {
|
|
|
|
locality.layer_ix = end;
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.layer_bounds = (end - fanout, end);
|
2019-01-02 00:46:15 -08:00
|
|
|
locality.neighbor_bounds = locality.layer_bounds;
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds = None;
|
|
|
|
locality.next_layer_peers = vec![];
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
ix => {
|
|
|
|
locality.layer_ix = ix;
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.layer_bounds = (layer_start, layer_indices[next]);
|
2019-01-02 00:46:15 -08:00
|
|
|
locality.neighbor_bounds = (
|
2019-05-07 13:24:58 -07:00
|
|
|
((hood_ix * fanout) + layer_start),
|
|
|
|
((hood_ix + 1) * fanout + layer_start),
|
2019-01-02 00:46:15 -08:00
|
|
|
);
|
2019-05-07 13:24:58 -07:00
|
|
|
|
2019-01-02 00:46:15 -08:00
|
|
|
if next == end {
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds = None;
|
|
|
|
locality.next_layer_peers = vec![];
|
2019-01-02 00:46:15 -08:00
|
|
|
} else {
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds =
|
2019-01-02 00:46:15 -08:00
|
|
|
Some((layer_indices[next], layer_indices[next + 1]));
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_peers = ClusterInfo::next_layer_peers(
|
2019-01-02 00:46:15 -08:00
|
|
|
select_index,
|
2019-05-07 13:24:58 -07:00
|
|
|
hood_ix,
|
2019-01-02 00:46:15 -08:00
|
|
|
layer_indices[next],
|
2019-05-07 13:24:58 -07:00
|
|
|
fanout,
|
2019-01-02 00:46:15 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(locality)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
/// Given a array of layer indices and an index of interest, returns (as a `Locality`) the layer,
|
|
|
|
/// layer-bounds, and neighborhood-bounds in which the index resides
|
|
|
|
fn localize(layer_indices: &[usize], fanout: usize, select_index: usize) -> Locality {
|
2019-01-02 00:46:15 -08:00
|
|
|
(0..layer_indices.len())
|
2019-05-07 13:24:58 -07:00
|
|
|
.find_map(|i| ClusterInfo::localize_item(layer_indices, fanout, select_index, i))
|
2019-01-02 00:46:15 -08:00
|
|
|
.or_else(|| Some(Locality::default()))
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
/// Selects a range in the next layer and chooses nodes from that range as peers for the given index
|
|
|
|
fn next_layer_peers(index: usize, hood_ix: usize, start: usize, fanout: usize) -> Vec<usize> {
|
|
|
|
// Each neighborhood is only tasked with pushing to `fanout` neighborhoods where each neighborhood contains `fanout` nodes.
|
|
|
|
let fanout_nodes = fanout * fanout;
|
|
|
|
// Skip first N nodes, where N is hood_ix * (fanout_nodes)
|
|
|
|
let start = start + (hood_ix * fanout_nodes);
|
|
|
|
let end = start + fanout_nodes;
|
2019-01-02 00:46:15 -08:00
|
|
|
(start..end)
|
2019-05-07 13:24:58 -07:00
|
|
|
.step_by(fanout)
|
|
|
|
.map(|x| x + index % fanout)
|
2019-01-02 00:46:15 -08:00
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// retransmit messages to a list of nodes
|
2018-04-28 00:31:20 -07:00
|
|
|
/// # Remarks
|
2019-07-01 16:49:05 -07:00
|
|
|
/// We need to avoid having obj locked while doing a io, such as the `send_to`
|
2019-01-02 00:46:15 -08:00
|
|
|
pub fn retransmit_to(
|
2019-10-04 11:52:02 -07:00
|
|
|
peers: &[&ContactInfo],
|
2019-10-10 15:02:36 -07:00
|
|
|
packet: &mut Packet,
|
2019-05-23 23:20:04 -07:00
|
|
|
slot_leader_pubkey: Option<Pubkey>,
|
2019-01-02 00:46:15 -08:00
|
|
|
s: &UdpSocket,
|
2019-04-20 16:44:06 -07:00
|
|
|
forwarded: bool,
|
2019-01-02 00:46:15 -08:00
|
|
|
) -> Result<()> {
|
2019-10-08 14:41:16 -07:00
|
|
|
trace!("retransmit orders {}", peers.len());
|
2019-10-10 15:02:36 -07:00
|
|
|
let dests: Vec<_> = peers
|
2019-10-10 13:24:03 -07:00
|
|
|
.iter()
|
2019-05-23 23:20:04 -07:00
|
|
|
.filter(|v| v.id != slot_leader_pubkey.unwrap_or_default())
|
2019-10-10 15:02:36 -07:00
|
|
|
.map(|v| if forwarded { &v.tvu_forwards } else { &v.tvu })
|
2018-12-07 19:01:28 -08:00
|
|
|
.collect();
|
2019-10-10 15:02:36 -07:00
|
|
|
|
|
|
|
let mut sent = 0;
|
|
|
|
while sent < dests.len() {
|
2019-10-10 19:38:48 -07:00
|
|
|
match multicast(s, &mut packet.data[..packet.meta.size], &dests[sent..]) {
|
2019-10-10 15:02:36 -07:00
|
|
|
Ok(n) => sent += n,
|
|
|
|
Err(e) => {
|
|
|
|
inc_new_counter_error!(
|
|
|
|
"cluster_info-retransmit-send_to_error",
|
|
|
|
dests.len() - sent,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
error!("retransmit result {:?}", e);
|
|
|
|
return Err(Error::IO(e));
|
|
|
|
}
|
2018-04-28 00:31:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
fn insert_self(&self) {
|
|
|
|
let value =
|
|
|
|
CrdsValue::new_signed(CrdsData::ContactInfo(self.my_contact_info()), &self.keypair);
|
|
|
|
let _ = self.gossip.write().unwrap().crds.insert(value, timestamp());
|
|
|
|
}
|
|
|
|
|
2019-03-08 18:08:24 -08:00
|
|
|
// If the network entrypoint hasn't been discovered yet, add it to the crds table
|
2020-04-21 12:54:45 -07:00
|
|
|
fn append_entrypoint_to_pulls(
|
|
|
|
&self,
|
|
|
|
pulls: &mut Vec<(Pubkey, CrdsFilter, SocketAddr, CrdsValue)>,
|
|
|
|
) {
|
|
|
|
let pull_from_entrypoint = {
|
|
|
|
let mut w_entrypoint = self.entrypoint.write().unwrap();
|
|
|
|
if let Some(ref mut entrypoint) = &mut *w_entrypoint {
|
|
|
|
if pulls.is_empty() {
|
|
|
|
// Nobody else to pull from, try the entrypoint
|
|
|
|
true
|
2019-10-04 14:18:07 -07:00
|
|
|
} else {
|
2020-04-21 12:54:45 -07:00
|
|
|
let now = timestamp();
|
|
|
|
// Only consider pulling from the entrypoint periodically to avoid spamming it
|
|
|
|
if timestamp() - entrypoint.wallclock <= CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS / 2 {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
entrypoint.wallclock = now;
|
2020-05-25 15:03:34 -07:00
|
|
|
let found_entrypoint = self
|
|
|
|
.time_gossip_read_lock("entrypoint", &self.stats.entrypoint)
|
|
|
|
.crds
|
|
|
|
.table
|
|
|
|
.iter()
|
|
|
|
.any(|(_, v)| {
|
2020-04-21 12:54:45 -07:00
|
|
|
v.value
|
|
|
|
.contact_info()
|
|
|
|
.map(|ci| ci.gossip == entrypoint.gossip)
|
|
|
|
.unwrap_or(false)
|
|
|
|
});
|
|
|
|
!found_entrypoint
|
|
|
|
}
|
2019-10-04 14:18:07 -07:00
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
} else {
|
|
|
|
false
|
2019-10-04 14:18:07 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if pull_from_entrypoint {
|
2020-04-21 12:54:45 -07:00
|
|
|
let id_and_gossip = {
|
|
|
|
self.entrypoint
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.as_ref()
|
|
|
|
.map(|e| (e.id, e.gossip))
|
|
|
|
};
|
|
|
|
if let Some((id, gossip)) = id_and_gossip {
|
2020-05-25 15:03:34 -07:00
|
|
|
let r_gossip = self.time_gossip_read_lock("entrypoint", &self.stats.entrypoint2);
|
2020-04-21 12:54:45 -07:00
|
|
|
let self_info = r_gossip
|
2019-03-08 18:08:24 -08:00
|
|
|
.crds
|
|
|
|
.lookup(&CrdsValueLabel::ContactInfo(self.id()))
|
|
|
|
.unwrap_or_else(|| panic!("self_id invalid {}", self.id()));
|
2020-05-25 15:03:34 -07:00
|
|
|
r_gossip
|
2019-08-13 18:04:14 -07:00
|
|
|
.pull
|
2020-04-21 12:54:45 -07:00
|
|
|
.build_crds_filters(&r_gossip.crds, MAX_BLOOM_SIZE)
|
2019-08-13 18:04:14 -07:00
|
|
|
.into_iter()
|
2020-04-21 12:54:45 -07:00
|
|
|
.for_each(|filter| pulls.push((id, filter, gossip, self_info.clone())));
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-14 12:47:21 -08:00
|
|
|
|
2019-07-30 15:43:17 -07:00
|
|
|
/// Splits a Vec of CrdsValues into a nested Vec, trying to make sure that
|
2020-02-29 00:11:20 -08:00
|
|
|
/// each Vec is no larger than `MAX_PROTOCOL_PAYLOAD_SIZE`
|
2019-07-30 15:43:17 -07:00
|
|
|
/// Note: some messages cannot be contained within that size so in the worst case this returns
|
|
|
|
/// N nested Vecs with 1 item each.
|
2019-11-19 11:51:51 -08:00
|
|
|
fn split_gossip_messages(msgs: Vec<CrdsValue>) -> Vec<Vec<CrdsValue>> {
|
2019-07-30 15:43:17 -07:00
|
|
|
let mut messages = vec![];
|
2019-11-19 11:51:51 -08:00
|
|
|
let mut payload = vec![];
|
|
|
|
let base_size = serialized_size(&payload).expect("Couldn't check size");
|
|
|
|
let max_payload_size = MAX_PROTOCOL_PAYLOAD_SIZE - base_size;
|
|
|
|
let mut payload_size = 0;
|
|
|
|
for msg in msgs {
|
|
|
|
let msg_size = msg.size();
|
|
|
|
// If the message is too big to fit in this batch
|
|
|
|
if payload_size + msg_size > max_payload_size as u64 {
|
|
|
|
// See if it can fit in the next batch
|
|
|
|
if msg_size <= max_payload_size as u64 {
|
|
|
|
if !payload.is_empty() {
|
|
|
|
// Flush the current payload
|
|
|
|
messages.push(payload);
|
|
|
|
// Init the next payload
|
|
|
|
payload = vec![msg];
|
|
|
|
payload_size = msg_size;
|
2019-08-13 18:04:14 -07:00
|
|
|
}
|
2019-11-19 11:51:51 -08:00
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
"dropping message larger than the maximum payload size {:?}",
|
|
|
|
msg
|
|
|
|
);
|
2019-07-30 15:43:17 -07:00
|
|
|
}
|
2019-11-19 11:51:51 -08:00
|
|
|
continue;
|
2019-07-30 15:43:17 -07:00
|
|
|
}
|
2019-11-19 11:51:51 -08:00
|
|
|
payload_size += msg_size;
|
|
|
|
payload.push(msg);
|
|
|
|
}
|
|
|
|
if !payload.is_empty() {
|
2019-07-30 15:43:17 -07:00
|
|
|
messages.push(payload);
|
|
|
|
}
|
|
|
|
messages
|
|
|
|
}
|
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
fn new_pull_requests(&self, stakes: &HashMap<Pubkey, u64>) -> Vec<(SocketAddr, Protocol)> {
|
2018-11-15 13:23:26 -08:00
|
|
|
let now = timestamp();
|
2020-04-21 12:54:45 -07:00
|
|
|
let mut pulls: Vec<_> = {
|
2020-05-25 15:03:34 -07:00
|
|
|
let r_gossip =
|
|
|
|
self.time_gossip_read_lock("new_pull_reqs", &self.stats.new_pull_requests);
|
2020-04-21 12:54:45 -07:00
|
|
|
r_gossip
|
|
|
|
.new_pull_request(now, stakes, MAX_BLOOM_SIZE)
|
|
|
|
.ok()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|(peer, filters, me)| {
|
|
|
|
let peer_label = CrdsValueLabel::ContactInfo(peer);
|
|
|
|
r_gossip
|
|
|
|
.crds
|
|
|
|
.lookup(&peer_label)
|
|
|
|
.and_then(CrdsValue::contact_info)
|
|
|
|
.map(move |peer_info| {
|
|
|
|
filters
|
|
|
|
.into_iter()
|
|
|
|
.map(move |f| (peer, f, peer_info.gossip, me.clone()))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect()
|
|
|
|
};
|
|
|
|
self.append_entrypoint_to_pulls(&mut pulls);
|
2019-08-13 18:04:14 -07:00
|
|
|
pulls
|
|
|
|
.into_iter()
|
2018-12-06 12:52:47 -08:00
|
|
|
.map(|(peer, filter, gossip, self_info)| {
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_write_lock("mark_pull", &self.stats.mark_pull_request)
|
2020-04-21 12:54:45 -07:00
|
|
|
.mark_pull_request_creation_time(&peer, now);
|
2018-12-06 12:52:47 -08:00
|
|
|
(gossip, Protocol::PullRequest(filter, self_info))
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.collect()
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2020-04-21 12:54:45 -07:00
|
|
|
fn new_push_requests(&self) -> Vec<(SocketAddr, Protocol)> {
|
|
|
|
let self_id = self.id();
|
2020-05-25 15:03:34 -07:00
|
|
|
let (_, push_messages) = self
|
|
|
|
.time_gossip_write_lock("new_push_requests", &self.stats.new_push_requests)
|
|
|
|
.new_push_messages(timestamp());
|
2020-06-01 08:37:54 -07:00
|
|
|
let messages: Vec<_> = push_messages
|
2018-11-15 13:23:26 -08:00
|
|
|
.into_iter()
|
2019-05-28 18:39:40 -07:00
|
|
|
.filter_map(|(peer, messages)| {
|
|
|
|
let peer_label = CrdsValueLabel::ContactInfo(peer);
|
2020-05-25 15:03:34 -07:00
|
|
|
self.time_gossip_read_lock("push_req_lookup", &self.stats.new_push_requests2)
|
2018-11-15 13:23:26 -08:00
|
|
|
.crds
|
|
|
|
.lookup(&peer_label)
|
2019-04-15 14:56:08 -07:00
|
|
|
.and_then(CrdsValue::contact_info)
|
2019-05-28 18:39:40 -07:00
|
|
|
.map(|p| (p.gossip, messages))
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
Maintenance : simplify a few patterns, remove unneeded dependencies (#8137)
* Simplify a few pattern matches
* Removing unneeded dependencies, upgrading internal version #s
net-shaper: Removing log, semver, serde_derive
bench-tps: Removing serde, serde_derive
banking-bench: Removing solana
ledger-tool: Removing bincode, serde, serde_derive
librapay: Removing solana, language_e2e_tests
log-analyzer: Removing log, semver, serde_derive
exchange: Removing solana
core: Removing crc, memmap, symlink, untrusted
perf: Removing serde_derive
genesis: Removing hex, serde_derive
sdk-c: Removing sha2
sys-tuner: Removing semver
bench-exchange: Removing bincode, bs58, env_logger, serde, serde_derive, untrusted, ws
btc_spv_bin: Removing serde_json
btc_spv: Removing chrono
bpf_loader: Removing serde
ledger: Removing dlopen, dlopen_derive, serde_derive
move_loader: Removing byteorder, libc, language_e2e_tests
ownable: Removing serde, serde_derive
client: Removing rand
archiver-utils: Removing rand_chacha
validator: Removing serde_json, tempfile
param_passing_dep: Removing solana
failure: Removing log
vest: Removing log
vote-signer: Removing bs58, serde
local-cluster: Removing symlink
keygen: Removing rpassword
install: Removing bs58, log
upload-perf: Removing log
runtime: Removing serde_json
stake: Removing rand
* Add modified Cargo.lock
* fixup! Simplify a few pattern matches
* fixup! Simplify a few pattern matches
2020-02-06 09:02:38 -08:00
|
|
|
.flat_map(|(peer, msgs)| {
|
2019-07-30 15:43:17 -07:00
|
|
|
Self::split_gossip_messages(msgs)
|
|
|
|
.into_iter()
|
|
|
|
.map(move |payload| (peer, Protocol::PushMessage(self_id, payload)))
|
|
|
|
})
|
2020-06-01 08:37:54 -07:00
|
|
|
.collect();
|
|
|
|
self.stats
|
|
|
|
.new_push_requests_num
|
|
|
|
.add_relaxed(messages.len() as u64);
|
|
|
|
messages
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2018-06-18 23:50:41 -07:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
fn gossip_request(&self, stakes: &HashMap<Pubkey, u64>) -> Vec<(SocketAddr, Protocol)> {
|
2019-02-20 20:02:47 -08:00
|
|
|
let pulls: Vec<_> = self.new_pull_requests(stakes);
|
2018-11-15 13:23:26 -08:00
|
|
|
let pushes: Vec<_> = self.new_push_requests();
|
2019-11-14 11:27:01 -08:00
|
|
|
vec![pulls, pushes].into_iter().flatten().collect()
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// At random pick a node and try to get updated changes from them
|
2019-02-20 17:08:56 -08:00
|
|
|
fn run_gossip(
|
2020-04-21 12:54:45 -07:00
|
|
|
obj: &Self,
|
2019-11-20 11:25:18 -08:00
|
|
|
recycler: &PacketsRecycler,
|
2019-02-20 20:02:47 -08:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
2019-11-14 10:24:53 -08:00
|
|
|
sender: &PacketSender,
|
2019-02-20 17:08:56 -08:00
|
|
|
) -> Result<()> {
|
2020-04-21 12:54:45 -07:00
|
|
|
let reqs = obj.gossip_request(&stakes);
|
2019-11-20 11:25:18 -08:00
|
|
|
if !reqs.is_empty() {
|
|
|
|
let packets = to_packets_with_destination(recycler.clone(), &reqs);
|
|
|
|
sender.send(packets)?;
|
|
|
|
}
|
2018-04-21 11:02:49 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2018-10-10 16:49:41 -07:00
|
|
|
|
2018-04-21 11:02:49 -07:00
|
|
|
/// randomly pick a node and ask them for updates asynchronously
|
2018-05-27 18:21:39 -07:00
|
|
|
pub fn gossip(
|
2020-04-21 12:54:45 -07:00
|
|
|
obj: Arc<Self>,
|
2019-02-20 21:36:08 -08:00
|
|
|
bank_forks: Option<Arc<RwLock<BankForks>>>,
|
2019-11-14 10:24:53 -08:00
|
|
|
sender: PacketSender,
|
2019-03-04 20:50:02 -08:00
|
|
|
exit: &Arc<AtomicBool>,
|
2018-05-27 18:21:39 -07:00
|
|
|
) -> JoinHandle<()> {
|
2019-03-04 20:50:02 -08:00
|
|
|
let exit = exit.clone();
|
2018-05-30 13:25:32 -07:00
|
|
|
Builder::new()
|
|
|
|
.name("solana-gossip".to_string())
|
2018-11-15 13:23:26 -08:00
|
|
|
.spawn(move || {
|
|
|
|
let mut last_push = timestamp();
|
2019-08-26 18:31:14 -07:00
|
|
|
let mut last_contact_info_trace = timestamp();
|
2020-04-21 12:54:45 -07:00
|
|
|
let mut adopt_shred_version = obj.my_shred_version() == 0;
|
2019-11-20 11:25:18 -08:00
|
|
|
let recycler = PacketsRecycler::default();
|
2020-05-11 15:02:01 -07:00
|
|
|
|
|
|
|
let message = CrdsData::Version(Version::new(obj.id()));
|
|
|
|
obj.push_message(CrdsValue::new_signed(message, &obj.keypair));
|
2018-11-15 13:23:26 -08:00
|
|
|
loop {
|
|
|
|
let start = timestamp();
|
2019-11-20 11:25:18 -08:00
|
|
|
thread_mem_usage::datapoint("solana-gossip");
|
2019-08-26 18:31:14 -07:00
|
|
|
if start - last_contact_info_trace > 10000 {
|
|
|
|
// Log contact info every 10 seconds
|
2020-04-21 12:54:45 -07:00
|
|
|
info!("\n{}", obj.contact_info_trace());
|
2019-08-26 18:31:14 -07:00
|
|
|
last_contact_info_trace = start;
|
|
|
|
}
|
|
|
|
|
2019-02-20 21:36:08 -08:00
|
|
|
let stakes: HashMap<_, _> = match bank_forks {
|
2019-05-14 16:15:51 -07:00
|
|
|
Some(ref bank_forks) => {
|
|
|
|
staking_utils::staked_nodes(&bank_forks.read().unwrap().working_bank())
|
|
|
|
}
|
2019-02-20 20:02:47 -08:00
|
|
|
None => HashMap::new(),
|
|
|
|
};
|
2019-11-20 11:25:18 -08:00
|
|
|
let _ = Self::run_gossip(&obj, &recycler, &stakes, &sender);
|
2018-11-15 13:23:26 -08:00
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return;
|
|
|
|
}
|
2019-11-20 11:25:18 -08:00
|
|
|
let timeout = {
|
|
|
|
if let Some(ref bank_forks) = bank_forks {
|
|
|
|
let bank = bank_forks.read().unwrap().working_bank();
|
|
|
|
let epoch = bank.epoch();
|
|
|
|
let epoch_schedule = bank.epoch_schedule();
|
|
|
|
epoch_schedule.get_slots_in_epoch(epoch) * DEFAULT_MS_PER_SLOT
|
|
|
|
} else {
|
|
|
|
inc_new_counter_info!("cluster_info-purge-no_working_bank", 1);
|
|
|
|
CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS
|
|
|
|
}
|
|
|
|
};
|
2020-04-21 12:54:45 -07:00
|
|
|
let timeouts = obj.gossip.read().unwrap().make_timeouts(&stakes, timeout);
|
2020-05-25 15:03:34 -07:00
|
|
|
let num_purged = obj
|
|
|
|
.time_gossip_write_lock("purge", &obj.stats.purge)
|
|
|
|
.purge(timestamp(), &timeouts);
|
2019-11-20 11:25:18 -08:00
|
|
|
inc_new_counter_info!("cluster_info-purge-count", num_purged);
|
2020-04-21 12:54:45 -07:00
|
|
|
let table_size = obj.gossip.read().unwrap().crds.table.len();
|
2019-11-20 11:25:18 -08:00
|
|
|
datapoint_debug!(
|
|
|
|
"cluster_info-purge",
|
2020-01-30 13:22:05 -08:00
|
|
|
("table_size", table_size as i64, i64),
|
2019-11-20 11:25:18 -08:00
|
|
|
("purge_stake_timeout", timeout as i64, i64)
|
|
|
|
);
|
2020-01-30 00:15:37 -08:00
|
|
|
// Adopt the entrypoint's `shred_version` if ours is unset
|
|
|
|
if adopt_shred_version {
|
|
|
|
// If gossip was given an entrypoint, lookup its id
|
2020-04-21 12:54:45 -07:00
|
|
|
let entrypoint_id = obj.entrypoint.read().unwrap().as_ref().map(|e| e.id);
|
2020-01-30 00:15:37 -08:00
|
|
|
if let Some(entrypoint_id) = entrypoint_id {
|
|
|
|
// If a pull from the entrypoint was successful, it should exist in the crds table
|
2020-04-21 12:54:45 -07:00
|
|
|
let entrypoint =
|
|
|
|
obj.lookup_contact_info(&entrypoint_id, |ci| ci.clone());
|
2020-01-30 00:15:37 -08:00
|
|
|
if let Some(entrypoint) = entrypoint {
|
|
|
|
if entrypoint.shred_version == 0 {
|
2020-01-30 13:22:05 -08:00
|
|
|
info!("Unable to adopt entrypoint's shred version");
|
2020-01-30 00:15:37 -08:00
|
|
|
} else {
|
2020-01-30 13:22:05 -08:00
|
|
|
info!(
|
|
|
|
"Setting shred version to {:?} from entrypoint {:?}",
|
|
|
|
entrypoint.shred_version, entrypoint.id
|
|
|
|
);
|
2020-04-21 12:54:45 -07:00
|
|
|
obj.my_contact_info.write().unwrap().shred_version =
|
|
|
|
entrypoint.shred_version;
|
2020-05-05 20:15:19 -07:00
|
|
|
obj.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.set_shred_version(entrypoint.shred_version);
|
2020-04-21 12:54:45 -07:00
|
|
|
obj.insert_self();
|
2020-01-30 00:15:37 -08:00
|
|
|
adopt_shred_version = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
//TODO: possibly tune this parameter
|
|
|
|
//we saw a deadlock passing an obj.read().unwrap().timeout into sleep
|
|
|
|
if start - last_push > CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS / 2 {
|
2020-04-21 12:54:45 -07:00
|
|
|
obj.push_self(&stakes);
|
2018-11-15 13:23:26 -08:00
|
|
|
last_push = timestamp();
|
|
|
|
}
|
|
|
|
let elapsed = timestamp() - start;
|
|
|
|
if GOSSIP_SLEEP_MILLIS > elapsed {
|
|
|
|
let time_left = GOSSIP_SLEEP_MILLIS - elapsed;
|
|
|
|
sleep(Duration::from_millis(time_left));
|
|
|
|
}
|
2018-06-23 16:08:53 -07:00
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.unwrap()
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2019-02-07 15:10:54 -08:00
|
|
|
|
2020-02-14 11:11:55 -08:00
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2019-11-14 10:24:53 -08:00
|
|
|
fn handle_packets(
|
2020-04-21 12:54:45 -07:00
|
|
|
me: &Self,
|
2019-11-20 11:25:18 -08:00
|
|
|
recycler: &PacketsRecycler,
|
2019-06-26 00:30:16 -07:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
2019-11-14 10:24:53 -08:00
|
|
|
packets: Packets,
|
|
|
|
response_sender: &PacketSender,
|
2020-02-07 12:38:24 -08:00
|
|
|
epoch_ms: u64,
|
2019-08-15 17:04:45 -07:00
|
|
|
) {
|
2019-11-14 11:49:31 -08:00
|
|
|
// iter over the packets, collect pulls separately and process everything else
|
2019-11-21 18:39:29 -08:00
|
|
|
let allocated = thread_mem_usage::Allocatedp::default();
|
2019-08-15 17:04:45 -07:00
|
|
|
let mut gossip_pull_data: Vec<PullData> = vec![];
|
2020-04-21 12:54:45 -07:00
|
|
|
let timeouts = me.gossip.read().unwrap().make_timeouts(&stakes, epoch_ms);
|
2019-11-14 10:24:53 -08:00
|
|
|
packets.packets.iter().for_each(|packet| {
|
|
|
|
let from_addr = packet.meta.addr();
|
|
|
|
limited_deserialize(&packet.data[..packet.meta.size])
|
2019-08-15 17:04:45 -07:00
|
|
|
.into_iter()
|
2020-04-27 11:06:00 -07:00
|
|
|
.filter(|r: &Protocol| r.sanitize().is_ok())
|
2019-08-15 17:04:45 -07:00
|
|
|
.for_each(|request| match request {
|
|
|
|
Protocol::PullRequest(filter, caller) => {
|
2019-11-20 11:25:18 -08:00
|
|
|
let start = allocated.get();
|
2019-08-15 17:04:45 -07:00
|
|
|
if !caller.verify() {
|
2020-04-10 10:21:46 -07:00
|
|
|
inc_new_counter_info!(
|
2019-08-15 17:04:45 -07:00
|
|
|
"cluster_info-gossip_pull_request_verify_fail",
|
|
|
|
1
|
|
|
|
);
|
2019-11-06 14:32:37 -08:00
|
|
|
} else if let Some(contact_info) = caller.contact_info() {
|
2020-04-21 12:54:45 -07:00
|
|
|
if contact_info.id == me.id() {
|
2019-08-15 17:04:45 -07:00
|
|
|
warn!("PullRequest ignored, I'm talking to myself");
|
|
|
|
inc_new_counter_debug!("cluster_info-window-request-loopback", 1);
|
2020-05-28 11:38:13 -07:00
|
|
|
} else if contact_info.shred_version == 0
|
|
|
|
|| contact_info.shred_version == me.my_shred_version()
|
|
|
|
|| me.my_shred_version() == 0
|
|
|
|
{
|
2019-08-15 17:04:45 -07:00
|
|
|
gossip_pull_data.push(PullData {
|
|
|
|
from_addr,
|
|
|
|
caller,
|
|
|
|
filter,
|
|
|
|
});
|
2020-05-28 11:38:13 -07:00
|
|
|
} else {
|
|
|
|
me.stats.skip_pull_shred_version.add_relaxed(1);
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
}
|
2019-11-20 11:25:18 -08:00
|
|
|
datapoint_debug!(
|
|
|
|
"solana-gossip-listen-memory",
|
|
|
|
("pull_request", (allocated.get() - start) as i64, i64),
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
Protocol::PullResponse(from, mut data) => {
|
2019-11-20 11:25:18 -08:00
|
|
|
let start = allocated.get();
|
2019-08-15 17:04:45 -07:00
|
|
|
data.retain(|v| {
|
|
|
|
let ret = v.verify();
|
|
|
|
if !ret {
|
2020-04-10 10:21:46 -07:00
|
|
|
inc_new_counter_info!(
|
2019-08-15 17:04:45 -07:00
|
|
|
"cluster_info-gossip_pull_response_verify_fail",
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
});
|
2020-02-07 12:38:24 -08:00
|
|
|
Self::handle_pull_response(me, &from, data, &timeouts);
|
2019-11-20 11:25:18 -08:00
|
|
|
datapoint_debug!(
|
|
|
|
"solana-gossip-listen-memory",
|
|
|
|
("pull_response", (allocated.get() - start) as i64, i64),
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
Protocol::PushMessage(from, mut data) => {
|
2019-11-20 11:25:18 -08:00
|
|
|
let start = allocated.get();
|
2019-08-15 17:04:45 -07:00
|
|
|
data.retain(|v| {
|
|
|
|
let ret = v.verify();
|
|
|
|
if !ret {
|
2020-04-10 10:21:46 -07:00
|
|
|
inc_new_counter_info!(
|
2019-08-15 17:04:45 -07:00
|
|
|
"cluster_info-gossip_push_msg_verify_fail",
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
ret
|
|
|
|
});
|
2019-11-20 11:25:18 -08:00
|
|
|
let rsp = Self::handle_push_message(me, recycler, &from, data, stakes);
|
|
|
|
if let Some(rsp) = rsp {
|
|
|
|
let _ignore_disconnect = response_sender.send(rsp);
|
|
|
|
}
|
|
|
|
datapoint_debug!(
|
|
|
|
"solana-gossip-listen-memory",
|
|
|
|
("push_message", (allocated.get() - start) as i64, i64),
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
Protocol::PruneMessage(from, data) => {
|
2019-11-20 11:25:18 -08:00
|
|
|
let start = allocated.get();
|
2019-08-15 17:04:45 -07:00
|
|
|
if data.verify() {
|
|
|
|
inc_new_counter_debug!("cluster_info-prune_message", 1);
|
|
|
|
inc_new_counter_debug!(
|
|
|
|
"cluster_info-prune_message-size",
|
|
|
|
data.prunes.len()
|
|
|
|
);
|
2020-05-25 15:03:34 -07:00
|
|
|
match me
|
|
|
|
.time_gossip_write_lock("process_prune", &me.stats.process_prune)
|
|
|
|
.process_prune_msg(
|
|
|
|
&from,
|
|
|
|
&data.destination,
|
|
|
|
&data.prunes,
|
|
|
|
data.wallclock,
|
|
|
|
timestamp(),
|
|
|
|
) {
|
2019-08-15 17:04:45 -07:00
|
|
|
Err(CrdsGossipError::PruneMessageTimeout) => {
|
|
|
|
inc_new_counter_debug!("cluster_info-prune_message_timeout", 1)
|
|
|
|
}
|
|
|
|
Err(CrdsGossipError::BadPruneDestination) => {
|
|
|
|
inc_new_counter_debug!("cluster_info-bad_prune_destination", 1)
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
inc_new_counter_debug!("cluster_info-gossip_prune_msg_verify_fail", 1);
|
|
|
|
}
|
2019-11-20 11:25:18 -08:00
|
|
|
datapoint_debug!(
|
|
|
|
"solana-gossip-listen-memory",
|
|
|
|
("prune_message", (allocated.get() - start) as i64, i64),
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
// process the collected pulls together
|
2020-04-18 22:11:17 -07:00
|
|
|
let rsp = Self::handle_pull_requests(me, recycler, gossip_pull_data, stakes);
|
2019-11-20 11:25:18 -08:00
|
|
|
if let Some(rsp) = rsp {
|
|
|
|
let _ignore_disconnect = response_sender.send(rsp);
|
|
|
|
}
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
|
2020-05-28 11:38:13 -07:00
|
|
|
fn update_data_budget(&self, stakes: &HashMap<Pubkey, u64>) {
|
|
|
|
let mut w_outbound_budget = self.outbound_budget.write().unwrap();
|
|
|
|
|
|
|
|
let now = timestamp();
|
|
|
|
const INTERVAL_MS: u64 = 100;
|
|
|
|
// allow 50kBps per staked validator, epoch slots + votes ~= 1.5kB/slot ~= 4kB/s
|
|
|
|
const BYTES_PER_INTERVAL: usize = 5000;
|
|
|
|
const MAX_BUDGET_MULTIPLE: usize = 5; // allow budget build-up to 5x the interval default
|
|
|
|
|
|
|
|
if now - w_outbound_budget.last_timestamp_ms > INTERVAL_MS {
|
|
|
|
let len = std::cmp::max(stakes.len(), 2);
|
|
|
|
w_outbound_budget.bytes += len * BYTES_PER_INTERVAL;
|
|
|
|
w_outbound_budget.bytes = std::cmp::min(
|
|
|
|
w_outbound_budget.bytes,
|
|
|
|
MAX_BUDGET_MULTIPLE * len * BYTES_PER_INTERVAL,
|
|
|
|
);
|
|
|
|
w_outbound_budget.last_timestamp_ms = now;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-18 22:11:17 -07:00
|
|
|
// Pull requests take an incoming bloom filter of contained entries from a node
|
|
|
|
// and tries to send back to them the values it detects are missing.
|
2019-11-20 11:25:18 -08:00
|
|
|
fn handle_pull_requests(
|
2020-04-21 12:54:45 -07:00
|
|
|
me: &Self,
|
2019-11-20 11:25:18 -08:00
|
|
|
recycler: &PacketsRecycler,
|
|
|
|
requests: Vec<PullData>,
|
2020-04-18 22:11:17 -07:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
2019-11-20 11:25:18 -08:00
|
|
|
) -> Option<Packets> {
|
2019-08-15 17:04:45 -07:00
|
|
|
// split the requests into addrs and filters
|
|
|
|
let mut caller_and_filters = vec![];
|
|
|
|
let mut addrs = vec![];
|
2020-04-18 22:11:17 -07:00
|
|
|
let mut time = Measure::start("handle_pull_requests");
|
2020-05-28 11:38:13 -07:00
|
|
|
me.update_data_budget(stakes);
|
2019-08-15 17:04:45 -07:00
|
|
|
for pull_data in requests {
|
|
|
|
caller_and_filters.push((pull_data.caller, pull_data.filter));
|
|
|
|
addrs.push(pull_data.from_addr);
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
let now = timestamp();
|
2020-04-21 12:54:45 -07:00
|
|
|
let self_id = me.id();
|
2020-05-28 11:38:13 -07:00
|
|
|
|
2019-08-15 17:04:45 -07:00
|
|
|
let pull_responses = me
|
2020-05-28 11:38:13 -07:00
|
|
|
.time_gossip_read_lock("generate_pull_responses", &me.stats.generate_pull_responses)
|
|
|
|
.generate_pull_responses(&caller_and_filters);
|
|
|
|
|
|
|
|
me.time_gossip_write_lock("process_pull_reqs", &me.stats.process_pull_requests)
|
2019-08-15 17:04:45 -07:00
|
|
|
.process_pull_requests(caller_and_filters, now);
|
2020-04-18 22:11:17 -07:00
|
|
|
|
|
|
|
// Filter bad to addresses
|
|
|
|
let pull_responses: Vec<_> = pull_responses
|
2019-07-30 15:43:17 -07:00
|
|
|
.into_iter()
|
2019-08-15 17:04:45 -07:00
|
|
|
.zip(addrs.into_iter())
|
2020-04-18 22:11:17 -07:00
|
|
|
.filter_map(|(responses, from_addr)| {
|
|
|
|
if !from_addr.ip().is_unspecified()
|
|
|
|
&& from_addr.port() != 0
|
|
|
|
&& !responses.is_empty()
|
|
|
|
{
|
|
|
|
Some((responses, from_addr))
|
2020-02-18 16:14:20 -08:00
|
|
|
} else {
|
2020-04-18 22:11:17 -07:00
|
|
|
None
|
2020-02-18 16:14:20 -08:00
|
|
|
}
|
2020-04-18 22:11:17 -07:00
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
if pull_responses.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut stats: Vec<_> = pull_responses
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(i, (responses, _from_addr))| {
|
|
|
|
let score: u64 = if stakes.get(&responses[0].pubkey()).is_some() {
|
|
|
|
2
|
|
|
|
} else {
|
|
|
|
1
|
|
|
|
};
|
|
|
|
responses
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(j, _response)| ResponseScore {
|
|
|
|
to: i,
|
|
|
|
responses_index: j,
|
|
|
|
score,
|
|
|
|
})
|
|
|
|
.collect::<Vec<ResponseScore>>()
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
stats.sort_by(|a, b| a.score.cmp(&b.score));
|
|
|
|
let weights: Vec<_> = stats.iter().map(|stat| stat.score).collect();
|
|
|
|
|
|
|
|
let seed = [48u8; 32];
|
|
|
|
let rng = &mut ChaChaRng::from_seed(seed);
|
|
|
|
let weighted_index = WeightedIndex::new(weights).unwrap();
|
|
|
|
|
|
|
|
let mut packets = Packets::new_with_recycler(recycler.clone(), 64, "handle_pull_requests");
|
|
|
|
let mut total_bytes = 0;
|
|
|
|
let mut sent = HashSet::new();
|
|
|
|
while sent.len() < stats.len() {
|
|
|
|
let index = weighted_index.sample(rng);
|
|
|
|
if sent.contains(&index) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let stat = &stats[index];
|
|
|
|
let from_addr = pull_responses[stat.to].1;
|
|
|
|
let response = pull_responses[stat.to].0[stat.responses_index].clone();
|
|
|
|
let protocol = Protocol::PullResponse(self_id, vec![response]);
|
2020-04-21 12:54:45 -07:00
|
|
|
let new_packet = Packet::from_data(&from_addr, protocol);
|
|
|
|
{
|
|
|
|
let mut w_outbound_budget = me.outbound_budget.write().unwrap();
|
|
|
|
if w_outbound_budget.bytes > new_packet.meta.size {
|
|
|
|
sent.insert(index);
|
|
|
|
w_outbound_budget.bytes -= new_packet.meta.size;
|
|
|
|
total_bytes += new_packet.meta.size;
|
|
|
|
packets.packets.push(new_packet)
|
|
|
|
} else {
|
|
|
|
inc_new_counter_info!("gossip_pull_request-no_budget", 1);
|
|
|
|
break;
|
|
|
|
}
|
2020-04-18 22:11:17 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
time.stop();
|
|
|
|
inc_new_counter_info!("gossip_pull_request-sent_requests", sent.len());
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"gossip_pull_request-dropped_requests",
|
|
|
|
stats.len() - sent.len()
|
|
|
|
);
|
|
|
|
debug!(
|
|
|
|
"handle_pull_requests: {} sent: {} total: {} total_bytes: {}",
|
|
|
|
time,
|
|
|
|
sent.len(),
|
|
|
|
stats.len(),
|
|
|
|
total_bytes
|
|
|
|
);
|
2019-11-20 11:25:18 -08:00
|
|
|
if packets.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(packets)
|
2018-07-18 17:07:01 -07:00
|
|
|
}
|
2019-06-26 00:30:16 -07:00
|
|
|
|
2020-05-28 11:38:13 -07:00
|
|
|
// Returns (failed, timeout, success)
|
2020-02-07 12:38:24 -08:00
|
|
|
fn handle_pull_response(
|
2020-04-21 12:54:45 -07:00
|
|
|
me: &Self,
|
2020-02-07 12:38:24 -08:00
|
|
|
from: &Pubkey,
|
2020-05-28 11:38:13 -07:00
|
|
|
mut crds_values: Vec<CrdsValue>,
|
2020-02-07 12:38:24 -08:00
|
|
|
timeouts: &HashMap<Pubkey, u64>,
|
2020-05-28 11:38:13 -07:00
|
|
|
) -> (usize, usize, usize) {
|
|
|
|
let len = crds_values.len();
|
2020-05-25 15:03:34 -07:00
|
|
|
trace!("PullResponse me: {} from: {} len={}", me.id, from, len);
|
2020-05-28 11:38:13 -07:00
|
|
|
|
|
|
|
if let Some(shred_version) = me.lookup_contact_info(from, |ci| ci.shred_version) {
|
|
|
|
Self::filter_by_shred_version(
|
|
|
|
from,
|
|
|
|
&mut crds_values,
|
|
|
|
shred_version,
|
|
|
|
me.my_shred_version(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let filtered_len = crds_values.len();
|
|
|
|
|
|
|
|
let (fail, timeout_count, success) = me
|
2020-05-25 15:03:34 -07:00
|
|
|
.time_gossip_write_lock("process_pull", &me.stats.process_pull_response)
|
2020-05-28 11:38:13 -07:00
|
|
|
.process_pull_response(from, timeouts, crds_values, timestamp());
|
|
|
|
|
|
|
|
me.stats
|
|
|
|
.skip_pull_response_shred_version
|
|
|
|
.add_relaxed((len - filtered_len) as u64);
|
2020-05-25 15:03:34 -07:00
|
|
|
me.stats.process_pull_response_count.add_relaxed(1);
|
2020-05-28 11:38:13 -07:00
|
|
|
me.stats
|
|
|
|
.process_pull_response_len
|
|
|
|
.add_relaxed(filtered_len as u64);
|
2020-05-25 15:03:34 -07:00
|
|
|
me.stats
|
|
|
|
.process_pull_response_timeout
|
|
|
|
.add_relaxed(timeout_count as u64);
|
2020-05-28 11:38:13 -07:00
|
|
|
me.stats.process_pull_response_fail.add_relaxed(fail as u64);
|
|
|
|
me.stats
|
|
|
|
.process_pull_response_success
|
|
|
|
.add_relaxed(success as u64);
|
|
|
|
|
|
|
|
(fail, timeout_count, success)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn filter_by_shred_version(
|
|
|
|
from: &Pubkey,
|
|
|
|
crds_values: &mut Vec<CrdsValue>,
|
|
|
|
shred_version: u16,
|
|
|
|
my_shred_version: u16,
|
|
|
|
) {
|
|
|
|
if my_shred_version != 0 && shred_version != 0 && shred_version != my_shred_version {
|
|
|
|
// Allow someone to update their own ContactInfo so they
|
|
|
|
// can change shred versions if needed.
|
|
|
|
crds_values.retain(|crds_value| match &crds_value.data {
|
|
|
|
CrdsData::ContactInfo(contact_info) => contact_info.id == *from,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2019-06-26 00:30:16 -07:00
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
fn handle_push_message(
|
2020-04-21 12:54:45 -07:00
|
|
|
me: &Self,
|
2019-11-20 11:25:18 -08:00
|
|
|
recycler: &PacketsRecycler,
|
2019-03-09 19:28:43 -08:00
|
|
|
from: &Pubkey,
|
2020-05-28 11:38:13 -07:00
|
|
|
mut crds_values: Vec<CrdsValue>,
|
2019-06-26 00:30:16 -07:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
2019-11-20 11:25:18 -08:00
|
|
|
) -> Option<Packets> {
|
2020-04-21 12:54:45 -07:00
|
|
|
let self_id = me.id();
|
2020-05-28 11:38:13 -07:00
|
|
|
me.stats.push_message_count.add_relaxed(1);
|
|
|
|
let len = crds_values.len();
|
|
|
|
|
|
|
|
if let Some(shred_version) = me.lookup_contact_info(from, |ci| ci.shred_version) {
|
|
|
|
Self::filter_by_shred_version(
|
|
|
|
from,
|
|
|
|
&mut crds_values,
|
|
|
|
shred_version,
|
|
|
|
me.my_shred_version(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let filtered_len = crds_values.len();
|
|
|
|
me.stats
|
|
|
|
.push_message_value_count
|
|
|
|
.add_relaxed(filtered_len as u64);
|
|
|
|
me.stats
|
|
|
|
.skip_push_message_shred_version
|
|
|
|
.add_relaxed((len - filtered_len) as u64);
|
2019-05-23 03:50:41 -07:00
|
|
|
|
2020-05-25 15:03:34 -07:00
|
|
|
let updated: Vec<_> = me
|
|
|
|
.time_gossip_write_lock("process_push", &me.stats.process_push_message)
|
2020-05-28 11:38:13 -07:00
|
|
|
.process_push_message(from, crds_values, timestamp());
|
2019-06-26 00:30:16 -07:00
|
|
|
|
|
|
|
let updated_labels: Vec<_> = updated.into_iter().map(|u| u.value.label()).collect();
|
|
|
|
let prunes_map: HashMap<Pubkey, HashSet<Pubkey>> = me
|
2020-05-25 15:03:34 -07:00
|
|
|
.time_gossip_write_lock("prune_received_cache", &me.stats.prune_received_cache)
|
2019-06-26 00:30:16 -07:00
|
|
|
.prune_received_cache(updated_labels, stakes);
|
2019-05-23 03:50:41 -07:00
|
|
|
|
2019-11-14 10:24:53 -08:00
|
|
|
let rsp: Vec<_> = prunes_map
|
2019-06-26 00:30:16 -07:00
|
|
|
.into_iter()
|
2019-11-14 10:24:53 -08:00
|
|
|
.filter_map(|(from, prune_set)| {
|
2019-06-26 00:30:16 -07:00
|
|
|
inc_new_counter_debug!("cluster_info-push_message-prunes", prune_set.len());
|
2020-04-21 12:54:45 -07:00
|
|
|
me.lookup_contact_info(&from, |ci| ci.clone())
|
|
|
|
.and_then(|ci| {
|
|
|
|
let mut prune_msg = PruneData {
|
|
|
|
pubkey: self_id,
|
|
|
|
prunes: prune_set.into_iter().collect(),
|
|
|
|
signature: Signature::default(),
|
|
|
|
destination: from,
|
|
|
|
wallclock: timestamp(),
|
|
|
|
};
|
|
|
|
prune_msg.sign(&me.keypair);
|
|
|
|
let rsp = Protocol::PruneMessage(self_id, prune_msg);
|
|
|
|
Some((ci.gossip, rsp))
|
|
|
|
})
|
2019-06-26 00:30:16 -07:00
|
|
|
})
|
|
|
|
.collect();
|
2019-11-20 11:25:18 -08:00
|
|
|
if rsp.is_empty() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let mut packets = to_packets_with_destination(recycler.clone(), &rsp);
|
2020-06-01 08:37:54 -07:00
|
|
|
me.stats
|
|
|
|
.push_response_count
|
|
|
|
.add_relaxed(packets.packets.len() as u64);
|
2019-11-14 10:24:53 -08:00
|
|
|
if !packets.is_empty() {
|
2020-04-21 12:54:45 -07:00
|
|
|
let pushes: Vec<_> = me.new_push_requests();
|
2019-06-26 00:30:16 -07:00
|
|
|
inc_new_counter_debug!("cluster_info-push_message-pushes", pushes.len());
|
2019-11-14 10:24:53 -08:00
|
|
|
pushes.into_iter().for_each(|(remote_gossip_addr, req)| {
|
2020-02-18 16:14:20 -08:00
|
|
|
if !remote_gossip_addr.ip().is_unspecified() && remote_gossip_addr.port() != 0 {
|
|
|
|
let p = Packet::from_data(&remote_gossip_addr, &req);
|
|
|
|
packets.packets.push(p);
|
|
|
|
} else {
|
|
|
|
trace!("Dropping Gossip push response, as destination is unknown");
|
|
|
|
}
|
2019-11-14 10:24:53 -08:00
|
|
|
});
|
2019-11-20 11:25:18 -08:00
|
|
|
Some(packets)
|
2018-11-15 13:23:26 -08:00
|
|
|
} else {
|
2019-11-20 11:25:18 -08:00
|
|
|
None
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
2019-04-06 19:41:22 -07:00
|
|
|
|
2018-05-27 18:21:39 -07:00
|
|
|
/// Process messages from the network
|
|
|
|
fn run_listen(
|
2020-04-21 12:54:45 -07:00
|
|
|
obj: &Self,
|
2019-11-20 11:25:18 -08:00
|
|
|
recycler: &PacketsRecycler,
|
2019-06-26 00:30:16 -07:00
|
|
|
bank_forks: Option<&Arc<RwLock<BankForks>>>,
|
2019-11-14 10:24:53 -08:00
|
|
|
requests_receiver: &PacketReceiver,
|
|
|
|
response_sender: &PacketSender,
|
2020-02-19 19:31:55 -08:00
|
|
|
thread_pool: &ThreadPool,
|
2020-05-25 15:03:34 -07:00
|
|
|
last_print: &mut Instant,
|
2018-05-27 18:21:39 -07:00
|
|
|
) -> Result<()> {
|
|
|
|
//TODO cache connections
|
|
|
|
let timeout = Duration::new(1, 0);
|
2020-02-19 19:31:55 -08:00
|
|
|
let mut requests = vec![requests_receiver.recv_timeout(timeout)?];
|
|
|
|
let mut num_requests = requests.last().unwrap().packets.len();
|
|
|
|
while let Ok(more_reqs) = requests_receiver.try_recv() {
|
|
|
|
if num_requests >= MAX_GOSSIP_TRAFFIC {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
num_requests += more_reqs.packets.len();
|
|
|
|
requests.push(more_reqs)
|
|
|
|
}
|
|
|
|
if num_requests >= MAX_GOSSIP_TRAFFIC {
|
2020-05-05 20:15:19 -07:00
|
|
|
warn!(
|
|
|
|
"Too much gossip traffic, ignoring some messages (requests={}, max requests={})",
|
|
|
|
num_requests, MAX_GOSSIP_TRAFFIC
|
|
|
|
);
|
2020-02-19 19:31:55 -08:00
|
|
|
}
|
2020-02-07 12:38:24 -08:00
|
|
|
let epoch_ms;
|
2019-06-26 00:30:16 -07:00
|
|
|
let stakes: HashMap<_, _> = match bank_forks {
|
|
|
|
Some(ref bank_forks) => {
|
2020-02-07 12:38:24 -08:00
|
|
|
let bank = bank_forks.read().unwrap().working_bank();
|
|
|
|
let epoch = bank.epoch();
|
|
|
|
let epoch_schedule = bank.epoch_schedule();
|
|
|
|
epoch_ms = epoch_schedule.get_slots_in_epoch(epoch) * DEFAULT_MS_PER_SLOT;
|
|
|
|
staking_utils::staked_nodes(&bank)
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
inc_new_counter_info!("cluster_info-purge-no_working_bank", 1);
|
|
|
|
epoch_ms = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
|
|
|
HashMap::new()
|
2019-06-26 00:30:16 -07:00
|
|
|
}
|
|
|
|
};
|
2020-02-19 19:31:55 -08:00
|
|
|
let sender = response_sender.clone();
|
|
|
|
thread_pool.install(|| {
|
|
|
|
requests.into_par_iter().for_each_with(sender, |s, reqs| {
|
|
|
|
Self::handle_packets(obj, &recycler, &stakes, reqs, s, epoch_ms)
|
|
|
|
});
|
|
|
|
});
|
2019-06-26 00:30:16 -07:00
|
|
|
|
2020-05-25 15:03:34 -07:00
|
|
|
Self::print_reset_stats(obj, last_print);
|
|
|
|
|
2018-04-21 11:02:49 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2020-05-25 15:03:34 -07:00
|
|
|
|
|
|
|
fn print_reset_stats(&self, last_print: &mut Instant) {
|
|
|
|
if last_print.elapsed().as_millis() > 1000 {
|
|
|
|
datapoint_info!(
|
|
|
|
"cluster_info_stats",
|
|
|
|
("entrypoint", self.stats.entrypoint.clear(), i64),
|
|
|
|
("entrypoint2", self.stats.entrypoint2.clear(), i64),
|
|
|
|
("push_vote_read", self.stats.push_vote_read.clear(), i64),
|
|
|
|
(
|
|
|
|
"vote_process_push",
|
|
|
|
self.stats.vote_process_push.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("get_votes", self.stats.get_votes.clear(), i64),
|
|
|
|
(
|
|
|
|
"get_accounts_hash",
|
|
|
|
self.stats.get_accounts_hash.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("all_tvu_peers", self.stats.all_tvu_peers.clear(), i64),
|
|
|
|
("tvu_peers", self.stats.tvu_peers.clear(), i64),
|
2020-06-01 08:37:54 -07:00
|
|
|
(
|
|
|
|
"new_push_requests_num",
|
|
|
|
self.stats.new_push_requests2.clear(),
|
|
|
|
i64
|
|
|
|
),
|
2020-05-25 15:03:34 -07:00
|
|
|
);
|
|
|
|
datapoint_info!(
|
|
|
|
"cluster_info_stats2",
|
|
|
|
("retransmit_peers", self.stats.retransmit_peers.clear(), i64),
|
|
|
|
("repair_peers", self.stats.repair_peers.clear(), i64),
|
|
|
|
(
|
|
|
|
"new_push_requests",
|
|
|
|
self.stats.new_push_requests.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"new_push_requests2",
|
|
|
|
self.stats.new_push_requests2.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("purge", self.stats.purge.clear(), i64),
|
|
|
|
(
|
|
|
|
"process_pull_resp",
|
|
|
|
self.stats.process_pull_response.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"process_pull_resp_count",
|
|
|
|
self.stats.process_pull_response_count.clear(),
|
|
|
|
i64
|
|
|
|
),
|
2020-06-01 08:37:54 -07:00
|
|
|
(
|
|
|
|
"process_pull_resp_success",
|
|
|
|
self.stats.process_pull_response_success.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"process_pull_resp_timeout",
|
|
|
|
self.stats.process_pull_response_timeout.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"process_pull_resp_fail",
|
|
|
|
self.stats.process_pull_response_fail.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"push_response_count",
|
|
|
|
self.stats.push_response_count.clear(),
|
|
|
|
i64
|
|
|
|
),
|
2020-05-25 15:03:34 -07:00
|
|
|
);
|
|
|
|
datapoint_info!(
|
|
|
|
"cluster_info_stats3",
|
|
|
|
(
|
|
|
|
"process_pull_resp_len",
|
|
|
|
self.stats.process_pull_response_len.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"process_pull_requests",
|
|
|
|
self.stats.process_pull_requests.clear(),
|
|
|
|
i64
|
|
|
|
),
|
2020-05-28 11:38:13 -07:00
|
|
|
(
|
|
|
|
"generate_pull_responses",
|
|
|
|
self.stats.generate_pull_responses.clear(),
|
|
|
|
i64
|
|
|
|
),
|
2020-05-25 15:03:34 -07:00
|
|
|
("process_prune", self.stats.process_prune.clear(), i64),
|
|
|
|
(
|
|
|
|
"process_push_message",
|
|
|
|
self.stats.process_push_message.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"prune_received_cache",
|
|
|
|
self.stats.prune_received_cache.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"epoch_slots_lookup",
|
|
|
|
self.stats.epoch_slots_lookup.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
("epoch_slots_push", self.stats.epoch_slots_push.clear(), i64),
|
|
|
|
("push_message", self.stats.push_message.clear(), i64),
|
|
|
|
(
|
|
|
|
"new_pull_requests",
|
|
|
|
self.stats.new_pull_requests.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"mark_pull_request",
|
|
|
|
self.stats.mark_pull_request.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
);
|
2020-05-28 11:38:13 -07:00
|
|
|
datapoint_info!(
|
|
|
|
"cluster_info_shred_version_skip",
|
|
|
|
(
|
|
|
|
"skip_push_message_shred_version",
|
|
|
|
self.stats.skip_push_message_shred_version.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"skip_pull_response_shred_version",
|
|
|
|
self.stats.skip_pull_response_shred_version.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"skip_pull_shred_version",
|
|
|
|
self.stats.skip_pull_shred_version.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"push_message_count",
|
|
|
|
self.stats.push_message_count.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
(
|
|
|
|
"push_message_value_count",
|
|
|
|
self.stats.push_message_value_count.clear(),
|
|
|
|
i64
|
|
|
|
),
|
|
|
|
);
|
2020-05-25 15:03:34 -07:00
|
|
|
|
|
|
|
*last_print = Instant::now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-21 11:02:49 -07:00
|
|
|
pub fn listen(
|
2020-04-21 12:54:45 -07:00
|
|
|
me: Arc<Self>,
|
2019-06-26 00:30:16 -07:00
|
|
|
bank_forks: Option<Arc<RwLock<BankForks>>>,
|
2019-11-14 10:24:53 -08:00
|
|
|
requests_receiver: PacketReceiver,
|
|
|
|
response_sender: PacketSender,
|
2019-03-04 20:50:02 -08:00
|
|
|
exit: &Arc<AtomicBool>,
|
2018-04-21 11:02:49 -07:00
|
|
|
) -> JoinHandle<()> {
|
2019-03-04 20:50:02 -08:00
|
|
|
let exit = exit.clone();
|
2019-11-20 11:25:18 -08:00
|
|
|
let recycler = PacketsRecycler::default();
|
2018-05-30 13:25:32 -07:00
|
|
|
Builder::new()
|
|
|
|
.name("solana-listen".to_string())
|
2020-02-19 19:31:55 -08:00
|
|
|
.spawn(move || {
|
|
|
|
let thread_pool = rayon::ThreadPoolBuilder::new()
|
|
|
|
.num_threads(get_thread_count())
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
2020-05-25 15:03:34 -07:00
|
|
|
let mut last_print = Instant::now();
|
2020-02-19 19:31:55 -08:00
|
|
|
loop {
|
|
|
|
let e = Self::run_listen(
|
|
|
|
&me,
|
|
|
|
&recycler,
|
|
|
|
bank_forks.as_ref(),
|
|
|
|
&requests_receiver,
|
|
|
|
&response_sender,
|
|
|
|
&thread_pool,
|
2020-05-25 15:03:34 -07:00
|
|
|
&mut last_print,
|
2018-05-30 13:25:32 -07:00
|
|
|
);
|
2020-02-19 19:31:55 -08:00
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if e.is_err() {
|
2020-04-21 12:54:45 -07:00
|
|
|
let r_gossip = me.gossip.read().unwrap();
|
2020-02-19 19:31:55 -08:00
|
|
|
debug!(
|
|
|
|
"{}: run_listen timeout, table size: {}",
|
2020-04-21 12:54:45 -07:00
|
|
|
me.id(),
|
|
|
|
r_gossip.crds.table.len()
|
2020-02-19 19:31:55 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
thread_mem_usage::datapoint("solana-listen");
|
2018-05-30 13:25:32 -07:00
|
|
|
}
|
2018-12-07 19:01:28 -08:00
|
|
|
})
|
|
|
|
.unwrap()
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2018-08-26 11:36:27 -07:00
|
|
|
|
2020-05-13 19:37:40 -07:00
|
|
|
pub fn gossip_contact_info(id: &Pubkey, gossip: SocketAddr, shred_version: u16) -> ContactInfo {
|
2020-02-12 12:58:51 -08:00
|
|
|
ContactInfo {
|
|
|
|
id: *id,
|
|
|
|
gossip,
|
|
|
|
wallclock: timestamp(),
|
2020-05-13 19:37:40 -07:00
|
|
|
shred_version,
|
2020-02-12 12:58:51 -08:00
|
|
|
..ContactInfo::default()
|
|
|
|
}
|
2020-01-19 17:50:31 -08:00
|
|
|
}
|
|
|
|
|
2019-04-30 16:42:56 -07:00
|
|
|
/// An alternative to Spy Node that has a valid gossip address and fully participate in Gossip.
|
2019-10-24 15:35:33 -07:00
|
|
|
pub fn gossip_node(
|
|
|
|
id: &Pubkey,
|
|
|
|
gossip_addr: &SocketAddr,
|
2020-05-13 19:37:40 -07:00
|
|
|
shred_version: u16,
|
2019-10-24 15:35:33 -07:00
|
|
|
) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
2020-03-04 21:46:43 -08:00
|
|
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
2019-10-24 15:35:33 -07:00
|
|
|
let (port, (gossip_socket, ip_echo)) =
|
2020-03-04 21:46:43 -08:00
|
|
|
Node::get_gossip_port(gossip_addr, VALIDATOR_PORT_RANGE, bind_ip_addr);
|
2020-05-13 19:37:40 -07:00
|
|
|
let contact_info =
|
|
|
|
Self::gossip_contact_info(id, SocketAddr::new(gossip_addr.ip(), port), shred_version);
|
2019-04-30 16:42:56 -07:00
|
|
|
|
2020-01-19 17:50:31 -08:00
|
|
|
(contact_info, gossip_socket, Some(ip_echo))
|
2019-04-30 16:42:56 -07:00
|
|
|
}
|
|
|
|
|
2020-01-19 17:50:31 -08:00
|
|
|
/// A Node with dummy ports to spy on gossip via pull requests
|
2020-05-13 19:37:40 -07:00
|
|
|
pub fn spy_node(
|
|
|
|
id: &Pubkey,
|
|
|
|
shred_version: u16,
|
|
|
|
) -> (ContactInfo, UdpSocket, Option<TcpListener>) {
|
2020-03-04 21:46:43 -08:00
|
|
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
|
|
|
let (_, gossip_socket) = bind_in_range(bind_ip_addr, VALIDATOR_PORT_RANGE).unwrap();
|
2020-05-13 19:37:40 -07:00
|
|
|
let contact_info = Self::gossip_contact_info(id, socketaddr_any!(), shred_version);
|
2018-08-30 12:07:54 -07:00
|
|
|
|
2020-01-19 17:50:31 -08:00
|
|
|
(contact_info, gossip_socket, None)
|
2018-07-18 01:07:43 -07:00
|
|
|
}
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
|
|
|
|
2019-06-06 12:48:40 -07:00
|
|
|
/// Turbine logic
|
2019-02-18 08:51:00 -08:00
|
|
|
/// 1 - For the current node find out if it is in layer 1
|
|
|
|
/// 1.1 - If yes, then broadcast to all layer 1 nodes
|
|
|
|
/// 1 - using the layer 1 index, broadcast to all layer 2 nodes assuming you know neighborhood size
|
|
|
|
/// 1.2 - If no, then figure out what layer the node is in and who the neighbors are and only broadcast to them
|
2019-05-07 13:24:58 -07:00
|
|
|
/// 1 - also check if there are nodes in the next layer and repeat the layer 1 to layer 2 logic
|
2019-02-18 08:51:00 -08:00
|
|
|
|
2019-10-04 11:52:02 -07:00
|
|
|
/// Returns Neighbor Nodes and Children Nodes `(neighbors, children)` for a given node based on its stake
|
2019-06-03 20:38:05 -07:00
|
|
|
pub fn compute_retransmit_peers(
|
2019-02-18 08:51:00 -08:00
|
|
|
fanout: usize,
|
2019-06-03 20:38:05 -07:00
|
|
|
my_index: usize,
|
2019-10-04 11:52:02 -07:00
|
|
|
stakes_and_index: Vec<usize>,
|
|
|
|
) -> (Vec<usize>, Vec<usize>) {
|
2019-02-18 08:51:00 -08:00
|
|
|
//calc num_layers and num_neighborhoods using the total number of nodes
|
2019-10-04 11:52:02 -07:00
|
|
|
let (num_layers, layer_indices) =
|
|
|
|
ClusterInfo::describe_data_plane(stakes_and_index.len(), fanout);
|
2019-02-18 08:51:00 -08:00
|
|
|
|
|
|
|
if num_layers <= 1 {
|
|
|
|
/* single layer data plane */
|
2019-10-04 11:52:02 -07:00
|
|
|
(stakes_and_index, vec![])
|
2019-02-18 08:51:00 -08:00
|
|
|
} else {
|
|
|
|
//find my layer
|
2019-05-07 13:24:58 -07:00
|
|
|
let locality = ClusterInfo::localize(&layer_indices, fanout, my_index);
|
2019-10-04 11:52:02 -07:00
|
|
|
let upper_bound = cmp::min(locality.neighbor_bounds.1, stakes_and_index.len());
|
|
|
|
let neighbors = stakes_and_index[locality.neighbor_bounds.0..upper_bound].to_vec();
|
2019-02-18 08:51:00 -08:00
|
|
|
let mut children = Vec::new();
|
2019-05-07 13:24:58 -07:00
|
|
|
for ix in locality.next_layer_peers {
|
2019-10-04 11:52:02 -07:00
|
|
|
if let Some(peer) = stakes_and_index.get(ix) {
|
|
|
|
children.push(*peer);
|
2019-02-18 08:51:00 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(neighbors, children)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-21 15:32:15 -07:00
|
|
|
#[derive(Debug)]
|
2018-05-27 18:21:39 -07:00
|
|
|
pub struct Sockets {
|
|
|
|
pub gossip: UdpSocket,
|
2019-09-19 17:16:22 -07:00
|
|
|
pub ip_echo: Option<TcpListener>,
|
2018-12-07 14:09:29 -08:00
|
|
|
pub tvu: Vec<UdpSocket>,
|
2019-08-20 17:16:06 -07:00
|
|
|
pub tvu_forwards: Vec<UdpSocket>,
|
2018-12-07 14:09:29 -08:00
|
|
|
pub tpu: Vec<UdpSocket>,
|
2019-07-30 14:50:02 -07:00
|
|
|
pub tpu_forwards: Vec<UdpSocket>,
|
2019-12-16 17:11:18 -08:00
|
|
|
pub broadcast: Vec<UdpSocket>,
|
2018-06-02 08:32:51 -07:00
|
|
|
pub repair: UdpSocket,
|
2019-10-10 13:24:03 -07:00
|
|
|
pub retransmit_sockets: Vec<UdpSocket>,
|
2020-01-31 14:23:51 -08:00
|
|
|
pub serve_repair: UdpSocket,
|
2018-05-27 18:21:39 -07:00
|
|
|
}
|
|
|
|
|
2018-09-21 15:32:15 -07:00
|
|
|
#[derive(Debug)]
|
2018-08-28 16:32:40 -07:00
|
|
|
pub struct Node {
|
2019-03-08 17:23:07 -08:00
|
|
|
pub info: ContactInfo,
|
2018-05-27 18:21:39 -07:00
|
|
|
pub sockets: Sockets,
|
|
|
|
}
|
2018-04-21 11:02:49 -07:00
|
|
|
|
2018-08-28 16:32:40 -07:00
|
|
|
impl Node {
|
2018-07-16 19:31:52 -07:00
|
|
|
pub fn new_localhost() -> Self {
|
2019-03-30 20:37:33 -07:00
|
|
|
let pubkey = Pubkey::new_rand();
|
2019-03-09 19:28:43 -08:00
|
|
|
Self::new_localhost_with_pubkey(&pubkey)
|
2018-07-09 15:53:49 -07:00
|
|
|
}
|
2019-03-09 19:28:43 -08:00
|
|
|
pub fn new_localhost_with_pubkey(pubkey: &Pubkey) -> Self {
|
2020-03-04 21:46:43 -08:00
|
|
|
let bind_ip_addr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
|
2018-12-07 14:09:29 -08:00
|
|
|
let tpu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2020-03-04 21:46:43 -08:00
|
|
|
let (gossip_port, (gossip, ip_echo)) =
|
|
|
|
bind_common_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
2019-09-19 17:16:22 -07:00
|
|
|
let gossip_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), gossip_port);
|
2018-12-07 14:09:29 -08:00
|
|
|
let tvu = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2019-08-20 17:16:06 -07:00
|
|
|
let tvu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2019-07-30 14:50:02 -07:00
|
|
|
let tpu_forwards = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2018-07-16 19:31:52 -07:00
|
|
|
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2020-03-04 21:46:43 -08:00
|
|
|
let rpc_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
2018-11-05 09:50:58 -08:00
|
|
|
let rpc_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_port);
|
2020-03-04 21:46:43 -08:00
|
|
|
let rpc_pubsub_port = find_available_port_in_range(bind_ip_addr, (1024, 65535)).unwrap();
|
2018-11-05 09:50:58 -08:00
|
|
|
let rpc_pubsub_addr =
|
|
|
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), rpc_pubsub_port);
|
2018-07-16 19:31:52 -07:00
|
|
|
|
2019-12-16 17:11:18 -08:00
|
|
|
let broadcast = vec![UdpSocket::bind("0.0.0.0:0").unwrap()];
|
2019-10-10 13:24:03 -07:00
|
|
|
let retransmit_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
2020-05-14 18:22:47 -07:00
|
|
|
let unused = UdpSocket::bind("0.0.0.0:0").unwrap();
|
2020-01-31 14:23:51 -08:00
|
|
|
let serve_repair = UdpSocket::bind("127.0.0.1:0").unwrap();
|
2020-02-12 12:58:51 -08:00
|
|
|
let info = ContactInfo {
|
|
|
|
id: *pubkey,
|
|
|
|
gossip: gossip_addr,
|
|
|
|
tvu: tvu.local_addr().unwrap(),
|
|
|
|
tvu_forwards: tvu_forwards.local_addr().unwrap(),
|
|
|
|
repair: repair.local_addr().unwrap(),
|
|
|
|
tpu: tpu.local_addr().unwrap(),
|
|
|
|
tpu_forwards: tpu_forwards.local_addr().unwrap(),
|
2020-05-14 18:22:47 -07:00
|
|
|
unused: unused.local_addr().unwrap(),
|
2020-02-12 12:58:51 -08:00
|
|
|
rpc: rpc_addr,
|
|
|
|
rpc_pubsub: rpc_pubsub_addr,
|
|
|
|
serve_repair: serve_repair.local_addr().unwrap(),
|
|
|
|
wallclock: timestamp(),
|
|
|
|
shred_version: 0,
|
|
|
|
};
|
2018-08-28 16:32:40 -07:00
|
|
|
Node {
|
2018-08-31 00:10:39 -07:00
|
|
|
info,
|
2018-05-27 18:21:39 -07:00
|
|
|
sockets: Sockets {
|
|
|
|
gossip,
|
2019-09-19 17:16:22 -07:00
|
|
|
ip_echo: Some(ip_echo),
|
2018-12-07 14:09:29 -08:00
|
|
|
tvu: vec![tvu],
|
2019-08-20 17:16:06 -07:00
|
|
|
tvu_forwards: vec![tvu_forwards],
|
2018-12-07 14:09:29 -08:00
|
|
|
tpu: vec![tpu],
|
2019-07-30 14:50:02 -07:00
|
|
|
tpu_forwards: vec![tpu_forwards],
|
2018-05-27 18:21:39 -07:00
|
|
|
broadcast,
|
2018-06-02 08:32:51 -07:00
|
|
|
repair,
|
2019-10-10 13:24:03 -07:00
|
|
|
retransmit_sockets: vec![retransmit_socket],
|
2020-01-31 14:23:51 -08:00
|
|
|
serve_repair,
|
2018-05-27 18:21:39 -07:00
|
|
|
},
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2018-04-26 13:48:42 -07:00
|
|
|
}
|
2019-09-19 17:16:22 -07:00
|
|
|
fn get_gossip_port(
|
|
|
|
gossip_addr: &SocketAddr,
|
|
|
|
port_range: PortRange,
|
2020-03-04 21:46:43 -08:00
|
|
|
bind_ip_addr: IpAddr,
|
2019-09-19 17:16:22 -07:00
|
|
|
) -> (u16, (UdpSocket, TcpListener)) {
|
2019-03-18 11:12:19 -07:00
|
|
|
if gossip_addr.port() != 0 {
|
2018-12-06 12:52:47 -08:00
|
|
|
(
|
|
|
|
gossip_addr.port(),
|
2020-03-04 21:46:43 -08:00
|
|
|
bind_common(bind_ip_addr, gossip_addr.port(), false).unwrap_or_else(|e| {
|
2018-12-17 08:40:56 -08:00
|
|
|
panic!("gossip_addr bind_to port {}: {}", gossip_addr.port(), e)
|
|
|
|
}),
|
2018-12-06 12:52:47 -08:00
|
|
|
)
|
2018-08-25 10:24:16 -07:00
|
|
|
} else {
|
2020-03-04 21:46:43 -08:00
|
|
|
bind_common_in_range(bind_ip_addr, port_range).expect("Failed to bind")
|
2019-03-18 11:12:19 -07:00
|
|
|
}
|
|
|
|
}
|
2020-03-04 21:46:43 -08:00
|
|
|
fn bind(bind_ip_addr: IpAddr, port_range: PortRange) -> (u16, UdpSocket) {
|
|
|
|
bind_in_range(bind_ip_addr, port_range).expect("Failed to bind")
|
2019-03-18 11:12:19 -07:00
|
|
|
}
|
2020-03-04 21:46:43 -08:00
|
|
|
|
2019-04-12 18:17:34 -07:00
|
|
|
pub fn new_with_external_ip(
|
|
|
|
pubkey: &Pubkey,
|
|
|
|
gossip_addr: &SocketAddr,
|
|
|
|
port_range: PortRange,
|
2020-03-04 21:46:43 -08:00
|
|
|
bind_ip_addr: IpAddr,
|
2019-04-12 18:17:34 -07:00
|
|
|
) -> Node {
|
2020-03-04 21:46:43 -08:00
|
|
|
let (gossip_port, (gossip, ip_echo)) =
|
|
|
|
Self::get_gossip_port(gossip_addr, port_range, bind_ip_addr);
|
2018-08-30 12:07:54 -07:00
|
|
|
|
2020-03-04 21:46:43 -08:00
|
|
|
let (tvu_port, tvu_sockets) =
|
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu multi_bind");
|
2018-09-14 16:56:06 -07:00
|
|
|
|
2019-08-20 17:16:06 -07:00
|
|
|
let (tvu_forwards_port, tvu_forwards_sockets) =
|
2020-03-04 21:46:43 -08:00
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tvu_forwards multi_bind");
|
2019-08-20 17:16:06 -07:00
|
|
|
|
2020-03-04 21:46:43 -08:00
|
|
|
let (tpu_port, tpu_sockets) =
|
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 32).expect("tpu multi_bind");
|
2018-09-06 14:13:40 -07:00
|
|
|
|
2019-07-30 14:50:02 -07:00
|
|
|
let (tpu_forwards_port, tpu_forwards_sockets) =
|
2020-03-04 21:46:43 -08:00
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("tpu_forwards multi_bind");
|
2019-10-10 13:24:03 -07:00
|
|
|
|
|
|
|
let (_, retransmit_sockets) =
|
2020-03-04 21:46:43 -08:00
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 8).expect("retransmit multi_bind");
|
2019-03-08 14:59:11 -08:00
|
|
|
|
2020-03-04 21:46:43 -08:00
|
|
|
let (repair_port, repair) = Self::bind(bind_ip_addr, port_range);
|
|
|
|
let (serve_repair_port, serve_repair) = Self::bind(bind_ip_addr, port_range);
|
2020-01-31 14:23:51 -08:00
|
|
|
|
2020-03-04 21:46:43 -08:00
|
|
|
let (_, broadcast) =
|
|
|
|
multi_bind_in_range(bind_ip_addr, port_range, 4).expect("broadcast multi_bind");
|
2018-08-25 10:24:16 -07:00
|
|
|
|
2020-02-12 12:58:51 -08:00
|
|
|
let info = ContactInfo {
|
|
|
|
id: *pubkey,
|
|
|
|
gossip: SocketAddr::new(gossip_addr.ip(), gossip_port),
|
|
|
|
tvu: SocketAddr::new(gossip_addr.ip(), tvu_port),
|
|
|
|
tvu_forwards: SocketAddr::new(gossip_addr.ip(), tvu_forwards_port),
|
|
|
|
repair: SocketAddr::new(gossip_addr.ip(), repair_port),
|
|
|
|
tpu: SocketAddr::new(gossip_addr.ip(), tpu_port),
|
|
|
|
tpu_forwards: SocketAddr::new(gossip_addr.ip(), tpu_forwards_port),
|
2020-05-14 18:22:47 -07:00
|
|
|
unused: socketaddr_any!(),
|
2020-02-12 12:58:51 -08:00
|
|
|
rpc: socketaddr_any!(),
|
|
|
|
rpc_pubsub: socketaddr_any!(),
|
|
|
|
serve_repair: SocketAddr::new(gossip_addr.ip(), serve_repair_port),
|
|
|
|
wallclock: 0,
|
|
|
|
shred_version: 0,
|
|
|
|
};
|
2019-03-08 17:23:07 -08:00
|
|
|
trace!("new ContactInfo: {:?}", info);
|
2018-08-25 10:24:16 -07:00
|
|
|
|
2018-08-28 16:32:40 -07:00
|
|
|
Node {
|
2018-08-31 00:10:39 -07:00
|
|
|
info,
|
2018-08-25 10:24:16 -07:00
|
|
|
sockets: Sockets {
|
|
|
|
gossip,
|
2018-12-07 14:09:29 -08:00
|
|
|
tvu: tvu_sockets,
|
2019-08-20 17:16:06 -07:00
|
|
|
tvu_forwards: tvu_forwards_sockets,
|
2018-12-07 14:09:29 -08:00
|
|
|
tpu: tpu_sockets,
|
2019-07-30 14:50:02 -07:00
|
|
|
tpu_forwards: tpu_forwards_sockets,
|
2018-08-25 10:24:16 -07:00
|
|
|
broadcast,
|
|
|
|
repair,
|
2019-10-10 13:24:03 -07:00
|
|
|
retransmit_sockets,
|
2020-01-31 14:23:51 -08:00
|
|
|
serve_repair,
|
2019-09-19 17:16:22 -07:00
|
|
|
ip_echo: Some(ip_echo),
|
2018-08-25 10:24:16 -07:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2018-05-27 18:21:39 -07:00
|
|
|
}
|
2018-04-26 13:48:42 -07:00
|
|
|
|
2020-04-06 17:36:22 -07:00
|
|
|
pub fn stake_weight_peers<S: std::hash::BuildHasher>(
|
|
|
|
peers: &mut Vec<ContactInfo>,
|
|
|
|
stakes: Option<Arc<HashMap<Pubkey, u64, S>>>,
|
|
|
|
) -> Vec<(u64, usize)> {
|
|
|
|
peers.dedup();
|
|
|
|
ClusterInfo::sorted_stakes_with_index(peers, stakes)
|
|
|
|
}
|
|
|
|
|
2018-05-27 18:21:39 -07:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-11-15 13:23:26 -08:00
|
|
|
use super::*;
|
2020-05-24 15:38:35 -07:00
|
|
|
use crate::crds_value::{CrdsValue, CrdsValueLabel, Vote as CrdsVote};
|
2019-10-10 13:24:03 -07:00
|
|
|
use rayon::prelude::*;
|
2019-11-06 10:52:30 -08:00
|
|
|
use solana_perf::test_tx::test_tx;
|
2020-02-20 13:28:55 -08:00
|
|
|
use solana_sdk::signature::{Keypair, Signer};
|
2020-05-24 15:38:35 -07:00
|
|
|
use solana_vote_program::{vote_instruction, vote_state::Vote};
|
2019-01-02 00:46:15 -08:00
|
|
|
use std::collections::HashSet;
|
2019-03-08 19:28:19 -08:00
|
|
|
use std::net::{IpAddr, Ipv4Addr};
|
2020-04-21 12:54:45 -07:00
|
|
|
use std::sync::Arc;
|
2018-04-26 13:48:42 -07:00
|
|
|
|
2019-04-30 16:42:56 -07:00
|
|
|
#[test]
|
|
|
|
fn test_gossip_node() {
|
|
|
|
//check that a gossip nodes always show up as spies
|
2020-05-13 19:37:40 -07:00
|
|
|
let (node, _, _) = ClusterInfo::spy_node(&Pubkey::new_rand(), 0);
|
2019-04-30 16:42:56 -07:00
|
|
|
assert!(ClusterInfo::is_spy_node(&node));
|
2019-10-24 15:35:33 -07:00
|
|
|
let (node, _, _) =
|
2020-05-13 19:37:40 -07:00
|
|
|
ClusterInfo::gossip_node(&Pubkey::new_rand(), &"1.1.1.1:1111".parse().unwrap(), 0);
|
2019-04-30 16:42:56 -07:00
|
|
|
assert!(ClusterInfo::is_spy_node(&node));
|
|
|
|
}
|
|
|
|
|
2020-05-28 11:38:13 -07:00
|
|
|
#[test]
|
|
|
|
fn test_handle_pull() {
|
|
|
|
let node = Node::new_localhost();
|
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(node.info));
|
|
|
|
|
|
|
|
let entrypoint_pubkey = Pubkey::new_rand();
|
|
|
|
let data = test_crds_values(entrypoint_pubkey);
|
|
|
|
let timeouts = HashMap::new();
|
|
|
|
assert_eq!(
|
|
|
|
(0, 0, 1),
|
|
|
|
ClusterInfo::handle_pull_response(
|
|
|
|
&cluster_info,
|
|
|
|
&entrypoint_pubkey,
|
|
|
|
data.clone(),
|
|
|
|
&timeouts
|
|
|
|
)
|
|
|
|
);
|
|
|
|
|
|
|
|
let entrypoint_pubkey2 = Pubkey::new_rand();
|
|
|
|
assert_eq!(
|
|
|
|
(1, 0, 0),
|
|
|
|
ClusterInfo::handle_pull_response(&cluster_info, &entrypoint_pubkey2, data, &timeouts)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_crds_values(pubkey: Pubkey) -> Vec<CrdsValue> {
|
|
|
|
let entrypoint = ContactInfo::new_localhost(&pubkey, timestamp());
|
2020-05-29 00:26:06 -07:00
|
|
|
let entrypoint_crdsvalue = CrdsValue::new_unsigned(CrdsData::ContactInfo(entrypoint));
|
2020-05-28 11:38:13 -07:00
|
|
|
vec![entrypoint_crdsvalue]
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_filter_shred_version() {
|
|
|
|
let from = Pubkey::new_rand();
|
|
|
|
let my_shred_version = 1;
|
|
|
|
let other_shred_version = 1;
|
|
|
|
|
|
|
|
// Allow same shred_version
|
|
|
|
let mut values = test_crds_values(from);
|
|
|
|
ClusterInfo::filter_by_shred_version(
|
|
|
|
&from,
|
|
|
|
&mut values,
|
|
|
|
other_shred_version,
|
|
|
|
my_shred_version,
|
|
|
|
);
|
|
|
|
assert_eq!(values.len(), 1);
|
|
|
|
|
|
|
|
// Allow shred_version=0.
|
|
|
|
let other_shred_version = 0;
|
|
|
|
ClusterInfo::filter_by_shred_version(
|
|
|
|
&from,
|
|
|
|
&mut values,
|
|
|
|
other_shred_version,
|
|
|
|
my_shred_version,
|
|
|
|
);
|
|
|
|
assert_eq!(values.len(), 1);
|
|
|
|
|
|
|
|
// Change to sender's ContactInfo version, allow that.
|
|
|
|
let other_shred_version = 2;
|
|
|
|
ClusterInfo::filter_by_shred_version(
|
|
|
|
&from,
|
|
|
|
&mut values,
|
|
|
|
other_shred_version,
|
|
|
|
my_shred_version,
|
|
|
|
);
|
|
|
|
assert_eq!(values.len(), 1);
|
|
|
|
|
|
|
|
let snapshot_hash_data = CrdsValue::new_unsigned(CrdsData::SnapshotHashes(SnapshotHash {
|
|
|
|
from: Pubkey::new_rand(),
|
|
|
|
hashes: vec![],
|
|
|
|
wallclock: 0,
|
|
|
|
}));
|
|
|
|
values.push(snapshot_hash_data);
|
|
|
|
// Change to sender's ContactInfo version, allow that.
|
|
|
|
let other_shred_version = 2;
|
|
|
|
ClusterInfo::filter_by_shred_version(
|
|
|
|
&from,
|
|
|
|
&mut values,
|
|
|
|
other_shred_version,
|
|
|
|
my_shred_version,
|
|
|
|
);
|
|
|
|
assert_eq!(values.len(), 1);
|
|
|
|
}
|
|
|
|
|
2018-11-17 19:57:28 -08:00
|
|
|
#[test]
|
|
|
|
fn test_cluster_spy_gossip() {
|
|
|
|
//check that gossip doesn't try to push to invalid addresses
|
|
|
|
let node = Node::new_localhost();
|
2020-05-13 19:37:40 -07:00
|
|
|
let (spy, _, _) = ClusterInfo::spy_node(&Pubkey::new_rand(), 0);
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(ClusterInfo::new_with_invalid_keypair(node.info));
|
|
|
|
cluster_info.insert_info(spy);
|
2018-11-17 19:57:28 -08:00
|
|
|
cluster_info
|
|
|
|
.gossip
|
2019-02-20 20:02:47 -08:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-04-21 12:54:45 -07:00
|
|
|
.refresh_push_active_set(&HashMap::new());
|
|
|
|
let reqs = cluster_info.gossip_request(&HashMap::new());
|
2018-11-17 19:57:28 -08:00
|
|
|
//assert none of the addrs are invalid.
|
|
|
|
reqs.iter().all(|(addr, _)| {
|
|
|
|
let res = ContactInfo::is_valid_address(addr);
|
|
|
|
assert!(res);
|
|
|
|
res
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-04-21 11:02:49 -07:00
|
|
|
#[test]
|
2018-11-15 13:23:26 -08:00
|
|
|
fn test_cluster_info_new() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let d = ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp());
|
2019-03-06 13:47:18 -08:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(d.clone());
|
2020-04-21 12:54:45 -07:00
|
|
|
assert_eq!(d.id, cluster_info.id());
|
2018-04-21 11:02:49 -07:00
|
|
|
}
|
2018-10-14 06:45:02 -07:00
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
#[test]
|
|
|
|
fn insert_info_test() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let d = ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(d);
|
2019-03-30 20:37:33 -07:00
|
|
|
let d = ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp());
|
2018-11-15 13:23:26 -08:00
|
|
|
let label = CrdsValueLabel::ContactInfo(d.id);
|
|
|
|
cluster_info.insert_info(d);
|
2020-04-21 12:54:45 -07:00
|
|
|
assert!(cluster_info
|
|
|
|
.gossip
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.lookup(&label)
|
|
|
|
.is_some());
|
2018-04-28 00:31:20 -07:00
|
|
|
}
|
2018-06-02 08:32:51 -07:00
|
|
|
#[test]
|
2020-04-21 12:54:45 -07:00
|
|
|
#[should_panic]
|
|
|
|
fn test_update_contact_info() {
|
2019-03-30 20:37:33 -07:00
|
|
|
let d = ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp());
|
2020-05-15 09:35:43 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(d);
|
2019-03-08 18:08:24 -08:00
|
|
|
let entry_label = CrdsValueLabel::ContactInfo(cluster_info.id());
|
2020-04-21 12:54:45 -07:00
|
|
|
assert!(cluster_info
|
|
|
|
.gossip
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.lookup(&entry_label)
|
|
|
|
.is_some());
|
2019-03-08 18:08:24 -08:00
|
|
|
|
2020-04-21 12:54:45 -07:00
|
|
|
let now = timestamp();
|
|
|
|
cluster_info.update_contact_info(|ci| ci.wallclock = now);
|
|
|
|
assert_eq!(
|
|
|
|
cluster_info
|
|
|
|
.gossip
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.lookup(&entry_label)
|
|
|
|
.unwrap()
|
|
|
|
.contact_info()
|
|
|
|
.unwrap()
|
|
|
|
.wallclock,
|
|
|
|
now
|
|
|
|
);
|
|
|
|
|
|
|
|
// Inserting Contactinfo with different pubkey should panic,
|
|
|
|
// and update should fail
|
|
|
|
cluster_info.update_contact_info(|ci| ci.id = Pubkey::new_rand())
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|
2019-02-14 12:47:21 -08:00
|
|
|
|
2019-03-18 11:02:32 -07:00
|
|
|
fn assert_in_range(x: u16, range: (u16, u16)) {
|
|
|
|
assert!(x >= range.0);
|
|
|
|
assert!(x < range.1);
|
|
|
|
}
|
|
|
|
|
2020-05-15 09:35:43 -07:00
|
|
|
fn check_sockets(sockets: &[UdpSocket], ip: IpAddr, range: (u16, u16)) {
|
2019-03-18 11:02:32 -07:00
|
|
|
assert!(sockets.len() > 1);
|
|
|
|
let port = sockets[0].local_addr().unwrap().port();
|
|
|
|
for socket in sockets.iter() {
|
|
|
|
check_socket(socket, ip, range);
|
|
|
|
assert_eq!(socket.local_addr().unwrap().port(), port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_socket(socket: &UdpSocket, ip: IpAddr, range: (u16, u16)) {
|
|
|
|
let local_addr = socket.local_addr().unwrap();
|
|
|
|
assert_eq!(local_addr.ip(), ip);
|
|
|
|
assert_in_range(local_addr.port(), range);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_node_sockets(node: &Node, ip: IpAddr, range: (u16, u16)) {
|
|
|
|
check_socket(&node.sockets.gossip, ip, range);
|
|
|
|
check_socket(&node.sockets.repair, ip, range);
|
|
|
|
|
|
|
|
check_sockets(&node.sockets.tvu, ip, range);
|
|
|
|
check_sockets(&node.sockets.tpu, ip, range);
|
|
|
|
}
|
|
|
|
|
2018-08-25 10:24:16 -07:00
|
|
|
#[test]
|
|
|
|
fn new_with_external_ip_test_random() {
|
2018-08-31 00:10:39 -07:00
|
|
|
let ip = Ipv4Addr::from(0);
|
2019-04-12 18:17:34 -07:00
|
|
|
let node = Node::new_with_external_ip(
|
|
|
|
&Pubkey::new_rand(),
|
|
|
|
&socketaddr!(ip, 0),
|
2019-10-10 16:33:00 -07:00
|
|
|
VALIDATOR_PORT_RANGE,
|
2020-03-04 21:46:43 -08:00
|
|
|
IpAddr::V4(ip),
|
2019-04-12 18:17:34 -07:00
|
|
|
);
|
2019-03-18 11:02:32 -07:00
|
|
|
|
2019-10-10 16:33:00 -07:00
|
|
|
check_node_sockets(&node, IpAddr::V4(ip), VALIDATOR_PORT_RANGE);
|
2018-08-25 10:24:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn new_with_external_ip_test_gossip() {
|
2020-04-09 19:03:25 -07:00
|
|
|
// Can't use VALIDATOR_PORT_RANGE because if this test runs in parallel with others, the
|
|
|
|
// port returned by `bind_in_range()` might be snatched up before `Node::new_with_external_ip()` runs
|
|
|
|
let port_range = (VALIDATOR_PORT_RANGE.1 + 10, VALIDATOR_PORT_RANGE.1 + 20);
|
|
|
|
|
2018-08-30 12:07:54 -07:00
|
|
|
let ip = IpAddr::V4(Ipv4Addr::from(0));
|
2020-04-09 19:03:25 -07:00
|
|
|
let port = bind_in_range(ip, port_range).expect("Failed to bind").0;
|
|
|
|
let node =
|
|
|
|
Node::new_with_external_ip(&Pubkey::new_rand(), &socketaddr!(0, port), port_range, ip);
|
2019-03-18 11:02:32 -07:00
|
|
|
|
2020-04-09 19:03:25 -07:00
|
|
|
check_node_sockets(&node, ip, port_range);
|
2018-08-25 10:24:16 -07:00
|
|
|
|
2019-03-20 12:24:40 -07:00
|
|
|
assert_eq!(node.sockets.gossip.local_addr().unwrap().port(), port);
|
2018-08-25 10:24:16 -07:00
|
|
|
}
|
2018-12-01 12:00:30 -08:00
|
|
|
|
|
|
|
//test that all cluster_info objects only generate signed messages
|
|
|
|
//when constructed with keypairs
|
|
|
|
#[test]
|
|
|
|
fn test_gossip_signature_verification() {
|
|
|
|
//create new cluster info, leader, and peer
|
|
|
|
let keypair = Keypair::new();
|
|
|
|
let peer_keypair = Keypair::new();
|
2019-03-09 19:28:43 -08:00
|
|
|
let contact_info = ContactInfo::new_localhost(&keypair.pubkey(), 0);
|
|
|
|
let peer = ContactInfo::new_localhost(&peer_keypair.pubkey(), 0);
|
2020-05-15 09:35:43 -07:00
|
|
|
let cluster_info = ClusterInfo::new(contact_info, Arc::new(keypair));
|
|
|
|
cluster_info.insert_info(peer);
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.refresh_push_active_set(&HashMap::new());
|
2018-12-01 12:00:30 -08:00
|
|
|
//check that all types of gossip messages are signed correctly
|
2020-04-21 12:54:45 -07:00
|
|
|
let (_, push_messages) = cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.new_push_messages(timestamp());
|
2018-12-01 12:00:30 -08:00
|
|
|
// there should be some pushes ready
|
2020-05-15 09:35:43 -07:00
|
|
|
assert_eq!(push_messages.is_empty(), false);
|
2019-05-28 18:39:40 -07:00
|
|
|
push_messages
|
|
|
|
.values()
|
|
|
|
.for_each(|v| v.par_iter().for_each(|v| assert!(v.verify())));
|
2018-12-01 12:00:30 -08:00
|
|
|
|
|
|
|
let (_, _, val) = cluster_info
|
|
|
|
.gossip
|
2020-04-21 12:54:45 -07:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2019-11-14 10:24:53 -08:00
|
|
|
.new_pull_request(timestamp(), &HashMap::new(), MAX_BLOOM_SIZE)
|
2018-12-01 12:00:30 -08:00
|
|
|
.ok()
|
|
|
|
.unwrap();
|
|
|
|
assert!(val.verify());
|
|
|
|
}
|
2019-01-02 00:46:15 -08:00
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
fn num_layers(nodes: usize, fanout: usize) -> usize {
|
|
|
|
ClusterInfo::describe_data_plane(nodes, fanout).0
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_describe_data_plane() {
|
|
|
|
// no nodes
|
2019-05-07 13:24:58 -07:00
|
|
|
assert_eq!(num_layers(0, 200), 0);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
|
|
|
// 1 node
|
2019-05-07 13:24:58 -07:00
|
|
|
assert_eq!(num_layers(1, 200), 1);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
// 10 nodes with fanout of 2
|
|
|
|
assert_eq!(num_layers(10, 2), 3);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
2019-05-07 13:24:58 -07:00
|
|
|
// fanout + 1 nodes with fanout of 2
|
|
|
|
assert_eq!(num_layers(3, 2), 2);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
|
|
|
// A little more realistic
|
2019-05-07 13:24:58 -07:00
|
|
|
assert_eq!(num_layers(100, 10), 2);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
|
|
|
// A little more realistic with odd numbers
|
2019-05-07 13:24:58 -07:00
|
|
|
assert_eq!(num_layers(103, 13), 2);
|
|
|
|
|
|
|
|
// A little more realistic with just enough for 3 layers
|
|
|
|
assert_eq!(num_layers(111, 10), 3);
|
2019-01-02 00:46:15 -08:00
|
|
|
|
|
|
|
// larger
|
2019-05-07 13:24:58 -07:00
|
|
|
let (layer_cnt, layer_indices) = ClusterInfo::describe_data_plane(10_000, 10);
|
|
|
|
assert_eq!(layer_cnt, 4);
|
|
|
|
// distances between index values should increase by `fanout` for every layer.
|
|
|
|
let mut capacity = 10 * 10;
|
2019-01-02 00:46:15 -08:00
|
|
|
assert_eq!(layer_indices[1], 10);
|
2019-05-07 13:24:58 -07:00
|
|
|
layer_indices[1..].windows(2).for_each(|x| {
|
|
|
|
if x.len() == 2 {
|
|
|
|
assert_eq!(x[1] - x[0], capacity);
|
|
|
|
capacity *= 10;
|
|
|
|
}
|
|
|
|
});
|
2019-01-02 00:46:15 -08:00
|
|
|
|
|
|
|
// massive
|
2019-05-07 13:24:58 -07:00
|
|
|
let (layer_cnt, layer_indices) = ClusterInfo::describe_data_plane(500_000, 200);
|
|
|
|
let mut capacity = 200 * 200;
|
|
|
|
assert_eq!(layer_cnt, 3);
|
|
|
|
// distances between index values should increase by `fanout` for every layer.
|
2019-01-02 00:46:15 -08:00
|
|
|
assert_eq!(layer_indices[1], 200);
|
2019-05-07 13:24:58 -07:00
|
|
|
layer_indices[1..].windows(2).for_each(|x| {
|
|
|
|
if x.len() == 2 {
|
|
|
|
assert_eq!(x[1] - x[0], capacity);
|
|
|
|
capacity *= 200;
|
|
|
|
}
|
|
|
|
});
|
2019-01-02 00:46:15 -08:00
|
|
|
let total_capacity: usize = *layer_indices.last().unwrap();
|
|
|
|
assert!(total_capacity >= 500_000);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_localize() {
|
|
|
|
// go for gold
|
2019-05-07 13:24:58 -07:00
|
|
|
let (_, layer_indices) = ClusterInfo::describe_data_plane(500_000, 200);
|
2019-01-02 00:46:15 -08:00
|
|
|
let mut me = 0;
|
|
|
|
let mut layer_ix = 0;
|
|
|
|
let locality = ClusterInfo::localize(&layer_indices, 200, me);
|
|
|
|
assert_eq!(locality.layer_ix, layer_ix);
|
|
|
|
assert_eq!(
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds,
|
2019-01-02 00:46:15 -08:00
|
|
|
Some((layer_indices[layer_ix + 1], layer_indices[layer_ix + 2]))
|
|
|
|
);
|
|
|
|
me = 201;
|
|
|
|
layer_ix = 1;
|
|
|
|
let locality = ClusterInfo::localize(&layer_indices, 200, me);
|
|
|
|
assert_eq!(
|
|
|
|
locality.layer_ix, layer_ix,
|
|
|
|
"layer_indices[layer_ix] is actually {}",
|
|
|
|
layer_indices[layer_ix]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds,
|
2019-01-02 00:46:15 -08:00
|
|
|
Some((layer_indices[layer_ix + 1], layer_indices[layer_ix + 2]))
|
|
|
|
);
|
2019-05-07 13:24:58 -07:00
|
|
|
me = 20_000;
|
|
|
|
layer_ix = 1;
|
2019-01-02 00:46:15 -08:00
|
|
|
let locality = ClusterInfo::localize(&layer_indices, 200, me);
|
|
|
|
assert_eq!(
|
|
|
|
locality.layer_ix, layer_ix,
|
|
|
|
"layer_indices[layer_ix] is actually {}",
|
|
|
|
layer_indices[layer_ix]
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2019-05-07 13:24:58 -07:00
|
|
|
locality.next_layer_bounds,
|
2019-01-02 00:46:15 -08:00
|
|
|
Some((layer_indices[layer_ix + 1], layer_indices[layer_ix + 2]))
|
|
|
|
);
|
|
|
|
|
|
|
|
// test no child layer since last layer should have massive capacity
|
2019-05-07 13:24:58 -07:00
|
|
|
let (_, layer_indices) = ClusterInfo::describe_data_plane(500_000, 200);
|
|
|
|
me = 40_201;
|
2019-01-02 00:46:15 -08:00
|
|
|
layer_ix = 2;
|
|
|
|
let locality = ClusterInfo::localize(&layer_indices, 200, me);
|
|
|
|
assert_eq!(
|
|
|
|
locality.layer_ix, layer_ix,
|
|
|
|
"layer_indices[layer_ix] is actually {}",
|
|
|
|
layer_indices[layer_ix]
|
|
|
|
);
|
2019-05-07 13:24:58 -07:00
|
|
|
assert_eq!(locality.next_layer_bounds, None);
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_localize_child_peer_overlap() {
|
2019-05-07 13:24:58 -07:00
|
|
|
let (_, layer_indices) = ClusterInfo::describe_data_plane(500_000, 200);
|
2019-01-02 00:46:15 -08:00
|
|
|
let last_ix = layer_indices.len() - 1;
|
|
|
|
// sample every 33 pairs to reduce test time
|
|
|
|
for x in (0..*layer_indices.get(last_ix - 2).unwrap()).step_by(33) {
|
|
|
|
let me_locality = ClusterInfo::localize(&layer_indices, 200, x);
|
|
|
|
let buddy_locality = ClusterInfo::localize(&layer_indices, 200, x + 1);
|
2019-05-07 13:24:58 -07:00
|
|
|
assert!(!me_locality.next_layer_peers.is_empty());
|
|
|
|
assert!(!buddy_locality.next_layer_peers.is_empty());
|
2019-01-02 00:46:15 -08:00
|
|
|
me_locality
|
2019-05-07 13:24:58 -07:00
|
|
|
.next_layer_peers
|
2019-01-02 00:46:15 -08:00
|
|
|
.iter()
|
2019-05-07 13:24:58 -07:00
|
|
|
.zip(buddy_locality.next_layer_peers.iter())
|
2019-01-02 00:46:15 -08:00
|
|
|
.for_each(|(x, y)| assert_ne!(x, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_network_coverage() {
|
|
|
|
// pretend to be each node in a scaled down network and make sure the set of all the broadcast peers
|
|
|
|
// includes every node in the network.
|
2019-05-07 13:24:58 -07:00
|
|
|
let (_, layer_indices) = ClusterInfo::describe_data_plane(25_000, 10);
|
2019-01-02 00:46:15 -08:00
|
|
|
let mut broadcast_set = HashSet::new();
|
|
|
|
for my_index in 0..25_000 {
|
|
|
|
let my_locality = ClusterInfo::localize(&layer_indices, 10, my_index);
|
|
|
|
broadcast_set.extend(my_locality.neighbor_bounds.0..my_locality.neighbor_bounds.1);
|
2019-05-07 13:24:58 -07:00
|
|
|
broadcast_set.extend(my_locality.next_layer_peers);
|
2019-01-02 00:46:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
for i in 0..25_000 {
|
|
|
|
assert!(broadcast_set.contains(&(i as usize)));
|
|
|
|
}
|
|
|
|
assert!(broadcast_set.contains(&(layer_indices.last().unwrap() - 1)));
|
|
|
|
//sanity check for past total capacity.
|
|
|
|
assert!(!broadcast_set.contains(&(layer_indices.last().unwrap())));
|
|
|
|
}
|
2019-01-31 15:51:29 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_push_vote() {
|
|
|
|
let keys = Keypair::new();
|
2019-03-09 19:28:43 -08:00
|
|
|
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
2019-01-31 15:51:29 -08:00
|
|
|
|
|
|
|
// make sure empty crds is handled correctly
|
2020-03-15 20:31:05 -07:00
|
|
|
let now = timestamp();
|
|
|
|
let (_, votes, max_ts) = cluster_info.get_votes(now);
|
2019-01-31 15:51:29 -08:00
|
|
|
assert_eq!(votes, vec![]);
|
|
|
|
assert_eq!(max_ts, now);
|
|
|
|
|
|
|
|
// add a vote
|
|
|
|
let tx = test_tx();
|
2020-03-15 20:31:05 -07:00
|
|
|
let index = 1;
|
|
|
|
cluster_info.push_vote(index, tx.clone());
|
2019-01-31 15:51:29 -08:00
|
|
|
|
|
|
|
// -1 to make sure that the clock is strictly lower then when insert occurred
|
2020-03-15 20:31:05 -07:00
|
|
|
let (labels, votes, max_ts) = cluster_info.get_votes(now - 1);
|
2019-01-31 15:51:29 -08:00
|
|
|
assert_eq!(votes, vec![tx]);
|
2020-03-15 20:31:05 -07:00
|
|
|
assert_eq!(labels.len(), 1);
|
|
|
|
match labels[0] {
|
|
|
|
CrdsValueLabel::Vote(_, pubkey) => {
|
|
|
|
assert_eq!(pubkey, keys.pubkey());
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => panic!("Bad match"),
|
|
|
|
}
|
2019-01-31 15:51:29 -08:00
|
|
|
assert!(max_ts >= now - 1);
|
|
|
|
|
|
|
|
// make sure timestamp filter works
|
2020-03-15 20:31:05 -07:00
|
|
|
let (_, votes, new_max_ts) = cluster_info.get_votes(max_ts);
|
2019-01-31 15:51:29 -08:00
|
|
|
assert_eq!(votes, vec![]);
|
|
|
|
assert_eq!(max_ts, new_max_ts);
|
|
|
|
}
|
2019-07-30 15:43:17 -07:00
|
|
|
|
2020-03-11 21:31:50 -07:00
|
|
|
#[test]
|
|
|
|
fn test_push_epoch_slots() {
|
|
|
|
let keys = Keypair::new();
|
|
|
|
let contact_info = ContactInfo::new_localhost(&keys.pubkey(), 0);
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(contact_info);
|
2020-03-11 21:31:50 -07:00
|
|
|
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
|
|
|
assert!(slots.is_empty());
|
|
|
|
assert!(since.is_none());
|
|
|
|
cluster_info.push_epoch_slots(&[0]);
|
|
|
|
|
|
|
|
let (slots, since) = cluster_info.get_epoch_slots_since(Some(std::u64::MAX));
|
|
|
|
assert!(slots.is_empty());
|
|
|
|
assert_eq!(since, Some(std::u64::MAX));
|
|
|
|
|
|
|
|
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
|
|
|
assert_eq!(slots.len(), 1);
|
|
|
|
assert!(since.is_some());
|
|
|
|
|
|
|
|
let (slots, since2) = cluster_info.get_epoch_slots_since(since.clone());
|
|
|
|
assert!(slots.is_empty());
|
|
|
|
assert_eq!(since2, since);
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:43:17 -07:00
|
|
|
#[test]
|
2020-04-21 12:54:45 -07:00
|
|
|
fn test_append_entrypoint_to_pulls() {
|
2019-07-30 15:43:17 -07:00
|
|
|
let node_keypair = Arc::new(Keypair::new());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new(
|
2019-07-30 15:43:17 -07:00
|
|
|
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
|
|
|
node_keypair,
|
|
|
|
);
|
|
|
|
let entrypoint_pubkey = Pubkey::new_rand();
|
|
|
|
let entrypoint = ContactInfo::new_localhost(&entrypoint_pubkey, timestamp());
|
|
|
|
cluster_info.set_entrypoint(entrypoint.clone());
|
|
|
|
let pulls = cluster_info.new_pull_requests(&HashMap::new());
|
2019-08-13 18:04:14 -07:00
|
|
|
assert_eq!(1, pulls.len() as u64);
|
2019-07-30 15:43:17 -07:00
|
|
|
match pulls.get(0) {
|
|
|
|
Some((addr, msg)) => {
|
|
|
|
assert_eq!(*addr, entrypoint.gossip);
|
|
|
|
match msg {
|
|
|
|
Protocol::PullRequest(_, value) => {
|
|
|
|
assert!(value.verify());
|
|
|
|
assert_eq!(value.pubkey(), cluster_info.id())
|
|
|
|
}
|
|
|
|
_ => panic!("wrong protocol"),
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|
|
|
|
}
|
2019-07-30 15:43:17 -07:00
|
|
|
None => panic!("entrypoint should be a pull destination"),
|
|
|
|
}
|
|
|
|
|
|
|
|
// now add this message back to the table and make sure after the next pull, the entrypoint is unset
|
2019-11-03 10:07:51 -08:00
|
|
|
let entrypoint_crdsvalue =
|
|
|
|
CrdsValue::new_unsigned(CrdsData::ContactInfo(entrypoint.clone()));
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = Arc::new(cluster_info);
|
|
|
|
let timeouts = cluster_info.gossip.read().unwrap().make_timeouts_test();
|
2019-07-30 15:43:17 -07:00
|
|
|
ClusterInfo::handle_pull_response(
|
|
|
|
&cluster_info,
|
|
|
|
&entrypoint_pubkey,
|
|
|
|
vec![entrypoint_crdsvalue],
|
2020-02-07 12:38:24 -08:00
|
|
|
&timeouts,
|
2019-07-30 15:43:17 -07:00
|
|
|
);
|
2020-04-21 12:54:45 -07:00
|
|
|
let pulls = cluster_info.new_pull_requests(&HashMap::new());
|
2019-08-13 18:04:14 -07:00
|
|
|
assert_eq!(1, pulls.len() as u64);
|
2020-04-21 12:54:45 -07:00
|
|
|
assert_eq!(*cluster_info.entrypoint.read().unwrap(), Some(entrypoint));
|
2019-07-30 15:43:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_split_messages_small() {
|
2019-11-03 10:07:51 -08:00
|
|
|
let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
2019-07-30 15:43:17 -07:00
|
|
|
test_split_messages(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_split_messages_large() {
|
2020-03-11 21:31:50 -07:00
|
|
|
let value = CrdsValue::new_unsigned(CrdsData::LowestSlot(
|
2020-02-19 20:24:09 -08:00
|
|
|
0,
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot::new(Pubkey::default(), 0, 0),
|
2020-02-19 20:24:09 -08:00
|
|
|
));
|
2019-07-30 15:43:17 -07:00
|
|
|
test_split_messages(value);
|
|
|
|
}
|
|
|
|
|
2019-11-19 11:51:51 -08:00
|
|
|
#[test]
|
|
|
|
fn test_split_messages_packet_size() {
|
2020-01-13 14:59:31 -08:00
|
|
|
// Test that if a value is smaller than payload size but too large to be wrapped in a vec
|
2019-11-19 11:51:51 -08:00
|
|
|
// that it is still dropped
|
|
|
|
let payload: Vec<CrdsValue> = vec![];
|
|
|
|
let vec_size = serialized_size(&payload).unwrap();
|
|
|
|
let desired_size = MAX_PROTOCOL_PAYLOAD_SIZE - vec_size;
|
2020-03-16 08:37:31 -07:00
|
|
|
let mut value = CrdsValue::new_unsigned(CrdsData::SnapshotHashes(SnapshotHash {
|
2020-03-11 21:31:50 -07:00
|
|
|
from: Pubkey::default(),
|
|
|
|
hashes: vec![],
|
|
|
|
wallclock: 0,
|
|
|
|
}));
|
2019-11-19 11:51:51 -08:00
|
|
|
|
|
|
|
let mut i = 0;
|
2020-01-13 14:59:31 -08:00
|
|
|
while value.size() <= desired_size {
|
2020-03-16 08:37:31 -07:00
|
|
|
value.data = CrdsData::SnapshotHashes(SnapshotHash {
|
2020-03-11 21:31:50 -07:00
|
|
|
from: Pubkey::default(),
|
|
|
|
hashes: vec![(0, Hash::default()); i],
|
|
|
|
wallclock: 0,
|
|
|
|
});
|
2019-11-19 11:51:51 -08:00
|
|
|
i += 1;
|
|
|
|
}
|
2020-05-15 09:35:43 -07:00
|
|
|
let split = ClusterInfo::split_gossip_messages(vec![value]);
|
2019-11-19 11:51:51 -08:00
|
|
|
assert_eq!(split.len(), 0);
|
|
|
|
}
|
|
|
|
|
2019-07-30 15:43:17 -07:00
|
|
|
fn test_split_messages(value: CrdsValue) {
|
2020-01-31 14:23:51 -08:00
|
|
|
const NUM_VALUES: u64 = 30;
|
2019-07-30 15:43:17 -07:00
|
|
|
let value_size = value.size();
|
2020-01-31 14:23:51 -08:00
|
|
|
let num_values_per_payload = (MAX_PROTOCOL_PAYLOAD_SIZE / value_size).max(1);
|
|
|
|
|
|
|
|
// Expected len is the ceiling of the division
|
|
|
|
let expected_len = (NUM_VALUES + num_values_per_payload - 1) / num_values_per_payload;
|
|
|
|
let msgs = vec![value; NUM_VALUES as usize];
|
2019-07-30 15:43:17 -07:00
|
|
|
|
|
|
|
let split = ClusterInfo::split_gossip_messages(msgs);
|
2020-01-31 14:23:51 -08:00
|
|
|
assert!(split.len() as u64 <= expected_len);
|
2019-07-30 15:43:17 -07:00
|
|
|
}
|
|
|
|
|
2019-08-19 18:14:10 -07:00
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_size() {
|
|
|
|
//sanity test to ensure filter size never exceeds MTU size
|
|
|
|
check_pull_request_size(CrdsFilter::new_rand(1000, 10));
|
|
|
|
check_pull_request_size(CrdsFilter::new_rand(1000, 1000));
|
2020-05-15 09:35:43 -07:00
|
|
|
check_pull_request_size(CrdsFilter::new_rand(100_000, 1000));
|
|
|
|
check_pull_request_size(CrdsFilter::new_rand(100_000, MAX_BLOOM_SIZE));
|
2019-08-19 18:14:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_pull_request_size(filter: CrdsFilter) {
|
2019-11-03 10:07:51 -08:00
|
|
|
let value = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
2020-05-15 09:35:43 -07:00
|
|
|
let protocol = Protocol::PullRequest(filter, value);
|
2019-08-19 18:14:10 -07:00
|
|
|
assert!(serialized_size(&protocol).unwrap() <= PACKET_DATA_SIZE as u64);
|
|
|
|
}
|
2019-10-01 09:38:29 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_tvu_peers_and_stakes() {
|
|
|
|
let d = ContactInfo::new_localhost(&Pubkey::new(&[0; 32]), timestamp());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new_with_invalid_keypair(d.clone());
|
2019-10-01 09:38:29 -07:00
|
|
|
let mut stakes = HashMap::new();
|
|
|
|
|
|
|
|
// no stake
|
|
|
|
let id = Pubkey::new(&[1u8; 32]);
|
|
|
|
let contact_info = ContactInfo::new_localhost(&id, timestamp());
|
|
|
|
cluster_info.insert_info(contact_info);
|
|
|
|
|
|
|
|
// normal
|
|
|
|
let id2 = Pubkey::new(&[2u8; 32]);
|
|
|
|
let mut contact_info = ContactInfo::new_localhost(&id2, timestamp());
|
|
|
|
cluster_info.insert_info(contact_info.clone());
|
|
|
|
stakes.insert(id2, 10);
|
|
|
|
|
|
|
|
// duplicate
|
|
|
|
contact_info.wallclock = timestamp() + 1;
|
|
|
|
cluster_info.insert_info(contact_info);
|
|
|
|
|
|
|
|
// no tvu
|
|
|
|
let id3 = Pubkey::new(&[3u8; 32]);
|
|
|
|
let mut contact_info = ContactInfo::new_localhost(&id3, timestamp());
|
|
|
|
contact_info.tvu = "0.0.0.0:0".parse().unwrap();
|
|
|
|
cluster_info.insert_info(contact_info);
|
|
|
|
stakes.insert(id3, 10);
|
|
|
|
|
2020-01-30 07:54:34 -08:00
|
|
|
// normal but with different shred version
|
|
|
|
let id4 = Pubkey::new(&[4u8; 32]);
|
|
|
|
let mut contact_info = ContactInfo::new_localhost(&id4, timestamp());
|
|
|
|
contact_info.shred_version = 1;
|
|
|
|
assert_ne!(contact_info.shred_version, d.shred_version);
|
2020-05-15 09:35:43 -07:00
|
|
|
cluster_info.insert_info(contact_info);
|
2020-01-30 07:54:34 -08:00
|
|
|
stakes.insert(id4, 10);
|
|
|
|
|
2019-12-16 17:11:18 -08:00
|
|
|
let stakes = Arc::new(stakes);
|
2020-04-06 17:36:22 -07:00
|
|
|
let mut peers = cluster_info.tvu_peers();
|
|
|
|
let peers_and_stakes = stake_weight_peers(&mut peers, Some(stakes));
|
2019-10-01 09:38:29 -07:00
|
|
|
assert_eq!(peers.len(), 2);
|
|
|
|
assert_eq!(peers[0].id, id);
|
|
|
|
assert_eq!(peers[1].id, id2);
|
|
|
|
assert_eq!(peers_and_stakes.len(), 2);
|
|
|
|
assert_eq!(peers_and_stakes[0].0, 10);
|
|
|
|
assert_eq!(peers_and_stakes[1].0, 1);
|
|
|
|
}
|
2019-10-04 14:18:07 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_pull_from_entrypoint_if_not_present() {
|
|
|
|
let node_keypair = Arc::new(Keypair::new());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new(
|
2019-10-04 14:18:07 -07:00
|
|
|
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
|
|
|
node_keypair,
|
|
|
|
);
|
|
|
|
let entrypoint_pubkey = Pubkey::new_rand();
|
|
|
|
let mut entrypoint = ContactInfo::new_localhost(&entrypoint_pubkey, timestamp());
|
|
|
|
entrypoint.gossip = socketaddr!("127.0.0.2:1234");
|
|
|
|
cluster_info.set_entrypoint(entrypoint.clone());
|
|
|
|
|
|
|
|
let mut stakes = HashMap::new();
|
|
|
|
|
|
|
|
let other_node_pubkey = Pubkey::new_rand();
|
|
|
|
let other_node = ContactInfo::new_localhost(&other_node_pubkey, timestamp());
|
|
|
|
assert_ne!(other_node.gossip, entrypoint.gossip);
|
|
|
|
cluster_info.insert_info(other_node.clone());
|
|
|
|
stakes.insert(other_node_pubkey, 10);
|
|
|
|
|
|
|
|
// Pull request 1: `other_node` is present but `entrypoint` was just added (so it has a
|
|
|
|
// fresh timestamp). There should only be one pull request to `other_node`
|
|
|
|
let pulls = cluster_info.new_pull_requests(&stakes);
|
|
|
|
assert_eq!(1, pulls.len() as u64);
|
|
|
|
assert_eq!(pulls.get(0).unwrap().0, other_node.gossip);
|
|
|
|
|
|
|
|
// Pull request 2: pretend it's been a while since we've pulled from `entrypoint`. There should
|
|
|
|
// now be two pull requests
|
2020-04-21 12:54:45 -07:00
|
|
|
cluster_info
|
|
|
|
.entrypoint
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.as_mut()
|
|
|
|
.unwrap()
|
|
|
|
.wallclock = 0;
|
2019-10-04 14:18:07 -07:00
|
|
|
let pulls = cluster_info.new_pull_requests(&stakes);
|
|
|
|
assert_eq!(2, pulls.len() as u64);
|
|
|
|
assert_eq!(pulls.get(0).unwrap().0, other_node.gossip);
|
|
|
|
assert_eq!(pulls.get(1).unwrap().0, entrypoint.gossip);
|
|
|
|
|
|
|
|
// Pull request 3: `other_node` is present and `entrypoint` was just pulled from. There should
|
|
|
|
// only be one pull request to `other_node`
|
|
|
|
let pulls = cluster_info.new_pull_requests(&stakes);
|
|
|
|
assert_eq!(1, pulls.len() as u64);
|
|
|
|
assert_eq!(pulls.get(0).unwrap().0, other_node.gossip);
|
|
|
|
}
|
2019-11-14 10:24:53 -08:00
|
|
|
|
2019-12-05 11:25:13 -08:00
|
|
|
#[test]
|
|
|
|
fn test_repair_peers() {
|
|
|
|
let node_keypair = Arc::new(Keypair::new());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new(
|
2019-12-05 11:25:13 -08:00
|
|
|
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
|
|
|
node_keypair,
|
|
|
|
);
|
|
|
|
for i in 0..10 {
|
2020-05-15 09:35:43 -07:00
|
|
|
// make these invalid for the upcoming repair request
|
|
|
|
let peer_lowest = if i >= 5 { 10 } else { 0 };
|
2019-12-05 11:25:13 -08:00
|
|
|
let other_node_pubkey = Pubkey::new_rand();
|
|
|
|
let other_node = ContactInfo::new_localhost(&other_node_pubkey, timestamp());
|
|
|
|
cluster_info.insert_info(other_node.clone());
|
2020-03-11 21:31:50 -07:00
|
|
|
let value = CrdsValue::new_unsigned(CrdsData::LowestSlot(
|
2020-02-17 12:39:30 -08:00
|
|
|
0,
|
2020-03-11 21:31:50 -07:00
|
|
|
LowestSlot::new(other_node_pubkey, peer_lowest, timestamp()),
|
2020-02-19 20:24:09 -08:00
|
|
|
));
|
2020-04-21 12:54:45 -07:00
|
|
|
let _ = cluster_info
|
|
|
|
.gossip
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.crds
|
|
|
|
.insert(value, timestamp());
|
2019-12-05 11:25:13 -08:00
|
|
|
}
|
|
|
|
// only half the visible peers should be eligible to serve this repair
|
|
|
|
assert_eq!(cluster_info.repair_peers(5).len(), 5);
|
|
|
|
}
|
|
|
|
|
2019-11-14 10:24:53 -08:00
|
|
|
#[test]
|
|
|
|
fn test_max_bloom_size() {
|
2020-03-11 21:31:50 -07:00
|
|
|
// check that the constant fits into the dynamic size
|
|
|
|
assert!(MAX_BLOOM_SIZE <= max_bloom_size());
|
2019-11-14 10:24:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_protocol_size() {
|
|
|
|
let contact_info = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default()));
|
|
|
|
let dummy_vec =
|
|
|
|
vec![CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())); 10];
|
|
|
|
let dummy_vec_size = serialized_size(&dummy_vec).unwrap();
|
|
|
|
let mut max_protocol_size;
|
|
|
|
|
|
|
|
max_protocol_size =
|
|
|
|
serialized_size(&Protocol::PullRequest(CrdsFilter::default(), contact_info)).unwrap()
|
|
|
|
- serialized_size(&CrdsFilter::default()).unwrap();
|
|
|
|
max_protocol_size = max_protocol_size.max(
|
|
|
|
serialized_size(&Protocol::PullResponse(
|
|
|
|
Pubkey::default(),
|
|
|
|
dummy_vec.clone(),
|
|
|
|
))
|
|
|
|
.unwrap()
|
|
|
|
- dummy_vec_size,
|
|
|
|
);
|
|
|
|
max_protocol_size = max_protocol_size.max(
|
|
|
|
serialized_size(&Protocol::PushMessage(Pubkey::default(), dummy_vec)).unwrap()
|
|
|
|
- dummy_vec_size,
|
|
|
|
);
|
|
|
|
max_protocol_size = max_protocol_size.max(
|
|
|
|
serialized_size(&Protocol::PruneMessage(
|
|
|
|
Pubkey::default(),
|
|
|
|
PruneData::default(),
|
|
|
|
))
|
|
|
|
.unwrap()
|
|
|
|
- serialized_size(&PruneData::default()).unwrap(),
|
|
|
|
);
|
|
|
|
|
|
|
|
// finally assert the header size estimation is correct
|
|
|
|
assert_eq!(MAX_PROTOCOL_HEADER_SIZE, max_protocol_size);
|
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
#[test]
|
|
|
|
fn test_protocol_sanitize() {
|
|
|
|
let mut pd = PruneData::default();
|
|
|
|
pd.wallclock = MAX_WALLCLOCK;
|
|
|
|
let msg = Protocol::PruneMessage(Pubkey::default(), pd);
|
2020-04-29 18:12:51 -07:00
|
|
|
assert_eq!(msg.sanitize(), Err(SanitizeError::ValueOutOfBounds));
|
2020-04-27 11:06:00 -07:00
|
|
|
}
|
|
|
|
|
2019-11-14 10:24:53 -08:00
|
|
|
// computes the maximum size for pull request blooms
|
|
|
|
fn max_bloom_size() -> usize {
|
|
|
|
let filter_size = serialized_size(&CrdsFilter::default())
|
|
|
|
.expect("unable to serialize default filter") as usize;
|
|
|
|
let protocol = Protocol::PullRequest(
|
|
|
|
CrdsFilter::default(),
|
|
|
|
CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::default())),
|
|
|
|
);
|
|
|
|
let protocol_size =
|
|
|
|
serialized_size(&protocol).expect("unable to serialize gossip protocol") as usize;
|
|
|
|
PACKET_DATA_SIZE - (protocol_size - filter_size)
|
|
|
|
}
|
2020-02-17 12:39:30 -08:00
|
|
|
|
|
|
|
#[test]
|
2020-03-11 21:31:50 -07:00
|
|
|
fn test_push_epoch_slots_large() {
|
|
|
|
use rand::Rng;
|
|
|
|
let node_keypair = Arc::new(Keypair::new());
|
2020-04-21 12:54:45 -07:00
|
|
|
let cluster_info = ClusterInfo::new(
|
2020-03-11 21:31:50 -07:00
|
|
|
ContactInfo::new_localhost(&node_keypair.pubkey(), timestamp()),
|
|
|
|
node_keypair,
|
2020-02-17 12:39:30 -08:00
|
|
|
);
|
2020-03-11 21:31:50 -07:00
|
|
|
let mut range: Vec<Slot> = vec![];
|
|
|
|
//random should be hard to compress
|
|
|
|
for _ in 0..32000 {
|
|
|
|
let last = *range.last().unwrap_or(&0);
|
|
|
|
range.push(last + rand::thread_rng().gen_range(1, 32));
|
|
|
|
}
|
|
|
|
cluster_info.push_epoch_slots(&range[..16000]);
|
|
|
|
cluster_info.push_epoch_slots(&range[16000..]);
|
|
|
|
let (slots, since) = cluster_info.get_epoch_slots_since(None);
|
|
|
|
let slots: Vec<_> = slots.iter().flat_map(|x| x.to_slots(0)).collect();
|
|
|
|
assert_eq!(slots, range);
|
|
|
|
assert!(since.is_some());
|
2020-02-17 12:39:30 -08:00
|
|
|
}
|
2020-05-24 15:38:35 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_vote_size() {
|
|
|
|
let slots = vec![1; 32];
|
|
|
|
let vote = Vote::new(slots, Hash::default());
|
|
|
|
let keypair = Arc::new(Keypair::new());
|
|
|
|
|
|
|
|
// Create the biggest possible vote transaction
|
|
|
|
let vote_ix = vote_instruction::vote_switch(
|
|
|
|
&keypair.pubkey(),
|
|
|
|
&keypair.pubkey(),
|
|
|
|
vote,
|
|
|
|
Hash::default(),
|
|
|
|
);
|
|
|
|
let mut vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&keypair.pubkey()));
|
|
|
|
|
|
|
|
vote_tx.partial_sign(&[keypair.as_ref()], Hash::default());
|
|
|
|
vote_tx.partial_sign(&[keypair.as_ref()], Hash::default());
|
|
|
|
|
|
|
|
let vote = CrdsVote {
|
|
|
|
from: keypair.pubkey(),
|
|
|
|
transaction: vote_tx,
|
|
|
|
wallclock: 0,
|
|
|
|
};
|
|
|
|
let vote = CrdsValue::new_signed(CrdsData::Vote(1, vote), &Keypair::new());
|
|
|
|
assert!(bincode::serialized_size(&vote).unwrap() <= MAX_PROTOCOL_PAYLOAD_SIZE);
|
|
|
|
}
|
2019-03-08 18:08:24 -08:00
|
|
|
}
|