mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-06-06 13:10:09 +02:00
Merge #15323: rpc: Expose g_is_mempool_loaded via getmempoolinfo
effe81f750 Move g_is_mempool_loaded into CTxMemPool::m_is_loaded (Ben Woosley) bb8ae2c419 rpc: Expose g_is_mempool_loaded via getmempoolinfo and /rest/mempool/info.json (Ben Woosley) Pull request description: And use it to fix a race condition in mempool_persist.py: https://travis-ci.org/Empact/bitcoin/jobs/487577243 Since e.g. getrawmempool returns errors based on this status, this enables users to test it for readiness. Fixes #12863 ACKs for commit effe81: MarcoFalke: utACK effe81f750 jnewbery: utACK effe81f7503d2ca3c88cfdea687f9f997f353e0d Tree-SHA512: 74328b0c17a97efb8a000d4ee49b9a673c2b6dde7ea30c43a6a2eff961a233351c9471f9a42344412135786c02bdf2ee1b2526651bb8fed68bd94d2120c4ef86
This commit is contained in:
commit
12aa2ac988
@ -101,6 +101,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
|
|||||||
|
|
||||||
Returns various information about the TX mempool.
|
Returns various information about the TX mempool.
|
||||||
Only supports JSON as output format.
|
Only supports JSON as output format.
|
||||||
|
* loaded : (boolean) if the mempool is fully loaded
|
||||||
* size : (numeric) the number of transactions in the TX mempool
|
* size : (numeric) the number of transactions in the TX mempool
|
||||||
* bytes : (numeric) size of the TX mempool in bytes
|
* bytes : (numeric) size of the TX mempool in bytes
|
||||||
* usage : (numeric) total TX mempool memory usage
|
* usage : (numeric) total TX mempool memory usage
|
||||||
|
@ -241,8 +241,8 @@ void Shutdown(InitInterfaces& interfaces)
|
|||||||
g_txindex.reset();
|
g_txindex.reset();
|
||||||
DestroyAllBlockFilterIndexes();
|
DestroyAllBlockFilterIndexes();
|
||||||
|
|
||||||
if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
||||||
DumpMempool();
|
DumpMempool(::mempool);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fFeeEstimatesInitialized)
|
if (fFeeEstimatesInitialized)
|
||||||
@ -735,9 +735,9 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
|
|||||||
}
|
}
|
||||||
} // End scope of CImportingNow
|
} // End scope of CImportingNow
|
||||||
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
|
||||||
LoadMempool();
|
LoadMempool(::mempool);
|
||||||
}
|
}
|
||||||
g_is_mempool_loaded = !ShutdownRequested();
|
::mempool.SetIsLoaded(!ShutdownRequested());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sanity checks
|
/** Sanity checks
|
||||||
|
@ -1491,6 +1491,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
|
|||||||
// Make sure this call is atomic in the pool.
|
// Make sure this call is atomic in the pool.
|
||||||
LOCK(pool.cs);
|
LOCK(pool.cs);
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
|
ret.pushKV("loaded", pool.IsLoaded());
|
||||||
ret.pushKV("size", (int64_t)pool.size());
|
ret.pushKV("size", (int64_t)pool.size());
|
||||||
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
|
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
|
||||||
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
|
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
|
||||||
@ -1511,6 +1512,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
|
|||||||
{},
|
{},
|
||||||
RPCResult{
|
RPCResult{
|
||||||
"{\n"
|
"{\n"
|
||||||
|
" \"loaded\": true|false (boolean) True if the mempool is fully loaded\n"
|
||||||
" \"size\": xxxxx, (numeric) Current tx count\n"
|
" \"size\": xxxxx, (numeric) Current tx count\n"
|
||||||
" \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
|
" \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n"
|
||||||
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
|
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
|
||||||
@ -2061,11 +2063,11 @@ static UniValue savemempool(const JSONRPCRequest& request)
|
|||||||
}.ToString());
|
}.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_is_mempool_loaded) {
|
if (!::mempool.IsLoaded()) {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
|
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DumpMempool()) {
|
if (!DumpMempool(::mempool)) {
|
||||||
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,4 +1091,16 @@ void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CTxMemPool::IsLoaded() const
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
return m_is_loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTxMemPool::SetIsLoaded(bool loaded)
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
m_is_loaded = loaded;
|
||||||
|
}
|
||||||
|
|
||||||
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
|
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}
|
||||||
|
@ -455,6 +455,8 @@ private:
|
|||||||
|
|
||||||
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
||||||
|
|
||||||
|
bool m_is_loaded GUARDED_BY(cs){false};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
|
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
|
||||||
@ -672,6 +674,12 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
|
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
|
||||||
|
|
||||||
|
/** @returns true if the mempool is fully loaded */
|
||||||
|
bool IsLoaded() const;
|
||||||
|
|
||||||
|
/** Sets the current loaded state */
|
||||||
|
void SetIsLoaded(bool loaded);
|
||||||
|
|
||||||
unsigned long size() const
|
unsigned long size() const
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
|
@ -256,7 +256,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
|
|||||||
|
|
||||||
CBlockPolicyEstimator feeEstimator;
|
CBlockPolicyEstimator feeEstimator;
|
||||||
CTxMemPool mempool(&feeEstimator);
|
CTxMemPool mempool(&feeEstimator);
|
||||||
std::atomic_bool g_is_mempool_loaded{false};
|
|
||||||
|
|
||||||
/** Constant stuff for coinbase transactions we create: */
|
/** Constant stuff for coinbase transactions we create: */
|
||||||
CScript COINBASE_FLAGS;
|
CScript COINBASE_FLAGS;
|
||||||
@ -4735,7 +4734,7 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D
|
|||||||
|
|
||||||
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
|
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
|
||||||
|
|
||||||
bool LoadMempool()
|
bool LoadMempool(CTxMemPool& pool)
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
|
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
|
||||||
@ -4770,12 +4769,12 @@ bool LoadMempool()
|
|||||||
|
|
||||||
CAmount amountdelta = nFeeDelta;
|
CAmount amountdelta = nFeeDelta;
|
||||||
if (amountdelta) {
|
if (amountdelta) {
|
||||||
mempool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
|
||||||
}
|
}
|
||||||
CValidationState state;
|
CValidationState state;
|
||||||
if (nTime + nExpiryTimeout > nNow) {
|
if (nTime + nExpiryTimeout > nNow) {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
|
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nullptr /* pfMissingInputs */, nTime,
|
||||||
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
|
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
|
||||||
false /* test_accept */);
|
false /* test_accept */);
|
||||||
if (state.IsValid()) {
|
if (state.IsValid()) {
|
||||||
@ -4785,7 +4784,7 @@ bool LoadMempool()
|
|||||||
// wallet(s) having loaded it while we were processing
|
// wallet(s) having loaded it while we were processing
|
||||||
// mempool transactions; consider these as valid, instead of
|
// mempool transactions; consider these as valid, instead of
|
||||||
// failed, but mark them as 'already there'
|
// failed, but mark them as 'already there'
|
||||||
if (mempool.exists(tx->GetHash())) {
|
if (pool.exists(tx->GetHash())) {
|
||||||
++already_there;
|
++already_there;
|
||||||
} else {
|
} else {
|
||||||
++failed;
|
++failed;
|
||||||
@ -4801,7 +4800,7 @@ bool LoadMempool()
|
|||||||
file >> mapDeltas;
|
file >> mapDeltas;
|
||||||
|
|
||||||
for (const auto& i : mapDeltas) {
|
for (const auto& i : mapDeltas) {
|
||||||
mempool.PrioritiseTransaction(i.first, i.second);
|
pool.PrioritiseTransaction(i.first, i.second);
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
|
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
|
||||||
@ -4812,7 +4811,7 @@ bool LoadMempool()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DumpMempool()
|
bool DumpMempool(const CTxMemPool& pool)
|
||||||
{
|
{
|
||||||
int64_t start = GetTimeMicros();
|
int64_t start = GetTimeMicros();
|
||||||
|
|
||||||
@ -4823,11 +4822,11 @@ bool DumpMempool()
|
|||||||
LOCK(dump_mutex);
|
LOCK(dump_mutex);
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(mempool.cs);
|
LOCK(pool.cs);
|
||||||
for (const auto &i : mempool.mapDeltas) {
|
for (const auto &i : pool.mapDeltas) {
|
||||||
mapDeltas[i.first] = i.second;
|
mapDeltas[i.first] = i.second;
|
||||||
}
|
}
|
||||||
vinfo = mempool.infoAll();
|
vinfo = pool.infoAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t mid = GetTimeMicros();
|
int64_t mid = GetTimeMicros();
|
||||||
|
@ -142,7 +142,6 @@ extern CScript COINBASE_FLAGS;
|
|||||||
extern CCriticalSection cs_main;
|
extern CCriticalSection cs_main;
|
||||||
extern CBlockPolicyEstimator feeEstimator;
|
extern CBlockPolicyEstimator feeEstimator;
|
||||||
extern CTxMemPool mempool;
|
extern CTxMemPool mempool;
|
||||||
extern std::atomic_bool g_is_mempool_loaded;
|
|
||||||
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
||||||
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
|
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
|
||||||
extern Mutex g_best_block_mutex;
|
extern Mutex g_best_block_mutex;
|
||||||
@ -474,10 +473,10 @@ static const unsigned int REJECT_HIGHFEE = 0x100;
|
|||||||
CBlockFileInfo* GetBlockFileInfo(size_t n);
|
CBlockFileInfo* GetBlockFileInfo(size_t n);
|
||||||
|
|
||||||
/** Dump the mempool to disk. */
|
/** Dump the mempool to disk. */
|
||||||
bool DumpMempool();
|
bool DumpMempool(const CTxMemPool& pool);
|
||||||
|
|
||||||
/** Load the mempool from disk. */
|
/** Load the mempool from disk. */
|
||||||
bool LoadMempool();
|
bool LoadMempool(CTxMemPool& pool);
|
||||||
|
|
||||||
//! Check whether the block associated with this index entry is pruned or not.
|
//! Check whether the block associated with this index entry is pruned or not.
|
||||||
inline bool IsBlockPruned(const CBlockIndex* pblockindex)
|
inline bool IsBlockPruned(const CBlockIndex* pblockindex)
|
||||||
|
@ -37,7 +37,6 @@ Test is as follows:
|
|||||||
"""
|
"""
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import os
|
import os
|
||||||
import time
|
|
||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until
|
from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until
|
||||||
@ -83,9 +82,10 @@ class MempoolPersistTest(BitcoinTestFramework):
|
|||||||
self.start_node(1, extra_args=["-persistmempool=0"])
|
self.start_node(1, extra_args=["-persistmempool=0"])
|
||||||
self.start_node(0)
|
self.start_node(0)
|
||||||
self.start_node(2)
|
self.start_node(2)
|
||||||
# Give bitcoind a second to reload the mempool
|
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"], timeout=1)
|
||||||
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5, timeout=1)
|
wait_until(lambda: self.nodes[2].getmempoolinfo()["loaded"], timeout=1)
|
||||||
wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5, timeout=1)
|
assert_equal(len(self.nodes[0].getrawmempool()), 5)
|
||||||
|
assert_equal(len(self.nodes[2].getrawmempool()), 5)
|
||||||
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
|
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
|
||||||
assert_equal(len(self.nodes[1].getrawmempool()), 0)
|
assert_equal(len(self.nodes[1].getrawmempool()), 0)
|
||||||
|
|
||||||
@ -100,14 +100,14 @@ class MempoolPersistTest(BitcoinTestFramework):
|
|||||||
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
|
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
|
||||||
self.stop_nodes()
|
self.stop_nodes()
|
||||||
self.start_node(0, extra_args=["-persistmempool=0"])
|
self.start_node(0, extra_args=["-persistmempool=0"])
|
||||||
# Give bitcoind a second to reload the mempool
|
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
|
||||||
time.sleep(1)
|
|
||||||
assert_equal(len(self.nodes[0].getrawmempool()), 0)
|
assert_equal(len(self.nodes[0].getrawmempool()), 0)
|
||||||
|
|
||||||
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
|
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
|
||||||
self.stop_nodes()
|
self.stop_nodes()
|
||||||
self.start_node(0)
|
self.start_node(0)
|
||||||
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
|
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
|
||||||
|
assert_equal(len(self.nodes[0].getrawmempool()), 5)
|
||||||
|
|
||||||
mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
|
mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
|
||||||
mempooldat1 = os.path.join(self.nodes[1].datadir, 'regtest', 'mempool.dat')
|
mempooldat1 = os.path.join(self.nodes[1].datadir, 'regtest', 'mempool.dat')
|
||||||
@ -120,7 +120,8 @@ class MempoolPersistTest(BitcoinTestFramework):
|
|||||||
os.rename(mempooldat0, mempooldat1)
|
os.rename(mempooldat0, mempooldat1)
|
||||||
self.stop_nodes()
|
self.stop_nodes()
|
||||||
self.start_node(1, extra_args=[])
|
self.start_node(1, extra_args=[])
|
||||||
wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
|
wait_until(lambda: self.nodes[1].getmempoolinfo()["loaded"])
|
||||||
|
assert_equal(len(self.nodes[1].getrawmempool()), 5)
|
||||||
|
|
||||||
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
|
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
|
||||||
# to test the exception we are creating a tmp folder called mempool.dat.new
|
# to test the exception we are creating a tmp folder called mempool.dat.new
|
||||||
|
Loading…
x
Reference in New Issue
Block a user