p2p_opportunistic_1p1c.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2024-present 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  """
   6  Test opportunistic 1p1c package submission logic.
   7  """
   8  
   9  from decimal import Decimal
  10  import random
  11  import time
  12  
  13  from test_framework.blocktools import MAX_STANDARD_TX_WEIGHT
  14  from test_framework.mempool_util import (
  15      create_large_orphan,
  16      DEFAULT_MIN_RELAY_TX_FEE,
  17      fill_mempool,
  18  )
  19  from test_framework.messages import (
  20      CInv,
  21      COIN,
  22      COutPoint,
  23      CTransaction,
  24      CTxIn,
  25      CTxOut,
  26      CTxInWitness,
  27      MAX_BIP125_RBF_SEQUENCE,
  28      MSG_WTX,
  29      msg_inv,
  30      msg_tx,
  31      tx_from_hex,
  32  )
  33  from test_framework.p2p import (
  34      NONPREF_PEER_TX_DELAY,
  35      P2PInterface,
  36      TXID_RELAY_DELAY,
  37  )
  38  from test_framework.script import (
  39      CScript,
  40      OP_NOP,
  41      OP_RETURN,
  42  )
  43  from test_framework.test_framework import BitcoinTestFramework
  44  from test_framework.util import (
  45      assert_equal,
  46      assert_greater_than,
  47      assert_greater_than_or_equal,
  48  )
  49  from test_framework.wallet import (
  50      MiniWallet,
  51      MiniWalletMode,
  52  )
  53  
  54  # 1sat/vB feerate denominated in BTC/KvB
  55  FEERATE_1SAT_VB = Decimal("0.00001000")
  56  # Number of seconds to wait to ensure no getdata is received
  57  GETDATA_WAIT = 60
  58  
  59  def cleanup(func):
  60      def wrapper(self, *args, **kwargs):
  61          func(self, *args, **kwargs)
  62  
  63          self.nodes[0].disconnect_p2ps()
  64          # Do not clear the node's mempool, as each test requires mempool min feerate > min
  65          # relay feerate. However, do check that this is the case.
  66          assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], self.nodes[0].getnetworkinfo()["relayfee"])
  67          # Ensure we do not try to spend the same UTXOs in subsequent tests, as they will look like RBF attempts.
  68          self.wallet.rescan_utxos(include_mempool=True)
  69  
  70          # Resets if mocktime was used
  71          self.nodes[0].setmocktime(0)
  72      return wrapper
  73  
  74  class PackageRelayTest(BitcoinTestFramework):
  75      def set_test_params(self):
  76          self.setup_clean_chain = True
  77          self.num_nodes = 1
  78          self.extra_args = [[
  79              "-maxmempool=5",
  80          ]]
  81  
  82      def create_tx_below_mempoolminfee(self, wallet, utxo_to_spend=None):
  83          """Create a 1-input 0.1sat/vB transaction using a confirmed UTXO. Decrement and use
  84          self.sequence so that subsequent calls to this function result in unique transactions."""
  85  
  86          self.sequence -= 1
  87          assert_greater_than(self.nodes[0].getmempoolinfo()["mempoolminfee"], Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN)
  88  
  89          return wallet.create_self_transfer(fee_rate=Decimal(DEFAULT_MIN_RELAY_TX_FEE) / COIN, sequence=self.sequence, utxo_to_spend=utxo_to_spend, confirmed_only=True)
  90  
  91      @cleanup
  92      def test_basic_child_then_parent(self):
  93          node = self.nodes[0]
  94          self.log.info("Check that opportunistic 1p1c logic works when child is received before parent")
  95          node.setmocktime(int(time.time()))
  96  
  97          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet)
  98          high_fee_child = self.wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB)
  99  
 100          peer_sender = node.add_p2p_connection(P2PInterface())
 101  
 102          # 1. Child is received first (perhaps the low feerate parent didn't meet feefilter or the requests were sent to different nodes). It is missing an input.
 103          high_child_wtxid_int = high_fee_child["tx"].wtxid_int
 104          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=high_child_wtxid_int)]))
 105          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 106          peer_sender.wait_for_getdata([high_child_wtxid_int])
 107          peer_sender.send_and_ping(msg_tx(high_fee_child["tx"]))
 108  
 109          # 2. Node requests the missing parent by txid.
 110          parent_txid_int = int(low_fee_parent["txid"], 16)
 111          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 112          peer_sender.wait_for_getdata([parent_txid_int])
 113  
 114          # 3. Sender relays the parent. Parent+Child are evaluated as a package and accepted.
 115          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 116  
 117          # 4. Both transactions should now be in mempool.
 118          node_mempool = node.getrawmempool()
 119          assert low_fee_parent["txid"] in node_mempool
 120          assert high_fee_child["txid"] in node_mempool
 121  
 122          node.disconnect_p2ps()
 123  
 124      @cleanup
 125      def test_basic_parent_then_child(self, wallet):
 126          node = self.nodes[0]
 127          node.setmocktime(int(time.time()))
 128          low_fee_parent = self.create_tx_below_mempoolminfee(wallet)
 129          high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB)
 130  
 131          peer_sender = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="outbound-full-relay")
 132          peer_ignored = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=2, connection_type="outbound-full-relay")
 133  
 134          # 1. Parent is relayed first. It is too low feerate.
 135          parent_wtxid_int = low_fee_parent["tx"].wtxid_int
 136          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)]))
 137          peer_sender.wait_for_getdata([parent_wtxid_int])
 138          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 139          assert low_fee_parent["txid"] not in node.getrawmempool()
 140  
 141          # Send again from peer_ignored, check that it is ignored
 142          peer_ignored.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)]))
 143          assert "getdata" not in peer_ignored.last_message
 144  
 145          # 2. Child is relayed next. It is missing an input.
 146          high_child_wtxid_int = high_fee_child["tx"].wtxid_int
 147          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=high_child_wtxid_int)]))
 148          peer_sender.wait_for_getdata([high_child_wtxid_int])
 149          peer_sender.send_and_ping(msg_tx(high_fee_child["tx"]))
 150  
 151          # 3. Node requests the missing parent by txid.
 152          # It should do so even if it has previously rejected that parent for being too low feerate.
 153          parent_txid_int = int(low_fee_parent["txid"], 16)
 154          node.bumpmocktime(TXID_RELAY_DELAY)
 155          peer_sender.wait_for_getdata([parent_txid_int])
 156  
 157          # 4. Sender re-relays the parent. Parent+Child are evaluated as a package and accepted.
 158          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 159  
 160          # 5. Both transactions should now be in mempool.
 161          node_mempool = node.getrawmempool()
 162          assert low_fee_parent["txid"] in node_mempool
 163          assert high_fee_child["txid"] in node_mempool
 164  
 165      @cleanup
 166      def test_low_and_high_child(self, wallet):
 167          node = self.nodes[0]
 168          node.setmocktime(int(time.time()))
 169          low_fee_parent = self.create_tx_below_mempoolminfee(wallet)
 170          # This feerate is above mempoolminfee, but not enough to also bump the low feerate parent.
 171          feerate_just_above = node.getmempoolinfo()["mempoolminfee"]
 172          med_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=feerate_just_above)
 173          high_fee_child = wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*FEERATE_1SAT_VB)
 174  
 175          peer_sender = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="outbound-full-relay")
 176          peer_ignored = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=2, connection_type="outbound-full-relay")
 177  
 178          self.log.info("Check that tx caches low fee parent + low fee child package rejections")
 179  
 180          # 1. Send parent, rejected for being low feerate.
 181          parent_wtxid_int = low_fee_parent["tx"].wtxid_int
 182          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)]))
 183          peer_sender.wait_for_getdata([parent_wtxid_int])
 184          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 185          assert low_fee_parent["txid"] not in node.getrawmempool()
 186  
 187          # Send again from peer_ignored, check that it is ignored
 188          peer_ignored.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)]))
 189          assert "getdata" not in peer_ignored.last_message
 190  
 191          # 2. Send an (orphan) child that has a higher feerate, but not enough to bump the parent.
 192          med_child_wtxid_int = med_fee_child["tx"].wtxid_int
 193          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=med_child_wtxid_int)]))
 194          peer_sender.wait_for_getdata([med_child_wtxid_int])
 195          peer_sender.send_and_ping(msg_tx(med_fee_child["tx"]))
 196  
 197          # 3. Node requests the orphan's missing parent.
 198          parent_txid_int = int(low_fee_parent["txid"], 16)
 199          node.bumpmocktime(TXID_RELAY_DELAY)
 200          peer_sender.wait_for_getdata([parent_txid_int])
 201  
 202          # 4. The low parent + low child are submitted as a package. They are not accepted due to low package feerate.
 203          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 204  
 205          assert low_fee_parent["txid"] not in node.getrawmempool()
 206          assert med_fee_child["txid"] not in node.getrawmempool()
 207  
 208          # If peer_ignored announces the low feerate child, it should be ignored
 209          peer_ignored.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=med_child_wtxid_int)]))
 210          assert "getdata" not in peer_ignored.last_message
 211          # If either peer sends the parent again, package evaluation should not be attempted
 212          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 213          peer_ignored.send_and_ping(msg_tx(low_fee_parent["tx"]))
 214  
 215          assert low_fee_parent["txid"] not in node.getrawmempool()
 216          assert med_fee_child["txid"] not in node.getrawmempool()
 217  
 218          # 5. Send the high feerate (orphan) child
 219          high_child_wtxid_int = high_fee_child["tx"].wtxid_int
 220          peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=high_child_wtxid_int)]))
 221          peer_sender.wait_for_getdata([high_child_wtxid_int])
 222          peer_sender.send_and_ping(msg_tx(high_fee_child["tx"]))
 223  
 224          # 6. Node requests the orphan's parent, even though it has already been rejected, both by
 225          # itself and with a child. This is necessary, otherwise high_fee_child can be censored.
 226          parent_txid_int = int(low_fee_parent["txid"], 16)
 227          node.bumpmocktime(TXID_RELAY_DELAY)
 228          peer_sender.wait_for_getdata([parent_txid_int])
 229  
 230          # 7. The low feerate parent + high feerate child are submitted as a package.
 231          peer_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 232  
 233          # 8. Both transactions should now be in mempool
 234          node_mempool = node.getrawmempool()
 235          assert low_fee_parent["txid"] in node_mempool
 236          assert high_fee_child["txid"] in node_mempool
 237          assert med_fee_child["txid"] not in node_mempool
 238  
 239      @cleanup
 240      def test_orphan_consensus_failure(self):
 241          self.log.info("Check opportunistic 1p1c logic requires parent and child to be from the same peer")
 242          node = self.nodes[0]
 243          node.setmocktime(int(time.time()))
 244          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet)
 245          coin = low_fee_parent["new_utxo"]
 246          address = node.get_deterministic_priv_key().address
 247          # Create raw transaction spending the parent, but with no signature (a consensus error).
 248          hex_orphan_no_sig = node.createrawtransaction([{"txid": coin["txid"], "vout": coin["vout"]}], {address : coin["value"] - Decimal("0.0001")})
 249          tx_orphan_bad_wit = tx_from_hex(hex_orphan_no_sig)
 250          tx_orphan_bad_wit.wit.vtxinwit.append(CTxInWitness())
 251          tx_orphan_bad_wit.wit.vtxinwit[0].scriptWitness.stack = [b'garbage']
 252  
 253          bad_orphan_sender = node.add_p2p_connection(P2PInterface())
 254          parent_sender = node.add_p2p_connection(P2PInterface())
 255  
 256          # 1. Child is received first. It is missing an input.
 257          child_wtxid_int = tx_orphan_bad_wit.wtxid_int
 258          bad_orphan_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=child_wtxid_int)]))
 259          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 260          bad_orphan_sender.wait_for_getdata([child_wtxid_int])
 261          bad_orphan_sender.send_and_ping(msg_tx(tx_orphan_bad_wit))
 262  
 263          # 2. Node requests the missing parent by txid.
 264          parent_txid_int = int(low_fee_parent["txid"], 16)
 265          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 266          bad_orphan_sender.wait_for_getdata([parent_txid_int])
 267  
 268          # 3. A different peer relays the parent. Package is not evaluated because the transactions
 269          # were not sent from the same peer.
 270          parent_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 271  
 272          # 4. Transactions should not be in mempool.
 273          node_mempool = node.getrawmempool()
 274          assert low_fee_parent["txid"] not in node_mempool
 275          assert tx_orphan_bad_wit.txid_hex not in node_mempool
 276  
 277          # 5. Have the other peer send the tx too, so that tx_orphan_bad_wit package is attempted.
 278          bad_orphan_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 279  
 280          # The bad orphan sender should not be disconnected.
 281          bad_orphan_sender.sync_with_ping()
 282  
 283          # The peer that didn't provide the orphan should not be disconnected.
 284          parent_sender.sync_with_ping()
 285  
 286      @cleanup
 287      def test_parent_consensus_failure(self):
 288          self.log.info("Check opportunistic 1p1c logic with consensus-invalid parent causes disconnect of the correct peer")
 289          node = self.nodes[0]
 290          node.setmocktime(int(time.time()))
 291  
 292          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet)
 293          high_fee_child = self.wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=999*FEERATE_1SAT_VB)
 294  
 295          # Create invalid version of parent with a bad signature.
 296          tx_parent_bad_wit = tx_from_hex(low_fee_parent["hex"])
 297          tx_parent_bad_wit.wit.vtxinwit.append(CTxInWitness())
 298          tx_parent_bad_wit.wit.vtxinwit[0].scriptWitness.stack = [b'garbage']
 299  
 300          package_sender = node.add_p2p_connection(P2PInterface())
 301          fake_parent_sender = node.add_p2p_connection(P2PInterface())
 302  
 303          # 1. Child is received first. It is missing an input.
 304          child_wtxid_int = high_fee_child["tx"].wtxid_int
 305          package_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=child_wtxid_int)]))
 306          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 307          package_sender.wait_for_getdata([child_wtxid_int])
 308          package_sender.send_and_ping(msg_tx(high_fee_child["tx"]))
 309  
 310          # 2. Node requests the missing parent by txid.
 311          parent_txid_int = tx_parent_bad_wit.txid_int
 312          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 313          package_sender.wait_for_getdata([parent_txid_int])
 314  
 315          # 3. A different node relays the parent. The parent is first evaluated by itself and
 316          # rejected for being too low feerate. It is not evaluated as a package because the child was
 317          # sent from a different peer, so we don't find out that the child is consensus-invalid.
 318          fake_parent_sender.send_and_ping(msg_tx(tx_parent_bad_wit))
 319  
 320          # 4. Transactions should not be in mempool.
 321          node_mempool = node.getrawmempool()
 322          assert tx_parent_bad_wit.txid_hex not in node_mempool
 323          assert high_fee_child["txid"] not in node_mempool
 324  
 325          self.log.info("Check that fake parent does not cause orphan to be deleted and real package can still be submitted")
 326          # 5. Child-sending should not have been punished and the orphan should remain in orphanage.
 327          # It can send the "real" parent transaction, and the package is accepted.
 328          parent_wtxid_int = low_fee_parent["tx"].wtxid_int
 329          package_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=parent_wtxid_int)]))
 330          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 331          package_sender.wait_for_getdata([parent_wtxid_int])
 332          package_sender.send_and_ping(msg_tx(low_fee_parent["tx"]))
 333  
 334          node_mempool = node.getrawmempool()
 335          assert low_fee_parent["txid"] in node_mempool
 336          assert high_fee_child["txid"] in node_mempool
 337  
 338      @cleanup
 339      def test_multiple_parents(self):
 340          self.log.info("Check that node does not request more than 1 previously-rejected low feerate parent")
 341  
 342          node = self.nodes[0]
 343          node.setmocktime(int(time.time()))
 344  
 345          # 2-parent-1-child package where both parents are below mempool min feerate
 346          parent_low_1 = self.create_tx_below_mempoolminfee(self.wallet_nonsegwit)
 347          parent_low_2 = self.create_tx_below_mempoolminfee(self.wallet_nonsegwit)
 348          child_bumping = self.wallet_nonsegwit.create_self_transfer_multi(
 349              utxos_to_spend=[parent_low_1["new_utxo"], parent_low_2["new_utxo"]],
 350              fee_per_output=999*parent_low_1["tx"].get_vsize(),
 351          )
 352  
 353          peer_sender = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="outbound-full-relay")
 354  
 355          # 1. Send both parents. Each should be rejected for being too low feerate.
 356          # Send unsolicited so that we can later check that no "getdata" was ever received.
 357          peer_sender.send_and_ping(msg_tx(parent_low_1["tx"]))
 358          peer_sender.send_and_ping(msg_tx(parent_low_2["tx"]))
 359  
 360          # parent_low_1 and parent_low_2 are rejected for being low feerate.
 361          assert parent_low_1["txid"] not in node.getrawmempool()
 362          assert parent_low_2["txid"] not in node.getrawmempool()
 363  
 364          # 2. Send child.
 365          peer_sender.send_and_ping(msg_tx(child_bumping["tx"]))
 366  
 367          # 3. Node should not request any parents, as it should recognize that it will not accept
 368          # multi-parent-1-child packages.
 369          node.bumpmocktime(GETDATA_WAIT)
 370          peer_sender.sync_with_ping()
 371          assert "getdata" not in peer_sender.last_message
 372  
 373      @cleanup
 374      def test_other_parent_in_mempool(self):
 375          self.log.info("Check opportunistic 1p1c works when part of a 2p1c (child already has another parent in mempool)")
 376          node = self.nodes[0]
 377          node.setmocktime(int(time.time()))
 378  
 379          # Grandparent will enter mempool by itself
 380          grandparent_high = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True)
 381  
 382          # This parent needs CPFP
 383          parent_low = self.create_tx_below_mempoolminfee(self.wallet, utxo_to_spend=grandparent_high["new_utxo"])
 384          # This parent does not need CPFP and can be submitted alone ahead of time
 385          parent_high = self.wallet.create_self_transfer(fee_rate=FEERATE_1SAT_VB*10, confirmed_only=True)
 386          child = self.wallet.create_self_transfer_multi(
 387              utxos_to_spend=[parent_high["new_utxo"], parent_low["new_utxo"]],
 388              fee_per_output=999*parent_low["tx"].get_vsize(),
 389          )
 390  
 391          peer_sender = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=1, connection_type="outbound-full-relay")
 392  
 393          # 1. Send grandparent which is accepted
 394          peer_sender.send_and_ping(msg_tx(grandparent_high["tx"]))
 395          assert grandparent_high["txid"] in node.getrawmempool()
 396  
 397          # 2. Send first parent which is accepted.
 398          peer_sender.send_and_ping(msg_tx(parent_high["tx"]))
 399          assert parent_high["txid"] in node.getrawmempool()
 400  
 401          # 3. Send child which is handled as an orphan.
 402          peer_sender.send_and_ping(msg_tx(child["tx"]))
 403  
 404          # 4. Node requests parent_low.
 405          parent_low_txid_int = int(parent_low["txid"], 16)
 406          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 407          peer_sender.wait_for_getdata([parent_low_txid_int])
 408          peer_sender.send_and_ping(msg_tx(parent_low["tx"]))
 409  
 410          node_mempool = node.getrawmempool()
 411          assert grandparent_high["txid"] in node_mempool
 412          assert parent_high["txid"] in node_mempool
 413          assert parent_low["txid"] in node_mempool
 414          assert child["txid"] in node_mempool
 415  
 416      def create_small_orphan(self):
 417          """Create small orphan transaction"""
 418          tx = CTransaction()
 419          # Nonexistent UTXO
 420          tx.vin = [CTxIn(COutPoint(random.randrange(1 << 256), random.randrange(1, 100)))]
 421          tx.wit.vtxinwit = [CTxInWitness()]
 422          tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_NOP] * 5)]
 423          tx.vout = [CTxOut(100, CScript([OP_RETURN, b'a' * 3]))]
 424          return tx
 425  
 426      @cleanup
 427      def test_orphanage_dos_large(self):
 428          self.log.info("Test that the node can still resolve orphans when peers use lots of orphanage space")
 429          node = self.nodes[0]
 430          node.setmocktime(int(time.time()))
 431  
 432          peer_normal = node.add_p2p_connection(P2PInterface())
 433          peer_doser = node.add_p2p_connection(P2PInterface())
 434          num_individual_dosers = 10
 435  
 436          self.log.info("Create very large orphans to be sent by DoSy peers (may take a while)")
 437          large_orphans = [create_large_orphan() for _ in range(50)]
 438          # Check to make sure these are orphans, within max standard size (to be accepted into the orphanage)
 439          for large_orphan in large_orphans:
 440              assert_greater_than_or_equal(100000, large_orphan.get_vsize())
 441              assert_greater_than(MAX_STANDARD_TX_WEIGHT, large_orphan.get_weight())
 442              assert_greater_than_or_equal(3 * large_orphan.get_vsize(), 2 * 100000)
 443              testres = node.testmempoolaccept([large_orphan.serialize().hex()])
 444              assert not testres[0]["allowed"]
 445              assert_equal(testres[0]["reject-reason"], "missing-inputs")
 446  
 447          self.log.info(f"Connect {num_individual_dosers} peers and send a very large orphan from each one")
 448          # This test assumes that unrequested transactions are processed (skipping inv and
 449          # getdata steps because they require going through request delays)
 450          # Connect 10 peers and have each of them send a large orphan.
 451          for large_orphan in large_orphans[:num_individual_dosers]:
 452              peer_doser_individual = node.add_p2p_connection(P2PInterface())
 453              peer_doser_individual.send_and_ping(msg_tx(large_orphan))
 454              node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 455              peer_doser_individual.wait_for_getdata([large_orphan.vin[0].prevout.hash])
 456  
 457          # Make sure that these transactions are going through the orphan handling codepaths.
 458          # Subsequent rounds will not wait for getdata because the time mocking will cause the
 459          # normal package request to time out.
 460          self.wait_until(lambda: len(node.getorphantxs()) == num_individual_dosers)
 461  
 462          self.log.info("Send an orphan from a non-DoSy peer. Its orphan should not be evicted.")
 463          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet)
 464          high_fee_child = self.wallet.create_self_transfer(
 465              utxo_to_spend=low_fee_parent["new_utxo"],
 466              fee_rate=200*FEERATE_1SAT_VB,
 467              target_vsize=100000
 468          )
 469  
 470          # Announce
 471          orphan_tx = high_fee_child["tx"]
 472          orphan_inv = CInv(t=MSG_WTX, h=orphan_tx.wtxid_int)
 473  
 474          # Wait for getdata
 475          peer_normal.send_and_ping(msg_inv([orphan_inv]))
 476          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 477          peer_normal.wait_for_getdata([orphan_tx.wtxid_int])
 478          peer_normal.send_and_ping(msg_tx(orphan_tx))
 479  
 480          # Wait for parent request
 481          parent_txid_int = int(low_fee_parent["txid"], 16)
 482          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 483          peer_normal.wait_for_getdata([parent_txid_int])
 484  
 485          self.log.info("Send another round of very large orphans from a DoSy peer")
 486          for large_orphan in large_orphans[num_individual_dosers:]:
 487              peer_doser.send_and_ping(msg_tx(large_orphan))
 488  
 489          # Something was evicted; the orphanage does not contain all large orphans + the 1p1c child
 490          self.wait_until(lambda: len(node.getorphantxs()) < len(large_orphans) + 1)
 491  
 492          self.log.info("Provide the orphan's parent. This 1p1c package should be successfully accepted.")
 493          peer_normal.send_and_ping(msg_tx(low_fee_parent["tx"]))
 494          assert_equal(node.getmempoolentry(orphan_tx.txid_hex)["ancestorcount"], 2)
 495  
 496      @cleanup
 497      def test_orphanage_dos_many(self):
 498          self.log.info("Test that the node can still resolve orphans when peers are sending tons of orphans")
 499          node = self.nodes[0]
 500          node.setmocktime(int(time.time()))
 501  
 502          peer_normal = node.add_p2p_connection(P2PInterface())
 503  
 504          # The first set of peers all send the same batch_size orphans. Then a single peer sends
 505          # batch_single_doser distinct orphans.
 506          batch_size = 51
 507          num_peers_shared = 60
 508          batch_single_doser = 100
 509          assert_greater_than(num_peers_shared * batch_size + batch_single_doser, 3000)
 510          # 60 peers * 51 orphans = 3060 announcements
 511          shared_orphans = [self.create_small_orphan() for _ in range(batch_size)]
 512          self.log.info(f"Send the same {batch_size} orphans from {num_peers_shared} DoSy peers (may take a while)")
 513          peer_doser_shared = [node.add_p2p_connection(P2PInterface()) for _ in range(num_peers_shared)]
 514          for i in range(num_peers_shared):
 515              for orphan in shared_orphans:
 516                  peer_doser_shared[i].send_without_ping(msg_tx(orphan))
 517  
 518          # We sync peers to make sure we have processed as many orphans as possible. Ensure at least
 519          # one of the orphans was processed.
 520          for peer_doser in peer_doser_shared:
 521              peer_doser.sync_with_ping()
 522          self.wait_until(lambda: any([tx.txid_hex in node.getorphantxs() for tx in shared_orphans]))
 523  
 524          self.log.info("Send an orphan from a non-DoSy peer. Its orphan should not be evicted.")
 525          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet)
 526          high_fee_child = self.wallet.create_self_transfer(
 527              utxo_to_spend=low_fee_parent["new_utxo"],
 528              fee_rate=200*FEERATE_1SAT_VB,
 529          )
 530  
 531          # Announce
 532          orphan_tx = high_fee_child["tx"]
 533          orphan_inv = CInv(t=MSG_WTX, h=orphan_tx.wtxid_int)
 534  
 535          # Wait for getdata
 536          peer_normal.send_and_ping(msg_inv([orphan_inv]))
 537          node.bumpmocktime(NONPREF_PEER_TX_DELAY)
 538          peer_normal.wait_for_getdata([orphan_tx.wtxid_int])
 539          peer_normal.send_and_ping(msg_tx(orphan_tx))
 540  
 541          # Orphan has been entered and evicted something else
 542          self.wait_until(lambda: high_fee_child["txid"] in node.getorphantxs())
 543  
 544          # Wait for parent request
 545          parent_txid_int = low_fee_parent["tx"].txid_int
 546          node.bumpmocktime(NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY)
 547          peer_normal.wait_for_getdata([parent_txid_int])
 548  
 549          self.log.info(f"Send {batch_single_doser} new orphans from one DoSy peer")
 550          peer_doser_batch = node.add_p2p_connection(P2PInterface())
 551          this_batch_orphans = [self.create_small_orphan() for _ in range(batch_single_doser)]
 552          for tx in this_batch_orphans:
 553              # Don't wait for responses, because it dramatically increases the runtime of this test.
 554              peer_doser_batch.send_without_ping(msg_tx(tx))
 555  
 556          peer_doser_batch.sync_with_ping()
 557          self.wait_until(lambda: any([tx.txid_hex in node.getorphantxs() for tx in this_batch_orphans]))
 558  
 559          self.log.info("Check that orphan from normal peer still exists in orphanage")
 560          assert high_fee_child["txid"] in node.getorphantxs()
 561  
 562          self.log.info("Provide the orphan's parent. This 1p1c package should be successfully accepted.")
 563          peer_normal.send_and_ping(msg_tx(low_fee_parent["tx"]))
 564          assert orphan_tx.txid_hex in node.getrawmempool()
 565          assert_equal(node.getmempoolentry(orphan_tx.txid_hex)["ancestorcount"], 2)
 566  
 567      @cleanup
 568      def test_1p1c_on_1p1c(self):
 569          self.log.info("Test that opportunistic 1p1c works when part of a 4-generation chain (1p1c chained from a 1p1c)")
 570          node = self.nodes[0]
 571  
 572          # Prep 2 generations of 1p1c packages to be relayed
 573          low_fee_great_grandparent = self.create_tx_below_mempoolminfee(self.wallet)
 574          high_fee_grandparent = self.wallet.create_self_transfer(utxo_to_spend=low_fee_great_grandparent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB)
 575  
 576          low_fee_parent = self.create_tx_below_mempoolminfee(self.wallet, utxo_to_spend=high_fee_grandparent["new_utxo"])
 577          high_fee_child = self.wallet.create_self_transfer(utxo_to_spend=low_fee_parent["new_utxo"], fee_rate=20*FEERATE_1SAT_VB)
 578  
 579          peer_sender = node.add_p2p_connection(P2PInterface())
 580  
 581          # The 1p1c that spends the confirmed utxo must be received first. Afterwards, the "younger" 1p1c can be received.
 582          for package in [[low_fee_great_grandparent, high_fee_grandparent], [low_fee_parent, high_fee_child]]:
 583              # Aliases
 584              parent_relative, child_relative = package
 585  
 586              # 1. Child is received first (perhaps the low feerate parent didn't meet feefilter or the requests were sent to different nodes). It is missing an input.
 587              high_child_wtxid_int = child_relative["tx"].wtxid_int
 588              peer_sender.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=high_child_wtxid_int)]))
 589              peer_sender.wait_for_getdata([high_child_wtxid_int])
 590              peer_sender.send_and_ping(msg_tx(child_relative["tx"]))
 591  
 592              # 2. Node requests the missing parent by txid.
 593              parent_txid_int = parent_relative["tx"].txid_int
 594              peer_sender.wait_for_getdata([parent_txid_int])
 595  
 596              # 3. Sender relays the parent. Parent+Child are evaluated as a package and accepted.
 597              peer_sender.send_and_ping(msg_tx(parent_relative["tx"]))
 598  
 599          # 4. All transactions should now be in mempool.
 600          node_mempool = node.getrawmempool()
 601          assert low_fee_great_grandparent["txid"] in node_mempool
 602          assert high_fee_grandparent["txid"] in node_mempool
 603          assert low_fee_parent["txid"] in node_mempool
 604          assert high_fee_child["txid"] in node_mempool
 605          assert_equal(node.getmempoolentry(low_fee_great_grandparent["txid"])["descendantcount"], 4)
 606  
 607      def run_test(self):
 608          node = self.nodes[0]
 609          # To avoid creating transactions with the same txid (can happen if we set the same feerate
 610          # and reuse the same input as a previous transaction that wasn't successfully submitted),
 611          # we give each subtest a different nSequence for its transactions.
 612          self.sequence = MAX_BIP125_RBF_SEQUENCE
 613  
 614          self.wallet = MiniWallet(node)
 615          self.wallet_nonsegwit = MiniWallet(node, mode=MiniWalletMode.RAW_P2PK)
 616          self.generate(self.wallet_nonsegwit, 10)
 617          self.generate(self.wallet, 20)
 618  
 619          fill_mempool(self, node)
 620  
 621          self.log.info("Check opportunistic 1p1c logic when parent (txid != wtxid) is received before child")
 622          self.test_basic_parent_then_child(self.wallet)
 623  
 624          self.log.info("Check opportunistic 1p1c logic when parent (txid == wtxid) is received before child")
 625          self.test_basic_parent_then_child(self.wallet_nonsegwit)
 626  
 627          self.log.info("Check opportunistic 1p1c logic when child is received before parent")
 628          self.test_basic_child_then_parent()
 629  
 630          self.log.info("Check opportunistic 1p1c logic when 2 candidate children exist (parent txid != wtxid)")
 631          self.test_low_and_high_child(self.wallet)
 632  
 633          self.log.info("Check opportunistic 1p1c logic when 2 candidate children exist (parent txid == wtxid)")
 634          self.test_low_and_high_child(self.wallet_nonsegwit)
 635  
 636          self.test_orphan_consensus_failure()
 637          self.test_parent_consensus_failure()
 638          self.test_multiple_parents()
 639          self.test_other_parent_in_mempool()
 640          self.test_1p1c_on_1p1c()
 641  
 642          self.test_orphanage_dos_large()
 643          self.test_orphanage_dos_many()
 644  
 645  
 646  if __name__ == '__main__':
 647      PackageRelayTest(__file__).main()
 648