The PR branch HEAD was b15079ac7b at the time of this review club meeting.
The content of a transaction can be separated into two components:
Since Segwit activation, there are two ways a transaction can be serialized,
with and without the witness data.
A transaction’s txid is the sha256d hash of its non-witness serialization.
A transaction’s wtxid is the sha256d hash of its full (including its witness)
(Hashing the serialization of only the witness data isn’t useful.)
It’s possible for two transactions to have the same txid but different
wtxids. The opposite is not possible (same wtxids but different txids).
That is, the mapping from txid to wtxid is one-to-many.
The only place any type of transaction ID appears
on the blockchain is within transaction inputs, each of which contains,
among other things, a
This object “points” to an output of the source transaction using the
txid (note, not wtxid) of the source transaction and the index
into its outputs array. This is the output that this input is “spending”.
Currently, when a transaction is submitted to the mempool and an
existing mempool transaction has the same txid, the incoming
transaction is immediately rejected.
Replace-by-fee (RBF), as described in
allows transactions that have already been accepted into the mempool to
be replaced by a newly-arriving transaction. Previous to BIP125, an
incoming transaction that spent any of the same outputs as an existing
in-mempool transaction would be rejected as a double-spend attempt.
This has been called the “first seen safe” policy.
If an RBF replacement does occur, any descendant (downstream) transactions
in the mempool must be removed. These are transactions that (recursively)
spend outputs of the transaction being replaced.
The replacement transaction can spend different inputs (except at least
one, or else it wouldn’t conflict and replacement wouldn’t be needed),
can have different outputs, and therefore will always have a different
txid than the transaction(s) being replaced.
There are several conditions that must be met for an RBF replacement to occur, documented
including a requirement that the replacement pay more fees than the original transaction(s).
implements something similar to RBF except the txid of the two transactions is the same
(but the wtxids are different). As it’s not possible for a same-txid-different witness
transaction to include a different absolute fee amount, the rules for witness replacement differ
from that of regular RBF.
A wtxid “commits to” an entire transaction, including its witness,
while a txid does not. What does the phrase “commits to” mean in Bitcoin?
Is the mempool “indexed” by txid or wtxid? Equivalently, we can
ask: Can the mempool contain multiple transactions with the same txid
(but different wtxids)? If not, would it make sense for it to do so?
Does this PR change a consensus rule? Why or why not? What happens
if some nodes are running this PR and their peers are not?
Should Bitcoin Core policies be miner incentive-compatible?
When an RBF replacement occurs, why is it necessary to remove the
When a witness replacement occurs, is it necessary to remove the
descendant transactions? Why or why not?
This PR allows replacement even if the existing transaction hasn’t
signaled replaceability. Is this an oversight?
Why would a witness-replacement transaction be broadcast? Why not
broadcast the replacement transaction initially? What is this PR’s
How can a transaction have multiple possible witness data that are
different sizes? (Hint: see the
the replacement transaction’s size to be 95% or less than the
size of the replaced transaction. Why can’t it just use the normal RBF rules?
Why is this
check written as (simplified) new_size * 100 >= old_size * 95 rather than the more obvious
new_size >= old_size * 0.95 ?
(Extra credit) How does witness replacement interact with packages?
<stickies-v> If you want to change (and broadcast) the witness of an already broadcasted transaction (e.g. by spending a different path in the script), this is currently not allowed because the txid doesn't change when just the witness data changes. This PR allows such updated transactions to be acceptedin mempool and broadcasted, provided that the new witness is sufficiently small enough.
<stickies-v> signatures are also malleable (hence segwit), so I think even without segwit that could be the case. It wouldn't hit the 5% witness size decrease requirement of this PR though, just for completeness
<larryruane> Okay, feel free to continue this line of thought, but if we're ready to move to the mempool ... the mempool is a collection of unconfirmed transactions, each represented by key-value pair (with unique keys, like a std::map) ... what is the key?
<larryruane> OliverOffing: perfect, that's exactly it! transactions in the mempool can _never_ be conflicting, and two transactions with the same txid are necessarily conflicting (spending the same inputs)
<glozow> if the question is whether boost multi index container will let us have 2 entries, i'll need to go read the docs. if the question is whether our node will survive if we put 2 transactions with the same txid in the mempool, the answer is no
<larryruane> svav: that's a great question, the answer is yes and no ... It's yes in the sense that two tx with the same txid must have the same *effect* (let's review, what is the effect of a tx? It's to destroy some UTXOs and create some new ones) ... but these two tx can be *enabled* by different witnesses, so they're different in that way ... Do I have this right? (I'm kind of new to this myself!)
<OliverOffing> different nodes would keep different transactions in the mempool but that's not really a problem for the network—only for the user trying to replace their tx's wit data as stickies-v mentioned
<stickies-v> I think so. If not, you encourage miners to set up individual backchannels (e.g. sending tx directly to miner for a direct fee) to help get non-standard transactions mined, and that puts smaller/anonymous miners at a disadvantage
<larryruane> here's a crazy thought, BTW (not in the notes), we sometimes have these policies that we're not sure miners are following - maybe we can look at the tx that arrive in each *mined block* and dynamically adjust our policies to match!
<stickies-v> erik-etsuji-kato I'm not sure that's relevant here actually, because those transactions don't get propagated to the network (because then if someone else mines them, the miner loses money)
<stickies-v> hmm they don't need to collude I think, any dishonest miner can do this, it's trivial to just construct some high fee tx's and inject them only into your own block. I just mean that this wouldn't affect policy, because they're not propagated out of block
<larryruane> okay let's see (but again, feel free to keep going on any previous thread), question 6, quick detour to RBF, when RBF occurs, why is it necessary to remove mempool decendants? Is that necessary for witness-replacement?
<stickies-v> I think this would break protocols that rely on unconfirmed wtxids, but I don't think those protocols currently exist and that seems like an unreliable thing to do anyway so we probably shouldn't care?
<theStack_> though, even without RBF signalling there is a reason to be worried, as the tx could just have a too-low fee and eventually get kicked out of the mempool, and _then_ get replaced? (probably a different discussion though)
<glozow> re: use cases, it seem like we would only be interested in this if there's a pinning attack. like, you have counterparties on your transaction and somebody bloats up the witness by thousands of vbytes or something. i can't imagine why a normal user is broadcasting their own transaction multiple times with various witnesses
<stickies-v> larryruane I can potentially see this being useful in protocols where one party signals they're willing to go on chain in a suboptimal path by broadcasting that larger transaction, maybe encouraging all parties to instead lower the fees and go for key spend (in taproot case) instead
<monlovesmango> larryruane: for the attack mentioned in that post, wouldn't it make more send to require the witness to be smaller by a certain byte size rather than percentage to eliminate this attack vector? or is there a reason we are using percentage of data size?