1use std::{borrow::Cow, fmt::Formatter, marker::PhantomData, ops::Not};
2
3use hopr_types::primitive::prelude::GeneralError;
4
5use crate::{
6 HoprSphinxHeaderSpec, HoprSphinxSuite, PAYLOAD_SIZE_INT,
7 sphinx::{
8 errors::SphinxError,
9 prelude::{PaddedPayload, SURB, SphinxHeaderSpec, SphinxSuite},
10 },
11};
12
13flagset::flags! {
14 #[repr(u8)]
16 #[derive(PartialOrd, Ord, strum::EnumString, strum::Display)]
17 pub enum PacketSignal: u8 {
18 SurbDistress = 0b0000_0001,
22 OutOfSurbs = 0b0000_0011,
29 }
30}
31
32pub type PacketSignals = flagset::FlagSet<PacketSignal>;
37
38pub struct PacketMessage<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize>(PaddedPayload<P>, PhantomData<(S, H)>);
40
41pub type HoprPacketMessage = PacketMessage<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT>;
43
44pub struct PacketParts<'a, S: SphinxSuite, H: SphinxHeaderSpec> {
47 pub surbs: Vec<SURB<S, H>>,
49 pub payload: Cow<'a, [u8]>,
51 pub signals: PacketSignals,
53}
54
55impl<S: SphinxSuite, H: SphinxHeaderSpec> Clone for PacketParts<'_, S, H>
56where
57 H::KeyId: Clone,
58 H::SurbReceiverData: Clone,
59{
60 fn clone(&self) -> Self {
61 Self {
62 surbs: self.surbs.clone(),
63 payload: self.payload.clone(),
64 signals: self.signals,
65 }
66 }
67}
68
69impl<S: SphinxSuite, H: SphinxHeaderSpec> std::fmt::Debug for PacketParts<'_, S, H>
70where
71 H::KeyId: std::fmt::Debug,
72 H::SurbReceiverData: std::fmt::Debug,
73{
74 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("PacketParts")
76 .field("surbs", &self.surbs)
77 .field("payload", &self.payload)
78 .field("signals", &self.signals)
79 .finish()
80 }
81}
82
83impl<S: SphinxSuite, H: SphinxHeaderSpec> PartialEq for PacketParts<'_, S, H>
84where
85 H::KeyId: PartialEq,
86 H::SurbReceiverData: PartialEq,
87{
88 fn eq(&self, other: &Self) -> bool {
89 self.surbs == other.surbs && self.payload == other.payload && self.signals == other.signals
90 }
91}
92
93impl<S: SphinxSuite, H: SphinxHeaderSpec> Eq for PacketParts<'_, S, H>
94where
95 H::KeyId: Eq,
96 H::SurbReceiverData: Eq,
97{
98}
99
100pub type HoprPacketParts<'a> = PacketParts<'a, HoprSphinxSuite, HoprSphinxHeaderSpec>;
102
103pub(crate) const S_MASK: u8 = 0b0000_1111;
105
106impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> PacketMessage<S, H, P> {
107 pub const HEADER_LEN: usize = 1;
111 pub const MAX_SURBS_PER_MESSAGE: usize = S_MASK as usize;
116}
117
118impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> TryFrom<PacketParts<'_, S, H>> for PacketMessage<S, H, P> {
119 type Error = SphinxError;
120
121 fn try_from(value: PacketParts<S, H>) -> Result<Self, Self::Error> {
122 if value.surbs.len() > Self::MAX_SURBS_PER_MESSAGE {
123 return Err(GeneralError::ParseError("HoprPacketMessage.num_surbs not valid".into()).into());
124 }
125
126 if value.signals.bits() > S_MASK {
127 return Err(GeneralError::ParseError("HoprPacketMessage.flags not valid".into()).into());
128 }
129
130 if Self::HEADER_LEN + value.surbs.len() * SURB::<S, H>::SIZE + value.payload.len() > P {
132 return Err(GeneralError::ParseError("HoprPacketMessage.size not valid".into()).into());
133 }
134
135 let mut ret = Vec::with_capacity(PaddedPayload::<P>::SIZE);
136 let flags_and_len = (value.signals.bits() << S_MASK.trailing_ones()) | (value.surbs.len() as u8 & S_MASK);
137 ret.push(flags_and_len);
138 for surb in value.surbs.into_iter().map(|s| s.into_boxed()) {
139 ret.extend(surb);
140 }
141 ret.extend_from_slice(value.payload.as_ref());
142
143 Ok(Self(PaddedPayload::new_from_vec(ret)?, PhantomData))
145 }
146}
147
148impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> TryFrom<PacketMessage<S, H, P>> for PacketParts<'_, S, H> {
149 type Error = SphinxError;
150
151 fn try_from(value: PacketMessage<S, H, P>) -> Result<Self, Self::Error> {
152 let data = value.0.into_unpadded()?;
153 if data.is_empty() {
154 return Err(GeneralError::ParseError("HoprPacketMessage.size".into()).into());
155 }
156
157 let num_surbs = (data[0] & S_MASK) as usize;
158 let signals = PacketSignals::new((data[0] & S_MASK.not()) >> S_MASK.trailing_ones())
159 .map_err(|_| GeneralError::ParseError("HoprPacketMessage.signals".into()))?;
160
161 if num_surbs > 0 {
162 let surb_end = num_surbs * SURB::<S, H>::SIZE;
163 if surb_end >= data.len() {
164 return Err(GeneralError::ParseError("HoprPacketMessage.num_surbs not valid".into()).into());
165 }
166
167 let mut data = data.into_vec();
168
169 let surbs = data[1..=surb_end]
170 .chunks_exact(SURB::<S, H>::SIZE)
171 .map(SURB::<S, H>::try_from)
172 .collect::<Result<Vec<_>, _>>()?;
173
174 data.drain(0..=surb_end).for_each(drop);
176
177 Ok(PacketParts {
178 surbs,
179 payload: Cow::Owned(data),
180 signals,
181 })
182 } else {
183 let mut data = data.into_vec();
184 data.remove(0);
185 Ok(PacketParts {
186 surbs: Vec::with_capacity(0),
187 payload: Cow::Owned(data),
188 signals,
189 })
190 }
191 }
192}
193
194impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> From<PaddedPayload<P>> for PacketMessage<S, H, P> {
195 fn from(value: PaddedPayload<P>) -> Self {
196 Self(value, PhantomData)
197 }
198}
199
200impl<S: SphinxSuite, H: SphinxHeaderSpec, const P: usize> From<PacketMessage<S, H, P>> for PaddedPayload<P> {
201 fn from(value: PacketMessage<S, H, P>) -> Self {
202 value.0
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use anyhow::anyhow;
209 use bimap::BiHashMap;
210 use hex_literal::hex;
211 use hopr_types::{
212 crypto::prelude::*, crypto_random::Randomizable, internal::routing::HoprSenderId, primitive::prelude::*,
213 };
214
215 use super::*;
216 use crate::{
217 HoprSphinxHeaderSpec, HoprSphinxSuite, HoprSurb,
218 packet::HoprPacket,
219 por::{SurbReceiverInfo, generate_proof_of_relay},
220 sphinx::prelude::*,
221 };
222
223 lazy_static::lazy_static! {
224 static ref PEERS: [(ChainKeypair, OffchainKeypair); 4] = [
225 (hex!("a7c486ceccf5ab53bd428888ab1543dc2667abd2d5e80aae918da8d4b503a426"), hex!("5eb212d4d6aa5948c4f71574d45dad43afef6d330edb873fca69d0e1b197e906")),
226 (hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed"), hex!("e995db483ada5174666c46bafbf3628005aca449c94ebdc0c9239c3f65d61ae0")),
227 (hex!("ca4bdfd54a8467b5283a0216288fdca7091122479ccf3cfb147dfa59d13f3486"), hex!("9dec751c00f49e50fceff7114823f726a0425a68a8dc6af0e4287badfea8f4a4")),
228 (hex!("e306ebfb0d01d0da0952c9a567d758093a80622c6cb55052bf5f1a6ebd8d7b5c"), hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed"))
229 ].map(|(p1,p2)| (ChainKeypair::from_secret(&p1).expect("lazy static keypair should be valid"), OffchainKeypair::from_secret(&p2).expect("lazy static keypair should be valid")));
230
231 static ref MAPPER: SimpleBiMapper<HoprSphinxSuite, HoprSphinxHeaderSpec> = PEERS
232 .iter()
233 .enumerate()
234 .map(|(i, (_, k))| (KeyIdent::from(i as u32), *k.public()))
235 .collect::<BiHashMap<_, _>>()
236 .into();
237 }
238
239 fn generate_surbs(count: usize) -> anyhow::Result<Vec<SURB<HoprSphinxSuite, HoprSphinxHeaderSpec>>> {
240 let path = PEERS.iter().map(|(_, k)| *k.public()).collect::<Vec<_>>();
241 let path_ids = MAPPER
242 .map_keys_to_ids(&path)
243 .into_iter()
244 .map(|v| v.ok_or(anyhow!("missing id")))
245 .collect::<Result<Vec<_>, _>>()?;
246 let pseudonym = SimplePseudonym::random();
247 let recv_data = HoprSenderId::new(&pseudonym);
248
249 Ok((0..count)
250 .map(|_| {
251 let shared_keys = HoprSphinxSuite::new_shared_keys(&path)?;
252 let (por_strings, por_values) = generate_proof_of_relay(&shared_keys.secrets)
253 .map_err(|e| CryptoError::Other(GeneralError::NonSpecificError(e.to_string())))?;
254
255 create_surb::<HoprSphinxSuite, HoprSphinxHeaderSpec>(
256 shared_keys,
257 &path_ids,
258 &por_strings,
259 recv_data,
260 SurbReceiverInfo::new(por_values, [0u8; 32]),
261 )
262 .map(|(s, _)| s)
263 })
264 .collect::<Result<Vec<_>, _>>()?)
265 }
266
267 #[test]
268 fn hopr_packet_message_message_only() -> anyhow::Result<()> {
269 let parts_1 = HoprPacketParts {
270 surbs: vec![],
271 payload: b"test message".into(),
272 signals: PacketSignal::OutOfSurbs.into(),
273 };
274
275 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
276 assert_eq!(parts_1, parts_2);
277
278 Ok(())
279 }
280
281 #[test]
282 fn hopr_packet_message_surbs_only() -> anyhow::Result<()> {
283 let parts_1 = HoprPacketParts {
284 surbs: generate_surbs(2)?,
285 payload: Cow::default(),
286 signals: PacketSignal::OutOfSurbs.into(),
287 };
288
289 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
290 assert_eq!(parts_1, parts_2);
291
292 Ok(())
293 }
294
295 #[test]
296 fn hopr_packet_message_surbs_and_msg() -> anyhow::Result<()> {
297 let parts_1 = HoprPacketParts {
298 surbs: generate_surbs(2)?,
299 payload: b"test msg".into(),
300 signals: PacketSignal::OutOfSurbs.into(),
301 };
302
303 let parts_2: HoprPacketParts = HoprPacketMessage::try_from(parts_1.clone())?.try_into()?;
304 assert_eq!(parts_1, parts_2);
305
306 Ok(())
307 }
308
309 #[test]
310 fn hopr_packet_size_msg_size_limit() {
311 let res = HoprPacketMessage::try_from(HoprPacketParts {
312 surbs: vec![],
313 payload: (&[1u8; HoprPacket::PAYLOAD_SIZE + 1]).into(),
314 signals: None.into(),
315 });
316 assert!(res.is_err());
317 }
318
319 #[test]
320 fn hopr_packet_message_surbs_size_limit() -> anyhow::Result<()> {
321 let res = HoprPacketMessage::try_from(PacketParts {
322 surbs: generate_surbs(HoprPacketMessage::MAX_SURBS_PER_MESSAGE + 1)?,
323 payload: Cow::default(),
324 signals: None.into(),
325 });
326 assert!(res.is_err());
327
328 let res = HoprPacketMessage::try_from(HoprPacketParts {
329 surbs: generate_surbs(3)?,
330 payload: Cow::default(),
331 signals: None.into(),
332 });
333 assert!(res.is_err());
334
335 Ok(())
336 }
337
338 #[test]
339 fn hopr_packet_message_surbs_flag_limit() -> anyhow::Result<()> {
340 let res = HoprPacketMessage::try_from(PacketParts {
341 surbs: generate_surbs(3)?,
342 payload: Cow::default(),
343 signals: unsafe { PacketSignals::new_unchecked(16) },
344 });
345 assert!(res.is_err());
346
347 Ok(())
348 }
349
350 #[test]
351 fn hopr_packet_size_msg_and_surb_size_limit() -> anyhow::Result<()> {
352 let res = HoprPacketMessage::try_from(PacketParts {
353 surbs: generate_surbs(2)?,
354 payload: (&[1u8; HoprPacket::PAYLOAD_SIZE - 2 * HoprSurb::SIZE + 1]).into(),
355 signals: None.into(),
356 });
357 assert!(res.is_err());
358
359 Ok(())
360 }
361}