Line data Source code
1 : // Copyright (c) 2011-2021 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 <policy/policy.h>
6 : #include <test/util/txmempool.h>
7 : #include <txmempool.h>
8 : #include <util/system.h>
9 : #include <util/time.h>
10 :
11 : #include <test/util/setup_common.h>
12 :
13 : #include <boost/test/unit_test.hpp>
14 : #include <vector>
15 :
16 146 : BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
17 :
18 : static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::MANUAL;
19 :
20 149 : BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
21 : {
22 : // Test CTxMemPool::remove functionality
23 :
24 1 : TestMemPoolEntryHelper entry;
25 : // Parent transaction with three children,
26 : // and three grand-children:
27 1 : CMutableTransaction txParent;
28 1 : txParent.vin.resize(1);
29 1 : txParent.vin[0].scriptSig = CScript() << OP_11;
30 1 : txParent.vout.resize(3);
31 4 : for (int i = 0; i < 3; i++)
32 : {
33 3 : txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
34 3 : txParent.vout[i].nValue = 33000LL;
35 3 : }
36 3 : CMutableTransaction txChild[3];
37 4 : for (int i = 0; i < 3; i++)
38 : {
39 3 : txChild[i].vin.resize(1);
40 3 : txChild[i].vin[0].scriptSig = CScript() << OP_11;
41 3 : txChild[i].vin[0].prevout.hash = txParent.GetHash();
42 3 : txChild[i].vin[0].prevout.n = i;
43 3 : txChild[i].vout.resize(1);
44 3 : txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
45 3 : txChild[i].vout[0].nValue = 11000LL;
46 3 : }
47 3 : CMutableTransaction txGrandChild[3];
48 4 : for (int i = 0; i < 3; i++)
49 : {
50 3 : txGrandChild[i].vin.resize(1);
51 3 : txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
52 3 : txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
53 3 : txGrandChild[i].vin[0].prevout.n = 0;
54 3 : txGrandChild[i].vout.resize(1);
55 3 : txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
56 3 : txGrandChild[i].vout[0].nValue = 11000LL;
57 3 : }
58 :
59 :
60 1 : CTxMemPool& testPool = *Assert(m_node.mempool);
61 1 : LOCK2(cs_main, testPool.cs);
62 :
63 : // Nothing in pool, remove should do nothing:
64 1 : unsigned int poolSize = testPool.size();
65 1 : testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
66 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize);
67 :
68 : // Just the parent:
69 1 : testPool.addUnchecked(entry.FromTx(txParent));
70 1 : poolSize = testPool.size();
71 1 : testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
72 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
73 :
74 : // Parent, children, grandchildren:
75 1 : testPool.addUnchecked(entry.FromTx(txParent));
76 4 : for (int i = 0; i < 3; i++)
77 : {
78 3 : testPool.addUnchecked(entry.FromTx(txChild[i]));
79 3 : testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
80 3 : }
81 : // Remove Child[0], GrandChild[0] should be removed:
82 1 : poolSize = testPool.size();
83 1 : testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
84 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
85 : // ... make sure grandchild and child are gone:
86 1 : poolSize = testPool.size();
87 1 : testPool.removeRecursive(CTransaction(txGrandChild[0]), REMOVAL_REASON_DUMMY);
88 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize);
89 1 : poolSize = testPool.size();
90 1 : testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
91 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize);
92 : // Remove parent, all children/grandchildren should go:
93 1 : poolSize = testPool.size();
94 1 : testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
95 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
96 1 : BOOST_CHECK_EQUAL(testPool.size(), 0U);
97 :
98 : // Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
99 4 : for (int i = 0; i < 3; i++)
100 : {
101 3 : testPool.addUnchecked(entry.FromTx(txChild[i]));
102 3 : testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
103 3 : }
104 : // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
105 : // put into the mempool (maybe because it is non-standard):
106 1 : poolSize = testPool.size();
107 1 : testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
108 1 : BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
109 1 : BOOST_CHECK_EQUAL(testPool.size(), 0U);
110 1 : }
111 :
112 : template <typename name>
113 12 : static void CheckSort(CTxMemPool& pool, std::vector<std::string>& sortedOrder) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
114 : {
115 12 : BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size());
116 12 : typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin();
117 12 : int count = 0;
118 97 : for (; it != pool.mapTx.get<name>().end(); ++it, ++count) {
119 85 : BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]);
120 85 : }
121 12 : }
122 :
123 149 : BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
124 : {
125 1 : CTxMemPool& pool = *Assert(m_node.mempool);
126 1 : LOCK2(cs_main, pool.cs);
127 1 : TestMemPoolEntryHelper entry;
128 :
129 : /* 3rd highest fee */
130 1 : CMutableTransaction tx1 = CMutableTransaction();
131 1 : tx1.vout.resize(1);
132 1 : tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
133 1 : tx1.vout[0].nValue = 10 * COIN;
134 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
135 :
136 : /* highest fee */
137 1 : CMutableTransaction tx2 = CMutableTransaction();
138 1 : tx2.vout.resize(1);
139 1 : tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
140 1 : tx2.vout[0].nValue = 2 * COIN;
141 1 : pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
142 :
143 : /* lowest fee */
144 1 : CMutableTransaction tx3 = CMutableTransaction();
145 1 : tx3.vout.resize(1);
146 1 : tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
147 1 : tx3.vout[0].nValue = 5 * COIN;
148 1 : pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
149 :
150 : /* 2nd highest fee */
151 1 : CMutableTransaction tx4 = CMutableTransaction();
152 1 : tx4.vout.resize(1);
153 1 : tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
154 1 : tx4.vout[0].nValue = 6 * COIN;
155 1 : pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
156 :
157 : /* equal fee rate to tx1, but newer */
158 1 : CMutableTransaction tx5 = CMutableTransaction();
159 1 : tx5.vout.resize(1);
160 1 : tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
161 1 : tx5.vout[0].nValue = 11 * COIN;
162 1 : entry.time = NodeSeconds{1s};
163 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
164 1 : BOOST_CHECK_EQUAL(pool.size(), 5U);
165 :
166 1 : std::vector<std::string> sortedOrder;
167 1 : sortedOrder.resize(5);
168 1 : sortedOrder[0] = tx3.GetHash().ToString(); // 0
169 1 : sortedOrder[1] = tx5.GetHash().ToString(); // 10000
170 1 : sortedOrder[2] = tx1.GetHash().ToString(); // 10000
171 1 : sortedOrder[3] = tx4.GetHash().ToString(); // 15000
172 1 : sortedOrder[4] = tx2.GetHash().ToString(); // 20000
173 1 : CheckSort<descendant_score>(pool, sortedOrder);
174 :
175 : /* low fee but with high fee child */
176 : /* tx6 -> tx7 -> tx8, tx9 -> tx10 */
177 1 : CMutableTransaction tx6 = CMutableTransaction();
178 1 : tx6.vout.resize(1);
179 1 : tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
180 1 : tx6.vout[0].nValue = 20 * COIN;
181 1 : pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
182 1 : BOOST_CHECK_EQUAL(pool.size(), 6U);
183 : // Check that at this point, tx6 is sorted low
184 1 : sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
185 1 : CheckSort<descendant_score>(pool, sortedOrder);
186 :
187 1 : CTxMemPool::setEntries setAncestors;
188 1 : setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
189 1 : CMutableTransaction tx7 = CMutableTransaction();
190 1 : tx7.vin.resize(1);
191 1 : tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
192 1 : tx7.vin[0].scriptSig = CScript() << OP_11;
193 1 : tx7.vout.resize(2);
194 1 : tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
195 1 : tx7.vout[0].nValue = 10 * COIN;
196 1 : tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
197 1 : tx7.vout[1].nValue = 1 * COIN;
198 :
199 1 : CTxMemPool::setEntries setAncestorsCalculated;
200 1 : std::string dummy;
201 1 : BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
202 1 : BOOST_CHECK(setAncestorsCalculated == setAncestors);
203 :
204 1 : pool.addUnchecked(entry.FromTx(tx7), setAncestors);
205 1 : BOOST_CHECK_EQUAL(pool.size(), 7U);
206 :
207 : // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
208 1 : sortedOrder.erase(sortedOrder.begin());
209 1 : sortedOrder.push_back(tx6.GetHash().ToString());
210 1 : sortedOrder.push_back(tx7.GetHash().ToString());
211 1 : CheckSort<descendant_score>(pool, sortedOrder);
212 :
213 : /* low fee child of tx7 */
214 1 : CMutableTransaction tx8 = CMutableTransaction();
215 1 : tx8.vin.resize(1);
216 1 : tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
217 1 : tx8.vin[0].scriptSig = CScript() << OP_11;
218 1 : tx8.vout.resize(1);
219 1 : tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
220 1 : tx8.vout[0].nValue = 10 * COIN;
221 1 : setAncestors.insert(pool.mapTx.find(tx7.GetHash()));
222 1 : pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8), setAncestors);
223 :
224 : // Now tx8 should be sorted low, but tx6/tx both high
225 1 : sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
226 1 : CheckSort<descendant_score>(pool, sortedOrder);
227 :
228 : /* low fee child of tx7 */
229 1 : CMutableTransaction tx9 = CMutableTransaction();
230 1 : tx9.vin.resize(1);
231 1 : tx9.vin[0].prevout = COutPoint(tx7.GetHash(), 1);
232 1 : tx9.vin[0].scriptSig = CScript() << OP_11;
233 1 : tx9.vout.resize(1);
234 1 : tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
235 1 : tx9.vout[0].nValue = 1 * COIN;
236 1 : pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{3s}).FromTx(tx9), setAncestors);
237 :
238 : // tx9 should be sorted low
239 1 : BOOST_CHECK_EQUAL(pool.size(), 9U);
240 1 : sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
241 1 : CheckSort<descendant_score>(pool, sortedOrder);
242 :
243 1 : std::vector<std::string> snapshotOrder = sortedOrder;
244 :
245 1 : setAncestors.insert(pool.mapTx.find(tx8.GetHash()));
246 1 : setAncestors.insert(pool.mapTx.find(tx9.GetHash()));
247 : /* tx10 depends on tx8 and tx9 and has a high fee*/
248 1 : CMutableTransaction tx10 = CMutableTransaction();
249 1 : tx10.vin.resize(2);
250 1 : tx10.vin[0].prevout = COutPoint(tx8.GetHash(), 0);
251 1 : tx10.vin[0].scriptSig = CScript() << OP_11;
252 1 : tx10.vin[1].prevout = COutPoint(tx9.GetHash(), 0);
253 1 : tx10.vin[1].scriptSig = CScript() << OP_11;
254 1 : tx10.vout.resize(1);
255 1 : tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
256 1 : tx10.vout[0].nValue = 10 * COIN;
257 :
258 1 : setAncestorsCalculated.clear();
259 1 : BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(NodeSeconds{4s}).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
260 1 : BOOST_CHECK(setAncestorsCalculated == setAncestors);
261 :
262 1 : pool.addUnchecked(entry.FromTx(tx10), setAncestors);
263 :
264 : /**
265 : * tx8 and tx9 should both now be sorted higher
266 : * Final order after tx10 is added:
267 : *
268 : * tx3 = 0 (1)
269 : * tx5 = 10000 (1)
270 : * tx1 = 10000 (1)
271 : * tx4 = 15000 (1)
272 : * tx2 = 20000 (1)
273 : * tx9 = 200k (2 txs)
274 : * tx8 = 200k (2 txs)
275 : * tx10 = 200k (1 tx)
276 : * tx6 = 2.2M (5 txs)
277 : * tx7 = 2.2M (4 txs)
278 : */
279 1 : sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning
280 1 : sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
281 1 : sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
282 1 : sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
283 1 : CheckSort<descendant_score>(pool, sortedOrder);
284 :
285 : // there should be 10 transactions in the mempool
286 1 : BOOST_CHECK_EQUAL(pool.size(), 10U);
287 :
288 : // Now try removing tx10 and verify the sort order returns to normal
289 1 : pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
290 1 : CheckSort<descendant_score>(pool, snapshotOrder);
291 :
292 1 : pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
293 1 : pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
294 1 : }
295 :
296 149 : BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
297 : {
298 1 : CTxMemPool& pool = *Assert(m_node.mempool);
299 1 : LOCK2(cs_main, pool.cs);
300 1 : TestMemPoolEntryHelper entry;
301 :
302 : /* 3rd highest fee */
303 1 : CMutableTransaction tx1 = CMutableTransaction();
304 1 : tx1.vout.resize(1);
305 1 : tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
306 1 : tx1.vout[0].nValue = 10 * COIN;
307 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
308 :
309 : /* highest fee */
310 1 : CMutableTransaction tx2 = CMutableTransaction();
311 1 : tx2.vout.resize(1);
312 1 : tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
313 1 : tx2.vout[0].nValue = 2 * COIN;
314 1 : pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
315 1 : uint64_t tx2Size = GetVirtualTransactionSize(CTransaction(tx2));
316 :
317 : /* lowest fee */
318 1 : CMutableTransaction tx3 = CMutableTransaction();
319 1 : tx3.vout.resize(1);
320 1 : tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
321 1 : tx3.vout[0].nValue = 5 * COIN;
322 1 : pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
323 :
324 : /* 2nd highest fee */
325 1 : CMutableTransaction tx4 = CMutableTransaction();
326 1 : tx4.vout.resize(1);
327 1 : tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
328 1 : tx4.vout[0].nValue = 6 * COIN;
329 1 : pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
330 :
331 : /* equal fee rate to tx1, but newer */
332 1 : CMutableTransaction tx5 = CMutableTransaction();
333 1 : tx5.vout.resize(1);
334 1 : tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
335 1 : tx5.vout[0].nValue = 11 * COIN;
336 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
337 1 : BOOST_CHECK_EQUAL(pool.size(), 5U);
338 :
339 1 : std::vector<std::string> sortedOrder;
340 1 : sortedOrder.resize(5);
341 1 : sortedOrder[0] = tx2.GetHash().ToString(); // 20000
342 1 : sortedOrder[1] = tx4.GetHash().ToString(); // 15000
343 : // tx1 and tx5 are both 10000
344 : // Ties are broken by hash, not timestamp, so determine which
345 : // hash comes first.
346 1 : if (tx1.GetHash() < tx5.GetHash()) {
347 1 : sortedOrder[2] = tx1.GetHash().ToString();
348 1 : sortedOrder[3] = tx5.GetHash().ToString();
349 1 : } else {
350 0 : sortedOrder[2] = tx5.GetHash().ToString();
351 0 : sortedOrder[3] = tx1.GetHash().ToString();
352 : }
353 1 : sortedOrder[4] = tx3.GetHash().ToString(); // 0
354 :
355 1 : CheckSort<ancestor_score>(pool, sortedOrder);
356 :
357 : /* low fee parent with high fee child */
358 : /* tx6 (0) -> tx7 (high) */
359 1 : CMutableTransaction tx6 = CMutableTransaction();
360 1 : tx6.vout.resize(1);
361 1 : tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
362 1 : tx6.vout[0].nValue = 20 * COIN;
363 1 : uint64_t tx6Size = GetVirtualTransactionSize(CTransaction(tx6));
364 :
365 1 : pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
366 1 : BOOST_CHECK_EQUAL(pool.size(), 6U);
367 : // Ties are broken by hash
368 1 : if (tx3.GetHash() < tx6.GetHash())
369 0 : sortedOrder.push_back(tx6.GetHash().ToString());
370 : else
371 1 : sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
372 :
373 1 : CheckSort<ancestor_score>(pool, sortedOrder);
374 :
375 1 : CMutableTransaction tx7 = CMutableTransaction();
376 1 : tx7.vin.resize(1);
377 1 : tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
378 1 : tx7.vin[0].scriptSig = CScript() << OP_11;
379 1 : tx7.vout.resize(1);
380 1 : tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
381 1 : tx7.vout[0].nValue = 10 * COIN;
382 1 : uint64_t tx7Size = GetVirtualTransactionSize(CTransaction(tx7));
383 :
384 : /* set the fee to just below tx2's feerate when including ancestor */
385 1 : CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
386 :
387 1 : pool.addUnchecked(entry.Fee(fee).FromTx(tx7));
388 1 : BOOST_CHECK_EQUAL(pool.size(), 7U);
389 1 : sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString());
390 1 : CheckSort<ancestor_score>(pool, sortedOrder);
391 :
392 : /* after tx6 is mined, tx7 should move up in the sort */
393 1 : std::vector<CTransactionRef> vtx;
394 1 : vtx.push_back(MakeTransactionRef(tx6));
395 1 : pool.removeForBlock(vtx, 1);
396 :
397 1 : sortedOrder.erase(sortedOrder.begin()+1);
398 : // Ties are broken by hash
399 1 : if (tx3.GetHash() < tx6.GetHash())
400 0 : sortedOrder.pop_back();
401 : else
402 1 : sortedOrder.erase(sortedOrder.end()-2);
403 1 : sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
404 1 : CheckSort<ancestor_score>(pool, sortedOrder);
405 :
406 : // High-fee parent, low-fee child
407 : // tx7 -> tx8
408 1 : CMutableTransaction tx8 = CMutableTransaction();
409 1 : tx8.vin.resize(1);
410 1 : tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
411 1 : tx8.vin[0].scriptSig = CScript() << OP_11;
412 1 : tx8.vout.resize(1);
413 1 : tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
414 1 : tx8.vout[0].nValue = 10*COIN;
415 :
416 : // Check that we sort by min(feerate, ancestor_feerate):
417 : // set the fee so that the ancestor feerate is above tx1/5,
418 : // but the transaction's own feerate is lower
419 1 : pool.addUnchecked(entry.Fee(5000LL).FromTx(tx8));
420 1 : sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
421 1 : CheckSort<ancestor_score>(pool, sortedOrder);
422 1 : }
423 :
424 :
425 149 : BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
426 : {
427 1 : CTxMemPool& pool = *Assert(m_node.mempool);
428 1 : LOCK2(cs_main, pool.cs);
429 1 : TestMemPoolEntryHelper entry;
430 :
431 1 : CMutableTransaction tx1 = CMutableTransaction();
432 1 : tx1.vin.resize(1);
433 1 : tx1.vin[0].scriptSig = CScript() << OP_1;
434 1 : tx1.vout.resize(1);
435 1 : tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
436 1 : tx1.vout[0].nValue = 10 * COIN;
437 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
438 :
439 1 : CMutableTransaction tx2 = CMutableTransaction();
440 1 : tx2.vin.resize(1);
441 1 : tx2.vin[0].scriptSig = CScript() << OP_2;
442 1 : tx2.vout.resize(1);
443 1 : tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
444 1 : tx2.vout[0].nValue = 10 * COIN;
445 1 : pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
446 :
447 1 : pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
448 1 : BOOST_CHECK(pool.exists(tx1.GetHash()));
449 1 : BOOST_CHECK(pool.exists(tx2.GetHash()));
450 :
451 1 : pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
452 1 : BOOST_CHECK(pool.exists(tx1.GetHash()));
453 1 : BOOST_CHECK(!pool.exists(tx2.GetHash()));
454 :
455 1 : pool.addUnchecked(entry.FromTx(tx2));
456 1 : CMutableTransaction tx3 = CMutableTransaction();
457 1 : tx3.vin.resize(1);
458 1 : tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
459 1 : tx3.vin[0].scriptSig = CScript() << OP_2;
460 1 : tx3.vout.resize(1);
461 1 : tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
462 1 : tx3.vout[0].nValue = 10 * COIN;
463 1 : pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
464 :
465 1 : pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
466 1 : BOOST_CHECK(!pool.exists(tx1.GetHash()));
467 1 : BOOST_CHECK(pool.exists(tx2.GetHash()));
468 1 : BOOST_CHECK(pool.exists(tx3.GetHash()));
469 :
470 1 : pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
471 1 : BOOST_CHECK(!pool.exists(tx1.GetHash()));
472 1 : BOOST_CHECK(!pool.exists(tx2.GetHash()));
473 1 : BOOST_CHECK(!pool.exists(tx3.GetHash()));
474 :
475 1 : CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
476 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
477 :
478 1 : CMutableTransaction tx4 = CMutableTransaction();
479 1 : tx4.vin.resize(2);
480 1 : tx4.vin[0].prevout.SetNull();
481 1 : tx4.vin[0].scriptSig = CScript() << OP_4;
482 1 : tx4.vin[1].prevout.SetNull();
483 1 : tx4.vin[1].scriptSig = CScript() << OP_4;
484 1 : tx4.vout.resize(2);
485 1 : tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
486 1 : tx4.vout[0].nValue = 10 * COIN;
487 1 : tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
488 1 : tx4.vout[1].nValue = 10 * COIN;
489 :
490 1 : CMutableTransaction tx5 = CMutableTransaction();
491 1 : tx5.vin.resize(2);
492 1 : tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
493 1 : tx5.vin[0].scriptSig = CScript() << OP_4;
494 1 : tx5.vin[1].prevout.SetNull();
495 1 : tx5.vin[1].scriptSig = CScript() << OP_5;
496 1 : tx5.vout.resize(2);
497 1 : tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
498 1 : tx5.vout[0].nValue = 10 * COIN;
499 1 : tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
500 1 : tx5.vout[1].nValue = 10 * COIN;
501 :
502 1 : CMutableTransaction tx6 = CMutableTransaction();
503 1 : tx6.vin.resize(2);
504 1 : tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
505 1 : tx6.vin[0].scriptSig = CScript() << OP_4;
506 1 : tx6.vin[1].prevout.SetNull();
507 1 : tx6.vin[1].scriptSig = CScript() << OP_6;
508 1 : tx6.vout.resize(2);
509 1 : tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
510 1 : tx6.vout[0].nValue = 10 * COIN;
511 1 : tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
512 1 : tx6.vout[1].nValue = 10 * COIN;
513 :
514 1 : CMutableTransaction tx7 = CMutableTransaction();
515 1 : tx7.vin.resize(2);
516 1 : tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
517 1 : tx7.vin[0].scriptSig = CScript() << OP_5;
518 1 : tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
519 1 : tx7.vin[1].scriptSig = CScript() << OP_6;
520 1 : tx7.vout.resize(2);
521 1 : tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
522 1 : tx7.vout[0].nValue = 10 * COIN;
523 1 : tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
524 1 : tx7.vout[1].nValue = 10 * COIN;
525 :
526 1 : pool.addUnchecked(entry.Fee(7000LL).FromTx(tx4));
527 1 : pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
528 1 : pool.addUnchecked(entry.Fee(1100LL).FromTx(tx6));
529 1 : pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
530 :
531 : // we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
532 1 : pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
533 1 : BOOST_CHECK(pool.exists(tx4.GetHash()));
534 1 : BOOST_CHECK(pool.exists(tx6.GetHash()));
535 1 : BOOST_CHECK(!pool.exists(tx7.GetHash()));
536 :
537 1 : if (!pool.exists(tx5.GetHash()))
538 1 : pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
539 1 : pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
540 :
541 1 : pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
542 1 : BOOST_CHECK(pool.exists(tx4.GetHash()));
543 1 : BOOST_CHECK(!pool.exists(tx5.GetHash()));
544 1 : BOOST_CHECK(pool.exists(tx6.GetHash()));
545 1 : BOOST_CHECK(!pool.exists(tx7.GetHash()));
546 :
547 1 : pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
548 1 : pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
549 :
550 1 : std::vector<CTransactionRef> vtx;
551 1 : SetMockTime(42);
552 1 : SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
553 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
554 : // ... we should keep the same min fee until we get a block
555 1 : pool.removeForBlock(vtx, 1);
556 1 : SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
557 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
558 : // ... then feerate should drop 1/2 each halflife
559 :
560 1 : SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
561 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
562 : // ... with a 1/2 halflife when mempool is < 1/2 its target size
563 :
564 1 : SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
565 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
566 : // ... with a 1/4 halflife when mempool is < 1/4 its target size
567 :
568 1 : SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
569 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
570 : // ... but feerate should never drop below 1000
571 :
572 1 : SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
573 1 : BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
574 : // ... unless it has gone all the way to 0 (after getting past 1000/2)
575 1 : }
576 :
577 14 : inline 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>())
578 : {
579 14 : CMutableTransaction tx = CMutableTransaction();
580 14 : tx.vin.resize(inputs.size());
581 14 : tx.vout.resize(output_values.size());
582 27 : for (size_t i = 0; i < inputs.size(); ++i) {
583 13 : tx.vin[i].prevout.hash = inputs[i]->GetHash();
584 13 : tx.vin[i].prevout.n = input_indices.size() > i ? input_indices[i] : 0;
585 13 : }
586 32 : for (size_t i = 0; i < output_values.size(); ++i) {
587 18 : tx.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
588 18 : tx.vout[i].nValue = output_values[i];
589 18 : }
590 14 : return MakeTransactionRef(tx);
591 14 : }
592 :
593 :
594 149 : BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
595 : {
596 : size_t ancestors, descendants;
597 :
598 1 : CTxMemPool& pool = *Assert(m_node.mempool);
599 1 : LOCK2(cs_main, pool.cs);
600 1 : TestMemPoolEntryHelper entry;
601 :
602 : /* Base transaction */
603 : //
604 : // [tx1]
605 : //
606 1 : CTransactionRef tx1 = make_tx(/*output_values=*/{10 * COIN});
607 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
608 :
609 : // Ancestors / descendants should be 1 / 1 (itself / itself)
610 1 : pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
611 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
612 1 : BOOST_CHECK_EQUAL(descendants, 1ULL);
613 :
614 : /* Child transaction */
615 : //
616 : // [tx1].0 <- [tx2]
617 : //
618 1 : CTransactionRef tx2 = make_tx(/*output_values=*/{495 * CENT, 5 * COIN}, /*inputs=*/{tx1});
619 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx2));
620 :
621 : // Ancestors / descendants should be:
622 : // transaction ancestors descendants
623 : // ============ =========== ===========
624 : // tx1 1 (tx1) 2 (tx1,2)
625 : // tx2 2 (tx1,2) 2 (tx1,2)
626 1 : pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
627 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
628 1 : BOOST_CHECK_EQUAL(descendants, 2ULL);
629 1 : pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
630 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
631 1 : BOOST_CHECK_EQUAL(descendants, 2ULL);
632 :
633 : /* Grand-child 1 */
634 : //
635 : // [tx1].0 <- [tx2].0 <- [tx3]
636 : //
637 1 : CTransactionRef tx3 = make_tx(/*output_values=*/{290 * CENT, 200 * CENT}, /*inputs=*/{tx2});
638 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx3));
639 :
640 : // Ancestors / descendants should be:
641 : // transaction ancestors descendants
642 : // ============ =========== ===========
643 : // tx1 1 (tx1) 3 (tx1,2,3)
644 : // tx2 2 (tx1,2) 3 (tx1,2,3)
645 : // tx3 3 (tx1,2,3) 3 (tx1,2,3)
646 1 : pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
647 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
648 1 : BOOST_CHECK_EQUAL(descendants, 3ULL);
649 1 : pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
650 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
651 1 : BOOST_CHECK_EQUAL(descendants, 3ULL);
652 1 : pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
653 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
654 1 : BOOST_CHECK_EQUAL(descendants, 3ULL);
655 :
656 : /* Grand-child 2 */
657 : //
658 : // [tx1].0 <- [tx2].0 <- [tx3]
659 : // |
660 : // \---1 <- [tx4]
661 : //
662 1 : CTransactionRef tx4 = make_tx(/*output_values=*/{290 * CENT, 250 * CENT}, /*inputs=*/{tx2}, /*input_indices=*/{1});
663 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tx4));
664 :
665 : // Ancestors / descendants should be:
666 : // transaction ancestors descendants
667 : // ============ =========== ===========
668 : // tx1 1 (tx1) 4 (tx1,2,3,4)
669 : // tx2 2 (tx1,2) 4 (tx1,2,3,4)
670 : // tx3 3 (tx1,2,3) 4 (tx1,2,3,4)
671 : // tx4 3 (tx1,2,4) 4 (tx1,2,3,4)
672 1 : pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
673 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
674 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
675 1 : pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
676 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
677 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
678 1 : pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
679 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
680 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
681 1 : pool.GetTransactionAncestry(tx4->GetHash(), ancestors, descendants);
682 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
683 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
684 :
685 : /* Make an alternate branch that is longer and connect it to tx3 */
686 : //
687 : // [ty1].0 <- [ty2].0 <- [ty3].0 <- [ty4].0 <- [ty5].0
688 : // |
689 : // [tx1].0 <- [tx2].0 <- [tx3].0 <- [ty6] --->--/
690 : // |
691 : // \---1 <- [tx4]
692 : //
693 1 : CTransactionRef ty1, ty2, ty3, ty4, ty5;
694 1 : CTransactionRef* ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5};
695 1 : CAmount v = 5 * COIN;
696 6 : for (uint64_t i = 0; i < 5; i++) {
697 5 : CTransactionRef& tyi = *ty[i];
698 5 : tyi = make_tx(/*output_values=*/{v}, /*inputs=*/i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{});
699 5 : v -= 50 * CENT;
700 5 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tyi));
701 5 : pool.GetTransactionAncestry(tyi->GetHash(), ancestors, descendants);
702 5 : BOOST_CHECK_EQUAL(ancestors, i+1);
703 5 : BOOST_CHECK_EQUAL(descendants, i+1);
704 5 : }
705 1 : CTransactionRef ty6 = make_tx(/*output_values=*/{5 * COIN}, /*inputs=*/{tx3, ty5});
706 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(ty6));
707 :
708 : // Ancestors / descendants should be:
709 : // transaction ancestors descendants
710 : // ============ =================== ===========
711 : // tx1 1 (tx1) 5 (tx1,2,3,4, ty6)
712 : // tx2 2 (tx1,2) 5 (tx1,2,3,4, ty6)
713 : // tx3 3 (tx1,2,3) 5 (tx1,2,3,4, ty6)
714 : // tx4 3 (tx1,2,4) 5 (tx1,2,3,4, ty6)
715 : // ty1 1 (ty1) 6 (ty1,2,3,4,5,6)
716 : // ty2 2 (ty1,2) 6 (ty1,2,3,4,5,6)
717 : // ty3 3 (ty1,2,3) 6 (ty1,2,3,4,5,6)
718 : // ty4 4 (y1234) 6 (ty1,2,3,4,5,6)
719 : // ty5 5 (y12345) 6 (ty1,2,3,4,5,6)
720 : // ty6 9 (tx123, ty123456) 6 (ty1,2,3,4,5,6)
721 1 : pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
722 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
723 1 : BOOST_CHECK_EQUAL(descendants, 5ULL);
724 1 : pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
725 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
726 1 : BOOST_CHECK_EQUAL(descendants, 5ULL);
727 1 : pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
728 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
729 1 : BOOST_CHECK_EQUAL(descendants, 5ULL);
730 1 : pool.GetTransactionAncestry(tx4->GetHash(), ancestors, descendants);
731 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
732 1 : BOOST_CHECK_EQUAL(descendants, 5ULL);
733 1 : pool.GetTransactionAncestry(ty1->GetHash(), ancestors, descendants);
734 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
735 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
736 1 : pool.GetTransactionAncestry(ty2->GetHash(), ancestors, descendants);
737 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
738 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
739 1 : pool.GetTransactionAncestry(ty3->GetHash(), ancestors, descendants);
740 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
741 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
742 1 : pool.GetTransactionAncestry(ty4->GetHash(), ancestors, descendants);
743 1 : BOOST_CHECK_EQUAL(ancestors, 4ULL);
744 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
745 1 : pool.GetTransactionAncestry(ty5->GetHash(), ancestors, descendants);
746 1 : BOOST_CHECK_EQUAL(ancestors, 5ULL);
747 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
748 1 : pool.GetTransactionAncestry(ty6->GetHash(), ancestors, descendants);
749 1 : BOOST_CHECK_EQUAL(ancestors, 9ULL);
750 1 : BOOST_CHECK_EQUAL(descendants, 6ULL);
751 1 : }
752 :
753 149 : BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)
754 : {
755 : size_t ancestors, descendants;
756 :
757 1 : CTxMemPool& pool = *Assert(m_node.mempool);
758 1 : LOCK2(::cs_main, pool.cs);
759 1 : TestMemPoolEntryHelper entry;
760 :
761 : /* Ancestors represented more than once ("diamond") */
762 : //
763 : // [ta].0 <- [tb].0 -----<------- [td].0
764 : // | |
765 : // \---1 <- [tc].0 --<--/
766 : //
767 1 : CTransactionRef ta, tb, tc, td;
768 1 : ta = make_tx(/*output_values=*/{10 * COIN});
769 1 : tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
770 1 : tc = make_tx(/*output_values=*/{2 * COIN}, /*inputs=*/{tb}, /*input_indices=*/{1});
771 1 : td = make_tx(/*output_values=*/{6 * COIN}, /*inputs=*/{tb, tc}, /*input_indices=*/{0, 0});
772 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(ta));
773 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tb));
774 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(tc));
775 1 : pool.addUnchecked(entry.Fee(10000LL).FromTx(td));
776 :
777 : // Ancestors / descendants should be:
778 : // transaction ancestors descendants
779 : // ============ =================== ===========
780 : // ta 1 (ta 4 (ta,tb,tc,td)
781 : // tb 2 (ta,tb) 4 (ta,tb,tc,td)
782 : // tc 3 (ta,tb,tc) 4 (ta,tb,tc,td)
783 : // td 4 (ta,tb,tc,td) 4 (ta,tb,tc,td)
784 1 : pool.GetTransactionAncestry(ta->GetHash(), ancestors, descendants);
785 1 : BOOST_CHECK_EQUAL(ancestors, 1ULL);
786 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
787 1 : pool.GetTransactionAncestry(tb->GetHash(), ancestors, descendants);
788 1 : BOOST_CHECK_EQUAL(ancestors, 2ULL);
789 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
790 1 : pool.GetTransactionAncestry(tc->GetHash(), ancestors, descendants);
791 1 : BOOST_CHECK_EQUAL(ancestors, 3ULL);
792 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
793 1 : pool.GetTransactionAncestry(td->GetHash(), ancestors, descendants);
794 1 : BOOST_CHECK_EQUAL(ancestors, 4ULL);
795 1 : BOOST_CHECK_EQUAL(descendants, 4ULL);
796 1 : }
797 :
798 146 : BOOST_AUTO_TEST_SUITE_END()
|