Changeset View
Changeset View
Standalone View
Standalone View
src/test/mempool_tests.cpp
Show All 29 Lines | BOOST_AUTO_TEST_CASE(TestPackageAccounting) { | ||||
// Construct a parent for the rest of the chain | // Construct a parent for the rest of the chain | ||||
parentOfAll.vin.resize(1); | parentOfAll.vin.resize(1); | ||||
parentOfAll.vin[0].scriptSig = CScript(); | parentOfAll.vin[0].scriptSig = CScript(); | ||||
// Give us a couple outpoints so we can spend them | // Give us a couple outpoints so we can spend them | ||||
for (size_t i = 0; i < maxOutputs; i++) { | for (size_t i = 0; i < maxOutputs; i++) { | ||||
parentOfAll.vout.emplace_back(10 * SATOSHI, CScript() << OP_TRUE); | parentOfAll.vout.emplace_back(10 * SATOSHI, CScript() << OP_TRUE); | ||||
} | } | ||||
TxId parentOfAllId = parentOfAll.GetId(); | TxId parentOfAllId = parentOfAll.GetId(); | ||||
testPool.addUnchecked(parentOfAllId, | testPool.addUnchecked(entry.SigOpCount(0).FromTx(parentOfAll)); | ||||
entry.SigOpCount(0).FromTx(parentOfAll)); | |||||
// Add some outpoints to the tracking vector | // Add some outpoints to the tracking vector | ||||
for (size_t i = 0; i < maxOutputs; i++) { | for (size_t i = 0; i < maxOutputs; i++) { | ||||
outpoints.emplace_back(COutPoint(parentOfAllId, i)); | outpoints.emplace_back(COutPoint(parentOfAllId, i)); | ||||
} | } | ||||
Amount totalFee = Amount::zero(); | Amount totalFee = Amount::zero(); | ||||
size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); | size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | for (size_t totalTransactions = 0; totalTransactions < 100; | ||||
for (size_t output = tx.vout.size(); output > 0; output--) { | for (size_t output = tx.vout.size(); output > 0; output--) { | ||||
outpoints.emplace_back(COutPoint(curId, output)); | outpoints.emplace_back(COutPoint(curId, output)); | ||||
} | } | ||||
Amount randFee = int64_t(InsecureRandRange(300)) * SATOSHI; | Amount randFee = int64_t(InsecureRandRange(300)) * SATOSHI; | ||||
int randSigOpCount = InsecureRandRange(5); | int randSigOpCount = InsecureRandRange(5); | ||||
testPool.addUnchecked( | testPool.addUnchecked( | ||||
curId, entry.Fee(randFee).SigOpCount(randSigOpCount).FromTx(tx)); | entry.Fee(randFee).SigOpCount(randSigOpCount).FromTx(tx)); | ||||
// Add this transaction to the totals. | // Add this transaction to the totals. | ||||
minAncestors += 1; | minAncestors += 1; | ||||
maxAncestors += 1; | maxAncestors += 1; | ||||
minFees += randFee; | minFees += randFee; | ||||
maxFees += randFee; | maxFees += randFee; | ||||
minSize += CTransaction(tx).GetTotalSize(); | minSize += CTransaction(tx).GetTotalSize(); | ||||
maxSize += CTransaction(tx).GetTotalSize(); | maxSize += CTransaction(tx).GetTotalSize(); | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(MempoolRemoveTest) { | ||||
LOCK2(cs_main, testPool.cs); | LOCK2(cs_main, testPool.cs); | ||||
// Nothing in pool, remove should do nothing: | // Nothing in pool, remove should do nothing: | ||||
unsigned int poolSize = testPool.size(); | unsigned int poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txParent)); | testPool.removeRecursive(CTransaction(txParent)); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize); | BOOST_CHECK_EQUAL(testPool.size(), poolSize); | ||||
// Just the parent: | // Just the parent: | ||||
testPool.addUnchecked(txParent.GetId(), entry.FromTx(txParent)); | testPool.addUnchecked(entry.FromTx(txParent)); | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txParent)); | testPool.removeRecursive(CTransaction(txParent)); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1); | BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1); | ||||
// Parent, children, grandchildren: | // Parent, children, grandchildren: | ||||
testPool.addUnchecked(txParent.GetId(), entry.FromTx(txParent)); | testPool.addUnchecked(entry.FromTx(txParent)); | ||||
for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
testPool.addUnchecked(txChild[i].GetId(), entry.FromTx(txChild[i])); | testPool.addUnchecked(entry.FromTx(txChild[i])); | ||||
testPool.addUnchecked(txGrandChild[i].GetId(), | testPool.addUnchecked(entry.FromTx(txGrandChild[i])); | ||||
entry.FromTx(txGrandChild[i])); | |||||
} | } | ||||
// Remove Child[0], GrandChild[0] should be removed: | // Remove Child[0], GrandChild[0] should be removed: | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txChild[0])); | testPool.removeRecursive(CTransaction(txChild[0])); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2); | BOOST_CHECK_EQUAL(testPool.size(), poolSize - 2); | ||||
// ... make sure grandchild and child are gone: | // ... make sure grandchild and child are gone: | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txGrandChild[0])); | testPool.removeRecursive(CTransaction(txGrandChild[0])); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize); | BOOST_CHECK_EQUAL(testPool.size(), poolSize); | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txChild[0])); | testPool.removeRecursive(CTransaction(txChild[0])); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize); | BOOST_CHECK_EQUAL(testPool.size(), poolSize); | ||||
// Remove parent, all children/grandchildren should go: | // Remove parent, all children/grandchildren should go: | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txParent)); | testPool.removeRecursive(CTransaction(txParent)); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5); | BOOST_CHECK_EQUAL(testPool.size(), poolSize - 5); | ||||
BOOST_CHECK_EQUAL(testPool.size(), 0UL); | BOOST_CHECK_EQUAL(testPool.size(), 0UL); | ||||
// Add children and grandchildren, but NOT the parent (simulate the parent | // Add children and grandchildren, but NOT the parent (simulate the parent | ||||
// being in a block) | // being in a block) | ||||
for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
testPool.addUnchecked(txChild[i].GetId(), entry.FromTx(txChild[i])); | testPool.addUnchecked(entry.FromTx(txChild[i])); | ||||
testPool.addUnchecked(txGrandChild[i].GetId(), | testPool.addUnchecked(entry.FromTx(txGrandChild[i])); | ||||
entry.FromTx(txGrandChild[i])); | |||||
} | } | ||||
// Now remove the parent, as might happen if a block-re-org occurs but the | // Now remove the parent, as might happen if a block-re-org occurs but the | ||||
// parent cannot be put into the mempool (maybe because it is non-standard): | // parent cannot be put into the mempool (maybe because it is non-standard): | ||||
poolSize = testPool.size(); | poolSize = testPool.size(); | ||||
testPool.removeRecursive(CTransaction(txParent)); | testPool.removeRecursive(CTransaction(txParent)); | ||||
BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6); | BOOST_CHECK_EQUAL(testPool.size(), poolSize - 6); | ||||
BOOST_CHECK_EQUAL(testPool.size(), 0UL); | BOOST_CHECK_EQUAL(testPool.size(), 0UL); | ||||
Show All 16 Lines | BOOST_AUTO_TEST_CASE(MempoolClearTest) { | ||||
CTxMemPool testPool; | CTxMemPool testPool; | ||||
LOCK2(cs_main, testPool.cs); | LOCK2(cs_main, testPool.cs); | ||||
// Nothing in pool, clear should do nothing: | // Nothing in pool, clear should do nothing: | ||||
testPool.clear(); | testPool.clear(); | ||||
BOOST_CHECK_EQUAL(testPool.size(), 0UL); | BOOST_CHECK_EQUAL(testPool.size(), 0UL); | ||||
// Add the transaction | // Add the transaction | ||||
testPool.addUnchecked(txParent.GetId(), entry.FromTx(txParent)); | testPool.addUnchecked(entry.FromTx(txParent)); | ||||
BOOST_CHECK_EQUAL(testPool.size(), 1UL); | BOOST_CHECK_EQUAL(testPool.size(), 1UL); | ||||
BOOST_CHECK_EQUAL(testPool.mapTx.size(), 1UL); | BOOST_CHECK_EQUAL(testPool.mapTx.size(), 1UL); | ||||
BOOST_CHECK_EQUAL(testPool.mapNextTx.size(), 1UL); | BOOST_CHECK_EQUAL(testPool.mapNextTx.size(), 1UL); | ||||
BOOST_CHECK_EQUAL(testPool.vTxHashes.size(), 1UL); | BOOST_CHECK_EQUAL(testPool.vTxHashes.size(), 1UL); | ||||
// CTxMemPool's members should be empty after a clear | // CTxMemPool's members should be empty after a clear | ||||
testPool.clear(); | testPool.clear(); | ||||
BOOST_CHECK_EQUAL(testPool.size(), 0UL); | BOOST_CHECK_EQUAL(testPool.size(), 0UL); | ||||
Show All 24 Lines | BOOST_AUTO_TEST_CASE(MempoolIndexingTest) { | ||||
LOCK2(cs_main, pool.cs); | LOCK2(cs_main, pool.cs); | ||||
TestMemPoolEntryHelper entry; | TestMemPoolEntryHelper entry; | ||||
/* 3rd highest fee */ | /* 3rd highest fee */ | ||||
CMutableTransaction tx1 = CMutableTransaction(); | CMutableTransaction tx1 = CMutableTransaction(); | ||||
tx1.vout.resize(1); | tx1.vout.resize(1); | ||||
tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx1.vout[0].nValue = 10 * COIN; | tx1.vout[0].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx1.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx1)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx1)); | ||||
/* highest fee */ | /* highest fee */ | ||||
CMutableTransaction tx2 = CMutableTransaction(); | CMutableTransaction tx2 = CMutableTransaction(); | ||||
tx2.vout.resize(1); | tx2.vout.resize(1); | ||||
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx2.vout[0].nValue = 2 * COIN; | tx2.vout[0].nValue = 2 * COIN; | ||||
pool.addUnchecked(tx2.GetId(), entry.Fee(20000 * SATOSHI).FromTx(tx2)); | pool.addUnchecked(entry.Fee(20000 * SATOSHI).FromTx(tx2)); | ||||
/* lowest fee */ | /* lowest fee */ | ||||
CMutableTransaction tx3 = CMutableTransaction(); | CMutableTransaction tx3 = CMutableTransaction(); | ||||
tx3.vout.resize(1); | tx3.vout.resize(1); | ||||
tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx3.vout[0].nValue = 5 * COIN; | tx3.vout[0].nValue = 5 * COIN; | ||||
pool.addUnchecked(tx3.GetId(), entry.Fee(Amount::zero()).FromTx(tx3)); | pool.addUnchecked(entry.Fee(Amount::zero()).FromTx(tx3)); | ||||
/* 2nd highest fee */ | /* 2nd highest fee */ | ||||
CMutableTransaction tx4 = CMutableTransaction(); | CMutableTransaction tx4 = CMutableTransaction(); | ||||
tx4.vout.resize(1); | tx4.vout.resize(1); | ||||
tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx4.vout[0].nValue = 6 * COIN; | tx4.vout[0].nValue = 6 * COIN; | ||||
pool.addUnchecked(tx4.GetId(), entry.Fee(15000 * SATOSHI).FromTx(tx4)); | pool.addUnchecked(entry.Fee(15000 * SATOSHI).FromTx(tx4)); | ||||
/* equal fee rate to tx1, but newer */ | /* equal fee rate to tx1, but newer */ | ||||
CMutableTransaction tx5 = CMutableTransaction(); | CMutableTransaction tx5 = CMutableTransaction(); | ||||
tx5.vout.resize(1); | tx5.vout.resize(1); | ||||
tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx5.vout[0].nValue = 11 * COIN; | tx5.vout[0].nValue = 11 * COIN; | ||||
entry.nTime = 1; | entry.nTime = 1; | ||||
pool.addUnchecked(tx5.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx5)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx5)); | ||||
BOOST_CHECK_EQUAL(pool.size(), 5UL); | BOOST_CHECK_EQUAL(pool.size(), 5UL); | ||||
std::vector<std::string> sortedOrder; | std::vector<std::string> sortedOrder; | ||||
sortedOrder.resize(5); | sortedOrder.resize(5); | ||||
sortedOrder[0] = tx3.GetId().ToString(); // 0 | sortedOrder[0] = tx3.GetId().ToString(); // 0 | ||||
sortedOrder[1] = tx5.GetId().ToString(); // 10000 | sortedOrder[1] = tx5.GetId().ToString(); // 10000 | ||||
sortedOrder[2] = tx1.GetId().ToString(); // 10000 | sortedOrder[2] = tx1.GetId().ToString(); // 10000 | ||||
sortedOrder[3] = tx4.GetId().ToString(); // 15000 | sortedOrder[3] = tx4.GetId().ToString(); // 15000 | ||||
sortedOrder[4] = tx2.GetId().ToString(); // 20000 | sortedOrder[4] = tx2.GetId().ToString(); // 20000 | ||||
CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest1"); | CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest1"); | ||||
/* low fee but with high fee child */ | /* low fee but with high fee child */ | ||||
/* tx6 -> tx7 -> tx8, tx9 -> tx10 */ | /* tx6 -> tx7 -> tx8, tx9 -> tx10 */ | ||||
CMutableTransaction tx6 = CMutableTransaction(); | CMutableTransaction tx6 = CMutableTransaction(); | ||||
tx6.vout.resize(1); | tx6.vout.resize(1); | ||||
tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx6.vout[0].nValue = 20 * COIN; | tx6.vout[0].nValue = 20 * COIN; | ||||
pool.addUnchecked(tx6.GetId(), entry.Fee(Amount::zero()).FromTx(tx6)); | pool.addUnchecked(entry.Fee(Amount::zero()).FromTx(tx6)); | ||||
BOOST_CHECK_EQUAL(pool.size(), 6UL); | BOOST_CHECK_EQUAL(pool.size(), 6UL); | ||||
// Check that at this point, tx6 is sorted low | // Check that at this point, tx6 is sorted low | ||||
sortedOrder.insert(sortedOrder.begin(), tx6.GetId().ToString()); | sortedOrder.insert(sortedOrder.begin(), tx6.GetId().ToString()); | ||||
CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest2"); | CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest2"); | ||||
CTxMemPool::setEntries setAncestors; | CTxMemPool::setEntries setAncestors; | ||||
setAncestors.insert(pool.mapTx.find(tx6.GetId())); | setAncestors.insert(pool.mapTx.find(tx6.GetId())); | ||||
CMutableTransaction tx7 = CMutableTransaction(); | CMutableTransaction tx7 = CMutableTransaction(); | ||||
Show All 10 Lines | BOOST_AUTO_TEST_CASE(MempoolIndexingTest) { | ||||
std::string dummy; | std::string dummy; | ||||
BOOST_CHECK_EQUAL( | BOOST_CHECK_EQUAL( | ||||
pool.CalculateMemPoolAncestors(entry.Fee(2000000 * SATOSHI).FromTx(tx7), | pool.CalculateMemPoolAncestors(entry.Fee(2000000 * SATOSHI).FromTx(tx7), | ||||
setAncestorsCalculated, 100, 1000000, | setAncestorsCalculated, 100, 1000000, | ||||
1000, 1000000, dummy), | 1000, 1000000, dummy), | ||||
true); | true); | ||||
BOOST_CHECK(setAncestorsCalculated == setAncestors); | BOOST_CHECK(setAncestorsCalculated == setAncestors); | ||||
pool.addUnchecked(tx7.GetId(), entry.FromTx(tx7), setAncestors); | pool.addUnchecked(entry.FromTx(tx7), setAncestors); | ||||
BOOST_CHECK_EQUAL(pool.size(), 7UL); | BOOST_CHECK_EQUAL(pool.size(), 7UL); | ||||
// Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ... | // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ... | ||||
sortedOrder.erase(sortedOrder.begin()); | sortedOrder.erase(sortedOrder.begin()); | ||||
sortedOrder.push_back(tx6.GetId().ToString()); | sortedOrder.push_back(tx6.GetId().ToString()); | ||||
sortedOrder.push_back(tx7.GetId().ToString()); | sortedOrder.push_back(tx7.GetId().ToString()); | ||||
CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest3"); | CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest3"); | ||||
/* low fee child of tx7 */ | /* low fee child of tx7 */ | ||||
CMutableTransaction tx8 = CMutableTransaction(); | CMutableTransaction tx8 = CMutableTransaction(); | ||||
tx8.vin.resize(1); | tx8.vin.resize(1); | ||||
tx8.vin[0].prevout = COutPoint(tx7.GetId(), 0); | tx8.vin[0].prevout = COutPoint(tx7.GetId(), 0); | ||||
tx8.vin[0].scriptSig = CScript() << OP_11; | tx8.vin[0].scriptSig = CScript() << OP_11; | ||||
tx8.vout.resize(1); | tx8.vout.resize(1); | ||||
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx8.vout[0].nValue = 10 * COIN; | tx8.vout[0].nValue = 10 * COIN; | ||||
setAncestors.insert(pool.mapTx.find(tx7.GetId())); | setAncestors.insert(pool.mapTx.find(tx7.GetId())); | ||||
pool.addUnchecked(tx8.GetId(), | pool.addUnchecked(entry.Fee(Amount::zero()).Time(2).FromTx(tx8), | ||||
entry.Fee(Amount::zero()).Time(2).FromTx(tx8), | |||||
setAncestors); | setAncestors); | ||||
// Now tx8 should be sorted low, but tx6/tx both high | // Now tx8 should be sorted low, but tx6/tx both high | ||||
sortedOrder.insert(sortedOrder.begin(), tx8.GetId().ToString()); | sortedOrder.insert(sortedOrder.begin(), tx8.GetId().ToString()); | ||||
CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest4"); | CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest4"); | ||||
/* low fee child of tx7 */ | /* low fee child of tx7 */ | ||||
CMutableTransaction tx9 = CMutableTransaction(); | CMutableTransaction tx9 = CMutableTransaction(); | ||||
tx9.vin.resize(1); | tx9.vin.resize(1); | ||||
tx9.vin[0].prevout = COutPoint(tx7.GetId(), 1); | tx9.vin[0].prevout = COutPoint(tx7.GetId(), 1); | ||||
tx9.vin[0].scriptSig = CScript() << OP_11; | tx9.vin[0].scriptSig = CScript() << OP_11; | ||||
tx9.vout.resize(1); | tx9.vout.resize(1); | ||||
tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx9.vout[0].nValue = 1 * COIN; | tx9.vout[0].nValue = 1 * COIN; | ||||
pool.addUnchecked(tx9.GetId(), | pool.addUnchecked(entry.Fee(Amount::zero()).Time(3).FromTx(tx9), | ||||
entry.Fee(Amount::zero()).Time(3).FromTx(tx9), | |||||
setAncestors); | setAncestors); | ||||
// tx9 should be sorted low | // tx9 should be sorted low | ||||
BOOST_CHECK_EQUAL(pool.size(), 9UL); | BOOST_CHECK_EQUAL(pool.size(), 9UL); | ||||
sortedOrder.insert(sortedOrder.begin(), tx9.GetId().ToString()); | sortedOrder.insert(sortedOrder.begin(), tx9.GetId().ToString()); | ||||
CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest5"); | CheckSort<descendant_score>(pool, sortedOrder, "MempoolIndexingTest5"); | ||||
std::vector<std::string> snapshotOrder = sortedOrder; | std::vector<std::string> snapshotOrder = sortedOrder; | ||||
Show All 14 Lines | BOOST_AUTO_TEST_CASE(MempoolIndexingTest) { | ||||
setAncestorsCalculated.clear(); | setAncestorsCalculated.clear(); | ||||
BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors( | BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors( | ||||
entry.Fee(200000 * SATOSHI).Time(4).FromTx(tx10), | entry.Fee(200000 * SATOSHI).Time(4).FromTx(tx10), | ||||
setAncestorsCalculated, 100, 1000000, 1000, 1000000, | setAncestorsCalculated, 100, 1000000, 1000, 1000000, | ||||
dummy), | dummy), | ||||
true); | true); | ||||
BOOST_CHECK(setAncestorsCalculated == setAncestors); | BOOST_CHECK(setAncestorsCalculated == setAncestors); | ||||
pool.addUnchecked(tx10.GetId(), entry.FromTx(tx10), setAncestors); | pool.addUnchecked(entry.FromTx(tx10), setAncestors); | ||||
/** | /** | ||||
* tx8 and tx9 should both now be sorted higher | * tx8 and tx9 should both now be sorted higher | ||||
* Final order after tx10 is added: | * Final order after tx10 is added: | ||||
* | * | ||||
* tx3 = 0 (1) | * tx3 = 0 (1) | ||||
* tx5 = 10000 (1) | * tx5 = 10000 (1) | ||||
* tx1 = 10000 (1) | * tx1 = 10000 (1) | ||||
Show All 29 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) { | ||||
LOCK2(cs_main, pool.cs); | LOCK2(cs_main, pool.cs); | ||||
TestMemPoolEntryHelper entry; | TestMemPoolEntryHelper entry; | ||||
/* 3rd highest fee */ | /* 3rd highest fee */ | ||||
CMutableTransaction tx1 = CMutableTransaction(); | CMutableTransaction tx1 = CMutableTransaction(); | ||||
tx1.vout.resize(1); | tx1.vout.resize(1); | ||||
tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx1.vout[0].nValue = 10 * COIN; | tx1.vout[0].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx1.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx1)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx1)); | ||||
/* highest fee */ | /* highest fee */ | ||||
CMutableTransaction tx2 = CMutableTransaction(); | CMutableTransaction tx2 = CMutableTransaction(); | ||||
tx2.vout.resize(1); | tx2.vout.resize(1); | ||||
tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx2.vout[0].nValue = 2 * COIN; | tx2.vout[0].nValue = 2 * COIN; | ||||
pool.addUnchecked(tx2.GetId(), entry.Fee(20000 * SATOSHI).FromTx(tx2)); | pool.addUnchecked(entry.Fee(20000 * SATOSHI).FromTx(tx2)); | ||||
uint64_t tx2Size = CTransaction(tx2).GetTotalSize(); | uint64_t tx2Size = CTransaction(tx2).GetTotalSize(); | ||||
/* lowest fee */ | /* lowest fee */ | ||||
CMutableTransaction tx3 = CMutableTransaction(); | CMutableTransaction tx3 = CMutableTransaction(); | ||||
tx3.vout.resize(1); | tx3.vout.resize(1); | ||||
tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx3.vout[0].nValue = 5 * COIN; | tx3.vout[0].nValue = 5 * COIN; | ||||
pool.addUnchecked(tx3.GetId(), entry.Fee(Amount::zero()).FromTx(tx3)); | pool.addUnchecked(entry.Fee(Amount::zero()).FromTx(tx3)); | ||||
/* 2nd highest fee */ | /* 2nd highest fee */ | ||||
CMutableTransaction tx4 = CMutableTransaction(); | CMutableTransaction tx4 = CMutableTransaction(); | ||||
tx4.vout.resize(1); | tx4.vout.resize(1); | ||||
tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx4.vout[0].nValue = 6 * COIN; | tx4.vout[0].nValue = 6 * COIN; | ||||
pool.addUnchecked(tx4.GetId(), entry.Fee(15000 * SATOSHI).FromTx(tx4)); | pool.addUnchecked(entry.Fee(15000 * SATOSHI).FromTx(tx4)); | ||||
/* equal fee rate to tx1, but newer */ | /* equal fee rate to tx1, but newer */ | ||||
CMutableTransaction tx5 = CMutableTransaction(); | CMutableTransaction tx5 = CMutableTransaction(); | ||||
tx5.vout.resize(1); | tx5.vout.resize(1); | ||||
tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx5.vout[0].nValue = 11 * COIN; | tx5.vout[0].nValue = 11 * COIN; | ||||
pool.addUnchecked(tx5.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx5)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx5)); | ||||
BOOST_CHECK_EQUAL(pool.size(), 5UL); | BOOST_CHECK_EQUAL(pool.size(), 5UL); | ||||
std::vector<std::string> sortedOrder; | std::vector<std::string> sortedOrder; | ||||
sortedOrder.resize(5); | sortedOrder.resize(5); | ||||
sortedOrder[0] = tx2.GetId().ToString(); // 20000 | sortedOrder[0] = tx2.GetId().ToString(); // 20000 | ||||
sortedOrder[1] = tx4.GetId().ToString(); // 15000 | sortedOrder[1] = tx4.GetId().ToString(); // 15000 | ||||
// tx1 and tx5 are both 10000 | // tx1 and tx5 are both 10000 | ||||
// Ties are broken by hash, not timestamp, so determine which hash comes | // Ties are broken by hash, not timestamp, so determine which hash comes | ||||
Show All 13 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) { | ||||
/* low fee parent with high fee child */ | /* low fee parent with high fee child */ | ||||
/* tx6 (0) -> tx7 (high) */ | /* tx6 (0) -> tx7 (high) */ | ||||
CMutableTransaction tx6 = CMutableTransaction(); | CMutableTransaction tx6 = CMutableTransaction(); | ||||
tx6.vout.resize(1); | tx6.vout.resize(1); | ||||
tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx6.vout[0].nValue = 20 * COIN; | tx6.vout[0].nValue = 20 * COIN; | ||||
uint64_t tx6Size = CTransaction(tx6).GetTotalSize(); | uint64_t tx6Size = CTransaction(tx6).GetTotalSize(); | ||||
pool.addUnchecked(tx6.GetId(), entry.Fee(Amount::zero()).FromTx(tx6)); | pool.addUnchecked(entry.Fee(Amount::zero()).FromTx(tx6)); | ||||
BOOST_CHECK_EQUAL(pool.size(), 6UL); | BOOST_CHECK_EQUAL(pool.size(), 6UL); | ||||
// Ties are broken by hash | // Ties are broken by hash | ||||
if (tx3.GetId() < tx6.GetId()) { | if (tx3.GetId() < tx6.GetId()) { | ||||
sortedOrder.push_back(tx6.GetId().ToString()); | sortedOrder.push_back(tx6.GetId().ToString()); | ||||
} else { | } else { | ||||
sortedOrder.insert(sortedOrder.end() - 1, tx6.GetId().ToString()); | sortedOrder.insert(sortedOrder.end() - 1, tx6.GetId().ToString()); | ||||
} | } | ||||
CheckSort<ancestor_score>(pool, sortedOrder, | CheckSort<ancestor_score>(pool, sortedOrder, | ||||
"MempoolAncestorIndexingTest2"); | "MempoolAncestorIndexingTest2"); | ||||
CMutableTransaction tx7 = CMutableTransaction(); | CMutableTransaction tx7 = CMutableTransaction(); | ||||
tx7.vin.resize(1); | tx7.vin.resize(1); | ||||
tx7.vin[0].prevout = COutPoint(tx6.GetId(), 0); | tx7.vin[0].prevout = COutPoint(tx6.GetId(), 0); | ||||
tx7.vin[0].scriptSig = CScript() << OP_11; | tx7.vin[0].scriptSig = CScript() << OP_11; | ||||
tx7.vout.resize(1); | tx7.vout.resize(1); | ||||
tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx7.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx7.vout[0].nValue = 10 * COIN; | tx7.vout[0].nValue = 10 * COIN; | ||||
uint64_t tx7Size = CTransaction(tx7).GetTotalSize(); | uint64_t tx7Size = CTransaction(tx7).GetTotalSize(); | ||||
/* set the fee to just below tx2's feerate when including ancestor */ | /* set the fee to just below tx2's feerate when including ancestor */ | ||||
Amount fee = int64_t((20000 / tx2Size) * (tx7Size + tx6Size) - 1) * SATOSHI; | Amount fee = int64_t((20000 / tx2Size) * (tx7Size + tx6Size) - 1) * SATOSHI; | ||||
// CTxMemPoolEntry entry7(tx7, fee, 2, 10.0, 1, true); | // CTxMemPoolEntry entry7(tx7, fee, 2, 10.0, 1, true); | ||||
pool.addUnchecked(tx7.GetId(), entry.Fee(Amount(fee)).FromTx(tx7)); | pool.addUnchecked(entry.Fee(Amount(fee)).FromTx(tx7)); | ||||
BOOST_CHECK_EQUAL(pool.size(), 7UL); | BOOST_CHECK_EQUAL(pool.size(), 7UL); | ||||
sortedOrder.insert(sortedOrder.begin() + 1, tx7.GetId().ToString()); | sortedOrder.insert(sortedOrder.begin() + 1, tx7.GetId().ToString()); | ||||
CheckSort<ancestor_score>(pool, sortedOrder, | CheckSort<ancestor_score>(pool, sortedOrder, | ||||
"MempoolAncestorIndexingTest3"); | "MempoolAncestorIndexingTest3"); | ||||
/* after tx6 is mined, tx7 should move up in the sort */ | /* after tx6 is mined, tx7 should move up in the sort */ | ||||
std::vector<CTransactionRef> vtx; | std::vector<CTransactionRef> vtx; | ||||
vtx.push_back(MakeTransactionRef(tx6)); | vtx.push_back(MakeTransactionRef(tx6)); | ||||
Show All 18 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) { | ||||
tx8.vin[0].scriptSig = CScript() << OP_11; | tx8.vin[0].scriptSig = CScript() << OP_11; | ||||
tx8.vout.resize(1); | tx8.vout.resize(1); | ||||
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; | ||||
tx8.vout[0].nValue = 10 * COIN; | tx8.vout[0].nValue = 10 * COIN; | ||||
// Check that we sort by min(feerate, ancestor_feerate): | // Check that we sort by min(feerate, ancestor_feerate): | ||||
// set the fee so that the ancestor feerate is above tx1/5, | // set the fee so that the ancestor feerate is above tx1/5, | ||||
// but the transaction's own feerate is lower | // but the transaction's own feerate is lower | ||||
pool.addUnchecked(tx8.GetId(), | pool.addUnchecked(entry.Fee(Amount(5000 * SATOSHI)).FromTx(tx8)); | ||||
entry.Fee(Amount(5000 * SATOSHI)).FromTx(tx8)); | |||||
sortedOrder.insert(sortedOrder.end() - 1, tx8.GetId().ToString()); | sortedOrder.insert(sortedOrder.end() - 1, tx8.GetId().ToString()); | ||||
CheckSort<ancestor_score>(pool, sortedOrder, | CheckSort<ancestor_score>(pool, sortedOrder, | ||||
"MempoolAncestorIndexingTest5"); | "MempoolAncestorIndexingTest5"); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) { | BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) { | ||||
CTxMemPool pool; | CTxMemPool pool; | ||||
LOCK2(cs_main, pool.cs); | LOCK2(cs_main, pool.cs); | ||||
TestMemPoolEntryHelper entry; | TestMemPoolEntryHelper entry; | ||||
Amount feeIncrement = MEMPOOL_FULL_FEE_INCREMENT.GetFeePerK(); | Amount feeIncrement = MEMPOOL_FULL_FEE_INCREMENT.GetFeePerK(); | ||||
CMutableTransaction tx1 = CMutableTransaction(); | CMutableTransaction tx1 = CMutableTransaction(); | ||||
tx1.vin.resize(1); | tx1.vin.resize(1); | ||||
tx1.vin[0].scriptSig = CScript() << OP_1; | tx1.vin[0].scriptSig = CScript() << OP_1; | ||||
tx1.vout.resize(1); | tx1.vout.resize(1); | ||||
tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; | tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; | ||||
tx1.vout[0].nValue = 10 * COIN; | tx1.vout[0].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx1.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx1)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx1)); | ||||
CMutableTransaction tx2 = CMutableTransaction(); | CMutableTransaction tx2 = CMutableTransaction(); | ||||
tx2.vin.resize(1); | tx2.vin.resize(1); | ||||
tx2.vin[0].scriptSig = CScript() << OP_2; | tx2.vin[0].scriptSig = CScript() << OP_2; | ||||
tx2.vout.resize(1); | tx2.vout.resize(1); | ||||
tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; | tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; | ||||
tx2.vout[0].nValue = 10 * COIN; | tx2.vout[0].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx2.GetId(), entry.Fee(5000 * SATOSHI).FromTx(tx2)); | pool.addUnchecked(entry.Fee(5000 * SATOSHI).FromTx(tx2)); | ||||
// should do nothing | // should do nothing | ||||
pool.TrimToSize(pool.DynamicMemoryUsage()); | pool.TrimToSize(pool.DynamicMemoryUsage()); | ||||
BOOST_CHECK(pool.exists(tx1.GetId())); | BOOST_CHECK(pool.exists(tx1.GetId())); | ||||
BOOST_CHECK(pool.exists(tx2.GetId())); | BOOST_CHECK(pool.exists(tx2.GetId())); | ||||
// should remove the lower-feerate transaction | // should remove the lower-feerate transaction | ||||
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); | pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); | ||||
BOOST_CHECK(pool.exists(tx1.GetId())); | BOOST_CHECK(pool.exists(tx1.GetId())); | ||||
BOOST_CHECK(!pool.exists(tx2.GetId())); | BOOST_CHECK(!pool.exists(tx2.GetId())); | ||||
pool.addUnchecked(tx2.GetId(), entry.FromTx(tx2)); | pool.addUnchecked(entry.FromTx(tx2)); | ||||
CMutableTransaction tx3 = CMutableTransaction(); | CMutableTransaction tx3 = CMutableTransaction(); | ||||
tx3.vin.resize(1); | tx3.vin.resize(1); | ||||
tx3.vin[0].prevout = COutPoint(tx2.GetId(), 0); | tx3.vin[0].prevout = COutPoint(tx2.GetId(), 0); | ||||
tx3.vin[0].scriptSig = CScript() << OP_2; | tx3.vin[0].scriptSig = CScript() << OP_2; | ||||
tx3.vout.resize(1); | tx3.vout.resize(1); | ||||
tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; | tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; | ||||
tx3.vout[0].nValue = 10 * COIN; | tx3.vout[0].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx3.GetId(), entry.Fee(20000 * SATOSHI).FromTx(tx3)); | pool.addUnchecked(entry.Fee(20000 * SATOSHI).FromTx(tx3)); | ||||
// tx3 should pay for tx2 (CPFP) | // tx3 should pay for tx2 (CPFP) | ||||
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); | pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); | ||||
BOOST_CHECK(!pool.exists(tx1.GetId())); | BOOST_CHECK(!pool.exists(tx1.GetId())); | ||||
BOOST_CHECK(pool.exists(tx2.GetId())); | BOOST_CHECK(pool.exists(tx2.GetId())); | ||||
BOOST_CHECK(pool.exists(tx3.GetId())); | BOOST_CHECK(pool.exists(tx3.GetId())); | ||||
// mempool is limited to tx1's size in memory usage, so nothing fits | // mempool is limited to tx1's size in memory usage, so nothing fits | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) { | ||||
tx7.vin[1].prevout = COutPoint(tx6.GetId(), 0); | tx7.vin[1].prevout = COutPoint(tx6.GetId(), 0); | ||||
tx7.vin[1].scriptSig = CScript() << OP_6; | tx7.vin[1].scriptSig = CScript() << OP_6; | ||||
tx7.vout.resize(2); | tx7.vout.resize(2); | ||||
tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; | tx7.vout[0].scriptPubKey = CScript() << OP_7 << OP_EQUAL; | ||||
tx7.vout[0].nValue = 10 * COIN; | tx7.vout[0].nValue = 10 * COIN; | ||||
tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; | tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; | ||||
tx7.vout[1].nValue = 10 * COIN; | tx7.vout[1].nValue = 10 * COIN; | ||||
pool.addUnchecked(tx4.GetId(), entry.Fee(7000 * SATOSHI).FromTx(tx4)); | pool.addUnchecked(entry.Fee(7000 * SATOSHI).FromTx(tx4)); | ||||
pool.addUnchecked(tx5.GetId(), entry.Fee(1000 * SATOSHI).FromTx(tx5)); | pool.addUnchecked(entry.Fee(1000 * SATOSHI).FromTx(tx5)); | ||||
pool.addUnchecked(tx6.GetId(), entry.Fee(1100 * SATOSHI).FromTx(tx6)); | pool.addUnchecked(entry.Fee(1100 * SATOSHI).FromTx(tx6)); | ||||
pool.addUnchecked(tx7.GetId(), entry.Fee(9000 * SATOSHI).FromTx(tx7)); | pool.addUnchecked(entry.Fee(9000 * SATOSHI).FromTx(tx7)); | ||||
// we only require this to remove, at max, 2 txn, because it's not clear | // we only require this to remove, at max, 2 txn, because it's not clear | ||||
// what we're really optimizing for aside from that | // what we're really optimizing for aside from that | ||||
pool.TrimToSize(pool.DynamicMemoryUsage() - 1); | pool.TrimToSize(pool.DynamicMemoryUsage() - 1); | ||||
BOOST_CHECK(pool.exists(tx4.GetId())); | BOOST_CHECK(pool.exists(tx4.GetId())); | ||||
BOOST_CHECK(pool.exists(tx6.GetId())); | BOOST_CHECK(pool.exists(tx6.GetId())); | ||||
BOOST_CHECK(!pool.exists(tx7.GetId())); | BOOST_CHECK(!pool.exists(tx7.GetId())); | ||||
if (!pool.exists(tx5.GetId())) | if (!pool.exists(tx5.GetId())) | ||||
pool.addUnchecked(tx5.GetId(), entry.Fee(1000 * SATOSHI).FromTx(tx5)); | pool.addUnchecked(entry.Fee(1000 * SATOSHI).FromTx(tx5)); | ||||
pool.addUnchecked(tx7.GetId(), entry.Fee(9000 * SATOSHI).FromTx(tx7)); | pool.addUnchecked(entry.Fee(9000 * SATOSHI).FromTx(tx7)); | ||||
// should maximize mempool size by only removing 5/7 | // should maximize mempool size by only removing 5/7 | ||||
pool.TrimToSize(pool.DynamicMemoryUsage() / 2); | pool.TrimToSize(pool.DynamicMemoryUsage() / 2); | ||||
BOOST_CHECK(pool.exists(tx4.GetId())); | BOOST_CHECK(pool.exists(tx4.GetId())); | ||||
BOOST_CHECK(!pool.exists(tx5.GetId())); | BOOST_CHECK(!pool.exists(tx5.GetId())); | ||||
BOOST_CHECK(pool.exists(tx6.GetId())); | BOOST_CHECK(pool.exists(tx6.GetId())); | ||||
BOOST_CHECK(!pool.exists(tx7.GetId())); | BOOST_CHECK(!pool.exists(tx7.GetId())); | ||||
pool.addUnchecked(tx5.GetId(), entry.Fee(1000 * SATOSHI).FromTx(tx5)); | pool.addUnchecked(entry.Fee(1000 * SATOSHI).FromTx(tx5)); | ||||
pool.addUnchecked(tx7.GetId(), entry.Fee(9000 * SATOSHI).FromTx(tx7)); | pool.addUnchecked(entry.Fee(9000 * SATOSHI).FromTx(tx7)); | ||||
std::vector<CTransactionRef> vtx; | std::vector<CTransactionRef> vtx; | ||||
SetMockTime(42); | SetMockTime(42); | ||||
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); | SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE); | ||||
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), | BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), | ||||
maxFeeRateRemoved.GetFeePerK() + feeIncrement); | maxFeeRateRemoved.GetFeePerK() + feeIncrement); | ||||
// ... we should keep the same min fee until we get a block | // ... we should keep the same min fee until we get a block | ||||
pool.removeForBlock(vtx, 1); | pool.removeForBlock(vtx, 1); | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | for (auto &disconnectedTxns : | ||||
CheckDisconnectPoolOrder(disconnectPool, correctlyOrderedIds, | CheckDisconnectPoolOrder(disconnectPool, correctlyOrderedIds, | ||||
disconnectedTxns.size()); | disconnectedTxns.size()); | ||||
{ | { | ||||
LOCK2(cs_main, testPool.cs); | LOCK2(cs_main, testPool.cs); | ||||
// Add all unconfirmed transactions in testPool | // Add all unconfirmed transactions in testPool | ||||
for (auto tx : unconfTxns) { | for (auto tx : unconfTxns) { | ||||
TestMemPoolEntryHelper entry; | TestMemPoolEntryHelper entry; | ||||
testPool.addUnchecked(tx->GetId(), entry.FromTx(*tx)); | testPool.addUnchecked(entry.FromTx(*tx)); | ||||
} | } | ||||
} | } | ||||
// Now we test importMempool with a non empty mempool | // Now we test importMempool with a non empty mempool | ||||
disconnectPool.importMempool(testPool); | disconnectPool.importMempool(testPool); | ||||
CheckDisconnectPoolOrder(disconnectPool, correctlyOrderedIds, | CheckDisconnectPoolOrder(disconnectPool, correctlyOrderedIds, | ||||
disconnectedTxns.size() + | disconnectedTxns.size() + | ||||
unconfTxns.size()); | unconfTxns.size()); | ||||
Show All 37 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestryTests) { | ||||
LOCK2(cs_main, pool.cs); | LOCK2(cs_main, pool.cs); | ||||
TestMemPoolEntryHelper entry; | TestMemPoolEntryHelper entry; | ||||
/* Base transaction */ | /* Base transaction */ | ||||
// | // | ||||
// [tx1] | // [tx1] | ||||
// | // | ||||
CTransactionRef tx1 = make_tx(MK_OUTPUTS(10 * COIN)); | CTransactionRef tx1 = make_tx(MK_OUTPUTS(10 * COIN)); | ||||
pool.addUnchecked(tx1->GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx1)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx1)); | ||||
// Ancestors / descendants should be 1 / 1 (itself / itself) | // Ancestors / descendants should be 1 / 1 (itself / itself) | ||||
pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | ||||
BOOST_CHECK_EQUAL(ancestors, 1ULL); | BOOST_CHECK_EQUAL(ancestors, 1ULL); | ||||
BOOST_CHECK_EQUAL(descendants, 1ULL); | BOOST_CHECK_EQUAL(descendants, 1ULL); | ||||
/* Child transaction */ | /* Child transaction */ | ||||
// | // | ||||
// [tx1].0 <- [tx2] | // [tx1].0 <- [tx2] | ||||
// | // | ||||
CTransactionRef tx2 = | CTransactionRef tx2 = | ||||
make_tx(MK_OUTPUTS(495 * CENT, 5 * COIN), MK_INPUTS(tx1)); | make_tx(MK_OUTPUTS(495 * CENT, 5 * COIN), MK_INPUTS(tx1)); | ||||
pool.addUnchecked(tx2->GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx2)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx2)); | ||||
// Ancestors / descendants should be: | // Ancestors / descendants should be: | ||||
// transaction ancestors descendants | // transaction ancestors descendants | ||||
// ============ =========== =========== | // ============ =========== =========== | ||||
// tx1 1 (tx1) 2 (tx1,2) | // tx1 1 (tx1) 2 (tx1,2) | ||||
// tx2 2 (tx1,2) 2 (tx1,2) | // tx2 2 (tx1,2) 2 (tx1,2) | ||||
pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | ||||
BOOST_CHECK_EQUAL(ancestors, 1ULL); | BOOST_CHECK_EQUAL(ancestors, 1ULL); | ||||
BOOST_CHECK_EQUAL(descendants, 2ULL); | BOOST_CHECK_EQUAL(descendants, 2ULL); | ||||
pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants); | pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants); | ||||
BOOST_CHECK_EQUAL(ancestors, 2ULL); | BOOST_CHECK_EQUAL(ancestors, 2ULL); | ||||
BOOST_CHECK_EQUAL(descendants, 2ULL); | BOOST_CHECK_EQUAL(descendants, 2ULL); | ||||
/* Grand-child 1 */ | /* Grand-child 1 */ | ||||
// | // | ||||
// [tx1].0 <- [tx2].0 <- [tx3] | // [tx1].0 <- [tx2].0 <- [tx3] | ||||
// | // | ||||
CTransactionRef tx3 = | CTransactionRef tx3 = | ||||
make_tx(MK_OUTPUTS(290 * CENT, 200 * CENT), MK_INPUTS(tx2)); | make_tx(MK_OUTPUTS(290 * CENT, 200 * CENT), MK_INPUTS(tx2)); | ||||
pool.addUnchecked(tx3->GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx3)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx3)); | ||||
// Ancestors / descendants should be: | // Ancestors / descendants should be: | ||||
// transaction ancestors descendants | // transaction ancestors descendants | ||||
// ============ =========== =========== | // ============ =========== =========== | ||||
// tx1 1 (tx1) 3 (tx1,2,3) | // tx1 1 (tx1) 3 (tx1,2,3) | ||||
// tx2 2 (tx1,2) 3 (tx1,2,3) | // tx2 2 (tx1,2) 3 (tx1,2,3) | ||||
// tx3 3 (tx1,2,3) 3 (tx1,2,3) | // tx3 3 (tx1,2,3) 3 (tx1,2,3) | ||||
pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants); | ||||
Show All 9 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestryTests) { | ||||
/* Grand-child 2 */ | /* Grand-child 2 */ | ||||
// | // | ||||
// [tx1].0 <- [tx2].0 <- [tx3] | // [tx1].0 <- [tx2].0 <- [tx3] | ||||
// | | // | | ||||
// \---1 <- [tx4] | // \---1 <- [tx4] | ||||
// | // | ||||
CTransactionRef tx4 = make_tx(MK_OUTPUTS(290 * CENT, 250 * CENT), | CTransactionRef tx4 = make_tx(MK_OUTPUTS(290 * CENT, 250 * CENT), | ||||
MK_INPUTS(tx2), MK_INPUT_IDX(1)); | MK_INPUTS(tx2), MK_INPUT_IDX(1)); | ||||
pool.addUnchecked(tx4->GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx4)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx4)); | ||||
// Ancestors / descendants should be: | // Ancestors / descendants should be: | ||||
// transaction ancestors descendants | // transaction ancestors descendants | ||||
// ============ =========== =========== | // ============ =========== =========== | ||||
// tx1 1 (tx1) 4 (tx1,2,3,4) | // tx1 1 (tx1) 4 (tx1,2,3,4) | ||||
// tx2 2 (tx1,2) 4 (tx1,2,3,4) | // tx2 2 (tx1,2) 4 (tx1,2,3,4) | ||||
// tx3 3 (tx1,2,3) 4 (tx1,2,3,4) | // tx3 3 (tx1,2,3) 4 (tx1,2,3,4) | ||||
// tx4 3 (tx1,2,4) 4 (tx1,2,3,4) | // tx4 3 (tx1,2,4) 4 (tx1,2,3,4) | ||||
Show All 21 Lines | BOOST_AUTO_TEST_CASE(MempoolAncestryTests) { | ||||
CTransactionRef ty1, ty2, ty3, ty4, ty5; | CTransactionRef ty1, ty2, ty3, ty4, ty5; | ||||
CTransactionRef *ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5}; | CTransactionRef *ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5}; | ||||
Amount v = 5 * COIN; | Amount v = 5 * COIN; | ||||
for (uint64_t i = 0; i < 5; i++) { | for (uint64_t i = 0; i < 5; i++) { | ||||
CTransactionRef &tyi = *ty[i]; | CTransactionRef &tyi = *ty[i]; | ||||
tyi = make_tx(MK_OUTPUTS(v), i > 0 ? MK_INPUTS(*ty[i - 1]) | tyi = make_tx(MK_OUTPUTS(v), i > 0 ? MK_INPUTS(*ty[i - 1]) | ||||
: std::vector<CTransactionRef>()); | : std::vector<CTransactionRef>()); | ||||
v -= 50 * CENT; | v -= 50 * CENT; | ||||
pool.addUnchecked(tyi->GetId(), entry.Fee(10000 * SATOSHI).FromTx(tyi)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tyi)); | ||||
pool.GetTransactionAncestry(tyi->GetId(), ancestors, descendants); | pool.GetTransactionAncestry(tyi->GetId(), ancestors, descendants); | ||||
BOOST_CHECK_EQUAL(ancestors, i + 1); | BOOST_CHECK_EQUAL(ancestors, i + 1); | ||||
BOOST_CHECK_EQUAL(descendants, i + 1); | BOOST_CHECK_EQUAL(descendants, i + 1); | ||||
} | } | ||||
CTransactionRef ty6 = make_tx(MK_OUTPUTS(5 * COIN), MK_INPUTS(tx3, ty5)); | CTransactionRef ty6 = make_tx(MK_OUTPUTS(5 * COIN), MK_INPUTS(tx3, ty5)); | ||||
pool.addUnchecked(ty6->GetId(), entry.Fee(10000 * SATOSHI).FromTx(ty6)); | pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(ty6)); | ||||
// Ancestors / descendants should be: | // Ancestors / descendants should be: | ||||
// transaction ancestors descendants | // transaction ancestors descendants | ||||
// ============ =================== =========== | // ============ =================== =========== | ||||
// tx1 1 (tx1) 5 (tx1,2,3,4, ty6) | // tx1 1 (tx1) 5 (tx1,2,3,4, ty6) | ||||
// tx2 2 (tx1,2) 5 (tx1,2,3,4, ty6) | // tx2 2 (tx1,2) 5 (tx1,2,3,4, ty6) | ||||
// tx3 3 (tx1,2,3) 5 (tx1,2,3,4, ty6) | // tx3 3 (tx1,2,3) 5 (tx1,2,3,4, ty6) | ||||
// tx4 3 (tx1,2,4) 5 (tx1,2,3,4, ty6) | // tx4 3 (tx1,2,4) 5 (tx1,2,3,4, ty6) | ||||
Show All 39 Lines |