diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -77,6 +77,8 @@ bool fRet = false; + RPCServer rpcServer; + // // Parameters // @@ -151,7 +153,7 @@ // up on console exit(1); } - if (!AppInitParameterInteraction(config)) { + if (!AppInitParameterInteraction(config, rpcServer)) { // InitError will have been called with detailed error, which ends // up on console exit(1); diff --git a/src/httprpc.cpp b/src/httprpc.cpp --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -273,8 +273,8 @@ return false; } -static bool HTTPReq_JSONRPC(Config &config, HTTPRequest *req, - const std::string &) { +static bool HTTPReq_JSONRPC(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &) { // First, check and/or set CORS headers if (checkCORS(req)) { return true; @@ -323,13 +323,14 @@ if (valRequest.isObject()) { jreq.parse(valRequest); - UniValue result = tableRPC.execute(config, jreq); + UniValue result = rpcServer.ExecuteCommand(config, jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); } else if (valRequest.isArray()) { // array of requests - strReply = JSONRPCExecBatch(config, jreq, valRequest.get_array()); + strReply = JSONRPCExecBatch(config, rpcServer, jreq, + valRequest.get_array()); } else { throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); } diff --git a/src/httpserver.h b/src/httpserver.h --- a/src/httpserver.h +++ b/src/httpserver.h @@ -9,6 +9,8 @@ #include #include +#include "rpc/server.h" + static const int DEFAULT_HTTP_THREADS = 4; static const int DEFAULT_HTTP_WORKQUEUE = 16; static const int DEFAULT_HTTP_SERVER_TIMEOUT = 30; @@ -22,6 +24,7 @@ struct HTTPCallbackArgs { Config *config; + RPCServer *rpcServer; HTTPCallbackArgs(Config *configIn) : config(configIn) {} }; @@ -42,8 +45,8 @@ void StopHTTPServer(); /** Handler for requests to a certain HTTP path */ -typedef std::function +typedef std::function HTTPRequestHandler; /** Register handler for prefix. * If multiple handlers match a prefix, the first-registered one will diff --git a/src/httpserver.cpp b/src/httpserver.cpp --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -49,11 +49,13 @@ /** HTTP request work item */ class HTTPWorkItem final : public HTTPClosure { public: - HTTPWorkItem(Config &_config, std::unique_ptr _req, - const std::string &_path, const HTTPRequestHandler &_func) - : req(std::move(_req)), path(_path), func(_func), config(&_config) {} + HTTPWorkItem(Config &_config, RPCServer &_rpcServer, + std::unique_ptr _req, const std::string &_path, + const HTTPRequestHandler &_func) + : req(std::move(_req)), path(_path), func(_func), config(&_config), + rpcServer(&_rpcServer) {} - void operator()() override { func(*config, req.get(), path); } + void operator()() override { func(*config, *rpcServer, req.get(), path); } std::unique_ptr req; @@ -61,6 +63,7 @@ std::string path; HTTPRequestHandler func; Config *config; + RPCServer *rpcServer; }; /** @@ -236,6 +239,7 @@ HTTPCallbackArgs &callbackArgs = *reinterpret_cast(args); Config &config = *callbackArgs.config; + RPCServer &rpcServer = *callbackArgs.rpcServer; std::unique_ptr hreq(new HTTPRequest(req)); @@ -275,8 +279,8 @@ // Dispatch to worker thread. if (i != iend) { - std::unique_ptr item( - new HTTPWorkItem(config, std::move(hreq), path, i->handler)); + std::unique_ptr item(new HTTPWorkItem( + config, rpcServer, std::move(hreq), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) { /* if true, queue took ownership */ diff --git a/src/init.h b/src/init.h --- a/src/init.h +++ b/src/init.h @@ -6,6 +6,8 @@ #ifndef BITCOIN_INIT_H #define BITCOIN_INIT_H +#include "rpc/server.h" + #include class Config; @@ -38,7 +40,7 @@ * @pre Parameters should be parsed and config file should be read, * AppInitBasicSetup should have been called. */ -bool AppInitParameterInteraction(Config &config); +bool AppInitParameterInteraction(Config &config, RPCServer &rpcServer); /** * Initialization sanity checks: ecc init, sanity checks, dir lock. * @note This can be done before daemonization. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1360,7 +1360,7 @@ return true; } -bool AppInitParameterInteraction(Config &config) { +bool AppInitParameterInteraction(Config &config, RPCServer &rpcServer) { const CChainParams &chainparams = config.GetChainParams(); // Step 2: parameter interactions @@ -1586,7 +1586,8 @@ fPruneMode = true; } - RegisterAllRPCCommands(tableRPC); + RegisterAllRPCCommands(config, rpcServer); + RegisterAllContextFreeRPCCommands(tableRPC); #ifdef ENABLE_WALLET RegisterWalletRPCCommands(tableRPC); RegisterDumpRPCCommands(tableRPC); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -281,6 +281,7 @@ void BitcoinABC::initialize(Config *cfg, HTTPCallbackArgs *httpCallbackArgs) { Config &config(*cfg); + RPCServer &rpcServer = *rpcSrv; try { qDebug() << __func__ << ": Running AppInit2 in thread"; if (!AppInitBasicSetup()) { @@ -288,7 +289,7 @@ return; } - if (!AppInitParameterInteraction(config)) { + if (!AppInitParameterInteraction(config, rpcServer)) { Q_EMIT initializeResult(false); return; } @@ -787,6 +788,8 @@ !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); + RPCServer rpcServer; + try { app.createWindow(&config, networkStyle.data()); app.requestInitialize(config, httpCallbackArgs); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -37,7 +37,7 @@ // Do some test setup could be moved to a more generic place when we add // more tests on QT level const Config &config = GetConfig(); - RegisterAllRPCCommands(tableRPC); + RegisterAllContextFreeRPCCommands(tableRPC); tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); ClearDatadirCache(); std::string path = diff --git a/src/rest.cpp b/src/rest.cpp --- a/src/rest.cpp +++ b/src/rest.cpp @@ -126,7 +126,7 @@ return true; } -static bool rest_headers(Config &config, HTTPRequest *req, +static bool rest_headers(Config &config, RPCServer &rpcServer, HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; @@ -212,8 +212,9 @@ return true; } -static bool rest_block(const Config &config, HTTPRequest *req, - const std::string &strURIPart, bool showTxDetails) { +static bool rest_block(const Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart, + bool showTxDetails) { if (!CheckWarmup(req)) { return false; } @@ -286,18 +287,20 @@ return true; } -static bool rest_block_extended(Config &config, HTTPRequest *req, +static bool rest_block_extended(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { - return rest_block(config, req, strURIPart, true); + return rest_block(config, rpcServer, req, strURIPart, true); } -static bool rest_block_notxdetails(Config &config, HTTPRequest *req, +static bool rest_block_notxdetails(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { - return rest_block(config, req, strURIPart, false); + return rest_block(config, rpcServer, req, strURIPart, false); } -static bool rest_chaininfo(Config &config, HTTPRequest *req, - const std::string &strURIPart) { +static bool rest_chaininfo(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; } @@ -326,8 +329,8 @@ return true; } -static bool rest_mempool_info(Config &config, HTTPRequest *req, - const std::string &strURIPart) { +static bool rest_mempool_info(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; } @@ -355,7 +358,8 @@ return true; } -static bool rest_mempool_contents(Config &config, HTTPRequest *req, +static bool rest_mempool_contents(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; @@ -384,7 +388,7 @@ return true; } -static bool rest_tx(Config &config, HTTPRequest *req, +static bool rest_tx(Config &config, RPCServer &rpcServer, HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; @@ -445,8 +449,8 @@ return true; } -static bool rest_getutxos(Config &config, HTTPRequest *req, - const std::string &strURIPart) { +static bool rest_getutxos(Config &config, RPCServer &rpcServer, + HTTPRequest *req, const std::string &strURIPart) { if (!CheckWarmup(req)) { return false; } @@ -669,7 +673,7 @@ static const struct { const char *prefix; - bool (*handler)(Config &config, HTTPRequest *req, + bool (*handler)(Config &config, RPCServer &rpcServer, HTTPRequest *req, const std::string &strReq); } uri_prefixes[] = { {"/rest/tx/", rest_tx}, diff --git a/src/rpc/register.h b/src/rpc/register.h --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -2,8 +2,10 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_RPCREGISTER_H -#define BITCOIN_RPCREGISTER_H +#ifndef BITCOIN_RPC_REGISTER_H +#define BITCOIN_RPC_REGISTER_H + +#include "rpc/server.h" /** These are in one header file to avoid creating tons of single-function * headers for everything under src/rpc/ */ @@ -22,7 +24,19 @@ /** Register ABC RPC commands */ void RegisterABCRPCCommands(CRPCTable &tableRPC); -static inline void RegisterAllRPCCommands(CRPCTable &t) { +/** + * Register all context-sensitive RPC commands. + */ +static inline void RegisterAllRPCCommands(const Config &config, + RPCServer &rpcServer) { + // TODO Register context-sensitive RPC commands using rpcServer +} + +/** + * Register all context-free (legacy) RPC commands, except for wallet and dump + * RPC commands. + */ +static inline void RegisterAllContextFreeRPCCommands(CRPCTable &t) { RegisterBlockchainRPCCommands(t); RegisterNetRPCCommands(t); RegisterMiscRPCCommands(t); diff --git a/src/rpc/server.h b/src/rpc/server.h --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -4,8 +4,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef BITCOIN_RPCSERVER_H -#define BITCOIN_RPCSERVER_H +#ifndef BITCOIN_RPC_SERVER_H +#define BITCOIN_RPC_SERVER_H #include "amount.h" #include "rpc/jsonrpcrequest.h" @@ -18,6 +18,7 @@ #include #include +#include #include static const unsigned int DEFAULT_RPC_SERIALIZE_VERSION = 1; @@ -46,6 +47,21 @@ UniValue::VType type; }; +/** + * Class for registering and managing all RPC calls. + */ +class RPCServer : public boost::noncopyable { +public: + RPCServer() {} + + /** + * Attempts to execute an RPC command from the given request. + * If no RPC command exists that matches the request, an error is returned. + */ + UniValue ExecuteCommand(Config &config, + const JSONRPCRequest &request) const; +}; + /** * Query whether RPC is running */ @@ -253,8 +269,8 @@ bool StartRPC(); void InterruptRPC(); void StopRPC(); -std::string JSONRPCExecBatch(Config &config, const JSONRPCRequest &req, - const UniValue &vReq); +std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer, + const JSONRPCRequest &req, const UniValue &vReq); void RPCNotifyBlockChange(bool ibd, const CBlockIndex *); /** @@ -262,4 +278,4 @@ */ int RPCSerializationFlags(); -#endif // BITCOIN_RPCSERVER_H +#endif // BITCOIN_RPC_SERVER_H diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -35,6 +35,23 @@ /* Map of name to timer. */ static std::map> deadlineTimers; +UniValue RPCServer::ExecuteCommand(Config &config, + const JSONRPCRequest &request) const { + // Return immediately if in warmup + { + LOCK(cs_rpcWarmup); + if (fRPCInWarmup) { + throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); + } + } + + // TODO Only call tableRPC.execute() if no context-sensitive RPC command + // exists + + // Check if context-free RPC method is valid and execute it + return tableRPC.execute(config, request); +} + static struct CRPCSignals { boost::signals2::signal Started; boost::signals2::signal Stopped; @@ -402,14 +419,14 @@ return fRPCInWarmup; } -static UniValue JSONRPCExecOne(Config &config, JSONRPCRequest jreq, - const UniValue &req) { +static UniValue JSONRPCExecOne(Config &config, RPCServer &rpcServer, + JSONRPCRequest jreq, const UniValue &req) { UniValue rpc_result(UniValue::VOBJ); try { jreq.parse(req); - UniValue result = tableRPC.execute(config, jreq); + UniValue result = rpcServer.ExecuteCommand(config, jreq); rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } catch (const UniValue &objError) { rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); @@ -421,11 +438,11 @@ return rpc_result; } -std::string JSONRPCExecBatch(Config &config, const JSONRPCRequest &jreq, - const UniValue &vReq) { +std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer, + const JSONRPCRequest &jreq, const UniValue &vReq) { UniValue ret(UniValue::VARR); for (size_t i = 0; i < vReq.size(); i++) { - ret.push_back(JSONRPCExecOne(config, jreq, vReq[i])); + ret.push_back(JSONRPCExecOne(config, rpcServer, jreq, vReq[i])); } return ret.write() + "\n"; @@ -485,7 +502,8 @@ } } - // Find method + // Check if legacy RPC method is valid. + // See RPCServer::ExecuteCommand for context-sensitive RPC commands. const ContextFreeRPCCommand *pcmd = tableRPC[request.strMethod]; if (!pcmd) { throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -73,7 +73,9 @@ // Ideally we'd move all the RPC tests to the functional testing framework // instead of unit tests, but for now we need these here. const Config &config = GetConfig(); - RegisterAllRPCCommands(tableRPC); + RPCServer rpcServer; + RegisterAllRPCCommands(config, rpcServer); + RegisterAllContextFreeRPCCommands(tableRPC); ClearDatadirCache(); pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(),