Refactor: CTxMempool class to its own txmempool.{cpp,h}
This commit is contained in:
parent
39b4f0d7dd
commit
319b11607f
11 changed files with 319 additions and 248 deletions
|
@ -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 \
|
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 \
|
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 \
|
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_H = json/json_spirit.h json/json_spirit_error_position.h \
|
||||||
json/json_spirit_reader.h json/json_spirit_reader_template.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 \
|
init.cpp key.cpp keystore.cpp leveldb.cpp main.cpp miner.cpp \
|
||||||
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
|
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
|
||||||
rpcmining.cpp rpcnet.cpp rpcrawtransaction.cpp rpcwallet.cpp script.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)
|
$(BITCOIN_CORE_H)
|
||||||
|
|
||||||
nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h
|
nodist_libbitcoin_a_SOURCES = $(top_srcdir)/src/obj/build.h
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#ifndef CHECKQUEUE_H
|
#ifndef CHECKQUEUE_H
|
||||||
#define CHECKQUEUE_H
|
#define CHECKQUEUE_H
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/thread/mutex.hpp>
|
#include <boost/thread/mutex.hpp>
|
||||||
#include <boost/thread/locks.hpp>
|
#include <boost/thread/locks.hpp>
|
||||||
#include <boost/thread/condition_variable.hpp>
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
|
|
@ -106,7 +106,7 @@ void Shutdown()
|
||||||
if (!lockShutdown) return;
|
if (!lockShutdown) return;
|
||||||
|
|
||||||
RenameThread("bitcoin-shutoff");
|
RenameThread("bitcoin-shutoff");
|
||||||
nTransactionsUpdated++;
|
mempool.AddTransactionsUpdated(1);
|
||||||
StopRPCThreads();
|
StopRPCThreads();
|
||||||
ShutdownRPCMining();
|
ShutdownRPCMining();
|
||||||
if (pwalletMain)
|
if (pwalletMain)
|
||||||
|
@ -478,7 +478,7 @@ bool AppInit2(boost::thread_group& threadGroup)
|
||||||
InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
|
InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
|
||||||
|
|
||||||
fBenchmark = GetBoolArg("-benchmark", false);
|
fBenchmark = GetBoolArg("-benchmark", false);
|
||||||
mempool.fChecks = GetBoolArg("-checkmempool", RegTest());
|
mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest()));
|
||||||
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
|
Checkpoints::fEnabled = GetBoolArg("-checkpoints", true);
|
||||||
|
|
||||||
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
// -par=0 means autodetect, but nScriptCheckThreads==0 means no concurrency
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#ifndef BITCOIN_LIMITEDMAP_H
|
#ifndef BITCOIN_LIMITEDMAP_H
|
||||||
#define BITCOIN_LIMITEDMAP_H
|
#define BITCOIN_LIMITEDMAP_H
|
||||||
|
|
||||||
|
#include <assert.h> // TODO: remove
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
|
|
254
src/main.cpp
254
src/main.cpp
|
@ -3,19 +3,21 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "alert.h"
|
|
||||||
#include "checkpoints.h"
|
|
||||||
#include "db.h"
|
|
||||||
#include "txdb.h"
|
|
||||||
#include "net.h"
|
|
||||||
#include "init.h"
|
|
||||||
#include "ui_interface.h"
|
|
||||||
#include "checkqueue.h"
|
|
||||||
#include "chainparams.h"
|
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
|
|
||||||
|
#include "alert.h"
|
||||||
|
#include "chainparams.h"
|
||||||
|
#include "checkpoints.h"
|
||||||
|
#include "checkqueue.h"
|
||||||
|
#include "db.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "net.h"
|
||||||
|
#include "txdb.h"
|
||||||
|
#include "txmempool.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
|
|
||||||
|
@ -29,7 +31,6 @@ set<CWallet*> setpwalletRegistered;
|
||||||
CCriticalSection cs_main;
|
CCriticalSection cs_main;
|
||||||
|
|
||||||
CTxMemPool mempool;
|
CTxMemPool mempool;
|
||||||
unsigned int nTransactionsUpdated = 0;
|
|
||||||
|
|
||||||
map<uint256, CBlockIndex*> mapBlockIndex;
|
map<uint256, CBlockIndex*> mapBlockIndex;
|
||||||
CChain chainActive;
|
CChain chainActive;
|
||||||
|
@ -319,6 +320,15 @@ unsigned int CCoinsViewCache::GetCacheSize() {
|
||||||
return cacheCoins.size();
|
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.
|
/** CCoinsView that brings transactions from a memorypool into view.
|
||||||
It does not check for spendings by memory pool transactions. */
|
It does not check for spendings by memory pool transactions. */
|
||||||
CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView &baseIn, CTxMemPool &mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { }
|
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) {
|
bool CCoinsViewMemPool::GetCoins(const uint256 &txid, CCoins &coins) {
|
||||||
if (base->GetCoins(txid, coins))
|
if (base->GetCoins(txid, coins))
|
||||||
return true;
|
return true;
|
||||||
if (mempool.exists(txid)) {
|
CTransaction tx;
|
||||||
const CTransaction &tx = mempool.lookup(txid);
|
if (mempool.lookup(txid, tx)) {
|
||||||
coins = CCoins(tx, MEMPOOL_HEIGHT);
|
coins = CCoins(tx, MEMPOOL_HEIGHT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -734,56 +744,39 @@ int64 GetMinFee(const CTransaction& tx, bool fAllowFree, enum GetMinFee_mode mod
|
||||||
return nMinFee;
|
return nMinFee;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPool::pruneSpent(const uint256 &hashTx, CCoins &coins)
|
|
||||||
{
|
|
||||||
LOCK(cs);
|
|
||||||
|
|
||||||
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.lower_bound(COutPoint(hashTx, 0));
|
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
|
||||||
|
|
||||||
// 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* pfMissingInputs, bool fRejectInsaneFee)
|
bool* pfMissingInputs, bool fRejectInsaneFee)
|
||||||
{
|
{
|
||||||
if (pfMissingInputs)
|
if (pfMissingInputs)
|
||||||
*pfMissingInputs = false;
|
*pfMissingInputs = false;
|
||||||
|
|
||||||
if (!CheckTransaction(tx, state))
|
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
|
// Coinbase is only valid in a block, not as a loose transaction
|
||||||
if (tx.IsCoinBase())
|
if (tx.IsCoinBase())
|
||||||
return state.DoS(100, error("CTxMemPool::accept() : coinbase as individual tx"));
|
return state.DoS(100, error("AcceptToMemoryPool: : 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");
|
|
||||||
|
|
||||||
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
||||||
string reason;
|
string reason;
|
||||||
if (Params().NetworkID() == CChainParams::MAIN && !IsStandardTx(tx, 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());
|
reason.c_str());
|
||||||
|
|
||||||
// is it already in the memory pool?
|
// is it already in the memory pool?
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
{
|
if (pool.exists(hash))
|
||||||
LOCK(cs);
|
|
||||||
if (mapTx.count(hash))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Check for conflicts with in-memory transactions
|
// Check for conflicts with in-memory transactions
|
||||||
CTransaction* ptxOld = NULL;
|
CTransaction* ptxOld = NULL;
|
||||||
|
{
|
||||||
|
LOCK(pool.cs); // protect pool.mapNextTx
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
COutPoint outpoint = tx.vin[i].prevout;
|
COutPoint outpoint = tx.vin[i].prevout;
|
||||||
if (mapNextTx.count(outpoint))
|
if (pool.mapNextTx.count(outpoint))
|
||||||
{
|
{
|
||||||
// Disable replacement feature for now
|
// Disable replacement feature for now
|
||||||
return false;
|
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
|
// Allow replacing with a newer version of the same transaction
|
||||||
if (i != 0)
|
if (i != 0)
|
||||||
return false;
|
return false;
|
||||||
ptxOld = mapNextTx[outpoint].ptx;
|
ptxOld = pool.mapNextTx[outpoint].ptx;
|
||||||
if (IsFinalTx(*ptxOld))
|
if (IsFinalTx(*ptxOld))
|
||||||
return false;
|
return false;
|
||||||
if (!tx.IsNewerThan(*ptxOld))
|
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++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
COutPoint outpoint = tx.vin[i].prevout;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
CCoinsView dummy;
|
CCoinsView dummy;
|
||||||
CCoinsViewCache view(dummy);
|
CCoinsViewCache view(dummy);
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(pool.cs);
|
||||||
CCoinsViewMemPool viewMemPool(*pcoinsTip, *this);
|
CCoinsViewMemPool viewMemPool(*pcoinsTip, pool);
|
||||||
view.SetBackend(viewMemPool);
|
view.SetBackend(viewMemPool);
|
||||||
|
|
||||||
// do we already have it?
|
// do we already have it?
|
||||||
|
@ -832,7 +826,7 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL
|
||||||
|
|
||||||
// are the actual inputs available?
|
// are the actual inputs available?
|
||||||
if (!view.HaveInputs(tx))
|
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
|
// Bring the best block into scope
|
||||||
view.GetBestBlock();
|
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
|
// Check for non-standard pay-to-script-hash in inputs
|
||||||
if (Params().NetworkID() == CChainParams::MAIN && !AreInputsStandard(tx, view))
|
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
|
// Note: if you modify this code to accept non-standard transactions, then
|
||||||
// you should add code here to check that the transaction does a
|
// 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
|
// Don't accept it if it can't get into a block
|
||||||
int64 txMinFee = GetMinFee(tx, true, GMF_RELAY);
|
int64 txMinFee = GetMinFee(tx, true, GMF_RELAY);
|
||||||
if (fLimitFree && nFees < txMinFee)
|
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(),
|
hash.ToString().c_str(),
|
||||||
nFees, txMinFee);
|
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.
|
// be annoying or make others' transactions take longer to confirm.
|
||||||
if (fLimitFree && nFees < CTransaction::nMinRelayTxFee)
|
if (fLimitFree && nFees < CTransaction::nMinRelayTxFee)
|
||||||
{
|
{
|
||||||
|
static CCriticalSection csFreeLimiter;
|
||||||
static double dFreeCount;
|
static double dFreeCount;
|
||||||
static int64 nLastTime;
|
static int64 nLastTime;
|
||||||
int64 nNow = GetTime();
|
int64 nNow = GetTime();
|
||||||
|
|
||||||
LOCK(cs);
|
LOCK(csFreeLimiter);
|
||||||
|
|
||||||
// Use an exponentially decaying ~10-minute window:
|
// Use an exponentially decaying ~10-minute window:
|
||||||
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
|
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
|
// -limitfreerelay unit is thousand-bytes-per-minute
|
||||||
// At default rate it would take over a month to fill 1GB
|
// At default rate it would take over a month to fill 1GB
|
||||||
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
|
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
|
||||||
return error("CTxMemPool::accept() : free transaction rejected by rate limiter");
|
return error("AcceptToMemoryPool: : free transaction rejected by rate limiter");
|
||||||
if (fDebug)
|
|
||||||
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
|
||||||
dFreeCount += nSize;
|
dFreeCount += nSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fRejectInsaneFee && nFees > CTransaction::nMinRelayTxFee * 10000)
|
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(),
|
hash.ToString().c_str(),
|
||||||
nFees, CTransaction::nMinRelayTxFee * 10000);
|
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.
|
// 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))
|
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
|
// Store transaction in memory
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
|
||||||
if (ptxOld)
|
if (ptxOld)
|
||||||
{
|
{
|
||||||
LogPrint("mempool", "CTxMemPool::accept() : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
|
LogPrint("mempool", "AcceptToMemoryPool: : replacing tx %s with new version\n", ptxOld->GetHash().ToString().c_str());
|
||||||
remove(*ptxOld);
|
pool.remove(*ptxOld);
|
||||||
}
|
}
|
||||||
addUnchecked(hash, tx);
|
pool.addUnchecked(hash, tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
///// are we sure this is ok when loading transactions or restoring block txes
|
///// are we sure this is ok when loading transactions or restoring block txes
|
||||||
|
@ -912,126 +905,13 @@ bool CTxMemPool::accept(CValidationState &state, const CTransaction &tx, bool fL
|
||||||
g_signals.EraseTransaction(ptxOld->GetHash());
|
g_signals.EraseTransaction(ptxOld->GetHash());
|
||||||
g_signals.SyncTransaction(hash, tx, NULL);
|
g_signals.SyncTransaction(hash, tx, NULL);
|
||||||
|
|
||||||
LogPrint("mempool", "CTxMemPool::accept() : accepted %s (poolsz %"PRIszu")\n",
|
LogPrint("mempool", "AcceptToMemoryPool: : accepted %s (poolsz %"PRIszu")\n",
|
||||||
hash.ToString().c_str(),
|
hash.ToString().c_str(),
|
||||||
mapTx.size());
|
pool.mapTx.size());
|
||||||
return true;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
int CMerkleTx::GetDepthInMainChain(CBlockIndex* &pindexRet) const
|
||||||
{
|
{
|
||||||
if (hashBlock == 0 || nIndex == -1)
|
if (hashBlock == 0 || nIndex == -1)
|
||||||
|
@ -1069,7 +949,7 @@ int CMerkleTx::GetBlocksToMaturity() const
|
||||||
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree)
|
bool CMerkleTx::AcceptToMemoryPool(bool fLimitFree)
|
||||||
{
|
{
|
||||||
CValidationState state;
|
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(cs_main);
|
||||||
{
|
{
|
||||||
LOCK(mempool.cs);
|
if (mempool.lookup(hash, txOut))
|
||||||
if (mempool.exists(hash))
|
|
||||||
{
|
{
|
||||||
txOut = mempool.lookup(hash);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1959,7 +1837,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
|
||||||
|
|
||||||
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
||||||
{
|
{
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(&LookupFromTip);
|
||||||
|
|
||||||
// All modifications to the coin state will be done in this cache.
|
// All modifications to the coin state will be done in this cache.
|
||||||
// Only when all have succeeded, we push it to pcoinsTip.
|
// 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) {
|
BOOST_FOREACH(CTransaction& tx, vResurrect) {
|
||||||
// ignore validation errors in resurrected transactions
|
// ignore validation errors in resurrected transactions
|
||||||
CValidationState stateDummy;
|
CValidationState stateDummy;
|
||||||
if (!mempool.accept(stateDummy, tx, false, NULL))
|
if (!AcceptToMemoryPool(mempool,stateDummy, tx, false, NULL))
|
||||||
mempool.remove(tx, true);
|
mempool.remove(tx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2082,7 +1960,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
||||||
mempool.removeConflicts(tx);
|
mempool.removeConflicts(tx);
|
||||||
}
|
}
|
||||||
|
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(&LookupFromTip);
|
||||||
|
|
||||||
// Update best block in wallet (so we can detect restored wallets)
|
// Update best block in wallet (so we can detect restored wallets)
|
||||||
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
|
if ((pindexNew->nHeight % 20160) == 0 || (!fIsInitialDownload && (pindexNew->nHeight % 144) == 0))
|
||||||
|
@ -2090,7 +1968,7 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew)
|
||||||
|
|
||||||
// New best block
|
// New best block
|
||||||
nTimeBestReceived = GetTime();
|
nTimeBestReceived = GetTime();
|
||||||
nTransactionsUpdated++;
|
mempool.AddTransactionsUpdated(1);
|
||||||
LogPrintf("SetBestChain: new best=%s height=%d log2_work=%.8g tx=%lu date=%s progress=%f\n",
|
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,
|
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(),
|
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:
|
case MSG_TX:
|
||||||
{
|
{
|
||||||
bool txInMap = false;
|
bool txInMap = false;
|
||||||
{
|
|
||||||
LOCK(mempool.cs);
|
|
||||||
txInMap = mempool.exists(inv.hash);
|
txInMap = mempool.exists(inv.hash);
|
||||||
}
|
|
||||||
return txInMap || mapOrphanTransactions.count(inv.hash) ||
|
return txInMap || mapOrphanTransactions.count(inv.hash) ||
|
||||||
pcoinsTip->HaveCoins(inv.hash);
|
pcoinsTip->HaveCoins(inv.hash);
|
||||||
}
|
}
|
||||||
|
@ -3264,9 +3139,8 @@ void static ProcessGetData(CNode* pfrom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!pushed && inv.type == MSG_TX) {
|
if (!pushed && inv.type == MSG_TX) {
|
||||||
LOCK(mempool.cs);
|
CTransaction tx;
|
||||||
if (mempool.exists(inv.hash)) {
|
if (mempool.lookup(inv.hash, tx)) {
|
||||||
CTransaction tx = mempool.lookup(inv.hash);
|
|
||||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
ss.reserve(1000);
|
ss.reserve(1000);
|
||||||
ss << tx;
|
ss << tx;
|
||||||
|
@ -3658,9 +3532,9 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
|
|
||||||
bool fMissingInputs = false;
|
bool fMissingInputs = false;
|
||||||
CValidationState state;
|
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);
|
RelayTransaction(tx, inv.hash);
|
||||||
mapAlreadyAskedFor.erase(inv);
|
mapAlreadyAskedFor.erase(inv);
|
||||||
vWorkQueue.push_back(inv.hash);
|
vWorkQueue.push_back(inv.hash);
|
||||||
|
@ -3682,7 +3556,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
// anyone relaying LegitTxX banned)
|
// anyone relaying LegitTxX banned)
|
||||||
CValidationState stateDummy;
|
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());
|
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString().c_str());
|
||||||
RelayTransaction(orphanTx, orphanHash);
|
RelayTransaction(orphanTx, orphanHash);
|
||||||
|
@ -3696,7 +3570,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
vEraseQueue.push_back(orphanHash);
|
vEraseQueue.push_back(orphanHash);
|
||||||
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString().c_str());
|
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")
|
else if (strCommand == "mempool")
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK2(cs_main, pfrom->cs_filter);
|
||||||
|
|
||||||
std::vector<uint256> vtxid;
|
std::vector<uint256> vtxid;
|
||||||
LOCK2(mempool.cs, pfrom->cs_filter);
|
|
||||||
mempool.queryHashes(vtxid);
|
mempool.queryHashes(vtxid);
|
||||||
vector<CInv> vInv;
|
vector<CInv> vInv;
|
||||||
BOOST_FOREACH(uint256& hash, vtxid) {
|
BOOST_FOREACH(uint256& hash, vtxid) {
|
||||||
CInv inv(MSG_TX, hash);
|
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))
|
(!pfrom->pfilter))
|
||||||
vInv.push_back(inv);
|
vInv.push_back(inv);
|
||||||
if (vInv.size() == MAX_INV_SZ) {
|
if (vInv.size() == MAX_INV_SZ) {
|
||||||
|
|
52
src/main.h
52
src/main.h
|
@ -12,6 +12,7 @@
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "bignum.h"
|
#include "bignum.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
|
#include "txmempool.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "script.h"
|
#include "script.h"
|
||||||
|
|
||||||
|
@ -19,12 +20,11 @@
|
||||||
|
|
||||||
class CBlock;
|
class CBlock;
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CKeyItem;
|
|
||||||
class CReserveKey;
|
|
||||||
|
|
||||||
class CAddress;
|
|
||||||
class CInv;
|
class CInv;
|
||||||
|
class CKeyItem;
|
||||||
class CNode;
|
class CNode;
|
||||||
|
class CReserveKey;
|
||||||
|
class CWallet;
|
||||||
|
|
||||||
/** The maximum allowed size for a serialized block, in bytes (network rule) */
|
/** The maximum allowed size for a serialized block, in bytes (network rule) */
|
||||||
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
||||||
|
@ -70,8 +70,8 @@ extern CScript COINBASE_FLAGS;
|
||||||
|
|
||||||
|
|
||||||
extern CCriticalSection cs_main;
|
extern CCriticalSection cs_main;
|
||||||
|
extern CTxMemPool mempool;
|
||||||
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
|
extern std::map<uint256, CBlockIndex*> mapBlockIndex;
|
||||||
extern unsigned int nTransactionsUpdated;
|
|
||||||
extern uint64 nLastBlockTx;
|
extern uint64 nLastBlockTx;
|
||||||
extern uint64 nLastBlockSize;
|
extern uint64 nLastBlockSize;
|
||||||
extern const std::string strMessageMagic;
|
extern const std::string strMessageMagic;
|
||||||
|
@ -175,7 +175,9 @@ bool VerifySignature(const CCoins& txFrom, const CTransaction& txTo, unsigned in
|
||||||
/** Abort with a message */
|
/** Abort with a message */
|
||||||
bool AbortNode(const std::string &msg);
|
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
|
struct CCoinsStats
|
||||||
{
|
{
|
||||||
int nHeight;
|
int nHeight;
|
||||||
|
|
|
@ -521,7 +521,7 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||||
//
|
//
|
||||||
// Create new block
|
// Create new block
|
||||||
//
|
//
|
||||||
unsigned int nTransactionsUpdatedLast = nTransactionsUpdated;
|
unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||||
CBlockIndex* pindexPrev = chainActive.Tip();
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
||||||
|
|
||||||
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
|
auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
|
||||||
|
@ -623,7 +623,7 @@ void static BitcoinMiner(CWallet *pwallet)
|
||||||
break;
|
break;
|
||||||
if (nBlockNonce >= 0xffff0000)
|
if (nBlockNonce >= 0xffff0000)
|
||||||
break;
|
break;
|
||||||
if (nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60)
|
if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
|
||||||
break;
|
break;
|
||||||
if (pindexPrev != chainActive.Tip())
|
if (pindexPrev != chainActive.Tip())
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -190,7 +190,7 @@ Value getwork(const Array& params, bool fHelp)
|
||||||
static int64 nStart;
|
static int64 nStart;
|
||||||
static CBlockTemplate* pblocktemplate;
|
static CBlockTemplate* pblocktemplate;
|
||||||
if (pindexPrev != chainActive.Tip() ||
|
if (pindexPrev != chainActive.Tip() ||
|
||||||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
|
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60))
|
||||||
{
|
{
|
||||||
if (pindexPrev != chainActive.Tip())
|
if (pindexPrev != chainActive.Tip())
|
||||||
{
|
{
|
||||||
|
@ -205,7 +205,7 @@ Value getwork(const Array& params, bool fHelp)
|
||||||
pindexPrev = NULL;
|
pindexPrev = NULL;
|
||||||
|
|
||||||
// Store the pindexBest used before CreateNewBlock, to avoid races
|
// Store the pindexBest used before CreateNewBlock, to avoid races
|
||||||
nTransactionsUpdatedLast = nTransactionsUpdated;
|
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||||
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
||||||
nStart = GetTime();
|
nStart = GetTime();
|
||||||
|
|
||||||
|
@ -326,13 +326,13 @@ Value getblocktemplate(const Array& params, bool fHelp)
|
||||||
static int64 nStart;
|
static int64 nStart;
|
||||||
static CBlockTemplate* pblocktemplate;
|
static CBlockTemplate* pblocktemplate;
|
||||||
if (pindexPrev != chainActive.Tip() ||
|
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
|
// Clear pindexPrev so future calls make a new block, despite any failures from here on
|
||||||
pindexPrev = NULL;
|
pindexPrev = NULL;
|
||||||
|
|
||||||
// Store the pindexBest used before CreateNewBlock, to avoid races
|
// Store the pindexBest used before CreateNewBlock, to avoid races
|
||||||
nTransactionsUpdatedLast = nTransactionsUpdated;
|
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
|
||||||
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
CBlockIndex* pindexPrevNew = chainActive.Tip();
|
||||||
nStart = GetTime();
|
nStart = GetTime();
|
||||||
|
|
||||||
|
|
|
@ -550,7 +550,7 @@ Value sendrawtransaction(const Array& params, bool fHelp)
|
||||||
if (!fHave) {
|
if (!fHave) {
|
||||||
// push to local node
|
// push to local node
|
||||||
CValidationState state;
|
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
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
162
src/txmempool.cpp
Normal file
162
src/txmempool.cpp
Normal file
|
@ -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;
|
||||||
|
}
|
67
src/txmempool.h
Normal file
67
src/txmempool.h
Normal file
|
@ -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 */
|
Loading…
Reference in a new issue