def test_hash_full_non_empty(self): hasher = ledger.tree_hasher.TreeHasher() leaves = [c.encode() for c in "abcde"] for i in range(1, len(leaves) + 1): root_hash, hashes = hasher._hash_full(leaves, 0, i) self.assertEqual(hasher._hash_fold(hashes), root_hash) self.assertEqual(len(hashes), count_bits_set(i)) if count_bits_set(i) == 1: # 2^k self.assertEqual(hashes, (root_hash, ))
def test_hash_full_non_empty(self): hasher = ledger.tree_hasher.TreeHasher() leaves = [c.encode() for c in "abcde"] for i in range(1, len(leaves) + 1): root_hash, hashes = hasher._hash_full(leaves, 0, i) self.assertEqual(hasher._hash_fold(hashes), root_hash) self.assertEqual(len(hashes), count_bits_set(i)) if count_bits_set(i) == 1: # 2^k self.assertEqual(hashes, (root_hash,))
def verifyConsistency(self, expectedLeafCount = -1) -> bool: if expectedLeafCount > 0 and expectedLeafCount != self.leafCount: raise ConsistencyVerificationFailed() expectedNodeCount = count_bits_set(self.leafCount) if not expectedNodeCount == self.nodeCount: raise ConsistencyVerificationFailed() return True
def hash_full_tree(self, leaves): """Hash a set of leaves representing a valid full tree.""" root_hash, hashes = self._hash_full(leaves, 0, len(leaves)) assert len(hashes) == count_bits_set(len(leaves)) assert (self._hash_fold(hashes) == root_hash if hashes else root_hash == self.hash_empty()) return root_hash
def verifyConsistency(self, expectedLeafCount=-1) -> bool: if expectedLeafCount > 0 and expectedLeafCount != self.leafCount: raise ConsistencyVerificationFailed() expectedNodeCount = count_bits_set(self.leafCount) if not expectedNodeCount == self.nodeCount: raise ConsistencyVerificationFailed() return True
def _update(self, tree_size: int, hashes: Sequence[bytes]): bits_set = count_bits_set(tree_size) num_hashes = len(hashes) if num_hashes != bits_set: msgfmt = "number of hashes != bits set in tree_size: %s vs %s" raise ValueError(msgfmt % (num_hashes, bits_set)) self.__tree_size = tree_size self.__hashes = tuple(hashes) # height of the smallest subtree, or 0 if none exists (empty tree) self.__mintree_height = lowest_bit_set(tree_size) self.__root_hash = None
def getNodePosition(cls, start, height=None) -> int: """ Calculates node position based on start and height :param start: The sequence number of the first leaf under this tree. :param height: Height of this node in the merkle tree :return: the node's position """ pwr = highest_bit_set(start) - 1 height = height or pwr if count_bits_set(start) == 1: adj = height - pwr return start - 1 + adj else: c = pow(2, pwr) return cls.getNodePosition(c, pwr) + \ cls.getNodePosition(start - c, height)
def _push_subtree(self, leaves: List[bytes]): """Extend with a full subtree <= the current minimum subtree. The leaves must form a full subtree, i.e. of size 2^k for some k. If there is a minimum subtree (i.e. __mintree_height > 0), then the input subtree must be smaller or of equal size to the minimum subtree. If the subtree is smaller (or no such minimum exists, in an empty tree), we can simply append its hash to self.hashes, since this maintains the invariant property of being sorted in descending size order. If the subtree is of equal size, we are in a similar situation to an addition carry. We handle it by combining the two subtrees into a larger subtree (of size 2^(k+1)), then recursively trying to add this new subtree back into the tree. Any collection of leaves larger than the minimum subtree must undergo additional partition to conform with the structure of a merkle tree, which is a more complex operation, performed by extend(). """ size = len(leaves) if count_bits_set(size) != 1: raise ValueError("invalid subtree with size != 2^k: %s" % size) # in general we want the highest bit, but here it's also the lowest bit # so just reuse that code instead of writing a new highest_bit_set() subtree_h, mintree_h = lowest_bit_set(size), self.__mintree_height if mintree_h > 0 and subtree_h > mintree_h: raise ValueError("subtree %s > current smallest subtree %s" % (subtree_h, mintree_h)) root_hash, hashes = self.__hasher._hash_full(leaves, 0, size) assert hashes == (root_hash, ) if self.hashStore: for h in hashes: self.hashStore.writeLeaf(h) new_node_hashes = self.__push_subtree_hash(subtree_h, root_hash) nodes = [(self.tree_size, height, h) for h, height in new_node_hashes] if self.hashStore: for node in nodes: self.hashStore.writeNode(node)
def _push_subtree(self, leaves: List[bytes]): """Extend with a full subtree <= the current minimum subtree. The leaves must form a full subtree, i.e. of size 2^k for some k. If there is a minimum subtree (i.e. __mintree_height > 0), then the input subtree must be smaller or of equal size to the minimum subtree. If the subtree is smaller (or no such minimum exists, in an empty tree), we can simply append its hash to self.hashes, since this maintains the invariant property of being sorted in descending size order. If the subtree is of equal size, we are in a similar situation to an addition carry. We handle it by combining the two subtrees into a larger subtree (of size 2^(k+1)), then recursively trying to add this new subtree back into the tree. Any collection of leaves larger than the minimum subtree must undergo additional partition to conform with the structure of a merkle tree, which is a more complex operation, performed by extend(). """ size = len(leaves) if count_bits_set(size) != 1: raise ValueError("invalid subtree with size != 2^k: %s" % size) # in general we want the highest bit, but here it's also the lowest bit # so just reuse that code instead of writing a new highest_bit_set() subtree_h, mintree_h = lowest_bit_set(size), self.__mintree_height if mintree_h > 0 and subtree_h > mintree_h: raise ValueError("subtree %s > current smallest subtree %s" % ( subtree_h, mintree_h)) root_hash, hashes = self.__hasher._hash_full(leaves, 0, size) assert hashes == (root_hash,) if self.hashStore: for h in hashes: self.hashStore.writeLeaf(h) new_node_hashes = self.__push_subtree_hash(subtree_h, root_hash) nodes = [(self.tree_size, height, h) for h, height in new_node_hashes] if self.hashStore: for node in nodes: self.hashStore.writeNode(node)