@@ -70,6 +70,16 @@ static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& out
7070 return MakeTransactionRef (tx);
7171}
7272
73+ // Make another (not necessarily valid) tx with the same txid but different wtxid.
74+ static CTransactionRef MakeMutation (const CTransactionRef& ptx)
75+ {
76+ CMutableTransaction tx (*ptx);
77+ tx.vin [0 ].scriptWitness .stack .push_back ({5 });
78+ auto mutated_tx = MakeTransactionRef (tx);
79+ assert (ptx->GetHash () == mutated_tx->GetHash ());
80+ return mutated_tx;
81+ }
82+
7383static bool EqualTxns (const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
7484{
7585 if (vec_txns.size () != set_txns.size ()) return false ;
@@ -180,6 +190,49 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
180190 BOOST_CHECK (orphanage.CountOrphans () == 0 );
181191}
182192
193+ BOOST_AUTO_TEST_CASE (same_txid_diff_witness)
194+ {
195+ FastRandomContext det_rand{true };
196+ TxOrphanage orphanage;
197+ NodeId peer{0 };
198+
199+ std::vector<COutPoint> empty_outpoints;
200+ auto parent = MakeTransactionSpending (empty_outpoints, det_rand);
201+
202+ // Create children to go into orphanage.
203+ auto child_normal = MakeTransactionSpending ({{parent->GetHash (), 0 }}, det_rand);
204+ auto child_mutated = MakeMutation (child_normal);
205+
206+ const auto & normal_wtxid = child_normal->GetWitnessHash ();
207+ const auto & mutated_wtxid = child_mutated->GetWitnessHash ();
208+ BOOST_CHECK (normal_wtxid != mutated_wtxid);
209+
210+ BOOST_CHECK (orphanage.AddTx (child_normal, peer));
211+ // EraseTx fails as transaction by this wtxid doesn't exist.
212+ BOOST_CHECK_EQUAL (orphanage.EraseTx (mutated_wtxid), 0 );
213+ BOOST_CHECK (orphanage.HaveTx (normal_wtxid));
214+ BOOST_CHECK (!orphanage.HaveTx (mutated_wtxid));
215+
216+ // Must succeed. Both transactions should be present in orphanage.
217+ BOOST_CHECK (orphanage.AddTx (child_mutated, peer));
218+ BOOST_CHECK (orphanage.HaveTx (normal_wtxid));
219+ BOOST_CHECK (orphanage.HaveTx (mutated_wtxid));
220+
221+ // Outpoints map should track all entries: check that both are returned as children of the parent.
222+ std::set<CTransactionRef> expected_children{child_normal, child_mutated};
223+ BOOST_CHECK (EqualTxns (expected_children, orphanage.GetChildrenFromSamePeer (parent, peer)));
224+
225+ // Erase by wtxid: mutated first
226+ BOOST_CHECK_EQUAL (orphanage.EraseTx (mutated_wtxid), 1 );
227+ BOOST_CHECK (orphanage.HaveTx (normal_wtxid));
228+ BOOST_CHECK (!orphanage.HaveTx (mutated_wtxid));
229+
230+ BOOST_CHECK_EQUAL (orphanage.EraseTx (normal_wtxid), 1 );
231+ BOOST_CHECK (!orphanage.HaveTx (normal_wtxid));
232+ BOOST_CHECK (!orphanage.HaveTx (mutated_wtxid));
233+ }
234+
235+
183236BOOST_AUTO_TEST_CASE (get_children)
184237{
185238 FastRandomContext det_rand{true };
0 commit comments