hopr_protocol_hopr/codec/
mod.rs

1mod decoder;
2mod encoder;
3
4pub use decoder::HoprDecoder;
5pub use encoder::{HoprEncoder, MAX_ACKNOWLEDGEMENTS_BATCH_SIZE};
6
7fn default_outgoing_win_prob() -> Option<hopr_internal_types::prelude::WinningProbability> {
8    Some(hopr_internal_types::prelude::WinningProbability::ALWAYS)
9}
10
11/// Configuration of [`HoprEncoder`] and [`HoprDecoder`].
12#[cfg_attr(feature = "serde", cfg_eval::cfg_eval, serde_with::serde_as)]
13#[derive(Clone, Copy, Debug, smart_default::SmartDefault, validator::Validate)]
14#[cfg_attr(
15    feature = "serde",
16    derive(serde::Serialize, serde::Deserialize),
17    serde(deny_unknown_fields)
18)]
19pub struct HoprCodecConfig {
20    /// Optional price of outgoing tickets.
21    ///
22    /// If not set, the network default will be used, which is the minimum allowed ticket price in the HOPR network.
23    #[cfg_attr(
24        feature = "serde",
25        serde(default),
26        serde_as(as = "Option<serde_with::DisplayFromStr>")
27    )]
28    pub outgoing_ticket_price: Option<hopr_primitive_types::balance::HoprBalance>,
29    /// Optional probability of winning an outgoing ticket.
30    ///
31    /// If not set, the network default will be used, which is the minimum allowed winning probability in the HOPR
32    /// network.
33    ///
34    /// The default is [`WinningProbability::ALWAYS`](hopr_internal_types::prelude::WinningProbability::ALWAYS).
35    #[cfg_attr(
36        feature = "serde",
37        serde(default = "default_outgoing_win_prob"),
38        serde_as(as = "Option<serde_with::DisplayFromStr>")
39    )]
40    #[default(default_outgoing_win_prob())]
41    pub outgoing_win_prob: Option<hopr_internal_types::prelude::WinningProbability>,
42}
43
44impl PartialEq for HoprCodecConfig {
45    fn eq(&self, other: &Self) -> bool {
46        self.outgoing_ticket_price.eq(&other.outgoing_ticket_price)
47            && match (self.outgoing_win_prob, other.outgoing_win_prob) {
48                (Some(a), Some(b)) => a.approx_eq(&b),
49                (None, None) => true,
50                _ => false,
51            }
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use std::sync::Arc;
58
59    use hopr_chain_connector::{
60        HoprBlockchainSafeConnector,
61        testing::{BlokliTestClient, StaticState},
62    };
63    use hopr_crypto_random::Randomizable;
64    use hopr_crypto_types::prelude::*;
65    use hopr_db_node::HoprNodeDb;
66    use hopr_internal_types::prelude::*;
67    use hopr_network_types::prelude::ResolvedTransportRouting;
68
69    use crate::{
70        HoprCodecConfig, HoprDecoder, HoprEncoder, HoprTicketProcessor, HoprTicketProcessorConfig, MemorySurbStore,
71        PacketDecoder, PacketEncoder, SurbStoreConfig, codec::encoder::MAX_ACKNOWLEDGEMENTS_BATCH_SIZE, utils::*,
72    };
73
74    type TestEncoder = HoprEncoder<
75        Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>,
76        MemorySurbStore,
77        HoprTicketProcessor<Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>, HoprNodeDb>,
78    >;
79
80    type TestDecoder = HoprDecoder<
81        Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>,
82        MemorySurbStore,
83        HoprTicketProcessor<Arc<HoprBlockchainSafeConnector<BlokliTestClient<StaticState>>>, HoprNodeDb>,
84    >;
85
86    pub fn create_encoder(sender: &Node) -> TestEncoder {
87        HoprEncoder::new(
88            sender.chain_key.clone(),
89            sender.chain_api.clone(),
90            MemorySurbStore::new(SurbStoreConfig::default()),
91            HoprTicketProcessor::new(
92                sender.chain_api.clone(),
93                sender.node_db.clone(),
94                sender.chain_key.clone(),
95                Hash::default(),
96                HoprTicketProcessorConfig::default(),
97            ),
98            Hash::default(),
99            HoprCodecConfig::default(),
100        )
101    }
102
103    pub fn create_decoder(receiver: &Node) -> TestDecoder {
104        HoprDecoder::new(
105            (receiver.offchain_key.clone(), receiver.chain_key.clone()),
106            receiver.chain_api.clone(),
107            MemorySurbStore::new(SurbStoreConfig::default()),
108            HoprTicketProcessor::new(
109                receiver.chain_api.clone(),
110                receiver.node_db.clone(),
111                receiver.chain_key.clone(),
112                Hash::default(),
113                HoprTicketProcessorConfig::default(),
114            ),
115            Hash::default(),
116            HoprCodecConfig::default(),
117        )
118    }
119
120    #[tokio::test]
121    async fn encode_decode_packet() -> anyhow::Result<()> {
122        let blokli_client = create_blokli_client()?;
123        let sender = create_node(0, &blokli_client).await?;
124        let receiver = create_node(1, &blokli_client).await?;
125
126        let encoder = create_encoder(&sender);
127        let decoder = create_decoder(&receiver);
128
129        let data = b"some random message to encode and decode";
130
131        let out_packet = encoder
132            .encode_packet(
133                data,
134                ResolvedTransportRouting::Forward {
135                    pseudonym: HoprPseudonym::random(),
136                    forward_path: ValidatedPath::direct(
137                        *receiver.offchain_key.public(),
138                        receiver.chain_key.public().to_address(),
139                    ),
140                    return_paths: vec![],
141                },
142                None,
143            )
144            .await?;
145
146        let in_packet = decoder
147            .decode(sender.offchain_key.public().into(), out_packet.data)
148            .await?;
149        let in_packet = in_packet.try_as_final().ok_or(anyhow::anyhow!("packet is not final"))?;
150
151        assert_eq!(data, in_packet.plain_text.as_ref());
152        Ok(())
153    }
154
155    #[tokio::test]
156    async fn encode_decode_packet_should_fail_for_too_long_messages() -> anyhow::Result<()> {
157        let blokli_client = create_blokli_client()?;
158        let sender = create_node(0, &blokli_client).await?;
159        let receiver = create_node(1, &blokli_client).await?;
160
161        let encoder = create_encoder(&sender);
162
163        let data = hopr_crypto_random::random_bytes::<2048>();
164
165        assert!(
166            encoder
167                .encode_packet(
168                    data,
169                    ResolvedTransportRouting::Forward {
170                        pseudonym: HoprPseudonym::random(),
171                        forward_path: ValidatedPath::direct(
172                            *receiver.offchain_key.public(),
173                            receiver.chain_key.public().to_address(),
174                        ),
175                        return_paths: vec![],
176                    },
177                    None,
178                )
179                .await
180                .is_err()
181        );
182
183        Ok(())
184    }
185
186    #[tokio::test]
187    async fn encode_decode_packet_on_relay() -> anyhow::Result<()> {
188        let blokli_client = create_blokli_client()?;
189        let sender = create_node(0, &blokli_client).await?;
190        let relay = create_node(1, &blokli_client).await?;
191        let receiver = create_node(2, &blokli_client).await?;
192
193        let sender_encoder = create_encoder(&sender);
194        let relay_decoder = create_decoder(&relay);
195        let receiver_decoder = create_decoder(&receiver);
196
197        let data = b"some random message to encode and decode";
198
199        let out_packet = sender_encoder
200            .encode_packet(
201                data,
202                ResolvedTransportRouting::Forward {
203                    pseudonym: HoprPseudonym::random(),
204                    forward_path: ValidatedPath::new(
205                        sender.chain_key.public().to_address(),
206                        vec![
207                            relay.chain_key.public().to_address(),
208                            receiver.chain_key.public().to_address(),
209                        ],
210                        &sender.chain_api.as_path_resolver(),
211                    )
212                    .await?,
213                    return_paths: vec![],
214                },
215                None,
216            )
217            .await?;
218
219        let fwd_packet = relay_decoder
220            .decode(sender.offchain_key.public().into(), out_packet.data)
221            .await?;
222        let fwd_packet = fwd_packet
223            .try_as_forwarded()
224            .ok_or(anyhow::anyhow!("packet is not forwarded"))?;
225
226        let in_packet = receiver_decoder
227            .decode(relay.offchain_key.public().into(), fwd_packet.data)
228            .await?;
229        let in_packet = in_packet.try_as_final().ok_or(anyhow::anyhow!("packet is not final"))?;
230
231        assert_eq!(data, in_packet.plain_text.as_ref());
232        Ok(())
233    }
234
235    #[tokio::test]
236    async fn encode_decode_acknowledgements() -> anyhow::Result<()> {
237        let blokli_client = create_blokli_client()?;
238        let sender = create_node(0, &blokli_client).await?;
239        let receiver = create_node(1, &blokli_client).await?;
240
241        let encoder = create_encoder(&sender);
242        let decoder = create_decoder(&receiver);
243
244        let acks = (0..MAX_ACKNOWLEDGEMENTS_BATCH_SIZE)
245            .map(|_| VerifiedAcknowledgement::random(&PEERS[0].1))
246            .collect::<Vec<_>>();
247        let out_packet = encoder.encode_acknowledgements(&acks, PEERS[1].1.public()).await?;
248
249        let in_packet = decoder
250            .decode(sender.offchain_key.public().into(), out_packet.data)
251            .await?;
252        let in_packet = in_packet
253            .try_as_acknowledgement()
254            .ok_or(anyhow::anyhow!("packet is not acknowledgement"))?;
255
256        assert_eq!(acks.len(), in_packet.received_acks.len());
257
258        for (i, ack) in in_packet.received_acks.into_iter().enumerate() {
259            let verified = ack.verify(PEERS[0].1.public())?;
260            assert_eq!(acks[i], verified);
261        }
262
263        Ok(())
264    }
265
266    #[tokio::test]
267    async fn encode_should_fail_on_too_many_acknowledgements() -> anyhow::Result<()> {
268        let blokli_client = create_blokli_client()?;
269        let sender = create_node(0, &blokli_client).await?;
270
271        let encoder = create_encoder(&sender);
272        let acks = (0..MAX_ACKNOWLEDGEMENTS_BATCH_SIZE + 1)
273            .map(|_| VerifiedAcknowledgement::random(&PEERS[0].1))
274            .collect::<Vec<_>>();
275
276        assert!(
277            encoder
278                .encode_acknowledgements(&acks, PEERS[1].1.public())
279                .await
280                .is_err()
281        );
282
283        Ok(())
284    }
285}