-
Notifications
You must be signed in to change notification settings - Fork 37.8k
rpc: Distinguish between vsize and sigop adjusted mempool vsize #32800
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
rpc: Distinguish between vsize and sigop adjusted mempool vsize #32800
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. Code Coverage & BenchmarksFor details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32800. ReviewsSee the guideline for information on the review process.
If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update. |
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
969193c
to
89d1220
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments / NITs on the documentation side of this PR.
89d1220
to
c8da5c1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed c8da5c1
src/rpc/rawtransaction.cpp
Outdated
const size_t txSize = entry->GetTxSize(); | ||
const size_t vSize = GetVirtualTransactionSize(txSize, 0, 0); | ||
// Only report sigopsize when vSize is strictly less than the raw tx size | ||
if (vSize < txSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q1: Why do we want to only conditionally report sigopsize
, is there some comment to that affect from the old PR? I don't get that impression from #27591 (comment).
Q2: In the above linked comment, do you understand what is meant by "(Or, even better, do the sigop adjustment in cases where we can -- getrawtransaction of txs in blocks could pull up the utxo being spent via rev*.dat and calculate the sigop adjustment?)"? I must admit I don't fully understand it myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q1. Yeah there's no comment about that in #27591, Am still skeptical about conditionally reporting the sigosize
, the initial idea's to check when txSize which is the transaction in the mempool (which might be sigops adjusted from entry->GetTxSize()
) is less than vSize GetVirtualTransactionSize(txSize, 0, 0)
which is not sigop adjusted. But I think it will make more sense to remove the codition and report the sigopsize
.
Q2. My understanding of the comment is; I think it’s suggesting that getrawtransaction
could compute sigopsize for confirmed transactions by retrieving spent output scripts from rev*.dat. While possible, this is expensive and sometimes not feasible (e.g., on pruned nodes). That’s why sigopsize
is only reported for mempool transactions, where all required data is readily available in memory. (I might wrong in as well)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q1. Yeah, I think the interface is less confusing if we remove the condition. #27591 seems to use the term "sigop-adjusted size" rather than "sigop-equivalent size". So could change the description from:
bitcoin/src/rpc/rawtransaction.cpp
Line 284 in be75fa4
{RPCResult::Type::NUM, "sigopsize", /*optional=*/true, "Sigop-equivalent size in bytes, present for mempool transactions with non-default virtual size due to sigops."}, |
to:
"Sigop-adjusted size in bytes, only present for mempool transactions."
(Unless we implement Q2, which I agree is not critical).
Maybe replace "sigop-equivalent" with "sigop-adjusted" everywhere else too unless there is precedent for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed as suggested in 70316b5. Thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized the comment is now out of date:
- // Add sigopsize if the transaction exists in the mempool and its size is affected by it.
+ // Add sigopsize if the transaction exists in the mempool.
src/rpc/rawtransaction.cpp
Outdated
if (vSize < txSize) { | ||
// bytes per sigop = witness scale factor (4) | ||
const uint64_t sigopSize = uint64_t(entry->GetSigOpCost()) * uint64_t(nBytesPerSigOp); | ||
result.pushKV("sigopsize", sigopSize); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: #27591 (comment) mentions adding sigopsize
to other RPCs as well. Not sure if necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't think it's necessary that's why I decide to report it (sigopsize) only in getrawtransaction
RPC
4cc9be2
to
be75fa4
Compare
Thanks @hodlinator and @janb84 for the detailed review. Well Appreciated, Please let me know if there's any comment left unaddressed. |
It makes much more sense to stick to weight for consensus-related matters, and vsize for policy in all cases. |
Sorry, I didn't quite fully understand the comment (do you mean sigopsize should return only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK be75fa4
Might be good to add release notes for this PR as it is adding to the user-facing RPC interface, similarly to: https://github.com/bitcoin/bitcoin/blob/be75fa48fa77c417b9ebdd3be4d1ddb212ab16b3/doc/release-notes-27826.md
src/rpc/rawtransaction.cpp
Outdated
const size_t txSize = entry->GetTxSize(); | ||
const size_t vSize = GetVirtualTransactionSize(txSize, 0, 0); | ||
// Only report sigopsize when vSize is strictly less than the raw tx size | ||
if (vSize < txSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q1. Yeah, I think the interface is less confusing if we remove the condition. #27591 seems to use the term "sigop-adjusted size" rather than "sigop-equivalent size". So could change the description from:
bitcoin/src/rpc/rawtransaction.cpp
Line 284 in be75fa4
{RPCResult::Type::NUM, "sigopsize", /*optional=*/true, "Sigop-equivalent size in bytes, present for mempool transactions with non-default virtual size due to sigops."}, |
to:
"Sigop-adjusted size in bytes, only present for mempool transactions."
(Unless we implement Q2, which I agree is not critical).
Maybe replace "sigop-equivalent" with "sigop-adjusted" everywhere else too unless there is precedent for it?
abe0c4d
to
ede20b4
Compare
8aa3175
to
8158ca7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 8158ca7
Found some final things before A-C-K.
@@ -97,8 +97,8 @@ def test_sigops_limit(self, bytes_per_sigop, num_sigops): | |||
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad+1)]) | |||
res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0] | |||
assert_equal(res['allowed'], True) | |||
assert_equal(res['vsize'], sigop_equivalent_vsize+1) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
meganit: This newline was logically separating the two commented code blocks and should probably be brought back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From #32800 (comment):
Addressed in 46acee8
The newline is added back in the second commit now, it would be better to change the first commit to avoid removing it.
doc/policy/feerates-and-vsize.md
Outdated
consideration of signature operations. To protect against “2D‑Knapsack” attacks that exploit a | ||
combination of block weight and high sigop counts, the mempool applies | ||
a **sigop‑adjusted size**. This value is the maximum of the pure BIP‑141 vsize and a sigop‑based | ||
calculation (sigop count multiplied by a configurable `bytesPerSigOp` parameter, which defaults to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: It would be good to hyphen-prefix to make it stand out visually (from fields like vsize
etc) and also call it "setting" or "argument" rather than "parameter" as those are the terms used in the code.
calculation (sigop count multiplied by a configurable `bytesPerSigOp` parameter, which defaults to | |
calculation (sigop count multiplied by a configurable `-bytesPerSigOp` setting, which defaults to |
doc/policy/feerates-and-vsize.md
Outdated
the field named `vsize` represents the **sigop‑adjusted size**, reflecting the policy‑adjusted estimate | ||
of block space usage. These same RPCs also provide a field called `vsize_bip141` which exposes the | ||
pure BIP‑141 virtual size without sigop adjustment. An exception to this rule is `getrawtransaction`, | ||
when the reported `vsize` is the BIP‑141 value; sigop adjustment is not applied in that context by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think the end is a bit redundant due to prior sentances, better to explain why:
when the reported `vsize` is the BIP‑141 value; sigop adjustment is not applied in that context by default. | |
when the reported `vsize` is the BIP‑141 value due to backwards compatibility. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be better to change "when" to "where" in the beginning of the line. Sorry for not noticing initially!
doc/policy/feerates-and-vsize.md
Outdated
consensus‑level weight calculations, refer instead to `vsize_bip141`. If you need to inspect the raw | ||
BIP‑141 size of a transaction not yet in the mempool, use `getrawtransaction`; but if you need the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation of getrawtransaction
says:
By default, this call only returns a transaction if it is in the mempool.
So not sure what you mean by "not yet in the mempool".
src/rpc/rawtransaction.cpp
Outdated
const size_t txSize = entry->GetTxSize(); | ||
const size_t vSize = GetVirtualTransactionSize(txSize, 0, 0); | ||
// Only report sigopsize when vSize is strictly less than the raw tx size | ||
if (vSize < txSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized the comment is now out of date:
- // Add sigopsize if the transaction exists in the mempool and its size is affected by it.
+ // Add sigopsize if the transaction exists in the mempool.
doc/policy/feerates-and-vsize.md
Outdated
When performing fee estimation and building block templates, **always use the sigop‑adjusted `vsize`** | ||
from the mempool RPCs, as this reflects how Bitcoin Core internally prioritizes transactions. For |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When building block templates, I'm not sure we want to recommend using the sigop-adjusted vsize? That might leave space over in the block if some transactions have a lot of sigops. This would make a miner earn less fees due to not filling up the block.
f542e15
to
1523e8d
Compare
Addressed in 46acee8
Fixed as suggested
Fixed as suggested
Corrected to "transaction in the mempool"
Fixed as suggested
New change is now Thanks for the review!!! @hodlinator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -97,8 +97,8 @@ def test_sigops_limit(self, bytes_per_sigop, num_sigops): | |||
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad+1)]) | |||
res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0] | |||
assert_equal(res['allowed'], True) | |||
assert_equal(res['vsize'], sigop_equivalent_vsize+1) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From #32800 (comment):
Addressed in 46acee8
The newline is added back in the second commit now, it would be better to change the first commit to avoid removing it.
doc/policy/feerates-and-vsize.md
Outdated
the field named `vsize` represents the **sigop‑adjusted size**, reflecting the policy‑adjusted estimate | ||
of block space usage. These same RPCs also provide a field called `vsize_bip141` which exposes the | ||
pure BIP‑141 virtual size without sigop adjustment. An exception to this rule is `getrawtransaction`, | ||
when the reported `vsize` is the BIP‑141 value; sigop adjustment is not applied in that context by default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be better to change "when" to "where" in the beginning of the line. Sorry for not noticing initially!
doc/policy/feerates-and-vsize.md
Outdated
When performing mempool policy operations (e.g fee estimation, eviction, or acceptance), use the | ||
sigop-adjusted `vsize`. However, when building block templates, miners should prioritize transactions | ||
by actual fee per weight unit and be mindful of the 80,000 sigop limit, rather than relying solely | ||
on sigop-adjusted `vsize` from the mempool RPCs. For consensus‑level weight calculations, refer instead | ||
to `vsize_bip141`. If you need to inspect the raw BIP‑141 size of a transaction in the mempool, use | ||
`getrawtransaction`; but if you need the policy‑adjusted size, use `getmempoolentry` or `getrawmempool`. | ||
Node operators should be cautious when changing the `-bytespersigop` setting, as it can affect fee | ||
estimation accuracy and block utilization without impacting consensus rules. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Reworded initial sentence to account for fee estimation not being a policy itself.
- Use plain "size" instead of "
vsize
" to avoid loading foot-gun aroundgetrawtransaction
'svsize
. - Add sentence motivate reasoning behind initial recommendation.
- Bring up the
-bytespersigop
sentence up to section around sigop-adjusted size and split paragraph. - Use non-breaking space instead of comma as numeric separator for consistency with "4 000 000 units" above.
- Avoid detailing RPCs as the prior section on them should be enough.
When performing mempool policy operations (e.g fee estimation, eviction, or acceptance), use the | |
sigop-adjusted `vsize`. However, when building block templates, miners should prioritize transactions | |
by actual fee per weight unit and be mindful of the 80,000 sigop limit, rather than relying solely | |
on sigop-adjusted `vsize` from the mempool RPCs. For consensus‑level weight calculations, refer instead | |
to `vsize_bip141`. If you need to inspect the raw BIP‑141 size of a transaction in the mempool, use | |
`getrawtransaction`; but if you need the policy‑adjusted size, use `getmempoolentry` or `getrawmempool`. | |
Node operators should be cautious when changing the `-bytespersigop` setting, as it can affect fee | |
estimation accuracy and block utilization without impacting consensus rules. | |
When performing operations involving or affected by mempool policy (e.g fee estimation, eviction, or acceptance), use the | |
sigop-adjusted size. Doing so should lead to better accuracy assuming most of the | |
network/miners do the same. Node operators should also be cautious when changing the | |
`-bytespersigop` setting, as it affects these aspects. | |
However, when building block templates, miners should prioritize transactions | |
by actual fee per weight unit and be mindful of the 80 000 sigop limit, instead of using the | |
sigop-adjusted size from the mempool RPCs. |
1523e8d
to
571b11c
Compare
This commit adds a new `vsize_bip141` field to mempool acceptance and submission RPCs, including `testmempoolaccept` and `submitpackage`, to explicitly report the virtual transaction size as defined in BIP 141. RPC help texts are updated to reflect this addition. Tests in `mempool_accept.py, mempool_accept, p2p_segwit, rpc_packages, mempool_sigoplimit` are extended to verify the presence and correctness of the new field. Co-authored-by: Gloria Zhao <gloriajzhao@gmail.com>
…sactions Extend the `getrawtransaction` RPC to include a new field `sigopsize` when the transaction is in the mempool. The `sigopsize` provides the mempool's accounting size for the transaction based on its sigop cost, which can exceed its serialized vsize under `-bytespersigop` policies. Test coverage is added to verify the correct calculation and exposure of the `sigopsize` field via `mempool_sigoplimit.py`.
3e395e9
to
bd46943
Compare
Addressed and fixed as suggested. Thanks!!!
Hi @glozow, @ajtowns, @Sjors — since you were involved in #27591, I’d really appreciate your thoughts on the latest state of this PR. Just a friendly ping in case you'd like to take a look. 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think documentation is definitely helpful, but I'm not sure an extra vsize_bip141
field is really necessary (they can just divide weight by 4) and I don't really see a reason for ca16b18
doc/policy/feerates-and-vsize.md
Outdated
affects these aspects. However, when building block templates, miners should prioritize transactions | ||
by actual fee per weight unit and be mindful of the 80 000 sigop limit, instead of using the | ||
sigop-adjusted size from the mempool RPCs. For consensus‑level weight calculations, refer instead |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This contradicts the earlier statement that the purpose of sigop-adjusted vsize is to avoid 2D knapsack in block template building.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in ca9c521
Thanks!!!
doc/policy/feerates-and-vsize.md
Outdated
block weight of 4 000 000 units. | ||
**BIP‑141 virtual size** (sometimes referred to as `vsize_bip141`) is simply the block weight divided | ||
by four, rounded up. This value represents the consensus‑level “size” of a transaction without any | ||
consideration of signature operations. To protect against “2D‑Knapsack” attacks that exploit a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the problem is this severe. Rolling the two cost metrics into one helps simplify the template building algorithm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in ca9c521
Thanks!!!
doc/release-notes-32800.md
Outdated
@@ -0,0 +1,5 @@ | |||
- Mempool-related RPCs (`getrawmempool`, `getmempoolentry`, `testmempoolaccept`, `submitpackage`) now | |||
clarify the difference between BIP-141 virtual size (`vsize_bip141`) and policy-adjusted virtual size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can just say that there is an additional field now that gives the bip141 virtual size, in case you don't want the sigop-adjusted one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 74530ac
Thank you so much for reviewing the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: The adjusted beginning is nicer, but would rather bring back the end from 1523e8d:
Additionally,
getrawtransaction
now includes
+asigopsize
field and continues to report the unadjusted BIP-141 virtual size in itsvsize
field.
68ec63e
to
05cc5e5
Compare
🚧 At least one of the CI tasks failed. HintsTry to run the tests locally, according to the documentation. However, a CI failure may still
Leave a comment here, if you need help tracking down a confusing failure. |
This commit introduces a new policy document at `doc/policy/feerates‑and‑vsize.md` that clarifies: - How transaction weight, BIP‑141 virtual size, and sigop‑adjusted size are calculated. - Which RPCs expose “vsize” (sigop‑adjusted) versus “vsize_bip141” (pure BIP‑141). - Guidance for users on fee estimation, block template construction, and consensus‑level size reporting.
05cc5e5
to
74530ac
Compare
I understand it can be calculated from I can remove ca16b18 if you feel strongly about it. I added it because of the discussion here: #27591. Thank you so much! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 74530ac
Clarifies the distinction between sigop-adjusted and non-sigop-adjusted vsize for RPC users.
Minor fixups and corrections since my last review #32800 (review)
defaulting to 20 vbytes per sigop). Using a single combined metric like this simplifies block template | ||
construction by avoiding the need to optimize for both weight and sigops separately — a potential | ||
“two-dimensional knapsack” problem — though in practice the impact of that problem is often limited. | ||
Some miners prefer to handle weight and sigops as separate constraints, while others use the sigop-adjusted | ||
size for simplicity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
info: Verified that Bitcoin Core seems to use sigop-adjusted size for block template construction as it sounds like that in this part. IIUC, we do, going by:
-
Checking the block is full with
packageSize
...
Lines 209 to 214 in ca9c521
bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost) const { // TODO: switch to weight-based accounting for packages instead of vsize-based accounting. if (nBlockWeight + WITNESS_SCALE_FACTOR * packageSize >= m_options.nBlockMaxWeight) { return false; }
...which comes fromGetSizeWithAncestors()
...
Lines 376 to 390 in ca9c521
uint64_t packageSize = iter->GetSizeWithAncestors(); CAmount packageFees = iter->GetModFeesWithAncestors(); int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors(); if (fUsingModified) { packageSize = modit->nSizeWithAncestors; packageFees = modit->nModFeesWithAncestors; packageSigOpsCost = modit->nSigOpCostWithAncestors; } if (packageFees < m_options.blockMinFeeRate.GetFee(packageSize)) { // Everything else we might consider has a lower fee rate return; } if (!TestPackage(packageSize, packageSigOpsCost)) {
...which comes from...
bitcoin/src/kernel/mempool_entry.h
Line 126 in ca9c521
nSizeWithAncestors{GetTxSize()},
...which is sigop-adjusted:
bitcoin/src/kernel/mempool_entry.h
Lines 140 to 143 in ca9c521
int32_t GetTxSize() const { return GetVirtualTransactionSize(nTxWeight, sigOpCost, ::nBytesPerSigOp); } -
Ordering by feerate, which uses
GetTxSize()
:
Line 313 in ca9c521
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = mempool.mapTx.get<ancestor_score>().begin();
doc/release-notes-32800.md
Outdated
@@ -0,0 +1,5 @@ | |||
- Mempool-related RPCs (`getrawmempool`, `getmempoolentry`, `testmempoolaccept`, `submitpackage`) now | |||
clarify the difference between BIP-141 virtual size (`vsize_bip141`) and policy-adjusted virtual size |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: The adjusted beginning is nicer, but would rather bring back the end from 1523e8d:
Additionally,
getrawtransaction
now includes
+asigopsize
field and continues to report the unadjusted BIP-141 virtual size in itsvsize
field.
|
||
Both methods are valid: the first prioritizes simplicity, the second prioritizes potentially optimal | ||
space usage. For consensus-level weight calculations, refer to `vsize_bip141`. If you need to inspect | ||
the raw BIP-141 size of a transaction in the mempool, use `getrawtransaction`; if you need the | ||
policy-adjusted size, use `getmempoolentry` or `getrawmempool`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think this part is repetitive and somewhat arbitrary, currently the PR ensures sigop-adjusted and non-sigop-adjusted sizes are available for all mentioned RPCs. If I was using RPCs for each transaction for this calculation I would just pick the fastest one. Maybe I'm overlooking something though.
Both methods are valid: the first prioritizes simplicity, the second prioritizes potentially optimal | |
space usage. For consensus-level weight calculations, refer to `vsize_bip141`. If you need to inspect | |
the raw BIP-141 size of a transaction in the mempool, use `getrawtransaction`; if you need the | |
policy-adjusted size, use `getmempoolentry` or `getrawmempool`. |
Motivation and Problem
CTxMemPoolEntry::GetTxSize()
returns the larger of two values: the BIP 141 virtual size (vsize) and the "sigop-adjusted size." This sigop-adjusted size is used by mempool validation and mining algorithms as a safeguard to prevent overfilling blocks with transactions that approach both the weight and signature operation (sigop) limits in a way that could exploit block space efficiency.In the current implementation, the sigop-adjusted size is reported as the "vsize" in RPCs that provide mempool transaction data, such as
getmempoolentry
,getrawmempool
,testmempoolaccept
, andsubmitpackage
. However, the documentation for these RPCs typically describes this value simply as the "virtual transaction size as defined in BIP 141," without acknowledging the sigop adjustment. Since the reported size may differ from the pure BIP 141 definition, this confuses people as in this tweet, discrepancy can be misleading, as the reported size may differ from the pure BIP 141 definition.Proposed Solution
To resolve this, all mempool-related RPCs now return two separate fields:
vsize: the sigop-adjusted size, i.e. max(BIP 141 vsize, sigop-adjusted size), which reflects the value previously returned under the vsize label and continues to drive mempool acceptance and block template scoring.
vsize_bip141: the pure BIP 141 virtual size, strictly
ceil(weight/4)
, matching the consensus definition and now reported separately for clarity.This preserves the behavior for clients that depend on the mempool policy size being reported as vsize while making the consensus size explicitly accessible under vsize_bip141.
Additionally, this PR updates the relevant RPC help text to clearly document the distinction between these two sizes, and adds supporting documentation
doc/policy/feerates-and-vsize.md
to better explain fee rates, virtual size calculations, sigop adjustments, and the mempool policy heuristics.A new field, sigopsize, has also been added to the getrawtransaction RPC result when input information (transaction is in the mempool) is available. This field explicitly reports the sigop-adjusted size as calculated by
ceil(sigop cost * bytes_per_sigop / WITNESS_SCALE_FACTOR)
. Exposing this value provides users with more precise insight into how the transaction’s sigops impact its effective size for policy and fee estimation.Note: This picks up work from the closed #27591
Fixes #32775