Contrary to BIP125 or other full-node implementation (e.g btcd), Bitcoin Core’s mempool policy doesn’t implement inherited signaling.
This PR documents our mempool behavior on this and add a test demonstrating the case.
Contrary to BIP125 or other full-node implementation (e.g btcd), Bitcoin Core’s mempool policy doesn’t implement inherited signaling.
This PR documents our mempool behavior on this and add a test demonstrating the case.
621+ replacement_child_tx = self.nodes[0].signrawtransactionwithwallet(replacement_child_tx)
622+
623+ # Broadcast replacement child tx
624+ # BIP 125 :
625+ # 1. The original transactions signal replaceability explicitly or through inheritance as described in the above
626+ # Summary section.
validation.cpp
along side this test.
Concept ACK
Maybe we can mention this in doc/bips.md as well
633+ // *not* replaceable under this policy, even if one of their
634+ // unconfirmed ancestors signals replaceability. This is diverging
635+ // from BIP125's inherited signaling description (see CVE-2021-31876).
636+ // Applications relying on first-seen mempool behavior should be
637+ // checking all unconfirmed ancestors; doing otherwise an opt-in ancestor
638+ // might be replaced, causing removal of this descendant.
suggestions, most important one being s/policy/logic|code/, the others are grammar
0 // Transactions that don't explicitly signal replaceability are
1- // *not* replaceable under this policy, even if one of their
2- // unconfirmed ancestors signals replaceability. This is diverging
3+ // *not* replaceable with the current logic, even if one of their
4+ // unconfirmed ancestors signals replaceability. This diverges
5 // from BIP125's inherited signaling description (see CVE-2021-31876).
6- // Applications relying on first-seen mempool behavior should be
7- // checking all unconfirmed ancestors; doing otherwise an opt-in ancestor
8+ // Applications relying on first-seen mempool behavior should
9+ // check all unconfirmed ancestors, otherwise an opt-in ancestor
10 // might be replaced, causing removal of this descendant.
570+ # Send tx from which to conflict outputs later
571+ base_txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
572+ self.nodes[0].generate(1)
573+ self.sync_blocks()
574+
575+ # Create a explicitly opting parent transaction
0 # Create an explicitly opt-in parent transaction
597+ res = self.nodes[0].testmempoolaccept(rawtxs=[replacement_parent_tx['hex']], maxfeerate=0)[0]
598+
599+ # Parent can be replaced.
600+ assert_equal(res['allowed'], True)
601+
602+ # Create a optout child tx spending opting parent
0 # Create an opt-out child tx spending the opt-in parent
ACK 3d57b5eb053c7a34c3621875fa60401782a280b4
Happy to re-ACK if you update per the comments.
Thanks @jonatack and @Sjors, I think I’ve addressed all your comments at 2eb0eed, but lmk if you’ve more I’ll address them quickly. @prayank23
Well in fact there is another well-known supplemental check implemented by our logic not mentioned in the BIP, which is the feerate comparison here : https://github.com/bitcoin/bitcoin/blob/ecf5f2c1a06edd8372402872525f8de1d4277453/src/validation.cpp#L848 and absolute fee checks is done on original transactions and their descendants, should we document all of them ?
Well in fact there is another well-known supplemental check implemented by our logic not mentioned in the BIP, which is the feerate comparison here :
and absolute fee checks is done on original transactions and their descendants, should we document all of them ?
Not sure about this. According to my understanding fee rate and fees both should be higher in replacement transaction.
Example: Tx1 has only 1 input and two outputs (RBF enabled) Tx2 is replacement transaction for Tx1 which has 5 inputs and 2 outputs.
It is possible that Tx2 uses a lower fee rate compared to Tx1 but more fees. Miners normally prioritize based on fee rate so a higher fee rate would be better.
However, if there is a difference in BIP and the implementation it should be mentioned in doc/bip.md IMO
ACK 2eb0eeda39cab997f9a5f817f7c12e7fffeaf35a
Is a fix for the actual CVE being worked on as well?
Yes, unless you have a strong opinion here, I prefer differences between BIPs and the implementation in another PR, like one adding test coverage for incremental relay fee. @benthecarman
Not really, as a fix, I’m currently studying moving towards full-rbf. If we do so, there wouldn’t be any kind of signaling to proceed at all.
IMO, main motivation to not fix it is avoiding increasing the DoS surface of the mempool, where at replacement transaction submission you might have to traverse N * package_ancestor_limits
. With N your number of allowed inputs for a MAX_STANDARD_TX_WEIGHT
-sized transaction.
IMO, main motivation to not fix it is avoiding increasing the DoS surface of the mempool, where at replacement transaction submission you might have to traverse N *
package_ancestor_limits
. With N your number of allowed inputs for aMAX_STANDARD_TX_WEIGHT
-sized transaction.
Adding a bool rbf_enabled
to CTxMemPoolEntry
that’s true if the tx signals for rbf or any of its inputs have rbf_enabled == true
would remove that problem fairly easily, as far as I can see.
@MarcoFalke I believe you’re aware of few mempool-related RPCs which should be updated in consequence as follow-up of this PR?
Sure, filed as #22209
IIUC we would have to re-order RBF opt-in checks after fetching parents coins from the cache in PreChecks
, but I don’t believe such re-order would change anything in DoS prevention strategy around mempool acceptance. Any CTxMemPoolEntry
would inherit rbf_enable == true
if one of its parent entries have the flag sets so, that way you only check first-depth of ancestors of the replaced transactions.
At first sight, I don’t see any blocker for this approach, though I was planning to propose soon moving towards full-rbf. Beyond simplifying a bit PreChecks
code sanity, opt-in RBF raises issues for multi-party funded transactions. If full-rbf turns as a flames war like last time, we can consider again this approach to fix the discrepancy between the spec and our code.