Skip to main content

hopr_protocol_hopr/
traits.rs

1use bytes::Bytes;
2use hopr_api::types::{crypto::prelude::*, internal::prelude::*};
3use hopr_crypto_packet::prelude::*;
4
5pub use crate::{
6    errors::IncomingPacketError,
7    types::{FoundSurb, IncomingPacket, OutgoingPacket, ResolvedAcknowledgement, SurbInsertOutcome},
8};
9
10/// A trait defining the operations required to store and retrieve SURBs (Single Use Reply Blocks) and their reply
11/// openers.
12///
13/// The sending side stores the reply openers, whereas the SURBs are stored by the replying side
14/// of the communication.
15#[auto_impl::auto_impl(&, Box, Arc)]
16pub trait SurbStore {
17    /// Tries to find SURB using the given [`matcher`](SurbMatcher).
18    ///
19    /// This is used by the replying side when it is about to send a reply packet back
20    /// to the sender.
21    fn find_surb(&self, matcher: SurbMatcher) -> Option<FoundSurb>;
22
23    /// Stores the `surbs` and associates them with the given [`pseudonym`](HoprPseudonym).
24    ///
25    /// This is used by the replying side when it receives packets containing SURBs from the sender
26    /// with the given `pseudonym`.
27    ///
28    /// Returns the total number of SURBs available for that `pseudonym` including the newly inserted
29    /// ones, along with how many had to be dropped to make room. The eviction count must not be
30    /// discarded lightly: it is the only evidence that the sender is over-producing.
31    fn insert_surbs(&self, pseudonym: HoprPseudonym, surbs: Vec<(HoprSurbId, HoprSurb)>) -> SurbInsertOutcome;
32
33    /// Stores the given [`opener`](ReplyOpener) for the given [`sender_id`](HoprSenderId).
34    ///
35    /// This is done by the sending side, when it creates a packet containing a SURB to be delivered
36    /// to the replying side.
37    ///
38    /// The operation should happen reasonably fast, as it is called from the packet processing code.
39    fn insert_reply_opener(&self, sender_id: HoprSenderId, opener: ReplyOpener);
40
41    /// Tries to find a [`ReplyOpener`] given the [`sender_id`](HoprSenderId).
42    ///
43    /// This is done by the sending side of the original packet when the reply to that
44    /// packet is received and needs to be decrypted.
45    ///
46    /// The operation should happen reasonably fast, as it is called from the packet processing code.
47    fn find_reply_opener(&self, sender_id: &HoprSenderId) -> Option<ReplyOpener>;
48
49    /// Marks the edge from this node to `relayer` as unusable, so stored SURBs whose return path
50    /// starts there are no longer handed out.
51    ///
52    /// Used by the replying side once the payment channel towards `relayer` is closing or closed:
53    /// replying over it would only burn the SURB. SURBs with a direct return path are unaffected —
54    /// their first relayer is the final recipient and needs no channel.
55    ///
56    /// Defaults to a no-op, for stores that do not track edge validity.
57    fn invalidate_relayer(&self, _relayer: &HoprKeyIdent) {}
58
59    /// Reverts [`SurbStore::invalidate_relayer`] once the channel towards `relayer` re-opens.
60    ///
61    /// Defaults to a no-op, for stores that do not track edge validity.
62    fn revalidate_relayer(&self, _relayer: &HoprKeyIdent) {}
63}
64
65/// Trait defining encoder for [outgoing HOPR packets](OutgoingPacket).
66///
67/// These operations are done directly by the packet processing pipeline before
68/// the outgoing packet is handled to the underlying p2p transport.
69#[auto_impl::auto_impl(&, Box, Arc)]
70pub trait PacketEncoder {
71    type Error: std::error::Error + Send + Sync + 'static;
72
73    /// Encodes the given `data` and [`signals`](PacketSignals) for sending.
74    ///
75    /// The `data` MUST be already correctly sized for HOPR packets, otherwise the operation
76    /// must fail.
77    fn encode_packet<T: AsRef<[u8]> + Send + 'static, S: Into<PacketSignals> + Send + 'static>(
78        &self,
79        data: T,
80        routing: ResolvedTransportRouting<HoprSurb>,
81        signals: S,
82    ) -> Result<OutgoingPacket, Self::Error>;
83
84    /// Encodes the given vector of [`VerifiedAcknowledgements`](VerifiedAcknowledgement) as an outgoing packet to be
85    /// sent to the given [`destination`](OffchainPublicKey).
86    fn encode_acknowledgements(
87        &self,
88        acks: &[VerifiedAcknowledgement],
89        destination: &OffchainPublicKey,
90    ) -> Result<OutgoingPacket, Self::Error>;
91}
92
93/// Trait defining decoder HOPR packets.
94///
95/// This operation is done directly by the packet processing pipeline after
96/// the underlying p2p transport hands over incoming data packets.
97#[auto_impl::auto_impl(&, Box, Arc)]
98pub trait PacketDecoder {
99    type Error: std::error::Error + Send + Sync + 'static;
100
101    /// Decodes the `data` received from the given [`sender`](PeerId)
102    /// returns the corresponding [`IncomingPacket`] if the decoding into a HOPR packet was successful.
103    fn decode(&self, sender: PeerId, data: Bytes) -> Result<IncomingPacket, IncomingPacketError<Self::Error>>;
104}
105
106/// Defines errors returned by `UnacknowledgedTicketProcessor::acknowledge_ticket`.
107#[derive(Debug, thiserror::Error)]
108pub enum TicketAcknowledgementError<E> {
109    /// An acknowledgement from a peer was not expected.
110    #[error("acknowledgement from the peer was not expected")]
111    UnexpectedAcknowledgement,
112    /// An error occurred while processing the acknowledgement.
113    #[error(transparent)]
114    Inner(E),
115}
116
117impl<E> TicketAcknowledgementError<E> {
118    pub fn inner<F: Into<E>>(e: F) -> Self {
119        Self::Inner(e.into())
120    }
121}
122
123/// Performs necessary processing of unacknowledged tickets in the HOPR packet processing pipeline.
124#[auto_impl::auto_impl(&, Box, Arc)]
125pub trait UnacknowledgedTicketProcessor {
126    type Error: std::error::Error + Send + Sync + 'static;
127
128    /// Inserts a verified unacknowledged ticket from a delivered packet into the internal storage.
129    ///
130    /// The [`ticket`](UnacknowledgedTicket) corresponds to the given [`challenge`](HalfKeyChallenge)
131    /// and awaits to be [acknowledged](UnacknowledgedTicketProcessor::acknowledge_tickets)
132    /// once an [`Acknowledgement`] is received from the `next_hop`.
133    ///
134    /// This operation should be reasonably fast and should not block the main processing pipeline.
135    fn insert_unacknowledged_ticket(
136        &self,
137        next_hop: &OffchainPublicKey,
138        challenge: HalfKeyChallenge,
139        ticket: UnacknowledgedTicket,
140    ) -> Result<(), Self::Error>;
141
142    /// Finds and acknowledges previously inserted tickets, using incoming [`Acknowledgements`](Acknowledgement) from
143    /// the upstream [`peer`](OffchainPublicKey).
144    ///
145    /// Function should first check if any acknowledgements are expected from the given `peer`.
146    ///
147    /// Furthermore, the function must verify each given acknowledgement and find if it evaluates to any solutions
148    /// to challenges of previously [inserted tickets](UnacknowledgedTicketProcessor::insert_unacknowledged_ticket).
149    ///
150    /// On success, the [resolutions](ResolvedAcknowledgement) contain decisions whether the previously
151    /// stored ticket with a matching challenge was found, and whether it is winning (and thus also redeemable) or
152    /// losing.
153    /// Challenges for which tickets were not found are skipped.
154    ///
155    /// Must return [`TicketAcknowledgementError::UnexpectedAcknowledgement`] if no `Acknowledgements` from the given
156    /// `peer` was expected.
157    ///
158    /// This operation is expected to be somewhat long-running and significantly blocking.
159    fn acknowledge_tickets(
160        &self,
161        peer: OffchainPublicKey,
162        acks: Vec<Acknowledgement>,
163    ) -> Result<Vec<ResolvedAcknowledgement>, TicketAcknowledgementError<Self::Error>>;
164}