Skip to content

Commit bc561b4

Browse files
author
MarcoFalke
committed
Merge #11099: [RPC][mempool]: Add savemempool RPC
1aa97ee Add savemempool RPC (Lawrence Nahum) 467cbbc Add return value to DumpMempool (Lawrence Nahum) Pull request description: Adds a simple parameterless rpc command to dump the mempool. Rationale: Sometimes there can be a crash for whatever reason (bug, power loss, etc) causing the mempool.dat file to not be saved. This change allows to script/cron the rpc call to have more regular saves to the file as well as cli/ad-hoc. This should solve issue #11086 Tree-SHA512: e856ae9777425a4521279c9b58e69285d8e374790bebefd3284cf91931eac0e456f86224f427a087a01bf70440bf6e439fa02c8a34940eb1046ae473e98b6aaa
2 parents 961901f + 1aa97ee commit bc561b4

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed

src/rpc/blockchain.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,6 +1534,25 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
15341534
return ret;
15351535
}
15361536

1537+
UniValue savemempool(const JSONRPCRequest& request)
1538+
{
1539+
if (request.fHelp || request.params.size() != 0) {
1540+
throw std::runtime_error(
1541+
"savemempool\n"
1542+
"\nDumps the mempool to disk.\n"
1543+
"\nExamples:\n"
1544+
+ HelpExampleCli("savemempool", "")
1545+
+ HelpExampleRpc("savemempool", "")
1546+
);
1547+
}
1548+
1549+
if (!DumpMempool()) {
1550+
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
1551+
}
1552+
1553+
return NullUniValue;
1554+
}
1555+
15371556
static const CRPCCommand commands[] =
15381557
{ // category name actor (function) argNames
15391558
// --------------------- ------------------------ ----------------------- ----------
@@ -1554,6 +1573,7 @@ static const CRPCCommand commands[] =
15541573
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
15551574
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
15561575
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
1576+
{ "blockchain", "savemempool", &savemempool, {} },
15571577
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
15581578

15591579
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },

src/validation.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4331,7 +4331,7 @@ bool LoadMempool(void)
43314331
return true;
43324332
}
43334333

4334-
void DumpMempool(void)
4334+
bool DumpMempool(void)
43354335
{
43364336
int64_t start = GetTimeMicros();
43374337

@@ -4351,7 +4351,7 @@ void DumpMempool(void)
43514351
try {
43524352
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
43534353
if (!filestr) {
4354-
return;
4354+
return false;
43554355
}
43564356

43574357
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
@@ -4375,7 +4375,9 @@ void DumpMempool(void)
43754375
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
43764376
} catch (const std::exception& e) {
43774377
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
4378+
return false;
43784379
}
4380+
return true;
43794381
}
43804382

43814383
//! Guess how far we are in the verification process at the given block index

src/validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ static const unsigned int REJECT_HIGHFEE = 0x100;
478478
CBlockFileInfo* GetBlockFileInfo(size_t n);
479479

480480
/** Dump the mempool to disk. */
481-
void DumpMempool();
481+
bool DumpMempool();
482482

483483
/** Load the mempool from disk. */
484484
bool LoadMempool();

test/functional/mempool_persist.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,14 @@
2828
- Restart node0 with -persistmempool. Verify that it has 5
2929
transactions in its mempool. This tests that -persistmempool=0
3030
does not overwrite a previously valid mempool stored on disk.
31+
- Remove node0 mempool.dat and verify savemempool RPC recreates it
32+
and verify that node1 can load it and has 5 transaction in its
33+
mempool.
34+
- Verify that savemempool throws when the RPC is called if
35+
node1 can't write to disk.
3136
3237
"""
38+
import os
3339
import time
3440

3541
from test_framework.test_framework import BitcoinTestFramework
@@ -78,5 +84,27 @@ def run_test(self):
7884
self.start_node(0)
7985
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
8086

87+
mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat')
88+
mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat')
89+
self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it")
90+
os.remove(mempooldat0)
91+
self.nodes[0].savemempool()
92+
assert os.path.isfile(mempooldat0)
93+
94+
self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 5 transactions")
95+
os.rename(mempooldat0, mempooldat1)
96+
self.stop_nodes()
97+
self.start_node(1, extra_args=[])
98+
wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
99+
100+
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
101+
# to test the exception we are setting bad permissions on a tmp file called mempool.dat.new
102+
# which is an implementation detail that could change and break this test
103+
mempooldotnew1 = mempooldat1 + '.new'
104+
with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'):
105+
pass
106+
assert_raises_jsonrpc(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
107+
os.remove(mempooldotnew1)
108+
81109
if __name__ == '__main__':
82110
MempoolPersistTest().main()

0 commit comments

Comments
 (0)