#![allow(unused)]
use std::iter::Filter;
use std::slice::Iter;
use bip_util::bt::NodeId;
use bip_util::sha::{self, ShaHash, XorRep};
use rand;
use routing::bucket::{self, Bucket};
use routing::node::{Node, NodeStatus};
pub const MAX_BUCKETS: usize = sha::SHA_HASH_LEN * 8;
pub struct RoutingTable {
buckets: Vec<Bucket>,
node_id: NodeId,
}
impl RoutingTable {
pub fn new(node_id: NodeId) -> RoutingTable {
let buckets = vec![Bucket::new()];
RoutingTable {
buckets: buckets,
node_id: node_id,
}
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
pub fn closest_nodes<'a>(&'a self, node_id: NodeId) -> ClosestNodes<'a> {
ClosestNodes::new(&self.buckets, self.node_id, node_id)
}
pub fn buckets<'a>(&'a self) -> Buckets<'a> {
Buckets::new(&self.buckets)
}
pub fn find_node(&self, node: &Node) -> Option<&Node> {
let bucket_index = leading_bit_count(self.node_id, node.id());
let opt_bucket_contents = if let Some(c) = self.buckets().skip(bucket_index).next() {
Some(c)
} else {
self.buckets().find(|c| {
match c {
&BucketContents::Empty => false,
&BucketContents::Sorted(_) => false,
&BucketContents::Assorted(_) => true,
}
})
};
match opt_bucket_contents {
Some(BucketContents::Sorted(b)) => b.pingable_nodes().find(|n| n == &node),
Some(BucketContents::Assorted(b)) => b.pingable_nodes().find(|n| n == &node),
_ => None,
}
}
pub fn add_node(&mut self, node: Node) {
if node.status() == NodeStatus::Bad {
return;
}
let num_same_bits = leading_bit_count(self.node_id, node.id());
if num_same_bits != MAX_BUCKETS {
self.bucket_node(node, num_same_bits);
}
}
fn bucket_node(&mut self, node: Node, num_same_bits: usize) {
let bucket_index = bucket_placement(num_same_bits, self.buckets.len());
if !self.buckets[bucket_index].add_node(node.clone()) {
if self.split_bucket(bucket_index) {
self.bucket_node(node.clone(), num_same_bits);
}
}
}
fn split_bucket(&mut self, bucket_index: usize) -> bool {
if !can_split_bucket(self.buckets.len(), bucket_index) {
return false;
}
let split_bucket = match self.buckets.pop() {
Some(bucket) => bucket,
None => panic!("No buckets present in RoutingTable, implementation error..."),
};
self.buckets.push(Bucket::new());
self.buckets.push(Bucket::new());
for node in split_bucket.iter() {
self.add_node(node.clone());
}
true
}
}
fn can_split_bucket(num_buckets: usize, bucket_index: usize) -> bool {
bucket_index == num_buckets - 1 && bucket_index != MAX_BUCKETS - 1
}
pub fn random_node_id() -> NodeId {
let mut random_sha_hash = [0u8; sha::SHA_HASH_LEN];
for byte in random_sha_hash.iter_mut() {
*byte = rand::random::<u8>();
}
ShaHash::from(random_sha_hash)
}
pub fn leading_bit_count(local_node: NodeId, remote_node: NodeId) -> usize {
let diff_id = local_node ^ remote_node;
diff_id.bits().take_while(|&x| x == XorRep::Same).count()
}
fn bucket_placement(num_same_bits: usize, num_buckets: usize) -> usize {
let ideal_index = num_same_bits;
if ideal_index >= num_buckets {
num_buckets - 1
} else {
ideal_index
}
}
#[derive(Copy, Clone)]
pub enum BucketContents<'a> {
Empty,
Sorted(&'a Bucket),
Assorted(&'a Bucket),
}
impl<'a> BucketContents<'a> {
fn is_empty(&self) -> bool {
match self {
&BucketContents::Empty => true,
_ => false,
}
}
fn is_sorted(&self) -> bool {
match self {
&BucketContents::Sorted(_) => true,
_ => false,
}
}
fn is_assorted(&self) -> bool {
match self {
&BucketContents::Assorted(_) => true,
_ => false,
}
}
}
#[derive(Copy, Clone)]
pub struct Buckets<'a> {
buckets: &'a [Bucket],
index: usize,
}
impl<'a> Buckets<'a> {
fn new(buckets: &'a [Bucket]) -> Buckets<'a> {
Buckets {
buckets: buckets,
index: 0,
}
}
}
impl<'a> Iterator for Buckets<'a> {
type Item = BucketContents<'a>;
fn next(&mut self) -> Option<BucketContents<'a>> {
if self.index > MAX_BUCKETS {
return None;
} else if self.index == MAX_BUCKETS {
self.index += 1;
return if self.buckets.len() == MAX_BUCKETS || self.buckets.is_empty() {
None
} else {
Some(BucketContents::Assorted(&self.buckets[self.buckets.len() - 1]))
};
}
if self.index + 1 < self.buckets.len() || self.buckets.len() == MAX_BUCKETS {
self.index += 1;
Some(BucketContents::Sorted(&self.buckets[self.index - 1]))
} else {
self.index += 1;
Some(BucketContents::Empty)
}
}
}
type GoodNodes<'a> = Filter<Iter<'a, Node>, fn(&&Node) -> bool>;
pub struct ClosestNodes<'a> {
buckets: &'a [Bucket],
current_iter: Option<GoodNodes<'a>>,
current_index: usize,
start_index: usize,
assorted_nodes: Option<[(usize, &'a Node, bool); bucket::MAX_BUCKET_SIZE]>,
}
impl<'a> ClosestNodes<'a> {
fn new(buckets: &'a [Bucket], self_node_id: NodeId, other_node_id: NodeId) -> ClosestNodes<'a> {
let start_index = leading_bit_count(self_node_id, other_node_id);
let current_iter = bucket_iterator(buckets, start_index);
let assorted_nodes = precompute_assorted_nodes(buckets, self_node_id);
ClosestNodes {
buckets: buckets,
current_iter: current_iter,
current_index: start_index,
start_index: start_index,
assorted_nodes: assorted_nodes,
}
}
}
impl<'a> Iterator for ClosestNodes<'a> {
type Item = &'a Node;
fn next(&mut self) -> Option<&'a Node> {
let current_index = self.current_index;
if let Some(ref mut iter) = self.current_iter {
match iter.next() {
Some(node) => return Some(node),
None => (),
};
}
if let Some(ref mut nodes) = self.assorted_nodes {
let mut nodes_iter = nodes.iter_mut().filter(|tup| is_good_node(&tup.1));
match nodes_iter.find(|tup| tup.0 == current_index && !tup.2) {
Some(node) => {
node.2 = true;
return Some(node.1);
}
None => (),
};
}
match next_bucket_index(MAX_BUCKETS, self.start_index, self.current_index) {
Some(new_index) => {
self.current_index = new_index;
self.current_iter = bucket_iterator(self.buckets, self.current_index);
self.next()
}
None => None,
}
}
}
fn precompute_assorted_nodes<'a>(buckets: &'a [Bucket],
self_node_id: NodeId)
-> Option<[(usize, &'a Node, bool); bucket::MAX_BUCKET_SIZE]> {
if buckets.len() == MAX_BUCKETS {
return None;
}
let assorted_bucket = &buckets[buckets.len() - 1];
let mut assorted_iter = assorted_bucket.iter().peekable();
if let Some(&init_reference) = assorted_iter.peek() {
let mut assorted_nodes = [(0, init_reference, true); bucket::MAX_BUCKET_SIZE];
for (index, node) in assorted_iter.enumerate() {
let bucket_index = leading_bit_count(self_node_id, node.id());
assorted_nodes[index] = (bucket_index, node, false);
}
Some(assorted_nodes)
} else {
None
}
}
fn bucket_iterator<'a>(buckets: &'a [Bucket], index: usize) -> Option<GoodNodes<'a>> {
if buckets.len() == MAX_BUCKETS {
buckets
} else {
&buckets[..(buckets.len() - 1)]
}
.get(index)
.map(|bucket| good_node_filter(bucket.iter()))
}
fn good_node_filter<'a>(iter: Iter<'a, Node>) -> GoodNodes<'a> {
iter.filter(is_good_node)
}
fn is_good_node(node: &&Node) -> bool {
let status = node.status();
status == NodeStatus::Good || status == NodeStatus::Questionable
}
fn next_bucket_index(num_buckets: usize, start_index: usize, curr_index: usize) -> Option<usize> {
if curr_index == start_index {
let right_index = start_index.checked_add(1);
let left_index = start_index.checked_sub(1);
if index_is_in_bounds(num_buckets, right_index) {
Some(right_index.unwrap())
} else if index_is_in_bounds(num_buckets, left_index) {
Some(left_index.unwrap())
} else {
None
}
} else if curr_index > start_index {
let offset = curr_index - start_index;
let left_index = start_index.checked_sub(offset);
let right_index = curr_index.checked_add(1);
if index_is_in_bounds(num_buckets, left_index) {
Some(left_index.unwrap())
} else if index_is_in_bounds(num_buckets, right_index) {
Some(right_index.unwrap())
} else {
None
}
} else {
let offset = (start_index - curr_index) + 1;
let right_index = start_index.checked_add(offset);
let left_index = curr_index.checked_sub(1);
if index_is_in_bounds(num_buckets, right_index) {
Some(right_index.unwrap())
} else if index_is_in_bounds(num_buckets, left_index) {
Some(left_index.unwrap())
} else {
None
}
}
}
fn index_is_in_bounds(length: usize, checked_index: Option<usize>) -> bool {
match checked_index {
Some(index) => index < length,
None => false,
}
}
#[cfg(test)]
mod tests {
use bip_util::bt::{self, NodeId};
use bip_util::test as bip_test;
use routing::table::{self, RoutingTable, BucketContents};
use routing::bucket;
use routing::node::Node;
fn flip_id_bit_at_index(node_id: NodeId, index: usize) -> NodeId {
let mut id_bytes: [u8; bt::NODE_ID_LEN] = node_id.into();
let (byte_index, bit_index) = (index / 8, index % 8);
let actual_bit_index = 7 - bit_index;
id_bytes[byte_index] ^= 1 << actual_bit_index;
id_bytes.into()
}
#[test]
fn positive_add_node_max_recursion() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
let mut node_id = table_id;
node_id[bt::NODE_ID_LEN - 1] = 0;
let block_addrs = bip_test::dummy_block_socket_addrs((bucket::MAX_BUCKET_SIZE + 1) as u16);
for index in 0..(bucket::MAX_BUCKET_SIZE + 1) {
let node = Node::as_good(node_id.into(), block_addrs[index]);
table.add_node(node);
}
}
#[test]
fn positive_initial_empty_buckets() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
assert_eq!(table.buckets().take(table::MAX_BUCKETS).count(),
table::MAX_BUCKETS);
assert!(table.buckets()
.take(table::MAX_BUCKETS)
.fold(true, |prev, contents| prev && contents.is_empty()));
assert_eq!(table.buckets().skip(table::MAX_BUCKETS).count(), 1);
for bucket in table.buckets().skip(table::MAX_BUCKETS) {
match bucket {
BucketContents::Assorted(b) => assert_eq!(b.pingable_nodes().count(), 0),
_ => panic!("Expected BucketContents::Assorted"),
}
}
}
#[test]
fn positive_first_bucket_sorted() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
let mut node_id = table_id;
node_id[0] |= 128;
let block_addrs = bip_test::dummy_block_socket_addrs((bucket::MAX_BUCKET_SIZE + 1) as u16);
for index in 0..(bucket::MAX_BUCKET_SIZE + 1) {
let node = Node::as_good(node_id.into(), block_addrs[index]);
table.add_node(node);
}
assert_eq!(table.buckets().take(1).count(), 1);
for bucket in table.buckets().take(1) {
match bucket {
BucketContents::Sorted(b) => {
assert_eq!(b.pingable_nodes().count(), bucket::MAX_BUCKET_SIZE)
}
_ => panic!("Expected BucketContents::Sorted"),
}
}
assert_eq!(table.buckets().skip(1).take(table::MAX_BUCKETS - 1).count(),
table::MAX_BUCKETS - 1);
assert!(table.buckets()
.skip(1)
.take(table::MAX_BUCKETS - 1)
.fold(true, |prev, contents| prev && contents.is_empty()));
assert_eq!(table.buckets().skip(table::MAX_BUCKETS).count(), 1);
for bucket in table.buckets().skip(table::MAX_BUCKETS) {
match bucket {
BucketContents::Assorted(b) => assert_eq!(b.pingable_nodes().count(), 0),
_ => panic!("Expected BucketContents::Assorted"),
}
}
}
#[test]
fn positive_last_bucket_sorted() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
let mut node_id = table_id;
node_id[bt::NODE_ID_LEN - 1] = 0;
let block_addrs = bip_test::dummy_block_socket_addrs((bucket::MAX_BUCKET_SIZE + 1) as u16);
for index in 0..(bucket::MAX_BUCKET_SIZE + 1) {
let node = Node::as_good(node_id.into(), block_addrs[index]);
table.add_node(node);
}
assert_eq!(table.buckets().take(table::MAX_BUCKETS - 1).count(),
table::MAX_BUCKETS - 1);
for bucket in table.buckets().take(table::MAX_BUCKETS - 1) {
match bucket {
BucketContents::Sorted(b) => assert_eq!(b.pingable_nodes().count(), 0),
_ => panic!("Expected BucketContents::Sorted"),
}
}
assert_eq!(table.buckets().skip(table::MAX_BUCKETS - 1).take(1).count(),
1);
for bucket in table.buckets().skip(table::MAX_BUCKETS - 1).take(1) {
match bucket {
BucketContents::Sorted(b) => {
assert_eq!(b.pingable_nodes().count(), bucket::MAX_BUCKET_SIZE)
}
_ => panic!("Expected BucketContents::Sorted"),
}
}
assert_eq!(table.buckets().skip(table::MAX_BUCKETS).count(), 0);
}
#[test]
fn positive_all_sorted_buckets() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
let block_addrs = bip_test::dummy_block_socket_addrs(bucket::MAX_BUCKET_SIZE as u16);
for bit_flip_index in 0..table::MAX_BUCKETS {
for addr_index in 0..block_addrs.len() {
let bucket_node_id = flip_id_bit_at_index(table_id.into(), bit_flip_index);
table.add_node(Node::as_good(bucket_node_id, block_addrs[addr_index]));
}
}
assert_eq!(table.buckets().count(), table::MAX_BUCKETS);
for bucket in table.buckets() {
match bucket {
BucketContents::Sorted(b) => {
assert_eq!(b.pingable_nodes().count(), bucket::MAX_BUCKET_SIZE)
}
_ => panic!("Expected BucketContents::Sorted"),
}
}
}
#[test]
fn negative_node_id_equal_table_id() {
let table_id = [1u8; bt::NODE_ID_LEN];
let mut table = RoutingTable::new(table_id.into());
assert_eq!(table.closest_nodes(table_id.into()).count(), 0);
let node = Node::as_good(table_id.into(), bip_test::dummy_socket_addr_v4());
table.add_node(node);
assert_eq!(table.closest_nodes(table_id.into()).count(), 0);
}
}