#![allow(clippy::all)]
use enr::{
k256::sha2::digest::generic_array::{typenum::U32, GenericArray},
NodeId,
};
use uint::construct_uint;
construct_uint! {
pub(super) struct U256(4);
}
#[derive(Clone, Debug)]
pub struct Key<T> {
preimage: T,
hash: GenericArray<u8, U32>,
}
impl<T> PartialEq for Key<T> {
fn eq(&self, other: &Key<T>) -> bool {
self.hash == other.hash
}
}
impl<T> Eq for Key<T> {}
impl<TPeerId> AsRef<Key<TPeerId>> for Key<TPeerId> {
fn as_ref(&self) -> &Key<TPeerId> {
self
}
}
impl<T> Key<T> {
pub fn new_raw(preimage: T, hash: GenericArray<u8, U32>) -> Key<T> {
Key { preimage, hash }
}
pub fn preimage(&self) -> &T {
&self.preimage
}
pub fn into_preimage(self) -> T {
self.preimage
}
pub fn distance<U>(&self, other: &Key<U>) -> Distance {
let a = U256::from(self.hash.as_slice());
let b = U256::from(other.hash.as_slice());
Distance(a ^ b)
}
pub fn log2_distance<U>(&self, other: &Key<U>) -> Option<u64> {
let xor_dist = self.distance(other);
let log_dist = u64::from(256 - xor_dist.0.leading_zeros());
if log_dist == 0 {
None
} else {
Some(log_dist)
}
}
}
impl From<NodeId> for Key<NodeId> {
fn from(node_id: NodeId) -> Self {
Key {
preimage: node_id,
hash: *GenericArray::from_slice(&node_id.raw()),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Debug)]
pub struct Distance(pub(super) U256);
#[cfg(test)]
mod tests {
use super::*;
use crate::kbucket::bucket::tests::arbitrary_node_id;
use quickcheck::*;
impl Arbitrary for Key<NodeId> {
fn arbitrary<G: Gen>(g: &mut G) -> Key<NodeId> {
Key::from(arbitrary_node_id(g))
}
}
#[test]
fn identity() {
fn prop(a: Key<NodeId>) -> bool {
a.distance(&a) == Distance::default()
}
quickcheck(prop as fn(_) -> _)
}
#[test]
fn symmetry() {
fn prop(a: Key<NodeId>, b: Key<NodeId>) -> bool {
a.distance(&b) == b.distance(&a)
}
quickcheck(prop as fn(_, _) -> _)
}
#[test]
fn triangle_inequality() {
fn prop(a: Key<NodeId>, b: Key<NodeId>, c: Key<NodeId>) -> TestResult {
let ab = a.distance(&b);
let bc = b.distance(&c);
let (ab_plus_bc, overflow) = ab.0.overflowing_add(bc.0);
if overflow {
TestResult::discard()
} else {
TestResult::from_bool(a.distance(&c) <= Distance(ab_plus_bc))
}
}
quickcheck(prop as fn(_, _, _) -> _)
}
#[test]
fn unidirectionality() {
fn prop(a: Key<NodeId>, b: Key<NodeId>) -> bool {
let d = a.distance(&b);
(0..100).all(|_| {
let c = Key::from(NodeId::random());
a.distance(&c) != d || b == c
})
}
quickcheck(prop as fn(_, _) -> _)
}
}