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