Skip to main content

commonware_utils/
lib.rs

1//! Leverage common functionality across multiple primitives.
2
3#![doc(
4    html_logo_url = "https://commonware.xyz/imgs/rustdoc_logo.svg",
5    html_favicon_url = "https://commonware.xyz/favicon.ico"
6)]
7#![cfg_attr(not(any(feature = "std", test)), no_std)]
8
9commonware_macros::stability_scope!(ALPHA, cfg(feature = "std") {
10    pub mod rng;
11    pub use rng::{test_rng, test_rng_seeded, FuzzRng};
12
13    pub mod thread_local;
14    pub use thread_local::Cached;
15});
16commonware_macros::stability_scope!(BETA {
17    #[cfg(not(feature = "std"))]
18    extern crate alloc;
19
20    #[cfg(not(feature = "std"))]
21    use alloc::{boxed::Box, vec::Vec};
22    use bytes::{BufMut, BytesMut};
23    use core::time::Duration;
24    pub mod faults;
25    pub use faults::{Faults, N3f1, N5f1};
26
27    pub mod sequence;
28    pub use sequence::{Array, Span};
29
30    pub mod hostname;
31    pub use hostname::Hostname;
32
33    pub mod bitmap;
34    pub mod ordered;
35    pub mod range;
36
37    use bytes::Buf;
38    use commonware_codec::{varint::UInt, EncodeSize, Error as CodecError, Read, ReadExt, Write};
39
40    /// 64-bit golden-ratio-derived odd mixing constant.
41    ///
42    /// Equal to `floor(2^64 / phi)`. Because it is odd, multiplication by it
43    /// is a bijection modulo `2^64`.
44    pub const GOLDEN_RATIO: u64 = 0x9e37_79b9_7f4a_7c15;
45
46    /// Represents a participant/validator index within a consensus committee.
47    ///
48    /// Participant indices are used to identify validators in attestations,
49    /// votes, and certificates. The index corresponds to the position of the
50    /// validator's public key in the ordered participant set.
51    #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
52    #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
53    pub struct Participant(u32);
54
55    impl Participant {
56        /// Creates a new participant from a u32 index.
57        pub const fn new(index: u32) -> Self {
58            Self(index)
59        }
60
61        /// Creates a new participant from a usize index.
62        ///
63        /// # Panics
64        ///
65        /// Panics if `index` exceeds `u32::MAX`.
66        pub fn from_usize(index: usize) -> Self {
67            Self(u32::try_from(index).expect("participant index exceeds u32::MAX"))
68        }
69
70        /// Returns the underlying u32 index.
71        pub const fn get(self) -> u32 {
72            self.0
73        }
74    }
75
76    impl From<Participant> for usize {
77        fn from(p: Participant) -> Self {
78            p.0 as Self
79        }
80    }
81
82    impl core::fmt::Display for Participant {
83        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84            write!(f, "{}", self.0)
85        }
86    }
87
88    impl Read for Participant {
89        type Cfg = ();
90
91        fn read_cfg(buf: &mut impl Buf, _cfg: &Self::Cfg) -> Result<Self, CodecError> {
92            let value: u32 = UInt::read(buf)?.into();
93            Ok(Self(value))
94        }
95    }
96
97    impl Write for Participant {
98        fn write(&self, buf: &mut impl bytes::BufMut) {
99            UInt(self.0).write(buf);
100        }
101    }
102
103    impl EncodeSize for Participant {
104        fn encode_size(&self) -> usize {
105            UInt(self.0).encode_size()
106        }
107    }
108
109    /// A type that can be constructed from an iterator, possibly failing.
110    pub trait TryFromIterator<T>: Sized {
111        /// The error type returned when construction fails.
112        type Error;
113
114        /// Attempts to construct `Self` from an iterator.
115        fn try_from_iter<I: IntoIterator<Item = T>>(iter: I) -> Result<Self, Self::Error>;
116    }
117
118    /// Extension trait for iterators that provides fallible collection.
119    pub trait TryCollect: Iterator + Sized {
120        /// Attempts to collect elements into a collection that may fail.
121        fn try_collect<C: TryFromIterator<Self::Item>>(self) -> Result<C, C::Error> {
122            C::try_from_iter(self)
123        }
124    }
125
126    impl<I: Iterator> TryCollect for I {}
127
128    /// Alias for boxed errors that are `Send` and `Sync`.
129    pub type BoxedError = Box<dyn core::error::Error + Send + Sync>;
130
131    /// Computes the union of two byte slices.
132    pub fn union(a: &[u8], b: &[u8]) -> Vec<u8> {
133        let mut union = Vec::with_capacity(a.len() + b.len());
134        union.extend_from_slice(a);
135        union.extend_from_slice(b);
136        union
137    }
138
139    /// Concatenate a namespace and a message, prepended by a varint encoding of the namespace length.
140    ///
141    /// This produces a unique byte sequence (i.e. no collisions) for each `(namespace, msg)` pair.
142    pub fn union_unique(namespace: &[u8], msg: &[u8]) -> Vec<u8> {
143        use commonware_codec::EncodeSize;
144        let len_prefix = namespace.len();
145        let mut buf =
146            BytesMut::with_capacity(len_prefix.encode_size() + namespace.len() + msg.len());
147        len_prefix.write(&mut buf);
148        BufMut::put_slice(&mut buf, namespace);
149        BufMut::put_slice(&mut buf, msg);
150        buf.into()
151    }
152
153    /// Compute the modulo of bytes interpreted as a big-endian integer.
154    ///
155    /// This function is used to select a random entry from an array when the bytes are a random seed.
156    ///
157    /// # Panics
158    ///
159    /// Panics if `n` is zero.
160    pub fn modulo(bytes: &[u8], n: u64) -> u64 {
161        assert_ne!(n, 0, "modulus must be non-zero");
162
163        let n = n as u128;
164        let mut result = 0u128;
165        for &byte in bytes {
166            result = (result << 8) | (byte as u128);
167            result %= n;
168        }
169
170        // Result is either 0 or modulo `n`, so we can safely cast to u64
171        result as u64
172    }
173
174    /// A wrapper around `Duration` that guarantees the duration is non-zero.
175    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
176    pub struct NonZeroDuration(Duration);
177
178    impl NonZeroDuration {
179        /// Creates a `NonZeroDuration` if the given duration is non-zero.
180        pub fn new(duration: Duration) -> Option<Self> {
181            if duration == Duration::ZERO {
182                None
183            } else {
184                Some(Self(duration))
185            }
186        }
187
188        /// Creates a `NonZeroDuration` from the given duration, panicking if it's zero.
189        pub fn new_panic(duration: Duration) -> Self {
190            Self::new(duration).expect("duration must be non-zero")
191        }
192
193        /// Returns the wrapped `Duration`.
194        pub const fn get(self) -> Duration {
195            self.0
196        }
197    }
198
199    impl From<NonZeroDuration> for Duration {
200        fn from(nz_duration: NonZeroDuration) -> Self {
201            nz_duration.0
202        }
203    }
204});
205commonware_macros::stability_scope!(BETA, cfg(feature = "std") {
206    pub mod acknowledgement;
207    pub use acknowledgement::Acknowledgement;
208
209    pub mod net;
210    pub use net::IpAddrExt;
211
212    pub mod time;
213    pub use time::{DurationExt, SystemTimeExt};
214
215    pub mod rational;
216    pub use rational::BigRationalExt;
217
218    mod priority_set;
219    pub use priority_set::PrioritySet;
220
221    pub mod channel;
222    pub mod concurrency;
223    pub mod futures;
224    pub mod sync;
225});
226#[cfg(not(any(
227    commonware_stability_GAMMA,
228    commonware_stability_DELTA,
229    commonware_stability_EPSILON,
230    commonware_stability_RESERVED
231)))] // BETA
232pub mod vec;
233
234/// A macro to create a `NonZeroUsize` from a value, panicking if the value is zero.
235/// For literal values, validation occurs at compile time. For expressions, validation
236/// occurs at runtime.
237#[macro_export]
238macro_rules! NZUsize {
239    ($val:literal) => {
240        const { ::core::num::NonZeroUsize::new($val).expect("value must be non-zero") }
241    };
242    ($val:expr) => {
243        // This will panic at runtime if $val is zero.
244        ::core::num::NonZeroUsize::new($val).expect("value must be non-zero")
245    };
246}
247
248/// A macro to create a `NonZeroU8` from a value, panicking if the value is zero.
249/// For literal values, validation occurs at compile time. For expressions, validation
250/// occurs at runtime.
251#[macro_export]
252macro_rules! NZU8 {
253    ($val:literal) => {
254        const { ::core::num::NonZeroU8::new($val).expect("value must be non-zero") }
255    };
256    ($val:expr) => {
257        // This will panic at runtime if $val is zero.
258        ::core::num::NonZeroU8::new($val).expect("value must be non-zero")
259    };
260}
261
262/// A macro to create a `NonZeroU16` from a value, panicking if the value is zero.
263/// For literal values, validation occurs at compile time. For expressions, validation
264/// occurs at runtime.
265#[macro_export]
266macro_rules! NZU16 {
267    ($val:literal) => {
268        const { ::core::num::NonZeroU16::new($val).expect("value must be non-zero") }
269    };
270    ($val:expr) => {
271        // This will panic at runtime if $val is zero.
272        ::core::num::NonZeroU16::new($val).expect("value must be non-zero")
273    };
274}
275
276/// A macro to create a `NonZeroU32` from a value, panicking if the value is zero.
277/// For literal values, validation occurs at compile time. For expressions, validation
278/// occurs at runtime.
279#[macro_export]
280macro_rules! NZU32 {
281    ($val:literal) => {
282        const { ::core::num::NonZeroU32::new($val).expect("value must be non-zero") }
283    };
284    ($val:expr) => {
285        // This will panic at runtime if $val is zero.
286        ::core::num::NonZeroU32::new($val).expect("value must be non-zero")
287    };
288}
289
290/// A macro to create a `NonZeroU64` from a value, panicking if the value is zero.
291/// For literal values, validation occurs at compile time. For expressions, validation
292/// occurs at runtime.
293#[macro_export]
294macro_rules! NZU64 {
295    ($val:literal) => {
296        const { ::core::num::NonZeroU64::new($val).expect("value must be non-zero") }
297    };
298    ($val:expr) => {
299        // This will panic at runtime if $val is zero.
300        ::core::num::NonZeroU64::new($val).expect("value must be non-zero")
301    };
302}
303
304/// Re-export of `commonware_formatting` so that the `fixed_bytes!` macro's
305/// expansion can resolve `hex!` in any caller's namespace.
306#[doc(hidden)]
307pub use ::commonware_formatting as __formatting;
308
309/// Macro for converting sequence of string literals containing hex-encoded data
310/// into a [`crate::sequence::FixedBytes`] type.
311#[cfg(not(any(
312    commonware_stability_GAMMA,
313    commonware_stability_DELTA,
314    commonware_stability_EPSILON,
315    commonware_stability_RESERVED
316)))] // BETA
317#[macro_export]
318macro_rules! fixed_bytes {
319    ($s:tt) => {
320        const { $crate::sequence::FixedBytes::new($crate::__formatting::hex!($s)) }
321    };
322}
323
324/// A macro to create a `NonZeroDuration` from a duration, panicking if the duration is zero.
325#[macro_export]
326macro_rules! NZDuration {
327    ($val:expr) => {
328        // This will panic at runtime if $val is zero.
329        $crate::NonZeroDuration::new_panic($val)
330    };
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336    use commonware_formatting::hex;
337    use num_bigint::BigUint;
338    use rand::{rngs::StdRng, Rng, SeedableRng};
339
340    #[test]
341    fn test_union() {
342        // Test case 0: empty slices
343        assert_eq!(union(&[], &[]), Vec::<u8>::new());
344
345        // Test case 1: empty and non-empty slices
346        assert_eq!(union(&[], &hex!("0x010203")), hex!("0x010203"));
347
348        // Test case 2: non-empty and non-empty slices
349        assert_eq!(
350            union(&hex!("0x010203"), &hex!("0x040506")),
351            hex!("0x010203040506")
352        );
353    }
354
355    #[test]
356    fn test_union_unique() {
357        let namespace = b"namespace";
358        let msg = b"message";
359
360        let length_encoding = vec![0b0000_1001];
361        let mut expected = Vec::with_capacity(length_encoding.len() + namespace.len() + msg.len());
362        expected.extend_from_slice(&length_encoding);
363        expected.extend_from_slice(namespace);
364        expected.extend_from_slice(msg);
365
366        let result = union_unique(namespace, msg);
367        assert_eq!(result, expected);
368        assert_eq!(result.len(), result.capacity());
369    }
370
371    #[test]
372    fn test_union_unique_zero_length() {
373        let namespace = b"";
374        let msg = b"message";
375
376        let length_encoding = vec![0];
377        let mut expected = Vec::with_capacity(length_encoding.len() + namespace.len() + msg.len());
378        expected.extend_from_slice(&length_encoding);
379        expected.extend_from_slice(msg);
380
381        let result = union_unique(namespace, msg);
382        assert_eq!(result, expected);
383        assert_eq!(result.len(), result.capacity());
384    }
385
386    #[test]
387    fn test_union_unique_long_length() {
388        // Use a namespace of over length 127.
389        let namespace = &b"n".repeat(256);
390        let msg = b"message";
391
392        let length_encoding = vec![0b1000_0000, 0b0000_0010];
393        let mut expected = Vec::with_capacity(length_encoding.len() + namespace.len() + msg.len());
394        expected.extend_from_slice(&length_encoding);
395        expected.extend_from_slice(namespace);
396        expected.extend_from_slice(msg);
397
398        let result = union_unique(namespace, msg);
399        assert_eq!(result, expected);
400        assert_eq!(result.len(), result.capacity());
401    }
402
403    #[test]
404    fn test_modulo() {
405        // Test case 0: empty bytes
406        assert_eq!(modulo(&[], 1), 0);
407
408        // Test case 1: single byte
409        assert_eq!(modulo(&hex!("0x01"), 1), 0);
410
411        // Test case 2: multiple bytes
412        assert_eq!(modulo(&hex!("0x010203"), 10), 1);
413
414        // Test case 3: check equivalence with BigUint
415        for i in 0..100 {
416            let mut rng = StdRng::seed_from_u64(i);
417            let bytes: [u8; 32] = rng.gen();
418
419            // 1-byte modulus
420            let n = 11u64;
421            let big_modulo = BigUint::from_bytes_be(&bytes) % n;
422            let utils_modulo = modulo(&bytes, n);
423            assert_eq!(big_modulo, BigUint::from(utils_modulo));
424
425            // 2-byte modulus
426            let n = 11_111u64;
427            let big_modulo = BigUint::from_bytes_be(&bytes) % n;
428            let utils_modulo = modulo(&bytes, n);
429            assert_eq!(big_modulo, BigUint::from(utils_modulo));
430
431            // 8-byte modulus
432            let n = 0xDFFFFFFFFFFFFFFD;
433            let big_modulo = BigUint::from_bytes_be(&bytes) % n;
434            let utils_modulo = modulo(&bytes, n);
435            assert_eq!(big_modulo, BigUint::from(utils_modulo));
436        }
437    }
438
439    #[test]
440    #[should_panic]
441    fn test_modulo_zero_panics() {
442        modulo(&hex!("0x010203"), 0);
443    }
444
445    #[test]
446    fn test_non_zero_macros_compile_time() {
447        // Literal values are validated at compile time.
448        // NZU32!(0) would be a compile error.
449        assert_eq!(NZUsize!(1).get(), 1);
450        assert_eq!(NZU8!(2).get(), 2);
451        assert_eq!(NZU16!(3).get(), 3);
452        assert_eq!(NZU32!(4).get(), 4);
453        assert_eq!(NZU64!(5).get(), 5);
454
455        // Literals can be used in const contexts
456        const _: core::num::NonZeroUsize = NZUsize!(1);
457        const _: core::num::NonZeroU8 = NZU8!(2);
458        const _: core::num::NonZeroU16 = NZU16!(3);
459        const _: core::num::NonZeroU32 = NZU32!(4);
460        const _: core::num::NonZeroU64 = NZU64!(5);
461    }
462
463    #[test]
464    fn test_non_zero_macros_runtime() {
465        // Runtime variables are validated at runtime
466        let one_usize: usize = 1;
467        let two_u8: u8 = 2;
468        let three_u16: u16 = 3;
469        let four_u32: u32 = 4;
470        let five_u64: u64 = 5;
471
472        assert_eq!(NZUsize!(one_usize).get(), 1);
473        assert_eq!(NZU8!(two_u8).get(), 2);
474        assert_eq!(NZU16!(three_u16).get(), 3);
475        assert_eq!(NZU32!(four_u32).get(), 4);
476        assert_eq!(NZU64!(five_u64).get(), 5);
477
478        // Zero runtime values panic
479        let zero_usize: usize = 0;
480        let zero_u8: u8 = 0;
481        let zero_u16: u16 = 0;
482        let zero_u32: u32 = 0;
483        let zero_u64: u64 = 0;
484
485        assert!(std::panic::catch_unwind(|| NZUsize!(zero_usize)).is_err());
486        assert!(std::panic::catch_unwind(|| NZU8!(zero_u8)).is_err());
487        assert!(std::panic::catch_unwind(|| NZU16!(zero_u16)).is_err());
488        assert!(std::panic::catch_unwind(|| NZU32!(zero_u32)).is_err());
489        assert!(std::panic::catch_unwind(|| NZU64!(zero_u64)).is_err());
490
491        // NZDuration is runtime-only since Duration has no literal syntax
492        assert!(std::panic::catch_unwind(|| NZDuration!(Duration::ZERO)).is_err());
493        assert_eq!(
494            NZDuration!(Duration::from_secs(1)).get(),
495            Duration::from_secs(1)
496        );
497    }
498
499    #[test]
500    fn test_non_zero_duration() {
501        // Test case 0: zero duration
502        assert!(NonZeroDuration::new(Duration::ZERO).is_none());
503
504        // Test case 1: non-zero duration
505        let duration = Duration::from_millis(100);
506        let nz_duration = NonZeroDuration::new(duration).unwrap();
507        assert_eq!(nz_duration.get(), duration);
508        assert_eq!(Duration::from(nz_duration), duration);
509
510        // Test case 2: panic on zero
511        assert!(std::panic::catch_unwind(|| NonZeroDuration::new_panic(Duration::ZERO)).is_err());
512
513        // Test case 3: ordering
514        let d1 = NonZeroDuration::new(Duration::from_millis(100)).unwrap();
515        let d2 = NonZeroDuration::new(Duration::from_millis(200)).unwrap();
516        assert!(d1 < d2);
517    }
518
519    #[test]
520    fn test_participant_constructors() {
521        assert_eq!(Participant::new(0).get(), 0);
522        assert_eq!(Participant::new(42).get(), 42);
523        assert_eq!(Participant::from_usize(0).get(), 0);
524        assert_eq!(Participant::from_usize(42).get(), 42);
525        assert_eq!(Participant::from_usize(u32::MAX as usize).get(), u32::MAX);
526    }
527
528    #[test]
529    #[should_panic(expected = "participant index exceeds u32::MAX")]
530    fn test_participant_from_usize_overflow() {
531        Participant::from_usize((u32::MAX as usize) + 1);
532    }
533
534    #[test]
535    fn test_participant_display() {
536        assert_eq!(format!("{}", Participant::new(0)), "0");
537        assert_eq!(format!("{}", Participant::new(42)), "42");
538        assert_eq!(format!("{}", Participant::new(1000)), "1000");
539    }
540
541    #[test]
542    fn test_participant_ordering() {
543        assert!(Participant::new(0) < Participant::new(1));
544        assert!(Participant::new(5) < Participant::new(10));
545        assert!(Participant::new(10) > Participant::new(5));
546        assert_eq!(Participant::new(42), Participant::new(42));
547    }
548
549    #[test]
550    fn test_participant_encode_decode() {
551        use commonware_codec::{DecodeExt, Encode};
552
553        let cases = vec![0u32, 1, 127, 128, 255, 256, u32::MAX];
554        for value in cases {
555            let participant = Participant::new(value);
556            let encoded = participant.encode();
557            assert_eq!(encoded.len(), participant.encode_size());
558            let decoded = Participant::decode(encoded).unwrap();
559            assert_eq!(participant, decoded);
560        }
561    }
562
563    #[cfg(feature = "arbitrary")]
564    mod conformance {
565        use super::*;
566        use commonware_codec::conformance::CodecConformance;
567
568        commonware_conformance::conformance_tests! {
569            CodecConformance<Participant>,
570        }
571    }
572}