Repository: bitcoin/bitcoin
File: test/functional/mempool_limit.py (Python)

Code before:
        mempoolmin_feerate = current_info["mempoolminfee"]

        package_hex = []
        # UTXOs to be spent by the ultimate child transaction
        parent_utxos = []

        evicted_vsize = 2000
        # Mempool transaction which is evicted due to being at the "bottom" of the mempool when the
        # mempool overflows and evicts by descendant score. It's important that the eviction doesn't
        # happen in the middle of package evaluation, as it can invalidate the coins cache.
        mempool_evicted_tx = self.wallet.send_self_transfer(
            from_node=node,
            fee_rate=mempoolmin_feerate,
            target_vsize=evicted_vsize,
            confirmed_only=True
        )
        # Already in mempool when package is submitted.
        assert mempool_evicted_tx["txid"] in node.getrawmempool()

        # This parent spends the above mempool transaction that exists when its inputs are first
        # looked up, but disappears later. It is rejected for being too low fee (but eligible for
        # reconsideration), and its inputs are cached. When the mempool transaction is evicted, its
        # coin is no longer available, but the cache could still contains the tx.
        cpfp_parent = self.wallet.create_self_transfer(
            utxo_to_spend=mempool_evicted_tx["new_utxo"],
            fee_rate=mempoolmin_feerate - Decimal('0.00001'),
            confirmed_only=True)
        package_hex.append(cpfp_parent["hex"])
        parent_utxos.append(cpfp_parent["new_utxo"])
        assert_equal(node.testmempoolaccept([cpfp_parent["hex"]])[0]["reject-reason"], "mempool min fee not met")

        self.wallet.rescan_utxos()

        # Series of parents that don't need CPFP and are submitted individually. Each one is large and
        # high feerate, which means they should trigger eviction but not be evicted.
        parent_vsize = 25000
        num_big_parents = 3
        # Need to be large enough to trigger eviction
        # (note that the mempool usage of a tx is about three times its vsize)
        assert_greater_than(parent_vsize * num_big_parents * 3, current_info["maxmempool"] - current_info["bytes"])
        parent_feerate = 100 * mempoolmin_feerate

        big_parent_txids = []
        for i in range(num_big_parents):
            parent = self.wallet.create_self_transfer(fee_rate=parent_feerate, target_vsize=parent_vsize, confirmed_only=True)
            parent_utxos.append(parent["new_utxo"])
            package_hex.append(parent["hex"])
            big_parent_txids.append(parent["txid"])
            # There is room for each of these transactions independently
            assert node.testmempoolaccept([parent["hex"]])[0]["allowed"]


Review comment (suggestion):
IIUC shouldn't `usage` be used here instead of `bytes` because the first argument of assert uses memory usage?
https://github.com/bitcoin/bitcoin/blob/master/src/rpc/mempool.cpp#L699-L700

Code after:
        fill_mempool(self, node)
        current_info = node.getmempoolinfo()
        mempoolmin_feerate = current_info["mempoolminfee"]

        mempool_txids = node.getrawmempool()
        mempool_entries = [node.getmempoolentry(entry) for entry in mempool_txids]
        fees_btc_per_kvb = [entry["fees"]["base"] / (Decimal(entry["vsize"]) / 1000) for entry in mempool_entries]
        mempool_entry_minrate = min(fees_btc_per_kvb)
        mempool_entry_minrate = mempool_entry_minrate.quantize(Decimal("0.00000000"))

        # There is a gap, our parents will be minrate, with child bringing up descendant fee sufficiently to avoid
        # eviction even though parents cause eviction on their own
        assert_greater_than(mempool_entry_minrate, mempoolmin_feerate)

        package_hex = []
        # UTXOs to be spent by the ultimate child transaction
        parent_utxos = []

        # Series of parents that don't need CPFP and are submitted individually. Each one is large
        # which means in aggregate they could trigger eviction, but child submission should result
        # in them not being evicted
        parent_vsize = 25000
        num_big_parents = 3
        # Need to be large enough to trigger eviction
        # (note that the mempool usage of a tx is about three times its vsize)
        assert_greater_than(parent_vsize * num_big_parents * 3, current_info["maxmempool"] - current_info["bytes"])

        big_parent_txids = []
        big_parent_wtxids = []
        for i in range(num_big_parents):
            # Last parent is higher feerate causing other parents to possibly
            # be evicted if trimming was allowed, which would cause the package to end up failing
            parent_feerate = mempoolmin_feerate + Decimal("0.00000001") if i == num_big_parents - 1 else mempoolmin_feerate
            parent = self.wallet.create_self_transfer(fee_rate=parent_feerate, target_vsize=parent_vsize, confirmed_only=True)
            parent_utxos.append(parent["new_utxo"])
            package_hex.append(parent["hex"])
            big_parent_txids.append(parent["txid"])
            big_parent_wtxids.append(parent["wtxid"])
            # There is room for each of these transactions independently
            assert node.testmempoolaccept([parent["hex"]])[0]["allowed"]

        # Create a child spending everything with an insane fee, bumping the package above mempool_entry_minrate
        child = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=10000000)
        package_hex.append(child["hex"])

        # Package should be submitted, temporarily exceeding maxmempool, but not evicted.
        package_res = None
        with node.assert_debug_log(expected_msgs=["rolling minimum fee bumped"]):
            package_res = node.submitpackage(package=package_hex, maxfeerate=0)

        assert_equal(package_res["package_msg"], "success")