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  ```