From 2366480563b99de4f46da4c107cfc51985d768a1 Mon Sep 17 00:00:00 2001 From: Vijay Das Manikpuri <2348066+vijaydasmp@users.noreply.github.com> Date: Wed, 20 Oct 2021 05:13:40 +0530 Subject: [PATCH] Merge #15267: doc: explain AcceptToMemoryPoolWorker's coins_to_uncache --- src/coins.h | 4 ++++ src/validation.cpp | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/coins.h b/src/coins.h index 3180961bd7..edc54d066b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -331,6 +331,10 @@ public: bool HaveInputs(const CTransaction& tx) const; private: + /** + * @note this is marked const, but may actually append to `cacheCoins`, increasing + * memory usage. + */ CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; }; diff --git a/src/validation.cpp b/src/validation.cpp index d809248c1b..6d281e53b5 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -646,6 +646,13 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); } +/** + * @param[out] coins_to_uncache Return any outpoints which were not previously present in the + * coins cache, but were added as a result of validating the tx + * for mempool acceptance. This allows the caller to optionally + * remove the cache additions if the associated transaction ends + * up being rejected by the mempool. + */ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits, const CAmount& nAbsurdFee, std::vector& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) @@ -733,6 +740,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { coins_to_uncache.push_back(txin.prevout); } + + // Note: this call may add txin.prevout to the coins cache + // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed + // later (via coins_to_uncache) if this tx turns out to be invalid. if (!view.HaveCoin(txin.prevout)) { // Are inputs missing because we already have the tx? for (size_t out = 0; out < tx.vout.size(); out++) { @@ -927,6 +938,11 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept); if (!res || test_accept) { if(!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage()); + // Remove coins that were not present in the coins cache before calling ATMPW; + // this is to prevent memory DoS in case we receive a large number of + // invalid transactions that attempt to overrun the in-memory coins cache + // (`CCoinsViewCache::cacheCoins`). + for (const COutPoint& hashTx : coins_to_uncache) pcoinsTip->Uncache(hashTx); }