Skip to main content

blueprint_std/
rand_helper.rs

1use rand::RngCore;
2
3#[cfg(not(feature = "std"))]
4use rand::prelude::StdRng;
5
6pub use rand::{
7    self, CryptoRng, Rng,
8    distributions::{Distribution, Standard},
9};
10
11/// Trait for generating uniform random values
12pub trait UniformRand: Sized {
13    fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self;
14}
15
16impl<T> UniformRand for T
17where
18    Standard: Distribution<T>,
19{
20    #[inline]
21    fn rand<R: Rng + ?Sized>(rng: &mut R) -> Self {
22        rng.sample(Standard)
23    }
24}
25
26/// A random number generator that works in both std and `no_std` environments
27pub struct BlueprintRng(RngImpl);
28
29#[cfg(feature = "std")]
30type RngImpl = rand::rngs::OsRng;
31
32#[cfg(not(feature = "std"))]
33type RngImpl = StdRng;
34
35impl BlueprintRng {
36    /// Create a new cryptographically secure random number generator
37    #[must_use]
38    pub fn new() -> Self {
39        #[cfg(feature = "std")]
40        {
41            Self(rand::rngs::OsRng)
42        }
43        #[cfg(not(feature = "std"))]
44        {
45            test_rng()
46        }
47    }
48
49    /// Create a deterministic RNG from a seed (for testing only)
50    #[must_use]
51    pub fn from_seed(seed: [u8; 32]) -> Self {
52        #[cfg(feature = "std")]
53        {
54            // In std mode, always use OsRng regardless of seed for security
55            let _ = seed;
56            Self(rand::rngs::OsRng)
57        }
58        #[cfg(not(feature = "std"))]
59        {
60            use rand::SeedableRng;
61            Self(StdRng::from_seed(seed))
62        }
63    }
64}
65
66impl Default for BlueprintRng {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl CryptoRng for BlueprintRng {}
73
74#[cfg(feature = "std")]
75impl RngCore for BlueprintRng {
76    fn next_u32(&mut self) -> u32 {
77        self.0.next_u32()
78    }
79    fn next_u64(&mut self) -> u64 {
80        self.0.next_u64()
81    }
82    fn fill_bytes(&mut self, dest: &mut [u8]) {
83        self.0.fill_bytes(dest);
84    }
85    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
86        self.0.try_fill_bytes(dest)
87    }
88}
89
90#[cfg(not(feature = "std"))]
91impl RngCore for BlueprintRng {
92    fn next_u32(&mut self) -> u32 {
93        self.0.r#gen()
94    }
95    fn next_u64(&mut self) -> u64 {
96        self.0.r#gen()
97    }
98    fn fill_bytes(&mut self, dest: &mut [u8]) {
99        self.0.fill_bytes(dest);
100    }
101    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
102        self.0.fill_bytes(dest);
103        Ok(())
104    }
105}
106
107/// Create a deterministic RNG for testing
108#[must_use]
109pub fn test_rng() -> BlueprintRng {
110    const TEST_SEED: [u8; 32] = [
111        1, 0, 0, 0, 23, 0, 0, 0, 200, 1, 0, 0, 210, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112        0, 0, 0, 0,
113    ];
114    BlueprintRng::from_seed(TEST_SEED)
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn test_rng_generates_different_values() {
123        let mut rng = BlueprintRng::new();
124        assert_ne!(rng.next_u64(), rng.next_u64());
125    }
126
127    #[test]
128    fn test_deterministic_rng() {
129        #[cfg(not(feature = "std"))]
130        {
131            let mut rng1 = BlueprintRng::from_seed([1u8; 32]);
132            let mut rng2 = BlueprintRng::from_seed([1u8; 32]);
133            assert_eq!(rng1.next_u64(), rng2.next_u64());
134        }
135    }
136}