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

Code before:
        # Create a reorg, bringing tx_with_multi_children back into the mempool with a descendant count of 3.
        node.invalidateblock(block_to_disconnect)
        self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
        assert_equal(node.getmempoolentry(tx_with_multi_children["txid"])["descendantcount"], 3)

        # Sibling eviction is not allowed because there are two siblings
        tx_with_sibling3 = self.wallet.create_self_transfer(
            version=3,
            utxo_to_spend=tx_with_multi_children["new_utxos"][2],
            fee_rate=DEFAULT_FEE*50
        )
        expected_error_2siblings = f"TRUC-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
        assert_raises_rpc_error(-26, expected_error_2siblings, node.sendrawtransaction, tx_with_sibling3["hex"])

        # However, an RBF (with conflicting inputs) is possible even if the resulting cluster size exceeds 2
        tx_with_sibling3_rbf = self.wallet.send_self_transfer(
            from_node=node,
            version=3,
            utxo_to_spend=tx_with_multi_children["new_utxos"][0],
            fee_rate=DEFAULT_FEE*50
        )
        self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]])


    def run_test(self):
        self.log.info("Generate blocks to create UTXOs")
        node = self.nodes[0]
        self.wallet = MiniWallet(node)
        self.generate(self.wallet, 120)
        self.test_truc_max_vsize()
        self.test_truc_acceptance()
        self.test_truc_replacement()
        self.test_truc_reorg()
        self.test_nondefault_package_limits()
        self.test_truc_ancestors_package()
        self.test_truc_ancestors_package_and_mempool()
        self.test_sibling_eviction_package()
        self.test_truc_package_inheritance()
        self.test_truc_in_testmempoolaccept()
        self.test_reorg_2child_rbf()
        self.test_truc_sibling_eviction()
        self.test_reorg_sibling_eviction_1p2c()


if __name__ == "__main__":
    MempoolTRUC(__file__).main()


Review comment (suggestion):
In "[test] check bypass of minrelay for various minrelaytxfee settings" d40de30fdf0470a4ec6712aef4d5e8ed40e5ea91


We dont need the cleanup here I think
```suggestion
    def test_minrelay_in_package_combos(self)
```

Code after:

        # Create a reorg, bringing tx_with_multi_children back into the mempool with a descendant count of 3.
        node.invalidateblock(block_to_disconnect)
        self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
        assert_equal(node.getmempoolentry(tx_with_multi_children["txid"])["descendantcount"], 3)

        # Sibling eviction is not allowed because there are two siblings
        tx_with_sibling3 = self.wallet.create_self_transfer(
            version=3,
            utxo_to_spend=tx_with_multi_children["new_utxos"][2],
            fee_rate=DEFAULT_FEE*50
        )
        expected_error_2siblings = f"TRUC-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
        assert_raises_rpc_error(-26, expected_error_2siblings, node.sendrawtransaction, tx_with_sibling3["hex"])

        # However, an RBF (with conflicting inputs) is possible even if the resulting cluster size exceeds 2
        tx_with_sibling3_rbf = self.wallet.send_self_transfer(
            from_node=node,
            version=3,
            utxo_to_spend=tx_with_multi_children["new_utxos"][0],
            fee_rate=DEFAULT_FEE*50
        )
        self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]])

    @cleanup(extra_args=None)
    def test_minrelay_in_package_combos(self):
        node = self.nodes[0]
        self.log.info("Test that only TRUC transactions can be under minrelaytxfee for various settings...")

        for minrelay_setting in (0, 5, 10, 100, 500, 1000, 5000, 333333, 2500000):
            self.log.info(f"-> Test -minrelaytxfee={minrelay_setting}sat/kvB...")
            setting_decimal = minrelay_setting / Decimal(COIN)
            self.restart_node(0, extra_args=[f"-minrelaytxfee={setting_decimal:.8f}", "-persistmempool=0"])
            minrelayfeerate = node.getmempoolinfo()["minrelaytxfee"]
            high_feerate = minrelayfeerate * 50

            tx_v3_0fee_parent = self.wallet.create_self_transfer(fee=0, fee_rate=0, confirmed_only=True, version=3)
            tx_v3_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_0fee_parent["new_utxo"], fee_rate=high_feerate, version=3)
            total_v3_fee = tx_v3_child["fee"] + tx_v3_0fee_parent["fee"]
            total_v3_size = tx_v3_child["tx"].get_vsize() + tx_v3_0fee_parent["tx"].get_vsize()
            assert_greater_than_or_equal(total_v3_fee, get_fee(total_v3_size, minrelayfeerate))
            if minrelayfeerate > 0:
                assert_greater_than(get_fee(tx_v3_0fee_parent["tx"].get_vsize(), minrelayfeerate), 0)
                # Always need to pay at least 1 satoshi for entry, even if minimum feerate is very low
                assert_greater_than(total_v3_fee, 0)

            tx_v2_0fee_parent = self.wallet.create_self_transfer(fee=0, fee_rate=0, confirmed_only=True, version=2)
            tx_v2_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v2_0fee_parent["new_utxo"], fee_rate=high_feerate, version=2)
            total_v2_fee = tx_v2_child["fee"] + tx_v2_0fee_parent["fee"]
            total_v2_size = tx_v2_child["tx"].get_vsize() + tx_v2_0fee_parent["tx"].get_vsize()
            assert_greater_than_or_equal(total_v2_fee, get_fee(total_v2_size, minrelayfeerate))