mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
util: Implement PCG for a deterministic PRNG used for testing and sim.
This commit is contained in:
@@ -249,6 +249,7 @@ tx = [
|
||||
]
|
||||
|
||||
util = [
|
||||
"rand",
|
||||
"simplelog",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -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
src/util/pcg.rs
Normal file
104
src/util/pcg.rs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user