diff --git a/src/chainparams.h b/src/chainparams.h index 1930e49af..0dac79aed 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -101,4 +101,8 @@ inline bool TestNet() { return Params().NetworkID() == CChainParams::TESTNET; } +inline bool RegTest() { + return Params().NetworkID() == CChainParams::REGTEST; +} + #endif diff --git a/src/init.cpp b/src/init.cpp index 09871c012..9f3d52641 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -424,6 +424,7 @@ bool AppInit2(boost::thread_group& threadGroup) fDebug = GetBoolArg("-debug", false); fBenchmark = GetBoolArg("-benchmark", false); + mempool.fChecks = GetBoolArg("-checkmempool", RegTest()); // -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency nScriptCheckThreads = GetArg("-par", 0); diff --git a/src/main.cpp b/src/main.cpp index e78e05514..2ccd5131d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -973,15 +973,15 @@ bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) { LOCK(cs); uint256 hash = tx.GetHash(); + if (fRecursive) { + for (unsigned int i = 0; i < tx.vout.size(); i++) { + std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); + if (it != mapNextTx.end()) + remove(*it->second.ptx, true); + } + } if (mapTx.count(hash)) { - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } BOOST_FOREACH(const CTxIn& txin, tx.vin) mapNextTx.erase(txin.prevout); mapTx.erase(hash); @@ -1014,6 +1014,45 @@ void CTxMemPool::clear() ++nTransactionsUpdated; } +bool CTxMemPool::fChecks = false; + +void CTxMemPool::check(CCoinsViewCache *pcoins) const +{ + if (!fChecks) + return; + + printf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); + + LOCK(cs); + for (std::map::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { + unsigned int i = 0; + BOOST_FOREACH(const CTxIn &txin, it->second.vin) { + // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. + std::map::const_iterator it2 = mapTx.find(txin.prevout.hash); + if (it2 != mapTx.end()) { + assert(it2->second.vout.size() > txin.prevout.n && !it2->second.vout[txin.prevout.n].IsNull()); + } else { + CCoins &coins = pcoins->GetCoins(txin.prevout.hash); + assert(coins.IsAvailable(txin.prevout.n)); + } + // Check whether its inputs are marked in mapNextTx. + std::map::const_iterator it3 = mapNextTx.find(txin.prevout); + assert(it3 != mapNextTx.end()); + assert(it3->second.ptx == &it->second); + assert(it3->second.n == i); + i++; + } + } + for (std::map::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { + uint256 hash = it->second.ptx->GetHash(); + std::map::const_iterator it2 = mapTx.find(hash); + assert(it2 != mapTx.end()); + assert(&it2->second == it->second.ptx); + assert(it2->second.vin.size() > it->second.n); + assert(it->first == it->second.ptx->vin[it->second.n].prevout); + } +} + void CTxMemPool::queryHashes(std::vector& vtxid) { vtxid.clear(); @@ -1970,6 +2009,8 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { + mempool.check(pcoinsTip); + // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. CCoinsViewCache view(*pcoinsTip, true); @@ -2083,7 +2124,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - mempool.accept(stateDummy, tx, false, NULL); + if (!mempool.accept(stateDummy, tx, false, NULL)) + mempool.remove(tx, true); } // Delete redundant memory transactions that are in the connected branch @@ -2092,6 +2134,8 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } + mempool.check(pcoinsTip); + // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) { @@ -3677,6 +3721,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) CValidationState state; if (mempool.accept(state, tx, true, &fMissingInputs)) { + mempool.check(pcoinsTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3712,6 +3757,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); printf(" removed orphan tx %s\n", orphanHash.ToString().c_str()); } + mempool.check(pcoinsTip); } } diff --git a/src/main.h b/src/main.h index ea86a2bcc..a690a2bc9 100644 --- a/src/main.h +++ b/src/main.h @@ -1077,6 +1077,7 @@ public: class CTxMemPool { public: + static bool fChecks; mutable CCriticalSection cs; std::map mapTx; std::map mapNextTx; @@ -1088,6 +1089,7 @@ public: void clear(); void queryHashes(std::vector& vtxid); void pruneSpent(const uint256& hash, CCoins &coins); + void check(CCoinsViewCache *pcoins) const; unsigned long size() {