hopr_protocol_hopr/codec/
mod.rs1mod decoder;
2mod encoder;
3
4pub use decoder::HoprDecoder;
5pub use encoder::{HoprEncoder, MAX_ACKNOWLEDGEMENTS_BATCH_SIZE};
6
7#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_with::serde_as)]
9#[derive(Clone, Copy, Debug, smart_default::SmartDefault, validator::Validate)]
10#[cfg_attr(
11 feature = "serde",
12 derive(serde::Serialize, serde::Deserialize),
13 serde(deny_unknown_fields)
14)]
15pub struct HoprCodecConfig {
16 #[cfg_attr(
21 feature = "serde",
22 serde(default),
23 serde_as(as = "Option<serde_with::DisplayFromStr>")
24 )]
25 pub outgoing_ticket_price: Option<hopr_api::types::primitive::balance::HoprBalance>,
26 #[cfg_attr(
33 feature = "serde",
34 serde(default),
35 serde_as(as = "Option<serde_with::DisplayFromStr>")
36 )]
37 pub min_incoming_ticket_price: Option<hopr_api::types::primitive::balance::HoprBalance>,
38 #[cfg_attr(
43 feature = "serde",
44 serde(default),
45 serde_as(as = "Option<serde_with::DisplayFromStr>")
46 )]
47 pub outgoing_win_prob: Option<hopr_api::types::internal::prelude::WinningProbability>,
48}
49
50impl PartialEq for HoprCodecConfig {
51 fn eq(&self, other: &Self) -> bool {
52 self.outgoing_ticket_price.eq(&other.outgoing_ticket_price)
53 && match (self.outgoing_win_prob, other.outgoing_win_prob) {
54 (Some(a), Some(b)) => a.approx_eq(&b),
55 (None, None) => true,
56 _ => false,
57 }
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use std::sync::Arc;
64
65 use hopr_api::types::{
66 crypto::prelude::*,
67 crypto_random::Randomizable,
68 internal::{prelude::*, routing::ResolvedTransportRouting},
69 };
70 use hopr_chain_connector::{
71 HoprBlockchainSafeConnector,
72 testing::{BlokliTestClient, StaticState},
73 };
74 use hopr_ticket_manager::{HoprTicketFactory, MemoryStore};
75
76 use crate::{
77 HoprCodecConfig, HoprDecoder, HoprEncoder, MemorySurbStore, PacketDecoder, PacketEncoder, SurbStoreConfig,
78 codec::encoder::MAX_ACKNOWLEDGEMENTS_BATCH_SIZE, utils::*,
79 };
80
81 type TestEncoder = HoprEncoder<
82 Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>,
83 MemorySurbStore,
84 HoprTicketFactory<MemoryStore>,
85 >;
86
87 type TestDecoder = HoprDecoder<
88 Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>,
89 MemorySurbStore,
90 HoprTicketFactory<MemoryStore>,
91 >;
92
93 pub fn create_encoder(sender: &Node) -> TestEncoder {
94 HoprEncoder::new(
95 sender.chain_key.clone(),
96 sender.chain_api.clone(),
97 MemorySurbStore::new(SurbStoreConfig::default()),
98 HoprTicketFactory::new(MemoryStore::default()),
99 Hash::default(),
100 HoprCodecConfig::default(),
101 )
102 }
103
104 pub fn create_decoder(receiver: &Node) -> TestDecoder {
105 HoprDecoder::new(
106 (receiver.offchain_key.clone(), receiver.chain_key.clone()),
107 receiver.chain_api.clone(),
108 MemorySurbStore::new(SurbStoreConfig::default()),
109 HoprTicketFactory::new(MemoryStore::default()),
110 Hash::default(),
111 HoprCodecConfig::default(),
112 )
113 }
114
115 #[tokio::test]
116 async fn encode_decode_packet() -> anyhow::Result<()> {
117 let blokli_client = create_blokli_client()?;
118 let sender = create_node(0, &blokli_client).await?;
119 let receiver = create_node(1, &blokli_client).await?;
120
121 let encoder = create_encoder(&sender);
122 let decoder = create_decoder(&receiver);
123
124 let data = b"some random message to encode and decode";
125
126 let out_packet = encoder.encode_packet(
127 data,
128 ResolvedTransportRouting::Forward {
129 pseudonym: HoprPseudonym::random(),
130 forward_path: ValidatedPath::direct(
131 *receiver.offchain_key.public(),
132 receiver.chain_key.public().to_address(),
133 ),
134 return_paths: vec![],
135 },
136 None,
137 )?;
138
139 let in_packet = decoder.decode(sender.offchain_key.public().into(), out_packet.data)?;
140 let in_packet = in_packet.try_as_final().ok_or(anyhow::anyhow!("packet is not final"))?;
141
142 assert_eq!(data, in_packet.plain_text.as_ref());
143 Ok(())
144 }
145
146 #[tokio::test]
147 async fn encode_decode_packet_should_fail_for_too_long_messages() -> anyhow::Result<()> {
148 let blokli_client = create_blokli_client()?;
149 let sender = create_node(0, &blokli_client).await?;
150 let receiver = create_node(1, &blokli_client).await?;
151
152 let encoder = create_encoder(&sender);
153
154 let data = hopr_api::types::crypto_random::random_bytes::<2048>();
155
156 assert!(
157 encoder
158 .encode_packet(
159 data,
160 ResolvedTransportRouting::Forward {
161 pseudonym: HoprPseudonym::random(),
162 forward_path: ValidatedPath::direct(
163 *receiver.offchain_key.public(),
164 receiver.chain_key.public().to_address(),
165 ),
166 return_paths: vec![],
167 },
168 None,
169 )
170 .is_err()
171 );
172
173 Ok(())
174 }
175
176 #[tokio::test]
177 async fn encode_decode_packet_on_relay() -> anyhow::Result<()> {
178 let blokli_client = create_blokli_client()?;
179 let sender = create_node(0, &blokli_client).await?;
180 let relay = create_node(1, &blokli_client).await?;
181 let receiver = create_node(2, &blokli_client).await?;
182
183 let sender_encoder = create_encoder(&sender);
184 let relay_decoder = create_decoder(&relay);
185 let receiver_decoder = create_decoder(&receiver);
186
187 let data = b"some random message to encode and decode";
188
189 let out_packet = sender_encoder.encode_packet(
190 data,
191 ResolvedTransportRouting::Forward {
192 pseudonym: HoprPseudonym::random(),
193 forward_path: ValidatedPath::new(
194 sender.chain_key.public().to_address(),
195 vec![
196 relay.chain_key.public().to_address(),
197 receiver.chain_key.public().to_address(),
198 ],
199 &sender.chain_api.as_path_resolver(),
200 )
201 .await?,
202 return_paths: vec![],
203 },
204 None,
205 )?;
206
207 let fwd_packet = relay_decoder.decode(sender.offchain_key.public().into(), out_packet.data)?;
208 let fwd_packet = fwd_packet
209 .try_as_forwarded()
210 .ok_or(anyhow::anyhow!("packet is not forwarded"))?;
211
212 let in_packet = receiver_decoder.decode(relay.offchain_key.public().into(), fwd_packet.data)?;
213 let in_packet = in_packet.try_as_final().ok_or(anyhow::anyhow!("packet is not final"))?;
214
215 assert_eq!(data, in_packet.plain_text.as_ref());
216 Ok(())
217 }
218
219 #[tokio::test]
220 async fn encode_decode_acknowledgements() -> anyhow::Result<()> {
221 let blokli_client = create_blokli_client()?;
222 let sender = create_node(0, &blokli_client).await?;
223 let receiver = create_node(1, &blokli_client).await?;
224
225 let encoder = create_encoder(&sender);
226 let decoder = create_decoder(&receiver);
227
228 let acks = (0..MAX_ACKNOWLEDGEMENTS_BATCH_SIZE)
229 .map(|_| VerifiedAcknowledgement::random(&PEERS[0].1))
230 .collect::<Vec<_>>();
231 let out_packet = encoder.encode_acknowledgements(&acks, PEERS[1].1.public())?;
232
233 let in_packet = decoder.decode(sender.offchain_key.public().into(), out_packet.data)?;
234 let in_packet = in_packet
235 .try_as_acknowledgement()
236 .ok_or(anyhow::anyhow!("packet is not acknowledgement"))?;
237
238 assert_eq!(acks.len(), in_packet.received_acks.len());
239
240 for (i, ack) in in_packet.received_acks.into_iter().enumerate() {
241 let verified = ack.verify(PEERS[0].1.public())?;
242 assert_eq!(acks[i], verified);
243 }
244
245 Ok(())
246 }
247
248 #[tokio::test]
249 async fn encode_should_fail_on_too_many_acknowledgements() -> anyhow::Result<()> {
250 let blokli_client = create_blokli_client()?;
251 let sender = create_node(0, &blokli_client).await?;
252
253 let encoder = create_encoder(&sender);
254 let acks = (0..MAX_ACKNOWLEDGEMENTS_BATCH_SIZE + 1)
255 .map(|_| VerifiedAcknowledgement::random(&PEERS[0].1))
256 .collect::<Vec<_>>();
257
258 assert!(encoder.encode_acknowledgements(&acks, PEERS[1].1.public()).is_err());
259
260 Ok(())
261 }
262
263 #[tokio::test]
264 async fn decode_should_fail_on_invalid_data() -> anyhow::Result<()> {
265 let blokli_client = create_blokli_client()?;
266 let receiver = create_node(0, &blokli_client).await?;
267 let decoder = create_decoder(&receiver);
268
269 let sender_peer_id = PEERS[1].1.public().into();
270 let invalid_data = bytes::Bytes::from(vec![0u8; 100]);
271
272 let result = decoder.decode(sender_peer_id, invalid_data);
273 assert!(result.is_err());
274 assert!(result.unwrap_err().is_undecodable());
275
276 Ok(())
277 }
278
279 #[tokio::test]
280 async fn decode_should_fail_on_replay() -> anyhow::Result<()> {
281 let blokli_client = create_blokli_client()?;
282 let sender = create_node(0, &blokli_client).await?;
283 let receiver = create_node(1, &blokli_client).await?;
284
285 let encoder = create_encoder(&sender);
286 let decoder = create_decoder(&receiver);
287
288 let data = b"some message";
289 let out_packet = encoder.encode_packet(
290 data,
291 ResolvedTransportRouting::Forward {
292 pseudonym: HoprPseudonym::random(),
293 forward_path: ValidatedPath::direct(
294 *receiver.offchain_key.public(),
295 receiver.chain_key.public().to_address(),
296 ),
297 return_paths: vec![],
298 },
299 None,
300 )?;
301
302 decoder.decode(sender.offchain_key.public().into(), out_packet.data.clone())?;
304
305 let result = decoder.decode(sender.offchain_key.public().into(), out_packet.data);
307 assert!(result.is_err());
308 assert!(result.unwrap_err().is_processing_error());
309
310 Ok(())
311 }
312
313 #[tokio::test]
314 async fn decode_should_fail_on_incorrect_key() -> anyhow::Result<()> {
315 let blokli_client = create_blokli_client()?;
316 let sender = create_node(0, &blokli_client).await?;
317 let receiver = create_node(1, &blokli_client).await?;
318 let incorrect_receiver = create_node(2, &blokli_client).await?;
319
320 let encoder = create_encoder(&sender);
321 let decoder = create_decoder(&incorrect_receiver);
322
323 let data = b"some message";
324 let out_packet = encoder.encode_packet(
325 data,
326 ResolvedTransportRouting::Forward {
327 pseudonym: HoprPseudonym::random(),
328 forward_path: ValidatedPath::direct(
329 *receiver.offchain_key.public(),
330 receiver.chain_key.public().to_address(),
331 ),
332 return_paths: vec![],
333 },
334 None,
335 )?;
336
337 let result = decoder.decode(sender.offchain_key.public().into(), out_packet.data);
338 assert!(result.is_err());
339 assert!(result.unwrap_err().is_undecodable());
340
341 Ok(())
342 }
343}