diff --git a/beacon_chain/types/src/shard_and_committee.rs b/beacon_chain/types/src/shard_and_committee.rs index c7fed2e8d..44c2e57ff 100644 --- a/beacon_chain/types/src/shard_and_committee.rs +++ b/beacon_chain/types/src/shard_and_committee.rs @@ -1,5 +1,3 @@ -use super::ssz::{merkle_hash, TreeHash}; - #[derive(Clone, Debug, PartialEq)] pub struct ShardAndCommittee { pub shard: u16, @@ -17,22 +15,6 @@ impl ShardAndCommittee { } } -impl TreeHash for ShardAndCommittee { - fn tree_hash(&self) -> Vec { - let mut committee_ssz_items = Vec::new(); - for c in &self.committee { - let mut h = (*c as u32).tree_hash(); - h.resize(3, 0); - committee_ssz_items.push(h); - } - let mut result = Vec::new(); - result.append(&mut self.shard.tree_hash()); - result.append(&mut merkle_hash(&mut committee_ssz_items)); - - result.as_slice().tree_hash() - } -} - #[cfg(test)] mod tests { use super::*; @@ -43,15 +25,4 @@ mod tests { assert_eq!(s.shard, 0); assert_eq!(s.committee.len(), 0); } - - #[test] - fn test_shard_and_committee_tree_hash() { - let s = ShardAndCommittee { - shard: 1, - committee: vec![1, 2, 3], - }; - - // should test a known hash value - assert_eq!(s.tree_hash().len(), 32); - } } diff --git a/beacon_chain/types/src/validator_record.rs b/beacon_chain/types/src/validator_record.rs index 799e0137f..3a15baeec 100644 --- a/beacon_chain/types/src/validator_record.rs +++ b/beacon_chain/types/src/validator_record.rs @@ -1,17 +1,6 @@ use super::bls::{Keypair, PublicKey}; -use super::ssz::TreeHash; use super::{Address, Hash256}; -pub const HASH_SSZ_VALIDATOR_RECORD_LENGTH: usize = { - 32 + // pubkey.to_bytes(32, 'big') - 2 + // withdrawal_shard.to_bytes(2, 'big') - 20 + // withdrawal_address - 32 + // randao_commitment - 16 + // balance.to_bytes(16, 'big') - 16 + // start_dynasty.to_bytes(8, 'big') - 8 // end_dynasty.to_bytes(8, 'big') -}; - #[derive(Debug, PartialEq, Clone, Copy)] pub enum ValidatorStatus { PendingActivation = 0, @@ -55,32 +44,6 @@ impl ValidatorRecord { } } -impl TreeHash for ValidatorRecord { - fn tree_hash(&self) -> Vec { - let mut ssz = Vec::with_capacity(HASH_SSZ_VALIDATOR_RECORD_LENGTH); - - // From python sample: "val.pubkey.to_bytes(32, 'big')" - // TODO: - // Need to actually convert (szz) pubkey into a big-endian 32 byte - // array. - // Also, our ValidatorRecord seems to be missing the start_dynasty - // and end_dynasty fields - let pub_key_bytes = &mut self.pubkey.as_bytes(); - pub_key_bytes.resize(32, 0); - ssz.append(pub_key_bytes); - - ssz.append(&mut self.withdrawal_shard.tree_hash()); - ssz.append(&mut self.withdrawal_address.tree_hash()); - ssz.append(&mut self.randao_commitment.tree_hash()); - - let mut balance = self.balance.tree_hash(); - balance.resize(16, 0); - ssz.append(&mut balance); - - ssz.as_slice().tree_hash() - } -} - #[cfg(test)] mod tests { use super::*; @@ -96,13 +59,4 @@ mod tests { assert_eq!(v.status, 0); assert_eq!(v.exit_slot, 0); } - - #[test] - fn test_validator_record_ree_hash() { - let (v, _kp) = ValidatorRecord::zero_with_thread_rand_keypair(); - let h = v.tree_hash(); - - // TODO: should check a known hash result value - assert_eq!(h.len(), 32); - } } diff --git a/beacon_chain/utils/ssz/Cargo.toml b/beacon_chain/utils/ssz/Cargo.toml index ec9100927..d70a692b6 100644 --- a/beacon_chain/utils/ssz/Cargo.toml +++ b/beacon_chain/utils/ssz/Cargo.toml @@ -6,4 +6,4 @@ authors = ["Paul Hauner "] [dependencies] bytes = "0.4.9" ethereum-types = "0.4.0" -blake2-rfc = "0.2.18" \ No newline at end of file +hashing = { path = "../hashing" } \ No newline at end of file diff --git a/beacon_chain/utils/ssz/src/impl_tree_hash.rs b/beacon_chain/utils/ssz/src/impl_tree_hash.rs index dfa6da2b2..9d86da2dd 100644 --- a/beacon_chain/utils/ssz/src/impl_tree_hash.rs +++ b/beacon_chain/utils/ssz/src/impl_tree_hash.rs @@ -1,6 +1,6 @@ -extern crate blake2_rfc; +extern crate hashing; -use self::blake2_rfc::blake2b::blake2b; +use self::hashing::canonical_hash; use super::ethereum_types::{Address, H256}; use super::{merkle_hash, ssz_encode, TreeHash}; use std::cmp::Ord; @@ -84,11 +84,8 @@ where } } -/// From the Spec: -/// We define hash(x) as BLAKE2b-512(x)[0:32] fn hash(data: &[u8]) -> Vec { - let result = blake2b(32, &[], &data); - result.as_bytes().to_vec() + canonical_hash(data) } #[cfg(test)] @@ -113,13 +110,12 @@ mod tests { map.insert("f", 5); let result = map.tree_hash(); - // TODO: resolve inconsistencies between the python sample code and - // the spec; and create tests that tie-out to an offical result + // TODO: create tests that tie-out to an offical result assert_eq!( result, [ - 59, 110, 242, 24, 177, 184, 73, 109, 190, 19, 172, 39, 74, 94, 224, 198, 0, 170, - 225, 152, 249, 59, 10, 76, 137, 124, 52, 159, 37, 42, 26, 157 + 232, 63, 235, 91, 115, 69, 159, 54, 95, 239, 147, 30, 179, 96, 232, 210, 225, 31, + 12, 95, 149, 104, 134, 158, 45, 51, 20, 101, 202, 164, 200, 163 ] ); } diff --git a/beacon_chain/utils/ssz/src/tree_hash.rs b/beacon_chain/utils/ssz/src/tree_hash.rs index 0375d207c..33bece9c4 100644 --- a/beacon_chain/utils/ssz/src/tree_hash.rs +++ b/beacon_chain/utils/ssz/src/tree_hash.rs @@ -1,4 +1,4 @@ -const CHUNKSIZE: usize = 128; +const SSZ_CHUNK_SIZE: usize = 128; const HASHSIZE: usize = 32; pub trait TreeHash { @@ -9,15 +9,15 @@ pub trait TreeHash { /// Note that this will consume 'list'. pub fn merkle_hash(list: &mut Vec>) -> Vec { // flatten list - let data = &mut list_to_blob(list); + let (chunk_size, mut data) = list_to_blob(list); // get data_len as bytes. It will hashed will the merkle root - let dlen = data.len() as u64; + let dlen = list.len() as u64; let data_len_bytes = &mut dlen.tree_hash(); data_len_bytes.resize(32, 0); // merklize - let mut mhash = hash_level(data, CHUNKSIZE); + let mut mhash = hash_level(&mut data, chunk_size); while mhash.len() > HASHSIZE { mhash = hash_level(&mut mhash, HASHSIZE); } @@ -33,9 +33,9 @@ fn hash_level(data: &mut Vec, chunk_size: usize) -> Vec { for two_chunks in data.chunks(chunk_size * 2) { if two_chunks.len() == chunk_size && data.len() > chunk_size { // if there is only one chunk here, hash it with a zero-byte - // CHUNKSIZE vector + // SSZ_CHUNK_SIZE vector let mut c = two_chunks.to_vec(); - c.append(&mut vec![0; CHUNKSIZE]); + c.append(&mut vec![0; SSZ_CHUNK_SIZE]); result.append(&mut c.as_slice().tree_hash()); } else { result.append(&mut two_chunks.tree_hash()); @@ -45,46 +45,30 @@ fn hash_level(data: &mut Vec, chunk_size: usize) -> Vec { result } -fn list_to_blob(list: &mut Vec>) -> Vec { - if list[0].len().is_power_of_two() == false { - for x in list.iter_mut() { - extend_to_power_of_2(x); +fn list_to_blob(list: &mut Vec>) -> (usize, Vec) { + let chunk_size = if list.is_empty() { + SSZ_CHUNK_SIZE + } else if list[0].len() < SSZ_CHUNK_SIZE { + let items_per_chunk = SSZ_CHUNK_SIZE / list[0].len(); + items_per_chunk * list[0].len() + } else { + list[0].len() + }; + + let mut data = Vec::new(); + if list.is_empty() { + // handle and empty list + data.append(&mut vec![0; SSZ_CHUNK_SIZE]); + } else { + // just create a blob here; we'll divide into + // chunked slices when we merklize + data.reserve(list[0].len() * list.len()); + for item in list.iter_mut() { + data.append(item); } } - let mut data_len = list[0].len() * list.len(); - - // do we need padding? - let extend_by = if data_len % CHUNKSIZE > 0 { - CHUNKSIZE - (data_len % CHUNKSIZE) - } else { - 0 - }; - - // allocate buffer and append each list element (flatten the vec of vecs) - data_len += extend_by; - let mut data: Vec = Vec::with_capacity(data_len); - for x in list.iter_mut() { - data.append(x); - } - - // add padding - let mut i = 0; - while i < extend_by { - data.push(0); - i += 1; - } - - data -} - -/// Extends data length to a power of 2 by minimally right-zero-padding -fn extend_to_power_of_2(data: &mut Vec) { - let len = data.len(); - let new_len = len.next_power_of_two(); - if new_len > len { - data.resize(new_len, 0); - } + (chunk_size, data) } #[cfg(test)] @@ -103,5 +87,4 @@ mod tests { assert_eq!(HASHSIZE, result.len()); println!("merkle_hash: {:?}", result); } - }