beacon_chain/utils/shuffling: Introduce test vectors from sigp/shuffling_sandbox

Also:
 - return early if we shuffle an empty list
 - change RAND_MAX as per spec
This commit is contained in:
pinkiebell 2018-10-20 18:39:10 +02:00
parent 2fb9dfbf14
commit 5252b54a04
4 changed files with 212 additions and 8 deletions

View File

@ -5,3 +5,6 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
hashing = { path = "../hashing" }
[dev-dependencies]
yaml-rust = "0.4.2"

View File

@ -25,9 +25,15 @@ pub fn shuffle<T>(
-> Result<Vec<T>, ShuffleErr>
{
let mut rng = ShuffleRng::new(seed);
if list.len() > rng.rand_max as usize {
return Err(ShuffleErr::ExceedsListLength);
}
if list.len() == 0 {
return Ok(list);
}
for i in 0..(list.len() - 1) {
let n = list.len() - i;
let j = rng.rand_range(n as u32) as usize + i;
@ -39,17 +45,43 @@ pub fn shuffle<T>(
#[cfg(test)]
mod tests {
extern crate yaml_rust;
use super::*;
use super::hashing::canonical_hash;
use std::fs::File;
use std::io::prelude::*;
use self::yaml_rust::yaml;
#[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],
)
let mut file = File::open("./src/specs/shuffle_test_vectors.yaml").unwrap();
let mut yaml_str = String::new();
file.read_to_string(&mut yaml_str).unwrap();
let docs = yaml::YamlLoader::load_from_str(&yaml_str).unwrap();
let doc = &docs[0];
let test_cases = doc["test_cases"].as_vec();
for test_case in test_cases.unwrap() {
let input = test_case["input"].clone().into_vec().unwrap();
let output = test_case["output"].clone().into_vec().unwrap();
let seed_bytes = test_case["seed"].as_str().unwrap().as_bytes();
let mut seed;
if seed_bytes.len() > 0 {
seed = canonical_hash(seed_bytes);
} else {
seed = vec![];
}
let mut s = shuffle(&seed, input).unwrap();
assert_eq!(
s,
output,
);
}
}
}

View File

@ -2,7 +2,7 @@ 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
const RAND_MAX: u32 = 16_777_215; // 2 ** (rand_bytes * 8) - 1
/// A pseudo-random number generator which given a seed
/// uses successive blake2s hashing to generate "entropy".

View File

@ -0,0 +1,169 @@
# This file was generated with a modified version of shuffling_sandbox
# python3 sandbox.py test_vectors
#diff --git a/sandbox.py b/sandbox.py
#index 99b0ba7..cd7cafe 100644
#--- a/sandbox.py
#+++ b/sandbox.py
#@@ -126,13 +126,13 @@ elif args.method == "test_vectors":
# results = []
#
# seeds = [
#- b"",
#- blake("4kn4driuctg8".encode()), # known to cause conflicts with old shuffler
#- blake("ytre1p".encode()),
#- blake("mytobcffnkvj".encode()),
#- blake("myzu3g7evxp5nkvj".encode()),
#- blake("xdpli1jsx5xb".encode()),
#- blake("oab3mbb3xe8qsx5xb".encode()),
#+ "",
#+ "4kn4driuctg8", # known to cause conflicts with old shuffler
#+ "ytre1p",
#+ "mytobcffnkvj",
#+ "myzu3g7evxp5nkvj",
#+ "xdpli1jsx5xb",
#+ "oab3mbb3xe8qsx5xb",
# ]
# lists = [
# [],
#@@ -147,7 +147,8 @@ elif args.method == "test_vectors":
#
# for seed in seeds:
# for lst in lists:
#- output = shuffler(lst, seed)
#+ blake_seed = blake(seed.encode()) if len(seed) > 0 else b""
#+ output = shuffler(lst, blake_seed)
# results.append({"seed": seed, "input": lst, "output": output})
#
# body = {
title: Shuffling Algorithm Tests
summary: Test vectors for shuffling a list based upon a seed.
test_suite: Shuffling
test_cases:
- input: []
output: []
seed: ''
- input: [0]
output: [0]
seed: ''
- input: [255]
output: [255]
seed: ''
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [1, 6, 4, 1, 6, 6, 2, 2, 4, 5]
seed: ''
- 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: ''
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [1, 6, 65, 1, 6, 6, 2, 2, 4, 5]
seed: ''
- input: []
output: []
seed: 4kn4driuctg8
- input: [0]
output: [0]
seed: 4kn4driuctg8
- input: [255]
output: [255]
seed: 4kn4driuctg8
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 4, 2, 5, 4, 2, 6, 6, 1, 1]
seed: 4kn4driuctg8
- 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: 4kn4driuctg8
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 65, 2, 5, 4, 2, 6, 6, 1, 1]
seed: 4kn4driuctg8
- input: []
output: []
seed: ytre1p
- input: [0]
output: [0]
seed: ytre1p
- input: [255]
output: [255]
seed: ytre1p
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 5, 1, 6, 4, 1, 2, 4, 6]
seed: ytre1p
- 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: ytre1p
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 5, 1, 6, 4, 1, 2, 65, 6]
seed: ytre1p
- input: []
output: []
seed: mytobcffnkvj
- input: [0]
output: [0]
seed: mytobcffnkvj
- input: [255]
output: [255]
seed: mytobcffnkvj
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [5, 6, 2, 1, 6, 4, 6, 4, 1, 2]
seed: mytobcffnkvj
- 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: mytobcffnkvj
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [5, 6, 2, 1, 6, 65, 6, 4, 1, 2]
seed: mytobcffnkvj
- input: []
output: []
seed: myzu3g7evxp5nkvj
- input: [0]
output: [0]
seed: myzu3g7evxp5nkvj
- input: [255]
output: [255]
seed: myzu3g7evxp5nkvj
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 6, 5, 4, 4, 1, 6, 2, 1]
seed: myzu3g7evxp5nkvj
- 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: myzu3g7evxp5nkvj
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 6, 5, 65, 4, 1, 6, 2, 1]
seed: myzu3g7evxp5nkvj
- input: []
output: []
seed: xdpli1jsx5xb
- input: [0]
output: [0]
seed: xdpli1jsx5xb
- input: [255]
output: [255]
seed: xdpli1jsx5xb
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 4, 1, 2, 6, 5, 1, 6, 4]
seed: xdpli1jsx5xb
- 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: xdpli1jsx5xb
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [6, 2, 65, 1, 2, 6, 5, 1, 6, 4]
seed: xdpli1jsx5xb
- input: []
output: []
seed: oab3mbb3xe8qsx5xb
- input: [0]
output: [0]
seed: oab3mbb3xe8qsx5xb
- input: [255]
output: [255]
seed: oab3mbb3xe8qsx5xb
- input: [4, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 5, 1, 6, 1, 2, 6, 6, 4, 4]
seed: oab3mbb3xe8qsx5xb
- 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: oab3mbb3xe8qsx5xb
- input: [65, 6, 2, 6, 1, 4, 6, 2, 1, 5]
output: [2, 5, 1, 6, 1, 2, 6, 6, 65, 4]
seed: oab3mbb3xe8qsx5xb