0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-12 11:19:08 -05:00

Merge #18740: Remove g_rpc_node global

b3f7f375ef refactor: Remove g_rpc_node global (Russell Yanofsky)
ccb5059ee8 scripted-diff: Remove g_rpc_node references (Russell Yanofsky)
6fca33b2ed refactor: Pass NodeContext to RPC and REST methods through util::Ref (Russell Yanofsky)
691c817b34 Add util::Ref class as temporary alternative for c++17 std::any (Russell Yanofsky)

Pull request description:

  This PR removes the `g_rpc_node` global, to get same benefits we see removing other globals and make RPC code more testable, modular, and reusable.

  This uses a hybrid of the approaches suggested in #17548. Instead of using `std::any`, which isn't available in c++11, or `void*`, which isn't type safe, it uses a small new `util::Ref` helper class, which acts like a simplified `std::any` that only holds references, not values.

  Motivation for writing this was to provide an simpler alternative to #18647 by Harris Brakmić (brakmic) which avoids some shortcomings of that PR (https://github.com/bitcoin/bitcoin/pull/18647#issuecomment-617878826)

ACKs for top commit:
  MarcoFalke:
    re-ACK b3f7f375ef, only change is adding back const and more tests 🚾
  ajtowns:
    ACK b3f7f375ef

Tree-SHA512: 56292268a001bdbe34d641db1180c215351503966ff451e55cc96c9137f1d262225d7d7733de9c9da7ce7d7a4b34213a98c2476266b58c89dbbb0f3cb5aa5d70
This commit is contained in:
MarcoFalke 2020-05-21 06:53:30 -04:00
commit 25ad2c623a
No known key found for this signature in database
GPG key ID: CE2B75697E69A548
22 changed files with 262 additions and 131 deletions

View file

@ -227,6 +227,7 @@ BITCOIN_CORE_H = \
util/message.h \ util/message.h \
util/moneystr.h \ util/moneystr.h \
util/rbf.h \ util/rbf.h \
util/ref.h \
util/settings.h \ util/settings.h \
util/string.h \ util/string.h \
util/threadnames.h \ util/threadnames.h \

View file

@ -229,6 +229,7 @@ BITCOIN_TESTS =\
test/prevector_tests.cpp \ test/prevector_tests.cpp \
test/raii_event_tests.cpp \ test/raii_event_tests.cpp \
test/random_tests.cpp \ test/random_tests.cpp \
test/ref_tests.cpp \
test/reverselock_tests.cpp \ test/reverselock_tests.cpp \
test/rpc_tests.cpp \ test/rpc_tests.cpp \
test/sanity_tests.cpp \ test/sanity_tests.cpp \

View file

@ -16,6 +16,7 @@
#include <noui.h> #include <noui.h>
#include <shutdown.h> #include <shutdown.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <util/ref.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h> #include <util/system.h>
#include <util/threadnames.h> #include <util/threadnames.h>
@ -77,6 +78,7 @@ static bool AppInit(int argc, char* argv[])
return true; return true;
} }
util::Ref context{node};
try try
{ {
if (!CheckDataDirOption()) { if (!CheckDataDirOption()) {
@ -145,7 +147,7 @@ static bool AppInit(int argc, char* argv[])
// If locking the data directory failed, exit immediately // If locking the data directory failed, exit immediately
return false; return false;
} }
fRet = AppInitMain(node); fRet = AppInitMain(context, node);
} }
catch (const std::exception& e) { catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInit()"); PrintExceptionContinue(&e, "AppInit()");

View file

@ -150,7 +150,7 @@ static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUserna
return multiUserAuthorized(strUserPass); return multiUserAuthorized(strUserPass);
} }
static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) static bool HTTPReq_JSONRPC(const util::Ref& context, HTTPRequest* req)
{ {
// JSONRPC handles only POST // JSONRPC handles only POST
if (req->GetRequestMethod() != HTTPRequest::POST) { if (req->GetRequestMethod() != HTTPRequest::POST) {
@ -165,7 +165,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
return false; return false;
} }
JSONRPCRequest jreq; JSONRPCRequest jreq(context);
jreq.peerAddr = req->GetPeer().ToString(); jreq.peerAddr = req->GetPeer().ToString();
if (!RPCAuthorized(authHeader.second, jreq.authUser)) { if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr); LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
@ -284,15 +284,16 @@ static bool InitRPCAuthentication()
return true; return true;
} }
bool StartHTTPRPC() bool StartHTTPRPC(const util::Ref& context)
{ {
LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
if (!InitRPCAuthentication()) if (!InitRPCAuthentication())
return false; return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC); auto handle_rpc = [&context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
RegisterHTTPHandler("/", true, handle_rpc);
if (g_wallet_init_interface.HasWalletSupport()) { if (g_wallet_init_interface.HasWalletSupport()) {
RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); RegisterHTTPHandler("/wallet/", false, handle_rpc);
} }
struct event_base* eventBase = EventBase(); struct event_base* eventBase = EventBase();
assert(eventBase); assert(eventBase);

View file

@ -5,11 +5,14 @@
#ifndef BITCOIN_HTTPRPC_H #ifndef BITCOIN_HTTPRPC_H
#define BITCOIN_HTTPRPC_H #define BITCOIN_HTTPRPC_H
namespace util {
class Ref;
} // namespace util
/** Start HTTP RPC subsystem. /** Start HTTP RPC subsystem.
* Precondition; HTTP and RPC has been started. * Precondition; HTTP and RPC has been started.
*/ */
bool StartHTTPRPC(); bool StartHTTPRPC(const util::Ref& context);
/** Interrupt HTTP RPC subsystem. /** Interrupt HTTP RPC subsystem.
*/ */
void InterruptHTTPRPC(); void InterruptHTTPRPC();
@ -21,7 +24,7 @@ void StopHTTPRPC();
/** Start HTTP REST subsystem. /** Start HTTP REST subsystem.
* Precondition; HTTP and RPC has been started. * Precondition; HTTP and RPC has been started.
*/ */
void StartREST(); void StartREST(const util::Ref& context);
/** Interrupt RPC REST subsystem. /** Interrupt RPC REST subsystem.
*/ */
void InterruptREST(); void InterruptREST();

View file

@ -784,16 +784,16 @@ static bool InitSanityCheck()
return true; return true;
} }
static bool AppInitServers() static bool AppInitServers(const util::Ref& context)
{ {
RPCServer::OnStarted(&OnRPCStarted); RPCServer::OnStarted(&OnRPCStarted);
RPCServer::OnStopped(&OnRPCStopped); RPCServer::OnStopped(&OnRPCStopped);
if (!InitHTTPServer()) if (!InitHTTPServer())
return false; return false;
StartRPC(); StartRPC();
if (!StartHTTPRPC()) if (!StartHTTPRPC(context))
return false; return false;
if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(); if (gArgs.GetBoolArg("-rest", DEFAULT_REST_ENABLE)) StartREST(context);
StartHTTPServer(); StartHTTPServer();
return true; return true;
} }
@ -1238,7 +1238,7 @@ bool AppInitLockDataDirectory()
return true; return true;
} }
bool AppInitMain(NodeContext& node) bool AppInitMain(const util::Ref& context, NodeContext& node)
{ {
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
// ********************************************************* Step 4a: application initialization // ********************************************************* Step 4a: application initialization
@ -1340,7 +1340,6 @@ bool AppInitMain(NodeContext& node)
for (const auto& client : node.chain_clients) { for (const auto& client : node.chain_clients) {
client->registerRpcs(); client->registerRpcs();
} }
g_rpc_node = &node;
#if ENABLE_ZMQ #if ENABLE_ZMQ
RegisterZMQRPCCommands(tableRPC); RegisterZMQRPCCommands(tableRPC);
#endif #endif
@ -1353,7 +1352,7 @@ bool AppInitMain(NodeContext& node)
if (gArgs.GetBoolArg("-server", false)) if (gArgs.GetBoolArg("-server", false))
{ {
uiInterface.InitMessage_connect(SetRPCWarmupStatus); uiInterface.InitMessage_connect(SetRPCWarmupStatus);
if (!AppInitServers()) if (!AppInitServers(context))
return InitError(_("Unable to start HTTP server. See debug log for details.")); return InitError(_("Unable to start HTTP server. See debug log for details."));
} }

View file

@ -14,6 +14,9 @@ struct NodeContext;
namespace boost { namespace boost {
class thread_group; class thread_group;
} // namespace boost } // namespace boost
namespace util {
class Ref;
} // namespace util
/** Interrupt threads */ /** Interrupt threads */
void Interrupt(NodeContext& node); void Interrupt(NodeContext& node);
@ -51,7 +54,7 @@ bool AppInitLockDataDirectory();
* @note This should only be done after daemonization. Call Shutdown() if this function fails. * @note This should only be done after daemonization. Call Shutdown() if this function fails.
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called. * @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
*/ */
bool AppInitMain(NodeContext& node); bool AppInitMain(const util::Ref& context, NodeContext& node);
/** /**
* Register all arguments with the ArgsManager * Register all arguments with the ArgsManager

View file

@ -27,6 +27,7 @@
#include <sync.h> #include <sync.h>
#include <txmempool.h> #include <txmempool.h>
#include <ui_interface.h> #include <ui_interface.h>
#include <util/ref.h>
#include <util/system.h> #include <util/system.h>
#include <util/translation.h> #include <util/translation.h>
#include <validation.h> #include <validation.h>
@ -80,7 +81,7 @@ public:
bool appInitMain() override bool appInitMain() override
{ {
m_context.chain = MakeChain(m_context); m_context.chain = MakeChain(m_context);
return AppInitMain(m_context); return AppInitMain(m_context_ref, m_context);
} }
void appShutdown() override void appShutdown() override
{ {
@ -225,7 +226,7 @@ public:
CFeeRate getDustRelayFee() override { return ::dustRelayFee; } CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
{ {
JSONRPCRequest req; JSONRPCRequest req(m_context_ref);
req.params = params; req.params = params;
req.strMethod = command; req.strMethod = command;
req.URI = uri; req.URI = uri;
@ -323,6 +324,7 @@ public:
} }
NodeContext* context() override { return &m_context; } NodeContext* context() override { return &m_context; }
NodeContext m_context; NodeContext m_context;
util::Ref m_context_ref{m_context};
}; };
} // namespace } // namespace

View file

@ -18,6 +18,7 @@
#include <sync.h> #include <sync.h>
#include <txmempool.h> #include <txmempool.h>
#include <util/check.h> #include <util/check.h>
#include <util/ref.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <validation.h> #include <validation.h>
#include <version.h> #include <version.h>
@ -75,13 +76,14 @@ static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string me
* @param[in] req the HTTP request * @param[in] req the HTTP request
* return pointer to the mempool or nullptr if no mempool found * return pointer to the mempool or nullptr if no mempool found
*/ */
static CTxMemPool* GetMemPool(HTTPRequest* req) static CTxMemPool* GetMemPool(const util::Ref& context, HTTPRequest* req)
{ {
if (!g_rpc_node || !g_rpc_node->mempool) { NodeContext* node = context.Has<NodeContext>() ? &context.Get<NodeContext>() : nullptr;
if (!node || !node->mempool) {
RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found");
return nullptr; return nullptr;
} }
return g_rpc_node->mempool; return node->mempool;
} }
static RetFormat ParseDataFormat(std::string& param, const std::string& strReq) static RetFormat ParseDataFormat(std::string& param, const std::string& strReq)
@ -129,7 +131,8 @@ static bool CheckWarmup(HTTPRequest* req)
return true; return true;
} }
static bool rest_headers(HTTPRequest* req, static bool rest_headers(const util::Ref& context,
HTTPRequest* req,
const std::string& strURIPart) const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) if (!CheckWarmup(req))
@ -270,12 +273,12 @@ static bool rest_block(HTTPRequest* req,
} }
} }
static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart) static bool rest_block_extended(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
return rest_block(req, strURIPart, true); return rest_block(req, strURIPart, true);
} }
static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart) static bool rest_block_notxdetails(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
return rest_block(req, strURIPart, false); return rest_block(req, strURIPart, false);
} }
@ -283,7 +286,7 @@ static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPa
// A bit of a hack - dependency on a function defined in rpc/blockchain.cpp // A bit of a hack - dependency on a function defined in rpc/blockchain.cpp
UniValue getblockchaininfo(const JSONRPCRequest& request); UniValue getblockchaininfo(const JSONRPCRequest& request);
static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) static bool rest_chaininfo(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) if (!CheckWarmup(req))
return false; return false;
@ -292,7 +295,7 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
switch (rf) { switch (rf) {
case RetFormat::JSON: { case RetFormat::JSON: {
JSONRPCRequest jsonRequest; JSONRPCRequest jsonRequest(context);
jsonRequest.params = UniValue(UniValue::VARR); jsonRequest.params = UniValue(UniValue::VARR);
UniValue chainInfoObject = getblockchaininfo(jsonRequest); UniValue chainInfoObject = getblockchaininfo(jsonRequest);
std::string strJSON = chainInfoObject.write() + "\n"; std::string strJSON = chainInfoObject.write() + "\n";
@ -306,11 +309,11 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart)
} }
} }
static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) static bool rest_mempool_info(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) if (!CheckWarmup(req))
return false; return false;
const CTxMemPool* mempool = GetMemPool(req); const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false; if (!mempool) return false;
std::string param; std::string param;
const RetFormat rf = ParseDataFormat(param, strURIPart); const RetFormat rf = ParseDataFormat(param, strURIPart);
@ -330,10 +333,10 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart)
} }
} }
static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart) static bool rest_mempool_contents(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) return false; if (!CheckWarmup(req)) return false;
const CTxMemPool* mempool = GetMemPool(req); const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false; if (!mempool) return false;
std::string param; std::string param;
const RetFormat rf = ParseDataFormat(param, strURIPart); const RetFormat rf = ParseDataFormat(param, strURIPart);
@ -353,7 +356,7 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar
} }
} }
static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) if (!CheckWarmup(req))
return false; return false;
@ -409,7 +412,7 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart)
} }
} }
static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) static bool rest_getutxos(const util::Ref& context, HTTPRequest* req, const std::string& strURIPart)
{ {
if (!CheckWarmup(req)) if (!CheckWarmup(req))
return false; return false;
@ -518,7 +521,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
}; };
if (fCheckMemPool) { if (fCheckMemPool) {
const CTxMemPool* mempool = GetMemPool(req); const CTxMemPool* mempool = GetMemPool(context, req);
if (!mempool) return false; if (!mempool) return false;
// use db+mempool as cache backend in case user likes to query mempool // use db+mempool as cache backend in case user likes to query mempool
LOCK2(cs_main, mempool->cs); LOCK2(cs_main, mempool->cs);
@ -595,7 +598,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
} }
} }
static bool rest_blockhash_by_height(HTTPRequest* req, static bool rest_blockhash_by_height(const util::Ref& context, HTTPRequest* req,
const std::string& str_uri_part) const std::string& str_uri_part)
{ {
if (!CheckWarmup(req)) return false; if (!CheckWarmup(req)) return false;
@ -643,7 +646,7 @@ static bool rest_blockhash_by_height(HTTPRequest* req,
static const struct { static const struct {
const char* prefix; const char* prefix;
bool (*handler)(HTTPRequest* req, const std::string& strReq); bool (*handler)(const util::Ref& context, HTTPRequest* req, const std::string& strReq);
} uri_prefixes[] = { } uri_prefixes[] = {
{"/rest/tx/", rest_tx}, {"/rest/tx/", rest_tx},
{"/rest/block/notxdetails/", rest_block_notxdetails}, {"/rest/block/notxdetails/", rest_block_notxdetails},
@ -656,10 +659,12 @@ static const struct {
{"/rest/blockhashbyheight/", rest_blockhash_by_height}, {"/rest/blockhashbyheight/", rest_blockhash_by_height},
}; };
void StartREST() void StartREST(const util::Ref& context)
{ {
for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) for (const auto& up : uri_prefixes) {
RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler); auto handler = [&context, up](HTTPRequest* req, const std::string& prefix) { return up.handler(context, req, prefix); };
RegisterHTTPHandler(up.prefix, false, handler);
}
} }
void InterruptREST() void InterruptREST()

View file

@ -29,6 +29,7 @@
#include <txdb.h> #include <txdb.h>
#include <txmempool.h> #include <txmempool.h>
#include <undo.h> #include <undo.h>
#include <util/ref.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h> #include <util/system.h>
#include <validation.h> #include <validation.h>
@ -53,13 +54,21 @@ static Mutex cs_blockchange;
static std::condition_variable cond_blockchange; static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock; static CUpdatedBlock latestblock;
CTxMemPool& EnsureMemPool() NodeContext& EnsureNodeContext(const util::Ref& context)
{ {
CHECK_NONFATAL(g_rpc_node); if (!context.Has<NodeContext>()) {
if (!g_rpc_node->mempool) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Node context not found");
}
return context.Get<NodeContext>();
}
CTxMemPool& EnsureMemPool(const util::Ref& context)
{
NodeContext& node = EnsureNodeContext(context);
if (!node.mempool) {
throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
} }
return *g_rpc_node->mempool; return *node.mempool;
} }
/* Calculate the difficulty for a given block index. /* Calculate the difficulty for a given block index.
@ -519,7 +528,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request)
if (!request.params[0].isNull()) if (!request.params[0].isNull())
fVerbose = request.params[0].get_bool(); fVerbose = request.params[0].get_bool();
return MempoolToJSON(EnsureMemPool(), fVerbose); return MempoolToJSON(EnsureMemPool(request.context), fVerbose);
} }
static UniValue getmempoolancestors(const JSONRPCRequest& request) static UniValue getmempoolancestors(const JSONRPCRequest& request)
@ -549,7 +558,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1"); uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); CTxMemPool::txiter it = mempool.mapTx.find(hash);
@ -612,7 +621,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1"); uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); CTxMemPool::txiter it = mempool.mapTx.find(hash);
@ -662,7 +671,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request)
uint256 hash = ParseHashV(request.params[0], "parameter 1"); uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CTxMemPool::txiter it = mempool.mapTx.find(hash); CTxMemPool::txiter it = mempool.mapTx.find(hash);
@ -1045,7 +1054,7 @@ UniValue gettxout(const JSONRPCRequest& request)
CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip(); CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip();
if (fMempool) { if (fMempool) {
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(mempool.cs); LOCK(mempool.cs);
CCoinsViewMemPool view(coins_view, mempool); CCoinsViewMemPool view(coins_view, mempool);
if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { if (!view.GetCoin(out, coin) || mempool.isSpent(out)) {
@ -1415,7 +1424,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
return MempoolInfoToJSON(EnsureMemPool()); return MempoolInfoToJSON(EnsureMemPool(request.context));
} }
static UniValue preciousblock(const JSONRPCRequest& request) static UniValue preciousblock(const JSONRPCRequest& request)
@ -1934,7 +1943,7 @@ static UniValue savemempool(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!mempool.IsLoaded()) { 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");
@ -2385,5 +2394,3 @@ static const CRPCCommand commands[] =
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]); t.appendCommand(commands[vcidx].name, &commands[vcidx]);
} }
NodeContext* g_rpc_node = nullptr;

View file

@ -18,6 +18,9 @@ class CBlockIndex;
class CTxMemPool; class CTxMemPool;
class UniValue; class UniValue;
struct NodeContext; struct NodeContext;
namespace util {
class Ref;
} // namespace util
static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5;
@ -47,11 +50,7 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
/** Used by getblockstats to get feerates at different percentiles by weight */ /** Used by getblockstats to get feerates at different percentiles by weight */
void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight); void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], std::vector<std::pair<CAmount, int64_t>>& scores, int64_t total_weight);
//! Pointer to node state that needs to be declared as a global to be accessible NodeContext& EnsureNodeContext(const util::Ref& context);
//! RPC methods. Due to limitations of the RPC framework, there's currently no CTxMemPool& EnsureMemPool(const util::Ref& context);
//! direct way to pass in state to RPC methods without globals.
extern NodeContext* g_rpc_node;
CTxMemPool& EnsureMemPool();
#endif #endif

View file

@ -227,7 +227,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error);
} }
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
return generateBlocks(mempool, coinbase_script, num_blocks, max_tries); return generateBlocks(mempool, coinbase_script, num_blocks, max_tries);
} }
@ -265,7 +265,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address");
} }
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
CScript coinbase_script = GetScriptForDestination(destination); CScript coinbase_script = GetScriptForDestination(destination);
@ -311,7 +311,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
coinbase_script = GetScriptForDestination(destination); coinbase_script = GetScriptForDestination(destination);
} }
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
std::vector<CTransactionRef> txs; std::vector<CTransactionRef> txs;
const auto raw_txs_or_txids = request.params[1].get_array(); const auto raw_txs_or_txids = request.params[1].get_array();
@ -403,7 +403,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request)
}.Check(request); }.Check(request);
LOCK(cs_main); LOCK(cs_main);
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", (int)::ChainActive().Height()); obj.pushKV("blocks", (int)::ChainActive().Height());
@ -449,7 +449,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0."); throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.");
} }
EnsureMemPool().PrioritiseTransaction(hash, nAmount); EnsureMemPool(request.context).PrioritiseTransaction(hash, nAmount);
return true; return true;
} }
@ -635,17 +635,18 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
if (strMode != "template") if (strMode != "template")
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode"); throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if (g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) if (node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0)
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!"); throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, PACKAGE_NAME " is not connected!");
if (::ChainstateActive().IsInitialBlockDownload()) if (::ChainstateActive().IsInitialBlockDownload())
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks...");
static unsigned int nTransactionsUpdatedLast; static unsigned int nTransactionsUpdatedLast;
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
if (!lpval.isNull()) if (!lpval.isNull())
{ {

View file

@ -15,6 +15,7 @@
#include <script/descriptor.h> #include <script/descriptor.h>
#include <util/check.h> #include <util/check.h>
#include <util/message.h> // For MessageSign(), MessageVerify() #include <util/message.h> // For MessageSign(), MessageVerify()
#include <util/ref.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/system.h> #include <util/system.h>
@ -366,8 +367,8 @@ static UniValue setmocktime(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM}); RPCTypeCheck(request.params, {UniValue::VNUM});
int64_t time = request.params[0].get_int64(); int64_t time = request.params[0].get_int64();
SetMockTime(time); SetMockTime(time);
if (g_rpc_node) { if (request.context.Has<NodeContext>()) {
for (const auto& chain_client : g_rpc_node->chain_clients) { for (const auto& chain_client : request.context.Get<NodeContext>().chain_clients) {
chain_client->setMockTime(time); chain_client->setMockTime(time);
} }
} }
@ -398,9 +399,10 @@ static UniValue mockscheduler(const JSONRPCRequest& request)
} }
// protect against null pointer dereference // protect against null pointer dereference
CHECK_NONFATAL(g_rpc_node); CHECK_NONFATAL(request.context.Has<NodeContext>());
CHECK_NONFATAL(g_rpc_node->scheduler); NodeContext& node = request.context.Get<NodeContext>();
g_rpc_node->scheduler->MockForward(std::chrono::seconds(delta_seconds)); CHECK_NONFATAL(node.scheduler);
node.scheduler->MockForward(std::chrono::seconds(delta_seconds));
return NullUniValue; return NullUniValue;
} }

View file

@ -42,10 +42,11 @@ static UniValue getconnectioncount(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
return (int)g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL); return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
} }
static UniValue ping(const JSONRPCRequest& request) static UniValue ping(const JSONRPCRequest& request)
@ -62,11 +63,12 @@ static UniValue ping(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
// Request that each node send a ping during next message processing pass // Request that each node send a ping during next message processing pass
g_rpc_node->connman->ForEachNode([](CNode* pnode) { node.connman->ForEachNode([](CNode* pnode) {
pnode->fPingQueued = true; pnode->fPingQueued = true;
}); });
return NullUniValue; return NullUniValue;
@ -139,11 +141,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
std::vector<CNodeStats> vstats; std::vector<CNodeStats> vstats;
g_rpc_node->connman->GetNodeStats(vstats); node.connman->GetNodeStats(vstats);
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
@ -248,7 +251,8 @@ static UniValue addnode(const JSONRPCRequest& request)
}, },
}.ToString()); }.ToString());
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
std::string strNode = request.params[0].get_str(); std::string strNode = request.params[0].get_str();
@ -256,18 +260,18 @@ static UniValue addnode(const JSONRPCRequest& request)
if (strCommand == "onetry") if (strCommand == "onetry")
{ {
CAddress addr; CAddress addr;
g_rpc_node->connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true); node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true);
return NullUniValue; return NullUniValue;
} }
if (strCommand == "add") if (strCommand == "add")
{ {
if(!g_rpc_node->connman->AddNode(strNode)) if(!node.connman->AddNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
} }
else if(strCommand == "remove") else if(strCommand == "remove")
{ {
if(!g_rpc_node->connman->RemoveAddedNode(strNode)) if(!node.connman->RemoveAddedNode(strNode))
throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
} }
@ -293,7 +297,8 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
bool success; bool success;
@ -302,11 +307,11 @@ static UniValue disconnectnode(const JSONRPCRequest& request)
if (!address_arg.isNull() && id_arg.isNull()) { if (!address_arg.isNull() && id_arg.isNull()) {
/* handle disconnect-by-address */ /* handle disconnect-by-address */
success = g_rpc_node->connman->DisconnectNode(address_arg.get_str()); success = node.connman->DisconnectNode(address_arg.get_str());
} else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) { } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
/* handle disconnect-by-id */ /* handle disconnect-by-id */
NodeId nodeid = (NodeId) id_arg.get_int64(); NodeId nodeid = (NodeId) id_arg.get_int64();
success = g_rpc_node->connman->DisconnectNode(nodeid); success = node.connman->DisconnectNode(nodeid);
} else { } else {
throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided."); throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
} }
@ -350,10 +355,11 @@ static UniValue getaddednodeinfo(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
std::vector<AddedNodeInfo> vInfo = g_rpc_node->connman->GetAddedNodeInfo(); std::vector<AddedNodeInfo> vInfo = node.connman->GetAddedNodeInfo();
if (!request.params[0].isNull()) { if (!request.params[0].isNull()) {
bool found = false; bool found = false;
@ -417,21 +423,22 @@ static UniValue getnettotals(const JSONRPCRequest& request)
+ HelpExampleRpc("getnettotals", "") + HelpExampleRpc("getnettotals", "")
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->connman) NodeContext& node = EnsureNodeContext(request.context);
if(!node.connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
UniValue obj(UniValue::VOBJ); UniValue obj(UniValue::VOBJ);
obj.pushKV("totalbytesrecv", g_rpc_node->connman->GetTotalBytesRecv()); obj.pushKV("totalbytesrecv", node.connman->GetTotalBytesRecv());
obj.pushKV("totalbytessent", g_rpc_node->connman->GetTotalBytesSent()); obj.pushKV("totalbytessent", node.connman->GetTotalBytesSent());
obj.pushKV("timemillis", GetTimeMillis()); obj.pushKV("timemillis", GetTimeMillis());
UniValue outboundLimit(UniValue::VOBJ); UniValue outboundLimit(UniValue::VOBJ);
outboundLimit.pushKV("timeframe", g_rpc_node->connman->GetMaxOutboundTimeframe()); outboundLimit.pushKV("timeframe", node.connman->GetMaxOutboundTimeframe());
outboundLimit.pushKV("target", g_rpc_node->connman->GetMaxOutboundTarget()); outboundLimit.pushKV("target", node.connman->GetMaxOutboundTarget());
outboundLimit.pushKV("target_reached", g_rpc_node->connman->OutboundTargetReached(false)); outboundLimit.pushKV("target_reached", node.connman->OutboundTargetReached(false));
outboundLimit.pushKV("serve_historical_blocks", !g_rpc_node->connman->OutboundTargetReached(true)); outboundLimit.pushKV("serve_historical_blocks", !node.connman->OutboundTargetReached(true));
outboundLimit.pushKV("bytes_left_in_cycle", g_rpc_node->connman->GetOutboundTargetBytesLeft()); outboundLimit.pushKV("bytes_left_in_cycle", node.connman->GetOutboundTargetBytesLeft());
outboundLimit.pushKV("time_left_in_cycle", g_rpc_node->connman->GetMaxOutboundTimeLeftInCycle()); outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle());
obj.pushKV("uploadtarget", outboundLimit); obj.pushKV("uploadtarget", outboundLimit);
return obj; return obj;
} }
@ -513,16 +520,17 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.pushKV("version", CLIENT_VERSION); obj.pushKV("version", CLIENT_VERSION);
obj.pushKV("subversion", strSubVersion); obj.pushKV("subversion", strSubVersion);
obj.pushKV("protocolversion",PROTOCOL_VERSION); obj.pushKV("protocolversion",PROTOCOL_VERSION);
if (g_rpc_node->connman) { NodeContext& node = EnsureNodeContext(request.context);
ServiceFlags services = g_rpc_node->connman->GetLocalServices(); if (node.connman) {
ServiceFlags services = node.connman->GetLocalServices();
obj.pushKV("localservices", strprintf("%016x", services)); obj.pushKV("localservices", strprintf("%016x", services));
obj.pushKV("localservicesnames", GetServicesNames(services)); obj.pushKV("localservicesnames", GetServicesNames(services));
} }
obj.pushKV("localrelay", g_relay_txes); obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset()); obj.pushKV("timeoffset", GetTimeOffset());
if (g_rpc_node->connman) { if (node.connman) {
obj.pushKV("networkactive", g_rpc_node->connman->GetNetworkActive()); obj.pushKV("networkactive", node.connman->GetNetworkActive());
obj.pushKV("connections", (int)g_rpc_node->connman->GetNodeCount(CConnman::CONNECTIONS_ALL)); obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
} }
obj.pushKV("networks", GetNetworksInfo()); obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()));
@ -567,7 +575,8 @@ static UniValue setban(const JSONRPCRequest& request)
if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) { if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) {
throw std::runtime_error(help.ToString()); throw std::runtime_error(help.ToString());
} }
if (!g_rpc_node->banman) { NodeContext& node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
} }
@ -591,7 +600,7 @@ static UniValue setban(const JSONRPCRequest& request)
if (strCommand == "add") if (strCommand == "add")
{ {
if (isSubnet ? g_rpc_node->banman->IsBanned(subNet) : g_rpc_node->banman->IsBanned(netAddr)) { if (isSubnet ? node.banman->IsBanned(subNet) : node.banman->IsBanned(netAddr)) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
} }
@ -604,20 +613,20 @@ static UniValue setban(const JSONRPCRequest& request)
absolute = true; absolute = true;
if (isSubnet) { if (isSubnet) {
g_rpc_node->banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute); node.banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute);
if (g_rpc_node->connman) { if (node.connman) {
g_rpc_node->connman->DisconnectNode(subNet); node.connman->DisconnectNode(subNet);
} }
} else { } else {
g_rpc_node->banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); node.banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute);
if (g_rpc_node->connman) { if (node.connman) {
g_rpc_node->connman->DisconnectNode(netAddr); node.connman->DisconnectNode(netAddr);
} }
} }
} }
else if(strCommand == "remove") else if(strCommand == "remove")
{ {
if (!( isSubnet ? g_rpc_node->banman->Unban(subNet) : g_rpc_node->banman->Unban(netAddr) )) { if (!( isSubnet ? node.banman->Unban(subNet) : node.banman->Unban(netAddr) )) {
throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned."); throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously banned.");
} }
} }
@ -645,12 +654,13 @@ static UniValue listbanned(const JSONRPCRequest& request)
}, },
}.Check(request); }.Check(request);
if(!g_rpc_node->banman) { NodeContext& node = EnsureNodeContext(request.context);
if(!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
} }
banmap_t banMap; banmap_t banMap;
g_rpc_node->banman->GetBanned(banMap); node.banman->GetBanned(banMap);
UniValue bannedAddresses(UniValue::VARR); UniValue bannedAddresses(UniValue::VARR);
for (const auto& entry : banMap) for (const auto& entry : banMap)
@ -679,11 +689,12 @@ static UniValue clearbanned(const JSONRPCRequest& request)
+ HelpExampleRpc("clearbanned", "") + HelpExampleRpc("clearbanned", "")
}, },
}.Check(request); }.Check(request);
if (!g_rpc_node->banman) { NodeContext& node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
} }
g_rpc_node->banman->ClearBanned(); node.banman->ClearBanned();
return NullUniValue; return NullUniValue;
} }
@ -699,13 +710,14 @@ static UniValue setnetworkactive(const JSONRPCRequest& request)
RPCExamples{""}, RPCExamples{""},
}.Check(request); }.Check(request);
if (!g_rpc_node->connman) { NodeContext& node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
} }
g_rpc_node->connman->SetNetworkActive(request.params[0].get_bool()); node.connman->SetNetworkActive(request.params[0].get_bool());
return g_rpc_node->connman->GetNetworkActive(); return node.connman->GetNetworkActive();
} }
static UniValue getnodeaddresses(const JSONRPCRequest& request) static UniValue getnodeaddresses(const JSONRPCRequest& request)
@ -732,7 +744,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
+ HelpExampleRpc("getnodeaddresses", "8") + HelpExampleRpc("getnodeaddresses", "8")
}, },
}.Check(request); }.Check(request);
if (!g_rpc_node->connman) { NodeContext& node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
} }
@ -744,7 +757,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
} }
} }
// returns a shuffled list of CAddress // returns a shuffled list of CAddress
std::vector<CAddress> vAddr = g_rpc_node->connman->GetAddresses(); std::vector<CAddress> vAddr = node.connman->GetAddresses();
UniValue ret(UniValue::VARR); UniValue ret(UniValue::VARR);
int address_return_count = std::min<int>(count, vAddr.size()); int address_return_count = std::min<int>(count, vAddr.size());

View file

@ -653,7 +653,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request)
CCoinsView viewDummy; CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy); CCoinsViewCache view(&viewDummy);
{ {
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK(cs_main); LOCK(cs_main);
LOCK(mempool.cs); LOCK(mempool.cs);
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
@ -778,7 +778,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
for (const CTxIn& txin : mtx.vin) { for (const CTxIn& txin : mtx.vin) {
coins[txin.prevout]; // Create empty map entry keyed by prevout. coins[txin.prevout]; // Create empty map entry keyed by prevout.
} }
FindCoins(*g_rpc_node, coins); NodeContext& node = EnsureNodeContext(request.context);
FindCoins(node, coins);
// Parse the prevtxs array // Parse the prevtxs array
ParsePrevouts(request.params[2], &keystore, coins); ParsePrevouts(request.params[2], &keystore, coins);
@ -837,7 +838,8 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
std::string err_string; std::string err_string;
AssertLockNotHeld(cs_main); AssertLockNotHeld(cs_main);
const TransactionError err = BroadcastTransaction(*g_rpc_node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true); NodeContext& node = EnsureNodeContext(request.context);
const TransactionError err = BroadcastTransaction(node, tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
if (TransactionError::OK != err) { if (TransactionError::OK != err) {
throw JSONRPCTransactionError(err, err_string); throw JSONRPCTransactionError(err, err_string);
} }
@ -904,7 +906,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
DEFAULT_MAX_RAW_TX_FEE_RATE : DEFAULT_MAX_RAW_TX_FEE_RATE :
CFeeRate(AmountFromValue(request.params[1])); CFeeRate(AmountFromValue(request.params[1]));
CTxMemPool& mempool = EnsureMemPool(); CTxMemPool& mempool = EnsureMemPool(request.context);
int64_t virtual_size = GetVirtualTransactionSize(*tx); int64_t virtual_size = GetVirtualTransactionSize(*tx);
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
@ -1555,7 +1557,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request)
CCoinsView viewDummy; CCoinsView viewDummy;
CCoinsViewCache view(&viewDummy); CCoinsViewCache view(&viewDummy);
{ {
const CTxMemPool& mempool = EnsureMemPool(); const CTxMemPool& mempool = EnsureMemPool(request.context);
LOCK2(cs_main, mempool.cs); LOCK2(cs_main, mempool.cs);
CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip();
CCoinsViewMemPool viewMempool(&viewChain, mempool); CCoinsViewMemPool viewMempool(&viewChain, mempool);

View file

@ -10,6 +10,10 @@
#include <univalue.h> #include <univalue.h>
namespace util {
class Ref;
} // namespace util
UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id); UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id);
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
@ -34,8 +38,9 @@ public:
std::string URI; std::string URI;
std::string authUser; std::string authUser;
std::string peerAddr; std::string peerAddr;
const util::Ref& context;
JSONRPCRequest() : id(NullUniValue), params(NullUniValue), fHelp(false) {} JSONRPCRequest(const util::Ref& context) : id(NullUniValue), params(NullUniValue), fHelp(false), context(context) {}
void parse(const UniValue& valRequest); void parse(const UniValue& valRequest);
}; };

33
src/test/ref_tests.cpp Normal file
View file

@ -0,0 +1,33 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <util/ref.h>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(ref_tests)
BOOST_AUTO_TEST_CASE(ref_test)
{
util::Ref ref;
BOOST_CHECK(!ref.Has<int>());
BOOST_CHECK_THROW(ref.Get<int>(), NonFatalCheckError);
int value = 5;
ref.Set(value);
BOOST_CHECK(ref.Has<int>());
BOOST_CHECK_EQUAL(ref.Get<int>(), 5);
++ref.Get<int>();
BOOST_CHECK_EQUAL(ref.Get<int>(), 6);
BOOST_CHECK_EQUAL(value, 6);
++value;
BOOST_CHECK_EQUAL(value, 7);
BOOST_CHECK_EQUAL(ref.Get<int>(), 7);
BOOST_CHECK(!ref.Has<bool>());
BOOST_CHECK_THROW(ref.Get<bool>(), NonFatalCheckError);
ref.Clear();
BOOST_CHECK(!ref.Has<int>());
BOOST_CHECK_THROW(ref.Get<int>(), NonFatalCheckError);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -10,6 +10,7 @@
#include <interfaces/chain.h> #include <interfaces/chain.h>
#include <node/context.h> #include <node/context.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/ref.h>
#include <util/time.h> #include <util/time.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -19,13 +20,20 @@
#include <rpc/blockchain.h> #include <rpc/blockchain.h>
UniValue CallRPC(std::string args) class RPCTestingSetup : public TestingSetup
{
public:
UniValue CallRPC(std::string args);
};
UniValue RPCTestingSetup::CallRPC(std::string args)
{ {
std::vector<std::string> vArgs; std::vector<std::string> vArgs;
boost::split(vArgs, args, boost::is_any_of(" \t")); boost::split(vArgs, args, boost::is_any_of(" \t"));
std::string strMethod = vArgs[0]; std::string strMethod = vArgs[0];
vArgs.erase(vArgs.begin()); vArgs.erase(vArgs.begin());
JSONRPCRequest request; util::Ref context{m_node};
JSONRPCRequest request(context);
request.strMethod = strMethod; request.strMethod = strMethod;
request.params = RPCConvertValues(strMethod, vArgs); request.params = RPCConvertValues(strMethod, vArgs);
request.fHelp = false; request.fHelp = false;
@ -40,7 +48,7 @@ UniValue CallRPC(std::string args)
} }
BOOST_FIXTURE_TEST_SUITE(rpc_tests, TestingSetup) BOOST_FIXTURE_TEST_SUITE(rpc_tests, RPCTestingSetup)
BOOST_AUTO_TEST_CASE(rpc_rawparams) BOOST_AUTO_TEST_CASE(rpc_rawparams)
{ {

View file

@ -123,7 +123,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
// Ideally we'd move all the RPC tests to the functional testing framework // Ideally we'd move all the RPC tests to the functional testing framework
// instead of unit tests, but for now we need these here. // instead of unit tests, but for now we need these here.
g_rpc_node = &m_node;
RegisterAllCoreRPCCommands(tableRPC); RegisterAllCoreRPCCommands(tableRPC);
m_node.scheduler = MakeUnique<CScheduler>(); m_node.scheduler = MakeUnique<CScheduler>();
@ -131,7 +130,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const
// We have to run a scheduler thread to prevent ActivateBestChain // We have to run a scheduler thread to prevent ActivateBestChain
// from blocking due to queue overrun. // from blocking due to queue overrun.
threadGroup.create_thread([&]{ m_node.scheduler->serviceQueue(); }); threadGroup.create_thread([&]{ m_node.scheduler->serviceQueue(); });
GetMainSignals().RegisterBackgroundSignalScheduler(*g_rpc_node->scheduler); GetMainSignals().RegisterBackgroundSignalScheduler(*m_node.scheduler);
pblocktree.reset(new CBlockTreeDB(1 << 20, true)); pblocktree.reset(new CBlockTreeDB(1 << 20, true));
@ -176,7 +175,6 @@ TestingSetup::~TestingSetup()
threadGroup.join_all(); threadGroup.join_all();
GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().FlushBackgroundCallbacks();
GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterBackgroundSignalScheduler();
g_rpc_node = nullptr;
m_node.connman.reset(); m_node.connman.reset();
m_node.banman.reset(); m_node.banman.reset();
m_node.args = nullptr; m_node.args = nullptr;

View file

@ -5,6 +5,10 @@
#ifndef BITCOIN_UTIL_CHECK_H #ifndef BITCOIN_UTIL_CHECK_H
#define BITCOIN_UTIL_CHECK_H #define BITCOIN_UTIL_CHECK_H
#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif
#include <tinyformat.h> #include <tinyformat.h>
#include <stdexcept> #include <stdexcept>

38
src/util/ref.h Normal file
View file

@ -0,0 +1,38 @@
// Copyright (c) 2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_UTIL_REF_H
#define BITCOIN_UTIL_REF_H
#include <util/check.h>
#include <typeindex>
namespace util {
/**
* Type-safe dynamic reference.
*
* This implements a small subset of the functionality in C++17's std::any
* class, and can be dropped when the project updates to C++17
* (https://github.com/bitcoin/bitcoin/issues/16684)
*/
class Ref
{
public:
Ref() = default;
template<typename T> Ref(T& value) { Set(value); }
template<typename T> T& Get() const { CHECK_NONFATAL(Has<T>()); return *static_cast<T*>(m_value); }
template<typename T> void Set(T& value) { m_value = &value; m_type = std::type_index(typeid(T)); }
template<typename T> bool Has() const { return m_value && m_type == std::type_index(typeid(T)); }
void Clear() { m_value = nullptr; m_type = std::type_index(typeid(void)); }
private:
void* m_value = nullptr;
std::type_index m_type = std::type_index(typeid(void));
};
} // namespace util
#endif // BITCOIN_UTIL_REF_H

View file

@ -15,6 +15,7 @@
#include <rpc/server.h> #include <rpc/server.h>
#include <test/util/logging.h> #include <test/util/logging.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <util/ref.h>
#include <util/translation.h> #include <util/translation.h>
#include <validation.h> #include <validation.h>
#include <wallet/coincontrol.h> #include <wallet/coincontrol.h>
@ -208,7 +209,8 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup)
key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1); key.pushKV("timestamp", newTip->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1);
key.pushKV("internal", UniValue(true)); key.pushKV("internal", UniValue(true));
keys.push_back(key); keys.push_back(key);
JSONRPCRequest request; util::Ref context;
JSONRPCRequest request(context);
request.params.setArray(); request.params.setArray();
request.params.push_back(keys); request.params.push_back(keys);
@ -262,7 +264,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
AddWallet(wallet); AddWallet(wallet);
wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash()); wallet->SetLastBlockProcessed(::ChainActive().Height(), ::ChainActive().Tip()->GetBlockHash());
} }
JSONRPCRequest request; util::Ref context;
JSONRPCRequest request(context);
request.params.setArray(); request.params.setArray();
request.params.push_back(backup_file); request.params.push_back(backup_file);
@ -277,7 +280,8 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup)
LOCK(wallet->cs_wallet); LOCK(wallet->cs_wallet);
wallet->SetupLegacyScriptPubKeyMan(); wallet->SetupLegacyScriptPubKeyMan();
JSONRPCRequest request; util::Ref context;
JSONRPCRequest request(context);
request.params.setArray(); request.params.setArray();
request.params.push_back(backup_file); request.params.push_back(backup_file);
AddWallet(wallet); AddWallet(wallet);