diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -8,12 +8,124 @@ #include "test/test_bitcoin.h" +#include #include #include #include BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup) +BOOST_AUTO_TEST_CASE(TestPackageAccounting) { + CTxMemPool testPool; + TestMemPoolEntryHelper entry; + CMutableTransaction parentOfAll; + + std::vector outpoints; + const size_t maxOutputs = 3; + + // Construct a parent for the rest of the chain + parentOfAll.vin.resize(1); + parentOfAll.vin[0].scriptSig = CScript(); + // Give us a couple outpoints so we can spend them + for (size_t i = 0; i < maxOutputs; i++) { + parentOfAll.vout.emplace_back(10 * SATOSHI, CScript() << OP_TRUE); + } + TxId parentOfAllId = parentOfAll.GetId(); + testPool.addUnchecked(parentOfAllId, entry.FromTx(parentOfAll)); + + // Add some outpoints to the tracking vector + for (size_t i = 0; i < maxOutputs; i++) { + outpoints.emplace_back(COutPoint(parentOfAllId, i)); + } + + Amount totalFee = Amount::zero(); + size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); + size_t totalTransactions = 1; + + // Generate 100 transactions + while (totalTransactions < 100) { + CMutableTransaction mtx; + totalTransactions++; + + uint64_t minAncestors = std::numeric_limits::max(); + uint64_t maxAncestors = 0; + Amount minFees = MAX_MONEY; + Amount maxFees = Amount::zero(); + uint64_t minSize = std::numeric_limits::max(); + uint64_t maxSize = 0; + // Consume random inputs, but make sure we don't consume more than + // available + for (size_t input = std::min(InsecureRandRange(maxOutputs) + 1, + uint64_t(outpoints.size())); + input > 0; input--) { + std::swap(outpoints[InsecureRandRange(outpoints.size())], + outpoints.back()); + mtx.vin.emplace_back(outpoints.back()); + outpoints.pop_back(); + + // We don't know exactly how many ancestors this transaction has + // due to possible duplicates. Calculate a valid range based on + // parents. + + CTxMemPoolEntry parent = + *testPool.mapTx.find(mtx.vin.back().prevout.GetTxId()); + + minAncestors = + std::min(minAncestors, parent.GetCountWithAncestors()); + maxAncestors += parent.GetCountWithAncestors(); + minFees = std::min(minFees, parent.GetModFeesWithAncestors()); + maxFees += parent.GetModFeesWithAncestors(); + minSize = std::min(minSize, parent.GetSizeWithAncestors()); + maxSize += parent.GetSizeWithAncestors(); + } + + // Produce random number of outputs + for (size_t output = InsecureRandRange(maxOutputs) + 1; output > 0; + output--) { + mtx.vout.emplace_back(10 * SATOSHI, CScript() << OP_TRUE); + } + + CTransaction tx(mtx); + TxId curId = tx.GetId(); + + // Record the outputs + for (size_t output = tx.vout.size(); output > 0; output--) { + outpoints.emplace_back(COutPoint(curId, output)); + } + + testPool.addUnchecked(curId, entry.Fee(1 * SATOSHI).FromTx(tx)); + + // Add this transaction to the totals. + minAncestors += 1; + maxAncestors += 1; + minFees += 1 * SATOSHI; + maxFees += 1 * SATOSHI; + minSize += CTransaction(tx).GetTotalSize(); + maxSize += CTransaction(tx).GetTotalSize(); + + // Calculate overall values + totalFee += 1 * SATOSHI; + totalSize += CTransaction(tx).GetTotalSize(); + CTxMemPoolEntry parentEntry = *testPool.mapTx.find(parentOfAllId); + CTxMemPoolEntry latestEntry = *testPool.mapTx.find(curId); + + // Ensure values are within the expected ranges + BOOST_CHECK(latestEntry.GetCountWithAncestors() >= minAncestors); + BOOST_CHECK(latestEntry.GetCountWithAncestors() <= maxAncestors); + + BOOST_CHECK(latestEntry.GetSizeWithAncestors() >= minSize); + BOOST_CHECK(latestEntry.GetSizeWithAncestors() <= maxSize); + + BOOST_CHECK(latestEntry.GetModFeesWithAncestors() >= minFees); + BOOST_CHECK(latestEntry.GetModFeesWithAncestors() <= maxFees); + + BOOST_CHECK_EQUAL(parentEntry.GetCountWithDescendants(), + testPool.mapTx.size()); + BOOST_CHECK_EQUAL(parentEntry.GetSizeWithDescendants(), totalSize); + BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(), totalFee); + } +} + BOOST_AUTO_TEST_CASE(MempoolRemoveTest) { // Test CTxMemPool::remove functionality