hopr_protocol_hopr/codec/
encoder.rs1use bytes::{BufMut, BytesMut};
2use hopr_api::{
3 chain::*,
4 types::{
5 crypto::{crypto_traits::Randomizable, prelude::*},
6 internal::prelude::*,
7 primitive::prelude::*,
8 },
9};
10use hopr_crypto_packet::prelude::*;
11use hopr_protocol_pix::EntryShareGenerator;
12
13use crate::{HoprCodecConfig, OutgoingPacket, PacketEncoder, SurbStore, errors::HoprProtocolError};
14
15pub const MAX_ACKNOWLEDGEMENTS_BATCH_SIZE: usize =
20 (HoprPacket::PAYLOAD_SIZE - size_of::<u16>()) / Acknowledgement::SIZE;
21
22pub struct HoprEncoder<Chain, G, S, T> {
24 chain_api: Chain,
25 surb_store: S,
26 ticket_factory: T,
27 chain_key: ChainKeypair,
28 channels_dst: Hash,
29 ssa_generator: G,
30 cfg: HoprCodecConfig,
31}
32
33impl<Chain, G, S, T> HoprEncoder<Chain, G, S, T> {
34 pub fn new(
36 chain_key: ChainKeypair,
37 chain_api: Chain,
38 surb_store: S,
39 ticket_factory: T,
40 channels_dst: Hash,
41 ssa_generator: G,
42 cfg: HoprCodecConfig,
43 ) -> Self {
44 Self {
45 chain_api,
46 surb_store,
47 ticket_factory,
48 chain_key,
49 channels_dst,
50 ssa_generator,
51 cfg,
52 }
53 }
54}
55
56impl<Chain, G, S, T> HoprEncoder<Chain, G, S, T>
57where
58 Chain: ChainKeyOperations + ChainReadChannelOperations + ChainReadTicketOperations + ChainValues + Sync,
59 G: EntryShareGenerator<HoprPixSpec>,
60 S: SurbStore,
61 T: hopr_api::tickets::TicketFactory + Sync,
62{
63 fn encode_packet_internal<D: AsRef<[u8]> + Send + 'static, Sig: Into<PacketSignals> + Send + 'static>(
64 &self,
65 next_peer: OffchainPublicKey,
66 data: D,
67 num_hops: usize,
68 signals: Sig,
69 routing: PacketRouting<ValidatedPath>,
70 pseudonym: HoprPseudonym,
71 ) -> Result<OutgoingPacket, HoprProtocolError> {
72 let next_peer = self
73 .chain_api
74 .packet_key_to_chain_key(&next_peer)
75 .map_err(HoprProtocolError::resolver)?
76 .ok_or(HoprProtocolError::KeyNotFound)?;
77
78 let next_ticket = if num_hops > 1 {
80 let channel = self
81 .chain_api
82 .channel_by_parties(self.chain_key.as_ref(), &next_peer)
83 .map_err(HoprProtocolError::resolver)?
84 .ok_or_else(|| HoprProtocolError::ChannelNotFound(*self.chain_key.as_ref(), next_peer))?;
85
86 let (outgoing_ticket_win_prob, outgoing_ticket_price) = self
87 .chain_api
88 .outgoing_ticket_values(self.cfg.outgoing_win_prob, self.cfg.outgoing_ticket_price)
89 .map_err(HoprProtocolError::resolver)?;
90
91 self.ticket_factory
92 .new_multihop_ticket(
93 &channel,
94 (num_hops as u8).try_into().expect("cannot fail due to num_hops > 1"),
95 outgoing_ticket_win_prob,
96 outgoing_ticket_price,
97 )
98 .map_err(HoprProtocolError::ticket_factory)?
99 } else {
100 TicketBuilder::zero_hop().counterparty(next_peer)
101 };
102
103 let (packet, openers) = HoprPacket::into_outgoing(
105 data.as_ref(),
106 &pseudonym,
107 routing,
108 &self.chain_key,
109 next_ticket,
110 self.chain_api.key_id_mapper_ref(),
111 &self.channels_dst,
112 &self.ssa_generator,
113 signals,
114 )?;
115
116 let mut minted_surbs = Vec::with_capacity(openers.len());
119 openers.into_iter().for_each(|(surb_id, opener)| {
120 minted_surbs.push(surb_id);
121 self.surb_store
122 .insert_reply_opener(HoprSenderId::from_pseudonym_and_id(&pseudonym, surb_id), opener);
123 });
124
125 let out = packet.try_as_outgoing().ok_or(HoprProtocolError::InvalidState(
126 "cannot send out packet that is not outgoing",
127 ))?;
128
129 let mut transport_payload = BytesMut::with_capacity(HoprPacket::SIZE);
130 transport_payload.put_slice(out.packet.as_ref());
131 transport_payload.put_slice(&out.ticket.into_encoded());
132
133 Ok(OutgoingPacket {
134 next_hop: out.next_hop,
135 ack_challenge: out.ack_challenge,
136 encrypted_pix_share: out.encrypted_pix_share,
137 data: transport_payload.freeze(),
138 minted_surbs,
139 })
140 }
141}
142
143impl<Chain, G, S, T> PacketEncoder for HoprEncoder<Chain, G, S, T>
144where
145 Chain: ChainKeyOperations + ChainReadChannelOperations + ChainReadTicketOperations + ChainValues + Send + Sync,
146 G: EntryShareGenerator<HoprPixSpec>,
147 S: SurbStore + Send + Sync,
148 T: hopr_api::tickets::TicketFactory + Send + Sync,
149{
150 type Error = HoprProtocolError;
151
152 #[tracing::instrument(skip_all, level = "trace")]
153 fn encode_packet<D: AsRef<[u8]> + Send + 'static, Sig: Into<PacketSignals> + Send + 'static>(
154 &self,
155 data: D,
156 routing: ResolvedTransportRouting<HoprSurb>,
157 signals: Sig,
158 generation: Option<u8>,
159 ) -> Result<OutgoingPacket, Self::Error> {
160 let (next_peer, num_hops, pseudonym, routing) = match routing {
162 ResolvedTransportRouting::Forward {
163 pseudonym,
164 forward_path,
165 return_paths,
166 } => (
167 forward_path[0],
168 forward_path.num_hops(),
169 pseudonym,
170 PacketRouting::ForwardPath {
171 forward_path,
172 return_paths,
173 generation: generation.unwrap_or_else(|| self.surb_store.current_generation(&pseudonym)),
179 },
180 ),
181 ResolvedTransportRouting::Return(sender_id, surb) => {
182 let next = self
183 .chain_api
184 .key_id_mapper_ref()
185 .map_id_to_public(&surb.first_relayer)
186 .ok_or(HoprProtocolError::KeyNotFound)?;
187
188 (
189 next,
190 surb.additional_data_receiver.proof_of_relay_values().chain_length() as usize,
191 sender_id.pseudonym(),
192 PacketRouting::Surb(sender_id, surb),
193 )
194 }
195 };
196
197 tracing::trace!(len = data.as_ref().len(), "encoding packet");
198 self.encode_packet_internal(next_peer, data, num_hops, signals, routing, pseudonym)
199 }
200
201 #[tracing::instrument(skip_all, level = "trace", fields(destination = destination.to_peerid_str()))]
202 fn encode_acknowledgements(
203 &self,
204 acks: &[VerifiedAcknowledgement],
205 destination: &OffchainPublicKey,
206 ) -> Result<OutgoingPacket, Self::Error> {
207 tracing::trace!(num_acks = acks.len(), "encoding acknowledgements");
208
209 let mut all_acks = Vec::<u8>::with_capacity(size_of::<u16>() + acks.len() * Acknowledgement::SIZE);
210 all_acks.extend((acks.len() as u16).to_be_bytes());
211 acks.iter().for_each(|ack| all_acks.extend(ack.leak().as_ref()));
212
213 self.encode_packet_internal(
214 *destination,
215 all_acks,
216 0,
217 None,
218 PacketRouting::NoAck(*destination),
219 HoprPseudonym::random(),
220 )
221 }
222}