Skip to content

Commit

Permalink
Merge pull request #6872
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
laanwj committed Dec 2, 2015
2 parents 4a63f94 + dd5862c commit bdda4d5
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 15 deletions.
14 changes: 14 additions & 0 deletions src/coins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
}
Expand Down
13 changes: 13 additions & 0 deletions src/coins.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,13 @@ class CCoinsViewCache : public CCoinsViewBacked
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
Expand Down Expand Up @@ -437,6 +444,12 @@ class CCoinsViewCache : public CCoinsViewBacked
*/
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;

Expand Down
49 changes: 38 additions & 11 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
}
Expand All @@ -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)
{
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}


Expand Down
22 changes: 20 additions & 2 deletions src/txmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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))
Expand Down
7 changes: 5 additions & 2 deletions src/txmempool.h
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,11 @@ class CTxMemPool
*/
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);
Expand Down

0 comments on commit bdda4d5

Please sign in to comment.