diff --git a/doc/release-notes.md b/doc/release-notes.md
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -4,4 +4,7 @@
-This is a maintenance release with no user-visible change.
+This release includes the following features and fixes:
+ - The `ancestorcount`, `ancestorsize` and `ancestorfees` fields from the
+ `listunspent` RPC have been removed. They were deprecated since the v0.27.0
+ and are no longer computed since the May 15th, 2023 eCash network upgrade.
diff --git a/src/bench/chained_tx.cpp b/src/bench/chained_tx.cpp
--- a/src/bench/chained_tx.cpp
+++ b/src/bench/chained_tx.cpp
@@ -131,22 +131,12 @@
static void benchATMP(const Config &config, node::NodeContext &node,
benchmark::Bench &bench,
const std::vector chainedTxs) {
- // ATMP uses gArgs, so we oblige
- gArgs.ForceSetArg("-limitdescendantcount", ToString(chainedTxs.size()));
- gArgs.ForceSetArg("-limitancestorcount", ToString(chainedTxs.size()));
- gArgs.ForceSetArg("-limitancestorsize", ToString(chainedTxs.size() * 1000));
- gArgs.ForceSetArg("-limitdescendantsize",
- ToString(chainedTxs.size() * 1000));
-
auto chainman = Assert(node.chainman.get());
Chainstate &activeChainState = chainman->ActiveChainstate();
CTxMemPool &mempool{*Assert(activeChainState.GetMempool())};
assert(mempool.size() == 0);
- // test with wellington latched (faster)
- const bool wellingtonBefore = mempool.wellingtonLatched.exchange(true);
-
bench.run([&] {
LOCK(::cs_main);
for (const auto &tx : chainedTxs) {
@@ -158,14 +148,6 @@
}
mempool.clear();
});
-
- // restore state
- mempool.wellingtonLatched = wellingtonBefore;
-
- gArgs.ClearForcedArg("-limitdescendantcount");
- gArgs.ClearForcedArg("-limitancestorcount");
- gArgs.ClearForcedArg("-limitancestorsize");
- gArgs.ClearForcedArg("-limitdescendantsize");
}
/// Run benchmark that reorganizes blocks with one-input-one-output transaction
@@ -195,9 +177,6 @@
CTxMemPool &mempool{*Assert(activeChainState.GetMempool())};
assert(mempool.size() == 0);
- // test with wellington latched (faster)
- const bool wellingtonBefore = mempool.wellingtonLatched.exchange(true);
-
// Build blocks
TestMemPoolEntryHelper entry;
entry.nFee = 1337 * SATOSHI;
@@ -225,14 +204,6 @@
}
CBlockIndex *mostWorkTip = activeChainState.m_chain.Tip();
- // `AcceptToMemoryPool` is used during re-org, so we need to ajust its
- // limits.
- gArgs.ForceSetArg("-limitdescendantcount", ToString(chainSizePerBlock));
- gArgs.ForceSetArg("-limitancestorcount", ToString(chainSizePerBlock));
- gArgs.ForceSetArg("-limitancestorsize", ToString(chainSizePerBlock * 1000));
- gArgs.ForceSetArg("-limitdescendantsize",
- ToString(chainSizePerBlock * 1000));
-
bench.run([&] {
BlockValidationState state;
@@ -265,14 +236,6 @@
assert(activeChainState.m_chain.Tip() == mostWorkTip);
assert(mempool.size() == 0);
});
-
- // restore state
- mempool.wellingtonLatched = wellingtonBefore;
-
- gArgs.ClearForcedArg("-limitdescendantcount");
- gArgs.ClearForcedArg("-limitancestorcount");
- gArgs.ClearForcedArg("-limitancestorsize");
- gArgs.ClearForcedArg("-limitdescendantsize");
}
static void
@@ -286,9 +249,6 @@
Chainstate &activeChainState = chainman->ActiveChainstate();
CTxMemPool &mempool{*Assert(activeChainState.GetMempool())};
- // test with wellington latched (faster)
- const bool wellingtonBefore = mempool.wellingtonLatched.exchange(true);
-
// Fill mempool
size_t txCount = 0;
for (const auto &chain : chains) {
@@ -313,9 +273,6 @@
// +1 for coinbase
assert(blocktemplate->block.vtx.size() == txCount + 1);
});
-
- // restore state
- mempool.wellingtonLatched = wellingtonBefore;
}
static void
@@ -334,8 +291,6 @@
++i) {
pools.emplace_back();
CTxMemPool &pool = pools.back();
- // test with wellington latched (faster)
- pool.wellingtonLatched = true;
TestMemPoolEntryHelper entry;
// Fill mempool
size_t txCount = 0;
diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h
--- a/src/interfaces/chain.h
+++ b/src/interfaces/chain.h
@@ -205,22 +205,12 @@
const Amount &max_tx_fee, bool relay,
std::string &err_string) = 0;
- //! Calculate mempool ancestor and descendant counts for the given
- //! transaction.
- virtual void getTransactionAncestry(const TxId &txid, size_t &ancestors,
- size_t &descendants,
- size_t *ancestorsize = nullptr,
- Amount *ancestorfees = nullptr) = 0;
-
//! Get the node's package limits.
//! Currently only returns the ancestor and descendant count limits, but
//! could be enhanced to return more policy settings.
virtual void getPackageLimits(size_t &limit_ancestor_count,
size_t &limit_descendant_count) = 0;
- //! Check if transaction will pass the mempool's chain limits.
- virtual bool checkChainLimits(const CTransactionRef &tx) = 0;
-
//! Estimate fee
virtual CFeeRate estimateFee() const = 0;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -623,17 +623,6 @@
// to know about.
return err == TransactionError::OK;
}
- void getTransactionAncestry(const TxId &txid, size_t &ancestors,
- size_t &descendants, size_t *ancestorsize,
- Amount *ancestorfees) override {
- ancestors = descendants = 0;
- // After wellington this stat will no longer exist
- if (!m_node.mempool || m_node.mempool->wellingtonLatched) {
- return;
- }
- m_node.mempool->GetTransactionAncestry(txid, ancestors, descendants,
- ancestorsize, ancestorfees);
- }
void getPackageLimits(size_t &limit_ancestor_count,
size_t &limit_descendant_count) override {
limit_ancestor_count = size_t(
@@ -643,33 +632,6 @@
1, gArgs.GetIntArg("-limitdescendantcount",
DEFAULT_DESCENDANT_LIMIT)));
}
- bool checkChainLimits(const CTransactionRef &tx) override {
- // After wellington this limitation will no longer exist
- if (!m_node.mempool || m_node.mempool->wellingtonLatched) {
- return true;
- }
- LockPoints lp;
- CTxMemPoolEntry entry(tx, Amount(), 0, 0, false, 0, lp);
- CTxMemPool::setEntries ancestors;
- auto limit_ancestor_count =
- gArgs.GetIntArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
- auto limit_ancestor_size =
- gArgs.GetIntArg("-limitancestorsize",
- DEFAULT_ANCESTOR_SIZE_LIMIT) *
- 1000;
- auto limit_descendant_count = gArgs.GetIntArg(
- "-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
- auto limit_descendant_size =
- gArgs.GetIntArg("-limitdescendantsize",
- DEFAULT_DESCENDANT_SIZE_LIMIT) *
- 1000;
- std::string unused_error_string;
- LOCK(m_node.mempool->cs);
- return m_node.mempool->CalculateMemPoolAncestors(
- entry, ancestors, limit_ancestor_count, limit_ancestor_size,
- limit_descendant_count, limit_descendant_size,
- unused_error_string);
- }
CFeeRate estimateFee() const override {
if (!m_node.mempool) {
return {};
diff --git a/src/test/activation_tests.cpp b/src/test/activation_tests.cpp
--- a/src/test/activation_tests.cpp
+++ b/src/test/activation_tests.cpp
@@ -77,54 +77,4 @@
BOOST_CHECK(IsWellingtonEnabled(params, activation + 1));
}
-BOOST_FIXTURE_TEST_CASE(wellington_latch, RegTestingSetup) {
- const Consensus::Params ¶ms = Params().GetConsensus();
- const auto activation = gArgs.GetIntArg("-wellingtonactivationtime",
- params.wellingtonActivationTime);
- SetMockTime(activation - 1000000);
-
- const Config &config = GetConfig();
- auto &mempool = *Assert(m_node.mempool);
- auto &chainman = *Assert(m_node.chainman);
- auto &activeChainstate = chainman.ActiveChainstate();
-
- std::array blocks;
- for (size_t i = 1; i < blocks.size() - 1; ++i) {
- blocks[i].pprev = &blocks[i - 1];
- }
- // Hack: we need to call MineBlock to avoid null pointer dereference in
- // ATMP. Let's make sure it's our tip so SetMTP works as expected.
- MineBlock(config, m_node, CScript() << OP_TRUE);
- blocks[11] = *activeChainstate.m_chain.Tip();
- blocks[11].pprev = &blocks[10];
- activeChainstate.m_chain.SetTip(&blocks[11]);
-
- auto submitTxToMemPool = [&config, &activeChainstate]() {
- LOCK(cs_main);
- return AcceptToMemoryPool(config, activeChainstate,
- MakeTransactionRef(),
- /*accept_time=*/0,
- /*bypass_limits=*/false);
- };
-
- BOOST_CHECK(!IsWellingtonEnabled(params, &blocks.back()));
- submitTxToMemPool();
- BOOST_CHECK(!mempool.wellingtonLatched);
-
- // Activate Wellington
- SetMTP(blocks, activation);
- BOOST_CHECK(IsWellingtonEnabled(params, &blocks.back()));
-
- // Upon first tx submission, the flag is latched
- BOOST_CHECK(!mempool.wellingtonLatched);
- submitTxToMemPool();
- BOOST_CHECK(mempool.wellingtonLatched);
-
- // Simulate a reorg and check the flag did latch
- SetMTP(blocks, activation - 1);
- BOOST_CHECK(mempool.wellingtonLatched);
- submitTxToMemPool();
- BOOST_CHECK(mempool.wellingtonLatched);
-}
-
BOOST_AUTO_TEST_SUITE_END()
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
@@ -18,212 +18,6 @@
BOOST_FIXTURE_TEST_SUITE(mempool_tests, TestingSetup)
-// this test should be removed after wellington is checkpointed
-BOOST_AUTO_TEST_CASE(TestPreAndPostWellingtonAccounting) {
- for (const bool wellington : {false, true}) {
- CTxMemPool testPool;
- // test pre-wellington versus post-wellington behavior
- testPool.wellingtonLatched = wellington;
- LOCK2(cs_main, testPool.cs);
- TestMemPoolEntryHelper entry;
- CMutableTransaction parentOfAll;
-
- // Vector to track unspent outputs that are used to construct txs
- 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(entry.SigChecks(0).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 totalVirtualSize = totalSize;
- int64_t totalSigChecks = 0;
-
- // Generate 100 transactions
- for (size_t totalTransactions = 0; totalTransactions < 100;
- totalTransactions++) {
- CMutableTransaction tx;
-
- 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;
- uint64_t minVirtualSize = std::numeric_limits::max();
- uint64_t maxVirtualSize = 0;
- int64_t minSigChecks = std::numeric_limits::max();
- int64_t maxSigChecks = 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());
- tx.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.
-
- const CTxMemPoolEntry &parent =
- *testPool.mapTx.find(tx.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();
- minVirtualSize =
- std::min(minSize, parent.GetVirtualSizeWithAncestors());
- maxVirtualSize += parent.GetVirtualSizeWithAncestors();
- minSigChecks =
- std::min(minSigChecks, parent.GetSigChecksWithAncestors());
- maxSigChecks += parent.GetSigChecksWithAncestors();
- }
-
- // Produce random number of outputs
- for (size_t output = InsecureRandRange(maxOutputs) + 1; output > 0;
- output--) {
- tx.vout.emplace_back(10 * SATOSHI, CScript() << OP_TRUE);
- }
-
- TxId curId = tx.GetId();
-
- // Record the outputs
- for (size_t output = tx.vout.size(); output > 0; output--) {
- outpoints.emplace_back(COutPoint(curId, output));
- }
-
- const Amount randFee = int64_t(InsecureRandRange(300)) * SATOSHI;
- const int randSigChecks = InsecureRandRange(5);
-
- testPool.addUnchecked(
- entry.Fee(randFee).SigChecks(randSigChecks).FromTx(tx));
-
- // Add this transaction to the totals.
- minAncestors += 1;
- maxAncestors += 1;
- minFees += randFee;
- maxFees += randFee;
- minSize += CTransaction(tx).GetTotalSize();
- maxSize += CTransaction(tx).GetTotalSize();
- // virtualsize is a nonlinear function of its arguments, so we can't
- // make as strong guarantees about its range; but assuming
- // virtualsize is monotonically increasing in each argument, we can
- // say the following:
- minVirtualSize += 0;
- maxVirtualSize += GetVirtualTransactionSize(
- CTransaction(tx).GetTotalSize(), randSigChecks);
- minSigChecks += randSigChecks;
- maxSigChecks += randSigChecks;
-
- // Calculate overall values
- totalFee += randFee;
- totalSize += CTransaction(tx).GetTotalSize();
- totalVirtualSize += GetVirtualTransactionSize(
- CTransaction(tx).GetTotalSize(), randSigChecks);
- totalSigChecks += randSigChecks;
- const CTxMemPoolEntry &parentEntry =
- *testPool.mapTx.find(parentOfAllId);
- const CTxMemPoolEntry &latestEntry = *testPool.mapTx.find(curId);
-
- // Based on size/sigChecks ranges we can compute more strict bounds
- // for the virtual size ranges/totals, assuming virtualsize is
- // monotonic in each argument.
- uint64_t minVirtualSize_strict =
- GetVirtualTransactionSize(minSize, minSigChecks);
- uint64_t maxVirtualSize_strict =
- GetVirtualTransactionSize(maxSize, maxSigChecks);
- uint64_t totalVirtualSize_strict =
- GetVirtualTransactionSize(totalSize, totalSigChecks);
-
- if (!wellington) {
- // these are as-good or better than the earlier estimations.
- BOOST_CHECK(minVirtualSize_strict >= minVirtualSize);
- BOOST_CHECK(maxVirtualSize_strict <= maxVirtualSize);
- BOOST_CHECK(totalVirtualSize_strict <= totalVirtualSize);
-
- // 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.GetVirtualSizeWithAncestors() >=
- minVirtualSize_strict);
- BOOST_CHECK(latestEntry.GetVirtualSizeWithAncestors() <=
- maxVirtualSize_strict);
-
- BOOST_CHECK(latestEntry.GetSigChecksWithAncestors() >=
- minSigChecks);
- BOOST_CHECK(latestEntry.GetSigChecksWithAncestors() <=
- maxSigChecks);
-
- 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.GetVirtualSizeWithDescendants(),
- totalVirtualSize_strict);
- BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(),
- totalFee);
- BOOST_CHECK_EQUAL(parentEntry.GetSigChecksWithDescendants(),
- totalSigChecks);
- } else {
- // with wellington latched, we stop tracking these -- they stay
- // at their defaults
- BOOST_CHECK_EQUAL(latestEntry.GetCountWithAncestors(), 1);
- BOOST_CHECK_EQUAL(latestEntry.GetSizeWithAncestors(),
- latestEntry.GetTxSize());
- BOOST_CHECK_EQUAL(latestEntry.GetVirtualSizeWithAncestors(),
- latestEntry.GetTxVirtualSize());
- BOOST_CHECK_EQUAL(latestEntry.GetSigChecksWithAncestors(),
- latestEntry.GetSigChecks());
- BOOST_CHECK_EQUAL(latestEntry.GetModFeesWithAncestors(),
- latestEntry.GetModifiedFee());
-
- BOOST_CHECK_EQUAL(parentEntry.GetCountWithDescendants(), 1);
- BOOST_CHECK_EQUAL(parentEntry.GetSizeWithDescendants(),
- parentEntry.GetTxSize());
- BOOST_CHECK_EQUAL(parentEntry.GetVirtualSizeWithDescendants(),
- parentEntry.GetTxVirtualSize());
- BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(),
- parentEntry.GetModifiedFee());
- BOOST_CHECK_EQUAL(parentEntry.GetSigChecks(), 0);
- BOOST_CHECK_EQUAL(parentEntry.GetSigChecksWithDescendants(), 0);
- }
- // Verify that wellington activation status didn't accidentally
- // change during the test.
- BOOST_CHECK_EQUAL(testPool.wellingtonLatched, wellington);
- }
- }
-}
-
static constexpr auto REMOVAL_REASON_DUMMY = MemPoolRemovalReason::REPLACED;
BOOST_AUTO_TEST_CASE(MempoolRemoveTest) {
@@ -706,211 +500,6 @@
return MakeTransactionRef(tx);
}
-BOOST_AUTO_TEST_CASE(MempoolAncestryTests) {
- size_t ancestors, descendants;
-
- CTxMemPool pool;
- LOCK2(cs_main, pool.cs);
- TestMemPoolEntryHelper entry;
-
- /* Base transaction */
- //
- // [tx1]
- //
- CTransactionRef tx1 = make_tx(/* output_values */ {10 * COIN});
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx1));
-
- // Ancestors / descendants should be 1 / 1 (itself / itself)
- pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 1ULL);
-
- /* Child transaction */
- //
- // [tx1].0 <- [tx2]
- //
- CTransactionRef tx2 =
- make_tx(/* output_values */ {495 * CENT, 5 * COIN}, /* inputs */ {tx1});
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx2));
-
- // Ancestors / descendants should be:
- // transaction ancestors descendants
- // ============ =========== ===========
- // tx1 1 (tx1) 2 (tx1,2)
- // tx2 2 (tx1,2) 2 (tx1,2)
- pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 2ULL);
- pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 2ULL);
-
- /* Grand-child 1 */
- //
- // [tx1].0 <- [tx2].0 <- [tx3]
- //
- CTransactionRef tx3 = make_tx(/* output_values */ {290 * CENT, 200 * CENT},
- /* inputs */ {tx2});
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx3));
-
- // Ancestors / descendants should be:
- // transaction ancestors descendants
- // ============ =========== ===========
- // tx1 1 (tx1) 3 (tx1,2,3)
- // tx2 2 (tx1,2) 3 (tx1,2,3)
- // tx3 3 (tx1,2,3) 3 (tx1,2,3)
- pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 3ULL);
- pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 3ULL);
- pool.GetTransactionAncestry(tx3->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 3ULL);
-
- /* Grand-child 2 */
- //
- // [tx1].0 <- [tx2].0 <- [tx3]
- // |
- // \---1 <- [tx4]
- //
- CTransactionRef tx4 = make_tx(/* output_values */ {290 * CENT, 250 * CENT},
- /* inputs */ {tx2}, /* input_indices */ {1});
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tx4));
-
- // Ancestors / descendants should be:
- // transaction ancestors descendants
- // ============ =========== ===========
- // tx1 1 (tx1) 4 (tx1,2,3,4)
- // tx2 2 (tx1,2) 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)
- pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(tx3->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(tx4->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
-
- /* Make an alternate branch that is longer and connect it to tx3 */
- //
- // [ty1].0 <- [ty2].0 <- [ty3].0 <- [ty4].0 <- [ty5].0
- // |
- // [tx1].0 <- [tx2].0 <- [tx3].0 <- [ty6] --->--/
- // |
- // \---1 <- [tx4]
- //
- CTransactionRef ty1, ty2, ty3, ty4, ty5;
- CTransactionRef *ty[5] = {&ty1, &ty2, &ty3, &ty4, &ty5};
- Amount v = 5 * COIN;
- for (uint64_t i = 0; i < 5; i++) {
- CTransactionRef &tyi = *ty[i];
- tyi = make_tx(/* output_values */ {v},
- /* inputs */ i > 0
- ? std::vector{*ty[i - 1]}
- : std::vector{});
- v -= 50 * CENT;
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tyi));
- pool.GetTransactionAncestry(tyi->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, i + 1);
- BOOST_CHECK_EQUAL(descendants, i + 1);
- }
- CTransactionRef ty6 =
- make_tx(/* output_values */ {5 * COIN}, /* inputs */ {tx3, ty5});
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(ty6));
-
- // Ancestors / descendants should be:
- // transaction ancestors descendants
- // ============ =================== ===========
- // tx1 1 (tx1) 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)
- // tx4 3 (tx1,2,4) 5 (tx1,2,3,4, ty6)
- // ty1 1 (ty1) 6 (ty1,2,3,4,5,6)
- // ty2 2 (ty1,2) 6 (ty1,2,3,4,5,6)
- // ty3 3 (ty1,2,3) 6 (ty1,2,3,4,5,6)
- // ty4 4 (y1234) 6 (ty1,2,3,4,5,6)
- // ty5 5 (y12345) 6 (ty1,2,3,4,5,6)
- // ty6 9 (tx123, ty123456) 6 (ty1,2,3,4,5,6)
- pool.GetTransactionAncestry(tx1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 5ULL);
- pool.GetTransactionAncestry(tx2->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 5ULL);
- pool.GetTransactionAncestry(tx3->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 5ULL);
- pool.GetTransactionAncestry(tx4->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 5ULL);
- pool.GetTransactionAncestry(ty1->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
- pool.GetTransactionAncestry(ty2->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
- pool.GetTransactionAncestry(ty3->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
- pool.GetTransactionAncestry(ty4->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 4ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
- pool.GetTransactionAncestry(ty5->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 5ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
- pool.GetTransactionAncestry(ty6->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 9ULL);
- BOOST_CHECK_EQUAL(descendants, 6ULL);
-
- /* Ancestors represented more than once ("diamond") */
- //
- // [ta].0 <- [tb].0 -----<------- [td].0
- // | |
- // \---1 <- [tc].0 --<--/
- //
- CTransactionRef ta, tb, tc, td;
- ta = make_tx(/* output_values */ {10 * COIN});
- tb = make_tx(/* output_values */ {5 * COIN, 3 * COIN}, /* inputs */ {ta});
- tc = make_tx(/* output_values */ {2 * COIN}, /* inputs */ {tb},
- /* input_indices */ {1});
- td = make_tx(/* output_values */ {6 * COIN}, /* inputs */ {tb, tc},
- /* input_indices */ {0, 0});
- pool.clear();
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(ta));
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tb));
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(tc));
- pool.addUnchecked(entry.Fee(10000 * SATOSHI).FromTx(td));
-
- // Ancestors / descendants should be:
- // transaction ancestors descendants
- // ============ =================== ===========
- // ta 1 (ta 4 (ta,tb,tc,td)
- // tb 2 (ta,tb) 4 (ta,tb,tc,td)
- // tc 3 (ta,tb,tc) 4 (ta,tb,tc,td)
- // td 4 (ta,tb,tc,td) 4 (ta,tb,tc,td)
-
- pool.GetTransactionAncestry(ta->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 1ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(tb->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 2ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(tc->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 3ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
- pool.GetTransactionAncestry(td->GetId(), ancestors, descendants);
- BOOST_CHECK_EQUAL(ancestors, 4ULL);
- BOOST_CHECK_EQUAL(descendants, 4ULL);
-}
-
BOOST_AUTO_TEST_CASE(GetModifiedFeeRateTest) {
CMutableTransaction tx = CMutableTransaction();
tx.vin.resize(1);
diff --git a/src/txmempool.h b/src/txmempool.h
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -444,10 +444,6 @@
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
typedef std::set setEntries;
- /// Remove after wellington activates as this will be inaccurate
- uint64_t CalculateDescendantMaximum(txiter entry) const
- EXCLUSIVE_LOCKS_REQUIRED(cs);
-
private:
void UpdateParent(txiter entry, txiter parent, bool add)
EXCLUSIVE_LOCKS_REQUIRED(cs);
@@ -482,15 +478,6 @@
indirectmap mapNextTx GUARDED_BY(cs);
std::map mapDeltas GUARDED_BY(cs);
- /**
- * Wellington activation latch. This is latched permanently to true in
- * AcceptToMemoryPool the first time a tx arrives and
- * IsWellingtonActivated() returns true. This should be removed after
- * wellington is checkpointed and its mempool-accept/relay rules become
- * retroactively permanent.
- */
- std::atomic wellingtonLatched{false};
-
/**
* Create a new CTxMemPool.
* Sanity checks will be off by default for performance, because otherwise
@@ -666,23 +653,6 @@
std::chrono::seconds age)
EXCLUSIVE_LOCKS_REQUIRED(cs, ::cs_main);
- /**
- * Calculate the ancestor and descendant count for the given transaction.
- * The counts include the transaction itself.
- * When ancestors is non-zero (ie, the transaction itself is in the
- * mempool), ancestorsize and ancestorfees will also be set to the
- * appropriate values.
- *
- * NOTE: Since we are removing the unconf. ancestor limits after wellington,
- * this function's existence is a potential DoS. It should not be
- * called after wellington since it relies on calculating quadratic
- * stats.
- */
- void GetTransactionAncestry(const TxId &txid, size_t &ancestors,
- size_t &descendants,
- size_t *ancestorsize = nullptr,
- Amount *ancestorfees = nullptr) const;
-
/** @returns true if the mempool is fully loaded */
bool IsLoaded() const;
@@ -758,10 +728,7 @@
/**
* Update parents of `it` to add/remove it as a child transaction.
*/
- void UpdateParentsOf(
- bool add, txiter it,
- const setEntries *setAncestors = nullptr /* only used pre-wellington */)
- EXCLUSIVE_LOCKS_REQUIRED(cs);
+ void UpdateParentsOf(bool add, txiter it) EXCLUSIVE_LOCKS_REQUIRED(cs);
/**
* For each transaction being removed, update ancestors and any direct
* children. If updateDescendants is true, then also update in-mempool
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -269,45 +269,11 @@
limitDescendantSize, errString);
}
-void CTxMemPool::UpdateParentsOf(bool add, txiter it,
- const setEntries *setAncestors) {
+void CTxMemPool::UpdateParentsOf(bool add, txiter it) {
// add or remove this tx as a child of each parent
for (const CTxMemPoolEntry &parent : it->GetMemPoolParentsConst()) {
UpdateChild(mapTx.iterator_to(parent), it, add);
}
-
- // Remove this after wellington
- if (setAncestors && !wellingtonLatched) {
- const int64_t updateCount = (add ? 1 : -1);
- const int64_t updateSize = updateCount * it->GetTxSize();
- const int64_t updateSigChecks = updateCount * it->GetSigChecks();
- const Amount updateFee = updateCount * it->GetModifiedFee();
- for (txiter ancestorIt : *setAncestors) {
- mapTx.modify(ancestorIt,
- update_descendant_state(updateSize, updateFee,
- updateCount, updateSigChecks));
- }
- }
-}
-
-void CTxMemPool::UpdateEntryForAncestors(txiter it,
- const setEntries *setAncestors) {
- if (!setAncestors || wellingtonLatched) {
- return;
- }
-
- int64_t updateCount = setAncestors->size();
- int64_t updateSize = 0;
- int64_t updateSigChecks = 0;
- Amount updateFee = Amount::zero();
-
- for (txiter ancestorIt : *setAncestors) {
- updateSize += ancestorIt->GetTxSize();
- updateFee += ancestorIt->GetModifiedFee();
- updateSigChecks += ancestorIt->GetSigChecks();
- }
- mapTx.modify(it, update_ancestor_state(updateSize, updateFee, updateCount,
- updateSigChecks));
}
void CTxMemPool::UpdateChildrenForRemoval(txiter it) {
@@ -319,56 +285,12 @@
void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove,
bool updateDescendants) {
- if (!wellingtonLatched) {
- // remove this branch after wellington
- // slow quadratic branch, only for pre-activation compatibility
-
- // For each entry, walk back all ancestors and decrement size associated
- // with this transaction.
- if (updateDescendants) {
- // updateDescendants should be true whenever we're not recursively
- // removing a tx and all its descendants, eg when a transaction is
- // confirmed in a block.
- // Here we only update statistics and not data in
- // CTxMemPool::Parents and CTxMemPoolEntry::Children (which we need
- // to preserve until we're finished with all operations that need to
- // traverse the mempool).
- for (txiter removeIt : entriesToRemove) {
- setEntries setDescendants;
- CalculateDescendants(removeIt, setDescendants);
- setDescendants.erase(removeIt); // don't update state for self
- int64_t modifySize = -int64_t(removeIt->GetTxSize());
- Amount modifyFee = -1 * removeIt->GetModifiedFee();
- int modifySigChecks = -removeIt->GetSigChecks();
- for (txiter dit : setDescendants) {
- mapTx.modify(dit,
- update_ancestor_state(modifySize, modifyFee,
- -1, modifySigChecks));
- }
- }
- }
-
- for (txiter removeIt : entriesToRemove) {
- setEntries setAncestors;
- const CTxMemPoolEntry &entry = *removeIt;
- std::string dummy;
- // Since this is a tx that is already in the mempool, we can call
- // CMPA with fSearchForParents = false. If the mempool is in a
- // consistent state, then using true or false should both be
- // correct, though false should be a bit faster.
- CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit,
- nNoLimit, nNoLimit, dummy, false);
- // Note that UpdateParentsOf severs the child links that point to
- // removeIt in the entries for the parents of removeIt.
- UpdateParentsOf(false, removeIt, &setAncestors);
- }
- } else {
- for (txiter removeIt : entriesToRemove) {
- // Note that UpdateParentsOf severs the child links that point to
- // removeIt in the entries for the parents of removeIt.
- UpdateParentsOf(false, removeIt);
- }
+ for (txiter removeIt : entriesToRemove) {
+ // Note that UpdateParentsOf severs the child links that point to
+ // removeIt in the entries for the parents of removeIt.
+ UpdateParentsOf(false, removeIt);
}
+
// After updating all the parent links, we can now sever the link between
// each transaction being removed and any mempool children (ie, update
// CTxMemPoolEntry::m_parents for each direct child of a transaction being
@@ -466,9 +388,7 @@
UpdateParent(newit, pit, true);
}
- const setEntries *pSetEntries = wellingtonLatched ? nullptr : &setAncestors;
- UpdateParentsOf(true, newit, pSetEntries);
- UpdateEntryForAncestors(newit, pSetEntries);
+ UpdateParentsOf(true, newit);
nTransactionsUpdated++;
totalTxSize += entry.GetTxSize();
@@ -710,24 +630,6 @@
entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
assert(ok);
- if (!wellingtonLatched) {
- uint64_t nCountCheck = setAncestors.size() + 1;
- uint64_t nSizeCheck = entry.GetTxSize();
- Amount nFeesCheck = entry.GetModifiedFee();
- int64_t nSigChecksCheck = entry.GetSigChecks();
-
- for (txiter ancestorIt : setAncestors) {
- nSizeCheck += ancestorIt->GetTxSize();
- nFeesCheck += ancestorIt->GetModifiedFee();
- nSigChecksCheck += ancestorIt->GetSigChecks();
- }
-
- assert(entry.GetCountWithAncestors() == nCountCheck);
- assert(entry.GetSizeWithAncestors() == nSizeCheck);
- assert(entry.GetSigChecksWithAncestors() == nSigChecksCheck);
- assert(entry.GetModFeesWithAncestors() == nFeesCheck);
- }
-
// all ancestors should have entryId < this tx's entryId
for (const auto &ancestor : setAncestors) {
assert(ancestor->GetEntryId() < entry.GetEntryId());
@@ -753,15 +655,6 @@
entry.GetMemPoolChildrenConst().size());
assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(),
entry.GetMemPoolChildrenConst().begin(), comp));
- if (!wellingtonLatched) {
- // Also check to make sure size is greater than sum with immediate
- // children. Just a sanity check, not definitive that this calc is
- // correct...
- assert(entry.GetSizeWithDescendants() >=
- child_sizes + entry.GetTxSize());
- assert(entry.GetSigChecksWithDescendants() >=
- child_sigChecks + entry.GetSigChecks());
- }
// Not used. CheckTxInputs() should always pass
TxValidationState dummy_state;
@@ -873,27 +766,6 @@
if (it != mapTx.end()) {
mapTx.modify(
it, [&delta](CTxMemPoolEntry &e) { e.UpdateFeeDelta(delta); });
- // Remove after wellington
- if (!wellingtonLatched) {
- // Now update all ancestors' modified fees with descendants
- setEntries setAncestors;
- std::string dummy;
- CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit,
- nNoLimit, nNoLimit, dummy, false);
- for (txiter ancestorIt : setAncestors) {
- mapTx.modify(ancestorIt,
- update_descendant_state(0, nFeeDelta, 0, 0));
- }
-
- // Now update all descendants' modified fees with ancestors
- setEntries setDescendants;
- CalculateDescendants(it, setDescendants);
- setDescendants.erase(it);
- for (txiter descendantIt : setDescendants) {
- mapTx.modify(descendantIt,
- update_ancestor_state(0, nFeeDelta, 0, 0));
- }
- }
++nTransactionsUpdated;
}
}
@@ -1054,11 +926,6 @@
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry) {
setEntries setAncestors;
- if (!wellingtonLatched) {
- std::string dummy;
- CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit,
- nNoLimit, nNoLimit, dummy);
- }
return addUnchecked(entry, setAncestors);
}
@@ -1158,51 +1025,6 @@
}
}
-/// Remove after wellington; after wellington activates this will be inaccurate
-uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
- // find parent with highest descendant count
- std::vector candidates;
- setEntries counted;
- candidates.push_back(entry);
- uint64_t maximum = 0;
- while (candidates.size()) {
- txiter candidate = candidates.back();
- candidates.pop_back();
- if (!counted.insert(candidate).second) {
- continue;
- }
- const CTxMemPoolEntry::Parents &parents =
- candidate->GetMemPoolParentsConst();
- if (parents.size() == 0) {
- maximum = std::max(maximum, candidate->GetCountWithDescendants());
- } else {
- for (const CTxMemPoolEntry &i : parents) {
- candidates.push_back(mapTx.iterator_to(i));
- }
- }
- }
- return maximum;
-}
-
-void CTxMemPool::GetTransactionAncestry(const TxId &txid, size_t &ancestors,
- size_t &descendants,
- size_t *const ancestorsize,
- Amount *const ancestorfees) const {
- LOCK(cs);
- auto it = mapTx.find(txid);
- ancestors = descendants = 0;
- if (it != mapTx.end()) {
- ancestors = it->GetCountWithAncestors();
- if (ancestorsize) {
- *ancestorsize = it->GetSizeWithAncestors();
- }
- if (ancestorfees) {
- *ancestorfees = it->GetModFeesWithAncestors();
- }
- descendants = CalculateDescendantMaximum(it);
- }
-}
-
bool CTxMemPool::IsLoaded() const {
LOCK(cs);
return m_is_loaded;
diff --git a/src/validation.cpp b/src/validation.cpp
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -704,19 +704,6 @@
strprintf("%d < %d", ws.m_modified_fees, mempoolRejectFee));
}
- // Remove after wellington
- if (!m_pool.wellingtonLatched) {
- // Calculate in-mempool ancestors, up to a limit.
- std::string errString;
- if (!m_pool.CalculateMemPoolAncestors(
- *entry, ws.m_ancestors, m_limit_ancestors,
- m_limit_ancestor_size, m_limit_descendants,
- m_limit_descendant_size, errString)) {
- return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
- "too-long-mempool-chain", errString);
- }
- }
-
return true;
}
@@ -917,15 +904,6 @@
args.m_config.GetChainParams().GetConsensus();
const CBlockIndex *tip = m_active_chainstate.m_chain.Tip();
- // After wellington we will stop computing the ancestors and descendant
- // limits, and stop enforcing them. Because the mempool has no idea what the
- // current tip is (and it should not), we will use an activation latch so it
- // knows when the statistics can be skipped. This also makes an hypothetical
- // reorg event easier to handle.
- // Note that |= operator is not defined for atomic bool !
- m_pool.wellingtonLatched =
- m_pool.wellingtonLatched || IsWellingtonEnabled(consensusParams, tip);
-
Workspace ws(ptx, GetNextBlockScriptFlags(consensusParams, tip));
// Perform the inexpensive checks first and avoid hashing and signature
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -3251,22 +3251,6 @@
entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", out.tx->tx->vout[out.i].nValue);
entry.pushKV("confirmations", out.nDepth);
-
- // Deprecated in v0.27.0
- if (!out.nDepth &&
- IsDeprecatedRPCEnabled(gArgs,
- "mempool_ancestors_descendants")) {
- size_t ancestor_count, descendant_count, ancestor_size;
- Amount ancestor_fees;
- pwallet->chain().getTransactionAncestry(
- out.tx->GetId(), ancestor_count, descendant_count,
- &ancestor_size, &ancestor_fees);
- if (ancestor_count) {
- entry.pushKV("ancestorcount", uint64_t(ancestor_count));
- entry.pushKV("ancestorsize", uint64_t(ancestor_size));
- entry.pushKV("ancestorfees", ancestor_fees);
- }
- }
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -314,9 +314,7 @@
// deprecated -- after wellington activation these 2 stats should
// always just be 0 since these stat becomes irrelevant at that
// point
- size_t ancestors, descendants;
- wallet.chain().getTransactionAncestry(output.tx->GetId(), ancestors,
- descendants);
+ size_t ancestors = 0, descendants = 0;
if (!single_coin &&
ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey,
@@ -948,16 +946,6 @@
return false;
}
- // After wellington this option will no longer exist
- if (gArgs.GetBoolArg("-walletrejectlongchains",
- DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
- // Lastly, ensure this tx will pass the mempool's chain limits
- if (!wallet.chain().checkChainLimits(tx)) {
- error = _("Transaction has too long of a mempool chain");
- return false;
- }
- }
-
// Before we return success, we assume any change key will be used to
// prevent accidental re-use.
reservedest.KeepDestination();
diff --git a/test/functional/abc_mempool_chainedtx.py b/test/functional/abc_mempool_chainedtx.py
--- a/test/functional/abc_mempool_chainedtx.py
+++ b/test/functional/abc_mempool_chainedtx.py
@@ -5,36 +5,22 @@
"""Test the chained txs limit."""
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import (
- assert_equal,
- assert_greater_than,
- assert_raises_rpc_error,
-)
+from test_framework.util import assert_equal
from test_framework.wallet import create_raw_chain
-MAX_CHAINED_TX = 5
-WELLINGTON_ACTIVATION_TIME = 2000000000
+LEGACY_MAX_CHAINED_TX = 5
class ChainedTxTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.setup_clean_chain = True
- self.extra_args = [
- [
- f"-replayprotectionactivationtime={WELLINGTON_ACTIVATION_TIME + 1000}",
- f"-wellingtonactivationtime={WELLINGTON_ACTIVATION_TIME}",
- f"-limitancestorcount={MAX_CHAINED_TX}",
- f"-limitdescendantcount={MAX_CHAINED_TX + 1}",
- ]
- ]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def run_test(self):
node = self.nodes[0]
- node.setmocktime(WELLINGTON_ACTIVATION_TIME - 1000)
self.privkeys = [node.get_deterministic_priv_key().key]
self.address = node.get_deterministic_priv_key().address
@@ -50,10 +36,8 @@
}
)
- self.log.info("Before Wellington, the chained-tx limit applies")
-
- assert_greater_than(
- WELLINGTON_ACTIVATION_TIME, node.getblockchaininfo()["mediantime"]
+ self.log.info(
+ "Since Wellington activation, the chained-tx limit no longer applies"
)
chain_hex, _ = create_raw_chain(
@@ -61,69 +45,15 @@
self.coins.pop(),
self.address,
self.privkeys,
- chain_length=MAX_CHAINED_TX + 1,
+ chain_length=LEGACY_MAX_CHAINED_TX * 2,
)
- for i in range(MAX_CHAINED_TX):
+ for i in range(LEGACY_MAX_CHAINED_TX * 2):
txid = node.sendrawtransaction(chain_hex[i])
mempool = node.getrawmempool()
assert_equal(len(mempool), i + 1)
assert txid in mempool
- assert_raises_rpc_error(
- -26,
- (
- "too-long-mempool-chain, too many unconfirmed ancestors [limit:"
- f" {MAX_CHAINED_TX}]"
- ),
- node.sendrawtransaction,
- chain_hex[-1],
- )
-
- self.log.info("Activate Wellington")
-
- node.setmocktime(WELLINGTON_ACTIVATION_TIME)
- self.generate(node, 6)
- assert_equal(node.getblockchaininfo()["mediantime"], WELLINGTON_ACTIVATION_TIME)
-
- self.log.info("After Wellington, the chained-tx limit no longer applies")
-
- chain_hex, _ = create_raw_chain(
- node,
- self.coins.pop(),
- self.address,
- self.privkeys,
- chain_length=MAX_CHAINED_TX * 2,
- )
-
- for i in range(MAX_CHAINED_TX * 2):
- txid = node.sendrawtransaction(chain_hex[i])
- mempool = node.getrawmempool()
- assert_equal(len(mempool), i + 1)
- assert txid in mempool
-
- self.log.info("Upon reorg the mempool policy is maintained")
-
- node.invalidateblock(node.getbestblockhash())
- assert_greater_than(
- WELLINGTON_ACTIVATION_TIME, node.getblockchaininfo()["mediantime"]
- )
-
- # Mempool size should be limited again
- assert_equal(len(node.getrawmempool()), MAX_CHAINED_TX * 2)
-
- # Mine an activation block to clear the mempool
- self.generate(node, 1)
- assert_equal(len(node.getrawmempool()), 0)
-
- # Reorg that block, and make sure all the txs are added back to the
- # mempool
- node.invalidateblock(node.getbestblockhash())
- assert_greater_than(
- WELLINGTON_ACTIVATION_TIME, node.getblockchaininfo()["mediantime"]
- )
- assert_equal(len(node.getrawmempool()), MAX_CHAINED_TX * 2)
-
if __name__ == "__main__":
ChainedTxTest().main()
diff --git a/test/functional/mempool_package_limits.py b/test/functional/mempool_package_limits.py
deleted file mode 100755
--- a/test/functional/mempool_package_limits.py
+++ /dev/null
@@ -1,676 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (c) 2021 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-"""Test logic for limiting mempool and package ancestors/descendants."""
-
-from decimal import Decimal
-
-from test_framework.address import ADDRESS_ECREG_P2SH_OP_TRUE, SCRIPTSIG_OP_TRUE
-from test_framework.messages import XEC, CTransaction, FromHex, ToHex
-from test_framework.test_framework import BitcoinTestFramework
-from test_framework.txtools import pad_tx
-from test_framework.util import assert_equal
-from test_framework.wallet import (
- bulk_transaction,
- create_child_with_parents,
- make_chain,
-)
-
-FAR_IN_THE_FUTURE = 2000000000
-
-
-class MempoolPackageLimitsTest(BitcoinTestFramework):
- def set_test_params(self):
- self.num_nodes = 1
- self.setup_clean_chain = True
- self.extra_args = [
- [
- # The packages mempool limits are no longer applied after wellington
- # activation.
- f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}",
- ]
- ]
-
- def run_test(self):
- self.log.info("Generate blocks to create UTXOs")
- node = self.nodes[0]
- self.privkeys = [node.get_deterministic_priv_key().key]
- self.address = node.get_deterministic_priv_key().address
- self.coins = []
- # The last 100 coinbase transactions are premature
- for b in self.generatetoaddress(node, 200, self.address)[:100]:
- coinbase = node.getblock(blockhash=b, verbosity=2)["tx"][0]
- self.coins.append(
- {
- "txid": coinbase["txid"],
- "amount": coinbase["vout"][0]["value"],
- "scriptPubKey": coinbase["vout"][0]["scriptPubKey"],
- }
- )
-
- self.test_chain_limits()
- self.test_desc_count_limits()
- self.test_desc_count_limits_2()
- self.test_anc_count_limits()
- self.test_anc_count_limits_2()
- self.test_anc_count_limits_bushy()
-
- # The node will accept our (nonstandard) extra large OP_RETURN outputs
- self.restart_node(0, extra_args=["-acceptnonstdtxn=1"])
- self.test_anc_size_limits()
- self.test_desc_size_limits()
-
- def test_chain_limits_helper(self, mempool_count, package_count):
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- first_coin = self.coins.pop()
- spk = None
- txid = first_coin["txid"]
- chain_hex = []
- chain_txns = []
- value = first_coin["amount"]
-
- for i in range(mempool_count + package_count):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- txid = tx.get_id()
- if i < mempool_count:
- node.sendrawtransaction(txhex)
- else:
- chain_hex.append(txhex)
- chain_txns.append(tx)
- testres_too_long = node.testmempoolaccept(rawtxs=chain_hex)
- for txres in testres_too_long:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True for res in node.testmempoolaccept(rawtxs=chain_hex)
- )
-
- def test_chain_limits(self):
- """Create chains from mempool and package transactions that are longer than 50,
- but only if both in-mempool and in-package transactions are considered together.
- This checks that both mempool and in-package transactions are taken into account
- when calculating ancestors/descendant limits.
- """
- self.log.info(
- "Check that in-package ancestors count for mempool ancestor limits"
- )
-
- self.test_chain_limits_helper(mempool_count=49, package_count=2)
- self.test_chain_limits_helper(mempool_count=2, package_count=49)
- self.test_chain_limits_helper(mempool_count=26, package_count=26)
-
- def test_desc_count_limits(self):
- """Create an 'A' shaped package with 49 transactions in the mempool and 2 in the package:
- M1
- ^ ^
- M2a M2b
- . .
- . .
- M25a M25b
- ^ ^
- Pa Pb
- The top ancestor in the package exceeds descendant limits but only if the in-mempool and in-package
- descendants are all considered together (49 including in-mempool descendants and 51 including both
- package transactions).
- """
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- self.log.info(
- "Check that in-mempool and in-package descendants are calculated properly"
- " in packages"
- )
- # Top parent in mempool, M1
- first_coin = self.coins.pop()
- # Deduct reasonable fee and make 2 outputs
- parent_value = (first_coin["amount"] - Decimal("200.00")) / 2
- inputs = [{"txid": first_coin["txid"], "vout": 0}]
- outputs = [
- {self.address: parent_value},
- {ADDRESS_ECREG_P2SH_OP_TRUE: parent_value},
- ]
- rawtx = node.createrawtransaction(inputs, outputs)
-
- parent_signed = node.signrawtransactionwithkey(
- hexstring=rawtx, privkeys=self.privkeys
- )
- assert parent_signed["complete"]
- parent_tx = FromHex(CTransaction(), parent_signed["hex"])
- parent_txid = parent_tx.rehash()
- node.sendrawtransaction(parent_signed["hex"])
-
- package_hex = []
-
- # Chain A
- spk = parent_tx.vout[0].scriptPubKey.hex()
- value = parent_value
- txid = parent_txid
- for i in range(25):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- txid = tx.get_id()
- if i < 24:
- # M2a... M25a
- node.sendrawtransaction(txhex)
- else:
- # Pa
- package_hex.append(txhex)
-
- # Chain B
- value = parent_value - Decimal("100.00")
- rawtx_b = node.createrawtransaction(
- [{"txid": parent_txid, "vout": 1}], {self.address: value}
- )
- # M2b
- tx_child_b = FromHex(CTransaction(), rawtx_b)
- tx_child_b.vin[0].scriptSig = SCRIPTSIG_OP_TRUE
- pad_tx(tx_child_b)
- tx_child_b_hex = ToHex(tx_child_b)
- node.sendrawtransaction(tx_child_b_hex)
- spk = tx_child_b.vout[0].scriptPubKey.hex()
- txid = tx_child_b.rehash()
- for i in range(24):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- txid = tx.get_id()
- if i < 23:
- # M3b... M25b
- node.sendrawtransaction(txhex)
- else:
- # Pb
- package_hex.append(txhex)
-
- assert_equal(49, node.getmempoolinfo()["size"])
- assert_equal(2, len(package_hex))
- testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
- for txres in testres_too_long:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True for res in node.testmempoolaccept(rawtxs=package_hex)
- )
-
- def test_desc_count_limits_2(self):
- """Create a Package with 49 transactions in mempool and 2 transactions
- in package:
- M1
- ^ ^
- M2 ^
- . ^
- . ^
- . ^
- M49 ^
- ^
- P1
- ^
- P2
- P1 has M1 as a mempool ancestor, P2 has no in-mempool ancestors, but
- when combined P2 has M1 as an ancestor and M1 exceeds descendant_limits
- (48 in-mempool descendants + 2 in-package descendants, a total of 51
- including itself).
- """
-
- node = self.nodes[0]
- package_hex = []
- # M1
- first_coin_a = self.coins.pop()
- # Deduct reasonable fee and make 2 outputs
- parent_value = (first_coin_a["amount"] - Decimal("200.0")) / 2
- inputs = [{"txid": first_coin_a["txid"], "vout": 0}]
- outputs = [
- {self.address: parent_value},
- {ADDRESS_ECREG_P2SH_OP_TRUE: parent_value},
- ]
- rawtx = node.createrawtransaction(inputs, outputs)
-
- parent_signed = node.signrawtransactionwithkey(
- hexstring=rawtx, privkeys=self.privkeys
- )
- assert parent_signed["complete"]
- parent_tx = FromHex(CTransaction(), parent_signed["hex"])
- pad_tx(parent_tx)
- parent_txid = parent_tx.rehash()
- node.sendrawtransaction(parent_signed["hex"])
-
- # Chain M2...M49
- spk = parent_tx.vout[0].scriptPubKey.hex()
- value = parent_value
- txid = parent_txid
- for _ in range(48):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- pad_tx(tx)
- txid = tx.hash
- node.sendrawtransaction(txhex)
-
- # P1
- value_p1 = parent_value - Decimal("100")
- rawtx_p1 = node.createrawtransaction(
- [{"txid": parent_txid, "vout": 1}], [{self.address: value_p1}]
- )
- tx_child_p1 = FromHex(CTransaction(), rawtx_p1)
- tx_child_p1.vin[0].scriptSig = SCRIPTSIG_OP_TRUE
- pad_tx(tx_child_p1)
- tx_child_p1_hex = tx_child_p1.serialize().hex()
- package_hex.append(tx_child_p1_hex)
- tx_child_p1_spk = tx_child_p1.vout[0].scriptPubKey.hex()
-
- # P2
- (_, tx_child_p2_hex, _, _) = make_chain(
- node,
- self.address,
- self.privkeys,
- tx_child_p1.hash,
- value_p1,
- 0,
- tx_child_p1_spk,
- )
- package_hex.append(tx_child_p2_hex)
-
- assert_equal(49, node.getmempoolinfo()["size"])
- assert_equal(2, len(package_hex))
- testres = node.testmempoolaccept(rawtxs=package_hex)
- assert_equal(len(testres), len(package_hex))
- for txres in testres:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(res["allowed"] for res in node.testmempoolaccept(rawtxs=package_hex))
-
- def test_anc_count_limits(self):
- """Create a 'V' shaped chain with 49 transactions in the mempool and 3 in the package:
- M1a
- ^ M1b
- M2a ^
- . M2b
- . .
- . .
- M25a M24b
- ^ ^
- Pa Pb
- ^ ^
- Pc
- The lowest descendant, Pc, exceeds ancestor limits, but only if the in-mempool
- and in-package ancestors are all considered together.
- """
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- package_hex = []
- parents_tx = []
- values = []
- scripts = []
-
- self.log.info(
- "Check that in-mempool and in-package ancestors are calculated "
- "properly in packages"
- )
-
- # Two chains of 26 & 25 transactions
- for chain_length in [26, 25]:
- spk = None
- top_coin = self.coins.pop()
- txid = top_coin["txid"]
- value = top_coin["amount"]
- for i in range(chain_length):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- txid = tx.get_id()
- if i < chain_length - 1:
- node.sendrawtransaction(txhex)
- else:
- # Save the last transaction for the package
- package_hex.append(txhex)
- parents_tx.append(tx)
- scripts.append(spk)
- values.append(value)
-
- # Child Pc
- child_hex = create_child_with_parents(
- node, self.address, self.privkeys, parents_tx, values, scripts
- )
- package_hex.append(child_hex)
-
- assert_equal(49, node.getmempoolinfo()["size"])
- assert_equal(3, len(package_hex))
- testres_too_long = node.testmempoolaccept(rawtxs=package_hex)
- for txres in testres_too_long:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True for res in node.testmempoolaccept(rawtxs=package_hex)
- )
-
- def test_anc_count_limits_2(self):
- """Create a 'Y' shaped chain with 49 transactions in the mempool and 2 in the package:
- M1a
- ^ M1b
- M2a ^
- . M2b
- . .
- . .
- M25a M24b
- ^ ^
- Pc
- ^
- Pd
- The lowest descendant, Pc, exceeds ancestor limits, but only if the in-mempool
- and in-package ancestors are all considered together.
- """
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- parents_tx = []
- values = []
- scripts = []
-
- self.log.info(
- "Check that in-mempool and in-package ancestors are calculated properly in"
- " packages"
- )
- # Two chains of 25 & 24 transactions
- for chain_length in [25, 24]:
- spk = None
- top_coin = self.coins.pop()
- txid = top_coin["txid"]
- value = top_coin["amount"]
- for i in range(chain_length):
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk
- )
- txid = tx.get_id()
- node.sendrawtransaction(txhex)
- if i == chain_length - 1:
- # last 2 transactions will be the parents of Pc
- parents_tx.append(tx)
- values.append(value)
- scripts.append(spk)
-
- # Child Pc
- pc_hex = create_child_with_parents(
- node, self.address, self.privkeys, parents_tx, values, scripts
- )
- pc_tx = FromHex(CTransaction(), pc_hex)
- pc_value = sum(values) - Decimal("100.00")
- pc_spk = pc_tx.vout[0].scriptPubKey.hex()
-
- # Child Pd
- (_, pd_hex, _, _) = make_chain(
- node, self.address, self.privkeys, pc_tx.get_id(), pc_value, 0, pc_spk
- )
-
- assert_equal(49, node.getmempoolinfo()["size"])
- testres_too_long = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
- for txres in testres_too_long:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True
- for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
- )
-
- def test_anc_count_limits_bushy(self):
- """Create a tree with 45 transactions in the mempool and 6 in the package:
- M1...M9 M10...M18 M19...M27 M28...M36 M37...M45
- ^ ^ ^ ^ ^ (each with 9 parents)
- P0 P1 P2 P3 P4
- ^ ^ ^ ^ ^ (5 parents)
- PC
- Where M(9i+1)...M+(9i+9) are the parents of Pi and P0, P1, P2, P3, and P4 are the parents of PC.
- P0... P4 individually only have 9 parents each, and PC has no in-mempool parents. But
- combined, PC has 50 in-mempool and in-package parents.
- """
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- package_hex = []
- parent_txns = []
- parent_values = []
- scripts = []
- # Make package transactions P0 ... P4
- for _ in range(5):
- gp_tx = []
- gp_values = []
- gp_scripts = []
- # Make mempool transactions M(9i+1)...M(9i+9)
- for _ in range(9):
- parent_coin = self.coins.pop()
- value = parent_coin["amount"]
- txid = parent_coin["txid"]
- (tx, txhex, value, spk) = make_chain(
- node, self.address, self.privkeys, txid, value
- )
- gp_tx.append(tx)
- gp_values.append(value)
- gp_scripts.append(spk)
- node.sendrawtransaction(txhex)
- # Package transaction Pi
- pi_hex = create_child_with_parents(
- node, self.address, self.privkeys, gp_tx, gp_values, gp_scripts
- )
- package_hex.append(pi_hex)
- pi_tx = FromHex(CTransaction(), pi_hex)
- parent_txns.append(pi_tx)
- parent_values.append(Decimal(pi_tx.vout[0].nValue) / XEC)
- scripts.append(pi_tx.vout[0].scriptPubKey.hex())
- # Package transaction PC
- package_hex.append(
- create_child_with_parents(
- node, self.address, self.privkeys, parent_txns, parent_values, scripts
- )
- )
-
- assert_equal(45, node.getmempoolinfo()["size"])
- assert_equal(6, len(package_hex))
- testres = node.testmempoolaccept(rawtxs=package_hex)
- for txres in testres:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True for res in node.testmempoolaccept(rawtxs=package_hex)
- )
-
- def test_anc_size_limits(self):
- """Test Case with 2 independent transactions in the mempool and a parent + child in the
- package, where the package parent is the child of both mempool transactions (30KB each):
- A B
- ^ ^
- C
- ^
- D
- The lowest descendant, D, exceeds ancestor size limits, but only if the in-mempool
- and in-package ancestors are all considered together.
- """
- node = self.nodes[0]
-
- assert_equal(0, node.getmempoolinfo()["size"])
- parents_tx = []
- values = []
- scripts = []
- target_size = 30_000
- # 10 sats/B
- high_fee = Decimal("3000.00")
- self.log.info(
- "Check that in-mempool and in-package ancestor size limits are calculated"
- " properly in packages"
- )
- # Mempool transactions A and B
- for _ in range(2):
- spk = None
- top_coin = self.coins.pop()
- txid = top_coin["txid"]
- value = top_coin["amount"]
- (tx, _, _, _) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk, high_fee
- )
- bulked_tx = bulk_transaction(tx, node, target_size, self.privkeys)
- node.sendrawtransaction(ToHex(bulked_tx))
- parents_tx.append(bulked_tx)
- values.append(Decimal(bulked_tx.vout[0].nValue) / XEC)
- scripts.append(bulked_tx.vout[0].scriptPubKey.hex())
-
- # Package transaction C
- small_pc_hex = create_child_with_parents(
- node, self.address, self.privkeys, parents_tx, values, scripts, high_fee
- )
- pc_tx = bulk_transaction(
- FromHex(CTransaction(), small_pc_hex), node, target_size, self.privkeys
- )
- pc_value = Decimal(pc_tx.vout[0].nValue) / XEC
- pc_spk = pc_tx.vout[0].scriptPubKey.hex()
- pc_hex = ToHex(pc_tx)
-
- # Package transaction D
- (small_pd, _, val, spk) = make_chain(
- node,
- self.address,
- self.privkeys,
- pc_tx.rehash(),
- pc_value,
- 0,
- pc_spk,
- high_fee,
- )
- prevtxs = [
- {
- "txid": pc_tx.get_id(),
- "vout": 0,
- "scriptPubKey": spk,
- "amount": pc_value,
- }
- ]
- pd_tx = bulk_transaction(small_pd, node, target_size, self.privkeys, prevtxs)
- pd_hex = ToHex(pd_tx)
-
- assert_equal(2, node.getmempoolinfo()["size"])
- testres_too_heavy = node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
- for txres in testres_too_heavy:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True
- for res in node.testmempoolaccept(rawtxs=[pc_hex, pd_hex])
- )
-
- def test_desc_size_limits(self):
- """Create 3 mempool transactions and 2 package transactions (25KB each):
- Ma
- ^ ^
- Mb Mc
- ^ ^
- Pd Pe
- The top ancestor in the package exceeds descendant size limits but only if the in-mempool
- and in-package descendants are all considered together.
- """
- node = self.nodes[0]
- assert_equal(0, node.getmempoolinfo()["size"])
- target_size = 21_000
- # 10 sats/vB
- high_fee = Decimal("2100.00")
- self.log.info(
- "Check that in-mempool and in-package descendant sizes are calculated"
- " properly in packages"
- )
- # Top parent in mempool, Ma
- first_coin = self.coins.pop()
- # Deduct fee and make 2 outputs
- parent_value = (first_coin["amount"] - high_fee) / 2
- inputs = [{"txid": first_coin["txid"], "vout": 0}]
- outputs = [
- {self.address: parent_value},
- {ADDRESS_ECREG_P2SH_OP_TRUE: parent_value},
- ]
- rawtx = node.createrawtransaction(inputs, outputs)
- parent_tx = bulk_transaction(
- FromHex(CTransaction(), rawtx), node, target_size, self.privkeys
- )
- node.sendrawtransaction(ToHex(parent_tx))
-
- package_hex = []
- # Two legs (left and right)
- for j in range(2):
- # Mempool transaction (Mb and Mc)
- spk = parent_tx.vout[j].scriptPubKey.hex()
- value = Decimal(parent_tx.vout[j].nValue) / XEC
- txid = parent_tx.get_id()
- prevtxs = [
- {
- "txid": txid,
- "vout": j,
- "scriptPubKey": spk,
- "amount": value,
- }
- ]
- if j == 0:
- # normal key
- (tx_small, _, _, _) = make_chain(
- node, self.address, self.privkeys, txid, value, j, spk, high_fee
- )
- mempool_tx = bulk_transaction(
- tx_small, node, target_size, self.privkeys, prevtxs
- )
-
- else:
- # OP_TRUE
- inputs = [{"txid": txid, "vout": 1}]
- outputs = {self.address: value - high_fee}
- small_tx = FromHex(
- CTransaction(), node.createrawtransaction(inputs, outputs)
- )
- mempool_tx = bulk_transaction(
- small_tx, node, target_size, None, prevtxs
- )
- node.sendrawtransaction(ToHex(mempool_tx))
-
- # Package transaction (Pd and Pe)
- spk = mempool_tx.vout[0].scriptPubKey.hex()
- value = Decimal(mempool_tx.vout[0].nValue) / XEC
- txid = mempool_tx.get_id()
- (tx_small, _, _, _) = make_chain(
- node, self.address, self.privkeys, txid, value, 0, spk, high_fee
- )
- prevtxs = [
- {
- "txid": txid,
- "vout": 0,
- "scriptPubKey": spk,
- "amount": value,
- }
- ]
- package_tx = bulk_transaction(
- tx_small, node, target_size, self.privkeys, prevtxs
- )
- package_hex.append(ToHex(package_tx))
-
- assert_equal(3, node.getmempoolinfo()["size"])
- assert_equal(2, len(package_hex))
- testres_too_heavy = node.testmempoolaccept(rawtxs=package_hex)
- for txres in testres_too_heavy:
- assert_equal(txres["package-error"], "package-mempool-limits")
-
- # Clear mempool and check that the package passes now
- self.generate(node, 1)
- assert all(
- res["allowed"] is True for res in node.testmempoolaccept(rawtxs=package_hex)
- )
-
-
-if __name__ == "__main__":
- MempoolPackageLimitsTest().main()
diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py
--- a/test/functional/mempool_packages.py
+++ b/test/functional/mempool_packages.py
@@ -8,33 +8,15 @@
from test_framework.p2p import P2PTxInvStore
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, satoshi_round
+from test_framework.util import assert_equal, satoshi_round
-# default limits
MAX_ANCESTORS = 50
-MAX_DESCENDANTS = 50
-
-# custom limits for node1
-MAX_ANCESTORS_CUSTOM = 5
-
-FAR_IN_THE_FUTURE = 2000000000
class MempoolPackagesTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
- common_params = [
- "-maxorphantx=1000",
- "-deprecatedrpc=mempool_ancestors_descendants",
- # This test tests mempool ancestor chain limits, which are no longer
- # enforced after wellington, so we need to force wellington to
- # activate in the distant future
- f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}",
- ]
- self.extra_args = [
- common_params,
- common_params + [f"-limitancestorcount={MAX_ANCESTORS_CUSTOM}"],
- ]
+ self.extra_args = [["-maxorphantx=1000"]] * self.num_nodes
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -62,11 +44,7 @@
self.generate(self.nodes[0], 101)
utxo = self.nodes[0].listunspent(10)
txid = utxo[0]["txid"]
- vout = utxo[0]["vout"]
value = utxo[0]["amount"]
- assert "ancestorcount" not in utxo[0]
- assert "ancestorsize" not in utxo[0]
- assert "ancestorfees" not in utxo[0]
fee = Decimal("100")
# MAX_ANCESTORS transactions off a confirmed tx should be fine
@@ -80,39 +58,22 @@
value = sent_value
chain.append(txid)
- # Check that listunspent ancestor{count, size, fees} yield the
- # correct results
- wallet_unspent = self.nodes[0].listunspent(minconf=0)
- this_unspent = next(
- utxo_info for utxo_info in wallet_unspent if utxo_info["txid"] == txid
- )
- assert_equal(this_unspent["ancestorcount"], i + 1)
- ancestor_size += self.nodes[0].getrawtransaction(txid=txid, verbose=True)[
- "size"
- ]
- assert_equal(this_unspent["ancestorsize"], ancestor_size)
- ancestor_fees -= self.nodes[0].gettransaction(txid=txid)["fee"]
- assert_equal(this_unspent["ancestorfees"], ancestor_fees)
-
# Wait until mempool transactions have passed initial broadcast
# (sent inv and received getdata)
# Otherwise, getrawmempool may be inconsistent with getmempoolentry if
# unbroadcast changes in between
peer_inv_store.wait_for_broadcast(chain)
- # Check mempool has MAX_ANCESTORS transactions in it, and descendant and ancestor
- # count and fees should look correct
+ # Check mempool has MAX_ANCESTORS transactions in it
mempool = self.nodes[0].getrawmempool(True)
assert_equal(len(mempool), MAX_ANCESTORS)
descendant_count = 1
descendant_fees = 0
descendant_size = 0
- assert_equal(ancestor_size, sum([mempool[tx]["size"] for tx in mempool]))
+ ancestor_size = sum([mempool[tx]["size"] for tx in mempool])
ancestor_count = MAX_ANCESTORS
- assert_equal(
- ancestor_fees, sum([mempool[tx]["fees"]["base"] for tx in mempool])
- )
+ ancestor_fees = sum([mempool[tx]["fees"]["base"] for tx in mempool])
descendants = []
ancestors = list(chain)
@@ -120,19 +81,11 @@
# Check that getmempoolentry is consistent with getrawmempool
entry = self.nodes[0].getmempoolentry(x)
assert_equal(entry, mempool[x])
- # Check that the descendant calculations are correct
- assert_equal(mempool[x]["descendantcount"], descendant_count)
descendant_fees += mempool[x]["fees"]["base"]
assert_equal(mempool[x]["fees"]["modified"], mempool[x]["fees"]["base"])
- assert_equal(mempool[x]["fees"]["descendant"], descendant_fees)
descendant_size += mempool[x]["size"]
- assert_equal(mempool[x]["descendantsize"], descendant_size)
descendant_count += 1
- # Check that ancestor calculations are correct
- assert_equal(mempool[x]["ancestorcount"], ancestor_count)
- assert_equal(mempool[x]["fees"]["ancestor"], ancestor_fees)
- assert_equal(mempool[x]["ancestorsize"], ancestor_size)
ancestor_size -= mempool[x]["size"]
ancestor_fees -= mempool[x]["fees"]["base"]
ancestor_count -= 1
@@ -151,10 +104,6 @@
self.nodes[0].getmempooldescendants(x, True).items()
):
assert_equal(dinfo["depends"], [chain[chain.index(descendant) - 1]])
- if dinfo["descendantcount"] > 1:
- assert_equal(dinfo["spentby"], [chain[chain.index(descendant) + 1]])
- else:
- assert_equal(dinfo["spentby"], [])
descendants.append(x)
# Check that getmempoolancestors is correct
@@ -166,10 +115,6 @@
# Check that getmempoolancestors verbose output is correct
for ancestor, ainfo in self.nodes[0].getmempoolancestors(x, True).items():
assert_equal(ainfo["spentby"], [chain[chain.index(ancestor) + 1]])
- if ainfo["ancestorcount"] > 1:
- assert_equal(ainfo["depends"], [chain[chain.index(ancestor) - 1]])
- else:
- assert_equal(ainfo["depends"], [])
# Check that getmempoolancestors/getmempooldescendants correctly handle
# verbose=true
@@ -185,45 +130,6 @@
assert_equal(mempool[x], v_descendants[x])
assert chain[0] not in v_descendants.keys()
- # Check that ancestor modified fees includes fee deltas from
- # prioritisetransaction
- self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=1000)
- mempool = self.nodes[0].getrawmempool(True)
- ancestor_fees = 0
- for x in chain:
- ancestor_fees += mempool[x]["fees"]["base"]
- assert_equal(
- mempool[x]["fees"]["ancestor"], ancestor_fees + Decimal("10.00")
- )
-
- # Undo the prioritisetransaction for later tests
- self.nodes[0].prioritisetransaction(txid=chain[0], fee_delta=-1000)
-
- # Check that descendant modified fees includes fee deltas from
- # prioritisetransaction
- self.nodes[0].prioritisetransaction(txid=chain[-1], fee_delta=1000)
- mempool = self.nodes[0].getrawmempool(True)
-
- descendant_fees = 0
- for x in reversed(chain):
- descendant_fees += mempool[x]["fees"]["base"]
- assert_equal(
- mempool[x]["fees"]["descendant"], descendant_fees + Decimal("10.00")
- )
-
- # Adding one more transaction on to the chain should fail.
- assert_raises_rpc_error(
- -26,
- "too-long-mempool-chain",
- self.chain_transaction,
- self.nodes[0],
- txid,
- vout,
- value,
- fee,
- 1,
- )
-
# Check that prioritising a tx before it's added to the mempool works
# First clear the mempool by mining a block.
self.generate(self.nodes[0], 1)
@@ -239,25 +145,17 @@
# modified fee
mempool = self.nodes[0].getrawmempool(True)
- descendant_fees = 0
for x in reversed(chain):
- descendant_fees += mempool[x]["fees"]["base"]
if x == chain[-1]:
assert_equal(
mempool[x]["fees"]["modified"],
mempool[x]["fees"]["base"] + satoshi_round(20.00),
)
- assert_equal(
- mempool[x]["fees"]["descendant"], descendant_fees + satoshi_round(20.00)
- )
- # Check that node1's mempool is as expected (-> custom ancestor limit)
+ # Check that node1's mempool is as expected
mempool0 = self.nodes[0].getrawmempool(False)
mempool1 = self.nodes[1].getrawmempool(False)
- assert_equal(len(mempool1), MAX_ANCESTORS_CUSTOM)
- assert set(mempool1).issubset(set(mempool0))
- for tx in chain[:MAX_ANCESTORS_CUSTOM]:
- assert tx in mempool1
+ assert_equal(mempool0, mempool1)
# TODO: more detailed check of node1's mempool (fees etc.)
# check transaction unbroadcast info (should be false if in both
# mempools)
@@ -265,129 +163,6 @@
for tx in mempool:
assert_equal(mempool[tx]["unbroadcast"], False)
- # TODO: test ancestor size limits
-
- # Now test descendant chain limits
- txid = utxo[1]["txid"]
- value = utxo[1]["amount"]
- vout = utxo[1]["vout"]
-
- transaction_package = []
- tx_children = []
- # First create one parent tx with 10 children
- (txid, sent_value) = self.chain_transaction(
- self.nodes[0], txid, vout, value, fee, 10
- )
- parent_transaction = txid
- for i in range(10):
- transaction_package.append({"txid": txid, "vout": i, "amount": sent_value})
-
- # Sign and send up to MAX_DESCENDANT transactions chained off the
- # parent tx
- for _ in range(MAX_DESCENDANTS - 1):
- utxo = transaction_package.pop(0)
- (txid, sent_value) = self.chain_transaction(
- self.nodes[0], utxo["txid"], utxo["vout"], utxo["amount"], fee, 10
- )
- if utxo["txid"] is parent_transaction:
- tx_children.append(txid)
- for j in range(10):
- transaction_package.append(
- {"txid": txid, "vout": j, "amount": sent_value}
- )
-
- mempool = self.nodes[0].getrawmempool(True)
- assert_equal(mempool[parent_transaction]["descendantcount"], MAX_DESCENDANTS)
- assert_equal(
- sorted(mempool[parent_transaction]["spentby"]), sorted(tx_children)
- )
-
- for child in tx_children:
- assert_equal(mempool[child]["depends"], [parent_transaction])
-
- # Sending one more chained transaction will fail
- utxo = transaction_package.pop(0)
- assert_raises_rpc_error(
- -26,
- "too-long-mempool-chain",
- self.chain_transaction,
- self.nodes[0],
- utxo["txid"],
- utxo["vout"],
- utxo["amount"],
- fee,
- 10,
- )
-
- # TODO: check that node1's mempool is as expected
-
- # TODO: test descendant size limits
-
- # Test reorg handling
- # First, the basics:
- self.generate(self.nodes[0], 1)
- self.nodes[1].invalidateblock(self.nodes[0].getbestblockhash())
- self.nodes[1].reconsiderblock(self.nodes[0].getbestblockhash())
-
- # Now test the case where node1 has a transaction T in its mempool that
- # depends on transactions A and B which are in a mined block, and the
- # block containing A and B is disconnected, AND B is not accepted back
- # into node1's mempool because its ancestor count is too high.
-
- # Create 8 transactions, like so:
- # Tx0 -> Tx1 (vout0)
- # \--> Tx2 (vout1) -> Tx3 -> Tx4 -> Tx5 -> Tx6 -> Tx7
- #
- # Mine them in the next block, then generate a new tx8 that spends
- # Tx1 and Tx7, and add to node1's mempool, then disconnect the
- # last block.
-
- # Create tx0 with 2 outputs
- utxo = self.nodes[0].listunspent()
- txid = utxo[0]["txid"]
- value = utxo[0]["amount"]
- vout = utxo[0]["vout"]
-
- send_value = satoshi_round((value - fee) / 2)
- inputs = [{"txid": txid, "vout": vout}]
- outputs = {}
- for _ in range(2):
- outputs[self.nodes[0].getnewaddress()] = send_value
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- txid = self.nodes[0].sendrawtransaction(signedtx["hex"])
- tx0_id = txid
- value = send_value
-
- # Create tx1
- tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1)
-
- # Create tx2-7
- vout = 1
- txid = tx0_id
- for _ in range(6):
- (txid, sent_value) = self.chain_transaction(
- self.nodes[0], txid, vout, value, fee, 1
- )
- vout = 0
- value = sent_value
-
- # Mine these in a block
- self.generate(self.nodes[0], 1)
-
- # Now generate tx8, with a big fee
- inputs = [{"txid": tx1_id, "vout": 0}, {"txid": txid, "vout": 0}]
- outputs = {self.nodes[0].getnewaddress(): send_value + value - 4 * fee}
- rawtx = self.nodes[0].createrawtransaction(inputs, outputs)
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- txid = self.nodes[0].sendrawtransaction(signedtx["hex"])
- self.sync_mempools()
-
- # Now try to disconnect the tip on each node...
- self.nodes[1].invalidateblock(self.nodes[1].getbestblockhash())
- self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
- self.sync_blocks()
-
if __name__ == "__main__":
MempoolPackagesTest().main()
diff --git a/test/functional/mempool_updatefromblock.py b/test/functional/mempool_updatefromblock.py
--- a/test/functional/mempool_updatefromblock.py
+++ b/test/functional/mempool_updatefromblock.py
@@ -17,16 +17,7 @@
class MempoolUpdateFromBlockTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
- self.limit_ancestor_descendant_count = 60
- self.extra_args = [
- [
- "-limitdescendantsize=5000",
- "-limitancestorsize=5000",
- f"-limitancestorcount={self.limit_ancestor_descendant_count}",
- f"-limitdescendantcount={self.limit_ancestor_descendant_count}",
- "-deprecatedrpc=mempool_ancestors_descendants",
- ],
- ]
+ self.ancestor_descendant_count = 60
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
@@ -143,28 +134,16 @@
f"the mempool in {end - start} seconds."
)
- self.log.info(
- "Checking descendants/ancestors properties of all of the"
- " in-mempool transactions..."
- )
- for k, tx in enumerate(tx_id):
- self.log.debug(f"Check transaction #{k}.")
- entry = self.nodes[0].getmempoolentry(tx)
- assert_equal(entry["descendantcount"], size - k)
- assert_equal(entry["descendantsize"], sum(tx_size[k:size]))
- assert_equal(entry["ancestorcount"], k + 1)
- assert_equal(entry["ancestorsize"], sum(tx_size[0 : (k + 1)]))
-
def run_test(self):
# Mine the transactions in batches so we get reorg_depth blocks
# reorg'ed
reorg_depth = 4
self.transaction_graph_test(
- size=self.limit_ancestor_descendant_count,
+ size=self.ancestor_descendant_count,
n_tx_to_mine=range(
0,
- self.limit_ancestor_descendant_count,
- self.limit_ancestor_descendant_count // reorg_depth,
+ self.ancestor_descendant_count,
+ self.ancestor_descendant_count // reorg_depth,
),
)
diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py
--- a/test/functional/rpc_deprecated.py
+++ b/test/functional/rpc_deprecated.py
@@ -42,12 +42,6 @@
"-deprecatedrpc=isfinaltransaction_noerror",
"-deprecatedrpc=getblocktemplate_sigops",
"-deprecatedrpc=softforks",
- # This test checks for the presence of the ancestor count in
- # the listunspent output. However this is only displayed if the
- # count is non-null, which will no longer be the case after
- # wellington activation.
- f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}",
- "-deprecatedrpc=mempool_ancestors_descendants",
],
]
@@ -161,24 +155,6 @@
res = self.nodes[1].getblockchaininfo()
assert_equal(res["softforks"], {})
- txid = self.nodes[0].sendtoaddress(
- self.nodes[0].get_deterministic_priv_key().address, 1000000
- )
- assert txid in self.nodes[0].getrawmempool()
- utxo = self.nodes[0].listunspent(minconf=0, maxconf=0)[0]
- assert "ancestorcount" not in utxo
- assert "ancestorsize" not in utxo
- assert "ancestorfees" not in utxo
-
- txid = self.nodes[1].sendtoaddress(
- self.nodes[1].get_deterministic_priv_key().address, 1000000
- )
- assert txid in self.nodes[1].getrawmempool()
- utxo = self.nodes[1].listunspent(minconf=0, maxconf=0)[0]
- assert "ancestorcount" in utxo
- assert "ancestorsize" in utxo
- assert "ancestorfees" in utxo
-
if __name__ == "__main__":
DeprecatedRpcTest().main()
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -16,12 +16,8 @@
)
from test_framework.wallet_util import test_address
-FAR_IN_THE_FUTURE = 2000000000
-
class WalletTest(BitcoinTestFramework):
- WELLINGTON_FAR_FUTURE = f"-wellingtonactivationtime={int(9e9)}"
-
def set_test_params(self):
self.num_nodes = 4
self.setup_clean_chain = True
@@ -29,10 +25,6 @@
[
"-acceptnonstdtxn=1",
"-whitelist=noban@127.0.0.1",
- # This test tests mempool ancestor chain limits, which are no
- # longer enforced after wellington, so we need to force
- # wellington to activate in the distant future
- f"-wellingtonactivationtime={FAR_IN_THE_FUTURE}",
],
] * self.num_nodes
self.supports_cli = False
@@ -609,82 +601,6 @@
assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1])
assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0)
- # ==Check that wallet prefers to use coins that don't exceed mempool li
-
- # Get all non-zero utxos together
- chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()]
- singletxid = self.nodes[0].sendtoaddress(
- chain_addrs[0], self.nodes[0].getbalance(), "", "", True
- )
- self.generate(self.nodes[0], 1, sync_fun=self.no_op)
- node0_balance = self.nodes[0].getbalance()
- # Split into two chains
- rawtx = self.nodes[0].createrawtransaction(
- [{"txid": singletxid, "vout": 0}],
- {
- chain_addrs[0]: node0_balance / 2 - Decimal("10000"),
- chain_addrs[1]: node0_balance / 2 - Decimal("10000"),
- },
- )
- signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
- singletxid = self.nodes[0].sendrawtransaction(
- hexstring=signedtx["hex"], maxfeerate=0
- )
- self.generate(self.nodes[0], 1, sync_fun=self.no_op)
-
- # Make a long chain of unconfirmed payments without hitting mempool limit
- # Each tx we make leaves only one output of change on a chain 1 longer
- # Since the amount to send is always much less than the outputs, we only ever need one output
- # So we should be able to generate exactly chainlimit txs for each
- # original output
- sending_addr = self.nodes[1].getnewaddress()
- txid_list = []
- for _ in range(chainlimit * 2):
- txid_list.append(
- self.nodes[0].sendtoaddress(sending_addr, Decimal("10000"))
- )
- assert_equal(self.nodes[0].getmempoolinfo()["size"], chainlimit * 2)
- assert_equal(len(txid_list), chainlimit * 2)
-
- # Without walletrejectlongchains, we will still generate a txid
- # The tx will be stored in the wallet but not accepted to the mempool
- extra_txid = self.nodes[0].sendtoaddress(sending_addr, Decimal("10000"))
- assert extra_txid not in self.nodes[0].getrawmempool()
- assert extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()]
- self.nodes[0].abandontransaction(extra_txid)
- total_txs = len(self.nodes[0].listtransactions("*", 99999))
-
- # Try with walletrejectlongchains
- # Double chain limit but require combining inputs, so we pass
- # SelectCoinsMinConf
- self.stop_node(0)
- self.start_node(
- 0,
- self.extra_args[0]
- + ["-walletrejectlongchains", f"-limitancestorcount={str(2 * chainlimit)}"],
- )
-
- # wait until the wallet has submitted all transactions to the mempool
- self.wait_until(lambda: len(self.nodes[0].getrawmempool()) == chainlimit * 2)
-
- # Prevent potential race condition when calling wallet RPCs right after
- # restart
- self.nodes[0].syncwithvalidationinterfacequeue()
-
- node0_balance = self.nodes[0].getbalance()
- # With walletrejectlongchains we will not create the tx and store it in
- # our wallet.
- assert_raises_rpc_error(
- -6,
- "Transaction has too long of a mempool chain",
- self.nodes[0].sendtoaddress,
- sending_addr,
- node0_balance - Decimal("10000"),
- )
-
- # Verify nothing new in wallet
- assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999)))
-
# Test getaddressinfo on external address. Note that these addresses
# are taken from disablewallet.py
assert_raises_rpc_error(