aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2015-12-02 10:12:19 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2015-12-02 10:17:43 +0100
commitbdda4d567eedd44d3088980fa47ab03827103f68 (patch)
tree085c3f0ff182276ba87cc9eb1c2b8713ba503231
parent4a63f946760666adcb1a4dde26fc7c6d31c301af (diff)
parentdd5862c4cdc02535948042fe519694166bcd2bb7 (diff)
downloadbitcoin-bdda4d567eedd44d3088980fa47ab03827103f68.tar.xz
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)
-rw-r--r--src/coins.cpp14
-rw-r--r--src/coins.h13
-rw-r--r--src/main.cpp49
-rw-r--r--src/txmempool.cpp22
-rw-r--r--src/txmempool.h7
5 files changed, 90 insertions, 15 deletions
diff --git a/src/coins.cpp b/src/coins.cpp
index 723e114708..122bf4e48d 100644
--- a/src/coins.cpp
+++ b/src/coins.cpp
@@ -144,6 +144,11 @@ bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
return (it != cacheCoins.end() && !it->second.coins.vout.empty());
}
+bool CCoinsViewCache::HaveCoinsInCache(const uint256 &txid) const {
+ CCoinsMap::const_iterator it = cacheCoins.find(txid);
+ return it != cacheCoins.end();
+}
+
uint256 CCoinsViewCache::GetBestBlock() const {
if (hashBlock.IsNull())
hashBlock = base->GetBestBlock();
@@ -206,6 +211,15 @@ bool CCoinsViewCache::Flush() {
return fOk;
}
+void CCoinsViewCache::Uncache(const uint256& hash)
+{
+ CCoinsMap::iterator it = cacheCoins.find(hash);
+ if (it != cacheCoins.end() && it->second.flags == 0) {
+ cachedCoinsUsage -= it->second.coins.DynamicMemoryUsage();
+ cacheCoins.erase(it);
+ }
+}
+
unsigned int CCoinsViewCache::GetCacheSize() const {
return cacheCoins.size();
}
diff --git a/src/coins.h b/src/coins.h
index d174422100..60c1ba8a78 100644
--- a/src/coins.h
+++ b/src/coins.h
@@ -406,6 +406,13 @@ public:
bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
/**
+ * Check if we have the given tx already loaded in this cache.
+ * The semantics are the same as HaveCoins(), but no calls to
+ * the backing CCoinsView are made.
+ */
+ bool HaveCoinsInCache(const uint256 &txid) const;
+
+ /**
* Return a pointer to CCoins in the cache, or NULL if not found. This is
* more efficient than GetCoins. Modifications to other cache entries are
* allowed while accessing the returned pointer.
@@ -437,6 +444,12 @@ public:
*/
bool Flush();
+ /**
+ * Removes the transaction with the given hash from the cache, if it is
+ * not modified.
+ */
+ void Uncache(const uint256 &txid);
+
//! Calculate the size of the cache (in number of transactions)
unsigned int GetCacheSize() const;
diff --git a/src/main.cpp b/src/main.cpp
index e9e9820434..c41dd58d11 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -789,6 +789,17 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state)
return true;
}
+void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
+ int expired = pool.Expire(GetTime() - age);
+ if (expired != 0)
+ LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
+
+ std::vector<uint256> vNoSpendsRemaining;
+ pool.TrimToSize(limit, &vNoSpendsRemaining);
+ BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
+ pcoinsTip->Uncache(removed);
+}
+
CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree)
{
uint256 hash = tx.GetHash();
@@ -824,8 +835,9 @@ std::string FormatStateMessage(const CValidationState &state)
state.GetRejectCode());
}
-bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
- bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
+bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee,
+ std::vector<uint256>& vHashTxnToUncache)
{
AssertLockHeld(cs_main);
if (pfMissingInputs)
@@ -906,13 +918,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
view.SetBackend(viewMemPool);
// do we already have it?
- if (view.HaveCoins(hash))
+ bool fHadTxInCache = pcoinsTip->HaveCoinsInCache(hash);
+ if (view.HaveCoins(hash)) {
+ if (!fHadTxInCache)
+ vHashTxnToUncache.push_back(hash);
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-known");
+ }
// do all inputs exist?
// Note that this does not check for the presence of actual outputs (see the next check for that),
// and only helps with filling in pfMissingInputs (to determine missing vs spent).
BOOST_FOREACH(const CTxIn txin, tx.vin) {
+ if (!pcoinsTip->HaveCoinsInCache(txin.prevout.hash))
+ vHashTxnToUncache.push_back(txin.prevout.hash);
if (!view.HaveCoins(txin.prevout.hash)) {
if (pfMissingInputs)
*pfMissingInputs = true;
@@ -1210,12 +1228,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// trim mempool and check if tx was trimmed
if (!fOverrideMempoolLimit) {
- int expired = pool.Expire(GetTime() - GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
- if (expired != 0)
- LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
-
- pool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
- if (!pool.exists(tx.GetHash()))
+ LimitMempoolSize(pool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
+ if (!pool.exists(hash))
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full");
}
}
@@ -1225,6 +1239,18 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return true;
}
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee)
+{
+ std::vector<uint256> vHashTxToUncache;
+ bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, fRejectAbsurdFee, vHashTxToUncache);
+ if (!res) {
+ BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
+ pcoinsTip->Uncache(hashTx);
+ }
+ return res;
+}
+
/** Return transaction in tx, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{
@@ -2571,7 +2597,7 @@ static bool ActivateBestChainStep(CValidationState& state, const CChainParams& c
if (fBlocksDisconnected) {
mempool.removeForReorg(pcoinsTip, chainActive.Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS);
- mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
}
mempool.check(pcoinsTip);
@@ -2686,7 +2712,7 @@ bool InvalidateBlock(CValidationState& state, const Consensus::Params& consensus
}
}
- mempool.TrimToSize(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ LimitMempoolSize(mempool, GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60);
// The resulting new best tip may not be in setBlockIndexCandidates anymore, so
// add it again.
@@ -4804,6 +4830,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (nDoS > 0)
Misbehaving(pfrom->GetId(), nDoS);
}
+ FlushStateToDisk(state, FLUSH_STATE_PERIODIC);
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 35be216287..fea5da8029 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -944,7 +944,7 @@ void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
}
}
-void CTxMemPool::TrimToSize(size_t sizelimit) {
+void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining) {
LOCK(cs);
unsigned nTxnRemoved = 0;
@@ -963,8 +963,26 @@ void CTxMemPool::TrimToSize(size_t sizelimit) {
setEntries stage;
CalculateDescendants(mapTx.project<0>(it), stage);
- RemoveStaged(stage);
nTxnRemoved += stage.size();
+
+ std::vector<CTransaction> txn;
+ if (pvNoSpendsRemaining) {
+ txn.reserve(stage.size());
+ BOOST_FOREACH(txiter it, stage)
+ txn.push_back(it->GetTx());
+ }
+ RemoveStaged(stage);
+ if (pvNoSpendsRemaining) {
+ BOOST_FOREACH(const CTransaction& tx, txn) {
+ BOOST_FOREACH(const CTxIn& txin, tx.vin) {
+ if (exists(txin.prevout.hash))
+ continue;
+ std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(txin.prevout.hash, 0));
+ if (it == mapNextTx.end() || it->first.hash != txin.prevout.hash)
+ pvNoSpendsRemaining->push_back(txin.prevout.hash);
+ }
+ }
+ }
}
if (maxFeeRateRemoved > CFeeRate(0))
diff --git a/src/txmempool.h b/src/txmempool.h
index 5652969f4b..9203171868 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -483,8 +483,11 @@ public:
*/
CFeeRate GetMinFee(size_t sizelimit) const;
- /** Remove transactions from the mempool until its dynamic size is <= sizelimit. */
- void TrimToSize(size_t sizelimit);
+ /** Remove transactions from the mempool until its dynamic size is <= sizelimit.
+ * pvNoSpendsRemaining, if set, will be populated with the list of transactions
+ * which are not in mempool which no longer have any spends in this mempool.
+ */
+ void TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRemaining=NULL);
/** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */
int Expire(int64_t time);