Skip to content

Commit

Permalink
rpc: Expose g_is_mempool_loaded via getmempoolinfo and
Browse files Browse the repository at this point in the history
/rest/mempool/info.json

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.
Github-Pull: #2254
Rebased-From: 0169a75
  • Loading branch information
Empact authored and furszy committed Apr 8, 2021
1 parent e10c1b6 commit 584bbba
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 29 deletions.
1 change: 1 addition & 0 deletions doc/REST-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76

Returns various information about the TX mempool.
Only supports JSON as output format.
* loaded : (boolean) if the mempool is fully loaded
* size : (numeric) the number of transactions in the TX mempool
* bytes : (numeric) size of the TX mempool in bytes

Expand Down
9 changes: 9 additions & 0 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "kernel.h"
#include "masternodeman.h"
#include "policy/feerate.h"
#include "policy/policy.h"
#include "rpc/server.h"
#include "sync.h"
#include "txdb.h"
Expand Down Expand Up @@ -1149,9 +1150,13 @@ UniValue getchaintips(const JSONRPCRequest& request)
UniValue mempoolInfoToJSON()
{
UniValue ret(UniValue::VOBJ);
ret.pushKV("loaded", g_is_mempool_loaded);
ret.pushKV("size", (int64_t) mempool.size());
ret.pushKV("bytes", (int64_t) mempool.GetTotalTxSize());
ret.pushKV("usage", (int64_t) mempool.DynamicMemoryUsage());
size_t maxmempool = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
ret.pushKV("mempoolminfee", ValueFromAmount(std::max(mempool.GetMinFee(maxmempool), ::minRelayTxFee).GetFeePerK()));
ret.pushKV("minrelaytxfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));

return ret;
}
Expand All @@ -1165,9 +1170,13 @@ UniValue getmempoolinfo(const JSONRPCRequest& request)

"\nResult:\n"
"{\n"
" \"loaded\": true|false (boolean) True if the mempool is fully loaded\n"
" \"size\": xxxxx (numeric) Current tx count\n"
" \"bytes\": xxxxx (numeric) Sum of all tx sizes\n"
" \"usage\": xxxxx (numeric) Total memory usage for the mempool\n"
" \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n"
" \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted. Is the maximum of minrelaytxfee and minimum mempool fee\n"
" \"minrelaytxfee\": xxxxx (numeric) Current minimum relay fee for transactions\n"
"}\n"

"\nExamples:\n" +
Expand Down
67 changes: 38 additions & 29 deletions test/functional/mempool_persist.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@
"""

from decimal import Decimal
import os

from test_framework.test_framework import PivxTestFramework
from test_framework.util import *
from test_framework.util import (
assert_equal,
wait_until,
)

class MempoolPersistTest(PivxTestFramework):
def set_test_params(self):
Expand Down Expand Up @@ -64,10 +70,12 @@ def run_test(self):
self.start_node(1, extra_args=["-persistmempool=0"])
self.start_node(0)
self.start_node(2)
# Give pivxd a second to reload the mempool
wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5, timeout=1)
wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5, timeout=1)
# The others loaded their mempool. If node_1 loaded anything, we'd probably notice by now:

wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"], timeout=1)
wait_until(lambda: self.nodes[2].getmempoolinfo()["loaded"], 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:
assert_equal(len(self.nodes[1].getrawmempool()), 0)

# Verify accounting of mempool transactions after restart is correct
Expand All @@ -77,38 +85,39 @@ def run_test(self):
self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.")
self.stop_nodes()
self.start_node(0, extra_args=["-persistmempool=0"])
# Give bitcoind a second to reload the mempool
time.sleep(1)
wait_until(lambda: self.nodes[0].getmempoolinfo()["loaded"])
assert_equal(len(self.nodes[0].getrawmempool()), 0)

self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.")
self.stop_nodes()
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)

# Following code is ahead of our current repository state. Future back port.
'''
mempooldat0 = os.path.join(self.nodes[0].datadir, 'regtest', 'mempool.dat')
mempooldat1 = os.path.join(self.nodes[1].datadir, '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: self.nodes[1].getmempoolinfo()["loaded"])
assert_equal(len(self.nodes[1].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_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
# os.remove(mempooldotnew1)
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
# which is an implementation detail that could change and break this test
mempooldotnew1 = mempooldat1 + '.new'
os.mkdir(mempooldotnew1)
assert_raises_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool)
os.rmdir(mempooldotnew1)
'''

if __name__ == '__main__':
MempoolPersistTest().main()

0 comments on commit 584bbba

Please sign in to comment.