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)
This commit is contained in:
commit
bdda4d567e
@ -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();
|
||||
}
|
||||
|
13
src/coins.h
13
src/coins.h
@ -405,6 +405,13 @@ public:
|
||||
void SetBestBlock(const uint256 &hashBlock);
|
||||
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
|
||||
@ -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;
|
||||
|
||||
|
49
src/main.cpp
49
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user