aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2013-11-03 20:07:36 -0800
committerGavin Andresen <gavinandresen@gmail.com>2013-11-03 20:07:36 -0800
commita95a1c06b1823d13e8b685c6b18696ead1d17422 (patch)
tree35a62a9d0df1c0bb9157e8cc85ab086c01c55217
parent39b4f0d7ddc964b789876d15d888c3b29022939e (diff)
parent319b11607f8592d7ef67ec82fa73545ad7430974 (diff)
Merge pull request #3154 from gavinandresen/mempool_refactor
Mempool refactor
-rw-r--r--src/Makefile.am4
-rw-r--r--src/checkqueue.h1
-rw-r--r--src/init.cpp4
-rw-r--r--src/limitedmap.h1
-rw-r--r--src/main.cpp258
-rw-r--r--src/main.h52
-rw-r--r--src/miner.cpp4
-rw-r--r--src/rpcmining.cpp8
-rw-r--r--src/rpcrawtransaction.cpp2
-rw-r--r--src/txmempool.cpp162
-rw-r--r--src/txmempool.h67
11 files changed, 317 insertions, 246 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 14d1dd03a6..508063d5e2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,7 @@ BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \
clientversion.h compat.h core.h crypter.h db.h hash.h init.h \
key.h keystore.h leveldb.h limitedmap.h main.h miner.h mruset.h \
netbase.h net.h protocol.h script.h serialize.h sync.h threadsafety.h \
- txdb.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h
+ txdb.h txmempool.h ui_interface.h uint256.h util.h version.h walletdb.h wallet.h
JSON_H = json/json_spirit.h json/json_spirit_error_position.h \
json/json_spirit_reader.h json/json_spirit_reader_template.h \
@@ -36,7 +36,7 @@ libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom
init.cpp key.cpp keystore.cpp leveldb.cpp main.cpp miner.cpp \
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.cpp \
- sync.cpp txdb.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \
+ sync.cpp txdb.cpp txmempool.cpp util.cpp version.cpp wallet.cpp walletdb.cpp $(JSON_H) \
$(BITCOIN_CORE_H)
nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h
diff --git a/src/checkqueue.h b/src/checkqueue.h
index eba424fbaa..6e2f609fcf 100644
--- a/src/checkqueue.h
+++ b/src/checkqueue.h
@@ -4,6 +4,7 @@
#ifndef CHECKQUEUE_H
#define CHECKQUEUE_H
+#include <boost/foreach.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/condition_variable.hpp>
diff --git a/src/init.cpp b/src/init.cpp
index 647b8d52ea..a803598f32 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -106,7 +106,7 @@ void Shutdown()
if (!lockShutdown) return;
RenameThread("bitcoin-shutoff");
- nTransactionsUpdated++;
+ mempool.AddTransactionsUpdated(1);
StopRPCThreads();
ShutdownRPCMining();
if (pwalletMain)
@@ -478,7 +478,7 @@ bool AppInit2(boost::thread_group& threadGroup)
InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
fBenchmark = GetBoolArg("-benchmark", false);
- mempool.fChecks = GetBoolArg("-checkmempool", RegTest());
+ mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest()));
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
diff --git a/src/limitedmap.h b/src/limitedmap.h
index 7049d68e5a..01d1b07df4 100644
--- a/src/limitedmap.h
+++ b/src/limitedmap.h
@@ -4,6 +4,7 @@
#ifndef BITCOIN_LIMITEDMAP_H
#define BITCOIN_LIMITEDMAP_H
+#include <assert.h> // TODO: remove
#include <map>
#include <deque>
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) {
diff --git a/src/main.h b/src/main.h
index d568d8e6b8..80972330f3 100644
--- a/src/main.h
+++ b/src/main.h
@@ -12,6 +12,7 @@
#include "core.h"
#include "bignum.h"
#include "sync.h"
+#include "txmempool.h"
#include "net.h"
#include "script.h"
@@ -19,12 +20,11 @@
class CBlock;
class CBlockIndex;
-class CKeyItem;
-class CReserveKey;
-
-class CAddress;
class CInv;
+class CKeyItem;
class CNode;
+class CReserveKey;
+class CWallet;
/** The maximum allowed size for a serialized block, in bytes (network rule) */
static const unsigned int MAX_BLOCK_SIZE = 1000000;
@@ -70,8 +70,8 @@ extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
+extern CTxMemPool mempool;
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
-extern unsigned int nTransactionsUpdated;
extern uint64 nLastBlockTx;
extern uint64 nLastBlockSize;
extern const std::string strMessageMagic;
@@ -175,7 +175,9 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
/** Abort with a message */
bool AbortNode(const std::string &msg);
-
+/** (try to) add transaction to memory pool **/
+bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
+ bool* pfMissingInputs, bool fRejectInsaneFee=false);
@@ -1029,44 +1031,6 @@ extern CChain chainActive;
-
-
-class CTxMemPool
-{
-public:
- static bool fChecks;
- mutable CCriticalSection cs;
- std::map<uint256, CTransaction> mapTx;
- std::map<COutPoint, CInPoint> mapNextTx;
-
- bool accept(CValidationState &state, const CTransaction &tx, bool fLimitFree, bool* pfMissingInputs, bool fRejectInsaneFee = false);
- bool addUnchecked(const uint256& hash, const CTransaction &tx);
- bool remove(const CTransaction &tx, bool fRecursive = false);
- bool removeConflicts(const CTransaction &tx);
- void clear();
- void queryHashes(std::vector<uint256>& vtxid);
- void pruneSpent(const uint256& hash, CCoins &coins);
- void check(CCoinsViewCache *pcoins) const;
-
- unsigned long size()
- {
- LOCK(cs);
- return mapTx.size();
- }
-
- bool exists(uint256 hash)
- {
- return (mapTx.count(hash) != 0);
- }
-
- CTransaction& lookup(uint256 hash)
- {
- return mapTx[hash];
- }
-};
-
-extern CTxMemPool mempool;
-
struct CCoinsStats
{
int nHeight;
diff --git a/src/miner.cpp b/src/miner.cpp
index dca8609e17..b03f915dc9 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -521,7 +521,7 @@ void static BitcoinMiner(CWallet *pwallet)
//
// Create new block
//
- unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
+ unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrev = chainActive.Tip();
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
@@ -623,7 +623,7 @@ void static BitcoinMiner(CWallet *pwallet)
break;
if (nBlockNonce >= 0xffff0000)
break;
- if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
break;
if (pindexPrev != chainActive.Tip())
break;
diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp
index 77dc13815d..d91f26e20a 100644
--- a/src/rpcmining.cpp
+++ b/src/rpcmining.cpp
@@ -190,7 +190,7 @@ Value getwork(const Array& params, bool fHelp)
static int64 nStart;
static CBlockTemplate* pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
- (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
+ (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60))
{
if (pindexPrev != chainActive.Tip())
{
@@ -205,7 +205,7 @@ Value getwork(const Array& params, bool fHelp)
pindexPrev = NULL;
// Store the pindexBest used before CreateNewBlock, to avoid races
- nTransactionsUpdatedLast = nTransactionsUpdated;
+ nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrevNew = chainActive.Tip();
nStart = GetTime();
@@ -326,13 +326,13 @@ Value getblocktemplate(const Array& params, bool fHelp)
static int64 nStart;
static CBlockTemplate* pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
- (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
+ (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 5))
{
// Clear pindexPrev so future calls make a new block, despite any failures from here on
pindexPrev = NULL;
// Store the pindexBest used before CreateNewBlock, to avoid races
- nTransactionsUpdatedLast = nTransactionsUpdated;
+ nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex* pindexPrevNew = chainActive.Tip();
nStart = GetTime();
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 49987ecc47..4771094e1f 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -550,7 +550,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
if (!fHave) {
// push to local node
CValidationState state;
- if (!mempool.accept(state, tx, false, NULL, !fOverrideFees))
+ if (!AcceptToMemoryPool(mempool, state, tx, false, NULL, !fOverrideFees))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
}
}
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
new file mode 100644
index 0000000000..975955a458
--- /dev/null
+++ b/src/txmempool.cpp
@@ -0,0 +1,162 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2013 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "core.h"
+#include "txmempool.h"
+
+using namespace std;
+
+CTxMemPool::CTxMemPool()
+{
+ // Sanity checks off by default for performance, because otherwise
+ // accepting transactions becomes O(N^2) where N is the number
+ // of transactions in the pool
+ fSanityCheck = false;
+}
+
+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++;
+ }
+}
+
+unsigned int CTxMemPool::GetTransactionsUpdated() const
+{
+ LOCK(cs);
+ return nTransactionsUpdated;
+}
+
+void CTxMemPool::AddTransactionsUpdated(unsigned int n)
+{
+ LOCK(cs);
+ nTransactionsUpdated += n;
+}
+
+
+bool CTxMemPool::addUnchecked(const uint256& hash, const CTransaction &tx)
+{
+ // Add to memory pool without checking anything.
+ // Used by main.cpp AcceptToMemoryPool(), which DOES do
+ // all the appropriate checks.
+ LOCK(cs);
+ {
+ 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);
+ }
+ }
+ return true;
+}
+
+void CTxMemPool::clear()
+{
+ LOCK(cs);
+ mapTx.clear();
+ mapNextTx.clear();
+ ++nTransactionsUpdated;
+}
+
+void CTxMemPool::check(CTxMemPool::CoinLookupFunc fnLookup) const
+{
+ if (!fSanityCheck)
+ return;
+
+ LogPrint("mempool", "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 = (*fnLookup)(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);
+}
+
+bool CTxMemPool::lookup(uint256 hash, CTransaction& result) const
+{
+ LOCK(cs);
+ std::map<uint256, CTransaction>::const_iterator i = mapTx.find(hash);
+ if (i == mapTx.end()) return false;
+ result = i->second;
+ return true;
+}
diff --git a/src/txmempool.h b/src/txmempool.h
new file mode 100644
index 0000000000..1b555d99f4
--- /dev/null
+++ b/src/txmempool.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2013 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#ifndef BITCOIN_TXMEMPOOL_H
+#define BITCOIN_TXMEMPOOL_H
+
+#include "core.h"
+
+/*
+ * CTxMemPool stores valid-according-to-the-current-best-chain
+ * transactions that may be included in the next block.
+ *
+ * Transactions are added when they are seen on the network
+ * (or created by the local node), but not all transactions seen
+ * are added to the pool: if a new transaction double-spends
+ * an input of a transaction in the pool, it is dropped,
+ * as are non-standard transactions.
+ */
+class CTxMemPool
+{
+private:
+ bool fSanityCheck; // Normally false, true if -checkmempool or -regtest
+ unsigned int nTransactionsUpdated;
+
+public:
+ mutable CCriticalSection cs;
+ std::map<uint256, CTransaction> mapTx;
+ std::map<COutPoint, CInPoint> mapNextTx;
+
+ CTxMemPool();
+
+ /*
+ * If sanity-checking is turned on, check makes sure the pool is
+ * consistent (does not contain two transactions that spend the same inputs,
+ * all inputs are in the mapNextTx array). If sanity-checking is turned off,
+ * check does nothing.
+ */
+ typedef CCoins& (*CoinLookupFunc)(const uint256&);
+ void check(CoinLookupFunc fnLookup) const;
+ void setSanityCheck(bool _fSanityCheck) { fSanityCheck = _fSanityCheck; }
+
+ bool addUnchecked(const uint256& hash, const CTransaction &tx);
+ bool remove(const CTransaction &tx, bool fRecursive = false);
+ bool removeConflicts(const CTransaction &tx);
+ void clear();
+ void queryHashes(std::vector<uint256>& vtxid);
+ void pruneSpent(const uint256& hash, CCoins &coins);
+ unsigned int GetTransactionsUpdated() const;
+ void AddTransactionsUpdated(unsigned int n);
+
+ unsigned long size()
+ {
+ LOCK(cs);
+ return mapTx.size();
+ }
+
+ bool exists(uint256 hash)
+ {
+ LOCK(cs);
+ return (mapTx.count(hash) != 0);
+ }
+
+ bool lookup(uint256 hash, CTransaction& result) const;
+};
+
+#endif /* BITCOIN_TXMEMPOOL_H */