Skip to main content

rings_core/message/protocols/
relay.rs

1#![warn(missing_docs)]
2
3use serde::Deserialize;
4use serde::Serialize;
5
6use crate::dht::Did;
7use crate::error::Error;
8use crate::error::Result;
9
10/// MessageRelay guide message passing on rings network by relay.
11///
12/// All messages should be sent with `MessageRelay`.
13/// By calling `relay` method in correct place, `MessageRelay` help to do things:
14/// - Record the whole transport path for inspection.
15/// - Get the sender of a message.
16#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
17pub struct MessageRelay {
18    /// A push only stack. Record routes when handling messages.
19    pub path: Vec<Did>,
20
21    /// The next node to handle the message.
22    /// A message handler will pick transport by this field.
23    pub next_hop: Did,
24
25    /// The destination of the message.
26    /// It may help the handler to find out `next_hop` in some situations.
27    pub destination: Did,
28}
29
30impl MessageRelay {
31    /// Create a new `MessageRelay`.
32    pub fn new(path: Vec<Did>, next_hop: Did, destination: Did) -> Self {
33        Self {
34            path,
35            next_hop,
36            destination,
37        }
38    }
39
40    /// Validate relay, then create a new `MessageRelay` that have `current` did in the end of path.
41    /// The new relay will use `next_hop` as `next_hop` and `self.destination` as `destination`.
42    pub fn forward(&self, current: Did, next_hop: Did) -> Result<Self> {
43        self.validate(current)?;
44
45        if self.next_hop != current {
46            return Err(Error::InvalidNextHop);
47        }
48
49        let mut path = self.path.clone();
50        path.push(current);
51
52        Ok(Self {
53            path,
54            next_hop,
55            destination: self.destination,
56        })
57    }
58
59    /// Validate relay, then create a new `MessageRelay` that used to report the message.
60    /// The new relay will use `self.path[self.path.len() - 1]` as `next_hop` and `self.sender()` as `destination`.
61    /// In the new relay, the path will be cleared and only have `current` did.
62    pub fn report(&self, current: Did) -> Result<Self> {
63        self.validate(current)?;
64
65        if self.path.is_empty() {
66            return Err(Error::CannotInferNextHop);
67        }
68
69        Ok(Self {
70            path: vec![current],
71            next_hop: self.path.last().copied().ok_or(Error::CannotInferNextHop)?,
72            destination: self.try_origin_sender()?,
73        })
74    }
75
76    /// Sometime the sender may not know the destination of the message. They just use next_hop as destination.
77    /// The next node can find a new next_hop, and may use this function to set that next_hop as destination again.
78    pub fn reset_destination(&self, destination: Did) -> Self {
79        let mut relay = self.clone();
80        relay.destination = destination;
81        relay
82    }
83
84    /// Check if path and destination is valid.
85    pub fn validate(&self, current: Did) -> Result<()> {
86        if self.next_hop != current {
87            return Err(Error::InvalidNextHop);
88        }
89
90        // Adjacent elements in self.path cannot be equal
91        if self
92            .path
93            .windows(2)
94            .any(|window| matches!(window, [left, right] if left == right))
95        {
96            return Err(Error::InvalidRelayPath);
97        }
98
99        // Prevent infinite loop
100        if has_infinite_loop(&self.path) {
101            tracing::error!("Infinite path detected {:?}", self.path);
102            return Err(Error::InfiniteRelayPath);
103        }
104
105        Ok(())
106    }
107
108    /// Get the origin sender of current message.
109    /// Should be the first element of path.
110    #[deprecated(note = "please use `origin_sender` instead")]
111    pub fn sender(&self) -> Did {
112        self.origin_sender()
113    }
114
115    /// Get the origin sender of current message as a checked relay-path boundary.
116    pub fn try_origin_sender(&self) -> Result<Did> {
117        self.path.first().copied().ok_or(Error::CannotInferNextHop)
118    }
119
120    /// Get the origin sender of current message.
121    ///
122    /// The origin should be the first element of `path`. Empty relay paths keep
123    /// the legacy fallback to `destination`; callers that must distinguish an
124    /// invalid relay boundary from a real origin should use
125    /// [`try_origin_sender`](Self::try_origin_sender).
126    pub fn origin_sender(&self) -> Did {
127        self.path.first().copied().unwrap_or(self.destination)
128    }
129}
130
131// Since rust cannot zip N iterators, when you change this number,
132// you should also change the code of `has_infinite_loop` below.
133const INFINITE_LOOP_TOLERANCE: usize = 3;
134
135fn has_infinite_loop<T>(path: &[T]) -> bool
136where T: PartialEq {
137    // Invariant: a relay loop is witnessed by a non-empty suffix period P such
138    // that the final path segment is P repeated INFINITE_LOOP_TOLERANCE times.
139    for period in 1..=path.len() / INFINITE_LOOP_TOLERANCE {
140        let repeated_len = period * INFINITE_LOOP_TOLERANCE;
141        let start = path.len() - repeated_len;
142        let Some(suffix) = path.get(start..) else {
143            continue;
144        };
145        let mut chunks = suffix.chunks_exact(period);
146        let Some(first) = chunks.next() else {
147            continue;
148        };
149        if chunks.all(|chunk| chunk == first) {
150            return true;
151        }
152    }
153
154    false
155}
156
157#[cfg(test)]
158mod test {
159    use super::*;
160
161    #[test]
162    #[rustfmt::skip]
163    fn test_has_infinite_loop() {
164        assert!(!has_infinite_loop(&Vec::<u8>::new()));
165
166        assert!(!has_infinite_loop(&[
167            1, 2, 3,
168        ]));
169
170        assert!(!has_infinite_loop(&[
171            1, 2, 3,
172            1, 2, 3,
173        ]));
174
175        assert!(has_infinite_loop(&[
176            1, 2, 3,
177            1, 2, 3,
178            1, 2, 3,
179        ]));
180
181        assert!(has_infinite_loop(&[
182            1, 1, 2, 3,
183               1, 2, 3,
184               1, 2, 3,
185        ]));
186
187        assert!(!has_infinite_loop(&[
188               1, 2, 3,
189            1, 1, 2, 3,
190               1, 2, 3,
191        ]));
192
193        assert!(has_infinite_loop(&[
194            1, 2, 1, 2, 3,
195                  1, 2, 3,
196                  1, 2, 3,
197        ]));
198
199        assert!(has_infinite_loop(&[
200            4, 5, 1, 2, 3,
201                  1, 2, 3,
202                  1, 2, 3,
203        ]));
204
205        assert!(!has_infinite_loop(&[
206            1, 2, 3,
207                  3,
208            1, 2, 3,
209                  3,
210            1, 2, 3,
211        ]));
212
213        assert!(!has_infinite_loop(&[
214                  1,
215            1, 2, 3,
216                  3,
217            1, 2, 3,
218                  3,
219            1, 2, 3,
220        ]));
221
222        assert!(has_infinite_loop(&[
223                  3,
224            1, 2, 3,
225                  3,
226            1, 2, 3,
227                  3,
228            1, 2, 3,
229        ]));
230
231        assert!(has_infinite_loop(&[
232            1, 2, 3,
233            1, 2, 3,
234                  3,
235            1, 2, 3,
236                  3,
237            1, 2, 3,
238        ]));
239
240        assert!(has_infinite_loop(&[
241                  1, 2,
242               3, 1, 2,
243            3, 3, 1, 2,
244            3, 3, 1, 2,
245            3, 3, 1, 2,
246        ]));
247
248        assert!(!has_infinite_loop(&[
249               2, 3,
250               4, 3,
251            1, 2, 3,
252               4, 3,
253            1, 2, 3,
254               4, 3,
255        ]));
256
257        assert!(has_infinite_loop(&[
258            1, 2, 3,
259               4, 3,
260            1, 2, 3,
261               4, 3,
262            1, 2, 3,
263               4, 3,
264        ]));
265
266        assert!(has_infinite_loop(&[
267               1, 2, 3, 4,
268            3, 1, 2, 3, 4,
269            3, 1, 2, 3, 4,
270            3, 1, 2, 3, 4,
271        ]));
272    }
273
274    #[test]
275    fn empty_path_origin_sender_is_checked() {
276        let fallback_destination = Did::from(2);
277        let relay = MessageRelay::new(vec![], Did::from(1), fallback_destination);
278
279        assert!(matches!(
280            relay.try_origin_sender(),
281            Err(Error::CannotInferNextHop)
282        ));
283        assert_eq!(relay.origin_sender(), fallback_destination);
284    }
285}