1use bloomfilter::Bloom;
2use ethers::utils::hex;
3use serde::{Deserialize, Serialize};
4use std::fmt::{Display, Formatter};
5use tracing::warn;
6
7use hopr_crypto_random::random_bytes;
8use hopr_crypto_types::prelude::*;
9use hopr_primitive_types::prelude::*;
10
11use crate::errors::{CoreTypesError, CoreTypesError::PayloadSizeExceeded, Result};
12use crate::tickets::UnacknowledgedTicket;
13
14pub const INTERMEDIATE_HOPS: usize = 3;
16
17pub const PAYLOAD_SIZE: usize = 496;
19
20pub const DEFAULT_MINIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0;
22
23pub const DEFAULT_MAXIMUM_INCOMING_TICKET_WIN_PROB: f64 = 1.0; pub const DEFAULT_OUTGOING_TICKET_WIN_PROB: f64 = 1.0;
29
30pub const LOWEST_POSSIBLE_WINNING_PROB: f64 = 0.00000001;
32
33pub type Tag = u16;
35
36pub const DEFAULT_APPLICATION_TAG: Tag = 0;
38
39#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
41pub struct Acknowledgement {
42 ack_signature: OffchainSignature,
43 pub ack_key_share: HalfKey,
44 validated: bool,
45}
46
47impl Acknowledgement {
48 pub fn new(ack_key_share: HalfKey, node_keypair: &OffchainKeypair) -> Self {
49 Self {
50 ack_signature: OffchainSignature::sign_message(ack_key_share.as_ref(), node_keypair),
51 ack_key_share,
52 validated: true,
53 }
54 }
55
56 pub fn random(offchain_keypair: &OffchainKeypair) -> Self {
58 Self::new(HalfKey::random(), offchain_keypair)
59 }
60
61 #[tracing::instrument(level = "debug", skip(self, sender_node_key))]
66 pub fn validate(&mut self, sender_node_key: &OffchainPublicKey) -> bool {
67 self.validated = self
68 .ack_signature
69 .verify_message(self.ack_key_share.as_ref(), sender_node_key);
70
71 self.validated
72 }
73
74 pub fn ack_challenge(&self) -> HalfKeyChallenge {
76 assert!(self.validated, "acknowledgement not validated");
77 self.ack_key_share.to_challenge()
78 }
79}
80
81#[allow(clippy::large_enum_variant)]
84#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
85pub enum PendingAcknowledgement {
86 WaitingAsSender,
88 WaitingAsRelayer(UnacknowledgedTicket),
90}
91
92const TAGBLOOM_BINCODE_CONFIGURATION: bincode::config::Configuration = bincode::config::standard()
93 .with_little_endian()
94 .with_variable_int_encoding();
95
96#[derive(Debug, Clone)]
97struct SerializableBloomWrapper(Bloom<PacketTag>);
98
99impl Serialize for SerializableBloomWrapper {
100 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
101 where
102 S: serde::Serializer,
103 {
104 bloomfilter::serialize(&self.0, serializer)
105 }
106}
107
108impl<'de> Deserialize<'de> for SerializableBloomWrapper {
109 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
110 where
111 D: serde::Deserializer<'de>,
112 {
113 bloomfilter::deserialize(deserializer).map(Self)
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct TagBloomFilter {
124 bloom: SerializableBloomWrapper,
125 count: usize,
126 capacity: usize,
127}
128
129impl TagBloomFilter {
130 const FALSE_POSITIVE_RATE: f64 = 0.00001_f64;
132
133 const DEFAULT_MAX_ITEMS: usize = 10_000_000;
136
137 pub fn count(&self) -> usize {
139 self.count
140 }
141
142 pub fn capacity(&self) -> usize {
143 self.capacity
144 }
145
146 pub fn set(&mut self, tag: &PacketTag) {
148 if self.count == self.capacity {
149 warn!("maximum number of items in the Bloom filter reached!");
150 self.bloom.0.clear();
151 self.count = 0;
152 }
153
154 self.bloom.0.set(tag);
155 self.count += 1;
156 }
157
158 pub fn check(&self, tag: &PacketTag) -> bool {
161 self.bloom.0.check(tag)
162 }
163
164 pub fn check_and_set(&mut self, tag: &PacketTag) -> bool {
166 if self.count == self.capacity {
168 let is_present = self.bloom.0.check(tag);
169 if !is_present {
170 warn!("maximum number of items in the Bloom filter reached!");
172 self.bloom.0.clear();
173 self.bloom.0.set(tag);
174 self.count = 1;
175 }
176 is_present
177 } else {
178 let was_present = self.bloom.0.check_and_set(tag);
180 if !was_present {
181 self.count += 1;
182 }
183 was_present
184 }
185 }
186
187 pub fn from_bytes(data: &[u8]) -> Result<Self> {
189 bincode::serde::borrow_decode_from_slice(data, TAGBLOOM_BINCODE_CONFIGURATION)
190 .map(|(v, _bytes)| v)
191 .map_err(|e| CoreTypesError::ParseError(e.to_string()))
192 }
193
194 pub fn to_bytes(&self) -> Box<[u8]> {
196 bincode::serde::encode_to_vec(self, TAGBLOOM_BINCODE_CONFIGURATION)
197 .expect("serialization of bloom filter must not fail")
198 .into_boxed_slice()
199 }
200
201 fn with_capacity(size: usize) -> Self {
202 Self {
203 bloom: SerializableBloomWrapper(
204 Bloom::new_for_fp_rate_with_seed(size, Self::FALSE_POSITIVE_RATE, &random_bytes())
205 .expect("bloom filter with the specified capacity is constructible"),
206 ),
207 count: 0,
208 capacity: size,
209 }
210 }
211}
212
213impl Default for TagBloomFilter {
214 fn default() -> Self {
215 Self::with_capacity(Self::DEFAULT_MAX_ITEMS)
216 }
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
221pub struct ApplicationData {
222 pub application_tag: Option<Tag>,
224 #[serde(with = "serde_bytes")]
225 pub plain_text: Box<[u8]>,
226}
227
228impl ApplicationData {
229 pub fn new(application_tag: Option<Tag>, plain_text: &[u8]) -> Result<Self> {
230 if plain_text.len() <= PAYLOAD_SIZE - Self::SIZE {
231 Ok(Self {
232 application_tag,
233 plain_text: plain_text.into(),
234 })
235 } else {
236 Err(PayloadSizeExceeded)
237 }
238 }
239
240 pub fn new_from_owned(application_tag: Option<Tag>, plain_text: Box<[u8]>) -> Result<Self> {
241 if plain_text.len() <= PAYLOAD_SIZE - Self::SIZE {
242 Ok(Self {
243 application_tag,
244 plain_text,
245 })
246 } else {
247 Err(PayloadSizeExceeded)
248 }
249 }
250}
251
252impl Display for ApplicationData {
253 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
254 write!(
255 f,
256 "({}): {}",
257 self.application_tag.unwrap_or(DEFAULT_APPLICATION_TAG),
258 hex::encode(&self.plain_text)
259 )
260 }
261}
262
263impl ApplicationData {
264 const SIZE: usize = 2; pub fn from_bytes(data: &[u8]) -> hopr_primitive_types::errors::Result<Self> {
267 if data.len() <= PAYLOAD_SIZE && data.len() >= Self::SIZE {
268 let mut tag = [0u8; 2];
269 tag.copy_from_slice(&data[0..2]);
270 let tag = u16::from_be_bytes(tag);
271 Ok(Self {
272 application_tag: if tag != DEFAULT_APPLICATION_TAG {
273 Some(tag)
274 } else {
275 None
276 },
277 plain_text: (&data[2..]).into(),
278 })
279 } else {
280 Err(GeneralError::ParseError("ApplicationData".into()))
281 }
282 }
283
284 pub fn to_bytes(&self) -> Box<[u8]> {
285 let mut buf = Vec::with_capacity(Self::SIZE + self.plain_text.len());
286 let tag = self.application_tag.unwrap_or(DEFAULT_APPLICATION_TAG);
287 buf.extend_from_slice(&tag.to_be_bytes());
288 buf.extend_from_slice(&self.plain_text);
289 buf.into_boxed_slice()
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296 use hopr_crypto_random::random_bytes;
297
298 use hex_literal::hex;
299
300 const PRIVATE_KEY: [u8; 32] = hex!("51d3003d908045a4d76d0bfc0d84f6ff946b5934b7ea6a2958faf02fead4567a");
301
302 #[test]
303 fn acknowledgement_binary_compatibility_with_the_v2_format() -> anyhow::Result<()> {
304 let offchain_kp = OffchainKeypair::from_secret(&PRIVATE_KEY)?;
305 let mut ack = Acknowledgement::new(HalfKey::default(), &offchain_kp);
306
307 assert!(ack.validate(offchain_kp.public()));
308
309 let buf = Vec::new();
310 let serialized = cbor4ii::serde::to_vec(buf, &ack)?;
311
312 const EXPECTED_V2_BINARY_REPRESENTATION_CBOR_HEX: [u8; 213] = hex!("a36d61636b5f7369676e6174757265a1697369676e617475726598401859182418be184818a218c318cb1869186018270218391853186c18ff18e018b518d9187b187900188218da184e1869187518ec1828181b081821187718bb0c18ba18f418331218ea187c1880182318d6189f189f18d7141876186a1890186b1885189718a718b9189018fc18bc18260918e318a5182a006d61636b5f6b65795f7368617265a164686b6579982000000000000000000000000000000000000000000000000000000000000000016976616c696461746564f5");
313
314 assert_eq!(&serialized, &EXPECTED_V2_BINARY_REPRESENTATION_CBOR_HEX);
315
316 Ok(())
317 }
318
319 #[test]
320 fn test_application_data() -> anyhow::Result<()> {
321 let ad_1 = ApplicationData::new(Some(10), &[0_u8, 1_u8])?;
322 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
323 assert_eq!(ad_1, ad_2);
324
325 let ad_1 = ApplicationData::new(None, &[])?;
326 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
327 assert_eq!(ad_1, ad_2);
328
329 let ad_1 = ApplicationData::new(Some(10), &[0_u8, 1_u8])?;
330 let ad_2 = ApplicationData::from_bytes(&ad_1.to_bytes())?;
331 assert_eq!(ad_1, ad_2);
332
333 Ok(())
334 }
335
336 const ZEROS_TAG: [u8; PACKET_TAG_LENGTH] = [0; PACKET_TAG_LENGTH];
337 const ONES_TAG: [u8; PACKET_TAG_LENGTH] = [1; PACKET_TAG_LENGTH];
338
339 #[test]
340 fn test_packet_tag_bloom_filter() -> anyhow::Result<()> {
341 let mut filter1 = TagBloomFilter::default();
342
343 let items = (0..10_000)
344 .map(|i| {
345 let mut ret = random_bytes::<{ hopr_crypto_types::types::PACKET_TAG_LENGTH }>();
346 ret[i % hopr_crypto_types::types::PACKET_TAG_LENGTH] = 0xaa; ret
348 })
349 .collect::<Vec<_>>();
350
351 items.iter().for_each(|item| filter1.set(item));
353
354 assert_eq!(items.len(), filter1.count(), "invalid number of items in bf");
355
356 let match_count_1 = items.iter().filter(|item| filter1.check(item)).count();
360
361 let filter2 = TagBloomFilter::from_bytes(&filter1.to_bytes())?;
362
363 let match_count_2 = items.iter().filter(|item| filter2.check(item)).count();
365
366 assert_eq!(
367 match_count_1, match_count_2,
368 "the number of false positives must be equal"
369 );
370 assert_eq!(filter1.count(), filter2.count(), "the number of items must be equal");
371
372 assert!(!filter1.check(&ZEROS_TAG), "bf 1 must not contain zero tag");
374 assert!(!filter2.check(&ZEROS_TAG), "bf 2 must not contain zero tag");
375
376 Ok(())
377 }
378
379 #[test]
380 fn tag_bloom_filter_count() {
381 let mut filter = TagBloomFilter::default();
382 assert!(!filter.check_and_set(&ZEROS_TAG));
383 assert_eq!(1, filter.count());
384
385 assert!(filter.check_and_set(&ZEROS_TAG));
386 assert_eq!(1, filter.count());
387
388 assert!(!filter.check_and_set(&ONES_TAG));
389 assert_eq!(2, filter.count());
390
391 assert!(filter.check_and_set(&ZEROS_TAG));
392 assert_eq!(2, filter.count());
393 }
394
395 #[test]
396 fn tag_bloom_filter_wrap_around() {
397 let mut filter = TagBloomFilter::with_capacity(1000);
398 for _ in 1..filter.capacity() {
399 let mut tag: PacketTag = hopr_crypto_random::random_bytes();
400 tag[0] = 0xaa; assert!(!filter.check_and_set(&tag));
402 }
403 assert_eq!(filter.capacity() - 1, filter.count());
405
406 assert!(!filter.check_and_set(&ZEROS_TAG));
408
409 assert_eq!(filter.capacity(), filter.count());
411 assert!(filter.check(&ZEROS_TAG));
412
413 assert!(filter.check_and_set(&ZEROS_TAG));
415 assert_eq!(filter.capacity(), filter.count());
416
417 assert!(!filter.check_and_set(&ONES_TAG));
419 assert_eq!(1, filter.count());
420 assert!(filter.check(&ONES_TAG));
421 }
422}