@@ -38,14 +38,56 @@ class TxOrphanageTest : public TxOrphanage
38
38
}
39
39
};
40
40
41
- static void MakeNewKeyWithFastRandomContext (CKey& key)
41
+ static void MakeNewKeyWithFastRandomContext (CKey& key, FastRandomContext& rand_ctx = g_insecure_rand_ctx )
42
42
{
43
43
std::vector<unsigned char > keydata;
44
- keydata = g_insecure_rand_ctx .randbytes (32 );
44
+ keydata = rand_ctx .randbytes (32 );
45
45
key.Set (keydata.data (), keydata.data () + keydata.size (), /* fCompressedIn=*/ true );
46
46
assert (key.IsValid ());
47
47
}
48
48
49
+ // Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.
50
+ static CTransactionRef MakeTransactionSpending (const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
51
+ {
52
+ CKey key;
53
+ MakeNewKeyWithFastRandomContext (key, det_rand);
54
+ CMutableTransaction tx;
55
+ // If no outpoints are given, create a random one.
56
+ if (outpoints.empty ()) {
57
+ tx.vin .emplace_back (Txid::FromUint256 (det_rand.rand256 ()), 0 );
58
+ } else {
59
+ for (const auto & outpoint : outpoints) {
60
+ tx.vin .emplace_back (outpoint);
61
+ }
62
+ }
63
+ // Ensure txid != wtxid
64
+ tx.vin [0 ].scriptWitness .stack .push_back ({1 });
65
+ tx.vout .resize (2 );
66
+ tx.vout [0 ].nValue = CENT;
67
+ tx.vout [0 ].scriptPubKey = GetScriptForDestination (PKHash (key.GetPubKey ()));
68
+ tx.vout [1 ].nValue = 3 * CENT;
69
+ tx.vout [1 ].scriptPubKey = GetScriptForDestination (WitnessV0KeyHash (key.GetPubKey ()));
70
+ return MakeTransactionRef (tx);
71
+ }
72
+
73
+ static bool EqualTxns (const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
74
+ {
75
+ if (vec_txns.size () != set_txns.size ()) return false ;
76
+ for (const auto & tx : vec_txns) {
77
+ if (!set_txns.contains (tx)) return false ;
78
+ }
79
+ return true ;
80
+ }
81
+ static bool EqualTxns (const std::set<CTransactionRef>& set_txns,
82
+ const std::vector<std::pair<CTransactionRef, NodeId>>& vec_txns)
83
+ {
84
+ if (vec_txns.size () != set_txns.size ()) return false ;
85
+ for (const auto & [tx, nodeid] : vec_txns) {
86
+ if (!set_txns.contains (tx)) return false ;
87
+ }
88
+ return true ;
89
+ }
90
+
49
91
BOOST_AUTO_TEST_CASE (DoS_mapOrphans)
50
92
{
51
93
// This test had non-deterministic coverage due to
@@ -138,4 +180,105 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
138
180
BOOST_CHECK (orphanage.CountOrphans () == 0 );
139
181
}
140
182
183
+ BOOST_AUTO_TEST_CASE (get_children)
184
+ {
185
+ FastRandomContext det_rand{true };
186
+ std::vector<COutPoint> empty_outpoints;
187
+
188
+ auto parent1 = MakeTransactionSpending (empty_outpoints, det_rand);
189
+ auto parent2 = MakeTransactionSpending (empty_outpoints, det_rand);
190
+
191
+ // Make sure these parents have different txids otherwise this test won't make sense.
192
+ while (parent1->GetHash () == parent2->GetHash ()) {
193
+ parent2 = MakeTransactionSpending (empty_outpoints, det_rand);
194
+ }
195
+
196
+ // Create children to go into orphanage.
197
+ auto child_p1n0 = MakeTransactionSpending ({{parent1->GetHash (), 0 }}, det_rand);
198
+ auto child_p2n1 = MakeTransactionSpending ({{parent2->GetHash (), 1 }}, det_rand);
199
+ // Spends the same tx twice. Should not cause duplicates.
200
+ auto child_p1n0_p1n1 = MakeTransactionSpending ({{parent1->GetHash (), 0 }, {parent1->GetHash (), 1 }}, det_rand);
201
+ // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique.
202
+ auto child_p1n0_p2n0 = MakeTransactionSpending ({{parent1->GetHash (), 0 }, {parent2->GetHash (), 0 }}, det_rand);
203
+
204
+ const NodeId node1{1 };
205
+ const NodeId node2{2 };
206
+
207
+ // All orphans provided by node1
208
+ {
209
+ TxOrphanage orphanage;
210
+ BOOST_CHECK (orphanage.AddTx (child_p1n0, node1));
211
+ BOOST_CHECK (orphanage.AddTx (child_p2n1, node1));
212
+ BOOST_CHECK (orphanage.AddTx (child_p1n0_p1n1, node1));
213
+ BOOST_CHECK (orphanage.AddTx (child_p1n0_p2n0, node1));
214
+
215
+ std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
216
+ std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
217
+
218
+ BOOST_CHECK (EqualTxns (expected_parent1_children, orphanage.GetChildrenFromSamePeer (parent1, node1)));
219
+ BOOST_CHECK (EqualTxns (expected_parent2_children, orphanage.GetChildrenFromSamePeer (parent2, node1)));
220
+
221
+ BOOST_CHECK (EqualTxns (expected_parent1_children, orphanage.GetChildrenFromDifferentPeer (parent1, node2)));
222
+ BOOST_CHECK (EqualTxns (expected_parent2_children, orphanage.GetChildrenFromDifferentPeer (parent2, node2)));
223
+
224
+ // The peer must match
225
+ BOOST_CHECK (orphanage.GetChildrenFromSamePeer (parent1, node2).empty ());
226
+ BOOST_CHECK (orphanage.GetChildrenFromSamePeer (parent2, node2).empty ());
227
+
228
+ // There shouldn't be any children of this tx in the orphanage
229
+ BOOST_CHECK (orphanage.GetChildrenFromSamePeer (child_p1n0_p2n0, node1).empty ());
230
+ BOOST_CHECK (orphanage.GetChildrenFromSamePeer (child_p1n0_p2n0, node2).empty ());
231
+ BOOST_CHECK (orphanage.GetChildrenFromDifferentPeer (child_p1n0_p2n0, node1).empty ());
232
+ BOOST_CHECK (orphanage.GetChildrenFromDifferentPeer (child_p1n0_p2n0, node2).empty ());
233
+ }
234
+
235
+ // Orphans provided by node1 and node2
236
+ {
237
+ TxOrphanage orphanage;
238
+ BOOST_CHECK (orphanage.AddTx (child_p1n0, node1));
239
+ BOOST_CHECK (orphanage.AddTx (child_p2n1, node1));
240
+ BOOST_CHECK (orphanage.AddTx (child_p1n0_p1n1, node2));
241
+ BOOST_CHECK (orphanage.AddTx (child_p1n0_p2n0, node2));
242
+
243
+ // +----------------+---------------+----------------------------------+
244
+ // | | sender=node1 | sender=node2 |
245
+ // +----------------+---------------+----------------------------------+
246
+ // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 |
247
+ // | spends parent2 | child_p2n1 | child_p1n0_p2n0 |
248
+ // +----------------+---------------+----------------------------------+
249
+
250
+ // Children of parent1 from node1:
251
+ {
252
+ std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
253
+
254
+ BOOST_CHECK (EqualTxns (expected_parent1_node1, orphanage.GetChildrenFromSamePeer (parent1, node1)));
255
+ BOOST_CHECK (EqualTxns (expected_parent1_node1, orphanage.GetChildrenFromDifferentPeer (parent1, node2)));
256
+ }
257
+
258
+ // Children of parent2 from node1:
259
+ {
260
+ std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
261
+
262
+ BOOST_CHECK (EqualTxns (expected_parent2_node1, orphanage.GetChildrenFromSamePeer (parent2, node1)));
263
+ BOOST_CHECK (EqualTxns (expected_parent2_node1, orphanage.GetChildrenFromDifferentPeer (parent2, node2)));
264
+ }
265
+
266
+ // Children of parent1 from node2:
267
+ {
268
+ std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
269
+
270
+ BOOST_CHECK (EqualTxns (expected_parent1_node2, orphanage.GetChildrenFromSamePeer (parent1, node2)));
271
+ BOOST_CHECK (EqualTxns (expected_parent1_node2, orphanage.GetChildrenFromDifferentPeer (parent1, node1)));
272
+ }
273
+
274
+ // Children of parent2 from node2:
275
+ {
276
+ std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
277
+
278
+ BOOST_CHECK (EqualTxns (expected_parent2_node2, orphanage.GetChildrenFromSamePeer (parent2, node2)));
279
+ BOOST_CHECK (EqualTxns (expected_parent2_node2, orphanage.GetChildrenFromDifferentPeer (parent2, node1)));
280
+ }
281
+ }
282
+ }
283
+
141
284
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments