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