1 // Copyright (c) 2011-2015 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));
58 std::vector
<CTransactionRef
> removed
;
60 // Nothing in pool, remove should do nothing:
61 testPool
.removeRecursive(txParent
, &removed
);
62 BOOST_CHECK_EQUAL(removed
.size(), 0);
65 testPool
.addUnchecked(txParent
.GetHash(), entry
.FromTx(txParent
));
66 testPool
.removeRecursive(txParent
, &removed
);
67 BOOST_CHECK_EQUAL(removed
.size(), 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 testPool
.removeRecursive(txChild
[0], &removed
);
79 BOOST_CHECK_EQUAL(removed
.size(), 2);
81 // ... make sure grandchild and child are gone:
82 testPool
.removeRecursive(txGrandChild
[0], &removed
);
83 BOOST_CHECK_EQUAL(removed
.size(), 0);
84 testPool
.removeRecursive(txChild
[0], &removed
);
85 BOOST_CHECK_EQUAL(removed
.size(), 0);
86 // Remove parent, all children/grandchildren should go:
87 testPool
.removeRecursive(txParent
, &removed
);
88 BOOST_CHECK_EQUAL(removed
.size(), 5);
89 BOOST_CHECK_EQUAL(testPool
.size(), 0);
92 // Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
93 for (int i
= 0; i
< 3; i
++)
95 testPool
.addUnchecked(txChild
[i
].GetHash(), entry
.FromTx(txChild
[i
]));
96 testPool
.addUnchecked(txGrandChild
[i
].GetHash(), entry
.FromTx(txGrandChild
[i
]));
98 // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
99 // put into the mempool (maybe because it is non-standard):
100 testPool
.removeRecursive(txParent
, &removed
);
101 BOOST_CHECK_EQUAL(removed
.size(), 6);
102 BOOST_CHECK_EQUAL(testPool
.size(), 0);
106 template<typename name
>
107 void CheckSort(CTxMemPool
&pool
, std::vector
<std::string
> &sortedOrder
)
109 BOOST_CHECK_EQUAL(pool
.size(), sortedOrder
.size());
110 typename
CTxMemPool::indexed_transaction_set::index
<name
>::type::iterator it
= pool
.mapTx
.get
<name
>().begin();
112 for (; it
!= pool
.mapTx
.get
<name
>().end(); ++it
, ++count
) {
113 BOOST_CHECK_EQUAL(it
->GetTx().GetHash().ToString(), sortedOrder
[count
]);
117 BOOST_AUTO_TEST_CASE(MempoolIndexingTest
)
119 CTxMemPool
pool(CFeeRate(0));
120 TestMemPoolEntryHelper entry
;
121 entry
.hadNoDependencies
= true;
123 /* 3rd highest fee */
124 CMutableTransaction tx1
= CMutableTransaction();
126 tx1
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
127 tx1
.vout
[0].nValue
= 10 * COIN
;
128 pool
.addUnchecked(tx1
.GetHash(), entry
.Fee(10000LL).Priority(10.0).FromTx(tx1
));
131 CMutableTransaction tx2
= CMutableTransaction();
133 tx2
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
134 tx2
.vout
[0].nValue
= 2 * COIN
;
135 pool
.addUnchecked(tx2
.GetHash(), entry
.Fee(20000LL).Priority(9.0).FromTx(tx2
));
138 CMutableTransaction tx3
= CMutableTransaction();
140 tx3
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
141 tx3
.vout
[0].nValue
= 5 * COIN
;
142 pool
.addUnchecked(tx3
.GetHash(), entry
.Fee(0LL).Priority(100.0).FromTx(tx3
));
144 /* 2nd highest fee */
145 CMutableTransaction tx4
= CMutableTransaction();
147 tx4
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
148 tx4
.vout
[0].nValue
= 6 * COIN
;
149 pool
.addUnchecked(tx4
.GetHash(), entry
.Fee(15000LL).Priority(1.0).FromTx(tx4
));
151 /* equal fee rate to tx1, but newer */
152 CMutableTransaction tx5
= CMutableTransaction();
154 tx5
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
155 tx5
.vout
[0].nValue
= 11 * COIN
;
157 entry
.dPriority
= 10.0;
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
)
322 CTxMemPool
pool(CFeeRate(0));
323 TestMemPoolEntryHelper entry
;
324 entry
.hadNoDependencies
= true;
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 sortedOrder
.push_back(tx6
.GetHash().ToString());
392 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
394 CMutableTransaction tx7
= CMutableTransaction();
396 tx7
.vin
[0].prevout
= COutPoint(tx6
.GetHash(), 0);
397 tx7
.vin
[0].scriptSig
= CScript() << OP_11
;
399 tx7
.vout
[0].scriptPubKey
= CScript() << OP_11
<< OP_EQUAL
;
400 tx7
.vout
[0].nValue
= 10 * COIN
;
401 uint64_t tx7Size
= GetVirtualTransactionSize(tx7
);
403 /* set the fee to just below tx2's feerate when including ancestor */
404 CAmount fee
= (20000/tx2Size
)*(tx7Size
+ tx6Size
) - 1;
406 //CTxMemPoolEntry entry7(tx7, fee, 2, 10.0, 1, true);
407 pool
.addUnchecked(tx7
.GetHash(), entry
.Fee(fee
).FromTx(tx7
));
408 BOOST_CHECK_EQUAL(pool
.size(), 7);
409 sortedOrder
.insert(sortedOrder
.begin()+1, tx7
.GetHash().ToString());
410 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
412 /* after tx6 is mined, tx7 should move up in the sort */
413 std::vector
<CTransactionRef
> vtx
;
414 vtx
.push_back(MakeTransactionRef(tx6
));
415 pool
.removeForBlock(vtx
, 1, NULL
, false);
417 sortedOrder
.erase(sortedOrder
.begin()+1);
418 sortedOrder
.pop_back();
419 sortedOrder
.insert(sortedOrder
.begin(), tx7
.GetHash().ToString());
420 CheckSort
<ancestor_score
>(pool
, sortedOrder
);
424 BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest
)
426 CTxMemPool
pool(CFeeRate(1000));
427 TestMemPoolEntryHelper entry
;
428 entry
.dPriority
= 10.0;
430 CMutableTransaction tx1
= CMutableTransaction();
432 tx1
.vin
[0].scriptSig
= CScript() << OP_1
;
434 tx1
.vout
[0].scriptPubKey
= CScript() << OP_1
<< OP_EQUAL
;
435 tx1
.vout
[0].nValue
= 10 * COIN
;
436 pool
.addUnchecked(tx1
.GetHash(), entry
.Fee(10000LL).FromTx(tx1
, &pool
));
438 CMutableTransaction tx2
= CMutableTransaction();
440 tx2
.vin
[0].scriptSig
= CScript() << OP_2
;
442 tx2
.vout
[0].scriptPubKey
= CScript() << OP_2
<< OP_EQUAL
;
443 tx2
.vout
[0].nValue
= 10 * COIN
;
444 pool
.addUnchecked(tx2
.GetHash(), entry
.Fee(5000LL).FromTx(tx2
, &pool
));
446 pool
.TrimToSize(pool
.DynamicMemoryUsage()); // should do nothing
447 BOOST_CHECK(pool
.exists(tx1
.GetHash()));
448 BOOST_CHECK(pool
.exists(tx2
.GetHash()));
450 pool
.TrimToSize(pool
.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
451 BOOST_CHECK(pool
.exists(tx1
.GetHash()));
452 BOOST_CHECK(!pool
.exists(tx2
.GetHash()));
454 pool
.addUnchecked(tx2
.GetHash(), entry
.FromTx(tx2
, &pool
));
455 CMutableTransaction tx3
= CMutableTransaction();
457 tx3
.vin
[0].prevout
= COutPoint(tx2
.GetHash(), 0);
458 tx3
.vin
[0].scriptSig
= CScript() << OP_2
;
460 tx3
.vout
[0].scriptPubKey
= CScript() << OP_3
<< OP_EQUAL
;
461 tx3
.vout
[0].nValue
= 10 * COIN
;
462 pool
.addUnchecked(tx3
.GetHash(), entry
.Fee(20000LL).FromTx(tx3
, &pool
));
464 pool
.TrimToSize(pool
.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
465 BOOST_CHECK(!pool
.exists(tx1
.GetHash()));
466 BOOST_CHECK(pool
.exists(tx2
.GetHash()));
467 BOOST_CHECK(pool
.exists(tx3
.GetHash()));
469 pool
.TrimToSize(GetVirtualTransactionSize(tx1
)); // mempool is limited to tx1's size in memory usage, so nothing fits
470 BOOST_CHECK(!pool
.exists(tx1
.GetHash()));
471 BOOST_CHECK(!pool
.exists(tx2
.GetHash()));
472 BOOST_CHECK(!pool
.exists(tx3
.GetHash()));
474 CFeeRate
maxFeeRateRemoved(25000, GetVirtualTransactionSize(tx3
) + GetVirtualTransactionSize(tx2
));
475 BOOST_CHECK_EQUAL(pool
.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved
.GetFeePerK() + 1000);
477 CMutableTransaction tx4
= CMutableTransaction();
479 tx4
.vin
[0].prevout
.SetNull();
480 tx4
.vin
[0].scriptSig
= CScript() << OP_4
;
481 tx4
.vin
[1].prevout
.SetNull();
482 tx4
.vin
[1].scriptSig
= CScript() << OP_4
;
484 tx4
.vout
[0].scriptPubKey
= CScript() << OP_4
<< OP_EQUAL
;
485 tx4
.vout
[0].nValue
= 10 * COIN
;
486 tx4
.vout
[1].scriptPubKey
= CScript() << OP_4
<< OP_EQUAL
;
487 tx4
.vout
[1].nValue
= 10 * COIN
;
489 CMutableTransaction tx5
= CMutableTransaction();
491 tx5
.vin
[0].prevout
= COutPoint(tx4
.GetHash(), 0);
492 tx5
.vin
[0].scriptSig
= CScript() << OP_4
;
493 tx5
.vin
[1].prevout
.SetNull();
494 tx5
.vin
[1].scriptSig
= CScript() << OP_5
;
496 tx5
.vout
[0].scriptPubKey
= CScript() << OP_5
<< OP_EQUAL
;
497 tx5
.vout
[0].nValue
= 10 * COIN
;
498 tx5
.vout
[1].scriptPubKey
= CScript() << OP_5
<< OP_EQUAL
;
499 tx5
.vout
[1].nValue
= 10 * COIN
;
501 CMutableTransaction tx6
= CMutableTransaction();
503 tx6
.vin
[0].prevout
= COutPoint(tx4
.GetHash(), 1);
504 tx6
.vin
[0].scriptSig
= CScript() << OP_4
;
505 tx6
.vin
[1].prevout
.SetNull();
506 tx6
.vin
[1].scriptSig
= CScript() << OP_6
;
508 tx6
.vout
[0].scriptPubKey
= CScript() << OP_6
<< OP_EQUAL
;
509 tx6
.vout
[0].nValue
= 10 * COIN
;
510 tx6
.vout
[1].scriptPubKey
= CScript() << OP_6
<< OP_EQUAL
;
511 tx6
.vout
[1].nValue
= 10 * COIN
;
513 CMutableTransaction tx7
= CMutableTransaction();
515 tx7
.vin
[0].prevout
= COutPoint(tx5
.GetHash(), 0);
516 tx7
.vin
[0].scriptSig
= CScript() << OP_5
;
517 tx7
.vin
[1].prevout
= COutPoint(tx6
.GetHash(), 0);
518 tx7
.vin
[1].scriptSig
= CScript() << OP_6
;
520 tx7
.vout
[0].scriptPubKey
= CScript() << OP_7
<< OP_EQUAL
;
521 tx7
.vout
[0].nValue
= 10 * COIN
;
522 tx7
.vout
[1].scriptPubKey
= CScript() << OP_7
<< OP_EQUAL
;
523 tx7
.vout
[1].nValue
= 10 * COIN
;
525 pool
.addUnchecked(tx4
.GetHash(), entry
.Fee(7000LL).FromTx(tx4
, &pool
));
526 pool
.addUnchecked(tx5
.GetHash(), entry
.Fee(1000LL).FromTx(tx5
, &pool
));
527 pool
.addUnchecked(tx6
.GetHash(), entry
.Fee(1100LL).FromTx(tx6
, &pool
));
528 pool
.addUnchecked(tx7
.GetHash(), entry
.Fee(9000LL).FromTx(tx7
, &pool
));
530 // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
531 pool
.TrimToSize(pool
.DynamicMemoryUsage() - 1);
532 BOOST_CHECK(pool
.exists(tx4
.GetHash()));
533 BOOST_CHECK(pool
.exists(tx6
.GetHash()));
534 BOOST_CHECK(!pool
.exists(tx7
.GetHash()));
536 if (!pool
.exists(tx5
.GetHash()))
537 pool
.addUnchecked(tx5
.GetHash(), entry
.Fee(1000LL).FromTx(tx5
, &pool
));
538 pool
.addUnchecked(tx7
.GetHash(), entry
.Fee(9000LL).FromTx(tx7
, &pool
));
540 pool
.TrimToSize(pool
.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
541 BOOST_CHECK(pool
.exists(tx4
.GetHash()));
542 BOOST_CHECK(!pool
.exists(tx5
.GetHash()));
543 BOOST_CHECK(pool
.exists(tx6
.GetHash()));
544 BOOST_CHECK(!pool
.exists(tx7
.GetHash()));
546 pool
.addUnchecked(tx5
.GetHash(), entry
.Fee(1000LL).FromTx(tx5
, &pool
));
547 pool
.addUnchecked(tx7
.GetHash(), entry
.Fee(9000LL).FromTx(tx7
, &pool
));
549 std::vector
<CTransactionRef
> vtx
;
551 SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE
);
552 BOOST_CHECK_EQUAL(pool
.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved
.GetFeePerK() + 1000);
553 // ... we should keep the same min fee until we get a block
554 pool
.removeForBlock(vtx
, 1);
555 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE
);
556 BOOST_CHECK_EQUAL(pool
.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved
.GetFeePerK() + 1000)/2);
557 // ... then feerate should drop 1/2 each halflife
559 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE
+ CTxMemPool::ROLLING_FEE_HALFLIFE
/2);
560 BOOST_CHECK_EQUAL(pool
.GetMinFee(pool
.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved
.GetFeePerK() + 1000)/4);
561 // ... with a 1/2 halflife when mempool is < 1/2 its target size
563 SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE
+ CTxMemPool::ROLLING_FEE_HALFLIFE
/2 + CTxMemPool::ROLLING_FEE_HALFLIFE
/4);
564 BOOST_CHECK_EQUAL(pool
.GetMinFee(pool
.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved
.GetFeePerK() + 1000)/8);
565 // ... with a 1/4 halflife when mempool is < 1/4 its target size
567 SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE
+ CTxMemPool::ROLLING_FEE_HALFLIFE
/2 + CTxMemPool::ROLLING_FEE_HALFLIFE
/4);
568 BOOST_CHECK_EQUAL(pool
.GetMinFee(1).GetFeePerK(), 1000);
569 // ... but feerate should never drop below 1000
571 SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE
+ CTxMemPool::ROLLING_FEE_HALFLIFE
/2 + CTxMemPool::ROLLING_FEE_HALFLIFE
/4);
572 BOOST_CHECK_EQUAL(pool
.GetMinFee(1).GetFeePerK(), 0);
573 // ... unless it has gone all the way to 0 (after getting past 1000/2)
578 BOOST_AUTO_TEST_SUITE_END()