Refactor tree hashing (#861)

* Pre-allocated tree hash caches

* Add SmallVec to tree hash cache

* Avoid allocation for validator.pubkey

* Avoid iterator which seems to be doing heap alloc

* Add more smallvecs

* MOAR SMALLVEC

* Move non-test code to Hash256 tree hash

* Fix byte ordering error

* Add incomplete but working merkle stream impl

* Fix zero hash error

* Add zero hash fn

* Add MerkleStream comments

* Add smallvec, tidy

* Integrate into tree hash derive

* Update ssz_types tree hash

* Don't heap alloc for mix in length

* Add byte-level streaming to MerkleStream

* Avoid recursion in write method

* Update BLS to MerkleStream

* Fix some not-compiling tests

* Remove debug profiling

* Remove code duplication

* Move beacon state tree hash to new hasher

* Fix failing tests

* Update comments

* Add some fast-paths to tree_hash::merkle_root

* Remove unncessary test

* Rename MerkleStream -> MerkleHasher

* Rename new_with_leaf_count -> with_leaves

* Tidy

* Remove NonZeroUsize

* Remove todo

* Update smallvec
This commit is contained in:
Paul Hauner
2020-03-05 08:07:27 +11:00
committed by GitHub
parent 12999fb06c
commit 7f6ae4c2f5
43 changed files with 1076 additions and 292 deletions

View File

@@ -56,13 +56,20 @@ macro_rules! impl_tree_hash {
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> Vec<u8> {
fn tree_hash_root(&self) -> tree_hash::Hash256 {
// We could use the tree hash implementation for `FixedVec<u8, $byte_size>`,
// but benchmarks have show that to be at least 15% slower because of the
// unnecessary copying and allocation (one Vec per byte)
let values_per_chunk = tree_hash::BYTES_PER_CHUNK;
let minimum_chunk_count = ($byte_size + values_per_chunk - 1) / values_per_chunk;
tree_hash::merkle_root(&self.as_ssz_bytes(), minimum_chunk_count)
let mut hasher = tree_hash::MerkleHasher::with_leaves(minimum_chunk_count);
hasher
.write(&self.as_ssz_bytes())
.expect("bls should not exceed leaf count");
hasher
.finish()
.expect("bls should not exceed leaf count from buffer")
}
}
};
@@ -111,6 +118,10 @@ macro_rules! bytes_struct {
self.bytes.to_vec()
}
pub fn as_slice(&self) -> &[u8] {
&self.bytes
}
fn get_bytes(bytes: &[u8]) -> Result<[u8; $byte_size], ssz::DecodeError> {
let mut result = [0; $byte_size];
if bytes.len() != $byte_size {
@@ -171,7 +182,28 @@ macro_rules! bytes_struct {
impl_ssz!($name, $byte_size, "$type");
impl_tree_hash!($name, $byte_size);
impl tree_hash::TreeHash for $name {
fn tree_hash_type() -> tree_hash::TreeHashType {
tree_hash::TreeHashType::Vector
}
fn tree_hash_packed_encoding(&self) -> Vec<u8> {
unreachable!("Vector should never be packed.")
}
fn tree_hash_packing_factor() -> usize {
unreachable!("Vector should never be packed.")
}
fn tree_hash_root(&self) -> tree_hash::Hash256 {
let values_per_chunk = tree_hash::BYTES_PER_CHUNK;
let minimum_chunk_count = ($byte_size + values_per_chunk - 1) / values_per_chunk;
let mut hasher = tree_hash::MerkleHasher::with_leaves(minimum_chunk_count);
hasher.write(&self.bytes).expect("bls should not exceed leaf count");
hasher.finish().expect("bls should not exceed leaf count from buffer")
}
}
impl serde::ser::Serialize for $name {
/// Serde serialization is compliant the Ethereum YAML test format.