hopr_protocol_hopr/codec/
encoder.rs

1use hopr_api::chain::*;
2use hopr_crypto_packet::prelude::*;
3use hopr_crypto_types::{crypto_traits::Randomizable, prelude::*};
4use hopr_internal_types::prelude::*;
5use hopr_network_types::prelude::*;
6use hopr_primitive_types::prelude::*;
7
8use crate::{
9    HoprCodecConfig, OutgoingPacket, PacketEncoder, SurbStore, TicketCreationError, TicketTracker,
10    errors::HoprProtocolError,
11};
12
13/// Default [encoder](PacketEncoder) implementation for HOPR packets.
14pub struct HoprEncoder<Chain, S, T> {
15    chain_api: Chain,
16    surb_store: S,
17    tracker: T,
18    chain_key: ChainKeypair,
19    channels_dst: Hash,
20    cfg: HoprCodecConfig,
21}
22
23impl<Chain, S, T> HoprEncoder<Chain, S, T> {
24    /// Creates a new instance of the encoder.
25    pub fn new(
26        chain_key: ChainKeypair,
27        chain_api: Chain,
28        surb_store: S,
29        tracker: T,
30        channels_dst: Hash,
31        cfg: HoprCodecConfig,
32    ) -> Self {
33        Self {
34            chain_api,
35            surb_store,
36            tracker,
37            chain_key,
38            channels_dst,
39            cfg,
40        }
41    }
42}
43
44impl<Chain, S, T> HoprEncoder<Chain, S, T>
45where
46    Chain: ChainKeyOperations + ChainReadChannelOperations + ChainValues + Sync,
47    S: SurbStore,
48    T: TicketTracker + Sync,
49{
50    async fn encode_packet_internal<D: AsRef<[u8]> + Send + 'static, Sig: Into<PacketSignals> + Send + 'static>(
51        &self,
52        next_peer: OffchainPublicKey,
53        data: D,
54        num_hops: usize,
55        signals: Sig,
56        routing: PacketRouting<ValidatedPath>,
57        pseudonym: HoprPseudonym,
58    ) -> Result<OutgoingPacket, HoprProtocolError> {
59        let next_peer = self
60            .chain_api
61            .packet_key_to_chain_key(&next_peer)
62            .await
63            .map_err(|e| HoprProtocolError::ResolverError(e.into()))?
64            .ok_or(HoprProtocolError::KeyNotFound)?;
65
66        // Decide whether to create a multi-hop or a zero-hop ticket
67        let next_ticket = if num_hops > 1 {
68            let channel = self
69                .chain_api
70                .channel_by_parties(self.chain_key.as_ref(), &next_peer)
71                .await
72                .map_err(|e| HoprProtocolError::ResolverError(e.into()))?
73                .ok_or_else(|| HoprProtocolError::ChannelNotFound(*self.chain_key.as_ref(), next_peer))?;
74
75            let (outgoing_ticket_win_prob, outgoing_ticket_price) = self
76                .chain_api
77                .outgoing_ticket_values(self.cfg.outgoing_win_prob, self.cfg.outgoing_ticket_price)
78                .await
79                .map_err(|e| HoprProtocolError::ResolverError(e.into()))?;
80
81            self.tracker
82                .create_multihop_ticket(
83                    &channel,
84                    num_hops as u8,
85                    outgoing_ticket_win_prob,
86                    outgoing_ticket_price,
87                )
88                .await
89                .map_err(|e| match e {
90                    TicketCreationError::OutOfFunds(id, a) => HoprProtocolError::OutOfFunds(id, a),
91                    e => HoprProtocolError::TicketTrackerError(e.into()),
92                })?
93        } else {
94            TicketBuilder::zero_hop().counterparty(next_peer)
95        };
96
97        // Construct the outgoing packet
98        let chain_key = self.chain_key.clone();
99        let mapper = self.chain_api.key_id_mapper_ref().clone();
100        let domain_separator = self.channels_dst;
101        let (packet, openers) = hopr_parallelize::cpu::spawn_fifo_blocking(move || {
102            HoprPacket::into_outgoing(
103                data.as_ref(),
104                &pseudonym,
105                routing,
106                &chain_key,
107                next_ticket,
108                &mapper,
109                &domain_separator,
110                signals,
111            )
112        })
113        .await?;
114
115        // Store the reply openers under the given SenderId
116        // This is a no-op for reply packets
117        openers.into_iter().for_each(|(surb_id, opener)| {
118            self.surb_store
119                .insert_reply_opener(HoprSenderId::from_pseudonym_and_id(&pseudonym, surb_id), opener);
120        });
121
122        let out = packet.try_as_outgoing().ok_or(HoprProtocolError::InvalidState(
123            "cannot send out packet that is not outgoing",
124        ))?;
125
126        let mut transport_payload = Vec::with_capacity(HoprPacket::SIZE);
127        transport_payload.extend_from_slice(out.packet.as_ref());
128        transport_payload.extend_from_slice(&out.ticket.into_encoded());
129
130        Ok(OutgoingPacket {
131            next_hop: out.next_hop,
132            ack_challenge: out.ack_challenge,
133            data: transport_payload.into_boxed_slice(),
134        })
135    }
136}
137
138#[async_trait::async_trait]
139impl<Chain, S, T> PacketEncoder for HoprEncoder<Chain, S, T>
140where
141    Chain: ChainKeyOperations + ChainReadChannelOperations + ChainValues + Send + Sync,
142    S: SurbStore + Send + Sync,
143    T: TicketTracker + Send + Sync,
144{
145    type Error = HoprProtocolError;
146
147    async fn encode_packet<D: AsRef<[u8]> + Send + 'static, Sig: Into<PacketSignals> + Send + 'static>(
148        &self,
149        data: D,
150        routing: ResolvedTransportRouting,
151        signals: Sig,
152    ) -> Result<OutgoingPacket, Self::Error> {
153        // Get necessary packet routing values
154        let (next_peer, num_hops, pseudonym, routing) = match routing {
155            ResolvedTransportRouting::Forward {
156                pseudonym,
157                forward_path,
158                return_paths,
159            } => (
160                forward_path[0],
161                forward_path.num_hops(),
162                pseudonym,
163                PacketRouting::ForwardPath {
164                    forward_path,
165                    return_paths,
166                },
167            ),
168            ResolvedTransportRouting::Return(sender_id, surb) => {
169                let next = self
170                    .chain_api
171                    .key_id_mapper_ref()
172                    .map_id_to_public(&surb.first_relayer)
173                    .ok_or(HoprProtocolError::KeyNotFound)?;
174
175                (
176                    next,
177                    surb.additional_data_receiver.proof_of_relay_values().chain_length() as usize,
178                    sender_id.pseudonym(),
179                    PacketRouting::Surb(sender_id.surb_id(), surb),
180                )
181            }
182        };
183
184        self.encode_packet_internal(next_peer, data, num_hops, signals, routing, pseudonym)
185            .await
186    }
187
188    async fn encode_acknowledgement(
189        &self,
190        ack: VerifiedAcknowledgement,
191        peer: &OffchainPublicKey,
192    ) -> Result<OutgoingPacket, Self::Error> {
193        self.encode_packet_internal(
194            *peer,
195            ack.leak(),
196            0,
197            None,
198            PacketRouting::NoAck(*peer),
199            HoprPseudonym::random(),
200        )
201        .await
202    }
203}