Merge #12079: Improve prioritisetransaction test coverage
[bitcoinplatinum.git] / src / test / mempool_tests.cpp
blobf56f34149860efea2ea341619c875d09aae8fed0
1 // Copyright (c) 2011-2017 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;
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;
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).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).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).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).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 pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5));
159 BOOST_CHECK_EQUAL(pool.size(), 5);
161 std::vector<std::string> sortedOrder;
162 sortedOrder.resize(5);
163 sortedOrder[0] = tx3.GetHash().ToString(); // 0
164 sortedOrder[1] = tx5.GetHash().ToString(); // 10000
165 sortedOrder[2] = tx1.GetHash().ToString(); // 10000
166 sortedOrder[3] = tx4.GetHash().ToString(); // 15000
167 sortedOrder[4] = tx2.GetHash().ToString(); // 20000
168 LOCK(pool.cs);
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;
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).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).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).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).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 LOCK(pool.cs);
380 CheckSort<ancestor_score>(pool, sortedOrder);
382 /* low fee parent with high fee child */
383 /* tx6 (0) -> tx7 (high) */
384 CMutableTransaction tx6 = CMutableTransaction();
385 tx6.vout.resize(1);
386 tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
387 tx6.vout[0].nValue = 20 * COIN;
388 uint64_t tx6Size = GetVirtualTransactionSize(tx6);
390 pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
391 BOOST_CHECK_EQUAL(pool.size(), 6);
392 // Ties are broken by hash
393 if (tx3.GetHash() < tx6.GetHash())
394 sortedOrder.push_back(tx6.GetHash().ToString());
395 else
396 sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
398 CheckSort<ancestor_score>(pool, sortedOrder);
400 CMutableTransaction tx7 = CMutableTransaction();
401 tx7.vin.resize(1);
402 tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
403 tx7.vin[0].scriptSig = CScript() << OP_11;
404 tx7.vout.resize(1);
405 tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
406 tx7.vout[0].nValue = 10 * COIN;
407 uint64_t tx7Size = GetVirtualTransactionSize(tx7);
409 /* set the fee to just below tx2's feerate when including ancestor */
410 CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
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;
436 TestMemPoolEntryHelper entry;
438 CMutableTransaction tx1 = CMutableTransaction();
439 tx1.vin.resize(1);
440 tx1.vin[0].scriptSig = CScript() << OP_1;
441 tx1.vout.resize(1);
442 tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
443 tx1.vout[0].nValue = 10 * COIN;
444 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1));
446 CMutableTransaction tx2 = CMutableTransaction();
447 tx2.vin.resize(1);
448 tx2.vin[0].scriptSig = CScript() << OP_2;
449 tx2.vout.resize(1);
450 tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
451 tx2.vout[0].nValue = 10 * COIN;
452 pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2));
454 pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
455 BOOST_CHECK(pool.exists(tx1.GetHash()));
456 BOOST_CHECK(pool.exists(tx2.GetHash()));
458 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
459 BOOST_CHECK(pool.exists(tx1.GetHash()));
460 BOOST_CHECK(!pool.exists(tx2.GetHash()));
462 pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2));
463 CMutableTransaction tx3 = CMutableTransaction();
464 tx3.vin.resize(1);
465 tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
466 tx3.vin[0].scriptSig = CScript() << OP_2;
467 tx3.vout.resize(1);
468 tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
469 tx3.vout[0].nValue = 10 * COIN;
470 pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3));
472 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
473 BOOST_CHECK(!pool.exists(tx1.GetHash()));
474 BOOST_CHECK(pool.exists(tx2.GetHash()));
475 BOOST_CHECK(pool.exists(tx3.GetHash()));
477 pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits
478 BOOST_CHECK(!pool.exists(tx1.GetHash()));
479 BOOST_CHECK(!pool.exists(tx2.GetHash()));
480 BOOST_CHECK(!pool.exists(tx3.GetHash()));
482 CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2));
483 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
485 CMutableTransaction tx4 = CMutableTransaction();
486 tx4.vin.resize(2);
487 tx4.vin[0].prevout.SetNull();
488 tx4.vin[0].scriptSig = CScript() << OP_4;
489 tx4.vin[1].prevout.SetNull();
490 tx4.vin[1].scriptSig = CScript() << OP_4;
491 tx4.vout.resize(2);
492 tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
493 tx4.vout[0].nValue = 10 * COIN;
494 tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
495 tx4.vout[1].nValue = 10 * COIN;
497 CMutableTransaction tx5 = CMutableTransaction();
498 tx5.vin.resize(2);
499 tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
500 tx5.vin[0].scriptSig = CScript() << OP_4;
501 tx5.vin[1].prevout.SetNull();
502 tx5.vin[1].scriptSig = CScript() << OP_5;
503 tx5.vout.resize(2);
504 tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
505 tx5.vout[0].nValue = 10 * COIN;
506 tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
507 tx5.vout[1].nValue = 10 * COIN;
509 CMutableTransaction tx6 = CMutableTransaction();
510 tx6.vin.resize(2);
511 tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
512 tx6.vin[0].scriptSig = CScript() << OP_4;
513 tx6.vin[1].prevout.SetNull();
514 tx6.vin[1].scriptSig = CScript() << OP_6;
515 tx6.vout.resize(2);
516 tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
517 tx6.vout[0].nValue = 10 * COIN;
518 tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
519 tx6.vout[1].nValue = 10 * COIN;
521 CMutableTransaction tx7 = CMutableTransaction();
522 tx7.vin.resize(2);
523 tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
524 tx7.vin[0].scriptSig = CScript() << OP_5;
525 tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
526 tx7.vin[1].scriptSig = CScript() << OP_6;
527 tx7.vout.resize(2);
528 tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
529 tx7.vout[0].nValue = 10 * COIN;
530 tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
531 tx7.vout[1].nValue = 10 * COIN;
533 pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4));
534 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
535 pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6));
536 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
538 // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
539 pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
540 BOOST_CHECK(pool.exists(tx4.GetHash()));
541 BOOST_CHECK(pool.exists(tx6.GetHash()));
542 BOOST_CHECK(!pool.exists(tx7.GetHash()));
544 if (!pool.exists(tx5.GetHash()))
545 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
546 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
548 pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
549 BOOST_CHECK(pool.exists(tx4.GetHash()));
550 BOOST_CHECK(!pool.exists(tx5.GetHash()));
551 BOOST_CHECK(pool.exists(tx6.GetHash()));
552 BOOST_CHECK(!pool.exists(tx7.GetHash()));
554 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
555 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
557 std::vector<CTransactionRef> vtx;
558 SetMockTime(42);
559 SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
560 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
561 // ... we should keep the same min fee until we get a block
562 pool.removeForBlock(vtx, 1);
563 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
564 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
565 // ... then feerate should drop 1/2 each halflife
567 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
568 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
569 // ... with a 1/2 halflife when mempool is < 1/2 its target size
571 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
572 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
573 // ... with a 1/4 halflife when mempool is < 1/4 its target size
575 SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
576 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
577 // ... but feerate should never drop below 1000
579 SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
580 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
581 // ... unless it has gone all the way to 0 (after getting past 1000/2)
583 SetMockTime(0);
586 BOOST_AUTO_TEST_SUITE_END()