@@ -281,4 +281,158 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
281
281
CheckSort (pool, snapshotOrder);
282
282
}
283
283
284
+ BOOST_AUTO_TEST_CASE (MempoolSizeLimitTest)
285
+ {
286
+ CTxMemPool pool (CFeeRate (1000 ));
287
+
288
+ CMutableTransaction tx1 = CMutableTransaction ();
289
+ tx1.vin .resize (1 );
290
+ tx1.vin [0 ].scriptSig = CScript () << OP_1;
291
+ tx1.vout .resize (1 );
292
+ tx1.vout [0 ].scriptPubKey = CScript () << OP_1 << OP_EQUAL;
293
+ tx1.vout [0 ].nValue = 10 * COIN;
294
+ pool.addUnchecked (tx1.GetHash (), CTxMemPoolEntry (tx1, 10000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx1)));
295
+
296
+ CMutableTransaction tx2 = CMutableTransaction ();
297
+ tx2.vin .resize (1 );
298
+ tx2.vin [0 ].scriptSig = CScript () << OP_2;
299
+ tx2.vout .resize (1 );
300
+ tx2.vout [0 ].scriptPubKey = CScript () << OP_2 << OP_EQUAL;
301
+ tx2.vout [0 ].nValue = 10 * COIN;
302
+ pool.addUnchecked (tx2.GetHash (), CTxMemPoolEntry (tx2, 5000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx2)));
303
+
304
+ pool.TrimToSize (pool.DynamicMemoryUsage ()); // should do nothing
305
+ BOOST_CHECK (pool.exists (tx1.GetHash ()));
306
+ BOOST_CHECK (pool.exists (tx2.GetHash ()));
307
+
308
+ pool.TrimToSize (pool.DynamicMemoryUsage () * 3 / 4 ); // should remove the lower-feerate transaction
309
+ BOOST_CHECK (pool.exists (tx1.GetHash ()));
310
+ BOOST_CHECK (!pool.exists (tx2.GetHash ()));
311
+
312
+ pool.addUnchecked (tx2.GetHash (), CTxMemPoolEntry (tx2, 5000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx2)));
313
+ CMutableTransaction tx3 = CMutableTransaction ();
314
+ tx3.vin .resize (1 );
315
+ tx3.vin [0 ].prevout = COutPoint (tx2.GetHash (), 0 );
316
+ tx3.vin [0 ].scriptSig = CScript () << OP_2;
317
+ tx3.vout .resize (1 );
318
+ tx3.vout [0 ].scriptPubKey = CScript () << OP_3 << OP_EQUAL;
319
+ tx3.vout [0 ].nValue = 10 * COIN;
320
+ pool.addUnchecked (tx3.GetHash (), CTxMemPoolEntry (tx3, 20000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx3)));
321
+
322
+ pool.TrimToSize (pool.DynamicMemoryUsage () * 3 / 4 ); // tx3 should pay for tx2 (CPFP)
323
+ BOOST_CHECK (!pool.exists (tx1.GetHash ()));
324
+ BOOST_CHECK (pool.exists (tx2.GetHash ()));
325
+ BOOST_CHECK (pool.exists (tx3.GetHash ()));
326
+
327
+ pool.TrimToSize (::GetSerializeSize (CTransaction (tx1), SER_NETWORK, PROTOCOL_VERSION)); // mempool is limited to tx1's size in memory usage, so nothing fits
328
+ BOOST_CHECK (!pool.exists (tx1.GetHash ()));
329
+ BOOST_CHECK (!pool.exists (tx2.GetHash ()));
330
+ BOOST_CHECK (!pool.exists (tx3.GetHash ()));
331
+
332
+ CFeeRate maxFeeRateRemoved (25000 , ::GetSerializeSize (CTransaction (tx3), SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize (CTransaction (tx2), SER_NETWORK, PROTOCOL_VERSION));
333
+ BOOST_CHECK_EQUAL (pool.GetMinFee (1 ).GetFeePerK (), maxFeeRateRemoved.GetFeePerK () + 1000 );
334
+
335
+ CMutableTransaction tx4 = CMutableTransaction ();
336
+ tx4.vin .resize (2 );
337
+ tx4.vin [0 ].prevout .SetNull ();
338
+ tx4.vin [0 ].scriptSig = CScript () << OP_4;
339
+ tx4.vin [1 ].prevout .SetNull ();
340
+ tx4.vin [1 ].scriptSig = CScript () << OP_4;
341
+ tx4.vout .resize (2 );
342
+ tx4.vout [0 ].scriptPubKey = CScript () << OP_4 << OP_EQUAL;
343
+ tx4.vout [0 ].nValue = 10 * COIN;
344
+ tx4.vout [1 ].scriptPubKey = CScript () << OP_4 << OP_EQUAL;
345
+ tx4.vout [1 ].nValue = 10 * COIN;
346
+
347
+ CMutableTransaction tx5 = CMutableTransaction ();
348
+ tx5.vin .resize (2 );
349
+ tx5.vin [0 ].prevout = COutPoint (tx4.GetHash (), 0 );
350
+ tx5.vin [0 ].scriptSig = CScript () << OP_4;
351
+ tx5.vin [1 ].prevout .SetNull ();
352
+ tx5.vin [1 ].scriptSig = CScript () << OP_5;
353
+ tx5.vout .resize (2 );
354
+ tx5.vout [0 ].scriptPubKey = CScript () << OP_5 << OP_EQUAL;
355
+ tx5.vout [0 ].nValue = 10 * COIN;
356
+ tx5.vout [1 ].scriptPubKey = CScript () << OP_5 << OP_EQUAL;
357
+ tx5.vout [1 ].nValue = 10 * COIN;
358
+
359
+ CMutableTransaction tx6 = CMutableTransaction ();
360
+ tx6.vin .resize (2 );
361
+ tx6.vin [0 ].prevout = COutPoint (tx4.GetHash (), 1 );
362
+ tx6.vin [0 ].scriptSig = CScript () << OP_4;
363
+ tx6.vin [1 ].prevout .SetNull ();
364
+ tx6.vin [1 ].scriptSig = CScript () << OP_6;
365
+ tx6.vout .resize (2 );
366
+ tx6.vout [0 ].scriptPubKey = CScript () << OP_6 << OP_EQUAL;
367
+ tx6.vout [0 ].nValue = 10 * COIN;
368
+ tx6.vout [1 ].scriptPubKey = CScript () << OP_6 << OP_EQUAL;
369
+ tx6.vout [1 ].nValue = 10 * COIN;
370
+
371
+ CMutableTransaction tx7 = CMutableTransaction ();
372
+ tx7.vin .resize (2 );
373
+ tx7.vin [0 ].prevout = COutPoint (tx5.GetHash (), 0 );
374
+ tx7.vin [0 ].scriptSig = CScript () << OP_5;
375
+ tx7.vin [1 ].prevout = COutPoint (tx6.GetHash (), 0 );
376
+ tx7.vin [1 ].scriptSig = CScript () << OP_6;
377
+ tx7.vout .resize (2 );
378
+ tx7.vout [0 ].scriptPubKey = CScript () << OP_7 << OP_EQUAL;
379
+ tx7.vout [0 ].nValue = 10 * COIN;
380
+ tx7.vout [0 ].scriptPubKey = CScript () << OP_7 << OP_EQUAL;
381
+ tx7.vout [0 ].nValue = 10 * COIN;
382
+
383
+ pool.addUnchecked (tx4.GetHash (), CTxMemPoolEntry (tx4, 7000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx4)));
384
+ pool.addUnchecked (tx5.GetHash (), CTxMemPoolEntry (tx5, 1000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx5)));
385
+ pool.addUnchecked (tx6.GetHash (), CTxMemPoolEntry (tx6, 1100LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx6)));
386
+ pool.addUnchecked (tx7.GetHash (), CTxMemPoolEntry (tx7, 9000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx7)));
387
+
388
+ // we only require this remove, at max, 2 txn, because its not clear what we're really optimizing for aside from that
389
+ pool.TrimToSize (pool.DynamicMemoryUsage () - 1 );
390
+ BOOST_CHECK (pool.exists (tx4.GetHash ()));
391
+ BOOST_CHECK (pool.exists (tx6.GetHash ()));
392
+ BOOST_CHECK (!pool.exists (tx7.GetHash ()));
393
+
394
+ if (!pool.exists (tx5.GetHash ()))
395
+ pool.addUnchecked (tx5.GetHash (), CTxMemPoolEntry (tx5, 1000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx5)));
396
+ pool.addUnchecked (tx7.GetHash (), CTxMemPoolEntry (tx7, 9000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx7)));
397
+
398
+ pool.TrimToSize (pool.DynamicMemoryUsage () / 2 ); // should maximize mempool size by only removing 5/7
399
+ BOOST_CHECK (pool.exists (tx4.GetHash ()));
400
+ BOOST_CHECK (!pool.exists (tx5.GetHash ()));
401
+ BOOST_CHECK (pool.exists (tx6.GetHash ()));
402
+ BOOST_CHECK (!pool.exists (tx7.GetHash ()));
403
+
404
+ pool.addUnchecked (tx5.GetHash (), CTxMemPoolEntry (tx5, 1000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx5)));
405
+ pool.addUnchecked (tx7.GetHash (), CTxMemPoolEntry (tx7, 9000LL , 0 , 10.0 , 1 , pool.HasNoInputsOf (tx7)));
406
+
407
+ std::vector<CTransaction> vtx;
408
+ std::list<CTransaction> conflicts;
409
+ SetMockTime (42 );
410
+ SetMockTime (42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
411
+ BOOST_CHECK_EQUAL (pool.GetMinFee (1 ).GetFeePerK (), maxFeeRateRemoved.GetFeePerK () + 1000 );
412
+ // ... we should keep the same min fee until we get a block
413
+ pool.removeForBlock (vtx, 1 , conflicts);
414
+ SetMockTime (42 + 2 *CTxMemPool::ROLLING_FEE_HALFLIFE);
415
+ BOOST_CHECK_EQUAL (pool.GetMinFee (1 ).GetFeePerK (), (maxFeeRateRemoved.GetFeePerK () + 1000 )/2 );
416
+ // ... then feerate should drop 1/2 each halflife
417
+
418
+ SetMockTime (42 + 2 *CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 );
419
+ BOOST_CHECK_EQUAL (pool.GetMinFee (pool.DynamicMemoryUsage () * 5 / 2 ).GetFeePerK (), (maxFeeRateRemoved.GetFeePerK () + 1000 )/4 );
420
+ // ... with a 1/2 halflife when mempool is < 1/2 its target size
421
+
422
+ SetMockTime (42 + 2 *CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4 );
423
+ BOOST_CHECK_EQUAL (pool.GetMinFee (pool.DynamicMemoryUsage () * 9 / 2 ).GetFeePerK (), (maxFeeRateRemoved.GetFeePerK () + 1000 )/8 );
424
+ // ... with a 1/4 halflife when mempool is < 1/4 its target size
425
+
426
+ SetMockTime (42 + 7 *CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4 );
427
+ BOOST_CHECK_EQUAL (pool.GetMinFee (1 ).GetFeePerK (), 1000 );
428
+ // ... but feerate should never drop below 1000
429
+
430
+ SetMockTime (42 + 8 *CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4 );
431
+ pool.GetMinFee (1 );
432
+ BOOST_CHECK_EQUAL (pool.GetMinFee (1 ).GetFeePerK (), 0 );
433
+ // ... unless it has gone all the way to 0 (after getting past 1000/2)
434
+
435
+ SetMockTime (0 );
436
+ }
437
+
284
438
BOOST_AUTO_TEST_SUITE_END ()
0 commit comments