Beispiel #1
0
    def test_maybe_spent_txs(self):
        add_new_block(self.manager, advance_clock=15)
        blocks = add_blocks_unlock_reward(self.manager)
        w = self.manager.wallet
        new_address = w.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
        tx1 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        self.assertEqual(len(tx1.inputs), 1)
        _input = tx1.inputs[0]
        key = (_input.tx_id, _input.index)
        self.assertNotIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertIn(key, w.maybe_spent_txs[settings.HATHOR_TOKEN_UID])
        self.run_to_completion()
        self.assertIn(key, w.unspent_txs[settings.HATHOR_TOKEN_UID])
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))

        # when we receive the new tx it will remove from maybe_spent
        tx2 = w.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = max(
            tx2.get_spent_tx(txin).timestamp for txin in tx2.inputs) + 1
        tx2.parents = self.manager.get_new_tx_parents(tx2.timestamp)
        tx2.weight = 1
        tx2.timestamp = blocks[-1].timestamp + 1
        tx2.resolve()
        self.assertTrue(self.manager.on_new_tx(tx2, fails_silently=False))
        self.clock.advance(2)
        self.assertEqual(0, len(w.maybe_spent_txs[settings.HATHOR_TOKEN_UID]))
    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)
Beispiel #3
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]))
    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)
    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)
Beispiel #6
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())
Beispiel #7
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)
Beispiel #8
0
    def test_create_token_transaction(self):
        add_new_block(self.manager, advance_clock=5)
        add_blocks_unlock_reward(self.manager)
        tx = create_tokens(self.manager)

        tokens_created = tx.outputs[0].value
        token_uid = tx.tokens[0]
        address_b58 = self.manager.wallet.get_unused_address()
        address = decode_address(address_b58)

        _, hathor_balance = self.manager.wallet.balance[
            settings.HATHOR_TOKEN_UID]
        # prepare tx with hathors and another token
        # hathor tx
        hathor_out = WalletOutputInfo(address, hathor_balance, None)
        # token tx
        token_out = WalletOutputInfo(address, tokens_created - 20, None,
                                     token_uid.hex())

        tx2 = self.manager.wallet.prepare_transaction_compute_inputs(
            Transaction, [hathor_out, token_out])
        tx2.storage = self.manager.tx_storage
        tx2.timestamp = tx.timestamp + 1
        tx2.parents = self.manager.get_new_tx_parents()
        tx2.resolve()
        tx2.verify()

        self.assertNotEqual(len(tx2.inputs), 0)
        token_dict = defaultdict(int)
        for _input in tx2.inputs:
            output_tx = self.manager.tx_storage.get_transaction(_input.tx_id)
            output = output_tx.outputs[_input.index]
            token_uid = output_tx.get_token_uid(output.get_token_index())
            token_dict[token_uid] += output.value

        # make sure balance is the same and we've checked both balances
        did_enter = 0
        for token_uid, value in token_dict.items():
            if token_uid == settings.HATHOR_TOKEN_UID:
                self.assertEqual(value, hathor_balance)
                did_enter += 1
            elif token_uid == token_uid:
                self.assertEqual(value, tokens_created)
                did_enter += 1

        self.assertEqual(did_enter, 2)
Beispiel #9
0
 def test_prepare_transaction(self):
     block = add_new_block(self.manager, advance_clock=5)
     w = self.manager.wallet
     new_address = w.get_unused_address()
     out = WalletOutputInfo(decode_address(new_address), 1, timelock=None)
     with self.assertRaises(InsufficientFunds):
         w.prepare_transaction_compute_inputs(Transaction,
                                              outputs=[out],
                                              timestamp=block.timestamp)
Beispiel #10
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]))
Beispiel #11
0
 def test_separate_inputs(self):
     block = add_new_block(self.manager, advance_clock=5)
     my_input = TxInput(block.hash, 0, b'')
     genesis_blocks = [
         tx for tx in get_genesis_transactions(None) if tx.is_block
     ]
     genesis_block = genesis_blocks[0]
     other_input = TxInput(genesis_block.hash, 0, b'')
     my_inputs, other_inputs = self.manager.wallet.separate_inputs(
         [my_input, other_input], self.manager.tx_storage)
     self.assertEqual(len(my_inputs), 1)
     self.assertEqual(my_inputs[0], my_input)
     self.assertEqual(len(other_inputs), 1)
     self.assertEqual(other_inputs[0], other_input)
Beispiel #12
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))
Beispiel #13
0
    def test_transaction_and_balance(self):
        # generate a new block and check if we increase balance
        new_address = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address), self.TOKENS, timelock=None)
        block = add_new_block(self.manager)
        block.verify()
        utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((block.hash, 0))
        self.assertIsNotNone(utxo)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.BLOCK_TOKENS))

        # create transaction spending this value, but sending to same wallet
        new_address2 = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address2), self.TOKENS, timelock=None)
        tx1 = self.wallet.prepare_transaction_compute_inputs(Transaction, outputs=[out])
        tx1.update_hash()
        tx1.verify_script(tx1.inputs[0], block)
        tx1.storage = self.tx_storage
        self.wallet.on_new_tx(tx1)
        self.tx_storage.save_transaction(tx1)
        self.assertEqual(len(self.wallet.spent_txs), 1)
        utxo = self.wallet.unspent_txs[settings.HATHOR_TOKEN_UID].get((tx1.hash, 0))
        self.assertIsNotNone(utxo)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS))

        # pass inputs and outputs to prepare_transaction, but not the input keys
        # spend output last transaction
        input_info = WalletInputInfo(tx1.hash, 0, None)
        new_address3 = self.wallet.get_unused_address()
        out = WalletOutputInfo(decode_address(new_address3), self.TOKENS, timelock=None)
        tx2 = self.wallet.prepare_transaction_incomplete_inputs(Transaction, inputs=[input_info],
                                                                outputs=[out], tx_storage=self.tx_storage)
        tx2.storage = self.tx_storage
        tx2.update_hash()
        tx2.storage = self.tx_storage
        tx2.verify_script(tx2.inputs[0], tx1)
        self.tx_storage.save_transaction(tx2)
        self.wallet.on_new_tx(tx2)
        self.assertEqual(len(self.wallet.spent_txs), 2)
        self.assertEqual(self.wallet.balance[settings.HATHOR_TOKEN_UID], WalletBalance(0, self.TOKENS))

        # Test getting more unused addresses than the gap limit
        for i in range(3):
            kwargs = {'mark_as_used': True}
            if i == 2:
                # Last one we dont mark as used
                kwargs['mark_as_used'] = False

            self.wallet.get_unused_address(**kwargs)
Beispiel #14
0
    def test_checkpoint_validation(self):
        from hathor.transaction.transaction_metadata import ValidationState

        # manually validate with sync_checkpoints=True
        block1 = add_new_block(self.manager, propagate=False)
        block1.validate_full(sync_checkpoints=True)
        self.assertTrue(block1.get_metadata().validation.is_checkpoint())
        self.manager.propagate_tx(block1)
        del block1

        # use on_new_tx with sync_checkpoints=True
        block2 = self.manager.generate_mining_block()
        block2.resolve()
        self.assertTrue(
            self.manager.on_new_tx(block2,
                                   sync_checkpoints=True,
                                   partial=True,
                                   fails_silently=False))
        self.assertEqual(block2.get_metadata().validation,
                         ValidationState.CHECKPOINT_FULL)
    def test_double_spending_attempt_1(self):
        manager = self.manager1

        add_new_blocks(manager, 5, advance_clock=15)
        add_blocks_unlock_reward(manager)

        from hathor.crypto.util import decode_address
        from hathor.graphviz import GraphvizVisualizer
        from hathor.transaction import Transaction
        from hathor.wallet.base_wallet import WalletInputInfo, WalletOutputInfo

        graphviz = GraphvizVisualizer(manager.tx_storage,
                                      include_verifications=True,
                                      include_funds=True)

        addr = manager.wallet.get_unused_address()
        outputs = []
        outputs.append(WalletOutputInfo(decode_address(addr), 1, None))
        outputs.append(WalletOutputInfo(decode_address(addr), 1000, None))
        outputs.append(
            WalletOutputInfo(decode_address(addr), 6400 - 1001, None))
        tx_fund0 = manager.wallet.prepare_transaction_compute_inputs(
            Transaction, outputs, manager.tx_storage)
        tx_fund0.weight = 1
        tx_fund0.parents = manager.get_new_tx_parents()
        tx_fund0.timestamp = int(self.clock.seconds())
        tx_fund0.resolve()
        self.assertTrue(manager.propagate_tx(tx_fund0))

        def do_step(tx_fund):
            inputs = [
                WalletInputInfo(tx_fund.hash, 0,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [WalletOutputInfo(decode_address(addr), 1, None)]
            tx1 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx1.weight = 1
            tx1.parents = manager.get_new_tx_parents(tx1.timestamp)
            tx1.resolve()
            self.assertTrue(manager.propagate_tx(tx1))

            inputs = []
            inputs.append(
                WalletInputInfo(tx1.hash, 0,
                                manager.wallet.get_private_key(addr)))
            inputs.append(
                WalletInputInfo(tx_fund.hash, 1,
                                manager.wallet.get_private_key(addr)))
            outputs = [
                WalletOutputInfo(decode_address(addr),
                                 tx_fund.outputs[1].value + 1, None)
            ]
            tx2 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx1.timestamp + 1)
            tx2.weight = 1
            tx2.parents = manager.get_new_tx_parents(tx2.timestamp)
            tx2.resolve()
            self.assertTrue(manager.propagate_tx(tx2))

            inputs = [
                WalletInputInfo(tx_fund.hash, 0,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [WalletOutputInfo(decode_address(addr), 1, None)]
            tx3 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx3.weight = tx1.weight + tx2.weight + 0.1
            tx3.parents = manager.get_new_tx_parents(tx3.timestamp)
            tx3.resolve()
            self.assertTrue(manager.propagate_tx(tx3))

            inputs = [
                WalletInputInfo(tx_fund.hash, 1,
                                manager.wallet.get_private_key(addr))
            ]
            outputs = [
                WalletOutputInfo(decode_address(addr),
                                 tx_fund.outputs[1].value, None)
            ]
            tx4 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx_fund.timestamp + 1)
            tx4.weight = 1
            tx4.parents = manager.get_new_tx_parents(tx4.timestamp)
            tx4.resolve()
            self.assertTrue(manager.propagate_tx(tx4))

            inputs = []
            inputs.append(
                WalletInputInfo(tx2.hash, 0,
                                manager.wallet.get_private_key(addr)))
            inputs.append(
                WalletInputInfo(tx4.hash, 0,
                                manager.wallet.get_private_key(addr)))
            outputs = []
            outputs.append(WalletOutputInfo(decode_address(addr), 1, None))
            outputs.append(
                WalletOutputInfo(decode_address(addr),
                                 2 * tx_fund.outputs[1].value, None))
            tx5 = manager.wallet.prepare_transaction(Transaction, inputs,
                                                     outputs,
                                                     tx2.timestamp + 1)
            tx5.weight = tx3.weight - tx1.weight + 0.1
            tx5.parents = [tx2.hash, tx4.hash]
            tx5.resolve()
            self.assertTrue(manager.propagate_tx(tx5))
            return tx5

        tx = tx_fund0
        N = 10
        for _ in range(N):
            tx = do_step(tx)

        block = add_new_block(manager)
        self.assertIn(tx.hash, block.parents)

        dot = graphviz.dot()
        dot.render('dot0')

        meta = tx.get_metadata()
        self.assertIsNone(meta.conflict_with)
        self.assertIsNone(meta.voided_by)
        self.assertEqual(tx.outputs[1].value, 1000 * 2**N)

        self.assertConsensusValid(manager)
Beispiel #16
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)
Beispiel #17
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)