Skip to content

Commit bdda4d5

Browse files
committed
Merge pull request #6872
dd5862c Flush coins cache also after transaction processing (Pieter Wuille) bde953e Uncache input txn in utxo cache if a tx is not accepted to mempool (Matt Corallo) 97bf377 Add CCoinsViewCache::HaveCoinsInCache to check if a tx is cached (Matt Corallo) 677aa3d Discard txn cache entries that were loaded for removed mempool txn (Matt Corallo) b2e74bd Get the set of now-uncacheable-txn from CTxMemPool::TrimToSize (Matt Corallo) 74d0f90 Add method to remove a tx from CCoinsViewCache if it is unchanged (Matt Corallo)
2 parents 4a63f94 + dd5862c commit bdda4d5

File tree

5 files changed

+90
-15
lines changed

5 files changed

+90
-15
lines changed

src/coins.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
144144
return (it != cacheCoins.end() && !it->second.coins.vout.empty());
145145
}
146146

147+
bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const {
148+
CCoinsMap::const_iterator it = cacheCoins.find(txid);
149+
return it != cacheCoins.end();
150+
}
151+
147152
uint256 CCoinsViewCache::GetBestBlock() const {
148153
if (hashBlock.IsNull())
149154
hashBlock = base->GetBestBlock();
@@ -206,6 +211,15 @@ bool CCoinsViewCache::Flush() {
206211
return fOk;
207212
}
208213

214+
void CCoinsViewCache::Uncache(const uint256& hash)
215+
{
216+
CCoinsMap::iterator it = cacheCoins.find(hash);
217+
if (it != cacheCoins.end() && it->second.flags == 0) {
218+
cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage();
219+
cacheCoins.erase(it);
220+
}
221+
}
222+
209223
unsigned int CCoinsViewCache::GetCacheSize() const {
210224
return cacheCoins.size();
211225
}

src/coins.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,13 @@ class CCoinsViewCache : public CCoinsViewBacked
405405
void SetBestBlock(const uint256 &hashBlock);
406406
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
407407

408+
/**
409+
* Check if we have the given tx already loaded in this cache.
410+
* The semantics are the same as HaveCoins(), but no calls to
411+
* the backing CCoinsView are made.
412+
*/
413+
bool HaveCoinsInCache(const uint256 &txid) const;
414+
408415
/**
409416
* Return a pointer to CCoins in the cache, or NULL if not found. This is
410417
* more efficient than GetCoins. Modifications to other cache entries are
@@ -437,6 +444,12 @@ class CCoinsViewCache : public CCoinsViewBacked
437444
*/
438445
bool Flush();
439446

447+
/**
448+
* Removes the transaction with the given hash from the cache, if it is
449+
* not modified.
450+
*/
451+
void Uncache(const uint256 &txid);
452+
440453
//! Calculate the size of the cache (in number of transactions)
441454
unsigned int GetCacheSize() const;
442455

src/main.cpp

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,17 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
789789
return true;
790790
}
791791

792+
void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
793+
int expired = pool.Expire(GetTime() - age);
794+
if (expired != 0)
795+
LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
796+
797+
std::vector<uint256> vNoSpendsRemaining;
798+
pool.TrimToSize(limit, &vNoSpendsRemaining);
799+
BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
800+
pcoinsTip->Uncache(removed);
801+
}
802+
792803
CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree)
793804
{
794805
uint256 hash = tx.GetHash();
@@ -824,8 +835,9 @@ std::string FormatStateMessage(const CValidationState &state)
824835
state.GetRejectCode());
825836
}
826837

827-
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
828-
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
838+
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
839+
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee,
840+
std::vector<uint256>& vHashTxnToUncache)
829841
{
830842
AssertLockHeld(cs_main);
831843
if (pfMissingInputs)
@@ -906,13 +918,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
906918
view.SetBackend(viewMemPool);
907919

908920
// do we already have it?
909-
if (view.HaveCoins(hash))
921+
bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash);
922+
if (view.HaveCoins(hash)) {
923+
if (!fHadTxInCache)
924+
vHashTxnToUncache.push_back(hash);
910925
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
926+
}
911927

912928
// do all inputs exist?
913929
// Note that this does not check for the presence of actual outputs (see the next check for that),
914930
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
915931
BOOST_FOREACH(const CTxIn txin, tx.vin) {
932+
if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash))
933+
vHashTxnToUncache.push_back(txin.prevout.hash);
916934
if (!view.HaveCoins(txin.prevout.hash)) {
917935
if (pfMissingInputs)
918936
*pfMissingInputs = true;
@@ -1210,12 +1228,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
12101228

12111229
// trim mempool and check if tx was trimmed
12121230
if (!fOverrideMempoolLimit) {
1213-
int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
1214-
if (expired != 0)
1215-
LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
1216-
1217-
pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
1218-
if (!pool.exists(tx.GetHash()))
1231+
LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
1232+
if (!pool.exists(hash))
12191233
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
12201234
}
12211235
}
@@ -1225,6 +1239,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
12251239
return true;
12261240
}
12271241

1242+
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
1243+
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
1244+
{
1245+
std::vector<uint256> vHashTxToUncache;
1246+
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache);
1247+
if (!res) {
1248+
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
1249+
pcoinsTip->Uncache(hashTx);
1250+
}
1251+
return res;
1252+
}
1253+
12281254
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
12291255
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
12301256
{
@@ -2571,7 +2597,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
25712597

25722598
if (fBlocksDisconnected) {
25732599
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
2574-
mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
2600+
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
25752601
}
25762602
mempool.check(pcoinsTip);
25772603

@@ -2686,7 +2712,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus
26862712
}
26872713
}
26882714

2689-
mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
2715+
LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
26902716

26912717
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
26922718
// add it again.
@@ -4804,6 +4830,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
48044830
if (nDoS > 0)
48054831
Misbehaving(pfrom->GetId(), nDoS);
48064832
}
4833+
FlushStateToDisk(state, FLUSH_STATE_PERIODIC);
48074834
}
48084835

48094836

src/txmempool.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
944944
}
945945
}
946946

947-
void CTxMemPool::TrimToSize(size_t sizelimit) {
947+
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining) {
948948
LOCK(cs);
949949

950950
unsigned nTxnRemoved = 0;
@@ -963,8 +963,26 @@ void CTxMemPool::TrimToSize(size_t sizelimit) {
963963

964964
setEntries stage;
965965
CalculateDescendants(mapTx.project<0>(it), stage);
966-
RemoveStaged(stage);
967966
nTxnRemoved += stage.size();
967+
968+
std::vector<CTransaction> txn;
969+
if (pvNoSpendsRemaining) {
970+
txn.reserve(stage.size());
971+
BOOST_FOREACH(txiter it, stage)
972+
txn.push_back(it->GetTx());
973+
}
974+
RemoveStaged(stage);
975+
if (pvNoSpendsRemaining) {
976+
BOOST_FOREACH(const CTransaction& tx, txn) {
977+
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
978+
if (exists(txin.prevout.hash))
979+
continue;
980+
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0));
981+
if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash)
982+
pvNoSpendsRemaining->push_back(txin.prevout.hash);
983+
}
984+
}
985+
}
968986
}
969987

970988
if (maxFeeRateRemoved > CFeeRate(0))

src/txmempool.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,11 @@ class CTxMemPool
483483
*/
484484
CFeeRate GetMinFee(size_t sizelimit) const;
485485

486-
/** Remove transactions from the mempool until its dynamic size is <= sizelimit. */
487-
void TrimToSize(size_t sizelimit);
486+
/** Remove transactions from the mempool until its dynamic size is <= sizelimit.
487+
* pvNoSpendsRemaining, if set, will be populated with the list of transactions
488+
* which are not in mempool which no longer have any spends in this mempool.
489+
*/
490+
void TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining=NULL);
488491

489492
/** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
490493
int Expire(int64_t time);

0 commit comments

Comments
 (0)