diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2013-08-27 15:51:57 +1000 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2013-11-04 11:27:02 +1000 |
commit | 319b11607f8592d7ef67ec82fa73545ad7430974 (patch) | |
tree | 35a62a9d0df1c0bb9157e8cc85ab086c01c55217 /src/main.cpp | |
parent | 39b4f0d7ddc964b789876d15d888c3b29022939e (diff) |
Refactor: CTxMempool class to its own txmempool.{cpp,h}
Diffstat (limited to 'src/main.cpp')
-rw-r--r-- | src/main.cpp | 258 |
1 files changed, 67 insertions, 191 deletions
diff --git a/src/main.cpp b/src/main.cpp index 4a4fcee34a..ecb9711664 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,18 +3,20 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <boost/algorithm/string/replace.hpp> +#include <boost/filesystem.hpp> +#include <boost/filesystem/fstream.hpp> + #include "alert.h" +#include "chainparams.h" #include "checkpoints.h" +#include "checkqueue.h" #include "db.h" -#include "txdb.h" -#include "net.h" #include "init.h" +#include "net.h" +#include "txdb.h" +#include "txmempool.h" #include "ui_interface.h" -#include "checkqueue.h" -#include "chainparams.h" -#include <boost/algorithm/string/replace.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> using namespace std; using namespace boost; @@ -29,7 +31,6 @@ set<CWallet*> setpwalletRegistered; CCriticalSection cs_main; CTxMemPool mempool; -unsigned int nTransactionsUpdated = 0; map<uint256, CBlockIndex*> mapBlockIndex; CChain chainActive; @@ -319,6 +320,15 @@ unsigned int CCoinsViewCache::GetCacheSize() { return cacheCoins.size(); } +/** Helper; lookup from tip (used calling mempool.check() + NOTE: code calling this MUST hold the cs_main lock so + another thread doesn't modify pcoinsTip. When we switch + to C++11 this should be replaced by lambda expressions... + **/ +static CCoins &LookupFromTip(const uint256& hash) { + return pcoinsTip->GetCoins(hash); +} + /** CCoinsView that brings transactions from a memorypool into view. It does not check for spendings by memory pool transactions. */ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } @@ -326,8 +336,8 @@ CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) { if (base->GetCoins(txid, coins)) return true; - if (mempool.exists(txid)) { - const CTransaction &tx = mempool.lookup(txid); + CTransaction tx; + if (mempool.lookup(txid, tx)) { coins = CCoins(tx, MEMPOOL_HEIGHT); return true; } @@ -734,56 +744,39 @@ int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mod return nMinFee; } -void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins) -{ - LOCK(cs); - - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0)); - - // iterate over all COutPoints in mapNextTx whose hash equals the provided hashTx - while (it != mapNextTx.end() && it->first.hash == hashTx) { - coins.Spend(it->first.n); // and remove those outputs from coins - it++; - } -} -bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee) { if (pfMissingInputs) *pfMissingInputs = false; if (!CheckTransaction(tx, state)) - return error("CTxMemPool::accept() : CheckTransaction failed"); + return error("AcceptToMemoryPool: : CheckTransaction failed"); // Coinbase is only valid in a block, not as a loose transaction if (tx.IsCoinBase()) - return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx")); - - // To help v0.1.5 clients who would see it as a negative number - if ((int64)tx.nLockTime > std::numeric_limits<int>::max()) - return error("CTxMemPool::accept() : not accepting nLockTime beyond 2038 yet"); + return state.DoS(100, error("AcceptToMemoryPool: : coinbase as individual tx")); // Rather not work on nonstandard transactions (unless -testnet/-regtest) string reason; if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, reason)) - return error("CTxMemPool::accept() : nonstandard transaction: %s", + return error("AcceptToMemoryPool: : nonstandard transaction: %s", reason.c_str()); // is it already in the memory pool? uint256 hash = tx.GetHash(); - { - LOCK(cs); - if (mapTx.count(hash)) - return false; - } + if (pool.exists(hash)) + return false; // Check for conflicts with in-memory transactions CTransaction* ptxOld = NULL; + { + LOCK(pool.cs); // protect pool.mapNextTx for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (mapNextTx.count(outpoint)) + if (pool.mapNextTx.count(outpoint)) { // Disable replacement feature for now return false; @@ -791,7 +784,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Allow replacing with a newer version of the same transaction if (i != 0) return false; - ptxOld = mapNextTx[outpoint].ptx; + ptxOld = pool.mapNextTx[outpoint].ptx; if (IsFinalTx(*ptxOld)) return false; if (!tx.IsNewerThan(*ptxOld)) @@ -799,20 +792,21 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL for (unsigned int i = 0; i < tx.vin.size(); i++) { COutPoint outpoint = tx.vin[i].prevout; - if (!mapNextTx.count(outpoint) || mapNextTx[outpoint].ptx != ptxOld) + if (!pool.mapNextTx.count(outpoint) || pool.mapNextTx[outpoint].ptx != ptxOld) return false; } break; } } + } { CCoinsView dummy; CCoinsViewCache view(dummy); { - LOCK(cs); - CCoinsViewMemPool viewMemPool(*pcoinsTip, *this); + LOCK(pool.cs); + CCoinsViewMemPool viewMemPool(*pcoinsTip, pool); view.SetBackend(viewMemPool); // do we already have it? @@ -832,7 +826,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // are the actual inputs available? if (!view.HaveInputs(tx)) - return state.Invalid(error("CTxMemPool::accept() : inputs already spent")); + return state.Invalid(error("AcceptToMemoryPool: : inputs already spent")); // Bring the best block into scope view.GetBestBlock(); @@ -843,7 +837,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Check for non-standard pay-to-script-hash in inputs if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view)) - return error("CTxMemPool::accept() : nonstandard transaction input"); + return error("AcceptToMemoryPool: : nonstandard transaction input"); // Note: if you modify this code to accept non-standard transactions, then // you should add code here to check that the transaction does a @@ -855,7 +849,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // Don't accept it if it can't get into a block int64 txMinFee = GetMinFee(tx, true, GMF_RELAY); if (fLimitFree && nFees < txMinFee) - return error("CTxMemPool::accept() : not enough fees %s, %"PRI64d" < %"PRI64d, + return error("AcceptToMemoryPool: : not enough fees %s, %"PRI64d" < %"PRI64d, hash.ToString().c_str(), nFees, txMinFee); @@ -864,11 +858,12 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nFees < CTransaction::nMinRelayTxFee) { + static CCriticalSection csFreeLimiter; static double dFreeCount; static int64 nLastTime; int64 nNow = GetTime(); - LOCK(cs); + LOCK(csFreeLimiter); // Use an exponentially decaying ~10-minute window: dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); @@ -876,14 +871,13 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000) - return error("CTxMemPool::accept() : free transaction rejected by rate limiter"); - if (fDebug) - LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + return error("AcceptToMemoryPool: : free transaction rejected by rate limiter"); + LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); dFreeCount += nSize; } if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000) - return error("CTxMemPool::accept() : insane fees %s, %"PRI64d" > %"PRI64d, + return error("AcceptToMemoryPool: : insane fees %s, %"PRI64d" > %"PRI64d, hash.ToString().c_str(), nFees, CTransaction::nMinRelayTxFee * 10000); @@ -891,19 +885,18 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL // This is done last to help prevent CPU exhaustion denial-of-service attacks. if (!CheckInputs(tx, state, view, true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC)) { - return error("CTxMemPool::accept() : ConnectInputs failed %s", hash.ToString().c_str()); + return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString().c_str()); } } // Store transaction in memory { - LOCK(cs); if (ptxOld) { - LogPrint("mempool", "CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); - remove(*ptxOld); + LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str()); + pool.remove(*ptxOld); } - addUnchecked(hash, tx); + pool.addUnchecked(hash, tx); } ///// are we sure this is ok when loading transactions or restoring block txes @@ -912,125 +905,12 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL g_signals.EraseTransaction(ptxOld->GetHash()); g_signals.SyncTransaction(hash, tx, NULL); - LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n", - hash.ToString().c_str(), - mapTx.size()); - return true; -} - - -bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx) -{ - // Add to memory pool without checking anything. Don't call this directly, - // call CTxMemPool::accept to properly check the transaction first. - { - mapTx[hash] = tx; - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&mapTx[hash], i); - nTransactionsUpdated++; - } - return true; -} - - -bool CTxMemPool::remove(const CTransaction &tx, bool fRecursive) -{ - // Remove transaction from memory pool - { - LOCK(cs); - uint256 hash = tx.GetHash(); - if (fRecursive) { - for (unsigned int i = 0; i < tx.vout.size(); i++) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(COutPoint(hash, i)); - if (it != mapNextTx.end()) - remove(*it->second.ptx, true); - } - } - if (mapTx.count(hash)) - { - BOOST_FOREACH(const CTxIn& txin, tx.vin) - mapNextTx.erase(txin.prevout); - mapTx.erase(hash); - nTransactionsUpdated++; - } - } - return true; -} - -bool CTxMemPool::removeConflicts(const CTransaction &tx) -{ - // Remove transactions which depend on inputs of tx, recursively - LOCK(cs); - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout); - if (it != mapNextTx.end()) { - const CTransaction &txConflict = *it->second.ptx; - if (txConflict != tx) - remove(txConflict, true); - } - } + LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n", + hash.ToString().c_str(), + pool.mapTx.size()); return true; } -void CTxMemPool::clear() -{ - LOCK(cs); - mapTx.clear(); - mapNextTx.clear(); - ++nTransactionsUpdated; -} - -bool CTxMemPool::fChecks = false; - -void CTxMemPool::check(CCoinsViewCache *pcoins) const -{ - if (!fChecks) - return; - - LogPrintf("Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); - - LOCK(cs); - for (std::map<uint256, CTransaction>::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<uint256, CTransaction>::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<COutPoint, CInPoint>::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<COutPoint, CInPoint>::const_iterator it = mapNextTx.begin(); it != mapNextTx.end(); it++) { - uint256 hash = it->second.ptx->GetHash(); - std::map<uint256, CTransaction>::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<uint256>& vtxid) -{ - vtxid.clear(); - - LOCK(cs); - vtxid.reserve(mapTx.size()); - for (map<uint256, CTransaction>::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) - vtxid.push_back((*mi).first); -} - - - int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const { @@ -1069,7 +949,7 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree) { CValidationState state; - return mempool.accept(state, *this, fLimitFree, NULL); + return ::AcceptToMemoryPool(mempool, state, *this, fLimitFree, NULL); } @@ -1080,10 +960,8 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock { LOCK(cs_main); { - LOCK(mempool.cs); - if (mempool.exists(hash)) + if (mempool.lookup(hash, txOut)) { - txOut = mempool.lookup(hash); return true; } } @@ -1959,7 +1837,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) { - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); // All modifications to the coin state will be done in this cache. // Only when all have succeeded, we push it to pcoinsTip. @@ -2072,7 +1950,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) BOOST_FOREACH(CTransaction& tx, vResurrect) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!mempool.accept(stateDummy, tx, false, NULL)) + if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL)) mempool.remove(tx, true); } @@ -2082,7 +1960,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); // Update best block in wallet (so we can detect restored wallets) if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0)) @@ -2090,7 +1968,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) // New best block nTimeBestReceived = GetTime(); - nTransactionsUpdated++; + mempool.AddTransactionsUpdated(1); LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n", chainActive.Tip()->GetBlockHash().ToString().c_str(), chainActive.Height(), log(chainActive.Tip()->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()).c_str(), @@ -3170,10 +3048,7 @@ bool static AlreadyHave(const CInv& inv) case MSG_TX: { bool txInMap = false; - { - LOCK(mempool.cs); - txInMap = mempool.exists(inv.hash); - } + txInMap = mempool.exists(inv.hash); return txInMap || mapOrphanTransactions.count(inv.hash) || pcoinsTip->HaveCoins(inv.hash); } @@ -3264,9 +3139,8 @@ void static ProcessGetData(CNode* pfrom) } } if (!pushed && inv.type == MSG_TX) { - LOCK(mempool.cs); - if (mempool.exists(inv.hash)) { - CTransaction tx = mempool.lookup(inv.hash); + CTransaction tx; + if (mempool.lookup(inv.hash, tx)) { CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss.reserve(1000); ss << tx; @@ -3658,9 +3532,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) bool fMissingInputs = false; CValidationState state; - if (mempool.accept(state, tx, true, &fMissingInputs)) + if (AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs)) { - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); RelayTransaction(tx, inv.hash); mapAlreadyAskedFor.erase(inv); vWorkQueue.push_back(inv.hash); @@ -3682,7 +3556,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) // anyone relaying LegitTxX banned) CValidationState stateDummy; - if (mempool.accept(stateDummy, orphanTx, true, &fMissingInputs2)) + if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) { LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str()); RelayTransaction(orphanTx, orphanHash); @@ -3696,7 +3570,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) vEraseQueue.push_back(orphanHash); LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str()); } - mempool.check(pcoinsTip); + mempool.check(&LookupFromTip); } } @@ -3753,15 +3627,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) else if (strCommand == "mempool") { - LOCK(cs_main); + LOCK2(cs_main, pfrom->cs_filter); std::vector<uint256> vtxid; - LOCK2(mempool.cs, pfrom->cs_filter); mempool.queryHashes(vtxid); vector<CInv> vInv; BOOST_FOREACH(uint256& hash, vtxid) { CInv inv(MSG_TX, hash); - if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(mempool.lookup(hash), hash)) || + CTransaction tx; + bool fInMemPool = mempool.lookup(hash, tx); + if (!fInMemPool) continue; // another thread removed since queryHashes, maybe... + if ((pfrom->pfilter && pfrom->pfilter->IsRelevantAndUpdate(tx, hash)) || (!pfrom->pfilter)) vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { |