Merge dd5f5713bc into merged_master (Bitcoin PR bitcoin/bitcoin#28391)

This commit is contained in:
Byron Hambly 2025-11-06 11:13:40 +02:00
commit d55f2b27cb
No known key found for this signature in database
GPG key ID: DE8F6EA20A661697
14 changed files with 113 additions and 94 deletions

View file

@ -113,12 +113,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
std::vector<bool> have_txn(txn_available.size());
{
LOCK(pool->cs);
for (size_t i = 0; i < pool->vTxHashes.size(); i++) {
uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first);
for (const auto& tx : pool->txns_randomized) {
uint64_t shortid = cmpctblock.GetShortID(tx->GetWitnessHash());
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
if (idit != shorttxids.end()) {
if (!have_txn[idit->second]) {
txn_available[idit->second] = pool->vTxHashes[i].second->GetSharedTx();
txn_available[idit->second] = tx;
have_txn[idit->second] = true;
mempool_count++;
} else {

View file

@ -84,7 +84,7 @@ private:
const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
const int64_t sigOpCost; //!< Total sigop cost
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
// mempool; if we remove this transaction we must remove all of these
@ -165,7 +165,7 @@ public:
}
// Update the LockPoints after a reorg
void UpdateLockPoints(const LockPoints& lp)
void UpdateLockPoints(const LockPoints& lp) const
{
lockPoints = lp;
}
@ -187,11 +187,13 @@ public:
Parents& GetMemPoolParents() const { return m_parents; }
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
// ELEMENTS:
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
};
using CTxMemPoolEntryRef = CTxMemPoolEntry::CTxMemPoolEntryRef;
#endif // BITCOIN_KERNEL_MEMPOOL_ENTRY_H

View file

@ -649,8 +649,9 @@ public:
{
if (!m_node.mempool) return false;
LOCK(m_node.mempool->cs);
auto it = m_node.mempool->GetIter(txid);
return it && (*it)->GetCountWithDescendants() > 1;
const auto entry{m_node.mempool->GetEntry(Txid::FromUint256(txid))};
if (entry == nullptr) return false;
return entry->GetCountWithDescendants() > 1;
}
bool broadcastTransaction(const CTransactionRef& tx,
const CAmount& max_tx_fee,
@ -704,9 +705,9 @@ public:
LockPoints lp;
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
CTxMemPoolEntry entry(tx, 0, 0, 0, 0, false, 0, lp, setPeginsSpent);
const CTxMemPool::Limits& limits{m_node.mempool->m_limits};
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
{
@ -808,7 +809,7 @@ public:
{
if (!m_node.mempool) return;
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());
}
}

View file

@ -241,7 +241,7 @@ void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries& testSet)
{
for (CTxMemPool::setEntries::iterator iit = testSet.begin(); iit != testSet.end(); ) {
// Only test txs not already in the block
if (inBlock.count(*iit)) {
if (inBlock.count((*iit)->GetSharedTx()->GetHash())) {
testSet.erase(iit++);
} else {
iit++;
@ -282,7 +282,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
++nBlockTx;
nBlockSigOpsCost += iter->GetSigOpCost();
nFees += iter->GetFee();
inBlock.insert(iter);
inBlock.insert(iter->GetSharedTx()->GetHash());
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) {
@ -351,7 +351,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// because some of their txs are already in the block
indexed_modified_transaction_set mapModifiedTx;
// Keep track of entries that failed inclusion, to avoid duplicate work
CTxMemPool::setEntries failedTx;
std::set<Txid> failedTx;
CTxMemPool::indexed_transaction_set::index<confidential_score>::type::iterator mi = mempool.mapTx.get<confidential_score>().begin();
CTxMemPool::txiter iter;
@ -379,7 +379,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
if (mi != mempool.mapTx.get<confidential_score>().end()) {
auto it = mempool.mapTx.project<0>(mi);
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;
continue;
}
@ -419,7 +419,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't
// contain anything that is inBlock.
assert(!inBlock.count(iter));
assert(!inBlock.count(iter->GetSharedTx()->GetHash()));
uint64_t packageSize = iter->GetSizeWithAncestors();
CAmount packageFees = iter->GetModFeesWithAncestors();
@ -449,7 +449,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
// we must erase failed entries so that we can consider the
// next best entry on the next loop iteration
mapModifiedTx.get<confidential_score>().erase(modit);
failedTx.insert(iter);
failedTx.insert(iter->GetSharedTx()->GetHash());
}
++nConsecutiveFailed;
@ -471,7 +471,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
if (!TestPackageTransactions(ancestors)) {
if (fUsingModified) {
mapModifiedTx.get<confidential_score>().erase(modit);
failedTx.insert(iter);
failedTx.insert(iter->GetSharedTx()->GetHash());
}
continue;
}

View file

@ -154,7 +154,7 @@ private:
uint64_t nBlockTx;
uint64_t nBlockSigOpsCost;
CAmount nFees;
CTxMemPool::setEntries inBlock;
std::unordered_set<Txid, SaltedTxidHasher> inBlock;
// Chain context for the block
int nHeight;

View file

@ -12,6 +12,7 @@
#include <tinyformat.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <util/moneystr.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
// 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(),
/*fSearchForParents=*/false)};

View file

@ -301,7 +301,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("descendantsize", e.GetSizeWithDescendants());
info.pushKV("ancestorcount", e.GetCountWithAncestors());
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);
fees.pushKV("base", ValueFromAmount(e.GetFee()));
@ -327,9 +327,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool
info.pushKV("depends", depends);
UniValue spent(UniValue::VARR);
const CTxMemPool::txiter& it = pool.mapTx.find(tx.GetHash());
const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst();
for (const CTxMemPoolEntry& child : children) {
for (const CTxMemPoolEntry& child : e.GetMemPoolChildrenConst()) {
spent.push_back(child.GetTx().GetHash().ToString());
}
@ -356,14 +354,13 @@ UniValue MempoolToJSON(const CTxMemPool& pool, bool verbose, bool include_mempoo
}
LOCK(pool.cs);
UniValue o(UniValue::VOBJ);
for (const CTxMemPoolEntry& e : pool.mapTx) {
const uint256& hash = e.GetTx().GetHash();
for (const CTxMemPoolEntry& e : pool.entryAll()) {
UniValue info(UniValue::VOBJ);
entryToJSON(pool, info, e);
// Mempool has unique entries so there is no advantage in using
// UniValue::pushKV, which checks if the key already exists in O(N).
// UniValue::pushKVEnd is used instead which currently is O(1).
o.pushKVEnd(hash.ToString(), info);
o.pushKVEnd(e.GetTx().GetHash().ToString(), info);
}
return o;
} else {
@ -472,12 +469,12 @@ static RPCHelpMan getmempoolancestors()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
if (entry == nullptr) {
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) {
UniValue o(UniValue::VARR);
@ -533,15 +530,15 @@ static RPCHelpMan getmempooldescendants()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
const auto it{mempool.GetIter(hash)};
if (!it) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
}
CTxMemPool::setEntries setDescendants;
mempool.CalculateDescendants(it, setDescendants);
mempool.CalculateDescendants(*it, setDescendants);
// CTxMemPool::CalculateDescendants will include the given tx
setDescendants.erase(it);
setDescendants.erase(*it);
if (!fVerbose) {
UniValue o(UniValue::VARR);
@ -585,14 +582,13 @@ static RPCHelpMan getmempoolentry()
const CTxMemPool& mempool = EnsureAnyMemPool(request.context);
LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash);
if (it == mempool.mapTx.end()) {
const auto entry{mempool.GetEntry(Txid::FromUint256(hash))};
if (entry == nullptr) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not in mempool");
}
const CTxMemPoolEntry &e = *it;
UniValue info(UniValue::VOBJ);
entryToJSON(mempool, info, e);
entryToJSON(mempool, info, *entry);
return info;
},
};

View file

@ -51,8 +51,8 @@ static CBlock BuildBlockTestCase() {
}
// Number of shared use_counts we expect for a tx we haven't touched
// (block + mempool + our copy from the GetSharedTx call)
constexpr long SHARED_TX_OFFSET{3};
// (block + mempool entry + mempool txns_randomized + our copy from the GetSharedTx call)
constexpr long SHARED_TX_OFFSET{4};
BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
{
@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
LOCK2(cs_main, pool.cs);
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
{
@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest)
BOOST_CHECK(!partialBlock.IsTxAvailable(1));
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();
pool.removeRecursive(*block.vtx[2], MemPoolRemovalReason::REPLACED);
@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
LOCK2(cs_main, pool.cs);
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;
@ -170,7 +170,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1));
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;
{
@ -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 = 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;
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(!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();
block.vtx.clear();
block2.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)
@ -215,7 +215,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
LOCK2(cs_main, pool.cs);
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;
@ -240,7 +240,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
BOOST_CHECK( partialBlock.IsTxAvailable(1));
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;
PartiallyDownloadedBlock partialBlockCopy = partialBlock;
@ -253,9 +253,9 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest)
txhash = block.vtx[1]->GetHash();
block.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)

View file

@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
CheckSort<descendant_score>(pool, sortedOrder);
CTxMemPool::setEntries setAncestors;
setAncestors.insert(pool.mapTx.find(tx6.GetHash()));
setAncestors.insert(pool.GetIter(tx6.GetHash()).value());
CMutableTransaction tx7 = CMutableTransaction();
tx7.vin.resize(1);
tx7.vin[0].prevout = COutPoint(tx6.GetHash(), 0);
@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
tx8.vout.resize(1);
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
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);
// Now tx8 should be sorted low, but tx6/tx both high
@ -248,8 +248,8 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
std::vector<std::string> snapshotOrder = sortedOrder;
setAncestors.insert(pool.mapTx.find(tx8.GetHash()));
setAncestors.insert(pool.mapTx.find(tx9.GetHash()));
setAncestors.insert(pool.GetIter(tx8.GetHash()).value());
setAncestors.insert(pool.GetIter(tx9.GetHash()).value());
/* tx10 depends on tx8 and tx9 and has a high fee*/
CMutableTransaction tx10 = CMutableTransaction();
tx10.vin.resize(2);
@ -292,11 +292,11 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest)
BOOST_CHECK_EQUAL(pool.size(), 10U);
// 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);
pool.removeRecursive(pool.mapTx.find(tx9.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
pool.removeRecursive(pool.mapTx.find(tx8.GetHash())->GetTx(), REMOVAL_REASON_DUMMY);
pool.removeRecursive(*Assert(pool.get(tx9.GetHash())), REMOVAL_REASON_DUMMY);
pool.removeRecursive(*Assert(pool.get(tx8.GetHash())), REMOVAL_REASON_DUMMY);
}
BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)

View file

@ -94,7 +94,7 @@ BOOST_FIXTURE_TEST_CASE(miniminer_negative, TestChain100Setup)
const CFeeRate feerate_zero(0);
mini_miner_target0.BuildMockTemplate(feerate_zero);
// 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());
// 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;
for (const auto& tx : all_transactions) {
const auto it = pool.GetIter(tx->GetHash()).value();
tx_dims.emplace(tx->GetHash(), TxDimensions{it->GetTxSize(), it->GetModifiedFee(),
CFeeRate(it->GetModifiedFee(), it->GetTxSize())});
const auto& entry{*Assert(pool.GetEntry(tx->GetHash()))};
tx_dims.emplace(tx->GetHash(), TxDimensions{entry.GetTxSize(), entry.GetModifiedFee(),
CFeeRate(entry.GetModifiedFee(), entry.GetTxSize())});
}
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.
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_iter = pool.GetIter(tx3->GetHash());
BOOST_CHECK(tx3_anc_feerate == CFeeRate(tx3_iter.value()->GetModFeesWithAncestors(), tx3_iter.value()->GetSizeWithAncestors()));
const auto& tx3_entry{*Assert(pool.GetEntry(tx3->GetHash()))};
BOOST_CHECK(tx3_anc_feerate == CFeeRate(tx3_entry.GetModFeesWithAncestors(), tx3_entry.GetSizeWithAncestors()));
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_iter = pool.GetIter(tx6->GetHash());
BOOST_CHECK(tx6_anc_feerate == CFeeRate(tx6_iter.value()->GetModFeesWithAncestors(), tx6_iter.value()->GetSizeWithAncestors()));
const auto& tx6_entry{*Assert(pool.GetEntry(tx6->GetHash()))};
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_iter = pool.GetIter(tx7->GetHash());
BOOST_CHECK(tx7_anc_feerate == CFeeRate(tx7_iter.value()->GetModFeesWithAncestors(), tx7_iter.value()->GetSizeWithAncestors()));
const auto& tx7_entry{*Assert(pool.GetEntry(tx7->GetHash()))};
BOOST_CHECK(tx7_anc_feerate == CFeeRate(tx7_entry.GetModFeesWithAncestors(), tx7_entry.GetSizeWithAncestors()));
BOOST_CHECK(tx4_feerate > tx6_anc_feerate);
BOOST_CHECK(tx4_feerate > tx7_anc_feerate);

View file

@ -286,8 +286,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Check that all txs are in the pool
{
LOCK(m_node.mempool->cs);
BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
BOOST_CHECK_EQUAL(m_node.mempool->size(), txs.size());
}
// Run a thread that simulates an RPC caller that is polling while
@ -298,7 +297,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// not some intermediate amount.
while (true) {
LOCK(m_node.mempool->cs);
if (m_node.mempool->mapTx.size() == 0) {
if (m_node.mempool->size() == 0) {
// We are done with the reorg
break;
}
@ -307,7 +306,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// be atomic. So the caller assumes that the returned mempool
// is consistent. That is, it has all txs that were there
// before the reorg.
assert(m_node.mempool->mapTx.size() == txs.size());
assert(m_node.mempool->size() == txs.size());
continue;
}
LOCK(cs_main);

View file

@ -489,8 +489,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
}
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
txns_randomized.emplace_back(newit->GetSharedTx());
newit->idx_randomized = txns_randomized.size() - 1;
TRACE3(mempool, added,
entry.GetTx().GetHash().data(),
@ -526,14 +526,16 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
RemoveUnbroadcastTx(hash, true /* add logging because unchecked */ );
if (vTxHashes.size() > 1) {
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
vTxHashes.pop_back();
if (vTxHashes.size() * 2 < vTxHashes.capacity())
vTxHashes.shrink_to_fit();
if (txns_randomized.size() > 1) {
// Update idx_randomized of the to-be-moved entry.
Assert(GetEntry(txns_randomized.back()->GetHash()))->idx_randomized = it->idx_randomized;
// Remove entry from txns_randomized by replacing it with the back and deleting the back.
txns_randomized[it->idx_randomized] = std::move(txns_randomized.back());
txns_randomized.pop_back();
if (txns_randomized.size() * 2 < txns_randomized.capacity())
txns_randomized.shrink_to_fit();
} else
vTxHashes.clear();
txns_randomized.clear();
totalTxSize -= it->GetTxSize();
m_total_fee -= it->GetFee();
@ -917,6 +919,18 @@ static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee(), it->GetDiscountTxSize()};
}
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
{
LOCK(cs);
@ -931,6 +945,13 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
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
{
LOCK(cs);
@ -1119,7 +1140,7 @@ bool CCoinsViewMemPool::IsPeginSpent(const std::pair<uint256, COutPoint> &outpoi
size_t CTxMemPool::DynamicMemoryUsage() const {
LOCK(cs);
// 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) {

View file

@ -448,7 +448,7 @@ public:
indexed_transaction_set mapTx GUARDED_BY(cs);
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;
@ -741,6 +741,8 @@ public:
return (mapTx.count(gtxid.GetHash()) != 0);
}
const CTxMemPoolEntry* GetEntry(const Txid& txid) const LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(cs);
CTransactionRef get(const uint256& hash) const;
txiter get_iter_from_wtxid(const uint256& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
{
@ -752,6 +754,7 @@ public:
/** Returns info for a transaction if its entry_sequence < last_sequence */
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;
size_t DynamicMemoryUsage() const;

View file

@ -366,7 +366,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
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)) {
// 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 {
return true;
}
@ -375,9 +375,7 @@ void Chainstate::MaybeUpdateMempoolForReorg(
// If the transaction spends any coinbase outputs, it must be mature.
if (it->GetSpendsCoinbase()) {
for (const CTxIn& txin : tx.vin) {
auto it2 = m_mempool->mapTx.find(txin.prevout.hash);
if (it2 != m_mempool->mapTx.end())
continue;
if (m_mempool->exists(GenTxid::Txid(txin.prevout.hash))) continue;
const Coin& coin{CoinsTip().AccessCoin(txin.prevout)};
assert(!coin.IsSpent());
const auto mempool_spend_height{m_chain.Tip()->nHeight + 1};
@ -1607,9 +1605,8 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// 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
// checking ancestor/descendant limits, or double-count transaction fees for fee-related policy.
auto iter = m_pool.GetIter(txid);
assert(iter != std::nullopt);
results_final.emplace(wtxid, MempoolAcceptResult::MempoolTx(iter.value()->GetTxSize(), iter.value()->GetFee()));
const auto& entry{*Assert(m_pool.GetEntry(txid))};
results_final.emplace(wtxid, MempoolAcceptResult::MempoolTx(entry.GetTxSize(), entry.GetFee()));
} else if (m_pool.exists(GenTxid::Txid(txid))) {
// Transaction with the same non-witness data but different witness (same txid,
// different wtxid) already exists in the mempool.
@ -1618,10 +1615,9 @@ PackageMempoolAcceptResult MemPoolAccept::AcceptPackage(const Package& package,
// transaction for the mempool one. Note that we are ignoring the validity of the
// package transaction passed in.
// TODO: allow witness replacement in packages.
auto iter = m_pool.GetIter(txid);
assert(iter != std::nullopt);
const auto& entry{*Assert(m_pool.GetEntry(txid))};
// 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 {
// Transaction does not already exist in the mempool.
// Try submitting the transaction on its own.