Rename weight -> clusterweight in RPC output, and add doc explaining mempool terminology

Co-authored-by: glozow <gloriajzhao@gmail.com>
This commit is contained in:
Suhas Daftuar
2025-11-25 14:20:47 -05:00
parent bc2eb931da
commit 6c1325a091
3 changed files with 30 additions and 11 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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]