From 08ecf0222b03e00ecc6d9ce37283273745775a0c Mon Sep 17 00:00:00 2001 From: Anthony Fieroni Date: Mon, 15 Nov 2021 08:52:02 +0200 Subject: [PATCH] Rework mempool accounts view Signed-off-by: Anthony Fieroni --- src/txmempool.cpp | 107 +++++++++++++++++++++------------------------ src/txmempool.h | 2 + src/validation.cpp | 5 ++- 3 files changed, 55 insertions(+), 59 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index e3686020c9c..5f0bd58fa02 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -333,6 +333,7 @@ CTxMemPool::CTxMemPool(CBlockPolicyEstimator* estimator) // accepting transactions becomes O(N^2) where N is the number // of transactions in the pool nCheckFrequency = 0; + accountsViewDirty = false; } bool CTxMemPool::isSpent(const COutPoint& outpoint) const @@ -582,49 +583,15 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigne // Before the txs in the new block have been removed from the mempool, update policy estimates if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);} - for (auto& it : staged) { - auto& tx = it->GetTx(); - removeConflicts(tx); - ClearPrioritisation(tx.GetHash()); - } - RemoveStaged(staged, true, MemPoolRemovalReason::BLOCK); - if (pcustomcsview) { // can happen in tests - // check entire mempool - CAmount txfee = 0; - accountsView().Discard(); - CCustomCSView viewDuplicate(accountsView()); - CCoinsViewCache mempoolDuplicate(&::ChainstateActive().CoinsTip()); - - setEntries staged; - // Check custom TX consensus types are now not in conflict with account layer - auto& txsByEntryTime = mapTx.get(); - for (auto it = txsByEntryTime.begin(); it != txsByEntryTime.end(); ++it) { - CValidationState state; - const auto& tx = it->GetTx(); - if (!Consensus::CheckTxInputs(tx, state, mempoolDuplicate, &viewDuplicate, nBlockHeight, txfee, Params())) { - LogPrintf("%s: Remove conflicting TX: %s\n", __func__, tx.GetHash().GetHex()); - staged.insert(mapTx.project<0>(it)); - continue; - } - auto res = ApplyCustomTx(viewDuplicate, mempoolDuplicate, tx, Params().GetConsensus(), nBlockHeight); - if (!res.ok && (res.code & CustomTxErrCodes::Fatal)) { - LogPrintf("%s: Remove conflicting custom TX: %s\n", __func__, tx.GetHash().GetHex()); - staged.insert(mapTx.project<0>(it)); - } - } - - for (auto& it : staged) { - auto& tx = it->GetTx(); - removeConflicts(tx); - ClearPrioritisation(tx.GetHash()); - } - - RemoveStaged(staged, true, MemPoolRemovalReason::BLOCK); - viewDuplicate.Flush(); + for (const auto& tx : vtx) { + removeConflicts(*tx); + ClearPrioritisation(tx->GetHash()); } + rebuildAccountsView(nBlockHeight); + lastRollingFeeUpdate = GetTime(); blockSinceLastRollingFeeBump = true; } @@ -975,27 +942,10 @@ size_t CTxMemPool::DynamicMemoryUsage() const { void CTxMemPool::RemoveStaged(const setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { AssertLockHeld(cs); UpdateForRemoveFromMempool(stage, updateDescendants); - std::set txids; for (txiter it : stage) { - txids.insert(it->GetTx().GetHash()); removeUnchecked(it, reason); } - if (pcustomcsview && !txids.empty()) { - auto& view = accountsView(); - std::map orderedTxs; - auto it = NewKVIterator(UndoKey{}, view.GetStorage().GetRaw()); - for (; it.Valid() && !txids.empty(); it.Next()) { - auto& key = it.Key(); - auto itTx = txids.find(key.txid); - if (itTx != txids.end()) { - orderedTxs.emplace(key.height, key.txid); - txids.erase(itTx); - } - } - for (auto it = orderedTxs.rbegin(); it != orderedTxs.rend(); ++it) { - view.OnUndoTx(it->second, it->first); - } - } + accountsViewDirty = accountsViewDirty || !stage.empty(); } int CTxMemPool::Expire(int64_t time) { @@ -1134,6 +1084,49 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector* pvNoSpends } } +void CTxMemPool::rebuildAccountsView(int height) +{ + if (!pcustomcsview || !accountsViewDirty) { + return; + } + + CAmount txfee = 0; + accountsView().Discard(); + CCustomCSView viewDuplicate(accountsView()); + CCoinsViewCache mempoolDuplicate(&::ChainstateActive().CoinsTip()); + + setEntries staged; + std::vector txs; + // Check custom TX consensus types are now not in conflict with account layer + auto& txsByEntryTime = mapTx.get(); + for (auto it = txsByEntryTime.begin(); it != txsByEntryTime.end(); ++it) { + CValidationState state; + const auto& tx = it->GetTx(); + if (!Consensus::CheckTxInputs(tx, state, mempoolDuplicate, &viewDuplicate, height, txfee, Params())) { + LogPrintf("%s: Remove conflicting TX: %s\n", __func__, tx.GetHash().GetHex()); + staged.insert(mapTx.project<0>(it)); + txs.push_back(it->GetSharedTx()); + continue; + } + auto res = ApplyCustomTx(viewDuplicate, mempoolDuplicate, tx, Params().GetConsensus(), height); + if (!res && (res.code & CustomTxErrCodes::Fatal)) { + LogPrintf("%s: Remove conflicting custom TX: %s\n", __func__, tx.GetHash().GetHex()); + staged.insert(mapTx.project<0>(it)); + txs.push_back(it->GetSharedTx()); + } + } + + RemoveStaged(staged, true, MemPoolRemovalReason::BLOCK); + + for (const auto& tx : txs) { + removeConflicts(*tx); + ClearPrioritisation(tx->GetHash()); + } + + viewDuplicate.Flush(); + accountsViewDirty = false; +} + uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const { // find parent with highest descendant count std::vector candidates; diff --git a/src/txmempool.h b/src/txmempool.h index 264e5f0b41c..8be66203708 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -548,6 +548,7 @@ class CTxMemPool std::vector GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs); + bool accountsViewDirty; std::unique_ptr acview; public: indirectmap mapNextTx GUARDED_BY(cs); @@ -704,6 +705,7 @@ class CTxMemPool boost::signals2::signal NotifyEntryRemoved; CCustomCSView& accountsView(); + void rebuildAccountsView(int height); private: /** UpdateForDescendants is used by UpdateTransactionsFromBlock to update * the descendants for a single transaction that has been added to the diff --git a/src/validation.cpp b/src/validation.cpp index 2ff3ad46342..f9508ae7b9b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -612,7 +612,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool const auto height = GetSpendHeight(view); - // it does not need to check mempool anymore it has view there + // rebuild accounts view if dirty + pool.rebuildAccountsView(height); CAmount nFees = 0; if (!Consensus::CheckTxInputs(tx, state, view, &mnview, height, nFees, chainparams)) { @@ -910,6 +911,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Store transaction in memory pool.addUnchecked(entry, setAncestors, validForFeeEstimation); + mnview.Flush(); // trim mempool and check if tx was trimmed if (!bypass_limits) { @@ -917,7 +919,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (!pool.exists(hash)) return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); } - mnview.Flush(); } GetMainSignals().TransactionAddedToMempool(ptx);