|
1 | 1 | use tree_hash::{Hash256, MerkleHasher, TreeHash, TreeHashType};
|
2 | 2 | use typenum::Unsigned;
|
| 3 | +use crate::typenum_helpers::to_usize; |
3 | 4 |
|
4 |
| -/// A helper function providing common functionality between the `TreeHash` implementations for |
5 |
| -/// `FixedVector` and `VariableList`. |
6 |
| -pub fn vec_tree_hash_root<T, N>(vec: &[T]) -> Hash256 |
7 |
| -where |
8 |
| - T: TreeHash, |
9 |
| - N: Unsigned, |
10 |
| -{ |
| 5 | +pub fn packing_factor<T: TreeHash>() -> usize { |
11 | 6 | match T::tree_hash_type() {
|
12 |
| - TreeHashType::Basic => { |
13 |
| - let mut hasher = MerkleHasher::with_leaves( |
14 |
| - (N::to_usize() + T::tree_hash_packing_factor() - 1) / T::tree_hash_packing_factor(), |
15 |
| - ); |
| 7 | + TreeHashType::Basic => T::tree_hash_packing_factor(), |
| 8 | + TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => 1, |
| 9 | + } |
| 10 | +} |
| 11 | + |
| 12 | +mod default_impl { |
| 13 | + use super::*; |
| 14 | + /// A helper function providing common functionality between the `TreeHash` implementations for |
| 15 | + /// `FixedVector` and `VariableList`. |
| 16 | + pub fn vec_tree_hash_root<T, N>(vec: &[T]) -> Hash256 |
| 17 | + where |
| 18 | + T: TreeHash, |
| 19 | + N: Unsigned, |
| 20 | + { |
| 21 | + match T::tree_hash_type() { |
| 22 | + TreeHashType::Basic => { |
| 23 | + let mut hasher = MerkleHasher::with_leaves( |
| 24 | + (to_usize::<N>() + T::tree_hash_packing_factor() - 1) / T::tree_hash_packing_factor(), |
| 25 | + ); |
| 26 | + |
| 27 | + for item in vec { |
| 28 | + hasher |
| 29 | + .write(&item.tree_hash_packed_encoding()) |
| 30 | + .expect("ssz_types variable vec should not contain more elements than max"); |
| 31 | + } |
16 | 32 |
|
17 |
| - for item in vec { |
18 | 33 | hasher
|
19 |
| - .write(&item.tree_hash_packed_encoding()) |
20 |
| - .expect("ssz_types variable vec should not contain more elements than max"); |
| 34 | + .finish() |
| 35 | + .expect("ssz_types variable vec should not have a remaining buffer") |
21 | 36 | }
|
| 37 | + TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => { |
| 38 | + let mut hasher = MerkleHasher::with_leaves(N::to_usize()); |
22 | 39 |
|
23 |
| - hasher |
24 |
| - .finish() |
25 |
| - .expect("ssz_types variable vec should not have a remaining buffer") |
26 |
| - } |
27 |
| - TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => { |
28 |
| - let mut hasher = MerkleHasher::with_leaves(N::to_usize()); |
| 40 | + for item in vec { |
| 41 | + hasher |
| 42 | + .write(item.tree_hash_root().as_slice()) |
| 43 | + .expect("ssz_types vec should not contain more elements than max"); |
| 44 | + } |
29 | 45 |
|
30 |
| - for item in vec { |
31 | 46 | hasher
|
32 |
| - .write(item.tree_hash_root().as_slice()) |
33 |
| - .expect("ssz_types vec should not contain more elements than max"); |
| 47 | + .finish() |
| 48 | + .expect("ssz_types vec should not have a remaining buffer") |
34 | 49 | }
|
| 50 | + } |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +#[cfg(feature="cap-typenum-to-usize-overflow")] |
| 55 | +mod arch_32x_workaround { |
| 56 | + use super::*; |
| 57 | + use tree_hash::{Hash256, TreeHash}; |
| 58 | + use typenum::Unsigned; |
| 59 | + use ethereum_hashing::{hash32_concat, ZERO_HASHES}; |
| 60 | + |
| 61 | + type MaxDepth = typenum::U536870912; |
35 | 62 |
|
36 |
| - hasher |
37 |
| - .finish() |
38 |
| - .expect("ssz_types vec should not have a remaining buffer") |
| 63 | + fn pad_to_depth<Current: Unsigned, Target: Unsigned>(hash: Hash256, target_depth: usize, current_depth: usize) -> Hash256 { |
| 64 | + let mut curhash: [u8; 32] = hash.0; |
| 65 | + for depth in current_depth..target_depth { |
| 66 | + curhash = hash32_concat(&curhash, ZERO_HASHES[depth].as_slice()); |
| 67 | + } |
| 68 | + curhash.into() |
| 69 | + } |
| 70 | + |
| 71 | + fn target_tree_depth<T: TreeHash, N: Unsigned>() -> usize { |
| 72 | + let packing_factor = packing_factor::<T>(); |
| 73 | + let packing_factor_log2 = packing_factor.next_power_of_two().ilog2() as usize; |
| 74 | + let tree_depth = N::to_u64().next_power_of_two().ilog2() as usize; |
| 75 | + tree_depth - packing_factor_log2 |
| 76 | + } |
| 77 | + |
| 78 | + pub fn vec_tree_hash_root<T: TreeHash, N: Unsigned>(vec: &[T]) -> Hash256 { |
| 79 | + if N::to_u64() <= MaxDepth::to_u64() { |
| 80 | + default_impl::vec_tree_hash_root::<T, N>(vec) |
| 81 | + } else { |
| 82 | + let main_tree_hash = default_impl::vec_tree_hash_root::<T, MaxDepth>(vec); |
| 83 | + |
| 84 | + let target_depth = target_tree_depth::<T, N>(); |
| 85 | + let current_depth = target_tree_depth::<T, MaxDepth>(); |
| 86 | + |
| 87 | + pad_to_depth::<MaxDepth, N>(main_tree_hash, target_depth, current_depth) |
39 | 88 | }
|
40 | 89 | }
|
41 | 90 | }
|
| 91 | + |
| 92 | +#[cfg(any(target_pointer_width = "64", not(feature="cap-typenum-to-usize-overflow")))] |
| 93 | +pub use default_impl::vec_tree_hash_root; |
| 94 | + |
| 95 | +#[cfg(all(not(target_pointer_width = "64"), feature="cap-typenum-to-usize-overflow"))] |
| 96 | +pub use arch_32x_workaround::vec_tree_hash_root; |
0 commit comments