Branch data Line data Source code
1 : : // Copyright (c) 2011-2022 The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <arith_uint256.h>
6 : : #include <consensus/validation.h>
7 : : #include <policy/policy.h>
8 : : #include <primitives/transaction.h>
9 : : #include <pubkey.h>
10 : : #include <script/sign.h>
11 : : #include <script/signingprovider.h>
12 : : #include <test/util/random.h>
13 : : #include <test/util/setup_common.h>
14 : : #include <test/util/transaction_utils.h>
15 : : #include <txorphanage.h>
16 : :
17 : : #include <array>
18 : : #include <cstdint>
19 : :
20 : : #include <boost/test/unit_test.hpp>
21 : :
22 : : BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
23 : :
24 : 0 : class TxOrphanageTest : public TxOrphanage
25 : : {
26 : : public:
27 : 8 : TxOrphanageTest(FastRandomContext& rng) : m_rng{rng} {}
28 : :
29 : 14 : inline size_t CountOrphans() const
30 : : {
31 [ + - ]: 3 : return m_orphans.size();
32 : : }
33 : :
34 : 60 : CTransactionRef RandomOrphan()
35 : : {
36 : 60 : std::map<Wtxid, OrphanTx>::iterator it;
37 : 60 : it = m_orphans.lower_bound(Wtxid::FromUint256(m_rng.rand256()));
38 [ - + ]: 60 : if (it == m_orphans.end())
39 : 0 : it = m_orphans.begin();
40 [ + - ]: 60 : return it->second.tx;
41 : : }
42 : :
43 : : FastRandomContext& m_rng;
44 : : };
45 : :
46 : 21 : static void MakeNewKeyWithFastRandomContext(CKey& key, FastRandomContext& rand_ctx)
47 : : {
48 : 21 : std::vector<unsigned char> keydata;
49 : 21 : keydata = rand_ctx.randbytes(32);
50 [ + - ]: 21 : key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn=*/true);
51 [ - + ]: 21 : assert(key.IsValid());
52 : 21 : }
53 : :
54 : : // Creates a transaction with 2 outputs. Spends all outpoints. If outpoints is empty, spends a random one.
55 : 20 : static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, FastRandomContext& det_rand)
56 : : {
57 : 20 : CKey key;
58 [ + - ]: 20 : MakeNewKeyWithFastRandomContext(key, det_rand);
59 [ + - ]: 20 : CMutableTransaction tx;
60 : : // If no outpoints are given, create a random one.
61 [ + + ]: 20 : if (outpoints.empty()) {
62 [ + - ]: 8 : tx.vin.emplace_back(Txid::FromUint256(det_rand.rand256()), 0);
63 : : } else {
64 [ + + ]: 28 : for (const auto& outpoint : outpoints) {
65 [ + - ]: 16 : tx.vin.emplace_back(outpoint);
66 : : }
67 : : }
68 : : // Ensure txid != wtxid
69 [ + - + - ]: 40 : tx.vin[0].scriptWitness.stack.push_back({1});
70 [ + - ]: 20 : tx.vout.resize(2);
71 [ + - ]: 20 : tx.vout[0].nValue = CENT;
72 [ + - + - : 20 : tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
+ - ]
73 [ + - ]: 20 : tx.vout[1].nValue = 3 * CENT;
74 [ + - + - : 20 : tx.vout[1].scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(key.GetPubKey()));
+ - ]
75 [ + - ]: 40 : return MakeTransactionRef(tx);
76 : 20 : }
77 : :
78 : : // Make another (not necessarily valid) tx with the same txid but different wtxid.
79 : 3 : static CTransactionRef MakeMutation(const CTransactionRef& ptx)
80 : : {
81 : 3 : CMutableTransaction tx(*ptx);
82 [ + - + - ]: 6 : tx.vin[0].scriptWitness.stack.push_back({5});
83 [ + - ]: 3 : auto mutated_tx = MakeTransactionRef(tx);
84 [ - + ]: 3 : assert(ptx->GetHash() == mutated_tx->GetHash());
85 : 3 : return mutated_tx;
86 : 3 : }
87 : :
88 : 7 : static bool EqualTxns(const std::set<CTransactionRef>& set_txns, const std::vector<CTransactionRef>& vec_txns)
89 : : {
90 [ + - ]: 7 : if (vec_txns.size() != set_txns.size()) return false;
91 [ + + ]: 19 : for (const auto& tx : vec_txns) {
92 [ + - ]: 12 : if (!set_txns.contains(tx)) return false;
93 : : }
94 : : return true;
95 : : }
96 : :
97 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
98 : : {
99 : : // This test had non-deterministic coverage due to
100 : : // randomly selected seeds.
101 : : // This seed is chosen so that all branches of the function
102 : : // ecdsa_signature_parse_der_lax are executed during this test.
103 : : // Specifically branches that run only when an ECDSA
104 : : // signature's R and S values have leading zeros.
105 : 1 : m_rng.Reseed(uint256{33});
106 : :
107 : 1 : TxOrphanageTest orphanage{m_rng};
108 : 1 : CKey key;
109 [ + - ]: 1 : MakeNewKeyWithFastRandomContext(key, m_rng);
110 : 1 : FillableSigningProvider keystore;
111 [ + - + - : 2 : BOOST_CHECK(keystore.AddKey(key));
+ - ]
112 : :
113 : : // Freeze time for length of test
114 : 1 : auto now{GetTime<std::chrono::seconds>()};
115 [ + - ]: 1 : SetMockTime(now);
116 : :
117 : : // 50 orphan transactions:
118 [ + + ]: 51 : for (int i = 0; i < 50; i++)
119 : : {
120 [ + - ]: 50 : CMutableTransaction tx;
121 [ + - ]: 50 : tx.vin.resize(1);
122 : 50 : tx.vin[0].prevout.n = 0;
123 [ + - ]: 50 : tx.vin[0].prevout.hash = Txid::FromUint256(m_rng.rand256());
124 [ + - ]: 50 : tx.vin[0].scriptSig << OP_1;
125 [ + - ]: 50 : tx.vout.resize(1);
126 [ + - ]: 50 : tx.vout[0].nValue = i*CENT;
127 [ + - + - : 50 : tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
+ - ]
128 : :
129 [ + - + - ]: 150 : orphanage.AddTx(MakeTransactionRef(tx), i);
130 : 50 : }
131 : :
132 : : // ... and 50 that depend on other orphans:
133 [ + + ]: 51 : for (int i = 0; i < 50; i++)
134 : : {
135 : 50 : CTransactionRef txPrev = orphanage.RandomOrphan();
136 : :
137 [ + - ]: 50 : CMutableTransaction tx;
138 [ + - ]: 50 : tx.vin.resize(1);
139 [ + - ]: 50 : tx.vin[0].prevout.n = 0;
140 [ + - ]: 50 : tx.vin[0].prevout.hash = txPrev->GetHash();
141 [ + - ]: 50 : tx.vout.resize(1);
142 [ + - ]: 50 : tx.vout[0].nValue = i*CENT;
143 [ + - + - : 50 : tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
+ - ]
144 : 50 : SignatureData empty;
145 [ + - + - : 100 : BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
+ - + - ]
146 : :
147 [ + - + - ]: 100 : orphanage.AddTx(MakeTransactionRef(tx), i);
148 [ + - ]: 150 : }
149 : :
150 : : // This really-big orphan should be ignored:
151 [ + + ]: 11 : for (int i = 0; i < 10; i++)
152 : : {
153 : 10 : CTransactionRef txPrev = orphanage.RandomOrphan();
154 : :
155 [ + - ]: 10 : CMutableTransaction tx;
156 [ + - ]: 10 : tx.vout.resize(1);
157 [ + - ]: 10 : tx.vout[0].nValue = 1*CENT;
158 [ + - + - : 10 : tx.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
+ - ]
159 [ + - ]: 10 : tx.vin.resize(2777);
160 [ + + ]: 27780 : for (unsigned int j = 0; j < tx.vin.size(); j++)
161 : : {
162 : 27770 : tx.vin[j].prevout.n = j;
163 : 27770 : tx.vin[j].prevout.hash = txPrev->GetHash();
164 : : }
165 : 10 : SignatureData empty;
166 [ + - + - : 20 : BOOST_CHECK(SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL, empty));
+ - ]
167 : : // Reuse same signature for other inputs
168 : : // (they don't have to be valid for this test)
169 [ + + ]: 27770 : for (unsigned int j = 1; j < tx.vin.size(); j++)
170 : 27760 : tx.vin[j].scriptSig = tx.vin[0].scriptSig;
171 : :
172 [ + - + - : 40 : BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), i));
+ - + - +
- ]
173 [ + - ]: 30 : }
174 : :
175 [ + - ]: 1 : size_t expected_num_orphans = orphanage.CountOrphans();
176 : :
177 : : // Non-existent peer; nothing should be deleted
178 [ + - ]: 1 : orphanage.EraseForPeer(/*peer=*/-1);
179 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
180 : :
181 : : // Each of first three peers stored
182 : : // two transactions each.
183 [ + + ]: 4 : for (NodeId i = 0; i < 3; i++)
184 : : {
185 [ + - ]: 3 : orphanage.EraseForPeer(i);
186 : 3 : expected_num_orphans -= 2;
187 [ + - + - ]: 6 : BOOST_CHECK(orphanage.CountOrphans() == expected_num_orphans);
188 : : }
189 : :
190 : : // Test LimitOrphanTxSize() function, nothing should timeout:
191 : 1 : FastRandomContext rng{/*fDeterministic=*/true};
192 [ + - ]: 1 : orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
193 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
194 : 1 : expected_num_orphans -= 1;
195 [ + - ]: 1 : orphanage.LimitOrphans(/*max_orphans=*/expected_num_orphans, rng);
196 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), expected_num_orphans);
197 [ - + ]: 1 : assert(expected_num_orphans > 40);
198 [ + - ]: 1 : orphanage.LimitOrphans(40, rng);
199 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 40);
200 [ + - ]: 1 : orphanage.LimitOrphans(10, rng);
201 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 10);
202 [ + - ]: 1 : orphanage.LimitOrphans(0, rng);
203 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
204 : :
205 : : // Add one more orphan, check timeout logic
206 [ + - ]: 1 : auto timeout_tx = MakeTransactionSpending(/*outpoints=*/{}, rng);
207 [ + - ]: 1 : orphanage.AddTx(timeout_tx, 0);
208 [ + - ]: 1 : orphanage.LimitOrphans(1, rng);
209 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
210 : :
211 : : // One second shy of expiration
212 [ + - ]: 1 : SetMockTime(now + ORPHAN_TX_EXPIRE_TIME - 1s);
213 [ + - ]: 1 : orphanage.LimitOrphans(1, rng);
214 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
215 : :
216 : : // Jump one more second, orphan should be timed out on limiting
217 [ + - ]: 1 : SetMockTime(now + ORPHAN_TX_EXPIRE_TIME);
218 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 1);
219 [ + - ]: 1 : orphanage.LimitOrphans(1, rng);
220 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.CountOrphans(), 0);
+ - ]
221 : 1 : }
222 : :
223 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(same_txid_diff_witness)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
224 : : {
225 : 1 : FastRandomContext det_rand{true};
226 : 1 : TxOrphanage orphanage;
227 : 1 : NodeId peer{0};
228 : :
229 : 1 : std::vector<COutPoint> empty_outpoints;
230 [ + - ]: 1 : auto parent = MakeTransactionSpending(empty_outpoints, det_rand);
231 : :
232 : : // Create children to go into orphanage.
233 [ + - + - : 2 : auto child_normal = MakeTransactionSpending({{parent->GetHash(), 0}}, det_rand);
+ - ]
234 [ + - ]: 1 : auto child_mutated = MakeMutation(child_normal);
235 : :
236 [ + - ]: 1 : const auto& normal_wtxid = child_normal->GetWitnessHash();
237 : 1 : const auto& mutated_wtxid = child_mutated->GetWitnessHash();
238 [ + - + - : 2 : BOOST_CHECK(normal_wtxid != mutated_wtxid);
+ - ]
239 : :
240 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_normal, peer));
+ - + - ]
241 : : // EraseTx fails as transaction by this wtxid doesn't exist.
242 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 0);
+ - ]
243 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
+ - + - ]
244 [ + - + - : 3 : BOOST_CHECK(orphanage.GetTx(normal_wtxid) == child_normal);
+ - + - +
- ]
245 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
+ - + - ]
246 [ + - + - : 2 : BOOST_CHECK(orphanage.GetTx(mutated_wtxid) == nullptr);
+ - - + +
- ]
247 : :
248 : : // Must succeed. Both transactions should be present in orphanage.
249 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_mutated, peer));
+ - + - ]
250 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
+ - + - ]
251 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(mutated_wtxid));
+ - + - ]
252 : :
253 : : // Outpoints map should track all entries: check that both are returned as children of the parent.
254 [ + + + - : 3 : std::set<CTransactionRef> expected_children{child_normal, child_mutated};
- - - - ]
255 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_children, orphanage.GetChildrenFromSamePeer(parent, peer)));
+ - + - ]
256 : :
257 : : // Erase by wtxid: mutated first
258 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.EraseTx(mutated_wtxid), 1);
+ - ]
259 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(normal_wtxid));
+ - + - ]
260 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
+ - + - ]
261 : :
262 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.EraseTx(normal_wtxid), 1);
+ - ]
263 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(normal_wtxid));
+ - + - ]
264 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(mutated_wtxid));
+ - ]
265 [ + - + - : 7 : }
- + + - +
- + - ]
266 : :
267 : :
268 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(get_children)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
269 : : {
270 : 1 : FastRandomContext det_rand{true};
271 : 1 : std::vector<COutPoint> empty_outpoints;
272 : :
273 [ + - ]: 1 : auto parent1 = MakeTransactionSpending(empty_outpoints, det_rand);
274 [ + - ]: 1 : auto parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
275 : :
276 : : // Make sure these parents have different txids otherwise this test won't make sense.
277 [ - + ]: 1 : while (parent1->GetHash() == parent2->GetHash()) {
278 [ # # # # ]: 0 : parent2 = MakeTransactionSpending(empty_outpoints, det_rand);
279 : : }
280 : :
281 : : // Create children to go into orphanage.
282 [ + - + - : 2 : auto child_p1n0 = MakeTransactionSpending({{parent1->GetHash(), 0}}, det_rand);
+ - ]
283 [ + - + - : 2 : auto child_p2n1 = MakeTransactionSpending({{parent2->GetHash(), 1}}, det_rand);
+ - ]
284 : : // Spends the same tx twice. Should not cause duplicates.
285 [ + - + - : 2 : auto child_p1n0_p1n1 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent1->GetHash(), 1}}, det_rand);
+ - ]
286 : : // Spends the same outpoint as previous tx. Should still be returned; don't assume outpoints are unique.
287 [ + - + - ]: 2 : auto child_p1n0_p2n0 = MakeTransactionSpending({{parent1->GetHash(), 0}, {parent2->GetHash(), 0}}, det_rand);
288 : :
289 : 1 : const NodeId node1{1};
290 : 1 : const NodeId node2{2};
291 : :
292 : : // All orphans provided by node1
293 : 1 : {
294 : 1 : TxOrphanage orphanage;
295 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
+ - + - ]
296 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
+ - + - ]
297 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node1));
+ - + - ]
298 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node1));
+ - + - ]
299 : :
300 [ + + + - : 4 : std::set<CTransactionRef> expected_parent1_children{child_p1n0, child_p1n0_p2n0, child_p1n0_p1n1};
- - - - ]
301 [ + + + - : 3 : std::set<CTransactionRef> expected_parent2_children{child_p2n1, child_p1n0_p2n0};
- - - - ]
302 : :
303 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent1_children, orphanage.GetChildrenFromSamePeer(parent1, node1)));
+ - + - ]
304 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent2_children, orphanage.GetChildrenFromSamePeer(parent2, node1)));
+ - + - ]
305 : :
306 : : // The peer must match
307 [ + - + - : 2 : BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent1, node2).empty());
+ - + - ]
308 [ + - + - : 2 : BOOST_CHECK(orphanage.GetChildrenFromSamePeer(parent2, node2).empty());
+ - + - ]
309 : :
310 : : // There shouldn't be any children of this tx in the orphanage
311 [ + - + - : 2 : BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node1).empty());
+ - + - ]
312 [ + - + - : 2 : BOOST_CHECK(orphanage.GetChildrenFromSamePeer(child_p1n0_p2n0, node2).empty());
+ - ]
313 : 1 : }
314 : :
315 : : // Orphans provided by node1 and node2
316 : 1 : {
317 : 1 : TxOrphanage orphanage;
318 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0, node1));
+ - + - ]
319 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p2n1, node1));
+ - + - ]
320 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0_p1n1, node2));
+ - + - ]
321 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(child_p1n0_p2n0, node2));
+ - + - ]
322 : :
323 : : // +----------------+---------------+----------------------------------+
324 : : // | | sender=node1 | sender=node2 |
325 : : // +----------------+---------------+----------------------------------+
326 : : // | spends parent1 | child_p1n0 | child_p1n0_p1n1, child_p1n0_p2n0 |
327 : : // | spends parent2 | child_p2n1 | child_p1n0_p2n0 |
328 : : // +----------------+---------------+----------------------------------+
329 : :
330 : : // Children of parent1 from node1:
331 : 1 : {
332 [ + + + - : 2 : std::set<CTransactionRef> expected_parent1_node1{child_p1n0};
- - - - ]
333 : :
334 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent1_node1, orphanage.GetChildrenFromSamePeer(parent1, node1)));
+ - ]
335 : 0 : }
336 : :
337 : : // Children of parent2 from node1:
338 : 1 : {
339 [ + + + - : 2 : std::set<CTransactionRef> expected_parent2_node1{child_p2n1};
- - - - ]
340 : :
341 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent2_node1, orphanage.GetChildrenFromSamePeer(parent2, node1)));
+ - ]
342 : 0 : }
343 : :
344 : : // Children of parent1 from node2:
345 : 1 : {
346 [ + + + - : 3 : std::set<CTransactionRef> expected_parent1_node2{child_p1n0_p1n1, child_p1n0_p2n0};
- - - - ]
347 : :
348 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent1_node2, orphanage.GetChildrenFromSamePeer(parent1, node2)));
+ - ]
349 : 0 : }
350 : :
351 : : // Children of parent2 from node2:
352 : 1 : {
353 [ + + + - : 2 : std::set<CTransactionRef> expected_parent2_node2{child_p1n0_p2n0};
- - - - ]
354 : :
355 [ + - + - : 2 : BOOST_CHECK(EqualTxns(expected_parent2_node2, orphanage.GetChildrenFromSamePeer(parent2, node2)));
+ - ]
356 : 0 : }
357 [ + - ]: 1 : }
358 [ + - + - : 22 : }
+ - - + +
- + - - +
+ - - + +
- - + + -
+ - - + +
- - + + -
+ - + - +
- + - ]
359 : :
360 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(too_large_orphan_tx)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
361 : : {
362 : 1 : TxOrphanage orphanage;
363 [ + - ]: 1 : CMutableTransaction tx;
364 [ + - ]: 1 : tx.vin.resize(1);
365 : :
366 : : // check that txs larger than MAX_STANDARD_TX_WEIGHT are not added to the orphanage
367 [ + - ]: 1 : BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT + 4);
368 [ + - + - : 1 : BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT + 4);
+ - ]
369 [ + - + - : 4 : BOOST_CHECK(!orphanage.AddTx(MakeTransactionRef(tx), 0));
+ - + - +
- ]
370 : :
371 : 1 : tx.vout.clear();
372 [ + - ]: 1 : BulkTransaction(tx, MAX_STANDARD_TX_WEIGHT);
373 [ + - + - : 1 : BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(tx)), MAX_STANDARD_TX_WEIGHT);
+ - ]
374 [ + - + - : 4 : BOOST_CHECK(orphanage.AddTx(MakeTransactionRef(tx), 0));
+ - + - +
- ]
375 : 1 : }
376 : :
377 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(process_block)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
378 : : {
379 : 1 : FastRandomContext det_rand{true};
380 : 1 : TxOrphanageTest orphanage{det_rand};
381 : :
382 : : // Create outpoints that will be spent by transactions in the block
383 : 1 : std::vector<COutPoint> outpoints;
384 : 1 : const uint32_t num_outpoints{6};
385 [ + - ]: 1 : outpoints.reserve(num_outpoints);
386 [ + + ]: 7 : for (uint32_t i{0}; i < num_outpoints; ++i) {
387 : : // All the hashes should be different, but change the n just in case.
388 [ + - ]: 6 : outpoints.emplace_back(Txid::FromUint256(det_rand.rand256()), i);
389 : : }
390 : :
391 : 1 : CBlock block;
392 : 1 : const NodeId node{0};
393 : :
394 [ + - ]: 1 : auto control_tx = MakeTransactionSpending({}, det_rand);
395 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(control_tx, node));
+ - + - ]
396 : :
397 [ + - + - : 2 : auto bo_tx_same_txid = MakeTransactionSpending({outpoints.at(0)}, det_rand);
+ - + - ]
398 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(bo_tx_same_txid, node));
+ - + - ]
399 [ + - ]: 1 : block.vtx.emplace_back(bo_tx_same_txid);
400 : :
401 : : // 2 transactions with the same txid but different witness
402 [ + - + - : 2 : auto b_tx_same_txid_diff_witness = MakeTransactionSpending({outpoints.at(1)}, det_rand);
+ - + - ]
403 [ + - ]: 1 : block.vtx.emplace_back(b_tx_same_txid_diff_witness);
404 : :
405 [ + - ]: 1 : auto o_tx_same_txid_diff_witness = MakeMutation(b_tx_same_txid_diff_witness);
406 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(o_tx_same_txid_diff_witness, node));
+ - + - ]
407 : :
408 : : // 2 different transactions that spend the same input.
409 [ + - + - : 2 : auto b_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
+ - + - ]
410 [ + - ]: 1 : block.vtx.emplace_back(b_tx_conflict);
411 : :
412 [ + - + - : 2 : auto o_tx_conflict = MakeTransactionSpending({outpoints.at(2)}, det_rand);
+ - + - ]
413 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(o_tx_conflict, node));
+ - + - ]
414 : :
415 : : // 2 different transactions that have 1 overlapping input.
416 [ + - + - : 2 : auto b_tx_conflict_partial = MakeTransactionSpending({outpoints.at(3), outpoints.at(4)}, det_rand);
+ - + - +
- ]
417 [ + - ]: 1 : block.vtx.emplace_back(b_tx_conflict_partial);
418 : :
419 [ + - + - : 2 : auto o_tx_conflict_partial_2 = MakeTransactionSpending({outpoints.at(4), outpoints.at(5)}, det_rand);
+ - + - +
- ]
420 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(o_tx_conflict_partial_2, node));
+ - + - ]
421 : :
422 [ + - ]: 1 : orphanage.EraseForBlock(block);
423 [ + - + - : 17 : for (const auto& expected_removed : {bo_tx_same_txid, o_tx_same_txid_diff_witness, o_tx_conflict, o_tx_conflict_partial_2}) {
+ - + - +
+ + - -
- ]
424 [ + - ]: 4 : const auto& expected_removed_wtxid = expected_removed->GetWitnessHash();
425 [ + - + - : 8 : BOOST_CHECK(!orphanage.HaveTx(expected_removed_wtxid));
+ - ]
426 [ + + - - ]: 5 : }
427 : : // Only remaining tx is control_tx
428 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), 1);
429 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(control_tx->GetWitnessHash()));
+ - + - ]
430 [ + - + - : 8 : }
+ - + - +
- + - +
- ]
431 : :
432 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(multiple_announcers)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
433 : : {
434 : 1 : const NodeId node0{0};
435 : 1 : const NodeId node1{1};
436 : 1 : const NodeId node2{2};
437 : 1 : size_t expected_total_count{0};
438 : 1 : FastRandomContext det_rand{true};
439 : 1 : TxOrphanageTest orphanage{det_rand};
440 : :
441 : : // Check accounting per peer.
442 : : // Check that EraseForPeer works with multiple announcers.
443 : 1 : {
444 [ + - ]: 1 : auto ptx = MakeTransactionSpending({}, det_rand);
445 [ + - ]: 1 : const auto& wtxid = ptx->GetWitnessHash();
446 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(ptx, node0));
+ - + - ]
447 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(wtxid));
+ - + - ]
448 : 1 : expected_total_count += 1;
449 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
450 : :
451 : : // Adding again should do nothing.
452 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddTx(ptx, node0));
+ - + - ]
453 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
454 : :
455 : : // We can add another tx with the same txid but different witness.
456 [ + - ]: 1 : auto ptx_mutated{MakeMutation(ptx)};
457 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(ptx_mutated, node0));
+ - + - ]
458 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(ptx_mutated->GetWitnessHash()));
+ - + - ]
459 : 1 : expected_total_count += 1;
460 : :
461 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddTx(ptx, node0));
+ - + - ]
462 : :
463 : : // Adding a new announcer should not change overall accounting.
464 [ + - + - : 2 : BOOST_CHECK(orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
+ - + - ]
465 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
466 : :
467 : : // If we already have this announcer, AddAnnouncer returns false.
468 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node2));
+ - + - ]
469 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddAnnouncer(ptx->GetWitnessHash(), node2));
+ - + - ]
470 : :
471 : : // Same with using AddTx for an existing tx, which is equivalent to using AddAnnouncer
472 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddTx(ptx, node1));
+ - + - ]
473 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
474 : :
475 : : // if EraseForPeer is called for an orphan with multiple announcers, the orphanage should only
476 : : // erase that peer from the announcers set.
477 [ + - ]: 1 : orphanage.EraseForPeer(node0);
478 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
+ - + - ]
479 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTxFromPeer(ptx->GetWitnessHash(), node0));
+ - + - ]
480 : : // node0 is the only one that announced ptx_mutated
481 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(ptx_mutated->GetWitnessHash()));
+ - + - ]
482 : 1 : expected_total_count -= 1;
483 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
484 : :
485 : : // EraseForPeer should delete the orphan if it's the only announcer left.
486 [ + - ]: 1 : orphanage.EraseForPeer(node1);
487 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
488 [ + - + - : 2 : BOOST_CHECK(orphanage.HaveTx(ptx->GetWitnessHash()));
+ - + - ]
489 [ + - ]: 1 : orphanage.EraseForPeer(node2);
490 : 1 : expected_total_count -= 1;
491 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
492 [ + - + - : 2 : BOOST_CHECK(!orphanage.HaveTx(ptx->GetWitnessHash()));
+ - + - ]
493 [ + - ]: 1 : }
494 : :
495 : : // Check that erasure for blocks removes for all peers.
496 : 1 : {
497 : 1 : CBlock block;
498 [ + - ]: 1 : auto tx_block = MakeTransactionSpending({}, det_rand);
499 [ + - ]: 1 : block.vtx.emplace_back(tx_block);
500 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(tx_block, node0));
+ - + - ]
501 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddTx(tx_block, node1));
+ - + - ]
502 : :
503 : 1 : expected_total_count += 1;
504 : :
505 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
506 : :
507 [ + - ]: 1 : orphanage.EraseForBlock(block);
508 : :
509 : 1 : expected_total_count -= 1;
510 : :
511 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.Size(), expected_total_count);
+ - ]
512 : 1 : }
513 : 1 : }
514 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(peer_worksets)
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- ]
515 : : {
516 : 1 : const NodeId node0{0};
517 : 1 : const NodeId node1{1};
518 : 1 : const NodeId node2{2};
519 : 1 : FastRandomContext det_rand{true};
520 : 1 : TxOrphanageTest orphanage{det_rand};
521 : : // AddChildrenToWorkSet should pick an announcer randomly
522 : 1 : {
523 [ + - ]: 1 : auto tx_missing_parent = MakeTransactionSpending({}, det_rand);
524 [ + - + - : 2 : auto tx_orphan = MakeTransactionSpending({COutPoint{tx_missing_parent->GetHash(), 0}}, det_rand);
+ - ]
525 [ + - ]: 1 : const auto& orphan_wtxid = tx_orphan->GetWitnessHash();
526 : :
527 : : // All 3 peers are announcers.
528 [ + - + - : 2 : BOOST_CHECK(orphanage.AddTx(tx_orphan, node0));
+ - + - ]
529 [ + - + - : 2 : BOOST_CHECK(!orphanage.AddTx(tx_orphan, node1));
+ - + - ]
530 [ + - + - : 2 : BOOST_CHECK(orphanage.AddAnnouncer(orphan_wtxid, node2));
+ - ]
531 [ + + ]: 4 : for (NodeId node = node0; node <= node2; ++node) {
532 [ + - + - : 6 : BOOST_CHECK(orphanage.HaveTxFromPeer(orphan_wtxid, node));
+ - ]
533 : : }
534 : :
535 : : // Parent accepted: add child to all 3 worksets.
536 [ + - ]: 1 : orphanage.AddChildrenToWorkSet(*tx_missing_parent);
537 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), tx_orphan);
+ - + - ]
538 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node1), tx_orphan);
+ - + - ]
539 : : // Don't call GetTxToReconsider(node2) yet because it mutates the workset.
540 : :
541 : : // EraseForPeer also removes that tx from the workset.
542 [ + - ]: 1 : orphanage.EraseForPeer(node0);
543 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node0), nullptr);
+ - - + ]
544 : :
545 : : // However, the other peers' worksets are not touched.
546 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node2), tx_orphan);
+ - + - ]
547 : :
548 : : // Delete this tx, clearing the orphanage.
549 [ + - + - : 1 : BOOST_CHECK_EQUAL(orphanage.EraseTx(orphan_wtxid), 1);
+ - ]
550 [ + - + - ]: 1 : BOOST_CHECK_EQUAL(orphanage.Size(), 0);
551 [ + + ]: 4 : for (NodeId node = node0; node <= node2; ++node) {
552 [ + - + - : 3 : BOOST_CHECK_EQUAL(orphanage.GetTxToReconsider(node), nullptr);
+ - - + ]
553 [ + - + - : 6 : BOOST_CHECK(!orphanage.HaveTxFromPeer(orphan_wtxid, node));
+ - ]
554 : : }
555 [ + - ]: 2 : }
556 : 1 : }
557 : : BOOST_AUTO_TEST_SUITE_END()
|