def test_merge_with_conflict_simple(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conflicting_block_hash1 = ChainGenerator.insert_dummy( dag, [block_hash1], 2) conflicting_block_hash2 = ChainGenerator.insert_dummy( dag, [block_hash1], 2) block_hash3 = ChainGenerator.insert_dummy(dag, [conflicting_block_hash2], 3) # DagVisualizer.visualize(dag, True) # uncomment for discover in visualization folder conflicts = [conflicting_block_hash1] merger = Merger(dag) res = merger.merge(dag.get_top_hashes(), conflicts) res = res.filter_out_skipped_blocks() # self.assertEqual(res[0].get_hash(), genesis_hash) #no genesis hash since common ancestor is first block self.assertEqual(res[0].get_hash(), block_hash1) self.assertEqual(res[1].get_hash(), conflicting_block_hash2) self.assertEqual(res[2].get_hash(), block_hash3)
def test_both_types_of_conflicts(self): dag = Dag(0) watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) actor3 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) watcher.on_new_block_by_validator(block1_hash, 1, actor1) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2_hash, 1, actor2) block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2c_hash, 1, actor2) block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash], 3) watcher.on_new_block_by_validator(block3_hash, 1, actor3) #this is possible if we have two epoch seeds block4_hash = ChainGenerator.insert_dummy(dag, [block2c_hash], 4) watcher.on_new_block_by_validator(block4_hash, 1, actor1) tops = dag.get_top_hashes() explicits, candidate_groups = watcher.find_conflicts_in_between(tops) self.assertEqual(len(explicits), 1) self.assertIn(block4_hash, explicits) self.assertEqual(len(candidate_groups), 1) self.assertEqual(len(candidate_groups[0]), 2) self.assertIn(block2_hash, candidate_groups[0]) self.assertIn(block2c_hash, candidate_groups[0])
def test_different_timeslot_watch(self): dag = Dag(0) conflict_watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) actor3 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) conflict_watcher.on_new_block_by_validator(block1_hash, 1, actor1) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2_hash, 1, actor2) # second block is signed by third validator # its not possible by usual means, but quite possible when we have two different epoch seeds block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2c_hash, 1, actor3) block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash, block2c_hash], 3) conflict_watcher.on_new_block_by_validator(block3_hash, 1, actor3) conflicts = conflict_watcher.get_conflicts_by_block(block3_hash) self.assertEqual(len(conflicts), 2) self.assertIn(block2c_hash, conflicts) self.assertIn(block3_hash, conflicts)
def test_same_timeslot_watch(self): dag = Dag(0) conflict_watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) actor3 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) conflict_watcher.on_new_block_by_validator(block1_hash, 1, actor1) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2_hash, 1, actor2) block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2c_hash, 1, actor2) block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash, block2c_hash], 3) conflict_watcher.on_new_block_by_validator(block3_hash, 1, actor3) conflicts = conflict_watcher.get_conflicts_by_block(block2_hash) self.assertEqual(len(conflicts), 2) self.assertIn(block2_hash, conflicts) self.assertIn(block2c_hash, conflicts) conflicts = conflict_watcher.get_conflicts_by_block(block1_hash) self.assertEqual(conflicts, None)
def test_find_conflicts(self): dag = Dag(0) watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) watcher.on_new_block_by_validator(block1_hash, 1, actor1) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2_hash, 1, actor2) block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2c_hash, 1, actor2) # block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash, block2c_hash], 3) # watcher.on_new_block_by_validator(block3_hash, 1, actor3) tops = dag.get_top_hashes() explicits, candidate_groups = watcher.find_conflicts_in_between(tops) self.assertEqual(len(explicits), 0) self.assertEqual(len(candidate_groups), 1) self.assertEqual(len(candidate_groups[0]), 2) self.assertIn(block2_hash, candidate_groups[0]) self.assertIn(block2c_hash, candidate_groups[0])
def test_explicit_conflict(self): dag = Dag(0) watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) watcher.on_new_block_by_validator(block1_hash, 1, actor2) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2_hash, 1, actor1) block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash], 3) watcher.on_new_block_by_validator(block3_hash, 1, actor2) block3c_hash = ChainGenerator.insert_dummy(dag, [block2_hash], 3) watcher.on_new_block_by_validator(block3c_hash, 1, actor2) tops = dag.get_top_hashes() #here block was signed by node even before merge appeared #this is explicit merge and both following blocks are conflicting explicits, candidates = watcher.find_conflicts_in_between(tops) self.assertEqual(len(explicits), 2) self.assertEqual(len(candidates), 0) self.assertIn(block3_hash, explicits) self.assertIn(block3c_hash, explicits)
def test_sane_prev_hashes_found(self): dag = Dag(0) epoch = Epoch(dag) genesis_hash = dag.genesis_block().get_hash() block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) block_hash2 = ChainGenerator.insert_dummy(dag, [block_hash1], 2) _block_hash3 = ChainGenerator.insert_dummy(dag, [genesis_hash, block_hash2], 3) verifier = BlockAcceptor(epoch, None) with self.assertRaises(AcceptionException): verifier.validate_non_ancestor_prev_hashes( [genesis_hash, block_hash2])
def test_skip_confirmation_requirement(self): dag = Dag(0) conf_req = ConfirmationRequirement(dag) genesis_hash = dag.genesis_block().get_hash() block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) # confirmation requirement decreases because we have large skip confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 5) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 3) skip = SkippedBlock(block_hash, 1) confirmation_requirement = conf_req.get_confirmation_requirement(skip) self.assertEqual(confirmation_requirement, 5) # do a larger gap block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 11) confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 3) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 1)) self.assertEqual(confirmation_requirement, 3) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 2)) self.assertEqual(confirmation_requirement, 4) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 4)) self.assertEqual(confirmation_requirement, 4) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 5)) self.assertEqual(confirmation_requirement, 5) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 6)) self.assertEqual(confirmation_requirement, 5) confirmation_requirement = conf_req.get_confirmation_requirement( SkippedBlock(block_hash, 10)) self.assertEqual(confirmation_requirement, ZETA_MAX)
def test_merge_with_conflict_simplest(self): dag = Dag(0) genesis_hash = dag.genesis_block().get_hash() block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) block_hash2 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) # DagVisualizer.visualize(dag, True) # uncomment for discover in visualization folder conflicts = [block_hash1] merger = Merger(dag) res = merger.merge(dag.get_top_hashes(), conflicts) res = res.filter_out_skipped_blocks() self.assertEqual(res[0].get_hash(), genesis_hash) self.assertEqual(res[1].get_hash(), block_hash2)
def test_sane_prev_hashes_not_found(self): dag = Dag(0) epoch = Epoch(dag) genesis_hash = dag.genesis_block().get_hash() block_hash1 = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) block_hash2 = ChainGenerator.insert_dummy(dag, [block_hash1], 2) block_hash3 = ChainGenerator.insert_dummy(dag, [genesis_hash], 3) _block_hash4 = ChainGenerator.insert_dummy(dag, [block_hash2, block_hash3], 4) verifier = BlockAcceptor(epoch, None) try: verifier.validate_non_ancestor_prev_hashes( [block_hash2, block_hash3]) except: self.fail("Prev hashes should not be self referential")
def test_recursive_sequence_skips(self): dag = Dag(0) conf_req = ConfirmationRequirement(dag) genesis_hash = dag.genesis_block().get_hash() block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 5 block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 5) # confirmation requirement decreases because we have large skip confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 4) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 9) # DagVisualizer.visualize(dag, True) # take a look to understand what's going on confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 3)
def test_explicit_conflict_right_on_common_ancestor(self): dag = Dag(0) watcher = ConflictWatcher(dag) actor2 = Private.publickey(Private.generate()) #consider common ancestor already resolved block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) watcher.on_new_block_by_validator(block1_hash, 1, actor2) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2_hash, 1, actor2) block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) watcher.on_new_block_by_validator(block2c_hash, 1, actor2) tops = dag.get_top_hashes() explicits, candidates = watcher.find_conflicts_in_between(tops) self.assertEqual(len(explicits), 2) self.assertEqual(len(candidates), 0) self.assertIn(block2_hash, explicits) self.assertIn(block2c_hash, explicits)
def test_recursive_sequence_found(self): dag = Dag(0) conf_req = ConfirmationRequirement(dag) genesis_hash = dag.genesis_block().get_hash() block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 2 block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 3 block_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 2) conf_req.blocks[block_hash] = 3 last_block_in_seq_hash = ChainGenerator.insert_dummy( dag, [genesis_hash], 1) conf_req.blocks[last_block_in_seq_hash] = 3 block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 3 block_hash = ChainGenerator.insert_dummy( dag, [last_block_in_seq_hash, block_hash], 2) conf_req.blocks[block_hash] = 4 block_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 3) conf_req.blocks[block_hash] = 4 block_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 4) # DagVisualizer.visualize(dag, True) # take a look to understand what's going on # there will be only two blocks with consecutive 4 behind, which is not enough to increase best_requirement = conf_req.choose_next_best_requirement(block_hash) self.assertEqual(best_requirement, 4) # make three blocks with consecutive 4 behind # it should lead to following block increasing requirement value conf_req.blocks[last_block_in_seq_hash] = 4 best_requirement = conf_req.choose_next_best_requirement(block_hash) self.assertEqual(best_requirement, 5)
def test_merged_dag(self): dag = Dag(0) # generate test case # time_slot [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] # ---------------------------------------- # 1 ------- [0, 1, 2, 3, 4] # 2 ------- [-, -, 2, s, s] # 3 ------- [-, -, 2, s, s] # 4 ------- [-, -, 2, 3, s] # s - same block (merged) conf_req = ConfirmationRequirement(dag) genesis_hash = dag.genesis_block().get_hash() block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 2 block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 3 block_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 2) conf_req.blocks[block_hash] = 3 last_block_in_seq_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[last_block_in_seq_hash] = 3 block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 3 block_hash = ChainGenerator.insert_dummy(dag, [last_block_in_seq_hash, block_hash], 2) conf_req.blocks[block_hash] = 4 block_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 3) conf_req.blocks[block_hash] = 4 top_hash = ChainGenerator.insert_dummy(dag, dag.get_top_hashes(), 4) # 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) # strong determined top # CHAIN ALREADY MERGED self.assertEqual(len(conflicts), 0)
def test_skips_and_restoring(self): dag = Dag(0) conf_req = ConfirmationRequirement(dag) genesis_hash = dag.genesis_block().get_hash() block_hash = ChainGenerator.insert_dummy(dag, [genesis_hash], 1) conf_req.blocks[block_hash] = 5 block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 5) # confirmation requirement decreases because we have large skip confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 4) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 6) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 7) #we still have 4 here confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 4) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 8) #but we have restored to 5 here, because of 3 previous consecutive blocks confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 5) #let's skip one block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 10) block_hash = ChainGenerator.insert_dummy(dag, [block_hash], 11) # DagVisualizer.visualize(dag, True) # take a look to understand what's going on #not affected by small skip confirmation_requirement = conf_req.get_confirmation_requirement( block_hash) self.assertEqual(confirmation_requirement, 5)
def test_different_epoch_watch(self): dag = Dag(0) conflict_watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) actor3 = Private.publickey(Private.generate()) block1_hash = ChainGenerator.insert_dummy(dag, [dag.genesis_hash()], 1) conflict_watcher.on_new_block_by_validator(block1_hash, 1, actor1) block2_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2_hash, 1, actor2) block2c_hash = ChainGenerator.insert_dummy(dag, [block1_hash], 2) conflict_watcher.on_new_block_by_validator(block2c_hash, 1, actor2) block3_hash = ChainGenerator.insert_dummy(dag, [block2_hash, block2c_hash], 3) conflict_watcher.on_new_block_by_validator(block3_hash, 1, actor3) #switch to next epoch block4_hash = ChainGenerator.insert_dummy(dag, [block3_hash], 4) conflict_watcher.on_new_block_by_validator(block4_hash, 2, actor2) block4c_hash = ChainGenerator.insert_dummy(dag, [block3_hash], 4) conflict_watcher.on_new_block_by_validator(block4c_hash, 2, actor2) #first epoch conflicts conflicts = conflict_watcher.get_conflicts_by_block(block2_hash) self.assertEqual(len(conflicts), 2) self.assertIn(block2_hash, conflicts) self.assertIn(block2c_hash, conflicts) #second epoch conflicts of the same public key conflicts = conflict_watcher.get_conflicts_by_block(block4_hash) self.assertEqual(len(conflicts), 2) self.assertIn(block4_hash, conflicts) self.assertIn(block4c_hash, conflicts) conflicts = conflict_watcher.get_conflicts_by_block(block1_hash) self.assertEqual(conflicts, None)