Merge pull request #230 from sigp/int_to_bytes

Implement int_to_bytes[n] functions
This commit is contained in:
Age Manning 2019-02-15 18:04:18 +11:00 committed by GitHub
commit cb5cd61f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 430 additions and 30 deletions

View File

@ -9,6 +9,7 @@ members = [
"eth2/utils/boolean-bitfield", "eth2/utils/boolean-bitfield",
"eth2/utils/hashing", "eth2/utils/hashing",
"eth2/utils/honey-badger-split", "eth2/utils/honey-badger-split",
"eth2/utils/int_to_bytes",
"eth2/utils/slot_clock", "eth2/utils/slot_clock",
"eth2/utils/ssz", "eth2/utils/ssz",
"eth2/utils/swap_or_not_shuffle", "eth2/utils/swap_or_not_shuffle",

View File

@ -5,6 +5,7 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
slot_clock = { path = "../../eth2/utils/slot_clock" } int_to_bytes = { path = "../utils/int_to_bytes" }
ssz = { path = "../../eth2/utils/ssz" } slot_clock = { path = "../utils/slot_clock" }
types = { path = "../../eth2/types" } ssz = { path = "../utils/ssz" }
types = { path = "../types" }

View File

@ -1,8 +1,8 @@
pub mod test_utils; pub mod test_utils;
mod traits; mod traits;
use int_to_bytes::int_to_bytes32;
use slot_clock::SlotClock; use slot_clock::SlotClock;
use ssz::ssz_encode;
use std::sync::Arc; use std::sync::Arc;
use types::{BeaconBlock, ChainSpec, Slot}; use types::{BeaconBlock, ChainSpec, Slot};
@ -132,7 +132,7 @@ impl<T: SlotClock, U: BeaconNode, V: DutiesReader, W: Signer> BlockProducer<T, U
fn produce_block(&mut self, slot: Slot) -> Result<PollOutcome, Error> { fn produce_block(&mut self, slot: Slot) -> Result<PollOutcome, Error> {
let randao_reveal = { let randao_reveal = {
// TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`. // TODO: add domain, etc to this message. Also ensure result matches `into_to_bytes32`.
let message = ssz_encode(&slot.epoch(self.spec.epoch_length)); let message = int_to_bytes32(slot.epoch(self.spec.epoch_length).as_u64());
match self.signer.sign_randao_reveal(&message) { match self.signer.sign_randao_reveal(&message) {
None => return Ok(PollOutcome::SignerRejection(slot)), None => return Ok(PollOutcome::SignerRejection(slot)),

View File

@ -6,6 +6,7 @@ edition = "2018"
[dependencies] [dependencies]
hashing = { path = "../utils/hashing" } hashing = { path = "../utils/hashing" }
int_to_bytes = { path = "../utils/int_to_bytes" }
integer-sqrt = "0.1" integer-sqrt = "0.1"
log = "0.4" log = "0.4"
ssz = { path = "../utils/ssz" } ssz = { path = "../utils/ssz" }

View File

@ -1,5 +1,6 @@
use crate::SlotProcessingError; use crate::SlotProcessingError;
use hashing::hash; use hashing::hash;
use int_to_bytes::int_to_bytes32;
use log::debug; use log::debug;
use ssz::{ssz_encode, TreeHash}; use ssz::{ssz_encode, TreeHash};
use types::{ use types::{
@ -110,7 +111,7 @@ fn per_block_processing_signature_optional(
ensure!( ensure!(
bls_verify( bls_verify(
&block_proposer.pubkey, &block_proposer.pubkey,
&ssz_encode(&state.current_epoch(spec)), &int_to_bytes32(state.current_epoch(spec).as_u64()),
&block.randao_reveal, &block.randao_reveal,
get_domain(&state.fork, state.current_epoch(spec), DOMAIN_RANDAO) get_domain(&state.fork, state.current_epoch(spec), DOMAIN_RANDAO)
), ),

View File

@ -0,0 +1,12 @@
[package]
name = "int_to_bytes"
version = "0.1.0"
authors = ["Paul Hauner <paul@paulhauner.com>"]
edition = "2018"
[dependencies]
bytes = "0.4"
[dev-dependencies]
yaml-rust = "0.4.2"
hex = "0.3"

View File

@ -0,0 +1,125 @@
use bytes::{BufMut, BytesMut};
/// Returns `int` as little-endian bytes with a length of 1.
pub fn int_to_bytes1(int: u8) -> Vec<u8> {
vec![int]
}
/// Returns `int` as little-endian bytes with a length of 2.
pub fn int_to_bytes2(int: u16) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(2);
bytes.put_u16_le(int);
bytes.to_vec()
}
/// Returns `int` as little-endian bytes with a length of 3.
///
/// An `Option` is returned as Rust does not support a native
/// `u24` type.
///
/// The Eth 2.0 specification uses `int.to_bytes(2, 'little')`, which throws an error if `int`
/// doesn't fit within 3 bytes. The specification relies upon implicit asserts for some validity
/// conditions, so we ensure the calling function is aware of the error condition as opposed to
/// hiding it with a modulo.
pub fn int_to_bytes3(int: u32) -> Option<Vec<u8>> {
if int < 2_u32.pow(3 * 8) {
let mut bytes = BytesMut::with_capacity(4);
bytes.put_u32_le(int);
Some(bytes[0..3].to_vec())
} else {
None
}
}
/// Returns `int` as little-endian bytes with a length of 4.
pub fn int_to_bytes4(int: u32) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(4);
bytes.put_u32_le(int);
bytes.to_vec()
}
/// Returns `int` as little-endian bytes with a length of 8.
pub fn int_to_bytes8(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(8);
bytes.put_u64_le(int);
bytes.to_vec()
}
/// Returns `int` as little-endian bytes with a length of 32.
pub fn int_to_bytes32(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(32);
bytes.put_u64_le(int);
bytes.resize(32, 0);
bytes.to_vec()
}
/// Returns `int` as little-endian bytes with a length of 48.
pub fn int_to_bytes48(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(48);
bytes.put_u64_le(int);
bytes.resize(48, 0);
bytes.to_vec()
}
/// Returns `int` as little-endian bytes with a length of 96.
pub fn int_to_bytes96(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(96);
bytes.put_u64_le(int);
bytes.resize(96, 0);
bytes.to_vec()
}
#[cfg(test)]
mod tests {
use super::*;
use hex;
use std::{fs::File, io::prelude::*, path::PathBuf};
use yaml_rust::yaml;
#[test]
fn int_to_bytes3_returns_none() {
assert_eq!(int_to_bytes3(2_u32.pow(24)), None);
}
#[test]
fn test_vectors() {
/*
* Test vectors are generated here:
*
* https://github.com/ethereum/eth2.0-test-generators
*/
let mut file = {
let mut file_path_buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
file_path_buf.push("src/specs/test_vector_int_to_bytes.yml");
File::open(file_path_buf).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().unwrap();
for test_case in test_cases {
let byte_length = test_case["byte_length"].as_i64().unwrap() as u64;
let int = test_case["int"].as_i64().unwrap() as u64;
let bytes_string = test_case["bytes"].clone().into_string().unwrap();
let bytes = hex::decode(bytes_string.replace("0x", "")).unwrap();
match byte_length {
1 => assert_eq!(int_to_bytes1(int as u8), bytes),
2 => assert_eq!(int_to_bytes2(int as u16), bytes),
3 => assert_eq!(int_to_bytes3(int as u32), Some(bytes)),
4 => assert_eq!(int_to_bytes4(int as u32), bytes),
8 => assert_eq!(int_to_bytes8(int), bytes),
32 => assert_eq!(int_to_bytes32(int), bytes),
48 => assert_eq!(int_to_bytes48(int), bytes),
96 => assert_eq!(int_to_bytes96(int), bytes),
_ => panic!("Unknown byte length in test vector."),
}
}
}
}

View File

@ -0,0 +1,215 @@
fork: tchaikovsky
summary: Test vectors for the `int_to_bytes[n]` functions.`
test_suite: int_to_bytes
title: int_to_bytes Tests
version: 1.0
test_cases:
- {byte_length: 1, bytes: '0x00', int: 0}
- {byte_length: 1, bytes: '0x01', int: 1}
- {byte_length: 1, bytes: '0xff', int: 255}
- {byte_length: 1, bytes: '0xc0', int: 192}
- {byte_length: 1, bytes: '0xc7', int: 199}
- {byte_length: 1, bytes: '0xf2', int: 242}
- {byte_length: 1, bytes: '0x26', int: 38}
- {byte_length: 1, bytes: '0xfb', int: 251}
- {byte_length: 1, bytes: '0xd5', int: 213}
- {byte_length: 1, bytes: '0x74', int: 116}
- {byte_length: 1, bytes: '0xa8', int: 168}
- {byte_length: 1, bytes: '0xc6', int: 198}
- {byte_length: 1, bytes: '0x3d', int: 61}
- {byte_length: 1, bytes: '0xc2', int: 194}
- {byte_length: 1, bytes: '0x68', int: 104}
- {byte_length: 1, bytes: '0x64', int: 100}
- {byte_length: 1, bytes: '0xc2', int: 194}
- {byte_length: 1, bytes: '0x78', int: 120}
- {byte_length: 1, bytes: '0x33', int: 51}
- {byte_length: 2, bytes: '0x0000', int: 0}
- {byte_length: 2, bytes: '0x0100', int: 1}
- {byte_length: 2, bytes: '0xffff', int: 65535}
- {byte_length: 2, bytes: '0xedea', int: 60141}
- {byte_length: 2, bytes: '0x2d93', int: 37677}
- {byte_length: 2, bytes: '0x611e', int: 7777}
- {byte_length: 2, bytes: '0x637c', int: 31843}
- {byte_length: 2, bytes: '0xe370', int: 28899}
- {byte_length: 2, bytes: '0x96b3', int: 45974}
- {byte_length: 2, bytes: '0xde44', int: 17630}
- {byte_length: 2, bytes: '0xa009', int: 2464}
- {byte_length: 2, bytes: '0xf6ba', int: 47862}
- {byte_length: 2, bytes: '0xef76', int: 30447}
- {byte_length: 2, bytes: '0x7e5f', int: 24446}
- {byte_length: 2, bytes: '0x393d', int: 15673}
- {byte_length: 2, bytes: '0xc820', int: 8392}
- {byte_length: 2, bytes: '0x9031', int: 12688}
- {byte_length: 2, bytes: '0x3963', int: 25401}
- {byte_length: 2, bytes: '0x033d', int: 15619}
- {byte_length: 3, bytes: '0x000000', int: 0}
- {byte_length: 3, bytes: '0x010000', int: 1}
- {byte_length: 3, bytes: '0xffffff', int: 16777215}
- {byte_length: 3, bytes: '0x1fdfb2', int: 11722527}
- {byte_length: 3, bytes: '0x2a7504', int: 292138}
- {byte_length: 3, bytes: '0x09fb20', int: 2161417}
- {byte_length: 3, bytes: '0xa4a6b2', int: 11708068}
- {byte_length: 3, bytes: '0x17feb7', int: 12058135}
- {byte_length: 3, bytes: '0x3ad0b1', int: 11653178}
- {byte_length: 3, bytes: '0xbc92c6', int: 13013692}
- {byte_length: 3, bytes: '0xb6c046', int: 4636854}
- {byte_length: 3, bytes: '0x937f00', int: 32659}
- {byte_length: 3, bytes: '0x8266cb', int: 13330050}
- {byte_length: 3, bytes: '0x8136e9', int: 15283841}
- {byte_length: 3, bytes: '0xe9e062', int: 6480105}
- {byte_length: 3, bytes: '0x50d054', int: 5558352}
- {byte_length: 3, bytes: '0xb95340', int: 4215737}
- {byte_length: 3, bytes: '0x779f52', int: 5414775}
- {byte_length: 3, bytes: '0x15aed0', int: 13676053}
- {byte_length: 4, bytes: '0x00000000', int: 0}
- {byte_length: 4, bytes: '0x01000000', int: 1}
- {byte_length: 4, bytes: '0xffffffff', int: 4294967295}
- {byte_length: 4, bytes: '0x389cd0ca', int: 3402669112}
- {byte_length: 4, bytes: '0xfb29dc70', int: 1893476859}
- {byte_length: 4, bytes: '0xf5f5c999', int: 2580149749}
- {byte_length: 4, bytes: '0xf4f0b8d1', int: 3518558452}
- {byte_length: 4, bytes: '0x830de883', int: 2213023107}
- {byte_length: 4, bytes: '0xe3b4e843', int: 1139324131}
- {byte_length: 4, bytes: '0x4c9ce594', int: 2498075724}
- {byte_length: 4, bytes: '0xa9826dab', int: 2876080809}
- {byte_length: 4, bytes: '0xc40aecb7', int: 3085699780}
- {byte_length: 4, bytes: '0x55490416', int: 369379669}
- {byte_length: 4, bytes: '0x4f2eedc5', int: 3320655439}
- {byte_length: 4, bytes: '0xdd07257e', int: 2116356061}
- {byte_length: 4, bytes: '0x481a57e9', int: 3914799688}
- {byte_length: 4, bytes: '0x4556a493', int: 2477020741}
- {byte_length: 4, bytes: '0xccb781ed', int: 3984701388}
- {byte_length: 4, bytes: '0x6b994065', int: 1698732395}
- {byte_length: 8, bytes: '0x0000000000000000', int: 0}
- {byte_length: 8, bytes: '0x0100000000000000', int: 1}
- {byte_length: 8, bytes: '0xffffffff00000000', int: 4294967295}
- {byte_length: 8, bytes: '0x77d6e31400000000', int: 350475895}
- {byte_length: 8, bytes: '0xf3e681bf00000000', int: 3212961523}
- {byte_length: 8, bytes: '0x62fa7bd800000000', int: 3632003682}
- {byte_length: 8, bytes: '0x82c67b4500000000', int: 1165739650}
- {byte_length: 8, bytes: '0x52577fba00000000', int: 3128907602}
- {byte_length: 8, bytes: '0x5eac939b00000000', int: 2610146398}
- {byte_length: 8, bytes: '0x12ba143700000000', int: 924105234}
- {byte_length: 8, bytes: '0x1d3b893a00000000', int: 982072093}
- {byte_length: 8, bytes: '0x8262153000000000', int: 806707842}
- {byte_length: 8, bytes: '0xbb9cc58e00000000', int: 2395315387}
- {byte_length: 8, bytes: '0x76fef6d100000000', int: 3522625142}
- {byte_length: 8, bytes: '0x0fc3d35700000000', int: 1473495823}
- {byte_length: 8, bytes: '0xc7f851de00000000', int: 3729914055}
- {byte_length: 8, bytes: '0x3a1e5cb200000000', int: 2992381498}
- {byte_length: 8, bytes: '0x3b748e3400000000', int: 881751099}
- {byte_length: 8, bytes: '0xdc92479600000000', int: 2521273052}
- {byte_length: 32, bytes: '0x0000000000000000000000000000000000000000000000000000000000000000',
int: 0}
- {byte_length: 32, bytes: '0x0100000000000000000000000000000000000000000000000000000000000000',
int: 1}
- {byte_length: 32, bytes: '0xffffffff00000000000000000000000000000000000000000000000000000000',
int: 4294967295}
- {byte_length: 32, bytes: '0x2395ad4c00000000000000000000000000000000000000000000000000000000',
int: 1286444323}
- {byte_length: 32, bytes: '0x38a735b800000000000000000000000000000000000000000000000000000000',
int: 3090523960}
- {byte_length: 32, bytes: '0x5a9416e100000000000000000000000000000000000000000000000000000000',
int: 3776353370}
- {byte_length: 32, bytes: '0x220f757500000000000000000000000000000000000000000000000000000000',
int: 1970605858}
- {byte_length: 32, bytes: '0x65bf635200000000000000000000000000000000000000000000000000000000',
int: 1382268773}
- {byte_length: 32, bytes: '0x033f902200000000000000000000000000000000000000000000000000000000',
int: 579878659}
- {byte_length: 32, bytes: '0x2b2d58ab00000000000000000000000000000000000000000000000000000000',
int: 2874682667}
- {byte_length: 32, bytes: '0x15af31da00000000000000000000000000000000000000000000000000000000',
int: 3660689173}
- {byte_length: 32, bytes: '0xd260642e00000000000000000000000000000000000000000000000000000000',
int: 778330322}
- {byte_length: 32, bytes: '0xcdf8429700000000000000000000000000000000000000000000000000000000',
int: 2537748685}
- {byte_length: 32, bytes: '0xc9304b0500000000000000000000000000000000000000000000000000000000',
int: 88813769}
- {byte_length: 32, bytes: '0xf7b7ba0200000000000000000000000000000000000000000000000000000000',
int: 45791223}
- {byte_length: 32, bytes: '0x1ee262d900000000000000000000000000000000000000000000000000000000',
int: 3647136286}
- {byte_length: 32, bytes: '0xb34b03d300000000000000000000000000000000000000000000000000000000',
int: 3540208563}
- {byte_length: 32, bytes: '0x3d52db4d00000000000000000000000000000000000000000000000000000000',
int: 1306219069}
- {byte_length: 32, bytes: '0xd86db47900000000000000000000000000000000000000000000000000000000',
int: 2041867736}
- {byte_length: 48, bytes: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 0}
- {byte_length: 48, bytes: '0x010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1}
- {byte_length: 48, bytes: '0xffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 4294967295}
- {byte_length: 48, bytes: '0x61aeae650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1705946721}
- {byte_length: 48, bytes: '0xd1c08fac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 2895102161}
- {byte_length: 48, bytes: '0x6f36b6c90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3384161903}
- {byte_length: 48, bytes: '0x102f2f3b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 992947984}
- {byte_length: 48, bytes: '0x0f53f9240000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 620319503}
- {byte_length: 48, bytes: '0x5c1d46b30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3007716700}
- {byte_length: 48, bytes: '0x955791510000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1368479637}
- {byte_length: 48, bytes: '0xf934170f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 253179129}
- {byte_length: 48, bytes: '0xc1a8b76f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1874307265}
- {byte_length: 48, bytes: '0xdf3f62c20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3261218783}
- {byte_length: 48, bytes: '0xbd741bc50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3306910909}
- {byte_length: 48, bytes: '0xfe5dc5540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1422220798}
- {byte_length: 48, bytes: '0x364f10df0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3742388022}
- {byte_length: 48, bytes: '0x4a3909450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1158232394}
- {byte_length: 48, bytes: '0xe04760380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 945833952}
- {byte_length: 48, bytes: '0x755c78540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1417174133}
- {byte_length: 96, bytes: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 0}
- {byte_length: 96, bytes: '0x010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1}
- {byte_length: 96, bytes: '0xffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 4294967295}
- {byte_length: 96, bytes: '0xa3274ee20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3796772771}
- {byte_length: 96, bytes: '0x1658135c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1544771606}
- {byte_length: 96, bytes: '0x2af24fb30000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3008361002}
- {byte_length: 96, bytes: '0x9e6bc40a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 180644766}
- {byte_length: 96, bytes: '0x0745b3c50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3316860167}
- {byte_length: 96, bytes: '0xe1b59f830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 2208282081}
- {byte_length: 96, bytes: '0x985a9e6e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1855871640}
- {byte_length: 96, bytes: '0x3d4e3a090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 154816061}
- {byte_length: 96, bytes: '0x6f5dfb630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1677417839}
- {byte_length: 96, bytes: '0x383cdecd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3453893688}
- {byte_length: 96, bytes: '0x38f55ceb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3948737848}
- {byte_length: 96, bytes: '0xcd746f5d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1567585485}
- {byte_length: 96, bytes: '0x3d971e910000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 2434701117}
- {byte_length: 96, bytes: '0x3adff0c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3237011258}
- {byte_length: 96, bytes: '0x5ed40a710000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 1896535134}
- {byte_length: 96, bytes: '0x755d2ed40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
int: 3559808373}

View File

@ -7,7 +7,9 @@ edition = "2018"
[dependencies] [dependencies]
bytes = "0.4" bytes = "0.4"
hashing = { path = "../hashing" } hashing = { path = "../hashing" }
int_to_bytes = { path = "../int_to_bytes" }
[dev-dependencies] [dev-dependencies]
yaml-rust = "0.4.2" yaml-rust = "0.4.2"
hex = "0.3" hex = "0.3"
ethereum-types = "0.5"

View File

@ -1,5 +1,6 @@
use bytes::{Buf, BufMut, BytesMut}; use bytes::Buf;
use hashing::hash; use hashing::hash;
use int_to_bytes::{int_to_bytes1, int_to_bytes4};
use std::cmp::max; use std::cmp::max;
use std::io::Cursor; use std::io::Cursor;
@ -12,14 +13,19 @@ use std::io::Cursor;
/// Returns `None` under any of the following conditions: /// Returns `None` under any of the following conditions:
/// - `list_size == 0` /// - `list_size == 0`
/// - `index >= list_size` /// - `index >= list_size`
/// - `list_size >= usize::max_value() / 2` /// - `list_size > 2**24`
/// - `list_size > usize::max_value() / 2`
pub fn get_permutated_index( pub fn get_permutated_index(
index: usize, index: usize,
list_size: usize, list_size: usize,
seed: &[u8], seed: &[u8],
shuffle_round_count: usize, shuffle_round_count: u8,
) -> Option<usize> { ) -> Option<usize> {
if list_size == 0 || index >= list_size || list_size >= usize::max_value() / 2 { if list_size == 0
|| index >= list_size
|| list_size > usize::max_value() / 2
|| list_size > 2_usize.pow(24)
{
return None; return None;
} }
@ -28,7 +34,7 @@ pub fn get_permutated_index(
let pivot = bytes_to_int64(&hash_with_round(seed, round)[..]) as usize % list_size; let pivot = bytes_to_int64(&hash_with_round(seed, round)[..]) as usize % list_size;
let flip = (pivot + list_size - index) % list_size; let flip = (pivot + list_size - index) % list_size;
let position = max(index, flip); let position = max(index, flip);
let source = hash_with_round_and_position(seed, round, position); let source = hash_with_round_and_position(seed, round, position)?;
let byte = source[(position % 256) / 8]; let byte = source[(position % 256) / 8];
let bit = (byte >> (position % 8)) % 2; let bit = (byte >> (position % 8)) % 2;
index = if bit == 1 { flip } else { index } index = if bit == 1 { flip } else { index }
@ -36,31 +42,23 @@ pub fn get_permutated_index(
Some(index) Some(index)
} }
fn hash_with_round_and_position(seed: &[u8], round: usize, position: usize) -> Vec<u8> { fn hash_with_round_and_position(seed: &[u8], round: u8, position: usize) -> Option<Vec<u8>> {
let mut seed = seed.to_vec(); let mut seed = seed.to_vec();
seed.append(&mut int_to_bytes1(round as u64)); seed.append(&mut int_to_bytes1(round));
seed.append(&mut int_to_bytes4(position as u64 / 256)); /*
hash(&seed[..]) * Note: the specification has an implicit assertion in `int_to_bytes4` that `position / 256 <
* 2**24`. For efficiency, we do not check for that here as it is checked in `get_permutated_index`.
*/
seed.append(&mut int_to_bytes4((position / 256) as u32));
Some(hash(&seed[..]))
} }
fn hash_with_round(seed: &[u8], round: usize) -> Vec<u8> { fn hash_with_round(seed: &[u8], round: u8) -> Vec<u8> {
let mut seed = seed.to_vec(); let mut seed = seed.to_vec();
seed.append(&mut int_to_bytes1(round as u64)); seed.append(&mut int_to_bytes1(round));
hash(&seed[..]) hash(&seed[..])
} }
fn int_to_bytes1(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(8);
bytes.put_u64_le(int);
vec![bytes[0]]
}
fn int_to_bytes4(int: u64) -> Vec<u8> {
let mut bytes = BytesMut::with_capacity(8);
bytes.put_u64_le(int);
bytes[0..4].to_vec()
}
fn bytes_to_int64(bytes: &[u8]) -> u64 { fn bytes_to_int64(bytes: &[u8]) -> u64 {
let mut cursor = Cursor::new(bytes); let mut cursor = Cursor::new(bytes);
cursor.get_u64_le() cursor.get_u64_le()
@ -69,10 +67,48 @@ fn bytes_to_int64(bytes: &[u8]) -> u64 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ethereum_types::H256 as Hash256;
use hex; use hex;
use std::{fs::File, io::prelude::*, path::PathBuf}; use std::{fs::File, io::prelude::*, path::PathBuf};
use yaml_rust::yaml; use yaml_rust::yaml;
#[test]
#[ignore]
fn fuzz_test() {
let max_list_size = 2_usize.pow(24);
let test_runs = 1000;
// Test at max list_size with the end index.
for _ in 0..test_runs {
let index = max_list_size - 1;
let list_size = max_list_size;
let seed = Hash256::random();
let shuffle_rounds = 90;
assert!(get_permutated_index(index, list_size, &seed[..], shuffle_rounds).is_some());
}
// Test at max list_size low indices.
for i in 0..test_runs {
let index = i;
let list_size = max_list_size;
let seed = Hash256::random();
let shuffle_rounds = 90;
assert!(get_permutated_index(index, list_size, &seed[..], shuffle_rounds).is_some());
}
// Test at max list_size high indices.
for i in 0..test_runs {
let index = max_list_size - 1 - i;
let list_size = max_list_size;
let seed = Hash256::random();
let shuffle_rounds = 90;
assert!(get_permutated_index(index, list_size, &seed[..], shuffle_rounds).is_some());
}
}
#[test] #[test]
fn returns_none_for_zero_length_list() { fn returns_none_for_zero_length_list() {
assert_eq!(None, get_permutated_index(100, 0, &[42, 42], 90)); assert_eq!(None, get_permutated_index(100, 0, &[42, 42], 90));
@ -117,10 +153,16 @@ mod tests {
let index = test_case["index"].as_i64().unwrap() as usize; let index = test_case["index"].as_i64().unwrap() as usize;
let list_size = test_case["list_size"].as_i64().unwrap() as usize; let list_size = test_case["list_size"].as_i64().unwrap() as usize;
let permutated_index = test_case["permutated_index"].as_i64().unwrap() as usize; let permutated_index = test_case["permutated_index"].as_i64().unwrap() as usize;
let shuffle_round_count = test_case["shuffle_round_count"].as_i64().unwrap() as usize; let shuffle_round_count = test_case["shuffle_round_count"].as_i64().unwrap();
let seed_string = test_case["seed"].clone().into_string().unwrap(); let seed_string = test_case["seed"].clone().into_string().unwrap();
let seed = hex::decode(seed_string.replace("0x", "")).unwrap(); let seed = hex::decode(seed_string.replace("0x", "")).unwrap();
let shuffle_round_count = if shuffle_round_count < (u8::max_value() as i64) {
shuffle_round_count as u8
} else {
panic!("shuffle_round_count must be a u8")
};
assert_eq!( assert_eq!(
Some(permutated_index), Some(permutated_index),
get_permutated_index(index, list_size, &seed[..], shuffle_round_count), get_permutated_index(index, list_size, &seed[..], shuffle_round_count),