solana/src/cluster_info.rs

2063 lines
76 KiB
Rust
Raw Normal View History

2018-10-08 19:55:54 -07:00
//! The `cluster_info` module defines a data structure that is shared by all the nodes in the network over
2018-04-28 00:31:20 -07:00
//! a gossip control plane. The goal is to share small bits of off-chain information and detect and
2018-04-21 11:02:49 -07:00
//! repair partitions.
//!
//! This CRDT only supports a very limited set of types. A map of Pubkey -> Versioned Struct.
2018-05-15 04:35:41 -07:00
//! The last version is always picked during an update.
2018-04-28 00:31:20 -07:00
//!
//! The network is arranged in layers:
//!
//! * layer 0 - Leader.
//! * layer 1 - As many nodes as we can fit
//! * layer 2 - Everyone else, if layer 1 is `2^10`, layer 2 should be able to fit `2^20` number of nodes.
//!
2018-05-14 14:33:11 -07:00
//! Bank needs to provide an interface for us to query the stake weight
use bincode::{deserialize, serialize, serialized_size};
use budget_instruction::Vote;
2018-06-26 14:43:20 -07:00
use choose_gossip_peer_strategy::{ChooseGossipPeerStrategy, ChooseWeightedPeerStrategy};
use counter::Counter;
2018-04-21 11:02:49 -07:00
use hash::Hash;
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
use leader_scheduler::LeaderScheduler;
2018-08-06 12:35:38 -07:00
use ledger::LedgerWindow;
use log::Level;
use netutil::{bind_in_range, bind_to, multi_bind_in_range};
use packet::{to_blob, Blob, SharedBlob, BLOB_SIZE};
use rand::{thread_rng, Rng};
2018-04-28 00:31:20 -07:00
use rayon::prelude::*;
use result::{Error, Result};
2018-09-26 16:55:36 -07:00
use signature::{Keypair, KeypairUtil};
use solana_program_interface::pubkey::Pubkey;
2018-05-16 23:11:51 -07:00
use std;
2018-04-21 11:02:49 -07:00
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
2018-04-21 11:02:49 -07:00
use std::sync::{Arc, RwLock};
2018-05-30 13:25:32 -07:00
use std::thread::{sleep, Builder, JoinHandle};
use std::time::{Duration, Instant};
2018-08-09 12:31:34 -07:00
use streamer::{BlobReceiver, BlobSender};
use timing::{duration_as_ms, timestamp};
2018-08-09 12:31:34 -07:00
use window::{SharedWindow, WindowIndex};
pub const FULLNODE_PORT_RANGE: (u16, u16) = (8000, 10_000);
2018-06-14 22:03:49 -07:00
/// milliseconds we sleep for between gossip requests
2018-07-09 17:35:23 -07:00
const GOSSIP_SLEEP_MILLIS: u64 = 100;
2018-07-09 15:53:49 -07:00
const GOSSIP_PURGE_MILLIS: u64 = 15000;
2018-04-21 11:02:49 -07:00
2018-06-14 22:03:49 -07:00
/// minimum membership table size before we start purging dead nodes
const MIN_TABLE_SIZE: usize = 2;
#[macro_export]
macro_rules! socketaddr {
($ip:expr, $port:expr) => {
SocketAddr::from((Ipv4Addr::from($ip), $port))
};
($str:expr) => {{
let a: SocketAddr = $str.parse().unwrap();
a
}};
}
#[macro_export]
macro_rules! socketaddr_any {
() => {
socketaddr!(0, 0)
};
}
#[derive(Debug, PartialEq, Eq)]
2018-10-08 19:55:54 -07:00
pub enum ClusterInfoError {
NoPeers,
2018-07-09 15:53:49 -07:00
NoLeader,
BadContactInfo,
BadNodeInfo,
BadGossipAddress,
}
2018-04-21 11:02:49 -07:00
/// Structure to be replicated by the network
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
2018-07-09 17:55:11 -07:00
pub struct ContactInfo {
2018-07-09 15:53:49 -07:00
/// gossip address
2018-07-09 17:55:11 -07:00
pub ncp: SocketAddr,
2018-04-21 11:02:49 -07:00
/// address to connect to for replication
2018-07-09 17:55:11 -07:00
pub tvu: SocketAddr,
2018-04-21 11:02:49 -07:00
/// address to connect to when this node is leader
2018-07-09 17:55:11 -07:00
pub rpu: SocketAddr,
2018-05-25 14:51:41 -07:00
/// transactions address
2018-07-09 17:55:11 -07:00
pub tpu: SocketAddr,
/// storage data address
pub storage_addr: SocketAddr,
2018-07-09 15:53:49 -07:00
/// if this struture changes update this value as well
/// Always update `NodeInfo` version too
2018-07-09 15:53:49 -07:00
/// This separate version for addresses allows us to use the `Vote`
/// as means of updating the `NodeInfo` table without touching the
2018-07-09 15:53:49 -07:00
/// addresses if they haven't changed.
pub version: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct LedgerState {
/// last verified hash that was submitted to the leader
pub last_id: Hash,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct NodeInfo {
pub id: Pubkey,
2018-07-09 15:53:49 -07:00
/// If any of the bits change, update increment this value
pub version: u64,
/// network addresses
2018-07-09 17:55:11 -07:00
pub contact_info: ContactInfo,
2018-04-21 11:02:49 -07:00
/// current leader identity
pub leader_id: Pubkey,
/// information about the state of the ledger
pub ledger_state: LedgerState,
2018-04-21 11:02:49 -07:00
}
impl NodeInfo {
2018-04-28 00:31:20 -07:00
pub fn new(
id: Pubkey,
2018-07-09 17:55:11 -07:00
ncp: SocketAddr,
tvu: SocketAddr,
rpu: SocketAddr,
tpu: SocketAddr,
storage_addr: SocketAddr,
) -> Self {
NodeInfo {
2018-04-21 11:02:49 -07:00
id,
version: 0,
2018-07-09 17:55:11 -07:00
contact_info: ContactInfo {
ncp,
tvu,
rpu,
tpu,
storage_addr,
2018-07-09 15:53:49 -07:00
version: 0,
},
leader_id: Pubkey::default(),
ledger_state: LedgerState {
last_id: Hash::default(),
},
2018-04-21 11:02:49 -07:00
}
}
pub fn new_localhost(id: Pubkey) -> Self {
Self::new(
id,
socketaddr!("127.0.0.1:1234"),
socketaddr!("127.0.0.1:1235"),
socketaddr!("127.0.0.1:1236"),
socketaddr!("127.0.0.1:1237"),
socketaddr!("127.0.0.1:1238"),
)
}
#[cfg(test)]
/// NodeInfo with unspecified addresses for adversarial testing.
pub fn new_unspecified() -> Self {
let addr = socketaddr!(0, 0);
assert!(addr.ip().is_unspecified());
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr, addr)
}
#[cfg(test)]
/// NodeInfo with multicast addresses for adversarial testing.
pub fn new_multicast() -> Self {
let addr = socketaddr!("224.0.1.255:1000");
assert!(addr.ip().is_multicast());
Self::new(Keypair::new().pubkey(), addr, addr, addr, addr, addr)
}
fn next_port(addr: &SocketAddr, nxt: u16) -> SocketAddr {
2018-07-11 13:40:46 -07:00
let mut nxt_addr = *addr;
nxt_addr.set_port(addr.port() + nxt);
nxt_addr
}
pub fn new_with_pubkey_socketaddr(pubkey: Pubkey, bind_addr: &SocketAddr) -> Self {
2018-07-11 13:40:46 -07:00
let transactions_addr = *bind_addr;
let gossip_addr = Self::next_port(&bind_addr, 1);
let replicate_addr = Self::next_port(&bind_addr, 2);
let requests_addr = Self::next_port(&bind_addr, 3);
NodeInfo::new(
pubkey,
gossip_addr,
replicate_addr,
requests_addr,
transactions_addr,
"0.0.0.0:0".parse().unwrap(),
)
}
pub fn new_with_socketaddr(bind_addr: &SocketAddr) -> Self {
2018-08-09 07:56:04 -07:00
let keypair = Keypair::new();
Self::new_with_pubkey_socketaddr(keypair.pubkey(), bind_addr)
2018-07-09 15:53:49 -07:00
}
//
pub fn new_entry_point(gossip_addr: &SocketAddr) -> Self {
let daddr: SocketAddr = socketaddr!("0.0.0.0:0");
NodeInfo::new(Pubkey::default(), *gossip_addr, daddr, daddr, daddr, daddr)
}
2018-04-21 11:02:49 -07:00
}
2018-10-08 19:55:54 -07:00
/// `ClusterInfo` structure keeps a table of `NodeInfo` structs
2018-04-21 11:02:49 -07:00
/// # Properties
/// * `table` - map of public id's to versioned and signed NodeInfo structs
2018-04-21 11:02:49 -07:00
/// * `local` - map of public id's to what `self.update_index` `self.table` was updated
/// * `remote` - map of public id's to the `remote.update_index` was sent
/// * `update_index` - my update index
/// # Remarks
/// This implements two services, `gossip` and `listen`.
/// * `gossip` - asynchronously ask nodes to send updates
/// * `listen` - listen for requests and responses
/// No attempt to keep track of timeouts or dropped requests is made, or should be.
2018-10-08 19:55:54 -07:00
pub struct ClusterInfo {
2018-07-09 15:53:49 -07:00
/// table of everyone in the network
pub table: HashMap<Pubkey, NodeInfo>,
2018-04-21 11:02:49 -07:00
/// Value of my update index when entry in table was updated.
/// Nodes will ask for updates since `update_index`, and this node
/// should respond with all the identities that are greater then the
/// request's `update_index` in this list
local: HashMap<Pubkey, u64>,
2018-05-15 04:35:41 -07:00
/// The value of the remote update index that I have last seen
2018-04-21 11:02:49 -07:00
/// This Node will ask external nodes for updates since the value in this list
pub remote: HashMap<Pubkey, u64>,
2018-07-09 15:53:49 -07:00
/// last time the public key had sent us a message
pub alive: HashMap<Pubkey, u64>,
2018-04-28 00:31:20 -07:00
pub update_index: u64,
pub id: Pubkey,
2018-07-09 15:53:49 -07:00
/// last time we heard from anyone getting a message fro this public key
/// these are rumers and shouldn't be trusted directly
external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>>,
2018-04-21 11:02:49 -07:00
}
2018-04-21 11:02:49 -07:00
// TODO These messages should be signed, and go through the gpu pipeline for spam filtering
2018-06-03 19:59:17 -07:00
#[derive(Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "cargo-clippy", allow(large_enum_variant))]
2018-04-21 11:02:49 -07:00
enum Protocol {
2018-04-26 13:48:42 -07:00
/// forward your own latest data structure when requesting an update
/// this doesn't update the `remote` update index, but it allows the
/// recepient of this request to add knowledge of this node to the network
2018-07-09 15:53:49 -07:00
/// (last update index i saw from you, my replicated data)
RequestUpdates(u64, NodeInfo),
2018-04-21 11:02:49 -07:00
//TODO might need a since?
/// * from - from id,
/// * max_update_index - from's max update index in the response
/// * nodes - (NodeInfo, remote_last_update) vector
ReceiveUpdates {
from: Pubkey,
max_update_index: u64,
nodes: Vec<(NodeInfo, u64)>,
},
2018-05-12 19:00:22 -07:00
/// ask for a missing index
2018-07-09 15:53:49 -07:00
/// (my replicated data to keep alive, missing window index)
RequestWindowIndex(NodeInfo, u64),
2018-04-21 11:02:49 -07:00
}
2018-10-08 19:55:54 -07:00
impl ClusterInfo {
pub fn new(node_info: NodeInfo) -> Result<ClusterInfo> {
if node_info.version != 0 {
2018-10-08 19:55:54 -07:00
return Err(Error::ClusterInfoError(ClusterInfoError::BadNodeInfo));
}
2018-10-08 19:55:54 -07:00
let mut me = ClusterInfo {
2018-04-21 11:02:49 -07:00
table: HashMap::new(),
local: HashMap::new(),
remote: HashMap::new(),
alive: HashMap::new(),
2018-06-18 23:50:41 -07:00
external_liveness: HashMap::new(),
id: node_info.id,
2018-04-21 11:02:49 -07:00
update_index: 1,
};
me.local.insert(node_info.id, me.update_index);
me.table.insert(node_info.id, node_info);
Ok(me)
2018-04-21 11:02:49 -07:00
}
pub fn my_data(&self) -> &NodeInfo {
&self.table[&self.id]
2018-04-28 00:31:20 -07:00
}
pub fn leader_data(&self) -> Option<&NodeInfo> {
let leader_id = self.table[&self.id].leader_id;
// leader_id can be 0s from network entry point
if leader_id == Pubkey::default() {
return None;
}
self.table.get(&leader_id)
2018-04-28 00:31:20 -07:00
}
pub fn node_info_trace(&self) -> String {
let leader_id = self.table[&self.id].leader_id;
let nodes: Vec<_> = self
.table
.values()
.filter(|n| Self::is_valid_address(&n.contact_info.rpu))
.cloned()
.map(|node| {
format!(
" ncp: {:20} | {}{}\n \
rpu: {:20} |\n \
tpu: {:20} |\n",
node.contact_info.ncp.to_string(),
node.id,
if node.id == leader_id {
" <==== leader"
} else {
""
},
node.contact_info.rpu.to_string(),
node.contact_info.tpu.to_string()
)
2018-09-14 16:25:14 -07:00
}).collect();
format!(
" NodeInfo.contact_info | Node identifier\n\
---------------------------+------------------\n\
{}\n \
Nodes: {}",
nodes.join(""),
nodes.len()
)
}
pub fn set_leader(&mut self, key: Pubkey) -> () {
2018-04-28 00:31:20 -07:00
let mut me = self.my_data().clone();
warn!("{}: LEADER_UPDATE TO {} from {}", me.id, key, me.leader_id);
me.leader_id = key;
2018-04-28 00:31:20 -07:00
me.version += 1;
2018-05-12 19:00:22 -07:00
self.insert(&me);
2018-04-26 13:48:42 -07:00
}
2018-04-28 00:31:20 -07:00
2018-09-15 23:46:16 -07:00
pub fn get_valid_peers(&self) -> Vec<NodeInfo> {
let me = self.my_data().id;
self.table
.values()
.filter(|x| x.id != me)
2018-10-08 19:55:54 -07:00
.filter(|x| ClusterInfo::is_valid_address(&x.contact_info.rpu))
2018-09-15 23:46:16 -07:00
.cloned()
.collect()
}
pub fn get_external_liveness_entry(&self, key: &Pubkey) -> Option<&HashMap<Pubkey, u64>> {
2018-06-18 23:50:41 -07:00
self.external_liveness.get(key)
}
pub fn insert_vote(&mut self, pubkey: &Pubkey, v: &Vote, last_id: Hash) {
if self.table.get(pubkey).is_none() {
warn!("{}: VOTE for unknown id: {}", self.id, pubkey);
return;
}
if v.contact_info_version > self.table[pubkey].contact_info.version {
warn!(
"{}: VOTE for new address version from: {} ours: {} vote: {:?}",
self.id, pubkey, self.table[pubkey].contact_info.version, v,
);
return;
}
if *pubkey == self.my_data().leader_id {
info!("{}: LEADER_VOTED! {}", self.id, pubkey);
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-insert_vote-leader_voted", 1);
}
if v.version <= self.table[pubkey].version {
debug!("{}: VOTE for old version: {}", self.id, pubkey);
self.update_liveness(*pubkey);
return;
} else {
let mut data = self.table[pubkey].clone();
data.version = v.version;
data.ledger_state.last_id = last_id;
debug!("{}: INSERTING VOTE! for {}", self.id, data.id);
self.update_liveness(data.id);
self.insert(&data);
}
}
pub fn insert_votes(&mut self, votes: &[(Pubkey, Vote, Hash)]) {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-vote-count", votes.len());
2018-07-11 13:40:46 -07:00
if !votes.is_empty() {
info!("{}: INSERTING VOTES {}", self.id, votes.len());
}
2018-07-11 13:40:46 -07:00
for v in votes {
self.insert_vote(&v.0, &v.1, v.2);
}
}
pub fn insert(&mut self, v: &NodeInfo) -> usize {
2018-04-28 00:31:20 -07:00
// TODO check that last_verified types are always increasing
2018-08-30 12:30:05 -07:00
// update the peer table
2018-04-21 11:02:49 -07:00
if self.table.get(&v.id).is_none() || (v.version > self.table[&v.id].version) {
2018-08-30 12:30:05 -07:00
//somehow we signed a message for our own identity with a higher version than
2018-04-28 00:31:20 -07:00
// we have stored ourselves
trace!("{}: insert v.id: {} version: {}", self.id, v.id, v.version);
2018-07-17 22:55:53 -07:00
if self.table.get(&v.id).is_none() {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-insert-new_entry", 1, 1);
2018-07-17 22:55:53 -07:00
}
2018-07-09 15:53:49 -07:00
2018-04-21 11:02:49 -07:00
self.update_index += 1;
2018-07-11 13:40:46 -07:00
let _ = self.table.insert(v.id, v.clone());
2018-04-21 11:02:49 -07:00
let _ = self.local.insert(v.id, self.update_index);
self.update_liveness(v.id);
1
2018-04-21 11:02:49 -07:00
} else {
2018-04-28 00:31:20 -07:00
trace!(
"{}: INSERT FAILED data: {} new.version: {} me.version: {}",
self.id,
v.id,
2018-04-28 00:31:20 -07:00
v.version,
self.table[&v.id].version
);
0
2018-04-21 11:02:49 -07:00
}
2018-07-09 15:53:49 -07:00
}
fn update_liveness(&mut self, id: Pubkey) {
//update the liveness table
let now = timestamp();
trace!("{} updating liveness {} to {}", self.id, id, now);
2018-07-09 15:53:49 -07:00
*self.alive.entry(id).or_insert(now) = now;
}
/// purge old validators
/// TODO: we need a robust membership protocol
/// http://asc.di.fct.unl.pt/~jleitao/pdf/dsn07-leitao.pdf
/// challenging part is that we are on a permissionless network
pub fn purge(&mut self, now: u64) {
2018-06-14 22:03:49 -07:00
if self.table.len() <= MIN_TABLE_SIZE {
2018-07-18 12:44:59 -07:00
trace!("purge: skipped: table too small: {}", self.table.len());
2018-06-14 22:03:49 -07:00
return;
}
2018-07-09 15:53:49 -07:00
if self.leader_data().is_none() {
2018-07-18 12:44:59 -07:00
trace!("purge: skipped: no leader_data");
2018-07-09 15:53:49 -07:00
return;
}
let leader_id = self.leader_data().unwrap().id;
let limit = GOSSIP_PURGE_MILLIS;
let dead_ids: Vec<Pubkey> = self
2018-08-03 11:27:44 -07:00
.alive
.iter()
2018-06-14 22:03:49 -07:00
.filter_map(|(&k, v)| {
if k != self.id && (now - v) > limit {
Some(k)
} else {
trace!("{} purge skipped {} {} {}", self.id, k, now - v, limit);
None
}
2018-09-14 16:25:14 -07:00
}).collect();
2018-06-18 23:50:41 -07:00
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-purge-count", dead_ids.len());
2018-07-11 13:40:46 -07:00
for id in &dead_ids {
2018-06-14 22:03:49 -07:00
self.alive.remove(id);
self.table.remove(id);
self.remote.remove(id);
self.local.remove(id);
2018-06-18 23:50:41 -07:00
self.external_liveness.remove(id);
info!("{}: PURGE {}", self.id, id);
2018-06-25 03:59:15 -07:00
for map in self.external_liveness.values_mut() {
map.remove(id);
}
if *id == leader_id {
info!("{}: PURGE LEADER {}", self.id, id,);
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-purge-purged_leader", 1, 1);
self.set_leader(Pubkey::default());
}
}
2018-04-21 11:02:49 -07:00
}
2018-04-28 00:31:20 -07:00
2018-07-09 15:53:49 -07:00
/// compute broadcast table
2018-04-28 00:31:20 -07:00
/// # Remarks
pub fn compute_broadcast_table(&self) -> Vec<NodeInfo> {
2018-07-09 17:35:23 -07:00
let live: Vec<_> = self.alive.iter().collect();
//thread_rng().shuffle(&mut live);
let me = &self.table[&self.id];
2018-08-03 11:27:44 -07:00
let cloned_table: Vec<NodeInfo> = live
.iter()
2018-07-09 15:53:49 -07:00
.map(|x| &self.table[x.0])
.filter(|v| {
2018-04-28 00:31:20 -07:00
if me.id == v.id {
//filter myself
false
} else if !(Self::is_valid_address(&v.contact_info.tvu)) {
2018-07-09 17:35:23 -07:00
trace!(
"{}:broadcast skip not listening {} {}",
me.id,
v.id,
v.contact_info.tvu,
2018-07-09 17:35:23 -07:00
);
false
} else {
trace!("{}:broadcast node {} {}", me.id, v.id, v.contact_info.tvu);
true
2018-04-28 00:31:20 -07:00
}
2018-09-14 16:25:14 -07:00
}).cloned()
2018-05-12 19:00:22 -07:00
.collect();
2018-07-09 15:53:49 -07:00
cloned_table
}
/// broadcast messages from the leader to layer 1 nodes
/// # Remarks
/// We need to avoid having obj locked while doing any io, such as the `send_to`
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
2018-07-09 15:53:49 -07:00
pub fn broadcast(
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
leader_scheduler: &Arc<RwLock<LeaderScheduler>>,
tick_height: u64,
me: &NodeInfo,
2018-07-11 13:40:46 -07:00
broadcast_table: &[NodeInfo],
window: &SharedWindow,
2018-07-09 15:53:49 -07:00
s: &UdpSocket,
transmit_index: &mut WindowIndex,
2018-07-09 15:53:49 -07:00
received_index: u64,
) -> Result<()> {
2018-07-11 13:40:46 -07:00
if broadcast_table.is_empty() {
2018-10-08 19:55:54 -07:00
debug!("{}:not enough peers in cluster_info table", me.id);
inc_new_counter_info!("cluster_info-broadcast-not_enough_peers_error", 1);
Err(ClusterInfoError::NoPeers)?;
2018-05-14 22:06:42 -07:00
}
trace!(
"{} transmit_index: {:?} received_index: {} broadcast_len: {}",
me.id,
*transmit_index,
received_index,
broadcast_table.len()
);
let old_transmit_index = transmit_index.data;
// enumerate all the blobs in the window, those are the indices
// transmit them to nodes, starting from a different node. Add one
// to the capacity in case we want to send an extra blob notifying the
// next leader about the blob right before leader rotation
let mut orders = Vec::with_capacity((received_index - transmit_index.data + 1) as usize);
2018-09-07 11:22:54 -07:00
let window_l = window.read().unwrap();
let mut br_idx = transmit_index.data as usize % broadcast_table.len();
for idx in transmit_index.data..received_index {
2018-07-18 10:10:34 -07:00
let w_idx = idx as usize % window_l.len();
trace!(
"{} broadcast order data w_idx {} br_idx {}",
me.id,
w_idx,
br_idx
);
2018-07-18 10:10:34 -07:00
// Make sure the next leader in line knows about the entries before his slot in the leader
// rotation so he can initiate repairs if necessary
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
{
let ls_lock = leader_scheduler.read().unwrap();
let next_leader_height = ls_lock.max_height_for_leader(tick_height);
let next_leader_id =
next_leader_height.map(|nlh| ls_lock.get_scheduled_leader(nlh));
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
// In the case the next scheduled leader is None, then the write_stage moved
// the schedule too far ahead and we no longer are in the known window
// (will happen during calculation of the next set of slots every epoch or
// seed_rotation_interval heights when we move the window forward in the
// LeaderScheduler). For correctness, this is fine write_stage will never send
// blobs past the point of when this node should stop being leader, so we just
// continue broadcasting until we catch up to write_stage. The downside is we
// can't guarantee the current leader will broadcast the last entry to the next
// scheduled leader, so the next leader will have to rely on avalanche/repairs
// to get this last blob, which could cause slowdowns during leader handoffs.
// See corresponding issue for repairs in repair() function in window.rs.
if let Some(Some(next_leader_id)) = next_leader_id {
if next_leader_id == me.id {
break;
}
let info_result = broadcast_table.iter().position(|n| n.id == next_leader_id);
if let Some(index) = info_result {
orders.push((window_l[w_idx].data.clone(), &broadcast_table[index]));
}
}
}
orders.push((window_l[w_idx].data.clone(), &broadcast_table[br_idx]));
br_idx += 1;
br_idx %= broadcast_table.len();
}
2018-07-18 10:10:34 -07:00
for idx in transmit_index.coding..received_index {
let w_idx = idx as usize % window_l.len();
// skip over empty slots
if window_l[w_idx].coding.is_none() {
continue;
}
trace!(
"{} broadcast order coding w_idx: {} br_idx :{}",
me.id,
w_idx,
br_idx,
);
orders.push((window_l[w_idx].coding.clone(), &broadcast_table[br_idx]));
br_idx += 1;
br_idx %= broadcast_table.len();
}
trace!("broadcast orders table {}", orders.len());
let errs: Vec<_> = orders
2018-05-12 19:00:22 -07:00
.into_iter()
.map(|(b, v)| {
2018-04-28 00:31:20 -07:00
// only leader should be broadcasting
assert!(me.leader_id != v.id);
let bl = b.unwrap();
let blob = bl.read().unwrap();
//TODO profile this, may need multiple sockets for par_iter
2018-06-05 12:24:39 -07:00
trace!(
"{}: BROADCAST idx: {} sz: {} to {},{} coding: {}",
me.id,
2018-06-05 12:24:39 -07:00
blob.get_index().unwrap(),
blob.meta.size,
v.id,
2018-07-09 17:55:11 -07:00
v.contact_info.tvu,
2018-06-05 12:24:39 -07:00
blob.is_coding()
);
2018-07-25 15:12:13 -07:00
assert!(blob.meta.size <= BLOB_SIZE);
2018-07-09 17:55:11 -07:00
let e = s.send_to(&blob.data[..blob.meta.size], &v.contact_info.tvu);
2018-07-09 17:35:23 -07:00
trace!(
"{}: done broadcast {} to {} {}",
me.id,
2018-07-09 17:35:23 -07:00
blob.meta.size,
v.id,
2018-07-09 17:55:11 -07:00
v.contact_info.tvu
2018-07-09 17:35:23 -07:00
);
2018-05-12 19:00:22 -07:00
e
2018-09-14 16:25:14 -07:00
}).collect();
2018-05-30 16:33:05 -07:00
trace!("broadcast results {}", errs.len());
2018-04-28 00:31:20 -07:00
for e in errs {
if let Err(e) = &e {
trace!("broadcast result {:?}", e);
2018-04-28 00:31:20 -07:00
}
e?;
if transmit_index.data < received_index {
transmit_index.data += 1;
}
2018-04-28 00:31:20 -07:00
}
inc_new_counter_info!(
2018-10-08 19:55:54 -07:00
"cluster_info-broadcast-max_idx",
(transmit_index.data - old_transmit_index) as usize
);
transmit_index.coding = transmit_index.data;
2018-04-28 00:31:20 -07:00
Ok(())
}
/// retransmit messages from the leader to layer 1 nodes
/// # Remarks
/// We need to avoid having obj locked while doing any io, such as the `send_to`
pub fn retransmit(obj: &Arc<RwLock<Self>>, blob: &SharedBlob, s: &UdpSocket) -> Result<()> {
let (me, table): (NodeInfo, Vec<NodeInfo>) = {
2018-05-15 04:35:41 -07:00
// copy to avoid locking during IO
2018-05-09 18:10:48 -07:00
let s = obj.read().expect("'obj' read lock in pub fn retransmit");
(s.my_data().clone(), s.table.values().cloned().collect())
2018-04-28 00:31:20 -07:00
};
2018-05-12 19:00:22 -07:00
blob.write()
.unwrap()
2018-05-12 19:00:22 -07:00
.set_id(me.id)
.expect("set_id in pub fn retransmit");
let rblob = blob.read().unwrap();
let orders: Vec<_> = table
.iter()
.filter(|v| {
2018-04-28 00:31:20 -07:00
if me.id == v.id {
trace!("skip retransmit to self {:?}", v.id);
false
} else if me.leader_id == v.id {
trace!("skip retransmit to leader {:?}", v.id);
false
} else if !(Self::is_valid_address(&v.contact_info.tvu)) {
trace!(
"skip nodes that are not listening {:?} {}",
v.id,
v.contact_info.tvu
);
false
} else {
true
2018-04-28 00:31:20 -07:00
}
2018-09-14 16:25:14 -07:00
}).collect();
trace!("retransmit orders {}", orders.len());
let errs: Vec<_> = orders
.par_iter()
.map(|v| {
2018-07-09 15:53:49 -07:00
debug!(
"{}: retransmit blob {} to {} {}",
me.id,
2018-05-12 19:00:22 -07:00
rblob.get_index().unwrap(),
v.id,
v.contact_info.tvu,
2018-05-12 19:00:22 -07:00
);
//TODO profile this, may need multiple sockets for par_iter
2018-07-26 08:55:00 -07:00
assert!(rblob.meta.size <= BLOB_SIZE);
2018-07-09 17:55:11 -07:00
s.send_to(&rblob.data[..rblob.meta.size], &v.contact_info.tvu)
2018-09-14 16:25:14 -07:00
}).collect();
2018-04-28 00:31:20 -07:00
for e in errs {
if let Err(e) = &e {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-retransmit-send_to_error", 1, 1);
error!("retransmit result {:?}", e);
2018-04-28 00:31:20 -07:00
}
e?;
2018-04-28 00:31:20 -07:00
}
Ok(())
}
2018-05-16 22:58:32 -07:00
// max number of nodes that we could be converged to
2018-05-16 22:54:06 -07:00
pub fn convergence(&self) -> u64 {
2018-05-16 23:17:45 -07:00
let max = self.remote.values().len() as u64 + 1;
2018-05-16 23:18:56 -07:00
self.remote.values().fold(max, |a, b| std::cmp::min(a, *b))
2018-05-16 22:54:06 -07:00
}
2018-05-16 23:11:51 -07:00
// TODO: fill in with real implmentation once staking is implemented
fn get_stake(_id: Pubkey) -> f64 {
1.0
2018-06-18 23:50:41 -07:00
}
fn max_updates(max_bytes: usize) -> usize {
let unit = (NodeInfo::new_localhost(Default::default()), 0);
let unit_size = serialized_size(&unit).unwrap();
let msg = Protocol::ReceiveUpdates {
from: Default::default(),
max_update_index: 0,
nodes: vec![unit],
};
let msg_size = serialized_size(&msg).unwrap();
((max_bytes - (msg_size as usize)) / (unit_size as usize)) + 1
}
// Get updated node since v up to a maximum of `max_bytes` updates
fn get_updates_since(&self, v: u64, max: usize) -> (Pubkey, u64, Vec<(NodeInfo, u64)>) {
let nodes: Vec<_> = self
2018-08-03 11:27:44 -07:00
.table
2018-04-21 11:02:49 -07:00
.values()
.filter(|x| x.id != Pubkey::default() && self.local[&x.id] > v)
2018-04-21 11:02:49 -07:00
.cloned()
.collect();
let liveness: Vec<u64> = nodes
.iter()
.map(|d| *self.remote.get(&d.id).unwrap_or(&0))
.collect();
let updates: Vec<u64> = nodes.iter().map(|d| self.local[&d.id]).collect();
trace!("{:?}", updates);
let id = self.id;
let mut out: Vec<(u64, (NodeInfo, u64))> = updates
.into_iter()
.zip(nodes.into_iter().zip(liveness))
.collect();
out.sort_by_key(|k| k.0);
let last_node = std::cmp::max(1, std::cmp::min(out.len(), max)) - 1;
let max_updated_node = out.get(last_node).map(|x| x.0).unwrap_or(0);
let updated_data: Vec<(NodeInfo, u64)> = out.into_iter().take(max).map(|x| x.1).collect();
trace!("get updates since response {} {}", v, updated_data.len());
(id, max_updated_node, updated_data)
2018-04-21 11:02:49 -07:00
}
pub fn valid_last_ids(&self) -> Vec<Hash> {
self.table
.values()
.filter(|r| {
r.id != Pubkey::default()
&& (Self::is_valid_address(&r.contact_info.tpu)
|| Self::is_valid_address(&r.contact_info.tvu))
2018-09-14 16:25:14 -07:00
}).map(|x| x.ledger_state.last_id)
.collect()
}
2018-05-12 19:00:22 -07:00
pub fn window_index_request(&self, ix: u64) -> Result<(SocketAddr, Vec<u8>)> {
// find a peer that appears to be accepting replication, as indicated
// by a valid tvu port location
2018-08-03 11:27:44 -07:00
let valid: Vec<_> = self
.table
.values()
.filter(|r| r.id != self.id && Self::is_valid_address(&r.contact_info.tvu))
.collect();
if valid.is_empty() {
2018-10-08 19:55:54 -07:00
Err(ClusterInfoError::NoPeers)?;
2018-05-12 19:00:22 -07:00
}
let n = thread_rng().gen::<usize>() % valid.len();
let addr = valid[n].contact_info.ncp; // send the request to the peer's gossip port
let req = Protocol::RequestWindowIndex(self.my_data().clone(), ix);
2018-05-12 19:00:22 -07:00
let out = serialize(&req)?;
Ok((addr, out))
}
2018-04-21 11:02:49 -07:00
/// Create a random gossip request
/// # Returns
2018-04-28 00:31:20 -07:00
/// (A,B)
/// * A - Address to send to
/// * B - RequestUpdates protocol message
fn gossip_request(&self) -> Result<(SocketAddr, Protocol)> {
2018-08-03 11:27:44 -07:00
let options: Vec<_> = self
.table
.values()
.filter(|v| {
v.id != self.id
&& !v.contact_info.ncp.ip().is_unspecified()
&& !v.contact_info.ncp.ip().is_multicast()
2018-09-14 16:25:14 -07:00
}).collect();
2018-06-18 23:50:41 -07:00
let choose_peer_strategy = ChooseWeightedPeerStrategy::new(
&self.remote,
&self.external_liveness,
&Self::get_stake,
);
let choose_peer_result = choose_peer_strategy.choose_peer(options);
2018-10-08 19:55:54 -07:00
if let Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)) = &choose_peer_result {
trace!(
"cluster_info too small for gossip {} {}",
self.id,
self.table.len()
);
2018-06-18 23:50:41 -07:00
};
let v = choose_peer_result?;
2018-06-18 23:50:41 -07:00
2018-04-21 11:02:49 -07:00
let remote_update_index = *self.remote.get(&v.id).unwrap_or(&0);
let req = Protocol::RequestUpdates(remote_update_index, self.my_data().clone());
trace!(
"created gossip request from {} {:?} to {} {}",
self.id,
self.my_data(),
v.id,
2018-07-09 17:55:11 -07:00
v.contact_info.ncp
);
2018-06-18 23:50:41 -07:00
2018-07-09 17:55:11 -07:00
Ok((v.contact_info.ncp, req))
2018-04-21 11:02:49 -07:00
}
2018-07-18 18:10:53 -07:00
pub fn new_vote(&mut self, last_id: Hash) -> Result<(Vote, SocketAddr)> {
let mut me = self.my_data().clone();
2018-10-08 19:55:54 -07:00
let leader = self
.leader_data()
.ok_or(ClusterInfoError::NoLeader)?
.clone();
me.version += 1;
me.ledger_state.last_id = last_id;
let vote = Vote {
version: me.version,
contact_info_version: me.contact_info.version,
};
self.insert(&me);
Ok((vote, leader.contact_info.tpu))
}
2018-04-21 11:02:49 -07:00
/// At random pick a node and try to get updated changes from them
fn run_gossip(obj: &Arc<RwLock<Self>>, blob_sender: &BlobSender) -> Result<()> {
2018-04-21 11:02:49 -07:00
//TODO we need to keep track of stakes and weight the selection by stake size
//TODO cache sockets
// Lock the object only to do this operation and not for any longer
// especially not when doing the `sock.send_to`
2018-08-03 11:27:44 -07:00
let (remote_gossip_addr, req) = obj
.read()
2018-05-11 11:38:52 -07:00
.expect("'obj' read lock in fn run_gossip")
.gossip_request()?;
2018-06-18 23:50:41 -07:00
2018-04-21 11:02:49 -07:00
// TODO this will get chatty, so we need to first ask for number of updates since
// then only ask for specific data that we dont have
let blob = to_blob(req, remote_gossip_addr)?;
blob_sender.send(vec![blob])?;
2018-04-21 11:02:49 -07:00
Ok(())
}
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
pub fn get_gossip_top_leader(&self) -> Option<&NodeInfo> {
2018-06-23 16:08:53 -07:00
let mut table = HashMap::new();
let def = Pubkey::default();
let cur = self.table.values().filter(|x| x.leader_id != def);
2018-06-23 16:08:53 -07:00
for v in cur {
let cnt = table.entry(&v.leader_id).or_insert(0);
2018-06-23 16:08:53 -07:00
*cnt += 1;
trace!("leader {} {}", v.leader_id, *cnt);
2018-06-23 16:08:53 -07:00
}
let mut sorted: Vec<(&Pubkey, usize)> = table.into_iter().collect();
2018-07-11 13:40:46 -07:00
for x in &sorted {
trace!("{}: sorted leaders {} votes: {}", self.id, x.0, x.1);
2018-07-02 11:23:20 -07:00
}
2018-06-23 16:08:53 -07:00
sorted.sort_by_key(|a| a.1);
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
let top_leader = sorted.last().map(|a| *a.0);
2018-06-23 16:08:53 -07:00
Leader scheduler plumbing (#1440) * Added LeaderScheduler module and tests * plumbing for LeaderScheduler in Fullnode + tests. Add vote processing for active set to ReplicateStage and WriteStage * Add LeaderScheduler plumbing for Tvu, window, and tests * Fix bank and switch tests to use new LeaderScheduler * move leader rotation check from window service to replicate stage * Add replicate_stage leader rotation exit test * removed leader scheduler from the window service and associated modules/tests * Corrected is_leader calculation in repair() function in window.rs * Integrate LeaderScheduler with write_stage for leader to validator transitions * Integrated LeaderScheduler with BroadcastStage * Removed gossip leader rotation from crdt * Add multi validator, leader test * Comments and cleanup * Remove unneeded checks from broadcast stage * Fix case where a validator/leader need to immediately transition on startup after reading ledger and seeing they are not in the correct role * Set new leader in validator -> validator transitions * Clean up for PR comments, refactor LeaderScheduler from process_entry/process_ledger_tail * Cleaned out LeaderScheduler options, implemented LeaderScheduler strategy that only picks the bootstrap leader to support existing tests, drone/airdrops * Ignore test_full_leader_validator_network test due to bug where the next leader in line fails to get the last entry before rotation (b/c it hasn't started up yet). Added a test test_dropped_handoff_recovery go track this bug
2018-10-10 16:49:41 -07:00
if let Some(l) = top_leader {
self.table.get(&l)
} else {
None
2018-06-23 16:08:53 -07:00
}
}
2018-04-21 11:02:49 -07:00
/// Apply updates that we received from the identity `from`
/// # Arguments
/// * `from` - identity of the sender of the updates
/// * `update_index` - the number of updates that `from` has completed and this set of `data` represents
/// * `data` - the update data
fn apply_updates(&mut self, from: Pubkey, update_index: u64, data: &[(NodeInfo, u64)]) {
2018-04-21 11:02:49 -07:00
trace!("got updates {}", data.len());
// TODO we need to punish/spam resist here
// sigverify the whole update and slash anyone who sends a bad update
let mut insert_total = 0;
2018-04-21 11:02:49 -07:00
for v in data {
insert_total += self.insert(&v.0);
2018-04-21 11:02:49 -07:00
}
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-update-count", insert_total);
2018-06-18 23:50:41 -07:00
for (node, external_remote_index) in data {
let pubkey = node.id;
let remote_entry = if let Some(v) = self.remote.get(&pubkey) {
2018-06-25 03:59:15 -07:00
*v
} else {
0
};
2018-06-18 23:50:41 -07:00
if remote_entry >= *external_remote_index {
continue;
}
2018-08-03 11:27:44 -07:00
let liveness_entry = self
.external_liveness
.entry(pubkey)
2018-07-11 13:40:46 -07:00
.or_insert_with(HashMap::new);
2018-06-18 23:50:41 -07:00
let peer_index = *liveness_entry.entry(from).or_insert(*external_remote_index);
if *external_remote_index > peer_index {
liveness_entry.insert(from, *external_remote_index);
}
}
2018-04-21 11:02:49 -07:00
*self.remote.entry(from).or_insert(update_index) = update_index;
2018-06-18 23:50:41 -07:00
// Clear the remote liveness table for this node, b/c we've heard directly from them
// so we don't need to rely on rumors
self.external_liveness.remove(&from);
2018-04-21 11:02:49 -07:00
}
/// randomly pick a node and ask them for updates asynchronously
pub fn gossip(
obj: Arc<RwLock<Self>>,
blob_sender: BlobSender,
exit: Arc<AtomicBool>,
) -> JoinHandle<()> {
2018-05-30 13:25:32 -07:00
Builder::new()
.name("solana-gossip".to_string())
.spawn(move || loop {
2018-06-23 16:08:53 -07:00
let start = timestamp();
let _ = Self::run_gossip(&obj, &blob_sender);
2018-05-30 13:25:32 -07:00
if exit.load(Ordering::Relaxed) {
return;
}
2018-07-09 15:53:49 -07:00
obj.write().unwrap().purge(timestamp());
2018-06-07 11:39:25 -07:00
//TODO: possibly tune this parameter
//we saw a deadlock passing an obj.read().unwrap().timeout into sleep
2018-06-23 16:08:53 -07:00
let elapsed = timestamp() - start;
if GOSSIP_SLEEP_MILLIS > elapsed {
2018-06-23 17:01:38 -07:00
let time_left = GOSSIP_SLEEP_MILLIS - elapsed;
sleep(Duration::from_millis(time_left));
2018-06-23 16:08:53 -07:00
}
2018-09-14 16:25:14 -07:00
}).unwrap()
2018-04-21 11:02:49 -07:00
}
2018-05-12 19:00:22 -07:00
fn run_window_request(
from: &NodeInfo,
from_addr: &SocketAddr,
window: &SharedWindow,
2018-08-06 12:35:38 -07:00
ledger_window: &mut Option<&mut LedgerWindow>,
me: &NodeInfo,
2018-05-12 19:00:22 -07:00
ix: u64,
) -> Option<SharedBlob> {
2018-05-12 19:00:22 -07:00
let pos = (ix as usize) % window.read().unwrap().len();
if let Some(ref mut blob) = &mut window.write().unwrap()[pos].data {
let mut wblob = blob.write().unwrap();
let blob_ix = wblob.get_index().expect("run_window_request get_index");
2018-05-12 19:00:22 -07:00
if blob_ix == ix {
let num_retransmits = wblob.meta.num_retransmits;
wblob.meta.num_retransmits += 1;
// Setting the sender id to the requester id
// prevents the requester from retransmitting this response
// to other peers
let mut sender_id = from.id;
// Allow retransmission of this response if the node
// is the leader and the number of repair requests equals
// a power of two
if me.leader_id == me.id
&& (num_retransmits == 0 || num_retransmits.is_power_of_two())
{
sender_id = me.id
}
let out = SharedBlob::default();
2018-05-12 19:00:22 -07:00
// copy to avoid doing IO inside the lock
{
let mut outblob = out.write().unwrap();
let sz = wblob.meta.size;
outblob.meta.size = sz;
outblob.data[..sz].copy_from_slice(&wblob.data[..sz]);
outblob.meta.set_addr(from_addr);
outblob.set_id(sender_id).expect("blob set_id");
}
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-pass", 1);
return Some(out);
} else {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-outside", 1);
trace!(
"requested ix {} != blob_ix {}, outside window!",
ix,
blob_ix
);
2018-08-06 12:35:38 -07:00
// falls through to checking window_ledger
2018-05-12 19:00:22 -07:00
}
}
2018-08-06 12:35:38 -07:00
if let Some(ledger_window) = ledger_window {
if let Ok(entry) = ledger_window.get_entry(ix) {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-ledger", 1);
2018-08-06 12:35:38 -07:00
let out = entry.to_blob(
Some(ix),
Some(me.id), // causes retransmission if I'm the leader
Some(from_addr),
);
2018-08-06 12:35:38 -07:00
return Some(out);
}
}
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-fail", 1);
trace!(
"{}: failed RequestWindowIndex {} {} {}",
me.id,
from.id,
2018-08-06 12:35:38 -07:00
ix,
pos,
);
None
2018-05-12 19:00:22 -07:00
}
//TODO we should first coalesce all the requests
fn handle_blob(
2018-05-12 19:00:22 -07:00
obj: &Arc<RwLock<Self>>,
window: &SharedWindow,
2018-08-06 12:35:38 -07:00
ledger_window: &mut Option<&mut LedgerWindow>,
blob: &Blob,
) -> Option<SharedBlob> {
match deserialize(&blob.data[..blob.meta.size]) {
Ok(request) => {
2018-10-08 19:55:54 -07:00
ClusterInfo::handle_protocol(obj, &blob.meta.addr(), request, window, ledger_window)
}
Err(_) => {
2018-10-08 19:55:54 -07:00
warn!("deserialize cluster_info packet failed");
None
}
}
}
fn handle_protocol(
me: &Arc<RwLock<Self>>,
from_addr: &SocketAddr,
request: Protocol,
window: &SharedWindow,
2018-08-06 12:35:38 -07:00
ledger_window: &mut Option<&mut LedgerWindow>,
) -> Option<SharedBlob> {
match request {
2018-04-21 11:02:49 -07:00
// TODO sigverify these
Protocol::RequestUpdates(version, mut from) => {
let id = me.read().unwrap().id;
trace!(
"{} RequestUpdates {} from {}, professing to be {}",
id,
version,
from_addr,
from.contact_info.ncp
);
if from.id == me.read().unwrap().id {
warn!(
"RequestUpdates ignored, I'm talking to myself: me={} remoteme={}",
me.read().unwrap().id,
from.id
);
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-loopback", 1);
return None;
}
// the remote side may not know his public IP:PORT, record what he looks like to us
// this may or may not be correct for everybody but it's better than leaving him with
// an unspecified address in our table
if from.contact_info.ncp.ip().is_unspecified() {
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-updates-unspec-ncp", 1);
from.contact_info.ncp = *from_addr;
}
let max = Self::max_updates(1024 * 64 - 512);
let (from_id, ups, data) = me.read().unwrap().get_updates_since(version, max);
// update entry only after collecting liveness
{
let mut me = me.write().unwrap();
me.insert(&from);
me.update_liveness(from.id);
}
let len = data.len();
trace!("get updates since response {} {}", version, len);
if data.is_empty() {
let me = me.read().unwrap();
trace!(
"no updates me {} ix {} since {}",
id,
me.update_index,
version
);
None
} else {
let rsp = Protocol::ReceiveUpdates {
from: from_id,
max_update_index: ups,
nodes: data,
};
if let Ok(r) = to_blob(rsp, from.contact_info.ncp) {
trace!(
"sending updates me {} len {} to {} {}",
id,
len,
from.id,
from.contact_info.ncp,
);
Some(r)
} else {
warn!("to_blob failed");
None
}
}
2018-04-21 11:02:49 -07:00
}
Protocol::ReceiveUpdates {
from,
max_update_index,
nodes,
} => {
let now = Instant::now();
trace!(
"ReceivedUpdates from={} update_index={} len={}",
from,
max_update_index,
nodes.len()
);
me.write()
.expect("'me' write lock in ReceiveUpdates")
.apply_updates(from, max_update_index, &nodes);
report_time_spent(
"ReceiveUpdates",
&now.elapsed(),
&format!(" len: {}", nodes.len()),
);
None
2018-04-21 11:02:49 -07:00
}
Protocol::RequestWindowIndex(from, ix) => {
let now = Instant::now();
2018-10-08 19:55:54 -07:00
//TODO this doesn't depend on cluster_info module, could be moved
2018-06-03 20:31:09 -07:00
//but we are using the listen thread to service these request
2018-05-12 19:00:22 -07:00
//TODO verify from is signed
if from.id == me.read().unwrap().id {
warn!(
"{}: Ignored received RequestWindowIndex from ME {} {} ",
me.read().unwrap().id,
from.id,
ix,
);
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-address-eq", 1);
return None;
}
me.write().unwrap().insert(&from);
let me = me.read().unwrap().my_data().clone();
2018-10-08 19:55:54 -07:00
inc_new_counter_info!("cluster_info-window-request-recv", 1);
trace!("{}: received RequestWindowIndex {} {} ", me.id, from.id, ix,);
let res =
Self::run_window_request(&from, &from_addr, &window, ledger_window, &me, ix);
report_time_spent(
"RequestWindowIndex",
&now.elapsed(),
&format!(" ix: {}", ix),
);
res
}
2018-04-21 11:02:49 -07:00
}
}
/// Process messages from the network
fn run_listen(
obj: &Arc<RwLock<Self>>,
window: &SharedWindow,
2018-08-06 12:35:38 -07:00
ledger_window: &mut Option<&mut LedgerWindow>,
requests_receiver: &BlobReceiver,
response_sender: &BlobSender,
) -> Result<()> {
//TODO cache connections
let timeout = Duration::new(1, 0);
let mut reqs = requests_receiver.recv_timeout(timeout)?;
while let Ok(mut more) = requests_receiver.try_recv() {
reqs.append(&mut more);
}
let mut resps = Vec::new();
for req in reqs {
if let Some(resp) = Self::handle_blob(obj, window, ledger_window, &req.read().unwrap())
{
resps.push(resp);
2018-08-06 12:35:38 -07:00
}
}
2018-08-06 12:35:38 -07:00
response_sender.send(resps)?;
2018-04-21 11:02:49 -07:00
Ok(())
}
pub fn listen(
me: Arc<RwLock<Self>>,
window: SharedWindow,
2018-08-06 12:35:38 -07:00
ledger_path: Option<&str>,
requests_receiver: BlobReceiver,
response_sender: BlobSender,
2018-04-21 11:02:49 -07:00
exit: Arc<AtomicBool>,
) -> JoinHandle<()> {
let mut ledger_window = ledger_path.map(|p| LedgerWindow::open(p).unwrap());
2018-08-06 12:35:38 -07:00
2018-05-30 13:25:32 -07:00
Builder::new()
.name("solana-listen".to_string())
.spawn(move || loop {
let e = Self::run_listen(
&me,
2018-05-30 13:25:32 -07:00
&window,
2018-08-06 12:35:38 -07:00
&mut ledger_window.as_mut(),
2018-05-30 13:25:32 -07:00
&requests_receiver,
&response_sender,
2018-05-23 21:45:40 -07:00
);
2018-07-09 15:53:49 -07:00
if exit.load(Ordering::Relaxed) {
return;
}
2018-05-30 13:25:32 -07:00
if e.is_err() {
let me = me.read().unwrap();
debug!(
"{}: run_listen timeout, table size: {}",
me.id,
me.table.len()
2018-05-30 13:25:32 -07:00
);
}
2018-09-14 16:25:14 -07:00
}).unwrap()
2018-04-21 11:02:49 -07:00
}
fn is_valid_ip(addr: IpAddr) -> bool {
!(addr.is_unspecified() || addr.is_multicast())
// || (addr.is_loopback() && !cfg_test))
// TODO: boot loopback in production networks
}
/// port must not be 0
/// ip must be specified and not mulitcast
/// loopback ip is only allowed in tests
pub fn is_valid_address(addr: &SocketAddr) -> bool {
(addr.port() != 0) && Self::is_valid_ip(addr.ip())
}
pub fn spy_node() -> (NodeInfo, UdpSocket) {
let (_, gossip_socket) = bind_in_range(FULLNODE_PORT_RANGE).unwrap();
let pubkey = Keypair::new().pubkey();
let daddr = socketaddr_any!();
let node = NodeInfo::new(pubkey, daddr, daddr, daddr, daddr, daddr);
(node, gossip_socket)
}
2018-04-21 11:02:49 -07:00
}
#[derive(Debug)]
pub struct Sockets {
pub gossip: UdpSocket,
pub requests: UdpSocket,
pub replicate: Vec<UdpSocket>,
pub transaction: Vec<UdpSocket>,
pub respond: UdpSocket,
pub broadcast: UdpSocket,
pub repair: UdpSocket,
pub retransmit: UdpSocket,
}
#[derive(Debug)]
pub struct Node {
pub info: NodeInfo,
pub sockets: Sockets,
}
2018-04-21 11:02:49 -07:00
impl Node {
pub fn new_localhost() -> Self {
2018-08-09 07:56:04 -07:00
let pubkey = Keypair::new().pubkey();
Self::new_localhost_with_pubkey(pubkey)
2018-07-09 15:53:49 -07:00
}
pub fn new_localhost_with_pubkey(pubkey: Pubkey) -> Self {
let transaction = UdpSocket::bind("127.0.0.1:0").unwrap();
let gossip = UdpSocket::bind("127.0.0.1:0").unwrap();
let replicate = UdpSocket::bind("127.0.0.1:0").unwrap();
let requests = UdpSocket::bind("127.0.0.1:0").unwrap();
let repair = UdpSocket::bind("127.0.0.1:0").unwrap();
let respond = UdpSocket::bind("0.0.0.0:0").unwrap();
let broadcast = UdpSocket::bind("0.0.0.0:0").unwrap();
let retransmit = UdpSocket::bind("0.0.0.0:0").unwrap();
let storage = UdpSocket::bind("0.0.0.0:0").unwrap();
let info = NodeInfo::new(
2018-04-28 00:31:20 -07:00
pubkey,
gossip.local_addr().unwrap(),
replicate.local_addr().unwrap(),
requests.local_addr().unwrap(),
transaction.local_addr().unwrap(),
storage.local_addr().unwrap(),
2018-04-28 00:31:20 -07:00
);
Node {
info,
sockets: Sockets {
gossip,
requests,
replicate: vec![replicate],
transaction: vec![transaction],
respond,
broadcast,
repair,
retransmit,
},
2018-04-21 11:02:49 -07:00
}
2018-04-26 13:48:42 -07:00
}
pub fn new_with_external_ip(pubkey: Pubkey, ncp: &SocketAddr) -> Node {
fn bind() -> (u16, UdpSocket) {
bind_in_range(FULLNODE_PORT_RANGE).expect("Failed to bind")
};
let (gossip_port, gossip) = if ncp.port() != 0 {
(ncp.port(), bind_to(ncp.port(), false).expect("ncp bind"))
} else {
bind()
};
let (replicate_port, replicate_sockets) =
multi_bind_in_range(FULLNODE_PORT_RANGE, 8).expect("tvu multi_bind");
let (requests_port, requests) = bind();
let (transaction_port, transaction_sockets) =
multi_bind_in_range(FULLNODE_PORT_RANGE, 32).expect("tpu multi_bind");
let (_, repair) = bind();
let (_, broadcast) = bind();
let (_, retransmit) = bind();
let (storage_port, _) = bind();
// Responses are sent from the same Udp port as requests are received
// from, in hopes that a NAT sitting in the middle will route the
// response Udp packet correctly back to the requester.
let respond = requests.try_clone().unwrap();
let info = NodeInfo::new(
pubkey,
SocketAddr::new(ncp.ip(), gossip_port),
SocketAddr::new(ncp.ip(), replicate_port),
SocketAddr::new(ncp.ip(), requests_port),
SocketAddr::new(ncp.ip(), transaction_port),
SocketAddr::new(ncp.ip(), storage_port),
);
trace!("new NodeInfo: {:?}", info);
Node {
info,
sockets: Sockets {
gossip,
requests,
replicate: replicate_sockets,
transaction: transaction_sockets,
respond,
broadcast,
repair,
retransmit,
},
}
}
}
2018-04-26 13:48:42 -07:00
fn report_time_spent(label: &str, time: &Duration, extra: &str) {
let count = duration_as_ms(time);
if count > 5 {
info!("{} took: {} ms {}", label, count, extra);
}
}
#[cfg(test)]
mod tests {
use bincode::serialize;
use budget_instruction::Vote;
2018-10-08 19:55:54 -07:00
use cluster_info::{
ClusterInfo, ClusterInfoError, Node, NodeInfo, Protocol, FULLNODE_PORT_RANGE,
GOSSIP_PURGE_MILLIS, GOSSIP_SLEEP_MILLIS, MIN_TABLE_SIZE,
};
2018-08-06 12:35:38 -07:00
use entry::Entry;
use hash::{hash, Hash};
2018-10-17 13:42:54 -07:00
use ledger::{get_tmp_ledger_path, LedgerWindow, LedgerWriter};
2018-06-23 16:38:49 -07:00
use logger;
use packet::SharedBlob;
use result::Error;
2018-09-26 16:55:36 -07:00
use signature::{Keypair, KeypairUtil};
use solana_program_interface::pubkey::Pubkey;
2018-08-06 12:35:38 -07:00
use std::fs::remove_dir_all;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
2018-06-03 19:59:17 -07:00
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel;
use std::sync::{Arc, RwLock};
2018-06-20 16:28:24 -07:00
use std::thread::sleep;
2018-06-03 19:59:17 -07:00
use std::time::Duration;
2018-08-09 12:31:34 -07:00
use window::default_window;
2018-04-26 13:48:42 -07:00
2018-04-21 11:02:49 -07:00
#[test]
fn insert_test() {
let mut d = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-04-21 11:02:49 -07:00
assert_eq!(d.version, 0);
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(d.clone()).unwrap();
assert_eq!(cluster_info.table[&d.id].version, 0);
assert!(!cluster_info.alive.contains_key(&d.id));
2018-04-21 11:02:49 -07:00
d.version = 2;
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d);
let liveness = cluster_info.alive[&d.id];
assert_eq!(cluster_info.table[&d.id].version, 2);
2018-04-21 11:02:49 -07:00
d.version = 1;
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d);
assert_eq!(cluster_info.table[&d.id].version, 2);
assert_eq!(liveness, cluster_info.alive[&d.id]);
// Ensure liveness will be updated for version 3
sleep(Duration::from_millis(1));
d.version = 3;
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d);
assert_eq!(cluster_info.table[&d.id].version, 3);
assert!(liveness < cluster_info.alive[&d.id]);
2018-04-21 11:02:49 -07:00
}
#[test]
fn test_new_vote() {
let d = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
assert_eq!(d.version, 0);
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(d.clone()).unwrap();
assert_eq!(cluster_info.table[&d.id].version, 0);
let leader = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1235"));
assert_ne!(d.id, leader.id);
assert_matches!(
2018-10-08 19:55:54 -07:00
cluster_info.new_vote(Hash::default()).err(),
Some(Error::ClusterInfoError(ClusterInfoError::NoLeader))
);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&leader);
assert_matches!(
2018-10-08 19:55:54 -07:00
cluster_info.new_vote(Hash::default()).err(),
Some(Error::ClusterInfoError(ClusterInfoError::NoLeader))
);
2018-10-08 19:55:54 -07:00
cluster_info.set_leader(leader.id);
assert_eq!(cluster_info.table[&d.id].version, 1);
let v = Vote {
version: 2, //version should increase when we vote
contact_info_version: 0,
};
2018-10-08 19:55:54 -07:00
let expected = (v, cluster_info.table[&leader.id].contact_info.tpu);
assert_eq!(cluster_info.new_vote(Hash::default()).unwrap(), expected);
}
#[test]
fn test_insert_vote() {
let d = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
assert_eq!(d.version, 0);
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(d.clone()).unwrap();
assert_eq!(cluster_info.table[&d.id].version, 0);
let vote_same_version = Vote {
version: d.version,
contact_info_version: 0,
};
2018-10-08 19:55:54 -07:00
cluster_info.insert_vote(&d.id, &vote_same_version, Hash::default());
assert_eq!(cluster_info.table[&d.id].version, 0);
let vote_new_version_new_addrs = Vote {
version: d.version + 1,
contact_info_version: 1,
};
2018-10-08 19:55:54 -07:00
cluster_info.insert_vote(&d.id, &vote_new_version_new_addrs, Hash::default());
//should be dropped since the address is newer then we know
2018-10-08 19:55:54 -07:00
assert_eq!(cluster_info.table[&d.id].version, 0);
let vote_new_version_old_addrs = Vote {
version: d.version + 1,
contact_info_version: 0,
};
2018-10-08 19:55:54 -07:00
cluster_info.insert_vote(&d.id, &vote_new_version_old_addrs, Hash::default());
//should be accepted, since the update is for the same address field as the one we know
2018-10-08 19:55:54 -07:00
assert_eq!(cluster_info.table[&d.id].version, 1);
}
fn sorted(ls: &Vec<(NodeInfo, u64)>) -> Vec<(NodeInfo, u64)> {
2018-05-30 10:04:11 -07:00
let mut copy: Vec<_> = ls.iter().cloned().collect();
copy.sort_by(|x, y| x.0.id.cmp(&y.0.id));
copy
}
2018-04-28 00:31:20 -07:00
#[test]
fn replicated_data_new_with_socketaddr_with_pubkey() {
2018-08-09 07:57:24 -07:00
let keypair = Keypair::new();
let d1 = NodeInfo::new_with_pubkey_socketaddr(
2018-08-09 07:57:24 -07:00
keypair.pubkey().clone(),
&socketaddr!("127.0.0.1:1234"),
2018-07-09 15:53:49 -07:00
);
2018-08-09 07:57:24 -07:00
assert_eq!(d1.id, keypair.pubkey());
assert_eq!(d1.contact_info.ncp, socketaddr!("127.0.0.1:1235"));
assert_eq!(d1.contact_info.tvu, socketaddr!("127.0.0.1:1236"));
assert_eq!(d1.contact_info.rpu, socketaddr!("127.0.0.1:1237"));
assert_eq!(d1.contact_info.tpu, socketaddr!("127.0.0.1:1234"));
}
#[test]
fn max_updates() {
let size = 1024 * 64 - 512;
let num = ClusterInfo::max_updates(size);
let msg = Protocol::ReceiveUpdates {
from: Default::default(),
max_update_index: 0,
nodes: vec![(NodeInfo::new_unspecified(), 0); num],
};
trace!("{} {} {}", serialize(&msg).unwrap().len(), size, num);
assert!(serialize(&msg).unwrap().len() <= size);
}
#[test]
fn update_test() {
let d1 = NodeInfo::new_localhost(Keypair::new().pubkey());
let d2 = NodeInfo::new_localhost(Keypair::new().pubkey());
let d3 = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(d1.clone()).expect("ClusterInfo::new");
let (key, ix, ups) = cluster_info.get_updates_since(0, 1);
assert_eq!(key, d1.id);
assert_eq!(ix, 1);
assert_eq!(ups.len(), 1);
assert_eq!(sorted(&ups), sorted(&vec![(d1.clone(), 0)]));
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d2);
let (key, ix, ups) = cluster_info.get_updates_since(0, 2);
assert_eq!(key, d1.id);
assert_eq!(ix, 2);
assert_eq!(ups.len(), 2);
assert_eq!(
sorted(&ups),
sorted(&vec![(d1.clone(), 0), (d2.clone(), 0)])
);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d3);
let (key, ix, ups) = cluster_info.get_updates_since(0, 3);
assert_eq!(key, d1.id);
assert_eq!(ix, 3);
assert_eq!(ups.len(), 3);
assert_eq!(
sorted(&ups),
sorted(&vec![(d1.clone(), 0), (d2.clone(), 0), (d3.clone(), 0)])
);
2018-10-08 19:55:54 -07:00
let mut cluster_info2 = ClusterInfo::new(d2.clone()).expect("ClusterInfo::new");
cluster_info2.apply_updates(key, ix, &ups);
2018-10-08 19:55:54 -07:00
assert_eq!(cluster_info2.table.values().len(), 3);
assert_eq!(
sorted(
&cluster_info2
.table
.values()
.map(|x| (x.clone(), 0))
.collect()
),
sorted(
&cluster_info
.table
.values()
.map(|x| (x.clone(), 0))
.collect()
)
);
let d4 = NodeInfo::new_entry_point(&socketaddr!("127.0.0.4:1234"));
2018-10-08 19:55:54 -07:00
cluster_info.insert(&d4);
let (_key, ix, ups) = cluster_info.get_updates_since(0, 3);
assert_eq!(
sorted(&ups),
sorted(&vec![(d2.clone(), 0), (d1.clone(), 0), (d3.clone(), 0)])
);
assert_eq!(ix, 3);
let (_key, ix, ups) = cluster_info.get_updates_since(0, 2);
assert_eq!(
sorted(&ups),
sorted(&vec![(d2.clone(), 0), (d1.clone(), 0)])
);
assert_eq!(ix, 2);
let (_key, ix, ups) = cluster_info.get_updates_since(0, 1);
assert_eq!(sorted(&ups), sorted(&vec![(d1.clone(), 0)]));
assert_eq!(ix, 1);
let (_key, ix, ups) = cluster_info.get_updates_since(1, 3);
assert_eq!(ups.len(), 2);
assert_eq!(ix, 3);
assert_eq!(sorted(&ups), sorted(&vec![(d2, 0), (d3, 0)]));
let (_key, ix, ups) = cluster_info.get_updates_since(3, 3);
assert_eq!(ups.len(), 0);
assert_eq!(ix, 0);
2018-04-28 00:31:20 -07:00
}
#[test]
fn window_index_request() {
let me = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(me).expect("ClusterInfo::new");
let rv = cluster_info.window_index_request(0);
assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)));
let ncp = socketaddr!([127, 0, 0, 1], 1234);
let nxt = NodeInfo::new(
2018-08-09 07:56:04 -07:00
Keypair::new().pubkey(),
ncp,
socketaddr!([127, 0, 0, 1], 1235),
socketaddr!([127, 0, 0, 1], 1236),
socketaddr!([127, 0, 0, 1], 1237),
socketaddr!([127, 0, 0, 1], 1238),
);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt);
let rv = cluster_info.window_index_request(0).unwrap();
assert_eq!(nxt.contact_info.ncp, ncp);
assert_eq!(rv.0, nxt.contact_info.ncp);
let ncp2 = socketaddr!([127, 0, 0, 2], 1234);
let nxt = NodeInfo::new(
2018-08-09 07:56:04 -07:00
Keypair::new().pubkey(),
ncp2,
socketaddr!([127, 0, 0, 1], 1235),
socketaddr!([127, 0, 0, 1], 1236),
socketaddr!([127, 0, 0, 1], 1237),
socketaddr!([127, 0, 0, 1], 1238),
);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt);
let mut one = false;
let mut two = false;
while !one || !two {
//this randomly picks an option, so eventually it should pick both
2018-10-08 19:55:54 -07:00
let rv = cluster_info.window_index_request(0).unwrap();
if rv.0 == ncp {
one = true;
}
if rv.0 == ncp2 {
two = true;
}
}
assert!(one && two);
}
2018-06-03 20:36:27 -07:00
#[test]
fn gossip_request_bad_addr() {
let me = NodeInfo::new(
2018-08-09 07:56:04 -07:00
Keypair::new().pubkey(),
socketaddr!("127.0.0.1:127"),
socketaddr!("127.0.0.1:127"),
socketaddr!("127.0.0.1:127"),
socketaddr!("127.0.0.1:127"),
socketaddr!("127.0.0.1:127"),
);
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(me).expect("ClusterInfo::new");
let nxt1 = NodeInfo::new_unspecified();
// Filter out unspecified addresses
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt1); //<--- attack!
let rv = cluster_info.gossip_request();
assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)));
let nxt2 = NodeInfo::new_multicast();
// Filter out multicast addresses
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2); //<--- attack!
let rv = cluster_info.gossip_request();
assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)));
}
2018-06-03 20:36:27 -07:00
/// test that gossip requests are eventually generated for all nodes
2018-06-03 19:59:17 -07:00
#[test]
fn gossip_request() {
let me = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(me.clone()).expect("ClusterInfo::new");
let rv = cluster_info.gossip_request();
assert_matches!(rv, Err(Error::ClusterInfoError(ClusterInfoError::NoPeers)));
let nxt1 = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-06-03 20:04:25 -07:00
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt1);
2018-06-03 20:04:25 -07:00
2018-10-08 19:55:54 -07:00
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt1.contact_info.ncp);
2018-06-03 20:04:25 -07:00
let nxt2 = NodeInfo::new_entry_point(&socketaddr!("127.0.0.3:1234"));
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2);
2018-06-03 20:36:27 -07:00
// check that the service works
// and that it eventually produces a request for both nodes
2018-06-03 19:59:17 -07:00
let (sender, reader) = channel();
let exit = Arc::new(AtomicBool::new(false));
2018-10-08 19:55:54 -07:00
let obj = Arc::new(RwLock::new(cluster_info));
let thread = ClusterInfo::gossip(obj, sender, exit.clone());
2018-06-03 20:04:25 -07:00
let mut one = false;
let mut two = false;
for _ in 0..30 {
//50% chance each try that we get a repeat
2018-06-03 20:31:09 -07:00
let mut rv = reader.recv_timeout(Duration::new(1, 0)).unwrap();
while let Ok(mut more) = reader.try_recv() {
rv.append(&mut more);
}
2018-06-03 20:04:25 -07:00
assert!(rv.len() > 0);
for i in rv.iter() {
if i.read().unwrap().meta.addr() == nxt1.contact_info.ncp {
2018-06-03 20:04:25 -07:00
one = true;
} else if i.read().unwrap().meta.addr() == nxt2.contact_info.ncp {
2018-06-03 20:04:25 -07:00
two = true;
} else {
//unexpected request
assert!(false);
}
}
if one && two {
break;
}
2018-06-03 19:59:17 -07:00
}
exit.store(true, Ordering::Relaxed);
thread.join().unwrap();
2018-06-03 20:04:25 -07:00
//created requests to both
assert!(one && two);
2018-06-03 19:59:17 -07:00
}
2018-06-03 20:31:09 -07:00
#[test]
fn purge_test() {
2018-07-09 15:53:49 -07:00
logger::setup();
let me = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(me.clone()).expect("ClusterInfo::new");
let nxt = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1234"));
2018-06-14 12:37:33 -07:00
assert_ne!(me.id, nxt.id);
2018-10-08 19:55:54 -07:00
cluster_info.set_leader(me.id);
cluster_info.insert(&nxt);
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt.contact_info.ncp);
2018-10-08 19:55:54 -07:00
let now = cluster_info.alive[&nxt.id];
cluster_info.purge(now);
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt.contact_info.ncp);
2018-10-08 19:55:54 -07:00
cluster_info.purge(now + GOSSIP_PURGE_MILLIS);
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt.contact_info.ncp);
2018-10-08 19:55:54 -07:00
cluster_info.purge(now + GOSSIP_PURGE_MILLIS + 1);
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt.contact_info.ncp);
2018-06-14 22:03:49 -07:00
let mut nxt2 = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1234"));
2018-06-14 22:03:49 -07:00
assert_ne!(me.id, nxt2.id);
assert_ne!(nxt.id, nxt2.id);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2);
while now == cluster_info.alive[&nxt2.id] {
2018-06-20 16:28:24 -07:00
sleep(Duration::from_millis(GOSSIP_SLEEP_MILLIS));
2018-08-25 19:50:04 -07:00
nxt2.version += 1;
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2);
2018-06-20 16:28:24 -07:00
}
2018-10-08 19:55:54 -07:00
let len = cluster_info.table.len() as u64;
2018-06-14 22:15:08 -07:00
assert!((MIN_TABLE_SIZE as u64) < len);
2018-10-08 19:55:54 -07:00
cluster_info.purge(now + GOSSIP_PURGE_MILLIS);
assert_eq!(len as usize, cluster_info.table.len());
2018-07-09 15:53:49 -07:00
trace!("purging");
2018-10-08 19:55:54 -07:00
cluster_info.purge(now + GOSSIP_PURGE_MILLIS + 1);
assert_eq!(len as usize - 1, cluster_info.table.len());
let rv = cluster_info.gossip_request().unwrap();
2018-07-09 17:55:11 -07:00
assert_eq!(rv.0, nxt.contact_info.ncp);
}
#[test]
fn purge_leader_test() {
logger::setup();
let me = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(me.clone()).expect("ClusterInfo::new");
let nxt = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1234"));
assert_ne!(me.id, nxt.id);
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt);
cluster_info.set_leader(nxt.id);
let now = cluster_info.alive[&nxt.id];
let mut nxt2 = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1234"));
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2);
while now == cluster_info.alive[&nxt2.id] {
sleep(Duration::from_millis(GOSSIP_SLEEP_MILLIS));
nxt2.version = nxt2.version + 1;
2018-10-08 19:55:54 -07:00
cluster_info.insert(&nxt2);
}
2018-10-08 19:55:54 -07:00
let len = cluster_info.table.len() as u64;
cluster_info.purge(now + GOSSIP_PURGE_MILLIS + 1);
assert_eq!(len as usize - 1, cluster_info.table.len());
assert_eq!(cluster_info.my_data().leader_id, Pubkey::default());
assert!(cluster_info.leader_data().is_none());
}
2018-06-03 20:36:27 -07:00
/// test window requests respond with the right blob, and do not overrun
2018-06-03 20:31:09 -07:00
#[test]
fn run_window_request() {
2018-08-06 12:35:38 -07:00
logger::setup();
let window = Arc::new(RwLock::new(default_window()));
let me = NodeInfo::new(
2018-08-09 07:56:04 -07:00
Keypair::new().pubkey(),
socketaddr!("127.0.0.1:1234"),
socketaddr!("127.0.0.1:1235"),
socketaddr!("127.0.0.1:1236"),
socketaddr!("127.0.0.1:1237"),
socketaddr!("127.0.0.1:1238"),
2018-06-03 20:31:09 -07:00
);
2018-10-08 19:55:54 -07:00
let rv =
ClusterInfo::run_window_request(&me, &socketaddr_any!(), &window, &mut None, &me, 0);
2018-06-03 20:31:09 -07:00
assert!(rv.is_none());
let out = SharedBlob::default();
out.write().unwrap().meta.size = 200;
2018-07-17 15:00:22 -07:00
window.write().unwrap()[0].data = Some(out);
2018-10-08 19:55:54 -07:00
let rv =
ClusterInfo::run_window_request(&me, &socketaddr_any!(), &window, &mut None, &me, 0);
2018-06-03 20:31:09 -07:00
assert!(rv.is_some());
let v = rv.unwrap();
//test we copied the blob
assert_eq!(v.read().unwrap().meta.size, 200);
2018-06-03 20:31:09 -07:00
let len = window.read().unwrap().len() as u64;
2018-10-08 19:55:54 -07:00
let rv =
ClusterInfo::run_window_request(&me, &socketaddr_any!(), &window, &mut None, &me, len);
assert!(rv.is_none());
2018-08-06 12:35:38 -07:00
fn tmp_ledger(name: &str) -> String {
2018-10-17 13:42:54 -07:00
let path = get_tmp_ledger_path(name);
2018-08-06 12:35:38 -07:00
let mut writer = LedgerWriter::open(&path, true).unwrap();
2018-08-06 12:35:38 -07:00
let zero = Hash::default();
let one = hash(&zero.as_ref());
writer
.write_entries(&vec![Entry::new_tick(0, &zero), Entry::new_tick(0, &one)].to_vec())
2018-08-06 12:35:38 -07:00
.unwrap();
path
}
let ledger_path = tmp_ledger("run_window_request");
let mut ledger_window = LedgerWindow::open(&ledger_path).unwrap();
2018-08-06 12:35:38 -07:00
2018-10-08 19:55:54 -07:00
let rv = ClusterInfo::run_window_request(
&me,
&socketaddr_any!(),
2018-08-06 12:35:38 -07:00
&window,
&mut Some(&mut ledger_window),
&me,
1,
);
assert!(rv.is_some());
remove_dir_all(ledger_path).unwrap();
}
/// test window requests respond with the right blob, and do not overrun
#[test]
fn run_window_request_with_backoff() {
let window = Arc::new(RwLock::new(default_window()));
let mut me = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
me.leader_id = me.id;
let mock_peer = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
// Simulate handling a repair request from mock_peer
2018-10-08 19:55:54 -07:00
let rv = ClusterInfo::run_window_request(
&mock_peer,
&socketaddr_any!(),
&window,
&mut None,
&me,
0,
);
2018-06-03 20:31:09 -07:00
assert!(rv.is_none());
let blob = SharedBlob::default();
2018-06-19 11:40:29 -07:00
let blob_size = 200;
blob.write().unwrap().meta.size = blob_size;
2018-07-17 15:00:22 -07:00
window.write().unwrap()[0].data = Some(blob);
2018-06-19 11:40:29 -07:00
let num_requests: u32 = 64;
for i in 0..num_requests {
2018-10-08 19:55:54 -07:00
let shared_blob = ClusterInfo::run_window_request(
&mock_peer,
&socketaddr_any!(),
&window,
&mut None,
&me,
0,
2018-08-06 12:35:38 -07:00
).unwrap();
let blob = shared_blob.read().unwrap();
// Test we copied the blob
2018-06-19 11:40:29 -07:00
assert_eq!(blob.meta.size, blob_size);
2018-06-19 11:40:29 -07:00
let id = if i == 0 || i.is_power_of_two() {
me.id
} else {
2018-06-19 11:40:29 -07:00
mock_peer.id
};
assert_eq!(blob.get_id().unwrap(), id);
}
2018-06-03 20:31:09 -07:00
}
#[test]
fn test_valid_last_ids() {
logger::setup();
let mut leader0 = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.2:1234"));
leader0.ledger_state.last_id = hash(b"0");
let mut leader1 = NodeInfo::new_multicast();
leader1.ledger_state.last_id = hash(b"1");
let mut leader2 =
NodeInfo::new_with_pubkey_socketaddr(Pubkey::default(), &socketaddr!("127.0.0.2:1234"));
leader2.ledger_state.last_id = hash(b"2");
// test that only valid tvu or tpu are retured as nodes
let mut leader3 = NodeInfo::new(
Keypair::new().pubkey(),
socketaddr!("127.0.0.1:1234"),
socketaddr_any!(),
socketaddr!("127.0.0.1:1236"),
socketaddr_any!(),
socketaddr_any!(),
);
leader3.ledger_state.last_id = hash(b"3");
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(leader0.clone()).expect("ClusterInfo::new");
cluster_info.insert(&leader1);
cluster_info.insert(&leader2);
cluster_info.insert(&leader3);
assert_eq!(
cluster_info.valid_last_ids(),
vec![leader0.ledger_state.last_id]
);
}
/// Validates the node that sent Protocol::ReceiveUpdates gets its
/// liveness updated, but not if the node sends Protocol::ReceiveUpdates
/// to itself.
#[test]
fn protocol_requestupdate_alive() {
logger::setup();
let window = Arc::new(RwLock::new(default_window()));
let node = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
let node_with_same_addr = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:1234"));
assert_ne!(node.id, node_with_same_addr.id);
let node_with_diff_addr = NodeInfo::new_with_socketaddr(&socketaddr!("127.0.0.1:4321"));
2018-10-08 19:55:54 -07:00
let cluster_info = ClusterInfo::new(node.clone()).expect("ClusterInfo::new");
assert_eq!(cluster_info.alive.len(), 0);
2018-10-08 19:55:54 -07:00
let obj = Arc::new(RwLock::new(cluster_info));
let request = Protocol::RequestUpdates(1, node.clone());
assert!(
2018-10-08 19:55:54 -07:00
ClusterInfo::handle_protocol(&obj, &node.contact_info.ncp, request, &window, &mut None,)
.is_none()
);
let request = Protocol::RequestUpdates(1, node_with_same_addr.clone());
assert!(
2018-10-08 19:55:54 -07:00
ClusterInfo::handle_protocol(&obj, &node.contact_info.ncp, request, &window, &mut None,)
.is_none()
);
let request = Protocol::RequestUpdates(1, node_with_diff_addr.clone());
2018-10-08 19:55:54 -07:00
ClusterInfo::handle_protocol(&obj, &node.contact_info.ncp, request, &window, &mut None);
let me = obj.write().unwrap();
// |node| and |node_with_same_addr| are ok to me in me.alive, should not be in me.alive, but
assert!(!me.alive.contains_key(&node.id));
// same addr might very well happen because of NAT
assert!(me.alive.contains_key(&node_with_same_addr.id));
// |node_with_diff_addr| should now be.
assert!(me.alive[&node_with_diff_addr.id] > 0);
}
2018-07-20 09:53:02 -07:00
#[test]
fn test_is_valid_address() {
assert!(cfg!(test));
let bad_address_port = socketaddr!("127.0.0.1:0");
2018-10-08 19:55:54 -07:00
assert!(!ClusterInfo::is_valid_address(&bad_address_port));
let bad_address_unspecified = socketaddr!(0, 1234);
2018-10-08 19:55:54 -07:00
assert!(!ClusterInfo::is_valid_address(&bad_address_unspecified));
let bad_address_multicast = socketaddr!([224, 254, 0, 0], 1234);
2018-10-08 19:55:54 -07:00
assert!(!ClusterInfo::is_valid_address(&bad_address_multicast));
let loopback = socketaddr!("127.0.0.1:1234");
2018-10-08 19:55:54 -07:00
assert!(ClusterInfo::is_valid_address(&loopback));
// assert!(!ClusterInfo::is_valid_ip_internal(loopback.ip(), false));
}
#[test]
fn test_default_leader() {
logger::setup();
let node_info = NodeInfo::new_localhost(Keypair::new().pubkey());
2018-10-08 19:55:54 -07:00
let mut cluster_info = ClusterInfo::new(node_info).unwrap();
let network_entry_point = NodeInfo::new_entry_point(&socketaddr!("127.0.0.1:1239"));
2018-10-08 19:55:54 -07:00
cluster_info.insert(&network_entry_point);
assert!(cluster_info.leader_data().is_none());
}
#[test]
fn new_with_external_ip_test_random() {
let ip = Ipv4Addr::from(0);
let node = Node::new_with_external_ip(Keypair::new().pubkey(), &socketaddr!(ip, 0));
assert_eq!(node.sockets.gossip.local_addr().unwrap().ip(), ip);
assert!(node.sockets.replicate.len() > 1);
for tx_socket in node.sockets.replicate.iter() {
assert_eq!(tx_socket.local_addr().unwrap().ip(), ip);
}
assert_eq!(node.sockets.requests.local_addr().unwrap().ip(), ip);
assert!(node.sockets.transaction.len() > 1);
for tx_socket in node.sockets.transaction.iter() {
assert_eq!(tx_socket.local_addr().unwrap().ip(), ip);
}
assert_eq!(node.sockets.repair.local_addr().unwrap().ip(), ip);
assert!(node.sockets.gossip.local_addr().unwrap().port() >= FULLNODE_PORT_RANGE.0);
assert!(node.sockets.gossip.local_addr().unwrap().port() < FULLNODE_PORT_RANGE.1);
let tx_port = node.sockets.replicate[0].local_addr().unwrap().port();
assert!(tx_port >= FULLNODE_PORT_RANGE.0);
assert!(tx_port < FULLNODE_PORT_RANGE.1);
for tx_socket in node.sockets.replicate.iter() {
assert_eq!(tx_socket.local_addr().unwrap().port(), tx_port);
}
assert!(node.sockets.requests.local_addr().unwrap().port() >= FULLNODE_PORT_RANGE.0);
assert!(node.sockets.requests.local_addr().unwrap().port() < FULLNODE_PORT_RANGE.1);
let tx_port = node.sockets.transaction[0].local_addr().unwrap().port();
assert!(tx_port >= FULLNODE_PORT_RANGE.0);
assert!(tx_port < FULLNODE_PORT_RANGE.1);
for tx_socket in node.sockets.transaction.iter() {
assert_eq!(tx_socket.local_addr().unwrap().port(), tx_port);
}
assert!(node.sockets.repair.local_addr().unwrap().port() >= FULLNODE_PORT_RANGE.0);
assert!(node.sockets.repair.local_addr().unwrap().port() < FULLNODE_PORT_RANGE.1);
}
#[test]
fn new_with_external_ip_test_gossip() {
let ip = IpAddr::V4(Ipv4Addr::from(0));
let node = Node::new_with_external_ip(Keypair::new().pubkey(), &socketaddr!(0, 8050));
assert_eq!(node.sockets.gossip.local_addr().unwrap().ip(), ip);
assert!(node.sockets.replicate.len() > 1);
for tx_socket in node.sockets.replicate.iter() {
assert_eq!(tx_socket.local_addr().unwrap().ip(), ip);
}
assert_eq!(node.sockets.requests.local_addr().unwrap().ip(), ip);
assert!(node.sockets.transaction.len() > 1);
for tx_socket in node.sockets.transaction.iter() {
assert_eq!(tx_socket.local_addr().unwrap().ip(), ip);
}
assert_eq!(node.sockets.repair.local_addr().unwrap().ip(), ip);
assert_eq!(node.sockets.gossip.local_addr().unwrap().port(), 8050);
let tx_port = node.sockets.replicate[0].local_addr().unwrap().port();
assert!(tx_port >= FULLNODE_PORT_RANGE.0);
assert!(tx_port < FULLNODE_PORT_RANGE.1);
for tx_socket in node.sockets.replicate.iter() {
assert_eq!(tx_socket.local_addr().unwrap().port(), tx_port);
}
assert!(node.sockets.requests.local_addr().unwrap().port() >= FULLNODE_PORT_RANGE.0);
assert!(node.sockets.requests.local_addr().unwrap().port() < FULLNODE_PORT_RANGE.1);
let tx_port = node.sockets.transaction[0].local_addr().unwrap().port();
assert!(tx_port >= FULLNODE_PORT_RANGE.0);
assert!(tx_port < FULLNODE_PORT_RANGE.1);
for tx_socket in node.sockets.transaction.iter() {
assert_eq!(tx_socket.local_addr().unwrap().port(), tx_port);
}
assert!(node.sockets.repair.local_addr().unwrap().port() >= FULLNODE_PORT_RANGE.0);
assert!(node.sockets.repair.local_addr().unwrap().port() < FULLNODE_PORT_RANGE.1);
}
2018-04-21 11:02:49 -07:00
}