Merge #8448: Store mempool and prioritization data to disk

582068a Add mempool.dat to doc/files.md (Pieter Wuille)
3f78562 Add DumpMempool and LoadMempool (Pieter Wuille)
ced7c94 Add AcceptToMemoryPoolWithTime function (Pieter Wuille)
c3efb58 Add feedelta to TxMempoolInfo (Pieter Wuille)
pull/308/head
Wladimir J. van der Laan 8 years ago
commit 101c642bef
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6

@ -10,6 +10,7 @@
* db.log: wallet database log file * db.log: wallet database log file
* debug.log: contains debug information and general logging generated by bitcoind or bitcoin-qt * debug.log: contains debug information and general logging generated by bitcoind or bitcoin-qt
* fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation; since 0.10.0 * fee_estimates.dat: stores statistics used to estimate minimum transaction fees and priorities required for confirmation; since 0.10.0
* mempool.dat: dump of the mempool's transactions; since 0.14.0.
* peers.dat: peer IP address database (custom format); since 0.7.0 * peers.dat: peer IP address database (custom format); since 0.7.0
* wallet.dat: personal wallet (BDB) with keys and transactions * wallet.dat: personal wallet (BDB) with keys and transactions
* .cookie: session RPC authentication cookie (written at start when cookie authentication is used, deleted on shutdown): since 0.12.0 * .cookie: session RPC authentication cookie (written at start when cookie authentication is used, deleted on shutdown): since 0.12.0

@ -207,6 +207,7 @@ void Shutdown()
StopTorControl(); StopTorControl();
UnregisterNodeSignals(GetNodeSignals()); UnregisterNodeSignals(GetNodeSignals());
DumpMempool();
if (fFeeEstimatesInitialized) if (fFeeEstimatesInitialized)
{ {
@ -659,6 +660,8 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
LogPrintf("Stopping after block import\n"); LogPrintf("Stopping after block import\n");
StartShutdown(); StartShutdown();
} }
LoadMempool();
} }
/** Sanity checks /** Sanity checks

@ -1135,7 +1135,7 @@ std::string FormatStateMessage(const CValidationState &state)
} }
bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee,
std::vector<uint256>& vHashTxnToUncache) std::vector<uint256>& vHashTxnToUncache)
{ {
const uint256 hash = tx.GetHash(); const uint256 hash = tx.GetHash();
@ -1308,7 +1308,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
} }
} }
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp); CTxMemPoolEntry entry(tx, nFees, nAcceptTime, dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
unsigned int nSize = entry.GetTxSize(); unsigned int nSize = entry.GetTxSize();
// Check that the transaction doesn't have an excessive number of // Check that the transaction doesn't have an excessive number of
@ -1572,11 +1572,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
return true; return true;
} }
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee) bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{ {
std::vector<uint256> vHashTxToUncache; std::vector<uint256> vHashTxToUncache;
bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache); bool res = AcceptToMemoryPoolWorker(pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, fOverrideMempoolLimit, nAbsurdFee, vHashTxToUncache);
if (!res) { if (!res) {
BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache) BOOST_FOREACH(const uint256& hashTx, vHashTxToUncache)
pcoinsTip->Uncache(hashTx); pcoinsTip->Uncache(hashTx);
@ -1584,6 +1584,12 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
return res; return res;
} }
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount nAbsurdFee)
{
return AcceptToMemoryPoolWithTime(pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), fOverrideMempoolLimit, nAbsurdFee);
}
/** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */
bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow) bool GetTransaction(const uint256 &hash, CTransaction &txOut, const Consensus::Params& consensusParams, uint256 &hashBlock, bool fAllowSlow)
{ {
@ -6929,6 +6935,119 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D
return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache); return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, versionbitscache);
} }
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
bool LoadMempool(void)
{
int64_t nExpiryTimeout = GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
FILE* filestr = fopen((GetDataDir() / "mempool.dat").string().c_str(), "r");
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
if (file.IsNull()) {
LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n");
return false;
}
int64_t count = 0;
int64_t skipped = 0;
int64_t failed = 0;
int64_t nNow = GetTime();
try {
uint64_t version;
file >> version;
if (version != MEMPOOL_DUMP_VERSION) {
return false;
}
uint64_t num;
file >> num;
double prioritydummy = 0;
while (num--) {
CTransaction tx;
int64_t nTime;
int64_t nFeeDelta;
file >> tx;
file >> nTime;
file >> nFeeDelta;
CAmount amountdelta = nFeeDelta;
if (amountdelta) {
mempool.PrioritiseTransaction(tx.GetHash(), tx.GetHash().ToString(), prioritydummy, amountdelta);
}
CValidationState state;
if (nTime + nExpiryTimeout > nNow) {
LOCK(cs_main);
AcceptToMemoryPoolWithTime(mempool, state, tx, true, NULL, nTime);
if (state.IsValid()) {
++count;
} else {
++failed;
}
} else {
++skipped;
}
}
std::map<uint256, CAmount> mapDeltas;
file >> mapDeltas;
for (const auto& i : mapDeltas) {
mempool.PrioritiseTransaction(i.first, i.first.ToString(), prioritydummy, i.second);
}
} catch (const std::exception& e) {
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
return false;
}
LogPrintf("Imported mempool transactions from disk: %i successes, %i failed, %i expired\n", count, failed, skipped);
return true;
}
void DumpMempool(void)
{
int64_t start = GetTimeMicros();
std::map<uint256, CAmount> mapDeltas;
std::vector<TxMempoolInfo> vinfo;
{
LOCK(mempool.cs);
for (const auto &i : mempool.mapDeltas) {
mapDeltas[i.first] = i.second.first;
}
vinfo = mempool.infoAll();
}
int64_t mid = GetTimeMicros();
try {
FILE* filestr = fopen((GetDataDir() / "mempool.dat.new").string().c_str(), "w");
if (!filestr) {
return;
}
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
uint64_t version = MEMPOOL_DUMP_VERSION;
file << version;
file << (uint64_t)vinfo.size();
for (const auto& i : vinfo) {
file << *(i.tx);
file << (int64_t)i.nTime;
file << (int64_t)i.nFeeDelta;
mapDeltas.erase(i.tx->GetHash());
}
file << mapDeltas;
FileCommit(file.Get());
file.fclose();
RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat");
int64_t last = GetTimeMicros();
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001);
} catch (const std::exception& e) {
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
}
}
class CMainCleanup class CMainCleanup
{ {
public: public:

@ -291,6 +291,10 @@ void PruneAndFlush();
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); bool* pfMissingInputs, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
/** (try to) add transaction to memory pool with a specified acceptance time **/
bool AcceptToMemoryPoolWithTime(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
bool* pfMissingInputs, int64_t nAcceptTime, bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0);
/** Convert CValidationState to a human-readable message for logging */ /** Convert CValidationState to a human-readable message for logging */
std::string FormatStateMessage(const CValidationState &state); std::string FormatStateMessage(const CValidationState &state);
@ -529,6 +533,12 @@ static const unsigned int REJECT_ALREADY_KNOWN = 0x101;
/** Transaction conflicts with a transaction already known */ /** Transaction conflicts with a transaction already known */
static const unsigned int REJECT_CONFLICT = 0x102; static const unsigned int REJECT_CONFLICT = 0x102;
/** Dump the mempool to disk. */
void DumpMempool();
/** Load the mempool from disk. */
bool LoadMempool();
// The following things handle network-processing logic // The following things handle network-processing logic
// (and should be moved to a separate file) // (and should be moved to a separate file)

@ -833,6 +833,10 @@ void CTxMemPool::queryHashes(vector<uint256>& vtxid)
} }
} }
static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) {
return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize()), it->GetModifiedFee() - it->GetFee()};
}
std::vector<TxMempoolInfo> CTxMemPool::infoAll() const std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
{ {
LOCK(cs); LOCK(cs);
@ -841,7 +845,7 @@ std::vector<TxMempoolInfo> CTxMemPool::infoAll() const
std::vector<TxMempoolInfo> ret; std::vector<TxMempoolInfo> ret;
ret.reserve(mapTx.size()); ret.reserve(mapTx.size());
for (auto it : iters) { for (auto it : iters) {
ret.push_back(TxMempoolInfo{it->GetSharedTx(), it->GetTime(), CFeeRate(it->GetFee(), it->GetTxSize())}); ret.push_back(GetInfo(it));
} }
return ret; return ret;
@ -862,7 +866,7 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
indexed_transaction_set::const_iterator i = mapTx.find(hash); indexed_transaction_set::const_iterator i = mapTx.find(hash);
if (i == mapTx.end()) if (i == mapTx.end())
return TxMempoolInfo(); return TxMempoolInfo();
return TxMempoolInfo{i->GetSharedTx(), i->GetTime(), CFeeRate(i->GetFee(), i->GetTxSize())}; return GetInfo(i);
} }
CFeeRate CTxMemPool::estimateFee(int nBlocks) const CFeeRate CTxMemPool::estimateFee(int nBlocks) const

@ -329,6 +329,9 @@ struct TxMempoolInfo
/** Feerate of the transaction. */ /** Feerate of the transaction. */
CFeeRate feeRate; CFeeRate feeRate;
/** The fee delta. */
int64_t nFeeDelta;
}; };
/** /**

Loading…
Cancel
Save