Merge #9578: Add missing mempool lock for CalculateMemPoolAncestors
[bitcoinplatinum.git] / src / test / mempool_tests.cpp
blob91f549fe48b2fe62ec1d0079053abf31235dbfa4
1 // Copyright (c) 2011-2016 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.
5 #include "policy/policy.h"
6 #include "txmempool.h"
7 #include "util.h"
9 #include "test/test_bitcoin.h"
11 #include <boost/test/unit_test.hpp>
12 #include <list>
13 #include <vector>
15 BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
17 BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
19 // Test CTxMemPool::remove functionality
21 TestMemPoolEntryHelper entry;
22 // Parent transaction with three children,
23 // and three grand-children:
24 CMutableTransaction txParent;
25 txParent.vin.resize(1);
26 txParent.vin[0].scriptSig = CScript() << OP_11;
27 txParent.vout.resize(3);
28 for (int i = 0; i < 3; i++)
30 txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
31 txParent.vout[i].nValue = 33000LL;
33 CMutableTransaction txChild[3];
34 for (int i = 0; i < 3; i++)
36 txChild[i].vin.resize(1);
37 txChild[i].vin[0].scriptSig = CScript() << OP_11;
38 txChild[i].vin[0].prevout.hash = txParent.GetHash();
39 txChild[i].vin[0].prevout.n = i;
40 txChild[i].vout.resize(1);
41 txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
42 txChild[i].vout[0].nValue = 11000LL;
44 CMutableTransaction txGrandChild[3];
45 for (int i = 0; i < 3; i++)
47 txGrandChild[i].vin.resize(1);
48 txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
49 txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
50 txGrandChild[i].vin[0].prevout.n = 0;
51 txGrandChild[i].vout.resize(1);
52 txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
53 txGrandChild[i].vout[0].nValue = 11000LL;
57 CTxMemPool testPool(CFeeRate(0));
59 // Nothing in pool, remove should do nothing:
60 unsigned int poolSize = testPool.size();
61 testPool.removeRecursive(txParent);
62 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
64 // Just the parent:
65 testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
66 poolSize = testPool.size();
67 testPool.removeRecursive(txParent);
68 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
70 // Parent, children, grandchildren:
71 testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent));
72 for (int i = 0; i < 3; i++)
74 testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i]));
75 testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i]));
77 // Remove Child[0], GrandChild[0] should be removed:
78 poolSize = testPool.size();
79 testPool.removeRecursive(txChild[0]);
80 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
81 // ... make sure grandchild and child are gone:
82 poolSize = testPool.size();
83 testPool.removeRecursive(txGrandChild[0]);
84 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
85 poolSize = testPool.size();
86 testPool.removeRecursive(txChild[0]);
87 BOOST_CHECK_EQUAL(testPool.size(), poolSize);
88 // Remove parent, all children/grandchildren should go:
89 poolSize = testPool.size();
90 testPool.removeRecursive(txParent);
91 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
92 BOOST_CHECK_EQUAL(testPool.size(), 0);
94 // Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
95 for (int i = 0; i < 3; i++)
97 testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i]));
98 testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i]));
100 // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
101 // put into the mempool (maybe because it is non-standard):
102 poolSize = testPool.size();
103 testPool.removeRecursive(txParent);
104 BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
105 BOOST_CHECK_EQUAL(testPool.size(), 0);
108 template<typename name>
109 void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder)
111 BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size());
112 typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin();
113 int count=0;
114 for (; it != pool.mapTx.get<name>().end(); ++it, ++count) {
115 BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]);
119 BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
121 CTxMemPool pool(CFeeRate(0));
122 TestMemPoolEntryHelper entry;
124 /* 3rd highest fee */
125 CMutableTransaction tx1 = CMutableTransaction();
126 tx1.vout.resize(1);
127 tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
128 tx1.vout[0].nValue = 10 * COIN;
129 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).Priority(10.0).FromTx(tx1));
131 /* highest fee */
132 CMutableTransaction tx2 = CMutableTransaction();
133 tx2.vout.resize(1);
134 tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
135 tx2.vout[0].nValue = 2 * COIN;
136 pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2));
138 /* lowest fee */
139 CMutableTransaction tx3 = CMutableTransaction();
140 tx3.vout.resize(1);
141 tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
142 tx3.vout[0].nValue = 5 * COIN;
143 pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).Priority(100.0).FromTx(tx3));
145 /* 2nd highest fee */
146 CMutableTransaction tx4 = CMutableTransaction();
147 tx4.vout.resize(1);
148 tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
149 tx4.vout[0].nValue = 6 * COIN;
150 pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).Priority(1.0).FromTx(tx4));
152 /* equal fee rate to tx1, but newer */
153 CMutableTransaction tx5 = CMutableTransaction();
154 tx5.vout.resize(1);
155 tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
156 tx5.vout[0].nValue = 11 * COIN;
157 entry.nTime = 1;
158 entry.dPriority = 10.0;
159 pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5));
160 BOOST_CHECK_EQUAL(pool.size(), 5);
162 std::vector<std::string> sortedOrder;
163 sortedOrder.resize(5);
164 sortedOrder[0] = tx3.GetHash().ToString(); // 0
165 sortedOrder[1] = tx5.GetHash().ToString(); // 10000
166 sortedOrder[2] = tx1.GetHash().ToString(); // 10000
167 sortedOrder[3] = tx4.GetHash().ToString(); // 15000
168 sortedOrder[4] = tx2.GetHash().ToString(); // 20000
169 CheckSort<descendant_score>(pool, sortedOrder);
171 /* low fee but with high fee child */
172 /* tx6 -> tx7 -> tx8, tx9 -> tx10 */
173 CMutableTransaction tx6 = CMutableTransaction();
174 tx6.vout.resize(1);
175 tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
176 tx6.vout[0].nValue = 20 * COIN;
177 pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
178 BOOST_CHECK_EQUAL(pool.size(), 6);
179 // Check that at this point, tx6 is sorted low
180 sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
181 CheckSort<descendant_score>(pool, sortedOrder);
183 CTxMemPool::setEntries setAncestors;
184 setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
185 CMutableTransaction tx7 = CMutableTransaction();
186 tx7.vin.resize(1);
187 tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
188 tx7.vin[0].scriptSig = CScript() << OP_11;
189 tx7.vout.resize(2);
190 tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
191 tx7.vout[0].nValue = 10 * COIN;
192 tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
193 tx7.vout[1].nValue = 1 * COIN;
195 CTxMemPool::setEntries setAncestorsCalculated;
196 std::string dummy;
197 BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
198 BOOST_CHECK(setAncestorsCalculated == setAncestors);
200 pool.addUnchecked(tx7.GetHash(), entry.FromTx(tx7), setAncestors);
201 BOOST_CHECK_EQUAL(pool.size(), 7);
203 // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
204 sortedOrder.erase(sortedOrder.begin());
205 sortedOrder.push_back(tx6.GetHash().ToString());
206 sortedOrder.push_back(tx7.GetHash().ToString());
207 CheckSort<descendant_score>(pool, sortedOrder);
209 /* low fee child of tx7 */
210 CMutableTransaction tx8 = CMutableTransaction();
211 tx8.vin.resize(1);
212 tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
213 tx8.vin[0].scriptSig = CScript() << OP_11;
214 tx8.vout.resize(1);
215 tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
216 tx8.vout[0].nValue = 10 * COIN;
217 setAncestors.insert(pool.mapTx.find(tx7.GetHash()));
218 pool.addUnchecked(tx8.GetHash(), entry.Fee(0LL).Time(2).FromTx(tx8), setAncestors);
220 // Now tx8 should be sorted low, but tx6/tx both high
221 sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
222 CheckSort<descendant_score>(pool, sortedOrder);
224 /* low fee child of tx7 */
225 CMutableTransaction tx9 = CMutableTransaction();
226 tx9.vin.resize(1);
227 tx9.vin[0].prevout = COutPoint(tx7.GetHash(), 1);
228 tx9.vin[0].scriptSig = CScript() << OP_11;
229 tx9.vout.resize(1);
230 tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
231 tx9.vout[0].nValue = 1 * COIN;
232 pool.addUnchecked(tx9.GetHash(), entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors);
234 // tx9 should be sorted low
235 BOOST_CHECK_EQUAL(pool.size(), 9);
236 sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
237 CheckSort<descendant_score>(pool, sortedOrder);
239 std::vector<std::string> snapshotOrder = sortedOrder;
241 setAncestors.insert(pool.mapTx.find(tx8.GetHash()));
242 setAncestors.insert(pool.mapTx.find(tx9.GetHash()));
243 /* tx10 depends on tx8 and tx9 and has a high fee*/
244 CMutableTransaction tx10 = CMutableTransaction();
245 tx10.vin.resize(2);
246 tx10.vin[0].prevout = COutPoint(tx8.GetHash(), 0);
247 tx10.vin[0].scriptSig = CScript() << OP_11;
248 tx10.vin[1].prevout = COutPoint(tx9.GetHash(), 0);
249 tx10.vin[1].scriptSig = CScript() << OP_11;
250 tx10.vout.resize(1);
251 tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
252 tx10.vout[0].nValue = 10 * COIN;
254 setAncestorsCalculated.clear();
255 BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(4).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
256 BOOST_CHECK(setAncestorsCalculated == setAncestors);
258 pool.addUnchecked(tx10.GetHash(), entry.FromTx(tx10), setAncestors);
261 * tx8 and tx9 should both now be sorted higher
262 * Final order after tx10 is added:
264 * tx3 = 0 (1)
265 * tx5 = 10000 (1)
266 * tx1 = 10000 (1)
267 * tx4 = 15000 (1)
268 * tx2 = 20000 (1)
269 * tx9 = 200k (2 txs)
270 * tx8 = 200k (2 txs)
271 * tx10 = 200k (1 tx)
272 * tx6 = 2.2M (5 txs)
273 * tx7 = 2.2M (4 txs)
275 sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning
276 sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
277 sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
278 sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
279 CheckSort<descendant_score>(pool, sortedOrder);
281 // there should be 10 transactions in the mempool
282 BOOST_CHECK_EQUAL(pool.size(), 10);
284 // Now try removing tx10 and verify the sort order returns to normal
285 pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx());
286 CheckSort<descendant_score>(pool, snapshotOrder);
288 pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
289 pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
290 /* Now check the sort on the mining score index.
291 * Final order should be:
293 * tx7 (2M)
294 * tx2 (20k)
295 * tx4 (15000)
296 * tx1/tx5 (10000)
297 * tx3/6 (0)
298 * (Ties resolved by hash)
300 sortedOrder.clear();
301 sortedOrder.push_back(tx7.GetHash().ToString());
302 sortedOrder.push_back(tx2.GetHash().ToString());
303 sortedOrder.push_back(tx4.GetHash().ToString());
304 if (tx1.GetHash() < tx5.GetHash()) {
305 sortedOrder.push_back(tx5.GetHash().ToString());
306 sortedOrder.push_back(tx1.GetHash().ToString());
307 } else {
308 sortedOrder.push_back(tx1.GetHash().ToString());
309 sortedOrder.push_back(tx5.GetHash().ToString());
311 if (tx3.GetHash() < tx6.GetHash()) {
312 sortedOrder.push_back(tx6.GetHash().ToString());
313 sortedOrder.push_back(tx3.GetHash().ToString());
314 } else {
315 sortedOrder.push_back(tx3.GetHash().ToString());
316 sortedOrder.push_back(tx6.GetHash().ToString());
318 CheckSort<mining_score>(pool, sortedOrder);
321 BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
323 CTxMemPool pool(CFeeRate(0));
324 TestMemPoolEntryHelper entry;
326 /* 3rd highest fee */
327 CMutableTransaction tx1 = CMutableTransaction();
328 tx1.vout.resize(1);
329 tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
330 tx1.vout[0].nValue = 10 * COIN;
331 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).Priority(10.0).FromTx(tx1));
333 /* highest fee */
334 CMutableTransaction tx2 = CMutableTransaction();
335 tx2.vout.resize(1);
336 tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
337 tx2.vout[0].nValue = 2 * COIN;
338 pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).Priority(9.0).FromTx(tx2));
339 uint64_t tx2Size = GetVirtualTransactionSize(tx2);
341 /* lowest fee */
342 CMutableTransaction tx3 = CMutableTransaction();
343 tx3.vout.resize(1);
344 tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
345 tx3.vout[0].nValue = 5 * COIN;
346 pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).Priority(100.0).FromTx(tx3));
348 /* 2nd highest fee */
349 CMutableTransaction tx4 = CMutableTransaction();
350 tx4.vout.resize(1);
351 tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
352 tx4.vout[0].nValue = 6 * COIN;
353 pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).Priority(1.0).FromTx(tx4));
355 /* equal fee rate to tx1, but newer */
356 CMutableTransaction tx5 = CMutableTransaction();
357 tx5.vout.resize(1);
358 tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
359 tx5.vout[0].nValue = 11 * COIN;
360 pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5));
361 BOOST_CHECK_EQUAL(pool.size(), 5);
363 std::vector<std::string> sortedOrder;
364 sortedOrder.resize(5);
365 sortedOrder[0] = tx2.GetHash().ToString(); // 20000
366 sortedOrder[1] = tx4.GetHash().ToString(); // 15000
367 // tx1 and tx5 are both 10000
368 // Ties are broken by hash, not timestamp, so determine which
369 // hash comes first.
370 if (tx1.GetHash() < tx5.GetHash()) {
371 sortedOrder[2] = tx1.GetHash().ToString();
372 sortedOrder[3] = tx5.GetHash().ToString();
373 } else {
374 sortedOrder[2] = tx5.GetHash().ToString();
375 sortedOrder[3] = tx1.GetHash().ToString();
377 sortedOrder[4] = tx3.GetHash().ToString(); // 0
379 CheckSort<ancestor_score>(pool, sortedOrder);
381 /* low fee parent with high fee child */
382 /* tx6 (0) -> tx7 (high) */
383 CMutableTransaction tx6 = CMutableTransaction();
384 tx6.vout.resize(1);
385 tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
386 tx6.vout[0].nValue = 20 * COIN;
387 uint64_t tx6Size = GetVirtualTransactionSize(tx6);
389 pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
390 BOOST_CHECK_EQUAL(pool.size(), 6);
391 // Ties are broken by hash
392 if (tx3.GetHash() < tx6.GetHash())
393 sortedOrder.push_back(tx6.GetHash().ToString());
394 else
395 sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
397 CheckSort<ancestor_score>(pool, sortedOrder);
399 CMutableTransaction tx7 = CMutableTransaction();
400 tx7.vin.resize(1);
401 tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
402 tx7.vin[0].scriptSig = CScript() << OP_11;
403 tx7.vout.resize(1);
404 tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
405 tx7.vout[0].nValue = 10 * COIN;
406 uint64_t tx7Size = GetVirtualTransactionSize(tx7);
408 /* set the fee to just below tx2's feerate when including ancestor */
409 CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
411 //CTxMemPoolEntry entry7(tx7, fee, 2, 10.0, 1, true);
412 pool.addUnchecked(tx7.GetHash(), entry.Fee(fee).FromTx(tx7));
413 BOOST_CHECK_EQUAL(pool.size(), 7);
414 sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString());
415 CheckSort<ancestor_score>(pool, sortedOrder);
417 /* after tx6 is mined, tx7 should move up in the sort */
418 std::vector<CTransactionRef> vtx;
419 vtx.push_back(MakeTransactionRef(tx6));
420 pool.removeForBlock(vtx, 1);
422 sortedOrder.erase(sortedOrder.begin()+1);
423 // Ties are broken by hash
424 if (tx3.GetHash() < tx6.GetHash())
425 sortedOrder.pop_back();
426 else
427 sortedOrder.erase(sortedOrder.end()-2);
428 sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
429 CheckSort<ancestor_score>(pool, sortedOrder);
433 BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
435 CTxMemPool pool(CFeeRate(1000));
436 TestMemPoolEntryHelper entry;
437 entry.dPriority = 10.0;
439 CMutableTransaction tx1 = CMutableTransaction();
440 tx1.vin.resize(1);
441 tx1.vin[0].scriptSig = CScript() << OP_1;
442 tx1.vout.resize(1);
443 tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
444 tx1.vout[0].nValue = 10 * COIN;
445 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1, &pool));
447 CMutableTransaction tx2 = CMutableTransaction();
448 tx2.vin.resize(1);
449 tx2.vin[0].scriptSig = CScript() << OP_2;
450 tx2.vout.resize(1);
451 tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
452 tx2.vout[0].nValue = 10 * COIN;
453 pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2, &pool));
455 pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
456 BOOST_CHECK(pool.exists(tx1.GetHash()));
457 BOOST_CHECK(pool.exists(tx2.GetHash()));
459 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
460 BOOST_CHECK(pool.exists(tx1.GetHash()));
461 BOOST_CHECK(!pool.exists(tx2.GetHash()));
463 pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2, &pool));
464 CMutableTransaction tx3 = CMutableTransaction();
465 tx3.vin.resize(1);
466 tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
467 tx3.vin[0].scriptSig = CScript() << OP_2;
468 tx3.vout.resize(1);
469 tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
470 tx3.vout[0].nValue = 10 * COIN;
471 pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3, &pool));
473 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
474 BOOST_CHECK(!pool.exists(tx1.GetHash()));
475 BOOST_CHECK(pool.exists(tx2.GetHash()));
476 BOOST_CHECK(pool.exists(tx3.GetHash()));
478 pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits
479 BOOST_CHECK(!pool.exists(tx1.GetHash()));
480 BOOST_CHECK(!pool.exists(tx2.GetHash()));
481 BOOST_CHECK(!pool.exists(tx3.GetHash()));
483 CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2));
484 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
486 CMutableTransaction tx4 = CMutableTransaction();
487 tx4.vin.resize(2);
488 tx4.vin[0].prevout.SetNull();
489 tx4.vin[0].scriptSig = CScript() << OP_4;
490 tx4.vin[1].prevout.SetNull();
491 tx4.vin[1].scriptSig = CScript() << OP_4;
492 tx4.vout.resize(2);
493 tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
494 tx4.vout[0].nValue = 10 * COIN;
495 tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
496 tx4.vout[1].nValue = 10 * COIN;
498 CMutableTransaction tx5 = CMutableTransaction();
499 tx5.vin.resize(2);
500 tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
501 tx5.vin[0].scriptSig = CScript() << OP_4;
502 tx5.vin[1].prevout.SetNull();
503 tx5.vin[1].scriptSig = CScript() << OP_5;
504 tx5.vout.resize(2);
505 tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
506 tx5.vout[0].nValue = 10 * COIN;
507 tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
508 tx5.vout[1].nValue = 10 * COIN;
510 CMutableTransaction tx6 = CMutableTransaction();
511 tx6.vin.resize(2);
512 tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
513 tx6.vin[0].scriptSig = CScript() << OP_4;
514 tx6.vin[1].prevout.SetNull();
515 tx6.vin[1].scriptSig = CScript() << OP_6;
516 tx6.vout.resize(2);
517 tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
518 tx6.vout[0].nValue = 10 * COIN;
519 tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
520 tx6.vout[1].nValue = 10 * COIN;
522 CMutableTransaction tx7 = CMutableTransaction();
523 tx7.vin.resize(2);
524 tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
525 tx7.vin[0].scriptSig = CScript() << OP_5;
526 tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
527 tx7.vin[1].scriptSig = CScript() << OP_6;
528 tx7.vout.resize(2);
529 tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
530 tx7.vout[0].nValue = 10 * COIN;
531 tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
532 tx7.vout[1].nValue = 10 * COIN;
534 pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4, &pool));
535 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool));
536 pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6, &pool));
537 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
539 // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
540 pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
541 BOOST_CHECK(pool.exists(tx4.GetHash()));
542 BOOST_CHECK(pool.exists(tx6.GetHash()));
543 BOOST_CHECK(!pool.exists(tx7.GetHash()));
545 if (!pool.exists(tx5.GetHash()))
546 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool));
547 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
549 pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
550 BOOST_CHECK(pool.exists(tx4.GetHash()));
551 BOOST_CHECK(!pool.exists(tx5.GetHash()));
552 BOOST_CHECK(pool.exists(tx6.GetHash()));
553 BOOST_CHECK(!pool.exists(tx7.GetHash()));
555 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5, &pool));
556 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7, &pool));
558 std::vector<CTransactionRef> vtx;
559 SetMockTime(42);
560 SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
561 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
562 // ... we should keep the same min fee until we get a block
563 pool.removeForBlock(vtx, 1);
564 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
565 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2);
566 // ... then feerate should drop 1/2 each halflife
568 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
569 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4);
570 // ... with a 1/2 halflife when mempool is < 1/2 its target size
572 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
573 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8);
574 // ... with a 1/4 halflife when mempool is < 1/4 its target size
576 SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
577 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
578 // ... but feerate should never drop below 1000
580 SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
581 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
582 // ... unless it has gone all the way to 0 (after getting past 1000/2)
584 SetMockTime(0);
587 BOOST_AUTO_TEST_SUITE_END()