diff --git a/Cargo.toml b/Cargo.toml index f0488b33d..5ab0ba847 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "eth2/utils/boolean-bitfield", "eth2/utils/hashing", "eth2/utils/honey-badger-split", + "eth2/utils/int_to_bytes", "eth2/utils/slot_clock", "eth2/utils/ssz", "eth2/utils/swap_or_not_shuffle", diff --git a/eth2/block_producer/Cargo.toml b/eth2/block_producer/Cargo.toml index 86dde92f7..15d1343cc 100644 --- a/eth2/block_producer/Cargo.toml +++ b/eth2/block_producer/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Paul Hauner "] edition = "2018" [dependencies] -slot_clock = { path = "../../eth2/utils/slot_clock" } -ssz = { path = "../../eth2/utils/ssz" } -types = { path = "../../eth2/types" } +int_to_bytes = { path = "../utils/int_to_bytes" } +slot_clock = { path = "../utils/slot_clock" } +ssz = { path = "../utils/ssz" } +types = { path = "../types" } diff --git a/eth2/block_producer/src/lib.rs b/eth2/block_producer/src/lib.rs index f6a0fd6df..7b15eb4e9 100644 --- a/eth2/block_producer/src/lib.rs +++ b/eth2/block_producer/src/lib.rs @@ -1,8 +1,8 @@ pub mod test_utils; mod traits; +use int_to_bytes::int_to_bytes32; use slot_clock::SlotClock; -use ssz::ssz_encode; use std::sync::Arc; use types::{BeaconBlock, ChainSpec, Slot}; @@ -132,7 +132,7 @@ impl BlockProducer Result { let randao_reveal = { // 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) { None => return Ok(PollOutcome::SignerRejection(slot)), diff --git a/eth2/state_processing/Cargo.toml b/eth2/state_processing/Cargo.toml index 683475f47..b6b0ea57c 100644 --- a/eth2/state_processing/Cargo.toml +++ b/eth2/state_processing/Cargo.toml @@ -6,6 +6,7 @@ edition = "2018" [dependencies] hashing = { path = "../utils/hashing" } +int_to_bytes = { path = "../utils/int_to_bytes" } integer-sqrt = "0.1" log = "0.4" ssz = { path = "../utils/ssz" } diff --git a/eth2/state_processing/src/block_processable.rs b/eth2/state_processing/src/block_processable.rs index f043a723d..368460116 100644 --- a/eth2/state_processing/src/block_processable.rs +++ b/eth2/state_processing/src/block_processable.rs @@ -1,5 +1,6 @@ use crate::SlotProcessingError; use hashing::hash; +use int_to_bytes::int_to_bytes32; use log::debug; use ssz::{ssz_encode, TreeHash}; use types::{ @@ -110,7 +111,7 @@ fn per_block_processing_signature_optional( ensure!( bls_verify( &block_proposer.pubkey, - &ssz_encode(&state.current_epoch(spec)), + &int_to_bytes32(state.current_epoch(spec).as_u64()), &block.randao_reveal, get_domain(&state.fork, state.current_epoch(spec), DOMAIN_RANDAO) ), diff --git a/eth2/utils/int_to_bytes/Cargo.toml b/eth2/utils/int_to_bytes/Cargo.toml new file mode 100644 index 000000000..f7424e032 --- /dev/null +++ b/eth2/utils/int_to_bytes/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "int_to_bytes" +version = "0.1.0" +authors = ["Paul Hauner "] +edition = "2018" + +[dependencies] +bytes = "0.4" + +[dev-dependencies] +yaml-rust = "0.4.2" +hex = "0.3" diff --git a/eth2/utils/int_to_bytes/src/lib.rs b/eth2/utils/int_to_bytes/src/lib.rs new file mode 100644 index 000000000..537d7b5c8 --- /dev/null +++ b/eth2/utils/int_to_bytes/src/lib.rs @@ -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 { + vec![int] +} + +/// Returns `int` as little-endian bytes with a length of 2. +pub fn int_to_bytes2(int: u16) -> Vec { + 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> { + 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 { + 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 { + 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 { + 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 { + 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 { + 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."), + } + } + } +} diff --git a/eth2/utils/int_to_bytes/src/specs/test_vector_int_to_bytes.yml b/eth2/utils/int_to_bytes/src/specs/test_vector_int_to_bytes.yml new file mode 100644 index 000000000..3b7eaa9c1 --- /dev/null +++ b/eth2/utils/int_to_bytes/src/specs/test_vector_int_to_bytes.yml @@ -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} diff --git a/eth2/utils/swap_or_not_shuffle/Cargo.toml b/eth2/utils/swap_or_not_shuffle/Cargo.toml index 1c898f7b1..272abf608 100644 --- a/eth2/utils/swap_or_not_shuffle/Cargo.toml +++ b/eth2/utils/swap_or_not_shuffle/Cargo.toml @@ -7,7 +7,9 @@ edition = "2018" [dependencies] bytes = "0.4" hashing = { path = "../hashing" } +int_to_bytes = { path = "../int_to_bytes" } [dev-dependencies] yaml-rust = "0.4.2" hex = "0.3" +ethereum-types = "0.5" diff --git a/eth2/utils/swap_or_not_shuffle/src/lib.rs b/eth2/utils/swap_or_not_shuffle/src/lib.rs index 3566ac23a..753265f3e 100644 --- a/eth2/utils/swap_or_not_shuffle/src/lib.rs +++ b/eth2/utils/swap_or_not_shuffle/src/lib.rs @@ -1,5 +1,6 @@ -use bytes::{Buf, BufMut, BytesMut}; +use bytes::Buf; use hashing::hash; +use int_to_bytes::{int_to_bytes1, int_to_bytes4}; use std::cmp::max; use std::io::Cursor; @@ -12,14 +13,19 @@ use std::io::Cursor; /// Returns `None` under any of the following conditions: /// - `list_size == 0` /// - `index >= list_size` -/// - `list_size >= usize::max_value() / 2` +/// - `list_size > 2**24` +/// - `list_size > usize::max_value() / 2` pub fn get_permutated_index( index: usize, list_size: usize, seed: &[u8], - shuffle_round_count: usize, + shuffle_round_count: u8, ) -> Option { - 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; } @@ -28,7 +34,7 @@ pub fn get_permutated_index( let pivot = bytes_to_int64(&hash_with_round(seed, round)[..]) as usize % list_size; let flip = (pivot + list_size - index) % list_size; 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 bit = (byte >> (position % 8)) % 2; index = if bit == 1 { flip } else { index } @@ -36,31 +42,23 @@ pub fn get_permutated_index( Some(index) } -fn hash_with_round_and_position(seed: &[u8], round: usize, position: usize) -> Vec { +fn hash_with_round_and_position(seed: &[u8], round: u8, position: usize) -> Option> { let mut seed = seed.to_vec(); - seed.append(&mut int_to_bytes1(round as u64)); - seed.append(&mut int_to_bytes4(position as u64 / 256)); - hash(&seed[..]) + seed.append(&mut int_to_bytes1(round)); + /* + * 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 { +fn hash_with_round(seed: &[u8], round: u8) -> 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[..]) } -fn int_to_bytes1(int: u64) -> Vec { - let mut bytes = BytesMut::with_capacity(8); - bytes.put_u64_le(int); - vec![bytes[0]] -} - -fn int_to_bytes4(int: u64) -> Vec { - let mut bytes = BytesMut::with_capacity(8); - bytes.put_u64_le(int); - bytes[0..4].to_vec() -} - fn bytes_to_int64(bytes: &[u8]) -> u64 { let mut cursor = Cursor::new(bytes); cursor.get_u64_le() @@ -69,10 +67,48 @@ fn bytes_to_int64(bytes: &[u8]) -> u64 { #[cfg(test)] mod tests { use super::*; + use ethereum_types::H256 as Hash256; use hex; use std::{fs::File, io::prelude::*, path::PathBuf}; 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] fn returns_none_for_zero_length_list() { 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 list_size = test_case["list_size"].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 = 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!( Some(permutated_index), get_permutated_index(index, list_size, &seed[..], shuffle_round_count),