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

Code before:

    def test_mid_package_eviction(self):
        node = self.nodes[0]
        self.log.info("Check a package where each parent passes the current mempoolminfee but would cause eviction before package submission terminates")

        self.restart_node(0, extra_args=self.extra_args[0])

        # Restarting the node resets mempool minimum feerate
        assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
        assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))

        fill_mempool(self, node)
        current_info = node.getmempoolinfo()
        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

Review comment (suggestion):
For my understanding, can you explain why is there a difference b/w `mempool_entry_minrate ` and `mempoolmin_feerate ` currently as `mempool_entry_minrate ` is calculated based on the current mempool entries that don't include the to be added parents and child?
https://github.com/bitcoin/bitcoin/blob/master/src/rpc/mempool.cpp#L703

Code after:

    def test_mid_package_eviction_success(self):
        node = self.nodes[0]
        self.log.info("Check a package where each parent passes the current mempoolminfee but a parent could be evicted before getting child's descendant feerate")

        # Clear mempool so it can be filled with minrelay txns
        self.restart_node(0, extra_args=self.extra_args[0] + ["-persistmempool=0"])
        assert_equal(node.getrawmempool(), [])

        # Restarting the node resets mempool minimum feerate
        assert_equal(node.getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000'))
        assert_equal(node.getmempoolinfo()['mempoolminfee'], Decimal('0.00001000'))

        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"])