#![allow(unused)]
use std::cell::Cell;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::net::SocketAddr;
use bip_util::bt::NodeId;
use bip_util::test;
use chrono::{Duration, DateTime, UTC};
const MAX_LAST_SEEN_MINS: i64 = 15;
const MAX_REFRESH_REQUESTS: usize = 2;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Ord, PartialOrd)]
pub enum NodeStatus {
Bad,
Questionable,
Good,
}
pub struct Node {
id: NodeId,
addr: SocketAddr,
last_request: Cell<Option<DateTime<UTC>>>,
last_response: Cell<Option<DateTime<UTC>>>,
refresh_requests: Cell<usize>,
}
impl Node {
pub fn as_good(id: NodeId, addr: SocketAddr) -> Node {
Node {
id: id,
addr: addr,
last_response: Cell::new(Some(UTC::now())),
last_request: Cell::new(None),
refresh_requests: Cell::new(0),
}
}
pub fn as_questionable(id: NodeId, addr: SocketAddr) -> Node {
let last_response_offset = Duration::minutes(MAX_LAST_SEEN_MINS);
let last_response = test::travel_into_past(last_response_offset);
Node {
id: id,
addr: addr,
last_response: Cell::new(Some(last_response)),
last_request: Cell::new(None),
refresh_requests: Cell::new(0),
}
}
pub fn as_bad(id: NodeId, addr: SocketAddr) -> Node {
Node {
id: id,
addr: addr,
last_response: Cell::new(None),
last_request: Cell::new(None),
refresh_requests: Cell::new(0),
}
}
pub fn local_request(&self) {
if self.status() != NodeStatus::Good {
let num_requests = self.refresh_requests.get() + 1;
self.refresh_requests.set(num_requests);
}
}
pub fn remote_request(&self) {
self.last_request.set(Some(UTC::now()));
}
pub fn remote_response(&self) {
self.last_response.set(Some(UTC::now()));
self.refresh_requests.set(0);
}
pub fn id(&self) -> NodeId {
self.id
}
pub fn addr(&self) -> SocketAddr {
self.addr
}
pub fn encode(&self) -> [u8; 26] {
let mut encoded = [0u8; 26];
{
let mut encoded_iter = encoded.iter_mut();
for (src, dst) in self.id.as_ref().iter().zip(encoded_iter.by_ref()) {
*dst = *src;
}
match self.addr {
SocketAddr::V4(v4) => {
for (src, dst) in v4.ip().octets().iter().zip(encoded_iter.by_ref()) {
*dst = *src;
}
}
_ => panic!("bip_dht: Cannot encode a SocketAddrV6..."),
}
}
let port = self.addr.port();
encoded[24] = (port >> 8) as u8;
encoded[25] = port as u8;
encoded
}
pub fn status(&self) -> NodeStatus {
let curr_time = UTC::now();
match recently_responded(self, curr_time) {
NodeStatus::Good => return NodeStatus::Good,
NodeStatus::Bad => return NodeStatus::Bad,
NodeStatus::Questionable => (),
};
recently_requested(self, curr_time)
}
}
impl Eq for Node {}
impl PartialEq<Node> for Node {
fn eq(&self, other: &Node) -> bool {
self.id == other.id && self.addr == other.addr
}
}
impl Hash for Node {
fn hash<H>(&self, state: &mut H)
where H: Hasher
{
self.id.hash(state);
self.addr.hash(state);
}
}
impl Clone for Node {
fn clone(&self) -> Node {
Node {
id: self.id,
addr: self.addr,
last_response: self.last_response.clone(),
last_request: self.last_request.clone(),
refresh_requests: self.refresh_requests.clone(),
}
}
}
impl Debug for Node {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
f.write_fmt(format_args!("Node{{ id: {:?}, addr: {:?}, last_request: {:?}, \
last_response: {:?}, refresh_requests: {:?} }}",
self.id,
self.addr,
self.last_request.get(),
self.last_response.get(),
self.refresh_requests.get()))
}
}
fn recently_responded(node: &Node, curr_time: DateTime<UTC>) -> NodeStatus {
let since_response = match node.last_response.get() {
Some(response_time) => curr_time - response_time,
None => return NodeStatus::Bad,
};
let max_last_response = Duration::minutes(MAX_LAST_SEEN_MINS);
if since_response < max_last_response {
NodeStatus::Good
} else {
NodeStatus::Questionable
}
}
fn recently_requested(node: &Node, curr_time: DateTime<UTC>) -> NodeStatus {
let max_last_request = Duration::minutes(MAX_LAST_SEEN_MINS);
if let Some(request_time) = node.last_request.get() {
let since_request = curr_time - request_time;
if since_request < max_last_request {
return NodeStatus::Good;
}
}
if node.refresh_requests.get() < MAX_REFRESH_REQUESTS {
NodeStatus::Questionable
} else {
NodeStatus::Bad
}
}
#[cfg(test)]
mod tests {
use std::iter;
use std::net::{Ipv4Addr, SocketAddrV4, SocketAddr};
use bip_util::bt::NodeId;
use bip_util::test as bip_test;
use chrono::Duration;
use routing::node::{Node, NodeStatus};
#[test]
fn positive_encode_node() {
let node_id = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
let ip_addr = [127, 0, 0, 1];
let port = 6881;
let v4_ip = Ipv4Addr::new(ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
let sock_addr = SocketAddr::V4(SocketAddrV4::new(v4_ip, port));
let node = Node::as_good(node_id.into(), sock_addr);
let encoded_node = node.encode();
let port_bytes = [(port >> 8) as u8, port as u8];
for (expected, actual) in node_id.iter()
.chain(ip_addr.iter())
.chain(port_bytes.iter())
.zip(encoded_node.iter()) {
assert_eq!(expected, actual);
}
}
#[test]
fn positive_as_bad() {
let node = Node::as_bad(bip_test::dummy_node_id(), bip_test::dummy_socket_addr_v4());
assert_eq!(node.status(), NodeStatus::Bad);
}
#[test]
fn positive_as_questionable() {
let node = Node::as_questionable(bip_test::dummy_node_id(),
bip_test::dummy_socket_addr_v4());
assert_eq!(node.status(), NodeStatus::Questionable);
}
#[test]
fn positive_as_good() {
let node = Node::as_good(bip_test::dummy_node_id(), bip_test::dummy_socket_addr_v4());
assert_eq!(node.status(), NodeStatus::Good);
}
#[test]
fn positive_response_renewal() {
let node = Node::as_questionable(bip_test::dummy_node_id(),
bip_test::dummy_socket_addr_v4());
node.remote_response();
assert_eq!(node.status(), NodeStatus::Good);
}
#[test]
fn positive_request_renewal() {
let node = Node::as_questionable(bip_test::dummy_node_id(),
bip_test::dummy_socket_addr_v4());
node.remote_request();
assert_eq!(node.status(), NodeStatus::Good);
}
#[test]
fn positive_node_idle() {
let node = Node::as_good(bip_test::dummy_node_id(), bip_test::dummy_socket_addr_v4());
let time_offset = Duration::minutes(super::MAX_LAST_SEEN_MINS);
let idle_time = bip_test::travel_into_past(time_offset);
node.last_response.set(Some(idle_time));
assert_eq!(node.status(), NodeStatus::Questionable);
}
#[test]
fn positive_node_idle_reqeusts() {
let node = Node::as_questionable(bip_test::dummy_node_id(),
bip_test::dummy_socket_addr_v4());
for _ in 0..super::MAX_REFRESH_REQUESTS {
node.local_request();
}
assert_eq!(node.status(), NodeStatus::Bad);
}
#[test]
fn positive_good_status_ordering() {
assert!(NodeStatus::Good > NodeStatus::Questionable);
assert!(NodeStatus::Good > NodeStatus::Bad);
assert!(NodeStatus::Good == NodeStatus::Good);
}
#[test]
fn positive_questionable_status_ordering() {
assert!(NodeStatus::Questionable > NodeStatus::Bad);
assert!(NodeStatus::Questionable < NodeStatus::Good);
assert!(NodeStatus::Questionable == NodeStatus::Questionable);
}
#[test]
fn positive_bad_status_ordering() {
assert!(NodeStatus::Bad < NodeStatus::Good);
assert!(NodeStatus::Bad < NodeStatus::Questionable);
assert!(NodeStatus::Bad == NodeStatus::Bad);
}
}