Skip to main content

nectar_primitives/
nonce.rs

1//! Typed nonce used in overlay address derivation.
2//!
3//! A [`Nonce`] is the 32-byte value mixed with an Ethereum address and a
4//! [`NetworkId`](crate::NetworkId) when deriving the Swarm overlay address.
5//! See [`compute_overlay`](crate::compute_overlay) for the canonical
6//! derivation matching bee `pkg/crypto/crypto.go:45-57`.
7
8use alloy_primitives::B256;
9use derive_more::{AsRef, Display, From, Into};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// 32-byte nonce mixed into the overlay address.
15///
16/// Persistent nodes (storers, bootnodes) keep the nonce stable across restarts
17/// so their overlay address is stable. Ephemeral nodes (clients) may rotate
18/// the nonce per run.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, Into, AsRef)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde", serde(transparent))]
22#[display("{_0}")]
23#[from(B256, [u8; 32])]
24#[into(B256, [u8; 32])]
25#[as_ref([u8])]
26pub struct Nonce(B256);
27
28impl Nonce {
29    /// Zero nonce, useful for tests and deterministic vectors.
30    pub const ZERO: Self = Self(B256::ZERO);
31
32    /// Construct from raw 32 bytes. `const` for static contexts; for runtime
33    /// conversions prefer the `From` impls.
34    #[inline]
35    pub const fn new(bytes: [u8; 32]) -> Self {
36        Self(B256::new(bytes))
37    }
38
39    /// Borrow the underlying 32 bytes.
40    #[inline]
41    pub const fn as_slice(&self) -> &[u8] {
42        self.0.as_slice()
43    }
44
45    /// Sample a cryptographically random nonce via `alloy_primitives::B256::random`.
46    pub fn random() -> Self {
47        Self(B256::random())
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn zero_is_all_zero_bytes() {
57        assert_eq!(Nonce::ZERO.as_slice(), &[0u8; 32]);
58    }
59
60    #[test]
61    fn roundtrips_via_from_impls() {
62        let bytes = [7u8; 32];
63        let n = Nonce::new(bytes);
64        assert_eq!(B256::from(n), B256::new(bytes));
65        assert_eq!(Nonce::from(B256::new(bytes)), n);
66        assert_eq!(<[u8; 32]>::from(n), bytes);
67        assert_eq!(Nonce::from(bytes), n);
68    }
69
70    #[test]
71    fn display_matches_b256_lowercase_hex() {
72        let n = Nonce::new([0xab; 32]);
73        let rendered = format!("{n}");
74        assert!(rendered.starts_with("0x"));
75        assert_eq!(rendered.len(), 66);
76        assert!(rendered.chars().skip(2).all(|c| c.is_ascii_hexdigit()));
77    }
78}