LCOV - code coverage report
Current view: top level - src/test - mempool_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 511 515 99.2 %
Date: 2020-09-26 01:30:44 Functions: 40 40 100.0 %

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

Generated by: LCOV version 1.15