Merge bitcoin/bitcoin#28391: refactor: Simplify CTxMempool/BlockAssembler fields, remove some external mapTx access

4dd94ca18f [refactor] remove access to mapTx in validation_block_tests (TheCharlatan)
d0cd2e804e [refactor] rewrite BlockAssembler inBlock and failedTx as sets of txids (glozow)
55b0939cab scripted-diff: rename vTxHashes to txns_randomized (TheCharlatan)
a03aef9cec [refactor] rewrite vTxHashes as a vector of CTransactionRef (glozow)
938643c3b2 [refactor] remove access to mapTx in validation.cpp (glozow)
333367a940 [txmempool] make CTxMemPoolEntry::lockPoints mutable (glozow)
1bf4855016 [refactor] use CheckPackageLimits for checkChainLimits (glozow)
dbc5bdbf59 [refactor] remove access to mapTx.find in mempool_tests.cpp (glozow)
f80909e7a3 [refactor] remove access to mapTx in blockencodings_tests.cpp (glozow)
8892d6b744 [refactor] remove access to mapTx from rpc/mempool.cpp (glozow)
fad61aa561 [refactor] get wtxid from entry instead of vTxHashes (glozow)
9cd8cafb77 [refactor] use exists() instead of mapTx.find() (glozow)
14804699e5 [refactor] remove access to mapTx from policy/rbf.cpp (glozow)
1c6a73abbd [refactor] Add helper for retrieving mempool entry (TheCharlatan)
453b4813eb [refactor] Add helper for iterating through mempool entries (stickies-v)

Pull request description:

  Motivation
  * It seems preferable to use stdlib data structures instead of boost if they can achieve close to the same thing.
  * Code external to mempool should ideally use its public helper methods instead of accessing `mapTx` or its iterators directly.
  * Reduce the number of complex boost multi index type interactions
  * Also see #28335 for further context/motivation. This PR together with #28385 simplifies that one.

  Overview of things done in this PR:
  * Make `vTxHashes` a vector of transaction references instead of a pair of transaction hash and iterator. The trade off here is that the data is retrieved on the fly with `GetEntry` instead of being cached in `vTxHashes`.
  * Introduce `GetEntry` helper method to replace the more involved `GetIter` where applicable
  * Replace `mapTx` access with `CTxMemPool` helper methods
  * Simplify `checkChainLimits` call in `node/interfaces.cpp`
  * Make `CTxMemPoolEntry`s `lockPoints`mutable such that they can be changed with a const iterator directly instead of going through `mapTx`
  * Make `BlockAssembler`'s `inBlock` and `failedTx` sets of transaction hashes.

ACKs for top commit:
  glozow:
    reACK 4dd94ca
  maflcko:
    re-ACK 4dd94ca18f 👝
  stickies-v:
    re-ACK 4dd94ca18f

Tree-SHA512: c4d043f2186e4fde337591883fac66cade3058173987b49502bd65cecf69207a3df1077f6626809652ab63230013167b7f39a2b39f1c5166959e5495df57065f
This commit is contained in:
fanquake committed 2023-11-13 10:51:41 +00:00
commit dd5f5713bc
14 files changed
+113 -94

No files matched your search

+3 -3
View File
@@ -107,12 +107,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
std::vector<bool> have_txn(txn_available.size()); std::vector<bool> have_txn(txn_available.size());
{ {
LOCK(pool->cs); LOCK(pool->cs);
for (size_t i = 0; i < pool->vTxHashes.size(); i++) { for (const auto& tx : pool->txns_randomized) {
uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first); uint64_t shortid = cmpctblock.GetShortID(tx->GetWitnessHash());
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid); std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
if (idit != shorttxids.end()) { if (idit != shorttxids.end()) {
if (!have_txn[idit->second]) { if (!have_txn[idit->second]) {
txn_available[idit->second] = pool->vTxHashes[i].second->GetSharedTx(); txn_available[idit->second] = tx;
have_txn[idit->second] = true; have_txn[idit->second] = true;
mempool_count++; mempool_count++;
} else { } else {
+5 -3
View File
@@ -83,7 +83,7 @@ private:
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
const int64_t sigOpCost; //!< Total sigop cost const int64_t sigOpCost; //!< Total sigop cost
CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block CAmount m_modified_fee; //!< Used for determining the priority of the transaction for mining in a block
LockPoints lockPoints; //!< Track the height and time at which tx was final mutable LockPoints lockPoints; //!< Track the height and time at which tx was final
// Information about descendants of this transaction that are in the // Information about descendants of this transaction that are in the
// mempool; if we remove this transaction we must remove all of these // mempool; if we remove this transaction we must remove all of these
@@ -151,7 +151,7 @@ public:
} }
// Update the LockPoints after a reorg // Update the LockPoints after a reorg
void UpdateLockPoints(const LockPoints& lp) void UpdateLockPoints(const LockPoints& lp) const
{ {
lockPoints = lp; lockPoints = lp;
} }
@@ -172,8 +172,10 @@ public:
Parents& GetMemPoolParents() const { return m_parents; } Parents& GetMemPoolParents() const { return m_parents; }
Children& GetMemPoolChildren() const { return m_children; } Children& GetMemPoolChildren() const { return m_children; }
mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes mutable size_t idx_randomized; //!< Index in mempool's txns_randomized
mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms mutable Epoch::Marker m_epoch_marker; //!< epoch when last touched, useful for graph algorithms
}; };
using CTxMemPoolEntryRef = CTxMemPoolEntry::CTxMemPoolEntryRef;
#endif // BITCOIN_KERNEL_MEMPOOL_ENTRY_H #endif // BITCOIN_KERNEL_MEMPOOL_ENTRY_H
+6 -5
View File
@@ -648,8 +648,9 @@ public:
{ {
if (!m_node.mempool) return false; if (!m_node.mempool) return false;
LOCK(m_node.mempool->cs); LOCK(m_node.mempool->cs);
auto it = m_node.mempool->GetIter(txid); const auto entry{m_node.mempool->GetEntry(Txid::FromUint256(txid))};
return it && (*it)->GetCountWithDescendants() > 1; if (entry == nullptr) return false;
return entry->GetCountWithDescendants() > 1;
} }
bool broadcastTransaction(const CTransactionRef& tx, bool broadcastTransaction(const CTransactionRef& tx,
const CAmount& max_tx_fee, const CAmount& max_tx_fee,
@@ -702,9 +703,9 @@ public:
if (!m_node.mempool) return true; if (!m_node.mempool) return true;
LockPoints lp; LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, 0, false, 0, lp); CTxMemPoolEntry entry(tx, 0, 0, 0, 0, false, 0, lp);
const CTxMemPool::Limits& limits{m_node.mempool->m_limits};
LOCK(m_node.mempool->cs); LOCK(m_node.mempool->cs);
return m_node.mempool->CalculateMemPoolAncestors(entry, limits).has_value(); std::string err_string;
return m_node.mempool->CheckPackageLimits({tx}, entry.GetTxSize(), err_string);
} }
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
{ {
@@ -806,7 +807,7 @@ public:
{ {
if (!m_node.mempool) return; if (!m_node.mempool) return;
LOCK2(::cs_main, m_node.mempool->cs); LOCK2(::cs_main, m_node.mempool->cs);
for (const CTxMemPoolEntry& entry : m_node.mempool->mapTx) { for (const CTxMemPoolEntry& entry : m_node.mempool->entryAll()) {
notifications.transactionAddedToMempool(entry.GetSharedTx()); notifications.transactionAddedToMempool(entry.GetSharedTx());
} }
} }
+7 -7
View File
@@ -188,7 +188,7 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet)
{ {
for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) { for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) {
// Only test txs not already in the block // Only test txs not already in the block
if (inBlock.count(*iit)) { if (inBlock.count((*iit)->GetSharedTx()->GetHash())) {
testSet.erase(iit++); testSet.erase(iit++);
} else { } else {
iit++; iit++;
@@ -229,7 +229,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
++nBlockTx; ++nBlockTx;
nBlockSigOpsCost += iter->GetSigOpCost(); nBlockSigOpsCost += iter->GetSigOpCost();
nFees += iter->GetFee(); nFees += iter->GetFee();
inBlock.insert(iter); inBlock.insert(iter->GetSharedTx()->GetHash());
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY); bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) { if (fPrintPriority) {
@@ -298,7 +298,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// because some of their txs are already in the block // because some of their txs are already in the block
indexed_modified_transaction_set mapModifiedTx; indexed_modified_transaction_set mapModifiedTx;
// Keep track of entries that failed inclusion, to avoid duplicate work // Keep track of entries that failed inclusion, to avoid duplicate work
CTxMemPool::setEntries failedTx; std::set<Txid> failedTx;
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = mempool.mapTx.get<ancestor_score>().begin(); CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator mi = mempool.mapTx.get<ancestor_score>().begin();
CTxMemPool::txiter iter; CTxMemPool::txiter iter;
@@ -326,7 +326,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
if (mi != mempool.mapTx.get<ancestor_score>().end()) { if (mi != mempool.mapTx.get<ancestor_score>().end()) {
auto it = mempool.mapTx.project<0>(mi); auto it = mempool.mapTx.project<0>(mi);
assert(it != mempool.mapTx.end()); assert(it != mempool.mapTx.end());
if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it)) { if (mapModifiedTx.count(it) || inBlock.count(it->GetSharedTx()->GetHash()) || failedTx.count(it->GetSharedTx()->GetHash())) {
++mi; ++mi;
continue; continue;
} }
@@ -360,7 +360,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't // We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't
// contain anything that is inBlock. // contain anything that is inBlock.
assert(!inBlock.count(iter)); assert(!inBlock.count(iter->GetSharedTx()->GetHash()));
uint64_t packageSize = iter->GetSizeWithAncestors(); uint64_t packageSize = iter->GetSizeWithAncestors();
CAmount packageFees = iter->GetModFeesWithAncestors(); CAmount packageFees = iter->GetModFeesWithAncestors();
@@ -382,7 +382,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// we must erase failed entries so that we can consider the // we must erase failed entries so that we can consider the
// next best entry on the next loop iteration // next best entry on the next loop iteration
mapModifiedTx.get<ancestor_score>().erase(modit); mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter); failedTx.insert(iter->GetSharedTx()->GetHash());
} }
++nConsecutiveFailed; ++nConsecutiveFailed;
@@ -404,7 +404,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
if (!TestPackageTransactions(ancestors)) { if (!TestPackageTransactions(ancestors)) {
if (fUsingModified) { if (fUsingModified) {
mapModifiedTx.get<ancestor_score>().erase(modit); mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter); failedTx.insert(iter->GetSharedTx()->GetHash());
} }
continue; continue;
} }
+1 -1
View File
@@ -142,7 +142,7 @@ private:
uint64_t nBlockTx; uint64_t nBlockTx;
uint64_t nBlockSigOpsCost; uint64_t nBlockSigOpsCost;
CAmount nFees; CAmount nFees;
CTxMemPool::setEntries inBlock; std::unordered_set<Txid, SaltedTxidHasher> inBlock;
// Chain context for the block // Chain context for the block
int nHeight; int nHeight;
+2 -1
View File
@@ -12,6 +12,7 @@
#include <tinyformat.h> #include <tinyformat.h>
#include <txmempool.h> #include <txmempool.h>
#include <uint256.h> #include <uint256.h>
#include <util/check.h>
#include <util/moneystr.h> #include <util/moneystr.h>
#include <util/rbf.h> #include <util/rbf.h>
@@ -35,7 +36,7 @@ RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
// If all the inputs have nSequence >= maxint-1, it still might be // If all the inputs have nSequence >= maxint-1, it still might be
// signaled for RBF if any unconfirmed parents have signaled. // signaled for RBF if any unconfirmed parents have signaled.
const CTxMemPoolEntry entry{*pool.mapTx.find(tx.GetHash())}; const auto& entry{*Assert(pool.GetEntry(tx.GetHash()))};
auto ancestors{pool.AssumeCalculateMemPoolAncestors(__func__, entry, CTxMemPool::Limits::NoLimits(), auto ancestors{pool.AssumeCalculateMemPoolAncestors(__func__, entry, CTxMemPool::Limits::NoLimits(),
/*fSearchForParents=*/false)}; /*fSearchForParents=*/false)};
+14 -18
View File
@@ -290,7 +290,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("descendantsize", e.GetSizeWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants());
info.pushKV("ancestorcount", e.GetCountWithAncestors()); info.pushKV("ancestorcount", e.GetCountWithAncestors());
info.pushKV("ancestorsize", e.GetSizeWithAncestors()); info.pushKV("ancestorsize", e.GetSizeWithAncestors());
info.pushKV("wtxid", pool.vTxHashes[e.vTxHashesIdx].first.ToString()); info.pushKV("wtxid", e.GetTx().GetWitnessHash().ToString());
UniValue fees(UniValue::VOBJ); UniValue fees(UniValue::VOBJ);
fees.pushKV("base", ValueFromAmount(e.GetFee())); fees.pushKV("base", ValueFromAmount(e.GetFee()));
@@ -316,9 +316,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("depends", depends); info.pushKV("depends", depends);
UniValue spent(UniValue::VARR); UniValue spent(UniValue::VARR);
const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash()); for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) {
const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
for (const CTxMemPoolEntry& child : children) {
spent.push_back(child.GetTx().GetHash().ToString()); spent.push_back(child.GetTx().GetHash().ToString());
} }
@@ -345,14 +343,13 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempoo
} }
LOCK(pool.cs); LOCK(pool.cs);
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
for (const CTxMemPoolEntry& e : pool.mapTx) { for (const CTxMemPoolEntry& e : pool.entryAll()) {
const uint256& hash = e.GetTx().GetHash();
UniValue info(UniValue::VOBJ); UniValue info(UniValue::VOBJ);
entryToJSON(pool, info, e); entryToJSON(pool, info, e);
// Mempool has unique entries so there is no advantage in using // Mempool has unique entries so there is no advantage in using
// UniValue::pushKV, which checks if the key already exists in O(N). // UniValue::pushKV, which checks if the key already exists in O(N).
// UniValue::pushKVEnd is used instead which currently is O(1). // UniValue::pushKVEnd is used instead which currently is O(1).
o.pushKVEnd(hash.ToString(), info); o.pushKVEnd(e.GetTx().GetHash().ToString(), info);
} }
return o; return o;
} else { } else {
@@ -461,12 +458,12 @@ static RPCHelpMan getmempoolancestors()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context); const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
if (it == mempool.mapTx.end()) { if (entry == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
} }
auto ancestors{mempool.AssumeCalculateMemPoolAncestors(self.m_name, *it, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false)}; auto ancestors{mempool.AssumeCalculateMemPoolAncestors(self.m_name, *entry, CTxMemPool::Limits::NoLimits(), /*fSearchForParents=*/false)};
if (!fVerbose) { if (!fVerbose) {
UniValue o(UniValue::VARR); UniValue o(UniValue::VARR);
@@ -522,15 +519,15 @@ static RPCHelpMan getmempooldescendants()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context); const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); const auto it{mempool.GetIter(hash)};
if (it == mempool.mapTx.end()) { if (!it) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
} }
CTxMemPool::setEntries setDescendants; CTxMemPool::setEntries setDescendants;
mempool.CalculateDescendants(it, setDescendants); mempool.CalculateDescendants(*it, setDescendants);
// CTxMemPool::CalculateDescendants will include the given tx // CTxMemPool::CalculateDescendants will include the given tx
setDescendants.erase(it); setDescendants.erase(*it);
if (!fVerbose) { if (!fVerbose) {
UniValue o(UniValue::VARR); UniValue o(UniValue::VARR);
@@ -574,14 +571,13 @@ static RPCHelpMan getmempoolentry()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context); const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
if (it == mempool.mapTx.end()) { if (entry == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
} }
const CTxMemPoolEntry &e = *it;
UniValue info(UniValue::VOBJ); UniValue info(UniValue::VOBJ);
entryToJSON(mempool, info, e); entryToJSON(mempool, info, *entry);
return info; return info;
}, },
}; };
+14 -14
View File
@@ -51,8 +51,8 @@ static CBlock BuildBlockTestCase() {
} }
// Number of shared use_counts we expect for a tx we haven't touched // Number of shared use_counts we expect for a tx we haven't touched
// (block + mempool + our copy from the GetSharedTx call) // (block + mempool entry + mempool txns_randomized + our copy from the GetSharedTx call)
constexpr long SHARED_TX_OFFSET{3}; constexpr long SHARED_TX_OFFSET{4};
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
{ {
@@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
LOCK2(cs_main, pool.cs); LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[2])); pool.addUnchecked(entry.FromTx(block.vtx[2]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
// Do a simple ShortTxIDs RT // Do a simple ShortTxIDs RT
{ {
@@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK(!partialBlock.IsTxAvailable(1)); BOOST_CHECK(!partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
size_t poolSize = pool.size(); size_t poolSize = pool.size();
pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED); pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
LOCK2(cs_main, pool.cs); LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[2])); pool.addUnchecked(entry.FromTx(block.vtx[2]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash; uint256 txhash;
@@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock
CBlock block2; CBlock block2;
{ {
@@ -185,7 +185,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that
partialBlock = tmp; partialBlock = tmp;
} }
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2
bool mutated; bool mutated;
BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated)); BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated));
@@ -196,15 +196,15 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString()); BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString());
BOOST_CHECK(!mutated); BOOST_CHECK(!mutated);
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3 BOOST_CHECK_EQUAL(pool.get(block.vtx[2]->GetHash()).use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3
txhash = block.vtx[2]->GetHash(); txhash = block.vtx[2]->GetHash();
block.vtx.clear(); block.vtx.clear();
block2.vtx.clear(); block2.vtx.clear();
block3.vtx.clear(); block3.vtx.clear();
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
} }
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
} }
BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
@@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
LOCK2(cs_main, pool.cs); LOCK2(cs_main, pool.cs);
pool.addUnchecked(entry.FromTx(block.vtx[1])); pool.addUnchecked(entry.FromTx(block.vtx[1]));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 0);
uint256 txhash; uint256 txhash;
@@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(1));
BOOST_CHECK( partialBlock.IsTxAvailable(2)); BOOST_CHECK( partialBlock.IsTxAvailable(2));
BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); BOOST_CHECK_EQUAL(pool.get(block.vtx[1]->GetHash()).use_count(), SHARED_TX_OFFSET + 1);
CBlock block2; CBlock block2;
PartiallyDownloadedBlock partialBlockCopy = partialBlock; PartiallyDownloadedBlock partialBlockCopy = partialBlock;
@@ -253,9 +253,9 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
txhash = block.vtx[1]->GetHash(); txhash = block.vtx[1]->GetHash();
block.vtx.clear(); block.vtx.clear();
block2.vtx.clear(); block2.vtx.clear();
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block.
} }
BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block BOOST_CHECK_EQUAL(pool.get(txhash).use_count(), SHARED_TX_OFFSET - 1); // -1 because of block
} }
BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest)
+7 -7
View File
@@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
CheckSort<descendant_score>(pool, sortedOrder); CheckSort<descendant_score>(pool, sortedOrder);
CTxMemPool::setEntries setAncestors; CTxMemPool::setEntries setAncestors;
setAncestors.insert(pool.mapTx.find(tx6.GetHash())); setAncestors.insert(pool.GetIter(tx6.GetHash()).value());
CMutableTransaction tx7 = CMutableTransaction(); CMutableTransaction tx7 = CMutableTransaction();
tx7.vin.resize(1); tx7.vin.resize(1);
tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0); tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
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.GetHash())); setAncestors.insert(pool.GetIter(tx7.GetHash()).value());
pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8), setAncestors); pool.addUnchecked(entry.Fee(0LL).Time(NodeSeconds{2s}).FromTx(tx8), setAncestors);
// Now tx8 should be sorted low, but tx6/tx both high // Now tx8 should be sorted low, but tx6/tx both high
@@ -247,8 +247,8 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
std::vector<std::string> snapshotOrder = sortedOrder; std::vector<std::string> snapshotOrder = sortedOrder;
setAncestors.insert(pool.mapTx.find(tx8.GetHash())); setAncestors.insert(pool.GetIter(tx8.GetHash()).value());
setAncestors.insert(pool.mapTx.find(tx9.GetHash())); setAncestors.insert(pool.GetIter(tx9.GetHash()).value());
/* tx10 depends on tx8 and tx9 and has a high fee*/ /* tx10 depends on tx8 and tx9 and has a high fee*/
CMutableTransaction tx10 = CMutableTransaction(); CMutableTransaction tx10 = CMutableTransaction();
tx10.vin.resize(2); tx10.vin.resize(2);
@@ -291,11 +291,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 10U); BOOST_CHECK_EQUAL(pool.size(), 10U);
// Now try removing tx10 and verify the sort order returns to normal // Now try removing tx10 and verify the sort order returns to normal
pool.removeRecursive(pool.mapTx.find(tx10.GetHash())->GetTx(), REMOVAL_REASON_DUMMY); pool.removeRecursive(*Assert(pool.get(tx10.GetHash())), REMOVAL_REASON_DUMMY);
CheckSort<descendant_score>(pool, snapshotOrder); CheckSort<descendant_score>(pool, snapshotOrder);
pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), REMOVAL_REASON_DUMMY); pool.removeRecursive(*Assert(pool.get(tx9.GetHash())), REMOVAL_REASON_DUMMY);
pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), REMOVAL_REASON_DUMMY); pool.removeRecursive(*Assert(pool.get(tx8.GetHash())), REMOVAL_REASON_DUMMY);
} }
BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
+10 -10
View File
@@ -94,7 +94,7 @@ BOOST_FIXTURE_TEST_CASE(miniminer_negative, TestChain100Setup)
const CFeeRate feerate_zero(0); const CFeeRate feerate_zero(0);
mini_miner_target0.BuildMockTemplate(feerate_zero); mini_miner_target0.BuildMockTemplate(feerate_zero);
// Check the quit condition: // Check the quit condition:
BOOST_CHECK(negative_modified_fees < feerate_zero.GetFee(pool.GetIter(tx_mod_negative->GetHash()).value()->GetTxSize())); BOOST_CHECK(negative_modified_fees < feerate_zero.GetFee(Assert(pool.GetEntry(tx_mod_negative->GetHash()))->GetTxSize()));
BOOST_CHECK(mini_miner_target0.GetMockTemplateTxids().empty()); BOOST_CHECK(mini_miner_target0.GetMockTemplateTxids().empty());
// With no target feerate, the template includes all transactions, even negative feerate ones. // With no target feerate, the template includes all transactions, even negative feerate ones.
@@ -179,9 +179,9 @@ BOOST_FIXTURE_TEST_CASE(miniminer_1p1c, TestChain100Setup)
}; };
std::map<uint256, TxDimensions> tx_dims; std::map<uint256, TxDimensions> tx_dims;
for (const auto& tx : all_transactions) { for (const auto& tx : all_transactions) {
const auto it = pool.GetIter(tx->GetHash()).value(); const auto& entry{*Assert(pool.GetEntry(tx->GetHash()))};
tx_dims.emplace(tx->GetHash(), TxDimensions{it->GetTxSize(), it->GetModifiedFee(), tx_dims.emplace(tx->GetHash(), TxDimensions{entry.GetTxSize(), entry.GetModifiedFee(),
CFeeRate(it->GetModifiedFee(), it->GetTxSize())}); CFeeRate(entry.GetModifiedFee(), entry.GetTxSize())});
} }
const std::vector<CFeeRate> various_normal_feerates({CFeeRate(0), CFeeRate(500), CFeeRate(999), const std::vector<CFeeRate> various_normal_feerates({CFeeRate(0), CFeeRate(500), CFeeRate(999),
@@ -447,15 +447,15 @@ BOOST_FIXTURE_TEST_CASE(miniminer_overlap, TestChain100Setup)
// tx3's feerate is lower than tx2's. same fee, different weight. // tx3's feerate is lower than tx2's. same fee, different weight.
BOOST_CHECK(tx2_feerate > tx3_feerate); BOOST_CHECK(tx2_feerate > tx3_feerate);
const auto tx3_anc_feerate = CFeeRate(low_fee + med_fee + high_fee + high_fee, tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]); const auto tx3_anc_feerate = CFeeRate(low_fee + med_fee + high_fee + high_fee, tx_vsizes[0] + tx_vsizes[1] + tx_vsizes[2] + tx_vsizes[3]);
const auto tx3_iter = pool.GetIter(tx3->GetHash()); const auto& tx3_entry{*Assert(pool.GetEntry(tx3->GetHash()))};
BOOST_CHECK(tx3_anc_feerate == CFeeRate(tx3_iter.value()->GetModFeesWithAncestors(), tx3_iter.value()->GetSizeWithAncestors())); BOOST_CHECK(tx3_anc_feerate == CFeeRate(tx3_entry.GetModFeesWithAncestors(), tx3_entry.GetSizeWithAncestors()));
const auto tx4_feerate = CFeeRate(high_fee, tx_vsizes[4]); const auto tx4_feerate = CFeeRate(high_fee, tx_vsizes[4]);
const auto tx6_anc_feerate = CFeeRate(high_fee + low_fee + med_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]); const auto tx6_anc_feerate = CFeeRate(high_fee + low_fee + med_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[6]);
const auto tx6_iter = pool.GetIter(tx6->GetHash()); const auto& tx6_entry{*Assert(pool.GetEntry(tx6->GetHash()))};
BOOST_CHECK(tx6_anc_feerate == CFeeRate(tx6_iter.value()->GetModFeesWithAncestors(), tx6_iter.value()->GetSizeWithAncestors())); BOOST_CHECK(tx6_anc_feerate == CFeeRate(tx6_entry.GetModFeesWithAncestors(), tx6_entry.GetSizeWithAncestors()));
const auto tx7_anc_feerate = CFeeRate(high_fee + low_fee + high_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]); const auto tx7_anc_feerate = CFeeRate(high_fee + low_fee + high_fee, tx_vsizes[4] + tx_vsizes[5] + tx_vsizes[7]);
const auto tx7_iter = pool.GetIter(tx7->GetHash()); const auto& tx7_entry{*Assert(pool.GetEntry(tx7->GetHash()))};
BOOST_CHECK(tx7_anc_feerate == CFeeRate(tx7_iter.value()->GetModFeesWithAncestors(), tx7_iter.value()->GetSizeWithAncestors())); BOOST_CHECK(tx7_anc_feerate == CFeeRate(tx7_entry.GetModFeesWithAncestors(), tx7_entry.GetSizeWithAncestors()));
BOOST_CHECK(tx4_feerate > tx6_anc_feerate); BOOST_CHECK(tx4_feerate > tx6_anc_feerate);
BOOST_CHECK(tx4_feerate > tx7_anc_feerate); BOOST_CHECK(tx4_feerate > tx7_anc_feerate);
+3 -4
View File
@@ -283,8 +283,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Check that all txs are in the pool // Check that all txs are in the pool
{ {
LOCK(m_node.mempool->cs); BOOST_CHECK_EQUAL(m_node.mempool->size(), txs.size());
BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
} }
// Run a thread that simulates an RPC caller that is polling while // Run a thread that simulates an RPC caller that is polling while
@@ -295,7 +294,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// not some intermediate amount. // not some intermediate amount.
while (true) { while (true) {
LOCK(m_node.mempool->cs); LOCK(m_node.mempool->cs);
if (m_node.mempool->mapTx.size() == 0) { if (m_node.mempool->size() == 0) {
// We are done with the reorg // We are done with the reorg
break; break;
} }
@@ -304,7 +303,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// be atomic. So the caller assumes that the returned mempool // be atomic. So the caller assumes that the returned mempool
// is consistent. That is, it has all txs that were there // is consistent. That is, it has all txs that were there
// before the reorg. // before the reorg.
assert(m_node.mempool->mapTx.size() == txs.size()); assert(m_node.mempool->size() == txs.size());
continue; continue;
} }
LOCK(cs_main); LOCK(cs_main);
+31 -10
View File
@@ -480,8 +480,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
minerPolicyEstimator->processTransaction(entry, validFeeEstimate); minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
} }
vTxHashes.emplace_back(tx.GetWitnessHash(), newit); txns_randomized.emplace_back(newit->GetSharedTx());
newit->vTxHashesIdx = vTxHashes.size() - 1; newit->idx_randomized = txns_randomized.size() - 1;
TRACE3(mempool, added, TRACE3(mempool, added,
entry.GetTx().GetHash().data(), entry.GetTx().GetHash().data(),
@@ -517,14 +517,16 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
RemoveUnbroadcastTx(hash, true /* add logging because unchecked */ ); RemoveUnbroadcastTx(hash, true /* add logging because unchecked */ );
if (vTxHashes.size() > 1) { if (txns_randomized.size() > 1) {
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back()); // Update idx_randomized of the to-be-moved entry.
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx; Assert(GetEntry(txns_randomized.back()->GetHash()))->idx_randomized = it->idx_randomized;
vTxHashes.pop_back(); // Remove entry from txns_randomized by replacing it with the back and deleting the back.
if (vTxHashes.size() * 2 < vTxHashes.capacity()) txns_randomized[it->idx_randomized] = std::move(txns_randomized.back());
vTxHashes.shrink_to_fit(); txns_randomized.pop_back();
if (txns_randomized.size() * 2 < txns_randomized.capacity())
txns_randomized.shrink_to_fit();
} else } else
vTxHashes.clear(); txns_randomized.clear();
totalTxSize -= it->GetTxSize(); totalTxSize -= it->GetTxSize();
m_total_fee -= it->GetFee(); m_total_fee -= it->GetFee();
@@ -836,6 +838,18 @@ static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()}; return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()};
} }
std::vector<CTxMemPoolEntryRef> CTxMemPool::entryAll() const
{
AssertLockHeld(cs);
std::vector<CTxMemPoolEntryRef> ret;
ret.reserve(mapTx.size());
for (const auto& it : GetSortedDepthAndScore()) {
ret.emplace_back(*it);
}
return ret;
}
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
{ {
LOCK(cs); LOCK(cs);
@@ -850,6 +864,13 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
return ret; return ret;
} }
const CTxMemPoolEntry* CTxMemPool::GetEntry(const Txid& txid) const
{
AssertLockHeld(cs);
const auto i = mapTx.find(txid);
return i == mapTx.end() ? nullptr : &(*i);
}
CTransactionRef CTxMemPool::get(const uint256& hash) const CTransactionRef CTxMemPool::get(const uint256& hash) const
{ {
LOCK(cs); LOCK(cs);
@@ -1033,7 +1054,7 @@ void CCoinsViewMemPool::Reset()
size_t CTxMemPool::DynamicMemoryUsage() const { size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs); LOCK(cs);
// Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(vTxHashes) + cachedInnerUsage; return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + cachedInnerUsage;
} }
void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) { void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) {
+4 -1
View File
@@ -392,7 +392,7 @@ public:
indexed_transaction_set mapTx GUARDED_BY(cs); indexed_transaction_set mapTx GUARDED_BY(cs);
using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator; using txiter = indexed_transaction_set::nth_index<0>::type::const_iterator;
std::vector<std::pair<uint256, txiter>> vTxHashes GUARDED_BY(cs); //!< All tx witness hashes/entries in mapTx, in random order std::vector<CTransactionRef> txns_randomized GUARDED_BY(cs); //!< All transactions in mapTx, in random order
typedef std::set<txiter, CompareIteratorByHash> setEntries; typedef std::set<txiter, CompareIteratorByHash> setEntries;
@@ -684,6 +684,8 @@ public:
return (mapTx.count(gtxid.GetHash()) != 0); return (mapTx.count(gtxid.GetHash()) != 0);
} }
const CTxMemPoolEntry* GetEntry(const Txid& txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs);
CTransactionRef get(const uint256& hash) const; CTransactionRef get(const uint256& hash) const;
txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs) txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
{ {
@@ -695,6 +697,7 @@ public:
/** Returns info for a transaction if its entry_sequence < last_sequence */ /** Returns info for a transaction if its entry_sequence < last_sequence */
TxMempoolInfo info_for_relay(const GenTxid& gtxid, uint64_t last_sequence) const; TxMempoolInfo info_for_relay(const GenTxid& gtxid, uint64_t last_sequence) const;
std::vector<CTxMemPoolEntryRef> entryAll() const EXCLUSIVE_LOCKS_REQUIRED(cs);
std::vector<TxMempoolInfo> infoAll() const; std::vector<TxMempoolInfo> infoAll() const;
size_t DynamicMemoryUsage() const; size_t DynamicMemoryUsage() const;
+6 -10
View File
@@ -353,7 +353,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)}; const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)};
if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) { if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) {
// Now update the mempool entry lockpoints as well. // Now update the mempool entry lockpoints as well.
m_mempool->mapTx.modify(it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints(*new_lock_points); }); it->UpdateLockPoints(*new_lock_points);
} else { } else {
return true; return true;
} }
@@ -362,9 +362,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
// If the transaction spends any coinbase outputs, it must be mature. // If the transaction spends any coinbase outputs, it must be mature.
if (it->GetSpendsCoinbase()) { if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) { for (const CTxIn& txin : tx.vin) {
auto it2 = m_mempool->mapTx.find(txin.prevout.hash); if (m_mempool->exists(GenTxid::Txid(txin.prevout.hash))) continue;
if (it2 != m_mempool->mapTx.end())
continue;
const Coin& coin{CoinsTip().AccessCoin(txin.prevout)}; const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
assert(!coin.IsSpent()); assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1}; const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
@@ -1506,9 +1504,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// transactions that are already in the mempool, and only call AcceptMultipleTransactions() with // transactions that are already in the mempool, and only call AcceptMultipleTransactions() with
// the new transactions. This ensures we don't double-count transaction counts and sizes when // the new transactions. This ensures we don't double-count transaction counts and sizes when
// checking ancestor/descendant limits, or double-count transaction fees for fee-related policy. // checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
auto iter = m_pool.GetIter(txid); const auto& entry{*Assert(m_pool.GetEntry(txid))};
assert(iter != std::nullopt); results_final.emplace(wtxid, MempoolAcceptResult::MempoolTx(entry.GetTxSize(), entry.GetFee()));
results_final.emplace(wtxid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee()));
} else if (m_pool.exists(GenTxid::Txid(txid))) { } else if (m_pool.exists(GenTxid::Txid(txid))) {
// Transaction with the same non-witness data but different witness (same txid, // Transaction with the same non-witness data but different witness (same txid,
// different wtxid) already exists in the mempool. // different wtxid) already exists in the mempool.
@@ -1517,10 +1514,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// transaction for the mempool one. Note that we are ignoring the validity of the // transaction for the mempool one. Note that we are ignoring the validity of the
// package transaction passed in. // package transaction passed in.
// TODO: allow witness replacement in packages. // TODO: allow witness replacement in packages.
auto iter = m_pool.GetIter(txid); const auto& entry{*Assert(m_pool.GetEntry(txid))};
assert(iter != std::nullopt);
// Provide the wtxid of the mempool tx so that the caller can look it up in the mempool. // Provide the wtxid of the mempool tx so that the caller can look it up in the mempool.
results_final.emplace(wtxid, MempoolAcceptResult::MempoolTxDifferentWitness(iter.value()->GetTx().GetWitnessHash())); results_final.emplace(wtxid, MempoolAcceptResult::MempoolTxDifferentWitness(entry.GetTx().GetWitnessHash()));
} else { } else {
// Transaction does not already exist in the mempool. // Transaction does not already exist in the mempool.
// Try submitting the transaction on its own. // Try submitting the transaction on its own.