Ejemplo n.º 1
0
        def setUp(self):
            super().setUp()

            self.manager = self.create_peer(network='testnet')

            self.hashes_before = set()
            for genesis in self.manager.tx_storage.get_all_genesis():
                self.hashes_before.add(genesis.hash)

            self.blocks_before = add_new_blocks(self.manager,
                                                3,
                                                advance_clock=1)
            self.blocks_before.extend(add_blocks_unlock_reward(self.manager))
            self.txs_before = add_new_transactions(self.manager, 5)
            for block in self.blocks_before:
                self.hashes_before.add(block.hash)
            for tx in self.txs_before:
                self.hashes_before.add(tx.hash)

            address = self.get_address(0)
            self.root_tx = add_new_tx(self.manager, address=address, value=100)

            self.blocks_after = add_blocks_unlock_reward(self.manager)
            self.txs_after = add_new_transactions(self.manager, 5)
            self.blocks_after.extend(
                add_new_blocks(self.manager, 3, advance_clock=1))

            self.hashes_after = set()
            for block in self.blocks_after:
                self.hashes_after.add(block.hash)
            for tx in self.txs_after:
                self.hashes_after.add(tx.hash)
Ejemplo n.º 2
0
    def test_wallet(self):
        tmpdir = tempfile.mkdtemp()
        tmpfile = tempfile.NamedTemporaryFile(dir=tmpdir,
                                              suffix='.prom',
                                              delete=False)
        filename = os.path.basename(tmpfile.name)
        full_path = os.path.join(tmpdir, filename)

        prometheus = PrometheusMetricsExporter(metrics=self.manager.metrics,
                                               path=tmpdir,
                                               filename=filename)
        prometheus.set_new_metrics()

        with open(full_path, 'r') as f:
            text = f.read().split('\n')
            self.assertEqual(text[5], 'blocks 1.0')
            self.assertEqual(text[2], 'transactions 2.0')

        add_new_blocks(self.manager, 30, advance_clock=1)
        add_new_transactions(self.manager, 5, advance_clock=1)

        with open(full_path, 'r') as f:
            text = f.read().split('\n')
            self.assertEqual(text[5], 'blocks 1.0')
            self.assertEqual(text[2], 'transactions 2.0')

        self.run_to_completion()
        prometheus.set_new_metrics()
        with open(full_path, 'r') as f:
            text = f.read().split('\n')
            self.assertEqual(text[5], 'blocks 31.0')
            self.assertEqual(text[2], 'transactions 7.0')

        # Removing tmpdir
        shutil.rmtree(tmpdir)
Ejemplo n.º 3
0
    def test_dont_revert_block_high_weight_transaction_verify_other(self):
        """ A conflict transaction will be propagated and voided. But this transaction
        verifies its conflicting transaction. So, its accumulated weight will always be smaller
        than the others and it will never be executed.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager)
        meta = conflicting_tx.get_metadata()
        self.assertEqual(len(meta.conflict_with), 1)
        self.assertIn(list(meta.conflict_with)[0], conflicting_tx.parents)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        # These blocks will be voided later.
        blocks2 = add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a high weight.
        b0 = manager.generate_mining_block()
        b0.parents = [
            blocks[-1].hash, conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        # b0.parents = [b0.parents[0], conflicting_tx.hash, conflicting_tx.parents[0]]
        b0.weight = 10
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot2')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        for block in blocks2:
            meta = block.get_metadata()
            self.assertIsNone(meta.voided_by)

        self.assertConsensusValid(manager)
Ejemplo n.º 4
0
    def test_tips_twin(self):
        add_new_blocks(self.manager, 6, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)

        tx1 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        tx3 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # 3 txs and the last one is still a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx3.hash]))

        # A new tx with custom parents, so tx3 and tx4 will become two tips
        tx4 = add_new_transactions(self.manager,
                                   1,
                                   advance_clock=1,
                                   propagate=False)[0]
        tx4.parents = [tx1.hash, tx2.hash]
        tx4.resolve()
        self.manager.propagate_tx(tx4, fails_silently=False)
        self.manager.reactor.advance(10)
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx4.hash, tx3.hash]))

        # A twin tx with tx4, that will be voided initially, then won't change the tips
        tx5 = Transaction.create_from_struct(tx4.get_struct())
        tx5.parents = [tx2.hash, tx3.hash]
        tx5.resolve()
        self.manager.propagate_tx(tx5)
        self.manager.reactor.advance(10)

        # tx4 and tx5 are twins, so both are voided
        self.assertIsNotNone(tx4.get_metadata(force_reload=True).voided_by)
        self.assertIsNotNone(tx5.get_metadata(force_reload=True).voided_by)
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx3.hash]))

        # add new tx confirming tx5, which will become valid and tx4 becomes voided
        tx6 = add_new_transactions(self.manager,
                                   1,
                                   advance_clock=1,
                                   propagate=False)[0]
        tx6.parents = [tx5.hash, tx2.hash]
        tx6.resolve()
        self.manager.propagate_tx(tx6, fails_silently=False)
        self.manager.reactor.advance(10)
        self.assertIsNotNone(tx4.get_metadata(force_reload=True).voided_by)
        self.assertIsNone(tx5.get_metadata(force_reload=True).voided_by)

        # tx6 is the only one left
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx6.hash]))
Ejemplo n.º 5
0
    def test_choose_tips(self):
        genesis = self.manager.tx_storage.get_all_genesis()
        genesis_txs_hashes = [tx.hash for tx in genesis if not tx.is_block]

        b = add_new_block(self.manager, advance_clock=1)
        # The txs parents are the genesis
        self.assertCountEqual(set(b.parents[1:]), set(genesis_txs_hashes))
        reward_blocks = add_blocks_unlock_reward(self.manager)
        # No tips
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)

        tx1 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # The tx parents will be the genesis txs still
        self.assertCountEqual(set(tx1.parents), set(genesis_txs_hashes))
        # The new tx will be a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx1.hash]))

        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # The tx2 parents will be the tx1 and one of the genesis
        self.assertTrue(tx1.hash in tx2.parents)
        # The other parent will be one of tx1 parents
        self.assertTrue(
            set(tx2.parents).issubset(set([tx1.hash] + tx1.parents)))
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx2.hash]))

        tx3 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx3 parents will be tx2 and one of tx2 parents
        self.assertTrue(tx2.hash in tx3.parents)
        self.assertTrue(
            set(tx3.parents).issubset(set([tx2.hash] + tx2.parents)))
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx3.hash]))

        b2 = add_new_block(self.manager, advance_clock=1)
        # With new block there are no tips and block parents
        # will be tx3 and one of tx3 parents
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)
        self.assertTrue(tx3.hash in b2.parents)
        self.assertTrue(reward_blocks[-1].hash in b2.parents)
        self.assertTrue(
            set(b2.parents).issubset(
                set([tx3.hash] + [reward_blocks[-1].hash] + tx3.parents)))

        tx4 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx4 had no tip, so the parents will be the last block parents
        self.assertCountEqual(set(tx4.parents), set(b2.parents[1:]))
        # Then tx4 will become a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx4.hash]))
Ejemplo n.º 6
0
    def test_dont_revert_block_low_weight(self):
        """ A conflict transaction will be propagated and voided.
        A new block with low weight will verify it, which won't be enough to flip to executed.
        So, it will remain voided.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager,
                                                 use_same_parents=True)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})
        for parent_hash in conflicting_tx.parents:
            self.assertNotIn(parent_hash, meta.conflict_with)

        # These blocks will be voided later.
        add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a low weight.
        # So, it is not enough to revert and this block will be voided as well.
        b0 = manager.generate_mining_block()
        b0.parents = [
            blocks[-1].hash, conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot1')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        b0_meta = b0.get_metadata()
        self.assertEqual(b0_meta.voided_by, {b0.hash, conflicting_tx.hash})

        self.assertConsensusValid(manager)
Ejemplo n.º 7
0
    def test_dont_revert_block_high_weight_verify_both(self):
        """ A conflicting transaction will be propagated and voided. But the block with high weight
        verifies both the conflicting transactions, so this block will always be voided.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager,
                                                 use_same_parents=True)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})
        for parent_hash in conflicting_tx.parents:
            self.assertNotIn(parent_hash, meta.conflict_with)

        # Add two blocks.
        blocks2 = add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a high weight.
        b0 = manager.generate_mining_block()
        b0.parents = [
            b0.parents[0], conflicting_tx.hash, conflicting_tx.parents[0]
        ]
        b0.weight = 10
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot3')

        self.assertNotEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})

        for block in blocks2:
            meta = block.get_metadata()
            self.assertIsNone(meta.voided_by)

        self.assertConsensusValid(manager)
Ejemplo n.º 8
0
    def test_tips_winner(self):
        add_new_block(self.manager, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)

        tx1 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx1 will be the tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx1.hash]))

        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx2 will be the tip now
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx2.hash]))

        tx3 = Transaction.create_from_struct(tx2.get_struct())
        tx3.parents = [tx2.parents[1], tx2.parents[0]]
        tx3.resolve()

        # Propagate a conflicting twin transaction with tx2
        self.manager.propagate_tx(tx3)

        meta1 = tx2.get_metadata(force_reload=True)
        self.assertEqual(meta1.conflict_with, [tx3.hash])
        self.assertEqual(meta1.voided_by, {tx2.hash})
        self.assertEqual(meta1.twins, [tx3.hash])
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx1.hash]))

        self.manager.reactor.advance(10)

        # Creating a new block that confirms tx3, then is will become valid and voiding tx2
        new_block = add_new_block(self.manager, propagate=False)
        new_block.parents = [new_block.parents[0], tx1.hash, tx3.hash]
        new_block.resolve()
        new_block.verify()
        self.manager.propagate_tx(new_block, fails_silently=False)

        self.manager.reactor.advance(10)

        self.assertIsNone(
            self.manager.tx_storage.get_metadata(tx3.hash).voided_by)
        self.assertIsNotNone(
            self.manager.tx_storage.get_metadata(tx2.hash).voided_by)
        # The block confirms tx3, so it's not a tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(), set())
Ejemplo n.º 9
0
    def test_single_chain(self):
        """ All new blocks belong to case (i).
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # The initial score is the sum of the genesis
        score = self.genesis_blocks[0].weight
        for tx in self.genesis_txs:
            score = sum_weights(score, tx.weight)

        # Mine 100 blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 100, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata(force_reload=True)
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Add some transactions between blocks
        txs = add_new_transactions(manager, 30, advance_clock=15)
        for tx in txs:
            score = sum_weights(score, tx.weight)

        # Mine 50 more blocks in a row with no transactions between them
        blocks = add_new_blocks(manager, 50)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)
            self.assertAlmostEqual(
                manager.consensus_algorithm.block_algorithm.calculate_score(
                    block), meta.score)

        # Mine 15 more blocks with 10 transactions between each block
        for _ in range(15):
            txs = add_new_transactions(manager, 10, advance_clock=15)
            for tx in txs:
                score = sum_weights(score, tx.weight)

            blocks = add_new_blocks(manager, 1)
            for i, block in enumerate(blocks):
                meta = block.get_metadata()
                score = sum_weights(score, block.weight)
                self.assertAlmostEqual(score, meta.score)
                self.assertAlmostEqual(
                    manager.consensus_algorithm.block_algorithm.
                    calculate_score(block), meta.score)

        self.assertConsensusValid(manager)
Ejemplo n.º 10
0
    def test_topological_sort(self):
        _set_test_mode(TestMode.TEST_ALL_WEIGHT)
        _total = 0
        blocks = add_new_blocks(self.manager, 1, advance_clock=1)
        _total += len(blocks)
        blocks = add_blocks_unlock_reward(self.manager)
        _total += len(blocks)
        add_new_transactions(self.manager, 1, advance_clock=1)

        total = 0
        for tx in self.tx_storage._topological_sort():
            total += 1

        # added blocks + genesis txs + added tx
        self.assertEqual(total, _total + 3 + 1)
Ejemplo n.º 11
0
    def test_get(self):

        # Success empty mempool
        response1 = yield self.web.get("mempool")
        data1 = response1.json_value()
        self.assertTrue(data1['success'])
        self.assertEqual(data1['transactions'], [])

        # Success mempool with single TX
        txs2 = add_new_transactions(self.manager, 1, advance_clock=1)
        response2 = yield self.web.get("mempool")
        data2 = response2.json_value()
        self.assertTrue(data2['success'])
        self.assertEqual(data2['transactions'],
                         list(map(lambda t: t.hash.hex(), txs2)))

        # Success mempool with multiple TX
        txs3 = add_new_transactions(self.manager, 2, advance_clock=1)
        response3 = yield self.web.get("mempool")
        data3 = response3.json_value()
        self.assertTrue(data3['success'])
        self.assertEqual(data3['transactions'],
                         list(map(lambda t: t.hash.hex(), txs2 + txs3)))

        # add block to confirm previous txs
        add_new_blocks(self.manager, 1, advance_clock=1)

        # and next call will not have previous mempool
        txs4 = add_new_transactions(self.manager, 2, advance_clock=1)
        response4 = yield self.web.get("mempool")
        data4 = response4.json_value()
        self.assertTrue(data4['success'])
        self.assertEqual(data4['transactions'],
                         list(map(lambda t: t.hash.hex(), txs4)))

        # add block to confirm previous txs
        add_new_blocks(self.manager, 1, advance_clock=1)

        # Add more than api limit and check truncated return
        add_new_transactions(self.manager,
                             settings.MEMPOOL_API_TX_LIMIT + 1,
                             advance_clock=1)
        response5 = yield self.web.get("mempool")
        data5 = response5.json_value()
        self.assertTrue(data5['success'])
        # default limit is 100
        self.assertEqual(len(data5['transactions']),
                         settings.MEMPOOL_API_TX_LIMIT)
    def test_generate_signature(self):
        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = add_new_transactions(self.manager, 1, advance_clock=1)[0]

        address = self.wallet.get_unused_address()
        keypair = self.wallet.keys[address]
        private_key_hex = keypair.private_key_bytes.hex()

        private_key = keypair.get_private_key(b'123')
        public_key = private_key.public_key()

        parser = create_parser()

        # Generate signature to validate
        args = parser.parse_args([tx.get_struct().hex(), private_key_hex])
        f = StringIO()
        with redirect_stdout(f):
            execute(args, '123')
        # Transforming prints str in array
        output = f.getvalue().split('\n')
        # Last element is always empty string
        output.pop()

        signature = bytes.fromhex(output[0].split(':')[1].strip())

        # Now we validate that the signature is correct
        data_to_sign = tx.get_sighash_all()
        hashed_data = hashlib.sha256(data_to_sign).digest()
        self.assertIsNone(
            public_key.verify(signature, hashed_data,
                              ec.ECDSA(hashes.SHA256())))
Ejemplo n.º 13
0
    def test_get_tips(self):
        genesis_txs = [
            tx for tx in self.manager.tx_storage.get_all_genesis()
            if not tx.is_block
        ]

        # Tips are only the genesis
        response1 = yield self.web.get("tips")
        data1 = response1.json_value()
        self.assertTrue(data1['success'])
        self.assertEqual(len(data1['tips']), len(genesis_txs))

        self.manager.wallet.unlock(b'MYPASS')

        # Add blocks to have funds
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)

        # Add one tx, now you have only one tip
        tx = add_new_transactions(self.manager, 1)[0]

        response2 = yield self.web.get("tips")
        data2 = response2.json_value()
        self.assertTrue(data2['success'])
        self.assertEqual(len(data2['tips']), 1)

        # Getting tips sending timestamp as parameter
        response3 = yield self.web.get("tips",
                                       {b'timestamp': tx.timestamp - 1})
        data3 = response3.json_value()
        self.assertEqual(len(data3), 2)
Ejemplo n.º 14
0
    def test_accumulated_weight_indirect_block(self):
        """ All new blocks belong to case (i).
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine 3 blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        tx_list = add_new_transactions(manager, 20, advance_clock=15)

        # Mine more 2 blocks in a row with no transactions between them
        blocks = add_new_blocks(manager, 2, weight=8)

        tx0 = tx_list[0]
        for block in blocks:
            self.assertNotIn(tx0.hash, block.parents)

        # All transactions and blocks should be verifying tx_list[0] directly or
        # indirectly.
        expected = 0
        for tx in tx_list:
            expected = sum_weights(expected, tx.weight)
        for block in blocks:
            expected = sum_weights(expected, block.weight)

        meta = tx0.update_accumulated_weight()
        self.assertAlmostEqual(meta.accumulated_weight, expected)
Ejemplo n.º 15
0
    def test_get_tips_histogram(self):
        # Add blocks to have funds
        add_new_blocks(self.manager, 2, 2)
        add_blocks_unlock_reward(self.manager)

        txs = add_new_transactions(self.manager, 10, 2)

        response1 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[0].timestamp
        })
        data1 = response1.json_value()
        self.assertEqual(len(data1), 1)
        self.assertEqual([txs[0].timestamp, 1], data1[0])

        response2 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[0].timestamp + 1
        })
        data2 = response2.json_value()
        self.assertEqual(len(data2), 2)
        self.assertEqual([txs[0].timestamp, 1], data2[0])
        self.assertEqual([txs[0].timestamp + 1, 1], data2[1])

        response3 = yield self.web.get("tips-histogram", {
            b'begin': txs[0].timestamp,
            b'end': txs[-1].timestamp
        })
        data3 = response3.json_value()
        self.assertEqual(len(data3), 19)
Ejemplo n.º 16
0
    def setUp(self):
        super().setUp()
        self.tx_storage = ModifiedTransactionMemoryStorage()
        self.network = 'testnet'
        self.manager = self.create_peer(self.network,
                                        tx_storage=self.tx_storage)

        self.all_hashes = set()
        for tx in self.manager.tx_storage.get_all_transactions():
            self.all_hashes.add(tx.hash)

        # generate blocks and transactions where blk1 is spent by tx1
        self.blk1 = add_new_block(self.manager, advance_clock=15)
        self.block_list = add_blocks_unlock_reward(self.manager)

        self.tx_list = add_new_transactions(self.manager, 5, advance_clock=15)
        self.tx1 = self.tx_list[0]
        self.assertTrue(self.tx1.inputs[0].tx_id == self.blk1.hash)

        self.block_list2 = add_new_blocks(self.manager, 8, advance_clock=15)

        # collect all hashes
        self.all_hashes.add(self.blk1.hash)
        self.all_hashes.update(x.hash for x in self.block_list)
        self.all_hashes.update(x.hash for x in self.tx_list)
        self.all_hashes.update(x.hash for x in self.block_list2)
Ejemplo n.º 17
0
    def test_tx_methods(self):
        blocks = add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        txs = add_new_transactions(self.manager, 2, advance_clock=1)

        # Validate __str__, __bytes__, __eq__
        tx = txs[0]
        tx2 = txs[1]
        str_tx = str(tx)
        self.assertTrue(isinstance(str_tx, str))
        self.assertEqual(bytes(tx), tx.get_struct())

        tx_equal = Transaction.create_from_struct(tx.get_struct())
        self.assertTrue(tx == tx_equal)
        self.assertFalse(tx == tx2)

        tx2_hash = tx2.hash
        tx2.hash = None
        self.assertFalse(tx == tx2)
        tx2.hash = tx2_hash

        # Validate is_genesis without storage
        tx_equal.storage = None
        self.assertFalse(tx_equal.is_genesis)

        # Pow error
        tx2.verify_pow()
        tx2.weight = 100
        with self.assertRaises(PowError):
            tx2.verify_pow()

        # Get sighashall is different with and without data
        self.assertNotEqual(tx.get_sighash_all(), tx.get_sighash_all(clear_input_data=False))

        # Verify parent timestamps
        tx2.verify_parents()
        tx2_timestamp = tx2.timestamp
        tx2.timestamp = 2
        with self.assertRaises(TimestampError):
            tx2.verify_parents()
        tx2.timestamp = tx2_timestamp

        # Verify inputs timestamps
        tx2.verify_inputs()
        tx2.timestamp = 2
        with self.assertRaises(TimestampError):
            tx2.verify_inputs()
        tx2.timestamp = tx2_timestamp

        # Validate maximum distance between blocks
        block = blocks[0]
        block2 = blocks[1]
        block2.timestamp = block.timestamp + settings.MAX_DISTANCE_BETWEEN_BLOCKS
        block2.verify_parents()
        block2.timestamp += 1
        with self.assertRaises(TimestampError):
            block2.verify_parents()
Ejemplo n.º 18
0
    def test_topological_sort_dfs(self):
        _set_test_mode(TestMode.TEST_ALL_WEIGHT)
        add_new_blocks(self.manager, 11, advance_clock=1)
        tx = add_new_transactions(self.manager, 1, advance_clock=1)[0]

        total = 0
        for tx in self.cache_storage._topological_sort_dfs(root=tx,
                                                           visited=dict()):
            total += 1
        self.assertEqual(total, 5)
Ejemplo n.º 19
0
    def setUp(self):
        super().setUp()

        self.network = 'testnet'
        self.manager = self.create_peer(self.network, unlock_wallet=True)

        add_new_blocks(self.manager, 1, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.tx = add_new_transactions(self.manager, 1, advance_clock=1)[0]

        self.parser = create_parser()
Ejemplo n.º 20
0
    def test_tips_back(self):
        add_new_block(self.manager, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        self.assertEqual(
            len(self.manager.tx_storage.indexes.mempool_tips.get()), 0)

        tx = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx will be the tip
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(), set([tx.hash]))

        tx2 = add_new_transactions(self.manager, 1, advance_clock=1)[0]
        # tx2 will be the tip now
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx2.hash]))

        # with a double spending tx2 must continue being the tip
        add_new_double_spending(self.manager)
        self.assertCountEqual(
            self.manager.tx_storage.indexes.mempool_tips.get(),
            set([tx2.hash]))
Ejemplo n.º 21
0
    def test_init_not_voided_tips(self):
        # add a bunch of blocks and transactions
        for i in range(30):
            add_new_block(self.manager, advance_clock=15)
            add_new_transactions(self.manager, 5, advance_clock=15)

        # add a bunch of conflicting transactions, these will all become voided
        for i in range(50):
            add_new_double_spending(self.manager)

        # finish up with another bunch of blocks and transactions
        for i in range(30):
            add_new_block(self.manager, advance_clock=15)
            add_new_transactions(self.manager, 5, advance_clock=15)

        # not the point of this test, but just a sanity check
        self.assertConsensusValid(self.manager)

        # make sure we have the right number of voided transactions
        self.assertEqual(
            50,
            sum(
                bool(tx.get_metadata().voided_by)
                for tx in self.tx_storage.get_all_transactions()))

        # create a new manager (which will initialize in the self.create_peer call)
        self.tx_storage._reset_cache()
        self.manager.stop()
        manager = self.create_peer(self.network,
                                   tx_storage=self.tx_storage,
                                   full_verification=False)

        # make sure none of its tx tips are voided
        all_tips = manager.generate_parent_txs(None).get_all_tips()
        iter_tips_meta = map(manager.tx_storage.get_metadata, all_tips)
        self.assertFalse(any(tx_meta.voided_by for tx_meta in iter_tips_meta))
Ejemplo n.º 22
0
    def test_get_one(self):
        genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis()
                          if x.is_block)
        response_success = yield self.web.get(
            "transaction", {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])
        dict_test = genesis_tx.to_json(decode_script=True)
        dict_test['raw'] = genesis_tx.get_struct().hex()
        dict_test['nonce'] = str(dict_test['nonce'])
        if genesis_tx.is_block:
            dict_test['height'] = genesis_tx.calculate_height()
        self.assertEqual(data_success['tx'], dict_test)

        # Test sending hash that does not exist
        response_error1 = yield self.web.get(
            "transaction", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a5533848'
            })
        data_error1 = response_error1.json_value()
        self.assertFalse(data_error1['success'])

        # Test sending invalid hash
        response_error2 = yield self.web.get(
            "transaction", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a553384'
            })
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])

        # Adding blocks to have funds
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = add_new_transactions(self.manager, 1)[0]

        tx2 = Transaction.create_from_struct(tx.get_struct())
        tx2.parents = [tx.parents[1], tx.parents[0]]
        tx2.resolve()

        self.manager.propagate_tx(tx2)

        # Now we get a tx with conflict, voided_by and twin
        response_conflict = yield self.web.get(
            "transaction", {b'id': bytes(tx2.hash.hex(), 'utf-8')})
        data_conflict = response_conflict.json_value()
        self.assertTrue(data_conflict['success'])
Ejemplo n.º 23
0
    def test_get_txs(self):
        first_block = add_new_blocks(self.manager, 30, advance_clock=1)[0]
        first_tx = add_new_transactions(self.manager, 3, advance_clock=1)[0]

        # Using timestamp as float to test code
        txs, _ = self.manager.tx_storage.get_older_txs_after(
            float(first_tx.timestamp), first_tx.hash, 3)
        self.assertEqual(len(txs), 2)

        txs, _ = self.manager.tx_storage.get_newer_txs_after(
            float(first_tx.timestamp), first_tx.hash, 3)
        self.assertEqual(len(txs), 2)

        blocks, _ = self.manager.tx_storage.get_older_blocks_after(
            float(first_block.timestamp), first_block.hash, 3)
        self.assertEqual(len(blocks), 1)

        blocks, _ = self.manager.tx_storage.get_newer_blocks_after(
            float(first_block.timestamp), first_block.hash, 3)
        self.assertEqual(len(blocks), 3)

        tx = txs[0]
        proto = tx.to_proto()
        tx2 = Transaction.create_from_proto(proto)
        self.assertEqual(tx, tx2)

        block = blocks[0]
        proto2 = block.to_proto()
        block2 = Block.create_from_proto(proto2)
        self.assertEqual(block, block2)

        tx3 = tx_or_block_from_proto(proto)
        self.assertEqual(tx, tx3)

        proto.ClearField('transaction')

        with self.assertRaises(ValueError):
            tx_or_block_from_proto(proto)

        t = datetime.datetime.now() - datetime.timedelta(seconds=1)
        t_tx = tx.get_time_from_now()
        t2_tx = tx.get_time_from_now(now=t)

        self.assertNotEqual(t_tx, t2_tx)
Ejemplo n.º 24
0
    def setUp(self):
        super().setUp()
        self.resource = self.create_resource()
        self.web = StubSite(self.resource)

        # Unlocking wallet
        self.manager.wallet.unlock(b'MYPASS')

        # Creating blocks, txs and a conflict tx to test graphviz with it
        add_new_blocks(self.manager, 2, advance_clock=2)
        add_blocks_unlock_reward(self.manager)
        txs = add_new_transactions(self.manager, 2, advance_clock=2)
        tx = txs[0]

        self.tx2 = Transaction.create_from_struct(tx.get_struct())
        self.tx2.parents = [tx.parents[1], tx.parents[0]]
        self.tx2.resolve()

        self.manager.propagate_tx(self.tx2)
    def test_get_data(self):
        genesis_tx = next(x for x in self.manager.tx_storage.get_all_genesis()
                          if x.is_transaction)
        response_success = yield self.web.get(
            "transaction_acc_weight",
            {b'id': bytes(genesis_tx.hash.hex(), 'utf-8')})
        data_success = response_success.json_value()
        self.assertTrue(data_success['success'])
        self.assertEqual(data_success['accumulated_weight'], genesis_tx.weight)
        self.assertEqual(data_success['confirmation_level'], 0)

        # Adding blocks to have funds
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        tx = add_new_transactions(self.manager, 5)[0]
        add_new_blocks(self.manager, 2, advance_clock=1)
        add_blocks_unlock_reward(self.manager)
        response_success2 = yield self.web.get(
            "transaction_acc_weight", {b'id': bytes(tx.hash.hex(), 'utf-8')})
        data_success2 = response_success2.json_value()
        self.assertGreater(data_success2['accumulated_weight'], tx.weight)
        self.assertEqual(data_success2['confirmation_level'], 1)

        # Test sending hash that does not exist
        response_error1 = yield self.web.get(
            "transaction_acc_weight", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a5533848'
            })
        data_error1 = response_error1.json_value()
        self.assertFalse(data_error1['success'])

        # Test sending invalid hash
        response_error2 = yield self.web.get(
            "transaction_acc_weight", {
                b'id':
                b'000000831cff82fa730cbdf8640fae6c130aab1681336e2f8574e314a553384'
            })
        data_error2 = response_error2.json_value()
        self.assertFalse(data_error2['success'])
Ejemplo n.º 26
0
    def test_single_fork_not_best(self):
        """ New blocks belong to cases (i), (ii), (iii), and (iv).
        The best chain never changes. All other chains are side chains.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # The initial score is the sum of the genesis
        score = self.genesis_blocks[0].weight
        for tx in self.genesis_txs:
            score = sum_weights(score, tx.weight)

        # Mine 30 blocks in a row with no transactions
        blocks = add_new_blocks(manager, 30, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Add some transactions between blocks
        txs = add_new_transactions(manager, 5, advance_clock=15)
        for tx in txs:
            score = sum_weights(score, tx.weight)

        # Mine 1 blocks
        blocks = add_new_blocks(manager, 1, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Generate a block which will be a fork in the middle of the chain
        # Change the order of the transactions to change the hash
        fork_block1 = manager.generate_mining_block()
        fork_block1.parents = [fork_block1.parents[0]
                               ] + fork_block1.parents[:0:-1]
        fork_block1.resolve()
        fork_block1.verify()

        # Mine 8 blocks in a row
        blocks = add_new_blocks(manager, 8, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Fork block must have the same parents as blocks[0] as well as the same score
        self.assertEqual(set(blocks[0].parents), set(fork_block1.parents))

        # Propagate fork block.
        # This block belongs to case (ii).
        self.assertTrue(manager.propagate_tx(fork_block1))
        fork_meta1 = fork_block1.get_metadata()
        self.assertEqual(fork_meta1.voided_by, {fork_block1.hash})

        # Add some transactions between blocks
        txs = add_new_transactions(manager, 5, advance_clock=15)
        for tx in txs:
            score = sum_weights(score, tx.weight)

        # Mine 5 blocks in a row
        # These blocks belong to case (i).
        blocks = add_new_blocks(manager, 5, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Add some transactions between blocks
        txs = add_new_transactions(manager, 2, advance_clock=15)
        for tx in txs:
            score = sum_weights(score, tx.weight)

        # Propagate a block connected to the voided chain
        # These blocks belongs to case (iii).
        sidechain1 = add_new_blocks(manager,
                                    3,
                                    parent_block_hash=fork_block1.hash)
        for block in sidechain1:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        # Add some transactions between blocks
        txs = add_new_transactions(manager, 2, advance_clock=15)
        for tx in txs:
            score = sum_weights(score, tx.weight)

        # Propagate a block connected to the voided chain
        # This block belongs to case (iv).
        fork_block3 = manager.generate_mining_block(
            parent_block_hash=fork_block1.hash)
        fork_block3.resolve()
        fork_block3.verify()
        self.assertTrue(manager.propagate_tx(fork_block3))
        fork_meta3 = fork_block3.get_metadata()
        self.assertEqual(fork_meta3.voided_by, {fork_block3.hash})

        self.assertConsensusValid(manager)
Ejemplo n.º 27
0
    def test_get_many(self):
        # Add some blocks and txs and get them in timestamp order
        blocks = add_new_blocks(self.manager, 4, advance_clock=1)
        _blocks = add_blocks_unlock_reward(self.manager)
        txs = sorted(add_new_transactions(self.manager, 25),
                     key=lambda x: (x.timestamp, x.hash))

        blocks.extend(_blocks)
        blocks = sorted(blocks, key=lambda x: (x.timestamp, x.hash))

        # Get last 2 blocks
        expected1 = blocks[-2:]
        expected1.reverse()

        response1 = yield self.web.get("transaction", {
            b'count': b'2',
            b'type': b'block'
        })
        data1 = response1.json_value()

        for expected, result in zip(expected1, data1['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertTrue(data1['has_more'])

        # Get last 8 txs
        expected2 = txs[-8:]
        expected2.reverse()

        response2 = yield self.web.get("transaction", {
            b'count': b'8',
            b'type': b'tx'
        })
        data2 = response2.json_value()

        for expected, result in zip(expected2, data2['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertTrue(data2['has_more'])

        # Get older blocks with hash reference
        expected3 = blocks[:2]
        expected3.reverse()

        response3 = yield self.web.get(
            "transaction", {
                b'count': b'3',
                b'type': b'block',
                b'timestamp': bytes(str(blocks[2].timestamp), 'utf-8'),
                b'hash': bytes(blocks[2].hash.hex(), 'utf-8'),
                b'page': b'next'
            })
        data3 = response3.json_value()

        for expected, result in zip(expected3, data3['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertFalse(data3['has_more'])

        # Get newer txs with hash reference
        response4 = yield self.web.get(
            "transaction", {
                b'count': b'16',
                b'type': b'tx',
                b'timestamp': bytes(str(txs[-9].timestamp), 'utf-8'),
                b'hash': bytes(txs[-9].hash.hex(), 'utf-8'),
                b'page': b'previous'
            })
        data4 = response4.json_value()

        for expected, result in zip(expected2, data4['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertFalse(data4['has_more'])

        # Get newer blocks with hash reference
        expected5 = blocks[-2:]
        expected5.reverse()

        response5 = yield self.web.get(
            "transaction", {
                b'count': b'3',
                b'type': b'block',
                b'timestamp': bytes(str(expected1[-1].timestamp), 'utf-8'),
                b'hash': bytes(expected1[-1].hash.hex(), 'utf-8'),
                b'page': b'previous'
            })
        data5 = response5.json_value()

        for expected, result in zip(expected5, data5['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertFalse(data5['has_more'])

        # Get txs with hash reference
        expected6 = txs[:8]
        expected6.reverse()

        response6 = yield self.web.get(
            "transaction", {
                b'count': b'8',
                b'type': b'tx',
                b'timestamp': bytes(str(txs[8].timestamp), 'utf-8'),
                b'hash': bytes(txs[8].hash.hex(), 'utf-8'),
                b'page': b'next'
            })
        data6 = response6.json_value()

        for expected, result in zip(expected6, data6['transactions']):
            self.assertEqual(expected.timestamp, result['timestamp'])
            self.assertEqual(expected.hash.hex(), result['tx_id'])

        self.assertTrue(data6['has_more'])
Ejemplo n.º 28
0
    def test_split_brain(self):
        debug_pdf = False

        manager1 = self.create_peer(self.network, unlock_wallet=True)
        manager1.avg_time_between_blocks = 3

        manager2 = self.create_peer(self.network, unlock_wallet=True)
        manager2.avg_time_between_blocks = 3

        for _ in range(10):
            add_new_block(manager1, advance_clock=1)
            add_blocks_unlock_reward(manager1)
            add_new_block(manager2, advance_clock=1)
            add_blocks_unlock_reward(manager2)
            self.clock.advance(10)
            for _ in range(random.randint(3, 10)):
                add_new_transactions(manager1, random.randint(2, 4))
                add_new_transactions(manager2, random.randint(3, 7))
                add_new_double_spending(manager1)
                add_new_double_spending(manager2)
                self.clock.advance(10)
        self.clock.advance(20)

        self.assertTipsNotEqual(manager1, manager2)
        self.assertConsensusValid(manager1)
        self.assertConsensusValid(manager2)

        if debug_pdf:
            dot1 = GraphvizVisualizer(manager1.tx_storage,
                                      include_verifications=True).dot()
            dot1.render('dot1-pre')

        conn = FakeConnection(manager1, manager2)

        conn.run_one_step()  # HELLO
        conn.run_one_step()  # PEER-ID

        empty_counter = 0
        for i in range(1000):
            if conn.is_empty():
                empty_counter += 1
                if empty_counter > 10:
                    break
            else:
                empty_counter = 0

            conn.run_one_step()
            self.clock.advance(0.2)

        if debug_pdf:
            dot1 = GraphvizVisualizer(manager1.tx_storage,
                                      include_verifications=True).dot()
            dot1.render('dot1-post')
            dot2 = GraphvizVisualizer(manager2.tx_storage,
                                      include_verifications=True).dot()
            dot2.render('dot2-post')

        node_sync = conn.proto1.state.get_sync_plugin()
        self.assertEqual(node_sync.synced_timestamp, node_sync.peer_timestamp)
        self.assertTipsEqual(manager1, manager2)
        self.assertConsensusEqual(manager1, manager2)
        # self.assertConsensusValid(manager1)
        self.assertConsensusValid(manager2)
Ejemplo n.º 29
0
    def test_multiple_forks(self):
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # The initial score is the sum of the genesis
        score = self.genesis_blocks[0].weight
        for tx in self.genesis_txs:
            score = sum_weights(score, tx.weight)

        # Mine 30 blocks in a row with no transactions, case (i).
        blocks = add_new_blocks(manager, 30, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Add some transactions between blocks
        txs1 = add_new_transactions(manager, 5, advance_clock=15)
        for tx in txs1:
            score = sum_weights(score, tx.weight)

        # Mine 1 blocks, case (i).
        blocks = add_new_blocks(manager, 1, advance_clock=15)
        block_before_fork = blocks[0]
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        for tx in txs1:
            meta = tx.get_metadata(force_reload=True)
            self.assertEqual(meta.first_block, blocks[0].hash)

        # Add some transactions between blocks
        txs2 = add_new_transactions(manager, 3, advance_clock=15)
        for tx in txs2:
            score = sum_weights(score, tx.weight)

        # Mine 5 blocks in a row, case (i).
        blocks = add_new_blocks(manager, 5, advance_clock=15)
        for i, block in enumerate(blocks):
            meta = block.get_metadata()
            score = sum_weights(score, block.weight)
            self.assertAlmostEqual(score, meta.score)

        # Mine 4 blocks, starting a fork.
        # All these blocks belong to case (ii).
        sidechain = add_new_blocks(manager,
                                   4,
                                   advance_clock=15,
                                   parent_block_hash=blocks[0].parents[0])

        # Fork block must have the same parents as blocks[0] as well as the same score
        self.assertEqual(set(blocks[0].parents), set(sidechain[0].parents))

        for block in blocks:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, None)

        for block in sidechain:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        # Propagate a block connected to the voided chain, case (iii).
        fork_block2 = manager.generate_mining_block(
            parent_block_hash=sidechain[-1].hash)
        fork_block2.resolve()
        fork_block2.verify()
        self.assertTrue(manager.propagate_tx(fork_block2))
        sidechain.append(fork_block2)

        # Now, both chains have the same score.
        for block in blocks:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for block in sidechain:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for tx in txs1:
            meta = tx.get_metadata(force_reload=True)
            self.assertEqual(meta.first_block, block_before_fork.hash)

        for tx in txs2:
            meta = tx.get_metadata(force_reload=True)
            self.assertIsNone(meta.first_block)

        # Mine 1 block, starting another fork.
        # This block belongs to case (vi).
        sidechain2 = add_new_blocks(manager,
                                    1,
                                    advance_clock=15,
                                    parent_block_hash=sidechain[0].hash)

        for block in sidechain2:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        # Mine 2 more blocks in the new fork.
        # These blocks belong to case (vii).
        sidechain2 += add_new_blocks(manager,
                                     2,
                                     advance_clock=15,
                                     parent_block_hash=sidechain2[-1].hash)

        for block in sidechain2:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        # Mine 1 block, starting another fork from sidechain2.
        # This block belongs to case (viii).
        sidechain3 = add_new_blocks(manager,
                                    1,
                                    advance_clock=15,
                                    parent_block_hash=sidechain2[-2].hash)

        for block in sidechain3:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        # Propagate a block connected to the side chain, case (v).
        fork_block3 = manager.generate_mining_block(
            parent_block_hash=fork_block2.hash)
        fork_block3.resolve()
        fork_block3.verify()
        self.assertTrue(manager.propagate_tx(fork_block3))
        sidechain.append(fork_block3)

        # The side chains have exceeded the score (after it has the same score)
        for block in blocks:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for block in sidechain:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, None)

        # from hathor.graphviz import GraphvizVisualizer
        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot0')

        for tx in txs2:
            meta = tx.get_metadata(force_reload=True)
            self.assertEqual(meta.first_block, sidechain[0].hash)

        # Propagate a block connected to the side chain, case (v).
        # Another side chain has direcly exceeded the best score.
        fork_block4 = manager.generate_mining_block(
            parent_block_hash=sidechain3[-1].hash)
        fork_block4.weight = 10
        fork_block4.resolve()
        fork_block4.verify()
        self.assertTrue(manager.propagate_tx(fork_block4))
        sidechain3.append(fork_block4)

        for block in blocks:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for block in sidechain[1:]:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for block in sidechain2[-1:]:
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, {block.hash})

        for block in chain(sidechain[:1], sidechain2[:-1], sidechain3):
            meta = block.get_metadata(force_reload=True)
            self.assertEqual(meta.voided_by, None)

        for tx in txs2:
            meta = tx.get_metadata(force_reload=True)
            self.assertEqual(meta.first_block, sidechain[0].hash)

        # dot = manager.tx_storage.graphviz(format='pdf')
        # dot.render('test_fork')

        self.assertConsensusValid(manager)
Ejemplo n.º 30
0
    def test_revert_block_high_weight(self):
        """ A conflict transaction will be propagated. At first, it will be voided.
        But, a new block with high weight will verify it, which will flip it to executed.
        """
        self.assertEqual(len(self.genesis_blocks), 1)
        manager = self.create_peer('testnet', tx_storage=self.tx_storage)

        # Mine a few blocks in a row with no transaction but the genesis
        blocks = add_new_blocks(manager, 3, advance_clock=15)
        add_blocks_unlock_reward(manager)

        # Add some transactions between blocks
        add_new_transactions(manager, 5, advance_clock=15)

        # Create a double spending transaction.
        conflicting_tx = add_new_double_spending(manager, use_same_parents=True)

        # Add a few transactions.
        add_new_transactions(manager, 10, advance_clock=15)

        meta = conflicting_tx.get_metadata()
        self.assertEqual(meta.voided_by, {conflicting_tx.hash})
        for parent_hash in conflicting_tx.parents:
            self.assertNotIn(parent_hash, meta.conflict_with)

        # These blocks will be voided later.
        blocks2 = add_new_blocks(manager, 2, advance_clock=15)

        # This block verifies the conflicting transaction and has a high weight.
        # So, it will be executed and previous blocks and transactions will be voided.
        tb0 = manager.make_custom_block_template(blocks[-1].hash, [conflicting_tx.hash, conflicting_tx.parents[0]])
        b0 = tb0.generate_mining_block(storage=manager.tx_storage)
        b0.weight = 10
        b0.resolve()
        b0.verify()
        manager.propagate_tx(b0, fails_silently=False)

        b1 = add_new_block(manager, advance_clock=15)
        b2 = add_new_block(manager, advance_clock=15)

        # from hathor.graphviz import GraphvizVisualizer
        # dot = GraphvizVisualizer(manager.tx_storage, include_verifications=True, include_funds=True).dot()
        # dot.render('dot0')

        self.assertEqual(b1.parents[0], b0.hash)
        self.assertEqual(b2.parents[0], b1.hash)

        meta = conflicting_tx.get_metadata()
        self.assertIsNone(meta.voided_by)

        # Find the other transaction voiding the blocks.
        tmp_tx = manager.tx_storage.get_transaction(blocks2[0].parents[1])
        tmp_tx_meta = tmp_tx.get_metadata()
        self.assertEqual(len(tmp_tx_meta.voided_by), 1)
        other_tx_hash = list(tmp_tx_meta.voided_by)[0]

        for block in blocks2:
            meta = block.get_metadata()
            self.assertEqual(meta.voided_by, {other_tx_hash, block.hash})

        self.assertConsensusValid(manager)