|
1 | 1 | use std::convert::TryFrom; |
2 | 2 |
|
3 | | -use bitcoin::hashes::{self, Hash}; |
| 3 | +use bitcoin::hashes::{self, sha256, Hash}; |
4 | 4 | use bitcoin::secp256k1::{self, rand}; |
5 | | -use rand::{random, thread_rng, RngCore}; |
| 5 | +use rand::{thread_rng, Rng, RngCore, SeedableRng}; |
| 6 | +use rand_chacha::ChaCha12Rng; |
6 | 7 |
|
7 | 8 | use crate::runtime::scope::{Mutable, ScopeRef}; |
8 | 9 | use crate::runtime::{Array, Error, Result, Value}; |
@@ -101,26 +102,38 @@ pub mod fns { |
101 | 102 | } |
102 | 103 |
|
103 | 104 | // Generate a random Bytes sequence |
104 | | - /// rand::bytes(Int size) -> Bytes |
| 105 | + /// rand::bytes(Int size[, Bytes seed]) -> Bytes |
105 | 106 | pub fn rand_bytes(args: Array, _: &ScopeRef) -> Result<Value> { |
106 | | - let size = args.arg_into()?; |
| 107 | + let (size, seed) = args.args_into()?; |
107 | 108 | let mut bytes = vec![0u8; size]; |
108 | | - thread_rng().fill_bytes(&mut bytes); |
| 109 | + make_rng(seed).fill_bytes(&mut bytes); |
109 | 110 | Ok(bytes.into()) |
110 | 111 | } |
111 | 112 |
|
112 | 113 | /// Generate a random signed 64-bit integer |
113 | | - /// rand::i64() -> Int |
| 114 | + /// rand::i64([Bytes seed]) -> Int |
114 | 115 | pub fn rand_i64(args: Array, _: &ScopeRef) -> Result<Value> { |
115 | | - args.no_args()?; |
116 | | - Ok(random::<i64>().into()) |
| 116 | + let seed = args.arg_into()?; |
| 117 | + Ok(make_rng(seed).gen::<i64>().into()) |
117 | 118 | } |
118 | 119 |
|
119 | 120 | /// Generate a random 64-bit float in the [0, 1) range |
120 | | - /// rand::f64() -> Float |
| 121 | + /// rand::f64([Bytes seed]) -> Float |
121 | 122 | pub fn rand_f64(args: Array, _: &ScopeRef) -> Result<Value> { |
122 | | - args.no_args()?; |
123 | | - Ok(random::<f64>().into()) |
| 123 | + let seed = args.arg_into()?; |
| 124 | + Ok(make_rng(seed).gen::<f64>().into()) |
| 125 | + } |
| 126 | + |
| 127 | + fn make_rng(seed: Option<Vec<u8>>) -> ChaCha12Rng { |
| 128 | + // Uses ChaCha12 (rand's current default StdRng) directly for reproducibility |
| 129 | + // From https://docs.rs/rand/latest/rand/rngs/struct.StdRng.html: "The algorithm is deterministic but should not be |
| 130 | + // considered reproducible due to dependence on configuration and possible replacement in future library versions. |
| 131 | + // For a secure reproducible generator, we recommend use of the rand_chacha crate directly." |
| 132 | + if let Some(seed) = seed { |
| 133 | + ChaCha12Rng::from_seed(sha256::Hash::hash(&seed).to_byte_array()) |
| 134 | + } else { |
| 135 | + ChaCha12Rng::from_rng(thread_rng()).unwrap() |
| 136 | + } |
124 | 137 | } |
125 | 138 | } |
126 | 139 |
|
|
0 commit comments