Skip to main content

hopr_protocol_hopr/codec/
mod.rs

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