Merge pull request #35 from sigp/update_shuffling

Update shuffling
This commit is contained in:
Age Manning 2018-10-03 13:55:37 +10:00 committed by GitHub
commit 589354eff9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 34 additions and 35 deletions

View File

@ -37,6 +37,7 @@ members = [
"beacon_chain/utils/bls", "beacon_chain/utils/bls",
"beacon_chain/utils/boolean-bitfield", "beacon_chain/utils/boolean-bitfield",
"beacon_chain/utils/hashing", "beacon_chain/utils/hashing",
"beacon_chain/utils/shuffling",
"beacon_chain/utils/ssz", "beacon_chain/utils/ssz",
"beacon_chain/utils/ssz_helpers", "beacon_chain/utils/ssz_helpers",
"lighthouse/db", "lighthouse/db",

View File

@ -1,2 +0,0 @@
This module includes the fundamental shuffling function. It does not do the
full validator delegation amongst slots.

View File

@ -0,0 +1,7 @@
[package]
name = "shuffling"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
hashing = { path = "../hashing" }

View File

@ -1,4 +1,8 @@
extern crate blake2_rfc; /// A library for performing deterministic, pseudo-random shuffling on a vector.
///
/// This library is designed to confirm to the Ethereum 2.0 specification.
extern crate hashing;
mod rng; mod rng;
@ -9,13 +13,16 @@ pub enum ShuffleErr {
ExceedsListLength, ExceedsListLength,
} }
/// Performs a deterministic, in-place shuffle of a vector of bytes. /// Performs a deterministic, in-place shuffle of a vector.
///
/// The final order of the shuffle is determined by successive hashes /// The final order of the shuffle is determined by successive hashes
/// of the supplied `seed`. /// of the supplied `seed`.
pub fn shuffle( ///
/// This is a Fisher-Yates-Durtstenfeld shuffle.
pub fn shuffle<T>(
seed: &[u8], seed: &[u8],
mut list: Vec<usize>) mut list: Vec<T>)
-> Result<Vec<usize>, ShuffleErr> -> Result<Vec<T>, ShuffleErr>
{ {
let mut rng = ShuffleRng::new(seed); let mut rng = ShuffleRng::new(seed);
if list.len() > rng.rand_max as usize { if list.len() > rng.rand_max as usize {
@ -33,20 +40,16 @@ pub fn shuffle(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use super::blake2_rfc::blake2s::{ blake2s, Blake2sResult }; use super::hashing::canonical_hash;
fn hash(seed: &[u8]) -> Blake2sResult {
blake2s(32, &[], seed)
}
#[test] #[test]
fn test_shuffling() { fn test_shuffling() {
let seed = hash(b"4kn4driuctg8"); let seed = canonical_hash(b"4kn4driuctg8");
let list: Vec<usize> = (0..12).collect(); let list: Vec<usize> = (0..12).collect();
let s = shuffle(seed.as_bytes(), list).unwrap(); let s = shuffle(&seed, list).unwrap();
assert_eq!( assert_eq!(
s, s,
vec![7, 4, 8, 6, 5, 3, 0, 11, 1, 2, 10, 9], vec![7, 3, 2, 5, 11, 9, 1, 0, 4, 6, 10, 8],
) )
} }
} }

View File

@ -1,4 +1,4 @@
use super::blake2_rfc::blake2s::{ Blake2s, Blake2sResult }; use super::hashing::canonical_hash;
const SEED_SIZE_BYTES: usize = 32; const SEED_SIZE_BYTES: usize = 32;
const RAND_BYTES: usize = 3; // 24 / 8 const RAND_BYTES: usize = 3; // 24 / 8
@ -7,7 +7,7 @@ const RAND_MAX: u32 = 16_777_216; // 2**24
/// A pseudo-random number generator which given a seed /// A pseudo-random number generator which given a seed
/// uses successive blake2s hashing to generate "entropy". /// uses successive blake2s hashing to generate "entropy".
pub struct ShuffleRng { pub struct ShuffleRng {
seed: Blake2sResult, seed: Vec<u8>,
idx: usize, idx: usize,
pub rand_max: u32, pub rand_max: u32,
} }
@ -16,7 +16,7 @@ impl ShuffleRng {
/// Create a new instance given some "seed" bytes. /// Create a new instance given some "seed" bytes.
pub fn new(initial_seed: &[u8]) -> Self { pub fn new(initial_seed: &[u8]) -> Self {
Self { Self {
seed: hash(initial_seed), seed: canonical_hash(initial_seed),
idx: 0, idx: 0,
rand_max: RAND_MAX, rand_max: RAND_MAX,
} }
@ -24,7 +24,7 @@ impl ShuffleRng {
/// "Regenerates" the seed by hashing it. /// "Regenerates" the seed by hashing it.
fn rehash_seed(&mut self) { fn rehash_seed(&mut self) {
self.seed = hash(self.seed.as_bytes()); self.seed = canonical_hash(&self.seed);
self.idx = 0; self.idx = 0;
} }
@ -36,7 +36,7 @@ impl ShuffleRng {
self.rand() self.rand()
} else { } else {
int_from_byte_slice( int_from_byte_slice(
self.seed.as_bytes(), &self.seed,
self.idx - RAND_BYTES, self.idx - RAND_BYTES,
) )
} }
@ -68,13 +68,6 @@ fn int_from_byte_slice(source: &[u8], offset: usize) -> u32 {
) )
} }
/// Peform a blake2s hash on the given bytes.
fn hash(bytes: &[u8]) -> Blake2sResult {
let mut hasher = Blake2s::new(SEED_SIZE_BYTES);
hasher.update(bytes);
hasher.finalize()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -115,15 +108,12 @@ mod tests {
#[test] #[test]
fn test_shuffling_hash_fn() { fn test_shuffling_hash_fn() {
let digest = hash(hash(b"4kn4driuctg8").as_bytes()); // double-hash is intentional let digest = canonical_hash(&canonical_hash(&"4kn4driuctg8".as_bytes())); // double-hash is intentional
let digest_bytes = digest.as_bytes();
let expected = [ let expected = [
0xff, 0xff, 0xff, 0x8f, 0xbb, 0xc7, 0xab, 0x64, 0x43, 0x9a, 103, 21, 99, 143, 60, 75, 116, 81, 248, 175, 190, 114, 54, 65, 23, 8, 3, 116,
0xe5, 0x12, 0x44, 0xd8, 0x70, 0xcf, 0xe5, 0x79, 0xf6, 0x55, 160, 178, 7, 75, 63, 47, 180, 239, 191, 247, 57, 194, 144, 88
0x6b, 0xbd, 0x81, 0x43, 0xc5, 0xcd, 0x70, 0x2b, 0xbe, 0xe3,
0x87, 0xc7,
]; ];
assert_eq!(digest_bytes.len(), expected.len()); assert_eq!(digest.len(), expected.len());
assert_eq!(digest_bytes, expected) assert_eq!(digest, expected)
} }
} }