#![warn(missing_docs)]
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.last().copied().ok_or(Error::CannotInferNextHop)?,
destination: self.try_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(|window| matches!(window, [left, right] if left == right))
{
return Err(Error::InvalidRelayPath);
}
if has_infinite_loop(&self.path) {
tracing::error!("Infinite path detected {:?}", self.path);
return Err(Error::InfiniteRelayPath);
}
Ok(())
}
#[deprecated(note = "please use `origin_sender` instead")]
pub fn sender(&self) -> Did {
self.origin_sender()
}
pub fn try_origin_sender(&self) -> Result<Did> {
self.path.first().copied().ok_or(Error::CannotInferNextHop)
}
pub fn origin_sender(&self) -> Did {
self.path.first().copied().unwrap_or(self.destination)
}
}
const INFINITE_LOOP_TOLERANCE: usize = 3;
fn has_infinite_loop<T>(path: &[T]) -> bool
where T: PartialEq {
for period in 1..=path.len() / INFINITE_LOOP_TOLERANCE {
let repeated_len = period * INFINITE_LOOP_TOLERANCE;
let start = path.len() - repeated_len;
let Some(suffix) = path.get(start..) else {
continue;
};
let mut chunks = suffix.chunks_exact(period);
let Some(first) = chunks.next() else {
continue;
};
if chunks.all(|chunk| chunk == first) {
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,
]));
}
#[test]
fn empty_path_origin_sender_is_checked() {
let fallback_destination = Did::from(2);
let relay = MessageRelay::new(vec![], Did::from(1), fallback_destination);
assert!(matches!(
relay.try_origin_sender(),
Err(Error::CannotInferNextHop)
));
assert_eq!(relay.origin_sender(), fallback_destination);
}
}