pretty sure there’s at least one bug here. f.e., if you have three chunks in one cluster, I think the conditional here is only hit the first time, as current_chunk_feerate is never reset to the next chunk’s size, so it’ll just start going negative.
0diff --git a/test/functional/mempool_cluster.py b/test/functional/mempool_cluster.py
1index 4b4812e619..a7eabef039 100755
2--- a/test/functional/mempool_cluster.py
3+++ b/test/functional/mempool_cluster.py
4@@ -299,4 +299,73 @@ class MempoolClusterTest(BitcoinTestFramework):
5 assert_equal(node.getmempoolcluster(tx_replacer["txid"])['txcount'], 2)
6
7+ [@cleanup](/bitcoin-bitcoin/contributor/cleanup/)
8+ def test_getmempoolcluster(self):
9+ node = self.nodes[0]
10+
11+ self.log.info("Testing getmempoolcluster")
12+
13+ assert_equal(node.getrawmempool(), [])
14+
15+ # Not in-mempool
16+ not_mempool_tx = self.wallet.create_self_transfer()
17+ assert_raises_rpc_error(-5, "Transaction not in mempool", node.getmempoolcluster, not_mempool_tx["txid"])
18+
19+ # Test that chunks are being recomputed properly
20+
21+ # One chunk with one tx
22+ first_chunk_tx = self.wallet.send_self_transfer(from_node=node)
23+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
24+ assert_equal(first_chunk_info, {'weight': first_chunk_tx["tx"].get_weight(), 'txcount': 1, 'chunks': [{'chunkfee': first_chunk_tx["fee"], 'chunkweight': first_chunk_tx["tx"].get_weight(), 'txs': [first_chunk_tx["txid"]]}]})
25+
26+ # Another unconnected tx, nothing should change
27+ other_chunk_tx = self.wallet.send_self_transfer(from_node=node)
28+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
29+ assert_equal(first_chunk_info, {'weight': first_chunk_tx["tx"].get_weight(), 'txcount': 1, 'chunks': [{'chunkfee': first_chunk_tx["fee"], 'chunkweight': first_chunk_tx["tx"].get_weight(), 'txs': [first_chunk_tx["txid"]]}]})
30+
31+ # Second connected tx, makes one chunk still with high enough fee
32+ second_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=first_chunk_tx["new_utxo"], fee_rate=Decimal("0.01"))
33+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
34+ # output is same across same cluster transactions
35+ assert_equal(first_chunk_info, node.getmempoolcluster(second_chunk_tx["txid"]))
36+ chunkweight = first_chunk_tx["tx"].get_weight() + second_chunk_tx["tx"].get_weight()
37+ chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"]
38+ assert_equal(first_chunk_info, {'weight': chunkweight, 'txcount': 2, 'chunks': [{'chunkfee': chunkfee, 'chunkweight': chunkweight, 'txs': [first_chunk_tx["txid"], second_chunk_tx["txid"]]}]})
39+
40+ # Third connected tx, makes one chunk still with high enough fee
41+ third_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=second_chunk_tx["new_utxo"], fee_rate=Decimal("0.1"))
42+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
43+ # output is same across same cluster transactions
44+ assert_equal(first_chunk_info, node.getmempoolcluster(third_chunk_tx["txid"]))
45+ chunkweight = first_chunk_tx["tx"].get_weight() + second_chunk_tx["tx"].get_weight() + third_chunk_tx["tx"].get_weight()
46+ chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"] + third_chunk_tx["fee"]
47+ assert_equal(first_chunk_info, {'weight': chunkweight, 'txcount': 3, 'chunks': [{'chunkfee': chunkfee, 'chunkweight': chunkweight, 'txs': [first_chunk_tx["txid"], second_chunk_tx["txid"], third_chunk_tx["txid"]]}]})
48+
49+ # Now test single cluster with each tx being its own chunk
50+
51+ # One chunk with one tx
52+ first_chunk_tx = self.wallet.send_self_transfer(from_node=node)
53+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
54+ assert_equal(first_chunk_info, {'weight': first_chunk_tx["tx"].get_weight(), 'txcount': 1, 'chunks': [{'chunkfee': first_chunk_tx["fee"], 'chunkweight': first_chunk_tx["tx"].get_weight(), 'txs': [first_chunk_tx["txid"]]}]})
55+
56+ # Second connected tx, lower fee
57+ second_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=first_chunk_tx["new_utxo"], fee_rate=Decimal("0.000002"))
58+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
59+ # output is same across same cluster transactions
60+ assert_equal(first_chunk_info, node.getmempoolcluster(second_chunk_tx["txid"]))
61+ first_chunkweight = first_chunk_tx["tx"].get_weight()
62+ second_chunkweight = second_chunk_tx["tx"].get_weight()
63+ assert_equal(first_chunk_info, {'weight': first_chunkweight + second_chunkweight, 'txcount': 2, 'chunks': [{'chunkfee': first_chunk_tx["fee"], 'chunkweight': first_chunkweight, 'txs': [first_chunk_tx["txid"]]}, {'chunkfee': second_chunk_tx["fee"], 'chunkweight': second_chunkweight, 'txs': [second_chunk_tx["txid"]]}]})
64+
65+ # Third connected tx, even lower fee
66+ third_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=second_chunk_tx["new_utxo"], fee_rate=Decimal("0.000001"))
67+ first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"])
68+ # output is same across same cluster transactions
69+ assert_equal(first_chunk_info, node.getmempoolcluster(third_chunk_tx["txid"]))
70+ first_chunkweight = first_chunk_tx["tx"].get_weight()
71+ second_chunkweight = second_chunk_tx["tx"].get_weight()
72+ third_chunkweight = third_chunk_tx["tx"].get_weight()
73+ chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"] + third_chunk_tx["fee"]
74+ # FIXME I think there's a big consolidating the second and third tx into one chunk
75+ assert_equal(first_chunk_info, {'weight': first_chunkweight + second_chunkweight + third_chunkweight, 'txcount': 3, 'chunks': [{'chunkfee': first_chunk_tx["fee"], 'chunkweight': first_chunkweight, 'txs': [first_chunk_tx["txid"]]}, {'chunkfee': second_chunk_tx["fee"], 'chunkweight': second_chunkweight, 'txs': [second_chunk_tx["txid"]]}, {'chunkfee': third_chunk_tx["fee"], 'chunkweight': third_chunkweight, 'txs': [third_chunk_tx["txid"]]}]})
76
77 def run_test(self):
78@@ -305,4 +374,6 @@ class MempoolClusterTest(BitcoinTestFramework):
79 self.generate(self.wallet, 400)
80
81+ self.test_getmempoolcluster()
82+
83 self.test_cluster_limit_rbf(DEFAULT_CLUSTER_LIMIT)