@@ -353,7 +353,7 @@ def test_multiple_parents(self):
353353
354354 @cleanup
355355 def test_other_parent_in_mempool (self ):
356- self .log .info ("Check opportunistic 1p1c works even if child already has another parent in mempool" )
356+ self .log .info ("Check opportunistic 1p1c works when part of a 2p1c ( child already has another parent in mempool) " )
357357 node = self .nodes [0 ]
358358
359359 # Grandparent will enter mempool by itself
@@ -550,6 +550,46 @@ def test_orphanage_dos_many(self):
550550 assert orphan_tx .txid_hex in node .getrawmempool ()
551551 assert_equal (node .getmempoolentry (orphan_tx .txid_hex )["ancestorcount" ], 2 )
552552
553+ @cleanup
554+ def test_1p1c_on_1p1c (self ):
555+ self .log .info ("Test that opportunistic 1p1c works when part of a 4-generation chain (1p1c chained from a 1p1c)" )
556+ node = self .nodes [0 ]
557+
558+ # Prep 2 generations of 1p1c packages to be relayed
559+ low_fee_great_grandparent = self .create_tx_below_mempoolminfee (self .wallet )
560+ high_fee_grandparent = self .wallet .create_self_transfer (utxo_to_spend = low_fee_great_grandparent ["new_utxo" ], fee_rate = 20 * FEERATE_1SAT_VB )
561+
562+ low_fee_parent = self .create_tx_below_mempoolminfee (self .wallet , utxo_to_spend = high_fee_grandparent ["new_utxo" ])
563+ high_fee_child = self .wallet .create_self_transfer (utxo_to_spend = low_fee_parent ["new_utxo" ], fee_rate = 20 * FEERATE_1SAT_VB )
564+
565+ peer_sender = node .add_p2p_connection (P2PInterface ())
566+
567+ # The 1p1c that spends the confirmed utxo must be received first. Afterwards, the "younger" 1p1c can be received.
568+ for package in [[low_fee_great_grandparent , high_fee_grandparent ], [low_fee_parent , high_fee_child ]]:
569+ # Aliases
570+ parent_relative , child_relative = package
571+
572+ # 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.
573+ high_child_wtxid_int = child_relative ["tx" ].wtxid_int
574+ peer_sender .send_and_ping (msg_inv ([CInv (t = MSG_WTX , h = high_child_wtxid_int )]))
575+ peer_sender .wait_for_getdata ([high_child_wtxid_int ])
576+ peer_sender .send_and_ping (msg_tx (child_relative ["tx" ]))
577+
578+ # 2. Node requests the missing parent by txid.
579+ parent_txid_int = parent_relative ["tx" ].txid_int
580+ peer_sender .wait_for_getdata ([parent_txid_int ])
581+
582+ # 3. Sender relays the parent. Parent+Child are evaluated as a package and accepted.
583+ peer_sender .send_and_ping (msg_tx (parent_relative ["tx" ]))
584+
585+ # 4. All transactions should now be in mempool.
586+ node_mempool = node .getrawmempool ()
587+ assert low_fee_great_grandparent ["txid" ] in node_mempool
588+ assert high_fee_grandparent ["txid" ] in node_mempool
589+ assert low_fee_parent ["txid" ] in node_mempool
590+ assert high_fee_child ["txid" ] in node_mempool
591+ assert_equal (node .getmempoolentry (low_fee_great_grandparent ["txid" ])["descendantcount" ], 4 )
592+
553593 def run_test (self ):
554594 node = self .nodes [0 ]
555595 # To avoid creating transactions with the same txid (can happen if we set the same feerate
@@ -583,6 +623,7 @@ def run_test(self):
583623 self .test_parent_consensus_failure ()
584624 self .test_multiple_parents ()
585625 self .test_other_parent_in_mempool ()
626+ self .test_1p1c_on_1p1c ()
586627
587628 self .test_orphanage_dos_large ()
588629 self .test_orphanage_dos_many ()
0 commit comments