From 6c1325a0913e22258ab6b62f381e56c7bebbd462 Mon Sep 17 00:00:00 2001 From: Suhas Daftuar Date: Tue, 25 Nov 2025 14:20:47 -0500 Subject: [PATCH] Rename weight -> clusterweight in RPC output, and add doc explaining mempool terminology Co-authored-by: glozow --- doc/policy/mempool-terminology.md | 19 +++++++++++++++++++ src/rpc/mempool.cpp | 4 ++-- test/functional/mempool_cluster.py | 18 +++++++++--------- 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 doc/policy/mempool-terminology.md diff --git a/doc/policy/mempool-terminology.md b/doc/policy/mempool-terminology.md new file mode 100644 index 00000000000..55c947e6588 --- /dev/null +++ b/doc/policy/mempool-terminology.md @@ -0,0 +1,19 @@ +## Fee and Size Terminology in Mempool Policy + + * Each transaction has a **weight** and virtual size as defined in BIP 141 (different from serialized size for witness transactions, as witness data is discounted and the value is rounded up to the nearest integer). + + * In the RPCs, "weight", refers to the weight as defined in BIP 141. + + * A transaction has a **sigops size**, defined as its sigop cost multiplied by the node's `-bytespersigop`, an adjustable policy. + + * A transaction's **virtual size (vsize)** refers to its **sigops-adjusted virtual size**: the maximum of its BIP 141 size and sigop size. This virtual size is used to simplify the process of building blocks that satisfy both the maximum weight limit and sigop limit. + + * In the RPCs, "vsize" refers to this sigops-adjusted virtual size. + + * Mempool entry data with the suffix "-size" (eg "ancestorsize") refer to the cumulative sigops-adjusted virtual size of the transactions in the associated set. + + * A transaction can also have a **sigops-adjusted weight**, defined similarly as the maximum of its BIP 141 weight and 4 times the sigops size. This value is used internally by the mempool to avoid losing precision, and mempool entry data with the suffix "-weight" (eg "chunkweight", "clusterweight") refer to this sigops-adjusted weight. + + * A transaction's **base fee** is the difference between its input and output values. + + * A transaction's **modified fee** is its base fee added to any **fee delta** introduced by using the `prioritisetransaction` RPC. Modified fee is used internally for all fee-related mempool policies and block building. diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index bc011e0b526..10b8614b80c 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -267,7 +267,7 @@ static RPCHelpMan testmempoolaccept() static std::vector ClusterDescription() { return { - RPCResult{RPCResult::Type::NUM, "weight", "total sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop'"}, + RPCResult{RPCResult::Type::NUM, "clusterweight", "total sigops-adjusted weight (as defined in BIP 141 and modified by '-bytespersigop')"}, RPCResult{RPCResult::Type::NUM, "txcount", "number of transactions"}, RPCResult{RPCResult::Type::ARR, "chunks", "chunks in this cluster (in mining order)", {RPCResult{RPCResult::Type::OBJ, "chunk", "", @@ -332,7 +332,7 @@ static void clusterToJSON(const CTxMemPool& pool, UniValue& info, std::vectorGetAdjustedWeight(); } - info.pushKV("weight", total_weight); + info.pushKV("clusterweight", total_weight); info.pushKV("txcount", (int)cluster.size()); // Output the cluster by chunk. This isn't handed to us by the mempool, but diff --git a/test/functional/mempool_cluster.py b/test/functional/mempool_cluster.py index cbe2684ca93..c8d09c2102c 100755 --- a/test/functional/mempool_cluster.py +++ b/test/functional/mempool_cluster.py @@ -173,7 +173,7 @@ class MempoolClusterTest(BitcoinTestFramework): target_vsize_per_tx = int((max_cluster_size_vbytes - 500) / num_txns) cluster_submitted = self.add_chain_cluster(node, num_txns, target_vsize_per_tx) - vsize_remaining = max_cluster_size_vbytes - weight_to_vsize(node.getmempoolcluster(cluster_submitted[0]["txid"])['weight']) + vsize_remaining = max_cluster_size_vbytes - weight_to_vsize(node.getmempoolcluster(cluster_submitted[0]["txid"])['clusterweight']) self.log.info("Test that cluster size limit is enforced") self.test_limit_enforcement(cluster_submitted, target_vsize_per_tx=vsize_remaining + 4) @@ -315,12 +315,12 @@ class MempoolClusterTest(BitcoinTestFramework): # One chunk with one tx first_chunk_tx = self.wallet.send_self_transfer(from_node=node) first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"]) - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"]]}]}) # Another unconnected tx, nothing should change self.wallet.send_self_transfer(from_node=node) first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"]) - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"]]}]}) # Second connected tx, makes one chunk still with high enough fee second_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=first_chunk_tx["new_utxo"], fee_rate=Decimal("0.01")) @@ -329,7 +329,7 @@ class MempoolClusterTest(BitcoinTestFramework): assert_equal(first_chunk_info, node.getmempoolcluster(second_chunk_tx["txid"])) chunkweight = first_chunk_tx["tx"].get_weight() + second_chunk_tx["tx"].get_weight() chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"] - assert_equal(first_chunk_info, {'weight': chunkweight, 'txcount': 2, 'chunks': [{'chunkfee': chunkfee, 'chunkweight': chunkweight, 'txs': [first_chunk_tx["txid"], second_chunk_tx["txid"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': chunkweight, 'txcount': 2, 'chunks': [{'chunkfee': chunkfee, 'chunkweight': chunkweight, 'txs': [first_chunk_tx["txid"], second_chunk_tx["txid"]]}]}) # Third connected tx, makes one chunk still with high enough fee third_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=second_chunk_tx["new_utxo"], fee_rate=Decimal("0.1")) @@ -338,14 +338,14 @@ class MempoolClusterTest(BitcoinTestFramework): assert_equal(first_chunk_info, node.getmempoolcluster(third_chunk_tx["txid"])) chunkweight = first_chunk_tx["tx"].get_weight() + second_chunk_tx["tx"].get_weight() + third_chunk_tx["tx"].get_weight() chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"] + third_chunk_tx["fee"] - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': chunkweight, 'txcount': 3, 'chunks': [{'chunkfee': chunkfee, 'chunkweight': chunkweight, 'txs': [first_chunk_tx["txid"], second_chunk_tx["txid"], third_chunk_tx["txid"]]}]}) # Now test single cluster with each tx being its own chunk # One chunk with one tx first_chunk_tx = self.wallet.send_self_transfer(from_node=node) first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"]) - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"]]}]}) # Second connected tx, lower fee second_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=first_chunk_tx["new_utxo"], fee_rate=Decimal("0.000002")) @@ -354,7 +354,7 @@ class MempoolClusterTest(BitcoinTestFramework): assert_equal(first_chunk_info, node.getmempoolcluster(second_chunk_tx["txid"])) first_chunkweight = first_chunk_tx["tx"].get_weight() second_chunkweight = second_chunk_tx["tx"].get_weight() - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"]]}]}) # Third connected tx, even lower fee third_chunk_tx = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=second_chunk_tx["new_utxo"], fee_rate=Decimal("0.000001")) @@ -365,12 +365,12 @@ class MempoolClusterTest(BitcoinTestFramework): second_chunkweight = second_chunk_tx["tx"].get_weight() third_chunkweight = third_chunk_tx["tx"].get_weight() chunkfee = first_chunk_tx["fee"] + second_chunk_tx["fee"] + third_chunk_tx["fee"] - 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"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"]]}]}) # If we prioritise the last transaction it can join the second transaction's chunk. node.prioritisetransaction(third_chunk_tx["txid"], 0, int(third_chunk_tx["fee"]*COIN) + 1) first_chunk_info = node.getmempoolcluster(first_chunk_tx["txid"]) - 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"] + 2*third_chunk_tx["fee"] + Decimal("0.00000001"), 'chunkweight': second_chunkweight + third_chunkweight, 'txs': [second_chunk_tx["txid"], third_chunk_tx["txid"]]}]}) + assert_equal(first_chunk_info, {'clusterweight': 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"] + 2*third_chunk_tx["fee"] + Decimal("0.00000001"), 'chunkweight': second_chunkweight + third_chunkweight, 'txs': [second_chunk_tx["txid"], third_chunk_tx["txid"]]}]}) def run_test(self): node = self.nodes[0]