示例#1
0
    def run_test(self):
        node = self.nodes[0]  # convenience reference to the node

        self.bootstrap_p2p()  # Add one p2p connection to the node

        best_block = self.nodes[0].getbestblockhash()
        tip = int(best_block, 16)
        best_block_time = self.nodes[0].getblock(best_block)['time']
        block_time = best_block_time + 1

        self.log.info("Create a new block with an anyone-can-spend coinbase.")
        height = 1
        block = create_block(tip, create_coinbase(height), block_time)
        block.solve()
        # Save the coinbase for later
        block1 = block
        node.p2ps[0].send_blocks_and_test([block], node, success=True)

        self.log.info("Mature the block.")
        self.generatetoaddress(
            self.nodes[0], 100,
            self.nodes[0].get_deterministic_priv_key().address)

        # Iterate through a list of known invalid transaction types, ensuring each is
        # rejected. Some are consensus invalid and some just violate policy.
        for BadTxTemplate in invalid_txs.iter_all_templates():
            self.log.info("Testing invalid transaction: %s",
                          BadTxTemplate.__name__)
            template = BadTxTemplate(spend_block=block1)
            tx = template.get_tx()
            node.p2ps[0].send_txs_and_test(
                [tx],
                node,
                success=False,
                expect_disconnect=template.expect_disconnect,
                reject_reason=template.reject_reason,
            )

            if template.expect_disconnect:
                self.log.info("Reconnecting to peer")
                self.reconnect_p2p()

        # Make two p2p connections to provide the node with orphans
        # * p2ps[0] will send valid orphan txs (one with low fee)
        # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
        self.reconnect_p2p(num_connections=2)

        self.log.info('Test orphan transaction handling ... ')
        # Create a root transaction that we withhold until all dependent transactions
        # are sent out and in the orphan cache
        SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
        tx_withhold = CTransaction()
        tx_withhold.vin.append(
            CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
        tx_withhold.vout = [
            CTxOut(nValue=25 * COIN - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
        ] * 2
        tx_withhold.calc_sha256()

        # Our first orphan tx with some outputs to create further orphan txs
        tx_orphan_1 = CTransaction()
        tx_orphan_1.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
        tx_orphan_1.vout = [
            CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
        ] * 3
        tx_orphan_1.calc_sha256()

        # A valid transaction with low fee
        tx_orphan_2_no_fee = CTransaction()
        tx_orphan_2_no_fee.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
        tx_orphan_2_no_fee.vout.append(
            CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        # A valid transaction with sufficient fee
        tx_orphan_2_valid = CTransaction()
        tx_orphan_2_valid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
        tx_orphan_2_valid.vout.append(
            CTxOut(nValue=8 * COIN - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_2_valid.calc_sha256()

        # An invalid transaction with negative fee
        tx_orphan_2_invalid = CTransaction()
        tx_orphan_2_invalid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
        tx_orphan_2_invalid.vout.append(
            CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_2_invalid.calc_sha256()

        self.log.info('Send the orphans ... ')
        # Send valid orphan txs from p2ps[0]
        node.p2ps[0].send_txs_and_test(
            [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid],
            node,
            success=False)
        # Send invalid tx from p2ps[1]
        node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid],
                                       node,
                                       success=False)

        assert_equal(0,
                     node.getmempoolinfo()['size'])  # Mempool should be empty
        assert_equal(2, len(node.getpeerinfo()))  # p2ps[1] is still connected

        self.log.info('Send the withhold tx ... ')
        with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
            node.p2ps[0].send_txs_and_test([tx_withhold], node, success=True)

        # Transactions that should end up in the mempool
        expected_mempool = {
            t.hash
            for t in [
                tx_withhold,  # The transaction that is the root for all orphans
                tx_orphan_1,  # The orphan transaction that splits the coins
                tx_orphan_2_valid,  # The valid transaction (with sufficient fee)
            ]
        }
        # Transactions that do not end up in the mempool:
        # tx_orphan_2_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
        # tx_orphan_2_invalid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)

        self.wait_until(lambda: 1 == len(node.getpeerinfo()),
                        timeout=12)  # p2ps[1] is no longer connected
        assert_equal(expected_mempool, set(node.getrawmempool()))

        self.log.info('Test orphan pool overflow')
        orphan_tx_pool = [CTransaction() for _ in range(101)]
        for i in range(len(orphan_tx_pool)):
            orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333)))
            orphan_tx_pool[i].vout.append(
                CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        with node.assert_debug_log(['orphanage overflow, removed 1 tx']):
            node.p2ps[0].send_txs_and_test(orphan_tx_pool, node, success=False)

        self.log.info('Test orphan with rejected parents')
        rejected_parent = CTransaction()
        rejected_parent.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.sha256, 0)))
        rejected_parent.vout.append(
            CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        rejected_parent.rehash()
        with node.assert_debug_log([
                'not keeping orphan with rejected parents {}'.format(
                    rejected_parent.hash)
        ]):
            node.p2ps[0].send_txs_and_test([rejected_parent],
                                           node,
                                           success=False)

        self.log.info(
            'Test that a peer disconnection causes erase its transactions from the orphan pool'
        )
        with node.assert_debug_log(['Erased 100 orphan tx from peer=25']):
            self.reconnect_p2p(num_connections=1)

        self.log.info(
            'Test that a transaction in the orphan pool is included in a new tip block causes erase this transaction from the orphan pool'
        )
        tx_withhold_until_block_A = CTransaction()
        tx_withhold_until_block_A.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold.sha256, 1)))
        tx_withhold_until_block_A.vout = [
            CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
        ] * 2
        tx_withhold_until_block_A.calc_sha256()

        tx_orphan_include_by_block_A = CTransaction()
        tx_orphan_include_by_block_A.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 0)))
        tx_orphan_include_by_block_A.vout.append(
            CTxOut(nValue=12 * COIN - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_include_by_block_A.calc_sha256()

        self.log.info('Send the orphan ... ')
        node.p2ps[0].send_txs_and_test([tx_orphan_include_by_block_A],
                                       node,
                                       success=False)

        tip = int(node.getbestblockhash(), 16)
        height = node.getblockcount() + 1
        block_A = create_block(tip, create_coinbase(height))
        block_A.vtx.extend([
            tx_withhold, tx_withhold_until_block_A,
            tx_orphan_include_by_block_A
        ])
        block_A.hashMerkleRoot = block_A.calc_merkle_root()
        block_A.solve()

        self.log.info('Send the block that includes the previous orphan ... ')
        with node.assert_debug_log(
            ["Erased 1 orphan tx included or conflicted by block"]):
            node.p2ps[0].send_blocks_and_test([block_A], node, success=True)

        self.log.info(
            'Test that a transaction in the orphan pool conflicts with a new tip block causes erase this transaction from the orphan pool'
        )
        tx_withhold_until_block_B = CTransaction()
        tx_withhold_until_block_B.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold_until_block_A.sha256, 1)))
        tx_withhold_until_block_B.vout.append(
            CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_withhold_until_block_B.calc_sha256()

        tx_orphan_include_by_block_B = CTransaction()
        tx_orphan_include_by_block_B.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
        tx_orphan_include_by_block_B.vout.append(
            CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_include_by_block_B.calc_sha256()

        tx_orphan_conflict_by_block_B = CTransaction()
        tx_orphan_conflict_by_block_B.vin.append(
            CTxIn(outpoint=COutPoint(tx_withhold_until_block_B.sha256, 0)))
        tx_orphan_conflict_by_block_B.vout.append(
            CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_conflict_by_block_B.calc_sha256()
        self.log.info('Send the orphan ... ')
        node.p2ps[0].send_txs_and_test([tx_orphan_conflict_by_block_B],
                                       node,
                                       success=False)

        tip = int(node.getbestblockhash(), 16)
        height = node.getblockcount() + 1
        block_B = create_block(tip, create_coinbase(height))
        block_B.vtx.extend(
            [tx_withhold_until_block_B, tx_orphan_include_by_block_B])
        block_B.hashMerkleRoot = block_B.calc_merkle_root()
        block_B.solve()

        self.log.info(
            'Send the block that includes a transaction which conflicts with the previous orphan ... '
        )
        with node.assert_debug_log(
            ["Erased 1 orphan tx included or conflicted by block"]):
            node.p2ps[0].send_blocks_and_test([block_B], node, success=True)
示例#2
0
    def run_test(self):
        node = self.nodes[0]  # convenience reference to the node

        self.bootstrap_p2p()  # Add one p2p connection to the node

        best_block = self.nodes[0].getbestblockhash()
        tip = int(best_block, 16)
        best_block_time = self.nodes[0].getblock(best_block)['time']
        block_time = best_block_time + 1

        self.log.info("Create a new block with an anyone-can-spend coinbase.")
        height = 1
        block = create_block(tip, create_coinbase(height), block_time)
        block.solve()
        # Save the coinbase for later
        block1 = block
        tip = block.sha256
        node.p2p.send_blocks_and_test([block], node, success=True)

        self.log.info("Mature the block.")
        self.nodes[0].generate(100)

        # Iterate through a list of known invalid transaction types, ensuring each is
        # rejected. Some are consensus invalid and some just violate policy.
        for BadTxTemplate in invalid_txs.iter_all_templates():
            self.log.info("Testing invalid transaction: %s", BadTxTemplate.__name__)
            template = BadTxTemplate(spend_block=block1)
            tx = template.get_tx()
            node.p2p.send_txs_and_test(
                [tx], node, success=False,
                expect_disconnect=template.expect_disconnect,
                reject_reason=template.reject_reason,
            )

            if template.expect_disconnect:
                self.log.info("Reconnecting to peer")
                self.reconnect_p2p()

        # Make two p2p connections to provide the node with orphans
        # * p2ps[0] will send valid orphan txs (one with low fee)
        # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
        self.reconnect_p2p(num_connections=2)

        self.log.info('Test orphan transaction handling ... ')
        # Create a root transaction that we withhold until all dependent transactions
        # are sent out and in the orphan cache
        SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51'
        tx_withhold = CTransaction()
        tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0)))
        tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_withhold.calc_sha256()

        # Our first orphan tx with some outputs to create further orphan txs
        tx_orphan_1 = CTransaction()
        tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0)))
        tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
        tx_orphan_1.calc_sha256()

        # A valid transaction with low fee
        tx_orphan_2_no_fee = CTransaction()
        tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0)))
        tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        # A valid transaction with sufficient fee
        tx_orphan_2_valid = CTransaction()
        tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1)))
        tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_2_valid.calc_sha256()

        # An invalid transaction with negative fee
        tx_orphan_2_invalid = CTransaction()
        tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2)))
        tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))

        self.log.info('Send the orphans ... ')
        # Send valid orphan txs from p2ps[0]
        node.p2p.send_txs_and_test([tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid], node, success=False)
        # Send invalid tx from p2ps[1]
        node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid], node, success=False)

        assert_equal(0, node.getmempoolinfo()['size'])  # Mempool should be empty
        assert_equal(2, len(node.getpeerinfo()))  # p2ps[1] is still connected

        self.log.info('Send the withhold tx ... ')
        with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
            node.p2p.send_txs_and_test([tx_withhold], node, success=True)

        # Transactions that should end up in the mempool
        expected_mempool = {
            t.hash
            for t in [
                tx_withhold,  # The transaction that is the root for all orphans
                tx_orphan_1,  # The orphan transaction that splits the coins
                tx_orphan_2_valid,  # The valid transaction (with sufficient fee)
            ]
        }
        # Transactions that do not end up in the mempool
        # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
        # tx_orphan_invaid, because it has negative fee (p2ps[1] is disconnected for relaying that tx)

        wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12)  # p2ps[1] is no longer connected
        assert_equal(expected_mempool, set(node.getrawmempool()))
示例#3
0
    def run_test(self):
        node = self.nodes[0]  # convenience reference to the node

        self.bootstrap_p2p()  # Add one p2p connection to the node

        best_block = self.nodes[0].getbestblockhash()
        tip = int(best_block, 16)
        best_block_time = self.nodes[0].getblock(best_block)['time']
        block_time = best_block_time + 1

        self.log.info("Create a new block with an anyone-can-spend coinbase.")
        height = 1
        blocks = []
        for _ in invalid_txs.iter_all_templates():
            block = create_block(tip, create_coinbase(height), block_time)
            block.nHeight = height
            prepare_block(block)
            block_time = block.nTime + 1
            height += 1
            # Save the coinbase for later
            blocks.append(block)
            tip = block.sha256
            node.p2p.send_blocks_and_test([block], node, success=True)

        self.log.info("Mature the blocks.")
        self.nodes[0].generatetoaddress(
            100, self.nodes[0].get_deterministic_priv_key().address)

        # Iterate through a list of known invalid transaction types, ensuring each is
        # rejected. Some are consensus invalid and some just violate policy.
        setup_txs = []
        for block, BadTxTemplate in zip(blocks,
                                        invalid_txs.iter_all_templates()):
            self.log.info("Testing invalid transaction: %s",
                          BadTxTemplate.__name__)
            template = BadTxTemplate(spend_block=block)
            setup_tx = template.get_setup_tx()
            if setup_tx is not None:
                node.p2p.send_txs_and_test([setup_tx], node)
                setup_txs.append(setup_tx)
                tx = template.get_tx(setup_tx)
            else:
                tx = template.get_tx()
            node.p2p.send_txs_and_test(
                [tx],
                node,
                success=False,
                expect_disconnect=template.expect_disconnect,
                reject_reason=template.reject_reason,
            )

            if template.expect_disconnect:
                self.log.info("Reconnecting to peer")
                self.reconnect_p2p()

        # Make two p2p connections to provide the node with orphans
        # * p2ps[0] will send valid orphan txs (one with low fee)
        # * p2ps[1] will send an invalid orphan tx (and is later disconnected for that)
        self.reconnect_p2p(num_connections=2)

        self.log.info('Test orphan transaction handling ... ')
        # Create a root transaction that we withold until all dependend transactions
        # are sent out and in the orphan cache
        SCRIPT_PUB_KEY_OP_TRUE = CScript([OP_TRUE])
        tx_withhold = CTransaction()
        tx_withhold.vin.append(
            CTxIn(outpoint=COutPoint(blocks[0].vtx[0].txid, 1)))
        tx_withhold.vout.append(
            CTxOut(nValue=int(SUBSIDY * COIN) - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        pad_tx(tx_withhold)
        tx_withhold.calc_txid()

        # Our first orphan tx with some outputs to create further orphan txs
        tx_orphan_1 = CTransaction()
        tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.txid, 0)))
        tx_orphan_1.vout = [
            CTxOut(nValue=int(0.1 * COIN), scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
        ] * 3
        pad_tx(tx_orphan_1)
        tx_orphan_1.calc_txid()

        # A valid transaction with low fee
        tx_orphan_2_no_fee = CTransaction()
        tx_orphan_2_no_fee.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 0)))
        tx_orphan_2_no_fee.vout.append(
            CTxOut(nValue=int(0.1 * COIN),
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        pad_tx(tx_orphan_2_no_fee)

        # A valid transaction with sufficient fee
        tx_orphan_2_valid = CTransaction()
        tx_orphan_2_valid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 1)))
        tx_orphan_2_valid.vout.append(
            CTxOut(nValue=int(0.1 * COIN) - 12000,
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        tx_orphan_2_valid.calc_txid()
        pad_tx(tx_orphan_2_valid)

        # An invalid transaction with negative fee
        tx_orphan_2_invalid = CTransaction()
        tx_orphan_2_invalid.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_1.txid, 2)))
        tx_orphan_2_invalid.vout.append(
            CTxOut(nValue=int(1.1 * COIN),
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        pad_tx(tx_orphan_2_invalid)
        tx_orphan_2_invalid.calc_txid()

        self.log.info('Send the orphans ... ')
        # Send valid orphan txs from p2ps[0]
        node.p2p.send_txs_and_test(
            [tx_orphan_1, tx_orphan_2_no_fee, tx_orphan_2_valid],
            node,
            success=False)
        # Send invalid tx from p2ps[1]
        node.p2ps[1].send_txs_and_test([tx_orphan_2_invalid],
                                       node,
                                       success=False)

        # Mempool should only have setup txs
        assert_equal(len(setup_txs), node.getmempoolinfo()['size'])
        # p2ps[1] is still connected
        assert_equal(2, len(node.getpeerinfo()))

        self.log.info('Send the withhold tx ... ')
        with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]):
            node.p2p.send_txs_and_test([tx_withhold], node, success=True)

        # Transactions that should end up in the mempool
        expected_mempool = {
            t.txid_hex
            for t in [
                tx_withhold,  # The transaction that is the root for all orphans
                tx_orphan_1,  # The orphan transaction that splits the coins
                # The valid transaction (with sufficient fee)
                tx_orphan_2_valid,
            ] + setup_txs  # The setup transactions we added in the beginning
        }
        # Transactions that do not end up in the mempool
        # tx_orphan_no_fee, because it has too low fee (p2ps[0] is not disconnected for relaying that tx)
        # tx_orphan_invaid, because it has negative fee (p2ps[1] is
        # disconnected for relaying that tx)

        # p2ps[1] is no longer connected
        wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12)
        assert_equal(expected_mempool, set(node.getrawmempool()))

        self.log.info('Test orphan pool overflow')
        orphan_tx_pool = [CTransaction() for _ in range(101)]
        for i in range(len(orphan_tx_pool)):
            orphan_tx_pool[i].vin.append(CTxIn(outpoint=COutPoint(i, 333)))
            orphan_tx_pool[i].vout.append(
                CTxOut(nValue=int(1.1 * COIN),
                       scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
            pad_tx(orphan_tx_pool[i])

        with node.assert_debug_log(['mapOrphan overflow, removed 1 tx']):
            node.p2p.send_txs_and_test(orphan_tx_pool, node, success=False)

        rejected_parent = CTransaction()
        rejected_parent.vin.append(
            CTxIn(outpoint=COutPoint(tx_orphan_2_invalid.txid, 0)))
        rejected_parent.vout.append(
            CTxOut(nValue=int(1.1 * COIN),
                   scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
        pad_tx(rejected_parent)
        rejected_parent.rehash()
        with node.assert_debug_log([
                'not keeping orphan with rejected parents {}'.format(
                    rejected_parent.txid_hex)
        ]):
            node.p2p.send_txs_and_test([rejected_parent], node, success=False)