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_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_genesis_is_first_era_hash(self): dag = Dag(0) first_era_hash = dag.get_era_hash(1) genesis_hash = dag.genesis_block().get_hash().digest() self.assertEqual(first_era_hash, genesis_hash)
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_merged_chain_without_transaction_conflicts(self): dag = Dag(0) block_hash, block_reward = ChainGenerator.insert_dummy_with_payments( dag, [dag.genesis_hash()], [], 1) payment1 = TransactionFactory.create_payment(block_reward, 0, [os.urandom(32)], [15]) block2_hash, block2_reward = ChainGenerator.insert_dummy_with_payments( dag, [block_hash], [payment1], 2) payment2 = TransactionFactory.create_payment(block2_reward, 0, [os.urandom(32)], [15]) block3_hash, block3_reward = ChainGenerator.insert_dummy_with_payments( dag, [block_hash], [payment2], 3) block4_hash, block4_reward = ChainGenerator.insert_dummy_with_payments( dag, [block2_hash, block3_hash], [], 4) iterator = MergingIter(dag, block4_hash) payments = [ block.block.payment_txs for block in iterator if block != None ] payments = list(reversed(payments)) spent, unspent = Resolver.resolve(payments) self.assertEqual(len(spent), 0) self.assertEqual(len(unspent), 4) self.assertIn(Entry(block3_reward, 0), unspent) self.assertIn(Entry(block4_reward, 0), unspent) self.assertIn(Entry(payment1.get_hash(), 0), unspent) self.assertIn(Entry(payment2.get_hash(), 0), unspent)
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_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_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_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 __init__(self, genesis_creation_time, node_id, network, block_signer): self.dag = Dag(genesis_creation_time) self.permissions = Permissions() self.mempool = Mempool() self.block_signer = block_signer self.network = network self.node_id = node_id
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_find_epoch_hash_for_block(self): dag = Dag(0) epoch = Epoch(dag) genesis_hash = dag.genesis_block().get_hash() genesis_epoch_hash = epoch.find_epoch_hash_for_block(genesis_hash) self.assertEqual(genesis_hash, genesis_epoch_hash) block = BlockFactory.create_block_with_timestamp([genesis_hash], BLOCK_TIME) signed_block = BlockFactory.sign_block(block, Private.generate()) dag.add_signed_block(1, signed_block) first_block_hash = block.get_hash() first_epoch_hash = epoch.find_epoch_hash_for_block(first_block_hash) self.assertEqual(genesis_hash, first_epoch_hash)
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_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 __init__(self, genesis_creation_time, logger): self.logger = logger self.dag = Dag(genesis_creation_time) self.epoch = Epoch(self.dag) self.epoch.set_logger(self.logger) self.logger.info("Starting announcer node") self.last_announced_round = None self.terminated = False
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_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_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)
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_penalty(self): dag = Dag(0) epoch = Epoch(dag) permissions = Permissions(epoch) node_private = Private.generate() initial_validators = Validators.read_genesis_validators_from_file() genesis_hash = dag.genesis_block().get_hash() last_block_number = Epoch.get_epoch_end_block_number(1) prev_hash = ChainGenerator.fill_with_dummies( dag, genesis_hash, range(1, last_block_number)) block = BlockFactory.create_block_with_timestamp( [prev_hash], BLOCK_TIME * last_block_number) tx = PenaltyTransaction() tx.conflicts = [prev_hash] tx.signature = Private.sign(tx.get_hash(), node_private) block.system_txs = [tx] signed_block = BlockFactory.sign_block(block, node_private) dag.add_signed_block(last_block_number, signed_block) initial_validators_order = permissions.get_signers_indexes( genesis_hash) # we substract two here: one because it is last but one block # and one, because epoch starts from 1 validator_index_to_penalize = initial_validators_order[ last_block_number - 2] resulting_validators = permissions.get_validators(block.get_hash()) self.assertNotEqual(len(initial_validators), len(resulting_validators)) initial_validators.pop(validator_index_to_penalize) init_pubkeys = list( map(lambda validator: validator.public_key, initial_validators)) result_pubkeys = list( map(lambda validator: validator.public_key, resulting_validators)) self.assertEqual(init_pubkeys, result_pubkeys)
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_merged_chain_with_transaction_conflicts(self): dag = Dag(0) block_hash, block_reward = ChainGenerator.insert_dummy_with_payments( dag, [dag.genesis_hash()], [], 1) #second block spends first block reward payment1 = TransactionFactory.create_payment(block_reward, 0, [os.urandom(32)], [15]) block2_hash, block2_reward = ChainGenerator.insert_dummy_with_payments( dag, [block_hash], [payment1], 2) #third block also spends first block reward payment2 = TransactionFactory.create_payment(block_reward, 0, [os.urandom(32)], [15]) block3_hash, block3_reward = ChainGenerator.insert_dummy_with_payments( dag, [block_hash], [payment2], 3) #making block2_hash go first, so its transactions will have a priority block4_hash, block4_reward = ChainGenerator.insert_dummy_with_payments( dag, [block2_hash, block3_hash], [], 4) iterator = MergingIter(dag, block4_hash) payments = [ block.block.payment_txs for block in iterator if block != None ] payments = list(reversed(payments)) spent, unspent = Resolver.resolve(payments) self.assertEqual(len(spent), 0) self.assertEqual(len(unspent), 4) self.assertIn(Entry(block2_reward, 0), unspent) self.assertIn(Entry(block3_reward, 0), unspent) self.assertIn(Entry(block4_reward, 0), unspent) self.assertIn(Entry(payment1.get_hash(), 0), unspent) self.assertNotIn( Entry(payment2.get_hash(), 0), unspent ) #payment 2 is consedered conflicting as it goes later in merged chain
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_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_simple(self): dag = Dag(0) block_hash, block_reward = ChainGenerator.insert_dummy_with_payments( dag, [dag.genesis_hash()], [], 1) payment = TransactionFactory.create_payment(block_reward, 0, [os.urandom(32)], [12]) block2_hash, block2_reward = ChainGenerator.insert_dummy_with_payments( dag, [block_hash], [payment], 2) iterator = MergingIter(dag, block2_hash) payments = [block.block.payment_txs for block in iterator] payments = list(reversed(payments)) spent, unspent = Resolver.resolve(payments) self.assertEqual(len(spent), 0) self.assertIn(Entry(block2_reward, 0), unspent) self.assertIn(Entry(payment.get_hash(), 0), unspent)
def test_storing_tx_by_hash(self): dag = Dag(0) private0 = Private.generate() private1 = Private.generate() private2 = Private.generate() # add block 1 block1 = BlockFactory.create_block_with_timestamp( [dag.genesis_block().get_hash()], BLOCK_TIME) signed_block1 = BlockFactory.sign_block(block1, private0) dag.add_signed_block(1, signed_block1) # check transactions in dag.transactions_by_hash for empty self.assertTrue(len(dag.transactions_by_hash) == 0) # add block 2 block2 = BlockFactory.create_block_with_timestamp([block1.get_hash()], BLOCK_TIME) # add penalty gossip case by tx in block tx1 = TransactionFactory.create_negative_gossip_transaction( 1, private1) tx2 = TransactionFactory.create_positive_gossip_transaction( block2.get_hash(), private1) block2.system_txs.append(tx1) block2.system_txs.append(tx2) # -------------------------------------- signed_block2 = BlockFactory.sign_block(block2, private1) dag.add_signed_block(2, signed_block2) # check transactions in dag.transactions_by_hash self.assertTrue( set(dag.transactions_by_hash).issuperset({tx1.get_hash(): tx1})) self.assertTrue( set(dag.transactions_by_hash).issuperset({tx2.get_hash(): tx2})) block3 = BlockFactory.create_block_with_timestamp([block2.get_hash()], BLOCK_TIME) signed_block3 = BlockFactory.sign_block(block3, private2) dag.add_signed_block(3, signed_block3) # check transactions in dag.transactions_by_hash self.assertTrue( set(dag.transactions_by_hash).issuperset({tx1.get_hash(): tx1})) self.assertTrue( set(dag.transactions_by_hash).issuperset({tx2.get_hash(): tx2}))
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_top_blocks(self): dag = Dag(0) private = Private.generate() block1 = BlockFactory.create_block_with_timestamp( [dag.genesis_block().get_hash()], BLOCK_TIME) signed_block1 = BlockFactory.sign_block(block1, private) dag.add_signed_block(1, signed_block1) block2 = BlockFactory.create_block_with_timestamp([block1.get_hash()], BLOCK_TIME * 2) signed_block2 = BlockFactory.sign_block(block2, private) dag.add_signed_block(2, signed_block2) block3 = BlockFactory.create_block_with_timestamp([block1.get_hash()], BLOCK_TIME * 3) signed_block3 = BlockFactory.sign_block(block3, private) dag.add_signed_block(3, signed_block3) top_hashes = dag.get_top_blocks_hashes() self.assertEqual(top_hashes[0], block2.get_hash()) self.assertEqual(top_hashes[1], block3.get_hash())