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)
@@ -127,6 +129,22 @@ def relay_transaction(self, peer, tx):
127129 peer .wait_for_getdata ([wtxid ])
128130 peer .send_and_ping (msg_tx (tx ))
129131
132+ def create_malleated_version (self , tx ):
133+ """
134+ Create a malleated version of the tx where the witness is replaced with garbage data.
135+ Returns a CTransaction object.
136+ """
137+ tx_bad_wit = tx_from_hex (tx ["hex" ])
138+ tx_bad_wit .wit .vtxinwit = [CTxInWitness ()]
139+ # Add garbage data to witness 0. We cannot simply strip the witness, as the node would
140+ # classify it as a transaction in which the witness was missing rather than wrong.
141+ tx_bad_wit .wit .vtxinwit [0 ].scriptWitness .stack = [b'garbage' ]
142+
143+ assert_equal (tx ["txid" ], tx_bad_wit .rehash ())
144+ assert tx ["wtxid" ] != tx_bad_wit .getwtxid ()
145+
146+ return tx_bad_wit
147+
130148 @cleanup
131149 def test_arrival_timing_orphan (self ):
132150 self .log .info ("Test missing parents that arrive during delay are not requested" )
@@ -284,8 +302,8 @@ def test_orphan_multiple_parents(self):
284302 # doesn't give up on the orphan. Once all of the missing parents are received, it should be
285303 # submitted to mempool.
286304 peer .send_message (msg_notfound (vec = [CInv (MSG_WITNESS_TX , int (txid_conf_old , 16 ))]))
305+ # Sync with ping to ensure orphans are reconsidered
287306 peer .send_and_ping (msg_tx (missing_tx ["tx" ]))
288- peer .sync_with_ping ()
289307 assert_equal (node .getmempoolentry (orphan ["txid" ])["ancestorcount" ], 3 )
290308
291309 @cleanup
@@ -394,10 +412,161 @@ def test_orphan_inherit_rejection(self):
394412 peer2 .assert_never_requested (child ["tx" ].getwtxid ())
395413
396414 # The child should never be requested, even if announced again with potentially different witness.
415+ # Sync with ping to ensure orphans are reconsidered
397416 peer3 .send_and_ping (msg_inv ([CInv (t = MSG_TX , h = int (child ["txid" ], 16 ))]))
398417 self .nodes [0 ].bumpmocktime (TXREQUEST_TIME_SKIP )
399418 peer3 .assert_never_requested (child ["txid" ])
400419
420+ @cleanup
421+ def test_same_txid_orphan (self ):
422+ self .log .info ("Check what happens when orphan with same txid is already in orphanage" )
423+ node = self .nodes [0 ]
424+
425+ tx_parent = self .wallet .create_self_transfer ()
426+
427+ # Create the real child
428+ tx_child = self .wallet .create_self_transfer (utxo_to_spend = tx_parent ["new_utxo" ])
429+
430+ # Create a fake version of the child
431+ tx_orphan_bad_wit = self .create_malleated_version (tx_child )
432+
433+ bad_peer = node .add_p2p_connection (P2PInterface ())
434+ honest_peer = node .add_p2p_connection (P2PInterface ())
435+
436+ # 1. Fake orphan is received first. It is missing an input.
437+ bad_peer .send_and_ping (msg_tx (tx_orphan_bad_wit ))
438+
439+ # 2. Node requests the missing parent by txid.
440+ parent_txid_int = int (tx_parent ["txid" ], 16 )
441+ node .bumpmocktime (NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY )
442+ bad_peer .wait_for_getdata ([parent_txid_int ])
443+
444+ # 3. Honest peer relays the real child, which is also missing parents and should be placed
445+ # in the orphanage.
446+ with node .assert_debug_log (["missingorspent" , "stored orphan tx" ]):
447+ honest_peer .send_and_ping (msg_tx (tx_child ["tx" ]))
448+
449+ # Time out the previous request for the parent (node will not request the same transaction
450+ # from multiple nodes at the same time)
451+ node .bumpmocktime (GETDATA_TX_INTERVAL )
452+
453+ # 4. The parent is requested. Honest peer sends it.
454+ honest_peer .wait_for_getdata ([parent_txid_int ])
455+ # Sync with ping to ensure orphans are reconsidered
456+ honest_peer .send_and_ping (msg_tx (tx_parent ["tx" ]))
457+
458+ # 5. After parent is accepted, orphans should be reconsidered.
459+ # The real child should be accepted and the fake one rejected.
460+ node_mempool = node .getrawmempool ()
461+ assert tx_parent ["txid" ] in node_mempool
462+ assert tx_child ["txid" ] in node_mempool
463+ assert_equal (node .getmempoolentry (tx_child ["txid" ])["wtxid" ], tx_child ["wtxid" ])
464+
465+ @cleanup
466+ def test_same_txid_orphan_of_orphan (self ):
467+ self .log .info ("Check what happens when orphan's parent with same txid is already in orphanage" )
468+ node = self .nodes [0 ]
469+
470+ tx_grandparent = self .wallet .create_self_transfer ()
471+
472+ # Create middle tx (both parent and child) which will be in orphanage.
473+ tx_middle = self .wallet .create_self_transfer (utxo_to_spend = tx_grandparent ["new_utxo" ])
474+
475+ # Create a fake version of the middle tx
476+ tx_orphan_bad_wit = self .create_malleated_version (tx_middle )
477+
478+ # Create grandchild spending from tx_middle (and spending from tx_orphan_bad_wit since they
479+ # have the same txid).
480+ tx_grandchild = self .wallet .create_self_transfer (utxo_to_spend = tx_middle ["new_utxo" ])
481+
482+ bad_peer = node .add_p2p_connection (P2PInterface ())
483+ honest_peer = node .add_p2p_connection (P2PInterface ())
484+
485+ # 1. Fake orphan is received first. It is missing an input.
486+ bad_peer .send_and_ping (msg_tx (tx_orphan_bad_wit ))
487+
488+ # 2. Node requests missing tx_grandparent by txid.
489+ grandparent_txid_int = int (tx_grandparent ["txid" ], 16 )
490+ node .bumpmocktime (NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY )
491+ bad_peer .wait_for_getdata ([grandparent_txid_int ])
492+
493+ # 3. Honest peer relays the grandchild, which is missing a parent. The parent by txid already
494+ # exists in orphanage, but should be re-requested because the node shouldn't assume that the
495+ # witness data is the same. In this case, a same-txid-different-witness transaction exists!
496+ with node .assert_debug_log (["stored orphan tx" ]):
497+ honest_peer .send_and_ping (msg_tx (tx_grandchild ["tx" ]))
498+ middle_txid_int = int (tx_middle ["txid" ], 16 )
499+ node .bumpmocktime (NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY )
500+ honest_peer .wait_for_getdata ([middle_txid_int ])
501+
502+ # 4. Honest peer relays the real child, which is also missing parents and should be placed
503+ # in the orphanage.
504+ with node .assert_debug_log (["stored orphan tx" ]):
505+ honest_peer .send_and_ping (msg_tx (tx_middle ["tx" ]))
506+ assert_equal (len (node .getrawmempool ()), 0 )
507+
508+ # 5. Honest peer sends tx_grandparent
509+ honest_peer .send_and_ping (msg_tx (tx_grandparent ["tx" ]))
510+
511+ # 6. After parent is accepted, orphans should be reconsidered.
512+ # The real child should be accepted and the fake one rejected.
513+ node_mempool = node .getrawmempool ()
514+ assert tx_grandparent ["txid" ] in node_mempool
515+ assert tx_middle ["txid" ] in node_mempool
516+ assert tx_grandchild ["txid" ] in node_mempool
517+ assert_equal (node .getmempoolentry (tx_middle ["txid" ])["wtxid" ], tx_middle ["wtxid" ])
518+
519+ @cleanup
520+ def test_orphan_txid_inv (self ):
521+ self .log .info ("Check node does not ignore announcement with same txid as tx in orphanage" )
522+ node = self .nodes [0 ]
523+
524+ tx_parent = self .wallet .create_self_transfer ()
525+
526+ # Create the real child and fake version
527+ tx_child = self .wallet .create_self_transfer (utxo_to_spend = tx_parent ["new_utxo" ])
528+ tx_orphan_bad_wit = self .create_malleated_version (tx_child )
529+
530+ bad_peer = node .add_p2p_connection (PeerTxRelayer ())
531+ # Must not send wtxidrelay because otherwise the inv(TX) will be ignored later
532+ honest_peer = node .add_p2p_connection (P2PInterface (wtxidrelay = False ))
533+
534+ # 1. Fake orphan is received first. It is missing an input.
535+ bad_peer .send_and_ping (msg_tx (tx_orphan_bad_wit ))
536+
537+ # 2. Node requests the missing parent by txid.
538+ parent_txid_int = int (tx_parent ["txid" ], 16 )
539+ node .bumpmocktime (NONPREF_PEER_TX_DELAY + TXID_RELAY_DELAY )
540+ bad_peer .wait_for_getdata ([parent_txid_int ])
541+
542+ # 3. Honest peer announces the real child, by txid (this isn't common but the node should
543+ # still keep track of it).
544+ child_txid_int = int (tx_child ["txid" ], 16 )
545+ honest_peer .send_and_ping (msg_inv ([CInv (t = MSG_TX , h = child_txid_int )]))
546+
547+ # 4. The child is requested. Honest peer sends it.
548+ node .bumpmocktime (TXREQUEST_TIME_SKIP )
549+ honest_peer .wait_for_getdata ([child_txid_int ])
550+ with node .assert_debug_log (["stored orphan tx" ]):
551+ honest_peer .send_and_ping (msg_tx (tx_child ["tx" ]))
552+
553+ # 5. After first parent request times out, the node sends another one for the missing parent
554+ # of the real orphan child.
555+ node .bumpmocktime (GETDATA_TX_INTERVAL )
556+ honest_peer .wait_for_getdata ([parent_txid_int ])
557+ honest_peer .send_and_ping (msg_tx (tx_parent ["tx" ]))
558+
559+ # 6. After parent is accepted, orphans should be reconsidered.
560+ # The real child should be accepted and the fake one rejected. This may happen in either
561+ # order since the message-processing is randomized. If tx_orphan_bad_wit is validated first,
562+ # its consensus error leads to disconnection of bad_peer. If tx_child is validated first,
563+ # tx_orphan_bad_wit is rejected for txn-same-nonwitness-data-in-mempool (no punishment).
564+ node_mempool = node .getrawmempool ()
565+ assert tx_parent ["txid" ] in node_mempool
566+ assert tx_child ["txid" ] in node_mempool
567+ assert_equal (node .getmempoolentry (tx_child ["txid" ])["wtxid" ], tx_child ["wtxid" ])
568+
569+
401570 def run_test (self ):
402571 self .nodes [0 ].setmocktime (int (time .time ()))
403572 self .wallet_nonsegwit = MiniWallet (self .nodes [0 ], mode = MiniWalletMode .RAW_P2PK )
@@ -410,6 +579,9 @@ def run_test(self):
410579 self .test_orphans_overlapping_parents ()
411580 self .test_orphan_of_orphan ()
412581 self .test_orphan_inherit_rejection ()
582+ self .test_same_txid_orphan ()
583+ self .test_same_txid_orphan_of_orphan ()
584+ self .test_orphan_txid_inv ()
413585
414586
415587if __name__ == '__main__' :
0 commit comments