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"
9 #include "test/test_bitcoin.h"
11 #include <boost/test/unit_test.hpp>
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
);
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();
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();
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
));
132 CMutableTransaction tx2
= CMutableTransaction();
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
));
139 CMutableTransaction tx3
= CMutableTransaction();
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();
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();
155 tx5
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
156 tx5
.vout
[0].nValue
= 11 * COIN
;
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();
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();
187 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
188 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
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
;
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();
212 tx8
.vin
[0].prevout
= COutPoint(tx7
.GetHash(), 0);
213 tx8
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
227 tx9
.vin
[0].prevout
= COutPoint(tx7
.GetHash(), 1);
228 tx9
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
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
;
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:
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:
298 * (Ties resolved by hash)
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());
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());
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();
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
));
334 CMutableTransaction tx2
= CMutableTransaction();
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
);
342 CMutableTransaction tx3
= CMutableTransaction();
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();
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();
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
370 if (tx1
.GetHash() < tx5
.GetHash()) {
371 sortedOrder
[2] = tx1
.GetHash().ToString();
372 sortedOrder
[3] = tx5
.GetHash().ToString();
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();
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());
395 sortedOrder
.insert(sortedOrder
.end()-1,tx6
.GetHash().ToString());
397 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
399 CMutableTransaction tx7
= CMutableTransaction();
401 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
402 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
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();
441 tx1
.vin
[0].scriptSig
= CScript() << OP_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();
449 tx2
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
466 tx3
.vin
[0].prevout
= COutPoint(tx2
.GetHash(), 0);
467 tx3
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
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
;
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();
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
;
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();
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
;
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();
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
;
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
;
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)
587 BOOST_AUTO_TEST_SUITE_END()