LCOV - code coverage report
Current view: top level - src/test - mempool_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 524 528 99.2 %
Date: 2026-06-25 07:23:43 Functions: 48 48 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2011-2021 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.
       4             : 
       5             : #include <policy/policy.h>
       6             : #include <test/util/txmempool.h>
       7             : #include <txmempool.h>
       8             : #include <util/system.h>
       9             : #include <util/time.h>
      10             : 
      11             : #include <test/util/setup_common.h>
      12             : 
      13             : #include <boost/test/unit_test.hpp>
      14             : #include <vector>
      15             : 
      16         146 : BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
      17             : 
      18             : static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::MANUAL;
      19             : 
      20         149 : BOOST_AUTO_TEST_CASE(MempoolRemoveTest)
      21             : {
      22             :     // Test CTxMemPool::remove functionality
      23             : 
      24           1 :     TestMemPoolEntryHelper entry;
      25             :     // Parent transaction with three children,
      26             :     // and three grand-children:
      27           1 :     CMutableTransaction txParent;
      28           1 :     txParent.vin.resize(1);
      29           1 :     txParent.vin[0].scriptSig = CScript() << OP_11;
      30           1 :     txParent.vout.resize(3);
      31           4 :     for (int i = 0; i < 3; i++)
      32             :     {
      33           3 :         txParent.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
      34           3 :         txParent.vout[i].nValue = 33000LL;
      35           3 :     }
      36           3 :     CMutableTransaction txChild[3];
      37           4 :     for (int i = 0; i < 3; i++)
      38             :     {
      39           3 :         txChild[i].vin.resize(1);
      40           3 :         txChild[i].vin[0].scriptSig = CScript() << OP_11;
      41           3 :         txChild[i].vin[0].prevout.hash = txParent.GetHash();
      42           3 :         txChild[i].vin[0].prevout.n = i;
      43           3 :         txChild[i].vout.resize(1);
      44           3 :         txChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
      45           3 :         txChild[i].vout[0].nValue = 11000LL;
      46           3 :     }
      47           3 :     CMutableTransaction txGrandChild[3];
      48           4 :     for (int i = 0; i < 3; i++)
      49             :     {
      50           3 :         txGrandChild[i].vin.resize(1);
      51           3 :         txGrandChild[i].vin[0].scriptSig = CScript() << OP_11;
      52           3 :         txGrandChild[i].vin[0].prevout.hash = txChild[i].GetHash();
      53           3 :         txGrandChild[i].vin[0].prevout.n = 0;
      54           3 :         txGrandChild[i].vout.resize(1);
      55           3 :         txGrandChild[i].vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
      56           3 :         txGrandChild[i].vout[0].nValue = 11000LL;
      57           3 :     }
      58             : 
      59             : 
      60           1 :     CTxMemPool& testPool = *Assert(m_node.mempool);
      61           1 :     LOCK2(cs_main, testPool.cs);
      62             : 
      63             :     // Nothing in pool, remove should do nothing:
      64           1 :     unsigned int poolSize = testPool.size();
      65           1 :     testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
      66           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize);
      67             : 
      68             :     // Just the parent:
      69           1 :     testPool.addUnchecked(entry.FromTx(txParent));
      70           1 :     poolSize = testPool.size();
      71           1 :     testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
      72           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1);
      73             : 
      74             :     // Parent, children, grandchildren:
      75           1 :     testPool.addUnchecked(entry.FromTx(txParent));
      76           4 :     for (int i = 0; i < 3; i++)
      77             :     {
      78           3 :         testPool.addUnchecked(entry.FromTx(txChild[i]));
      79           3 :         testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
      80           3 :     }
      81             :     // Remove Child[0], GrandChild[0] should be removed:
      82           1 :     poolSize = testPool.size();
      83           1 :     testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
      84           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2);
      85             :     // ... make sure grandchild and child are gone:
      86           1 :     poolSize = testPool.size();
      87           1 :     testPool.removeRecursive(CTransaction(txGrandChild[0]), REMOVAL_REASON_DUMMY);
      88           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize);
      89           1 :     poolSize = testPool.size();
      90           1 :     testPool.removeRecursive(CTransaction(txChild[0]), REMOVAL_REASON_DUMMY);
      91           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize);
      92             :     // Remove parent, all children/grandchildren should go:
      93           1 :     poolSize = testPool.size();
      94           1 :     testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
      95           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5);
      96           1 :     BOOST_CHECK_EQUAL(testPool.size(), 0U);
      97             : 
      98             :     // Add children and grandchildren, but NOT the parent (simulate the parent being in a block)
      99           4 :     for (int i = 0; i < 3; i++)
     100             :     {
     101           3 :         testPool.addUnchecked(entry.FromTx(txChild[i]));
     102           3 :         testPool.addUnchecked(entry.FromTx(txGrandChild[i]));
     103           3 :     }
     104             :     // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be
     105             :     // put into the mempool (maybe because it is non-standard):
     106           1 :     poolSize = testPool.size();
     107           1 :     testPool.removeRecursive(CTransaction(txParent), REMOVAL_REASON_DUMMY);
     108           1 :     BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6);
     109           1 :     BOOST_CHECK_EQUAL(testPool.size(), 0U);
     110           1 : }
     111             : 
     112             : template <typename name>
     113          12 : static void CheckSort(CTxMemPool& pool, std::vector<std::string>& sortedOrder) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
     114             : {
     115          12 :     BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size());
     116          12 :     typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin();
     117          12 :     int count = 0;
     118          97 :     for (; it != pool.mapTx.get<name>().end(); ++it, ++count) {
     119          85 :         BOOST_CHECK_EQUAL(it->GetTx().GetHash().ToString(), sortedOrder[count]);
     120          85 :     }
     121          12 : }
     122             : 
     123         149 : BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
     124             : {
     125           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     126           1 :     LOCK2(cs_main, pool.cs);
     127           1 :     TestMemPoolEntryHelper entry;
     128             : 
     129             :     /* 3rd highest fee */
     130           1 :     CMutableTransaction tx1 = CMutableTransaction();
     131           1 :     tx1.vout.resize(1);
     132           1 :     tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     133           1 :     tx1.vout[0].nValue = 10 * COIN;
     134           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
     135             : 
     136             :     /* highest fee */
     137           1 :     CMutableTransaction tx2 = CMutableTransaction();
     138           1 :     tx2.vout.resize(1);
     139           1 :     tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     140           1 :     tx2.vout[0].nValue = 2 * COIN;
     141           1 :     pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
     142             : 
     143             :     /* lowest fee */
     144           1 :     CMutableTransaction tx3 = CMutableTransaction();
     145           1 :     tx3.vout.resize(1);
     146           1 :     tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     147           1 :     tx3.vout[0].nValue = 5 * COIN;
     148           1 :     pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
     149             : 
     150             :     /* 2nd highest fee */
     151           1 :     CMutableTransaction tx4 = CMutableTransaction();
     152           1 :     tx4.vout.resize(1);
     153           1 :     tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     154           1 :     tx4.vout[0].nValue = 6 * COIN;
     155           1 :     pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
     156             : 
     157             :     /* equal fee rate to tx1, but newer */
     158           1 :     CMutableTransaction tx5 = CMutableTransaction();
     159           1 :     tx5.vout.resize(1);
     160           1 :     tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     161           1 :     tx5.vout[0].nValue = 11 * COIN;
     162           1 :     entry.time = NodeSeconds{1s};
     163           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
     164           1 :     BOOST_CHECK_EQUAL(pool.size(), 5U);
     165             : 
     166           1 :     std::vector<std::string> sortedOrder;
     167           1 :     sortedOrder.resize(5);
     168           1 :     sortedOrder[0] = tx3.GetHash().ToString(); // 0
     169           1 :     sortedOrder[1] = tx5.GetHash().ToString(); // 10000
     170           1 :     sortedOrder[2] = tx1.GetHash().ToString(); // 10000
     171           1 :     sortedOrder[3] = tx4.GetHash().ToString(); // 15000
     172           1 :     sortedOrder[4] = tx2.GetHash().ToString(); // 20000
     173           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     174             : 
     175             :     /* low fee but with high fee child */
     176             :     /* tx6 -> tx7 -> tx8, tx9 -> tx10 */
     177           1 :     CMutableTransaction tx6 = CMutableTransaction();
     178           1 :     tx6.vout.resize(1);
     179           1 :     tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     180           1 :     tx6.vout[0].nValue = 20 * COIN;
     181           1 :     pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
     182           1 :     BOOST_CHECK_EQUAL(pool.size(), 6U);
     183             :     // Check that at this point, tx6 is sorted low
     184           1 :     sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString());
     185           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     186             : 
     187           1 :     CTxMemPool::setEntries setAncestors;
     188           1 :     setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
     189           1 :     CMutableTransaction tx7 = CMutableTransaction();
     190           1 :     tx7.vin.resize(1);
     191           1 :     tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
     192           1 :     tx7.vin[0].scriptSig = CScript() << OP_11;
     193           1 :     tx7.vout.resize(2);
     194           1 :     tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     195           1 :     tx7.vout[0].nValue = 10 * COIN;
     196           1 :     tx7.vout[1].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     197           1 :     tx7.vout[1].nValue = 1 * COIN;
     198             : 
     199           1 :     CTxMemPool::setEntries setAncestorsCalculated;
     200           1 :     std::string dummy;
     201           1 :     BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
     202           1 :     BOOST_CHECK(setAncestorsCalculated == setAncestors);
     203             : 
     204           1 :     pool.addUnchecked(entry.FromTx(tx7), setAncestors);
     205           1 :     BOOST_CHECK_EQUAL(pool.size(), 7U);
     206             : 
     207             :     // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ...
     208           1 :     sortedOrder.erase(sortedOrder.begin());
     209           1 :     sortedOrder.push_back(tx6.GetHash().ToString());
     210           1 :     sortedOrder.push_back(tx7.GetHash().ToString());
     211           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     212             : 
     213             :     /* low fee child of tx7 */
     214           1 :     CMutableTransaction tx8 = CMutableTransaction();
     215           1 :     tx8.vin.resize(1);
     216           1 :     tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
     217           1 :     tx8.vin[0].scriptSig = CScript() << OP_11;
     218           1 :     tx8.vout.resize(1);
     219           1 :     tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     220           1 :     tx8.vout[0].nValue = 10 * COIN;
     221           1 :     setAncestors.insert(pool.mapTx.find(tx7.GetHash()));
     222           1 :     pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8), setAncestors);
     223             : 
     224             :     // Now tx8 should be sorted low, but tx6/tx both high
     225           1 :     sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString());
     226           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     227             : 
     228             :     /* low fee child of tx7 */
     229           1 :     CMutableTransaction tx9 = CMutableTransaction();
     230           1 :     tx9.vin.resize(1);
     231           1 :     tx9.vin[0].prevout = COutPoint(tx7.GetHash(), 1);
     232           1 :     tx9.vin[0].scriptSig = CScript() << OP_11;
     233           1 :     tx9.vout.resize(1);
     234           1 :     tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     235           1 :     tx9.vout[0].nValue = 1 * COIN;
     236           1 :     pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{3s}).FromTx(tx9), setAncestors);
     237             : 
     238             :     // tx9 should be sorted low
     239           1 :     BOOST_CHECK_EQUAL(pool.size(), 9U);
     240           1 :     sortedOrder.insert(sortedOrder.begin(), tx9.GetHash().ToString());
     241           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     242             : 
     243           1 :     std::vector<std::string> snapshotOrder = sortedOrder;
     244             : 
     245           1 :     setAncestors.insert(pool.mapTx.find(tx8.GetHash()));
     246           1 :     setAncestors.insert(pool.mapTx.find(tx9.GetHash()));
     247             :     /* tx10 depends on tx8 and tx9 and has a high fee*/
     248           1 :     CMutableTransaction tx10 = CMutableTransaction();
     249           1 :     tx10.vin.resize(2);
     250           1 :     tx10.vin[0].prevout = COutPoint(tx8.GetHash(), 0);
     251           1 :     tx10.vin[0].scriptSig = CScript() << OP_11;
     252           1 :     tx10.vin[1].prevout = COutPoint(tx9.GetHash(), 0);
     253           1 :     tx10.vin[1].scriptSig = CScript() << OP_11;
     254           1 :     tx10.vout.resize(1);
     255           1 :     tx10.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     256           1 :     tx10.vout[0].nValue = 10 * COIN;
     257             : 
     258           1 :     setAncestorsCalculated.clear();
     259           1 :     BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(NodeSeconds{4s}).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true);
     260           1 :     BOOST_CHECK(setAncestorsCalculated == setAncestors);
     261             : 
     262           1 :     pool.addUnchecked(entry.FromTx(tx10), setAncestors);
     263             : 
     264             :     /**
     265             :      *  tx8 and tx9 should both now be sorted higher
     266             :      *  Final order after tx10 is added:
     267             :      *
     268             :      *  tx3 = 0 (1)
     269             :      *  tx5 = 10000 (1)
     270             :      *  tx1 = 10000 (1)
     271             :      *  tx4 = 15000 (1)
     272             :      *  tx2 = 20000 (1)
     273             :      *  tx9 = 200k (2 txs)
     274             :      *  tx8 = 200k (2 txs)
     275             :      *  tx10 = 200k (1 tx)
     276             :      *  tx6 = 2.2M (5 txs)
     277             :      *  tx7 = 2.2M (4 txs)
     278             :      */
     279           1 :     sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin()+2); // take out tx9, tx8 from the beginning
     280           1 :     sortedOrder.insert(sortedOrder.begin()+5, tx9.GetHash().ToString());
     281           1 :     sortedOrder.insert(sortedOrder.begin()+6, tx8.GetHash().ToString());
     282           1 :     sortedOrder.insert(sortedOrder.begin()+7, tx10.GetHash().ToString()); // tx10 is just before tx6
     283           1 :     CheckSort<descendant_score>(pool, sortedOrder);
     284             : 
     285             :     // there should be 10 transactions in the mempool
     286           1 :     BOOST_CHECK_EQUAL(pool.size(), 10U);
     287             : 
     288             :     // Now try removing tx10 and verify the sort order returns to normal
     289           1 :     pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
     290           1 :     CheckSort<descendant_score>(pool, snapshotOrder);
     291             : 
     292           1 :     pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
     293           1 :     pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
     294           1 : }
     295             : 
     296         149 : BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
     297             : {
     298           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     299           1 :     LOCK2(cs_main, pool.cs);
     300           1 :     TestMemPoolEntryHelper entry;
     301             : 
     302             :     /* 3rd highest fee */
     303           1 :     CMutableTransaction tx1 = CMutableTransaction();
     304           1 :     tx1.vout.resize(1);
     305           1 :     tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     306           1 :     tx1.vout[0].nValue = 10 * COIN;
     307           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
     308             : 
     309             :     /* highest fee */
     310           1 :     CMutableTransaction tx2 = CMutableTransaction();
     311           1 :     tx2.vout.resize(1);
     312           1 :     tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     313           1 :     tx2.vout[0].nValue = 2 * COIN;
     314           1 :     pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2));
     315           1 :     uint64_t tx2Size = GetVirtualTransactionSize(CTransaction(tx2));
     316             : 
     317             :     /* lowest fee */
     318           1 :     CMutableTransaction tx3 = CMutableTransaction();
     319           1 :     tx3.vout.resize(1);
     320           1 :     tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     321           1 :     tx3.vout[0].nValue = 5 * COIN;
     322           1 :     pool.addUnchecked(entry.Fee(0LL).FromTx(tx3));
     323             : 
     324             :     /* 2nd highest fee */
     325           1 :     CMutableTransaction tx4 = CMutableTransaction();
     326           1 :     tx4.vout.resize(1);
     327           1 :     tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     328           1 :     tx4.vout[0].nValue = 6 * COIN;
     329           1 :     pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4));
     330             : 
     331             :     /* equal fee rate to tx1, but newer */
     332           1 :     CMutableTransaction tx5 = CMutableTransaction();
     333           1 :     tx5.vout.resize(1);
     334           1 :     tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     335           1 :     tx5.vout[0].nValue = 11 * COIN;
     336           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5));
     337           1 :     BOOST_CHECK_EQUAL(pool.size(), 5U);
     338             : 
     339           1 :     std::vector<std::string> sortedOrder;
     340           1 :     sortedOrder.resize(5);
     341           1 :     sortedOrder[0] = tx2.GetHash().ToString(); // 20000
     342           1 :     sortedOrder[1] = tx4.GetHash().ToString(); // 15000
     343             :     // tx1 and tx5 are both 10000
     344             :     // Ties are broken by hash, not timestamp, so determine which
     345             :     // hash comes first.
     346           1 :     if (tx1.GetHash() < tx5.GetHash()) {
     347           1 :         sortedOrder[2] = tx1.GetHash().ToString();
     348           1 :         sortedOrder[3] = tx5.GetHash().ToString();
     349           1 :     } else {
     350           0 :         sortedOrder[2] = tx5.GetHash().ToString();
     351           0 :         sortedOrder[3] = tx1.GetHash().ToString();
     352             :     }
     353           1 :     sortedOrder[4] = tx3.GetHash().ToString(); // 0
     354             : 
     355           1 :     CheckSort<ancestor_score>(pool, sortedOrder);
     356             : 
     357             :     /* low fee parent with high fee child */
     358             :     /* tx6 (0) -> tx7 (high) */
     359           1 :     CMutableTransaction tx6 = CMutableTransaction();
     360           1 :     tx6.vout.resize(1);
     361           1 :     tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     362           1 :     tx6.vout[0].nValue = 20 * COIN;
     363           1 :     uint64_t tx6Size = GetVirtualTransactionSize(CTransaction(tx6));
     364             : 
     365           1 :     pool.addUnchecked(entry.Fee(0LL).FromTx(tx6));
     366           1 :     BOOST_CHECK_EQUAL(pool.size(), 6U);
     367             :     // Ties are broken by hash
     368           1 :     if (tx3.GetHash() < tx6.GetHash())
     369           0 :         sortedOrder.push_back(tx6.GetHash().ToString());
     370             :     else
     371           1 :         sortedOrder.insert(sortedOrder.end()-1,tx6.GetHash().ToString());
     372             : 
     373           1 :     CheckSort<ancestor_score>(pool, sortedOrder);
     374             : 
     375           1 :     CMutableTransaction tx7 = CMutableTransaction();
     376           1 :     tx7.vin.resize(1);
     377           1 :     tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
     378           1 :     tx7.vin[0].scriptSig = CScript() << OP_11;
     379           1 :     tx7.vout.resize(1);
     380           1 :     tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     381           1 :     tx7.vout[0].nValue = 10 * COIN;
     382           1 :     uint64_t tx7Size = GetVirtualTransactionSize(CTransaction(tx7));
     383             : 
     384             :     /* set the fee to just below tx2's feerate when including ancestor */
     385           1 :     CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1;
     386             : 
     387           1 :     pool.addUnchecked(entry.Fee(fee).FromTx(tx7));
     388           1 :     BOOST_CHECK_EQUAL(pool.size(), 7U);
     389           1 :     sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString());
     390           1 :     CheckSort<ancestor_score>(pool, sortedOrder);
     391             : 
     392             :     /* after tx6 is mined, tx7 should move up in the sort */
     393           1 :     std::vector<CTransactionRef> vtx;
     394           1 :     vtx.push_back(MakeTransactionRef(tx6));
     395           1 :     pool.removeForBlock(vtx, 1);
     396             : 
     397           1 :     sortedOrder.erase(sortedOrder.begin()+1);
     398             :     // Ties are broken by hash
     399           1 :     if (tx3.GetHash() < tx6.GetHash())
     400           0 :         sortedOrder.pop_back();
     401             :     else
     402           1 :         sortedOrder.erase(sortedOrder.end()-2);
     403           1 :     sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
     404           1 :     CheckSort<ancestor_score>(pool, sortedOrder);
     405             : 
     406             :     // High-fee parent, low-fee child
     407             :     // tx7 -> tx8
     408           1 :     CMutableTransaction tx8 = CMutableTransaction();
     409           1 :     tx8.vin.resize(1);
     410           1 :     tx8.vin[0].prevout  = COutPoint(tx7.GetHash(), 0);
     411           1 :     tx8.vin[0].scriptSig = CScript() << OP_11;
     412           1 :     tx8.vout.resize(1);
     413           1 :     tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     414           1 :     tx8.vout[0].nValue = 10*COIN;
     415             : 
     416             :     // Check that we sort by min(feerate, ancestor_feerate):
     417             :     // set the fee so that the ancestor feerate is above tx1/5,
     418             :     // but the transaction's own feerate is lower
     419           1 :     pool.addUnchecked(entry.Fee(5000LL).FromTx(tx8));
     420           1 :     sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
     421           1 :     CheckSort<ancestor_score>(pool, sortedOrder);
     422           1 : }
     423             : 
     424             : 
     425         149 : BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
     426             : {
     427           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     428           1 :     LOCK2(cs_main, pool.cs);
     429           1 :     TestMemPoolEntryHelper entry;
     430             : 
     431           1 :     CMutableTransaction tx1 = CMutableTransaction();
     432           1 :     tx1.vin.resize(1);
     433           1 :     tx1.vin[0].scriptSig = CScript() << OP_1;
     434           1 :     tx1.vout.resize(1);
     435           1 :     tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
     436           1 :     tx1.vout[0].nValue = 10 * COIN;
     437           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
     438             : 
     439           1 :     CMutableTransaction tx2 = CMutableTransaction();
     440           1 :     tx2.vin.resize(1);
     441           1 :     tx2.vin[0].scriptSig = CScript() << OP_2;
     442           1 :     tx2.vout.resize(1);
     443           1 :     tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL;
     444           1 :     tx2.vout[0].nValue = 10 * COIN;
     445           1 :     pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2));
     446             : 
     447           1 :     pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing
     448           1 :     BOOST_CHECK(pool.exists(tx1.GetHash()));
     449           1 :     BOOST_CHECK(pool.exists(tx2.GetHash()));
     450             : 
     451           1 :     pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // should remove the lower-feerate transaction
     452           1 :     BOOST_CHECK(pool.exists(tx1.GetHash()));
     453           1 :     BOOST_CHECK(!pool.exists(tx2.GetHash()));
     454             : 
     455           1 :     pool.addUnchecked(entry.FromTx(tx2));
     456           1 :     CMutableTransaction tx3 = CMutableTransaction();
     457           1 :     tx3.vin.resize(1);
     458           1 :     tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0);
     459           1 :     tx3.vin[0].scriptSig = CScript() << OP_2;
     460           1 :     tx3.vout.resize(1);
     461           1 :     tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL;
     462           1 :     tx3.vout[0].nValue = 10 * COIN;
     463           1 :     pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3));
     464             : 
     465           1 :     pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP)
     466           1 :     BOOST_CHECK(!pool.exists(tx1.GetHash()));
     467           1 :     BOOST_CHECK(pool.exists(tx2.GetHash()));
     468           1 :     BOOST_CHECK(pool.exists(tx3.GetHash()));
     469             : 
     470           1 :     pool.TrimToSize(GetVirtualTransactionSize(CTransaction(tx1))); // mempool is limited to tx1's size in memory usage, so nothing fits
     471           1 :     BOOST_CHECK(!pool.exists(tx1.GetHash()));
     472           1 :     BOOST_CHECK(!pool.exists(tx2.GetHash()));
     473           1 :     BOOST_CHECK(!pool.exists(tx3.GetHash()));
     474             : 
     475           1 :     CFeeRate maxFeeRateRemoved(25000, GetVirtualTransactionSize(CTransaction(tx3)) + GetVirtualTransactionSize(CTransaction(tx2)));
     476           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
     477             : 
     478           1 :     CMutableTransaction tx4 = CMutableTransaction();
     479           1 :     tx4.vin.resize(2);
     480           1 :     tx4.vin[0].prevout.SetNull();
     481           1 :     tx4.vin[0].scriptSig = CScript() << OP_4;
     482           1 :     tx4.vin[1].prevout.SetNull();
     483           1 :     tx4.vin[1].scriptSig = CScript() << OP_4;
     484           1 :     tx4.vout.resize(2);
     485           1 :     tx4.vout[0].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
     486           1 :     tx4.vout[0].nValue = 10 * COIN;
     487           1 :     tx4.vout[1].scriptPubKey = CScript() << OP_4 << OP_EQUAL;
     488           1 :     tx4.vout[1].nValue = 10 * COIN;
     489             : 
     490           1 :     CMutableTransaction tx5 = CMutableTransaction();
     491           1 :     tx5.vin.resize(2);
     492           1 :     tx5.vin[0].prevout = COutPoint(tx4.GetHash(), 0);
     493           1 :     tx5.vin[0].scriptSig = CScript() << OP_4;
     494           1 :     tx5.vin[1].prevout.SetNull();
     495           1 :     tx5.vin[1].scriptSig = CScript() << OP_5;
     496           1 :     tx5.vout.resize(2);
     497           1 :     tx5.vout[0].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
     498           1 :     tx5.vout[0].nValue = 10 * COIN;
     499           1 :     tx5.vout[1].scriptPubKey = CScript() << OP_5 << OP_EQUAL;
     500           1 :     tx5.vout[1].nValue = 10 * COIN;
     501             : 
     502           1 :     CMutableTransaction tx6 = CMutableTransaction();
     503           1 :     tx6.vin.resize(2);
     504           1 :     tx6.vin[0].prevout = COutPoint(tx4.GetHash(), 1);
     505           1 :     tx6.vin[0].scriptSig = CScript() << OP_4;
     506           1 :     tx6.vin[1].prevout.SetNull();
     507           1 :     tx6.vin[1].scriptSig = CScript() << OP_6;
     508           1 :     tx6.vout.resize(2);
     509           1 :     tx6.vout[0].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
     510           1 :     tx6.vout[0].nValue = 10 * COIN;
     511           1 :     tx6.vout[1].scriptPubKey = CScript() << OP_6 << OP_EQUAL;
     512           1 :     tx6.vout[1].nValue = 10 * COIN;
     513             : 
     514           1 :     CMutableTransaction tx7 = CMutableTransaction();
     515           1 :     tx7.vin.resize(2);
     516           1 :     tx7.vin[0].prevout = COutPoint(tx5.GetHash(), 0);
     517           1 :     tx7.vin[0].scriptSig = CScript() << OP_5;
     518           1 :     tx7.vin[1].prevout = COutPoint(tx6.GetHash(), 0);
     519           1 :     tx7.vin[1].scriptSig = CScript() << OP_6;
     520           1 :     tx7.vout.resize(2);
     521           1 :     tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
     522           1 :     tx7.vout[0].nValue = 10 * COIN;
     523           1 :     tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL;
     524           1 :     tx7.vout[1].nValue = 10 * COIN;
     525             : 
     526           1 :     pool.addUnchecked(entry.Fee(7000LL).FromTx(tx4));
     527           1 :     pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
     528           1 :     pool.addUnchecked(entry.Fee(1100LL).FromTx(tx6));
     529           1 :     pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
     530             : 
     531             :     // we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that
     532           1 :     pool.TrimToSize(pool.DynamicMemoryUsage() - 1);
     533           1 :     BOOST_CHECK(pool.exists(tx4.GetHash()));
     534           1 :     BOOST_CHECK(pool.exists(tx6.GetHash()));
     535           1 :     BOOST_CHECK(!pool.exists(tx7.GetHash()));
     536             : 
     537           1 :     if (!pool.exists(tx5.GetHash()))
     538           1 :         pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
     539           1 :     pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
     540             : 
     541           1 :     pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7
     542           1 :     BOOST_CHECK(pool.exists(tx4.GetHash()));
     543           1 :     BOOST_CHECK(!pool.exists(tx5.GetHash()));
     544           1 :     BOOST_CHECK(pool.exists(tx6.GetHash()));
     545           1 :     BOOST_CHECK(!pool.exists(tx7.GetHash()));
     546             : 
     547           1 :     pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5));
     548           1 :     pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7));
     549             : 
     550           1 :     std::vector<CTransactionRef> vtx;
     551           1 :     SetMockTime(42);
     552           1 :     SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
     553           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
     554             :     // ... we should keep the same min fee until we get a block
     555           1 :     pool.removeForBlock(vtx, 1);
     556           1 :     SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
     557           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
     558             :     // ... then feerate should drop 1/2 each halflife
     559             : 
     560           1 :     SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2);
     561           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0));
     562             :     // ... with a 1/2 halflife when mempool is < 1/2 its target size
     563             : 
     564           1 :     SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
     565           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0));
     566             :     // ... with a 1/4 halflife when mempool is < 1/4 its target size
     567             : 
     568           1 :     SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
     569           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 1000);
     570             :     // ... but feerate should never drop below 1000
     571             : 
     572           1 :     SetMockTime(42 + 8*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4);
     573           1 :     BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), 0);
     574             :     // ... unless it has gone all the way to 0 (after getting past 1000/2)
     575           1 : }
     576             : 
     577          14 : inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector<CTransactionRef>&& inputs=std::vector<CTransactionRef>(), std::vector<uint32_t>&& input_indices=std::vector<uint32_t>())
     578             : {
     579          14 :     CMutableTransaction tx = CMutableTransaction();
     580          14 :     tx.vin.resize(inputs.size());
     581          14 :     tx.vout.resize(output_values.size());
     582          27 :     for (size_t i = 0; i < inputs.size(); ++i) {
     583          13 :         tx.vin[i].prevout.hash = inputs[i]->GetHash();
     584          13 :         tx.vin[i].prevout.n = input_indices.size() > i ? input_indices[i] : 0;
     585          13 :     }
     586          32 :     for (size_t i = 0; i < output_values.size(); ++i) {
     587          18 :         tx.vout[i].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
     588          18 :         tx.vout[i].nValue = output_values[i];
     589          18 :     }
     590          14 :     return MakeTransactionRef(tx);
     591          14 : }
     592             : 
     593             : 
     594         149 : BOOST_AUTO_TEST_CASE(MempoolAncestryTests)
     595             : {
     596             :     size_t ancestors, descendants;
     597             : 
     598           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     599           1 :     LOCK2(cs_main, pool.cs);
     600           1 :     TestMemPoolEntryHelper entry;
     601             : 
     602             :     /* Base transaction */
     603             :     //
     604             :     // [tx1]
     605             :     //
     606           1 :     CTransactionRef tx1 = make_tx(/*output_values=*/{10 * COIN});
     607           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1));
     608             : 
     609             :     // Ancestors / descendants should be 1 / 1 (itself / itself)
     610           1 :     pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
     611           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     612           1 :     BOOST_CHECK_EQUAL(descendants, 1ULL);
     613             : 
     614             :     /* Child transaction */
     615             :     //
     616             :     // [tx1].0 <- [tx2]
     617             :     //
     618           1 :     CTransactionRef tx2 = make_tx(/*output_values=*/{495 * CENT, 5 * COIN}, /*inputs=*/{tx1});
     619           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx2));
     620             : 
     621             :     // Ancestors / descendants should be:
     622             :     // transaction  ancestors   descendants
     623             :     // ============ =========== ===========
     624             :     // tx1          1 (tx1)     2 (tx1,2)
     625             :     // tx2          2 (tx1,2)   2 (tx1,2)
     626           1 :     pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
     627           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     628           1 :     BOOST_CHECK_EQUAL(descendants, 2ULL);
     629           1 :     pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
     630           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     631           1 :     BOOST_CHECK_EQUAL(descendants, 2ULL);
     632             : 
     633             :     /* Grand-child 1 */
     634             :     //
     635             :     // [tx1].0 <- [tx2].0 <- [tx3]
     636             :     //
     637           1 :     CTransactionRef tx3 = make_tx(/*output_values=*/{290 * CENT, 200 * CENT}, /*inputs=*/{tx2});
     638           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx3));
     639             : 
     640             :     // Ancestors / descendants should be:
     641             :     // transaction  ancestors   descendants
     642             :     // ============ =========== ===========
     643             :     // tx1          1 (tx1)     3 (tx1,2,3)
     644             :     // tx2          2 (tx1,2)   3 (tx1,2,3)
     645             :     // tx3          3 (tx1,2,3) 3 (tx1,2,3)
     646           1 :     pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
     647           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     648           1 :     BOOST_CHECK_EQUAL(descendants, 3ULL);
     649           1 :     pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
     650           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     651           1 :     BOOST_CHECK_EQUAL(descendants, 3ULL);
     652           1 :     pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
     653           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     654           1 :     BOOST_CHECK_EQUAL(descendants, 3ULL);
     655             : 
     656             :     /* Grand-child 2 */
     657             :     //
     658             :     // [tx1].0 <- [tx2].0 <- [tx3]
     659             :     //              |
     660             :     //              \---1 <- [tx4]
     661             :     //
     662           1 :     CTransactionRef tx4 = make_tx(/*output_values=*/{290 * CENT, 250 * CENT}, /*inputs=*/{tx2}, /*input_indices=*/{1});
     663           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tx4));
     664             : 
     665             :     // Ancestors / descendants should be:
     666             :     // transaction  ancestors   descendants
     667             :     // ============ =========== ===========
     668             :     // tx1          1 (tx1)     4 (tx1,2,3,4)
     669             :     // tx2          2 (tx1,2)   4 (tx1,2,3,4)
     670             :     // tx3          3 (tx1,2,3) 4 (tx1,2,3,4)
     671             :     // tx4          3 (tx1,2,4) 4 (tx1,2,3,4)
     672           1 :     pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
     673           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     674           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     675           1 :     pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
     676           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     677           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     678           1 :     pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
     679           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     680           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     681           1 :     pool.GetTransactionAncestry(tx4->GetHash(), ancestors, descendants);
     682           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     683           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     684             : 
     685             :     /* Make an alternate branch that is longer and connect it to tx3 */
     686             :     //
     687             :     // [ty1].0 <- [ty2].0 <- [ty3].0 <- [ty4].0 <- [ty5].0
     688             :     //                                              |
     689             :     // [tx1].0 <- [tx2].0 <- [tx3].0 <- [ty6] --->--/
     690             :     //              |
     691             :     //              \---1 <- [tx4]
     692             :     //
     693           1 :     CTransactionRef ty1, ty2, ty3, ty4, ty5;
     694           1 :     CTransactionRef* ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5};
     695           1 :     CAmount v = 5 * COIN;
     696           6 :     for (uint64_t i = 0; i < 5; i++) {
     697           5 :         CTransactionRef& tyi = *ty[i];
     698           5 :         tyi = make_tx(/*output_values=*/{v}, /*inputs=*/i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{});
     699           5 :         v -= 50 * CENT;
     700           5 :         pool.addUnchecked(entry.Fee(10000LL).FromTx(tyi));
     701           5 :         pool.GetTransactionAncestry(tyi->GetHash(), ancestors, descendants);
     702           5 :         BOOST_CHECK_EQUAL(ancestors, i+1);
     703           5 :         BOOST_CHECK_EQUAL(descendants, i+1);
     704           5 :     }
     705           1 :     CTransactionRef ty6 = make_tx(/*output_values=*/{5 * COIN}, /*inputs=*/{tx3, ty5});
     706           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(ty6));
     707             : 
     708             :     // Ancestors / descendants should be:
     709             :     // transaction  ancestors           descendants
     710             :     // ============ =================== ===========
     711             :     // tx1          1 (tx1)             5 (tx1,2,3,4, ty6)
     712             :     // tx2          2 (tx1,2)           5 (tx1,2,3,4, ty6)
     713             :     // tx3          3 (tx1,2,3)         5 (tx1,2,3,4, ty6)
     714             :     // tx4          3 (tx1,2,4)         5 (tx1,2,3,4, ty6)
     715             :     // ty1          1 (ty1)             6 (ty1,2,3,4,5,6)
     716             :     // ty2          2 (ty1,2)           6 (ty1,2,3,4,5,6)
     717             :     // ty3          3 (ty1,2,3)         6 (ty1,2,3,4,5,6)
     718             :     // ty4          4 (y1234)           6 (ty1,2,3,4,5,6)
     719             :     // ty5          5 (y12345)          6 (ty1,2,3,4,5,6)
     720             :     // ty6          9 (tx123, ty123456) 6 (ty1,2,3,4,5,6)
     721           1 :     pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants);
     722           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     723           1 :     BOOST_CHECK_EQUAL(descendants, 5ULL);
     724           1 :     pool.GetTransactionAncestry(tx2->GetHash(), ancestors, descendants);
     725           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     726           1 :     BOOST_CHECK_EQUAL(descendants, 5ULL);
     727           1 :     pool.GetTransactionAncestry(tx3->GetHash(), ancestors, descendants);
     728           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     729           1 :     BOOST_CHECK_EQUAL(descendants, 5ULL);
     730           1 :     pool.GetTransactionAncestry(tx4->GetHash(), ancestors, descendants);
     731           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     732           1 :     BOOST_CHECK_EQUAL(descendants, 5ULL);
     733           1 :     pool.GetTransactionAncestry(ty1->GetHash(), ancestors, descendants);
     734           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     735           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     736           1 :     pool.GetTransactionAncestry(ty2->GetHash(), ancestors, descendants);
     737           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     738           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     739           1 :     pool.GetTransactionAncestry(ty3->GetHash(), ancestors, descendants);
     740           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     741           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     742           1 :     pool.GetTransactionAncestry(ty4->GetHash(), ancestors, descendants);
     743           1 :     BOOST_CHECK_EQUAL(ancestors, 4ULL);
     744           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     745           1 :     pool.GetTransactionAncestry(ty5->GetHash(), ancestors, descendants);
     746           1 :     BOOST_CHECK_EQUAL(ancestors, 5ULL);
     747           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     748           1 :     pool.GetTransactionAncestry(ty6->GetHash(), ancestors, descendants);
     749           1 :     BOOST_CHECK_EQUAL(ancestors, 9ULL);
     750           1 :     BOOST_CHECK_EQUAL(descendants, 6ULL);
     751           1 : }
     752             : 
     753         149 : BOOST_AUTO_TEST_CASE(MempoolAncestryTestsDiamond)
     754             : {
     755             :     size_t ancestors, descendants;
     756             : 
     757           1 :     CTxMemPool& pool = *Assert(m_node.mempool);
     758           1 :     LOCK2(::cs_main, pool.cs);
     759           1 :     TestMemPoolEntryHelper entry;
     760             : 
     761             :     /* Ancestors represented more than once ("diamond") */
     762             :     //
     763             :     // [ta].0 <- [tb].0 -----<------- [td].0
     764             :     //            |                    |
     765             :     //            \---1 <- [tc].0 --<--/
     766             :     //
     767           1 :     CTransactionRef ta, tb, tc, td;
     768           1 :     ta = make_tx(/*output_values=*/{10 * COIN});
     769           1 :     tb = make_tx(/*output_values=*/{5 * COIN, 3 * COIN}, /*inputs=*/ {ta});
     770           1 :     tc = make_tx(/*output_values=*/{2 * COIN}, /*inputs=*/{tb}, /*input_indices=*/{1});
     771           1 :     td = make_tx(/*output_values=*/{6 * COIN}, /*inputs=*/{tb, tc}, /*input_indices=*/{0, 0});
     772           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(ta));
     773           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tb));
     774           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(tc));
     775           1 :     pool.addUnchecked(entry.Fee(10000LL).FromTx(td));
     776             : 
     777             :     // Ancestors / descendants should be:
     778             :     // transaction  ancestors           descendants
     779             :     // ============ =================== ===========
     780             :     // ta           1 (ta               4 (ta,tb,tc,td)
     781             :     // tb           2 (ta,tb)           4 (ta,tb,tc,td)
     782             :     // tc           3 (ta,tb,tc)        4 (ta,tb,tc,td)
     783             :     // td           4 (ta,tb,tc,td)     4 (ta,tb,tc,td)
     784           1 :     pool.GetTransactionAncestry(ta->GetHash(), ancestors, descendants);
     785           1 :     BOOST_CHECK_EQUAL(ancestors, 1ULL);
     786           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     787           1 :     pool.GetTransactionAncestry(tb->GetHash(), ancestors, descendants);
     788           1 :     BOOST_CHECK_EQUAL(ancestors, 2ULL);
     789           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     790           1 :     pool.GetTransactionAncestry(tc->GetHash(), ancestors, descendants);
     791           1 :     BOOST_CHECK_EQUAL(ancestors, 3ULL);
     792           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     793           1 :     pool.GetTransactionAncestry(td->GetHash(), ancestors, descendants);
     794           1 :     BOOST_CHECK_EQUAL(ancestors, 4ULL);
     795           1 :     BOOST_CHECK_EQUAL(descendants, 4ULL);
     796           1 : }
     797             : 
     798         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16