Bitcoin Core 31.99.0
P2P Digital Currency
mempool_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-present 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 <common/system.h>
6#include <policy/policy.h>
7#include <test/util/time.h>
9#include <txmempool.h>
10#include <util/time.h>
11
13
14#include <boost/test/unit_test.hpp>
15#include <vector>
16
18
19static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::REPLACED;
20
21class MemPoolTest final : public CTxMemPool
22{
23public:
25};
26
27BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
28{
29 // Test CTxMemPool::remove functionality
30
32 // Parent transaction with three children,
33 // and three grand-children:
34 CMutableTransaction txParent;
35 txParent.vin.resize(1);
36 txParent.vin[0].scriptSig = CScript() << OP_11;
37 txParent.vout.resize(3);
38 for (int i = 0; i < 3; i++)
39 {
40 txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
41 txParent.vout[i].nValue = 33000LL;
42 }
43 CMutableTransaction txChild[3];
44 for (int i = 0; i < 3; i++)
45 {
46 txChild[i].vin.resize(1);
47 txChild[i].vin[0].scriptSig = CScript() << OP_11;
48 txChild[i].vin[0].prevout.hash = txParent.GetHash();
49 txChild[i].vin[0].prevout.n = i;
50 txChild[i].vout.resize(1);
51 txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
52 txChild[i].vout[0].nValue = 11000LL;
53 }
54 CMutableTransaction txGrandChild[3];
55 for (int i = 0; i < 3; i++)
56 {
57 txGrandChild[i].vin.resize(1);
58 txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
59 txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
60 txGrandChild[i].vin[0].prevout.n = 0;
61 txGrandChild[i].vout.resize(1);
62 txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
63 txGrandChild[i].vout[0].nValue = 11000LL;
64 }
65
66
67 CTxMemPool& testPool = *Assert(m_node.mempool);
68 LOCK2(::cs_main, testPool.cs);
69
70 // Nothing in pool, remove should do nothing:
71 unsigned int poolSize = testPool.size();
73 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
74
75 // Just the parent:
76 TryAddToMempool(testPool, entry.FromTx(txParent));
77 poolSize = testPool.size();
79 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
80
81 // Parent, children, grandchildren:
82 TryAddToMempool(testPool, entry.FromTx(txParent));
83 for (int i = 0; i < 3; i++)
84 {
85 TryAddToMempool(testPool, entry.FromTx(txChild[i]));
86 TryAddToMempool(testPool, entry.FromTx(txGrandChild[i]));
87 }
88 // Remove Child[0], GrandChild[0] should be removed:
89 poolSize = testPool.size();
91 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
92 // ... make sure grandchild and child are gone:
93 poolSize = testPool.size();
94 testPool.removeRecursive(CTransaction(txGrandChild[0]), REMOVAL_REASON_DUMMY);
95 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
96 poolSize = testPool.size();
98 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
99 // Remove parent, all children/grandchildren should go:
100 poolSize = testPool.size();
102 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
103 BOOST_CHECK_EQUAL(testPool.size(), 0U);
104
105 // Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
106 for (int i = 0; i < 3; i++)
107 {
108 TryAddToMempool(testPool, entry.FromTx(txChild[i]));
109 TryAddToMempool(testPool, entry.FromTx(txGrandChild[i]));
110 }
111 // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
112 // put into the mempool (maybe because it is non-standard):
113 poolSize = testPool.size();
115 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
116 BOOST_CHECK_EQUAL(testPool.size(), 0U);
117}
118
119BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
120{
121 auto& pool = static_cast<MemPoolTest&>(*Assert(m_node.mempool));
122 LOCK2(cs_main, pool.cs);
124
126 tx1.vin.resize(1);
127 tx1.vin[0].scriptSig = CScript() << OP_1;
128 tx1.vout.resize(1);
129 tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
130 tx1.vout[0].nValue = 10 * COIN;
131 TryAddToMempool(pool, entry.Fee(1000LL).FromTx(tx1));
132
134 tx2.vin.resize(1);
135 tx2.vin[0].scriptSig = CScript() << OP_2;
136 tx2.vout.resize(1);
137 tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
138 tx2.vout[0].nValue = 10 * COIN;
139 TryAddToMempool(pool, entry.Fee(500LL).FromTx(tx2));
140
141 pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
142 BOOST_CHECK(pool.exists(tx1.GetHash()));
143 BOOST_CHECK(pool.exists(tx2.GetHash()));
144
145 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
146 BOOST_CHECK(pool.exists(tx1.GetHash()));
147 BOOST_CHECK(!pool.exists(tx2.GetHash()));
148
149 TryAddToMempool(pool, entry.FromTx(tx2));
151 tx3.vin.resize(1);
152 tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
153 tx3.vin[0].scriptSig = CScript() << OP_2;
154 tx3.vout.resize(1);
155 tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
156 tx3.vout[0].nValue = 10 * COIN;
157 TryAddToMempool(pool, entry.Fee(2000LL).FromTx(tx3));
158
159 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
160 BOOST_CHECK(!pool.exists(tx1.GetHash()));
161 BOOST_CHECK(pool.exists(tx2.GetHash()));
162 BOOST_CHECK(pool.exists(tx3.GetHash()));
163
164 pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
165 BOOST_CHECK(!pool.exists(tx1.GetHash()));
166 BOOST_CHECK(!pool.exists(tx2.GetHash()));
167 BOOST_CHECK(!pool.exists(tx3.GetHash()));
168
170 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);
171
173 tx4.vin.resize(2);
174 tx4.vin[0].prevout.SetNull();
175 tx4.vin[0].scriptSig = CScript() << OP_4;
176 tx4.vin[1].prevout.SetNull();
177 tx4.vin[1].scriptSig = CScript() << OP_4;
178 tx4.vout.resize(2);
179 tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
180 tx4.vout[0].nValue = 10 * COIN;
181 tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
182 tx4.vout[1].nValue = 10 * COIN;
183
185 tx5.vin.resize(2);
186 tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
187 tx5.vin[0].scriptSig = CScript() << OP_4;
188 tx5.vin[1].prevout.SetNull();
189 tx5.vin[1].scriptSig = CScript() << OP_5;
190 tx5.vout.resize(2);
191 tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
192 tx5.vout[0].nValue = 10 * COIN;
193 tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
194 tx5.vout[1].nValue = 10 * COIN;
195
197 tx6.vin.resize(2);
198 tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
199 tx6.vin[0].scriptSig = CScript() << OP_4;
200 tx6.vin[1].prevout.SetNull();
201 tx6.vin[1].scriptSig = CScript() << OP_6;
202 tx6.vout.resize(2);
203 tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
204 tx6.vout[0].nValue = 10 * COIN;
205 tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
206 tx6.vout[1].nValue = 10 * COIN;
207
209 tx7.vin.resize(2);
210 tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
211 tx7.vin[0].scriptSig = CScript() << OP_5;
212 tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
213 tx7.vin[1].scriptSig = CScript() << OP_6;
214 tx7.vout.resize(2);
215 tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
216 tx7.vout[0].nValue = 10 * COIN;
217 tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
218 tx7.vout[1].nValue = 10 * COIN;
219
220 TryAddToMempool(pool, entry.Fee(700LL).FromTx(tx4));
221 auto usage_with_tx4_only = pool.DynamicMemoryUsage();
222 TryAddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
223 TryAddToMempool(pool, entry.Fee(110LL).FromTx(tx6));
224 TryAddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
225
226 // From the topology above, tx7 must be sorted last, so it should
227 // definitely evicted first if we must trim. tx4 should definitely remain
228 // in the mempool since it has a higher feerate than its descendants and
229 // should be in its own chunk.
230 pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
231 BOOST_CHECK(pool.exists(tx4.GetHash()));
232 BOOST_CHECK(!pool.exists(tx7.GetHash()));
233
234 // Tx5 and Tx6 may be removed as well because they're in the same chunk as
235 // tx7, but this behavior need not be guaranteed.
236
237 if (!pool.exists(tx5.GetHash()))
238 TryAddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
239 if (!pool.exists(tx6.GetHash()))
240 TryAddToMempool(pool, entry.Fee(110LL).FromTx(tx6));
241 TryAddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
242
243 // If we trim sufficiently, everything but tx4 should be removed.
244 pool.TrimToSize(usage_with_tx4_only + 1);
245 BOOST_CHECK(pool.exists(tx4.GetHash()));
246 BOOST_CHECK(!pool.exists(tx5.GetHash()));
247 BOOST_CHECK(!pool.exists(tx6.GetHash()));
248 BOOST_CHECK(!pool.exists(tx7.GetHash()));
249
250 TryAddToMempool(pool, entry.Fee(100LL).FromTx(tx5));
251 TryAddToMempool(pool, entry.Fee(110LL).FromTx(tx6));
252 TryAddToMempool(pool, entry.Fee(900LL).FromTx(tx7));
253
254 std::vector<CTransactionRef> vtx;
255 NodeClockContext clock_ctx{42s};
256 constexpr std::chrono::seconds HALFLIFE{CTxMemPool::ROLLING_FEE_HALFLIFE};
257 clock_ctx += HALFLIFE;
258 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE);
259 // ... we should keep the same min fee until we get a block
260 pool.removeForBlock(vtx, 1);
261 clock_ctx += HALFLIFE;
262 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/2.0));
263 // ... then feerate should drop 1/2 each halflife
264
265 clock_ctx += HALFLIFE / 2;
266 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/4.0));
267 // ... with a 1/2 halflife when mempool is < 1/2 its target size
268
269 clock_ctx += HALFLIFE / 4;
270 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + DEFAULT_INCREMENTAL_RELAY_FEE)/8.0));
271 // ... with a 1/4 halflife when mempool is < 1/4 its target size
272
273 clock_ctx += 5 * HALFLIFE;
274 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), DEFAULT_INCREMENTAL_RELAY_FEE);
275 // ... but feerate should never drop below DEFAULT_INCREMENTAL_RELAY_FEE
276
277 clock_ctx += HALFLIFE;
278 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
279 // ... unless it has gone all the way to 0 (after getting past DEFAULT_INCREMENTAL_RELAY_FEE/2)
280}
281
282inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())
283{
285 tx.vin.resize(inputs.size());
286 tx.vout.resize(output_values.size());
287 for (size_t i = 0; i < inputs.size(); ++i) {
288 tx.vin[i].prevout.hash = inputs[i]->GetHash();
289 tx.vin[i].prevout.n = input_indices.size() > i ? input_indices[i] : 0;
290 }
291 for (size_t i = 0; i < output_values.size(); ++i) {
292 tx.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
293 tx.vout[i].nValue = output_values[i];
294 }
295 return MakeTransactionRef(tx);
296}
297
298
299BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
300{
301 size_t ancestors, clustersize;
302
304 LOCK2(cs_main, pool.cs);
306
307 /* Base transaction */
308 //
309 // [tx1]
310 //
311 CTransactionRef tx1 = make_tx(/*output_values=*/{10 * COIN});
312 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx1));
313
314 // Ancestors / clustersize should be 1 / 1 (itself / itself)
315 pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);
316 BOOST_CHECK_EQUAL(ancestors, 1ULL);
317 BOOST_CHECK_EQUAL(clustersize, 1ULL);
318
319 /* Child transaction */
320 //
321 // [tx1].0 <- [tx2]
322 //
323 CTransactionRef tx2 = make_tx(/*output_values=*/{495 * CENT, 5 * COIN}, /*inputs=*/{tx1});
324 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx2));
325
326 // Ancestors / clustersize should be:
327 // transaction ancestors clustersize
328 // ============ =========== ===========
329 // tx1 1 (tx1) 2 (tx1,2)
330 // tx2 2 (tx1,2) 2 (tx1,2)
331 pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);
332 BOOST_CHECK_EQUAL(ancestors, 1ULL);
333 BOOST_CHECK_EQUAL(clustersize, 2ULL);
334 pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);
335 BOOST_CHECK_EQUAL(ancestors, 2ULL);
336 BOOST_CHECK_EQUAL(clustersize, 2ULL);
337
338 /* Grand-child 1 */
339 //
340 // [tx1].0 <- [tx2].0 <- [tx3]
341 //
342 CTransactionRef tx3 = make_tx(/*output_values=*/{290 * CENT, 200 * CENT}, /*inputs=*/{tx2});
343 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx3));
344
345 // Ancestors / clustersize should be:
346 // transaction ancestors clustersize
347 // ============ =========== ===========
348 // tx1 1 (tx1) 3 (tx1,2,3)
349 // tx2 2 (tx1,2) 3 (tx1,2,3)
350 // tx3 3 (tx1,2,3) 3 (tx1,2,3)
351 pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);
352 BOOST_CHECK_EQUAL(ancestors, 1ULL);
353 BOOST_CHECK_EQUAL(clustersize, 3ULL);
354 pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);
355 BOOST_CHECK_EQUAL(ancestors, 2ULL);
356 BOOST_CHECK_EQUAL(clustersize, 3ULL);
357 pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);
358 BOOST_CHECK_EQUAL(ancestors, 3ULL);
359 BOOST_CHECK_EQUAL(clustersize, 3ULL);
360
361 /* Grand-child 2 */
362 //
363 // [tx1].0 <- [tx2].0 <- [tx3]
364 // |
365 // \---1 <- [tx4]
366 //
367 CTransactionRef tx4 = make_tx(/*output_values=*/{290 * CENT, 250 * CENT}, /*inputs=*/{tx2}, /*input_indices=*/{1});
368 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tx4));
369
370 // Ancestors / clustersize should be:
371 // transaction ancestors clustersize
372 // ============ =========== ===========
373 // tx1 1 (tx1) 4 (tx1,2,3,4)
374 // tx2 2 (tx1,2) 4 (tx1,2,3,4)
375 // tx3 3 (tx1,2,3) 4 (tx1,2,3,4)
376 // tx4 3 (tx1,2,4) 4 (tx1,2,3,4)
377 pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);
378 BOOST_CHECK_EQUAL(ancestors, 1ULL);
379 BOOST_CHECK_EQUAL(clustersize, 4ULL);
380 pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);
381 BOOST_CHECK_EQUAL(ancestors, 2ULL);
382 BOOST_CHECK_EQUAL(clustersize, 4ULL);
383 pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);
384 BOOST_CHECK_EQUAL(ancestors, 3ULL);
385 BOOST_CHECK_EQUAL(clustersize, 4ULL);
386 pool.GetTransactionAncestry(tx4->GetHash(), ancestors, clustersize);
387 BOOST_CHECK_EQUAL(ancestors, 3ULL);
388 BOOST_CHECK_EQUAL(clustersize, 4ULL);
389
390 /* Make an alternate branch that is longer and connect it to tx3 */
391 //
392 // [ty1].0 <- [ty2].0 <- [ty3].0 <- [ty4].0 <- [ty5].0
393 // |
394 // [tx1].0 <- [tx2].0 <- [tx3].0 <- [ty6] --->--/
395 // |
396 // \---1 <- [tx4]
397 //
398 CTransactionRef ty1, ty2, ty3, ty4, ty5;
399 CTransactionRef* ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5};
400 CAmount v = 5 * COIN;
401 for (uint64_t i = 0; i < 5; i++) {
402 CTransactionRef& tyi = *ty[i];
403 tyi = make_tx(/*output_values=*/{v}, /*inputs=*/i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{});
404 v -= 50 * CENT;
405 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tyi));
406 pool.GetTransactionAncestry(tyi->GetHash(), ancestors, clustersize);
407 BOOST_CHECK_EQUAL(ancestors, i+1);
408 BOOST_CHECK_EQUAL(clustersize, i+1);
409 }
410 CTransactionRef ty6 = make_tx(/*output_values=*/{5 * COIN}, /*inputs=*/{tx3, ty5});
411 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(ty6));
412
413 // Ancestors / clustersize should be:
414 // transaction ancestors clustersize
415 // ============ =================== ===========
416 // tx1 1 (tx1) 10 (tx1-5, ty1-5)
417 // tx2 2 (tx1,2) 10
418 // tx3 3 (tx1,2,3) 10
419 // tx4 3 (tx1,2,4) 10
420 // ty1 1 (ty1) 10
421 // ty2 2 (ty1,2) 10
422 // ty3 3 (ty1,2,3) 10
423 // ty4 4 (y1234) 10
424 // ty5 5 (y12345) 10
425 // ty6 9 (tx123, ty123456) 10
426 pool.GetTransactionAncestry(tx1->GetHash(), ancestors, clustersize);
427 BOOST_CHECK_EQUAL(ancestors, 1ULL);
428 BOOST_CHECK_EQUAL(clustersize, 10ULL);
429 pool.GetTransactionAncestry(tx2->GetHash(), ancestors, clustersize);
430 BOOST_CHECK_EQUAL(ancestors, 2ULL);
431 BOOST_CHECK_EQUAL(clustersize, 10ULL);
432 pool.GetTransactionAncestry(tx3->GetHash(), ancestors, clustersize);
433 BOOST_CHECK_EQUAL(ancestors, 3ULL);
434 BOOST_CHECK_EQUAL(clustersize, 10ULL);
435 pool.GetTransactionAncestry(tx4->GetHash(), ancestors, clustersize);
436 BOOST_CHECK_EQUAL(ancestors, 3ULL);
437 BOOST_CHECK_EQUAL(clustersize, 10ULL);
438 pool.GetTransactionAncestry(ty1->GetHash(), ancestors, clustersize);
439 BOOST_CHECK_EQUAL(ancestors, 1ULL);
440 BOOST_CHECK_EQUAL(clustersize, 10ULL);
441 pool.GetTransactionAncestry(ty2->GetHash(), ancestors, clustersize);
442 BOOST_CHECK_EQUAL(ancestors, 2ULL);
443 BOOST_CHECK_EQUAL(clustersize, 10ULL);
444 pool.GetTransactionAncestry(ty3->GetHash(), ancestors, clustersize);
445 BOOST_CHECK_EQUAL(ancestors, 3ULL);
446 BOOST_CHECK_EQUAL(clustersize, 10ULL);
447 pool.GetTransactionAncestry(ty4->GetHash(), ancestors, clustersize);
448 BOOST_CHECK_EQUAL(ancestors, 4ULL);
449 BOOST_CHECK_EQUAL(clustersize, 10ULL);
450 pool.GetTransactionAncestry(ty5->GetHash(), ancestors, clustersize);
451 BOOST_CHECK_EQUAL(ancestors, 5ULL);
452 BOOST_CHECK_EQUAL(clustersize, 10ULL);
453 pool.GetTransactionAncestry(ty6->GetHash(), ancestors, clustersize);
454 BOOST_CHECK_EQUAL(ancestors, 9ULL);
455 BOOST_CHECK_EQUAL(clustersize, 10ULL);
456}
457
458BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)
459{
460 size_t ancestors, descendants;
461
463 LOCK2(::cs_main, pool.cs);
465
466 /* Ancestors represented more than once ("diamond") */
467 //
468 // [ta].0 <- [tb].0 -----<------- [td].0
469 // | |
470 // \---1 <- [tc].0 --<--/
471 //
472 CTransactionRef ta, tb, tc, td;
473 ta = make_tx(/*output_values=*/{10 * COIN});
474 tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
475 tc = make_tx(/*output_values=*/{2 * COIN}, /*inputs=*/{tb}, /*input_indices=*/{1});
476 td = make_tx(/*output_values=*/{6 * COIN}, /*inputs=*/{tb, tc}, /*input_indices=*/{0, 0});
477 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(ta));
478 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tb));
479 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(tc));
480 TryAddToMempool(pool, entry.Fee(10000LL).FromTx(td));
481
482 // Ancestors / descendants should be:
483 // transaction ancestors descendants
484 // ============ =================== ===========
485 // ta 1 (ta 4 (ta,tb,tc,td)
486 // tb 2 (ta,tb) 4 (ta,tb,tc,td)
487 // tc 3 (ta,tb,tc) 4 (ta,tb,tc,td)
488 // td 4 (ta,tb,tc,td) 4 (ta,tb,tc,td)
489 pool.GetTransactionAncestry(ta->GetHash(), ancestors, descendants);
490 BOOST_CHECK_EQUAL(ancestors, 1ULL);
491 BOOST_CHECK_EQUAL(descendants, 4ULL);
492 pool.GetTransactionAncestry(tb->GetHash(), ancestors, descendants);
493 BOOST_CHECK_EQUAL(ancestors, 2ULL);
494 BOOST_CHECK_EQUAL(descendants, 4ULL);
495 pool.GetTransactionAncestry(tc->GetHash(), ancestors, descendants);
496 BOOST_CHECK_EQUAL(ancestors, 3ULL);
497 BOOST_CHECK_EQUAL(descendants, 4ULL);
498 pool.GetTransactionAncestry(td->GetHash(), ancestors, descendants);
499 BOOST_CHECK_EQUAL(ancestors, 4ULL);
500 BOOST_CHECK_EQUAL(descendants, 4ULL);
501}
502
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15
TryAddToMempool(pool, CTxMemPoolEntry(tx, fee, 0, 1, 0, false, 4, lp))
node::NodeContext m_node
Definition: bitcoin-gui.cpp:47
#define Assert(val)
Identity function.
Definition: check.h:116
Fee rate in satoshis per virtualbyte: CAmount / vB the feerate is represented internally as FeeFrac.
Definition: feerate.h:32
CAmount GetFeePerK() const
Return the fee in satoshis for a vsize of 1000 vbytes.
Definition: feerate.h:62
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:405
The basic transaction that is broadcasted on the network and contained in blocks.
Definition: transaction.h:281
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:187
CFeeRate GetMinFee() const
The minimum fee to get into the mempool, which may itself not be enough for larger-sized transactions...
Definition: txmempool.h:450
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
If sanity-checking is turned on, check makes sure the pool is consistent (does not contain two transa...
Definition: txmempool.h:323
void GetTransactionAncestry(const Txid &txid, size_t &ancestors, size_t &cluster_count, size_t *ancestorsize=nullptr, CAmount *ancestorfees=nullptr) const
Calculate the ancestor and cluster count for the given transaction.
Definition: txmempool.cpp:943
static const int ROLLING_FEE_HALFLIFE
Definition: txmempool.h:212
unsigned long size() const
Definition: txmempool.h:483
Helper to initialize the global NodeClock, let a duration elapse, and reset it after use in a test.
Definition: time.h:40
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
MemPoolRemovalReason
Reason why a transaction was removed from the mempool, this is passed to the notification signal.
CTransactionRef make_tx(std::vector< CAmount > &&output_values, std::vector< CTransactionRef > &&inputs=std::vector< CTransactionRef >(), std::vector< uint32_t > &&input_indices=std::vector< uint32_t >())
static constexpr auto REMOVAL_REASON_DUMMY
BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:17
#define BOOST_CHECK(expr)
Definition: object.cpp:16
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost, unsigned int bytes_per_sigop)
Compute the virtual transaction size (weight reinterpreted as bytes).
Definition: policy.cpp:395
static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE
Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or rep...
Definition: policy.h:48
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:404
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:403
@ OP_2
Definition: script.h:85
@ OP_EQUAL
Definition: script.h:146
@ OP_4
Definition: script.h:87
@ OP_1
Definition: script.h:83
@ OP_3
Definition: script.h:86
@ OP_11
Definition: script.h:94
@ OP_6
Definition: script.h:89
@ OP_7
Definition: script.h:90
@ OP_5
Definition: script.h:88
static constexpr CAmount CENT
Definition: setup_common.h:44
A mutable version of CTransaction.
Definition: transaction.h:358
std::vector< CTxOut > vout
Definition: transaction.h:360
Txid GetHash() const
Compute the hash of this CMutableTransaction.
Definition: transaction.cpp:69
std::vector< CTxIn > vin
Definition: transaction.h:359
Definition: txmempool.h:19
CTxMemPoolEntry FromTx(const CMutableTransaction &tx) const
Definition: txmempool.cpp:34
TestMemPoolEntryHelper & Fee(CAmount _fee)
Definition: txmempool.h:33
Testing setup that configures a complete environment.
Definition: setup_common.h:118
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:69
#define LOCK2(cs1, cs2)
Definition: sync.h:269