Rename weight -> clusterweight in RPC output, and add doc explaining mempool terminology
What changed, and why it matters
This commit renames a single field in a Bitcoin Core RPC output from 'weight' to 'clusterweight' and adds documentation explaining mempool fee/size terminology. It is a user-facing clarity/documentation change with no security-relevant code behavior change.
No security action required; this is a documentation and RPC field rename change. Reviewers may want to note the RPC output field change for API compatibility.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch changes the getmempoolcluster RPC output key from ‘weight’ to ‘clusterweight’ in src/rpc/mempool.cpp and updates the help text, tests, and adds doc/policy/mempool-terminology.md. The underlying computed value (total sigops-adjusted weight) is unchanged. No logic, validation, consensus, or cryptography code is modified.
Changed components
src/rpc/mempool.cppdoc/policy/mempool-terminology.mdtest/functional/mempool_cluster.pyInspect captured patch +30 / −11
diff --git a/doc/policy/mempool-terminology.md b/doc/policy/mempool-terminology.md
new file mode 100644
index 00000000..55c947e6
--- /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 bc011e0b..10b8614b 100644
--- a/src/rpc/mempool.cpp
+++ b/src/rpc/mempool.cpp
@@ -267,7 +267,7 @@ static RPCHelpMan testmempoolaccept()
static std::vector<RPCResult> 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::vector<co
for (const auto& tx : cluster) {
total_weight += tx->GetAdjustedWeight();
}
- 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 cbe2684c..c8d09c21 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]
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.