/ test / functional / mempool_accept_v3.py
mempool_accept_v3.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2024 The Bitcoin Core developers
  3  # Distributed under the MIT software license, see the accompanying
  4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5  from decimal import Decimal
  6  
  7  from test_framework.messages import (
  8      MAX_BIP125_RBF_SEQUENCE,
  9  )
 10  from test_framework.test_framework import BitcoinTestFramework
 11  from test_framework.util import (
 12      assert_equal,
 13      assert_greater_than,
 14      assert_greater_than_or_equal,
 15      assert_raises_rpc_error,
 16  )
 17  from test_framework.wallet import (
 18      COIN,
 19      DEFAULT_FEE,
 20      MiniWallet,
 21  )
 22  
 23  MAX_REPLACEMENT_CANDIDATES = 100
 24  
 25  def cleanup(extra_args=None):
 26      def decorator(func):
 27          def wrapper(self):
 28              try:
 29                  if extra_args is not None:
 30                      self.restart_node(0, extra_args=extra_args)
 31                  func(self)
 32              finally:
 33                  # Clear mempool again after test
 34                  self.generate(self.nodes[0], 1)
 35                  if extra_args is not None:
 36                      self.restart_node(0)
 37          return wrapper
 38      return decorator
 39  
 40  class MempoolAcceptV3(BitcoinTestFramework):
 41      def set_test_params(self):
 42          self.num_nodes = 1
 43          self.extra_args = [["-acceptnonstdtxn=1"]]
 44          self.setup_clean_chain = True
 45  
 46      def check_mempool(self, txids):
 47          """Assert exact contents of the node's mempool (by txid)."""
 48          mempool_contents = self.nodes[0].getrawmempool()
 49          assert_equal(len(txids), len(mempool_contents))
 50          assert all([txid in txids for txid in mempool_contents])
 51  
 52      @cleanup(extra_args=["-datacarriersize=1000", "-acceptnonstdtxn=1"])
 53      def test_v3_acceptance(self):
 54          node = self.nodes[0]
 55          self.log.info("Test a child of a v3 transaction cannot be more than 1000vB")
 56          tx_v3_parent_normal = self.wallet.send_self_transfer(from_node=node, version=3)
 57          self.check_mempool([tx_v3_parent_normal["txid"]])
 58          tx_v3_child_heavy = self.wallet.create_self_transfer(
 59              utxo_to_spend=tx_v3_parent_normal["new_utxo"],
 60              target_weight=4004,
 61              version=3
 62          )
 63          assert_greater_than_or_equal(tx_v3_child_heavy["tx"].get_vsize(), 1000)
 64          expected_error_child_heavy = f"v3-rule-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big"
 65          assert_raises_rpc_error(-26, expected_error_child_heavy, node.sendrawtransaction, tx_v3_child_heavy["hex"])
 66          self.check_mempool([tx_v3_parent_normal["txid"]])
 67          # tx has no descendants
 68          assert_equal(node.getmempoolentry(tx_v3_parent_normal["txid"])["descendantcount"], 1)
 69  
 70          self.log.info("Test that, during replacements, only the new transaction counts for v3 descendant limit")
 71          tx_v3_child_almost_heavy = self.wallet.send_self_transfer(
 72              from_node=node,
 73              fee_rate=DEFAULT_FEE,
 74              utxo_to_spend=tx_v3_parent_normal["new_utxo"],
 75              target_weight=3987,
 76              version=3
 77          )
 78          assert_greater_than_or_equal(1000, tx_v3_child_almost_heavy["tx"].get_vsize())
 79          self.check_mempool([tx_v3_parent_normal["txid"], tx_v3_child_almost_heavy["txid"]])
 80          assert_equal(node.getmempoolentry(tx_v3_parent_normal["txid"])["descendantcount"], 2)
 81          tx_v3_child_almost_heavy_rbf = self.wallet.send_self_transfer(
 82              from_node=node,
 83              fee_rate=DEFAULT_FEE * 2,
 84              utxo_to_spend=tx_v3_parent_normal["new_utxo"],
 85              target_weight=3500,
 86              version=3
 87          )
 88          assert_greater_than_or_equal(tx_v3_child_almost_heavy["tx"].get_vsize() + tx_v3_child_almost_heavy_rbf["tx"].get_vsize(), 1000)
 89          self.check_mempool([tx_v3_parent_normal["txid"], tx_v3_child_almost_heavy_rbf["txid"]])
 90          assert_equal(node.getmempoolentry(tx_v3_parent_normal["txid"])["descendantcount"], 2)
 91  
 92      @cleanup(extra_args=["-acceptnonstdtxn=1"])
 93      def test_v3_replacement(self):
 94          node = self.nodes[0]
 95          self.log.info("Test v3 transactions may be replaced by v3 transactions")
 96          utxo_v3_bip125 = self.wallet.get_utxo()
 97          tx_v3_bip125 = self.wallet.send_self_transfer(
 98              from_node=node,
 99              fee_rate=DEFAULT_FEE,
100              utxo_to_spend=utxo_v3_bip125,
101              sequence=MAX_BIP125_RBF_SEQUENCE,
102              version=3
103          )
104          self.check_mempool([tx_v3_bip125["txid"]])
105  
106          tx_v3_bip125_rbf = self.wallet.send_self_transfer(
107              from_node=node,
108              fee_rate=DEFAULT_FEE * 2,
109              utxo_to_spend=utxo_v3_bip125,
110              version=3
111          )
112          self.check_mempool([tx_v3_bip125_rbf["txid"]])
113  
114          self.log.info("Test v3 transactions may be replaced by V2 transactions")
115          tx_v3_bip125_rbf_v2 = self.wallet.send_self_transfer(
116              from_node=node,
117              fee_rate=DEFAULT_FEE * 3,
118              utxo_to_spend=utxo_v3_bip125,
119              version=2
120          )
121          self.check_mempool([tx_v3_bip125_rbf_v2["txid"]])
122  
123          self.log.info("Test that replacements cannot cause violation of inherited v3")
124          utxo_v3_parent = self.wallet.get_utxo()
125          tx_v3_parent = self.wallet.send_self_transfer(
126              from_node=node,
127              fee_rate=DEFAULT_FEE,
128              utxo_to_spend=utxo_v3_parent,
129              version=3
130          )
131          tx_v3_child = self.wallet.send_self_transfer(
132              from_node=node,
133              fee_rate=DEFAULT_FEE,
134              utxo_to_spend=tx_v3_parent["new_utxo"],
135              version=3
136          )
137          self.check_mempool([tx_v3_bip125_rbf_v2["txid"], tx_v3_parent["txid"], tx_v3_child["txid"]])
138  
139          tx_v3_child_rbf_v2 = self.wallet.create_self_transfer(
140              fee_rate=DEFAULT_FEE * 2,
141              utxo_to_spend=tx_v3_parent["new_utxo"],
142              version=2
143          )
144          expected_error_v2_v3 = f"v3-rule-violation, non-v3 tx {tx_v3_child_rbf_v2['txid']} (wtxid={tx_v3_child_rbf_v2['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})"
145          assert_raises_rpc_error(-26, expected_error_v2_v3, node.sendrawtransaction, tx_v3_child_rbf_v2["hex"])
146          self.check_mempool([tx_v3_bip125_rbf_v2["txid"], tx_v3_parent["txid"], tx_v3_child["txid"]])
147  
148  
149      @cleanup(extra_args=["-acceptnonstdtxn=1"])
150      def test_v3_bip125(self):
151          node = self.nodes[0]
152          self.log.info("Test v3 transactions that don't signal BIP125 are replaceable")
153          assert_equal(node.getmempoolinfo()["fullrbf"], False)
154          utxo_v3_no_bip125 = self.wallet.get_utxo()
155          tx_v3_no_bip125 = self.wallet.send_self_transfer(
156              from_node=node,
157              fee_rate=DEFAULT_FEE,
158              utxo_to_spend=utxo_v3_no_bip125,
159              sequence=MAX_BIP125_RBF_SEQUENCE + 1,
160              version=3
161          )
162  
163          self.check_mempool([tx_v3_no_bip125["txid"]])
164          assert not node.getmempoolentry(tx_v3_no_bip125["txid"])["bip125-replaceable"]
165          tx_v3_no_bip125_rbf = self.wallet.send_self_transfer(
166              from_node=node,
167              fee_rate=DEFAULT_FEE * 2,
168              utxo_to_spend=utxo_v3_no_bip125,
169              version=3
170          )
171          self.check_mempool([tx_v3_no_bip125_rbf["txid"]])
172  
173      @cleanup(extra_args=["-datacarriersize=40000", "-acceptnonstdtxn=1"])
174      def test_v3_reorg(self):
175          node = self.nodes[0]
176          self.log.info("Test that, during a reorg, v3 rules are not enforced")
177          tx_v2_block = self.wallet.send_self_transfer(from_node=node, version=2)
178          tx_v3_block = self.wallet.send_self_transfer(from_node=node, version=3)
179          tx_v3_block2 = self.wallet.send_self_transfer(from_node=node, version=3)
180          self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"]])
181  
182          block = self.generate(node, 1)
183          self.check_mempool([])
184          tx_v2_from_v3 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block["new_utxo"], version=2)
185          tx_v3_from_v2 = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v2_block["new_utxo"], version=3)
186          tx_v3_child_large = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=tx_v3_block2["new_utxo"], target_weight=5000, version=3)
187          assert_greater_than(node.getmempoolentry(tx_v3_child_large["txid"])["vsize"], 1000)
188          self.check_mempool([tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
189          node.invalidateblock(block[0])
190          self.check_mempool([tx_v3_block["txid"], tx_v2_block["txid"], tx_v3_block2["txid"], tx_v2_from_v3["txid"], tx_v3_from_v2["txid"], tx_v3_child_large["txid"]])
191          # This is needed because generate() will create the exact same block again.
192          node.reconsiderblock(block[0])
193  
194  
195      @cleanup(extra_args=["-limitdescendantsize=10", "-datacarriersize=40000", "-acceptnonstdtxn=1"])
196      def test_nondefault_package_limits(self):
197          """
198          Max standard tx size + v3 rules imply the ancestor/descendant rules (at their default
199          values), but those checks must not be skipped. Ensure both sets of checks are done by
200          changing the ancestor/descendant limit configurations.
201          """
202          node = self.nodes[0]
203          self.log.info("Test that a decreased limitdescendantsize also applies to v3 child")
204          tx_v3_parent_large1 = self.wallet.send_self_transfer(from_node=node, target_weight=99900, version=3)
205          tx_v3_child_large1 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent_large1["new_utxo"], version=3)
206          # Child is within v3 limits, but parent's descendant limit is exceeded
207          assert_greater_than(1000, tx_v3_child_large1["tx"].get_vsize())
208          assert_raises_rpc_error(-26, f"too-long-mempool-chain, exceeds descendant size limit for tx {tx_v3_parent_large1['txid']}", node.sendrawtransaction, tx_v3_child_large1["hex"])
209          self.check_mempool([tx_v3_parent_large1["txid"]])
210          assert_equal(node.getmempoolentry(tx_v3_parent_large1["txid"])["descendantcount"], 1)
211          self.generate(node, 1)
212  
213          self.log.info("Test that a decreased limitancestorsize also applies to v3 parent")
214          self.restart_node(0, extra_args=["-limitancestorsize=10", "-datacarriersize=40000", "-acceptnonstdtxn=1"])
215          tx_v3_parent_large2 = self.wallet.send_self_transfer(from_node=node, target_weight=99900, version=3)
216          tx_v3_child_large2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent_large2["new_utxo"], version=3)
217          # Child is within v3 limits
218          assert_greater_than_or_equal(1000, tx_v3_child_large2["tx"].get_vsize())
219          assert_raises_rpc_error(-26, f"too-long-mempool-chain, exceeds ancestor size limit", node.sendrawtransaction, tx_v3_child_large2["hex"])
220          self.check_mempool([tx_v3_parent_large2["txid"]])
221  
222      @cleanup(extra_args=["-datacarriersize=1000", "-acceptnonstdtxn=1"])
223      def test_v3_ancestors_package(self):
224          self.log.info("Test that v3 ancestor limits are checked within the package")
225          node = self.nodes[0]
226          tx_v3_parent_normal = self.wallet.create_self_transfer(
227              fee_rate=0,
228              target_weight=4004,
229              version=3
230          )
231          tx_v3_parent_2_normal = self.wallet.create_self_transfer(
232              fee_rate=0,
233              target_weight=4004,
234              version=3
235          )
236          tx_v3_child_multiparent = self.wallet.create_self_transfer_multi(
237              utxos_to_spend=[tx_v3_parent_normal["new_utxo"], tx_v3_parent_2_normal["new_utxo"]],
238              fee_per_output=10000,
239              version=3
240          )
241          tx_v3_child_heavy = self.wallet.create_self_transfer_multi(
242              utxos_to_spend=[tx_v3_parent_normal["new_utxo"]],
243              target_weight=4004,
244              fee_per_output=10000,
245              version=3
246          )
247  
248          self.check_mempool([])
249          result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_parent_2_normal["hex"], tx_v3_child_multiparent["hex"]])
250          assert_equal(result['package_msg'], f"v3-violation, tx {tx_v3_child_multiparent['txid']} (wtxid={tx_v3_child_multiparent['wtxid']}) would have too many ancestors")
251          self.check_mempool([])
252  
253          self.check_mempool([])
254          result = node.submitpackage([tx_v3_parent_normal["hex"], tx_v3_child_heavy["hex"]])
255          # tx_v3_child_heavy is heavy based on weight, not sigops.
256          assert_equal(result['package_msg'], f"v3-violation, v3 child tx {tx_v3_child_heavy['txid']} (wtxid={tx_v3_child_heavy['wtxid']}) is too big: {tx_v3_child_heavy['tx'].get_vsize()} > 1000 virtual bytes")
257          self.check_mempool([])
258  
259          tx_v3_parent = self.wallet.create_self_transfer(version=3)
260          tx_v3_child = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxo"], version=3)
261          tx_v3_grandchild = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_child["new_utxo"], version=3)
262          result = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child["hex"], tx_v3_grandchild["hex"]])
263          assert all([txresult["package-error"] == f"v3-violation, tx {tx_v3_grandchild['txid']} (wtxid={tx_v3_grandchild['wtxid']}) would have too many ancestors" for txresult in result])
264  
265      @cleanup(extra_args=["-acceptnonstdtxn=1"])
266      def test_v3_ancestors_package_and_mempool(self):
267          """
268          A v3 transaction in a package cannot have 2 v3 parents.
269          Test that if we have a transaction graph A -> B -> C, where A, B, C are
270          all v3 transactions, that we cannot use submitpackage to get the
271          transactions all into the mempool.
272  
273          Verify, in particular, that if A is already in the mempool, then
274          submitpackage(B, C) will fail.
275          """
276          node = self.nodes[0]
277          self.log.info("Test that v3 ancestor limits include transactions within the package and all in-mempool ancestors")
278          # This is our transaction "A":
279          tx_in_mempool = self.wallet.send_self_transfer(from_node=node, version=3)
280  
281          # Verify that A is in the mempool
282          self.check_mempool([tx_in_mempool["txid"]])
283  
284          # tx_0fee_parent is our transaction "B"; just create it.
285          tx_0fee_parent = self.wallet.create_self_transfer(utxo_to_spend=tx_in_mempool["new_utxo"], fee=0, fee_rate=0, version=3)
286  
287          # tx_child_violator is our transaction "C"; create it:
288          tx_child_violator = self.wallet.create_self_transfer_multi(utxos_to_spend=[tx_0fee_parent["new_utxo"]], version=3)
289  
290          # submitpackage(B, C) should fail
291          result = node.submitpackage([tx_0fee_parent["hex"], tx_child_violator["hex"]])
292          assert_equal(result['package_msg'], f"v3-violation, tx {tx_child_violator['txid']} (wtxid={tx_child_violator['wtxid']}) would have too many ancestors")
293          self.check_mempool([tx_in_mempool["txid"]])
294  
295      @cleanup(extra_args=["-acceptnonstdtxn=1"])
296      def test_sibling_eviction_package(self):
297          """
298          When a transaction has a mempool sibling, it may be eligible for sibling eviction.
299          However, this option is only available in single transaction acceptance. It doesn't work in
300          a multi-testmempoolaccept (where RBF is disabled) or when doing package CPFP.
301          """
302          self.log.info("Test v3 sibling eviction in submitpackage and multi-testmempoolaccept")
303          node = self.nodes[0]
304          # Add a parent + child to mempool
305          tx_mempool_parent = self.wallet.send_self_transfer_multi(
306              from_node=node,
307              utxos_to_spend=[self.wallet.get_utxo()],
308              num_outputs=2,
309              version=3
310          )
311          tx_mempool_sibling = self.wallet.send_self_transfer(
312              from_node=node,
313              utxo_to_spend=tx_mempool_parent["new_utxos"][0],
314              version=3
315          )
316          self.check_mempool([tx_mempool_parent["txid"], tx_mempool_sibling["txid"]])
317  
318          tx_sibling_1 = self.wallet.create_self_transfer(
319              utxo_to_spend=tx_mempool_parent["new_utxos"][1],
320              version=3,
321              fee_rate=DEFAULT_FEE*100,
322          )
323          tx_has_mempool_uncle = self.wallet.create_self_transfer(utxo_to_spend=tx_sibling_1["new_utxo"], version=3)
324  
325          tx_sibling_2 = self.wallet.create_self_transfer(
326              utxo_to_spend=tx_mempool_parent["new_utxos"][0],
327              version=3,
328              fee_rate=DEFAULT_FEE*200,
329          )
330  
331          tx_sibling_3 = self.wallet.create_self_transfer(
332              utxo_to_spend=tx_mempool_parent["new_utxos"][1],
333              version=3,
334              fee_rate=0,
335          )
336          tx_bumps_parent_with_sibling = self.wallet.create_self_transfer(
337              utxo_to_spend=tx_sibling_3["new_utxo"],
338              version=3,
339              fee_rate=DEFAULT_FEE*300,
340          )
341  
342          # Fails with another non-related transaction via testmempoolaccept
343          tx_unrelated = self.wallet.create_self_transfer(version=3)
344          result_test_unrelated = node.testmempoolaccept([tx_sibling_1["hex"], tx_unrelated["hex"]])
345          assert_equal(result_test_unrelated[0]["reject-reason"], "v3-rule-violation")
346  
347          # Fails in a package via testmempoolaccept
348          result_test_1p1c = node.testmempoolaccept([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
349          assert_equal(result_test_1p1c[0]["reject-reason"], "v3-rule-violation")
350  
351          # Allowed when tx is submitted in a package and evaluated individually.
352          # Note that the child failed since it would be the 3rd generation.
353          result_package_indiv = node.submitpackage([tx_sibling_1["hex"], tx_has_mempool_uncle["hex"]])
354          self.check_mempool([tx_mempool_parent["txid"], tx_sibling_1["txid"]])
355          expected_error_gen3 = f"v3-rule-violation, tx {tx_has_mempool_uncle['txid']} (wtxid={tx_has_mempool_uncle['wtxid']}) would have too many ancestors"
356  
357          assert_equal(result_package_indiv["tx-results"][tx_has_mempool_uncle['wtxid']]['error'], expected_error_gen3)
358  
359          # Allowed when tx is submitted in a package with in-mempool parent (which is deduplicated).
360          node.submitpackage([tx_mempool_parent["hex"], tx_sibling_2["hex"]])
361          self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
362  
363          # Child cannot pay for sibling eviction for parent, as it violates v3 topology limits
364          result_package_cpfp = node.submitpackage([tx_sibling_3["hex"], tx_bumps_parent_with_sibling["hex"]])
365          self.check_mempool([tx_mempool_parent["txid"], tx_sibling_2["txid"]])
366          expected_error_cpfp = f"v3-rule-violation, tx {tx_mempool_parent['txid']} (wtxid={tx_mempool_parent['wtxid']}) would exceed descendant count limit"
367  
368          assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp)
369  
370  
371      @cleanup(extra_args=["-datacarriersize=1000", "-acceptnonstdtxn=1"])
372      def test_v3_package_inheritance(self):
373          self.log.info("Test that v3 inheritance is checked within package")
374          node = self.nodes[0]
375          tx_v3_parent = self.wallet.create_self_transfer(
376              fee_rate=0,
377              target_weight=4004,
378              version=3
379          )
380          tx_v2_child = self.wallet.create_self_transfer_multi(
381              utxos_to_spend=[tx_v3_parent["new_utxo"]],
382              fee_per_output=10000,
383              version=2
384          )
385          self.check_mempool([])
386          result = node.submitpackage([tx_v3_parent["hex"], tx_v2_child["hex"]])
387          assert_equal(result['package_msg'], f"v3-violation, non-v3 tx {tx_v2_child['txid']} (wtxid={tx_v2_child['wtxid']}) cannot spend from v3 tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']})")
388          self.check_mempool([])
389  
390      @cleanup(extra_args=["-acceptnonstdtxn=1"])
391      def test_v3_in_testmempoolaccept(self):
392          node = self.nodes[0]
393  
394          self.log.info("Test that v3 inheritance is accurately assessed in testmempoolaccept")
395          tx_v2 = self.wallet.create_self_transfer(version=2)
396          tx_v2_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=2)
397          tx_v3_from_v2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v2["new_utxo"], version=3)
398          tx_v3 = self.wallet.create_self_transfer(version=3)
399          tx_v2_from_v3 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3["new_utxo"], version=2)
400          tx_v3_from_v3 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3["new_utxo"], version=3)
401  
402          # testmempoolaccept paths don't require child-with-parents topology. Ensure that topology
403          # assumptions aren't made in inheritance checks.
404          test_accept_v2_and_v3 = node.testmempoolaccept([tx_v2["hex"], tx_v3["hex"]])
405          assert all([result["allowed"] for result in test_accept_v2_and_v3])
406  
407          test_accept_v3_from_v2 = node.testmempoolaccept([tx_v2["hex"], tx_v3_from_v2["hex"]])
408          expected_error_v3_from_v2 = f"v3-violation, v3 tx {tx_v3_from_v2['txid']} (wtxid={tx_v3_from_v2['wtxid']}) cannot spend from non-v3 tx {tx_v2['txid']} (wtxid={tx_v2['wtxid']})"
409          assert all([result["package-error"] == expected_error_v3_from_v2 for result in test_accept_v3_from_v2])
410  
411          test_accept_v2_from_v3 = node.testmempoolaccept([tx_v3["hex"], tx_v2_from_v3["hex"]])
412          expected_error_v2_from_v3 = f"v3-violation, non-v3 tx {tx_v2_from_v3['txid']} (wtxid={tx_v2_from_v3['wtxid']}) cannot spend from v3 tx {tx_v3['txid']} (wtxid={tx_v3['wtxid']})"
413          assert all([result["package-error"] == expected_error_v2_from_v3 for result in test_accept_v2_from_v3])
414  
415          test_accept_pairs = node.testmempoolaccept([tx_v2["hex"], tx_v3["hex"], tx_v2_from_v2["hex"], tx_v3_from_v3["hex"]])
416          assert all([result["allowed"] for result in test_accept_pairs])
417  
418          self.log.info("Test that descendant violations are caught in testmempoolaccept")
419          tx_v3_independent = self.wallet.create_self_transfer(version=3)
420          tx_v3_parent = self.wallet.create_self_transfer_multi(num_outputs=2, version=3)
421          tx_v3_child_1 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][0], version=3)
422          tx_v3_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_v3_parent["new_utxos"][1], version=3)
423          test_accept_2children = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"]])
424          expected_error_2children = f"v3-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
425          assert all([result["package-error"] == expected_error_2children for result in test_accept_2children])
426  
427          # Extra v3 transaction does not get incorrectly marked as extra descendant
428          test_accept_1child_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_independent["hex"]])
429          assert all([result["allowed"] for result in test_accept_1child_with_exra])
430  
431          # Extra v3 transaction does not make us ignore the extra descendant
432          test_accept_2children_with_exra = node.testmempoolaccept([tx_v3_parent["hex"], tx_v3_child_1["hex"], tx_v3_child_2["hex"], tx_v3_independent["hex"]])
433          expected_error_extra = f"v3-violation, tx {tx_v3_parent['txid']} (wtxid={tx_v3_parent['wtxid']}) would exceed descendant count limit"
434          assert all([result["package-error"] == expected_error_extra for result in test_accept_2children_with_exra])
435          # Same result if the parent is already in mempool
436          node.sendrawtransaction(tx_v3_parent["hex"])
437          test_accept_2children_with_in_mempool_parent = node.testmempoolaccept([tx_v3_child_1["hex"], tx_v3_child_2["hex"]])
438          assert all([result["package-error"] == expected_error_extra for result in test_accept_2children_with_in_mempool_parent])
439  
440      @cleanup(extra_args=["-acceptnonstdtxn=1"])
441      def test_reorg_2child_rbf(self):
442          node = self.nodes[0]
443          self.log.info("Test that children of a v3 transaction can be replaced individually, even if there are multiple due to reorg")
444  
445          ancestor_tx = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)
446          self.check_mempool([ancestor_tx["txid"]])
447  
448          block = self.generate(node, 1)[0]
449          self.check_mempool([])
450  
451          child_1 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=ancestor_tx["new_utxos"][0])
452          child_2 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=ancestor_tx["new_utxos"][1])
453          self.check_mempool([child_1["txid"], child_2["txid"]])
454  
455          self.generate(node, 1)
456          self.check_mempool([])
457  
458          # Create a reorg, causing ancestor_tx to exceed the 1-child limit
459          node.invalidateblock(block)
460          self.check_mempool([ancestor_tx["txid"], child_1["txid"], child_2["txid"]])
461          assert_equal(node.getmempoolentry(ancestor_tx["txid"])["descendantcount"], 3)
462  
463          # Create a replacement of child_1. It does not conflict with child_2.
464          child_1_conflict = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=ancestor_tx["new_utxos"][0], fee_rate=Decimal("0.01"))
465  
466          # Ensure child_1 and child_1_conflict are different transactions
467          assert (child_1_conflict["txid"] != child_1["txid"])
468          self.check_mempool([ancestor_tx["txid"], child_1_conflict["txid"], child_2["txid"]])
469          assert_equal(node.getmempoolentry(ancestor_tx["txid"])["descendantcount"], 3)
470  
471      @cleanup(extra_args=["-acceptnonstdtxn=1"])
472      def test_v3_sibling_eviction(self):
473          self.log.info("Test sibling eviction for v3")
474          node = self.nodes[0]
475          tx_v3_parent = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=2, version=3)
476          # This is the sibling to replace
477          tx_v3_child_1 = self.wallet.send_self_transfer(
478              from_node=node, utxo_to_spend=tx_v3_parent["new_utxos"][0], fee_rate=DEFAULT_FEE * 2, version=3
479          )
480          assert tx_v3_child_1["txid"] in node.getrawmempool()
481  
482          self.log.info("Test tx must be higher feerate than sibling to evict it")
483          tx_v3_child_2_rule6 = self.wallet.create_self_transfer(
484              utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=DEFAULT_FEE, version=3
485          )
486          rule6_str = f"insufficient fee (including sibling eviction), rejecting replacement {tx_v3_child_2_rule6['txid']}; new feerate"
487          assert_raises_rpc_error(-26, rule6_str, node.sendrawtransaction, tx_v3_child_2_rule6["hex"])
488          self.check_mempool([tx_v3_parent['txid'], tx_v3_child_1['txid']])
489  
490          self.log.info("Test tx must meet absolute fee rules to evict sibling")
491          tx_v3_child_2_rule4 = self.wallet.create_self_transfer(
492              utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=2 * DEFAULT_FEE + Decimal("0.00000001"), version=3
493          )
494          rule4_str = f"insufficient fee (including sibling eviction), rejecting replacement {tx_v3_child_2_rule4['txid']}, not enough additional fees to relay"
495          assert_raises_rpc_error(-26, rule4_str, node.sendrawtransaction, tx_v3_child_2_rule4["hex"])
496          self.check_mempool([tx_v3_parent['txid'], tx_v3_child_1['txid']])
497  
498          self.log.info("Test tx cannot cause more than 100 evictions including RBF and sibling eviction")
499          # First add 4 groups of 25 transactions.
500          utxos_for_conflict = []
501          txids_v2_100 = []
502          for _ in range(4):
503              confirmed_utxo = self.wallet.get_utxo(confirmed_only=True)
504              utxos_for_conflict.append(confirmed_utxo)
505              # 25 is within descendant limits
506              chain_length = int(MAX_REPLACEMENT_CANDIDATES / 4)
507              chain = self.wallet.create_self_transfer_chain(chain_length=chain_length, utxo_to_spend=confirmed_utxo)
508              for item in chain:
509                  txids_v2_100.append(item["txid"])
510                  node.sendrawtransaction(item["hex"])
511          self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_1["txid"]])
512  
513          # Replacing 100 transactions is fine
514          tx_v3_replacement_only = self.wallet.create_self_transfer_multi(utxos_to_spend=utxos_for_conflict, fee_per_output=4000000)
515          # Override maxfeerate - it costs a lot to replace these 100 transactions.
516          assert node.testmempoolaccept([tx_v3_replacement_only["hex"]], maxfeerate=0)[0]["allowed"]
517          # Adding another one exceeds the limit.
518          utxos_for_conflict.append(tx_v3_parent["new_utxos"][1])
519          tx_v3_child_2_rule5 = self.wallet.create_self_transfer_multi(utxos_to_spend=utxos_for_conflict, fee_per_output=4000000, version=3)
520          rule5_str = f"too many potential replacements (including sibling eviction), rejecting replacement {tx_v3_child_2_rule5['txid']}; too many potential replacements (101 > 100)"
521          assert_raises_rpc_error(-26, rule5_str, node.sendrawtransaction, tx_v3_child_2_rule5["hex"])
522          self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_1["txid"]])
523  
524          self.log.info("Test sibling eviction is successful if it meets all RBF rules")
525          tx_v3_child_2 = self.wallet.create_self_transfer(
526              utxo_to_spend=tx_v3_parent["new_utxos"][1], fee_rate=DEFAULT_FEE*10, version=3
527          )
528          node.sendrawtransaction(tx_v3_child_2["hex"])
529          self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_2["txid"]])
530  
531          self.log.info("Test that it's possible to do a sibling eviction and RBF at the same time")
532          utxo_unrelated_conflict = self.wallet.get_utxo(confirmed_only=True)
533          tx_unrelated_replacee = self.wallet.send_self_transfer(from_node=node, utxo_to_spend=utxo_unrelated_conflict)
534          assert tx_unrelated_replacee["txid"] in node.getrawmempool()
535  
536          fee_to_beat_child2 = int(tx_v3_child_2["fee"] * COIN)
537  
538          tx_v3_child_3 = self.wallet.create_self_transfer_multi(
539              utxos_to_spend=[tx_v3_parent["new_utxos"][0], utxo_unrelated_conflict], fee_per_output=fee_to_beat_child2*5, version=3
540          )
541          node.sendrawtransaction(tx_v3_child_3["hex"])
542          self.check_mempool(txids_v2_100 + [tx_v3_parent["txid"], tx_v3_child_3["txid"]])
543  
544      @cleanup(extra_args=["-acceptnonstdtxn=1"])
545      def test_reorg_sibling_eviction_1p2c(self):
546          node = self.nodes[0]
547          self.log.info("Test that sibling eviction is not allowed when multiple siblings exist")
548  
549          tx_with_multi_children = self.wallet.send_self_transfer_multi(from_node=node, num_outputs=3, version=3, confirmed_only=True)
550          self.check_mempool([tx_with_multi_children["txid"]])
551  
552          block_to_disconnect = self.generate(node, 1)[0]
553          self.check_mempool([])
554  
555          tx_with_sibling1 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=tx_with_multi_children["new_utxos"][0])
556          tx_with_sibling2 = self.wallet.send_self_transfer(from_node=node, version=3, utxo_to_spend=tx_with_multi_children["new_utxos"][1])
557          self.check_mempool([tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
558  
559          # Create a reorg, bringing tx_with_multi_children back into the mempool with a descendant count of 3.
560          node.invalidateblock(block_to_disconnect)
561          self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling1["txid"], tx_with_sibling2["txid"]])
562          assert_equal(node.getmempoolentry(tx_with_multi_children["txid"])["descendantcount"], 3)
563  
564          # Sibling eviction is not allowed because there are two siblings
565          tx_with_sibling3 = self.wallet.create_self_transfer(
566              version=3,
567              utxo_to_spend=tx_with_multi_children["new_utxos"][2],
568              fee_rate=DEFAULT_FEE*50
569          )
570          expected_error_2siblings = f"v3-rule-violation, tx {tx_with_multi_children['txid']} (wtxid={tx_with_multi_children['wtxid']}) would exceed descendant count limit"
571          assert_raises_rpc_error(-26, expected_error_2siblings, node.sendrawtransaction, tx_with_sibling3["hex"])
572  
573          # However, an RBF (with conflicting inputs) is possible even if the resulting cluster size exceeds 2
574          tx_with_sibling3_rbf = self.wallet.send_self_transfer(
575              from_node=node,
576              version=3,
577              utxo_to_spend=tx_with_multi_children["new_utxos"][0],
578              fee_rate=DEFAULT_FEE*50
579          )
580          self.check_mempool([tx_with_multi_children["txid"], tx_with_sibling3_rbf["txid"], tx_with_sibling2["txid"]])
581  
582  
583      def run_test(self):
584          self.log.info("Generate blocks to create UTXOs")
585          node = self.nodes[0]
586          self.wallet = MiniWallet(node)
587          self.generate(self.wallet, 120)
588          self.test_v3_acceptance()
589          self.test_v3_replacement()
590          self.test_v3_bip125()
591          self.test_v3_reorg()
592          self.test_nondefault_package_limits()
593          self.test_v3_ancestors_package()
594          self.test_v3_ancestors_package_and_mempool()
595          self.test_sibling_eviction_package()
596          self.test_v3_package_inheritance()
597          self.test_v3_in_testmempoolaccept()
598          self.test_reorg_2child_rbf()
599          self.test_v3_sibling_eviction()
600          self.test_reorg_sibling_eviction_1p2c()
601  
602  
603  if __name__ == "__main__":
604      MempoolAcceptV3().main()