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