Skip to content

Commit 4b4dfaa

Browse files
committed
[functional test] orphan wtxid
1 parent 646aa4d commit 4b4dfaa

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

test/functional/p2p_orphan_handling.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from test_framework.messages import (
99
CInv,
10+
CTxInWitness,
1011
MSG_TX,
1112
MSG_WITNESS_TX,
1213
MSG_WTX,
@@ -21,6 +22,7 @@
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

415471
if __name__ == '__main__':

0 commit comments

Comments
 (0)