def test_conflicts_with_skips(self): dag = Dag(0) # generate test case # time_slot [0, 1, 2, 3, 4, 5] # ------------------------------- # 1 ------- [0, 1, 2, 3, , 5, 6, 7, 8] # 2 ------- [ , , , 3, 4, , 6, 7, 8] # 3 ------- [ , , , , 4, 5, 6, , 8] # block number 3 MUST BE signed by same key private1 = Private.generate() private2 = Private.generate() private3 = Private.generate() top_hash_1 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.genesis_block().get_hash(), range=range(1, 9), indices_to_skip=[4], dummy_private=private1) top_hash_2 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[2][0].get_hash(), range=range(3, 9), indices_to_skip=[5], dummy_private=private2) top_hash_3 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[3][1].get_hash(), range=range(4, 9), indices_to_skip=[7], dummy_private=private3) # DagVisualizer.visualize(dag) conflict_finder = ConflictFinder(dag) top_blocks = list(dag.get_top_blocks().keys()) top, conflicts = conflict_finder.find_conflicts(top_blocks) # assert determined top (it can be one of top_hash1,2,3) tops = [top_hash_1, top_hash_2, top_hash_3] self.assertIn(top, tops) if top == top_hash_1: # test conflicts # conflicts include all [3],[4,4],[5],[6,6],[7],[8,8] # EXCLUDE flatten top chain from list of conflict block hashes self.assertEqual(len(conflicts), 9) if top == top_hash_2: # test conflicts # conflicts include all [3],[4],[5,5],[6,6],[7],[8,8] # EXCLUDE flatten top chain from list of conflict block hashes self.assertEqual(len(conflicts), 9) if top == top_hash_3: # test conflicts # conflicts include all [3],[4],[5],[6,6],[7,7],[8,8] # EXCLUDE flatten top chain from list of conflict block hashes self.assertEqual(len(conflicts), 9)
def test_complicated_dag_with_skips(self): dag = Dag(0) # generate test case # time_slot [0, 1, 2, 3, 4, 5] # ------------------------------- # 1 ------- [-, -, 2, 3, 4, 5, , , 8] # 2 ------- [0, 1, 2, , , 5, 6, 7, 8] # 3 ------- [-, -, -, 3, 4, , 6, 7, 8] # 4 ------- [-, -, -, -, 4, 5, 6, , 8] # block number 3 MUST BE signed by same key private1 = Private.generate() private2 = Private.generate() private3 = Private.generate() private4 = Private.generate() top_hash_2 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.genesis_block().get_hash(), range=range(1, 9), indices_to_skip=[3, 4], dummy_private=private2) top_hash_1 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[1][0].get_hash(), range=range(2, 9), indices_to_skip=[6, 7], dummy_private=private1) top_hash_3 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[2][0].get_hash(), range=range(3, 9), indices_to_skip=[5], dummy_private=private3) top_hash_4 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[3][1].get_hash(), range=range(4, 9), indices_to_skip=[7], dummy_private=private4) # DagVisualizer.visualize(dag) conflict_finder = ConflictFinder(dag) top_blocks = list(dag.get_top_blocks().keys()) top, conflicts = conflict_finder.find_conflicts(top_blocks) # assert determined top (it can be one of longest top_hash_3,4) tops = [top_hash_3, top_hash_4] self.assertIn(top, tops) # test conflicts self.assertEqual(len(conflicts), 13)
def test_merge(self): dag = Dag(0) prev_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, prev_hash, range(1, 4), [1, 2]) ChainGenerator.fill_with_dummies_and_skips(dag, prev_hash, range(1, 4), [3]) merger = Merger(dag) res = merger.merge(dag.get_top_hashes()) res = res.filter_out_skipped_blocks() self.assertEqual(res[0], dag.blocks_by_number[0][0]) self.assertEqual(res[1], dag.blocks_by_number[1][0]) self.assertEqual(res[2], dag.blocks_by_number[2][0]) self.assertEqual(res[3], dag.blocks_by_number[3][0])
def test_find_conflicts_longest_chain(self): dag = Dag(0) # generate test case # time_slot [0, 1, 2, 3, 4, 5, 6] # ------------------------------- # 1 ------- [0, 1, 2, 3, 4, 5, 6] # 2 ------- [ , , , 3, 4, 5, ] # 3 ------- [ , , , , 4, 5, ] # block number 3 MUST BE signed by same key private1 = Private.generate() private2 = Private.generate() private3 = Private.generate() determinated_top_hash = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.genesis_block().get_hash(), range=range(1, 7), indices_to_skip=[], dummy_private=private1) ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[2][0].get_hash(), range=range(3, 6), indices_to_skip=[], dummy_private=private2) ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[3][1].get_hash(), range=range(4, 6), indices_to_skip=[], dummy_private=private3) # DagVisualizer.visualize(dag) conflict_finder = ConflictFinder(dag) top_blocks = list(dag.get_top_blocks().keys()) top, conflicts = conflict_finder.find_conflicts(top_blocks) # assert determined top self.assertEqual(determinated_top_hash, top) # test conflicts [3],[4,4],[5,5] EXCLUDE flatten top chain from list of conflict block hashes self.assertEqual(len(conflicts), 5)
def test_complex_merge(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, genesis_hash, range(1, 10), [2, 5, 7, 8]) first_block = dag.blocks_by_number[1][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, first_block, range(2, 10), [3, 4, 6, 7, 8, 9]) second_block = dag.blocks_by_number[2][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, second_block, range(3, 10), [3, 4, 5, 6, 9]) merger = Merger(dag) res = merger.merge(dag.get_top_hashes()) res = res.filter_out_skipped_blocks() # res.debug_print_block_numbers(dag) self.assertEqual(res[0], dag.blocks_by_number[1][0]) self.assertEqual(res[1], dag.blocks_by_number[3][0]) self.assertEqual(res[2], dag.blocks_by_number[4][0]) self.assertEqual(res[3], dag.blocks_by_number[6][0]) self.assertEqual(res[4], dag.blocks_by_number[9][0]) self.assertEqual( res[5], dag.blocks_by_number[2][0]) # TODO find out why is this 2 here self.assertEqual(res[6], dag.blocks_by_number[7][0]) self.assertEqual(res[7], dag.blocks_by_number[8][0]) self.assertEqual(res[8], dag.blocks_by_number[5][0])
def test_top_blocks_in_range(self): dag = Dag(0) prev_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, prev_hash, range(1, 8), [3, 5]) ChainGenerator.fill_with_dummies_and_skips(dag, prev_hash, range(1, 8), [4]) ChainGenerator.fill_with_dummies_and_skips(dag, prev_hash, range(1, 7), [4, 5]) tops = dag.get_branches_for_timeslot_range(3, 6) self.assertEqual(len(tops), 3) self.assertIn(dag.blocks_by_number[4][0].get_hash(), tops) self.assertIn(dag.blocks_by_number[5][0].get_hash(), tops) self.assertIn(dag.blocks_by_number[3][1].get_hash(), tops) tops = dag.get_branches_for_timeslot_range(4, 5) self.assertEqual(len(tops), 1) self.assertIn(dag.blocks_by_number[4][0].get_hash(), tops) tops = dag.get_branches_for_timeslot_range(3, 5) self.assertEqual(len(tops), 3) self.assertIn(dag.blocks_by_number[4][0].get_hash(), tops) self.assertIn(dag.blocks_by_number[3][0].get_hash(), tops) self.assertIn(dag.blocks_by_number[3][1].get_hash(), tops)
def test_two_tops_on_next_epoch_middle(self): # generate two blocks on epoch end # 1 --- [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,29] # 2 --- [ , , ,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,27,29] epoch_range = range(1, 30) epoch_range_2 = range(3, 30) dag = Dag(0) ChainGenerator.fill_with_dummies_and_skips( dag=dag, prev_hash=dag.genesis_block().get_hash(), range=epoch_range, indices_to_skip=[]) ChainGenerator.fill_with_dummies_and_skips( dag=dag, prev_hash=dag.blocks_by_number[2][0].get_hash(), range=epoch_range_2, indices_to_skip=[]) # DagVisualizer.visualize(dag) # uncomment for discover in visualization folder tops = dag.get_top_blocks_hashes() found_intersection = dag.get_common_ancestor([tops[0], tops[1]]) expected_intersection = dag.blocks_by_number[2][0].get_hash() self.assertEqual(expected_intersection, found_intersection)
def test_merge_in_merge(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, genesis_hash, range(1, 5), [1, 3]) second_block = dag.blocks_by_number[2][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, second_block, range(3, 4), []) tops = dag.get_top_hashes() merging_block = BlockFactory.create_block_with_timestamp( tops, BLOCK_TIME * 5) merging_signed_block = BlockFactory.sign_block(merging_block, Private.generate()) dag.add_signed_block(5, merging_signed_block) ChainGenerator.fill_with_dummies_and_skips(dag, genesis_hash, range(1, 7), [2, 3, 4, 5]) tops = dag.get_top_hashes() merging_block = BlockFactory.create_block_with_timestamp( tops, BLOCK_TIME * 7) merging_signed_block = BlockFactory.sign_block(merging_block, Private.generate()) dag.add_signed_block(7, merging_signed_block) # DagVisualizer.visualize(dag, True) iterator = MergingIter(dag, merging_block.get_hash()) #shortest chain goes last # 3 and 4 are swapped because 4 has a priority #TODO But is it okay? Maybe we should sometimes give priority to earlier blocks if equal?1 self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[7][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[6][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[1][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[5][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[3][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[4][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[2][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[0][0].get_hash())
def test_simple_merge(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, genesis_hash, range(1, 10), [2, 5, 7, 8]) first_block = dag.blocks_by_number[1][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, first_block, range(2, 10), [3, 4, 6, 7, 8, 9]) second_block = dag.blocks_by_number[2][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, second_block, range(3, 10), [3, 4, 5, 6, 9]) hanging_tips = dag.get_top_hashes() merging_block = BlockFactory.create_block_with_timestamp( hanging_tips, BLOCK_TIME * 10) merging_signed_block = BlockFactory.sign_block(merging_block, Private.generate()) dag.add_signed_block(10, merging_signed_block) # DagVisualizer.visualize(dag, True) iterator = MergingIter(dag, merging_block.get_hash()) self.assertEqual(iterator.next().get_hash(), merging_block.get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[5][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[8][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[7][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[2] [0].get_hash()) #TODO find out why is this 2 here self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[9][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[6][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[4][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[3][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[1][0].get_hash()) self.assertEqual(iterator.next().get_hash(), dag.blocks_by_number[0][0].get_hash())
def test_multiple_chain_common_ancestor(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, genesis_hash, range(1, 10), [2, 5, 7, 8]) first_block = dag.blocks_by_number[1][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, first_block, range(2, 10), [3, 4, 6, 7, 8, 9]) second_block = dag.blocks_by_number[2][0].get_hash() ChainGenerator.fill_with_dummies_and_skips(dag, second_block, range(3, 10), [3, 4, 5, 6, 9]) expected_intersection = dag.blocks_by_number[1][0].get_hash() tops = dag.get_top_blocks_hashes() found_intersection = dag.get_common_ancestor( [tops[0], tops[1], tops[2]]) self.assertEqual(expected_intersection, found_intersection)
def test_complicated_dag_with_skips_and_determined_top(self): dag = Dag(0) # generate test case # time_slot [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # ---------------------------------------- # 1 ------- [ , , 2, 3, 4, , , , 8, ] # 2 ------- [0, 1, 2, , , 5, 6, 7, 8, 9] # 3 ------- [ , , , 3, 4, , 6, 7, 8, ] # 4 ------- [ , , , , 4, 5, 6, , 8, ] # 5 ------- [ , , 2, 3, , , , , , 9] # block number 3 MUST BE signed by same key private1 = Private.generate() private2 = Private.generate() private3 = Private.generate() private4 = Private.generate() private5 = Private.generate() top_hash_2 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.genesis_block().get_hash(), range=range(1, 11), indices_to_skip=[3, 4, 10], dummy_private=private2) top_hash_1 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[1][0].get_hash(), range=range(2, 11), indices_to_skip=[6, 7], dummy_private=private1) top_hash_3 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[2][0].get_hash(), range=range(3, 11), indices_to_skip=[5, 7, 10], dummy_private=private3) top_hash_4 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[3][1].get_hash(), range=range(4, 11), indices_to_skip=[5, 8, 10], dummy_private=private4) top_hash_5 = \ ChainGenerator.fill_with_dummies_and_skips(dag=dag, prev_hash=dag.blocks_by_number[3][1].get_hash(), range=range(4, 11), indices_to_skip=[4, 5, 6, 7, 8], dummy_private=private5) # DagVisualizer.visualize(dag) conflict_finder = ConflictFinder(dag) top_blocks = list(dag.get_top_blocks().keys()) top, conflicts = conflict_finder.find_conflicts(top_blocks) self.assertEqual(top, top_hash_1) # strong determined top (longest chain in current case) min. skipped blocks # conflicts include all [2],[3],[4,4],[5],[6,6,6],[7,7],[8,8],[9,9,9,9],[10] # EXCLUDE flatten top chain from list of conflict block hashes self.assertEqual(len(conflicts), 17)