[tests] Add -blocknotify functional test
[bitcoinplatinum.git] / src / test / mempool_tests.cpp
blob116210a297486adb03d9ea7f6069abcd8320a6bf
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;
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 CheckSort<descendant_score>(pool, sortedOrder);
170 /* low fee but with high fee child */
171 /* tx6 -> tx7 -> tx8, tx9 -> tx10 */
172 CMutableTransaction tx6 = CMutableTransaction();
173 tx6.vout.resize(1);
174 tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
175 tx6.vout[0].nValue = 20 * COIN;
176 pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
177 BOOST_CHECK_EQUAL(pool.size(), 6);
178 // Check that at this point, tx6 is sorted low
179 sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
180 CheckSort<descendant_score>(pool, sortedOrder);
182 CTxMemPool::setEntries setAncestors;
183 setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
184 CMutableTransaction tx7 = CMutableTransaction();
185 tx7.vin.resize(1);
186 tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
187 tx7.vin[0].scriptSig = CScript() << OP_11;
188 tx7.vout.resize(2);
189 tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
190 tx7.vout[0].nValue = 10 * COIN;
191 tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
192 tx7.vout[1].nValue = 1 * COIN;
194 CTxMemPool::setEntries setAncestorsCalculated;
195 std::string dummy;
196 BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
197 BOOST_CHECK(setAncestorsCalculated == setAncestors);
199 pool.addUnchecked(tx7.GetHash(), entry.FromTx(tx7), setAncestors);
200 BOOST_CHECK_EQUAL(pool.size(), 7);
202 // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
203 sortedOrder.erase(sortedOrder.begin());
204 sortedOrder.push_back(tx6.GetHash().ToString());
205 sortedOrder.push_back(tx7.GetHash().ToString());
206 CheckSort<descendant_score>(pool, sortedOrder);
208 /* low fee child of tx7 */
209 CMutableTransaction tx8 = CMutableTransaction();
210 tx8.vin.resize(1);
211 tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
212 tx8.vin[0].scriptSig = CScript() << OP_11;
213 tx8.vout.resize(1);
214 tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
215 tx8.vout[0].nValue = 10 * COIN;
216 setAncestors.insert(pool.mapTx.find(tx7.GetHash()));
217 pool.addUnchecked(tx8.GetHash(), entry.Fee(0LL).Time(2).FromTx(tx8), setAncestors);
219 // Now tx8 should be sorted low, but tx6/tx both high
220 sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
221 CheckSort<descendant_score>(pool, sortedOrder);
223 /* low fee child of tx7 */
224 CMutableTransaction tx9 = CMutableTransaction();
225 tx9.vin.resize(1);
226 tx9.vin[0].prevout = COutPoint(tx7.GetHash(), 1);
227 tx9.vin[0].scriptSig = CScript() << OP_11;
228 tx9.vout.resize(1);
229 tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
230 tx9.vout[0].nValue = 1 * COIN;
231 pool.addUnchecked(tx9.GetHash(), entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors);
233 // tx9 should be sorted low
234 BOOST_CHECK_EQUAL(pool.size(), 9);
235 sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
236 CheckSort<descendant_score>(pool, sortedOrder);
238 std::vector<std::string> snapshotOrder = sortedOrder;
240 setAncestors.insert(pool.mapTx.find(tx8.GetHash()));
241 setAncestors.insert(pool.mapTx.find(tx9.GetHash()));
242 /* tx10 depends on tx8 and tx9 and has a high fee*/
243 CMutableTransaction tx10 = CMutableTransaction();
244 tx10.vin.resize(2);
245 tx10.vin[0].prevout = COutPoint(tx8.GetHash(), 0);
246 tx10.vin[0].scriptSig = CScript() << OP_11;
247 tx10.vin[1].prevout = COutPoint(tx9.GetHash(), 0);
248 tx10.vin[1].scriptSig = CScript() << OP_11;
249 tx10.vout.resize(1);
250 tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
251 tx10.vout[0].nValue = 10 * COIN;
253 setAncestorsCalculated.clear();
254 BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(4).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
255 BOOST_CHECK(setAncestorsCalculated == setAncestors);
257 pool.addUnchecked(tx10.GetHash(), entry.FromTx(tx10), setAncestors);
260 * tx8 and tx9 should both now be sorted higher
261 * Final order after tx10 is added:
263 * tx3 = 0 (1)
264 * tx5 = 10000 (1)
265 * tx1 = 10000 (1)
266 * tx4 = 15000 (1)
267 * tx2 = 20000 (1)
268 * tx9 = 200k (2 txs)
269 * tx8 = 200k (2 txs)
270 * tx10 = 200k (1 tx)
271 * tx6 = 2.2M (5 txs)
272 * tx7 = 2.2M (4 txs)
274 sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning
275 sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
276 sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
277 sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
278 CheckSort<descendant_score>(pool, sortedOrder);
280 // there should be 10 transactions in the mempool
281 BOOST_CHECK_EQUAL(pool.size(), 10);
283 // Now try removing tx10 and verify the sort order returns to normal
284 pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx());
285 CheckSort<descendant_score>(pool, snapshotOrder);
287 pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx());
288 pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx());
289 /* Now check the sort on the mining score index.
290 * Final order should be:
292 * tx7 (2M)
293 * tx2 (20k)
294 * tx4 (15000)
295 * tx1/tx5 (10000)
296 * tx3/6 (0)
297 * (Ties resolved by hash)
299 sortedOrder.clear();
300 sortedOrder.push_back(tx7.GetHash().ToString());
301 sortedOrder.push_back(tx2.GetHash().ToString());
302 sortedOrder.push_back(tx4.GetHash().ToString());
303 if (tx1.GetHash() < tx5.GetHash()) {
304 sortedOrder.push_back(tx5.GetHash().ToString());
305 sortedOrder.push_back(tx1.GetHash().ToString());
306 } else {
307 sortedOrder.push_back(tx1.GetHash().ToString());
308 sortedOrder.push_back(tx5.GetHash().ToString());
310 if (tx3.GetHash() < tx6.GetHash()) {
311 sortedOrder.push_back(tx6.GetHash().ToString());
312 sortedOrder.push_back(tx3.GetHash().ToString());
313 } else {
314 sortedOrder.push_back(tx3.GetHash().ToString());
315 sortedOrder.push_back(tx6.GetHash().ToString());
317 CheckSort<mining_score>(pool, sortedOrder);
320 BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
322 CTxMemPool pool;
323 TestMemPoolEntryHelper entry;
325 /* 3rd highest fee */
326 CMutableTransaction tx1 = CMutableTransaction();
327 tx1.vout.resize(1);
328 tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
329 tx1.vout[0].nValue = 10 * COIN;
330 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1));
332 /* highest fee */
333 CMutableTransaction tx2 = CMutableTransaction();
334 tx2.vout.resize(1);
335 tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
336 tx2.vout[0].nValue = 2 * COIN;
337 pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).FromTx(tx2));
338 uint64_t tx2Size = GetVirtualTransactionSize(tx2);
340 /* lowest fee */
341 CMutableTransaction tx3 = CMutableTransaction();
342 tx3.vout.resize(1);
343 tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
344 tx3.vout[0].nValue = 5 * COIN;
345 pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).FromTx(tx3));
347 /* 2nd highest fee */
348 CMutableTransaction tx4 = CMutableTransaction();
349 tx4.vout.resize(1);
350 tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
351 tx4.vout[0].nValue = 6 * COIN;
352 pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).FromTx(tx4));
354 /* equal fee rate to tx1, but newer */
355 CMutableTransaction tx5 = CMutableTransaction();
356 tx5.vout.resize(1);
357 tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
358 tx5.vout[0].nValue = 11 * COIN;
359 pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5));
360 BOOST_CHECK_EQUAL(pool.size(), 5);
362 std::vector<std::string> sortedOrder;
363 sortedOrder.resize(5);
364 sortedOrder[0] = tx2.GetHash().ToString(); // 20000
365 sortedOrder[1] = tx4.GetHash().ToString(); // 15000
366 // tx1 and tx5 are both 10000
367 // Ties are broken by hash, not timestamp, so determine which
368 // hash comes first.
369 if (tx1.GetHash() < tx5.GetHash()) {
370 sortedOrder[2] = tx1.GetHash().ToString();
371 sortedOrder[3] = tx5.GetHash().ToString();
372 } else {
373 sortedOrder[2] = tx5.GetHash().ToString();
374 sortedOrder[3] = tx1.GetHash().ToString();
376 sortedOrder[4] = tx3.GetHash().ToString(); // 0
378 CheckSort<ancestor_score>(pool, sortedOrder);
380 /* low fee parent with high fee child */
381 /* tx6 (0) -> tx7 (high) */
382 CMutableTransaction tx6 = CMutableTransaction();
383 tx6.vout.resize(1);
384 tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
385 tx6.vout[0].nValue = 20 * COIN;
386 uint64_t tx6Size = GetVirtualTransactionSize(tx6);
388 pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6));
389 BOOST_CHECK_EQUAL(pool.size(), 6);
390 // Ties are broken by hash
391 if (tx3.GetHash() < tx6.GetHash())
392 sortedOrder.push_back(tx6.GetHash().ToString());
393 else
394 sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
396 CheckSort<ancestor_score>(pool, sortedOrder);
398 CMutableTransaction tx7 = CMutableTransaction();
399 tx7.vin.resize(1);
400 tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
401 tx7.vin[0].scriptSig = CScript() << OP_11;
402 tx7.vout.resize(1);
403 tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
404 tx7.vout[0].nValue = 10 * COIN;
405 uint64_t tx7Size = GetVirtualTransactionSize(tx7);
407 /* set the fee to just below tx2's feerate when including ancestor */
408 CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
410 pool.addUnchecked(tx7.GetHash(), entry.Fee(fee).FromTx(tx7));
411 BOOST_CHECK_EQUAL(pool.size(), 7);
412 sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString());
413 CheckSort<ancestor_score>(pool, sortedOrder);
415 /* after tx6 is mined, tx7 should move up in the sort */
416 std::vector<CTransactionRef> vtx;
417 vtx.push_back(MakeTransactionRef(tx6));
418 pool.removeForBlock(vtx, 1);
420 sortedOrder.erase(sortedOrder.begin()+1);
421 // Ties are broken by hash
422 if (tx3.GetHash() < tx6.GetHash())
423 sortedOrder.pop_back();
424 else
425 sortedOrder.erase(sortedOrder.end()-2);
426 sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
427 CheckSort<ancestor_score>(pool, sortedOrder);
431 BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
433 CTxMemPool pool;
434 TestMemPoolEntryHelper entry;
436 CMutableTransaction tx1 = CMutableTransaction();
437 tx1.vin.resize(1);
438 tx1.vin[0].scriptSig = CScript() << OP_1;
439 tx1.vout.resize(1);
440 tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
441 tx1.vout[0].nValue = 10 * COIN;
442 pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1));
444 CMutableTransaction tx2 = CMutableTransaction();
445 tx2.vin.resize(1);
446 tx2.vin[0].scriptSig = CScript() << OP_2;
447 tx2.vout.resize(1);
448 tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
449 tx2.vout[0].nValue = 10 * COIN;
450 pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2));
452 pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
453 BOOST_CHECK(pool.exists(tx1.GetHash()));
454 BOOST_CHECK(pool.exists(tx2.GetHash()));
456 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
457 BOOST_CHECK(pool.exists(tx1.GetHash()));
458 BOOST_CHECK(!pool.exists(tx2.GetHash()));
460 pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2));
461 CMutableTransaction tx3 = CMutableTransaction();
462 tx3.vin.resize(1);
463 tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
464 tx3.vin[0].scriptSig = CScript() << OP_2;
465 tx3.vout.resize(1);
466 tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
467 tx3.vout[0].nValue = 10 * COIN;
468 pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3));
470 pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
471 BOOST_CHECK(!pool.exists(tx1.GetHash()));
472 BOOST_CHECK(pool.exists(tx2.GetHash()));
473 BOOST_CHECK(pool.exists(tx3.GetHash()));
475 pool.TrimToSize(GetVirtualTransactionSize(tx1)); // mempool is limited to tx1's size in memory usage, so nothing fits
476 BOOST_CHECK(!pool.exists(tx1.GetHash()));
477 BOOST_CHECK(!pool.exists(tx2.GetHash()));
478 BOOST_CHECK(!pool.exists(tx3.GetHash()));
480 CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3) + GetVirtualTransactionSize(tx2));
481 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
483 CMutableTransaction tx4 = CMutableTransaction();
484 tx4.vin.resize(2);
485 tx4.vin[0].prevout.SetNull();
486 tx4.vin[0].scriptSig = CScript() << OP_4;
487 tx4.vin[1].prevout.SetNull();
488 tx4.vin[1].scriptSig = CScript() << OP_4;
489 tx4.vout.resize(2);
490 tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
491 tx4.vout[0].nValue = 10 * COIN;
492 tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
493 tx4.vout[1].nValue = 10 * COIN;
495 CMutableTransaction tx5 = CMutableTransaction();
496 tx5.vin.resize(2);
497 tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
498 tx5.vin[0].scriptSig = CScript() << OP_4;
499 tx5.vin[1].prevout.SetNull();
500 tx5.vin[1].scriptSig = CScript() << OP_5;
501 tx5.vout.resize(2);
502 tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
503 tx5.vout[0].nValue = 10 * COIN;
504 tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
505 tx5.vout[1].nValue = 10 * COIN;
507 CMutableTransaction tx6 = CMutableTransaction();
508 tx6.vin.resize(2);
509 tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
510 tx6.vin[0].scriptSig = CScript() << OP_4;
511 tx6.vin[1].prevout.SetNull();
512 tx6.vin[1].scriptSig = CScript() << OP_6;
513 tx6.vout.resize(2);
514 tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
515 tx6.vout[0].nValue = 10 * COIN;
516 tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
517 tx6.vout[1].nValue = 10 * COIN;
519 CMutableTransaction tx7 = CMutableTransaction();
520 tx7.vin.resize(2);
521 tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
522 tx7.vin[0].scriptSig = CScript() << OP_5;
523 tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
524 tx7.vin[1].scriptSig = CScript() << OP_6;
525 tx7.vout.resize(2);
526 tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
527 tx7.vout[0].nValue = 10 * COIN;
528 tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
529 tx7.vout[1].nValue = 10 * COIN;
531 pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4));
532 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
533 pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6));
534 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
536 // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
537 pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
538 BOOST_CHECK(pool.exists(tx4.GetHash()));
539 BOOST_CHECK(pool.exists(tx6.GetHash()));
540 BOOST_CHECK(!pool.exists(tx7.GetHash()));
542 if (!pool.exists(tx5.GetHash()))
543 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
544 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
546 pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
547 BOOST_CHECK(pool.exists(tx4.GetHash()));
548 BOOST_CHECK(!pool.exists(tx5.GetHash()));
549 BOOST_CHECK(pool.exists(tx6.GetHash()));
550 BOOST_CHECK(!pool.exists(tx7.GetHash()));
552 pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5));
553 pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7));
555 std::vector<CTransactionRef> vtx;
556 SetMockTime(42);
557 SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
558 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
559 // ... we should keep the same min fee until we get a block
560 pool.removeForBlock(vtx, 1);
561 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
562 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
563 // ... then feerate should drop 1/2 each halflife
565 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
566 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
567 // ... with a 1/2 halflife when mempool is < 1/2 its target size
569 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
570 BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
571 // ... with a 1/4 halflife when mempool is < 1/4 its target size
573 SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
574 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
575 // ... but feerate should never drop below 1000
577 SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
578 BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
579 // ... unless it has gone all the way to 0 (after getting past 1000/2)
581 SetMockTime(0);
584 BOOST_AUTO_TEST_SUITE_END()