2018-11-15 13:23:26 -08:00
|
|
|
//! Crds Gossip Pull overlay
|
|
|
|
//! This module implements the anti-entropy protocol for the network.
|
|
|
|
//!
|
|
|
|
//! The basic strategy is as follows:
|
|
|
|
//! 1. Construct a bloom filter of the local data set
|
|
|
|
//! 2. Randomly ask a node on the network for data that is not contained in the bloom filter.
|
|
|
|
//!
|
|
|
|
//! Bloom filters have a false positive rate. Each requests uses a different bloom filter
|
|
|
|
//! with random hash functions. So each subsequent request will have a different distribution
|
|
|
|
//! of false positives.
|
|
|
|
|
2021-03-24 11:33:56 -07:00
|
|
|
use crate::{
|
2021-04-20 11:06:13 -07:00
|
|
|
cluster_info::{Ping, CRDS_UNIQUE_PUBKEY_CAPACITY},
|
2021-03-24 11:33:56 -07:00
|
|
|
contact_info::ContactInfo,
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
crds::{Crds, CrdsError},
|
2021-03-24 11:33:56 -07:00
|
|
|
crds_gossip::{get_stake, get_weight, CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS},
|
|
|
|
crds_gossip_error::CrdsGossipError,
|
2021-04-30 09:57:19 -07:00
|
|
|
crds_value::CrdsValue,
|
2021-04-20 11:06:13 -07:00
|
|
|
ping_pong::PingCache,
|
2021-03-24 11:33:56 -07:00
|
|
|
};
|
2020-12-18 10:45:12 -08:00
|
|
|
use itertools::Itertools;
|
2021-03-24 11:33:56 -07:00
|
|
|
use lru::LruCache;
|
2018-12-05 14:12:10 -08:00
|
|
|
use rand::distributions::{Distribution, WeightedIndex};
|
2019-08-13 18:04:14 -07:00
|
|
|
use rand::Rng;
|
2020-09-29 16:06:02 -07:00
|
|
|
use rayon::{prelude::*, ThreadPool};
|
|
|
|
use solana_runtime::bloom::{AtomicBloom, Bloom};
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
use solana_sdk::{
|
|
|
|
hash::{hash, Hash},
|
|
|
|
pubkey::Pubkey,
|
2021-04-20 11:06:13 -07:00
|
|
|
signature::{Keypair, Signer},
|
|
|
|
};
|
|
|
|
use std::{
|
|
|
|
cmp,
|
|
|
|
collections::{HashMap, HashSet, VecDeque},
|
|
|
|
convert::TryInto,
|
|
|
|
net::SocketAddr,
|
|
|
|
sync::Mutex,
|
|
|
|
time::Instant,
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
};
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
pub const CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS: u64 = 15000;
|
2020-02-07 12:38:24 -08:00
|
|
|
// The maximum age of a value received over pull responses
|
|
|
|
pub const CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS: u64 = 60000;
|
2020-09-30 17:39:22 -07:00
|
|
|
// Retention period of hashes of received outdated values.
|
|
|
|
const FAILED_INSERTS_RETENTION_MS: u64 = 20_000;
|
2020-11-12 08:09:37 -08:00
|
|
|
// Do not pull from peers which have not been updated for this long.
|
|
|
|
const PULL_ACTIVE_TIMEOUT_MS: u64 = 60_000;
|
2019-08-13 18:04:14 -07:00
|
|
|
pub const FALSE_RATE: f64 = 0.1f64;
|
2019-08-19 18:14:10 -07:00
|
|
|
pub const KEYS: f64 = 8f64;
|
2019-08-13 18:04:14 -07:00
|
|
|
|
2020-09-13 06:08:25 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, AbiExample)]
|
2019-08-13 18:04:14 -07:00
|
|
|
pub struct CrdsFilter {
|
|
|
|
pub filter: Bloom<Hash>,
|
|
|
|
mask: u64,
|
|
|
|
mask_bits: u32,
|
|
|
|
}
|
|
|
|
|
2020-09-13 06:08:25 -07:00
|
|
|
impl Default for CrdsFilter {
|
|
|
|
fn default() -> Self {
|
|
|
|
CrdsFilter {
|
|
|
|
filter: Bloom::default(),
|
|
|
|
mask: !0u64,
|
|
|
|
mask_bits: 0u32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 11:06:00 -07:00
|
|
|
impl solana_sdk::sanitize::Sanitize for CrdsFilter {
|
|
|
|
fn sanitize(&self) -> std::result::Result<(), solana_sdk::sanitize::SanitizeError> {
|
|
|
|
self.filter.sanitize()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 18:04:14 -07:00
|
|
|
impl CrdsFilter {
|
|
|
|
pub fn new_rand(num_items: usize, max_bytes: usize) -> Self {
|
|
|
|
let max_bits = (max_bytes * 8) as f64;
|
2019-08-19 18:14:10 -07:00
|
|
|
let max_items = Self::max_items(max_bits, FALSE_RATE, KEYS);
|
2019-08-13 18:04:14 -07:00
|
|
|
let mask_bits = Self::mask_bits(num_items as f64, max_items as f64);
|
2019-08-19 18:14:10 -07:00
|
|
|
let filter = Bloom::random(max_items as usize, FALSE_RATE, max_bits as usize);
|
2019-08-13 18:04:14 -07:00
|
|
|
let seed: u64 = rand::thread_rng().gen_range(0, 2u64.pow(mask_bits));
|
|
|
|
let mask = Self::compute_mask(seed, mask_bits);
|
|
|
|
CrdsFilter {
|
|
|
|
filter,
|
|
|
|
mask,
|
|
|
|
mask_bits,
|
|
|
|
}
|
|
|
|
}
|
2020-09-03 13:32:23 -07:00
|
|
|
|
2019-08-13 18:04:14 -07:00
|
|
|
fn compute_mask(seed: u64, mask_bits: u32) -> u64 {
|
|
|
|
assert!(seed <= 2u64.pow(mask_bits));
|
|
|
|
let seed: u64 = seed.checked_shl(64 - mask_bits).unwrap_or(0x0);
|
|
|
|
seed | (!0u64).checked_shr(mask_bits).unwrap_or(!0x0) as u64
|
|
|
|
}
|
2020-11-19 15:35:22 -08:00
|
|
|
fn max_items(max_bits: f64, false_rate: f64, num_keys: f64) -> f64 {
|
2019-08-13 18:04:14 -07:00
|
|
|
let m = max_bits;
|
|
|
|
let p = false_rate;
|
|
|
|
let k = num_keys;
|
|
|
|
(m / (-k / (1f64 - (p.ln() / k).exp()).ln())).ceil()
|
|
|
|
}
|
|
|
|
fn mask_bits(num_items: f64, max_items: f64) -> u32 {
|
|
|
|
// for small ratios this can result in a negative number, ensure it returns 0 instead
|
|
|
|
((num_items / max_items).log2().ceil()).max(0.0) as u32
|
|
|
|
}
|
2020-08-12 22:45:19 -07:00
|
|
|
pub fn hash_as_u64(item: &Hash) -> u64 {
|
2020-09-09 08:28:17 -07:00
|
|
|
let buf = item.as_ref()[..8].try_into().unwrap();
|
|
|
|
u64::from_le_bytes(buf)
|
2019-08-13 18:04:14 -07:00
|
|
|
}
|
2020-11-19 15:35:22 -08:00
|
|
|
fn test_mask(&self, item: &Hash) -> bool {
|
2019-08-13 18:04:14 -07:00
|
|
|
// only consider the highest mask_bits bits from the hash and set the rest to 1.
|
|
|
|
let ones = (!0u64).checked_shr(self.mask_bits).unwrap_or(!0u64);
|
|
|
|
let bits = Self::hash_as_u64(item) | ones;
|
|
|
|
bits == self.mask
|
|
|
|
}
|
2020-11-19 15:35:22 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
fn add(&mut self, item: &Hash) {
|
2019-08-13 18:04:14 -07:00
|
|
|
if self.test_mask(item) {
|
|
|
|
self.filter.add(item);
|
|
|
|
}
|
|
|
|
}
|
2020-11-19 15:35:22 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
fn contains(&self, item: &Hash) -> bool {
|
2019-08-13 18:04:14 -07:00
|
|
|
if !self.test_mask(item) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
self.filter.contains(item)
|
|
|
|
}
|
2020-11-19 15:35:22 -08:00
|
|
|
fn filter_contains(&self, item: &Hash) -> bool {
|
2020-08-12 22:45:19 -07:00
|
|
|
self.filter.contains(item)
|
|
|
|
}
|
2019-08-13 18:04:14 -07:00
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
|
2020-09-04 06:04:47 -07:00
|
|
|
/// A vector of crds filters that together hold a complete set of Hashes.
|
2020-09-29 16:06:02 -07:00
|
|
|
struct CrdsFilterSet {
|
|
|
|
filters: Vec<AtomicBloom<Hash>>,
|
|
|
|
mask_bits: u32,
|
|
|
|
}
|
2020-09-04 06:04:47 -07:00
|
|
|
|
|
|
|
impl CrdsFilterSet {
|
|
|
|
fn new(num_items: usize, max_bytes: usize) -> Self {
|
|
|
|
let max_bits = (max_bytes * 8) as f64;
|
|
|
|
let max_items = CrdsFilter::max_items(max_bits, FALSE_RATE, KEYS);
|
|
|
|
let mask_bits = CrdsFilter::mask_bits(num_items as f64, max_items as f64);
|
2020-09-29 16:06:02 -07:00
|
|
|
let filters = std::iter::repeat_with(|| {
|
|
|
|
Bloom::random(max_items as usize, FALSE_RATE, max_bits as usize).into()
|
|
|
|
})
|
|
|
|
.take(1 << mask_bits)
|
|
|
|
.collect();
|
|
|
|
Self { filters, mask_bits }
|
2020-09-04 06:04:47 -07:00
|
|
|
}
|
|
|
|
|
2020-09-29 16:06:02 -07:00
|
|
|
fn add(&self, hash_value: Hash) {
|
|
|
|
let index = CrdsFilter::hash_as_u64(&hash_value)
|
|
|
|
.checked_shr(64 - self.mask_bits)
|
|
|
|
.unwrap_or(0);
|
|
|
|
self.filters[index as usize].add(&hash_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-23 11:55:15 -08:00
|
|
|
impl From<CrdsFilterSet> for Vec<CrdsFilter> {
|
|
|
|
fn from(cfs: CrdsFilterSet) -> Self {
|
|
|
|
let mask_bits = cfs.mask_bits;
|
|
|
|
cfs.filters
|
2020-09-29 16:06:02 -07:00
|
|
|
.into_iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(seed, filter)| CrdsFilter {
|
|
|
|
filter: filter.into(),
|
|
|
|
mask: CrdsFilter::compute_mask(seed as u64, mask_bits),
|
|
|
|
mask_bits,
|
|
|
|
})
|
|
|
|
.collect()
|
2020-09-04 06:04:47 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-09 17:08:13 -07:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct ProcessPullStats {
|
|
|
|
pub success: usize,
|
|
|
|
pub failed_insert: usize,
|
|
|
|
pub failed_timeout: usize,
|
|
|
|
pub timeout_count: usize,
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
pub struct CrdsGossipPull {
|
|
|
|
/// timestamp of last request
|
2021-03-24 11:33:56 -07:00
|
|
|
pub(crate) pull_request_time: LruCache<Pubkey, u64>,
|
2018-11-15 13:23:26 -08:00
|
|
|
/// hash and insert time
|
2020-08-11 14:03:54 -07:00
|
|
|
pub purged_values: VecDeque<(Hash, u64)>,
|
2020-09-30 17:39:22 -07:00
|
|
|
// Hash value and record time (ms) of the pull responses which failed to be
|
|
|
|
// inserted in crds table; Preserved to stop the sender to send back the
|
|
|
|
// same outdated payload again by adding them to the filter for the next
|
|
|
|
// pull request.
|
|
|
|
pub failed_inserts: VecDeque<(Hash, u64)>,
|
2018-11-15 13:23:26 -08:00
|
|
|
pub crds_timeout: u64,
|
2020-02-07 12:38:24 -08:00
|
|
|
pub msg_timeout: u64,
|
2020-06-13 22:03:38 -07:00
|
|
|
pub num_pulls: usize,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for CrdsGossipPull {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
purged_values: VecDeque::new(),
|
2021-03-24 11:33:56 -07:00
|
|
|
pull_request_time: LruCache::new(CRDS_UNIQUE_PUBKEY_CAPACITY),
|
2020-09-30 17:39:22 -07:00
|
|
|
failed_inserts: VecDeque::new(),
|
2018-11-15 13:23:26 -08:00
|
|
|
crds_timeout: CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS,
|
2020-02-07 12:38:24 -08:00
|
|
|
msg_timeout: CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
2020-06-13 22:03:38 -07:00
|
|
|
num_pulls: 0,
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl CrdsGossipPull {
|
|
|
|
/// generate a random request
|
2021-04-20 11:06:13 -07:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2018-11-15 13:23:26 -08:00
|
|
|
pub fn new_pull_request(
|
|
|
|
&self,
|
2020-09-29 16:06:02 -07:00
|
|
|
thread_pool: &ThreadPool,
|
2018-11-15 13:23:26 -08:00
|
|
|
crds: &Crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
self_keypair: &Keypair,
|
2020-05-05 20:15:19 -07:00
|
|
|
self_shred_version: u16,
|
2018-11-15 13:23:26 -08:00
|
|
|
now: u64,
|
2020-09-11 12:00:16 -07:00
|
|
|
gossip_validators: Option<&HashSet<Pubkey>>,
|
2019-02-20 20:02:47 -08:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
2019-08-13 18:04:14 -07:00
|
|
|
bloom_size: usize,
|
2021-04-20 11:06:13 -07:00
|
|
|
ping_cache: &Mutex<PingCache>,
|
|
|
|
pings: &mut Vec<(SocketAddr, Ping)>,
|
2021-04-28 06:19:12 -07:00
|
|
|
) -> Result<(ContactInfo, Vec<CrdsFilter>), CrdsGossipError> {
|
2021-04-20 11:06:13 -07:00
|
|
|
let (weights, peers): (Vec<_>, Vec<_>) = self
|
|
|
|
.pull_options(
|
|
|
|
crds,
|
|
|
|
&self_keypair.pubkey(),
|
|
|
|
self_shred_version,
|
|
|
|
now,
|
|
|
|
gossip_validators,
|
|
|
|
stakes,
|
|
|
|
)
|
|
|
|
.into_iter()
|
|
|
|
.unzip();
|
|
|
|
if peers.is_empty() {
|
2018-11-15 13:23:26 -08:00
|
|
|
return Err(CrdsGossipError::NoPeers);
|
|
|
|
}
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut peers = {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let num_samples = peers.len() * 2;
|
|
|
|
let index = WeightedIndex::new(weights).unwrap();
|
|
|
|
let sample_peer = move || peers[index.sample(&mut rng)];
|
|
|
|
std::iter::repeat_with(sample_peer).take(num_samples)
|
|
|
|
};
|
|
|
|
let peer = {
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let mut ping_cache = ping_cache.lock().unwrap();
|
|
|
|
let mut pingf = move || Ping::new_rand(&mut rng, self_keypair).ok();
|
|
|
|
let now = Instant::now();
|
|
|
|
peers.find(|peer| {
|
|
|
|
let node = (peer.id, peer.gossip);
|
|
|
|
let (check, ping) = ping_cache.check(now, node, &mut pingf);
|
|
|
|
if let Some(ping) = ping {
|
|
|
|
pings.push((peer.gossip, ping));
|
|
|
|
}
|
|
|
|
check
|
|
|
|
})
|
|
|
|
};
|
|
|
|
match peer {
|
|
|
|
None => Err(CrdsGossipError::NoPeers),
|
|
|
|
Some(peer) => {
|
|
|
|
let filters = self.build_crds_filters(thread_pool, crds, bloom_size);
|
|
|
|
Ok((peer.clone(), filters))
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
2019-02-26 11:45:10 -08:00
|
|
|
fn pull_options<'a>(
|
|
|
|
&self,
|
|
|
|
crds: &'a Crds,
|
|
|
|
self_id: &Pubkey,
|
2020-05-05 20:15:19 -07:00
|
|
|
self_shred_version: u16,
|
2019-02-26 11:45:10 -08:00
|
|
|
now: u64,
|
2020-09-11 12:00:16 -07:00
|
|
|
gossip_validators: Option<&HashSet<Pubkey>>,
|
2019-02-26 11:45:10 -08:00
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
|
|
|
) -> Vec<(f32, &'a ContactInfo)> {
|
2020-11-12 08:09:37 -08:00
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let active_cutoff = now.saturating_sub(PULL_ACTIVE_TIMEOUT_MS);
|
2020-11-15 08:38:04 -08:00
|
|
|
crds.get_nodes()
|
2020-11-12 08:09:37 -08:00
|
|
|
.filter_map(|value| {
|
2020-11-15 08:38:04 -08:00
|
|
|
let info = value.value.contact_info().unwrap();
|
2020-11-12 08:09:37 -08:00
|
|
|
// Stop pulling from nodes which have not been active recently.
|
|
|
|
if value.local_timestamp < active_cutoff {
|
|
|
|
// In order to mitigate eclipse attack, for staked nodes
|
|
|
|
// continue retrying periodically.
|
|
|
|
let stake = stakes.get(&info.id).unwrap_or(&0);
|
|
|
|
if *stake == 0 || !rng.gen_ratio(1, 16) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(info)
|
|
|
|
})
|
2020-05-05 20:15:19 -07:00
|
|
|
.filter(|v| {
|
|
|
|
v.id != *self_id
|
|
|
|
&& ContactInfo::is_valid_address(&v.gossip)
|
2020-08-18 18:52:45 -07:00
|
|
|
&& (self_shred_version == 0 || self_shred_version == v.shred_version)
|
2020-09-11 12:00:16 -07:00
|
|
|
&& gossip_validators
|
|
|
|
.map_or(true, |gossip_validators| gossip_validators.contains(&v.id))
|
2020-05-05 20:15:19 -07:00
|
|
|
})
|
2019-02-26 11:45:10 -08:00
|
|
|
.map(|item| {
|
|
|
|
let max_weight = f32::from(u16::max_value()) - 1.0;
|
2021-03-24 11:33:56 -07:00
|
|
|
let req_time: u64 = self
|
|
|
|
.pull_request_time
|
|
|
|
.peek(&item.id)
|
|
|
|
.copied()
|
|
|
|
.unwrap_or_default();
|
|
|
|
let since = (now.saturating_sub(req_time).min(3600 * 1000) / 1024) as u32;
|
2019-02-26 11:45:10 -08:00
|
|
|
let stake = get_stake(&item.id, stakes);
|
|
|
|
let weight = get_weight(max_weight, since, stake);
|
|
|
|
(weight, item)
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
/// time when a request to `from` was initiated
|
|
|
|
/// This is used for weighted random selection during `new_pull_request`
|
|
|
|
/// It's important to use the local nodes request creation time as the weight
|
|
|
|
/// instead of the response received time otherwise failed nodes will increase their weight.
|
2021-04-28 06:19:12 -07:00
|
|
|
pub fn mark_pull_request_creation_time(&mut self, from: Pubkey, now: u64) {
|
|
|
|
self.pull_request_time.put(from, now);
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Store an old hash in the purged values set
|
|
|
|
pub fn record_old_hash(&mut self, hash: Hash, timestamp: u64) {
|
|
|
|
self.purged_values.push_back((hash, timestamp))
|
|
|
|
}
|
|
|
|
|
2020-05-28 11:38:13 -07:00
|
|
|
/// process a pull request
|
2020-10-28 10:03:02 -07:00
|
|
|
pub fn process_pull_requests<I>(&mut self, crds: &mut Crds, callers: I, now: u64)
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = CrdsValue>,
|
|
|
|
{
|
|
|
|
for caller in callers {
|
2019-08-15 17:04:45 -07:00
|
|
|
let key = caller.label().pubkey();
|
2020-10-06 06:48:32 -07:00
|
|
|
if let Ok(Some(val)) = crds.insert(caller, now) {
|
2021-04-15 06:18:39 -07:00
|
|
|
self.purged_values.push_back((val.value_hash, now));
|
2019-08-15 17:04:45 -07:00
|
|
|
}
|
|
|
|
crds.update_record_timestamp(&key, now);
|
2020-10-28 10:03:02 -07:00
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2020-05-28 11:38:13 -07:00
|
|
|
|
|
|
|
/// Create gossip responses to pull requests
|
|
|
|
pub fn generate_pull_responses(
|
|
|
|
&self,
|
|
|
|
crds: &Crds,
|
|
|
|
requests: &[(CrdsValue, CrdsFilter)],
|
2020-12-18 10:45:12 -08:00
|
|
|
output_size_limit: usize, // Limit number of crds values returned.
|
2020-08-11 06:26:42 -07:00
|
|
|
now: u64,
|
2020-05-28 11:38:13 -07:00
|
|
|
) -> Vec<Vec<CrdsValue>> {
|
2020-12-18 10:45:12 -08:00
|
|
|
self.filter_crds_values(crds, requests, output_size_limit, now)
|
2020-05-28 11:38:13 -07:00
|
|
|
}
|
|
|
|
|
2020-06-09 17:08:13 -07:00
|
|
|
// Checks if responses should be inserted and
|
|
|
|
// returns those responses converted to VersionedCrdsValue
|
2020-09-30 17:39:22 -07:00
|
|
|
// Separated in three vecs as:
|
2020-06-09 17:08:13 -07:00
|
|
|
// .0 => responses that update the owner timestamp
|
|
|
|
// .1 => responses that do not update the owner timestamp
|
2020-09-30 17:39:22 -07:00
|
|
|
// .2 => hash value of outdated values which will fail to insert.
|
2020-06-09 17:08:13 -07:00
|
|
|
pub fn filter_pull_responses(
|
|
|
|
&self,
|
|
|
|
crds: &Crds,
|
2020-02-07 12:38:24 -08:00
|
|
|
timeouts: &HashMap<Pubkey, u64>,
|
2020-06-09 17:08:13 -07:00
|
|
|
responses: Vec<CrdsValue>,
|
2018-11-15 13:23:26 -08:00
|
|
|
now: u64,
|
2020-06-09 17:08:13 -07:00
|
|
|
stats: &mut ProcessPullStats,
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
) -> (Vec<CrdsValue>, Vec<CrdsValue>, Vec<Hash>) {
|
|
|
|
let mut active_values = vec![];
|
|
|
|
let mut expired_values = vec![];
|
2020-09-30 17:39:22 -07:00
|
|
|
let mut failed_inserts = vec![];
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
let mut maybe_push = |response, values: &mut Vec<CrdsValue>| {
|
|
|
|
if crds.upserts(&response) {
|
|
|
|
values.push(response);
|
2020-09-30 17:39:22 -07:00
|
|
|
} else {
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
let response = bincode::serialize(&response).unwrap();
|
|
|
|
failed_inserts.push(hash(&response));
|
2020-09-30 17:39:22 -07:00
|
|
|
}
|
|
|
|
};
|
2021-04-14 13:18:00 -07:00
|
|
|
let default_timeout = timeouts
|
|
|
|
.get(&Pubkey::default())
|
|
|
|
.copied()
|
|
|
|
.unwrap_or(self.msg_timeout);
|
|
|
|
for response in responses {
|
|
|
|
let owner = response.label().pubkey();
|
2020-02-07 12:38:24 -08:00
|
|
|
// Check if the crds value is older than the msg_timeout
|
2021-04-14 13:18:00 -07:00
|
|
|
let timeout = timeouts.get(&owner).copied().unwrap_or(default_timeout);
|
|
|
|
// Before discarding this value, check if a ContactInfo for the
|
|
|
|
// owner exists in the table. If it doesn't, that implies that this
|
|
|
|
// value can be discarded
|
|
|
|
if now <= response.wallclock().saturating_add(timeout) {
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
maybe_push(response, &mut active_values);
|
2021-04-14 13:18:00 -07:00
|
|
|
} else if crds.get_contact_info(owner).is_some() {
|
|
|
|
// Silently insert this old value without bumping record
|
|
|
|
// timestamps
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
maybe_push(response, &mut expired_values);
|
2021-04-14 13:18:00 -07:00
|
|
|
} else {
|
|
|
|
stats.timeout_count += 1;
|
|
|
|
stats.failed_timeout += 1;
|
2020-02-07 12:38:24 -08:00
|
|
|
}
|
2020-06-09 17:08:13 -07:00
|
|
|
}
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
(active_values, expired_values, failed_inserts)
|
2020-06-09 17:08:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// process a vec of pull responses
|
|
|
|
pub fn process_pull_responses(
|
|
|
|
&mut self,
|
|
|
|
crds: &mut Crds,
|
|
|
|
from: &Pubkey,
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
responses: Vec<CrdsValue>,
|
|
|
|
responses_expired_timeout: Vec<CrdsValue>,
|
2020-09-30 17:39:22 -07:00
|
|
|
mut failed_inserts: Vec<Hash>,
|
2020-06-09 17:08:13 -07:00
|
|
|
now: u64,
|
|
|
|
stats: &mut ProcessPullStats,
|
2021-04-30 09:57:19 -07:00
|
|
|
) {
|
2020-06-09 17:08:13 -07:00
|
|
|
let mut owners = HashSet::new();
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
for response in responses_expired_timeout {
|
|
|
|
match crds.insert(response, now) {
|
2021-04-27 05:06:49 -07:00
|
|
|
Ok(None) => (),
|
|
|
|
Ok(Some(old)) => self.purged_values.push_back((old.value_hash, now)),
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
Err(CrdsError::InsertFailed(value_hash)) => failed_inserts.push(value_hash),
|
|
|
|
Err(CrdsError::UnknownStakes) => (),
|
2020-09-30 17:39:22 -07:00
|
|
|
}
|
2020-06-09 17:08:13 -07:00
|
|
|
}
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
for response in responses {
|
2021-04-30 09:57:19 -07:00
|
|
|
let owner = response.pubkey();
|
removes delayed crds inserts when upserting gossip table (#16806)
It is crucial that VersionedCrdsValue::insert_timestamp does not go
backward in time:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L67-L79
Otherwise methods such as get_votes and get_epoch_slots_since will
break, which will break their downstream flow, including vote-listener
and optimistic confirmation:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1197-L1215
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L1274-L1298
For that, Crds::new_versioned is intended to be called "atomically" with
Crds::insert_verioned (as the comment already says so):
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/crds.rs#L126-L129
However, currently this is violated in the code. For example,
filter_pull_responses creates VersionedCrdsValues (with the current
timestamp), then acquires an exclusive lock on gossip, then
process_pull_responses writes those values to the crds table:
https://github.com/solana-labs/solana/blob/ec37a843a/core/src/cluster_info.rs#L2375-L2392
Depending on the workload and lock contention, the insert_timestamps may
well be in the past when these values finally are inserted into gossip.
To avoid such scenarios, this commit:
* removes Crds::new_versioned and Crd::insert_versioned.
* makes VersionedCrdsValue constructor private, only invoked in
Crds::insert, so that insert_timestamp is populated right before
insert.
This will improve insert_timestamp monotonicity as long as Crds::insert
is not called with a stalled timestamp. Following commits may further
improve this by calling timestamp() inside Crds::insert, and/or
switching to std::time::Instant which guarantees monotonicity.
2021-04-28 04:56:13 -07:00
|
|
|
match crds.insert(response, now) {
|
|
|
|
Err(CrdsError::InsertFailed(value_hash)) => failed_inserts.push(value_hash),
|
|
|
|
Err(CrdsError::UnknownStakes) => (),
|
2020-09-30 17:39:22 -07:00
|
|
|
Ok(old) => {
|
|
|
|
stats.success += 1;
|
|
|
|
self.num_pulls += 1;
|
2021-04-30 09:57:19 -07:00
|
|
|
owners.insert(owner);
|
2020-09-30 17:39:22 -07:00
|
|
|
if let Some(val) = old {
|
2021-04-15 06:18:39 -07:00
|
|
|
self.purged_values.push_back((val.value_hash, now))
|
2020-09-30 17:39:22 -07:00
|
|
|
}
|
|
|
|
}
|
2020-05-28 11:38:13 -07:00
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2020-06-09 17:08:13 -07:00
|
|
|
owners.insert(*from);
|
|
|
|
for owner in owners {
|
|
|
|
crds.update_record_timestamp(&owner, now);
|
|
|
|
}
|
2020-09-30 17:39:22 -07:00
|
|
|
stats.failed_insert += failed_inserts.len();
|
|
|
|
self.purge_failed_inserts(now);
|
|
|
|
self.failed_inserts
|
|
|
|
.extend(failed_inserts.into_iter().zip(std::iter::repeat(now)));
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2020-09-30 17:39:22 -07:00
|
|
|
|
|
|
|
pub fn purge_failed_inserts(&mut self, now: u64) {
|
|
|
|
if FAILED_INSERTS_RETENTION_MS < now {
|
|
|
|
let cutoff = now - FAILED_INSERTS_RETENTION_MS;
|
|
|
|
let outdated = self
|
|
|
|
.failed_inserts
|
|
|
|
.iter()
|
|
|
|
.take_while(|(_, ts)| *ts < cutoff)
|
|
|
|
.count();
|
|
|
|
self.failed_inserts.drain(..outdated);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 18:04:14 -07:00
|
|
|
// build a set of filters of the current crds table
|
2020-04-15 15:22:16 -07:00
|
|
|
// num_filters - used to increase the likelyhood of a value in crds being added to some filter
|
2020-09-29 16:06:02 -07:00
|
|
|
pub fn build_crds_filters(
|
|
|
|
&self,
|
|
|
|
thread_pool: &ThreadPool,
|
|
|
|
crds: &Crds,
|
|
|
|
bloom_size: usize,
|
|
|
|
) -> Vec<CrdsFilter> {
|
|
|
|
const PAR_MIN_LENGTH: usize = 512;
|
2018-12-01 12:00:30 -08:00
|
|
|
let num = cmp::max(
|
2019-08-13 18:04:14 -07:00
|
|
|
CRDS_GOSSIP_DEFAULT_BLOOM_ITEMS,
|
2020-11-19 12:57:40 -08:00
|
|
|
crds.len() + self.purged_values.len() + self.failed_inserts.len(),
|
2018-12-01 12:00:30 -08:00
|
|
|
);
|
2020-09-29 16:06:02 -07:00
|
|
|
let filters = CrdsFilterSet::new(num, bloom_size);
|
2020-09-30 17:39:22 -07:00
|
|
|
thread_pool.install(|| {
|
2020-11-19 12:57:40 -08:00
|
|
|
crds.par_values()
|
2020-09-30 17:39:22 -07:00
|
|
|
.with_min_len(PAR_MIN_LENGTH)
|
|
|
|
.map(|v| v.value_hash)
|
|
|
|
.chain(
|
|
|
|
self.purged_values
|
|
|
|
.par_iter()
|
|
|
|
.with_min_len(PAR_MIN_LENGTH)
|
|
|
|
.map(|(v, _)| *v),
|
|
|
|
)
|
|
|
|
.chain(
|
|
|
|
self.failed_inserts
|
|
|
|
.par_iter()
|
|
|
|
.with_min_len(PAR_MIN_LENGTH)
|
|
|
|
.map(|(v, _)| *v),
|
|
|
|
)
|
|
|
|
.for_each(|v| filters.add(v));
|
|
|
|
});
|
2020-09-29 16:06:02 -07:00
|
|
|
filters.into()
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
2020-08-11 14:03:54 -07:00
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
/// filter values that fail the bloom filter up to max_bytes
|
2019-08-15 17:04:45 -07:00
|
|
|
fn filter_crds_values(
|
2020-06-09 07:27:00 -07:00
|
|
|
&self,
|
2019-08-15 17:04:45 -07:00
|
|
|
crds: &Crds,
|
|
|
|
filters: &[(CrdsValue, CrdsFilter)],
|
2020-12-18 10:45:12 -08:00
|
|
|
mut output_size_limit: usize, // Limit number of crds values returned.
|
2020-08-11 06:26:42 -07:00
|
|
|
now: u64,
|
2019-08-15 17:04:45 -07:00
|
|
|
) -> Vec<Vec<CrdsValue>> {
|
2020-08-11 06:26:42 -07:00
|
|
|
let msg_timeout = CRDS_GOSSIP_PULL_CRDS_TIMEOUT_MS;
|
|
|
|
let jitter = rand::thread_rng().gen_range(0, msg_timeout / 4);
|
|
|
|
//skip filters from callers that are too old
|
|
|
|
let future = now.saturating_add(msg_timeout);
|
|
|
|
let past = now.saturating_sub(msg_timeout);
|
2020-09-17 07:05:16 -07:00
|
|
|
let mut dropped_requests = 0;
|
2020-08-11 06:26:42 -07:00
|
|
|
let mut total_skipped = 0;
|
2020-12-18 10:45:12 -08:00
|
|
|
let ret: Vec<_> = filters
|
2020-08-12 22:45:19 -07:00
|
|
|
.iter()
|
2020-09-17 07:05:16 -07:00
|
|
|
.map(|(caller, filter)| {
|
2020-12-18 10:45:12 -08:00
|
|
|
if output_size_limit == 0 {
|
|
|
|
return None;
|
|
|
|
}
|
2020-09-17 07:05:16 -07:00
|
|
|
let caller_wallclock = caller.wallclock();
|
|
|
|
if caller_wallclock >= future || caller_wallclock < past {
|
|
|
|
dropped_requests += 1;
|
2020-12-18 10:45:12 -08:00
|
|
|
return Some(vec![]);
|
2020-09-17 07:05:16 -07:00
|
|
|
}
|
|
|
|
let caller_wallclock = caller_wallclock.checked_add(jitter).unwrap_or(0);
|
2020-12-18 10:45:12 -08:00
|
|
|
let out: Vec<_> = crds
|
|
|
|
.filter_bitmask(filter.mask, filter.mask_bits)
|
2020-11-19 12:57:40 -08:00
|
|
|
.filter_map(|item| {
|
2020-09-17 07:05:16 -07:00
|
|
|
debug_assert!(filter.test_mask(&item.value_hash));
|
2020-08-12 22:45:19 -07:00
|
|
|
//skip values that are too new
|
2020-09-17 07:05:16 -07:00
|
|
|
if item.value.wallclock() > caller_wallclock {
|
2020-08-12 22:45:19 -07:00
|
|
|
total_skipped += 1;
|
2020-09-17 07:05:16 -07:00
|
|
|
None
|
|
|
|
} else if filter.filter_contains(&item.value_hash) {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(item.value.clone())
|
2020-08-12 22:45:19 -07:00
|
|
|
}
|
2020-09-17 07:05:16 -07:00
|
|
|
})
|
2020-12-18 10:45:12 -08:00
|
|
|
.take(output_size_limit)
|
|
|
|
.collect();
|
|
|
|
output_size_limit -= out.len();
|
|
|
|
Some(out)
|
2020-09-17 07:05:16 -07:00
|
|
|
})
|
2020-12-18 10:45:12 -08:00
|
|
|
.while_some()
|
2020-09-17 07:05:16 -07:00
|
|
|
.collect();
|
|
|
|
inc_new_counter_info!(
|
|
|
|
"gossip_filter_crds_values-dropped_requests",
|
2020-12-18 10:45:12 -08:00
|
|
|
dropped_requests + filters.len() - ret.len()
|
2020-09-17 07:05:16 -07:00
|
|
|
);
|
2020-08-11 06:26:42 -07:00
|
|
|
inc_new_counter_info!("gossip_filter_crds_values-dropped_values", total_skipped);
|
2018-11-15 13:23:26 -08:00
|
|
|
ret
|
|
|
|
}
|
2019-11-20 11:25:18 -08:00
|
|
|
pub fn make_timeouts_def(
|
|
|
|
&self,
|
|
|
|
self_id: &Pubkey,
|
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
|
|
|
epoch_ms: u64,
|
|
|
|
min_ts: u64,
|
|
|
|
) -> HashMap<Pubkey, u64> {
|
|
|
|
let mut timeouts: HashMap<Pubkey, u64> = stakes.keys().map(|s| (*s, epoch_ms)).collect();
|
|
|
|
timeouts.insert(*self_id, std::u64::MAX);
|
|
|
|
timeouts.insert(Pubkey::default(), min_ts);
|
|
|
|
timeouts
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn make_timeouts(
|
|
|
|
&self,
|
|
|
|
self_id: &Pubkey,
|
|
|
|
stakes: &HashMap<Pubkey, u64>,
|
|
|
|
epoch_ms: u64,
|
|
|
|
) -> HashMap<Pubkey, u64> {
|
|
|
|
self.make_timeouts_def(self_id, stakes, epoch_ms, self.crds_timeout)
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
/// Purge values from the crds that are older then `active_timeout`
|
|
|
|
/// The value_hash of an active item is put into self.purged_values queue
|
2019-11-20 11:25:18 -08:00
|
|
|
pub fn purge_active(
|
|
|
|
&mut self,
|
2020-10-23 07:17:37 -07:00
|
|
|
thread_pool: &ThreadPool,
|
2019-11-20 11:25:18 -08:00
|
|
|
crds: &mut Crds,
|
|
|
|
now: u64,
|
|
|
|
timeouts: &HashMap<Pubkey, u64>,
|
|
|
|
) -> usize {
|
2020-10-23 07:17:37 -07:00
|
|
|
let num_purged_values = self.purged_values.len();
|
|
|
|
self.purged_values.extend(
|
|
|
|
crds.find_old_labels(thread_pool, now, timeouts)
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(|label| {
|
|
|
|
let val = crds.remove(&label)?;
|
2021-04-15 06:18:39 -07:00
|
|
|
Some((val.value_hash, now))
|
2020-10-23 07:17:37 -07:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
self.purged_values.len() - num_purged_values
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
/// Purge values from the `self.purged_values` queue that are older then purge_timeout
|
|
|
|
pub fn purge_purged(&mut self, min_ts: u64) {
|
|
|
|
let cnt = self
|
|
|
|
.purged_values
|
|
|
|
.iter()
|
|
|
|
.take_while(|v| v.1 < min_ts)
|
|
|
|
.count();
|
|
|
|
self.purged_values.drain(..cnt);
|
|
|
|
}
|
2020-06-09 17:08:13 -07:00
|
|
|
|
|
|
|
/// For legacy tests
|
|
|
|
#[cfg(test)]
|
|
|
|
pub fn process_pull_response(
|
|
|
|
&mut self,
|
|
|
|
crds: &mut Crds,
|
|
|
|
from: &Pubkey,
|
|
|
|
timeouts: &HashMap<Pubkey, u64>,
|
|
|
|
response: Vec<CrdsValue>,
|
|
|
|
now: u64,
|
|
|
|
) -> (usize, usize, usize) {
|
|
|
|
let mut stats = ProcessPullStats::default();
|
2020-09-30 17:39:22 -07:00
|
|
|
let (versioned, versioned_expired_timeout, failed_inserts) =
|
2020-06-09 17:08:13 -07:00
|
|
|
self.filter_pull_responses(crds, timeouts, response, now, &mut stats);
|
|
|
|
self.process_pull_responses(
|
|
|
|
crds,
|
|
|
|
from,
|
|
|
|
versioned,
|
|
|
|
versioned_expired_timeout,
|
2020-09-30 17:39:22 -07:00
|
|
|
failed_inserts,
|
2020-06-09 17:08:13 -07:00
|
|
|
now,
|
|
|
|
&mut stats,
|
|
|
|
);
|
|
|
|
(
|
|
|
|
stats.failed_timeout + stats.failed_insert,
|
|
|
|
stats.timeout_count,
|
|
|
|
stats.success,
|
|
|
|
)
|
|
|
|
}
|
2021-03-24 11:33:56 -07:00
|
|
|
|
|
|
|
// Only for tests and simulations.
|
|
|
|
pub(crate) fn mock_clone(&self) -> Self {
|
|
|
|
let mut pull_request_time = LruCache::new(self.pull_request_time.cap());
|
|
|
|
for (k, v) in self.pull_request_time.iter().rev() {
|
|
|
|
pull_request_time.put(*k, *v);
|
|
|
|
}
|
|
|
|
Self {
|
|
|
|
pull_request_time,
|
|
|
|
purged_values: self.purged_values.clone(),
|
|
|
|
failed_inserts: self.failed_inserts.clone(),
|
|
|
|
..*self
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2020-09-29 16:06:02 -07:00
|
|
|
use crate::cluster_info::MAX_BLOOM_SIZE;
|
2018-12-07 19:16:27 -08:00
|
|
|
use crate::contact_info::ContactInfo;
|
2020-02-07 12:38:24 -08:00
|
|
|
use crate::crds_value::{CrdsData, Vote};
|
2019-08-13 18:04:14 -07:00
|
|
|
use itertools::Itertools;
|
2020-09-29 16:06:02 -07:00
|
|
|
use rand::thread_rng;
|
|
|
|
use rayon::ThreadPoolBuilder;
|
2020-02-07 12:38:24 -08:00
|
|
|
use solana_perf::test_tx::test_tx;
|
2021-03-24 11:33:56 -07:00
|
|
|
use solana_sdk::{
|
|
|
|
hash::{hash, HASH_BYTES},
|
|
|
|
packet::PACKET_DATA_SIZE,
|
|
|
|
timing::timestamp,
|
|
|
|
};
|
2021-04-20 11:06:13 -07:00
|
|
|
use std::{iter::repeat_with, time::Duration};
|
2019-02-20 17:08:56 -08:00
|
|
|
|
2020-09-09 08:28:17 -07:00
|
|
|
#[test]
|
|
|
|
fn test_hash_as_u64() {
|
|
|
|
let arr: Vec<u8> = (0..HASH_BYTES).map(|i| i as u8 + 1).collect();
|
|
|
|
let hash = Hash::new(&arr);
|
|
|
|
assert_eq!(CrdsFilter::hash_as_u64(&hash), 0x807060504030201);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_hash_as_u64_random() {
|
|
|
|
fn hash_as_u64_bitops(hash: &Hash) -> u64 {
|
|
|
|
let mut out = 0;
|
|
|
|
for (i, val) in hash.as_ref().iter().enumerate().take(8) {
|
|
|
|
out |= (u64::from(*val)) << (i * 8) as u64;
|
|
|
|
}
|
|
|
|
out
|
|
|
|
}
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
for _ in 0..100 {
|
2020-10-19 12:15:55 -07:00
|
|
|
let hash = solana_sdk::hash::new_rand(&mut rng);
|
2020-09-09 08:28:17 -07:00
|
|
|
assert_eq!(CrdsFilter::hash_as_u64(&hash), hash_as_u64_bitops(&hash));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-13 06:08:25 -07:00
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_default() {
|
|
|
|
let filter = CrdsFilter::default();
|
|
|
|
let mask = CrdsFilter::compute_mask(0, filter.mask_bits);
|
|
|
|
assert_eq!(filter.mask, mask);
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
for _ in 0..10 {
|
2020-10-19 12:15:55 -07:00
|
|
|
let hash = solana_sdk::hash::new_rand(&mut rng);
|
2020-09-13 06:08:25 -07:00
|
|
|
assert!(filter.test_mask(&hash));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 17:08:56 -08:00
|
|
|
#[test]
|
2019-02-20 20:02:47 -08:00
|
|
|
fn test_new_pull_with_stakes() {
|
2019-02-20 17:08:56 -08:00
|
|
|
let mut crds = Crds::default();
|
2019-02-20 20:02:47 -08:00
|
|
|
let mut stakes = HashMap::new();
|
2019-02-20 17:08:56 -08:00
|
|
|
let node = CrdsGossipPull::default();
|
2019-11-03 10:07:51 -08:00
|
|
|
let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2019-02-20 17:08:56 -08:00
|
|
|
crds.insert(me.clone(), 0).unwrap();
|
|
|
|
for i in 1..=30 {
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2019-02-20 17:08:56 -08:00
|
|
|
let id = entry.label().pubkey();
|
|
|
|
crds.insert(entry.clone(), 0).unwrap();
|
2019-02-20 20:02:47 -08:00
|
|
|
stakes.insert(id, i * 100);
|
2019-02-20 17:08:56 -08:00
|
|
|
}
|
|
|
|
let now = 1024;
|
2020-09-11 12:00:16 -07:00
|
|
|
let mut options = node.pull_options(&crds, &me.label().pubkey(), 0, now, None, &stakes);
|
2019-02-26 11:45:10 -08:00
|
|
|
assert!(!options.is_empty());
|
|
|
|
options.sort_by(|(weight_l, _), (weight_r, _)| weight_r.partial_cmp(weight_l).unwrap());
|
|
|
|
// check that the highest stake holder is also the heaviest weighted.
|
|
|
|
assert_eq!(
|
|
|
|
*stakes.get(&options.get(0).unwrap().1.id).unwrap(),
|
|
|
|
3000_u64
|
|
|
|
);
|
2019-02-20 17:08:56 -08:00
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
|
2020-05-05 20:15:19 -07:00
|
|
|
#[test]
|
|
|
|
fn test_no_pulls_from_different_shred_versions() {
|
|
|
|
let mut crds = Crds::default();
|
|
|
|
let stakes = HashMap::new();
|
|
|
|
let node = CrdsGossipPull::default();
|
|
|
|
|
|
|
|
let gossip = socketaddr!("127.0.0.1:1234");
|
|
|
|
|
|
|
|
let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-05-05 20:15:19 -07:00
|
|
|
shred_version: 123,
|
2020-05-15 09:35:43 -07:00
|
|
|
gossip,
|
2020-05-05 20:15:19 -07:00
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
let spy = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-05-05 20:15:19 -07:00
|
|
|
shred_version: 0,
|
2020-05-15 09:35:43 -07:00
|
|
|
gossip,
|
2020-05-05 20:15:19 -07:00
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-05-05 20:15:19 -07:00
|
|
|
shred_version: 123,
|
2020-05-15 09:35:43 -07:00
|
|
|
gossip,
|
2020-05-05 20:15:19 -07:00
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
let node_456 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-05-05 20:15:19 -07:00
|
|
|
shred_version: 456,
|
2020-05-15 09:35:43 -07:00
|
|
|
gossip,
|
2020-05-05 20:15:19 -07:00
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
|
|
|
|
crds.insert(me.clone(), 0).unwrap();
|
|
|
|
crds.insert(spy.clone(), 0).unwrap();
|
|
|
|
crds.insert(node_123.clone(), 0).unwrap();
|
|
|
|
crds.insert(node_456.clone(), 0).unwrap();
|
|
|
|
|
2020-08-18 18:52:45 -07:00
|
|
|
// shred version 123 should ignore nodes with versions 0 and 456
|
2020-05-05 20:15:19 -07:00
|
|
|
let options = node
|
2020-09-11 12:00:16 -07:00
|
|
|
.pull_options(&crds, &me.label().pubkey(), 123, 0, None, &stakes)
|
2020-05-05 20:15:19 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(_, c)| c.id)
|
|
|
|
.collect::<Vec<_>>();
|
2020-08-18 18:52:45 -07:00
|
|
|
assert_eq!(options.len(), 1);
|
|
|
|
assert!(!options.contains(&spy.pubkey()));
|
2020-05-05 20:15:19 -07:00
|
|
|
assert!(options.contains(&node_123.pubkey()));
|
|
|
|
|
|
|
|
// spy nodes will see all
|
|
|
|
let options = node
|
2020-09-11 12:00:16 -07:00
|
|
|
.pull_options(&crds, &spy.label().pubkey(), 0, 0, None, &stakes)
|
2020-05-05 20:15:19 -07:00
|
|
|
.iter()
|
|
|
|
.map(|(_, c)| c.id)
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(options.len(), 3);
|
|
|
|
assert!(options.contains(&me.pubkey()));
|
|
|
|
assert!(options.contains(&node_123.pubkey()));
|
|
|
|
assert!(options.contains(&node_456.pubkey()));
|
|
|
|
}
|
|
|
|
|
2020-09-11 12:00:16 -07:00
|
|
|
#[test]
|
|
|
|
fn test_pulls_only_from_allowed() {
|
|
|
|
let mut crds = Crds::default();
|
|
|
|
let stakes = HashMap::new();
|
|
|
|
let node = CrdsGossipPull::default();
|
|
|
|
let gossip = socketaddr!("127.0.0.1:1234");
|
|
|
|
|
|
|
|
let me = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-09-11 12:00:16 -07:00
|
|
|
gossip,
|
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
let node_123 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo {
|
2020-10-19 12:12:08 -07:00
|
|
|
id: solana_sdk::pubkey::new_rand(),
|
2020-09-11 12:00:16 -07:00
|
|
|
gossip,
|
|
|
|
..ContactInfo::default()
|
|
|
|
}));
|
|
|
|
|
|
|
|
crds.insert(me.clone(), 0).unwrap();
|
|
|
|
crds.insert(node_123.clone(), 0).unwrap();
|
|
|
|
|
|
|
|
// Empty gossip_validators -- will pull from nobody
|
|
|
|
let mut gossip_validators = HashSet::new();
|
|
|
|
let options = node.pull_options(
|
|
|
|
&crds,
|
|
|
|
&me.label().pubkey(),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
Some(&gossip_validators),
|
|
|
|
&stakes,
|
|
|
|
);
|
|
|
|
assert!(options.is_empty());
|
|
|
|
|
|
|
|
// Unknown pubkey in gossip_validators -- will pull from nobody
|
2020-10-19 12:12:08 -07:00
|
|
|
gossip_validators.insert(solana_sdk::pubkey::new_rand());
|
2020-09-11 12:00:16 -07:00
|
|
|
let options = node.pull_options(
|
|
|
|
&crds,
|
|
|
|
&me.label().pubkey(),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
Some(&gossip_validators),
|
|
|
|
&stakes,
|
|
|
|
);
|
|
|
|
assert!(options.is_empty());
|
|
|
|
|
|
|
|
// node_123 pubkey in gossip_validators -- will pull from it
|
|
|
|
gossip_validators.insert(node_123.pubkey());
|
|
|
|
let options = node.pull_options(
|
|
|
|
&crds,
|
|
|
|
&me.label().pubkey(),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
Some(&gossip_validators),
|
|
|
|
&stakes,
|
|
|
|
);
|
|
|
|
assert_eq!(options.len(), 1);
|
|
|
|
assert_eq!(options[0].1.id, node_123.pubkey());
|
|
|
|
}
|
|
|
|
|
2020-09-03 13:32:23 -07:00
|
|
|
#[test]
|
2020-09-29 16:06:02 -07:00
|
|
|
fn test_crds_filter_set_add() {
|
2020-09-03 13:32:23 -07:00
|
|
|
let mut rng = thread_rng();
|
2020-09-29 16:06:02 -07:00
|
|
|
let crds_filter_set =
|
|
|
|
CrdsFilterSet::new(/*num_items=*/ 9672788, /*max_bytes=*/ 8196);
|
2020-10-19 12:15:55 -07:00
|
|
|
let hash_values: Vec<_> = std::iter::repeat_with(|| solana_sdk::hash::new_rand(&mut rng))
|
2020-09-29 16:06:02 -07:00
|
|
|
.take(1024)
|
|
|
|
.collect();
|
|
|
|
for hash_value in &hash_values {
|
|
|
|
crds_filter_set.add(*hash_value);
|
|
|
|
}
|
|
|
|
let filters: Vec<CrdsFilter> = crds_filter_set.into();
|
|
|
|
assert_eq!(filters.len(), 1024);
|
|
|
|
for hash_value in hash_values {
|
2020-09-03 13:32:23 -07:00
|
|
|
let mut num_hits = 0;
|
2020-09-29 16:06:02 -07:00
|
|
|
let mut false_positives = 0;
|
|
|
|
for filter in &filters {
|
|
|
|
if filter.test_mask(&hash_value) {
|
2020-09-03 13:32:23 -07:00
|
|
|
num_hits += 1;
|
2020-09-29 16:06:02 -07:00
|
|
|
assert!(filter.contains(&hash_value));
|
|
|
|
assert!(filter.filter.contains(&hash_value));
|
|
|
|
} else if filter.filter.contains(&hash_value) {
|
|
|
|
false_positives += 1;
|
2020-09-03 13:32:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(num_hits, 1);
|
2020-09-29 16:06:02 -07:00
|
|
|
assert!(false_positives < 5);
|
2020-09-03 13:32:23 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2020-09-04 06:04:47 -07:00
|
|
|
fn test_crds_filter_set_new() {
|
|
|
|
// Validates invariances required by CrdsFilterSet::get in the
|
|
|
|
// vector of filters generated by CrdsFilterSet::new.
|
2020-09-29 16:06:02 -07:00
|
|
|
let filters: Vec<CrdsFilter> =
|
|
|
|
CrdsFilterSet::new(/*num_items=*/ 55345017, /*max_bytes=*/ 4098).into();
|
2020-09-03 13:32:23 -07:00
|
|
|
assert_eq!(filters.len(), 16384);
|
|
|
|
let mask_bits = filters[0].mask_bits;
|
|
|
|
let right_shift = 64 - mask_bits;
|
|
|
|
let ones = !0u64 >> mask_bits;
|
|
|
|
for (i, filter) in filters.iter().enumerate() {
|
|
|
|
// Check that all mask_bits are equal.
|
|
|
|
assert_eq!(mask_bits, filter.mask_bits);
|
|
|
|
assert_eq!(i as u64, filter.mask >> right_shift);
|
|
|
|
assert_eq!(ones, ones & filter.mask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 16:06:02 -07:00
|
|
|
#[test]
|
|
|
|
fn test_build_crds_filter() {
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
|
|
|
let mut crds_gossip_pull = CrdsGossipPull::default();
|
|
|
|
let mut crds = Crds::default();
|
|
|
|
for _ in 0..10_000 {
|
|
|
|
crds_gossip_pull
|
|
|
|
.purged_values
|
2020-10-19 12:15:55 -07:00
|
|
|
.push_back((solana_sdk::hash::new_rand(&mut rng), rng.gen()));
|
2020-09-29 16:06:02 -07:00
|
|
|
}
|
|
|
|
let mut num_inserts = 0;
|
|
|
|
for _ in 0..20_000 {
|
|
|
|
if crds
|
2020-11-15 08:38:04 -08:00
|
|
|
.insert(CrdsValue::new_rand(&mut rng, None), rng.gen())
|
2020-09-29 16:06:02 -07:00
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
num_inserts += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(num_inserts, 20_000);
|
|
|
|
let filters = crds_gossip_pull.build_crds_filters(&thread_pool, &crds, MAX_BLOOM_SIZE);
|
|
|
|
assert_eq!(filters.len(), 32);
|
|
|
|
let hash_values: Vec<_> = crds
|
|
|
|
.values()
|
|
|
|
.map(|v| v.value_hash)
|
|
|
|
.chain(
|
|
|
|
crds_gossip_pull
|
|
|
|
.purged_values
|
|
|
|
.iter()
|
|
|
|
.map(|(value_hash, _)| value_hash)
|
|
|
|
.cloned(),
|
|
|
|
)
|
|
|
|
.collect();
|
|
|
|
assert_eq!(hash_values.len(), 10_000 + 20_000);
|
|
|
|
let mut false_positives = 0;
|
|
|
|
for hash_value in hash_values {
|
|
|
|
let mut num_hits = 0;
|
|
|
|
for filter in &filters {
|
|
|
|
if filter.test_mask(&hash_value) {
|
|
|
|
num_hits += 1;
|
|
|
|
assert!(filter.contains(&hash_value));
|
|
|
|
assert!(filter.filter.contains(&hash_value));
|
|
|
|
} else if filter.filter.contains(&hash_value) {
|
|
|
|
false_positives += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert_eq!(num_hits, 1);
|
|
|
|
}
|
|
|
|
assert!(false_positives < 50_000, "fp: {}", false_positives);
|
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
#[test]
|
|
|
|
fn test_new_pull_request() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut crds = Crds::default();
|
2021-04-20 11:06:13 -07:00
|
|
|
let node_keypair = Keypair::new();
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair.pubkey(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2018-11-15 13:23:26 -08:00
|
|
|
let node = CrdsGossipPull::default();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut pings = Vec::new();
|
|
|
|
let ping_cache = Mutex::new(PingCache::new(
|
|
|
|
Duration::from_secs(20 * 60), // ttl
|
|
|
|
128, // capacity
|
|
|
|
));
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(
|
2020-09-29 16:06:02 -07:00
|
|
|
node.new_pull_request(
|
|
|
|
&thread_pool,
|
|
|
|
&crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2020-09-29 16:06:02 -07:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
None,
|
|
|
|
&HashMap::new(),
|
2021-04-20 11:06:13 -07:00
|
|
|
PACKET_DATA_SIZE,
|
|
|
|
&ping_cache,
|
|
|
|
&mut pings,
|
2020-09-29 16:06:02 -07:00
|
|
|
),
|
2018-11-15 13:23:26 -08:00
|
|
|
Err(CrdsGossipError::NoPeers)
|
|
|
|
);
|
|
|
|
|
2021-04-28 06:19:12 -07:00
|
|
|
crds.insert(entry, 0).unwrap();
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(
|
2020-09-29 16:06:02 -07:00
|
|
|
node.new_pull_request(
|
|
|
|
&thread_pool,
|
|
|
|
&crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2020-09-29 16:06:02 -07:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
None,
|
|
|
|
&HashMap::new(),
|
2021-04-20 11:06:13 -07:00
|
|
|
PACKET_DATA_SIZE,
|
|
|
|
&ping_cache,
|
|
|
|
&mut pings,
|
2020-09-29 16:06:02 -07:00
|
|
|
),
|
2018-11-15 13:23:26 -08:00
|
|
|
Err(CrdsGossipError::NoPeers)
|
|
|
|
);
|
2021-04-20 11:06:13 -07:00
|
|
|
let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
|
|
|
ping_cache
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
|
|
|
.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2018-11-15 13:23:26 -08:00
|
|
|
crds.insert(new.clone(), 0).unwrap();
|
2020-09-29 16:06:02 -07:00
|
|
|
let req = node.new_pull_request(
|
|
|
|
&thread_pool,
|
|
|
|
&crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2020-09-29 16:06:02 -07:00
|
|
|
0,
|
|
|
|
0,
|
|
|
|
None,
|
|
|
|
&HashMap::new(),
|
|
|
|
PACKET_DATA_SIZE,
|
2021-04-20 11:06:13 -07:00
|
|
|
&ping_cache,
|
|
|
|
&mut pings,
|
2020-09-29 16:06:02 -07:00
|
|
|
);
|
2021-04-28 06:19:12 -07:00
|
|
|
let (peer, _) = req.unwrap();
|
|
|
|
assert_eq!(peer, *new.contact_info().unwrap());
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_new_mark_creation_time() {
|
2020-11-12 08:09:37 -08:00
|
|
|
let now: u64 = 1_605_127_770_789;
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut ping_cache = PingCache::new(
|
|
|
|
Duration::from_secs(20 * 60), // ttl
|
|
|
|
128, // capacity
|
|
|
|
);
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut crds = Crds::default();
|
2021-04-20 11:06:13 -07:00
|
|
|
let node_keypair = Keypair::new();
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair.pubkey(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node = CrdsGossipPull::default();
|
2021-04-28 06:19:12 -07:00
|
|
|
crds.insert(entry, now).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let old = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
|
|
|
ping_cache.mock_pong(old.id, old.gossip, Instant::now());
|
|
|
|
let old = CrdsValue::new_unsigned(CrdsData::ContactInfo(old));
|
2020-11-12 08:09:37 -08:00
|
|
|
crds.insert(old.clone(), now).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
|
|
|
ping_cache.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2020-11-12 08:09:37 -08:00
|
|
|
crds.insert(new.clone(), now).unwrap();
|
2018-11-15 13:23:26 -08:00
|
|
|
|
2020-11-12 08:09:37 -08:00
|
|
|
// set request creation time to now.
|
|
|
|
let now = now + 50_000;
|
2021-04-28 06:19:12 -07:00
|
|
|
node.mark_pull_request_creation_time(new.label().pubkey(), now);
|
2018-11-15 13:23:26 -08:00
|
|
|
|
2020-11-12 08:09:37 -08:00
|
|
|
// odds of getting the other request should be close to 1.
|
|
|
|
let now = now + 1_000;
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut pings = Vec::new();
|
|
|
|
let ping_cache = Mutex::new(ping_cache);
|
2018-11-15 13:23:26 -08:00
|
|
|
for _ in 0..10 {
|
2019-08-13 18:04:14 -07:00
|
|
|
let req = node.new_pull_request(
|
2020-09-29 16:06:02 -07:00
|
|
|
&thread_pool,
|
2019-08-13 18:04:14 -07:00
|
|
|
&crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2020-05-05 20:15:19 -07:00
|
|
|
0,
|
2020-11-12 08:09:37 -08:00
|
|
|
now,
|
2020-09-11 12:00:16 -07:00
|
|
|
None,
|
2019-08-13 18:04:14 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
PACKET_DATA_SIZE,
|
2021-04-20 11:06:13 -07:00
|
|
|
&ping_cache,
|
|
|
|
&mut pings,
|
2019-08-13 18:04:14 -07:00
|
|
|
);
|
2021-04-28 06:19:12 -07:00
|
|
|
let (peer, _) = req.unwrap();
|
|
|
|
assert_eq!(peer, *old.contact_info().unwrap());
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 11:33:56 -07:00
|
|
|
#[test]
|
|
|
|
fn test_pull_request_time() {
|
|
|
|
const NUM_REPS: usize = 2 * CRDS_UNIQUE_PUBKEY_CAPACITY;
|
|
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let pubkeys: Vec<_> = repeat_with(Pubkey::new_unique).take(NUM_REPS).collect();
|
|
|
|
let mut node = CrdsGossipPull::default();
|
|
|
|
let mut requests = HashMap::new();
|
|
|
|
let now = timestamp();
|
|
|
|
for k in 0..NUM_REPS {
|
|
|
|
let pubkey = pubkeys[rng.gen_range(0, pubkeys.len())];
|
|
|
|
let now = now + k as u64;
|
2021-04-28 06:19:12 -07:00
|
|
|
node.mark_pull_request_creation_time(pubkey, now);
|
2021-03-24 11:33:56 -07:00
|
|
|
*requests.entry(pubkey).or_default() = now;
|
|
|
|
}
|
|
|
|
assert!(node.pull_request_time.len() <= CRDS_UNIQUE_PUBKEY_CAPACITY);
|
|
|
|
// Assert that timestamps match most recent request.
|
|
|
|
for (pk, ts) in &node.pull_request_time {
|
|
|
|
assert_eq!(*ts, requests[pk]);
|
|
|
|
}
|
|
|
|
// Assert that most recent pull timestamps are maintained.
|
|
|
|
let max_ts = requests
|
|
|
|
.iter()
|
|
|
|
.filter(|(pk, _)| !node.pull_request_time.contains(*pk))
|
|
|
|
.map(|(_, ts)| *ts)
|
|
|
|
.max()
|
|
|
|
.unwrap();
|
|
|
|
let min_ts = requests
|
|
|
|
.iter()
|
|
|
|
.filter(|(pk, _)| node.pull_request_time.contains(*pk))
|
|
|
|
.map(|(_, ts)| *ts)
|
|
|
|
.min()
|
|
|
|
.unwrap();
|
|
|
|
assert!(max_ts <= min_ts);
|
|
|
|
}
|
|
|
|
|
2020-08-11 06:26:42 -07:00
|
|
|
#[test]
|
|
|
|
fn test_generate_pull_responses() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let node_keypair = Keypair::new();
|
2020-08-11 06:26:42 -07:00
|
|
|
let mut node_crds = Crds::default();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut ping_cache = PingCache::new(
|
|
|
|
Duration::from_secs(20 * 60), // ttl
|
|
|
|
128, // capacity
|
|
|
|
);
|
2020-08-11 06:26:42 -07:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair.pubkey(),
|
2020-08-11 06:26:42 -07:00
|
|
|
0,
|
|
|
|
)));
|
2021-04-28 06:19:12 -07:00
|
|
|
let caller = entry.clone();
|
2020-08-11 06:26:42 -07:00
|
|
|
let node = CrdsGossipPull::default();
|
|
|
|
node_crds.insert(entry, 0).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
|
|
|
ping_cache.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2020-08-11 06:26:42 -07:00
|
|
|
node_crds.insert(new, 0).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut pings = Vec::new();
|
2020-08-11 06:26:42 -07:00
|
|
|
let req = node.new_pull_request(
|
2020-09-29 16:06:02 -07:00
|
|
|
&thread_pool,
|
2020-08-11 06:26:42 -07:00
|
|
|
&node_crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2020-08-11 06:26:42 -07:00
|
|
|
0,
|
|
|
|
0,
|
2020-09-11 12:00:16 -07:00
|
|
|
None,
|
2020-08-11 06:26:42 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
PACKET_DATA_SIZE,
|
2021-04-20 11:06:13 -07:00
|
|
|
&Mutex::new(ping_cache),
|
|
|
|
&mut pings,
|
2020-08-11 06:26:42 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
let mut dest_crds = Crds::default();
|
|
|
|
let dest = CrdsGossipPull::default();
|
2021-04-28 06:19:12 -07:00
|
|
|
let (_, filters) = req.unwrap();
|
2020-08-11 06:26:42 -07:00
|
|
|
let mut filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
2020-12-18 10:45:12 -08:00
|
|
|
let rsp = dest.generate_pull_responses(
|
|
|
|
&dest_crds,
|
|
|
|
&filters,
|
|
|
|
/*output_size_limit=*/ usize::MAX,
|
|
|
|
0,
|
|
|
|
);
|
2020-08-11 06:26:42 -07:00
|
|
|
|
|
|
|
assert_eq!(rsp[0].len(), 0);
|
|
|
|
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-08-11 06:26:42 -07:00
|
|
|
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
|
|
|
)));
|
|
|
|
dest_crds
|
|
|
|
.insert(new, CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
//should skip new value since caller is to old
|
2020-12-18 10:45:12 -08:00
|
|
|
let rsp = dest.generate_pull_responses(
|
|
|
|
&dest_crds,
|
|
|
|
&filters,
|
|
|
|
/*output_size_limit=*/ usize::MAX,
|
|
|
|
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
|
|
|
);
|
2020-08-11 06:26:42 -07:00
|
|
|
assert_eq!(rsp[0].len(), 0);
|
|
|
|
|
2020-08-21 12:32:37 -07:00
|
|
|
assert_eq!(filters.len(), 1);
|
|
|
|
filters.push(filters[0].clone());
|
2020-08-11 06:26:42 -07:00
|
|
|
//should return new value since caller is new
|
2020-08-21 12:32:37 -07:00
|
|
|
filters[1].0 = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2020-08-11 06:26:42 -07:00
|
|
|
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS + 1,
|
|
|
|
)));
|
|
|
|
|
2020-12-18 10:45:12 -08:00
|
|
|
let rsp = dest.generate_pull_responses(
|
|
|
|
&dest_crds,
|
|
|
|
&filters,
|
|
|
|
/*output_size_limit=*/ usize::MAX,
|
|
|
|
CRDS_GOSSIP_PULL_MSG_TIMEOUT_MS,
|
|
|
|
);
|
2020-08-21 12:32:37 -07:00
|
|
|
assert_eq!(rsp.len(), 2);
|
|
|
|
assert_eq!(rsp[0].len(), 0);
|
|
|
|
assert_eq!(rsp[1].len(), 1); // Orders are also preserved.
|
2020-08-11 06:26:42 -07:00
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
#[test]
|
|
|
|
fn test_process_pull_request() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let node_keypair = Keypair::new();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node_crds = Crds::default();
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair.pubkey(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2021-04-28 06:19:12 -07:00
|
|
|
let caller = entry.clone();
|
2018-11-15 13:23:26 -08:00
|
|
|
let node = CrdsGossipPull::default();
|
2020-05-15 09:35:43 -07:00
|
|
|
node_crds.insert(entry, 0).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut ping_cache = PingCache::new(
|
|
|
|
Duration::from_secs(20 * 60), // ttl
|
|
|
|
128, // capacity
|
|
|
|
);
|
|
|
|
let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 0);
|
|
|
|
ping_cache.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2020-05-15 09:35:43 -07:00
|
|
|
node_crds.insert(new, 0).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut pings = Vec::new();
|
2019-08-13 18:04:14 -07:00
|
|
|
let req = node.new_pull_request(
|
2020-09-29 16:06:02 -07:00
|
|
|
&thread_pool,
|
2019-08-13 18:04:14 -07:00
|
|
|
&node_crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2019-08-13 18:04:14 -07:00
|
|
|
0,
|
2020-05-05 20:15:19 -07:00
|
|
|
0,
|
2020-09-11 12:00:16 -07:00
|
|
|
None,
|
2019-08-13 18:04:14 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
PACKET_DATA_SIZE,
|
2021-04-20 11:06:13 -07:00
|
|
|
&Mutex::new(ping_cache),
|
|
|
|
&mut pings,
|
2019-08-13 18:04:14 -07:00
|
|
|
);
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
let mut dest_crds = Crds::default();
|
|
|
|
let mut dest = CrdsGossipPull::default();
|
2021-04-28 06:19:12 -07:00
|
|
|
let (_, filters) = req.unwrap();
|
2020-05-28 11:38:13 -07:00
|
|
|
let filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
2020-12-18 10:45:12 -08:00
|
|
|
let rsp = dest.generate_pull_responses(
|
|
|
|
&dest_crds,
|
|
|
|
&filters,
|
|
|
|
/*output_size_limit=*/ usize::MAX,
|
|
|
|
0,
|
|
|
|
);
|
2020-10-28 10:03:02 -07:00
|
|
|
dest.process_pull_requests(
|
|
|
|
&mut dest_crds,
|
|
|
|
filters.into_iter().map(|(caller, _)| caller),
|
|
|
|
1,
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
assert!(rsp.iter().all(|rsp| rsp.is_empty()));
|
2018-11-15 13:23:26 -08:00
|
|
|
assert!(dest_crds.lookup(&caller.label()).is_some());
|
|
|
|
assert_eq!(
|
|
|
|
dest_crds
|
|
|
|
.lookup_versioned(&caller.label())
|
|
|
|
.unwrap()
|
|
|
|
.local_timestamp,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_process_pull_request_response() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let node_keypair = Keypair::new();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node_crds = Crds::default();
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair.pubkey(),
|
2020-08-11 06:26:42 -07:00
|
|
|
1,
|
2019-11-03 10:07:51 -08:00
|
|
|
)));
|
2021-04-28 06:19:12 -07:00
|
|
|
let caller = entry.clone();
|
2019-05-23 23:20:04 -07:00
|
|
|
let node_pubkey = entry.label().pubkey();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node = CrdsGossipPull::default();
|
2020-05-15 09:35:43 -07:00
|
|
|
node_crds.insert(entry, 0).unwrap();
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut ping_cache = PingCache::new(
|
|
|
|
Duration::from_secs(20 * 60), // ttl
|
|
|
|
128, // capacity
|
|
|
|
);
|
|
|
|
let new = ContactInfo::new_localhost(&solana_sdk::pubkey::new_rand(), 1);
|
|
|
|
ping_cache.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2020-05-15 09:35:43 -07:00
|
|
|
node_crds.insert(new, 0).unwrap();
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
let mut dest = CrdsGossipPull::default();
|
|
|
|
let mut dest_crds = Crds::default();
|
2020-10-19 12:12:08 -07:00
|
|
|
let new_id = solana_sdk::pubkey::new_rand();
|
2021-04-20 11:06:13 -07:00
|
|
|
let new = ContactInfo::new_localhost(&new_id, 1);
|
|
|
|
ping_cache.mock_pong(new.id, new.gossip, Instant::now());
|
|
|
|
let new = CrdsValue::new_unsigned(CrdsData::ContactInfo(new));
|
2018-11-15 13:23:26 -08:00
|
|
|
dest_crds.insert(new.clone(), 0).unwrap();
|
|
|
|
|
|
|
|
// node contains a key from the dest node, but at an older local timestamp
|
2021-04-20 11:06:13 -07:00
|
|
|
let same_key = ContactInfo::new_localhost(&new_id, 0);
|
|
|
|
ping_cache.mock_pong(same_key.id, same_key.gossip, Instant::now());
|
|
|
|
let same_key = CrdsValue::new_unsigned(CrdsData::ContactInfo(same_key));
|
2019-03-08 19:28:19 -08:00
|
|
|
assert_eq!(same_key.label(), new.label());
|
|
|
|
assert!(same_key.wallclock() < new.wallclock());
|
2018-11-15 13:23:26 -08:00
|
|
|
node_crds.insert(same_key.clone(), 0).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
node_crds
|
|
|
|
.lookup_versioned(&same_key.label())
|
|
|
|
.unwrap()
|
|
|
|
.local_timestamp,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
let mut done = false;
|
2021-04-20 11:06:13 -07:00
|
|
|
let mut pings = Vec::new();
|
|
|
|
let ping_cache = Mutex::new(ping_cache);
|
2018-11-15 13:23:26 -08:00
|
|
|
for _ in 0..30 {
|
|
|
|
// there is a chance of a false positive with bloom filters
|
2019-08-13 18:04:14 -07:00
|
|
|
let req = node.new_pull_request(
|
2020-09-29 16:06:02 -07:00
|
|
|
&thread_pool,
|
2019-08-13 18:04:14 -07:00
|
|
|
&node_crds,
|
2021-04-20 11:06:13 -07:00
|
|
|
&node_keypair,
|
2019-08-13 18:04:14 -07:00
|
|
|
0,
|
2020-05-05 20:15:19 -07:00
|
|
|
0,
|
2020-09-11 12:00:16 -07:00
|
|
|
None,
|
2019-08-13 18:04:14 -07:00
|
|
|
&HashMap::new(),
|
|
|
|
PACKET_DATA_SIZE,
|
2021-04-20 11:06:13 -07:00
|
|
|
&ping_cache,
|
|
|
|
&mut pings,
|
2019-08-13 18:04:14 -07:00
|
|
|
);
|
2021-04-28 06:19:12 -07:00
|
|
|
let (_, filters) = req.unwrap();
|
2020-05-28 11:38:13 -07:00
|
|
|
let filters: Vec<_> = filters.into_iter().map(|f| (caller.clone(), f)).collect();
|
2020-12-18 10:45:12 -08:00
|
|
|
let mut rsp = dest.generate_pull_responses(
|
|
|
|
&dest_crds,
|
|
|
|
&filters,
|
|
|
|
/*output_size_limit=*/ usize::MAX,
|
|
|
|
0,
|
|
|
|
);
|
2020-10-28 10:03:02 -07:00
|
|
|
dest.process_pull_requests(
|
|
|
|
&mut dest_crds,
|
|
|
|
filters.into_iter().map(|(caller, _)| caller),
|
|
|
|
0,
|
|
|
|
);
|
2019-08-15 17:04:45 -07:00
|
|
|
// if there is a false positive this is empty
|
|
|
|
// prob should be around 0.1 per iteration
|
|
|
|
if rsp.is_empty() {
|
|
|
|
continue;
|
2019-08-13 18:04:14 -07:00
|
|
|
}
|
|
|
|
|
2018-11-15 13:23:26 -08:00
|
|
|
if rsp.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
assert_eq!(rsp.len(), 1);
|
2020-05-25 15:03:34 -07:00
|
|
|
let failed = node
|
|
|
|
.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&node_pubkey,
|
|
|
|
&node.make_timeouts_def(&node_pubkey, &HashMap::new(), 0, 1),
|
|
|
|
rsp.pop().unwrap(),
|
|
|
|
1,
|
|
|
|
)
|
|
|
|
.0;
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(failed, 0);
|
|
|
|
assert_eq!(
|
|
|
|
node_crds
|
|
|
|
.lookup_versioned(&new.label())
|
|
|
|
.unwrap()
|
|
|
|
.local_timestamp,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
// verify that the whole record was updated for dest since this is a response from dest
|
|
|
|
assert_eq!(
|
|
|
|
node_crds
|
|
|
|
.lookup_versioned(&same_key.label())
|
|
|
|
.unwrap()
|
|
|
|
.local_timestamp,
|
|
|
|
1
|
|
|
|
);
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert!(done);
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_gossip_purge() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let thread_pool = ThreadPoolBuilder::new().build().unwrap();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node_crds = Crds::default();
|
2019-11-03 10:07:51 -08:00
|
|
|
let entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2018-11-15 13:23:26 -08:00
|
|
|
let node_label = entry.label();
|
2019-05-23 23:20:04 -07:00
|
|
|
let node_pubkey = node_label.pubkey();
|
2018-11-15 13:23:26 -08:00
|
|
|
let mut node = CrdsGossipPull::default();
|
2020-05-15 09:35:43 -07:00
|
|
|
node_crds.insert(entry, 0).unwrap();
|
2019-11-03 10:07:51 -08:00
|
|
|
let old = CrdsValue::new_unsigned(CrdsData::ContactInfo(ContactInfo::new_localhost(
|
2020-10-19 12:12:08 -07:00
|
|
|
&solana_sdk::pubkey::new_rand(),
|
2019-11-03 10:07:51 -08:00
|
|
|
0,
|
|
|
|
)));
|
2018-11-15 13:23:26 -08:00
|
|
|
node_crds.insert(old.clone(), 0).unwrap();
|
|
|
|
let value_hash = node_crds.lookup_versioned(&old.label()).unwrap().value_hash;
|
|
|
|
|
|
|
|
//verify self is valid
|
|
|
|
assert_eq!(node_crds.lookup(&node_label).unwrap().label(), node_label);
|
|
|
|
|
|
|
|
// purge
|
2019-11-20 11:25:18 -08:00
|
|
|
let timeouts = node.make_timeouts_def(&node_pubkey, &HashMap::new(), 0, 1);
|
2020-10-23 07:17:37 -07:00
|
|
|
node.purge_active(&thread_pool, &mut node_crds, 2, &timeouts);
|
2018-11-15 13:23:26 -08:00
|
|
|
|
|
|
|
//verify self is still valid after purge
|
|
|
|
assert_eq!(node_crds.lookup(&node_label).unwrap().label(), node_label);
|
|
|
|
|
|
|
|
assert_eq!(node_crds.lookup_versioned(&old.label()), None);
|
|
|
|
assert_eq!(node.purged_values.len(), 1);
|
|
|
|
for _ in 0..30 {
|
|
|
|
// there is a chance of a false positive with bloom filters
|
|
|
|
// assert that purged value is still in the set
|
|
|
|
// chance of 30 consecutive false positives is 0.1^30
|
2020-09-29 16:06:02 -07:00
|
|
|
let filters = node.build_crds_filters(&thread_pool, &node_crds, PACKET_DATA_SIZE);
|
2019-08-13 18:04:14 -07:00
|
|
|
assert!(filters.iter().any(|filter| filter.contains(&value_hash)));
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// purge the value
|
2021-04-15 06:18:39 -07:00
|
|
|
node.purge_purged(3);
|
2018-11-15 13:23:26 -08:00
|
|
|
assert_eq!(node.purged_values.len(), 0);
|
|
|
|
}
|
2019-08-13 18:04:14 -07:00
|
|
|
#[test]
|
2020-05-15 09:35:43 -07:00
|
|
|
#[allow(clippy::float_cmp)]
|
2019-08-13 18:04:14 -07:00
|
|
|
fn test_crds_filter_mask() {
|
|
|
|
let filter = CrdsFilter::new_rand(1, 128);
|
|
|
|
assert_eq!(filter.mask, !0x0);
|
|
|
|
assert_eq!(CrdsFilter::max_items(80f64, 0.01, 8f64), 9f64);
|
|
|
|
//1000/9 = 111, so 7 bits are needed to mask it
|
|
|
|
assert_eq!(CrdsFilter::mask_bits(1000f64, 9f64), 7u32);
|
|
|
|
let filter = CrdsFilter::new_rand(1000, 10);
|
2020-05-15 09:35:43 -07:00
|
|
|
assert_eq!(filter.mask & 0x00_ffff_ffff, 0x00_ffff_ffff);
|
2019-08-13 18:04:14 -07:00
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_add_no_mask() {
|
|
|
|
let mut filter = CrdsFilter::new_rand(1, 128);
|
|
|
|
let h: Hash = hash(Hash::default().as_ref());
|
|
|
|
assert!(!filter.contains(&h));
|
|
|
|
filter.add(&h);
|
|
|
|
assert!(filter.contains(&h));
|
|
|
|
let h: Hash = hash(h.as_ref());
|
|
|
|
assert!(!filter.contains(&h));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_add_mask() {
|
|
|
|
let mut filter = CrdsFilter::new_rand(1000, 10);
|
|
|
|
let mut h: Hash = Hash::default();
|
|
|
|
while !filter.test_mask(&h) {
|
|
|
|
h = hash(h.as_ref());
|
|
|
|
}
|
|
|
|
assert!(filter.test_mask(&h));
|
|
|
|
//if the mask succeeds, we want the guaranteed negative
|
|
|
|
assert!(!filter.contains(&h));
|
|
|
|
filter.add(&h);
|
|
|
|
assert!(filter.contains(&h));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_complete_set_add_mask() {
|
2020-09-29 16:06:02 -07:00
|
|
|
let mut filters: Vec<CrdsFilter> = CrdsFilterSet::new(1000, 10).into();
|
2019-08-13 18:04:14 -07:00
|
|
|
assert!(filters.iter().all(|f| f.mask_bits > 0));
|
|
|
|
let mut h: Hash = Hash::default();
|
|
|
|
// rev to make the hash::default() miss on the first few test_masks
|
|
|
|
while !filters.iter().rev().any(|f| f.test_mask(&h)) {
|
|
|
|
h = hash(h.as_ref());
|
|
|
|
}
|
|
|
|
let filter = filters.iter_mut().find(|f| f.test_mask(&h)).unwrap();
|
|
|
|
assert!(filter.test_mask(&h));
|
|
|
|
//if the mask succeeds, we want the guaranteed negative
|
|
|
|
assert!(!filter.contains(&h));
|
|
|
|
filter.add(&h);
|
|
|
|
assert!(filter.contains(&h));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_crds_filter_contains_mask() {
|
|
|
|
let filter = CrdsFilter::new_rand(1000, 10);
|
|
|
|
assert!(filter.mask_bits > 0);
|
|
|
|
let mut h: Hash = Hash::default();
|
|
|
|
while filter.test_mask(&h) {
|
|
|
|
h = hash(h.as_ref());
|
|
|
|
}
|
|
|
|
assert!(!filter.test_mask(&h));
|
|
|
|
//if the mask fails, the hash is contained in the set, and can be treated as a false
|
|
|
|
//positive
|
|
|
|
assert!(filter.contains(&h));
|
|
|
|
}
|
|
|
|
#[test]
|
|
|
|
fn test_mask() {
|
|
|
|
for i in 0..16 {
|
|
|
|
run_test_mask(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn run_test_mask(mask_bits: u32) {
|
|
|
|
let masks: Vec<_> = (0..2u64.pow(mask_bits))
|
|
|
|
.map(|seed| CrdsFilter::compute_mask(seed, mask_bits))
|
|
|
|
.dedup()
|
|
|
|
.collect();
|
|
|
|
assert_eq!(masks.len(), 2u64.pow(mask_bits) as usize)
|
|
|
|
}
|
2020-02-07 12:38:24 -08:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_process_pull_response() {
|
|
|
|
let mut node_crds = Crds::default();
|
|
|
|
let mut node = CrdsGossipPull::default();
|
|
|
|
|
2020-10-19 12:12:08 -07:00
|
|
|
let peer_pubkey = solana_sdk::pubkey::new_rand();
|
2020-02-07 12:38:24 -08:00
|
|
|
let peer_entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(
|
|
|
|
ContactInfo::new_localhost(&peer_pubkey, 0),
|
|
|
|
));
|
|
|
|
let mut timeouts = HashMap::new();
|
|
|
|
timeouts.insert(Pubkey::default(), node.crds_timeout);
|
|
|
|
timeouts.insert(peer_pubkey, node.msg_timeout + 1);
|
|
|
|
// inserting a fresh value should be fine.
|
|
|
|
assert_eq!(
|
|
|
|
node.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&peer_pubkey,
|
|
|
|
&timeouts,
|
|
|
|
vec![peer_entry.clone()],
|
|
|
|
1,
|
2020-05-25 15:03:34 -07:00
|
|
|
)
|
|
|
|
.0,
|
2020-02-07 12:38:24 -08:00
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut node_crds = Crds::default();
|
|
|
|
let unstaked_peer_entry = CrdsValue::new_unsigned(CrdsData::ContactInfo(
|
|
|
|
ContactInfo::new_localhost(&peer_pubkey, 0),
|
|
|
|
));
|
|
|
|
// check that old contact infos fail if they are too old, regardless of "timeouts"
|
|
|
|
assert_eq!(
|
|
|
|
node.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&peer_pubkey,
|
|
|
|
&timeouts,
|
|
|
|
vec![peer_entry.clone(), unstaked_peer_entry],
|
|
|
|
node.msg_timeout + 100,
|
2020-05-25 15:03:34 -07:00
|
|
|
)
|
|
|
|
.0,
|
2020-02-07 12:38:24 -08:00
|
|
|
2
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut node_crds = Crds::default();
|
|
|
|
// check that old contact infos can still land as long as they have a "timeouts" entry
|
|
|
|
assert_eq!(
|
|
|
|
node.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&peer_pubkey,
|
|
|
|
&timeouts,
|
2020-05-15 09:35:43 -07:00
|
|
|
vec![peer_entry],
|
2020-02-07 12:38:24 -08:00
|
|
|
node.msg_timeout + 1,
|
2020-05-25 15:03:34 -07:00
|
|
|
)
|
|
|
|
.0,
|
2020-02-07 12:38:24 -08:00
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
// construct something that's not a contact info
|
|
|
|
let peer_vote =
|
2021-01-21 05:08:07 -08:00
|
|
|
CrdsValue::new_unsigned(CrdsData::Vote(0, Vote::new(peer_pubkey, test_tx(), 0)));
|
2020-02-07 12:38:24 -08:00
|
|
|
// check that older CrdsValues (non-ContactInfos) infos pass even if are too old,
|
|
|
|
// but a recent contact info (inserted above) exists
|
|
|
|
assert_eq!(
|
|
|
|
node.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&peer_pubkey,
|
|
|
|
&timeouts,
|
|
|
|
vec![peer_vote.clone()],
|
|
|
|
node.msg_timeout + 1,
|
2020-05-25 15:03:34 -07:00
|
|
|
)
|
|
|
|
.0,
|
2020-02-07 12:38:24 -08:00
|
|
|
0
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut node_crds = Crds::default();
|
|
|
|
// without a contact info, inserting an old value should fail
|
|
|
|
assert_eq!(
|
|
|
|
node.process_pull_response(
|
|
|
|
&mut node_crds,
|
|
|
|
&peer_pubkey,
|
|
|
|
&timeouts,
|
2020-05-15 09:35:43 -07:00
|
|
|
vec![peer_vote],
|
2021-04-14 13:18:00 -07:00
|
|
|
node.msg_timeout + 2,
|
2020-05-25 15:03:34 -07:00
|
|
|
)
|
|
|
|
.0,
|
2020-02-07 12:38:24 -08:00
|
|
|
1
|
|
|
|
);
|
|
|
|
}
|
2018-11-15 13:23:26 -08:00
|
|
|
}
|