diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -147,6 +147,8 @@ reverselock.h \ rpc/blockchain.h \ rpc/client.h \ + rpc/command.h \ + rpc/commandname.h \ rpc/jsonrpcrequest.h \ rpc/mining.h \ rpc/misc.h \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -79,6 +79,7 @@ test/random_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ + test/rpc_server_tests.cpp \ test/sanity_tests.cpp \ test/scheduler_tests.cpp \ test/script_commitment_tests.cpp \ diff --git a/src/rpc/command.h b/src/rpc/command.h new file mode 100644 --- /dev/null +++ b/src/rpc/command.h @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_COMMAND_H +#define BITCOIN_RPC_COMMAND_H + +#include "rpc/commandname.h" + +#include +#include + +#include + +/** + * Base class for all RPC commands. + */ +class RPCCommand { +private: + RPCCommandName name; + // TODO Parameter definitions (these will be used to generate help messages + // as well) + +public: + RPCCommand(RPCCommandName &nameIn) : name(nameIn) {} + + virtual UniValue Execute(std::map &args) const = 0; + + RPCCommandName GetName() const { return name; } +}; + +#endif // BITCOIN_RPC_COMMAND_H diff --git a/src/rpc/commandname.h b/src/rpc/commandname.h new file mode 100644 --- /dev/null +++ b/src/rpc/commandname.h @@ -0,0 +1,22 @@ +// Copyright (c) 2018 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_COMMAND_NAME_H +#define BITCOIN_RPC_COMMAND_NAME_H + +#include + +#include + +class RPCCommandName { +private: + std::string name; + +public: + RPCCommandName(std::string nameIn) : name(nameIn){}; + + std::string GetNameStr() const { return name; } +}; + +#endif // BITCOIN_RPC_COMMAND_NAME_H diff --git a/src/rpc/server.h b/src/rpc/server.h --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -8,6 +8,7 @@ #define BITCOIN_RPC_SERVER_H #include "amount.h" +#include "rpc/command.h" #include "rpc/jsonrpcrequest.h" #include "rpc/protocol.h" #include "uint256.h" @@ -50,6 +51,9 @@ * Class for registering and managing all RPC calls. */ class RPCServer : public boost::noncopyable { +private: + std::map commands; + public: RPCServer() {} @@ -59,6 +63,21 @@ */ UniValue ExecuteCommand(Config &config, const JSONRPCRequest &request) const; + + /** + * Register an RPC command. + */ + void RegisterCommand(RPCCommand *command) { + commands[command->GetName().GetNameStr()] = command; + } + + /** + * Return a command if one matches the given command name. + * Returns nullptr if no command matches. + * + * TODO: Add support for short names. + */ + RPCCommand *MatchCommand(std::string name) const; }; /** diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers +// Copyright (c) 2018 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -44,13 +45,34 @@ } } - // TODO Only call tableRPC.execute() if no context-sensitive RPC command - // exists + std::string commandName = request.strMethod; + RPCCommand *command = MatchCommand(commandName); + if (command != nullptr) { + std::map args; + const std::vector &keys = request.params.getKeys(); + const std::vector &values = request.params.getValues(); + for (size_t i = 0; i < keys.size(); i++) { + args[keys[i]] = values[i]; + } + return command->Execute(args); + } + + // TODO Remove the below call to tableRPC.execute() and only call it for + // context-free RPC commands via an implementation of RPCCommand. // Check if context-free RPC method is valid and execute it return tableRPC.execute(config, request); } +RPCCommand *RPCServer::MatchCommand(std::string name) const { + auto iter = commands.find(name); + if (iter == commands.end()) { + return nullptr; + } + + return iter->second; +} + static struct CRPCSignals { boost::signals2::signal Started; boost::signals2::signal Stopped; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -96,6 +96,7 @@ random_tests.cpp reverselock_tests.cpp rpc_tests.cpp + rpc_server_tests.cpp sanity_tests.cpp scheduler_tests.cpp script_commitment_tests.cpp diff --git a/src/test/rpc_server_tests.cpp b/src/test/rpc_server_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/rpc_server_tests.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2018 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "chainparams.h" +#include "config.h" +#include "rpc/jsonrpcrequest.h" +#include "rpc/server.h" + +#include "test/test_bitcoin.h" + +#include +#include + +#include +#include + +struct TestRPCCommand : public RPCCommand { + TestRPCCommand(RPCCommandName &commandName) : RPCCommand(commandName) {} + + UniValue Execute(std::map &args) const { + return UniValue(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(rpc_server_tests, TestingSetup) + +BOOST_AUTO_TEST_CASE(rpc_server_match_command) { + RPCServer rpcServer; + const std::string commandStr = "testcommand"; + RPCCommandName commandName = RPCCommandName(commandStr); + RPCCommand *command = new TestRPCCommand(commandName); + rpcServer.RegisterCommand(command); + + BOOST_CHECK_EQUAL(rpcServer.MatchCommand(commandStr), command); +} + +struct ArgsTestRPCCommand : public RPCCommand { + ArgsTestRPCCommand(RPCCommandName &commandName) : RPCCommand(commandName) {} + + UniValue Execute(std::map &args) const { + BOOST_CHECK_EQUAL(args["arg1"].get_str(), "value1"); + return UniValue(); + } +}; + +BOOST_AUTO_TEST_CASE(rpc_server_execute_command) { + DummyConfig config; + RPCServer rpcServer; + const std::string commandStr = "testcommand"; + RPCCommandName commandName = RPCCommandName(commandStr); + RPCCommand *command = new ArgsTestRPCCommand(commandName); + rpcServer.RegisterCommand(command); + + UniValue args(UniValue::VOBJ); + args.pushKV("arg1", "value1"); + + JSONRPCRequest request; + request.strMethod = commandStr; + request.params = args; + + rpcServer.ExecuteCommand(config, request); +} + +BOOST_AUTO_TEST_SUITE_END() 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 @@ -84,6 +84,12 @@ const Config &config = GetConfig(); RPCServer rpcServer; RegisterAllRPCCommands(config, rpcServer, tableRPC); + + std::string rpcWarmupStatus; + if (RPCIsInWarmup(&rpcWarmupStatus)) { + SetRPCWarmupFinished(); + } + ClearDatadirCache(); pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(),