#![warn(missing_docs)]
use itertools::izip;
use serde::Deserialize;
use serde::Serialize;
use crate::dht::Did;
use crate::err::Error;
use crate::err::Result;
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub enum RelayMethod {
SEND,
REPORT,
}
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct MessageRelay {
pub method: RelayMethod,
pub path: Vec<Did>,
pub path_end_cursor: usize,
pub next_hop: Option<Did>,
pub destination: Did,
}
impl MessageRelay {
pub fn new(
method: RelayMethod,
path: Vec<Did>,
path_end_cursor: Option<usize>,
next_hop: Option<Did>,
destination: Did,
) -> Self {
Self {
method,
path,
path_end_cursor: path_end_cursor.unwrap_or(0),
next_hop,
destination,
}
}
pub fn relay(&mut self, current: Did, next_hop: Option<Did>) -> Result<()> {
self.validate()?;
if self.next_hop.is_some() && self.next_hop.unwrap() != current {
return Err(Error::InvalidNextHop);
}
match self.method {
RelayMethod::SEND => {
self.path.push(current);
self.next_hop = next_hop;
Ok(())
}
RelayMethod::REPORT => {
if self.next_hop == Some(self.destination) {
self.path_end_cursor = self.path.len() - 1;
self.next_hop = None;
return Ok(());
}
let pos = self
.path
.iter()
.rev()
.skip(self.path_end_cursor)
.position(|&x| x == current);
if let (None, None) = (pos, next_hop) {
return Err(Error::CannotInferNextHop);
}
if let Some(pos) = pos {
self.path_end_cursor += pos;
}
self.next_hop = next_hop.or_else(|| self.path_prev());
Ok(())
}
}
}
pub fn report(&self) -> Result<Self> {
if self.method != RelayMethod::SEND {
return Err(Error::ReportNeedSend);
}
if self.path.len() < 2 {
return Err(Error::CannotInferNextHop);
}
Ok(Self {
method: RelayMethod::REPORT,
path: self.path.clone(),
path_end_cursor: 0,
next_hop: self.path_prev(),
destination: self.sender(),
})
}
pub fn reset_destination(&mut self, destination: Did) -> Result<()> {
if self.method == RelayMethod::SEND {
self.destination = destination;
Ok(())
} else {
Err(Error::ResetDestinationNeedSend)
}
}
pub fn validate(&self) -> Result<()> {
if self.path.windows(2).any(|w| w[0] == w[1]) {
return Err(Error::InvalidRelayPath);
}
if self.method == RelayMethod::REPORT && self.path[0] != self.destination {
return Err(Error::InvalidRelayDestination);
}
if self.method == RelayMethod::SEND && has_infinite_loop(&self.path) {
return Err(Error::InfiniteRelayPath);
}
Ok(())
}
pub fn origin(&self) -> Did {
*self.path.first().unwrap()
}
pub fn sender(&self) -> Did {
match self.method {
RelayMethod::SEND => self.origin(),
RelayMethod::REPORT => *self.path.last().unwrap(),
}
}
pub fn path_prev(&self) -> Option<Did> {
if self.path.len() < self.path_end_cursor + 2 {
None
} else {
Some(self.path[self.path.len() - 2 - self.path_end_cursor])
}
}
}
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::*;
use crate::ecc::SecretKey;
#[test]
fn test_path_end_cursor() {
let origin_sender = SecretKey::random().address().into();
let next_hop1 = SecretKey::random().address().into();
let next_hop2 = SecretKey::random().address().into();
let next_hop3 = SecretKey::random().address().into();
let mut send_relay = MessageRelay {
method: RelayMethod::SEND,
path: vec![origin_sender],
path_end_cursor: 0,
next_hop: None,
destination: next_hop3,
};
send_relay.relay(next_hop1, None).unwrap();
assert_eq!(send_relay.path_end_cursor, 0);
send_relay.relay(next_hop2, None).unwrap();
assert_eq!(send_relay.path_end_cursor, 0);
send_relay.relay(next_hop3, None).unwrap();
assert_eq!(send_relay.path_end_cursor, 0);
let mut report_relay = send_relay.report().unwrap();
assert_eq!(report_relay.path_end_cursor, 0);
report_relay.relay(next_hop2, None).unwrap();
assert_eq!(report_relay.path_end_cursor, 1);
report_relay.relay(next_hop1, None).unwrap();
assert_eq!(report_relay.path_end_cursor, 2);
}
#[test]
fn test_jump_to_previous_node_when_reporting() {
let origin_sender = SecretKey::random().address().into();
let next_hop1 = SecretKey::random().address().into();
let next_hop2 = SecretKey::random().address().into();
let next_hop3 = SecretKey::random().address().into();
let next_hop4 = SecretKey::random().address().into();
let mut send_relay = MessageRelay {
method: RelayMethod::SEND,
path: vec![origin_sender],
path_end_cursor: 0,
next_hop: None,
destination: next_hop4,
};
send_relay.relay(next_hop1, None).unwrap();
send_relay.relay(next_hop2, None).unwrap();
send_relay.relay(next_hop3, None).unwrap();
send_relay.relay(next_hop4, None).unwrap();
assert_eq!(send_relay.path, vec![
origin_sender,
next_hop1,
next_hop2,
next_hop3,
next_hop4
]);
assert_eq!(send_relay.path_end_cursor, 0);
let mut report_relay = send_relay.report().unwrap();
assert_eq!(report_relay.path, vec![
origin_sender,
next_hop1,
next_hop2,
next_hop3,
next_hop4
]);
assert_eq!(report_relay.next_hop, Some(next_hop3));
assert_eq!(report_relay.path_end_cursor, 0);
report_relay.next_hop = Some(next_hop2);
report_relay.relay(next_hop2, Some(origin_sender)).unwrap();
assert_eq!(report_relay.path, vec![
origin_sender,
next_hop1,
next_hop2,
next_hop3,
next_hop4
]);
assert_eq!(report_relay.next_hop, Some(origin_sender));
assert_eq!(report_relay.path_end_cursor, 2);
report_relay.relay(origin_sender, None).unwrap();
}
#[test]
fn test_path_prev() {
let origin_sender = SecretKey::random().address().into();
let next_hop1 = SecretKey::random().address().into();
let next_hop2 = SecretKey::random().address().into();
let mut relay = MessageRelay {
method: RelayMethod::SEND,
path: vec![origin_sender],
path_end_cursor: 0,
next_hop: None,
destination: next_hop2,
};
assert!(relay.path_prev().is_none());
relay.relay(next_hop1, None).unwrap();
assert_eq!(relay.path_prev(), Some(origin_sender));
relay.relay(next_hop2, None).unwrap();
assert_eq!(relay.path_prev(), Some(next_hop1));
}
#[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,
]));
}
}