mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-08-28 06:54:19 +02:00
relax child-with-unconfirmed-parents rule
This rule was originally introduced along with a very early proposal for package relay as a way to verify that the "correct" child-with-unconfirmed-parents package was provided for a transaction, where correctness was defined as all of the transactions unconfirmed parents. However, we are not planning to introduce a protocol where peers would be asked to send these packages. This rule has downsides: if a transaction has multiple parents but only 1 that requires package CPFP to be accepted, the current rule prevents us from accepting that package. Even if the other parents are already in mempool, the p2p logic will only submit the 1p1c package, which fails this check. See the test in p2p_1p1c_network.py
This commit is contained in:
@@ -101,7 +101,7 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
package_hex_2, parent_2, child_2 = self.create_basic_1p1c(self.wallet_nonsegwit)
|
||||
|
||||
# 3: 2-parent-1-child package. Both parents are above mempool min feerate. No package submission happens.
|
||||
# We require packages to be child-with-unconfirmed-parents and only allow 1-parent-1-child packages.
|
||||
# We require packages to be child-with-parents and only allow 1-parent-1-child packages.
|
||||
package_hex_3, parent_31, _parent_32, child_3 = self.create_package_2p1c(self.wallet)
|
||||
|
||||
# 4: parent + child package where the child spends 2 different outputs from the parent.
|
||||
|
@@ -78,14 +78,14 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
"-maxmempool=5",
|
||||
]]
|
||||
|
||||
def create_tx_below_mempoolminfee(self, wallet):
|
||||
def create_tx_below_mempoolminfee(self, wallet, utxo_to_spend=None):
|
||||
"""Create a 1-input 1sat/vB transaction using a confirmed UTXO. Decrement and use
|
||||
self.sequence so that subsequent calls to this function result in unique transactions."""
|
||||
|
||||
self.sequence -= 1
|
||||
assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], FEERATE_1SAT_VB)
|
||||
|
||||
return wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, sequence=self.sequence, confirmed_only=True)
|
||||
return wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB, sequence=self.sequence, utxo_to_spend=utxo_to_spend, confirmed_only=True)
|
||||
|
||||
@cleanup
|
||||
def test_basic_child_then_parent(self):
|
||||
@@ -353,11 +353,14 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
|
||||
@cleanup
|
||||
def test_other_parent_in_mempool(self):
|
||||
self.log.info("Check opportunistic 1p1c fails if child already has another parent in mempool")
|
||||
self.log.info("Check opportunistic 1p1c works even if child already has another parent in mempool")
|
||||
node = self.nodes[0]
|
||||
|
||||
# Grandparent will enter mempool by itself
|
||||
grandparent_high = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True)
|
||||
|
||||
# This parent needs CPFP
|
||||
parent_low = self.create_tx_below_mempoolminfee(self.wallet)
|
||||
parent_low = self.create_tx_below_mempoolminfee(self.wallet, utxo_to_spend=grandparent_high["new_utxo"])
|
||||
# This parent does not need CPFP and can be submitted alone ahead of time
|
||||
parent_high = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True)
|
||||
child = self.wallet.create_self_transfer_multi(
|
||||
@@ -367,27 +370,27 @@ class PackageRelayTest(BitcoinTestFramework):
|
||||
|
||||
peer_sender = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="outbound-full-relay")
|
||||
|
||||
# 1. Send first parent which will be accepted.
|
||||
# 1. Send grandparent which is accepted
|
||||
peer_sender.send_and_ping(msg_tx(grandparent_high["tx"]))
|
||||
assert grandparent_high["txid"] in node.getrawmempool()
|
||||
|
||||
# 2. Send first parent which is accepted.
|
||||
peer_sender.send_and_ping(msg_tx(parent_high["tx"]))
|
||||
assert parent_high["txid"] in node.getrawmempool()
|
||||
|
||||
# 2. Send child.
|
||||
# 3. Send child which is handled as an orphan.
|
||||
peer_sender.send_and_ping(msg_tx(child["tx"]))
|
||||
|
||||
# 3. Node requests parent_low. However, 1p1c fails because package-not-child-with-unconfirmed-parents
|
||||
# 4. Node requests parent_low.
|
||||
parent_low_txid_int = int(parent_low["txid"], 16)
|
||||
peer_sender.wait_for_getdata([parent_low_txid_int])
|
||||
peer_sender.send_and_ping(msg_tx(parent_low["tx"]))
|
||||
|
||||
node_mempool = node.getrawmempool()
|
||||
assert grandparent_high["txid"] in node_mempool
|
||||
assert parent_high["txid"] in node_mempool
|
||||
assert parent_low["txid"] not in node_mempool
|
||||
assert child["txid"] not in node_mempool
|
||||
|
||||
# Same error if submitted through submitpackage without parent_high
|
||||
package_hex_missing_parent = [parent_low["hex"], child["hex"]]
|
||||
result_missing_parent = node.submitpackage(package_hex_missing_parent)
|
||||
assert_equal(result_missing_parent["package_msg"], "package-not-child-with-unconfirmed-parents")
|
||||
assert parent_low["txid"] in node_mempool
|
||||
assert child["txid"] in node_mempool
|
||||
|
||||
def create_small_orphan(self):
|
||||
"""Create small orphan transaction"""
|
||||
|
@@ -271,9 +271,11 @@ class RPCPackagesTest(BitcoinTestFramework):
|
||||
assert_equal(submitres, {'package_msg': 'conflict-in-package', 'tx-results': {}, 'replaced-transactions': []})
|
||||
assert tx_child["txid"] not in node.getrawmempool()
|
||||
|
||||
# ... and without the in-mempool ancestor tx1 included in the call
|
||||
# without the in-mempool ancestor tx1 included in the call, tx2 can be submitted, but
|
||||
# tx_child is missing an input.
|
||||
submitres = node.submitpackage([tx2["hex"], tx_child["hex"]])
|
||||
assert_equal(submitres, {'package_msg': 'package-not-child-with-unconfirmed-parents', 'tx-results': {}, 'replaced-transactions': []})
|
||||
assert_equal(submitres["tx-results"][tx_child["wtxid"]], {"txid": tx_child["txid"], "error": "bad-txns-inputs-missingorspent"})
|
||||
assert tx2["txid"] in node.getrawmempool()
|
||||
|
||||
# Regardless of error type, the child can never enter the mempool
|
||||
assert tx_child["txid"] not in node.getrawmempool()
|
||||
|
Reference in New Issue
Block a user