def test_anc_size_limits(self):
        """Test Case with 2 independent transactions in the mempool and a parent + child in the
        package, where the package parent is the child of both mempool transactions (30KvB each):
              A     B
               ^   ^
                 C
                 ^
                 D
        The lowest descendant, D, exceeds ancestor size limits, but only if the in-mempool
        and in-package ancestors are all considered together.
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        parents_tx = []
        values = []
        scripts = []
        target_weight = WITNESS_SCALE_FACTOR * 1000 * 30 # 30KvB
        high_fee = Decimal("0.003") # 10 sats/vB
        self.log.info("Check that in-mempool and in-package ancestor size limits are calculated properly in packages")
        # Mempool transactions A and B
        for _ in range(2):
            spk = None
            top_coin = self.coins.pop()
            txid = top_coin["txid"]
            value = top_coin["amount"]
            (tx, _, _, _) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk, high_fee)
            bulked_tx = bulk_transaction(tx, node, target_weight, self.privkeys)
            node.sendrawtransaction(bulked_tx.serialize().hex())
            parents_tx.append(bulked_tx)
            values.append(Decimal(bulked_tx.vout[0].nValue) / COIN)
            scripts.append(bulked_tx.vout[0].scriptPubKey.hex())

        # Package transaction C
        small_pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts, high_fee)
        pc_tx = bulk_transaction(tx_from_hex(small_pc_hex), node, target_weight, self.privkeys)
        pc_value = Decimal(pc_tx.vout[0].nValue) / COIN
        pc_spk = pc_tx.vout[0].scriptPubKey.hex()
        pc_hex = pc_tx.serialize().hex()

        # Package transaction D
        (small_pd, _, val, spk) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk, high_fee)
        prevtxs = [{
            "txid": pc_tx.rehash(),
            "vout": 0,
            "scriptPubKey": spk,
            "amount": val,
        }]
        pd_tx = bulk_transaction(small_pd, node, target_weight, self.privkeys, prevtxs)
        pd_hex = pd_tx.serialize().hex()

        assert_equal(2, node.getmempoolinfo()["size"])
        testres_too_heavy = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
        for txres in testres_too_heavy:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        node.generate(1)
        assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])])
    def test_anc_count_limits_2(self):
        """Create a 'Y' shaped chain with 24 transactions in the mempool and 2 in the package:
        M1a                M1b
         ^                ^
          M2a            M2b
           .            .
            .          .
             .        .
            M12a    M12b
               ^    ^
                 Pc
                 ^
                 Pd
        The lowest descendant, Pd, exceeds ancestor limits, but only if the in-mempool
        and in-package ancestors are all considered together.
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        parents_tx = []
        values = []
        scripts = []

        self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages")
        # Two chains of 12 transactions each
        for _ in range(2):
            spk = None
            top_coin = self.coins.pop()
            txid = top_coin["txid"]
            value = top_coin["amount"]
            for i in range(12):
                (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
                txid = tx.rehash()
                value -= Decimal("0.0001")
                node.sendrawtransaction(txhex)
                if i == 11:
                    # last 2 transactions will be the parents of Pc
                    parents_tx.append(tx)
                    values.append(value)
                    scripts.append(spk)

        # Child Pc
        pc_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts)
        pc_tx = tx_from_hex(pc_hex)
        pc_value = sum(values) - Decimal("0.0002")
        pc_spk = pc_tx.vout[0].scriptPubKey.hex()

        # Child Pd
        (_, pd_hex, _, _) = make_chain(node, self.address, self.privkeys, pc_tx.rehash(), pc_value, 0, pc_spk)

        assert_equal(24, node.getmempoolinfo()["size"])
        testres_too_long = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
        for txres in testres_too_long:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        node.generate(1)
        assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])])
    def test_submit_cpfp(self):
        node = self.nodes[0]
        peer = node.add_p2p_connection(P2PTxInvStore())

        # 2 parent 1 child CPFP. First parent pays high fees, second parent pays 0 fees and is
        # fee-bumped by the child.
        coin_rich = self.coins.pop()
        coin_poor = self.coins.pop()
        tx_rich, hex_rich, value_rich, spk_rich = make_chain(
            node, self.address, self.privkeys, coin_rich["txid"],
            coin_rich["amount"])
        tx_poor, hex_poor, value_poor, spk_poor = make_chain(
            node,
            self.address,
            self.privkeys,
            coin_poor["txid"],
            coin_poor["amount"],
            fee=0)
        package_txns = [tx_rich, tx_poor]
        hex_child = create_child_with_parents(node, self.address,
                                              self.privkeys, package_txns,
                                              [value_rich, value_poor],
                                              [spk_rich, spk_poor])
        tx_child = tx_from_hex(hex_child)
        package_txns.append(tx_child)

        submitpackage_result = node.submitpackage(
            [hex_rich, hex_poor, hex_child])

        rich_parent_result = submitpackage_result["tx-results"][
            tx_rich.getwtxid()]
        poor_parent_result = submitpackage_result["tx-results"][
            tx_poor.getwtxid()]
        child_result = submitpackage_result["tx-results"][tx_child.getwtxid()]
        assert_equal(rich_parent_result["fees"]["base"], DEFAULT_FEE)
        assert_equal(poor_parent_result["fees"]["base"], 0)
        assert_equal(child_result["fees"]["base"], DEFAULT_FEE)
        # Package feerate is calculated for the remaining transactions after deduplication and
        # individual submission. Since this package had a 0-fee parent, package feerate must have
        # been used and returned.
        assert "package-feerate" in submitpackage_result
        assert_fee_amount(DEFAULT_FEE,
                          rich_parent_result["vsize"] + child_result["vsize"],
                          submitpackage_result["package-feerate"])

        # The node will broadcast each transaction, still abiding by its peer's fee filter
        peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns])
        self.generate(node, 1)
Exemple #4
0
    def test_multiple_children(self):
        node = self.nodes[0]

        self.log.info(
            "Testmempoolaccept a package in which a transaction has two children within the package"
        )
        first_coin = self.coins.pop()
        value = (first_coin["amount"] - Decimal("0.0002")
                 ) / 2  # Deduct reasonable fee and make 2 outputs
        inputs = [{"txid": first_coin["txid"], "vout": 0}]
        outputs = [{self.address: value}, {ADDRESS_BCRT1_P2WSH_OP_TRUE: value}]
        rawtx = node.createrawtransaction(inputs, outputs)

        parent_signed = node.signrawtransactionwithkey(hexstring=rawtx,
                                                       privkeys=self.privkeys)
        assert parent_signed["complete"]
        parent_tx = tx_from_hex(parent_signed["hex"])
        parent_txid = parent_tx.rehash()
        assert node.testmempoolaccept([parent_signed["hex"]])[0]["allowed"]

        parent_locking_script_a = parent_tx.vout[0].scriptPubKey.hex()
        child_value = value - Decimal("0.0001")

        # Child A
        (_, tx_child_a_hex, _,
         _) = make_chain(node, self.address, self.privkeys, parent_txid,
                         child_value, 0, parent_locking_script_a)
        assert not node.testmempoolaccept([tx_child_a_hex])[0]["allowed"]

        # Child B
        rawtx_b = node.createrawtransaction([{
            "txid": parent_txid,
            "vout": 1
        }], {self.address: child_value})
        tx_child_b = tx_from_hex(rawtx_b)
        tx_child_b.wit.vtxinwit = [CTxInWitness()]
        tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
        tx_child_b_hex = tx_child_b.serialize().hex()
        assert not node.testmempoolaccept([tx_child_b_hex])[0]["allowed"]

        self.log.info(
            "Testmempoolaccept with entire package, should work with children in either order"
        )
        testres_multiple_ab = node.testmempoolaccept(
            rawtxs=[parent_signed["hex"], tx_child_a_hex, tx_child_b_hex])
        testres_multiple_ba = node.testmempoolaccept(
            rawtxs=[parent_signed["hex"], tx_child_b_hex, tx_child_a_hex])
        assert all([
            testres["allowed"]
            for testres in testres_multiple_ab + testres_multiple_ba
        ])

        testres_single = []
        # Test accept and then submit each one individually, which should be identical to package testaccept
        for rawtx in [parent_signed["hex"], tx_child_a_hex, tx_child_b_hex]:
            testres = node.testmempoolaccept([rawtx])
            testres_single.append(testres[0])
            # Submit the transaction now so its child should have no problem validating
            node.sendrawtransaction(rawtx)
        assert_equal(testres_single, testres_multiple_ab)
    def test_chain_limits_helper(self, mempool_count, package_count):
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        first_coin = self.coins.pop()
        spk = None
        txid = first_coin["txid"]
        chain_hex = []
        chain_txns = []
        value = first_coin["amount"]

        for i in range(mempool_count + package_count):
            (tx, txhex, value, spk) = make_chain(node, self.address,
                                                 self.privkeys, txid, value, 0,
                                                 spk)
            txid = tx.rehash()
            if i < mempool_count:
                node.sendrawtransaction(txhex)
                assert_equal(
                    node.getmempoolentry(txid)["ancestorcount"], i + 1)
            else:
                chain_hex.append(txhex)
                chain_txns.append(tx)
        testres_too_long = node.testmempoolaccept(rawtxs=chain_hex)
        for txres in testres_too_long:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        self.generate(node, 1)
        assert all([
            res["allowed"] for res in node.testmempoolaccept(rawtxs=chain_hex)
        ])
Exemple #6
0
    def test_multiple_parents(self):
        node = self.nodes[0]

        self.log.info("Testmempoolaccept a package in which a transaction has multiple parents within the package")
        for num_parents in [2, 10, 24]:
            # Test a package with num_parents parents and 1 child transaction.
            package_hex = []
            parents_tx = []
            values = []
            parent_locking_scripts = []
            for _ in range(num_parents):
                parent_coin = self.coins.pop()
                value = parent_coin["amount"]
                (tx, txhex, value, parent_locking_script) = make_chain(node, self.address, self.privkeys, parent_coin["txid"], value)
                package_hex.append(txhex)
                parents_tx.append(tx)
                values.append(value)
                parent_locking_scripts.append(parent_locking_script)
            child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, parent_locking_scripts)
            # Package accept should work with the parents in any order (as long as parents come before child)
            for _ in range(10):
                random.shuffle(package_hex)
                testres_multiple = node.testmempoolaccept(rawtxs=package_hex + [child_hex])
                assert all([testres["allowed"] for testres in testres_multiple])

            testres_single = []
            # Test accept and then submit each one individually, which should be identical to package testaccept
            for rawtx in package_hex + [child_hex]:
                testres_single.append(node.testmempoolaccept([rawtx])[0])
                # Submit the transaction now so its child should have no problem validating
                node.sendrawtransaction(rawtx)
            assert_equal(testres_single, testres_multiple)
    def test_anc_count_limits_bushy(self):
        """Create a tree with 20 transactions in the mempool and 6 in the package:
        M1...M4 M5...M8 M9...M12 M13...M16 M17...M20
            ^      ^       ^        ^         ^             (each with 4 parents)
            P0     P1      P2      P3        P4
             ^     ^       ^       ^         ^              (5 parents)
                           PC
        Where M(4i+1)...M+(4i+4) are the parents of Pi and P0, P1, P2, P3, and P4 are the parents of PC.
        P0... P4 individually only have 4 parents each, and PC has no in-mempool parents. But
        combined, PC has 25 in-mempool and in-package parents.
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        package_hex = []
        parent_txns = []
        parent_values = []
        scripts = []
        for _ in range(5):  # Make package transactions P0 ... P4
            gp_tx = []
            gp_values = []
            gp_scripts = []
            for _ in range(4):  # Make mempool transactions M(4i+1)...M(4i+4)
                parent_coin = self.coins.pop()
                value = parent_coin["amount"]
                txid = parent_coin["txid"]
                (tx, txhex, value, spk) = make_chain(node, self.address,
                                                     self.privkeys, txid,
                                                     value)
                gp_tx.append(tx)
                gp_values.append(value)
                gp_scripts.append(spk)
                node.sendrawtransaction(txhex)
            # Package transaction Pi
            pi_hex = create_child_with_parents(node, self.address,
                                               self.privkeys, gp_tx, gp_values,
                                               gp_scripts)
            package_hex.append(pi_hex)
            pi_tx = tx_from_hex(pi_hex)
            parent_txns.append(pi_tx)
            parent_values.append(Decimal(pi_tx.vout[0].nValue) / COIN)
            scripts.append(pi_tx.vout[0].scriptPubKey.hex())
        # Package transaction PC
        package_hex.append(
            create_child_with_parents(node, self.address, self.privkeys,
                                      parent_txns, parent_values, scripts))

        assert_equal(20, node.getmempoolinfo()["size"])
        assert_equal(6, len(package_hex))
        testres = node.testmempoolaccept(rawtxs=package_hex)
        for txres in testres:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        self.generate(node, 1)
        assert all([
            res["allowed"]
            for res in node.testmempoolaccept(rawtxs=package_hex)
        ])
    def test_submit_child_with_parents(self, num_parents, partial_submit):
        node = self.nodes[0]
        peer = node.add_p2p_connection(P2PTxInvStore())
        # Test a package with num_parents parents and 1 child transaction.
        package_hex = []
        package_txns = []
        values = []
        scripts = []
        for _ in range(num_parents):
            parent_coin = self.coins.pop()
            value = parent_coin["amount"]
            (tx, txhex, value, spk) = make_chain(node, self.address,
                                                 self.privkeys,
                                                 parent_coin["txid"], value)
            package_hex.append(txhex)
            package_txns.append(tx)
            values.append(value)
            scripts.append(spk)
            if partial_submit and random.choice([True, False]):
                node.sendrawtransaction(txhex)
        child_hex = create_child_with_parents(node, self.address,
                                              self.privkeys, package_txns,
                                              values, scripts)
        package_hex.append(child_hex)
        package_txns.append(tx_from_hex(child_hex))

        testmempoolaccept_result = node.testmempoolaccept(rawtxs=package_hex)
        submitpackage_result = node.submitpackage(package=package_hex)

        # Check that each result is present, with the correct size and fees
        for i in range(num_parents + 1):
            tx = package_txns[i]
            wtxid = tx.getwtxid()
            assert wtxid in submitpackage_result["tx-results"]
            tx_result = submitpackage_result["tx-results"][wtxid]
            assert_equal(
                tx_result, {
                    "txid": tx.rehash(),
                    "vsize": tx.get_vsize(),
                    "fees": {
                        "base": DEFAULT_FEE,
                    }
                })

        # submitpackage result should be consistent with testmempoolaccept and getmempoolentry
        self.assert_equal_package_results(node, testmempoolaccept_result,
                                          submitpackage_result)

        # Package feerate is calculated for the remaining transactions after deduplication and
        # individual submission. If only 0 or 1 transaction is left, e.g. because all transactions
        # had high-feerates or were already in the mempool, no package feerate is provided.
        # In this case, since all of the parents have high fees, each is accepted individually.
        assert "package-feerate" not in submitpackage_result

        # The node should announce each transaction. No guarantees for propagation.
        peer.wait_for_broadcast([tx.getwtxid() for tx in package_txns])
        self.generate(node, 1)
    def test_anc_count_limits(self):
        """Create a 'V' shaped chain with 24 transactions in the mempool and 3 in the package:
        M1a                    M1b
         ^                     ^
          M2a                M2b
           .                 .
            .               .
             .             .
             M12a        M12b
               ^         ^
                Pa     Pb
                 ^    ^
                   Pc
        The lowest descendant, Pc, exceeds ancestor limits, but only if the in-mempool
        and in-package ancestors are all considered together.
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        package_hex = []
        parents_tx = []
        values = []
        scripts = []

        self.log.info("Check that in-mempool and in-package ancestors are calculated properly in packages")

        # Two chains of 13 transactions each
        for _ in range(2):
            spk = None
            top_coin = self.coins.pop()
            txid = top_coin["txid"]
            value = top_coin["amount"]
            for i in range(13):
                (tx, txhex, value, spk) = make_chain(node, self.address, self.privkeys, txid, value, 0, spk)
                txid = tx.rehash()
                if i < 12:
                    node.sendrawtransaction(txhex)
                else: # Save the 13th transaction for the package
                    package_hex.append(txhex)
                    parents_tx.append(tx)
                    scripts.append(spk)
                    values.append(value)

        # Child Pc
        child_hex = create_child_with_parents(node, self.address, self.privkeys, parents_tx, values, scripts)
        package_hex.append(child_hex)

        assert_equal(24, node.getmempoolinfo()["size"])
        assert_equal(3, len(package_hex))
        testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
        for txres in testres_too_long:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        node.generate(1)
        assert all([res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex)])
    def test_desc_size_limits(self):
        """Create 3 mempool transactions and 2 package transactions (25KvB each):
              Ma
             ^ ^
            Mb  Mc
           ^     ^
          Pd      Pe
        The top ancestor in the package exceeds descendant size limits but only if the in-mempool
        and in-package descendants are all considered together.
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        target_weight = 21 * 1000 * WITNESS_SCALE_FACTOR
        high_fee = Decimal("0.0021")  # 10 sats/vB
        self.log.info(
            "Check that in-mempool and in-package descendant sizes are calculated properly in packages"
        )
        # Top parent in mempool, Ma
        first_coin = self.coins.pop()
        parent_value = (first_coin["amount"] -
                        high_fee) / 2  # Deduct fee and make 2 outputs
        inputs = [{"txid": first_coin["txid"], "vout": 0}]
        outputs = [{
            self.address: parent_value
        }, {
            ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value
        }]
        rawtx = node.createrawtransaction(inputs, outputs)
        parent_tx = bulk_transaction(tx_from_hex(rawtx), node, target_weight,
                                     self.privkeys)
        node.sendrawtransaction(parent_tx.serialize().hex())

        package_hex = []
        for j in range(2):  # Two legs (left and right)
            # Mempool transaction (Mb and Mc)
            mempool_tx = CTransaction()
            spk = parent_tx.vout[j].scriptPubKey.hex()
            value = Decimal(parent_tx.vout[j].nValue) / COIN
            txid = parent_tx.rehash()
            prevtxs = [{
                "txid": txid,
                "vout": j,
                "scriptPubKey": spk,
                "amount": value,
            }]
            if j == 0:  # normal key
                (tx_small, _, _, _) = make_chain(node, self.address,
                                                 self.privkeys, txid, value, j,
                                                 spk, high_fee)
                mempool_tx = bulk_transaction(tx_small, node, target_weight,
                                              self.privkeys, prevtxs)
            else:  # OP_TRUE
                inputs = [{"txid": txid, "vout": 1}]
                outputs = {self.address: value - high_fee}
                small_tx = tx_from_hex(
                    node.createrawtransaction(inputs, outputs))
                mempool_tx = bulk_transaction(small_tx, node, target_weight,
                                              None, prevtxs)
            node.sendrawtransaction(mempool_tx.serialize().hex())

            # Package transaction (Pd and Pe)
            spk = mempool_tx.vout[0].scriptPubKey.hex()
            value = Decimal(mempool_tx.vout[0].nValue) / COIN
            txid = mempool_tx.rehash()
            (tx_small, _, _, _) = make_chain(node, self.address, self.privkeys,
                                             txid, value, 0, spk, high_fee)
            prevtxs = [{
                "txid": txid,
                "vout": 0,
                "scriptPubKey": spk,
                "amount": value,
            }]
            package_tx = bulk_transaction(tx_small, node, target_weight,
                                          self.privkeys, prevtxs)
            package_hex.append(package_tx.serialize().hex())

        assert_equal(3, node.getmempoolinfo()["size"])
        assert_equal(2, len(package_hex))
        testres_too_heavy = node.testmempoolaccept(rawtxs=package_hex)
        for txres in testres_too_heavy:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        self.generate(node, 1)
        assert all([
            res["allowed"]
            for res in node.testmempoolaccept(rawtxs=package_hex)
        ])
    def test_desc_count_limits_2(self):
        """Create a Package with 24 transaction in mempool and 2 transaction in package:
                      M1
                     ^  ^
                   M2    ^
                   .      ^
                  .        ^
                 .          ^
                M24          ^
                              ^
                              P1
                              ^
                              P2
        P1 has M1 as a mempool ancestor, P2 has no in-mempool ancestors, but when
        combined P2 has M1 as an ancestor and M1 exceeds descendant_limits(23 in-mempool
        descendants + 2 in-package descendants, a total of 26 including itself).
        """

        node = self.nodes[0]
        package_hex = []
        # M1
        first_coin_a = self.coins.pop()
        parent_value = (first_coin_a["amount"] - DEFAULT_FEE
                        ) / 2  # Deduct reasonable fee and make 2 outputs
        inputs = [{"txid": first_coin_a["txid"], "vout": 0}]
        outputs = [{
            self.address: parent_value
        }, {
            ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value
        }]
        rawtx = node.createrawtransaction(inputs, outputs)

        parent_signed = node.signrawtransactionwithkey(hexstring=rawtx,
                                                       privkeys=self.privkeys)
        assert parent_signed["complete"]
        parent_tx = tx_from_hex(parent_signed["hex"])
        parent_txid = parent_tx.rehash()
        node.sendrawtransaction(parent_signed["hex"])

        # Chain M2...M24
        spk = parent_tx.vout[0].scriptPubKey.hex()
        value = parent_value
        txid = parent_txid
        for i in range(23):  # M2...M24
            (tx, txhex, value, spk) = make_chain(node, self.address,
                                                 self.privkeys, txid, value, 0,
                                                 spk)
            txid = tx.rehash()
            node.sendrawtransaction(txhex)

        # P1
        value_p1 = (parent_value - DEFAULT_FEE)
        rawtx_p1 = node.createrawtransaction([{
            "txid": parent_txid,
            "vout": 1
        }], [{
            self.address: value_p1
        }])
        tx_child_p1 = tx_from_hex(rawtx_p1)
        tx_child_p1.wit.vtxinwit = [CTxInWitness()]
        tx_child_p1.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
        tx_child_p1_hex = tx_child_p1.serialize().hex()
        txid_child_p1 = tx_child_p1.rehash()
        package_hex.append(tx_child_p1_hex)
        tx_child_p1_spk = tx_child_p1.vout[0].scriptPubKey.hex()

        # P2
        (_, tx_child_p2_hex, _, _) = make_chain(node, self.address,
                                                self.privkeys, txid_child_p1,
                                                value_p1, 0, tx_child_p1_spk)
        package_hex.append(tx_child_p2_hex)

        assert_equal(24, node.getmempoolinfo()["size"])
        assert_equal(2, len(package_hex))
        testres = node.testmempoolaccept(rawtxs=package_hex)
        assert_equal(len(testres), len(package_hex))
        for txres in testres:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        self.generate(node, 1)
        assert all([
            res["allowed"]
            for res in node.testmempoolaccept(rawtxs=package_hex)
        ])
    def test_desc_count_limits(self):
        """Create an 'A' shaped package with 24 transactions in the mempool and 2 in the package:
                    M1
                   ^  ^
                 M2a  M2b
                .       .
               .         .
              .           .
             M12a          ^
            ^              M13b
           ^                 ^
          Pa                  Pb
        The top ancestor in the package exceeds descendant limits but only if the in-mempool and in-package
        descendants are all considered together (24 including in-mempool descendants and 26 including both
        package transactions).
        """
        node = self.nodes[0]
        assert_equal(0, node.getmempoolinfo()["size"])
        self.log.info(
            "Check that in-mempool and in-package descendants are calculated properly in packages"
        )
        # Top parent in mempool, M1
        first_coin = self.coins.pop()
        parent_value = (first_coin["amount"] - Decimal("0.0002")
                        ) / 2  # Deduct reasonable fee and make 2 outputs
        inputs = [{"txid": first_coin["txid"], "vout": 0}]
        outputs = [{
            self.address: parent_value
        }, {
            ADDRESS_BCRT1_P2WSH_OP_TRUE: parent_value
        }]
        rawtx = node.createrawtransaction(inputs, outputs)

        parent_signed = node.signrawtransactionwithkey(hexstring=rawtx,
                                                       privkeys=self.privkeys)
        assert parent_signed["complete"]
        parent_tx = tx_from_hex(parent_signed["hex"])
        parent_txid = parent_tx.rehash()
        node.sendrawtransaction(parent_signed["hex"])

        package_hex = []

        # Chain A
        spk = parent_tx.vout[0].scriptPubKey.hex()
        value = parent_value
        txid = parent_txid
        for i in range(12):
            (tx, txhex, value, spk) = make_chain(node, self.address,
                                                 self.privkeys, txid, value, 0,
                                                 spk)
            txid = tx.rehash()
            if i < 11:  # M2a... M12a
                node.sendrawtransaction(txhex)
            else:  # Pa
                package_hex.append(txhex)

        # Chain B
        value = parent_value - Decimal("0.0001")
        rawtx_b = node.createrawtransaction([{
            "txid": parent_txid,
            "vout": 1
        }], {self.address: value})
        tx_child_b = tx_from_hex(rawtx_b)  # M2b
        tx_child_b.wit.vtxinwit = [CTxInWitness()]
        tx_child_b.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
        tx_child_b_hex = tx_child_b.serialize().hex()
        node.sendrawtransaction(tx_child_b_hex)
        spk = tx_child_b.vout[0].scriptPubKey.hex()
        txid = tx_child_b.rehash()
        for i in range(12):
            (tx, txhex, value, spk) = make_chain(node, self.address,
                                                 self.privkeys, txid, value, 0,
                                                 spk)
            txid = tx.rehash()
            if i < 11:  # M3b... M13b
                node.sendrawtransaction(txhex)
            else:  # Pb
                package_hex.append(txhex)

        assert_equal(24, node.getmempoolinfo()["size"])
        assert_equal(2, len(package_hex))
        testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
        for txres in testres_too_long:
            assert_equal(txres["package-error"], "package-mempool-limits")

        # Clear mempool and check that the package passes now
        self.generate(node, 1)
        assert all([
            res["allowed"]
            for res in node.testmempoolaccept(rawtxs=package_hex)
        ])