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;
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
)
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).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).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).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).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 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();
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();
186 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
187 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
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
;
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();
211 tx8
.vin
[0].prevout
= COutPoint(tx7
.GetHash(), 0);
212 tx8
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
226 tx9
.vin
[0].prevout
= COutPoint(tx7
.GetHash(), 1);
227 tx9
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
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
;
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:
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:
297 * (Ties resolved by hash)
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());
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());
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
)
323 TestMemPoolEntryHelper entry
;
325 /* 3rd highest fee */
326 CMutableTransaction tx1
= CMutableTransaction();
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
));
333 CMutableTransaction tx2
= CMutableTransaction();
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
);
341 CMutableTransaction tx3
= CMutableTransaction();
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();
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();
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
369 if (tx1
.GetHash() < tx5
.GetHash()) {
370 sortedOrder
[2] = tx1
.GetHash().ToString();
371 sortedOrder
[3] = tx5
.GetHash().ToString();
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();
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());
394 sortedOrder
.insert(sortedOrder
.end()-1,tx6
.GetHash().ToString());
396 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
398 CMutableTransaction tx7
= CMutableTransaction();
400 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
401 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
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
)
434 TestMemPoolEntryHelper entry
;
436 CMutableTransaction tx1
= CMutableTransaction();
438 tx1
.vin
[0].scriptSig
= CScript() << OP_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();
446 tx2
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
463 tx3
.vin
[0].prevout
= COutPoint(tx2
.GetHash(), 0);
464 tx3
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
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
;
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();
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
;
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();
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
;
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();
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
;
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
;
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)
584 BOOST_AUTO_TEST_SUITE_END()