Example #1
0
  def update(self, old, new):
    """Updates a leaf.

    :param old: a leaf that is already in the tree.
      It can by either a real object (int, str, etc.)
      or the hash value of that object.
    :param old: a leaf that will replace the old one.
    """
    # accepting leaves of the same type only
    if type(old) != type(new):
      raise TypeError(
        'Old and the new value are of different types.'
        'You should hash them to avoid the error.'
      )
    mapping, hasher = self._mapping, self._hasher
    # first, assuming leaves are hash values in hex
    leaf = mapping.get(utils.from_hex(old))
    if leaf is None:
      leaf = mapping.get(hasher.hash_leaf(old))
      new = hasher.hash_leaf(new)
    # raise the error if the leaf's not found
    if not isinstance(leaf, MerkleNode):
      raise KeyError('Invalid old value.')
    leaf.hash = utils.from_hex(new)
    self._rehash(leaf)
Example #2
0
 def __eq__(self, other):
   """Checks if the trees are identical."""
   root_hash = self._root.hash
   if isinstance(other, MerkleTree):
     other_root_hash = utils.from_hex(other.merkle_root)
   else:
     other = utils.to_string(other)
     other_root_hash = utils.from_hex(other)
   return root_hash == other_root_hash
Example #3
0
def verify_tree_consistency(new_tree, old_root, old_size):
  """Verifies that the new tree contains the same nodes
   and in the same order as a given subtree.

  :param new_tree: Merkle tree whose certain nodes will be
    concatenated and hashed to produce the Merkle hash root of a subtree;
    thus proving consistency of both trees.
  :param old_root: the Merkle hash root of the old tree (or a subtree).
  :param old_size: number of leaves in the old tree.
  :return: True if both the old and new trees are consistent.
  """
  if not isinstance(new_tree, MerkleTree):
    raise TypeError(f'Expected MerkleTree, got {type(new_tree)}')

  new_size = len(new_tree)
  # the number of leaves in the old tree
  # cannot be greater than in the new tree
  if new_size < old_size:
    return False

  # assuming both hashes are hexadecimal strings
  old_root = utils.from_hex(old_root)
  new_root = utils.from_hex(new_tree.merkle_root)

  # if the number of leaves is identical
  # then roots also must be identical
  if new_size == old_size:
    return old_root == new_root

  leaves = new_tree.leaves
  index, paths = 0, []

  while old_size > 0:
    # level is the largest power of two smaller than old_size
    # log2(level) will indicate where we should be climbing
    level = 2**(old_size.bit_length() - 1)
    node = _climb_to(leaves[index], math.log(level, 2))
    if node is None:
      return False
    paths.append(node)
    index += level
    old_size -= level

  # if old_size is power of two (len(paths) == 1)
  # then we have our searched Merkle hash root
  # otherwise we will need to concatenate all nodes
  if len(paths) > 1:
    paths = paths[::-1]
    hasher = new_tree.hasher
    concat = lambda a,b: _concat(hasher, a, b)
    new_root = functools.reduce(concat, paths)
  else:
    new_root = paths[0].hash
  return new_root == old_root
Example #4
0
def lists_to_proof(hashlist, typelist):
    nodepath = []
    for nodehash, nodetype in zip(hashlist, typelist):
        nodepath.append(merklelib.AuditNode(utils.from_hex(nodehash),
                                            nodetype))
    proof = merklelib.AuditProof(nodepath)
    return proof
Example #5
0
  def get_proof(self, leaf):
    """Provides an audit proof for a leaf.

    :param leaf: a leaf which is represented by either a real object
      (int, str, etc.) or the hash value of that object.
    :return audit proof: a collection of all hashes
      such that if traversed and concatenated,
      will produce the original merkle hash root
    """
    mapping, hasher = self._mapping, self._hasher
    # assuming that leaf in hexadecimal representation
    target = mapping.get(utils.from_hex(leaf))
    if target is None:
      target = mapping.get(hasher.hash_leaf(leaf))
    # no leaf in mapping, return an empty AuditProof object
    if not isinstance(target, MerkleNode):
      return AuditProof([])
    root, paths = self._root, []
    # saving every sibiling node (if not _empty)
    # until the root node is reached
    while target is not root:
      sibiling = target.sibiling
      if sibiling is not _empty:
        node = AuditNode(sibiling.hash, sibiling.type)
        paths.append(node)
      target = target.parent
    return AuditProof(paths)
Example #6
0
def verify_leaf_inclusion(target, proof, hashobj, root_hash):
  """Verifies that a tree includes a leaf.

  :param target: a leaf which is represented by either a real object
    (int, str, etc.) or the hash value of that object.
  :param proof: a data structure that contains
    AuditNode objects which serve to recreate the original Merkle hash root.
  :param hashobj: a hash function or Hasher. If a hash function is provided,
    it will be used to convert a Hasher instance.
  :param root_hash: Merkle hash root provided by a trusted authority.
  :return: True if the leaf is included in the tree.
  """
  if not isinstance(hashobj, Hasher):
    if not callable(hashobj):
      raise TypeError(f'Expected callable, got {type(hashobj)}')
    hashobj = Hasher(hashobj)

  hasher = hashobj
  paths = None

  # any collection containing AuditNode objects.
  if isinstance(proof, collections.Iterable):
    if isinstance(proof[0], AuditNode):
      paths = proof
  elif isinstance(proof, AuditProof):
    paths = proof._nodes

  if paths is None:
    raise TypeError(
      'Proof must be either <AuditProof>, '
      'a collection of <AuditNode> objects.'
      )
  # keep it dry
  concat = lambda x,y: _concat(hasher, x, y)
  def _calculate_root(target):
    _proof = [target] + paths
    return functools.reduce(concat, _proof)

  new_root = _calculate_root(target)
  root_hash = utils.from_hex(root_hash)
  # try again if the user forgot to hash the target
  if new_root != root_hash:
    try:
      new_root = _calculate_root(hasher.hash_leaf(target))
    except:
      pass
  return new_root == root_hash
Example #7
0
 def _wrapper(*args, **kwargs):
   hash_value = func(*args, **kwargs)
   return utils.from_hex(hash_value)