Преглед изворни кода

util: Implement PCG for a deterministic PRNG used for testing and sim.

parazyd пре 3 година
родитељ
комит
bf2437ec2c
3 измењених фајлова са 109 додато и 0 уклоњено
  1. 1 0
      Cargo.toml
  2. 4 0
      src/util/mod.rs
  3. 104 0
      src/util/pcg.rs

+ 1 - 0
Cargo.toml

@@ -249,6 +249,7 @@ tx = [
 ]
 
 util = [
+    "rand",
     "simplelog",
     "serde",
     "serde_json",

+ 4 - 0
src/util/mod.rs

@@ -40,3 +40,7 @@ pub mod time;
 
 /// Ring Buffer implementation
 pub mod ringbuffer;
+
+/// Permuted Congruential Generator (PCG)
+/// This is an insecure PRNG used for simulations and tests.
+pub mod pcg;

+ 104 - 0
src/util/pcg.rs

@@ -0,0 +1,104 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use rand::{CryptoRng, Error, RngCore};
+
+pub struct Pcg32 {
+    state: u64,
+    increment: u64,
+}
+
+impl Pcg32 {
+    const MULTIPLIER: u64 = 6364136223846793005;
+    const INCREMENT: u64 = 1442695040888963407;
+
+    pub fn new(seed: u64) -> Self {
+        let mut rng = Self { state: 0, increment: Self::INCREMENT | 1 };
+        rng.state = rng.state.wrapping_add(seed);
+        rng.state = rng.state.wrapping_mul(Self::MULTIPLIER).wrapping_add(rng.increment);
+        rng
+    }
+
+    fn next_u32(&mut self) -> u32 {
+        let old_state = self.state;
+        self.state = old_state.wrapping_mul(Self::MULTIPLIER).wrapping_add(self.increment);
+        let xorshifted = ((old_state >> 18) ^ old_state) >> 27;
+        let rot = old_state >> 59;
+        (xorshifted >> rot | xorshifted << ((!rot).wrapping_add(1) & 31)) as u32
+    }
+}
+
+impl CryptoRng for Pcg32 {}
+
+impl RngCore for Pcg32 {
+    fn next_u32(&mut self) -> u32 {
+        self.next_u32()
+    }
+
+    fn next_u64(&mut self) -> u64 {
+        (self.next_u32() as u64) << 32 | (self.next_u32() as u64)
+    }
+
+    fn fill_bytes(&mut self, dest: &mut [u8]) {
+        let mut i = 0;
+        while i + 4 <= dest.len() {
+            let bytes = self.next_u32().to_le_bytes();
+            dest[i..i + 4].copy_from_slice(&bytes);
+            i += 4;
+        }
+        if i < dest.len() {
+            let bytes = self.next_u32().to_le_bytes();
+            for (j, dest_byte) in dest[i..].iter_mut().enumerate() {
+                *dest_byte = bytes[j];
+            }
+        }
+    }
+
+    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
+        Ok(self.fill_bytes(dest))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_pcg() {
+        const ITERS: usize = 10000;
+
+        let mut rng0 = Pcg32::new(42);
+        let mut rng1 = Pcg32::new(42);
+
+        for i in 0..ITERS {
+            let a = rng0.next_u32();
+            let b = rng1.next_u32();
+            assert!(a == b);
+
+            let a = rng0.next_u64();
+            let b = rng1.next_u64();
+            assert!(a == b);
+
+            let mut buf0 = vec![0u8; i];
+            let mut buf1 = vec![0u8; i];
+            rng0.fill_bytes(&mut buf0);
+            rng1.fill_bytes(&mut buf1);
+            assert!(buf0 == buf1);
+        }
+    }
+}