1use std::fmt::{Display, Formatter};
2
3use hopr_crypto_sphinx::prelude::*;
4use hopr_crypto_types::prelude::*;
5use hopr_internal_types::prelude::*;
6use hopr_path::{NonEmptyPath, TransportPath};
7use hopr_primitive_types::prelude::*;
8#[cfg(feature = "rayon")]
9use rayon::prelude::*;
10
11use crate::{
12 HoprPseudonym, HoprReplyOpener, HoprSphinxHeaderSpec, HoprSphinxSuite, HoprSurb, PAYLOAD_SIZE_INT,
13 errors::{
14 PacketError::{PacketConstructionError, PacketDecodingError},
15 Result,
16 },
17 por::{
18 ProofOfRelayString, ProofOfRelayValues, SurbReceiverInfo, derive_ack_key_share, generate_proof_of_relay,
19 pre_verify,
20 },
21 types::{HoprPacketMessage, HoprPacketParts, HoprSenderId, HoprSurbId, PacketSignals},
22};
23
24#[derive(Clone)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct PartialHoprPacket {
35 partial_packet: PartialPacket<HoprSphinxSuite, HoprSphinxHeaderSpec>,
36 surbs: Vec<HoprSurb>,
37 openers: Vec<HoprReplyOpener>,
38 ticket: Ticket,
39 next_hop: OffchainPublicKey,
40 ack_challenge: HalfKeyChallenge,
41}
42
43struct PathKeyData {
47 pub shared_keys: SharedKeys<<HoprSphinxSuite as SphinxSuite>::E, <HoprSphinxSuite as SphinxSuite>::G>,
49 pub por_strings: Vec<ProofOfRelayString>,
51 pub por_values: ProofOfRelayValues,
53}
54
55impl PathKeyData {
56 fn new(path: &[OffchainPublicKey]) -> Result<Self> {
57 let shared_keys = HoprSphinxSuite::new_shared_keys(path)?;
58 let (por_strings, por_values) = generate_proof_of_relay(&shared_keys.secrets)?;
59
60 Ok(Self {
61 shared_keys,
62 por_strings,
63 por_values,
64 })
65 }
66
67 fn iter_from_paths(paths: Vec<&[OffchainPublicKey]>) -> Result<impl Iterator<Item = Self>> {
71 #[cfg(not(feature = "rayon"))]
72 let paths = paths.into_iter();
73
74 #[cfg(feature = "rayon")]
75 let paths = paths.into_par_iter();
76
77 paths
78 .map(Self::new)
79 .collect::<Result<Vec<_>>>()
80 .map(|paths| paths.into_iter())
81 }
82}
83
84impl PartialHoprPacket {
85 pub fn new<M: KeyIdMapper<HoprSphinxSuite, HoprSphinxHeaderSpec>, P: NonEmptyPath<OffchainPublicKey> + Send>(
96 pseudonym: &HoprPseudonym,
97 routing: PacketRouting<P>,
98 chain_keypair: &ChainKeypair,
99 ticket: TicketBuilder,
100 mapper: &M,
101 domain_separator: &Hash,
102 ) -> Result<Self> {
103 match routing {
104 PacketRouting::ForwardPath {
105 forward_path,
106 return_paths,
107 } => {
108 let mut key_data = PathKeyData::iter_from_paths(
110 std::iter::once(forward_path.hops())
111 .chain(return_paths.iter().map(|p| p.hops()))
112 .collect(),
113 )?;
114
115 let PathKeyData {
116 shared_keys,
117 por_strings,
118 por_values,
119 } = key_data
120 .next()
121 .ok_or_else(|| PacketConstructionError("empty path".into()))?;
122
123 let receiver_data = HoprSenderId::new(pseudonym);
124
125 let (surbs, openers): (Vec<_>, Vec<_>) = key_data
129 .zip(return_paths)
130 .zip(receiver_data.into_sequence())
131 .map(|((key_data, rp), data)| create_surb_for_path((rp, key_data), data, mapper))
132 .collect::<Result<Vec<_>>>()?
133 .into_iter()
134 .unzip();
135
136 let ticket = ticket
138 .eth_challenge(por_values.ticket_challenge())
139 .build_signed(chain_keypair, domain_separator)?
140 .leak();
141
142 Ok(Self {
143 partial_packet: PartialPacket::<HoprSphinxSuite, HoprSphinxHeaderSpec>::new(
144 MetaPacketRouting::ForwardPath {
145 shared_keys,
146 forward_path: &forward_path,
147 receiver_data: &receiver_data,
148 additional_data_relayer: &por_strings,
149 no_ack: false,
150 },
151 mapper,
152 )?,
153 surbs,
154 openers,
155 ticket,
156 next_hop: forward_path[0],
157 ack_challenge: por_values.acknowledgement_challenge(),
158 })
159 }
160 PacketRouting::Surb(id, surb) => {
161 let ticket = ticket
163 .eth_challenge(surb.additional_data_receiver.proof_of_relay_values().ticket_challenge())
164 .build_signed(chain_keypair, domain_separator)?
165 .leak();
166
167 Ok(Self {
168 ticket,
169 next_hop: mapper.map_id_to_public(&surb.first_relayer).ok_or_else(|| {
170 PacketConstructionError(format!(
171 "failed to map key id {} to public key",
172 surb.first_relayer.to_hex()
173 ))
174 })?,
175 ack_challenge: surb
176 .additional_data_receiver
177 .proof_of_relay_values()
178 .acknowledgement_challenge(),
179 partial_packet: PartialPacket::<HoprSphinxSuite, HoprSphinxHeaderSpec>::new(
180 MetaPacketRouting::Surb(surb, &HoprSenderId::from_pseudonym_and_id(pseudonym, id)),
181 mapper,
182 )?,
183 surbs: vec![],
184 openers: vec![],
185 })
186 }
187 PacketRouting::NoAck(destination) => {
188 let PathKeyData {
190 shared_keys,
191 por_strings,
192 por_values,
193 ..
194 } = PathKeyData::new(&[destination])?;
195
196 let ticket = ticket
198 .eth_challenge(por_values.ticket_challenge())
199 .build_signed(chain_keypair, domain_separator)?
200 .leak();
201
202 Ok(Self {
203 partial_packet: PartialPacket::<HoprSphinxSuite, HoprSphinxHeaderSpec>::new(
204 MetaPacketRouting::ForwardPath {
205 shared_keys,
206 forward_path: &[destination],
207 receiver_data: &HoprSenderId::new(pseudonym),
208 additional_data_relayer: &por_strings,
209 no_ack: true, },
211 mapper,
212 )?,
213 ticket,
214 next_hop: destination,
215 ack_challenge: por_values.acknowledgement_challenge(),
216 surbs: vec![],
217 openers: vec![],
218 })
219 }
220 }
221 }
222
223 pub fn into_hopr_packet<S: Into<PacketSignals>>(
228 self,
229 msg: &[u8],
230 signals: S,
231 ) -> Result<(HoprPacket, Vec<HoprReplyOpener>)> {
232 let msg = HoprPacketMessage::try_from(HoprPacketParts {
233 surbs: self.surbs,
234 payload: msg.into(),
235 signals: signals.into(),
236 })?;
237 Ok((
238 HoprPacket::Outgoing(
239 HoprOutgoingPacket {
240 packet: self.partial_packet.into_meta_packet(msg.into()),
241 ticket: self.ticket,
242 next_hop: self.next_hop,
243 ack_challenge: self.ack_challenge,
244 }
245 .into(),
246 ),
247 self.openers,
248 ))
249 }
250}
251
252#[derive(Clone)]
254pub struct HoprIncomingPacket {
255 pub packet_tag: PacketTag,
257 pub ack_key: Option<HalfKey>,
262 pub previous_hop: OffchainPublicKey,
264 pub plain_text: Box<[u8]>,
266 pub sender: HoprPseudonym,
268 pub surbs: Vec<(HoprSurbId, HoprSurb)>,
270 pub signals: PacketSignals,
274}
275
276#[derive(Clone)]
278pub struct HoprOutgoingPacket {
279 pub packet: MetaPacket<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT>,
281 pub ticket: Ticket,
283 pub next_hop: OffchainPublicKey,
285 pub ack_challenge: HalfKeyChallenge,
287}
288
289#[derive(Clone)]
291pub struct HoprForwardedPacket {
292 pub outgoing: HoprOutgoingPacket,
294 pub packet_tag: PacketTag,
296 pub ack_key: HalfKey,
298 pub previous_hop: OffchainPublicKey,
300 pub own_key: HalfKey,
302 pub next_challenge: EthereumChallenge,
304 pub path_pos: u8,
306}
307
308#[derive(Clone, strum::EnumTryAs, strum::EnumIs)]
314pub enum HoprPacket {
315 Final(Box<HoprIncomingPacket>),
317 Forwarded(Box<HoprForwardedPacket>),
319 Outgoing(Box<HoprOutgoingPacket>),
321}
322
323impl Display for HoprPacket {
324 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
325 match &self {
326 Self::Final(_) => write!(f, "Final"),
327 Self::Forwarded(_) => write!(f, "Forwarded"),
328 Self::Outgoing(_) => write!(f, "Outgoing"),
329 }
330 }
331}
332
333#[derive(Clone)]
335pub enum PacketRouting<P: NonEmptyPath<OffchainPublicKey> = TransportPath> {
336 ForwardPath { forward_path: P, return_paths: Vec<P> },
340 Surb(HoprSurbId, HoprSurb),
342 NoAck(OffchainPublicKey),
345}
346
347fn create_surb_for_path<M: KeyIdMapper<HoprSphinxSuite, HoprSphinxHeaderSpec>, P: NonEmptyPath<OffchainPublicKey>>(
348 return_path: (P, PathKeyData),
349 recv_data: HoprSenderId,
350 mapper: &M,
351) -> Result<(HoprSurb, HoprReplyOpener)> {
352 let (
353 return_path,
354 PathKeyData {
355 shared_keys,
356 por_strings,
357 por_values,
358 },
359 ) = return_path;
360
361 Ok(create_surb::<HoprSphinxSuite, HoprSphinxHeaderSpec>(
362 shared_keys,
363 &return_path
364 .iter()
365 .map(|k| {
366 mapper
367 .map_key_to_id(k)
368 .ok_or_else(|| PacketConstructionError(format!("failed to map key {} to id", k.to_hex())))
369 })
370 .collect::<Result<Vec<_>>>()?,
371 &por_strings,
372 recv_data,
373 SurbReceiverInfo::new(por_values, [0u8; 32]),
374 )
375 .map(|(s, r)| (s, (recv_data.surb_id(), r)))?)
376}
377
378impl HoprPacket {
379 pub const MAX_SURBS_IN_PACKET: usize = HoprPacket::PAYLOAD_SIZE / HoprSurb::SIZE;
381 pub const PAYLOAD_SIZE: usize = PAYLOAD_SIZE_INT - HoprPacketMessage::HEADER_LEN;
385 pub const SIZE: usize =
387 MetaPacket::<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT>::PACKET_LEN + Ticket::SIZE;
388
389 #[allow(clippy::too_many_arguments)] pub fn into_outgoing<
405 M: KeyIdMapper<HoprSphinxSuite, HoprSphinxHeaderSpec>,
406 P: NonEmptyPath<OffchainPublicKey> + Send,
407 S: Into<PacketSignals>,
408 >(
409 msg: &[u8],
410 pseudonym: &HoprPseudonym,
411 routing: PacketRouting<P>,
412 chain_keypair: &ChainKeypair,
413 ticket: TicketBuilder,
414 mapper: &M,
415 domain_separator: &Hash,
416 signals: S,
417 ) -> Result<(Self, Vec<HoprReplyOpener>)> {
418 PartialHoprPacket::new(pseudonym, routing, chain_keypair, ticket, mapper, domain_separator)?
419 .into_hopr_packet(msg, signals)
420 }
421
422 pub const fn max_surbs_with_message(msg_len: usize) -> usize {
425 HoprPacket::PAYLOAD_SIZE.saturating_sub(msg_len) / HoprSurb::SIZE
426 }
427
428 pub const fn max_message_with_surbs(num_surbs: usize) -> usize {
431 HoprPacket::PAYLOAD_SIZE.saturating_sub(num_surbs * HoprSurb::SIZE)
432 }
433
434 pub fn from_incoming<M, F>(
437 data: &[u8],
438 node_keypair: &OffchainKeypair,
439 previous_hop: OffchainPublicKey,
440 mapper: &M,
441 reply_openers: F,
442 ) -> Result<Self>
443 where
444 M: KeyIdMapper<HoprSphinxSuite, HoprSphinxHeaderSpec>,
445 F: FnMut(&HoprSenderId) -> Option<ReplyOpener>,
446 {
447 if data.len() == Self::SIZE {
448 let (pre_packet, pre_ticket) =
449 data.split_at(MetaPacket::<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT>::PACKET_LEN);
450
451 let mp: MetaPacket<HoprSphinxSuite, HoprSphinxHeaderSpec, PAYLOAD_SIZE_INT> =
452 MetaPacket::try_from(pre_packet)?;
453
454 match mp.into_forwarded(node_keypair, mapper, reply_openers)? {
455 ForwardedMetaPacket::Relayed {
456 packet,
457 derived_secret,
458 additional_info,
459 packet_tag,
460 next_node,
461 path_pos,
462 ..
463 } => {
464 let ack_key = derive_ack_key_share(&derived_secret);
465
466 let ticket = Ticket::try_from(pre_ticket)?;
467 let verification_output = pre_verify(&derived_secret, &additional_info, &ticket.challenge)?;
468 Ok(Self::Forwarded(
469 HoprForwardedPacket {
470 outgoing: HoprOutgoingPacket {
471 packet,
472 ticket,
473 next_hop: next_node,
474 ack_challenge: verification_output.ack_challenge,
475 },
476 packet_tag,
477 ack_key,
478 previous_hop,
479 path_pos,
480 own_key: verification_output.own_key,
481 next_challenge: verification_output.next_ticket_challenge,
482 }
483 .into(),
484 ))
485 }
486 ForwardedMetaPacket::Final {
487 packet_tag,
488 plain_text,
489 derived_secret,
490 receiver_data,
491 no_ack,
492 } => {
493 let HoprPacketParts {
495 surbs,
496 payload,
497 signals,
498 } = HoprPacketMessage::from(plain_text).try_into()?;
499 let should_acknowledge = !no_ack;
500 Ok(Self::Final(
501 HoprIncomingPacket {
502 packet_tag,
503 ack_key: should_acknowledge.then(|| derive_ack_key_share(&derived_secret)),
504 previous_hop,
505 plain_text: payload.into(),
506 surbs: receiver_data.into_sequence().map(|d| d.surb_id()).zip(surbs).collect(),
507 sender: receiver_data.pseudonym(),
508 signals,
509 }
510 .into(),
511 ))
512 }
513 }
514 } else {
515 Err(PacketDecodingError("packet has invalid size".into()))
516 }
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use anyhow::{Context, bail};
523 use bimap::BiHashMap;
524 use hex_literal::hex;
525 use hopr_crypto_random::Randomizable;
526 use hopr_path::TransportPath;
527 use parameterized::parameterized;
528
529 use super::*;
530 use crate::types::PacketSignal;
531
532 lazy_static::lazy_static! {
533 static ref PEERS: [(ChainKeypair, OffchainKeypair); 5] = [
534 (hex!("a7c486ceccf5ab53bd428888ab1543dc2667abd2d5e80aae918da8d4b503a426"), hex!("5eb212d4d6aa5948c4f71574d45dad43afef6d330edb873fca69d0e1b197e906")),
535 (hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed"), hex!("e995db483ada5174666c46bafbf3628005aca449c94ebdc0c9239c3f65d61ae0")),
536 (hex!("ca4bdfd54a8467b5283a0216288fdca7091122479ccf3cfb147dfa59d13f3486"), hex!("9dec751c00f49e50fceff7114823f726a0425a68a8dc6af0e4287badfea8f4a4")),
537 (hex!("e306ebfb0d01d0da0952c9a567d758093a80622c6cb55052bf5f1a6ebd8d7b5c"), hex!("9a82976f7182c05126313bead5617c623b93d11f9f9691c87b1a26f869d569ed")),
538 (hex!("492057cf93e99b31d2a85bc5e98a9c3aa0021feec52c227cc8170e8f7d047775"), hex!("e0bf93e9c916104da00b1850adc4608bd7e9087bbd3f805451f4556aa6b3fd6e")),
539 ].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")));
540
541 static ref MAPPER: bimap::BiMap<KeyIdent, OffchainPublicKey> = PEERS
542 .iter()
543 .enumerate()
544 .map(|(i, (_, k))| (KeyIdent::from(i as u32), *k.public()))
545 .collect::<BiHashMap<_, _>>();
546 }
547
548 fn forward(
549 mut packet: HoprPacket,
550 chain_keypair: &ChainKeypair,
551 next_ticket: TicketBuilder,
552 domain_separator: &Hash,
553 ) -> HoprPacket {
554 if let HoprPacket::Forwarded(fwd) = &mut packet {
555 fwd.outgoing.ticket = next_ticket
556 .eth_challenge(fwd.next_challenge)
557 .build_signed(chain_keypair, domain_separator)
558 .expect("ticket should create")
559 .leak();
560 }
561
562 packet
563 }
564
565 impl HoprPacket {
566 pub fn to_bytes(&self) -> Box<[u8]> {
567 let dummy_ticket = hex!("67f0ca18102feec505e5bfedcc25963e9c64a6f8a250adcad7d2830dd607585700000000000000000000000000000000000000000000000000000000000000003891bf6fd4a78e868fc7ad477c09b16fc70dd01ea67e18264d17e3d04f6d8576de2e6472b0072e510df6e9fa1dfcc2727cc7633edfeb9ec13860d9ead29bee71d68de3736c2f7a9f42de76ccd57a5f5847bc7349");
568 let (packet, ticket) = match self {
569 Self::Final(packet) => (packet.plain_text.clone(), dummy_ticket.as_ref().into()),
570 Self::Forwarded(fwd) => (
571 Vec::from(fwd.outgoing.packet.as_ref()).into_boxed_slice(),
572 fwd.outgoing.ticket.clone().into_boxed(),
573 ),
574 Self::Outgoing(out) => (
575 Vec::from(out.packet.as_ref()).into_boxed_slice(),
576 out.ticket.clone().into_boxed(),
577 ),
578 };
579
580 let mut ret = Vec::with_capacity(Self::SIZE);
581 ret.extend_from_slice(packet.as_ref());
582 ret.extend_from_slice(&ticket);
583 ret.into_boxed_slice()
584 }
585 }
586
587 fn mock_ticket(
588 next_peer_channel_key: &PublicKey,
589 path_len: usize,
590 private_key: &ChainKeypair,
591 ) -> anyhow::Result<TicketBuilder> {
592 assert!(path_len > 0);
593 let price_per_packet: U256 = 10000000000000000u128.into();
594
595 if path_len > 1 {
596 Ok(TicketBuilder::default()
597 .direction(&private_key.public().to_address(), &next_peer_channel_key.to_address())
598 .amount(price_per_packet.div_f64(1.0)? * U256::from(path_len as u64 - 1))
599 .index(1)
600 .index_offset(1)
601 .win_prob(WinningProbability::ALWAYS)
602 .channel_epoch(1)
603 .eth_challenge(Default::default()))
604 } else {
605 Ok(TicketBuilder::zero_hop()
606 .direction(&private_key.public().to_address(), &next_peer_channel_key.to_address()))
607 }
608 }
609
610 const FLAGS: PacketSignal = PacketSignal::OutOfSurbs;
611
612 fn create_packet(
613 forward_hops: usize,
614 pseudonym: HoprPseudonym,
615 return_hops: Vec<usize>,
616 msg: &[u8],
617 ) -> anyhow::Result<(HoprPacket, Vec<HoprReplyOpener>)> {
618 assert!((0..=3).contains(&forward_hops), "forward hops must be between 1 and 3");
619 assert!(
620 return_hops.iter().all(|h| (0..=3).contains(h)),
621 "return hops must be between 1 and 3"
622 );
623
624 let ticket = mock_ticket(&PEERS[1].0.public(), forward_hops + 1, &PEERS[0].0)?;
625 let forward_path = TransportPath::new(PEERS[1..=forward_hops + 1].iter().map(|kp| *kp.1.public()))?;
626
627 let return_paths = return_hops
628 .into_iter()
629 .map(|h| TransportPath::new(PEERS[0..=h].iter().rev().map(|kp| *kp.1.public())))
630 .collect::<std::result::Result<Vec<_>, hopr_path::errors::PathError>>()?;
631
632 Ok(HoprPacket::into_outgoing(
633 msg,
634 &pseudonym,
635 PacketRouting::ForwardPath {
636 forward_path,
637 return_paths,
638 },
639 &PEERS[0].0,
640 ticket,
641 &*MAPPER,
642 &Hash::default(),
643 FLAGS,
644 )?)
645 }
646
647 fn create_packet_from_surb(
648 sender_node: usize,
649 surb_id: HoprSurbId,
650 surb: HoprSurb,
651 hopr_pseudonym: &HoprPseudonym,
652 msg: &[u8],
653 ) -> anyhow::Result<HoprPacket> {
654 assert!((1..=4).contains(&sender_node), "sender_node must be between 1 and 4");
655
656 let ticket = mock_ticket(
657 &PEERS[sender_node - 1].0.public(),
658 surb.additional_data_receiver.proof_of_relay_values().chain_length() as usize,
659 &PEERS[sender_node].0,
660 )?;
661
662 Ok(HoprPacket::into_outgoing(
663 msg,
664 hopr_pseudonym,
665 PacketRouting::<TransportPath>::Surb(surb_id, surb),
666 &PEERS[sender_node].0,
667 ticket,
668 &*MAPPER,
669 &Hash::default(),
670 FLAGS,
671 )?
672 .0)
673 }
674
675 fn process_packet_at_node<F>(
676 path_len: usize,
677 node_pos: usize,
678 is_reply: bool,
679 packet: HoprPacket,
680 openers: F,
681 ) -> anyhow::Result<HoprPacket>
682 where
683 F: FnMut(&HoprSenderId) -> Option<ReplyOpener>,
684 {
685 assert!((0..=4).contains(&node_pos), "node position must be between 1 and 3");
686
687 let prev_hop = match (node_pos, is_reply) {
688 (1, false) => *PEERS[0].1.public(),
689 (_, false) => *PEERS[node_pos - 1].1.public(),
690 (3, true) => *PEERS[4].1.public(),
691 (_, true) => *PEERS[node_pos + 1].1.public(),
692 };
693
694 let packet = HoprPacket::from_incoming(&packet.to_bytes(), &PEERS[node_pos].1, prev_hop, &*MAPPER, openers)
695 .context(format!("deserialization failure at hop {node_pos}"))?;
696
697 match &packet {
698 HoprPacket::Final(_) => Ok(packet),
699 HoprPacket::Forwarded(_) => {
700 let next_hop = match (node_pos, is_reply) {
701 (3, false) => PEERS[4].0.public().clone(),
702 (_, false) => PEERS[node_pos + 1].0.public().clone(),
703 (1, true) => PEERS[0].0.public().clone(),
704 (_, true) => PEERS[node_pos - 1].0.public().clone(),
705 };
706
707 let next_ticket = mock_ticket(&next_hop, path_len, &PEERS[node_pos].0)?;
708 Ok(forward(
709 packet.clone(),
710 &PEERS[node_pos].0,
711 next_ticket,
712 &Hash::default(),
713 ))
714 }
715 HoprPacket::Outgoing(_) => bail!("invalid packet state"),
716 }
717 }
718
719 #[parameterized(hops = { 0,1,2,3 })]
720 fn test_packet_forward_message_no_surb(hops: usize) -> anyhow::Result<()> {
721 let msg = b"some testing forward message";
722 let pseudonym = SimplePseudonym::random();
723 let (mut packet, opener) = create_packet(hops, pseudonym, vec![], msg)?;
724
725 assert!(opener.is_empty());
726 match &packet {
727 HoprPacket::Outgoing { .. } => {}
728 _ => bail!("invalid packet initial state"),
729 }
730
731 let mut actual_plain_text = Box::default();
732 for hop in 1..=hops + 1 {
733 packet = process_packet_at_node(hops + 1, hop, false, packet, |_| None)
734 .context(format!("packet decoding failed at hop {hop}"))?;
735
736 match &packet {
737 HoprPacket::Final(packet) => {
738 assert_eq!(hop - 1, hops, "final packet must be at the last hop");
739 assert!(packet.ack_key.is_some(), "must not be a no-ack packet");
740 assert_eq!(PacketSignals::from(FLAGS), packet.signals);
741 actual_plain_text = packet.plain_text.clone();
742 }
743 HoprPacket::Forwarded(fwd) => {
744 assert_eq!(PEERS[hop - 1].1.public(), &fwd.previous_hop, "invalid previous hop");
745 assert_eq!(PEERS[hop + 1].1.public(), &fwd.outgoing.next_hop, "invalid next hop");
746 assert_eq!(hops + 1 - hop, fwd.path_pos as usize, "invalid path position");
747 }
748 HoprPacket::Outgoing(_) => bail!("invalid packet state at hop {hop}"),
749 }
750 }
751
752 assert_eq!(actual_plain_text.as_ref(), msg, "invalid plaintext");
753 Ok(())
754 }
755
756 #[parameterized(forward_hops = { 0,1,2,3 }, return_hops = { 0, 1, 2, 3})]
757 fn test_packet_forward_message_with_surb(forward_hops: usize, return_hops: usize) -> anyhow::Result<()> {
758 let msg = b"some testing forward message";
759 let pseudonym = SimplePseudonym::random();
760 let (mut packet, openers) = create_packet(forward_hops, pseudonym, vec![return_hops], msg)?;
761
762 assert_eq!(1, openers.len(), "invalid number of openers");
763 match &packet {
764 HoprPacket::Outgoing { .. } => {}
765 _ => bail!("invalid packet initial state"),
766 }
767
768 let mut received_plain_text = Box::default();
769 let mut received_surbs = vec![];
770 for hop in 1..=forward_hops + 1 {
771 packet = process_packet_at_node(forward_hops + 1, hop, false, packet, |_| None)
772 .context(format!("packet decoding failed at hop {hop}"))?;
773
774 match &packet {
775 HoprPacket::Final(packet) => {
776 assert_eq!(hop - 1, forward_hops, "final packet must be at the last hop");
777 assert_eq!(pseudonym, packet.sender, "invalid sender");
778 assert!(packet.ack_key.is_some(), "must not be a no-ack packet");
779 assert_eq!(PacketSignals::from(FLAGS), packet.signals);
780 received_plain_text = packet.plain_text.clone();
781 received_surbs.extend(packet.surbs.clone());
782 }
783 HoprPacket::Forwarded(fwd) => {
784 assert_eq!(PEERS[hop - 1].1.public(), &fwd.previous_hop, "invalid previous hop");
785 assert_eq!(PEERS[hop + 1].1.public(), &fwd.outgoing.next_hop, "invalid next hop");
786 assert_eq!(forward_hops + 1 - hop, fwd.path_pos as usize, "invalid path position");
787 }
788 HoprPacket::Outgoing(_) => bail!("invalid packet state at hop {hop}"),
789 }
790 }
791
792 assert_eq!(received_plain_text.as_ref(), msg, "invalid plaintext");
793 assert_eq!(1, received_surbs.len(), "invalid number of surbs");
794 assert_eq!(
795 return_hops as u8 + 1,
796 received_surbs[0]
797 .1
798 .additional_data_receiver
799 .proof_of_relay_values()
800 .chain_length(),
801 "surb has invalid por chain length"
802 );
803
804 Ok(())
805 }
806
807 #[parameterized(
808 forward_hops = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 },
809 return_hops = { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 }
810 )]
811 fn test_packet_forward_and_reply_message(forward_hops: usize, return_hops: usize) -> anyhow::Result<()> {
812 let pseudonym = SimplePseudonym::random();
813
814 let fwd_msg = b"some testing forward message";
816 let (mut fwd_packet, mut openers) = create_packet(forward_hops, pseudonym, vec![return_hops], fwd_msg)?;
817
818 assert_eq!(1, openers.len(), "invalid number of openers");
819 match &fwd_packet {
820 HoprPacket::Outgoing { .. } => {}
821 _ => bail!("invalid packet initial state"),
822 }
823
824 let mut received_fwd_plain_text = Box::default();
825 let mut received_surbs = vec![];
826 for hop in 1..=forward_hops + 1 {
827 fwd_packet = process_packet_at_node(forward_hops + 1, hop, false, fwd_packet, |_| None)
828 .context(format!("packet decoding failed at hop {hop}"))?;
829
830 match &fwd_packet {
831 HoprPacket::Final(incoming) => {
832 assert_eq!(hop - 1, forward_hops, "final packet must be at the last hop");
833 assert_eq!(pseudonym, incoming.sender, "invalid sender");
834 assert!(incoming.ack_key.is_some(), "must not be a no-ack packet");
835 assert_eq!(PacketSignals::from(FLAGS), incoming.signals);
836 received_fwd_plain_text = incoming.plain_text.clone();
837 received_surbs.extend(incoming.surbs.clone());
838 }
839 HoprPacket::Forwarded(fwd) => {
840 assert_eq!(PEERS[hop - 1].1.public(), &fwd.previous_hop, "invalid previous hop");
841 assert_eq!(PEERS[hop + 1].1.public(), &fwd.outgoing.next_hop, "invalid next hop");
842 assert_eq!(forward_hops + 1 - hop, fwd.path_pos as usize, "invalid path position");
843 }
844 HoprPacket::Outgoing { .. } => bail!("invalid packet state at hop {hop}"),
845 }
846 }
847
848 assert_eq!(received_fwd_plain_text.as_ref(), fwd_msg, "invalid plaintext");
849 assert_eq!(1, received_surbs.len(), "invalid number of surbs");
850 assert_eq!(
851 return_hops as u8 + 1,
852 received_surbs[0]
853 .1
854 .additional_data_receiver
855 .proof_of_relay_values()
856 .chain_length(),
857 "surb has invalid por chain length"
858 );
859
860 let re_msg = b"some testing reply message";
862 let mut re_packet = create_packet_from_surb(
863 forward_hops + 1,
864 received_surbs[0].0,
865 received_surbs[0].1.clone(),
866 &pseudonym,
867 re_msg,
868 )?;
869
870 let mut openers_fn = |p: &HoprSenderId| {
871 assert_eq!(p.pseudonym(), pseudonym);
872 let opener = openers.pop();
873 assert!(opener.as_ref().is_none_or(|(id, _)| id == &p.surb_id()));
874 opener.map(|(_, opener)| opener)
875 };
876
877 match &re_packet {
878 HoprPacket::Outgoing { .. } => {}
879 _ => bail!("invalid packet initial state"),
880 }
881
882 let mut received_re_plain_text = Box::default();
883 for hop in (0..=return_hops).rev() {
884 re_packet = process_packet_at_node(return_hops + 1, hop, true, re_packet, &mut openers_fn)
885 .context(format!("packet decoding failed at hop {hop}"))?;
886
887 match &re_packet {
888 HoprPacket::Final(incoming) => {
889 assert_eq!(hop, 0, "final packet must be at the last hop");
890 assert_eq!(pseudonym, incoming.sender, "invalid sender");
891 assert!(incoming.ack_key.is_some(), "must not be a no-ack packet");
892 assert!(incoming.surbs.is_empty(), "must not receive surbs on reply");
893 assert_eq!(PacketSignals::from(FLAGS), incoming.signals);
894 received_re_plain_text = incoming.plain_text.clone();
895 }
896 HoprPacket::Forwarded(fwd) => {
897 assert_eq!(PEERS[hop + 1].1.public(), &fwd.previous_hop, "invalid previous hop");
898 assert_eq!(PEERS[hop - 1].1.public(), &fwd.outgoing.next_hop, "invalid next hop");
899 assert_eq!(hop, fwd.path_pos as usize, "invalid path position");
900 }
901 HoprPacket::Outgoing(_) => bail!("invalid packet state at hop {hop}"),
902 }
903 }
904
905 assert_eq!(received_re_plain_text.as_ref(), re_msg, "invalid plaintext");
906 Ok(())
907 }
908
909 #[parameterized(
910 forward_hops = { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 },
911 return_hops = { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3 }
912 )]
913 fn test_packet_surbs_only_and_reply_message(forward_hops: usize, return_hops: usize) -> anyhow::Result<()> {
914 let pseudonym = SimplePseudonym::random();
915
916 let (mut fwd_packet, mut openers) = create_packet(forward_hops, pseudonym, vec![return_hops; 2], &[])?;
918
919 assert_eq!(2, openers.len(), "invalid number of openers");
920 match &fwd_packet {
921 HoprPacket::Outgoing { .. } => {}
922 _ => bail!("invalid packet initial state"),
923 }
924
925 let mut received_surbs = vec![];
926 for hop in 1..=forward_hops + 1 {
927 fwd_packet = process_packet_at_node(forward_hops + 1, hop, false, fwd_packet, |_| None)
928 .context(format!("packet decoding failed at hop {hop}"))?;
929
930 match &fwd_packet {
931 HoprPacket::Final(incoming) => {
932 assert_eq!(hop - 1, forward_hops, "final packet must be at the last hop");
933 assert!(
934 incoming.plain_text.is_empty(),
935 "must not receive plaintext on surbs only packet"
936 );
937 assert!(incoming.ack_key.is_some(), "must not be a no-ack packet");
938 assert_eq!(2, incoming.surbs.len(), "invalid number of received surbs per packet");
939 assert_eq!(pseudonym, incoming.sender, "invalid sender");
940 assert_eq!(PacketSignals::from(FLAGS), incoming.signals);
941 received_surbs.extend(incoming.surbs.clone());
942 }
943 HoprPacket::Forwarded(fwd) => {
944 assert_eq!(PEERS[hop - 1].1.public(), &fwd.previous_hop, "invalid previous hop");
945 assert_eq!(PEERS[hop + 1].1.public(), &fwd.outgoing.next_hop, "invalid next hop");
946 assert_eq!(forward_hops + 1 - hop, fwd.path_pos as usize, "invalid path position");
947 }
948 HoprPacket::Outgoing { .. } => bail!("invalid packet state at hop {hop}"),
949 }
950 }
951
952 assert_eq!(2, received_surbs.len(), "invalid number of surbs");
953 for recv_surb in &received_surbs {
954 assert_eq!(
955 return_hops as u8 + 1,
956 recv_surb
957 .1
958 .additional_data_receiver
959 .proof_of_relay_values()
960 .chain_length(),
961 "surb has invalid por chain length"
962 );
963 }
964
965 let mut openers_fn = |p: &HoprSenderId| {
966 assert_eq!(p.pseudonym(), pseudonym);
967 let (id, opener) = openers.remove(0);
968 assert_eq!(id, p.surb_id());
969 Some(opener)
970 };
971
972 for (i, recv_surb) in received_surbs.into_iter().enumerate() {
974 let re_msg = format!("some testing reply message {i}");
975 let mut re_packet = create_packet_from_surb(
976 forward_hops + 1,
977 recv_surb.0,
978 recv_surb.1,
979 &pseudonym,
980 re_msg.as_bytes(),
981 )?;
982
983 match &re_packet {
984 HoprPacket::Outgoing { .. } => {}
985 _ => bail!("invalid packet initial state in reply {i}"),
986 }
987
988 let mut received_re_plain_text = Box::default();
989 for hop in (0..=return_hops).rev() {
990 re_packet = process_packet_at_node(return_hops + 1, hop, true, re_packet, &mut openers_fn)
991 .context(format!("packet decoding failed at hop {hop} in reply {i}"))?;
992
993 match &re_packet {
994 HoprPacket::Final(incoming) => {
995 assert_eq!(hop, 0, "final packet must be at the last hop for reply {i}");
996 assert!(incoming.ack_key.is_some(), "must not be a no-ack packet");
997 assert!(
998 incoming.surbs.is_empty(),
999 "must not receive surbs on reply for reply {i}"
1000 );
1001 assert_eq!(PacketSignals::from(FLAGS), incoming.signals);
1002 received_re_plain_text = incoming.plain_text.clone();
1003 }
1004 HoprPacket::Forwarded(fwd) => {
1005 assert_eq!(
1006 PEERS[hop + 1].1.public(),
1007 &fwd.previous_hop,
1008 "invalid previous hop in reply {i}"
1009 );
1010 assert_eq!(
1011 PEERS[hop - 1].1.public(),
1012 &fwd.outgoing.next_hop,
1013 "invalid next hop in reply {i}"
1014 );
1015 assert_eq!(hop, fwd.path_pos as usize, "invalid path position in reply {i}");
1016 }
1017 HoprPacket::Outgoing(_) => bail!("invalid packet state at hop {hop} in reply {i}"),
1018 }
1019 }
1020
1021 assert_eq!(
1022 received_re_plain_text.as_ref(),
1023 re_msg.as_bytes(),
1024 "invalid plaintext in reply {i}"
1025 );
1026 }
1027 Ok(())
1028 }
1029}