minisketch_rs/lib.rs
1#![deny(missing_debug_implementations)]
2#![deny(missing_docs)]
3#![deny(unused_results)]
4#![deny(dead_code)]
5#![doc(html_root_url = "https://docs.rs/minisketch_rs/0.1.9")]
6
7//! # minisketch-rs
8//!
9//! `minisketch-rs` is a wrapper around [minisketch],
10//! a C++ library by [Pieter Wuille] for efficient set reconciliation.
11//!
12//! Minisketch is proposed as part of an [Erlay] technique for bandwidth-efficient TX propagation in Bitcoin.
13//!
14//! This library exposes type-safe Rust bindings for all minisketch functions by providing [`Minisketch`] structure.
15//!
16//! # Examples
17//!
18//! See the [examples] module.
19//!
20//! [examples]: examples/index.html
21//! [minisketch]: https://github.com/sipa/minisketch
22//! [`Minisketch`]: struct.Minisketch.html
23//! [Pieter Wuille]: https://github.com/sipa
24//! [Erlay]: https://arxiv.org/abs/1905.10518
25
26pub mod examples;
27
28use std::error::Error;
29use std::fmt::{Debug, Display, Formatter};
30use std::ops::BitXorAssign;
31
32/// Error that originates from `libminisketch`, with a message.
33#[derive(Debug)]
34pub struct MinisketchError(String);
35
36impl MinisketchError {
37 /// Creates an error with a message.
38 pub fn new(msg: &str) -> Self {
39 MinisketchError(msg.to_owned())
40 }
41}
42
43impl Error for MinisketchError {}
44impl Display for MinisketchError {
45 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
46 write!(f, "MinisketchError({})", self.0)
47 }
48}
49
50#[doc(hidden)]
51mod ffi {
52 #![allow(non_upper_case_globals)]
53 #![allow(non_camel_case_types)]
54 #![allow(non_snake_case)]
55
56 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
57
58 unsafe impl Send for minisketch {}
59}
60
61/// Describes decoded sketches and holding underlying opaque type inside.
62pub struct Minisketch {
63 inner: *mut ffi::minisketch,
64 bits: u32,
65 implementation: u32,
66 capacity: usize,
67}
68
69impl Minisketch {
70 /// Tries to create a new empty sketch.
71 ///
72 /// # Errors
73 ///
74 /// If the combination of `bits` and `implementation` is unavailable, or if
75 /// `capacity` is 0, an `Err(MinisketchError)` is returned.
76 ///
77 /// # Examples
78 ///
79 /// ```rust
80 /// use minisketch_rs::Minisketch;
81 /// let sketch = Minisketch::try_new(12, 0, 4)?;
82 /// # Ok::<(), minisketch_rs::MinisketchError>(())
83 /// ```
84 pub fn try_new(
85 bits: u32,
86 implementation: u32,
87 capacity: usize,
88 ) -> Result<Self, MinisketchError> {
89 let inner = unsafe { ffi::minisketch_create(bits, implementation, capacity) };
90
91 if !inner.is_null() {
92 Ok(Minisketch {
93 inner,
94 bits,
95 implementation,
96 capacity,
97 })
98 } else {
99 Err(MinisketchError::new("Unsupported minisketch parameters"))
100 }
101 }
102
103 /// Determine whether support for elements of size of `bits` bits was compiled in.
104 pub fn bits_supported(bits: u32) -> bool {
105 let res = unsafe { ffi::minisketch_bits_supported(bits) };
106 res != 0
107 }
108
109 /// Determine the maximum number of implementations available.
110 ///
111 /// Multiple implementations may be available for a given element size, with
112 /// different performance characteristics on different hardware.
113 ///
114 /// Each implementation is identified by a number from 0 to the output of this
115 /// function call, inclusive. Note that not every combination of implementation
116 /// and element size may exist.
117 pub fn implementation_max() -> u32 {
118 unsafe { ffi::minisketch_implementation_max() }
119 }
120
121 /// Returns element size in a sketch in bits.
122 pub fn bits(&self) -> u32 {
123 unsafe { ffi::minisketch_bits(self.inner) }
124 }
125
126 /// Returns capacity of a sketch in number of elements.
127 pub fn capacity(&self) -> usize {
128 unsafe { ffi::minisketch_capacity(self.inner) }
129 }
130
131 /// Returns implementation version number.
132 pub fn implementation(&self) -> u32 {
133 unsafe { ffi::minisketch_implementation(self.inner) }
134 }
135
136 /// Returns the size in bytes for serializing a given sketch.
137 pub fn serialized_size(&self) -> usize {
138 unsafe { ffi::minisketch_serialized_size(self.inner) }
139 }
140
141 /// Adds a `u64` element to a sketch.
142 ///
143 /// If the element to be added is too large for the sketch, the most significant
144 /// bits of the element are dropped. More precisely, if the element size of
145 /// `sketch` is b bits, then this function adds the unsigned integer represented
146 /// by the b least significant bits of `element` to `sketch`.
147 ///
148 /// If the element to be added is 0 (after potentially dropping the most significant
149 /// bits), then this function is a no-op. Sketches cannot contain an element with
150 /// the value 0.
151 ///
152 /// Note that adding the same element a second time removes it again, as sketches have
153 /// set semantics, not multiset semantics.
154 ///
155 /// # Examples
156 ///
157 /// ```rust
158 /// use minisketch_rs::Minisketch;
159 /// let mut sketch = Minisketch::try_new(12, 0, 4)?;
160 /// sketch.add(42);
161 /// # Ok::<(), minisketch_rs::MinisketchError>(())
162 /// ```
163 pub fn add(&mut self, element: u64) {
164 unsafe { ffi::minisketch_add_uint64(self.inner, element) }
165 }
166
167 /// Set the seed for randomizing algorithm choices to a fixed value.
168 ///
169 /// By default, sketches are initialized with a random seed. This is important
170 /// to avoid scenarios where an attacker could force worst-case behavior.
171 ///
172 /// This function initializes the seed to a user-provided value (any 64-bit
173 /// integer is acceptable, regardless of field size).
174 ///
175 /// When seed is `std::u64::MAX`, a fixed internal value with predictable behavior is used.
176 /// It is only intended for testing.
177 ///
178 /// # Examples
179 ///
180 /// ```rust
181 /// use minisketch_rs::Minisketch;
182 /// let mut sketch = Minisketch::try_new(12, 0, 4)?;
183 /// sketch.set_seed(42);
184 /// # Ok::<(), minisketch_rs::MinisketchError>(())
185 /// ```
186 pub fn set_seed(&mut self, seed: u64) {
187 unsafe { ffi::minisketch_set_seed(self.inner, seed) }
188 }
189
190 /// Merge the elements of another sketch into this sketch.
191 ///
192 /// After merging, `sketch` will contain every element that existed in one but not
193 /// both of the input sketches. It can be seen as an exclusive or operation on
194 /// the set elements. If the capacity of `other_sketch` is lower than `sketch`'s,
195 /// merging reduces the capacity of `sketch` to that of `other_sketch`.
196 ///
197 /// Returns the `Ok(capacity)` of `sketch` after merging has been performed (where this capacity
198 /// is at least 1)
199 ///
200 /// It is also possible to perform this operation directly on the serializations
201 /// of two sketches with the same element size and capacity by performing a bitwise XOR
202 /// of the serializations.
203 ///
204 /// You can also merge two sketches by doing xor-assignment (`^=`).
205 ///
206 /// # Errors
207 ///
208 /// Returns `Err(MinisketchError)` to indicate that merging has failed
209 /// because the two input sketches differ in their element size or implementation. If `Err` is
210 /// returned, `sketch` (and its capacity) have not been modified.
211 ///
212 /// # Examples
213 ///
214 /// ```rust
215 /// use minisketch_rs::Minisketch;
216 /// let mut sketch_a = Minisketch::try_new(12, 0, 4)?;
217 /// sketch_a.add(10);
218 /// sketch_a.add(43);
219 ///
220 /// let mut sketch_b = Minisketch::try_new(12, 0, 4)?;
221 /// sketch_b.add(42);
222 /// sketch_b.add(43);
223 ///
224 /// // Merge two sketches and extract a difference
225 /// let num_diffs = sketch_a.merge(&sketch_b)?;
226 ///
227 /// // Extract difference
228 /// let mut differences = vec![0u64; num_diffs];
229 /// sketch_a.decode(&mut differences)?;
230 ///
231 /// assert!((differences[0] == 42 || differences[0] == 10) && (differences[1] == 10 || differences[1] == 42));
232 ///
233 /// # Ok::<(), minisketch_rs::MinisketchError>(())
234 /// ```
235 pub fn merge(&mut self, other: &Self) -> Result<usize, MinisketchError> {
236 let capacity = unsafe { ffi::minisketch_merge(self.inner, other.inner) };
237
238 if capacity == 0 {
239 Err(MinisketchError::new("Merge is failed"))
240 } else {
241 Ok(capacity)
242 }
243 }
244
245 /// Decode a sketch.
246 ///
247 /// `elements` is a mutable reference to a buffer of `u64`, which will be filled with the
248 /// elements in this sketch.
249 ///
250 /// Returns `Ok(num. of decoded elements)`
251 ///
252 /// # Errors
253 ///
254 /// Returns `Err(MinisketchError)` if decoding failed for any reason.
255 ///
256 /// # Examples
257 ///
258 /// ```rust
259 /// use minisketch_rs::Minisketch;
260 /// let mut sketch = Minisketch::try_new(12, 0, 2)?;
261 /// sketch.add(42);
262 /// sketch.add(10);
263 /// let mut elements = [0u64; 2];
264 /// sketch.decode(&mut elements)?;
265 ///
266 /// // Elements may come in arbitrary order, so check all possible variants
267 /// assert!((elements[0] == 42 || elements[0] == 10) && (elements[1] == 10 || elements[1] == 42));
268 /// # Ok::<(), minisketch_rs::MinisketchError>(())
269 /// ```
270 pub fn decode(&self, elements: &mut [u64]) -> Result<usize, MinisketchError> {
271 let result =
272 unsafe { ffi::minisketch_decode(self.inner, elements.len(), elements.as_mut_ptr()) };
273
274 if result == -1 {
275 Err(MinisketchError::new("Sketch decoding failed"))
276 } else {
277 Ok(result as usize)
278 }
279 }
280
281 /// Deserialize a sketch from bytes.
282 ///
283 /// # Examples
284 ///
285 /// ```rust
286 /// use minisketch_rs::Minisketch;
287 ///
288 /// // Create Alice's sketch
289 /// let mut sketch_alice = Minisketch::try_new(12, 0, 2)?;
290 /// sketch_alice.add(42);
291 /// sketch_alice.add(10);
292 ///
293 /// // Serialize sketch on Alice's side
294 /// let mut message = vec![0u8; sketch_alice.serialized_size()];
295 /// sketch_alice.serialize(&mut message);
296 ///
297 /// // ... message is sent from Alice to Bob ...
298 ///
299 /// // Deserialize sketch from Alice on Bob's side
300 /// let mut sketch_bob = Minisketch::try_new(12, 0, 2)?;
301 /// sketch_bob.deserialize(&message);
302 ///
303 /// // Decode received sketch
304 /// let mut elements = [0u64; 2];
305 /// sketch_bob.decode(&mut elements)?;
306 /// // Elements may come in arbitrary order, so check all possible variants
307 /// assert!((elements[0] == 42 || elements[0] == 10) && (elements[1] == 10 || elements[1] == 42));
308 /// # Ok::<(), minisketch_rs::MinisketchError>(())
309 /// ```
310 pub fn deserialize(&mut self, buf: &[u8]) {
311 unsafe { ffi::minisketch_deserialize(self.inner, buf.as_ptr()) }
312 }
313
314 /// Serialize a sketch to bytes.
315 ///
316 /// # Errors
317 ///
318 /// Returns `Err(MinisketchError)` if `.len()` of the provided buffer `buf` is less than a size in bytes of
319 /// the serialized representation of the sketch.
320 ///
321 /// # Examples
322 ///
323 /// ```rust
324 /// use minisketch_rs::Minisketch;
325 /// let mut sketch = Minisketch::try_new(12, 0, 2)?;
326 /// sketch.add(42);
327 /// sketch.add(10);
328 ///
329 /// let mut buf = vec![0u8; sketch.serialized_size()];
330 /// sketch.serialize(&mut buf);
331 /// # Ok::<(), minisketch_rs::MinisketchError>(())
332 /// ```
333 pub fn serialize(&self, buf: &mut [u8]) -> Result<(), MinisketchError> {
334 let size = self.serialized_size();
335
336 if size < buf.len() {
337 return Err(MinisketchError::new("Invalid size of the output buffer"));
338 }
339
340 unsafe { ffi::minisketch_serialize(self.inner, buf.as_mut_ptr()) }
341 Ok(())
342 }
343}
344
345/// Custom `Debug` implementation that shows basic information about opaque `minisketch`.
346impl Debug for Minisketch {
347 fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
348 write!(
349 f,
350 "Minisketch {{ bits = {}, implementation = {}, capacity = {} }}",
351 self.bits(),
352 self.implementation(),
353 self.capacity(),
354 )
355 }
356}
357
358/// Custom `Drop` implementation that frees an underlying opaque sketch.
359#[doc(hidden)]
360impl Drop for Minisketch {
361 fn drop(&mut self) {
362 unsafe {
363 ffi::minisketch_destroy(self.inner);
364 }
365 }
366}
367
368/// Custom `Clone` implementation that clones an underlying opaque sketch.
369#[doc(hidden)]
370impl Clone for Minisketch {
371 fn clone(&self) -> Self {
372 let inner = unsafe { ffi::minisketch_clone(self.inner) };
373
374 Minisketch {
375 inner,
376 bits: self.bits,
377 implementation: self.implementation,
378 capacity: self.capacity,
379 }
380 }
381}
382
383/// Custom `^=` operator implementation on two sketches that performs merging.
384///
385/// # Example
386///
387/// ```rust
388/// use minisketch_rs::Minisketch;
389/// let mut sketch_a = Minisketch::try_new(12, 0, 4)?;
390/// sketch_a.add(10);
391/// sketch_a.add(43);
392///
393/// let mut sketch_b = Minisketch::try_new(12, 0, 4)?;
394/// sketch_b.add(42);
395/// sketch_b.add(43);
396///
397/// // Merge two sketches with ^= operator
398/// sketch_a ^= sketch_b;
399///
400/// // Extract difference
401/// let mut differences = vec![0u64; 2];
402/// sketch_a.decode(&mut differences)?;
403///
404/// assert!((differences[0] == 42 || differences[0] == 10) && (differences[1] == 10 || differences[1] == 42));
405///
406/// # Ok::<(), minisketch_rs::MinisketchError>(())
407/// ```
408impl BitXorAssign for Minisketch {
409 fn bitxor_assign(&mut self, rhs: Minisketch) {
410 let _ = self.merge(&rhs);
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use crate::*;
417
418 fn validate_elements(elements: &[u64]) {
419 // Sort differences since they're come in arbitrary order from minisketch_decode()
420 let mut differences = Vec::from(elements);
421 differences.sort();
422
423 assert_eq!(differences[0], 3_000);
424 assert_eq!(differences[1], 3_001);
425 assert_eq!(differences[2], 3_010);
426 assert_eq!(differences[3], 3_011);
427 }
428
429 #[test]
430 // Implement an example from minisketch's README
431 pub fn test_sanity_check() {
432 use ffi::*;
433 unsafe {
434 // Alice's side
435 let sketch_a = minisketch_create(12, 0, 4);
436 for i in 3_000..3_010 {
437 minisketch_add_uint64(sketch_a, i as u64);
438 }
439
440 let sersize = minisketch_serialized_size(sketch_a);
441 assert_eq!(sersize, 12 * 4 / 8);
442
443 let mut buf_a = vec![0u8; sersize];
444 minisketch_serialize(sketch_a, buf_a.as_mut_slice().as_mut_ptr());
445 minisketch_destroy(sketch_a);
446
447 let sketch_b = minisketch_create(12, 0, 4);
448 for i in 3_002..3_012 {
449 minisketch_add_uint64(sketch_b, i as u64);
450 }
451
452 // Bob's side
453 {
454 let sketch_a = minisketch_create(12, 0, 4); // Alice's sketch
455 minisketch_deserialize(sketch_a, buf_a.as_ptr()); // Load Alice's sketch
456
457 // Merge the elements from sketch_a into sketch_b. The result is a sketch_b
458 // which contains all elements that occurred in Alice's or Bob's sets, but not
459 // in both.
460 let _ = minisketch_merge(sketch_b, sketch_a);
461
462 let mut differences = [0u64; 4];
463 let num_differences = minisketch_decode(sketch_b, 4, differences.as_mut_ptr());
464 minisketch_destroy(sketch_a);
465 minisketch_destroy(sketch_b);
466
467 assert!(num_differences > 0);
468 validate_elements(&differences[..]);
469 }
470 };
471 }
472
473 #[test]
474 // Implement a README's example with Rust API as a sanity check
475 pub fn sanity_check_rust_types() {
476 let mut sketch_a = Minisketch::try_new(12, 0, 4).unwrap();
477
478 assert_eq!(sketch_a.bits(), 12);
479 assert_eq!(sketch_a.implementation(), 0);
480 assert_eq!(sketch_a.capacity(), 4);
481
482 for i in 3_000..3_010 {
483 sketch_a.add(i);
484 }
485
486 let sersize = sketch_a.serialized_size();
487 assert_eq!(sersize, 12 * 4 / 8);
488
489 let mut buf_a = vec![0u8; sersize];
490 sketch_a.serialize(buf_a.as_mut_slice()).unwrap();
491
492 let mut sketch_b = Minisketch::try_new(12, 0, 4).unwrap();
493 for i in 3_002..3_012 {
494 sketch_b.add(i);
495 }
496
497 // Bob's side (with .merge() method)
498 {
499 let mut sketch_b = sketch_b.clone();
500 // Alice's sketch
501 let mut sketch_a = Minisketch::try_new(12, 0, 4).unwrap();
502 sketch_a.deserialize(&buf_a); // Load Alice's sketch
503
504 // Merge the elements from sketch_a into sketch_b. The result is a sketch_b
505 // which contains all elements that occurred in Alice's or Bob's sets, but not
506 // in both.
507 let _ = sketch_b.merge(&sketch_a).unwrap();
508
509 let mut differences = [0u64; 4];
510 let num_differences = sketch_b.decode(&mut differences[..]).unwrap();
511
512 assert!(num_differences > 0);
513 validate_elements(&differences[..]);
514 }
515
516 // Bob's side (with ^= instead of .merge())
517 {
518 let mut sketch_b = sketch_b.clone();
519
520 // Alice's sketch
521 let mut sketch_a = Minisketch::try_new(12, 0, 4).unwrap();
522 sketch_a.deserialize(&buf_a); // Load Alice's sketch
523
524 // Merge the elements from sketch_a into sketch_b. The result is a sketch_b
525 // which contains all elements that occurred in Alice's or Bob's sets, but not
526 // in both.
527 sketch_b ^= sketch_a;
528
529 let mut differences = [0u64; 4];
530 let num_differences = sketch_b.decode(&mut differences[..]).unwrap();
531
532 assert!(num_differences > 0);
533 validate_elements(&differences[..]);
534 }
535 }
536}