docs: def validate_proof(proof, target_hash, merkle_root, hash_f=sha256):
      """
      Takes a proof array, a target hash value, and a merkle root.
      Checks the validity of the proof and return true or false.
      :param proof:
      :param target_hash:
      :param merkle_root:
      :param hash_f:
      :return: whether proof is valid, Boolean
      """
  
      if not proof:
          # no siblings, single item tree, so the hash should also be the root
          return target_hash == merkle_root
  
      target_hash = get_buffer(target_hash)
      merkle_root = get_buffer(merkle_root)
  
      proof_hash = target_hash
      for x in proof:
          if 'left' in x:
              # then the sibling is a left node
              proof_hash = get_buffer(hash_f(get_buffer(x['left']) + proof_hash))
          elif 'right' in x:
              # then the sibling is a right node
              proof_hash = get_buffer(hash_f(proof_hash + get_buffer(x['right'])))
          else:
              # no left or right designation exists, proof is invalid
              return False
  
      return hexlify(proof_hash) == hexlify(merkle_root)