updated with latest spec changes

This commit is contained in:
mjkeating 2018-12-12 13:48:54 -08:00
parent 1e4e92bf2e
commit be2c82a732
5 changed files with 34 additions and 130 deletions

View File

@ -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<u8> {
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);
}
}

View File

@ -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<u8> {
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);
}
}

View File

@ -6,4 +6,4 @@ authors = ["Paul Hauner <paul@paulhauner.com>"]
[dependencies]
bytes = "0.4.9"
ethereum-types = "0.4.0"
blake2-rfc = "0.2.18"
hashing = { path = "../hashing" }

View File

@ -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<u8> {
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
]
);
}

View File

@ -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<u8>>) -> Vec<u8> {
// 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<u8>, chunk_size: usize) -> Vec<u8> {
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<u8>, chunk_size: usize) -> Vec<u8> {
result
}
fn list_to_blob(list: &mut Vec<Vec<u8>>) -> Vec<u8> {
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<Vec<u8>>) -> (usize, Vec<u8>) {
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<u8> = 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<u8>) {
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);
}
}