77
88from test_framework .messages import (
99 CInv ,
10+ CTxInWitness ,
1011 MSG_TX ,
1112 MSG_WITNESS_TX ,
1213 MSG_WTX ,
2122 NONPREF_PEER_TX_DELAY ,
2223 OVERLOADED_PEER_TX_DELAY ,
2324 p2p_lock ,
25+ P2PInterface ,
2426 P2PTxInvStore ,
2527 TXID_RELAY_DELAY ,
2628)
@@ -398,6 +400,59 @@ def test_orphan_inherit_rejection(self):
398400 self .nodes [0 ].bumpmocktime (TXREQUEST_TIME_SKIP )
399401 peer3 .assert_never_requested (child ["txid" ])
400402
403+ @cleanup
404+ def test_same_txid_orphan (self ):
405+ self .log .info ("Check what happens when orphan with same txid is already in orphanage" )
406+ node = self .nodes [0 ]
407+
408+ tx_parent = self .wallet .create_self_transfer ()
409+ coin = tx_parent ["new_utxo" ]
410+
411+ # Create the real child
412+ tx_child = self .wallet .create_self_transfer (utxo_to_spend = coin )
413+
414+ # Create a fake version of the child
415+ tx_orphan_bad_wit = tx_from_hex (tx_child ["hex" ])
416+ tx_orphan_bad_wit .wit .vtxinwit = [CTxInWitness ()]
417+ tx_orphan_bad_wit .wit .vtxinwit [0 ].scriptWitness .stack = [b'garbage' ]
418+
419+ assert_equal (tx_child ["txid" ], tx_orphan_bad_wit .rehash ())
420+ assert tx_child ["wtxid" ] != tx_orphan_bad_wit .getwtxid ()
421+
422+ bad_peer = node .add_p2p_connection (P2PInterface ())
423+ honest_peer = node .add_p2p_connection (P2PInterface ())
424+
425+ # 1. Fake orphan is received first. It is missing an input.
426+ with node .assert_debug_log (["missingorspent" , "stored orphan tx" ]):
427+ bad_peer .send_and_ping (msg_tx (tx_orphan_bad_wit ))
428+
429+ # 2. Node requests the missing parent by txid.
430+ parent_txid_int = int (tx_parent ["txid" ], 16 )
431+ node .bumpmocktime (NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY )
432+ bad_peer .wait_for_getdata ([parent_txid_int ])
433+
434+ # 3. Honest peer relays the real child, which is also missing parents and should be placed
435+ # in the orphanage.
436+ with node .assert_debug_log (["missingorspent" , "stored orphan tx" ]):
437+ honest_peer .send_and_ping (msg_tx (tx_child ["tx" ]))
438+
439+ # Time out the previous request for the parent (node will not request the same transaction
440+ # from multiple nodes at the same time)
441+ node .bumpmocktime (GETDATA_TX_INTERVAL )
442+
443+ # 4. The parent is requested. Honest peer sends it.
444+ honest_peer .wait_for_getdata ([parent_txid_int ])
445+ with node .assert_debug_log (["accepted" ]):
446+ honest_peer .send_message (msg_tx (tx_parent ["tx" ]))
447+
448+ # 5. After parent is accepted, orphans should be reconsidered.
449+ # The real child should be accepted and the fake one rejected.
450+ node_mempool = node .getrawmempool ()
451+ assert tx_parent ["txid" ] in node_mempool
452+ assert tx_child ["txid" ] in node_mempool
453+ assert node .getmempoolentry (tx_child ["txid" ])["wtxid" ] == tx_child ["wtxid" ]
454+
455+
401456 def run_test (self ):
402457 self .nodes [0 ].setmocktime (int (time .time ()))
403458 self .wallet_nonsegwit = MiniWallet (self .nodes [0 ], mode = MiniWalletMode .RAW_P2PK )
@@ -410,6 +465,7 @@ def run_test(self):
410465 self .test_orphans_overlapping_parents ()
411466 self .test_orphan_of_orphan ()
412467 self .test_orphan_inherit_rejection ()
468+ self .test_same_txid_orphan ()
413469
414470
415471if __name__ == '__main__' :
0 commit comments