mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge #11099: [RPC][mempool]: Add savemempool RPC
1aa97ee08
Add savemempool RPC (Lawrence Nahum)467cbbcbf
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 https://github.com/bitcoin/bitcoin/issues/11086 Tree-SHA512: e856ae9777425a4521279c9b58e69285d8e374790bebefd3284cf91931eac0e456f86224f427a087a01bf70440bf6e439fa02c8a34940eb1046ae473e98b6aaa
This commit is contained in:
commit
bc561b4b7d
4 changed files with 53 additions and 3 deletions
|
@ -1534,6 +1534,25 @@ UniValue getchaintxstats(const JSONRPCRequest& request)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UniValue savemempool(const JSONRPCRequest& request)
|
||||||
|
{
|
||||||
|
if (request.fHelp || request.params.size() != 0) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"savemempool\n"
|
||||||
|
"\nDumps the mempool to disk.\n"
|
||||||
|
"\nExamples:\n"
|
||||||
|
+ HelpExampleCli("savemempool", "")
|
||||||
|
+ HelpExampleRpc("savemempool", "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DumpMempool()) {
|
||||||
|
throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk");
|
||||||
|
}
|
||||||
|
|
||||||
|
return NullUniValue;
|
||||||
|
}
|
||||||
|
|
||||||
static const CRPCCommand commands[] =
|
static const CRPCCommand commands[] =
|
||||||
{ // category name actor (function) argNames
|
{ // category name actor (function) argNames
|
||||||
// --------------------- ------------------------ ----------------------- ----------
|
// --------------------- ------------------------ ----------------------- ----------
|
||||||
|
@ -1554,6 +1573,7 @@ static const CRPCCommand commands[] =
|
||||||
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
|
{ "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} },
|
||||||
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
|
{ "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} },
|
||||||
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
|
{ "blockchain", "pruneblockchain", &pruneblockchain, {"height"} },
|
||||||
|
{ "blockchain", "savemempool", &savemempool, {} },
|
||||||
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
|
{ "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} },
|
||||||
|
|
||||||
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
|
{ "blockchain", "preciousblock", &preciousblock, {"blockhash"} },
|
||||||
|
|
|
@ -4331,7 +4331,7 @@ bool LoadMempool(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DumpMempool(void)
|
bool DumpMempool(void)
|
||||||
{
|
{
|
||||||
int64_t start = GetTimeMicros();
|
int64_t start = GetTimeMicros();
|
||||||
|
|
||||||
|
@ -4351,7 +4351,7 @@ void DumpMempool(void)
|
||||||
try {
|
try {
|
||||||
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
|
FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb");
|
||||||
if (!filestr) {
|
if (!filestr) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
|
||||||
|
@ -4375,7 +4375,9 @@ void DumpMempool(void)
|
||||||
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
|
LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO);
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Guess how far we are in the verification process at the given block index
|
//! Guess how far we are in the verification process at the given block index
|
||||||
|
|
|
@ -478,7 +478,7 @@ 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. */
|
||||||
void DumpMempool();
|
bool DumpMempool();
|
||||||
|
|
||||||
/** Load the mempool from disk. */
|
/** Load the mempool from disk. */
|
||||||
bool LoadMempool();
|
bool LoadMempool();
|
||||||
|
|
|
@ -28,8 +28,14 @@ Test is as follows:
|
||||||
- Restart node0 with -persistmempool. Verify that it has 5
|
- Restart node0 with -persistmempool. Verify that it has 5
|
||||||
transactions in its mempool. This tests that -persistmempool=0
|
transactions in its mempool. This tests that -persistmempool=0
|
||||||
does not overwrite a previously valid mempool stored on disk.
|
does not overwrite a previously valid mempool stored on disk.
|
||||||
|
- Remove node0 mempool.dat and verify savemempool RPC recreates it
|
||||||
|
and verify that node1 can load it and has 5 transaction in its
|
||||||
|
mempool.
|
||||||
|
- Verify that savemempool throws when the RPC is called if
|
||||||
|
node1 can't write to disk.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from test_framework.test_framework import BitcoinTestFramework
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
@ -78,5 +84,27 @@ class MempoolPersistTest(BitcoinTestFramework):
|
||||||
self.start_node(0)
|
self.start_node(0)
|
||||||
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
|
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5)
|
||||||
|
|
||||||
|
mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat')
|
||||||
|
mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat')
|
||||||
|
self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it")
|
||||||
|
os.remove(mempooldat0)
|
||||||
|
self.nodes[0].savemempool()
|
||||||
|
assert os.path.isfile(mempooldat0)
|
||||||
|
|
||||||
|
self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 5 transactions")
|
||||||
|
os.rename(mempooldat0, mempooldat1)
|
||||||
|
self.stop_nodes()
|
||||||
|
self.start_node(1, extra_args=[])
|
||||||
|
wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5)
|
||||||
|
|
||||||
|
self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails")
|
||||||
|
# to test the exception we are setting bad permissions on a tmp file called mempool.dat.new
|
||||||
|
# which is an implementation detail that could change and break this test
|
||||||
|
mempooldotnew1 = mempooldat1 + '.new'
|
||||||
|
with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'):
|
||||||
|
pass
|
||||||
|
assert_raises_jsonrpc(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
|
||||||
|
os.remove(mempooldotnew1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
MempoolPersistTest().main()
|
MempoolPersistTest().main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue