-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Allow updating mempool-txn with cheaper witnesses #19645
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
Conversation
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
i'm leaning toward a concept nack. I don't think this is urgent to fix and incentivizes using the mempool as working space for negotiating smaller witnesses, rather than attempting to produce a smaller signature on first broadcast. Further, this opens up potential attacks where you can make malleable smaller and smaller witnesses and keep resubmitting to boost feerate or something. I also think it's sufficiently rare that we'd even have smaller valid witnesses to submit, but then if someone starts relying on this behavior we're stuck supporting it forever. where you might convince me that it's a problem is if Alice and Bob are doing a payjoin or something, and then Bob changes his witness to be an even bigger one and then broadcasts, locking Alice onto a lower fee rate for the transaction. But again, on urgency, these replacement policies are really only relevant once more complex taproot scripts are widely deployed and a change like this can be added to a future release policy easily. |
I think the notion of first broadcast doesn't hold when you have multiple-party involved with alternatives spending paths and so potential concurrent broadcasts. Parties can't produce smaller signatures because key distribution across script branches encodes a policy, it can be either Alice or Bob+Caroll+Dave, where Alice has a higher level of privileges. As a practical example you might have pre-signed vault transactions, like https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-April/017793.html
You're still liable to RBF rules compliance, especially rule 4 on incremental relay fee ?
As a first step you should avoid to introduce witness malleability if parties are distrusted, and currently as I mentioned it won't likely propagate due to default mempool settings. But yes if we change it in the future, it would be a limited guardrail for this kind of scenario. There is no urgency to fix, it's more making consistent our relay wtxid-policy with our mempool one. |
1d2ea3e
to
62f8304
Compare
Concept ACK. As long as this is re-using the BIP125 RBF logic, then I can't see how it can be any more of an attack vector than any other kind of RBF.
I think we'd want this widely deployed before such scripts were possible. |
Conceptually I agree this would be a reasonable (incentive-compatible) behavior, but I'm not sure this is worth the complexity/effort to implement this change. To do this correctly, you'd also want to not evict all the descendants of the replaced transaction in situations like this, which I think means more special cased mempool acceptance logic. Is there any application out there that would expect to use this behavior? I know this is fun to theorize about but it's hard for me to imagine there will be anyone using this -- so I am skeptical this is worth the effort and added code complexity. |
@sdaftuar I think the issue is more that an adversary can use it. E.g., say I do a coinjoin with you, and we create a txn with a feerate A and a witness weight of X. Then, as one of the parties to the txn, I create a much heavier witness weight 2X, and decrease the feerate to like 0.7A. Now it takes forever to confirm. So it's less so someone using it, than abusing it. |
I get that this is a theoretical concern, but has this ever actually happened to anyone? I can imagine there could be lots of reasons why behavior like this would not actually take place in practice. If stuff like this has happened, then I can get on board with wanting to deal with it in the right way. |
Accepting a transaction to the mempool only succeeds if this transaction isn't already present. This check operating on txid, it prevents new submissions of the transaction with identical non-witness data but smaller valid witnesses. Therefore, overall mempool feerate won't improve even if such higher-feerate mempool candidate is learnt through wtxid-relay. This commit changes mempool and transaction broadcast already-present checks from transaction's txid to its wtxid. A new-wtxid submission may still fail due to replacement rules in function of local policy settings.
Broadcast consequently two identical child transactions with size-divergent witnesses and thus different feerate. The second broadcast should successfully replace the first one. Add assert_not_equal helper in test_framework/util.py
62f8304
to
d86e7a1
Compare
I agree in-place substitution while conserving chain of transactions would be better bandwidth-wise. But that would require special RBF rules to handle it and I think it's better to strictly bound to our current RBF rules to make it easier to reason on. We already have the same issue with someone replacing a parent tx with a new txid and advertising again a chain of transaction paying the same outputs. Cheap RBF replacement should be addressed on its own, this PR doesn't pretend to make it better or worse ?
Beyond preventing abuse for potential coinjoin/payjoin/vaults and easing future usage for taproot branches, as of today there is the practical usage of selecting your best-feerate witness among a set of valid ones. I don't think a lot of users have yet this mental model but I expect this to change with wider adoption of Miniscript. Updated with better comment/logs. Let me know if you think it's worthy further review time otherwise I would close it. |
I think this concern is indeed valid and the problem should be addressed. However, it's hard for me to justify at which cost (code complexity) we want to address it. We also want to make sure that this solution is indeed sufficient and the best alternative among others. |
Just to clarify, this current proposal doesn't effectively mitigate this without further change to our RBF handling due to the default I think mitigating low-feerate pinning is an orthogonal discussion, see #14895 (comment) IMO, the code complexity of the current proposed PR is straightforward as it doesn't interfere with RBF. |
Suggest calling this "updating tx with cheaper witness" or similar -- "wtxid-acceptance" is pretty generic, it's more analogous to "replace by fee", except that you're not even replacing anything as far as the txid goes, which is 99% of what matters? I believe the rule is "txid remains the same but overall weight decreases; so only witness data can change, tx fee rate increases, and no descendants are evicted from the mempool". (Note the taproot bip raises the possibility of weight being artificially increased in some cases, so the witness data might strictly be larger despite the tx weight decreasing) |
Concept ACK |
@ajtowns Good call, took your suggestion. Initially called it "wtxid-acceptance" as this is literally what this PR does, evaluating an already-present utxo-spend candidate on wtxid rather than txid. In practice, I agree that txid is what matters, like evaluating descendants limits. Note, for implementation simplicity, it does evict descendants from mempool, we can implement in-place substitution as a follow-up if it feels needed. For the taproot hint, I think that rational holds, as from the miner viewpoint you want to increase feerate per weight unit. I don't think that effective memory-space of these weight units is a burden for miner mempools. |
@ariard does it make sense to attempt to merge the witnesses into a lowest fee form? E.g. if witA[0] > witB[0], but witA[1] + withA[0] < witB[0] + withB[1] it may make sense to make a new witC comprised of the best of both. |
@JeremyRubin Do you mean cross-inputs, i.e if we learn wtxid_1:(witA, witB) and wtxid_2:(witA', witB') and witA > witA' and witB < witB' we select witA and witB' to compose new wtxid_3 ? Or single-input, if by decomposing multiple known witnesses and analyzing witnessScript we're able to compress to a better feerate witness ? Either way, I think what your question is raising is a separation of concerns across the bitcoin stack. I don't think that's the job of network mempools to optimize your spends based on a set of witness element you may have broadcasted. This sounds like a new DoS vector and you would need to pay for the CPU time, thus reducing worthiness of operation. I think that composing best-feerate witness in function of your solvable scripts paths is better done by a higher tool. Of course, this software may not implement correctly all weight accounting rules, especially post-Taproot, so this PR allows you to verify composed witnesses against your local mempool, assuming you set |
While opening this PR, I thought too it was a theoretical concern, but reviewing the DLC specs last week, such protocol would be vulnerable to witness weight inflation discussed here. And a honest participant can't rely on RBF as it's easy for a malicious party to attach a high-absolute-fee, low-feerate chain of transactions to its change output Of course you can adopt carve-out mechanism and ensure you don't have more than one immediately spendable output by party but this come with far less protocol flexibility and you might loss few blocks between initial broadcast and trigger of your fee-bump. I agree that change advocated in this PR won't fix it without further modifications to our RBF policy but it's at least a minimal prerequisite. Or do you think we should first have a broader discussion on the mental model that higher protocol designers should adopt with regards to our mempool/relay policy ? |
It's not optimizing your spends, it's optimizing miner revenue, which is ~the whole point. In terms of DoS the issue that also occurs is that someone can malicious anti-merge your better witness, e.g., I see B which is better for every input then A, and then I cut B's witness with half A's witnesses, then broadcast. Now B is not minrelay increment better than half B half A, so i get pinned. |
I think at the very least there should be a clearer error for this. AFAIK right now a user would have to guess that the one in mempool has a different witness and then use In terms of DoSing, it seems to me that this is essentially "RBF taking into account witness data" (is that incorrect?) and can thus be evaluated in a similar manner. |
🐙 This pull request conflicts with the target branch and needs rebase. Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft". |
Good idea, that's maybe better for now to return a new error for an unsuccessful
In the context of multi-party protocol (e.g a CoinSwap, LN), what is concerning is a pinning (https://bitcoinops.org/en/topics/transaction-pinning/) where a malicious participant inflate a provided witness to delay a time-locked transaction either for timevalue-DoS or plainly stealing funds due to concurrent claiming. Because for the fix to be effective you need to change the RBF logic from requiring an absolute fee increase between 2 transactions from a feerate relative increase (see Line 910 in 283a73d
It needs more thoughts that the current PR which is more providing locally fee estimation between different witnesses to a user. |
…and same-nonwitness-data tx in mempool b7a8cd9 [test] submit same txid different wtxid as mempool tx (glozow) fdb4816 [validation] distinguish same txid different wtxid in mempool (glozow) Pull request description: On master, if you submit a transaction with the same txid but different witness to the mempool, it thinks the transactions are the same. Users submitting through `BroadcastTransaction()` (i.e. `sendrawtransaction` or the wallet) don't get notified that there's a different transaction in the mempool, although it doesn't crash. Users submitting through `testmempoolaccept()` will get a "txn-already-in-mempool" error. This PR simply distinguishes between `txn-already-in-mempool` and `txn-same-nonwitness-data-in-mempool`, without handling them differently: `sendrawtransaction` still will not throw, but `testmempoolaccept` will give you a different error. I believe the intention of #19645 is to allow full swaps of transactions that have different witnesses but identical nonwitness data. Returning a different error message + adding a test was suggested: bitcoin/bitcoin#19645 (comment) so this is that PR. ACKs for top commit: naumenkogs: ACK b7a8cd9 jnewbery: Code review ACK b7a8cd9 theStack: Code-review ACK b7a8cd9 darosior: re-utACK b7a8cd9 Tree-SHA512: 9c6591edaf8727ba5b4675977adb8cbdef7288584003b6cd659828032dc92d2ae915800a8ef8b6fdffe112c1b660df72297a3dcf2e2e3e1f959c6cb3678c63ee
There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
Closing, superseded by #24007 |
Accepting a transaction to the mempool only succeeds if this transaction
isn't already present. This check operating on txid, it prevents new
submissions of the transaction with identical non-witness data but
smaller valid witnesses. Therefore, overall mempool feerate won't improve
even if such higher-feerate mempool candidate is learnt through wtxid-relay.
This commit changes mempool and transaction broadcast already-present checks
from transaction's txid to its wtxid.
A new-wtxid submission may still fail due to replacement rules in
function of local policy settings.
@jnewbery RE: #18044 (comment) I think we agree on the "same txid, different txids" nonsense, I didn't mean to track multiple witnesses for one txid rather to accept and rebroadcast "the best mempool candidate known for a given txid". A script may have alternatives solvable branches and witnesses to spend them might be different in weight thus creating wtxid with divergent feerates. A best known candidate with regards to a txid is defined as a higher-feerate witness (a smaller one) transaction than our current in mempool one, with identical non-witness data.
You might submit a txidA:wtxid1 to your local node, and schedule it for initial-broadcast reattempt. Few minutes, latter you announce to your node a higher-feerate txidA:wtxid2, which effectively replace wtxid1. At
ReattemptInitialBroadcast
, only wtxid2 will be rebroadcast if needed, as best known candidate for txidA. According to me, that's where unbroadcast semantic is unclear, to which wtxid the user should expect seeing "broadcast-reattempt" property guarantee ?Given our current relay policy default, I concede this is rather a marginal case, beyond illustrating another limit of our mempool replacement rules. In the future, it's likely to worsen as taproot make it far easier to create witnesses with divergent feerates (e.g a 1-of-1 tapscript compared to a 15-of-15 one). In case of concurrent broadcast, the mempool won't necessary learn the best-feerate candidate. That said, IMO, it's worthy to fix because "txn-already-in-mempool" isn't accurate anymore in a wtxid-relay context.