Skip to content

Commit 12aa2ac

Browse files
author
MarcoFalke
committed
Merge #15323: rpc: Expose g_is_mempool_loaded via getmempoolinfo
effe81f Move g_is_mempool_loaded into CTxMemPool::m_is_loaded (Ben Woosley) bb8ae2c 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 effe81f jnewbery: utACK effe81f Tree-SHA512: 74328b0c17a97efb8a000d4ee49b9a673c2b6dde7ea30c43a6a2eff961a233351c9471f9a42344412135786c02bdf2ee1b2526651bb8fed68bd94d2120c4ef86
2 parents 86edb79 + effe81f commit 12aa2ac

File tree

8 files changed

+49
-27
lines changed

8 files changed

+49
-27
lines changed

doc/REST-interface.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
101101

102102
Returns various information about the TX mempool.
103103
Only supports JSON as output format.
104+
* loaded : (boolean) if the mempool is fully loaded
104105
* size : (numeric) the number of transactions in the TX mempool
105106
* bytes : (numeric) size of the TX mempool in bytes
106107
* usage : (numeric) total TX mempool memory usage

src/init.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,8 @@ void Shutdown(InitInterfaces& interfaces)
241241
g_txindex.reset();
242242
DestroyAllBlockFilterIndexes();
243243

244-
if (g_is_mempool_loaded && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
245-
DumpMempool();
244+
if (::mempool.IsLoaded() && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
245+
DumpMempool(::mempool);
246246
}
247247

248248
if (fFeeEstimatesInitialized)
@@ -735,9 +735,9 @@ static void ThreadImport(std::vector<fs::path> vImportFiles)
735735
}
736736
} // End scope of CImportingNow
737737
if (gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) {
738-
LoadMempool();
738+
LoadMempool(::mempool);
739739
}
740-
g_is_mempool_loaded = !ShutdownRequested();
740+
::mempool.SetIsLoaded(!ShutdownRequested());
741741
}
742742

743743
/** Sanity checks

src/rpc/blockchain.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,7 @@ UniValue MempoolInfoToJSON(const CTxMemPool& pool)
14911491
// Make sure this call is atomic in the pool.
14921492
LOCK(pool.cs);
14931493
UniValue ret(UniValue::VOBJ);
1494+
ret.pushKV("loaded", pool.IsLoaded());
14941495
ret.pushKV("size", (int64_t)pool.size());
14951496
ret.pushKV("bytes", (int64_t)pool.GetTotalTxSize());
14961497
ret.pushKV("usage", (int64_t)pool.DynamicMemoryUsage());
@@ -1511,6 +1512,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
15111512
{},
15121513
RPCResult{
15131514
"{\n"
1515+
" \"loaded\": true|false (boolean) True if the mempool is fully loaded\n"
15141516
" \"size\": xxxxx, (numeric) Current tx count\n"
15151517
" \"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"
15161518
" \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n"
@@ -2061,11 +2063,11 @@ static UniValue savemempool(const JSONRPCRequest& request)
20612063
}.ToString());
20622064
}
20632065

2064-
if (!g_is_mempool_loaded) {
2066+
if (!::mempool.IsLoaded()) {
20652067
throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet");
20662068
}
20672069

2068-
if (!DumpMempool()) {
2070+
if (!DumpMempool(::mempool)) {
20692071
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
20702072
}
20712073

src/txmempool.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,4 +1091,16 @@ void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors,
10911091
}
10921092
}
10931093

1094+
bool CTxMemPool::IsLoaded() const
1095+
{
1096+
LOCK(cs);
1097+
return m_is_loaded;
1098+
}
1099+
1100+
void CTxMemPool::SetIsLoaded(bool loaded)
1101+
{
1102+
LOCK(cs);
1103+
m_is_loaded = loaded;
1104+
}
1105+
10941106
SaltedTxidHasher::SaltedTxidHasher() : k0(GetRand(std::numeric_limits<uint64_t>::max())), k1(GetRand(std::numeric_limits<uint64_t>::max())) {}

src/txmempool.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ class CTxMemPool
455455

456456
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
457457

458+
bool m_is_loaded GUARDED_BY(cs){false};
459+
458460
public:
459461

460462
static const int ROLLING_FEE_HALFLIFE = 60 * 60 * 12; // public only for testing
@@ -672,6 +674,12 @@ class CTxMemPool
672674
*/
673675
void GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) const;
674676

677+
/** @returns true if the mempool is fully loaded */
678+
bool IsLoaded() const;
679+
680+
/** Sets the current loaded state */
681+
void SetIsLoaded(bool loaded);
682+
675683
unsigned long size() const
676684
{
677685
LOCK(cs);

src/validation.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
256256

257257
CBlockPolicyEstimator feeEstimator;
258258
CTxMemPool mempool(&feeEstimator);
259-
std::atomic_bool g_is_mempool_loaded{false};
260259

261260
/** Constant stuff for coinbase transactions we create: */
262261
CScript COINBASE_FLAGS;
@@ -4735,7 +4734,7 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D
47354734

47364735
static const uint64_t MEMPOOL_DUMP_VERSION = 1;
47374736

4738-
bool LoadMempool()
4737+
bool LoadMempool(CTxMemPool& pool)
47394738
{
47404739
const CChainParams& chainparams = Params();
47414740
int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60;
@@ -4770,12 +4769,12 @@ bool LoadMempool()
47704769

47714770
CAmount amountdelta = nFeeDelta;
47724771
if (amountdelta) {
4773-
mempool.PrioritiseTransaction(tx->GetHash(), amountdelta);
4772+
pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
47744773
}
47754774
CValidationState state;
47764775
if (nTime + nExpiryTimeout > nNow) {
47774776
LOCK(cs_main);
4778-
AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime,
4777+
AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nullptr /* pfMissingInputs */, nTime,
47794778
nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */,
47804779
false /* test_accept */);
47814780
if (state.IsValid()) {
@@ -4785,7 +4784,7 @@ bool LoadMempool()
47854784
// wallet(s) having loaded it while we were processing
47864785
// mempool transactions; consider these as valid, instead of
47874786
// failed, but mark them as 'already there'
4788-
if (mempool.exists(tx->GetHash())) {
4787+
if (pool.exists(tx->GetHash())) {
47894788
++already_there;
47904789
} else {
47914790
++failed;
@@ -4801,7 +4800,7 @@ bool LoadMempool()
48014800
file >> mapDeltas;
48024801

48034802
for (const auto& i : mapDeltas) {
4804-
mempool.PrioritiseTransaction(i.first, i.second);
4803+
pool.PrioritiseTransaction(i.first, i.second);
48054804
}
48064805
} catch (const std::exception& e) {
48074806
LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what());
@@ -4812,7 +4811,7 @@ bool LoadMempool()
48124811
return true;
48134812
}
48144813

4815-
bool DumpMempool()
4814+
bool DumpMempool(const CTxMemPool& pool)
48164815
{
48174816
int64_t start = GetTimeMicros();
48184817

@@ -4823,11 +4822,11 @@ bool DumpMempool()
48234822
LOCK(dump_mutex);
48244823

48254824
{
4826-
LOCK(mempool.cs);
4827-
for (const auto &i : mempool.mapDeltas) {
4825+
LOCK(pool.cs);
4826+
for (const auto &i : pool.mapDeltas) {
48284827
mapDeltas[i.first] = i.second;
48294828
}
4830-
vinfo = mempool.infoAll();
4829+
vinfo = pool.infoAll();
48314830
}
48324831

48334832
int64_t mid = GetTimeMicros();

src/validation.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ extern CScript COINBASE_FLAGS;
142142
extern CCriticalSection cs_main;
143143
extern CBlockPolicyEstimator feeEstimator;
144144
extern CTxMemPool mempool;
145-
extern std::atomic_bool g_is_mempool_loaded;
146145
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
147146
extern BlockMap& mapBlockIndex GUARDED_BY(cs_main);
148147
extern Mutex g_best_block_mutex;
@@ -474,10 +473,10 @@ static const unsigned int REJECT_HIGHFEE = 0x100;
474473
CBlockFileInfo* GetBlockFileInfo(size_t n);
475474

476475
/** Dump the mempool to disk. */
477-
bool DumpMempool();
476+
bool DumpMempool(const CTxMemPool& pool);
478477

479478
/** Load the mempool from disk. */
480-
bool LoadMempool();
479+
bool LoadMempool(CTxMemPool& pool);
481480

482481
//! Check whether the block associated with this index entry is pruned or not.
483482
inline bool IsBlockPruned(const CBlockIndex* pblockindex)

test/functional/mempool_persist.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
"""
3838
from decimal import Decimal
3939
import os
40-
import time
4140

4241
from test_framework.test_framework import BitcoinTestFramework
4342
from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until
@@ -83,9 +82,10 @@ def run_test(self):
8382
self.start_node(1, extra_args=["-persistmempool=0"])
8483
self.start_node(0)
8584
self.start_node(2)
86-
# Give bitcoind a second to reload the mempool
87-
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5, timeout=1)
88-
wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5, timeout=1)
85+
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"], timeout=1)
86+
wait_until(lambda: self.nodes[2].getmempoolinfo()["loaded"], timeout=1)
87+
assert_equal(len(self.nodes[0].getrawmempool()), 5)
88+
assert_equal(len(self.nodes[2].getrawmempool()), 5)
8989
# The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now:
9090
assert_equal(len(self.nodes[1].getrawmempool()), 0)
9191

@@ -100,14 +100,14 @@ def run_test(self):
100100
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
101101
self.stop_nodes()
102102
self.start_node(0, extra_args=["-persistmempool=0"])
103-
# Give bitcoind a second to reload the mempool
104-
time.sleep(1)
103+
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
105104
assert_equal(len(self.nodes[0].getrawmempool()), 0)
106105

107106
self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
108107
self.stop_nodes()
109108
self.start_node(0)
110-
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
109+
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
110+
assert_equal(len(self.nodes[0].getrawmempool()), 5)
111111

112112
mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
113113
mempooldat1 = os.path.join(self.nodes[1].datadir, 'regtest', 'mempool.dat')
@@ -120,7 +120,8 @@ def run_test(self):
120120
os.rename(mempooldat0, mempooldat1)
121121
self.stop_nodes()
122122
self.start_node(1, extra_args=[])
123-
wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
123+
wait_until(lambda: self.nodes[1].getmempoolinfo()["loaded"])
124+
assert_equal(len(self.nodes[1].getrawmempool()), 5)
124125

125126
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
126127
# to test the exception we are creating a tmp folder called mempool.dat.new

0 commit comments

Comments
 (0)