Skip to content

Commit

Permalink
test: incorrect rbf status when max replacement limit exceeded
Browse files Browse the repository at this point in the history
Highlight that transactions may signal they are replaceable even
though they are not due to BIP125 RBF (Rule bitcoin#5).
  • Loading branch information
mjdietzx committed Dec 1, 2021
1 parent 25f1e8a commit 64c2040
Showing 1 changed file with 32 additions and 3 deletions.
35 changes: 32 additions & 3 deletions test/functional/feature_rbf.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ def run_test(self):
self.log.info("Running test prechecks overestimates replacements...")
self.test_prechecks_overestimates_replacements()

self.log.info("Running test reorged inherited signaling...")
self.test_reorged_inherited_signaling()
self.log.info("Running test reorged inherited signaling and descendant limit...")
self.test_reorged_inherited_signaling_and_descendant_limit()

self.log.info("Passed")

Expand Down Expand Up @@ -705,7 +705,7 @@ def test_prechecks_overestimates_replacements(self):
self.wallet.get_utxo(txid=tx['txid'])
self.wallet.get_utxo(txid=replacement_tx['txid'])

def test_reorged_inherited_signaling(self):
def test_reorged_inherited_signaling_and_descendant_limit(self):
confirmed_utxos = [self.wallet.get_utxo(), self.wallet.get_utxo()]

# One opt-in parent transaction, one opt-out
Expand Down Expand Up @@ -738,5 +738,34 @@ def test_reorged_inherited_signaling(self):
self.nodes[0].invalidateblock(hash)
assert_equal(True, self.nodes[0].getmempoolentry(joined_tx_txid)['bip125-replaceable'])

# Create a chain the size of `MAX_REPLACEMENT_LIMIT` spending `joined_tx` - the BIP125 Rule #5 imposed limit
tx = None
for _ in range(MAX_REPLACEMENT_LIMIT - 1):
tx = self.wallet.send_self_transfer(
from_node=self.nodes[0],
utxo_to_spend=joined_utxo if tx is None else self.wallet.get_utxo(txid=tx['txid']), # a straight line of descendants
sequence=0xffffffff,
fee_rate=Decimal('0.0001'),
)
assert_equal(True, self.nodes[0].getmempoolentry(tx['txid'])['bip125-replaceable']) # inherited
# Now we have a chain of: `optin_parent_tx`, `joined_tx`, and 99 txs. The last tx in the loop exceeded `MAX_REPLACEMENT_LIMIT`

# Attempting to replace the opt-in parent transaction will now result in more than `MAX_REPLACEMENT_LIMIT`
# conflicting txns being evicted from the mempool. However, it (and all of its descendants) are still signaling replaceability.
# We would've expected that once `MAX_REPLACEMENT_LIMIT` is exceeded, the opt-in parent txn stops signaling
# replaceability, along with _all_ of its descendants.
entry = self.nodes[0].getmempoolentry(optin_parent_tx["txid"])
assert_greater_than(entry['descendantcount'], MAX_REPLACEMENT_LIMIT)
assert_equal(True, entry['bip125-replaceable'])
assert_equal(True, self.nodes[0].getmempoolentry(tx["txid"])['bip125-replaceable'])

# Case in point, we can't actually replace `optin_parent_tx` once it has `MAX_REPLACEMENT_LIMIT` descendants
self.wallet.create_self_transfer(
from_node=self.nodes[0],
utxo_to_spend=confirmed_utxos[0],
fee_rate=Decimal('0.01'),
mempool_valid=False,
)

if __name__ == '__main__':
ReplaceByFeeTest().main()

0 comments on commit 64c2040

Please sign in to comment.