#![warn(missing_docs)]
use itertools::izip;
use serde::Deserialize;
use serde::Serialize;
use crate::dht::Did;
use crate::error::Error;
use crate::error::Result;
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct MessageRelay {
pub path: Vec<Did>,
pub next_hop: Did,
pub destination: Did,
}
impl MessageRelay {
pub fn new(path: Vec<Did>, next_hop: Did, destination: Did) -> Self {
Self {
path,
next_hop,
destination,
}
}
pub fn forward(&self, current: Did, next_hop: Did) -> Result<Self> {
self.validate(current)?;
if self.next_hop != current {
return Err(Error::InvalidNextHop);
}
let mut path = self.path.clone();
path.push(current);
Ok(Self {
path,
next_hop,
destination: self.destination,
})
}
pub fn report(&self, current: Did) -> Result<Self> {
self.validate(current)?;
if self.path.is_empty() {
return Err(Error::CannotInferNextHop);
}
Ok(Self {
path: vec![current],
next_hop: self.path[self.path.len() - 1],
destination: self.origin_sender(),
})
}
pub fn reset_destination(&self, destination: Did) -> Self {
let mut relay = self.clone();
relay.destination = destination;
relay
}
pub fn validate(&self, current: Did) -> Result<()> {
if self.next_hop != current {
return Err(Error::InvalidNextHop);
}
if self.path.windows(2).any(|w| w[0] == w[1]) {
return Err(Error::InvalidRelayPath);
}
if has_infinite_loop(&self.path) {
return Err(Error::InfiniteRelayPath);
}
Ok(())
}
#[deprecated(note = "please use `origin_sender` instead")]
pub fn sender(&self) -> Did {
*self.path.first().unwrap()
}
pub fn origin_sender(&self) -> Did {
*self.path.first().unwrap()
}
}
const INFINITE_LOOP_TOLERANCE: usize = 3;
fn has_infinite_loop<T>(path: &[T]) -> bool
where T: PartialEq + std::fmt::Debug {
if let Some(last) = path.last() {
let indexes = path
.iter()
.rev()
.enumerate()
.filter(|(_, r)| r == &last)
.map(|(index, _)| index)
.take(INFINITE_LOOP_TOLERANCE)
.collect::<Vec<_>>();
if indexes.len() >= INFINITE_LOOP_TOLERANCE {
let p1 = path.iter().rev().skip(indexes[0]);
let p2 = path.iter().rev().skip(indexes[1]);
let p3 = path.iter().rev().skip(indexes[2]);
let lens = vec![
indexes[1] - indexes[0],
indexes[2] - indexes[1],
path.len() - indexes[2],
];
let min_len = lens.iter().min().unwrap();
for (i, (x, y, z)) in izip!(p1, p2, p3).enumerate() {
if !(x == y && y == z) {
return false;
}
if i == min_len - 1 {
break;
}
}
if lens[0] == lens[1] {
return true;
}
}
}
false
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[rustfmt::skip]
fn test_has_infinite_loop() {
assert!(!has_infinite_loop(&Vec::<u8>::new()));
assert!(!has_infinite_loop(&[
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
1, 2, 3,
1, 2, 3,
]));
assert!(has_infinite_loop(&[
1, 2, 3,
1, 2, 3,
1, 2, 3,
]));
assert!(has_infinite_loop(&[
1, 1, 2, 3,
1, 2, 3,
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
1, 2, 3,
1, 1, 2, 3,
1, 2, 3,
]));
assert!(has_infinite_loop(&[
1, 2, 1, 2, 3,
1, 2, 3,
1, 2, 3,
]));
assert!(has_infinite_loop(&[
4, 5, 1, 2, 3,
1, 2, 3,
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
1, 2, 3,
3,
1, 2, 3,
3,
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
1,
1, 2, 3,
3,
1, 2, 3,
3,
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
3,
1, 2, 3,
3,
1, 2, 3,
3,
1, 2, 3,
]));
assert!(!has_infinite_loop(&[
1, 2, 3,
1, 2, 3,
3,
1, 2, 3,
3,
1, 2, 3,
]));
assert!(has_infinite_loop(&[
1, 2,
3, 1, 2,
3, 3, 1, 2,
3, 3, 1, 2,
3, 3, 1, 2,
]));
assert!(!has_infinite_loop(&[
2, 3,
4, 3,
1, 2, 3,
4, 3,
1, 2, 3,
4, 3,
]));
assert!(!has_infinite_loop(&[
1, 2, 3,
4, 3,
1, 2, 3,
4, 3,
1, 2, 3,
4, 3,
]));
assert!(has_infinite_loop(&[
1, 2, 3, 4,
3, 1, 2, 3, 4,
3, 1, 2, 3, 4,
3, 1, 2, 3, 4,
]));
}
}