diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0dc3f141c..00283aba3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,7 +72,7 @@ add_library(rpc ${RPC}) add_library(wallet ${WALLET}) target_link_libraries(wallet mnemonics) add_executable(simplewallet ${SIMPLEWALLET} ) -target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common mnemonics ${UNBOUND_LIBRARY} ${UPNP_LIBRARIES} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) +target_link_libraries(simplewallet wallet rpc cryptonote_core crypto common mnemonics ${UNBOUND_LIBRARY} ${UPNP_LIBRARIES} ${NET_SKELETON_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) add_dependencies(daemon version) add_dependencies(rpc version) add_dependencies(simplewallet version) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 2229c9638..7a72bd7ad 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -50,7 +50,7 @@ using namespace epee; #include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "daemon_commands_handler.h" #include "version.h" -#include "rpc/json_rpc_handlers.h" +#include "rpc/daemon_json_rpc_handlers.h" #include "rpc/json_rpc_http_server.h" #if defined(WIN32) @@ -140,7 +140,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_dns_checkpoints); cryptonote::core::init_options(desc_cmd_sett); - RPC::init_options(desc_cmd_sett); + RPC::Daemon::init_options(desc_cmd_sett); nodetool::node_server >::init_options(desc_cmd_sett); cryptonote::miner::init_options(desc_cmd_sett); @@ -246,10 +246,10 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Protocol initialized OK"); LOG_PRINT_L0("Initializing core RPC server..."); - RPC::init(&ccore, &p2psrv, testnet_mode); + RPC::Daemon::init(&ccore, &p2psrv, testnet_mode); std::string ip_address, port; - RPC::get_address_and_port(vm, ip_address, port); - RPC::Json_rpc_http_server rpc_server(ip_address, port, &RPC::ev_handler); + RPC::Daemon::get_address_and_port(vm, ip_address, port); + RPC::Json_rpc_http_server rpc_server(ip_address, port, &RPC::Daemon::ev_handler); LOG_PRINT_GREEN("Core RPC server initialized on port: " << port, LOG_LEVEL_0); //initialize core here diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/daemon_json_rpc_handlers.cpp similarity index 94% rename from src/rpc/json_rpc_handlers.cpp rename to src/rpc/daemon_json_rpc_handlers.cpp index a8ffa383b..bcfacc7bf 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/daemon_json_rpc_handlers.cpp @@ -1,5 +1,5 @@ /*! - * \file json_rpc_handlers.cpp + * \file daemon_json_rpc_handlers.cpp * \brief Implementations of JSON RPC handlers (Daemon) */ @@ -15,7 +15,7 @@ // Trivial and error responses may be returned with ns_create_rpc_reply and ns_create_rpc_error // respectively. -#include "json_rpc_handlers.h" +#include "daemon_json_rpc_handlers.h" #define CHECK_CORE_BUSY() if (check_core_busy()) { \ return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, \ @@ -1122,69 +1122,76 @@ namespace namespace RPC { /*! - * \brief initializes module (must call this before handling requests) - * \param p_core Pointer to cryptonote core object - * \param p_p2p Pointer to P2P object - * \param p_testnet True if testnet false otherwise + * \namespace Daemon + * \brief RPC relevant to daemon */ - void init(cryptonote::core *p_core, - nodetool::node_server > *p_p2p, - bool p_testnet) + namespace Daemon { - core = p_core; - p2p = p_p2p; - testnet = p_testnet; - } + /*! + * \brief initializes module (must call this before handling requests) + * \param p_core Pointer to cryptonote core object + * \param p_p2p Pointer to P2P object + * \param p_testnet True if testnet false otherwise + */ + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p, + bool p_testnet) + { + core = p_core; + p2p = p_p2p; + testnet = p_testnet; + } - /*! - * \Inits certain options used in Daemon CLI. - * \param desc Instance of options description object - */ - void init_options(boost::program_options::options_description& desc) - { - command_line::add_arg(desc, arg_rpc_bind_ip); - command_line::add_arg(desc, arg_rpc_bind_port); - command_line::add_arg(desc, arg_testnet_rpc_bind_port); - } + /*! + * \Inits certain options used in Daemon CLI. + * \param desc Instance of options description object + */ + void init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); + command_line::add_arg(desc, arg_testnet_rpc_bind_port); + } - /*! - * \brief Gets IP address and port number from variable map - * \param vm Variable map - * \param ip_address IP address - * \param port Port number - */ - void get_address_and_port(const boost::program_options::variables_map& vm, - std::string &ip_address, std::string &port) - { - auto p2p_bind_arg = testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port; + /*! + * \brief Gets IP address and port number from variable map + * \param vm Variable map + * \param ip_address IP address + * \param port Port number + */ + void get_address_and_port(const boost::program_options::variables_map& vm, + std::string &ip_address, std::string &port) + { + auto p2p_bind_arg = testnet ? arg_testnet_rpc_bind_port : arg_rpc_bind_port; - ip_address = command_line::get_arg(vm, arg_rpc_bind_ip); - port = command_line::get_arg(vm, p2p_bind_arg); - } + ip_address = command_line::get_arg(vm, arg_rpc_bind_ip); + port = command_line::get_arg(vm, p2p_bind_arg); + } - /*! - * \brief Event handler that is invoked upon net_skeleton network events. - * - * Any change in behavior of RPC should happen from this point. - * \param nc net_skeleton connection - * \param ev Type of event - * \param ev_data Event data - */ - void ev_handler(struct ns_connection *nc, int ev, void *ev_data) - { - struct http_message *hm = (struct http_message *) ev_data; - char buf[MAX_RESPONSE_SIZE]; - switch (ev) { - case NS_HTTP_REQUEST: - ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), - method_names, handlers); - ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" - "Content-Type: application/json\r\n\r\n%s", - (int) strlen(buf), buf); - nc->flags |= NSF_FINISHED_SENDING_DATA; - break; - default: - break; + /*! + * \brief Event handler that is invoked upon net_skeleton network events. + * + * Any change in behavior of RPC should happen from this point. + * \param nc net_skeleton connection + * \param ev Type of event + * \param ev_data Event data + */ + void ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + struct http_message *hm = (struct http_message *) ev_data; + char buf[MAX_RESPONSE_SIZE]; + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + method_names, handlers); + ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: application/json\r\n\r\n%s", + (int) strlen(buf), buf); + nc->flags |= NSF_FINISHED_SENDING_DATA; + break; + default: + break; + } } } } diff --git a/src/rpc/daemon_json_rpc_handlers.h b/src/rpc/daemon_json_rpc_handlers.h new file mode 100644 index 000000000..bc9bb3065 --- /dev/null +++ b/src/rpc/daemon_json_rpc_handlers.h @@ -0,0 +1,75 @@ +/*! + * \file daemon_json_rpc_handlers.h + * \brief Header for JSON RPC handlers (Daemon) + */ + +#ifndef DAEMON_JSON_RPC_HANDLERS_H +#define DAEMON_JSON_RPC_HANDLERS_H + +#include "net_skeleton/net_skeleton.h" +#include "json_rpc_http_server.h" +#include "common/command_line.h" +#include "net/http_server_impl_base.h" +#include "cryptonote_core/cryptonote_core.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash-ops.h" + +#include + +/*! + * \namespace RPC + * \brief RPC related utilities + */ +namespace RPC +{ + /*! + * \namespace Daemon + * \brief RPC relevant to daemon + */ + namespace Daemon + { + /*! + * \brief initializes module (must call this before handling requests) + * \param p_core Pointer to cryptonote core object + * \param p_p2p Pointer to P2P object + * \param p_testnet True if testnet false otherwise + */ + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p, + bool p_testnet); + + /*! + * \Inits certain options used in Daemon CLI. + * \param desc Instance of options description object + */ + void init_options(boost::program_options::options_description& desc); + + /*! + * \brief Gets IP address and port number from variable map + * \param vm Variable map + * \param ip_address IP address + * \param port Port number + */ + void get_address_and_port(const boost::program_options::variables_map& vm, + std::string &ip_address, std::string &port); + + /*! + * \brief Event handler that is invoked upon net_skeleton network events. + * + * Any change in behavior of RPC should happen from this point. + * \param nc net_skeleton connection + * \param ev Type of event + * \param ev_data Event data + */ + void ev_handler(struct ns_connection *nc, int ev, void *ev_data); + } +} + +#endif diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h deleted file mode 100644 index 470a4dfc7..000000000 --- a/src/rpc/json_rpc_handlers.h +++ /dev/null @@ -1,68 +0,0 @@ -/*! - * \file json_rpc_handlers.h - * \brief Header for JSON RPC handlers (Daemon) - */ - -#ifndef JSON_RPC_HANDLERS_H -#define JSON_RPC_HANDLERS_H - -#include "net_skeleton/net_skeleton.h" -#include "json_rpc_http_server.h" -#include "common/command_line.h" -#include "net/http_server_impl_base.h" -#include "cryptonote_core/cryptonote_core.h" -#include "p2p/net_node.h" -#include "cryptonote_protocol/cryptonote_protocol_handler.h" -#include -#include "rapidjson/document.h" -#include "rapidjson/writer.h" -#include "rapidjson/stringbuffer.h" -#include -#include "cryptonote_core/cryptonote_basic.h" -#include "crypto/hash-ops.h" - -#include - -/*! - * \namespace RPC - * \brief RPC related utilities - */ -namespace RPC -{ - /*! - * \brief initializes module (must call this before handling requests) - * \param p_core Pointer to cryptonote core object - * \param p_p2p Pointer to P2P object - * \param p_testnet True if testnet false otherwise - */ - void init(cryptonote::core *p_core, - nodetool::node_server > *p_p2p, - bool p_testnet); - - /*! - * \Inits certain options used in Daemon CLI. - * \param desc Instance of options description object - */ - void init_options(boost::program_options::options_description& desc); - - /*! - * \brief Gets IP address and port number from variable map - * \param vm Variable map - * \param ip_address IP address - * \param port Port number - */ - void get_address_and_port(const boost::program_options::variables_map& vm, - std::string &ip_address, std::string &port); - - /*! - * \brief Event handler that is invoked upon net_skeleton network events. - * - * Any change in behavior of RPC should happen from this point. - * \param nc net_skeleton connection - * \param ev Type of event - * \param ev_data Event data - */ - void ev_handler(struct ns_connection *nc, int ev, void *ev_data); -} - -#endif diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 4cb9cff21..7ce2b647f 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -50,6 +50,8 @@ #include "storages/http_abstract_invoke.h" #include "rpc/core_rpc_server_commands_defs.h" #include "wallet/wallet_rpc_server.h" +#include "wallet/wallet_json_rpc_handlers.h" +#include "rpc/json_rpc_http_server.h" #include "version.h" #include "crypto/crypto.h" // for crypto::secret_key definition #include "mnemonics/electrum-words.h" @@ -1212,7 +1214,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_non_deterministic ); command_line::add_arg(desc_params, arg_electrum_seed ); command_line::add_arg(desc_params, arg_testnet); - tools::wallet_rpc_server::init_options(desc_params); + RPC::Wallet::init_options(desc_params); po::positional_options_description positional_options; positional_options.add(arg_command.name, -1); @@ -1261,7 +1263,7 @@ int main(int argc, char* argv[]) log_space::get_set_log_detalisation_level(true, command_line::get_arg(vm, arg_log_level)); } - if(command_line::has_arg(vm, tools::wallet_rpc_server::arg_rpc_bind_port)) + if (command_line::has_arg(vm, RPC::Wallet::arg_rpc_bind_port)) { log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL, LOG_LEVEL_2); //runs wallet with rpc interface @@ -1295,6 +1297,8 @@ int main(int argc, char* argv[]) daemon_address = std::string("http://") + daemon_host + ":" + std::to_string(daemon_port); tools::wallet2 wal(testnet); + RPC::Wallet::init(&wal); + try { LOG_PRINT_L0("Loading wallet..."); @@ -1308,16 +1312,16 @@ int main(int argc, char* argv[]) LOG_ERROR("Wallet initialize failed: " << e.what()); return 1; } - tools::wallet_rpc_server wrpc(wal); - bool r = wrpc.init(vm); - CHECK_AND_ASSERT_MES(r, 1, "Failed to initialize wallet rpc server"); + std::string ip_address, port; + RPC::Wallet::get_address_and_port(vm, ip_address, port); + RPC::Json_rpc_http_server rpc_server(ip_address, port, &RPC::Wallet::ev_handler); - tools::signal_handler::install([&wrpc, &wal] { - wrpc.send_stop_signal(); + tools::signal_handler::install([&rpc_server, &wal] { + rpc_server.stop(); wal.store(); }); LOG_PRINT_L0("Starting wallet rpc server"); - wrpc.run(); + rpc_server.start(); LOG_PRINT_L0("Stopped wallet rpc server"); try { diff --git a/src/wallet/wallet_json_rpc_handlers.cpp b/src/wallet/wallet_json_rpc_handlers.cpp new file mode 100644 index 000000000..0ab3b680e --- /dev/null +++ b/src/wallet/wallet_json_rpc_handlers.cpp @@ -0,0 +1,149 @@ +/*! + * \file wallet_json_rpc_handlers.cpp + * \brief Implementations of JSON RPC handlers (Wallet) + */ + +// NOTE: +// While this uses net_skeleton (aka fossa) for JSON RPC handling, JSON parsing +// and string conversion are done with rapidjson because it is way easier and better +// suited. +// To add a new method, add the name and function pointer to `method_names` and `handlers`. +// The handler function should have the same signature as the rest of them here. +// It should use rapidjson to parse the request string and the internal objects kept in the +// anonymous namespace to generate the response. The response must eventually get +// stringified using rapidjson. +// Trivial and error responses may be returned with ns_create_rpc_reply and ns_create_rpc_error +// respectively. + +#include "wallet_json_rpc_handlers.h" + +#define CHECK_CORE_BUSY() if (check_core_busy()) { \ + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, \ + CORE_RPC_STATUS_BUSY, "{}"); } + +#define MAX_RESPONSE_SIZE 2000 + +/*! + * \namespace + * \brief Anonymous namespace to keep things in the scope of this file. + */ +namespace +{ + tools::wallet2 *wallet; + + /*! + * \brief Constructs a response string given a result JSON object. + * + * It also adds boilerplate properties like id, method. + * \param req net_skeleton request object + * \param result_json rapidjson result object + * \param response_json Root rapidjson document that will eventually have the whole response + * \param response Response as a string gets written here. + */ + void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json, + rapidjson::Document &response_json, std::string &response) + { + response_json.SetObject(); + rapidjson::Value string_value(rapidjson::kStringType); + // If ID was present in request use it else use "null". + if (req->id != NULL) + { + string_value.SetString(req->id[0].ptr, req->id[0].len); + } + else + { + string_value.SetString("null", 4); + } + response_json.AddMember("id", string_value, response_json.GetAllocator()); + string_value.SetString(req->method[0].ptr, req->method[0].len); + response_json.AddMember("method", string_value, response_json.GetAllocator()); + response_json.AddMember("result", result_json, response_json.GetAllocator()); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + // Write string to `response`. + response = buffer.GetString(); + } + + // Contains a list of method names. + const char *method_names[] = { + NULL + }; + + // Contains a list of function pointers. These must map 1-1 by index with `method_names`. + ns_rpc_handler_t handlers[] = { + NULL + }; +} + +/*! + * \namespace RPC + * \brief RPC related utilities + */ +namespace RPC +{ + /*! + * \namespace Wallet + * \brief RPC relevant to wallet + */ + namespace Wallet + { + /*! + * \brief initializes module (must call this before handling requests) + * \param p_wallet Pointer to wallet2 object + */ + void init(tools::wallet2 *p_wallet) + { + wallet = p_wallet; + } + + /*! + * \Inits certain options used in Wallet CLI. + * \param desc Instance of options description object + */ + void init_options(boost::program_options::options_description& desc) + { + command_line::add_arg(desc, arg_rpc_bind_ip); + command_line::add_arg(desc, arg_rpc_bind_port); + } + + /*! + * \brief Gets IP address and port number from variable map + * \param vm Variable map + * \param ip_address IP address + * \param port Port number + */ + void get_address_and_port(const boost::program_options::variables_map& vm, + std::string &ip_address, std::string &port) + { + ip_address = command_line::get_arg(vm, arg_rpc_bind_ip); + port = command_line::get_arg(vm, arg_rpc_bind_port); + } + + /*! + * \brief Event handler that is invoked upon net_skeleton network events. + * + * Any change in behavior of RPC should happen from this point. + * \param nc net_skeleton connection + * \param ev Type of event + * \param ev_data Event data + */ + void ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + struct http_message *hm = (struct http_message *) ev_data; + char buf[MAX_RESPONSE_SIZE]; + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + method_names, handlers); + ns_printf(nc, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n" + "Content-Type: application/json\r\n\r\n%s", + (int) strlen(buf), buf); + nc->flags |= NSF_FINISHED_SENDING_DATA; + break; + default: + break; + } + } + } +} diff --git a/src/wallet/wallet_json_rpc_handlers.h b/src/wallet/wallet_json_rpc_handlers.h new file mode 100644 index 000000000..6ba9f6b08 --- /dev/null +++ b/src/wallet/wallet_json_rpc_handlers.h @@ -0,0 +1,85 @@ +/*! + * \file wallet_json_rpc_handlers.h + * \brief Header for JSON RPC handlers (Wallet) + */ + +#ifndef WALLET_JSON_RPC_HANDLERS_H +#define WALLET_JSON_RPC_HANDLERS_H + +#include "net_skeleton/net_skeleton.h" +#include "rpc/json_rpc_http_server.h" +#include "common/command_line.h" +#include "net/http_server_impl_base.h" +#include "cryptonote_core/cryptonote_core.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" +#include +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash-ops.h" +#include "wallet2.h" + +#include + +/*! + * \namespace RPC + * \brief RPC related utilities + */ +namespace RPC +{ + /*! + * \namespace Wallet + * \brief RPC relevant to wallet + */ + namespace Wallet + { + const command_line::arg_descriptor arg_rpc_bind_ip = { + "rpc-bind-ip", + "Specify ip to bind rpc server", + "127.0.0.1" + }; + + const command_line::arg_descriptor arg_rpc_bind_port = { + "rpc-bind-port", + "Starts wallet as rpc server for wallet operations, sets bind port for server", + "", + true + }; + + /*! + * \brief initializes module (must call this before handling requests) + * \param p_wallet Pointer to wallet2 object + */ + void init(tools::wallet2 *p_wallet); + + /*! + * \Inits certain options used in Daemon CLI. + * \param desc Instance of options description object + */ + void init_options(boost::program_options::options_description& desc); + + /*! + * \brief Gets IP address and port number from variable map + * \param vm Variable map + * \param ip_address IP address + * \param port Port number + */ + void get_address_and_port(const boost::program_options::variables_map& vm, + std::string &ip_address, std::string &port); + + /*! + * \brief Event handler that is invoked upon net_skeleton network events. + * + * Any change in behavior of RPC should happen from this point. + * \param nc net_skeleton connection + * \param ev Type of event + * \param ev_data Event data + */ + void ev_handler(struct ns_connection *nc, int ev, void *ev_data); + } +} + +#endif