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