def __compute_hash_chain_step__(self, node: Node, origin_node: Node) -> Node: """ Recursive function called to compute the hash chain. The hash chain is computed bottom (z_i) to top (root). This function only compute a "step" or level in the hash chain. As implemented the Node class _need_ a hash value so the clone node have the same value as the "reference" node. :param node: The node at the level we are at (must have origin_node as left or right child node) :type node: Node :param origin_node: The node from which this function call originated (must have node for parent) :type origin_node: Node :return: None if called at root level or a clone of the node we started at """ # Current cloned node (the level we are at) current_node = None # This algorithm shall end when trying to be above the root if not node: return current_node node.set_mark_compute() # Apply the same algorithm for our parent parent_node = self.__compute_hash_chain_step__(node.parent, node) # If the call originate from the left child if node.left_child is origin_node: node.right_child.set_mark() else: node.left_child.set_mark() right_node = copy(node.right_child) # type: Node left_node = copy(node.left_child) # type: Node current_node = copy(node) # type: Node current_node.left_child = left_node current_node.right_child = right_node current_node.set_mark_compute() if parent_node: # Link the parent's left child node to the current node if parent_node.left_child.hash == current_node.hash: parent_node.left_child = current_node # Link the parent's right child node to the current node elif parent_node.right_child.hash == current_node.hash: parent_node.right_child = current_node # Link current node to it's parent current_node.parent = parent_node return current_node
def __compute_hash_chain__(self, z_i: Node, pair_i: bool) -> Node: """ Clone the nodes used in the hash chain (z_i to root of the Merkle tree). :param z_i: The node at which to start the hash chain (bottom to top) :type z_i: Node :param pair_i: True if the index of z_i is pair (i.e. this is a special case, see the class documentation) :type pair_i: bool :return: The root of the hash chain :rtype: Node """ assert isinstance(z_i, Node) assert isinstance(z_i.parent, Node) # i is pair so z_i is a special case if pair_i: z_i.set_mark_z_i() # Go up one level z_i = z_i.parent assert isinstance(z_i.parent, Node) # Call the recursive function to clone nodes on the hash chain hash_chain_node = self.__compute_hash_chain_step__(z_i.parent, z_i) z_i.set_mark_z_i() # Set the z_i mark on the hash chain (left child) if hash_chain_node.left_child and hash_chain_node.left_child.uuid == z_i.uuid: if not pair_i: hash_chain_node.left_child.set_mark_z_i() # Set the exception mark if hash_chain_node.right_child: hash_chain_node.right_child.set_mark_exception() z_i.parent.right_child.set_mark_exception() if pair_i: z_i.set_mark_exception() # Go down one level z_i = z_i.right_child # This node was left-out with the call to __compute_hash_chain_step__() so we need to add it ourselves tmp = copy(z_i) # Link tmp to its parent tmp.parent = hash_chain_node.right_child # Link the parent to tmp hash_chain_node.right_child.right_child = tmp hash_chain_node.right_child.set_mark_exception() # Set the z_i mark on the hash chain (right child) if hash_chain_node.right_child and hash_chain_node.right_child.uuid == z_i.uuid: hash_chain_node.right_child.set_mark_z_i() # We want the whole tree so we need to get to the root of it while hash_chain_node.parent: hash_chain_node = hash_chain_node.parent return hash_chain_node
def test_coverage(self): # For total coverage with self.assertRaises(AttributeError): Node()