def test_chain_length(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([block2.get_hash()], BLOCK_TIME * 3) signed_block3 = BlockFactory.sign_block(block3, private) dag.add_signed_block(3, signed_block3) # alternative chain other_block2 = BlockFactory.create_block_with_timestamp( [block1.get_hash()], BLOCK_TIME * 2 + 1) other_signed_block2 = BlockFactory.sign_block(other_block2, private) dag.add_signed_block(2, other_signed_block2) self.assertEqual( dag.calculate_chain_length(other_block2.get_hash(), dag.genesis_hash()), 3) self.assertEqual( dag.calculate_chain_length(block3.get_hash(), dag.genesis_hash()), 4)
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_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_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_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_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_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_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_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_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_merged_chain_with_block_conflict(self): dag = Dag(0) watcher = ConflictWatcher(dag) actor1 = Private.publickey(Private.generate()) actor2 = Private.publickey(Private.generate()) actor3 = Private.publickey(Private.generate()) block1_hash, block1_reward = ChainGenerator.insert_dummy_with_payments( dag, [dag.genesis_hash()], [], 1) watcher.on_new_block_by_validator(block1_hash, 1, actor1) payment1 = TransactionFactory.create_payment(block1_reward, 0, [os.urandom(32)], [15]) block2_hash, block2_reward = ChainGenerator.insert_dummy_with_payments( dag, [block1_hash], [payment1], 2) watcher.on_new_block_by_validator(block2_hash, 1, actor2) #another block by the same validator spending the same output payment1c = TransactionFactory.create_payment(block1_reward, 0, [os.urandom(32)], [15]) block2c_hash, block2c_reward = ChainGenerator.insert_dummy_with_payments( dag, [block1_hash], [payment1c], 2) watcher.on_new_block_by_validator(block2c_hash, 1, actor2) block3_hash, block3_reward = ChainGenerator.insert_dummy_with_payments( dag, [block2_hash, block2c_hash], [], 3) watcher.on_new_block_by_validator(block3_hash, 1, actor3) # DagVisualizer.visualize(dag, True) iterator = MergingIter(dag, block3_hash, watcher) 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), 3) self.assertIn(Entry(block2_reward, 0), unspent) self.assertIn(Entry(block3_reward, 0), unspent) self.assertIn(Entry(payment1.get_hash(), 0), unspent) self.assertNotIn(Entry(payment1c.get_hash(), 0), unspent) total_block_rewards = 0 iterator = MergingIter(dag, block3_hash, watcher) for block in iterator: if block: if not block.block.payment_txs: continue #it might be genesis, or just some silly validator who decided not to earn a reward block_reward = block.block.payment_txs[0] total_block_rewards += block_reward.amounts[0] unspent_total = 0 unspent_list = unspent #HACKY rename :) for unspent in unspent_list: unspent_tx = dag.payments_by_hash[unspent.tx] unspent_output = unspent.number unspent_amount = unspent_tx.amounts[unspent_output] unspent_total += unspent_amount self.assertEqual(total_block_rewards, unspent_total)