diff --git a/substrate/primitives/sr-primitives/src/lib.rs b/substrate/primitives/sr-primitives/src/lib.rs index f98a06fbef2e90ea5b525e7fa96c8b30761dbba0..1bbf5ad55a66b3b5bb04825652f84f074e724663 100644 --- a/substrate/primitives/sr-primitives/src/lib.rs +++ b/substrate/primitives/sr-primitives/src/lib.rs @@ -52,6 +52,7 @@ pub mod offchain; pub mod testing; pub mod traits; pub mod transaction_validity; +pub mod random_number_generator; /// Re-export these since they're only "kind of" generic. pub use generic::{DigestItem, Digest}; @@ -70,6 +71,8 @@ pub use arithmetic::helpers_128bit; /// Re-export big_uint stuff. pub use arithmetic::biguint; +pub use random_number_generator::RandomNumberGenerator; + /// An abstraction over justification for a block's validity under a consensus algorithm. /// /// Essentially a finality proof. The exact formulation will vary between consensus diff --git a/substrate/primitives/sr-primitives/src/random_number_generator.rs b/substrate/primitives/sr-primitives/src/random_number_generator.rs new file mode 100644 index 0000000000000000000000000000000000000000..cb9acfa02807208540311c922504276b0dc7d4e2 --- /dev/null +++ b/substrate/primitives/sr-primitives/src/random_number_generator.rs @@ -0,0 +1,103 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! A simple pseudo random number generator that allows a stream of random numbers to be efficiently +//! created from a single initial seed hash. + +use codec::{Encode, Decode}; +use crate::traits::{Hash, TrailingZeroInput}; + +/// Pseudo-random number streamer. This retains the state of the random number stream. It's as +/// secure as the combination of the seed with which it is constructed and the hash function it uses +/// to cycle elements. +/// +/// It can be saved and later reloaded using the Codec traits. +/// +/// Example: +/// ``` +/// use sr_primitives::traits::{Hash, BlakeTwo256}; +/// use sr_primitives::RandomNumberGenerator; +/// let random_seed = BlakeTwo256::hash(b"Sixty-nine"); +/// let mut rng = <RandomNumberGenerator<BlakeTwo256>>::new(random_seed); +/// assert_eq!(rng.pick_u32(100), 59); +/// assert_eq!(rng.pick_item(&[1, 2, 3]), Some(&1)); +/// ``` +/// +/// This can use any cryptographic `Hash` function as the means of entropy-extension, and avoids +/// needless extensions of entropy. +/// +/// If you're persisting it over blocks, be aware that the sequence will start to repeat. This won't +/// be a practical issue unless you're using tiny hash types (e.g. 64-bit) and pulling hundred of +/// megabytes of data from it. +#[derive(Encode, Decode)] +pub struct RandomNumberGenerator<Hashing: Hash> { + current: Hashing::Output, + offset: u32, +} + +impl<Hashing: Hash> RandomNumberGenerator<Hashing> { + /// A new source of random data. + pub fn new(seed: Hashing::Output) -> Self { + Self { + current: seed, + offset: 0, + } + } + + fn offset(&self) -> usize { self.offset as usize } + + /// Returns a number at least zero, at most `max`. + pub fn pick_u32(&mut self, max: u32) -> u32 { + let needed = (4 - max.leading_zeros() / 8) as usize; + let top = ((1 << (needed as u64 * 8)) / ((max + 1) as u64) * ((max + 1) as u64) - 1) as u32; + loop { + if self.offset() + needed > self.current.as_ref().len() { + // rehash + self.current = Hashing::hash(self.current.as_ref()); + self.offset = 0; + } + let data = &self.current.as_ref()[self.offset()..self.offset() + needed]; + self.offset += needed as u32; + let raw = u32::decode(&mut TrailingZeroInput::new(data)).unwrap_or(0); + if raw <= top { + break if max < u32::max_value() { + raw % (max + 1) + } else { + raw + } + } + } + } + + /// Returns a number at least zero, at most `max`. + /// + /// This returns a `usize`, but internally it only uses `u32` so avoid consensus problems. + pub fn pick_usize(&mut self, max: usize) -> usize { + self.pick_u32(max as u32) as usize + } + + /// Pick a random element from an array of `items`. + /// + /// This is guaranteed to return `Some` except in the case that the given array `items` is + /// empty. + pub fn pick_item<'a, T>(&mut self, items: &'a [T]) -> Option<&'a T> { + if items.is_empty() { + None + } else { + Some(&items[self.pick_usize(items.len() - 1)]) + } + } +} diff --git a/substrate/primitives/sr-primitives/src/traits.rs b/substrate/primitives/sr-primitives/src/traits.rs index 2b2bed53a467ee9dfa2c86a0876faf17d84b0056..84ac039a1d94590e26b1194653de9518eacc6871 100644 --- a/substrate/primitives/sr-primitives/src/traits.rs +++ b/substrate/primitives/sr-primitives/src/traits.rs @@ -1026,7 +1026,14 @@ pub trait OpaqueKeys: Clone { } /// Input that adds infinite number of zero after wrapped input. -struct TrailingZeroInput<'a>(&'a [u8]); +pub struct TrailingZeroInput<'a>(&'a [u8]); + +impl<'a> TrailingZeroInput<'a> { + /// Create a new instance from the given byte array. + pub fn new(data: &'a [u8]) -> Self { + Self(data) + } +} impl<'a> codec::Input for TrailingZeroInput<'a> { fn remaining_len(&mut self) -> Result<Option<usize>, codec::Error> {