openethereum/util/network-devp2p/src/node_table.rs

728 lines
23 KiB
Rust
Raw Normal View History

// Copyright 2015-2018 Parity Technologies (UK) Ltd.
2016-02-12 09:52:32 +01:00
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
use discovery::{TableUpdates, NodeEntry};
use ethereum_types::H512;
use ip_utils::*;
use network::{Error, ErrorKind, AllowIP, IpFilter};
use rlp::{Rlp, RlpStream, DecoderError};
use serde_json;
use std::collections::{HashMap, HashSet};
use std::fmt::{self, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::net::{SocketAddr, ToSocketAddrs, SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr};
use std::path::PathBuf;
use std::str::FromStr;
use std::{fs, slice};
use std::time::{self, Duration, SystemTime};
use rand::{self, Rng};
2016-02-12 09:52:32 +01:00
2016-02-14 01:03:48 +01:00
/// Node public key
pub type NodeId = H512;
Backports to 2.0.0-beta (#9094) * parity-version: betalize 2.0 * Multiple improvements to discovery ping handling (#8771) * discovery: Only add nodes to routing table after receiving pong. Previously the discovery algorithm would add nodes to the routing table before confirming that the endpoint is participating in the protocol. This now tracks in-flight pings and adds to the routing table only after receiving a response. * discovery: Refactor packet creation into its own function. This function is useful inside unit tests. * discovery: Additional testing for new add_node behavior. * discovery: Track expiration of pings to non-yet-in-bucket nodes. Now that we may ping nodes before adding to a k-bucket, the timeout tracking must be separate from BucketEntry. * discovery: Verify echo hash on pong packets. Stores packet hash with in-flight requests and matches with pong response. * discovery: Track timeouts on FIND_NODE requests. * discovery: Retry failed pings with exponential backoff. UDP packets may get dropped, so instead of immediately booting nodes that fail to respond to a ping, retry 4 times with exponential backoff. * !fixup Use slice instead of Vec for request_backoff. * Add separate database directory for light client (#8927) (#9064) * Add seperate default DB path for light client (#8927) * Improve readability * Revert "Replace `std::env::home_dir` with `dirs::home_dir` (#9077)" (#9097) * Revert "Replace `std::env::home_dir` with `dirs::home_dir` (#9077)" This reverts commit 7e779327ebad5a60e068f39c60bcf944f3c99114. * Restore some of the changes * Update parity-common * Offload cull to IoWorker. (#9099) * Fix work-notify. (#9104) * Update hidapi, fixes #7542 (#9108) * docker: add cmake dependency (#9111) * Update light client hardcoded headers (#9098) * Insert Kovan hardcoded headers until #7690241 * Insert Kovan hardcoded headers until block 7690241 * Insert Ropsten hardcoded headers until #3612673 * Insert Mainnet hardcoded headers until block 5941249 * Make sure to produce full blocks. (#9115) * Insert ETC (classic) hardcoded headers until block #6170625 (#9121) * fix verification in ethcore-sync collect_blocks (#9135) * Completely remove all dapps struct from rpc (#9107) * Completely remove all dapps struct from rpc * Remove unused pub use * `evm bench` fix broken dependencies (#9134) * `evm bench` use valid dependencies Benchmarks of the `evm` used stale versions of a couple a crates that this commit fixes! * fix warnings * Update snapcraft.yaml (#9132)
2018-07-17 13:47:14 +02:00
#[derive(Debug, Clone, PartialEq)]
2016-02-14 01:03:48 +01:00
/// Node address info
pub struct NodeEndpoint {
/// IP(V4 or V6) address
pub address: SocketAddr,
/// Conneciton port.
pub udp_port: u16
}
impl NodeEndpoint {
pub fn udp_address(&self) -> SocketAddr {
match self.address {
SocketAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(*a.ip(), self.udp_port)),
SocketAddr::V6(a) => SocketAddr::V6(SocketAddrV6::new(*a.ip(), self.udp_port, a.flowinfo(), a.scope_id())),
2016-02-14 01:03:48 +01:00
}
}
pub fn is_allowed(&self, filter: &IpFilter) -> bool {
(self.is_allowed_by_predefined(&filter.predefined) || filter.custom_allow.iter().any(|ipnet| {
self.address.ip().is_within(ipnet)
2017-09-05 11:14:28 +02:00
}))
&& !filter.custom_block.iter().any(|ipnet| {
self.address.ip().is_within(ipnet)
})
}
pub fn is_allowed_by_predefined(&self, filter: &AllowIP) -> bool {
match filter {
AllowIP::All => true,
AllowIP::Private => self.address.ip().is_usable_private(),
AllowIP::Public => self.address.ip().is_usable_public(),
AllowIP::None => false,
}
}
pub fn from_rlp(rlp: &Rlp) -> Result<Self, DecoderError> {
let tcp_port = rlp.val_at::<u16>(2)?;
let udp_port = rlp.val_at::<u16>(1)?;
let addr_bytes = rlp.at(0)?.data()?;
let address = match addr_bytes.len() {
2016-02-14 01:03:48 +01:00
4 => Ok(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(addr_bytes[0], addr_bytes[1], addr_bytes[2], addr_bytes[3]), tcp_port))),
16 => unsafe {
let o: *const u16 = addr_bytes.as_ptr() as *const u16;
let o = slice::from_raw_parts(o, 8);
2016-02-14 01:03:48 +01:00
Ok(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7]), tcp_port, 0, 0)))
},
_ => Err(DecoderError::RlpInconsistentLengthAndData)
}?;
Ok(NodeEndpoint { address, udp_port })
2016-02-14 01:03:48 +01:00
}
pub fn to_rlp(&self, rlp: &mut RlpStream) {
match self.address {
SocketAddr::V4(a) => {
rlp.append(&(&a.ip().octets()[..]));
}
SocketAddr::V6(a) => unsafe {
let o: *const u8 = a.ip().segments().as_ptr() as *const u8;
rlp.append(&slice::from_raw_parts(o, 16));
2016-02-14 01:03:48 +01:00
}
};
rlp.append(&self.udp_port);
rlp.append(&self.address.port());
}
pub fn to_rlp_list(&self, rlp: &mut RlpStream) {
rlp.begin_list(3);
self.to_rlp(rlp);
}
Echo back the message hash of a ping in the pong request (#8042) * Echo back the message hash of a ping in the pong request * Fixed broken link in README (#8012) * Fixed broken link in README * Updated wiki link * [hardware wallet] sleeping -> pollling (#8018) * Use polling, enable missing doc warnings & docs * make try_connect_polling() a free function * `Client` refactoring (#7038) * Improves `BestBlock` comment * Improves `TraceDB` comment * Improves `journaldb::Algorithm` comment. Probably the whole enum should be renamed to `Strategy` or something alike. * Comments some of the `Client`'s fields * Deglobs client imports * Fixes comments * Extracts `import_lock` to `Importer` struct * Extracts `verifier` to `Importer` struct * Extracts `block_queue` to `Importer` struct * Extracts `miner` to `Importer` struct * Extracts `ancient_verifier` to `Importer` struct * Extracts `rng` to `Importer` struct * Extracts `import_old_block` to `Importer` struct * Adds `Nonce` trait * Adds `Balance` trait * Adds `ChainInfo` trait * Fixes imports for tests using `chain_info` method * Adds `BlockInfo` trait * Adds more `ChainInfo` imports * Adds `BlockInfo` imports * Adds `ReopenBlock` trait * Adds `PrepareOpenBlock` trait * Fixes import in tests * Adds `CallContract` trait * Fixes imports in tests using `call_contract` method * Adds `TransactionInfo` trait * Adds `RegistryInfo` trait * Fixes imports in tests using `registry_address` method * Adds `ScheduleInfo` trait * Adds `ImportSealedBlock` trait * Fixes imports in test using `import_sealed_block` method * Adds `BroadcastProposalBlock` trait * Migrates `Miner` to static dispatch * Fixes tests * Moves `calculate_enacted_retracted` to `Importer` * Moves import-related methods to `Importer` * Removes redundant `import_old_block` wrapper * Extracts `import_block*` into separate trait * Fixes tests * Handles `Pending` in `LightFetch` * Handles `Pending` in filters * Handles `Pending` in `ParityClient` * Handles `Pending` in `EthClient` * Removes `BlockId::Pending`, partly refactors dependent code * Adds `StateInfo` trait * Exports `StateOrBlock` and `BlockChain` types from `client` module * Refactors `balance` RPC using generic API * Refactors `storage_at` RPC using generic API * Makes `MinerService::pending_state`'s return type dynamic * Adds `StateOrBlock` and `BlockChain` types * Adds impl of `client::BlockChain` for `Client` * Exports `StateInfo` trait from `client` module * Missing `self` use To be fixed up to "Adds impl of `client::BlockChain` for `Client`" * Adds `number_to_id` and refactors dependent RPC methods * Refactors `code_at` using generic API * Adds `StateClient` trait * Refactors RPC to use `StateClient` trait * Reverts `client::BlockChain` trait stuff, refactors methods to accept `StateOrBlock` * Refactors TestClient * Adds helper function `block_number_to_id` * Uses `block_number_to_id` instead of local function * Handles `Pending` in `list_accounts` and `list_storage_keys` * Attempt to use associated types for state instead of trait objects * Simplifies `state_at_beginning` * Extracts `call` and `call_many` into separate trait * Refactors `build_last_hashes` to accept reference * Exports `Call` type from the module * Refactors `call` and `call_many` to accept state and header * Exports `state_at` in `StateClient` * Exports `pending_block_header` from `MinerService` * Refactors RPC `call` method using new API * Adds missing parentheses * Refactors `parity::call` to use new call API * Update .gitlab-ci.yml fix gitlab lint * Fixes error handling * Refactors `traces::call` and `call_many` to use new call API * Refactors `call_contract` * Refactors `block_header` * Refactors internal RPC method `block` * Moves `estimate_gas` to `Call` trait, refactors parameters * Refactors `estimate_gas` in RPC * Refactors `uncle` * Refactors RPC `transaction` * Covers missing branches * Makes it all compile, fixes compiler grumbles * Adds casts in `blockchain` module * Fixes `PendingBlock` tests, work on `MinerService` * Adds test stubs for StateClient and EngineInfo * Makes `state_db` public * Adds missing impls for `TestBlockChainClient` * Adds trait documentation * Adds missing docs to the `state_db` module * Fixes trivial compilation errors * Moves `code_hash` method to a `BlockInfo` trait * Refactors `Verifier` to be generic over client * Refactors `TransactionFilter` to be generic over client * Refactors `Miner` and `Client` to reflect changes in verifier and txfilter API * Moves `ServiceTransactionChecker` back to `ethcore` * Fixes trait bounds in `Miner` API * Fixes `Client` * Fixes lifetime bound in `FullFamilyParams` * Adds comments to `FullFamilyParams` * Fixes imports in `ethcore` * Fixes BlockNumber handling in `code_at` and `replay_block_transactions` * fix compile issues * First step to redundant trait merge * Fixes compilation error in RPC tests * Adds mock `State` as a stub for `TestClient` * Handles `StateOrBlock::State` in `TestBlockChainClient::balance` * Fixes `transaction_count` RPC * Fixes `transaction_count` * Moves `service_transaction.json` to the `contracts` subfolder * Fixes compilation errors in tests * Refactors client to use `AccountData` * Refactors client to use `BlockChain` * Refactors miner to use aggregate traits * Adds `SealedBlockImporter` trait * Refactors miner to use `SealedBlockImporter` trait * Removes unused imports * Simplifies `RegistryInfo::registry_address` * Fixes indentation * Removes commented out trait bound * Bump master to 1.11.0 (#8021) * Bump master to 1.11.0 * Bump price-info * Bump mac installer version * Fix gitlab builds * Add MCIP-6 Byzyantium transition to Musicoin spec (#7841) * Add test chain spec for musicoin byzantium testnet * Add MCIP-6 Byzyantium transition to Musicoin spec * Update mcip6_byz.json * ethcore: update musicoin byzantium block number * ethcore: update musicoin byzantium block number * ethcore: update musicoin bootnodes * Update musicoin.json * Update musicoin.json * More bootnodes. * prelude to the block module cleanup (#8025) * prelude to block cleanup * fixed tests * fix cache & snapcraft CI build (#8052) after successful testing it is necessary to port in a ```beta``` and ```stable``` * Update refs to shell (#8051) * Abstract devp2p (#8048) * Rename ethcore-network to ethcore-network-devp2p * Fix typo * Extract generic traits into util/network * Simplify util/network * Fix devp2p tests * Remove old feature * Fix RPC tests * Change port because testing environment didn't like those ports
2018-03-12 11:06:48 +01:00
/// Validates that the port is not 0 and address IP is specified
2016-02-14 01:03:48 +01:00
pub fn is_valid(&self) -> bool {
self.udp_port != 0 && self.address.port() != 0 &&
match self.address {
SocketAddr::V4(a) => !a.ip().is_unspecified(),
SocketAddr::V6(a) => !a.ip().is_unspecified()
2016-02-14 01:03:48 +01:00
}
}
}
impl FromStr for NodeEndpoint {
2017-11-13 14:37:08 +01:00
type Err = Error;
2016-02-14 01:03:48 +01:00
/// Create endpoint from string. Performs name resolution if given a host name.
2017-11-13 14:37:08 +01:00
fn from_str(s: &str) -> Result<NodeEndpoint, Error> {
2016-02-14 01:03:48 +01:00
let address = s.to_socket_addrs().map(|mut i| i.next());
match address {
Ok(Some(a)) => Ok(NodeEndpoint {
address: a,
udp_port: a.port()
}),
Ok(None) => bail!(ErrorKind::AddressResolve(None)),
Err(_) => Err(ErrorKind::AddressParse.into()) // always an io::Error of InvalidInput kind
2016-02-14 01:03:48 +01:00
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
2016-02-14 01:03:48 +01:00
pub enum PeerType {
_Required,
Optional
}
/// A type for representing an interaction (contact) with a node at a given time
/// that was either a success or a failure.
#[derive(Clone, Copy, Debug)]
pub enum NodeContact {
Success(SystemTime),
Failure(SystemTime),
}
impl NodeContact {
fn success() -> NodeContact {
NodeContact::Success(SystemTime::now())
}
fn failure() -> NodeContact {
NodeContact::Failure(SystemTime::now())
}
fn time(&self) -> SystemTime {
match *self {
NodeContact::Success(t) | NodeContact::Failure(t) => t
}
}
/// Filters and old contact, returning `None` if it happened longer than a
/// week ago.
fn recent(&self) -> Option<&NodeContact> {
let t = self.time();
if let Ok(d) = t.elapsed() {
if d < Duration::from_secs(60 * 60 * 24 * 7) {
return Some(self);
}
}
None
}
}
#[derive(Debug)]
2016-02-14 01:03:48 +01:00
pub struct Node {
pub id: NodeId,
pub endpoint: NodeEndpoint,
pub peer_type: PeerType,
pub last_contact: Option<NodeContact>,
2016-02-14 01:03:48 +01:00
}
impl Node {
pub fn new(id: NodeId, endpoint: NodeEndpoint) -> Node {
Node {
id,
endpoint,
2016-02-14 01:03:48 +01:00
peer_type: PeerType::Optional,
last_contact: None,
2016-02-14 01:03:48 +01:00
}
}
}
2016-02-15 11:54:38 +01:00
impl Display for Node {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
if self.endpoint.udp_port != self.endpoint.address.port() {
write!(f, "enode://{:x}@{}+{}", self.id, self.endpoint.address, self.endpoint.udp_port)?;
2016-02-15 11:54:38 +01:00
} else {
write!(f, "enode://{:x}@{}", self.id, self.endpoint.address)?;
2016-02-15 11:54:38 +01:00
}
Ok(())
}
}
2016-02-14 01:03:48 +01:00
impl FromStr for Node {
2017-11-13 14:37:08 +01:00
type Err = Error;
2016-02-14 01:03:48 +01:00
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (id, endpoint) = if s.len() > 136 && &s[0..8] == "enode://" && &s[136..137] == "@" {
2017-11-13 14:37:08 +01:00
(s[8..136].parse().map_err(|_| ErrorKind::InvalidNodeId)?, NodeEndpoint::from_str(&s[137..])?)
2016-02-14 01:03:48 +01:00
}
else {
(NodeId::new(), NodeEndpoint::from_str(s)?)
2016-02-14 01:03:48 +01:00
};
Ok(Node {
id,
endpoint,
2016-02-14 01:03:48 +01:00
peer_type: PeerType::Optional,
last_contact: None,
2016-02-14 01:03:48 +01:00
})
}
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Node {}
impl Hash for Node {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.id.hash(state)
}
}
const MAX_NODES: usize = 1024;
const NODES_FILE: &str = "nodes.json";
2016-02-14 01:03:48 +01:00
/// Node table backed by disk file.
2016-02-12 09:52:32 +01:00
pub struct NodeTable {
2016-02-15 11:54:38 +01:00
nodes: HashMap<NodeId, Node>,
useless_nodes: HashSet<NodeId>,
2016-02-15 11:54:38 +01:00
path: Option<String>,
2016-02-12 09:52:32 +01:00
}
impl NodeTable {
2016-02-15 11:54:38 +01:00
pub fn new(path: Option<String>) -> NodeTable {
2016-02-12 09:52:32 +01:00
NodeTable {
2016-02-15 11:54:38 +01:00
path: path.clone(),
nodes: NodeTable::load(path),
useless_nodes: HashSet::new(),
2016-02-12 09:52:32 +01:00
}
}
2016-02-14 01:03:48 +01:00
/// Add a node to table
2016-02-15 19:54:27 +01:00
pub fn add_node(&mut self, mut node: Node) {
// preserve node last_contact
node.last_contact = self.nodes.get(&node.id).and_then(|n| n.last_contact);
self.nodes.insert(node.id, node);
2016-02-12 09:52:32 +01:00
}
/// Returns a list of ordered nodes according to their most recent contact
/// and filtering useless nodes. The algorithm for creating the sorted nodes
/// is:
/// - Contacts that aren't recent (older than 1 week) are discarded
/// - (1) Nodes with a successful contact are ordered (most recent success first)
/// - (2) Nodes with unknown contact (older than 1 week or new nodes) are randomly shuffled
/// - (3) Nodes with a failed contact are ordered (oldest failure first)
/// - The final result is the concatenation of (1), (2) and (3)
fn ordered_entries(&self) -> Vec<&Node> {
let mut success = Vec::new();
let mut failures = Vec::new();
let mut unknown = Vec::new();
let nodes = self.nodes.values()
.filter(|n| !self.useless_nodes.contains(&n.id));
for node in nodes {
// discard contact points older that aren't recent
match node.last_contact.as_ref().and_then(|c| c.recent()) {
Some(&NodeContact::Success(_)) => {
success.push(node);
},
Some(&NodeContact::Failure(_)) => {
failures.push(node);
},
None => {
unknown.push(node);
},
}
}
success.sort_by(|a, b| {
let a = a.last_contact.expect("vector only contains values with defined last_contact; qed");
let b = b.last_contact.expect("vector only contains values with defined last_contact; qed");
// inverse ordering, most recent successes come first
b.time().cmp(&a.time())
});
failures.sort_by(|a, b| {
let a = a.last_contact.expect("vector only contains values with defined last_contact; qed");
let b = b.last_contact.expect("vector only contains values with defined last_contact; qed");
// normal ordering, most distant failures come first
a.time().cmp(&b.time())
});
rand::thread_rng().shuffle(&mut unknown);
success.append(&mut unknown);
success.append(&mut failures);
success
}
/// Returns node ids sorted by failure percentage, for nodes with the same failure percentage the absolute number of
/// failures is considered.
pub fn nodes(&self, filter: &IpFilter) -> Vec<NodeId> {
self.ordered_entries().iter()
.filter(|n| n.endpoint.is_allowed(&filter))
.map(|n| n.id)
.collect()
2016-02-12 09:52:32 +01:00
}
/// Ordered list of all entries by failure percentage, for nodes with the same failure percentage the absolute
/// number of failures is considered.
pub fn entries(&self) -> Vec<NodeEntry> {
self.ordered_entries().iter().map(|n| NodeEntry {
endpoint: n.endpoint.clone(),
id: n.id,
}).collect()
2016-02-15 20:28:27 +01:00
}
2016-02-14 01:03:48 +01:00
/// Get particular node
2016-02-12 09:52:32 +01:00
pub fn get_mut(&mut self, id: &NodeId) -> Option<&mut Node> {
self.nodes.get_mut(id)
}
/// Check if a node exists in the table.
pub fn contains(&self, id: &NodeId) -> bool {
self.nodes.contains_key(id)
}
2016-02-14 01:03:48 +01:00
/// Apply table changes coming from discovery
pub fn update(&mut self, mut update: TableUpdates, reserved: &HashSet<NodeId>) {
2016-02-13 22:57:39 +01:00
for (_, node) in update.added.drain() {
let entry = self.nodes.entry(node.id).or_insert_with(|| Node::new(node.id, node.endpoint.clone()));
2016-02-13 22:57:39 +01:00
entry.endpoint = node.endpoint;
}
2016-02-12 09:52:32 +01:00
for r in update.removed {
if !reserved.contains(&r) {
self.nodes.remove(&r);
}
2016-02-12 09:52:32 +01:00
}
}
/// Set last contact as failure for a node
2016-02-14 01:03:48 +01:00
pub fn note_failure(&mut self, id: &NodeId) {
if let Some(node) = self.nodes.get_mut(id) {
node.last_contact = Some(NodeContact::failure());
}
}
/// Set last contact as success for a node
pub fn note_success(&mut self, id: &NodeId) {
if let Some(node) = self.nodes.get_mut(id) {
node.last_contact = Some(NodeContact::success());
2016-02-14 01:03:48 +01:00
}
}
2016-02-15 11:54:38 +01:00
/// Mark as useless, no further attempts to connect until next call to `clear_useless`.
pub fn mark_as_useless(&mut self, id: &NodeId) {
self.useless_nodes.insert(id.clone());
}
/// Atempt to connect to useless nodes again.
pub fn clear_useless(&mut self) {
self.useless_nodes.clear();
}
/// Save the nodes.json file.
pub fn save(&self) {
let mut path = match self.path {
Some(ref path) => PathBuf::from(path),
None => return,
};
if let Err(e) = fs::create_dir_all(&path) {
warn!(target: "network", "Error creating node table directory: {:?}", e);
return;
}
path.push(NODES_FILE);
let node_ids = self.nodes(&IpFilter::default());
let nodes = node_ids.into_iter()
.map(|id| self.nodes.get(&id).expect("self.nodes() only returns node IDs from self.nodes"))
.take(MAX_NODES)
.map(|node| node.clone())
.map(Into::into)
.collect();
let table = json::NodeTable { nodes };
match fs::File::create(&path) {
Ok(file) => {
if let Err(e) = serde_json::to_writer_pretty(file, &table) {
warn!(target: "network", "Error writing node table file: {:?}", e);
2016-02-15 11:54:38 +01:00
}
},
Err(e) => {
warn!(target: "network", "Error creating node table file: {:?}", e);
2016-02-15 11:54:38 +01:00
}
}
}
fn load(path: Option<String>) -> HashMap<NodeId, Node> {
let path = match path {
Some(path) => PathBuf::from(path).join(NODES_FILE),
None => return Default::default(),
};
let file = match fs::File::open(&path) {
Ok(file) => file,
Err(e) => {
debug!(target: "network", "Error opening node table file: {:?}", e);
return Default::default();
},
};
let res: Result<json::NodeTable, _> = serde_json::from_reader(file);
match res {
Ok(table) => {
table.nodes.into_iter()
.filter_map(|n| n.into_node())
.map(|n| (n.id, n))
.collect()
},
Err(e) => {
warn!(target: "network", "Error reading node table file: {:?}", e);
Default::default()
},
2016-02-15 11:54:38 +01:00
}
}
}
impl Drop for NodeTable {
fn drop(&mut self) {
self.save();
}
2016-02-14 01:03:48 +01:00
}
2016-02-19 16:34:31 +01:00
/// Check if node url is valid
2017-11-13 14:37:08 +01:00
pub fn validate_node_url(url: &str) -> Option<Error> {
match Node::from_str(url) {
Ok(_) => None,
Err(e) => Some(e)
}
}
mod json {
use super::*;
#[derive(Serialize, Deserialize)]
pub struct NodeTable {
pub nodes: Vec<Node>,
}
#[derive(Serialize, Deserialize)]
pub enum NodeContact {
#[serde(rename = "success")]
Success(u64),
#[serde(rename = "failure")]
Failure(u64),
}
impl NodeContact {
pub fn into_node_contact(self) -> super::NodeContact {
match self {
NodeContact::Success(s) => super::NodeContact::Success(
time::UNIX_EPOCH + Duration::from_secs(s)
),
NodeContact::Failure(s) => super::NodeContact::Failure(
time::UNIX_EPOCH + Duration::from_secs(s)
),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct Node {
pub url: String,
pub last_contact: Option<NodeContact>,
}
impl Node {
pub fn into_node(self) -> Option<super::Node> {
match super::Node::from_str(&self.url) {
Ok(mut node) => {
node.last_contact = self.last_contact.map(|c| c.into_node_contact());
Some(node)
},
_ => None,
}
}
}
impl<'a> From<&'a super::Node> for Node {
fn from(node: &'a super::Node) -> Self {
let last_contact = node.last_contact.and_then(|c| {
match c {
super::NodeContact::Success(t) =>
t.duration_since(time::UNIX_EPOCH).ok().map(|d| NodeContact::Success(d.as_secs())),
super::NodeContact::Failure(t) =>
t.duration_since(time::UNIX_EPOCH).ok().map(|d| NodeContact::Failure(d.as_secs())),
}
});
Node {
url: format!("{}", node),
last_contact
}
}
}
}
2016-02-14 01:03:48 +01:00
#[cfg(test)]
mod tests {
use super::*;
2016-11-28 17:05:37 +01:00
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use ethereum_types::H512;
2016-02-14 01:03:48 +01:00
use std::str::FromStr;
use tempdir::TempDir;
use ipnetwork::IpNetwork;
2016-02-14 01:03:48 +01:00
#[test]
fn endpoint_parse() {
let endpoint = NodeEndpoint::from_str("123.99.55.44:7770");
assert!(endpoint.is_ok());
let v4 = match endpoint.unwrap().address {
SocketAddr::V4(v4address) => v4address,
_ => panic!("should be v4 address")
2016-02-14 01:03:48 +01:00
};
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(123, 99, 55, 44), 7770), v4);
}
#[test]
fn endpoint_parse_empty_ip_string_returns_error() {
let endpoint = NodeEndpoint::from_str("");
assert!(endpoint.is_err());
assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse);
}
#[test]
fn endpoint_parse_invalid_ip_string_returns_error() {
let endpoint = NodeEndpoint::from_str("beef");
assert!(endpoint.is_err());
assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse);
}
#[test]
fn endpoint_parse_valid_ip_without_port_returns_error() {
let endpoint = NodeEndpoint::from_str("123.123.123.123");
assert!(endpoint.is_err());
assert_matches!(endpoint.unwrap_err().kind(), &ErrorKind::AddressParse);
let endpoint = NodeEndpoint::from_str("123.123.123.123:123");
assert!(endpoint.is_ok())
}
2016-02-14 01:03:48 +01:00
#[test]
fn node_parse() {
2017-11-06 07:51:26 +01:00
assert!(validate_node_url("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").is_none());
2016-02-14 01:03:48 +01:00
let node = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770");
assert!(node.is_ok());
let node = node.unwrap();
let v4 = match node.endpoint.address {
SocketAddr::V4(v4address) => v4address,
_ => panic!("should ve v4 address")
};
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(22, 99, 55, 44), 7770), v4);
assert_eq!(
H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap(),
node.id);
}
2016-02-15 11:54:38 +01:00
#[test]
fn node_parse_fails_for_invalid_urls() {
let node = Node::from_str("foo");
assert!(node.is_err());
assert_matches!(node.unwrap_err().kind(), &ErrorKind::AddressParse);
let node = Node::from_str("enode://foo@bar");
assert!(node.is_err());
assert_matches!(node.unwrap_err().kind(), &ErrorKind::AddressParse);
}
2016-02-15 11:54:38 +01:00
#[test]
fn table_last_contact_order() {
2016-02-15 14:39:56 +01:00
let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node3 = Node::from_str("enode://c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node4 = Node::from_str("enode://d979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node5 = Node::from_str("enode://e979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node6 = Node::from_str("enode://f979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
2016-02-15 11:54:38 +01:00
let id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
2016-02-15 14:39:56 +01:00
let id3 = H512::from_str("c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id4 = H512::from_str("d979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id5 = H512::from_str("e979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id6 = H512::from_str("f979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
2016-02-15 11:54:38 +01:00
let mut table = NodeTable::new(None);
2016-02-15 14:39:56 +01:00
table.add_node(node1);
table.add_node(node2);
table.add_node(node3);
table.add_node(node4);
table.add_node(node5);
table.add_node(node6);
2016-02-15 14:39:56 +01:00
// failures - nodes 1 & 2
2016-02-15 14:39:56 +01:00
table.note_failure(&id1);
table.note_failure(&id2);
// success - nodes 3 & 4
table.note_success(&id3);
table.note_success(&id4);
// success - node 5 (old contact)
table.get_mut(&id5).unwrap().last_contact = Some(NodeContact::Success(time::UNIX_EPOCH));
// unknown - node 6
Backports for stable 2.0.7 (#9648) * parity-version: bump stable to 2.0.7 * HF in POA Sokol (2018-09-19) (#9607) https://github.com/poanetwork/poa-chain-spec/pull/86 * fix failing node-table tests on mac os, closes #9632 (#9633) * fix(light_fetch): avoid race with BlockNumber::Latest (#9665) * CI: Remove unnecessary pipes (#9681) * ci: reduce gitlab pipelines significantly * ci: build pipeline for PR * ci: remove dead weight * ci: remove github release script * ci: remove forever broken aura tests * ci: add random stuff to the end of the pipes * ci: add wind and mac to the end of the pipe * ci: remove snap artifacts * ci: (re)move dockerfiles * ci: clarify job names * ci: add cargo audit job * ci: make audit script executable * ci: ignore snap and docker files for rust check * ci: simplify audit script * ci: rename misc to optional * ci: add publish script to releaseable branches * ci: more verbose cp command for windows build * ci: fix weird binary checksum logic in push script * ci: fix regex in push script for windows * ci: simplify gitlab caching * docs: align README with ci changes * ci: specify default cargo target dir * ci: print verbose environment * ci: proper naming of scripts * ci: restore docker files * ci: use docker hub file * ci: use cargo home instead of cargo target dir * ci: touch random rust file to trigger real builds * ci: set cargo target dir for audit script * ci: remove temp file * ci: don't export the cargo target dir in the audit script * ci: fix windows unbound variable * docs: fix gitlab badge path * rename deprecated gitlab ci variables https://docs.gitlab.com/ee/ci/variables/#9-0-renaming * ci: fix git compare for nightly builds * test: skip c++ example for all platforms but linux * ci: add random rust file to trigger tests * ci: remove random rust file * disable cpp lib test for mac, win and beta (#9686) * cleanup ci merge * parity: bump clib * ci: fix tests * ci: disable c++ example * Docker: run as parity user (#9689) * CI: Skip docs job for nightly (#9693) * ci: force-tag wiki changes * ci: force-tag wiki changes * ci: skip docs job for master and nightly * ci: revert docs job checking for nightly tag * ci: exclude docs job from nightly builds in gitlab script * fix (light/provider) : Make `read_only executions` read-only (#9591) * `ExecutionsRequest` from light-clients as read-only This changes so all `ExecutionRequests` from light-clients are executed as read-only which the `virtual``flag == true ensures. This boost up the current transaction to always succeed Note, this only affects `eth_estimateGas` and `eth_call` AFAIK. * grumbles(revert renaming) : TransactionProof * grumbles(trace) : remove incorrect trace * grumbles(state/prove_tx) : explicit `virt` Remove the boolean flag to determine that a `state::prove_transaction` whether it should be executed in a virtual context or not. Because of that also rename the function to `state::prove_transction_virtual` to make more clear * ethcore: fix detection of major import (#9552) * sync: set state to idle after sync is completed * sync: refactor sync reset * parity: revert clib bump and fix tests * Fix path to parity.h (#9274) * Fix path to parity.h * Fix other paths as well * ethcore-io retries failed work steal (#9651) * ethcore-io uses newer version of crossbeam && retries failed work steal * ethcore-io non-mio service uses newer crossbeam
2018-10-10 17:33:03 +02:00
// nodes are also ordered according to their addition time
//
// nanosecond precision lost since mac os x high sierra update so let's not compare their order
// https://github.com/paritytech/parity-ethereum/issues/9632
let r = table.nodes(&IpFilter::default());
Backports for stable 2.0.7 (#9648) * parity-version: bump stable to 2.0.7 * HF in POA Sokol (2018-09-19) (#9607) https://github.com/poanetwork/poa-chain-spec/pull/86 * fix failing node-table tests on mac os, closes #9632 (#9633) * fix(light_fetch): avoid race with BlockNumber::Latest (#9665) * CI: Remove unnecessary pipes (#9681) * ci: reduce gitlab pipelines significantly * ci: build pipeline for PR * ci: remove dead weight * ci: remove github release script * ci: remove forever broken aura tests * ci: add random stuff to the end of the pipes * ci: add wind and mac to the end of the pipe * ci: remove snap artifacts * ci: (re)move dockerfiles * ci: clarify job names * ci: add cargo audit job * ci: make audit script executable * ci: ignore snap and docker files for rust check * ci: simplify audit script * ci: rename misc to optional * ci: add publish script to releaseable branches * ci: more verbose cp command for windows build * ci: fix weird binary checksum logic in push script * ci: fix regex in push script for windows * ci: simplify gitlab caching * docs: align README with ci changes * ci: specify default cargo target dir * ci: print verbose environment * ci: proper naming of scripts * ci: restore docker files * ci: use docker hub file * ci: use cargo home instead of cargo target dir * ci: touch random rust file to trigger real builds * ci: set cargo target dir for audit script * ci: remove temp file * ci: don't export the cargo target dir in the audit script * ci: fix windows unbound variable * docs: fix gitlab badge path * rename deprecated gitlab ci variables https://docs.gitlab.com/ee/ci/variables/#9-0-renaming * ci: fix git compare for nightly builds * test: skip c++ example for all platforms but linux * ci: add random rust file to trigger tests * ci: remove random rust file * disable cpp lib test for mac, win and beta (#9686) * cleanup ci merge * parity: bump clib * ci: fix tests * ci: disable c++ example * Docker: run as parity user (#9689) * CI: Skip docs job for nightly (#9693) * ci: force-tag wiki changes * ci: force-tag wiki changes * ci: skip docs job for master and nightly * ci: revert docs job checking for nightly tag * ci: exclude docs job from nightly builds in gitlab script * fix (light/provider) : Make `read_only executions` read-only (#9591) * `ExecutionsRequest` from light-clients as read-only This changes so all `ExecutionRequests` from light-clients are executed as read-only which the `virtual``flag == true ensures. This boost up the current transaction to always succeed Note, this only affects `eth_estimateGas` and `eth_call` AFAIK. * grumbles(revert renaming) : TransactionProof * grumbles(trace) : remove incorrect trace * grumbles(state/prove_tx) : explicit `virt` Remove the boolean flag to determine that a `state::prove_transaction` whether it should be executed in a virtual context or not. Because of that also rename the function to `state::prove_transction_virtual` to make more clear * ethcore: fix detection of major import (#9552) * sync: set state to idle after sync is completed * sync: refactor sync reset * parity: revert clib bump and fix tests * Fix path to parity.h (#9274) * Fix path to parity.h * Fix other paths as well * ethcore-io retries failed work steal (#9651) * ethcore-io uses newer version of crossbeam && retries failed work steal * ethcore-io non-mio service uses newer crossbeam
2018-10-10 17:33:03 +02:00
// most recent success
assert!(
(r[0] == id4 && r[1] == id3) ||
(r[0] == id3 && r[1] == id4)
);
// unknown (old contacts and new nodes), randomly shuffled
assert!(
Backports for stable 2.0.7 (#9648) * parity-version: bump stable to 2.0.7 * HF in POA Sokol (2018-09-19) (#9607) https://github.com/poanetwork/poa-chain-spec/pull/86 * fix failing node-table tests on mac os, closes #9632 (#9633) * fix(light_fetch): avoid race with BlockNumber::Latest (#9665) * CI: Remove unnecessary pipes (#9681) * ci: reduce gitlab pipelines significantly * ci: build pipeline for PR * ci: remove dead weight * ci: remove github release script * ci: remove forever broken aura tests * ci: add random stuff to the end of the pipes * ci: add wind and mac to the end of the pipe * ci: remove snap artifacts * ci: (re)move dockerfiles * ci: clarify job names * ci: add cargo audit job * ci: make audit script executable * ci: ignore snap and docker files for rust check * ci: simplify audit script * ci: rename misc to optional * ci: add publish script to releaseable branches * ci: more verbose cp command for windows build * ci: fix weird binary checksum logic in push script * ci: fix regex in push script for windows * ci: simplify gitlab caching * docs: align README with ci changes * ci: specify default cargo target dir * ci: print verbose environment * ci: proper naming of scripts * ci: restore docker files * ci: use docker hub file * ci: use cargo home instead of cargo target dir * ci: touch random rust file to trigger real builds * ci: set cargo target dir for audit script * ci: remove temp file * ci: don't export the cargo target dir in the audit script * ci: fix windows unbound variable * docs: fix gitlab badge path * rename deprecated gitlab ci variables https://docs.gitlab.com/ee/ci/variables/#9-0-renaming * ci: fix git compare for nightly builds * test: skip c++ example for all platforms but linux * ci: add random rust file to trigger tests * ci: remove random rust file * disable cpp lib test for mac, win and beta (#9686) * cleanup ci merge * parity: bump clib * ci: fix tests * ci: disable c++ example * Docker: run as parity user (#9689) * CI: Skip docs job for nightly (#9693) * ci: force-tag wiki changes * ci: force-tag wiki changes * ci: skip docs job for master and nightly * ci: revert docs job checking for nightly tag * ci: exclude docs job from nightly builds in gitlab script * fix (light/provider) : Make `read_only executions` read-only (#9591) * `ExecutionsRequest` from light-clients as read-only This changes so all `ExecutionRequests` from light-clients are executed as read-only which the `virtual``flag == true ensures. This boost up the current transaction to always succeed Note, this only affects `eth_estimateGas` and `eth_call` AFAIK. * grumbles(revert renaming) : TransactionProof * grumbles(trace) : remove incorrect trace * grumbles(state/prove_tx) : explicit `virt` Remove the boolean flag to determine that a `state::prove_transaction` whether it should be executed in a virtual context or not. Because of that also rename the function to `state::prove_transction_virtual` to make more clear * ethcore: fix detection of major import (#9552) * sync: set state to idle after sync is completed * sync: refactor sync reset * parity: revert clib bump and fix tests * Fix path to parity.h (#9274) * Fix path to parity.h * Fix other paths as well * ethcore-io retries failed work steal (#9651) * ethcore-io uses newer version of crossbeam && retries failed work steal * ethcore-io non-mio service uses newer crossbeam
2018-10-10 17:33:03 +02:00
(r[2] == id5 && r[3] == id6) ||
(r[2] == id6 && r[3] == id5)
);
Backports for stable 2.0.7 (#9648) * parity-version: bump stable to 2.0.7 * HF in POA Sokol (2018-09-19) (#9607) https://github.com/poanetwork/poa-chain-spec/pull/86 * fix failing node-table tests on mac os, closes #9632 (#9633) * fix(light_fetch): avoid race with BlockNumber::Latest (#9665) * CI: Remove unnecessary pipes (#9681) * ci: reduce gitlab pipelines significantly * ci: build pipeline for PR * ci: remove dead weight * ci: remove github release script * ci: remove forever broken aura tests * ci: add random stuff to the end of the pipes * ci: add wind and mac to the end of the pipe * ci: remove snap artifacts * ci: (re)move dockerfiles * ci: clarify job names * ci: add cargo audit job * ci: make audit script executable * ci: ignore snap and docker files for rust check * ci: simplify audit script * ci: rename misc to optional * ci: add publish script to releaseable branches * ci: more verbose cp command for windows build * ci: fix weird binary checksum logic in push script * ci: fix regex in push script for windows * ci: simplify gitlab caching * docs: align README with ci changes * ci: specify default cargo target dir * ci: print verbose environment * ci: proper naming of scripts * ci: restore docker files * ci: use docker hub file * ci: use cargo home instead of cargo target dir * ci: touch random rust file to trigger real builds * ci: set cargo target dir for audit script * ci: remove temp file * ci: don't export the cargo target dir in the audit script * ci: fix windows unbound variable * docs: fix gitlab badge path * rename deprecated gitlab ci variables https://docs.gitlab.com/ee/ci/variables/#9-0-renaming * ci: fix git compare for nightly builds * test: skip c++ example for all platforms but linux * ci: add random rust file to trigger tests * ci: remove random rust file * disable cpp lib test for mac, win and beta (#9686) * cleanup ci merge * parity: bump clib * ci: fix tests * ci: disable c++ example * Docker: run as parity user (#9689) * CI: Skip docs job for nightly (#9693) * ci: force-tag wiki changes * ci: force-tag wiki changes * ci: skip docs job for master and nightly * ci: revert docs job checking for nightly tag * ci: exclude docs job from nightly builds in gitlab script * fix (light/provider) : Make `read_only executions` read-only (#9591) * `ExecutionsRequest` from light-clients as read-only This changes so all `ExecutionRequests` from light-clients are executed as read-only which the `virtual``flag == true ensures. This boost up the current transaction to always succeed Note, this only affects `eth_estimateGas` and `eth_call` AFAIK. * grumbles(revert renaming) : TransactionProof * grumbles(trace) : remove incorrect trace * grumbles(state/prove_tx) : explicit `virt` Remove the boolean flag to determine that a `state::prove_transaction` whether it should be executed in a virtual context or not. Because of that also rename the function to `state::prove_transction_virtual` to make more clear * ethcore: fix detection of major import (#9552) * sync: set state to idle after sync is completed * sync: refactor sync reset * parity: revert clib bump and fix tests * Fix path to parity.h (#9274) * Fix path to parity.h * Fix other paths as well * ethcore-io retries failed work steal (#9651) * ethcore-io uses newer version of crossbeam && retries failed work steal * ethcore-io non-mio service uses newer crossbeam
2018-10-10 17:33:03 +02:00
// oldest failure
assert!(
(r[4] == id1 && r[5] == id2) ||
(r[4] == id2 && r[5] == id1)
);
2016-02-15 14:39:56 +01:00
}
#[test]
fn table_save_load() {
let tempdir = TempDir::new("").unwrap();
2016-02-15 14:39:56 +01:00
let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
let node3 = Node::from_str("enode://c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
2016-02-15 14:39:56 +01:00
let id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id3 = H512::from_str("c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
2016-02-15 14:39:56 +01:00
{
let mut table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned()));
2016-02-15 14:39:56 +01:00
table.add_node(node1);
table.add_node(node2);
table.add_node(node3);
table.note_success(&id2);
table.note_failure(&id3);
2016-02-15 14:39:56 +01:00
}
{
let table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned()));
let r = table.nodes(&IpFilter::default());
assert_eq!(r[0][..], id2[..]); // latest success
assert_eq!(r[1][..], id1[..]); // unknown
assert_eq!(r[2][..], id3[..]); // oldest failure
2016-02-15 14:39:56 +01:00
}
2016-02-15 11:54:38 +01:00
}
#[test]
fn custom_allow() {
let filter = IpFilter {
predefined: AllowIP::None,
custom_allow: vec![IpNetwork::from_str(&"10.0.0.0/8").unwrap(), IpNetwork::from_str(&"1.0.0.0/8").unwrap()],
custom_block: vec![],
};
assert!(!NodeEndpoint::from_str("123.99.55.44:7770").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("10.0.0.1:7770").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("1.0.0.55:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_block() {
let filter = IpFilter {
predefined: AllowIP::All,
custom_allow: vec![],
custom_block: vec![IpNetwork::from_str(&"10.0.0.0/8").unwrap(), IpNetwork::from_str(&"1.0.0.0/8").unwrap()],
};
assert!(NodeEndpoint::from_str("123.99.55.44:7770").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("10.0.0.1:7770").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("1.0.0.55:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_allow_ipv6() {
let filter = IpFilter {
predefined: AllowIP::None,
custom_allow: vec![IpNetwork::from_str(&"fc00::/8").unwrap()],
custom_block: vec![],
};
assert!(NodeEndpoint::from_str("[fc00::]:5550").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("[fd00::]:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_block_ipv6() {
let filter = IpFilter {
predefined: AllowIP::All,
custom_allow: vec![],
custom_block: vec![IpNetwork::from_str(&"fc00::/8").unwrap()],
};
assert!(!NodeEndpoint::from_str("[fc00::]:5550").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("[fd00::]:5550").unwrap().is_allowed(&filter));
}
2016-02-12 09:52:32 +01:00
}