Move shuffling to its own crate, update hash fn

Previously blake2s-256 was being used, now blake2b-512[:32] is being
used.
This commit is contained in:
Paul Hauner
2018-10-03 13:13:51 +10:00
parent 6a75aa3246
commit 2763f7bc00
6 changed files with 185 additions and 29 deletions

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

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

View File

@@ -0,0 +1,48 @@
extern crate hashing;
mod rng;
use self::rng::ShuffleRng;
#[derive(Debug)]
pub enum ShuffleErr {
ExceedsListLength,
}
/// Performs a deterministic, in-place shuffle of a vector of bytes.
/// The final order of the shuffle is determined by successive hashes
/// of the supplied `seed`.
pub fn shuffle(
seed: &[u8],
mut list: Vec<usize>)
-> Result<Vec<usize>, ShuffleErr>
{
let mut rng = ShuffleRng::new(seed);
if list.len() > rng.rand_max as usize {
return Err(ShuffleErr::ExceedsListLength);
}
for i in 0..(list.len() - 1) {
let n = list.len() - i;
let j = rng.rand_range(n as u32) as usize + i;
list.swap(i, j);
}
Ok(list)
}
#[cfg(test)]
mod tests {
use super::*;
use super::hashing::canonical_hash;
#[test]
fn test_shuffling() {
let seed = canonical_hash(b"4kn4driuctg8");
let list: Vec<usize> = (0..12).collect();
let s = shuffle(&seed, list).unwrap();
assert_eq!(
s,
vec![7, 3, 2, 5, 11, 9, 1, 0, 4, 6, 10, 8],
)
}
}

View File

@@ -0,0 +1,119 @@
use super::hashing::canonical_hash;
const SEED_SIZE_BYTES: usize = 32;
const RAND_BYTES: usize = 3; // 24 / 8
const RAND_MAX: u32 = 16_777_216; // 2**24
/// A pseudo-random number generator which given a seed
/// uses successive blake2s hashing to generate "entropy".
pub struct ShuffleRng {
seed: Vec<u8>,
idx: usize,
pub rand_max: u32,
}
impl ShuffleRng {
/// Create a new instance given some "seed" bytes.
pub fn new(initial_seed: &[u8]) -> Self {
Self {
seed: canonical_hash(initial_seed),
idx: 0,
rand_max: RAND_MAX,
}
}
/// "Regenerates" the seed by hashing it.
fn rehash_seed(&mut self) {
self.seed = canonical_hash(&self.seed);
self.idx = 0;
}
/// Extracts 3 bytes from the `seed`. Rehashes seed if required.
fn rand(&mut self) -> u32 {
self.idx += RAND_BYTES;
if self.idx >= SEED_SIZE_BYTES {
self.rehash_seed();
self.rand()
} else {
int_from_byte_slice(
&self.seed,
self.idx - RAND_BYTES,
)
}
}
/// Generate a random u32 below the specified maximum `n`.
///
/// Provides a filtered result from a higher-level rng, by discarding
/// results which may bias the output. Because of this, execution time is
/// not linear and may potentially be infinite.
pub fn rand_range(&mut self, n: u32) -> u32 {
assert!(n < RAND_MAX, "RAND_MAX exceed");
let mut x = self.rand();
while x >= self.rand_max - (self.rand_max % n) {
x = self.rand();
}
x % n
}
}
/// Reads the next three bytes of `source`, starting from `offset` and
/// interprets those bytes as a 24 bit big-endian integer.
/// Returns that integer.
fn int_from_byte_slice(source: &[u8], offset: usize) -> u32 {
(
u32::from(source[offset + 2])) |
(u32::from(source[offset + 1]) << 8) |
(u32::from(source[offset ]) << 16
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shuffling_int_from_slice() {
let mut x = int_from_byte_slice(
&[0, 0, 1],
0);
assert_eq!((x as u32), 1);
x = int_from_byte_slice(
&[0, 1, 1],
0);
assert_eq!(x, 257);
x = int_from_byte_slice(
&[1, 1, 1],
0);
assert_eq!(x, 65793);
x = int_from_byte_slice(
&[255, 1, 1],
0);
assert_eq!(x, 16711937);
x = int_from_byte_slice(
&[255, 255, 255],
0);
assert_eq!(x, 16777215);
x = int_from_byte_slice(
&[0x8f, 0xbb, 0xc7],
0);
assert_eq!(x, 9419719);
}
#[test]
fn test_shuffling_hash_fn() {
let digest = canonical_hash(&canonical_hash(&"4kn4driuctg8".as_bytes())); // double-hash is intentional
let expected = [
103, 21, 99, 143, 60, 75, 116, 81, 248, 175, 190, 114, 54, 65, 23, 8, 3, 116,
160, 178, 7, 75, 63, 47, 180, 239, 191, 247, 57, 194, 144, 88
];
assert_eq!(digest.len(), expected.len());
assert_eq!(digest, expected)
}
}

View File

@@ -0,0 +1,162 @@
- input: []
output: []
seed: !!binary ""
- input: [0]
output: [0]
seed: !!binary ""
- input: [255]
output: [255]
seed: !!binary ""
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [1, 6, 4, 1, 6, 6, 2, 2, 4, 5]
seed: !!binary ""
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [4, 7, 10, 13, 3, 1, 2, 9, 12, 6, 11, 8, 5]
seed: !!binary ""
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [1, 6, 65, 1, 6, 6, 2, 2, 4, 5]
seed: !!binary ""
- input: []
output: []
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [0]
output: [0]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [255]
output: [255]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 4, 2, 5, 4, 2, 6, 6, 1, 1]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [13, 1, 9, 8, 3, 10, 6, 2, 5, 12, 11, 4, 7]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 65, 2, 5, 4, 2, 6, 6, 1, 1]
seed: !!binary |
JlAYJ5H2j8g7PLiPHZI/rTS1uAvKiieOrifPN6Moso0=
- input: []
output: []
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: [0]
output: [0]
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: [255]
output: [255]
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 5, 1, 6, 4, 1, 2, 4, 6]
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [3, 8, 10, 4, 7, 11, 6, 1, 2, 5, 13, 9, 12]
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 5, 1, 6, 4, 1, 2, 65, 6]
seed: !!binary |
Jlpt1sTgPixEqKaQaX4rmZgxnh48U7igzJK1EjAkK0Q=
- input: []
output: []
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: [0]
output: [0]
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: [255]
output: [255]
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [5, 6, 2, 1, 6, 4, 6, 4, 1, 2]
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [12, 4, 11, 6, 13, 10, 9, 2, 3, 7, 8, 1, 5]
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [5, 6, 2, 1, 6, 65, 6, 4, 1, 2]
seed: !!binary |
zoulKyFpZkZG9AaUh1zxxcQ+unrNxwux10Svi4eBMmA=
- input: []
output: []
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: [0]
output: [0]
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: [255]
output: [255]
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 6, 5, 4, 4, 1, 6, 2, 1]
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [10, 12, 13, 3, 7, 11, 2, 4, 9, 8, 6, 5, 1]
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 6, 5, 65, 4, 1, 6, 2, 1]
seed: !!binary |
3gEwWl7gY5XRPg5iZgowSp9hyk4Sf/u92B+t88JdAEw=
- input: []
output: []
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: [0]
output: [0]
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: [255]
output: [255]
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 4, 1, 2, 6, 5, 1, 6, 4]
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [11, 8, 12, 9, 2, 1, 10, 4, 13, 5, 7, 3, 6]
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 65, 1, 2, 6, 5, 1, 6, 4]
seed: !!binary |
mHQhqYMuvHeBZNa6VLLs3lZiqD1jWKP0cRzntvM5N+U=
- input: []
output: []
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=
- input: [0]
output: [0]
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=
- input: [255]
output: [255]
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 5, 1, 6, 1, 2, 6, 6, 4, 4]
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=
- input: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
output: [5, 13, 9, 7, 11, 10, 12, 2, 6, 8, 3, 1, 4]
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 5, 1, 6, 1, 2, 6, 6, 65, 4]
seed: !!binary |
/1mYGDx96+OeS3HV1avIeE/pPQROpuhSjAj+t+H59DE=