use std::{fs, slice};
use std::collections::{HashMap, HashSet};
use std::fmt::{self, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{self, Duration, SystemTime};
use vapory_types::H512;
use log::{debug, warn};
use rand::seq::SliceRandom;
use tetsy_rlp::{DecoderError, Rlp, RlpStream};
use serde::{Deserialize, Serialize};
use serde_json;
use network::{AllowIP, Error, IpFilter};
use crate::{
discovery::{NodeEntry, TableUpdates},
ip_utils::*,
};
pub type NodeId = H512;
#[derive(Debug, Clone, PartialEq)]
pub struct NodeEndpoint {
pub address: SocketAddr,
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())),
}
}
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)
}))
&& !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() {
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);
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 })
}
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));
}
};
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);
}
pub fn is_valid_sync_node(&self) -> bool {
self.is_valid_discovery_node() && self.address.port() != 0
}
pub fn is_valid_discovery_node(&self) -> bool {
self.udp_port != 0 && match self.address {
SocketAddr::V4(a) => !a.ip().is_unspecified(),
SocketAddr::V6(a) => !a.ip().is_unspecified()
}
}
}
impl FromStr for NodeEndpoint {
type Err = Error;
fn from_str(s: &str) -> Result<NodeEndpoint, Error> {
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) => return Err(Error::AddressResolve(None.into())),
Err(_) => Err(Error::AddressParse)
}
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PeerType {
_Required,
Optional
}
#[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
}
}
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)]
pub struct Node {
pub id: NodeId,
pub endpoint: NodeEndpoint,
pub peer_type: PeerType,
pub last_contact: Option<NodeContact>,
}
impl Node {
pub fn new(id: NodeId, endpoint: NodeEndpoint) -> Node {
Node {
id,
endpoint,
peer_type: PeerType::Optional,
last_contact: None,
}
}
}
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)?;
} else {
write!(f, "enode://{:x}@{}", self.id, self.endpoint.address)?;
}
Ok(())
}
}
impl FromStr for Node {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (id, endpoint) = if s.len() > 136 && &s[0..8] == "enode://" && &s[136..137] == "@" {
(s[8..136].parse().map_err(|_| Error::InvalidNodeId)?, NodeEndpoint::from_str(&s[137..])?)
}
else {
(NodeId::default(), NodeEndpoint::from_str(s)?)
};
Ok(Node {
id,
endpoint,
peer_type: PeerType::Optional,
last_contact: None,
})
}
}
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)
}
}
pub const MAX_NODES_IN_TABLE: usize = 4096;
const MAX_NODES_IN_FILE: usize = 1024;
const NODES_FILE: &str = "nodes.json";
pub struct NodeTable {
nodes: HashMap<NodeId, Node>,
ordered_ids: Vec<NodeId>,
useless_nodes: HashSet<NodeId>,
path: Option<String>,
}
impl NodeTable {
pub fn new(path: Option<String>) -> NodeTable {
let nodes = NodeTable::load(path.clone());
let ordered_ids = NodeTable::make_ordered_entries(&nodes).iter().map(|m| m.id).collect();
NodeTable {
path,
nodes,
useless_nodes: HashSet::new(),
ordered_ids
}
}
pub fn add_node(&mut self, mut node: Node) {
node.last_contact = self.nodes.get(&node.id).and_then(|n| n.last_contact);
let id = node.id;
if self.ordered_ids.len() == MAX_NODES_IN_TABLE {
self.nodes.remove(&self.ordered_ids.pop().expect("ordered_ids is not empty; qed"));
};
let index = self.get_index_to_insert(node.last_contact);
if self.nodes.insert(node.id, node).is_none() {
self.ordered_ids.insert(index, id);
};
}
fn get_index_to_insert(&self, last_contact: Option<NodeContact>) -> usize {
let len = self.ordered_ids.len();
let mut index = len;
match last_contact {
Some(NodeContact::Success(last_contact_time)) => {
if let Some(i) = self.ordered_ids.iter().position(|&item| {
match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact {
Some(NodeContact::Success(last)) => last < last_contact_time,
_ => true
}
}) { index = i; };
},
None => {
if let Some(i) = self.ordered_ids.iter().position(|&item| {
match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact {
Some(NodeContact::Success(_)) => false,
_ => true
}
}) { index = i; };
},
Some(NodeContact::Failure(last_contact_time)) => {
if let Some(i) = self.ordered_ids.iter().rev().position(|&item| {
match self.nodes.get(&item).expect("nodes and ordered_ids do not get out of sync; qed").last_contact {
Some(NodeContact::Failure(last)) => last < last_contact_time,
_ => true
}
}) { index = len - i; };
}
};
index
}
fn ordered(&self) -> Vec<&Node> {
Vec::from_iter(
self.ordered_ids
.iter()
.filter(|id| !self.useless_nodes.contains(&id))
.map(|id| self.nodes.get(&id).expect("nodes and ordered_ids do not get out of sync; qed"))
)
}
fn make_ordered_entries(node_table: &HashMap<NodeId, Node>) -> Vec<&Node> {
let mut success = Vec::new();
let mut failures = Vec::new();
let mut unknown = Vec::new();
let nodes = node_table.values();
for node in nodes {
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");
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");
a.time().cmp(&b.time())
});
let mut rng = rand::thread_rng();
unknown.shuffle(&mut rng);
success.append(&mut unknown);
success.append(&mut failures);
success
}
pub fn nodes(&self, filter: &IpFilter) -> Vec<NodeId> {
self.ordered().iter()
.filter(|n| n.endpoint.is_allowed(&filter))
.map(|n| n.id)
.collect()
}
pub fn entries(&self) -> Vec<NodeEntry> {
self.ordered().iter().map(|n| NodeEntry {
endpoint: n.endpoint.clone(),
id: n.id,
}).collect()
}
pub fn get(&self, id: &NodeId) -> Option<&Node> {
self.nodes.get(id)
}
pub fn contains(&self, id: &NodeId) -> bool {
self.nodes.contains_key(id)
}
pub fn update(&mut self, mut update: TableUpdates, reserved: &HashSet<NodeId>) {
for (_, node) in update.added.drain() {
let mut add = false;
{
let entry = self.nodes.entry(node.id).or_insert_with(|| {
add = true;
Node::new(node.id, node.endpoint.clone())
});
entry.endpoint = node.endpoint;
}
if add {
if self.ordered_ids.len() == MAX_NODES_IN_TABLE {
self.nodes.remove(&self.ordered_ids.pop().expect("ordered_ids is not empty; qed"));
};
let index = self.get_index_to_insert(None);
self.ordered_ids.insert(index, node.id);
};
};
for r in update.removed {
if !reserved.contains(&r) {
self.ordered_ids.iter().position(|&i| r == i).map(|p| self.ordered_ids.remove(p));
self.nodes.remove(&r);
}
}
}
fn update_ordered_ids(&mut self, id: &NodeId, last_contact: Option<NodeContact>) {
if let Some(node) = self.nodes.get_mut(id) {
node.last_contact = last_contact;
}
if let Some(pos) = self.ordered_ids.iter().position(|i| id == i) {
self.ordered_ids.remove(pos);
let index = self.get_index_to_insert(last_contact);
self.ordered_ids.insert(index, *id);
}
}
pub fn note_failure(&mut self, id: &NodeId) {
self.update_ordered_ids(id, Some(NodeContact::failure()));
}
pub fn note_success(&mut self, id: &NodeId) {
self.update_ordered_ids(id, Some(NodeContact::success()));
}
pub fn mark_as_useless(&mut self, id: &NodeId) {
self.useless_nodes.insert(id.clone());
}
pub fn clear_useless(&mut self) {
self.useless_nodes.clear();
}
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_IN_FILE)
.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);
}
},
Err(e) => {
warn!(target: "network", "Error creating node table file: {:?}", e);
}
}
}
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()
},
}
}
}
impl Drop for NodeTable {
fn drop(&mut self) {
self.save();
}
}
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
}
}
}
}
#[cfg(test)]
mod tests {
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use vapory_types::H512;
use ipnetwork::IpNetwork;
use tempdir::TempDir;
use assert_matches::assert_matches;
use super::*;
#[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")
};
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(), Error::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(), Error::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(), Error::AddressParse);
let endpoint = NodeEndpoint::from_str("123.123.123.123:123");
assert!(endpoint.is_ok())
}
#[test]
fn node_parse() {
assert!(validate_node_url("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").is_none());
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);
}
#[test]
fn node_parse_fails_for_invalid_urls() {
let node = Node::from_str("foo");
assert!(node.is_err());
assert_matches!(node.unwrap_err(), Error::AddressParse);
let node = Node::from_str("enode://foo@bar");
assert!(node.is_err());
assert_matches!(node.unwrap_err(), Error::AddressParse);
}
#[test]
fn table_last_contact_order() {
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();
let id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
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();
let mut table = NodeTable::new(None);
assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0);
assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 0);
assert_eq!(table.get_index_to_insert(None), 0);
table.add_node(node1);
sleep(Duration::from_micros(1));
assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0);
assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 1);
assert_eq!(table.get_index_to_insert(None), 0);
table.add_node(node2);
sleep(Duration::from_micros(1));
assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0);
assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 2);
assert_eq!(table.get_index_to_insert(None), 0);
table.add_node(node3);
sleep(Duration::from_micros(1));
table.add_node(node4);
sleep(Duration::from_micros(1));
table.add_node(node5);
sleep(Duration::from_micros(1));
table.add_node(node6);
sleep(Duration::from_micros(1));
table.note_failure(&id1);
sleep(Duration::from_micros(1));
let time_in_between = SystemTime::now();
sleep(Duration::from_micros(1));
table.note_failure(&id2);
sleep(Duration::from_micros(1));
assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0);
assert_eq!(table.get_index_to_insert(Some(NodeContact::failure())), 6);
assert_eq!(table.get_index_to_insert(Some(NodeContact::Failure(time_in_between))), 5);
assert_eq!(table.get_index_to_insert(Some(NodeContact::Failure(time::UNIX_EPOCH))), 4);
assert_eq!(table.get_index_to_insert(None), 0);
table.note_success(&id5);
sleep(Duration::from_micros(1));
table.note_success(&id3);
sleep(Duration::from_micros(1));
assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time::UNIX_EPOCH))), 2);
assert_eq!(table.get_index_to_insert(None), 2);
let time_in_between = SystemTime::now();
sleep(Duration::from_micros(1));
table.note_success(&id4);
sleep(Duration::from_micros(1));
assert_eq!(table.get_index_to_insert(Some(NodeContact::success())), 0);
assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time_in_between))), 1);
assert_eq!(table.get_index_to_insert(Some(NodeContact::Success(time::UNIX_EPOCH))), 3);
assert_eq!(table.get_index_to_insert(None), 3);
let r = table.nodes(&IpFilter::default());
assert_eq!(r[0][..], id4[..]);
assert_eq!(r[1][..], id3[..]);
assert!(
r[2][..] == id5[..] && r[3][..] == id6[..] ||
r[2][..] == id6[..] && r[3][..] == id5[..]
);
assert_eq!(r[4][..], id1[..]);
assert_eq!(r[5][..], id2[..]);
}
#[test]
fn table_save_load() {
let tempdir = TempDir::new("").unwrap();
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 id1 = H512::from_str("a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id2 = H512::from_str("b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
let id3 = H512::from_str("c979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c").unwrap();
{
let mut table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned()));
table.add_node(node1);
table.add_node(node2);
table.add_node(node3);
table.note_success(&id2);
table.note_failure(&id3);
}
{
let table = NodeTable::new(Some(tempdir.path().to_str().unwrap().to_owned()));
let r = table.nodes(&IpFilter::default());
assert_eq!(r[0][..], id2[..]);
assert_eq!(r[1][..], id1[..]);
assert_eq!(r[2][..], id3[..]);
}
}
#[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));
}
}