hopr_protocol_hopr/traits.rs
1use std::ops::Mul;
2
3use hopr_crypto_packet::prelude::*;
4use hopr_crypto_types::prelude::*;
5use hopr_internal_types::prelude::*;
6use hopr_network_types::prelude::*;
7use hopr_primitive_types::prelude::*;
8
9use crate::TicketCreationError;
10pub use crate::{
11 errors::IncomingPacketError,
12 types::{FoundSurb, IncomingPacket, OutgoingPacket, ResolvedAcknowledgement},
13};
14
15/// A trait defining the operations required to store and retrieve SURBs (Single Use Reply Blocks) and their reply
16/// openers.
17///
18/// The sending side stores the reply openers, whereas the SURBs are stored by the replying side
19/// of the communication.
20#[async_trait::async_trait]
21#[auto_impl::auto_impl(&, Box, Arc)]
22pub trait SurbStore {
23 /// Tries to find SURB using the given [`matcher`](SurbMatcher).
24 ///
25 /// This is used by the replying side when it is about to send a reply packet back
26 /// to the sender.
27 async fn find_surb(&self, matcher: SurbMatcher) -> Option<FoundSurb>;
28
29 /// Stores the `surbs` and associates them with the given [`pseudonym`](HoprPseudonym).
30 ///
31 /// This is used by the replying side when it receives packets containing SURBs from the sender
32 /// with the given `pseudonym`.
33 ///
34 /// Returns the total number of SURBs available for that `pseudonym`, including the newly inserted
35 /// ones.
36 async fn insert_surbs(&self, pseudonym: HoprPseudonym, surbs: Vec<(HoprSurbId, HoprSurb)>) -> usize;
37
38 /// Stores the given [`opener`](ReplyOpener) for the given [`sender_id`](HoprSenderId).
39 ///
40 /// This is done by the sending side, when it creates a packet containing a SURB to be delivered
41 /// to the replying side.
42 ///
43 /// The operation should happen reasonably fast, as it is called from the packet processing code.
44 fn insert_reply_opener(&self, sender_id: HoprSenderId, opener: ReplyOpener);
45
46 /// Tries to find a [`ReplyOpener`] given the [`sender_id`](HoprSenderId).
47 ///
48 /// This is done by the sending side of the original packet when the reply to that
49 /// packet is received and needs to be decrypted.
50 ///
51 /// The operation should happen reasonably fast, as it is called from the packet processing code.
52 fn find_reply_opener(&self, sender_id: &HoprSenderId) -> Option<ReplyOpener>;
53}
54
55/// Trait defining encoder for [outgoing HOPR packets](OutgoingPacket).
56///
57/// These operations are done directly by the packet processing pipeline before
58/// the outgoing packet is handled to the underlying p2p transport.
59#[async_trait::async_trait]
60#[auto_impl::auto_impl(&, Box, Arc)]
61pub trait PacketEncoder {
62 type Error: std::error::Error + Send + Sync + 'static;
63
64 /// Encodes the given `data` and [`signals`](PacketSignals) for sending.
65 ///
66 /// The `data` MUST be already correctly sized for HOPR packets, otherwise the operation
67 /// must fail.
68 async fn encode_packet<T: AsRef<[u8]> + Send + 'static, S: Into<PacketSignals> + Send + 'static>(
69 &self,
70 data: T,
71 routing: ResolvedTransportRouting,
72 signals: S,
73 ) -> Result<OutgoingPacket, Self::Error>;
74
75 /// Encodes the given [`VerifiedAcknowledgement`] as an outgoing packet to be sent to the given
76 /// [`peer`](OffchainPublicKey).
77 async fn encode_acknowledgement(
78 &self,
79 ack: VerifiedAcknowledgement,
80 peer: &OffchainPublicKey,
81 ) -> Result<OutgoingPacket, Self::Error>;
82}
83
84/// Trait defining decoder HOPR packets.
85///
86/// This operation is done directly by the packet processing pipeline after
87/// the underlying p2p transport hands over incoming data packets.
88#[async_trait::async_trait]
89#[auto_impl::auto_impl(&, Box, Arc)]
90pub trait PacketDecoder {
91 type Error: std::error::Error + Send + Sync + 'static;
92
93 /// Decodes the `data` received from the given [`sender`](PeerId)
94 /// returns the corresponding [`IncomingPacket`] if the decoding into a HOPR packet was successful.
95 async fn decode(&self, sender: PeerId, data: Box<[u8]>)
96 -> Result<IncomingPacket, IncomingPacketError<Self::Error>>;
97}
98
99/// Performs necessary processing of unacknowledged tickets in the HOPR packet processing pipeline.
100#[async_trait::async_trait]
101#[auto_impl::auto_impl(&, Box, Arc)]
102pub trait UnacknowledgedTicketProcessor {
103 type Error: std::error::Error + Send + Sync + 'static;
104
105 /// Inserts a verified unacknowledged ticket from a delivered packet into the internal storage.
106 ///
107 /// The [`ticket`](UnacknowledgedTicket) corresponds to the given [`challenge`](HalfKeyChallenge)
108 /// and awaits to be [acknowledged](UnacknowledgedTicketProcessor::acknowledge_ticket)
109 /// once an [`Acknowledgement`] is received from the `next_hop`.
110 async fn insert_unacknowledged_ticket(
111 &self,
112 next_hop: &OffchainPublicKey,
113 challenge: HalfKeyChallenge,
114 ticket: UnacknowledgedTicket,
115 ) -> Result<(), Self::Error>;
116
117 /// Finds and acknowledges previously inserted ticket, using an [`Acknowledgement`] from the
118 /// upstream [`peer`](OffchainPublicKey).
119 ///
120 /// This function must verify the given acknowledgement and find if it contains a solution
121 /// to a challenge of a previously [inserted ticket](UnacknowledgedTicketProcessor::insert_unacknowledged_ticket).
122 ///
123 /// On success, the [resolution](ResolvedAcknowledgement) contains a decision whether the corresponding previously
124 /// stored ticket was found, and whether it is winning (and thus also redeemable) or losing.
125 ///
126 /// Returns `Ok(None)` if no [`Acknowledgement`] from `peer` was
127 /// expected.
128 ///
129 /// Returns an error if acknowledgement was expected from `peer`, but a ticket with the challenge
130 /// corresponding to the [`Acknowledgement`] was not found.
131 async fn acknowledge_ticket(
132 &self,
133 peer: OffchainPublicKey,
134 ack: Acknowledgement,
135 ) -> Result<Option<ResolvedAcknowledgement>, Self::Error>;
136}
137
138/// Allows tracking ticket indices of outgoing channels and
139/// unrealized balances of incoming channels.
140#[async_trait::async_trait]
141#[auto_impl::auto_impl(&, Box, Arc)]
142pub trait TicketTracker {
143 type Error: std::error::Error + Send + Sync + 'static;
144
145 /// Gets the next ticket index for an outgoing ticket for the given channel.
146 async fn next_outgoing_ticket_index(&self, channel_id: &ChannelId, epoch: u32) -> Result<u64, Self::Error>;
147
148 /// Retrieves the unrealized balance of the given channel.
149 ///
150 /// This allows guarding from situations where the ticket issuer issues more tickets
151 /// than there's balance in the given channel.
152 async fn incoming_channel_unrealized_balance(
153 &self,
154 channel_id: &ChannelId,
155 epoch: u32,
156 ) -> Result<HoprBalance, Self::Error>;
157
158 /// Convenience function that allows creating multi-hop tickets.
159 async fn create_multihop_ticket(
160 &self,
161 channel: &ChannelEntry,
162 current_path_pos: u8,
163 winning_prob: WinningProbability,
164 ticket_price: HoprBalance,
165 ) -> Result<TicketBuilder, TicketCreationError<Self::Error>> {
166 // The next ticket is worth: price * remaining hop count / winning probability
167 let amount = HoprBalance::from(
168 ticket_price
169 .amount()
170 .mul(U256::from(current_path_pos - 1))
171 .div_f64(winning_prob.into())
172 .expect("winning probability is always less than or equal to 1"),
173 );
174
175 if channel.balance.lt(&amount) {
176 return Err(TicketCreationError::OutOfFunds(*channel.get_id(), amount));
177 }
178
179 let ticket_builder = TicketBuilder::default()
180 .counterparty(channel.destination)
181 .balance(amount)
182 .index(
183 self.next_outgoing_ticket_index(channel.get_id(), channel.channel_epoch)
184 .await
185 .map_err(TicketCreationError::Other)?,
186 )
187 .win_prob(winning_prob)
188 .channel_epoch(channel.channel_epoch);
189
190 Ok(ticket_builder)
191 }
192}