github.com/prysmaticlabs/prysm@v1.4.4/tools/specs-checker/data/ssz/merkle-proofs.md (about) 1 ```python 2 def get_power_of_two_ceil(x: int) -> int: 3 """ 4 Get the power of 2 for given input, or the closest higher power of 2 if the input is not a power of 2. 5 Commonly used for "how many nodes do I need for a bottom tree layer fitting x elements?" 6 Example: 0->1, 1->1, 2->2, 3->4, 4->4, 5->8, 6->8, 7->8, 8->8, 9->16. 7 """ 8 if x <= 1: 9 return 1 10 elif x == 2: 11 return 2 12 else: 13 return 2 * get_power_of_two_ceil((x + 1) // 2) 14 ``` 15 ```python 16 def get_power_of_two_floor(x: int) -> int: 17 """ 18 Get the power of 2 for given input, or the closest lower power of 2 if the input is not a power of 2. 19 The zero case is a placeholder and not used for math with generalized indices. 20 Commonly used for "what power of two makes up the root bit of the generalized index?" 21 Example: 0->1, 1->1, 2->2, 3->2, 4->4, 5->4, 6->4, 7->4, 8->8, 9->8 22 """ 23 if x <= 1: 24 return 1 25 if x == 2: 26 return x 27 else: 28 return 2 * get_power_of_two_floor(x // 2) 29 ``` 30 ```python 31 def merkle_tree(leaves: Sequence[Bytes32]) -> Sequence[Bytes32]: 32 """ 33 Return an array representing the tree nodes by generalized index: 34 [0, 1, 2, 3, 4, 5, 6, 7], where each layer is a power of 2. The 0 index is ignored. The 1 index is the root. 35 The result will be twice the size as the padded bottom layer for the input leaves. 36 """ 37 bottom_length = get_power_of_two_ceil(len(leaves)) 38 o = [Bytes32()] * bottom_length + list(leaves) + [Bytes32()] * (bottom_length - len(leaves)) 39 for i in range(bottom_length - 1, 0, -1): 40 o[i] = hash(o[i * 2] + o[i * 2 + 1]) 41 return o 42 ``` 43 ```python 44 def item_length(typ: SSZType) -> int: 45 """ 46 Return the number of bytes in a basic type, or 32 (a full hash) for compound types. 47 """ 48 if issubclass(typ, BasicValue): 49 return typ.byte_len 50 else: 51 return 32 52 ``` 53 ```python 54 def get_elem_type(typ: Union[BaseBytes, BaseList, Container], 55 index_or_variable_name: Union[int, SSZVariableName]) -> SSZType: 56 """ 57 Return the type of the element of an object of the given type with the given index 58 or member variable name (eg. `7` for `x[7]`, `"foo"` for `x.foo`) 59 """ 60 return typ.get_fields()[index_or_variable_name] if issubclass(typ, Container) else typ.elem_type 61 ``` 62 ```python 63 def chunk_count(typ: SSZType) -> int: 64 """ 65 Return the number of hashes needed to represent the top-level elements in the given type 66 (eg. `x.foo` or `x[7]` but not `x[7].bar` or `x.foo.baz`). In all cases except lists/vectors 67 of basic types, this is simply the number of top-level elements, as each element gets one 68 hash. For lists/vectors of basic types, it is often fewer because multiple basic elements 69 can be packed into one 32-byte chunk. 70 """ 71 # typ.length describes the limit for list types, or the length for vector types. 72 if issubclass(typ, BasicValue): 73 return 1 74 elif issubclass(typ, Bits): 75 return (typ.length + 255) // 256 76 elif issubclass(typ, Elements): 77 return (typ.length * item_length(typ.elem_type) + 31) // 32 78 elif issubclass(typ, Container): 79 return len(typ.get_fields()) 80 else: 81 raise Exception(f"Type not supported: {typ}") 82 ``` 83 ```python 84 def get_item_position(typ: SSZType, index_or_variable_name: Union[int, SSZVariableName]) -> Tuple[int, int, int]: 85 """ 86 Return three variables: 87 (i) the index of the chunk in which the given element of the item is represented; 88 (ii) the starting byte position within the chunk; 89 (iii) the ending byte position within the chunk. 90 For example: for a 6-item list of uint64 values, index=2 will return (0, 16, 24), index=5 will return (1, 8, 16) 91 """ 92 if issubclass(typ, Elements): 93 index = int(index_or_variable_name) 94 start = index * item_length(typ.elem_type) 95 return start // 32, start % 32, start % 32 + item_length(typ.elem_type) 96 elif issubclass(typ, Container): 97 variable_name = index_or_variable_name 98 return typ.get_field_names().index(variable_name), 0, item_length(get_elem_type(typ, variable_name)) 99 else: 100 raise Exception("Only lists/vectors/containers supported") 101 ``` 102 ```python 103 def get_generalized_index(typ: SSZType, path: Sequence[Union[int, SSZVariableName]]) -> GeneralizedIndex: 104 """ 105 Converts a path (eg. `[7, "foo", 3]` for `x[7].foo[3]`, `[12, "bar", "__len__"]` for 106 `len(x[12].bar)`) into the generalized index representing its position in the Merkle tree. 107 """ 108 root = GeneralizedIndex(1) 109 for p in path: 110 assert not issubclass(typ, BasicValue) # If we descend to a basic type, the path cannot continue further 111 if p == '__len__': 112 typ = uint64 113 assert issubclass(typ, (List, ByteList)) 114 root = GeneralizedIndex(root * 2 + 1) 115 else: 116 pos, _, _ = get_item_position(typ, p) 117 base_index = (GeneralizedIndex(2) if issubclass(typ, (List, ByteList)) else GeneralizedIndex(1)) 118 root = GeneralizedIndex(root * base_index * get_power_of_two_ceil(chunk_count(typ)) + pos) 119 typ = get_elem_type(typ, p) 120 return root 121 ``` 122 ```python 123 def concat_generalized_indices(*indices: GeneralizedIndex) -> GeneralizedIndex: 124 """ 125 Given generalized indices i1 for A -> B, i2 for B -> C .... i_n for Y -> Z, returns 126 the generalized index for A -> Z. 127 """ 128 o = GeneralizedIndex(1) 129 for i in indices: 130 o = GeneralizedIndex(o * get_power_of_two_floor(i) + (i - get_power_of_two_floor(i))) 131 return o 132 ``` 133 ```python 134 def get_generalized_index_length(index: GeneralizedIndex) -> int: 135 """ 136 Return the length of a path represented by a generalized index. 137 """ 138 return int(log2(index)) 139 ``` 140 ```python 141 def get_generalized_index_bit(index: GeneralizedIndex, position: int) -> bool: 142 """ 143 Return the given bit of a generalized index. 144 """ 145 return (index & (1 << position)) > 0 146 ``` 147 ```python 148 def generalized_index_sibling(index: GeneralizedIndex) -> GeneralizedIndex: 149 return GeneralizedIndex(index ^ 1) 150 ``` 151 ```python 152 def generalized_index_child(index: GeneralizedIndex, right_side: bool) -> GeneralizedIndex: 153 return GeneralizedIndex(index * 2 + right_side) 154 ``` 155 ```python 156 def generalized_index_parent(index: GeneralizedIndex) -> GeneralizedIndex: 157 return GeneralizedIndex(index // 2) 158 ``` 159 ```python 160 def get_branch_indices(tree_index: GeneralizedIndex) -> Sequence[GeneralizedIndex]: 161 """ 162 Get the generalized indices of the sister chunks along the path from the chunk with the 163 given tree index to the root. 164 """ 165 o = [generalized_index_sibling(tree_index)] 166 while o[-1] > 1: 167 o.append(generalized_index_sibling(generalized_index_parent(o[-1]))) 168 return o[:-1] 169 ``` 170 ```python 171 def get_path_indices(tree_index: GeneralizedIndex) -> Sequence[GeneralizedIndex]: 172 """ 173 Get the generalized indices of the chunks along the path from the chunk with the 174 given tree index to the root. 175 """ 176 o = [tree_index] 177 while o[-1] > 1: 178 o.append(generalized_index_parent(o[-1])) 179 return o[:-1] 180 ``` 181 ```python 182 def get_helper_indices(indices: Sequence[GeneralizedIndex]) -> Sequence[GeneralizedIndex]: 183 """ 184 Get the generalized indices of all "extra" chunks in the tree needed to prove the chunks with the given 185 generalized indices. Note that the decreasing order is chosen deliberately to ensure equivalence to the 186 order of hashes in a regular single-item Merkle proof in the single-item case. 187 """ 188 all_helper_indices: Set[GeneralizedIndex] = set() 189 all_path_indices: Set[GeneralizedIndex] = set() 190 for index in indices: 191 all_helper_indices = all_helper_indices.union(set(get_branch_indices(index))) 192 all_path_indices = all_path_indices.union(set(get_path_indices(index))) 193 194 return sorted(all_helper_indices.difference(all_path_indices), reverse=True) 195 ``` 196 ```python 197 def calculate_merkle_root(leaf: Bytes32, proof: Sequence[Bytes32], index: GeneralizedIndex) -> Root: 198 assert len(proof) == get_generalized_index_length(index) 199 for i, h in enumerate(proof): 200 if get_generalized_index_bit(index, i): 201 leaf = hash(h + leaf) 202 else: 203 leaf = hash(leaf + h) 204 return leaf 205 ``` 206 ```python 207 def verify_merkle_proof(leaf: Bytes32, proof: Sequence[Bytes32], index: GeneralizedIndex, root: Root) -> bool: 208 return calculate_merkle_root(leaf, proof, index) == root 209 ``` 210 ```python 211 def calculate_multi_merkle_root(leaves: Sequence[Bytes32], 212 proof: Sequence[Bytes32], 213 indices: Sequence[GeneralizedIndex]) -> Root: 214 assert len(leaves) == len(indices) 215 helper_indices = get_helper_indices(indices) 216 assert len(proof) == len(helper_indices) 217 objects = { 218 **{index: node for index, node in zip(indices, leaves)}, 219 **{index: node for index, node in zip(helper_indices, proof)} 220 } 221 keys = sorted(objects.keys(), reverse=True) 222 pos = 0 223 while pos < len(keys): 224 k = keys[pos] 225 if k in objects and k ^ 1 in objects and k // 2 not in objects: 226 objects[GeneralizedIndex(k // 2)] = hash( 227 objects[GeneralizedIndex((k | 1) ^ 1)] + 228 objects[GeneralizedIndex(k | 1)] 229 ) 230 keys.append(GeneralizedIndex(k // 2)) 231 pos += 1 232 return objects[GeneralizedIndex(1)] 233 ``` 234 ```python 235 def verify_merkle_multiproof(leaves: Sequence[Bytes32], 236 proof: Sequence[Bytes32], 237 indices: Sequence[GeneralizedIndex], 238 root: Root) -> bool: 239 return calculate_multi_merkle_root(leaves, proof, indices) == root 240 ```