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>
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
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
)
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).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).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).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).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
380 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
382 /* low fee parent with high fee child */
383 /* tx6 (0) -> tx7 (high) */
384 CMutableTransaction tx6
= CMutableTransaction();
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());
396 sortedOrder
.insert(sortedOrder
.end()-1,tx6
.GetHash().ToString());
398 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
400 CMutableTransaction tx7
= CMutableTransaction();
402 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
403 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
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();
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
)
436 TestMemPoolEntryHelper entry
;
438 CMutableTransaction tx1
= CMutableTransaction();
440 tx1
.vin
[0].scriptSig
= CScript() << OP_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();
448 tx2
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
465 tx3
.vin
[0].prevout
= COutPoint(tx2
.GetHash(), 0);
466 tx3
.vin
[0].scriptSig
= CScript() << OP_2
;
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();
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
;
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();
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
;
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();
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
;
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();
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
;
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
;
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)
586 BOOST_AUTO_TEST_SUITE_END()