From edfaa2e4843cb44f42ef4834a9bc109297450006 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 1 Nov 2014 21:35:26 +0530 Subject: [PATCH 01/45] Wrote a JSON RPC HTTP Server class using net_skeleton. Has issues due to non-OOP nature of net_skeleton --- external/CMakeLists.txt | 4 ++ external/net_skeleton | 1 + src/rpc/json_rpc_http_server.cpp | 75 ++++++++++++++++++++++++++++++++ src/rpc/json_rpc_http_server.h | 30 +++++++++++++ 4 files changed, 110 insertions(+) create mode 160000 external/net_skeleton create mode 100644 src/rpc/json_rpc_http_server.cpp create mode 100644 src/rpc/json_rpc_http_server.h diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 83e9e6dee..dcf56da4c 100755 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -173,3 +173,7 @@ ELSE() MESSAGE(FATAL_ERROR "${BoldRed}Found libunbound includes, but could not find libunbound library. Please make sure you have installed libunbound or libunbound-dev or the equivalent${ColourReset}") ENDIF() ENDIF() + +set(NET_SKELETON_SRCS net_skeleton/net_skeleton.c) +add_library(net_skeleton ${NET_SKELETON_SRCS}) + diff --git a/external/net_skeleton b/external/net_skeleton new file mode 160000 index 000000000..f7b138a4c --- /dev/null +++ b/external/net_skeleton @@ -0,0 +1 @@ +Subproject commit f7b138a4ce9299651fa30b5fdfeb68811317d3c0 diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp new file mode 100644 index 000000000..d66eed830 --- /dev/null +++ b/src/rpc/json_rpc_http_server.cpp @@ -0,0 +1,75 @@ +#include "json_rpc_http_server.h" + +#include + +namespace RPC +{ + Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port) + { + m_ip = ip; + m_port = port; + m_is_running = false; + m_method_count = 0; + } + + Json_rpc_http_server::~Json_rpc_http_server() + { + stop(); + } + + void Json_rpc_http_server::start() + { + m_is_running = true; + server_thread = new boost::thread(&Json_rpc_http_server::poll, this); + } + + void Json_rpc_http_server::poll() + { + ns_mgr_init(&mgr, NULL); + nc = ns_bind(&mgr, (m_ip + ":" + m_port).c_str(), std::bind(&Json_rpc_http_server::ev_handler, this)); + ns_set_protocol_http_websocket(nc); + while (m_is_running) { + ns_mgr_poll(&mgr, 1000); + } + } + + void Json_rpc_http_server::stop() + { + m_is_running = false; + server_thread->join(); + delete server_thread; + ns_mgr_free(&mgr); + } + + void Json_rpc_http_server::ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + struct http_message *hm = (struct http_message *) ev_data; + char buf[100]; + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + m_method_names, m_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; + } + } + + bool Json_rpc_http_server::add_handler(const std::string &method_name, + int (*hander)(char *buf, int len, struct ns_rpc_request *req)) + { + if (m_method_count == MAX_METHODS) + { + return false; + } + m_method_names[m_method_count] = new char[method_name.length() + 1]; + strcpy(m_method_names[m_method_count], method_name.c_str()); + m_handlers[m_method_count] = hander; + m_method_count++; + return true; + } +} diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h new file mode 100644 index 000000000..e9cb01deb --- /dev/null +++ b/src/rpc/json_rpc_http_server.h @@ -0,0 +1,30 @@ +#include "net_skeleton/net_skeleton.h" +#include +#include + +#define MAX_METHODS 100 + +namespace RPC +{ + class Json_rpc_http_server + { + struct ns_mgr mgr; + struct ns_connection *nc; + boost::thread *server_thread; + void ev_handler(struct ns_connection *nc, int ev, void *ev_data); + void poll(); + std::string m_ip; + std::string m_port; + bool m_is_running; + char *m_method_names[MAX_METHODS]; + ns_rpc_handler_t m_handlers[MAX_METHODS]; + int m_method_count; + public: + Json_rpc_http_server(const std::string &ip, const std::string &port); + ~Json_rpc_http_server(); + void start(); + void stop(); + bool add_handler(const std::string &method_name, + int (*hander)(char *buf, int len, struct ns_rpc_request *req)); + }; +} From ac662269cf0fd94f710972c2dc15acced8300a6d Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 2 Nov 2014 11:58:08 +0530 Subject: [PATCH 02/45] Fixed problem in Json_rpc_http_server due to C-like nature of net_skeleton --- src/rpc/json_rpc_http_server.cpp | 32 +++++++++++++------------------- src/rpc/json_rpc_http_server.h | 13 ++++++++----- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index d66eed830..fd57941f4 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -4,12 +4,16 @@ namespace RPC { - Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port) + Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port, + void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data), + const char **method_names, ns_rpc_handler_t *handlers) : + m_method_names(method_names), m_handlers(handlers) { m_ip = ip; m_port = port; m_is_running = false; m_method_count = 0; + m_ev_handler = ev_handler; } Json_rpc_http_server::~Json_rpc_http_server() @@ -17,17 +21,21 @@ namespace RPC stop(); } - void Json_rpc_http_server::start() + bool Json_rpc_http_server::start() { m_is_running = true; + ns_mgr_init(&mgr, NULL); + nc = ns_bind(&mgr, (m_ip + ":" + m_port).c_str(), m_ev_handler); + if (!nc) + { + return false; + } + ns_set_protocol_http_websocket(nc); server_thread = new boost::thread(&Json_rpc_http_server::poll, this); } void Json_rpc_http_server::poll() { - ns_mgr_init(&mgr, NULL); - nc = ns_bind(&mgr, (m_ip + ":" + m_port).c_str(), std::bind(&Json_rpc_http_server::ev_handler, this)); - ns_set_protocol_http_websocket(nc); while (m_is_running) { ns_mgr_poll(&mgr, 1000); } @@ -58,18 +66,4 @@ namespace RPC break; } } - - bool Json_rpc_http_server::add_handler(const std::string &method_name, - int (*hander)(char *buf, int len, struct ns_rpc_request *req)) - { - if (m_method_count == MAX_METHODS) - { - return false; - } - m_method_names[m_method_count] = new char[method_name.length() + 1]; - strcpy(m_method_names[m_method_count], method_name.c_str()); - m_handlers[m_method_count] = hander; - m_method_count++; - return true; - } } diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index e9cb01deb..bdac9aac1 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -2,7 +2,7 @@ #include #include -#define MAX_METHODS 100 +#define JSON_RPC_MAX_METHODS 100 namespace RPC { @@ -16,13 +16,16 @@ namespace RPC std::string m_ip; std::string m_port; bool m_is_running; - char *m_method_names[MAX_METHODS]; - ns_rpc_handler_t m_handlers[MAX_METHODS]; + const char **m_method_names; + ns_rpc_handler_t *m_handlers; int m_method_count; + void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); public: - Json_rpc_http_server(const std::string &ip, const std::string &port); + Json_rpc_http_server(const std::string &ip, const std::string &port, + void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data), + const char **method_names, ns_rpc_handler_t *handlers); ~Json_rpc_http_server(); - void start(); + bool start(); void stop(); bool add_handler(const std::string &method_name, int (*hander)(char *buf, int len, struct ns_rpc_request *req)); From fedf1eb473a8e73cf86e9848ac3062cbe3e9279b Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 2 Nov 2014 20:45:26 +0530 Subject: [PATCH 03/45] net_skeleton server made usable with daemon --- external/CMakeLists.txt | 1 + src/CMakeLists.txt | 2 +- src/daemon/daemon.cpp | 6 +++++- src/rpc/json_rpc_handlers.h | 33 ++++++++++++++++++++++++++++++++ src/rpc/json_rpc_http_server.cpp | 24 ++--------------------- src/rpc/json_rpc_http_server.h | 11 +---------- 6 files changed, 43 insertions(+), 34 deletions(-) create mode 100644 src/rpc/json_rpc_handlers.h diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index dcf56da4c..000c1edb2 100755 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -176,4 +176,5 @@ ENDIF() set(NET_SKELETON_SRCS net_skeleton/net_skeleton.c) add_library(net_skeleton ${NET_SKELETON_SRCS}) +set(NET_SKELETON_LIBRARY "${CMAKE_CURRENT_BINARY_DIR}/libnet_skeleton.a" PARENT_SCOPE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aec615eb2..aa12b2747 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -65,7 +65,7 @@ add_library(mnemonics ${MNEMONICS}) add_executable(daemon ${DAEMON} ${P2P} ${CRYPTONOTE_PROTOCOL}) add_executable(connectivity_tool ${CONN_TOOL}) add_executable(simpleminer ${MINER}) -target_link_libraries(daemon rpc cryptonote_core crypto common ${UNBOUND_LIBRARY} ${UPNP_LIBRARIES} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) +target_link_libraries(daemon rpc cryptonote_core crypto common ${UNBOUND_LIBRARY} ${UPNP_LIBRARIES} ${NET_SKELETON_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) target_link_libraries(connectivity_tool cryptonote_core crypto common ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) target_link_libraries(simpleminer cryptonote_core crypto common ${UNBOUND_LIBRARY} ${Boost_LIBRARIES} ${EXTRA_LIBRARIES}) add_library(rpc ${RPC}) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index da09acfd4..8e3d0d28a 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -50,6 +50,8 @@ 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/json_rpc_http_server.h" #if defined(WIN32) #include @@ -107,7 +109,9 @@ bool command_line_preprocessor(const boost::program_options::variables_map& vm) int main(int argc, char* argv[]) { - + RPC::Json_rpc_http_server server2("127.0.0.1", "9997", &RPC::ev_handler); + if(!server2.start()) std::cout << "Couldn't start net_skeleton server\n"; + string_tools::set_module_name_and_folder(argv[0]); #ifdef WIN32 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h new file mode 100644 index 000000000..062ed3867 --- /dev/null +++ b/src/rpc/json_rpc_handlers.h @@ -0,0 +1,33 @@ +#include "net_skeleton/net_skeleton.h" +#include + +namespace RPC +{ + int foo(char *buf, int len, struct ns_rpc_request *req) { + std::cout << "Method name: "; + std::cout << req->method->ptr << std::endl; + return 0; + } + + const char *method_names[] = {"foo", NULL}; + ns_rpc_handler_t handlers[] = {foo, NULL}; + + void ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + std::cout << "evenet\n\n"; + struct http_message *hm = (struct http_message *) ev_data; + char buf[100]; + 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/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index fd57941f4..65eba883c 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -5,14 +5,11 @@ namespace RPC { Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port, - void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data), - const char **method_names, ns_rpc_handler_t *handlers) : - m_method_names(method_names), m_handlers(handlers) + void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)) { m_ip = ip; m_port = port; m_is_running = false; - m_method_count = 0; m_ev_handler = ev_handler; } @@ -32,6 +29,7 @@ namespace RPC } ns_set_protocol_http_websocket(nc); server_thread = new boost::thread(&Json_rpc_http_server::poll, this); + return true; } void Json_rpc_http_server::poll() @@ -48,22 +46,4 @@ namespace RPC delete server_thread; ns_mgr_free(&mgr); } - - void Json_rpc_http_server::ev_handler(struct ns_connection *nc, int ev, void *ev_data) - { - struct http_message *hm = (struct http_message *) ev_data; - char buf[100]; - switch (ev) { - case NS_HTTP_REQUEST: - ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), - m_method_names, m_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/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index bdac9aac1..4860fccd6 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -2,8 +2,6 @@ #include #include -#define JSON_RPC_MAX_METHODS 100 - namespace RPC { class Json_rpc_http_server @@ -11,23 +9,16 @@ namespace RPC struct ns_mgr mgr; struct ns_connection *nc; boost::thread *server_thread; - void ev_handler(struct ns_connection *nc, int ev, void *ev_data); void poll(); std::string m_ip; std::string m_port; bool m_is_running; - const char **m_method_names; - ns_rpc_handler_t *m_handlers; - int m_method_count; void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); public: Json_rpc_http_server(const std::string &ip, const std::string &port, - void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data), - const char **method_names, ns_rpc_handler_t *handlers); + void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)); ~Json_rpc_http_server(); bool start(); void stop(); - bool add_handler(const std::string &method_name, - int (*hander)(char *buf, int len, struct ns_rpc_request *req)); }; } From 5eb1d32ef715f989b95ce72ae3576b557fb698d7 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 3 Nov 2014 00:38:16 +0530 Subject: [PATCH 04/45] Replaced old RPC server in daemon with new one. Yet to add RPC handlers. --- src/daemon/daemon.cpp | 21 +++++++-------- src/rpc/core_rpc_server.cpp | 1 - src/rpc/json_rpc_handlers.h | 54 +++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 8e3d0d28a..23356502b 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -143,7 +143,7 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_cmd_sett, arg_dns_checkpoints); cryptonote::core::init_options(desc_cmd_sett); - cryptonote::core_rpc_server::init_options(desc_cmd_sett); + RPC::init_options(desc_cmd_sett); nodetool::node_server >::init_options(desc_cmd_sett); cryptonote::miner::init_options(desc_cmd_sett); @@ -232,7 +232,7 @@ int main(int argc, char* argv[]) cprotocol , testnet_mode ? std::move(config::testnet::NETWORK_ID) : std::move(config::NETWORK_ID) }; - cryptonote::core_rpc_server rpc_server {ccore, p2psrv, testnet_mode}; + cprotocol.set_p2p_endpoint(&p2psrv); ccore.set_cryptonote_protocol(&cprotocol); daemon_cmmands_handler dch(p2psrv, testnet_mode); @@ -249,9 +249,11 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Protocol initialized OK"); LOG_PRINT_L0("Initializing core RPC server..."); - res = rpc_server.init(vm); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core RPC server."); - LOG_PRINT_GREEN("Core RPC server initialized OK on port: " << rpc_server.get_binded_port(), LOG_LEVEL_0); + RPC::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); + LOG_PRINT_GREEN("Core RPC server initialized on port: " << port, LOG_LEVEL_0); //initialize core here LOG_PRINT_L0("Initializing core..."); @@ -266,8 +268,8 @@ int main(int argc, char* argv[]) } LOG_PRINT_L0("Starting core RPC server..."); - res = rpc_server.run(2, false); - CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize core RPC server."); + res = rpc_server.start(); + CHECK_AND_ASSERT_MES(res, 1, "Failed to start core RPC server."); LOG_PRINT_L0("Core RPC server started ok"); tools::signal_handler::install([&dch, &p2psrv] { @@ -281,14 +283,11 @@ int main(int argc, char* argv[]) //stop components LOG_PRINT_L0("Stopping core rpc server..."); - rpc_server.send_stop_signal(); - rpc_server.timed_wait_server_stop(5000); + rpc_server.stop(); //deinitialize components LOG_PRINT_L0("Deinitializing core..."); ccore.deinit(); - LOG_PRINT_L0("Deinitializing RPC server ..."); - rpc_server.deinit(); LOG_PRINT_L0("Deinitializing protocol..."); cprotocol.deinit(); LOG_PRINT_L0("Deinitializing P2P..."); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 036cb64ff..4d089f2f7 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -97,7 +97,6 @@ namespace cryptonote const boost::program_options::variables_map& vm ) { - m_net_server.set_threads_prefix("RPC"); bool r = handle_command_line(vm); CHECK_AND_ASSERT_MES(r, false, "Failed to process command line in core_rpc_server"); return epee::http_server_impl_base::init(m_port, m_bind_ip); diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 062ed3867..536220930 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -1,8 +1,62 @@ #include "net_skeleton/net_skeleton.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 namespace RPC { + cryptonote::core *core; + nodetool::node_server > *p2p; + bool testnet; + + const command_line::arg_descriptor arg_rpc_bind_ip = { + "rpc-bind-ip", + "IP for RPC server", + "127.0.0.1" + }; + + const command_line::arg_descriptor arg_rpc_bind_port = { + "rpc-bind-port", + "Port for RPC server", + std::to_string(config::RPC_DEFAULT_PORT) + }; + + const command_line::arg_descriptor arg_testnet_rpc_bind_port = { + "testnet-rpc-bind-port", + "Port for testnet RPC server", + std::to_string(config::testnet::RPC_DEFAULT_PORT) + }; + + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p, + bool p_testnet) + { + core = p_core; + p2p = p_p2p; + testnet = p_testnet; + } + + 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); + } + + 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); + } + int foo(char *buf, int len, struct ns_rpc_request *req) { std::cout << "Method name: "; std::cout << req->method->ptr << std::endl; From eb355a4021a5dd7ef225325bbdde93e6805cbfdf Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Thu, 6 Nov 2014 21:35:53 +0530 Subject: [PATCH 05/45] Added getheight --- CMakeLists.txt | 1 + src/rpc/json_rpc_handlers.h | 49 ++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78e4b426f..859f1416b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ endif() # set(BSDI TRUE) include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") +include_directories(src/common) if(APPLE) include_directories(SYSTEM /usr/include/malloc) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 536220930..e0c4244c1 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -8,6 +8,8 @@ #include +#define CHECK_CORE_BUSY() if (check_core_busy()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_BUSY); } + namespace RPC { cryptonote::core *core; @@ -57,14 +59,49 @@ namespace RPC port = command_line::get_arg(vm, p2p_bind_arg); } - int foo(char *buf, int len, struct ns_rpc_request *req) { - std::cout << "Method name: "; - std::cout << req->method->ptr << std::endl; - return 0; + bool check_core_busy() + { + if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + { + return true; + } + return false; } - const char *method_names[] = {"foo", NULL}; - ns_rpc_handler_t handlers[] = {foo, NULL}; + void create_map_from_request() + { + + } + + int getheight(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + uint64_t height = core->get_current_blockchain_height(); + // TODO: uint64_t -> long not ok ! + return ns_rpc_create_reply(buf, len, req, "{s:i,s:s}", "height", height, "status", CORE_RPC_STATUS_OK); + } + + int getblocks(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (!core->find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); + } + req->params + } + + const char *method_names[] = { + "getheight", + "getblocks", + NULL + }; + + ns_rpc_handler_t handlers[] = { + getheight, + getblocks, + NULL + }; void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { From 6a975998398034baf0bbcc369aeecdd57005307f Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 8 Nov 2014 17:29:18 +0530 Subject: [PATCH 06/45] Use rapidjson for all JSON processing in handlers. Added handler for getblocks. --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/common/rapidjson | 1 + src/daemon/daemon.cpp | 3 -- src/rpc/json_rpc_handlers.h | 95 ++++++++++++++++++++++++++++++++----- 5 files changed, 87 insertions(+), 16 deletions(-) create mode 160000 src/common/rapidjson diff --git a/CMakeLists.txt b/CMakeLists.txt index 859f1416b..4949c7385 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ endif() # set(BSDI TRUE) include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") -include_directories(src/common) +include_directories(src/common/rapidjson/include/) if(APPLE) include_directories(SYSTEM /usr/include/malloc) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa12b2747..0dc3f141c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,7 @@ add_definitions(-DSTATICLIB) # miniupnp changed their static define add_definitions(-DMINIUPNP_STATICLIB) -file(GLOB_RECURSE COMMON common/*) +file(GLOB COMMON common/*) file(GLOB_RECURSE CRYPTO crypto/*) file(GLOB_RECURSE CRYPTONOTE_CORE cryptonote_core/*) file(GLOB_RECURSE CRYPTONOTE_PROTOCOL cryptonote_protocol/*) diff --git a/src/common/rapidjson b/src/common/rapidjson new file mode 160000 index 000000000..8307f0f4c --- /dev/null +++ b/src/common/rapidjson @@ -0,0 +1 @@ +Subproject commit 8307f0f4c9035bd63853bdf9e1b951e8c0e37b62 diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 23356502b..2229c9638 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -109,9 +109,6 @@ bool command_line_preprocessor(const boost::program_options::variables_map& vm) int main(int argc, char* argv[]) { - RPC::Json_rpc_http_server server2("127.0.0.1", "9997", &RPC::ev_handler); - if(!server2.start()) std::cout << "Couldn't start net_skeleton server\n"; - string_tools::set_module_name_and_folder(argv[0]); #ifdef WIN32 _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index e0c4244c1..5674aaa40 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -5,6 +5,11 @@ #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 @@ -68,27 +73,96 @@ namespace RPC return false; } - void create_map_from_request() - { - - } - int getheight(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); uint64_t height = core->get_current_blockchain_height(); - // TODO: uint64_t -> long not ok ! - return ns_rpc_create_reply(buf, len, req, "{s:i,s:s}", "height", height, "status", CORE_RPC_STATUS_OK); + rapidjson::Document json; + json.SetObject(); + json.AddMember("height", height, json.GetAllocator()); + json.AddMember("status", CORE_RPC_STATUS_OK, json.GetAllocator()); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + json.Accept(writer); + std::string response = buffer.GetString(); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); } int getblocks(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); - if (!core->find_blockchain_supplement(req.start_height, req.block_ids, bs, res.current_height, res.start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->message[0].ptr, len); + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + } + + if (!request_json.HasMember("start_height") || !request_json["start_height"].IsUint64()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect start_height"); + } + if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect block_ids"); + } + + uint64_t start_height = request_json["start_height"].GetUint(); + std::list block_ids; + int ii = 0; + for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); it != request_json["block_ids"].End(); it++, ii++) + { + crypto::hash hash; + strcpy(hash.data, it->GetString()); + block_ids.push_back(hash); + } + + std::list > > bs; + uint64_t result_current_height = 0; + uint64_t result_start_height = 0; + if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, + result_current_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); } - req->params + + rapidjson::Document response_json; + response_json.SetObject(); + rapidjson::Value block_json(rapidjson::kArrayType); + rapidjson::Document::AllocatorType& allocator = response_json.GetAllocator(); + BOOST_FOREACH(auto &b, bs) + { + rapidjson::Value this_block(rapidjson::kObjectType); + std::string blob = block_to_blob(b.first); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(blob.c_str(), blob.length()); + this_block.AddMember("block", string_value, allocator); + rapidjson::Value txs_blocks(rapidjson::kArrayType); + BOOST_FOREACH(auto &t, b.second) + { + blob = cryptonote::tx_to_blob(t); + string_value.SetString(blob.c_str(), blob.length()); + txs_blocks.PushBack(string_value.Move(), allocator); + } + this_block.AddMember("txs", txs_blocks, allocator); + block_json.PushBack(this_block, allocator); + } + + response_json.AddMember("start_height", result_start_height, allocator); + response_json.AddMember("current_height", result_current_height, allocator); + response_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); + response_json.AddMember("blocks", block_json, allocator); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + std::string response = buffer.GetString(); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); } const char *method_names[] = { @@ -105,9 +179,8 @@ namespace RPC void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { - std::cout << "evenet\n\n"; struct http_message *hm = (struct http_message *) ev_data; - char buf[100]; + char buf[1000]; switch (ev) { case NS_HTTP_REQUEST: ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), From bd16e82e3cc4bda612591aabc6ef5b2b2238b370 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 8 Nov 2014 19:05:05 +0530 Subject: [PATCH 07/45] Added gettransactions. Check for types inside arrays --- src/rpc/json_rpc_handlers.h | 101 ++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 5674aaa40..94b9911e5 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -95,7 +95,8 @@ namespace RPC CHECK_CORE_BUSY(); rapidjson::Document request_json; char request_buf[1000]; - strncpy(request_buf, req->message[0].ptr, len); + strncpy(request_buf, req->message[0].ptr, req->message[0].len); + request_buf[req->message[0].len] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); @@ -113,9 +114,14 @@ namespace RPC uint64_t start_height = request_json["start_height"].GetUint(); std::list block_ids; int ii = 0; - for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); it != request_json["block_ids"].End(); it++, ii++) + for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); + it != request_json["block_ids"].End(); it++, ii++) { crypto::hash hash; + if (!it->IsString()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Wrong type in block_ids"); + } strcpy(hash.data, it->GetString()); block_ids.push_back(hash); } @@ -124,7 +130,7 @@ namespace RPC uint64_t result_current_height = 0; uint64_t result_start_height = 0; if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, - result_current_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); } @@ -132,7 +138,7 @@ namespace RPC rapidjson::Document response_json; response_json.SetObject(); rapidjson::Value block_json(rapidjson::kArrayType); - rapidjson::Document::AllocatorType& allocator = response_json.GetAllocator(); + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); BOOST_FOREACH(auto &b, bs) { rapidjson::Value this_block(rapidjson::kObjectType); @@ -155,6 +161,91 @@ namespace RPC response_json.AddMember("current_height", result_current_height, allocator); response_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); response_json.AddMember("blocks", block_json, allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + std::string response = buffer.GetString(); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int gettransactions(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->message[0].ptr, req->message[0].len); + request_buf[req->message[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + } + + if (!request_json.HasMember("txs_hashes") || !request_json["txs_hashes"].IsArray()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect txs_hashes"); + } + + std::list txs_hashes; + for (rapidjson::Value::ConstValueIterator it = request_json["txs_hashes"].Begin(); + it != request_json["txs_hashes"].End(); it++) + { + if (!it->IsString()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Wrong type in txs_hashes"); + } + txs_hashes.push_back(std::string(it->GetString())); + } + std::vector vh; + BOOST_FOREACH(const auto& tx_hex_str, txs_hashes) + { + cryptonote::blobdata b; + if (!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed to parse hex representation of transaction hash"); + } + if (b.size() != sizeof(crypto::hash)) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, size of data mismatch"); + } + vh.push_back(*reinterpret_cast(b.data())); + } + std::list missed_txs; + std::list txs; + bool r = core->get_transactions(vh, txs, missed_txs); + if (!r) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); + } + + rapidjson::Document response_json; + response_json.SetObject(); + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + + rapidjson::Value txs_as_hex(rapidjson::kArrayType); + BOOST_FOREACH(auto &tx, txs) + { + cryptonote::blobdata blob = t_serializable_object_to_blob(tx); + std::string string_blob = string_tools::buff_to_hex_nodelimer(blob); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(string_blob.c_str(), string_blob.length()); + txs_as_hex.PushBack(string_value, allocator); + } + response_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); + + rapidjson::Value missed_tx(rapidjson::kArrayType); + BOOST_FOREACH(const auto &miss_tx, missed_txs) + { + std::string string_blob = string_tools::pod_to_hex(miss_tx); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(string_blob.c_str(), string_blob.length()); + missed_tx.PushBack(string_value, allocator); + } + response_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); + rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); response_json.Accept(writer); @@ -168,12 +259,14 @@ namespace RPC const char *method_names[] = { "getheight", "getblocks", + "gettransactions", NULL }; ns_rpc_handler_t handlers[] = { getheight, getblocks, + gettransactions, NULL }; From ffe1737fef4998a6eed8f82d70465096a5b851ac Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 8 Nov 2014 21:14:50 +0530 Subject: [PATCH 08/45] Added startmining. Check for block ID length in getblocks. --- src/rpc/json_rpc_handlers.h | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 94b9911e5..a2923638f 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -10,6 +10,7 @@ #include "rapidjson/stringbuffer.h" #include #include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash-ops.h" #include @@ -122,6 +123,10 @@ namespace RPC { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Wrong type in block_ids"); } + if (strlen(it->GetString()) > crypto::HASH_SIZE) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Block ID exceeds length."); + } strcpy(hash.data, it->GetString()); block_ids.push_back(hash); } @@ -256,10 +261,50 @@ namespace RPC return response.length(); } + int startmining(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->message[0].ptr, req->message[0].len); + request_buf[req->message[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + } + + if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect miner_address"); + } + if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect threads_count"); + } + + std::string miner_address = request_json["miner_address"].GetString(); + uint64_t threads_count = request_json["threads_count"].GetUint(); + + cryptonote::account_public_address adr; + if (!cryptonote::get_account_address_from_str(adr, testnet, miner_address)) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, wrong address"); + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, mining not started"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + const char *method_names[] = { "getheight", "getblocks", "gettransactions", + "startmining", NULL }; @@ -267,6 +312,7 @@ namespace RPC getheight, getblocks, gettransactions, + startmining, NULL }; From a273353b322ac0a2abe95a0877c329271d010187 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 8 Nov 2014 21:41:43 +0530 Subject: [PATCH 09/45] Added stopmining. --- src/rpc/json_rpc_handlers.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index a2923638f..e08bd6613 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -300,11 +300,22 @@ namespace RPC return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + int stopmining(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (!core->get_miner().stop()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, mining not stopped"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + const char *method_names[] = { "getheight", "getblocks", "gettransactions", "startmining", + "stopmining", NULL }; @@ -313,6 +324,7 @@ namespace RPC getblocks, gettransactions, startmining, + stopmining, NULL }; From 0381c3a5a49aa68fb658f61b35abef0faa22bf58 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 9 Nov 2014 15:40:58 +0530 Subject: [PATCH 10/45] Added miningstatus, getblockcount. Puts method name and RPC id in response JSON. --- src/rpc/json_rpc_handlers.h | 97 +++++++++++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 16 deletions(-) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index e08bd6613..de2b4c5dc 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -74,18 +74,37 @@ namespace RPC return false; } + void construct_response_string(struct ns_rpc_request *req, rapidjson::Document &response_json, + std::string &response) + { + rapidjson::Value string_value(rapidjson::kStringType); + if (!req->id) + { + 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()); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + response = buffer.GetString(); + } + int getheight(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); uint64_t height = core->get_current_blockchain_height(); - rapidjson::Document json; - json.SetObject(); - json.AddMember("height", height, json.GetAllocator()); - json.AddMember("status", CORE_RPC_STATUS_OK, json.GetAllocator()); - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - json.Accept(writer); - std::string response = buffer.GetString(); + rapidjson::Document response_json; + response_json.SetObject(); + response_json.AddMember("height", height, response_json.GetAllocator()); + response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + std::string response; + construct_response_string(req, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); return response.length(); @@ -167,10 +186,8 @@ namespace RPC response_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); response_json.AddMember("blocks", block_json, allocator); - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - response_json.Accept(writer); - std::string response = buffer.GetString(); + std::string response; + construct_response_string(req, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -251,10 +268,8 @@ namespace RPC } response_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - response_json.Accept(writer); - std::string response = buffer.GetString(); + std::string response; + construct_response_string(req, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -310,12 +325,60 @@ namespace RPC return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + int miningstatus(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document response_json; + response_json.SetObject(); + bool active = core->get_miner().is_mining(); + response_json.AddMember("active", active, response_json.GetAllocator()); + + if (active) + { + uint64_t speed = core->get_miner().get_speed(); + uint64_t threads_count = core->get_miner().get_threads_count(); + const cryptonote::account_public_address &mining_address = core->get_miner().get_mining_address(); + std::string address = cryptonote::get_account_address_as_str(testnet, mining_address); + response_json.AddMember("speed", speed, response_json.GetAllocator()); + response_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); + rapidjson::Value string_address(rapidjson::kStringType); + string_address.SetString(address.c_str(), address.length()); + response_json.AddMember("address", string_address, response_json.GetAllocator()); + } + response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblockcount(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document response_json; + response_json.SetObject(); + response_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); + response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + const char *method_names[] = { "getheight", "getblocks", "gettransactions", "startmining", "stopmining", + "miningstatus", + "getblockcount", NULL }; @@ -325,6 +388,8 @@ namespace RPC gettransactions, startmining, stopmining, + miningstatus, + getblockcount, NULL }; From 5470c0c0702c00364fac558fc0885bd1e03ee0e5 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 9 Nov 2014 16:25:05 +0530 Subject: [PATCH 11/45] Added getblockhash. --- src/rpc/json_rpc_handlers.h | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index de2b4c5dc..fc03bb2ad 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -78,7 +78,7 @@ namespace RPC std::string &response) { rapidjson::Value string_value(rapidjson::kStringType); - if (!req->id) + if (req->id != NULL) { string_value.SetString(req->id[0].ptr, req->id[0].len); } @@ -371,6 +371,42 @@ namespace RPC return response.length(); } + int getblockhash(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->message[0].ptr, req->message[0].len); + request_buf[req->message[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + } + + if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect height"); + } + uint64_t height = request_json["height"].GetUint(); + if (core->get_current_blockchain_height() <= height) + { + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Height specified is too big."); + } + std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height)); + rapidjson::Document response_json; + response_json.SetObject(); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(hash.c_str(), hash.length()); + response_json.AddMember("hash", string_value, response_json.GetAllocator()); + response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + const char *method_names[] = { "getheight", "getblocks", @@ -379,6 +415,7 @@ namespace RPC "stopmining", "miningstatus", "getblockcount", + "getblockhash", NULL }; @@ -390,6 +427,7 @@ namespace RPC stopmining, miningstatus, getblockcount, + getblockhash, NULL }; From 43286b46e585a575e5b25781f09d6ef8b7563e4b Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 10 Nov 2014 02:14:09 +0530 Subject: [PATCH 12/45] More RPC handlers, more JSON RPC 2.0 compliant. --- src/rpc/json_rpc_handlers.h | 299 ++++++++++++++++++++++++------- src/rpc/json_rpc_http_server.cpp | 5 + src/rpc/json_rpc_http_server.h | 10 ++ 3 files changed, 254 insertions(+), 60 deletions(-) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index fc03bb2ad..bbb812084 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -1,4 +1,8 @@ +#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" @@ -74,9 +78,10 @@ namespace RPC return false; } - void construct_response_string(struct ns_rpc_request *req, rapidjson::Document &response_json, - std::string &response) + 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 (req->id != NULL) { @@ -89,22 +94,45 @@ namespace RPC 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); response = buffer.GetString(); } + //------------------------------------------------------------------------------------------------------------------------------ + // equivalent of strstr, but with arbitrary bytes (ie, NULs) + // This does not differentiate between "not found" and "found at offset 0" + uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) + { + const void *buf = start_buff; + const void *end = (const char*)buf + buflen; + if (patlen > buflen || patlen == 0) + { + return 0; + } + while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) + { + if (memcmp(buf,pat,patlen) == 0) + return (const char*)buf - (const char*)start_buff; + buf = (const char*)buf + 1; + buflen = (const char*)end - (const char*)buf; + } + return 0; + } + int getheight(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); uint64_t height = core->get_current_blockchain_height(); rapidjson::Document response_json; - response_json.SetObject(); - response_json.AddMember("height", height, response_json.GetAllocator()); - response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); return response.length(); @@ -113,22 +141,30 @@ namespace RPC int getblocks(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } rapidjson::Document request_json; char request_buf[1000]; - strncpy(request_buf, req->message[0].ptr, req->message[0].len); - request_buf[req->message[0].len] = '\0'; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); } if (!request_json.HasMember("start_height") || !request_json["start_height"].IsUint64()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect start_height"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect start_height", "{}"); } if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect block_ids"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect block_ids", "{}"); } uint64_t start_height = request_json["start_height"].GetUint(); @@ -140,11 +176,13 @@ namespace RPC crypto::hash hash; if (!it->IsString()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Wrong type in block_ids"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Wrong type in block_ids", "{}"); } if (strlen(it->GetString()) > crypto::HASH_SIZE) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Block ID exceeds length."); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Block ID exceeds length.", "{}"); } strcpy(hash.data, it->GetString()); block_ids.push_back(hash); @@ -156,11 +194,13 @@ namespace RPC if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed", "{}"); } rapidjson::Document response_json; - response_json.SetObject(); + rapidjson::Value result_json; + result_json.SetObject(); rapidjson::Value block_json(rapidjson::kArrayType); rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); BOOST_FOREACH(auto &b, bs) @@ -181,13 +221,13 @@ namespace RPC block_json.PushBack(this_block, allocator); } - response_json.AddMember("start_height", result_start_height, allocator); - response_json.AddMember("current_height", result_current_height, allocator); - response_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); - response_json.AddMember("blocks", block_json, allocator); + result_json.AddMember("start_height", result_start_height, allocator); + result_json.AddMember("current_height", result_current_height, allocator); + result_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); + result_json.AddMember("blocks", block_json, allocator); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -197,18 +237,25 @@ namespace RPC int gettransactions(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } rapidjson::Document request_json; char request_buf[1000]; - strncpy(request_buf, req->message[0].ptr, req->message[0].len); - request_buf[req->message[0].len] = '\0'; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); } if (!request_json.HasMember("txs_hashes") || !request_json["txs_hashes"].IsArray()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect txs_hashes"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect txs_hashes", "{}"); } std::list txs_hashes; @@ -217,7 +264,8 @@ namespace RPC { if (!it->IsString()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Wrong type in txs_hashes"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Wrong type in txs_hashes", "{}"); } txs_hashes.push_back(std::string(it->GetString())); } @@ -227,11 +275,13 @@ namespace RPC cryptonote::blobdata b; if (!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed to parse hex representation of transaction hash"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed to parse hex representation of transaction hash", "{}"); } if (b.size() != sizeof(crypto::hash)) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, size of data mismatch"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed, size of data mismatch", "{}"); } vh.push_back(*reinterpret_cast(b.data())); } @@ -240,11 +290,13 @@ namespace RPC bool r = core->get_transactions(vh, txs, missed_txs); if (!r) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed", "{}"); } rapidjson::Document response_json; - response_json.SetObject(); + rapidjson::Value result_json; + result_json.SetObject(); rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); rapidjson::Value txs_as_hex(rapidjson::kArrayType); @@ -256,7 +308,7 @@ namespace RPC string_value.SetString(string_blob.c_str(), string_blob.length()); txs_as_hex.PushBack(string_value, allocator); } - response_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); + result_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); rapidjson::Value missed_tx(rapidjson::kArrayType); BOOST_FOREACH(const auto &miss_tx, missed_txs) @@ -266,10 +318,10 @@ namespace RPC string_value.SetString(string_blob.c_str(), string_blob.length()); missed_tx.PushBack(string_value, allocator); } - response_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); + result_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -279,22 +331,30 @@ namespace RPC int startmining(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } rapidjson::Document request_json; char request_buf[1000]; - strncpy(request_buf, req->message[0].ptr, req->message[0].len); - request_buf[req->message[0].len] = '\0'; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); } if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect miner_address"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect miner_address", "{}"); } if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect threads_count"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect threads_count", "{}"); } std::string miner_address = request_json["miner_address"].GetString(); @@ -303,14 +363,16 @@ namespace RPC cryptonote::account_public_address adr; if (!cryptonote::get_account_address_from_str(adr, testnet, miner_address)) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, wrong address"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed, wrong address", "{}"); } boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, mining not started"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Failed, mining not started", "{}"); } return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } @@ -320,7 +382,8 @@ namespace RPC CHECK_CORE_BUSY(); if (!core->get_miner().stop()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Failed, mining not stopped"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Failed, mining not stopped", "{}"); } return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } @@ -329,9 +392,10 @@ namespace RPC { CHECK_CORE_BUSY(); rapidjson::Document response_json; - response_json.SetObject(); + rapidjson::Value result_json; + result_json.SetObject(); bool active = core->get_miner().is_mining(); - response_json.AddMember("active", active, response_json.GetAllocator()); + result_json.AddMember("active", active, response_json.GetAllocator()); if (active) { @@ -339,16 +403,16 @@ namespace RPC uint64_t threads_count = core->get_miner().get_threads_count(); const cryptonote::account_public_address &mining_address = core->get_miner().get_mining_address(); std::string address = cryptonote::get_account_address_as_str(testnet, mining_address); - response_json.AddMember("speed", speed, response_json.GetAllocator()); - response_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); + result_json.AddMember("speed", speed, response_json.GetAllocator()); + result_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); rapidjson::Value string_address(rapidjson::kStringType); string_address.SetString(address.c_str(), address.length()); - response_json.AddMember("address", string_address, response_json.GetAllocator()); + result_json.AddMember("address", string_address, response_json.GetAllocator()); } - response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -359,12 +423,13 @@ namespace RPC { CHECK_CORE_BUSY(); rapidjson::Document response_json; - response_json.SetObject(); - response_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); - response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); @@ -374,34 +439,144 @@ namespace RPC int getblockhash(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } rapidjson::Document request_json; char request_buf[1000]; - strncpy(request_buf, req->message[0].ptr, req->message[0].len); - request_buf[req->message[0].len] = '\0'; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; if (request_json.Parse(request_buf).HasParseError()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Invalid JSON passed"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); } if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Incorrect height"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect height", "{}"); } uint64_t height = request_json["height"].GetUint(); if (core->get_current_blockchain_height() <= height) { - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", "Height specified is too big."); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Height specified is too big.", "{}"); } std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height)); rapidjson::Document response_json; - response_json.SetObject(); + rapidjson::Value result_json; + result_json.SetObject(); rapidjson::Value string_value(rapidjson::kStringType); string_value.SetString(hash.c_str(), hash.length()); - response_json.AddMember("hash", string_value, response_json.GetAllocator()); - response_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + result_json.AddMember("hash", string_value, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); std::string response; - construct_response_string(req, response_json, response); + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect reserve_size", "{}"); + } + if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect wallet_address", "{}"); + } + + uint64_t reserve_size = request_json["reserve_size"].GetUint(); + std::string wallet_address = request_json["wallet_address"].GetString(); + + if (reserve_size > 255) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "To big reserved size, maximum 255", "{}"); + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + if (!wallet_address.size() || !cryptonote::get_account_address_from_str(acc, testnet, wallet_address)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed to parse wallet address", "{}"); + } + + cryptonote::block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(reserve_size, 0); + + uint64_t difficulty, height, reserved_offset; + if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) + { + LOG_ERROR("Failed to create block template"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed to create block template", "{}"); + } + + cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); + if (tx_pub_key == cryptonote::null_pkey) + { + LOG_ERROR("Failed to tx pub key in coinbase extra"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed to create block template", "{}"); + } + + reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if (!reserved_offset) + { + LOG_ERROR("Failed to find tx pub key in blockblob"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error: failed to create block template", "{}"); + } + + reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (reserved_offset + reserve_size > block_blob.size()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error: failed to create block template", "{}"); + } + cryptonote::blobdata blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("difficulty", difficulty, response_json.GetAllocator()); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("reserved_offset", reserved_offset, response_json.GetAllocator()); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(blocktemplate_blob.c_str(), blocktemplate_blob.length()); + result_json.AddMember("blocktemplate_blob", string_value, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); return response.length(); @@ -416,6 +591,7 @@ namespace RPC "miningstatus", "getblockcount", "getblockhash", + "getblocktemplate", NULL }; @@ -428,6 +604,7 @@ namespace RPC miningstatus, getblockcount, getblockhash, + getblocktemplate, NULL }; @@ -449,3 +626,5 @@ namespace RPC } } } + +#endif diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index 65eba883c..c34c11d3b 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -4,6 +4,11 @@ namespace RPC { + int Json_rpc_http_server::parse_error = -32700; + int Json_rpc_http_server::invalid_request = -32600; + int Json_rpc_http_server::invalid_params = -32602; + int Json_rpc_http_server::internal_error = -32603; + Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)) { diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index 4860fccd6..c3a9ffe59 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -1,3 +1,6 @@ +#ifndef JSON_RPC_HTTP_SERVER_H +#define JSON_RPC_HTTP_SERVER_H + #include "net_skeleton/net_skeleton.h" #include #include @@ -20,5 +23,12 @@ namespace RPC ~Json_rpc_http_server(); bool start(); void stop(); + + static int parse_error; + static int invalid_request; + static int invalid_params; + static int internal_error; }; } + +#endif From f3ac82b28e64e0971ef5e5bbd83f7d15e06b40ff Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 10 Nov 2014 20:03:16 +0530 Subject: [PATCH 13/45] Added submitblock and commented Json_rpc_http_server. --- src/rpc/json_rpc_handlers.h | 56 +++++++++++++++++++++++++++++ src/rpc/json_rpc_http_server.cpp | 30 ++++++++++++++++ src/rpc/json_rpc_http_server.h | 60 ++++++++++++++++++++++++++------ 3 files changed, 135 insertions(+), 11 deletions(-) diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index bbb812084..5975a335e 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -582,6 +582,60 @@ namespace RPC return response.length(); } + int submitblock(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + if (!request_json.HasMember("block") || !request_json["block"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect block", "{}"); + } + + std::string string_blockblob = request_json["block"].GetString(); + cryptonote::blobdata blockblob; + if (!string_tools::parse_hexstr_to_binbuff(string_blockblob, blockblob)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Incorrect block", "{}"); + } + // Fixing of high orphan issue for most pools + cryptonote::block b = AUTO_VAL_INIT(b); + if (!parse_and_validate_block_from_blob(blockblob, b)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Wrong block blob", "{}"); + } + // Fix from Boolberry neglects to check block + // size, do that with the function below + if (!core->check_incoming_block_size(blockblob)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Block blob size is too big, rejecting block", "{}"); + } + if (!core->handle_block_found(b)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Block not accepted.", "{}"); + } + + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + const char *method_names[] = { "getheight", "getblocks", @@ -592,6 +646,7 @@ namespace RPC "getblockcount", "getblockhash", "getblocktemplate", + "submitblock", NULL }; @@ -605,6 +660,7 @@ namespace RPC getblockcount, getblockhash, getblocktemplate, + submitblock, NULL }; diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index c34c11d3b..a27dd49d4 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -1,7 +1,16 @@ +/*! + * \file json_rpc_http_server.h + * \brief Header for Json_rpc_http_server class + */ + #include "json_rpc_http_server.h" #include +/*! + * \namespace RPC + * \brief RPC related utilities + */ namespace RPC { int Json_rpc_http_server::parse_error = -32700; @@ -9,6 +18,12 @@ namespace RPC int Json_rpc_http_server::invalid_params = -32602; int Json_rpc_http_server::internal_error = -32603; + /** + * \brief Constructor + * \param ip IP address to bind + * \param port Port number to bind + * \param ev_handler Event handler function pointer + */ Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)) { @@ -18,11 +33,18 @@ namespace RPC m_ev_handler = ev_handler; } + /** + * \brief Destructor + */ Json_rpc_http_server::~Json_rpc_http_server() { stop(); } + /*! + * \brief Starts the server + * \return True if start was successful + */ bool Json_rpc_http_server::start() { m_is_running = true; @@ -33,17 +55,25 @@ namespace RPC return false; } ns_set_protocol_http_websocket(nc); + // Start a new thread so it doesn't block. server_thread = new boost::thread(&Json_rpc_http_server::poll, this); return true; } + /*! + * \brief Repeated loops processing requests if any. + */ void Json_rpc_http_server::poll() { + // Loop until the server is running and poll. while (m_is_running) { ns_mgr_poll(&mgr, 1000); } } + /*! + * \brief Stops the server + */ void Json_rpc_http_server::stop() { m_is_running = false; diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index c3a9ffe59..b69a2f574 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -1,3 +1,8 @@ +/*! + * \file json_rpc_http_server.h + * \brief Header for Json_rpc_http_server class + */ + #ifndef JSON_RPC_HTTP_SERVER_H #define JSON_RPC_HTTP_SERVER_H @@ -5,29 +10,62 @@ #include #include +/*! + * \namespace RPC + * \brief RPC related utilities + */ namespace RPC { + /*! + * \class Json_rpc_http_server + * \brief JSON HTTP RPC Server implemented with net_skeleton (aka fossa). + * + * Provides a higher level interface to C-like net_skeleton. + */ class Json_rpc_http_server { - struct ns_mgr mgr; - struct ns_connection *nc; - boost::thread *server_thread; + struct ns_mgr mgr; /*!< Connection manager */ + struct ns_connection *nc; /*!< Connection pointer */ + boost::thread *server_thread; /*!< Server runs on this thread */ + /*! + * \brief Repeated loops processing requests if any. + */ void poll(); - std::string m_ip; - std::string m_port; - bool m_is_running; - void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); + std::string m_ip; /*!< IP address where its listening */ + std::string m_port; /*!< Port where its listening */ + bool m_is_running; /*!< Whether the server is currently running */ + void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); /*!< Server event handler function pointer */ public: + + /** + * \brief Constructor + * \param ip IP address to bind + * \param port Port number to bind + * \param ev_handler Event handler function pointer + */ Json_rpc_http_server(const std::string &ip, const std::string &port, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)); + + /** + * \brief Destructor + */ ~Json_rpc_http_server(); + + /*! + * \brief Starts the server + * \return True if start was successful + */ bool start(); + + /*! + * \brief Stops the server + */ void stop(); - static int parse_error; - static int invalid_request; - static int invalid_params; - static int internal_error; + static int parse_error; /*!< JSON request passed couldn't be parsed */ + static int invalid_request; /*!< JSON request invalid */ + static int invalid_params; /*!< JSON request had faulty/missing params */ + static int internal_error; /*!< JSON request resulted in an internal error */ }; } From 7910a3941f7c57ffd671770b62fe411e9a078d8e Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 10 Nov 2014 22:35:50 +0530 Subject: [PATCH 14/45] Split RPC handler definitions into separate .cpp file. --- src/rpc/json_rpc_handlers.cpp | 722 ++++++++++++++++++++++++++++++++++ src/rpc/json_rpc_handlers.h | 659 +------------------------------ 2 files changed, 726 insertions(+), 655 deletions(-) create mode 100644 src/rpc/json_rpc_handlers.cpp diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp new file mode 100644 index 000000000..9a84325a6 --- /dev/null +++ b/src/rpc/json_rpc_handlers.cpp @@ -0,0 +1,722 @@ +#include "json_rpc_handlers.h" + +#define CHECK_CORE_BUSY() if (check_core_busy()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_BUSY); } + +namespace +{ + cryptonote::core *core; + nodetool::node_server > *p2p; + bool testnet; + + const command_line::arg_descriptor arg_rpc_bind_ip = { + "rpc-bind-ip", + "IP for RPC server", + "127.0.0.1" + }; + + const command_line::arg_descriptor arg_rpc_bind_port = { + "rpc-bind-port", + "Port for RPC server", + std::to_string(config::RPC_DEFAULT_PORT) + }; + + const command_line::arg_descriptor arg_testnet_rpc_bind_port = { + "testnet-rpc-bind-port", + "Port for testnet RPC server", + std::to_string(config::testnet::RPC_DEFAULT_PORT) + }; + + bool check_core_busy() + { + if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + { + return true; + } + return false; + } + + 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 (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); + response = buffer.GetString(); + } + + //------------------------------------------------------------------------------------------------------------------------------ + // equivalent of strstr, but with arbitrary bytes (ie, NULs) + // This does not differentiate between "not found" and "found at offset 0" + uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) + { + const void *buf = start_buff; + const void *end = (const char*)buf + buflen; + if (patlen > buflen || patlen == 0) + { + return 0; + } + while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) + { + if (memcmp(buf,pat,patlen) == 0) + return (const char*)buf - (const char*)start_buff; + buf = (const char*)buf + 1; + buflen = (const char*)end - (const char*)buf; + } + return 0; + } +} + +namespace RPC +{ + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p, + bool p_testnet) + { + core = p_core; + p2p = p_p2p; + testnet = p_testnet; + } + + 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); + } + + 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); + } + + bool check_core_busy() + { + if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + { + return true; + } + return false; + } + + 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 (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); + response = buffer.GetString(); + } + + //------------------------------------------------------------------------------------------------------------------------------ + // equivalent of strstr, but with arbitrary bytes (ie, NULs) + // This does not differentiate between "not found" and "found at offset 0" + uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) + { + const void *buf = start_buff; + const void *end = (const char*)buf + buflen; + if (patlen > buflen || patlen == 0) + { + return 0; + } + while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) + { + if (memcmp(buf,pat,patlen) == 0) + return (const char*)buf - (const char*)start_buff; + buf = (const char*)buf + 1; + buflen = (const char*)end - (const char*)buf; + } + return 0; + } + + int getheight(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + uint64_t height = core->get_current_blockchain_height(); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblocks(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("start_height") || !request_json["start_height"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect start_height", "{}"); + } + if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect block_ids", "{}"); + } + + uint64_t start_height = request_json["start_height"].GetUint(); + std::list block_ids; + int ii = 0; + for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); + it != request_json["block_ids"].End(); it++, ii++) + { + crypto::hash hash; + if (!it->IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Wrong type in block_ids", "{}"); + } + if (strlen(it->GetString()) > crypto::HASH_SIZE) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Block ID exceeds length.", "{}"); + } + strcpy(hash.data, it->GetString()); + block_ids.push_back(hash); + } + + std::list > > bs; + uint64_t result_current_height = 0; + uint64_t result_start_height = 0; + if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, + result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + rapidjson::Value block_json(rapidjson::kArrayType); + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + BOOST_FOREACH(auto &b, bs) + { + rapidjson::Value this_block(rapidjson::kObjectType); + std::string blob = block_to_blob(b.first); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(blob.c_str(), blob.length()); + this_block.AddMember("block", string_value, allocator); + rapidjson::Value txs_blocks(rapidjson::kArrayType); + BOOST_FOREACH(auto &t, b.second) + { + blob = cryptonote::tx_to_blob(t); + string_value.SetString(blob.c_str(), blob.length()); + txs_blocks.PushBack(string_value.Move(), allocator); + } + this_block.AddMember("txs", txs_blocks, allocator); + block_json.PushBack(this_block, allocator); + } + + result_json.AddMember("start_height", result_start_height, allocator); + result_json.AddMember("current_height", result_current_height, allocator); + result_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); + result_json.AddMember("blocks", block_json, allocator); + + std::string response; + construct_response_string(req, result_json, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int gettransactions(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("txs_hashes") || !request_json["txs_hashes"].IsArray()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect txs_hashes", "{}"); + } + + std::list txs_hashes; + for (rapidjson::Value::ConstValueIterator it = request_json["txs_hashes"].Begin(); + it != request_json["txs_hashes"].End(); it++) + { + if (!it->IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Wrong type in txs_hashes", "{}"); + } + txs_hashes.push_back(std::string(it->GetString())); + } + std::vector vh; + BOOST_FOREACH(const auto& tx_hex_str, txs_hashes) + { + cryptonote::blobdata b; + if (!epee::string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed to parse hex representation of transaction hash", "{}"); + } + if (b.size() != sizeof(crypto::hash)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed, size of data mismatch", "{}"); + } + vh.push_back(*reinterpret_cast(b.data())); + } + std::list missed_txs; + std::list txs; + bool r = core->get_transactions(vh, txs, missed_txs); + if (!r) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + + rapidjson::Value txs_as_hex(rapidjson::kArrayType); + BOOST_FOREACH(auto &tx, txs) + { + cryptonote::blobdata blob = t_serializable_object_to_blob(tx); + std::string string_blob = epee::string_tools::buff_to_hex_nodelimer(blob); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(string_blob.c_str(), string_blob.length()); + txs_as_hex.PushBack(string_value, allocator); + } + result_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); + + rapidjson::Value missed_tx(rapidjson::kArrayType); + BOOST_FOREACH(const auto &miss_tx, missed_txs) + { + std::string string_blob = epee::string_tools::pod_to_hex(miss_tx); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(string_blob.c_str(), string_blob.length()); + missed_tx.PushBack(string_value, allocator); + } + result_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int startmining(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect miner_address", "{}"); + } + if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect threads_count", "{}"); + } + + std::string miner_address = request_json["miner_address"].GetString(); + uint64_t threads_count = request_json["threads_count"].GetUint(); + + cryptonote::account_public_address adr; + if (!cryptonote::get_account_address_from_str(adr, testnet, miner_address)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed, wrong address", "{}"); + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Failed, mining not started", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + + int stopmining(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (!core->get_miner().stop()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Failed, mining not stopped", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + + int miningstatus(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + bool active = core->get_miner().is_mining(); + result_json.AddMember("active", active, response_json.GetAllocator()); + + if (active) + { + uint64_t speed = core->get_miner().get_speed(); + uint64_t threads_count = core->get_miner().get_threads_count(); + const cryptonote::account_public_address &mining_address = core->get_miner().get_mining_address(); + std::string address = cryptonote::get_account_address_as_str(testnet, mining_address); + result_json.AddMember("speed", speed, response_json.GetAllocator()); + result_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); + rapidjson::Value string_address(rapidjson::kStringType); + string_address.SetString(address.c_str(), address.length()); + result_json.AddMember("address", string_address, response_json.GetAllocator()); + } + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblockcount(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblockhash(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect height", "{}"); + } + uint64_t height = request_json["height"].GetUint(); + if (core->get_current_blockchain_height() <= height) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Height specified is too big.", "{}"); + } + std::string hash = epee::string_tools::pod_to_hex(core->get_block_id_by_height(height)); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(hash.c_str(), hash.length()); + result_json.AddMember("hash", string_value, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect reserve_size", "{}"); + } + if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect wallet_address", "{}"); + } + + uint64_t reserve_size = request_json["reserve_size"].GetUint(); + std::string wallet_address = request_json["wallet_address"].GetString(); + + if (reserve_size > 255) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "To big reserved size, maximum 255", "{}"); + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + if (!wallet_address.size() || !cryptonote::get_account_address_from_str(acc, testnet, wallet_address)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Failed to parse wallet address", "{}"); + } + + cryptonote::block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(reserve_size, 0); + + uint64_t difficulty, height, reserved_offset; + if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) + { + LOG_ERROR("Failed to create block template"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed to create block template", "{}"); + } + + cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); + if (tx_pub_key == cryptonote::null_pkey) + { + LOG_ERROR("Failed to tx pub key in coinbase extra"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed to create block template", "{}"); + } + + reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if (!reserved_offset) + { + LOG_ERROR("Failed to find tx pub key in blockblob"); + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error: failed to create block template", "{}"); + } + + reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (reserved_offset + reserve_size > block_blob.size()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error: failed to create block template", "{}"); + } + cryptonote::blobdata blocktemplate_blob = epee::string_tools::buff_to_hex_nodelimer(block_blob); + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("difficulty", difficulty, response_json.GetAllocator()); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("reserved_offset", reserved_offset, response_json.GetAllocator()); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(blocktemplate_blob.c_str(), blocktemplate_blob.length()); + result_json.AddMember("blocktemplate_blob", string_value, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + int submitblock(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + if (!request_json.HasMember("block") || !request_json["block"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect block", "{}"); + } + + std::string string_blockblob = request_json["block"].GetString(); + cryptonote::blobdata blockblob; + if (!epee::string_tools::parse_hexstr_to_binbuff(string_blockblob, blockblob)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Incorrect block", "{}"); + } + // Fixing of high orphan issue for most pools + cryptonote::block b = AUTO_VAL_INIT(b); + if (!parse_and_validate_block_from_blob(blockblob, b)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Wrong block blob", "{}"); + } + // Fix from Boolberry neglects to check block + // size, do that with the function below + if (!core->check_incoming_block_size(blockblob)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Block blob size is too big, rejecting block", "{}"); + } + if (!core->handle_block_found(b)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Block not accepted.", "{}"); + } + + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); + } + + const char *method_names[] = { + "getheight", + "getblocks", + "gettransactions", + "startmining", + "stopmining", + "miningstatus", + "getblockcount", + "getblockhash", + "getblocktemplate", + "submitblock", + NULL + }; + + ns_rpc_handler_t handlers[] = { + getheight, + getblocks, + gettransactions, + startmining, + stopmining, + miningstatus, + getblockcount, + getblockhash, + getblocktemplate, + submitblock, + NULL + }; + + void ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + struct http_message *hm = (struct http_message *) ev_data; + char buf[1000]; + 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/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 5975a335e..40bfd5ddb 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -18,669 +18,18 @@ #include -#define CHECK_CORE_BUSY() if (check_core_busy()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_BUSY); } - namespace RPC { - cryptonote::core *core; - nodetool::node_server > *p2p; - bool testnet; - - const command_line::arg_descriptor arg_rpc_bind_ip = { - "rpc-bind-ip", - "IP for RPC server", - "127.0.0.1" - }; - - const command_line::arg_descriptor arg_rpc_bind_port = { - "rpc-bind-port", - "Port for RPC server", - std::to_string(config::RPC_DEFAULT_PORT) - }; - - const command_line::arg_descriptor arg_testnet_rpc_bind_port = { - "testnet-rpc-bind-port", - "Port for testnet RPC server", - std::to_string(config::testnet::RPC_DEFAULT_PORT) - }; - void init(cryptonote::core *p_core, nodetool::node_server > *p_p2p, - bool p_testnet) - { - core = p_core; - p2p = p_p2p; - testnet = p_testnet; - } + bool p_testnet); - 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); - } + void init_options(boost::program_options::options_description& desc); 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; + std::string &ip_address, std::string &port); - ip_address = command_line::get_arg(vm, arg_rpc_bind_ip); - port = command_line::get_arg(vm, p2p_bind_arg); - } - - bool check_core_busy() - { - if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return true; - } - return false; - } - - 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 (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); - response = buffer.GetString(); - } - - //------------------------------------------------------------------------------------------------------------------------------ - // equivalent of strstr, but with arbitrary bytes (ie, NULs) - // This does not differentiate between "not found" and "found at offset 0" - uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) - { - const void *buf = start_buff; - const void *end = (const char*)buf + buflen; - if (patlen > buflen || patlen == 0) - { - return 0; - } - while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) - { - if (memcmp(buf,pat,patlen) == 0) - return (const char*)buf - (const char*)start_buff; - buf = (const char*)buf + 1; - buflen = (const char*)end - (const char*)buf; - } - return 0; - } - - int getheight(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - uint64_t height = core->get_current_blockchain_height(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("height", height, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int getblocks(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("start_height") || !request_json["start_height"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect start_height", "{}"); - } - if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect block_ids", "{}"); - } - - uint64_t start_height = request_json["start_height"].GetUint(); - std::list block_ids; - int ii = 0; - for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); - it != request_json["block_ids"].End(); it++, ii++) - { - crypto::hash hash; - if (!it->IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Wrong type in block_ids", "{}"); - } - if (strlen(it->GetString()) > crypto::HASH_SIZE) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Block ID exceeds length.", "{}"); - } - strcpy(hash.data, it->GetString()); - block_ids.push_back(hash); - } - - std::list > > bs; - uint64_t result_current_height = 0; - uint64_t result_start_height = 0; - if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, - result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed", "{}"); - } - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Value block_json(rapidjson::kArrayType); - rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); - BOOST_FOREACH(auto &b, bs) - { - rapidjson::Value this_block(rapidjson::kObjectType); - std::string blob = block_to_blob(b.first); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(blob.c_str(), blob.length()); - this_block.AddMember("block", string_value, allocator); - rapidjson::Value txs_blocks(rapidjson::kArrayType); - BOOST_FOREACH(auto &t, b.second) - { - blob = cryptonote::tx_to_blob(t); - string_value.SetString(blob.c_str(), blob.length()); - txs_blocks.PushBack(string_value.Move(), allocator); - } - this_block.AddMember("txs", txs_blocks, allocator); - block_json.PushBack(this_block, allocator); - } - - result_json.AddMember("start_height", result_start_height, allocator); - result_json.AddMember("current_height", result_current_height, allocator); - result_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); - result_json.AddMember("blocks", block_json, allocator); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int gettransactions(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("txs_hashes") || !request_json["txs_hashes"].IsArray()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect txs_hashes", "{}"); - } - - std::list txs_hashes; - for (rapidjson::Value::ConstValueIterator it = request_json["txs_hashes"].Begin(); - it != request_json["txs_hashes"].End(); it++) - { - if (!it->IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Wrong type in txs_hashes", "{}"); - } - txs_hashes.push_back(std::string(it->GetString())); - } - std::vector vh; - BOOST_FOREACH(const auto& tx_hex_str, txs_hashes) - { - cryptonote::blobdata b; - if (!string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed to parse hex representation of transaction hash", "{}"); - } - if (b.size() != sizeof(crypto::hash)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed, size of data mismatch", "{}"); - } - vh.push_back(*reinterpret_cast(b.data())); - } - std::list missed_txs; - std::list txs; - bool r = core->get_transactions(vh, txs, missed_txs); - if (!r) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed", "{}"); - } - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); - - rapidjson::Value txs_as_hex(rapidjson::kArrayType); - BOOST_FOREACH(auto &tx, txs) - { - cryptonote::blobdata blob = t_serializable_object_to_blob(tx); - std::string string_blob = string_tools::buff_to_hex_nodelimer(blob); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(string_blob.c_str(), string_blob.length()); - txs_as_hex.PushBack(string_value, allocator); - } - result_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); - - rapidjson::Value missed_tx(rapidjson::kArrayType); - BOOST_FOREACH(const auto &miss_tx, missed_txs) - { - std::string string_blob = string_tools::pod_to_hex(miss_tx); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(string_blob.c_str(), string_blob.length()); - missed_tx.PushBack(string_value, allocator); - } - result_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int startmining(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect miner_address", "{}"); - } - if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect threads_count", "{}"); - } - - std::string miner_address = request_json["miner_address"].GetString(); - uint64_t threads_count = request_json["threads_count"].GetUint(); - - cryptonote::account_public_address adr; - if (!cryptonote::get_account_address_from_str(adr, testnet, miner_address)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed, wrong address", "{}"); - } - - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Failed, mining not started", "{}"); - } - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - int stopmining(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (!core->get_miner().stop()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Failed, mining not stopped", "{}"); - } - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - int miningstatus(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - bool active = core->get_miner().is_mining(); - result_json.AddMember("active", active, response_json.GetAllocator()); - - if (active) - { - uint64_t speed = core->get_miner().get_speed(); - uint64_t threads_count = core->get_miner().get_threads_count(); - const cryptonote::account_public_address &mining_address = core->get_miner().get_mining_address(); - std::string address = cryptonote::get_account_address_as_str(testnet, mining_address); - result_json.AddMember("speed", speed, response_json.GetAllocator()); - result_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); - rapidjson::Value string_address(rapidjson::kStringType); - string_address.SetString(address.c_str(), address.length()); - result_json.AddMember("address", string_address, response_json.GetAllocator()); - } - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int getblockcount(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int getblockhash(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect height", "{}"); - } - uint64_t height = request_json["height"].GetUint(); - if (core->get_current_blockchain_height() <= height) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Height specified is too big.", "{}"); - } - std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height)); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(hash.c_str(), hash.length()); - result_json.AddMember("hash", string_value, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect reserve_size", "{}"); - } - if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect wallet_address", "{}"); - } - - uint64_t reserve_size = request_json["reserve_size"].GetUint(); - std::string wallet_address = request_json["wallet_address"].GetString(); - - if (reserve_size > 255) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "To big reserved size, maximum 255", "{}"); - } - - cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); - if (!wallet_address.size() || !cryptonote::get_account_address_from_str(acc, testnet, wallet_address)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed to parse wallet address", "{}"); - } - - cryptonote::block b = AUTO_VAL_INIT(b); - cryptonote::blobdata blob_reserve; - blob_reserve.resize(reserve_size, 0); - - uint64_t difficulty, height, reserved_offset; - if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) - { - LOG_ERROR("Failed to create block template"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed to create block template", "{}"); - } - - cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); - if (tx_pub_key == cryptonote::null_pkey) - { - LOG_ERROR("Failed to tx pub key in coinbase extra"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed to create block template", "{}"); - } - - reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); - if (!reserved_offset) - { - LOG_ERROR("Failed to find tx pub key in blockblob"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Internal error: failed to create block template", "{}"); - } - - reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if (reserved_offset + reserve_size > block_blob.size()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Internal error: failed to create block template", "{}"); - } - cryptonote::blobdata blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("difficulty", difficulty, response_json.GetAllocator()); - result_json.AddMember("height", height, response_json.GetAllocator()); - result_json.AddMember("reserved_offset", reserved_offset, response_json.GetAllocator()); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(blocktemplate_blob.c_str(), blocktemplate_blob.length()); - result_json.AddMember("blocktemplate_blob", string_value, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - int submitblock(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - if (!request_json.HasMember("block") || !request_json["block"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect block", "{}"); - } - - std::string string_blockblob = request_json["block"].GetString(); - cryptonote::blobdata blockblob; - if (!string_tools::parse_hexstr_to_binbuff(string_blockblob, blockblob)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Incorrect block", "{}"); - } - // Fixing of high orphan issue for most pools - cryptonote::block b = AUTO_VAL_INIT(b); - if (!parse_and_validate_block_from_blob(blockblob, b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Wrong block blob", "{}"); - } - // Fix from Boolberry neglects to check block - // size, do that with the function below - if (!core->check_incoming_block_size(blockblob)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Block blob size is too big, rejecting block", "{}"); - } - if (!core->handle_block_found(b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Block not accepted.", "{}"); - } - - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - const char *method_names[] = { - "getheight", - "getblocks", - "gettransactions", - "startmining", - "stopmining", - "miningstatus", - "getblockcount", - "getblockhash", - "getblocktemplate", - "submitblock", - NULL - }; - - ns_rpc_handler_t handlers[] = { - getheight, - getblocks, - gettransactions, - startmining, - stopmining, - miningstatus, - getblockcount, - getblockhash, - getblocktemplate, - submitblock, - NULL - }; - - void ev_handler(struct ns_connection *nc, int ev, void *ev_data) - { - struct http_message *hm = (struct http_message *) ev_data; - char buf[1000]; - 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; - } - } + void ev_handler(struct ns_connection *nc, int ev, void *ev_data); } #endif From da6216288c0a7f2d06d3450c6eb60a076a618d56 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Tue, 11 Nov 2014 18:56:40 +0530 Subject: [PATCH 15/45] Documented JSON RPC handlers. --- src/rpc/json_rpc_handlers.cpp | 260 +++++++++++++++++++++++----------- src/rpc/json_rpc_handlers.h | 33 +++++ 2 files changed, 209 insertions(+), 84 deletions(-) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index 9a84325a6..265ac47ba 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -1,7 +1,28 @@ +/*! + * \file json_rpc_handlers.cpp + * \brief Implementations of JSON RPC handlers (Daemon) + */ + +// 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 "json_rpc_handlers.h" #define CHECK_CORE_BUSY() if (check_core_busy()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_BUSY); } +/*! + * \namespace + * \brief Anonymous namespace to keep things in the scope of this file. + */ namespace { cryptonote::core *core; @@ -26,6 +47,10 @@ namespace std::to_string(config::testnet::RPC_DEFAULT_PORT) }; + /*! + * \brief Tells if core is busy + * \return True if core is busy, false otherwise. + */ bool check_core_busy() { if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) @@ -35,11 +60,21 @@ namespace return false; } + /*! + * \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); @@ -55,93 +90,20 @@ namespace rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); response_json.Accept(writer); + // Write string to `response`. response = buffer.GetString(); } - //------------------------------------------------------------------------------------------------------------------------------ - // equivalent of strstr, but with arbitrary bytes (ie, NULs) - // This does not differentiate between "not found" and "found at offset 0" - uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) - { - const void *buf = start_buff; - const void *end = (const char*)buf + buflen; - if (patlen > buflen || patlen == 0) - { - return 0; - } - while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) - { - if (memcmp(buf,pat,patlen) == 0) - return (const char*)buf - (const char*)start_buff; - buf = (const char*)buf + 1; - buflen = (const char*)end - (const char*)buf; - } - return 0; - } -} - -namespace RPC -{ - void init(cryptonote::core *p_core, - nodetool::node_server > *p_p2p, - bool p_testnet) - { - core = p_core; - p2p = p_p2p; - testnet = p_testnet; - } - - 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); - } - - 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); - } - - bool check_core_busy() - { - if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return true; - } - return false; - } - - 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 (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); - response = buffer.GetString(); - } - - //------------------------------------------------------------------------------------------------------------------------------ - // equivalent of strstr, but with arbitrary bytes (ie, NULs) - // This does not differentiate between "not found" and "found at offset 0" + /*! + * \brief equivalent of strstr, but with arbitrary bytes (ie, NULs) + * + * This does not differentiate between "not found" and "found at offset 0" + * \param start_buff String to search + * \param buflen length of string + * \param pat Pattern to search + * \param patlen Length of pattern + * \return Position of match + */ uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) { const void *buf = start_buff; @@ -160,6 +122,15 @@ namespace RPC return 0; } + // The actual RPC method implementations start here. + + /*! + * \brief Implementation of 'getheight' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int getheight(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -176,6 +147,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'getblocks' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int getblocks(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -272,6 +250,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'gettransactions' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int gettransactions(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -366,6 +351,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'startmining' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int startmining(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -415,6 +407,13 @@ namespace RPC return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + /*! + * \brief Implementation of 'stopmining' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int stopmining(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -426,6 +425,13 @@ namespace RPC return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + /*! + * \brief Implementation of 'miningstatus' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int miningstatus(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -457,6 +463,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'getblockcount' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int getblockcount(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -474,6 +487,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'getblockhash' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int getblockhash(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -519,6 +539,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'getblocktemplate' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -620,6 +647,13 @@ namespace RPC return response.length(); } + /*! + * \brief Implementation of 'submitblock' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ int submitblock(char *buf, int len, struct ns_rpc_request *req) { CHECK_CORE_BUSY(); @@ -674,6 +708,7 @@ namespace RPC return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + // Contains a list of method names. const char *method_names[] = { "getheight", "getblocks", @@ -688,6 +723,7 @@ namespace RPC NULL }; + // Contains a list of function pointers. These must map 1-1 by index with `method_names`. ns_rpc_handler_t handlers[] = { getheight, getblocks, @@ -701,7 +737,63 @@ namespace RPC submitblock, NULL }; +} +/*! + * \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) + { + 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); + } + + /*! + * \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); + } + + /*! + * \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; diff --git a/src/rpc/json_rpc_handlers.h b/src/rpc/json_rpc_handlers.h index 40bfd5ddb..470a4dfc7 100644 --- a/src/rpc/json_rpc_handlers.h +++ b/src/rpc/json_rpc_handlers.h @@ -1,3 +1,8 @@ +/*! + * \file json_rpc_handlers.h + * \brief Header for JSON RPC handlers (Daemon) + */ + #ifndef JSON_RPC_HANDLERS_H #define JSON_RPC_HANDLERS_H @@ -18,17 +23,45 @@ #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); } From 37ddd82fe883e4d704d5469e495ef99786831359 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Tue, 11 Nov 2014 22:15:25 +0530 Subject: [PATCH 16/45] Added getlastblockheader. --- src/rpc/json_rpc_handlers.cpp | 80 ++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index 265ac47ba..b2364db50 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -17,7 +17,9 @@ #include "json_rpc_handlers.h" -#define CHECK_CORE_BUSY() if (check_core_busy()) { return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_BUSY); } +#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, "{}"); } /*! * \namespace @@ -122,6 +124,39 @@ namespace return 0; } + uint64_t get_block_reward(const cryptonote::block& blk) + { + uint64_t reward = 0; + BOOST_FOREACH(const cryptonote::tx_out& out, blk.miner_tx.vout) + { + reward += out.amount; + } + return reward; + } + + void fill_block_header_response(const cryptonote::block& blk, bool orphan_status, uint64_t height, + const crypto::hash& hash, rapidjson::Value &header_response, rapidjson::Document &root_doc) + { + rapidjson::Value string_value(rapidjson::kStringType); + header_response.AddMember("major_version", blk.major_version, root_doc.GetAllocator()); + header_response.AddMember("minor_version", blk.minor_version, root_doc.GetAllocator()); + header_response.AddMember("timestamp", blk.timestamp, root_doc.GetAllocator()); + std::string string_prev_hash = epee::string_tools::pod_to_hex(blk.prev_id); + string_value.SetString(string_prev_hash.c_str(), string_prev_hash.length()); + header_response.AddMember("prev_hash", string_value, root_doc.GetAllocator()); + header_response.AddMember("nonce", blk.nonce, root_doc.GetAllocator()); + header_response.AddMember("orphan_status", orphan_status, root_doc.GetAllocator()); + header_response.AddMember("height", height, root_doc.GetAllocator()); + header_response.AddMember("depth", core->get_current_blockchain_height() - height - 1, + root_doc.GetAllocator()); + std::string string_hash = epee::string_tools::pod_to_hex(hash); + string_value.SetString(string_hash.c_str(), string_hash.length()); + header_response.AddMember("hash", string_value, root_doc.GetAllocator()); + header_response.AddMember("difficulty", core->get_blockchain_storage().block_difficulty(height), + root_doc.GetAllocator()); + header_response.AddMember("reward", get_block_reward(blk), root_doc.GetAllocator()); + } + // The actual RPC method implementations start here. /*! @@ -708,6 +743,47 @@ namespace return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); } + /*! + * \brief Implementation of 'getlastblockheader' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getlastblockheader(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + uint64_t last_block_height; + crypto::hash last_block_hash; + bool have_last_block_hash = core->get_blockchain_top(last_block_height, last_block_hash); + + if (!have_last_block_hash) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Internal error: can't get last block hash", "{}"); + } + + cryptonote::block last_block; + bool have_last_block = core->get_block_by_hash(last_block_hash, last_block); + if (!have_last_block) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, + "Internal error: can't get last block hash", "{}"); + } + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + fill_block_header_response(last_block, false, last_block_height, last_block_hash, + result_json, response_json); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -720,6 +796,7 @@ namespace "getblockhash", "getblocktemplate", "submitblock", + "getlastblockheader", NULL }; @@ -735,6 +812,7 @@ namespace getblockhash, getblocktemplate, submitblock, + getlastblockheader, NULL }; } From bdd6b830fec0c57544f3ee0a473747ae67ef4c7c Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Wed, 12 Nov 2014 19:31:52 +0530 Subject: [PATCH 17/45] Added getblockheaderbyhash and getblockheaderbyheight. --- src/rpc/json_rpc_handlers.cpp | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index b2364db50..d9f2c5ef3 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -784,6 +784,134 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getblockheaderbyhash' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblockheaderbyhash(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + if (!request_json.HasMember("hash") || !request_json["hash"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect hash", "{}"); + } + + std::string hash = request_json["hash"].GetString(); + crypto::hash block_hash; + bool hash_parsed = parse_hash256(hash, block_hash); + if (!hash_parsed) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + std::string("Failed to parse hex representation of block hash. Hex = " + hash + '.').c_str(), "{}"); + } + + cryptonote::block blk; + bool have_block = core->get_block_by_hash(block_hash, blk); + if (!have_block) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + std::string("Internal error: can't get block by hash. Hash = " + hash + '.').c_str(), "{}"); + } + + if (blk.miner_tx.vin.front().type() != typeid(cryptonote::txin_gen)) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + std::string("Internal error: coinbase transaction in the block has the wrong type").c_str(), "{}"); + } + + uint64_t block_height = boost::get(blk.miner_tx.vin.front()).height; + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + fill_block_header_response(blk, false, block_height, block_hash, result_json, response_json); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'getblockheaderbyheight' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblockheaderbyheight(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect height", "{}"); + } + + uint64_t height = request_json["height"].GetUint(); + if (core->get_current_blockchain_height() <= height) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + std::string(std::string("To big height: ") + std::to_string(height) + + ", current blockchain height = " + std::to_string(core->get_current_blockchain_height())).c_str(), "{}"); + } + crypto::hash block_hash = core->get_block_id_by_height(height); + cryptonote::block blk; + bool have_block = core->get_block_by_hash(block_hash, blk); + if (!have_block) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + std::string("Internal error: can't get block by height. Height = " + std::to_string(height) + '.').c_str(), "{}"); + } + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + fill_block_header_response(blk, false, height, block_hash, result_json, response_json); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -797,6 +925,8 @@ namespace "getblocktemplate", "submitblock", "getlastblockheader", + "getblockheaderbyhash", + "getblockheaderbyheight", NULL }; @@ -813,6 +943,8 @@ namespace getblocktemplate, submitblock, getlastblockheader, + getblockheaderbyhash, + getblockheaderbyheight, NULL }; } From 6dee7774d5ced5809b5dc9b9584d431c8b1a8530 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Thu, 13 Nov 2014 21:49:12 +0530 Subject: [PATCH 18/45] Added getconnections. Increased max response size to 2000. --- src/rpc/json_rpc_handlers.cpp | 52 ++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index d9f2c5ef3..81af8e655 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -912,6 +912,54 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getconnections' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getconnections(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + + std::list connections = p2p->get_payload_object().get_connections(); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + rapidjson::Value connections_json(rapidjson::kArrayType); + for (std::list::iterator it = connections.begin(); + it != connections.end(); it++) + { + rapidjson::Value connection; + connection.SetObject(); + rapidjson::Value string_value; + connection.AddMember("incoming", it->incoming, response_json.GetAllocator()); + string_value.SetString(it->ip.c_str(), it->ip.length()); + connection.AddMember("ip", string_value, response_json.GetAllocator()); + string_value.SetString(it->port.c_str(), it->port.length()); + connection.AddMember("port", string_value, response_json.GetAllocator()); + string_value.SetString(it->peer_id.c_str(), it->peer_id.length()); + connection.AddMember("peer_id", string_value, response_json.GetAllocator()); + connection.AddMember("recv_count", it->recv_count, response_json.GetAllocator()); + connection.AddMember("recv_idle_time", it->recv_idle_time, response_json.GetAllocator()); + connection.AddMember("send_count", it->send_count, response_json.GetAllocator()); + connection.AddMember("send_idle_time", it->send_idle_time, response_json.GetAllocator()); + string_value.SetString(it->state.c_str(), it->state.length()); + connection.AddMember("state", string_value, response_json.GetAllocator()); + connection.AddMember("live_time", it->live_time, response_json.GetAllocator()); + connections_json.PushBack(connection, response_json.GetAllocator()); + } + result_json.AddMember("connections", connections_json, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -927,6 +975,7 @@ namespace "getlastblockheader", "getblockheaderbyhash", "getblockheaderbyheight", + "getconnections", NULL }; @@ -945,6 +994,7 @@ namespace getlastblockheader, getblockheaderbyhash, getblockheaderbyheight, + getconnections, NULL }; } @@ -1007,7 +1057,7 @@ namespace RPC void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; - char buf[1000]; + char buf[2000]; switch (ev) { case NS_HTTP_REQUEST: ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), From 2ec4502ec8c66c82bed93a8b4fe2af44e777e8d5 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 15 Nov 2014 00:42:28 +0530 Subject: [PATCH 19/45] Added getinfo. --- src/rpc/json_rpc_handlers.cpp | 51 ++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index 81af8e655..63d0c1285 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -21,6 +21,8 @@ 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. @@ -960,6 +962,51 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getinfo' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getinfo(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + uint64_t height = core->get_current_blockchain_height(); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("target_height", core->get_target_blockchain_height(), + response_json.GetAllocator()); + result_json.AddMember("difficulty", core->get_blockchain_storage().get_difficulty_for_next_block(), + response_json.GetAllocator()); + result_json.AddMember("tx_count", core->get_blockchain_storage().get_total_transactions() - height, + response_json.GetAllocator()); + result_json.AddMember("tx_pool_size", core->get_pool_transactions_count(), + response_json.GetAllocator()); + result_json.AddMember("alt_blocks_count", (uint64_t)core->get_blockchain_storage().get_alternative_blocks_count(), + response_json.GetAllocator()); + uint64_t total_conn = p2p->get_connections_count(); + uint64_t outgoing_connections_count = p2p->get_outgoing_connections_count(); + result_json.AddMember("outgoing_connections_count", outgoing_connections_count, + response_json.GetAllocator()); + result_json.AddMember("incoming_connections_count", total_conn - outgoing_connections_count, + response_json.GetAllocator()); + result_json.AddMember("white_peerlist_size", p2p->get_peerlist_manager().get_white_peers_count(), + response_json.GetAllocator()); + result_json.AddMember("grey_peerlist_size", p2p->get_peerlist_manager().get_gray_peers_count(), + response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -976,6 +1023,7 @@ namespace "getblockheaderbyhash", "getblockheaderbyheight", "getconnections", + "getinfo", NULL }; @@ -995,6 +1043,7 @@ namespace getblockheaderbyhash, getblockheaderbyheight, getconnections, + getinfo, NULL }; } @@ -1057,7 +1106,7 @@ namespace RPC void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; - char buf[2000]; + char buf[MAX_RESPONSE_SIZE]; switch (ev) { case NS_HTTP_REQUEST: ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), From 299ee2357ebf654b44ea6e37238c7ecc512a308b Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 16 Nov 2014 15:59:51 +0530 Subject: [PATCH 20/45] Added getindexes. --- src/rpc/json_rpc_handlers.cpp | 67 +++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/rpc/json_rpc_handlers.cpp b/src/rpc/json_rpc_handlers.cpp index 63d0c1285..a8ffa383b 100644 --- a/src/rpc/json_rpc_handlers.cpp +++ b/src/rpc/json_rpc_handlers.cpp @@ -1007,6 +1007,71 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getindexes' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getindexes(char *buf, int len, struct ns_rpc_request *req) + { + CHECK_CORE_BUSY(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, + "Invalid JSON passed", "{}"); + } + if (!request_json.HasMember("txid") || !request_json["txid"].IsString()) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "Incorrect txid", "{}"); + } + std::string txid_string = request_json["txid"].GetString(); + if (txid_string.length() < crypto::HASH_SIZE) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, + "txid is not of correct length", "{}"); + } + crypto::hash txid; + strncpy(txid.data, txid_string.c_str(), crypto::HASH_SIZE); + std::vector o_indexes; + + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + + rapidjson::Value o_indexes_json(rapidjson::kArrayType); + bool r = core->get_tx_outputs_gindexs(txid, o_indexes); + if (!r) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Failed", "{}"); + } + for (std::vector::iterator it = o_indexes.begin(); it != o_indexes.end(); it++) + { + o_indexes_json.PushBack(*it, response_json.GetAllocator()); + } + result_json.AddMember("o_indexes", o_indexes_json, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -1024,6 +1089,7 @@ namespace "getblockheaderbyheight", "getconnections", "getinfo", + "getindexes", NULL }; @@ -1044,6 +1110,7 @@ namespace getblockheaderbyheight, getconnections, getinfo, + getindexes, NULL }; } From ca86e346c2ff2cd4d3834a9f9d3b874b29e4e42f Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Thu, 4 Dec 2014 13:57:17 +0530 Subject: [PATCH 21/45] Make distinction between daemon and wallet RPC. Use new HTTP server for wallet. --- src/CMakeLists.txt | 2 +- src/daemon/daemon.cpp | 10 +- ...dlers.cpp => daemon_json_rpc_handlers.cpp} | 125 ++++++++------- src/rpc/daemon_json_rpc_handlers.h | 75 +++++++++ src/rpc/json_rpc_handlers.h | 68 -------- src/simplewallet/simplewallet.cpp | 20 ++- src/wallet/wallet_json_rpc_handlers.cpp | 149 ++++++++++++++++++ src/wallet/wallet_json_rpc_handlers.h | 85 ++++++++++ 8 files changed, 393 insertions(+), 141 deletions(-) rename src/rpc/{json_rpc_handlers.cpp => daemon_json_rpc_handlers.cpp} (94%) create mode 100644 src/rpc/daemon_json_rpc_handlers.h delete mode 100644 src/rpc/json_rpc_handlers.h create mode 100644 src/wallet/wallet_json_rpc_handlers.cpp create mode 100644 src/wallet/wallet_json_rpc_handlers.h 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 From 7f670dc77d0a7107b31e3b77d92ddd5feaabc6d3 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Fri, 5 Dec 2014 23:35:38 +0530 Subject: [PATCH 22/45] getbalance and getaddress RPC for wallet. Added zmq header. --- external/zmq.hpp | 595 ++++++++++++++++++++++++ src/wallet/wallet_json_rpc_handlers.cpp | 74 ++- 2 files changed, 665 insertions(+), 4 deletions(-) create mode 100644 external/zmq.hpp diff --git a/external/zmq.hpp b/external/zmq.hpp new file mode 100644 index 000000000..2af095654 --- /dev/null +++ b/external/zmq.hpp @@ -0,0 +1,595 @@ +/* + Copyright (c) 2009-2011 250bpm s.r.o. + Copyright (c) 2011 Botond Ballo + Copyright (c) 2007-2009 iMatix Corporation + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef __ZMQ_HPP_INCLUDED__ +#define __ZMQ_HPP_INCLUDED__ + +#include + +#include +#include +#include +#include +#include + +// Detect whether the compiler supports C++11 rvalue references. +#if (defined(__GNUC__) && (__GNUC__ > 4 || \ + (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \ + defined(__GXX_EXPERIMENTAL_CXX0X__)) + #define ZMQ_HAS_RVALUE_REFS + #define ZMQ_DELETED_FUNCTION = delete +#elif defined(__clang__) + #if __has_feature(cxx_rvalue_references) + #define ZMQ_HAS_RVALUE_REFS + #endif + + #if __has_feature(cxx_deleted_functions) + #define ZMQ_DELETED_FUNCTION = delete + #else + #define ZMQ_DELETED_FUNCTION + #endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1600) + #define ZMQ_HAS_RVALUE_REFS + #define ZMQ_DELETED_FUNCTION +#else + #define ZMQ_DELETED_FUNCTION +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) +#define ZMQ_NEW_MONITOR_EVENT_LAYOUT +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) +#define ZMQ_HAS_PROXY_STEERABLE +/* Socket event data */ +typedef struct { + uint16_t event; // id of the event as bitfield + int32_t value ; // value is either error code, fd or reconnect interval +} zmq_event_t; +#endif + +// In order to prevent unused variable warnings when building in non-debug +// mode use this macro to make assertions. +#ifndef NDEBUG +# define ZMQ_ASSERT(expression) assert(expression) +#else +# define ZMQ_ASSERT(expression) (void)(expression) +#endif + +namespace zmq +{ + + typedef zmq_free_fn free_fn; + typedef zmq_pollitem_t pollitem_t; + + class error_t : public std::exception + { + public: + + error_t () : errnum (zmq_errno ()) {} + + virtual const char *what () const throw () + { + return zmq_strerror (errnum); + } + + int num () const + { + return errnum; + } + + private: + + int errnum; + }; + + inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1) + { + int rc = zmq_poll (items_, nitems_, timeout_); + if (rc < 0) + throw error_t (); + return rc; + } + + inline void proxy (void *frontend, void *backend, void *capture) + { + int rc = zmq_proxy (frontend, backend, capture); + if (rc != 0) + throw error_t (); + } + +#ifdef ZMQ_HAS_PROXY_STEERABLE + inline void proxy_steerable (void *frontend, void *backend, void *capture, void *control) + { + int rc = zmq_proxy_steerable (frontend, backend, capture, control); + if (rc != 0) + throw error_t (); + } +#endif + + inline void version (int *major_, int *minor_, int *patch_) + { + zmq_version (major_, minor_, patch_); + } + + class message_t + { + friend class socket_t; + + public: + + inline message_t () + { + int rc = zmq_msg_init (&msg); + if (rc != 0) + throw error_t (); + } + + inline explicit message_t (size_t size_) + { + int rc = zmq_msg_init_size (&msg, size_); + if (rc != 0) + throw error_t (); + } + + inline message_t (void *data_, size_t size_, free_fn *ffn_, + void *hint_ = NULL) + { + int rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t (); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline message_t (message_t &&rhs) : msg (rhs.msg) + { + int rc = zmq_msg_init (&rhs.msg); + if (rc != 0) + throw error_t (); + } + + inline message_t &operator = (message_t &&rhs) + { + std::swap (msg, rhs.msg); + return *this; + } +#endif + + inline ~message_t () + { + int rc = zmq_msg_close (&msg); + ZMQ_ASSERT (rc == 0); + } + + inline void rebuild () + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init (&msg); + if (rc != 0) + throw error_t (); + } + + inline void rebuild (size_t size_) + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init_size (&msg, size_); + if (rc != 0) + throw error_t (); + } + + inline void rebuild (void *data_, size_t size_, free_fn *ffn_, + void *hint_ = NULL) + { + int rc = zmq_msg_close (&msg); + if (rc != 0) + throw error_t (); + rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t (); + } + + inline void move (message_t *msg_) + { + int rc = zmq_msg_move (&msg, &(msg_->msg)); + if (rc != 0) + throw error_t (); + } + + inline void copy (message_t *msg_) + { + int rc = zmq_msg_copy (&msg, &(msg_->msg)); + if (rc != 0) + throw error_t (); + } + + inline bool more () + { + int rc = zmq_msg_more (&msg); + return rc != 0; + } + + inline void *data () + { + return zmq_msg_data (&msg); + } + + inline const void* data () const + { + return zmq_msg_data (const_cast(&msg)); + } + + inline size_t size () const + { + return zmq_msg_size (const_cast(&msg)); + } + + private: + + // The underlying message + zmq_msg_t msg; + + // Disable implicit message copying, so that users won't use shared + // messages (less efficient) without being aware of the fact. + message_t (const message_t&); + void operator = (const message_t&); + }; + + class context_t + { + friend class socket_t; + + public: + inline context_t () + { + ptr = zmq_ctx_new (); + if (ptr == NULL) + throw error_t (); + } + + + inline explicit context_t (int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT) + { + ptr = zmq_ctx_new (); + if (ptr == NULL) + throw error_t (); + + int rc = zmq_ctx_set (ptr, ZMQ_IO_THREADS, io_threads_); + ZMQ_ASSERT (rc == 0); + + rc = zmq_ctx_set (ptr, ZMQ_MAX_SOCKETS, max_sockets_); + ZMQ_ASSERT (rc == 0); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline context_t (context_t &&rhs) : ptr (rhs.ptr) + { + rhs.ptr = NULL; + } + inline context_t &operator = (context_t &&rhs) + { + std::swap (ptr, rhs.ptr); + return *this; + } +#endif + + inline ~context_t () + { + close(); + } + + inline void close() + { + if (ptr == NULL) + return; + int rc = zmq_ctx_destroy (ptr); + ZMQ_ASSERT (rc == 0); + ptr = NULL; + } + + // Be careful with this, it's probably only useful for + // using the C api together with an existing C++ api. + // Normally you should never need to use this. + inline operator void* () + { + return ptr; + } + + private: + + void *ptr; + + context_t (const context_t&); + void operator = (const context_t&); + }; + + class socket_t + { + friend class monitor_t; + public: + + inline socket_t (context_t &context_, int type_) + { + ctxptr = context_.ptr; + ptr = zmq_socket (context_.ptr, type_); + if (ptr == NULL) + throw error_t (); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + inline socket_t(socket_t&& rhs) : ptr(rhs.ptr) + { + rhs.ptr = NULL; + } + inline socket_t& operator=(socket_t&& rhs) + { + std::swap(ptr, rhs.ptr); + return *this; + } +#endif + + inline ~socket_t () + { + close(); + } + + inline operator void* () + { + return ptr; + } + + inline void close() + { + if(ptr == NULL) + // already closed + return ; + int rc = zmq_close (ptr); + ZMQ_ASSERT (rc == 0); + ptr = 0 ; + } + + inline void setsockopt (int option_, const void *optval_, + size_t optvallen_) + { + int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_); + if (rc != 0) + throw error_t (); + } + + inline void getsockopt (int option_, void *optval_, + size_t *optvallen_) + { + int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_); + if (rc != 0) + throw error_t (); + } + + inline void bind (const char *addr_) + { + int rc = zmq_bind (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void unbind (const char *addr_) + { + int rc = zmq_unbind (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void connect (const char *addr_) + { + int rc = zmq_connect (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline void disconnect (const char *addr_) + { + int rc = zmq_disconnect (ptr, addr_); + if (rc != 0) + throw error_t (); + } + + inline bool connected() + { + return(ptr != NULL); + } + + inline size_t send (const void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_send (ptr, buf_, len_, flags_); + if (nbytes >= 0) + return (size_t) nbytes; + if (zmq_errno () == EAGAIN) + return 0; + throw error_t (); + } + + inline bool send (message_t &msg_, int flags_ = 0) + { + int nbytes = zmq_msg_send (&(msg_.msg), ptr, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno () == EAGAIN) + return false; + throw error_t (); + } + + inline size_t recv (void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_recv (ptr, buf_, len_, flags_); + if (nbytes >= 0) + return (size_t) nbytes; + if (zmq_errno () == EAGAIN) + return 0; + throw error_t (); + } + + inline bool recv (message_t *msg_, int flags_ = 0) + { + int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno () == EAGAIN) + return false; + throw error_t (); + } + + private: + void *ptr; + void *ctxptr; + + socket_t (const socket_t&) ZMQ_DELETED_FUNCTION; + void operator = (const socket_t&) ZMQ_DELETED_FUNCTION; + }; + + class monitor_t + { + public: + monitor_t() : socketPtr(NULL) {} + virtual ~monitor_t() {} + + void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) + { + int rc = zmq_socket_monitor(socket.ptr, addr_, events); + if (rc != 0) + throw error_t (); + + socketPtr = socket.ptr; + void *s = zmq_socket (socket.ctxptr, ZMQ_PAIR); + assert (s); + + rc = zmq_connect (s, addr_); + assert (rc == 0); + + on_monitor_started(); + + while (true) { + zmq_msg_t eventMsg; + zmq_msg_init (&eventMsg); + rc = zmq_recvmsg (s, &eventMsg, 0); + if (rc == -1 && zmq_errno() == ETERM) + break; + assert (rc != -1); +#if ZMQ_VERSION_MAJOR >= 4 + const char* data = static_cast(zmq_msg_data(&eventMsg)); + zmq_event_t msgEvent; + msgEvent.event = *(uint16_t*)data; data += sizeof(uint16_t); + msgEvent.value = *(int32_t*)data; + zmq_event_t* event = &msgEvent; +#else + zmq_event_t* event = static_cast(zmq_msg_data(&eventMsg)); +#endif + +#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT + zmq_msg_t addrMsg; + zmq_msg_init (&addrMsg); + rc = zmq_recvmsg (s, &addrMsg, 0); + if (rc == -1 && zmq_errno() == ETERM) + break; + assert (rc != -1); + const char* str = static_cast(zmq_msg_data (&addrMsg)); + std::string address(str, str + zmq_msg_size(&addrMsg)); + zmq_msg_close (&addrMsg); +#else + // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. + std::string address = event->data.connected.addr; +#endif + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + if (event->event == ZMQ_EVENT_MONITOR_STOPPED) + break; +#endif + + switch (event->event) { + case ZMQ_EVENT_CONNECTED: + on_event_connected(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_DELAYED: + on_event_connect_delayed(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_RETRIED: + on_event_connect_retried(*event, address.c_str()); + break; + case ZMQ_EVENT_LISTENING: + on_event_listening(*event, address.c_str()); + break; + case ZMQ_EVENT_BIND_FAILED: + on_event_bind_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPTED: + on_event_accepted(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPT_FAILED: + on_event_accept_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSED: + on_event_closed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSE_FAILED: + on_event_close_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_DISCONNECTED: + on_event_disconnected(*event, address.c_str()); + break; + default: + on_event_unknown(*event, address.c_str()); + break; + } + zmq_msg_close (&eventMsg); + } + zmq_close (s); + socketPtr = NULL; + } + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + void abort() + { + if (socketPtr) + zmq_socket_monitor(socketPtr, NULL, 0); + } +#endif + virtual void on_monitor_started() {} + virtual void on_event_connected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_connect_delayed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_connect_retried(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_listening(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_bind_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_accepted(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_accept_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_closed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_close_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_disconnected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + virtual void on_event_unknown(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } + private: + void* socketPtr; + }; +} + +#endif diff --git a/src/wallet/wallet_json_rpc_handlers.cpp b/src/wallet/wallet_json_rpc_handlers.cpp index 0ab3b680e..5334ebbf7 100644 --- a/src/wallet/wallet_json_rpc_handlers.cpp +++ b/src/wallet/wallet_json_rpc_handlers.cpp @@ -17,10 +17,6 @@ #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 /*! @@ -65,13 +61,83 @@ namespace response = buffer.GetString(); } + /*! + * \brief Implementation of 'getbalance' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getbalance(char *buf, int len, struct ns_rpc_request *req) + { + uint64_t balance, unlocked_balance; + try + { + balance = wallet->balance(); + unlocked_balance = wallet->unlocked_balance(); + } + catch (std::exception& e) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error", "{}"); + } + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("balance", balance, response_json.GetAllocator()); + result_json.AddMember("unlocked_balance", unlocked_balance, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'getaddress' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getaddress(char *buf, int len, struct ns_rpc_request *req) + { + std::string address; + try + { + address = wallet->get_account().get_public_address_str(wallet->testnet()); + } + catch (std::exception& e) + { + return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, + "Internal error", "{}"); + } + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(address.c_str(), address.length()); + result_json.AddMember("address", string_value, response_json.GetAllocator()); + result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { + "getbalance", + "getaddress", NULL }; // Contains a list of function pointers. These must map 1-1 by index with `method_names`. ns_rpc_handler_t handlers[] = { + getbalance, + getaddress, NULL }; } From 9eea54bfa6f58a9a5b5cc29b55ea80f88218c1a4 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 8 Dec 2014 22:46:47 +0530 Subject: [PATCH 23/45] 0MQ basic server in the works --- src/rpc/daemon_ipc_server.cpp | 135 +++++++++++++++++++++++++++++++ src/rpc/daemon_ipc_server.h | 97 ++++++++++++++++++++++ src/rpc/json_rpc_http_server.cpp | 6 +- src/rpc/json_rpc_http_server.h | 2 +- 4 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 src/rpc/daemon_ipc_server.cpp create mode 100644 src/rpc/daemon_ipc_server.h diff --git a/src/rpc/daemon_ipc_server.cpp b/src/rpc/daemon_ipc_server.cpp new file mode 100644 index 000000000..54f4232da --- /dev/null +++ b/src/rpc/daemon_ipc_server.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "json_rpc_http_server.h" + +#include + +namespace IPC +{ + Daemon_ipc_server::Daemon_ipc_server(const std::string &ip, const std::string &port, + nodetool::node_server > *p_p2p, + cryptonote::core *p_core) + { + if (m_is_running) + { + return false; + } + p2p = p_p2p; + core = p_core; + m_ip = ip; + m_port = port; + zmq::context_t context(1); + socket = new zmq::socket_t(context, ZMQ_REQ); + return true; + } + + void Daemon_ipc_server::start() + { + socket->bind(std::string("tcp://" + m_ip + ":" + m_port); + m_is_running = true; + // Start a new thread so it doesn't block. + server_thread = new boost::thread(&Daemon_ipc_server::poll, this); + } + + Daemon_ipc_server::~Daemon_ipc_server() + { + stop(); + } + + bool Daemon_ipc_server::check_core_busy() + { + if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + { + return false; + } + return true; + } + + /*! + * \brief Stops the server + */ + void Daemon_ipc_server::stop() + { + m_is_running = false; + server_thread->join(); + delete server_thread; + } + + void Daemon_ipc_server::getblocks(std::string &response) + { + + } + void Daemon_ipc_server::sendtransactions(std::string &response); + void Daemon_ipc_server::get_o_indexes(std::string &response); + + void handle_request(const std::string &request_string, std::string &response) + { + if (check_core_busy()) + { + response = ""; + } + if (request_string == "getblocks") + { + getblocks(response); + } + else if (request_string == "sendrawtransaction") + { + sendrawtransaction(response); + } + else if (request_string == "get_o_indexes") + { + get_o_indexes(response); + } + else + { + response = ""; + } + } + + /*! + * \brief Repeatedly loops processing requests if any. + */ + void Daemon_ipc_server::poll() + { + // Loop until the server is running and poll. + while (m_is_running) { + zmq::message_t request; + // Wait for next request from client + socket->recv(&request); + std::string request_string((char*)request.data(), request.size()); + std::string response; + handle_request(request_string, response); + zmq::message_t reply(response.length()); + memcpy((void*)reply.data(), response.c_str(), response.length()); + socket->send(reply); + } + } +} diff --git a/src/rpc/daemon_ipc_server.h b/src/rpc/daemon_ipc_server.h new file mode 100644 index 000000000..876e5b3ec --- /dev/null +++ b/src/rpc/daemon_ipc_server.h @@ -0,0 +1,97 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#ifndef JSON_RPC_HTTP_SERVER_H +#define JSON_RPC_HTTP_SERVER_H + +#include "include_base_utils.h" +using namespace epee; + +#include "wallet_rpc_server.h" +#include "common/command_line.h" +#include "cryptonote_core/cryptonote_format_utils.h" +#include "cryptonote_core/account.h" +#include "wallet_rpc_server_commands_defs.h" +#include "misc_language.h" +#include "string_tools.h" +#include "crypto/hash.h" +#include "zmq.hpp" + +namespace IPC +{ + class Daemon_ipc_server + { + boost::thread *server_thread; /*!< Server runs on this thread */ + /*! + * \brief Repeatedly loops processing requests if any. + */ + void poll(); + void handle_request(const std::string &request_string, std::string &response); + bool check_core_busy(); + + void getblocks(std::string &response); + void sendtransactions(std::string &response); + void get_o_indexes(std::string &response); + std::string m_ip; /*!< IP address where its listening */ + std::string m_port; /*!< Port where its listening */ + bool m_is_running; /*!< Whether the server is currently running */ + zmq::socket_t *socket; /*!< 0MQ Socket pointer */ + cryptonote::core *core; /*!< Pointer to the core */ + nodetool::node_server > *p2p; + /*!< Pointer to p2p node server */ + + public: + /** + * \brief Constructor + * \param ip IP address to bind + * \param port Port number to bind + * \param ev_handler Event handler function pointer + */ + Daemon_ipc_server(const std::string &ip, const std::string &port); + + /** + * \brief Destructor + */ + ~Daemon_ipc_server(); + + /*! + * \brief Starts the server + * \return True if start was successful + */ + bool start(); + + /*! + * \brief Stops the server + */ + void stop(); + }; +} + +#endif diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index a27dd49d4..86edc7d10 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -47,6 +47,10 @@ namespace RPC */ bool Json_rpc_http_server::start() { + if (m_is_running) + { + return false; + } m_is_running = true; ns_mgr_init(&mgr, NULL); nc = ns_bind(&mgr, (m_ip + ":" + m_port).c_str(), m_ev_handler); @@ -61,7 +65,7 @@ namespace RPC } /*! - * \brief Repeated loops processing requests if any. + * \brief Repeatedly loops processing requests if any. */ void Json_rpc_http_server::poll() { diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index b69a2f574..a262f6061 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -28,7 +28,7 @@ namespace RPC struct ns_connection *nc; /*!< Connection pointer */ boost::thread *server_thread; /*!< Server runs on this thread */ /*! - * \brief Repeated loops processing requests if any. + * \brief Repeatedly loops processing requests if any. */ void poll(); std::string m_ip; /*!< IP address where its listening */ From 81b2a49c6ed9d76435caf953371b4580d83e5230 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Fri, 16 Jan 2015 22:17:06 +0530 Subject: [PATCH 24/45] 0MQ in. CMake integrated. --- CMakeLists.txt | 1 + src/CMakeLists.txt | 5 +- src/daemon/CMakeLists.txt | 5 +- src/daemon/daemon.cpp | 3 + src/ipc/CMakeLists.txt | 58 + src/ipc/daemon_ipc_handlers.cpp | 86 + .../include/daemon_ipc_handlers.h} | 65 +- src/ipc/include/wallet.h | 18 + src/ipc/include/wap_classes.h | 22 + src/ipc/include/wap_client.h | 129 ++ src/ipc/include/wap_client_engine.inc | 1587 +++++++++++++++++ src/ipc/include/wap_library.h | 64 + src/ipc/include/wap_proto.h | 251 +++ src/ipc/include/wap_server.h | 85 + src/ipc/include/wap_server_engine.inc | 1238 +++++++++++++ src/ipc/wap_client/wap_client.c | 307 ++++ src/ipc/wap_proto.c | 1316 ++++++++++++++ src/ipc/wap_server/wap_server.c | 244 +++ src/rpc/daemon_ipc_server.cpp | 135 -- src/simplewallet/simplewallet.cpp | 3 +- src/simplewallet/simplewallet.h | 1 + src/wallet/CMakeLists.txt | 3 + src/wallet/wallet2.cpp | 6 +- src/wallet/wallet2.h | 15 +- .../transactions_flow_test.cpp | 2 - 25 files changed, 5456 insertions(+), 193 deletions(-) create mode 100644 src/ipc/CMakeLists.txt create mode 100644 src/ipc/daemon_ipc_handlers.cpp rename src/{rpc/daemon_ipc_server.h => ipc/include/daemon_ipc_handlers.h} (56%) create mode 100644 src/ipc/include/wallet.h create mode 100644 src/ipc/include/wap_classes.h create mode 100644 src/ipc/include/wap_client.h create mode 100644 src/ipc/include/wap_client_engine.inc create mode 100644 src/ipc/include/wap_library.h create mode 100644 src/ipc/include/wap_proto.h create mode 100644 src/ipc/include/wap_server.h create mode 100644 src/ipc/include/wap_server_engine.inc create mode 100644 src/ipc/wap_client/wap_client.c create mode 100644 src/ipc/wap_proto.c create mode 100644 src/ipc/wap_server/wap_server.c delete mode 100644 src/rpc/daemon_ipc_server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ce93d951..1417a3e59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ endif() include_directories(src contrib/epee/include external "${CMAKE_BINARY_DIR}/version") include_directories(src/common/rapidjson/include/) +include_directories(src/ipc/include) if(APPLE) include_directories(SYSTEM /usr/include/malloc) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a80dfe378..8d87a2b10 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,7 +51,6 @@ function (bitmonero_add_executable name) source_group("${name}" FILES ${ARGN}) - add_executable("${name}" ${ARGN}) target_link_libraries("${name}" @@ -86,10 +85,14 @@ function (bitmonero_add_library name) FOLDER "libs") endfunction () +find_library(ZMQ_LIB zmq) +find_library(CZMQ_LIB czmq) + add_subdirectory(common) add_subdirectory(crypto) add_subdirectory(cryptonote_core) add_subdirectory(mnemonics) +add_subdirectory(ipc) add_subdirectory(rpc) add_subdirectory(wallet) diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 03fdf84be..ea8b4f7ae 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -59,6 +59,7 @@ bitmonero_add_executable(daemon target_link_libraries(daemon LINK_PRIVATE rpc + server_ipc cryptonote_core crypto common @@ -71,7 +72,9 @@ target_link_libraries(daemon ${CMAKE_THREAD_LIBS_INIT} ${UPNP_LIBRARIES} ${EXTRA_LIBRARIES} - ${NET_SKELETON_LIBRARY}) + ${NET_SKELETON_LIBRARY} + ${ZMQ_LIB} + ${CZMQ_LIB}) add_dependencies(daemon version) set_property(TARGET daemon diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index 8fc1b5874..6fdefdc65 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -52,6 +52,7 @@ using namespace epee; #include "version.h" #include "rpc/daemon_json_rpc_handlers.h" #include "rpc/json_rpc_http_server.h" +#include "daemon_ipc_handlers.h" #if defined(WIN32) #include @@ -245,6 +246,8 @@ int main(int argc, char* argv[]) CHECK_AND_ASSERT_MES(res, 1, "Failed to initialize protocol."); LOG_PRINT_L0("Protocol initialized OK"); + // LOG_PRINT_L0("Initializing core IPC server..."); + // IPC::Daemon::init(&ccore, &p2psrv); LOG_PRINT_L0("Initializing core RPC server..."); RPC::Daemon::init(&ccore, &p2psrv, testnet_mode); std::string ip_address, port; diff --git a/src/ipc/CMakeLists.txt b/src/ipc/CMakeLists.txt new file mode 100644 index 000000000..6e2836cb3 --- /dev/null +++ b/src/ipc/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright (c) 2014-2015, The Monero Project +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are +# permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of +# conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list +# of conditions and the following disclaimer in the documentation and/or other +# materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be +# used to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +set(server_ipc_sources + wap_server/wap_server.c + wap_proto.c + daemon_ipc_handlers.cpp) + +set(client_ipc_sources + wap_client/wap_client.c + wap_proto.c) + +set_source_files_properties(${server_ipc_sources} ${client_ipc_sources} PROPERTIES LANGUAGE CXX) +set_source_files_properties(${server_ipc_sources} ${client_ipc_sources} PROPERTIES COMPILE_FLAGS "-Wno-write-strings -Wno-error -fpermissive") + +set(server_ipc_headers) +set(client_ipc_headers) + +bitmonero_add_library(server_ipc + ${server_ipc_sources} + ${server_ipc_headers}) +target_link_libraries(server_ipc + LINK_PRIVATE + cryptonote_core + ${ZMQ_LIB}) + +bitmonero_add_library(client_ipc + ${client_ipc_sources} + ${client_ipc_headers}) +target_link_libraries(client_ipc + LINK_PRIVATE + cryptonote_core + ${CZMQ_LIB}) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp new file mode 100644 index 000000000..23042267c --- /dev/null +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include "daemon_ipc_handlers.h" + +#include + +namespace +{ + cryptonote::core *core; /*!< Pointer to the core */ + nodetool::node_server > *p2p; + /*!< Pointer to p2p node server */ + zactor_t *server; + bool check_core_busy() + { + if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) + { + return false; + } + return true; + } +} + +namespace IPC +{ + namespace Daemon + { + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p) + { + p2p = p_p2p; + core = p_core; + /*server = zactor_new (wap_server, NULL); + zsock_send (server, "ss", "BIND", "ipc://@/monero"); + zsock_send (server, "sss", "SET", "server/timeout", "5000");*/ + } + + void start_mining(wap_proto_t *message) + { + uint64_t start_height = wap_proto_start_height(message); + wap_proto_set_curr_height(message, 2 * start_height); + } + + void getblocks(wap_proto_t *message) + { + + } + + void sendtransactions(wap_proto_t *message) + { + + } + + void get_o_indexes(wap_proto_t *message) + { + + } + } +} diff --git a/src/rpc/daemon_ipc_server.h b/src/ipc/include/daemon_ipc_handlers.h similarity index 56% rename from src/rpc/daemon_ipc_server.h rename to src/ipc/include/daemon_ipc_handlers.h index 876e5b3ec..5ef0aa350 100644 --- a/src/rpc/daemon_ipc_server.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -28,70 +28,35 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers -#ifndef JSON_RPC_HTTP_SERVER_H -#define JSON_RPC_HTTP_SERVER_H +#ifndef DAEMON_IPC_HANDLERS_H +#define DAEMON_IPC_HANDLERS_H #include "include_base_utils.h" using namespace epee; -#include "wallet_rpc_server.h" +#include "cryptonote_core/cryptonote_core.h" +#include "p2p/net_node.h" +#include "cryptonote_protocol/cryptonote_protocol_handler.h" #include "common/command_line.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" -#include "wallet_rpc_server_commands_defs.h" #include "misc_language.h" #include "string_tools.h" #include "crypto/hash.h" -#include "zmq.hpp" +#include "wap_library.h" +#include "wap_classes.h" namespace IPC { - class Daemon_ipc_server + namespace Daemon { - boost::thread *server_thread; /*!< Server runs on this thread */ - /*! - * \brief Repeatedly loops processing requests if any. - */ - void poll(); - void handle_request(const std::string &request_string, std::string &response); - bool check_core_busy(); - - void getblocks(std::string &response); - void sendtransactions(std::string &response); - void get_o_indexes(std::string &response); - std::string m_ip; /*!< IP address where its listening */ - std::string m_port; /*!< Port where its listening */ - bool m_is_running; /*!< Whether the server is currently running */ - zmq::socket_t *socket; /*!< 0MQ Socket pointer */ - cryptonote::core *core; /*!< Pointer to the core */ - nodetool::node_server > *p2p; - /*!< Pointer to p2p node server */ - - public: - /** - * \brief Constructor - * \param ip IP address to bind - * \param port Port number to bind - * \param ev_handler Event handler function pointer - */ - Daemon_ipc_server(const std::string &ip, const std::string &port); - - /** - * \brief Destructor - */ - ~Daemon_ipc_server(); - - /*! - * \brief Starts the server - * \return True if start was successful - */ - bool start(); - - /*! - * \brief Stops the server - */ - void stop(); - }; + void start_mining(wap_proto_t *message); + void get_blocks(wap_proto_t *message); + void send_transactions(wap_proto_t *message); + void get_o_indexes(wap_proto_t *message); + void init(cryptonote::core *p_core, + nodetool::node_server > *p_p2p); + } } #endif diff --git a/src/ipc/include/wallet.h b/src/ipc/include/wallet.h new file mode 100644 index 000000000..9a85764c2 --- /dev/null +++ b/src/ipc/include/wallet.h @@ -0,0 +1,18 @@ +/* ========================================================================= + wallet - Monero Wallet API + + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +#ifndef __WALLET_H_INCLUDED__ +#define __WALLET_H_INCLUDED__ + +// Include the project library file +#include "wap_library.h" + +// Add your own public definitions here, if you need them + +#endif diff --git a/src/ipc/include/wap_classes.h b/src/ipc/include/wap_classes.h new file mode 100644 index 000000000..f6b32ab43 --- /dev/null +++ b/src/ipc/include/wap_classes.h @@ -0,0 +1,22 @@ +/* ========================================================================= + wap_classes - private header file + + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) +################################################################################ +# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY # +# Please refer to the README for information about making permanent changes. # +################################################################################ + ========================================================================= +*/ + +#ifndef __WAP_CLASSES_H_INCLUDED__ +#define __WAP_CLASSES_H_INCLUDED__ + +// External API +#include "../include/wallet.h" + +// Internal API + +#endif diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h new file mode 100644 index 000000000..83b66b445 --- /dev/null +++ b/src/ipc/include/wap_client.h @@ -0,0 +1,129 @@ +/* ========================================================================= + wap_client - Wallet Client API + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_client.xml, or + * The code generation script that built this file: zproto_client_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +#ifndef __WAP_CLIENT_H_INCLUDED__ +#define __WAP_CLIENT_H_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +// Opaque class structure +#ifndef WAP_CLIENT_T_DEFINED +typedef struct _wap_client_t wap_client_t; +#define WAP_CLIENT_T_DEFINED +#endif + +// @interface +// Create a new wap_client +// Connect to server endpoint, with specified timeout in msecs (zero means wait +// forever). Constructor succeeds if connection is successful. The caller may +// specify its address. +WAP_EXPORT wap_client_t * + wap_client_new (const char *endpoint, uint32_t timeout, const char *identity); + +// Destroy the wap_client +WAP_EXPORT void + wap_client_destroy (wap_client_t **self_p); + +// Return actor, when caller wants to work with multiple actors and/or +// input sockets asynchronously. +WAP_EXPORT zactor_t * + wap_client_actor (wap_client_t *self); + +// Return message pipe for asynchronous message I/O. In the high-volume case, +// we send methods and get replies to the actor, in a synchronous manner, and +// we send/recv high volume message data to a second pipe, the msgpipe. In +// the low-volume case we can do everything over the actor pipe, if traffic +// is never ambiguous. +WAP_EXPORT zsock_t * + wap_client_msgpipe (wap_client_t *self); + +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p); + +// Send a raw transaction to the daemon. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_put (wap_client_t *self, zchunk_t **tx_data_p); + +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get (wap_client_t *self, const char *tx_id); + +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_save (wap_client_t *self); + +// Send start command to server. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_start (wap_client_t *self, uint64_t start_height); + +// Send stop command to server. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_stop (wap_client_t *self); + +// Return last received status +WAP_EXPORT int + wap_client_status (wap_client_t *self); + +// Return last received reason +WAP_EXPORT const char * + wap_client_reason (wap_client_t *self); + +// Return last received start_height +WAP_EXPORT uint32_t + wap_client_start_height (wap_client_t *self); + +// Return last received curr_height +WAP_EXPORT uint32_t + wap_client_curr_height (wap_client_t *self); + +// Return last received block_status +WAP_EXPORT const char * + wap_client_block_status (wap_client_t *self); + +// Return last received block_data +WAP_EXPORT zmsg_t * + wap_client_block_data (wap_client_t *self); + +// Return last received tx_id +WAP_EXPORT const char * + wap_client_tx_id (wap_client_t *self); + +// Return last received tx_data +WAP_EXPORT zchunk_t * + wap_client_tx_data (wap_client_t *self); + +// To enable verbose tracing (animation) of wap_client instances, set +// this to true. This lets you trace from and including construction. +WAP_EXPORT extern volatile int + wap_client_verbose; +// @end + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc new file mode 100644 index 000000000..c8dc78a7f --- /dev/null +++ b/src/ipc/include/wap_client_engine.inc @@ -0,0 +1,1587 @@ +/* ========================================================================= + wap_client_engine - Wallet Client API engine + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_client.xml, or + * The code generation script that built this file: zproto_client_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + + +// --------------------------------------------------------------------------- +// State machine constants + +typedef enum { + start_state = 1, + expect_open_ok_state = 2, + connected_state = 3, + expect_blocks_ok_state = 4, + expect_get_ok_state = 5, + expect_put_ok_state = 6, + expect_save_ok_state = 7, + expect_start_ok_state = 8, + expect_stop_ok_state = 9, + expect_close_ok_state = 10, + defaults_state = 11, + have_error_state = 12, + reexpect_open_ok_state = 13 +} state_t; + +typedef enum { + NULL_event = 0, + constructor_event = 1, + bad_endpoint_event = 2, + open_ok_event = 3, + expired_event = 4, + blocks_event = 5, + get_event = 6, + put_event = 7, + save_event = 8, + start_event = 9, + stop_event = 10, + destructor_event = 11, + blocks_ok_event = 12, + get_ok_event = 13, + put_ok_event = 14, + save_ok_event = 15, + start_ok_event = 16, + stop_ok_event = 17, + close_ok_event = 18, + connection_pong_event = 19, + error_event = 20, + command_invalid_event = 21, + other_event = 22 +} event_t; + +// Names for state machine logging and error reporting +static char * +s_state_name [] = { + "(NONE)", + "start", + "expect open ok", + "connected", + "expect blocks ok", + "expect get ok", + "expect put ok", + "expect save ok", + "expect start ok", + "expect stop ok", + "expect close ok", + "defaults", + "have error", + "reexpect open ok" +}; + +static char * +s_event_name [] = { + "(NONE)", + "constructor", + "bad_endpoint", + "OPEN_OK", + "expired", + "BLOCKS", + "GET", + "PUT", + "SAVE", + "START", + "STOP", + "destructor", + "BLOCKS_OK", + "GET_OK", + "PUT_OK", + "SAVE_OK", + "START_OK", + "STOP_OK", + "CLOSE_OK", + "connection_pong", + "ERROR", + "command_invalid", + "other" +}; + + +// --------------------------------------------------------------------------- +// Context for the client. This embeds the application-level client context +// at its start (the entire structure, not a reference), so we can cast a +// pointer between client_t and s_client_t arbitrarily. + +// These are the different method arguments we manage automatically +struct _client_args_t { + char *endpoint; + uint32_t timeout; + char *identity; + zlist_t *block_ids; + zchunk_t *tx_data; + char *tx_id; + uint64_t start_height; +}; + +typedef struct { + client_t client; // Application-level client context + zsock_t *cmdpipe; // Get/send commands from caller API + zsock_t *msgpipe; // Get/send messages from caller API + zsock_t *dealer; // Socket to talk to server + zloop_t *loop; // Listen to pipe and dealer + wap_proto_t *message; // Message received or sent + client_args_t args; // Method arguments structure + bool terminated; // True if client is shutdown + size_t timeout; // inactivity timeout, msecs + state_t state; // Current state + event_t event; // Current event + event_t next_event; // The next event + event_t exception; // Exception event, if any + int expiry_timer; // zloop timer for timeouts + int wakeup_timer; // zloop timer for alarms + event_t wakeup_event; // Wake up with this event +} s_client_t; + +static int + client_initialize (client_t *self); +static void + client_terminate (client_t *self); +static void + s_client_destroy (s_client_t **self_p); +static void + s_client_execute (s_client_t *self, event_t event); +static int + s_client_handle_wakeup (zloop_t *loop, int timer_id, void *argument); +static int + s_client_handle_timeout (zloop_t *loop, int timer_id, void *argument); +static void + s_satisfy_pedantic_compilers (void); +static void + connect_to_server_endpoint (client_t *self); +static void + set_client_identity (client_t *self); +static void + use_connect_timeout (client_t *self); +static void + signal_bad_endpoint (client_t *self); +static void + signal_success (client_t *self); +static void + use_heartbeat_timer (client_t *self); +static void + signal_server_not_present (client_t *self); +static void + prepare_blocks_command (client_t *self); +static void + prepare_get_command (client_t *self); +static void + prepare_put_command (client_t *self); +static void + prepare_save_command (client_t *self); +static void + prepare_start_command (client_t *self); +static void + signal_have_blocks_ok (client_t *self); +static void + signal_have_get_ok (client_t *self); +static void + signal_have_put_ok (client_t *self); +static void + signal_have_save_ok (client_t *self); +static void + signal_have_start_ok (client_t *self); +static void + signal_have_stop_ok (client_t *self); +static void + signal_failure (client_t *self); +static void + check_status_code (client_t *self); +static void + signal_unhandled_error (client_t *self); + +// Global tracing/animation indicator; we can't use a client method as +// that only works after construction (which we often want to trace). +volatile int wap_client_verbose = false; + +// Create a new client connection + +static s_client_t * +s_client_new (zsock_t *cmdpipe, zsock_t *msgpipe) +{ + s_client_t *self = (s_client_t *) zmalloc (sizeof (s_client_t)); + if (self) { + assert ((s_client_t *) &self->client == self); + self->cmdpipe = cmdpipe; + self->msgpipe = msgpipe; + self->dealer = zsock_new (ZMQ_DEALER); + if (self->dealer) + self->message = wap_proto_new (); + if (self->message) + self->loop = zloop_new (); + if (self->loop) { + // Give application chance to initialize and set next event + self->state = start_state; + self->event = NULL_event; + self->client.cmdpipe = self->cmdpipe; + self->client.msgpipe = self->msgpipe; + self->client.dealer = self->dealer; + self->client.message = self->message; + self->client.args = &self->args; + if (client_initialize (&self->client)) + s_client_destroy (&self); + } + else + s_client_destroy (&self); + } + s_satisfy_pedantic_compilers (); + return self; +} + +// Destroy the client connection + +static void +s_client_destroy (s_client_t **self_p) +{ + assert (self_p); + if (*self_p) { + s_client_t *self = *self_p; + zstr_free (&self->args.endpoint); + zstr_free (&self->args.identity); + zlist_destroy (&self->args.block_ids); + zchunk_destroy (&self->args.tx_data); + zstr_free (&self->args.tx_id); + client_terminate (&self->client); + wap_proto_destroy (&self->message); + zsock_destroy (&self->msgpipe); + zsock_destroy (&self->dealer); + zloop_destroy (&self->loop); + free (self); + *self_p = NULL; + } +} + +// --------------------------------------------------------------------------- +// These methods are an internal API for actions + +// Set the next event, needed in at least one action in an internal +// state; otherwise the state machine will wait for a message on the +// dealer socket and treat that as the event. + +static void +engine_set_next_event (client_t *client, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->next_event = event; + } +} + +// Raise an exception with 'event', halting any actions in progress. +// Continues execution of actions defined for the exception event. + +static void +engine_set_exception (client_t *client, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->exception = event; + } +} + +// Set wakeup alarm after 'delay' msecs. The next state should handle the +// wakeup event. The alarm is cancelled on any other event. + +static void +engine_set_wakeup_event (client_t *client, size_t delay, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + if (self->wakeup_timer) { + zloop_timer_end (self->loop, self->wakeup_timer); + self->wakeup_timer = 0; + } + self->wakeup_timer = zloop_timer ( + self->loop, delay, 1, s_client_handle_wakeup, self); + self->wakeup_event = event; + } +} + +// Set timeout for next protocol read. By default, will wait forever +// or until the process is interrupted. The timeout is in milliseconds. +// The state machine must handle the "expired" event. + +static void +engine_set_timeout (client_t *client, size_t timeout) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->timeout = timeout; + if (self->timeout) + self->expiry_timer = zloop_timer ( + self->loop, self->timeout, 1, s_client_handle_timeout, self); + } +} + +// Poll socket for activity, invoke handler on any received message. +// Handler must be a CZMQ zloop_fn function; receives client as arg. + +static void +engine_handle_socket (client_t *client, zsock_t *sock, zloop_reader_fn handler) +{ + if (client && sock) { + s_client_t *self = (s_client_t *) client; + if (handler != NULL) { + int rc = zloop_reader (self->loop, sock, handler, self); + assert (rc == 0); + zloop_reader_set_tolerant (self->loop, sock); + } + else + zloop_reader_end (self->loop, sock); + } +} + +// Pedantic compilers don't like unused functions, so we call the whole +// API, passing null references. It's nasty and horrid and sufficient. + +static void +s_satisfy_pedantic_compilers (void) +{ + engine_set_next_event (NULL, NULL_event); + engine_set_exception (NULL, NULL_event); + engine_set_timeout (NULL, 0); + engine_set_wakeup_event (NULL, 0, NULL_event); + engine_handle_socket (NULL, 0, NULL); +} + + +// --------------------------------------------------------------------------- +// Generic methods on protocol messages +// TODO: replace with lookup table, since ID is one byte + +static event_t +s_protocol_event (s_client_t *self, wap_proto_t *message) +{ + assert (message); + switch (wap_proto_id (message)) { + case WAP_PROTO_OPEN_OK: + return open_ok_event; + break; + case WAP_PROTO_BLOCKS: + return blocks_event; + break; + case WAP_PROTO_BLOCKS_OK: + return blocks_ok_event; + break; + case WAP_PROTO_PUT: + return put_event; + break; + case WAP_PROTO_PUT_OK: + return put_ok_event; + break; + case WAP_PROTO_GET: + return get_event; + break; + case WAP_PROTO_GET_OK: + return get_ok_event; + break; + case WAP_PROTO_SAVE: + return save_event; + break; + case WAP_PROTO_SAVE_OK: + return save_ok_event; + break; + case WAP_PROTO_START: + return start_event; + break; + case WAP_PROTO_START_OK: + return start_ok_event; + break; + case WAP_PROTO_STOP: + return stop_event; + break; + case WAP_PROTO_STOP_OK: + return stop_ok_event; + break; + case WAP_PROTO_CLOSE_OK: + return close_ok_event; + break; + case WAP_PROTO_ERROR: + return error_event; + break; + default: + zsys_error ("wap_client: unknown command %s, halting", wap_proto_command (message)); + self->terminated = true; + return NULL_event; + } +} + + +// Execute state machine as long as we have events; if event is NULL_event, +// or state machine is terminated, do nothing. + +static void +s_client_execute (s_client_t *self, event_t event) +{ + self->next_event = event; + // Cancel wakeup timer, if any was pending + if (self->wakeup_timer) { + zloop_timer_end (self->loop, self->wakeup_timer); + self->wakeup_timer = 0; + } + while (!self->terminated && self->next_event != NULL_event) { + self->event = self->next_event; + self->next_event = NULL_event; + self->exception = NULL_event; + if (wap_client_verbose) { + zsys_debug ("wap_client: %s:", s_state_name [self->state]); + zsys_debug ("wap_client: %s", s_event_name [self->event]); + } + switch (self->state) { + case start_state: + if (self->event == constructor_event) { + if (!self->exception) { + // connect to server endpoint + if (wap_client_verbose) + zsys_debug ("wap_client: $ connect to server endpoint"); + connect_to_server_endpoint (&self->client); + } + if (!self->exception) { + // set client identity + if (wap_client_verbose) + zsys_debug ("wap_client: $ set client identity"); + set_client_identity (&self->client); + } + if (!self->exception) { + // use connect timeout + if (wap_client_verbose) + zsys_debug ("wap_client: $ use connect timeout"); + use_connect_timeout (&self->client); + } + if (!self->exception) { + // send OPEN + if (wap_client_verbose) + zsys_debug ("wap_client: $ send OPEN"); + wap_proto_set_id (self->message, WAP_PROTO_OPEN); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_open_ok_state; + } + else + if (self->event == bad_endpoint_event) { + if (!self->exception) { + // signal bad endpoint + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal bad endpoint"); + signal_bad_endpoint (&self->client); + } + if (!self->exception) { + // terminate + if (wap_client_verbose) + zsys_debug ("wap_client: $ terminate"); + self->terminated = true; + } + } + else { + // Handle unexpected internal events + zsys_warning ("wap_client: unhandled event %s in %s", + s_event_name [self->event], s_state_name [self->state]); + assert (false); + } + break; + + case expect_open_ok_state: + if (self->event == open_ok_event) { + if (!self->exception) { + // signal success + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal success"); + signal_success (&self->client); + } + if (!self->exception) { + // use heartbeat timer + if (wap_client_verbose) + zsys_debug ("wap_client: $ use heartbeat timer"); + use_heartbeat_timer (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == expired_event) { + if (!self->exception) { + // signal server not present + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal server not present"); + signal_server_not_present (&self->client); + } + if (!self->exception) { + // terminate + if (wap_client_verbose) + zsys_debug ("wap_client: $ terminate"); + self->terminated = true; + } + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case connected_state: + if (self->event == blocks_event) { + if (!self->exception) { + // prepare blocks command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare blocks command"); + prepare_blocks_command (&self->client); + } + if (!self->exception) { + // send BLOCKS + if (wap_client_verbose) + zsys_debug ("wap_client: $ send BLOCKS"); + wap_proto_set_id (self->message, WAP_PROTO_BLOCKS); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_blocks_ok_state; + } + else + if (self->event == get_event) { + if (!self->exception) { + // prepare get command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get command"); + prepare_get_command (&self->client); + } + if (!self->exception) { + // send GET + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET"); + wap_proto_set_id (self->message, WAP_PROTO_GET); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_ok_state; + } + else + if (self->event == put_event) { + if (!self->exception) { + // prepare put command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare put command"); + prepare_put_command (&self->client); + } + if (!self->exception) { + // send PUT + if (wap_client_verbose) + zsys_debug ("wap_client: $ send PUT"); + wap_proto_set_id (self->message, WAP_PROTO_PUT); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_put_ok_state; + } + else + if (self->event == save_event) { + if (!self->exception) { + // prepare save command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare save command"); + prepare_save_command (&self->client); + } + if (!self->exception) { + // send SAVE + if (wap_client_verbose) + zsys_debug ("wap_client: $ send SAVE"); + wap_proto_set_id (self->message, WAP_PROTO_SAVE); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_save_ok_state; + } + else + if (self->event == start_event) { + if (!self->exception) { + // prepare start command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare start command"); + prepare_start_command (&self->client); + } + if (!self->exception) { + // send START + if (wap_client_verbose) + zsys_debug ("wap_client: $ send START"); + wap_proto_set_id (self->message, WAP_PROTO_START); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_start_ok_state; + } + else + if (self->event == stop_event) { + if (!self->exception) { + // send STOP + if (wap_client_verbose) + zsys_debug ("wap_client: $ send STOP"); + wap_proto_set_id (self->message, WAP_PROTO_STOP); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_stop_ok_state; + } + else + if (self->event == destructor_event) { + if (!self->exception) { + // send CLOSE + if (wap_client_verbose) + zsys_debug ("wap_client: $ send CLOSE"); + wap_proto_set_id (self->message, WAP_PROTO_CLOSE); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_close_ok_state; + } + else + if (self->event == expired_event) { + if (!self->exception) { + // send PING + if (wap_client_verbose) + zsys_debug ("wap_client: $ send PING"); + wap_proto_set_id (self->message, WAP_PROTO_PING); + wap_proto_send (self->message, self->dealer); + } + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_blocks_ok_state: + if (self->event == blocks_ok_event) { + if (!self->exception) { + // signal have blocks ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have blocks ok"); + signal_have_blocks_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_get_ok_state: + if (self->event == get_ok_event) { + if (!self->exception) { + // signal have get ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get ok"); + signal_have_get_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_put_ok_state: + if (self->event == put_ok_event) { + if (!self->exception) { + // signal have put ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have put ok"); + signal_have_put_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_save_ok_state: + if (self->event == save_ok_event) { + if (!self->exception) { + // signal have save ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have save ok"); + signal_have_save_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_start_ok_state: + if (self->event == start_ok_event) { + if (!self->exception) { + // signal have start ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have start ok"); + signal_have_start_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_stop_ok_state: + if (self->event == stop_ok_event) { + if (!self->exception) { + // signal have stop ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have stop ok"); + signal_have_stop_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case expect_close_ok_state: + if (self->event == close_ok_event) { + if (!self->exception) { + // signal success + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal success"); + signal_success (&self->client); + } + if (!self->exception) { + // terminate + if (wap_client_verbose) + zsys_debug ("wap_client: $ terminate"); + self->terminated = true; + } + } + else + if (self->event == expired_event) { + if (!self->exception) { + // signal failure + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal failure"); + signal_failure (&self->client); + } + if (!self->exception) { + // terminate + if (wap_client_verbose) + zsys_debug ("wap_client: $ terminate"); + self->terminated = true; + } + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case defaults_state: + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + + case have_error_state: + if (self->event == command_invalid_event) { + if (!self->exception) { + // use connect timeout + if (wap_client_verbose) + zsys_debug ("wap_client: $ use connect timeout"); + use_connect_timeout (&self->client); + } + if (!self->exception) { + // send OPEN + if (wap_client_verbose) + zsys_debug ("wap_client: $ send OPEN"); + wap_proto_set_id (self->message, WAP_PROTO_OPEN); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = reexpect_open_ok_state; + } + else + if (self->event == other_event) { + if (!self->exception) { + // signal unhandled error + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal unhandled error"); + signal_unhandled_error (&self->client); + } + if (!self->exception) { + // terminate + if (wap_client_verbose) + zsys_debug ("wap_client: $ terminate"); + self->terminated = true; + } + } + else { + // Handle unexpected internal events + zsys_warning ("wap_client: unhandled event %s in %s", + s_event_name [self->event], s_state_name [self->state]); + assert (false); + } + break; + + case reexpect_open_ok_state: + if (self->event == open_ok_event) { + if (!self->exception) { + // use heartbeat timer + if (wap_client_verbose) + zsys_debug ("wap_client: $ use heartbeat timer"); + use_heartbeat_timer (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == connection_pong_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + } + // If we had an exception event, interrupt normal programming + if (self->exception) { + if (wap_client_verbose) + zsys_debug ("wap_client: ! %s", s_event_name [self->exception]); + self->next_event = self->exception; + } + else + if (wap_client_verbose) + zsys_debug ("wap_client: > %s", s_state_name [self->state]); + } +} + +// zloop callback when client inactivity timer expires + +static int +s_client_handle_timeout (zloop_t *loop, int timer_id, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + s_client_execute (self, expired_event); + return self->terminated? -1: 0; +} + +// zloop callback when client wakeup timer expires + +static int +s_client_handle_wakeup (zloop_t *loop, int timer_id, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + s_client_execute (self, self->wakeup_event); + return 0; +} + + +// Handle command pipe to/from calling API + +static int +s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + char *method = zstr_recv (self->cmdpipe); + if (!method) + return -1; // Interrupted; exit zloop + if (wap_client_verbose) + zsys_debug ("wap_client: API command=%s", method); + + if (streq (method, "$TERM")) + self->terminated = true; // Shutdown the engine + else + if (streq (method, "CONSTRUCTOR")) { + zstr_free (&self->args.endpoint); + zstr_free (&self->args.identity); + zsock_recv (self->cmdpipe, "sis", &self->args.endpoint, &self->args.timeout, &self->args.identity); + s_client_execute (self, constructor_event); + } + else + if (streq (method, "DESTRUCTOR")) { + s_client_execute (self, destructor_event); + } + else + if (streq (method, "BLOCKS")) { + zlist_destroy (&self->args.block_ids); + zsock_recv (self->cmdpipe, "p", &self->args.block_ids); + s_client_execute (self, blocks_event); + } + else + if (streq (method, "PUT")) { + zchunk_destroy (&self->args.tx_data); + zsock_recv (self->cmdpipe, "p", &self->args.tx_data); + s_client_execute (self, put_event); + } + else + if (streq (method, "GET")) { + zstr_free (&self->args.tx_id); + zsock_recv (self->cmdpipe, "s", &self->args.tx_id); + s_client_execute (self, get_event); + } + else + if (streq (method, "SAVE")) { + s_client_execute (self, save_event); + } + else + if (streq (method, "START")) { + zsock_recv (self->cmdpipe, "i", &self->args.start_height); + s_client_execute (self, start_event); + } + else + if (streq (method, "STOP")) { + s_client_execute (self, stop_event); + } + // Cleanup pipe if any argument frames are still waiting to be eaten + if (zsock_rcvmore (self->cmdpipe)) { + zsys_error ("wap_client: trailing API command frames (%s)", method); + zmsg_t *more = zmsg_recv (self->cmdpipe); + zmsg_print (more); + zmsg_destroy (&more); + } + zstr_free (&method); + return self->terminated? -1: 0; +} + + +// Handle message pipe to/from calling API + +static int +s_client_handle_msgpipe (zloop_t *loop, zsock_t *reader, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + + // We will process as many messages as we can, to reduce the overhead + // of polling and the reactor: + while (zsock_events (self->msgpipe) & ZMQ_POLLIN) { + char *method = zstr_recv (self->msgpipe); + if (!method) + return -1; // Interrupted; exit zloop + if (wap_client_verbose) + zsys_debug ("wap_client: API message=%s", method); + + // Front-end shuts down msgpipe before cmdpipe + if (streq (method, "$TERM")) + zsock_signal (self->cmdpipe, 0); + // Cleanup pipe if any argument frames are still waiting to be eaten + if (zsock_rcvmore (self->msgpipe)) { + zsys_error ("wap_client: trailing API message frames (%s)", method); + zmsg_t *more = zmsg_recv (self->msgpipe); + zmsg_print (more); + zmsg_destroy (&more); + } + zstr_free (&method); + } + return 0; +} + + +// Handle a message (a protocol reply) from the server + +static int +s_client_handle_protocol (zloop_t *loop, zsock_t *reader, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + + // We will process as many messages as we can, to reduce the overhead + // of polling and the reactor: + while (zsock_events (self->dealer) & ZMQ_POLLIN) { + if (wap_proto_recv (self->message, self->dealer)) + return -1; // Interrupted; exit zloop + + // Any input from server counts as activity + if (self->expiry_timer) { + zloop_timer_end (self->loop, self->expiry_timer); + self->expiry_timer = 0; + } + // Reset expiry timer if timeout is not zero + if (self->timeout) + self->expiry_timer = zloop_timer ( + self->loop, self->timeout, 1, s_client_handle_timeout, self); + s_client_execute (self, s_protocol_event (self, self->message)); + if (self->terminated) + return -1; + } + return 0; +} + + +// --------------------------------------------------------------------------- +// This is the client actor, which polls its two sockets and processes +// incoming messages + +void +wap_client (zsock_t *cmdpipe, void *msgpipe) +{ + // Initialize + s_client_t *self = s_client_new (cmdpipe, (zsock_t *) msgpipe); + if (self) { + zsock_signal (cmdpipe, 0); + + // Set up handler for the sockets the client uses + engine_handle_socket ((client_t *) self, self->cmdpipe, s_client_handle_cmdpipe); + engine_handle_socket ((client_t *) self, self->msgpipe, s_client_handle_msgpipe); + engine_handle_socket ((client_t *) self, self->dealer, s_client_handle_protocol); + + // Run reactor until there's a termination signal + zloop_start (self->loop); + + // Reactor has ended + s_client_destroy (&self); + } + else + zsock_signal (cmdpipe, -1); +} + + +// --------------------------------------------------------------------------- +// Class interface + +struct _wap_client_t { + zactor_t *actor; // Client actor + zsock_t *msgpipe; // Pipe for async message flow + int status; // Returned by actor reply + char *reason; // Returned by actor reply + uint32_t start_height; // Returned by actor reply + uint32_t curr_height; // Returned by actor reply + char *block_status; // Returned by actor reply + zmsg_t *block_data; // Returned by actor reply + char *tx_id; // Returned by actor reply + zchunk_t *tx_data; // Returned by actor reply +}; + + +// --------------------------------------------------------------------------- +// Create a new wap_client +// Connect to server endpoint, with specified timeout in msecs (zero means wait +// forever). Constructor succeeds if connection is successful. The caller may +// specify its address. + +static int +wap_client_constructor (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity); + +WAP_EXPORT wap_client_t * +wap_client_new (const char *endpoint, uint32_t timeout, const char *identity) +{ + wap_client_t *self = (wap_client_t *) zmalloc (sizeof (wap_client_t)); + if (self) { + zsock_t *backend; + self->msgpipe = zsys_create_pipe (&backend); + self->actor = zactor_new (wap_client, backend); + if (self->actor) + self->status = wap_client_constructor (self, endpoint, timeout, identity); + if (self->status == -1) + zactor_destroy (&self->actor); + if (!self->actor) + wap_client_destroy (&self); + } + return self; +} + + +// --------------------------------------------------------------------------- +// Destroy the wap_client +// Disconnect from server. Waits for a short timeout for confirmation from the +// server, then disconnects anyhow. + +static int +wap_client_destructor (wap_client_t *self); + +void +wap_client_destroy (wap_client_t **self_p) +{ + assert (self_p); + if (*self_p) { + wap_client_t *self = *self_p; + if (self->actor && !zsys_interrupted) { + // Shut down msgpipe first so that client can do clean shutdown, + // sending any pending messages and handshaking goodbye to server + zstr_send (self->msgpipe, "$TERM"); + zsock_wait (self->actor); + wap_client_destructor (self); + } + zactor_destroy (&self->actor); + zsock_destroy (&self->msgpipe); + zstr_free (&self->reason); + zstr_free (&self->block_status); + zmsg_destroy (&self->block_data); + zstr_free (&self->tx_id); + zchunk_destroy (&self->tx_data); + free (self); + *self_p = NULL; + } +} + + +// --------------------------------------------------------------------------- +// Return actor, when caller wants to work with multiple actors and/or +// input sockets asynchronously. + +zactor_t * +wap_client_actor (wap_client_t *self) +{ + assert (self); + return self->actor; +} + + +// --------------------------------------------------------------------------- +// Return message pipe for asynchronous message I/O. In the high-volume case, +// we send methods and get replies to the actor, in a synchronous manner, and +// we send/recv high volume message data to a second pipe, the msgpipe. In +// the low-volume case we can do everything over the actor pipe, if traffic +// is never ambiguous. + +zsock_t * +wap_client_msgpipe (wap_client_t *self) +{ + assert (self); + return self->msgpipe; +} + + +// --------------------------------------------------------------------------- +// Get valid reply from actor; discard replies that does not match. Current +// implementation filters on first frame of message. Blocks until a valid +// reply is received, and properties can be loaded from it. Returns 0 if +// matched, -1 if interrupted or timed-out. + +static int +s_accept_reply (wap_client_t *self, ...) +{ + assert (self); + while (!zsys_interrupted) { + char *reply = zstr_recv (self->actor); + if (!reply) + break; // Interrupted or timed-out + + va_list args; + va_start (args, self); + char *filter = va_arg (args, char *); + while (filter) { + if (streq (reply, filter)) { + if (streq (reply, "SUCCESS")) { + zsock_recv (self->actor, "i", &self->status); + } + else + if (streq (reply, "FAILURE")) { + zstr_free (&self->reason); + zsock_recv (self->actor, "is", &self->status, &self->reason); + } + else + if (streq (reply, "BLOCKS OK")) { + zstr_free (&self->block_status); + zmsg_destroy (&self->block_data); + zsock_recv (self->actor, "iiisp", &self->status, &self->start_height, &self->curr_height, &self->block_status, &self->block_data); + } + else + if (streq (reply, "PUT OK")) { + zstr_free (&self->tx_id); + zsock_recv (self->actor, "is", &self->status, &self->tx_id); + } + else + if (streq (reply, "GET OK")) { + zchunk_destroy (&self->tx_data); + zsock_recv (self->actor, "ip", &self->status, &self->tx_data); + } + else + if (streq (reply, "SAVE OK")) { + zsock_recv (self->actor, "i", &self->status); + } + else + if (streq (reply, "START OK")) { + zsock_recv (self->actor, "ii", &self->status, &self->curr_height); + } + else + if (streq (reply, "STOP OK")) { + zsock_recv (self->actor, "i", &self->status); + } + break; + } + filter = va_arg (args, char *); + } + va_end (args); + // If anything was remaining on pipe, flush it + zsock_flush (self->actor); + if (filter) { + zstr_free (&reply); + return 0; // We matched one of the filters + } + } + return -1; // Interrupted or timed-out +} + + +// --------------------------------------------------------------------------- +// Connect to server endpoint, with specified timeout in msecs (zero means wait +// forever). Constructor succeeds if connection is successful. The caller may +// specify its address. +// Returns >= 0 if successful, -1 if interrupted. + +static int +wap_client_constructor (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity) +{ + assert (self); + zsock_send (self->actor, "ssis", "CONSTRUCTOR", endpoint, timeout, identity); + if (s_accept_reply (self, "SUCCESS", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Disconnect from server. Waits for a short timeout for confirmation from the +// server, then disconnects anyhow. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_destructor (wap_client_t *self) +{ + assert (self); + zsock_send (self->actor, "s", "DESTRUCTOR"); + if (s_accept_reply (self, "SUCCESS", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p) +{ + assert (self); + zsock_send (self->actor, "sp", "BLOCKS", *block_ids_p); + *block_ids_p = NULL; // Take ownership of block_ids + if (s_accept_reply (self, "BLOCKS OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Send a raw transaction to the daemon. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_put (wap_client_t *self, zchunk_t **tx_data_p) +{ + assert (self); + zsock_send (self->actor, "sp", "PUT", *tx_data_p); + *tx_data_p = NULL; // Take ownership of tx_data + if (s_accept_reply (self, "PUT OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get (wap_client_t *self, const char *tx_id) +{ + assert (self); + zsock_send (self->actor, "ss", "GET", tx_id); + if (s_accept_reply (self, "GET OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Request a set of blocks from the server. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_save (wap_client_t *self) +{ + assert (self); + zsock_send (self->actor, "s", "SAVE"); + if (s_accept_reply (self, "SAVE OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Send start command to server. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_start (wap_client_t *self, uint64_t start_height) +{ + assert (self); + zsock_send (self->actor, "si", "START", start_height); + if (s_accept_reply (self, "START OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Send stop command to server. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_stop (wap_client_t *self) +{ + assert (self); + zsock_send (self->actor, "s", "STOP"); + if (s_accept_reply (self, "STOP OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Return last received status + +int +wap_client_status (wap_client_t *self) +{ + assert (self); + return self->status; +} + + +// --------------------------------------------------------------------------- +// Return last received reason + +const char * +wap_client_reason (wap_client_t *self) +{ + assert (self); + return self->reason; +} + + +// --------------------------------------------------------------------------- +// Return last received start_height + +uint32_t +wap_client_start_height (wap_client_t *self) +{ + assert (self); + return self->start_height; +} + + +// --------------------------------------------------------------------------- +// Return last received curr_height + +uint32_t +wap_client_curr_height (wap_client_t *self) +{ + assert (self); + return self->curr_height; +} + + +// --------------------------------------------------------------------------- +// Return last received block_status + +const char * +wap_client_block_status (wap_client_t *self) +{ + assert (self); + return self->block_status; +} + + +// --------------------------------------------------------------------------- +// Return last received block_data + +zmsg_t * +wap_client_block_data (wap_client_t *self) +{ + assert (self); + return self->block_data; +} + + +// --------------------------------------------------------------------------- +// Return last received tx_id + +const char * +wap_client_tx_id (wap_client_t *self) +{ + assert (self); + return self->tx_id; +} + + +// --------------------------------------------------------------------------- +// Return last received tx_data + +zchunk_t * +wap_client_tx_data (wap_client_t *self) +{ + assert (self); + return self->tx_data; +} diff --git a/src/ipc/include/wap_library.h b/src/ipc/include/wap_library.h new file mode 100644 index 000000000..d0e4e0ab3 --- /dev/null +++ b/src/ipc/include/wap_library.h @@ -0,0 +1,64 @@ +/* ========================================================================= + wallet - WALLET wrapper + + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + +################################################################################ +# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY # +# Please refer to the README for information about making permanent changes. # +################################################################################ + ========================================================================= +*/ + +#ifndef __wap_library_H_INCLUDED__ +#define __wap_library_H_INCLUDED__ + +// External dependencies +#include + +// WALLET version macros for compile-time API detection + +#define WALLET_VERSION_MAJOR 0 +#define WALLET_VERSION_MINOR 0 +#define WALLET_VERSION_PATCH 1 + +#define WALLET_MAKE_VERSION(major, minor, patch) \ + ((major) * 10000 + (minor) * 100 + (patch)) +#define WALLET_VERSION \ + WALLET_MAKE_VERSION(WALLET_VERSION_MAJOR, WALLET_VERSION_MINOR, WALLET_VERSION_PATCH) + +#if defined (__WINDOWS__) +# if defined LIBWAP_STATIC +# define WAP_EXPORT +# elif defined LIBWAP_EXPORTS +# define WAP_EXPORT __declspec(dllexport) +# else +# define WAP_EXPORT __declspec(dllimport) +# endif +#else +# define WAP_EXPORT +#endif + +// Opaque class structures to allow forward references +typedef struct _wap_proto_t wap_proto_t; +#define WAP_PROTO_T_DEFINED +typedef struct _wap_server_t wap_server_t; +#define WAP_SERVER_T_DEFINED +typedef struct _wap_client_t wap_client_t; +#define WAP_CLIENT_T_DEFINED + + +// Public API classes +#include "wap_proto.h" +#include "wap_server.h" +#include "wap_client.h" + +#endif +/* +################################################################################ +# THIS FILE IS 100% GENERATED BY ZPROJECT; DO NOT EDIT EXCEPT EXPERIMENTALLY # +# Please refer to the README for information about making permanent changes. # +################################################################################ +*/ diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h new file mode 100644 index 000000000..8d8611231 --- /dev/null +++ b/src/ipc/include/wap_proto.h @@ -0,0 +1,251 @@ +/* ========================================================================= + wap_proto - Wallet Access Protocol + + Codec header for wap_proto. + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_proto.xml, or + * The code generation script that built this file: zproto_codec_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +#ifndef __WAP_PROTO_H_INCLUDED__ +#define __WAP_PROTO_H_INCLUDED__ + +/* These are the wap_proto messages: + + OPEN - Wallet opens a connection to the daemon. + protocol string + version number 2 Protocol version 1 + identity string Wallet identity + + OPEN_OK - Daemon accepts wallet open request. + + BLOCKS - Wallet requests a set of blocks from the daemon. Daemon replies with +BLOCKS-OK, or ERROR if the request is invalid. + block_ids strings + + BLOCKS_OK - Daemon returns a set of blocks to the wallet. + start_height number 8 + curr_height number 8 + block_status string + block_data msg Frames of block data + + PUT - Wallet sends a raw transaction to the daemon. Daemon replies with +PUT-OK, or ERROR. + tx_data chunk Transaction data + + PUT_OK - Daemon confirms that it accepted the raw transaction. + tx_id string Transaction ID + + GET - Wallet requests transaction data from the daemon. Daemon replies +with GET-OK, or ERROR. + tx_id string Transaction ID + + GET_OK - Daemon replies with transaction data. + tx_data chunk Transaction data + + SAVE - save_bc command. Details tbd. + + SAVE_OK - Daemon replies to a save_bc command. + + START - Wallet asks daemon to start mining. Daemon replies with START-OK, or +ERROR. + start_height number 8 + + START_OK - Daemon replies to a start mining request. + curr_height number 8 + + STOP - Wallet asks daemon to start mining. Daemon replies with START-OK, or +ERROR. + + STOP_OK - Daemon replies to a stop mining request. + + CLOSE - Wallet closes the connection. This is polite though not mandatory. +Daemon will reply with CLOSE-OK or ERROR. + + CLOSE_OK - Daemon replies to a wallet connection close request. + + PING - Wallet heartbeats an idle connection. + + PING_OK - Daemon replies to a wallet ping request. + + ERROR - Daemon replies with failure status. Status codes tbd. + status number 2 Error status + reason string Printable explanation +*/ + +#define WAP_PROTO_SUCCESS 200 +#define WAP_PROTO_NOT_DELIVERED 300 +#define WAP_PROTO_CONTENT_TOO_LARGE 301 +#define WAP_PROTO_TIMEOUT_EXPIRED 302 +#define WAP_PROTO_CONNECTION_REFUSED 303 +#define WAP_PROTO_RESOURCE_LOCKED 400 +#define WAP_PROTO_ACCESS_REFUSED 401 +#define WAP_PROTO_NOT_FOUND 404 +#define WAP_PROTO_COMMAND_INVALID 500 +#define WAP_PROTO_NOT_IMPLEMENTED 501 +#define WAP_PROTO_INTERNAL_ERROR 502 + +#define WAP_PROTO_OPEN 1 +#define WAP_PROTO_OPEN_OK 2 +#define WAP_PROTO_BLOCKS 3 +#define WAP_PROTO_BLOCKS_OK 4 +#define WAP_PROTO_PUT 5 +#define WAP_PROTO_PUT_OK 6 +#define WAP_PROTO_GET 7 +#define WAP_PROTO_GET_OK 8 +#define WAP_PROTO_SAVE 9 +#define WAP_PROTO_SAVE_OK 10 +#define WAP_PROTO_START 11 +#define WAP_PROTO_START_OK 12 +#define WAP_PROTO_STOP 13 +#define WAP_PROTO_STOP_OK 14 +#define WAP_PROTO_CLOSE 15 +#define WAP_PROTO_CLOSE_OK 16 +#define WAP_PROTO_PING 17 +#define WAP_PROTO_PING_OK 18 +#define WAP_PROTO_ERROR 19 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Opaque class structure +#ifndef WAP_PROTO_T_DEFINED +typedef struct _wap_proto_t wap_proto_t; +#define WAP_PROTO_T_DEFINED +#endif + +// @interface +// Create a new empty wap_proto +wap_proto_t * + wap_proto_new (void); + +// Destroy a wap_proto instance +void + wap_proto_destroy (wap_proto_t **self_p); + +// Receive a wap_proto from the socket. Returns 0 if OK, -1 if +// there was an error. Blocks if there is no message waiting. +int + wap_proto_recv (wap_proto_t *self, zsock_t *input); + +// Send the wap_proto to the output socket, does not destroy it +int + wap_proto_send (wap_proto_t *self, zsock_t *output); + +// Print contents of message to stdout +void + wap_proto_print (wap_proto_t *self); + +// Get/set the message routing id +zframe_t * + wap_proto_routing_id (wap_proto_t *self); +void + wap_proto_set_routing_id (wap_proto_t *self, zframe_t *routing_id); + +// Get the wap_proto id and printable command +int + wap_proto_id (wap_proto_t *self); +void + wap_proto_set_id (wap_proto_t *self, int id); +const char * + wap_proto_command (wap_proto_t *self); + +// Get/set the identity field +const char * + wap_proto_identity (wap_proto_t *self); +void + wap_proto_set_identity (wap_proto_t *self, const char *value); + +// Get/set the block_ids field +zlist_t * + wap_proto_block_ids (wap_proto_t *self); +// Get the block_ids field and transfer ownership to caller +zlist_t * + wap_proto_get_block_ids (wap_proto_t *self); +// Set the block_ids field, transferring ownership from caller +void + wap_proto_set_block_ids (wap_proto_t *self, zlist_t **block_ids_p); + +// Get/set the start_height field +uint64_t + wap_proto_start_height (wap_proto_t *self); +void + wap_proto_set_start_height (wap_proto_t *self, uint64_t start_height); + +// Get/set the curr_height field +uint64_t + wap_proto_curr_height (wap_proto_t *self); +void + wap_proto_set_curr_height (wap_proto_t *self, uint64_t curr_height); + +// Get/set the block_status field +const char * + wap_proto_block_status (wap_proto_t *self); +void + wap_proto_set_block_status (wap_proto_t *self, const char *value); + +// Get a copy of the block_data field +zmsg_t * + wap_proto_block_data (wap_proto_t *self); +// Get the block_data field and transfer ownership to caller +zmsg_t * + wap_proto_get_block_data (wap_proto_t *self); +// Set the block_data field, transferring ownership from caller +void + wap_proto_set_block_data (wap_proto_t *self, zmsg_t **msg_p); + +// Get a copy of the tx_data field +zchunk_t * + wap_proto_tx_data (wap_proto_t *self); +// Get the tx_data field and transfer ownership to caller +zchunk_t * + wap_proto_get_tx_data (wap_proto_t *self); +// Set the tx_data field, transferring ownership from caller +void + wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p); + +// Get/set the tx_id field +const char * + wap_proto_tx_id (wap_proto_t *self); +void + wap_proto_set_tx_id (wap_proto_t *self, const char *value); + +// Get/set the status field +uint16_t + wap_proto_status (wap_proto_t *self); +void + wap_proto_set_status (wap_proto_t *self, uint16_t status); + +// Get/set the reason field +const char * + wap_proto_reason (wap_proto_t *self); +void + wap_proto_set_reason (wap_proto_t *self, const char *value); + +// Self test of this class +int + wap_proto_test (bool verbose); +// @end + +// For backwards compatibility with old codecs +#define wap_proto_dump wap_proto_print + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ipc/include/wap_server.h b/src/ipc/include/wap_server.h new file mode 100644 index 000000000..73a84f475 --- /dev/null +++ b/src/ipc/include/wap_server.h @@ -0,0 +1,85 @@ +/* ========================================================================= + wap_server - Wallet Server + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_server.xml, or + * The code generation script that built this file: zproto_server_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +#ifndef __WAP_SERVER_H_INCLUDED__ +#define __WAP_SERVER_H_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +// @interface +// To work with wap_server, use the CZMQ zactor API: +// +// Create new wap_server instance, passing logging prefix: +// +// zactor_t *wap_server = zactor_new (wap_server, "myname"); +// +// Destroy wap_server instance +// +// zactor_destroy (&wap_server); +// +// Enable verbose logging of commands and activity: +// +// zstr_send (wap_server, "VERBOSE"); +// +// Bind wap_server to specified endpoint. TCP endpoints may specify +// the port number as "*" to aquire an ephemeral port: +// +// zstr_sendx (wap_server, "BIND", endpoint, NULL); +// +// Return assigned port number, specifically when BIND was done using an +// an ephemeral port: +// +// zstr_sendx (wap_server, "PORT", NULL); +// char *command, *port_str; +// zstr_recvx (wap_server, &command, &port_str, NULL); +// assert (streq (command, "PORT")); +// +// Specify configuration file to load, overwriting any previous loaded +// configuration file or options: +// +// zstr_sendx (wap_server, "CONFIGURE", filename, NULL); +// +// Set configuration path value: +// +// zstr_sendx (wap_server, "SET", path, value, NULL); +// +// Send zmsg_t instance to wap_server: +// +// zactor_send (wap_server, &msg); +// +// Receive zmsg_t instance from wap_server: +// +// zmsg_t *msg = zactor_recv (wap_server); +// +// This is the wap_server constructor as a zactor_fn: +// +WAP_EXPORT void + wap_server (zsock_t *pipe, void *args); + +// Self test of this class +WAP_EXPORT void + wap_server_test (bool verbose); +// @end + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc new file mode 100644 index 000000000..a7fc04154 --- /dev/null +++ b/src/ipc/include/wap_server_engine.inc @@ -0,0 +1,1238 @@ +/* ========================================================================= + wap_server_engine - wap_server engine + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_server.xml, or + * The code generation script that built this file: zproto_server_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + + +// --------------------------------------------------------------------------- +// State machine constants + +typedef enum { + start_state = 1, + connected_state = 2, + defaults_state = 3, + settling_state = 4 +} state_t; + +typedef enum { + NULL_event = 0, + terminate_event = 1, + open_event = 2, + blocks_event = 3, + put_event = 4, + get_event = 5, + save_event = 6, + start_event = 7, + stop_event = 8, + close_event = 9, + ping_event = 10, + expired_event = 11, + exception_event = 12, + settled_event = 13 +} event_t; + +// Names for state machine logging and error reporting +static char * +s_state_name [] = { + "(NONE)", + "start", + "connected", + "defaults", + "settling" +}; + +static char * +s_event_name [] = { + "(NONE)", + "terminate", + "OPEN", + "BLOCKS", + "PUT", + "GET", + "SAVE", + "START", + "STOP", + "CLOSE", + "PING", + "expired", + "exception", + "settled" +}; + +// --------------------------------------------------------------------------- +// Context for the whole server task. This embeds the application-level +// server context at its start (the entire structure, not a reference), +// so we can cast a pointer between server_t and s_server_t arbitrarily. + +typedef struct { + server_t server; // Application-level server context + zsock_t *pipe; // Socket to back to caller API + zsock_t *router; // Socket to talk to clients + int port; // Server port bound to + zloop_t *loop; // Reactor for server sockets + wap_proto_t *message; // Message received or sent + zhash_t *clients; // Clients we're connected to + zconfig_t *config; // Configuration tree + uint client_id; // Client identifier counter + size_t timeout; // Default client expiry timeout + bool verbose; // Verbose logging enabled? + char *log_prefix; // Default log prefix +} s_server_t; + + +// --------------------------------------------------------------------------- +// Context for each connected client. This embeds the application-level +// client context at its start (the entire structure, not a reference), +// so we can cast a pointer between client_t and s_client_t arbitrarily. + +typedef struct { + client_t client; // Application-level client context + s_server_t *server; // Parent server context + char *hashkey; // Key into server->clients hash + zframe_t *routing_id; // Routing_id back to client + uint unique_id; // Client identifier in server + state_t state; // Current state + event_t event; // Current event + event_t next_event; // The next event + event_t exception; // Exception event, if any + int wakeup; // zloop timer for client alarms + void *ticket; // zloop ticket for client timeouts + event_t wakeup_event; // Wake up with this event + char log_prefix [41]; // Log prefix string +} s_client_t; + +static int + server_initialize (server_t *self); +static void + server_terminate (server_t *self); +static zmsg_t * + server_method (server_t *self, const char *method, zmsg_t *msg); +static int + client_initialize (client_t *self); +static void + client_terminate (client_t *self); +static void + s_client_execute (s_client_t *client, event_t event); +static int + s_client_handle_wakeup (zloop_t *loop, int timer_id, void *argument); +static int + s_client_handle_ticket (zloop_t *loop, int timer_id, void *argument); +static void + register_wallet (client_t *self); +static void + signal_command_not_valid (client_t *self); +static void + retrieve_blocks (client_t *self); +static void + store_transaction (client_t *self); +static void + retrieve_transaction (client_t *self); +static void + start_mining_process (client_t *self); +static void + stop_mining_process (client_t *self); +static void + deregister_wallet (client_t *self); +static void + allow_time_to_settle (client_t *self); +static void + register_new_client (client_t *self); + +// --------------------------------------------------------------------------- +// These methods are an internal API for actions + +// Set the next event, needed in at least one action in an internal +// state; otherwise the state machine will wait for a message on the +// router socket and treat that as the event. + +static void +engine_set_next_event (client_t *client, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->next_event = event; + } +} + +// Raise an exception with 'event', halting any actions in progress. +// Continues execution of actions defined for the exception event. + +static void +engine_set_exception (client_t *client, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->exception = event; + } +} + +// Set wakeup alarm after 'delay' msecs. The next state should +// handle the wakeup event. The alarm is cancelled on any other +// event. + +static void +engine_set_wakeup_event (client_t *client, size_t delay, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + if (self->wakeup) { + zloop_timer_end (self->server->loop, self->wakeup); + self->wakeup = 0; + } + self->wakeup = zloop_timer ( + self->server->loop, delay, 1, s_client_handle_wakeup, self); + self->wakeup_event = event; + } +} + +// Execute 'event' on specified client. Use this to send events to +// other clients. Cancels any wakeup alarm on that client. + +static void +engine_send_event (client_t *client, event_t event) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + s_client_execute (self, event); + } +} + +// Execute 'event' on all clients known to the server. If you pass a +// client argument, that client will not receive the broadcast. If you +// want to pass any arguments, store them in the server context. + +static void +engine_broadcast_event (server_t *server, client_t *client, event_t event) +{ + if (server) { + s_server_t *self = (s_server_t *) server; + zlist_t *keys = zhash_keys (self->clients); + char *key = (char *) zlist_first (keys); + while (key) { + s_client_t *target = (s_client_t *) zhash_lookup (self->clients, key); + if (target != (s_client_t *) client) + s_client_execute (target, event); + key = (char *) zlist_next (keys); + } + zlist_destroy (&keys); + } +} + +// Poll socket for activity, invoke handler on any received message. +// Handler must be a CZMQ zloop_fn function; receives server as arg. + +static void +engine_handle_socket (server_t *server, zsock_t *socket, zloop_reader_fn handler) +{ + if (server) { + s_server_t *self = (s_server_t *) server; + if (handler != NULL) { + int rc = zloop_reader (self->loop, socket, handler, self); + assert (rc == 0); + zloop_reader_set_tolerant (self->loop, socket); + } + else + zloop_reader_end (self->loop, socket); + } +} + +// Register monitor function that will be called at regular intervals +// by the server engine + +static void +engine_set_monitor (server_t *server, size_t interval, zloop_timer_fn monitor) +{ + if (server) { + s_server_t *self = (s_server_t *) server; + int rc = zloop_timer (self->loop, interval, 0, monitor, self); + assert (rc >= 0); + } +} + +// Set log file prefix; this string will be added to log data, to make +// log data more searchable. The string is truncated to ~20 chars. + +static void +engine_set_log_prefix (client_t *client, const char *string) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + snprintf (self->log_prefix, sizeof (self->log_prefix) - 1, + "%6d:%-33s", self->unique_id, string); + } +} + +// Set a configuration value in the server's configuration tree. The +// properties this engine uses are: server/verbose, server/timeout, and +// server/background. You can also configure other abitrary properties. + +static void +engine_configure (server_t *server, const char *path, const char *value) +{ + if (server) { + s_server_t *self = (s_server_t *) server; + zconfig_put (self->config, path, value); + } +} + +// Return true if server is running in verbose mode, else return false. + +static bool +engine_verbose (server_t *server) +{ + if (server) { + s_server_t *self = (s_server_t *) server; + return self->verbose; + } + return false; +} + +// Pedantic compilers don't like unused functions, so we call the whole +// API, passing null references. It's nasty and horrid and sufficient. + +static void +s_satisfy_pedantic_compilers (void) +{ + engine_set_next_event (NULL, NULL_event); + engine_set_exception (NULL, NULL_event); + engine_set_wakeup_event (NULL, 0, NULL_event); + engine_send_event (NULL, NULL_event); + engine_broadcast_event (NULL, NULL, NULL_event); + engine_handle_socket (NULL, 0, NULL); + engine_set_monitor (NULL, 0, NULL); + engine_set_log_prefix (NULL, NULL); + engine_configure (NULL, NULL, NULL); + engine_verbose (NULL); +} + + +// --------------------------------------------------------------------------- +// Generic methods on protocol messages +// TODO: replace with lookup table, since ID is one byte + +static event_t +s_protocol_event (wap_proto_t *message) +{ + assert (message); + switch (wap_proto_id (message)) { + case WAP_PROTO_OPEN: + return open_event; + break; + case WAP_PROTO_BLOCKS: + return blocks_event; + break; + case WAP_PROTO_PUT: + return put_event; + break; + case WAP_PROTO_GET: + return get_event; + break; + case WAP_PROTO_SAVE: + return save_event; + break; + case WAP_PROTO_START: + return start_event; + break; + case WAP_PROTO_STOP: + return stop_event; + break; + case WAP_PROTO_CLOSE: + return close_event; + break; + case WAP_PROTO_PING: + return ping_event; + break; + default: + // Invalid wap_proto_t + return terminate_event; + } +} + + +// --------------------------------------------------------------------------- +// Client methods + +static s_client_t * +s_client_new (s_server_t *server, zframe_t *routing_id) +{ + s_client_t *self = (s_client_t *) zmalloc (sizeof (s_client_t)); + assert (self); + assert ((s_client_t *) &self->client == self); + + self->server = server; + self->hashkey = zframe_strhex (routing_id); + self->routing_id = zframe_dup (routing_id); + self->unique_id = server->client_id++; + engine_set_log_prefix (&self->client, server->log_prefix); + + self->client.server = (server_t *) server; + self->client.message = server->message; + + // If expiry timers are being used, create client ticket + if (server->timeout) + self->ticket = zloop_ticket (server->loop, s_client_handle_ticket, self); + // Give application chance to initialize and set next event + self->state = start_state; + self->event = NULL_event; + client_initialize (&self->client); + return self; +} + +static void +s_client_destroy (s_client_t **self_p) +{ + assert (self_p); + if (*self_p) { + s_client_t *self = *self_p; + if (self->wakeup) + zloop_timer_end (self->server->loop, self->wakeup); + if (self->ticket) + zloop_ticket_delete (self->server->loop, self->ticket); + zframe_destroy (&self->routing_id); + // Provide visual clue if application misuses client reference + engine_set_log_prefix (&self->client, "*** TERMINATED ***"); + client_terminate (&self->client); + free (self->hashkey); + free (self); + *self_p = NULL; + } +} + +// Callback when we remove client from 'clients' hash table +static void +s_client_free (void *argument) +{ + s_client_t *client = (s_client_t *) argument; + s_client_destroy (&client); +} + + +// Execute state machine as long as we have events + +static void +s_client_execute (s_client_t *self, event_t event) +{ + self->next_event = event; + // Cancel wakeup timer, if any was pending + if (self->wakeup) { + zloop_timer_end (self->server->loop, self->wakeup); + self->wakeup = 0; + } + while (self->next_event > 0) { + self->event = self->next_event; + self->next_event = NULL_event; + self->exception = NULL_event; + if (self->server->verbose) { + zsys_debug ("%s: %s:", + self->log_prefix, s_state_name [self->state]); + zsys_debug ("%s: %s", + self->log_prefix, s_event_name [self->event]); + } + switch (self->state) { + case start_state: + if (self->event == open_event) { + if (!self->exception) { + // register wallet + if (self->server->verbose) + zsys_debug ("%s: $ register wallet", self->log_prefix); + register_wallet (&self->client); + } + if (!self->exception) { + // send OPEN_OK + if (self->server->verbose) + zsys_debug ("%s: $ send OPEN_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_OPEN_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == close_event) { + if (!self->exception) { + // send CLOSE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send CLOSE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_CLOSE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == ping_event) { + if (!self->exception) { + // send PING_OK + if (self->server->verbose) + zsys_debug ("%s: $ send PING_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_PING_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == expired_event) { + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == exception_event) { + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else { + // Handle unexpected protocol events + if (!self->exception) { + // signal command not valid + if (self->server->verbose) + zsys_debug ("%s: $ signal command not valid", self->log_prefix); + signal_command_not_valid (&self->client); + } + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + break; + + case connected_state: + if (self->event == blocks_event) { + if (!self->exception) { + // retrieve blocks + if (self->server->verbose) + zsys_debug ("%s: $ retrieve blocks", self->log_prefix); + retrieve_blocks (&self->client); + } + if (!self->exception) { + // send BLOCKS_OK + if (self->server->verbose) + zsys_debug ("%s: $ send BLOCKS_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_BLOCKS_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == put_event) { + if (!self->exception) { + // store transaction + if (self->server->verbose) + zsys_debug ("%s: $ store transaction", self->log_prefix); + store_transaction (&self->client); + } + if (!self->exception) { + // send PUT_OK + if (self->server->verbose) + zsys_debug ("%s: $ send PUT_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_PUT_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == get_event) { + if (!self->exception) { + // retrieve transaction + if (self->server->verbose) + zsys_debug ("%s: $ retrieve transaction", self->log_prefix); + retrieve_transaction (&self->client); + } + if (!self->exception) { + // send GET_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == save_event) { + if (!self->exception) { + // send SAVE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send SAVE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_SAVE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == start_event) { + if (!self->exception) { + // start mining process + if (self->server->verbose) + zsys_debug ("%s: $ start mining process", self->log_prefix); + start_mining_process (&self->client); + } + if (!self->exception) { + // send START_OK + if (self->server->verbose) + zsys_debug ("%s: $ send START_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_START_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == stop_event) { + if (!self->exception) { + // stop mining process + if (self->server->verbose) + zsys_debug ("%s: $ stop mining process", self->log_prefix); + stop_mining_process (&self->client); + } + if (!self->exception) { + // send STOP_OK + if (self->server->verbose) + zsys_debug ("%s: $ send STOP_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_STOP_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == close_event) { + if (!self->exception) { + // send CLOSE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send CLOSE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_CLOSE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == ping_event) { + if (!self->exception) { + // send PING_OK + if (self->server->verbose) + zsys_debug ("%s: $ send PING_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_PING_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == expired_event) { + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == exception_event) { + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else { + // Handle unexpected protocol events + if (!self->exception) { + // signal command not valid + if (self->server->verbose) + zsys_debug ("%s: $ signal command not valid", self->log_prefix); + signal_command_not_valid (&self->client); + } + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + break; + + case defaults_state: + if (self->event == close_event) { + if (!self->exception) { + // send CLOSE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send CLOSE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_CLOSE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == ping_event) { + if (!self->exception) { + // send PING_OK + if (self->server->verbose) + zsys_debug ("%s: $ send PING_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_PING_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == expired_event) { + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else + if (self->event == exception_event) { + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + else { + // Handle unexpected protocol events + if (!self->exception) { + // signal command not valid + if (self->server->verbose) + zsys_debug ("%s: $ signal command not valid", self->log_prefix); + signal_command_not_valid (&self->client); + } + if (!self->exception) { + // send ERROR + if (self->server->verbose) + zsys_debug ("%s: $ send ERROR", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_ERROR); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) { + // deregister wallet + if (self->server->verbose) + zsys_debug ("%s: $ deregister wallet", self->log_prefix); + deregister_wallet (&self->client); + } + if (!self->exception) { + // allow time to settle + if (self->server->verbose) + zsys_debug ("%s: $ allow time to settle", self->log_prefix); + allow_time_to_settle (&self->client); + } + if (!self->exception) + self->state = settling_state; + } + break; + + case settling_state: + if (self->event == settled_event) { + if (!self->exception) { + // terminate + if (self->server->verbose) + zsys_debug ("%s: $ terminate", self->log_prefix); + self->next_event = terminate_event; + } + } + else + if (self->event == open_event) { + if (!self->exception) { + // register new client + if (self->server->verbose) + zsys_debug ("%s: $ register new client", self->log_prefix); + register_new_client (&self->client); + } + if (!self->exception) { + // send OPEN_OK + if (self->server->verbose) + zsys_debug ("%s: $ send OPEN_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_OPEN_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + if (!self->exception) + self->state = connected_state; + } + else { + // Handle unexpected protocol events + } + break; + } + // If we had an exception event, interrupt normal programming + if (self->exception) { + if (self->server->verbose) + zsys_debug ("%s: ! %s", + self->log_prefix, s_event_name [self->exception]); + + self->next_event = self->exception; + } + if (self->next_event == terminate_event) { + // Automatically calls s_client_destroy + zhash_delete (self->server->clients, self->hashkey); + break; + } + else + if (self->server->verbose) + zsys_debug ("%s: > %s", + self->log_prefix, s_state_name [self->state]); + } +} + +// zloop callback when client ticket expires + +static int +s_client_handle_ticket (zloop_t *loop, int timer_id, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + s_client_execute (self, expired_event); + self->ticket = NULL; // Ticket is now dead + return 0; +} + +// zloop callback when client wakeup timer expires + +static int +s_client_handle_wakeup (zloop_t *loop, int timer_id, void *argument) +{ + s_client_t *self = (s_client_t *) argument; + s_client_execute (self, self->wakeup_event); + return 0; +} + + +// Server methods + +static void +s_server_config_global (s_server_t *self) +{ + // Built-in server configuration options + // + // If we didn't already set verbose, check if the config tree wants it + if (!self->verbose + && atoi (zconfig_resolve (self->config, "server/verbose", "0"))) + self->verbose = true; + + // Default client timeout is 60 seconds + self->timeout = atoi ( + zconfig_resolve (self->config, "server/timeout", "60000")); + zloop_set_ticket_delay (self->loop, self->timeout); + + // Do we want to run server in the background? + int background = atoi ( + zconfig_resolve (self->config, "server/background", "0")); + if (!background) + zsys_set_logstream (stdout); +} + +static s_server_t * +s_server_new (zsock_t *pipe) +{ + s_server_t *self = (s_server_t *) zmalloc (sizeof (s_server_t)); + assert (self); + assert ((s_server_t *) &self->server == self); + + self->pipe = pipe; + self->router = zsock_new (ZMQ_ROUTER); + // By default the socket will discard outgoing messages above the + // HWM of 1,000. This isn't helpful for high-volume streaming. We + // will use a unbounded queue here. If applications need to guard + // against queue overflow, they should use a credit-based flow + // control scheme. + zsock_set_unbounded (self->router); + self->message = wap_proto_new (); + self->clients = zhash_new (); + self->config = zconfig_new ("root", NULL); + self->loop = zloop_new (); + srandom ((unsigned int) zclock_time ()); + self->client_id = randof (1000); + s_server_config_global (self); + + // Initialize application server context + self->server.pipe = self->pipe; + self->server.config = self->config; + server_initialize (&self->server); + + s_satisfy_pedantic_compilers (); + return self; +} + +static void +s_server_destroy (s_server_t **self_p) +{ + assert (self_p); + if (*self_p) { + s_server_t *self = *self_p; + wap_proto_destroy (&self->message); + // Destroy clients before destroying the server + zhash_destroy (&self->clients); + server_terminate (&self->server); + zsock_destroy (&self->router); + zconfig_destroy (&self->config); + zloop_destroy (&self->loop); + free (self); + *self_p = NULL; + } +} + +// Apply service-specific configuration tree: +// * apply server configuration +// * print any echo items in top-level sections +// * apply sections that match methods + +static void +s_server_config_service (s_server_t *self) +{ + // Apply echo commands and class methods + zconfig_t *section = zconfig_locate (self->config, "wap_server"); + if (section) + section = zconfig_child (section); + + while (section) { + if (streq (zconfig_name (section), "echo")) + zsys_notice ("%s", zconfig_value (section)); + else + if (streq (zconfig_name (section), "bind")) { + char *endpoint = zconfig_resolve (section, "endpoint", "?"); + if (zsock_bind (self->router, "%s", endpoint) == -1) + zsys_warning ("could not bind to %s (%s)", endpoint, zmq_strerror (zmq_errno ())); + } + else + if (streq (zconfig_name (section), "security")) { + char *mechanism = zconfig_resolve (section, "mechanism", "null"); + char *domain = zconfig_resolve (section, "domain", NULL); + if (streq (mechanism, "null")) { + if (domain) + zsock_set_zap_domain (self->router, NULL); + } + else + if (streq (mechanism, "plain")) + zsock_set_plain_server (self->router, 1); + else + zsys_warning ("mechanism=%s is not supported", mechanism); + } + section = zconfig_next (section); + } + s_server_config_global (self); +} + +// Process message from pipe + +static int +s_server_handle_pipe (zloop_t *loop, zsock_t *reader, void *argument) +{ + s_server_t *self = (s_server_t *) argument; + zmsg_t *msg = zmsg_recv (self->pipe); + if (!msg) + return -1; // Interrupted; exit zloop + char *method = zmsg_popstr (msg); + if (self->verbose) + zsys_debug ("%s: API command=%s", self->log_prefix, method); + + if (streq (method, "VERBOSE")) + self->verbose = true; + else + if (streq (method, "$TERM")) { + // Shutdown the engine + free (method); + zmsg_destroy (&msg); + return -1; + } + else + if (streq (method, "BIND")) { + // Bind to a specified endpoint, which may use an ephemeral port + char *endpoint = zmsg_popstr (msg); + self->port = zsock_bind (self->router, "%s", endpoint); + if (self->port == -1) + zsys_warning ("could not bind to %s", endpoint); + free (endpoint); + } + else + if (streq (method, "PORT")) { + // Return PORT + port number from the last bind, if any + zstr_sendm (self->pipe, "PORT"); + zstr_sendf (self->pipe, "%d", self->port); + } + else + if (streq (method, "CONFIGURE")) { + char *config_file = zmsg_popstr (msg); + zconfig_destroy (&self->config); + self->config = zconfig_load (config_file); + if (self->config) { + s_server_config_service (self); + self->server.config = self->config; + } + else { + zsys_warning ("cannot load config file '%s'\n", config_file); + self->config = zconfig_new ("root", NULL); + } + free (config_file); + } + else + if (streq (method, "SET")) { + char *path = zmsg_popstr (msg); + char *value = zmsg_popstr (msg); + zconfig_put (self->config, path, value); + if (streq (path, "server/animate")) { + zsys_warning ("'%s' is deprecated, use VERBOSE command instead", path); + self->verbose = atoi (value); + } + s_server_config_global (self); + free (path); + free (value); + } + else { + // Execute custom method + zmsg_t *reply = server_method (&self->server, method, msg); + // If reply isn't null, send it to caller + zmsg_send (&reply, self->pipe); + } + free (method); + zmsg_destroy (&msg); + return 0; +} + +// Handle a protocol message from the client + +static int +s_server_handle_protocol (zloop_t *loop, zsock_t *reader, void *argument) +{ + s_server_t *self = (s_server_t *) argument; + // We process as many messages as we can, to reduce the overhead + // of polling and the reactor: + while (zsock_events (self->router) & ZMQ_POLLIN) { + if (wap_proto_recv (self->message, self->router)) + return -1; // Interrupted; exit zloop + + // TODO: use binary hashing on routing_id + char *hashkey = zframe_strhex (wap_proto_routing_id (self->message)); + s_client_t *client = (s_client_t *) zhash_lookup (self->clients, hashkey); + if (client == NULL) { + client = s_client_new (self, wap_proto_routing_id (self->message)); + zhash_insert (self->clients, hashkey, client); + zhash_freefn (self->clients, hashkey, s_client_free); + } + free (hashkey); + // Any input from client counts as activity + if (client->ticket) + zloop_ticket_reset (self->loop, client->ticket); + + // Pass to client state machine + s_client_execute (client, s_protocol_event (self->message)); + } + return 0; +} + +// Watch server config file and reload if changed + +static int +s_watch_server_config (zloop_t *loop, int timer_id, void *argument) +{ + s_server_t *self = (s_server_t *) argument; + if (zconfig_has_changed (self->config) + && zconfig_reload (&self->config) == 0) { + s_server_config_service (self); + self->server.config = self->config; + zsys_notice ("reloaded configuration from %s", + zconfig_filename (self->config)); + } + return 0; +} + + +// --------------------------------------------------------------------------- +// This is the server actor, which polls its two sockets and processes +// incoming messages + +void +wap_server (zsock_t *pipe, void *args) +{ + // Initialize + s_server_t *self = s_server_new (pipe); + assert (self); + zsock_signal (pipe, 0); + // Actor argument may be a string used for logging + self->log_prefix = args? (char *) args: ""; + + // Set-up server monitor to watch for config file changes + engine_set_monitor ((server_t *) self, 1000, s_watch_server_config); + // Set up handler for the two main sockets the server uses + engine_handle_socket ((server_t *) self, self->pipe, s_server_handle_pipe); + engine_handle_socket ((server_t *) self, self->router, s_server_handle_protocol); + + // Run reactor until there's a termination signal + zloop_start (self->loop); + + // Reactor has ended + s_server_destroy (&self); +} diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c new file mode 100644 index 000000000..d95b4b10c --- /dev/null +++ b/src/ipc/wap_client/wap_client.c @@ -0,0 +1,307 @@ +/* ========================================================================= + wap_client - Wallet Client API + + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +/* +@header + Description of class for man page. +@discuss + Detailed discussion of the class, if any. +@end +*/ + +#include "wap_classes.h" +// TODO: Change these to match your project's needs +#include "../include/wap_proto.h" +#include "../include/wap_client.h" + +// Forward reference to method arguments structure +typedef struct _client_args_t client_args_t; + +// This structure defines the context for a client connection +typedef struct { + // These properties must always be present in the client_t + // and are set by the generated engine. The cmdpipe gets + // messages sent to the actor; the msgpipe may be used for + // faster asynchronous message flows. + zsock_t *cmdpipe; // Command pipe to/from caller API + zsock_t *msgpipe; // Message pipe to/from caller API + zsock_t *dealer; // Socket to talk to server + wap_proto_t *message; // Message to/from server + client_args_t *args; // Arguments from methods + + // Own properties + int heartbeat_timer; // Timeout for heartbeats to server +} client_t; + +// Include the generated client engine +#include "wap_client_engine.inc" + +// Allocate properties and structures for a new client instance. +// Return 0 if OK, -1 if failed + +static int +client_initialize (client_t *self) +{ + // We'll ping the server once per second + self->heartbeat_timer = 1000; + return 0; +} + +// Free properties and structures for a client instance + +static void +client_terminate (client_t *self) +{ + // Destroy properties here +} + + +// --------------------------------------------------------------------------- +// connect_to_server_endpoint +// + +static void +connect_to_server_endpoint (client_t *self) +{ + if (zsock_connect (self->dealer, "%s", self->args->endpoint)) { + engine_set_exception (self, bad_endpoint_event); + zsys_warning ("could not connect to %s", self->args->endpoint); + } +} + + +// --------------------------------------------------------------------------- +// set_client_identity +// + +static void +set_client_identity (client_t *self) +{ + wap_proto_set_identity (self->message, self->args->identity); +} + + +// --------------------------------------------------------------------------- +// use_connect_timeout +// + +static void +use_connect_timeout (client_t *self) +{ + engine_set_timeout (self, self->args->timeout); +} + + +// --------------------------------------------------------------------------- +// use_heartbeat_timer +// + +static void +use_heartbeat_timer (client_t *self) +{ + engine_set_timeout (self, self->heartbeat_timer); +} + + + +// --------------------------------------------------------------------------- +// prepare_blocks_command +// + +static void +prepare_blocks_command (client_t *self) +{ + wap_proto_set_block_ids (self->message, &self->args->block_ids); +} + + +// --------------------------------------------------------------------------- +// signal_have_blocks_ok +// + +static void +signal_have_blocks_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "siiisp", "BLOCKS OK", 0, + wap_proto_start_height (self->message), + wap_proto_curr_height (self->message), + wap_proto_block_status (self->message), + wap_proto_get_block_data (self->message)); +} + + +// --------------------------------------------------------------------------- +// prepare_start_command +// + +static void +prepare_start_command (client_t *self) +{ + wap_proto_set_start_height (self->message, self->args->start_height); +} + +// --------------------------------------------------------------------------- +// prepare_put_command +// + +static void +prepare_put_command (client_t *self) +{ + wap_proto_set_tx_data (self->message, &self->args->tx_data); +} + + +// --------------------------------------------------------------------------- +// signal_have_put_ok +// + +static void +signal_have_put_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "sis", "PUT OK", 0, + wap_proto_tx_id (self->message)); +} + + +// --------------------------------------------------------------------------- +// prepare_get_command +// + +static void +prepare_get_command (client_t *self) +{ + wap_proto_set_tx_id (self->message, self->args->tx_id); +} + + +// --------------------------------------------------------------------------- +// signal_have_get_ok +// + +static void +signal_have_get_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "sip", "GET OK", 0, + wap_proto_get_tx_data (self->message)); +} + + +// --------------------------------------------------------------------------- +// prepare_save_command +// + +static void +prepare_save_command (client_t *self) +{ +} + + + +// --------------------------------------------------------------------------- +// signal_have_save_ok +// + +static void +signal_have_save_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "si", "SAVE OK", 0); +} + + +// --------------------------------------------------------------------------- +// signal_have_start_ok +// + +static void +signal_have_start_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "sii", "START OK", 0, + wap_proto_curr_height (self->message)); +} + + +// --------------------------------------------------------------------------- +// signal_have_stop_ok +// + +static void +signal_have_stop_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "si", "STOP OK", 0); +} + + +// --------------------------------------------------------------------------- +// signal_success +// + +static void +signal_success (client_t *self) +{ + zsock_send (self->cmdpipe, "si", "SUCCESS", 0); +} + + +// --------------------------------------------------------------------------- +// signal_bad_endpoint +// + +static void +signal_bad_endpoint (client_t *self) +{ + zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Bad server endpoint"); +} + + +// --------------------------------------------------------------------------- +// signal_failure +// + +static void +signal_failure (client_t *self) +{ + zsock_send (self->cmdpipe, "sis", "FAILURE", -1, wap_proto_reason (self->message)); +} + + +// --------------------------------------------------------------------------- +// check_status_code +// + +static void +check_status_code (client_t *self) +{ + if (wap_proto_status (self->message) == WAP_PROTO_COMMAND_INVALID) + engine_set_next_event (self, command_invalid_event); + else + engine_set_next_event (self, other_event); +} + + +// --------------------------------------------------------------------------- +// signal_unhandled_error +// + +static void +signal_unhandled_error (client_t *self) +{ + zsys_error ("unhandled error code from server"); +} + + +// --------------------------------------------------------------------------- +// signal_server_not_present +// + +static void +signal_server_not_present (client_t *self) +{ + zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Server is not reachable"); +} + diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c new file mode 100644 index 000000000..38c680bbb --- /dev/null +++ b/src/ipc/wap_proto.c @@ -0,0 +1,1316 @@ +/* ========================================================================= + wap_proto - Wallet Access Protocol + + Codec class for wap_proto. + + ** WARNING ************************************************************* + THIS SOURCE FILE IS 100% GENERATED. If you edit this file, you will lose + your changes at the next build cycle. This is great for temporary printf + statements. DO NOT MAKE ANY CHANGES YOU WISH TO KEEP. The correct places + for commits are: + + * The XML model used for this code generation: wap_proto.xml, or + * The code generation script that built this file: zproto_codec_c + ************************************************************************ + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +/* +@header + wap_proto - Wallet Access Protocol +@discuss +@end +*/ + +#include "../include/wap_proto.h" + +// Structure of our class + +struct _wap_proto_t { + zframe_t *routing_id; // Routing_id from ROUTER, if any + int id; // wap_proto message ID + byte *needle; // Read/write pointer for serialization + byte *ceiling; // Valid upper limit for read pointer + char identity [256]; // Wallet identity + zlist_t *block_ids; // + uint64_t start_height; // + uint64_t curr_height; // + char block_status [256]; // + zmsg_t *block_data; // Frames of block data + zchunk_t *tx_data; // Transaction data + char tx_id [256]; // Transaction ID + uint16_t status; // Error status + char reason [256]; // Printable explanation +}; + +// -------------------------------------------------------------------------- +// Network data encoding macros + +// Put a block of octets to the frame +#define PUT_OCTETS(host,size) { \ + memcpy (self->needle, (host), size); \ + self->needle += size; \ +} + +// Get a block of octets from the frame +#define GET_OCTETS(host,size) { \ + if (self->needle + size > self->ceiling) { \ + zsys_warning ("wap_proto: GET_OCTETS failed"); \ + goto malformed; \ + } \ + memcpy ((host), self->needle, size); \ + self->needle += size; \ +} + +// Put a 1-byte number to the frame +#define PUT_NUMBER1(host) { \ + *(byte *) self->needle = (host); \ + self->needle++; \ +} + +// Put a 2-byte number to the frame +#define PUT_NUMBER2(host) { \ + self->needle [0] = (byte) (((host) >> 8) & 255); \ + self->needle [1] = (byte) (((host)) & 255); \ + self->needle += 2; \ +} + +// Put a 4-byte number to the frame +#define PUT_NUMBER4(host) { \ + self->needle [0] = (byte) (((host) >> 24) & 255); \ + self->needle [1] = (byte) (((host) >> 16) & 255); \ + self->needle [2] = (byte) (((host) >> 8) & 255); \ + self->needle [3] = (byte) (((host)) & 255); \ + self->needle += 4; \ +} + +// Put a 8-byte number to the frame +#define PUT_NUMBER8(host) { \ + self->needle [0] = (byte) (((host) >> 56) & 255); \ + self->needle [1] = (byte) (((host) >> 48) & 255); \ + self->needle [2] = (byte) (((host) >> 40) & 255); \ + self->needle [3] = (byte) (((host) >> 32) & 255); \ + self->needle [4] = (byte) (((host) >> 24) & 255); \ + self->needle [5] = (byte) (((host) >> 16) & 255); \ + self->needle [6] = (byte) (((host) >> 8) & 255); \ + self->needle [7] = (byte) (((host)) & 255); \ + self->needle += 8; \ +} + +// Get a 1-byte number from the frame +#define GET_NUMBER1(host) { \ + if (self->needle + 1 > self->ceiling) { \ + zsys_warning ("wap_proto: GET_NUMBER1 failed"); \ + goto malformed; \ + } \ + (host) = *(byte *) self->needle; \ + self->needle++; \ +} + +// Get a 2-byte number from the frame +#define GET_NUMBER2(host) { \ + if (self->needle + 2 > self->ceiling) { \ + zsys_warning ("wap_proto: GET_NUMBER2 failed"); \ + goto malformed; \ + } \ + (host) = ((uint16_t) (self->needle [0]) << 8) \ + + (uint16_t) (self->needle [1]); \ + self->needle += 2; \ +} + +// Get a 4-byte number from the frame +#define GET_NUMBER4(host) { \ + if (self->needle + 4 > self->ceiling) { \ + zsys_warning ("wap_proto: GET_NUMBER4 failed"); \ + goto malformed; \ + } \ + (host) = ((uint32_t) (self->needle [0]) << 24) \ + + ((uint32_t) (self->needle [1]) << 16) \ + + ((uint32_t) (self->needle [2]) << 8) \ + + (uint32_t) (self->needle [3]); \ + self->needle += 4; \ +} + +// Get a 8-byte number from the frame +#define GET_NUMBER8(host) { \ + if (self->needle + 8 > self->ceiling) { \ + zsys_warning ("wap_proto: GET_NUMBER8 failed"); \ + goto malformed; \ + } \ + (host) = ((uint64_t) (self->needle [0]) << 56) \ + + ((uint64_t) (self->needle [1]) << 48) \ + + ((uint64_t) (self->needle [2]) << 40) \ + + ((uint64_t) (self->needle [3]) << 32) \ + + ((uint64_t) (self->needle [4]) << 24) \ + + ((uint64_t) (self->needle [5]) << 16) \ + + ((uint64_t) (self->needle [6]) << 8) \ + + (uint64_t) (self->needle [7]); \ + self->needle += 8; \ +} + +// Put a string to the frame +#define PUT_STRING(host) { \ + size_t string_size = strlen (host); \ + PUT_NUMBER1 (string_size); \ + memcpy (self->needle, (host), string_size); \ + self->needle += string_size; \ +} + +// Get a string from the frame +#define GET_STRING(host) { \ + size_t string_size; \ + GET_NUMBER1 (string_size); \ + if (self->needle + string_size > (self->ceiling)) { \ + zsys_warning ("wap_proto: GET_STRING failed"); \ + goto malformed; \ + } \ + memcpy ((host), self->needle, string_size); \ + (host) [string_size] = 0; \ + self->needle += string_size; \ +} + +// Put a long string to the frame +#define PUT_LONGSTR(host) { \ + size_t string_size = strlen (host); \ + PUT_NUMBER4 (string_size); \ + memcpy (self->needle, (host), string_size); \ + self->needle += string_size; \ +} + +// Get a long string from the frame +#define GET_LONGSTR(host) { \ + size_t string_size; \ + GET_NUMBER4 (string_size); \ + if (self->needle + string_size > (self->ceiling)) { \ + zsys_warning ("wap_proto: GET_LONGSTR failed"); \ + goto malformed; \ + } \ + free ((host)); \ + (host) = (char *) malloc (string_size + 1); \ + memcpy ((host), self->needle, string_size); \ + (host) [string_size] = 0; \ + self->needle += string_size; \ +} + + +// -------------------------------------------------------------------------- +// Create a new wap_proto + +wap_proto_t * +wap_proto_new (void) +{ + wap_proto_t *self = (wap_proto_t *) zmalloc (sizeof (wap_proto_t)); + return self; +} + + +// -------------------------------------------------------------------------- +// Destroy the wap_proto + +void +wap_proto_destroy (wap_proto_t **self_p) +{ + assert (self_p); + if (*self_p) { + wap_proto_t *self = *self_p; + + // Free class properties + zframe_destroy (&self->routing_id); + if (self->block_ids) + zlist_destroy (&self->block_ids); + zmsg_destroy (&self->block_data); + zchunk_destroy (&self->tx_data); + + // Free object itself + free (self); + *self_p = NULL; + } +} + + +// -------------------------------------------------------------------------- +// Receive a wap_proto from the socket. Returns 0 if OK, -1 if +// there was an error. Blocks if there is no message waiting. + +int +wap_proto_recv (wap_proto_t *self, zsock_t *input) +{ + assert (input); + + if (zsock_type (input) == ZMQ_ROUTER) { + zframe_destroy (&self->routing_id); + self->routing_id = zframe_recv (input); + if (!self->routing_id || !zsock_rcvmore (input)) { + zsys_warning ("wap_proto: no routing ID"); + return -1; // Interrupted or malformed + } + } + zmq_msg_t frame; + zmq_msg_init (&frame); + int size = zmq_msg_recv (&frame, zsock_resolve (input), 0); + if (size == -1) { + zsys_warning ("wap_proto: interrupted"); + goto malformed; // Interrupted + } + // Get and check protocol signature + self->needle = (byte *) zmq_msg_data (&frame); + self->ceiling = self->needle + zmq_msg_size (&frame); + + uint16_t signature; + GET_NUMBER2 (signature); + if (signature != (0xAAA0 | 0)) { + zsys_warning ("wap_proto: invalid signature"); + // TODO: discard invalid messages and loop, and return + // -1 only on interrupt + goto malformed; // Interrupted + } + // Get message id and parse per message type + GET_NUMBER1 (self->id); + + switch (self->id) { + case WAP_PROTO_OPEN: + { + char protocol [256]; + GET_STRING (protocol); + if (strneq (protocol, "WAP")) { + zsys_warning ("wap_proto: protocol is invalid"); + goto malformed; + } + } + { + uint16_t version; + GET_NUMBER2 (version); + if (version != 1) { + zsys_warning ("wap_proto: version is invalid"); + goto malformed; + } + } + GET_STRING (self->identity); + break; + + case WAP_PROTO_OPEN_OK: + break; + + case WAP_PROTO_BLOCKS: + { + size_t list_size; + GET_NUMBER4 (list_size); + self->block_ids = zlist_new (); + zlist_autofree (self->block_ids); + while (list_size--) { + char *string = NULL; + GET_LONGSTR (string); + zlist_append (self->block_ids, string); + free (string); + } + } + break; + + case WAP_PROTO_BLOCKS_OK: + GET_NUMBER8 (self->start_height); + GET_NUMBER8 (self->curr_height); + GET_STRING (self->block_status); + // Get zero or more remaining frames + zmsg_destroy (&self->block_data); + if (zsock_rcvmore (input)) + self->block_data = zmsg_recv (input); + else + self->block_data = zmsg_new (); + break; + + case WAP_PROTO_PUT: + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: tx_data is missing data"); + goto malformed; + } + self->tx_data = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_PUT_OK: + GET_STRING (self->tx_id); + break; + + case WAP_PROTO_GET: + GET_STRING (self->tx_id); + break; + + case WAP_PROTO_GET_OK: + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: tx_data is missing data"); + goto malformed; + } + self->tx_data = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_SAVE: + break; + + case WAP_PROTO_SAVE_OK: + break; + + case WAP_PROTO_START: + GET_NUMBER8 (self->start_height); + break; + + case WAP_PROTO_START_OK: + GET_NUMBER8 (self->curr_height); + break; + + case WAP_PROTO_STOP: + break; + + case WAP_PROTO_STOP_OK: + break; + + case WAP_PROTO_CLOSE: + break; + + case WAP_PROTO_CLOSE_OK: + break; + + case WAP_PROTO_PING: + break; + + case WAP_PROTO_PING_OK: + break; + + case WAP_PROTO_ERROR: + GET_NUMBER2 (self->status); + GET_STRING (self->reason); + break; + + default: + zsys_warning ("wap_proto: bad message ID"); + goto malformed; + } + // Successful return + zmq_msg_close (&frame); + return 0; + + // Error returns + malformed: + zsys_warning ("wap_proto: wap_proto malformed message, fail"); + zmq_msg_close (&frame); + return -1; // Invalid message +} + + +// -------------------------------------------------------------------------- +// Send the wap_proto to the socket. Does not destroy it. Returns 0 if +// OK, else -1. + +int +wap_proto_send (wap_proto_t *self, zsock_t *output) +{ + assert (self); + assert (output); + + if (zsock_type (output) == ZMQ_ROUTER) + zframe_send (&self->routing_id, output, ZFRAME_MORE + ZFRAME_REUSE); + + size_t frame_size = 2 + 1; // Signature and message ID + switch (self->id) { + case WAP_PROTO_OPEN: + frame_size += 1 + strlen ("WAP"); + frame_size += 2; // version + frame_size += 1 + strlen (self->identity); + break; + case WAP_PROTO_BLOCKS: + frame_size += 4; // Size is 4 octets + if (self->block_ids) { + char *block_ids = (char *) zlist_first (self->block_ids); + while (block_ids) { + frame_size += 4 + strlen (block_ids); + block_ids = (char *) zlist_next (self->block_ids); + } + } + break; + case WAP_PROTO_BLOCKS_OK: + frame_size += 8; // start_height + frame_size += 8; // curr_height + frame_size += 1 + strlen (self->block_status); + break; + case WAP_PROTO_PUT: + frame_size += 4; // Size is 4 octets + if (self->tx_data) + frame_size += zchunk_size (self->tx_data); + break; + case WAP_PROTO_PUT_OK: + frame_size += 1 + strlen (self->tx_id); + break; + case WAP_PROTO_GET: + frame_size += 1 + strlen (self->tx_id); + break; + case WAP_PROTO_GET_OK: + frame_size += 4; // Size is 4 octets + if (self->tx_data) + frame_size += zchunk_size (self->tx_data); + break; + case WAP_PROTO_START: + frame_size += 8; // start_height + break; + case WAP_PROTO_START_OK: + frame_size += 8; // curr_height + break; + case WAP_PROTO_ERROR: + frame_size += 2; // status + frame_size += 1 + strlen (self->reason); + break; + } + // Now serialize message into the frame + zmq_msg_t frame; + zmq_msg_init_size (&frame, frame_size); + self->needle = (byte *) zmq_msg_data (&frame); + PUT_NUMBER2 (0xAAA0 | 0); + PUT_NUMBER1 (self->id); + bool send_block_data = false; + size_t nbr_frames = 1; // Total number of frames to send + + switch (self->id) { + case WAP_PROTO_OPEN: + PUT_STRING ("WAP"); + PUT_NUMBER2 (1); + PUT_STRING (self->identity); + break; + + case WAP_PROTO_BLOCKS: + if (self->block_ids) { + PUT_NUMBER4 (zlist_size (self->block_ids)); + char *block_ids = (char *) zlist_first (self->block_ids); + while (block_ids) { + PUT_LONGSTR (block_ids); + block_ids = (char *) zlist_next (self->block_ids); + } + } + else + PUT_NUMBER4 (0); // Empty string array + break; + + case WAP_PROTO_BLOCKS_OK: + PUT_NUMBER8 (self->start_height); + PUT_NUMBER8 (self->curr_height); + PUT_STRING (self->block_status); + nbr_frames += self->block_data? zmsg_size (self->block_data): 1; + send_block_data = true; + break; + + case WAP_PROTO_PUT: + if (self->tx_data) { + PUT_NUMBER4 (zchunk_size (self->tx_data)); + memcpy (self->needle, + zchunk_data (self->tx_data), + zchunk_size (self->tx_data)); + self->needle += zchunk_size (self->tx_data); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_PUT_OK: + PUT_STRING (self->tx_id); + break; + + case WAP_PROTO_GET: + PUT_STRING (self->tx_id); + break; + + case WAP_PROTO_GET_OK: + if (self->tx_data) { + PUT_NUMBER4 (zchunk_size (self->tx_data)); + memcpy (self->needle, + zchunk_data (self->tx_data), + zchunk_size (self->tx_data)); + self->needle += zchunk_size (self->tx_data); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_START: + PUT_NUMBER8 (self->start_height); + break; + + case WAP_PROTO_START_OK: + PUT_NUMBER8 (self->curr_height); + break; + + case WAP_PROTO_ERROR: + PUT_NUMBER2 (self->status); + PUT_STRING (self->reason); + break; + + } + // Now send the data frame + zmq_msg_send (&frame, zsock_resolve (output), --nbr_frames? ZMQ_SNDMORE: 0); + + // Now send the block_data if necessary + if (send_block_data) { + if (self->block_data) { + zframe_t *frame = zmsg_first (self->block_data); + while (frame) { + zframe_send (&frame, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + frame = zmsg_next (self->block_data); + } + } + else + zmq_send (zsock_resolve (output), NULL, 0, 0); + } + return 0; +} + + +// -------------------------------------------------------------------------- +// Print contents of message to stdout + +void +wap_proto_print (wap_proto_t *self) +{ + assert (self); + switch (self->id) { + case WAP_PROTO_OPEN: + zsys_debug ("WAP_PROTO_OPEN:"); + zsys_debug (" protocol=wap"); + zsys_debug (" version=1"); + if (self->identity) + zsys_debug (" identity='%s'", self->identity); + else + zsys_debug (" identity="); + break; + + case WAP_PROTO_OPEN_OK: + zsys_debug ("WAP_PROTO_OPEN_OK:"); + break; + + case WAP_PROTO_BLOCKS: + zsys_debug ("WAP_PROTO_BLOCKS:"); + zsys_debug (" block_ids="); + if (self->block_ids) { + char *block_ids = (char *) zlist_first (self->block_ids); + while (block_ids) { + zsys_debug (" '%s'", block_ids); + block_ids = (char *) zlist_next (self->block_ids); + } + } + break; + + case WAP_PROTO_BLOCKS_OK: + zsys_debug ("WAP_PROTO_BLOCKS_OK:"); + zsys_debug (" start_height=%ld", (long) self->start_height); + zsys_debug (" curr_height=%ld", (long) self->curr_height); + if (self->block_status) + zsys_debug (" block_status='%s'", self->block_status); + else + zsys_debug (" block_status="); + zsys_debug (" block_data="); + if (self->block_data) + zmsg_print (self->block_data); + else + zsys_debug ("(NULL)"); + break; + + case WAP_PROTO_PUT: + zsys_debug ("WAP_PROTO_PUT:"); + zsys_debug (" tx_data=[ ... ]"); + break; + + case WAP_PROTO_PUT_OK: + zsys_debug ("WAP_PROTO_PUT_OK:"); + if (self->tx_id) + zsys_debug (" tx_id='%s'", self->tx_id); + else + zsys_debug (" tx_id="); + break; + + case WAP_PROTO_GET: + zsys_debug ("WAP_PROTO_GET:"); + if (self->tx_id) + zsys_debug (" tx_id='%s'", self->tx_id); + else + zsys_debug (" tx_id="); + break; + + case WAP_PROTO_GET_OK: + zsys_debug ("WAP_PROTO_GET_OK:"); + zsys_debug (" tx_data=[ ... ]"); + break; + + case WAP_PROTO_SAVE: + zsys_debug ("WAP_PROTO_SAVE:"); + break; + + case WAP_PROTO_SAVE_OK: + zsys_debug ("WAP_PROTO_SAVE_OK:"); + break; + + case WAP_PROTO_START: + zsys_debug ("WAP_PROTO_START:"); + zsys_debug (" start_height=%ld", (long) self->start_height); + break; + + case WAP_PROTO_START_OK: + zsys_debug ("WAP_PROTO_START_OK:"); + zsys_debug (" curr_height=%ld", (long) self->curr_height); + break; + + case WAP_PROTO_STOP: + zsys_debug ("WAP_PROTO_STOP:"); + break; + + case WAP_PROTO_STOP_OK: + zsys_debug ("WAP_PROTO_STOP_OK:"); + break; + + case WAP_PROTO_CLOSE: + zsys_debug ("WAP_PROTO_CLOSE:"); + break; + + case WAP_PROTO_CLOSE_OK: + zsys_debug ("WAP_PROTO_CLOSE_OK:"); + break; + + case WAP_PROTO_PING: + zsys_debug ("WAP_PROTO_PING:"); + break; + + case WAP_PROTO_PING_OK: + zsys_debug ("WAP_PROTO_PING_OK:"); + break; + + case WAP_PROTO_ERROR: + zsys_debug ("WAP_PROTO_ERROR:"); + zsys_debug (" status=%ld", (long) self->status); + if (self->reason) + zsys_debug (" reason='%s'", self->reason); + else + zsys_debug (" reason="); + break; + + } +} + + +// -------------------------------------------------------------------------- +// Get/set the message routing_id + +zframe_t * +wap_proto_routing_id (wap_proto_t *self) +{ + assert (self); + return self->routing_id; +} + +void +wap_proto_set_routing_id (wap_proto_t *self, zframe_t *routing_id) +{ + if (self->routing_id) + zframe_destroy (&self->routing_id); + self->routing_id = zframe_dup (routing_id); +} + + +// -------------------------------------------------------------------------- +// Get/set the wap_proto id + +int +wap_proto_id (wap_proto_t *self) +{ + assert (self); + return self->id; +} + +void +wap_proto_set_id (wap_proto_t *self, int id) +{ + self->id = id; +} + +// -------------------------------------------------------------------------- +// Return a printable command string + +const char * +wap_proto_command (wap_proto_t *self) +{ + assert (self); + switch (self->id) { + case WAP_PROTO_OPEN: + return ("OPEN"); + break; + case WAP_PROTO_OPEN_OK: + return ("OPEN_OK"); + break; + case WAP_PROTO_BLOCKS: + return ("BLOCKS"); + break; + case WAP_PROTO_BLOCKS_OK: + return ("BLOCKS_OK"); + break; + case WAP_PROTO_PUT: + return ("PUT"); + break; + case WAP_PROTO_PUT_OK: + return ("PUT_OK"); + break; + case WAP_PROTO_GET: + return ("GET"); + break; + case WAP_PROTO_GET_OK: + return ("GET_OK"); + break; + case WAP_PROTO_SAVE: + return ("SAVE"); + break; + case WAP_PROTO_SAVE_OK: + return ("SAVE_OK"); + break; + case WAP_PROTO_START: + return ("START"); + break; + case WAP_PROTO_START_OK: + return ("START_OK"); + break; + case WAP_PROTO_STOP: + return ("STOP"); + break; + case WAP_PROTO_STOP_OK: + return ("STOP_OK"); + break; + case WAP_PROTO_CLOSE: + return ("CLOSE"); + break; + case WAP_PROTO_CLOSE_OK: + return ("CLOSE_OK"); + break; + case WAP_PROTO_PING: + return ("PING"); + break; + case WAP_PROTO_PING_OK: + return ("PING_OK"); + break; + case WAP_PROTO_ERROR: + return ("ERROR"); + break; + } + return "?"; +} + +// -------------------------------------------------------------------------- +// Get/set the identity field + +const char * +wap_proto_identity (wap_proto_t *self) +{ + assert (self); + return self->identity; +} + +void +wap_proto_set_identity (wap_proto_t *self, const char *value) +{ + assert (self); + assert (value); + if (value == self->identity) + return; + strncpy (self->identity, value, 255); + self->identity [255] = 0; +} + + +// -------------------------------------------------------------------------- +// Get the block_ids field, without transferring ownership + +zlist_t * +wap_proto_block_ids (wap_proto_t *self) +{ + assert (self); + return self->block_ids; +} + +// Get the block_ids field and transfer ownership to caller + +zlist_t * +wap_proto_get_block_ids (wap_proto_t *self) +{ + assert (self); + zlist_t *block_ids = self->block_ids; + self->block_ids = NULL; + return block_ids; +} + +// Set the block_ids field, transferring ownership from caller + +void +wap_proto_set_block_ids (wap_proto_t *self, zlist_t **block_ids_p) +{ + assert (self); + assert (block_ids_p); + zlist_destroy (&self->block_ids); + self->block_ids = *block_ids_p; + *block_ids_p = NULL; +} + + + +// -------------------------------------------------------------------------- +// Get/set the start_height field + +uint64_t +wap_proto_start_height (wap_proto_t *self) +{ + assert (self); + return self->start_height; +} + +void +wap_proto_set_start_height (wap_proto_t *self, uint64_t start_height) +{ + assert (self); + self->start_height = start_height; +} + + +// -------------------------------------------------------------------------- +// Get/set the curr_height field + +uint64_t +wap_proto_curr_height (wap_proto_t *self) +{ + assert (self); + return self->curr_height; +} + +void +wap_proto_set_curr_height (wap_proto_t *self, uint64_t curr_height) +{ + assert (self); + self->curr_height = curr_height; +} + + +// -------------------------------------------------------------------------- +// Get/set the block_status field + +const char * +wap_proto_block_status (wap_proto_t *self) +{ + assert (self); + return self->block_status; +} + +void +wap_proto_set_block_status (wap_proto_t *self, const char *value) +{ + assert (self); + assert (value); + if (value == self->block_status) + return; + strncpy (self->block_status, value, 255); + self->block_status [255] = 0; +} + + +// -------------------------------------------------------------------------- +// Get the block_data field without transferring ownership + +zmsg_t * +wap_proto_block_data (wap_proto_t *self) +{ + assert (self); + return self->block_data; +} + +// Get the block_data field and transfer ownership to caller + +zmsg_t * +wap_proto_get_block_data (wap_proto_t *self) +{ + zmsg_t *block_data = self->block_data; + self->block_data = NULL; + return block_data; +} + +// Set the block_data field, transferring ownership from caller + +void +wap_proto_set_block_data (wap_proto_t *self, zmsg_t **msg_p) +{ + assert (self); + assert (msg_p); + zmsg_destroy (&self->block_data); + self->block_data = *msg_p; + *msg_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the tx_data field without transferring ownership + +zchunk_t * +wap_proto_tx_data (wap_proto_t *self) +{ + assert (self); + return self->tx_data; +} + +// Get the tx_data field and transfer ownership to caller + +zchunk_t * +wap_proto_get_tx_data (wap_proto_t *self) +{ + zchunk_t *tx_data = self->tx_data; + self->tx_data = NULL; + return tx_data; +} + +// Set the tx_data field, transferring ownership from caller + +void +wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->tx_data); + self->tx_data = *chunk_p; + *chunk_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get/set the tx_id field + +const char * +wap_proto_tx_id (wap_proto_t *self) +{ + assert (self); + return self->tx_id; +} + +void +wap_proto_set_tx_id (wap_proto_t *self, const char *value) +{ + assert (self); + assert (value); + if (value == self->tx_id) + return; + strncpy (self->tx_id, value, 255); + self->tx_id [255] = 0; +} + + +// -------------------------------------------------------------------------- +// Get/set the status field + +uint16_t +wap_proto_status (wap_proto_t *self) +{ + assert (self); + return self->status; +} + +void +wap_proto_set_status (wap_proto_t *self, uint16_t status) +{ + assert (self); + self->status = status; +} + + +// -------------------------------------------------------------------------- +// Get/set the reason field + +const char * +wap_proto_reason (wap_proto_t *self) +{ + assert (self); + return self->reason; +} + +void +wap_proto_set_reason (wap_proto_t *self, const char *value) +{ + assert (self); + assert (value); + if (value == self->reason) + return; + strncpy (self->reason, value, 255); + self->reason [255] = 0; +} + + + +// -------------------------------------------------------------------------- +// Selftest + +int +wap_proto_test (bool verbose) +{ + printf (" * wap_proto: "); + + // @selftest + // Simple create/destroy test + wap_proto_t *self = wap_proto_new (); + assert (self); + wap_proto_destroy (&self); + + // Create pair of sockets we can send through + zsock_t *input = zsock_new (ZMQ_ROUTER); + assert (input); + zsock_connect (input, "inproc://selftest-wap_proto"); + + zsock_t *output = zsock_new (ZMQ_DEALER); + assert (output); + zsock_bind (output, "inproc://selftest-wap_proto"); + + // Encode/send/decode and verify each message type + int instance; + self = wap_proto_new (); + wap_proto_set_id (self, WAP_PROTO_OPEN); + + wap_proto_set_identity (self, "Life is short but Now lasts for ever"); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (streq (wap_proto_identity (self), "Life is short but Now lasts for ever")); + } + wap_proto_set_id (self, WAP_PROTO_OPEN_OK); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_BLOCKS); + + zlist_t *blocks_block_ids = zlist_new (); + zlist_append (blocks_block_ids, "Name: Brutus"); + zlist_append (blocks_block_ids, "Age: 43"); + wap_proto_set_block_ids (self, &blocks_block_ids); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + zlist_t *block_ids = wap_proto_get_block_ids (self); + assert (zlist_size (block_ids) == 2); + assert (streq ((char *) zlist_first (block_ids), "Name: Brutus")); + assert (streq ((char *) zlist_next (block_ids), "Age: 43")); + zlist_destroy (&block_ids); + } + wap_proto_set_id (self, WAP_PROTO_BLOCKS_OK); + + wap_proto_set_start_height (self, 123); + wap_proto_set_curr_height (self, 123); + wap_proto_set_block_status (self, "Life is short but Now lasts for ever"); + zmsg_t *blocks_ok_block_data = zmsg_new (); + wap_proto_set_block_data (self, &blocks_ok_block_data); + zmsg_addstr (wap_proto_block_data (self), "Hello, World"); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_start_height (self) == 123); + assert (wap_proto_curr_height (self) == 123); + assert (streq (wap_proto_block_status (self), "Life is short but Now lasts for ever")); + assert (zmsg_size (wap_proto_block_data (self)) == 1); + } + wap_proto_set_id (self, WAP_PROTO_PUT); + + zchunk_t *put_tx_data = zchunk_new ("Captcha Diem", 12); + wap_proto_set_tx_data (self, &put_tx_data); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (memcmp (zchunk_data (wap_proto_tx_data (self)), "Captcha Diem", 12) == 0); + } + wap_proto_set_id (self, WAP_PROTO_PUT_OK); + + wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (streq (wap_proto_tx_id (self), "Life is short but Now lasts for ever")); + } + wap_proto_set_id (self, WAP_PROTO_GET); + + wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (streq (wap_proto_tx_id (self), "Life is short but Now lasts for ever")); + } + wap_proto_set_id (self, WAP_PROTO_GET_OK); + + zchunk_t *get_ok_tx_data = zchunk_new ("Captcha Diem", 12); + wap_proto_set_tx_data (self, &get_ok_tx_data); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (memcmp (zchunk_data (wap_proto_tx_data (self)), "Captcha Diem", 12) == 0); + } + wap_proto_set_id (self, WAP_PROTO_SAVE); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_SAVE_OK); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_START); + + wap_proto_set_start_height (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_start_height (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_START_OK); + + wap_proto_set_curr_height (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_curr_height (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_STOP); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_STOP_OK); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_CLOSE); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_CLOSE_OK); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_PING); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_PING_OK); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_ERROR); + + wap_proto_set_status (self, 123); + wap_proto_set_reason (self, "Life is short but Now lasts for ever"); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (streq (wap_proto_reason (self), "Life is short but Now lasts for ever")); + } + + wap_proto_destroy (&self); + zsock_destroy (&input); + zsock_destroy (&output); + // @end + + printf ("OK\n"); + return 0; +} diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c new file mode 100644 index 000000000..f3ccdce4a --- /dev/null +++ b/src/ipc/wap_server/wap_server.c @@ -0,0 +1,244 @@ +/* ========================================================================= + wap_server - wap_server + + Copyright (c) the Contributors as noted in the AUTHORS file. + + (insert license text here) + ========================================================================= +*/ + +/* +@header + Description of class for man page. +@discuss + Detailed discussion of the class, if any. +@end +*/ + +#include "wap_classes.h" +// TODO: Change these to match your project's needs +#include "../include/wap_proto.h" +#include "../include/wap_server.h" +#include "daemon_ipc_handlers.h" + +// --------------------------------------------------------------------------- +// Forward declarations for the two main classes we use here + +typedef struct _server_t server_t; +typedef struct _client_t client_t; + +// This structure defines the context for each running server. Store +// whatever properties and structures you need for the server. + +struct _server_t { + // These properties must always be present in the server_t + // and are set by the generated engine; do not modify them! + zsock_t *pipe; // Actor pipe back to caller + zconfig_t *config; // Current loaded configuration + + // TODO: Add any properties you need here +}; + +// --------------------------------------------------------------------------- +// This structure defines the state for each client connection. It will +// be passed to each action in the 'self' argument. + +struct _client_t { + // These properties must always be present in the client_t + // and are set by the generated engine; do not modify them! + server_t *server; // Reference to parent server + wap_proto_t *message; // Message in and out + + // TODO: Add specific properties for your application +}; + +// Include the generated server engine +#include "wap_server_engine.inc" + +// Allocate properties and structures for a new server instance. +// Return 0 if OK, or -1 if there was an error. + +static int +server_initialize (server_t *self) +{ + // Construct properties here + return 0; +} + +// Free properties and structures for a server instance + +static void +server_terminate (server_t *self) +{ + // Destroy properties here +} + +// Process server API method, return reply message if any + +static zmsg_t * +server_method (server_t *self, const char *method, zmsg_t *msg) +{ + return NULL; +} + + +// Allocate properties and structures for a new client connection and +// optionally engine_set_next_event (). Return 0 if OK, or -1 on error. + +static int +client_initialize (client_t *self) +{ + // Construct properties here + return 0; +} + +// Free properties and structures for a client connection + +static void +client_terminate (client_t *self) +{ + // Destroy properties here +} + +// --------------------------------------------------------------------------- +// Selftest + +void +wap_server_test (bool verbose) +{ + printf (" * wap_server: "); + if (verbose) + printf ("\n"); + + // @selftest + zactor_t *server = zactor_new (wap_server, "server"); + if (verbose) + zstr_send (server, "VERBOSE"); + zstr_sendx (server, "BIND", "ipc://@/wap_server", NULL); + + zsock_t *client = zsock_new (ZMQ_DEALER); + assert (client); + zsock_set_rcvtimeo (client, 2000); + zsock_connect (client, "ipc://@/wap_server"); + + // TODO: fill this out + wap_proto_t *request = wap_proto_new (); + wap_proto_destroy (&request); + + zsock_destroy (&client); + zactor_destroy (&server); + // @end + printf ("OK\n"); +} + + +// --------------------------------------------------------------------------- +// register_wallet +// + +static void +register_wallet (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// retrieve_blocks +// + +static void +retrieve_blocks (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// store_transaction +// + +static void +store_transaction (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// retrieve_transaction +// + +static void +retrieve_transaction (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// start_mining_process +// + +static void +start_mining_process (client_t *self) +{ + IPC::Daemon::start_mining(self->message); + printf("\n\n Request: %d \n\n", (int)wap_proto_start_height(self->message)); + // wap_proto_set_curr_height(self->message, 100); +} + + +// --------------------------------------------------------------------------- +// stop_mining_process +// + +static void +stop_mining_process (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// deregister_wallet +// + +static void +deregister_wallet (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// allow_time_to_settle +// + +static void +allow_time_to_settle (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// register_new_client +// + +static void +register_new_client (client_t *self) +{ + +} + + +// --------------------------------------------------------------------------- +// signal_command_not_valid +// + +static void +signal_command_not_valid (client_t *self) +{ + wap_proto_set_status (self->message, WAP_PROTO_COMMAND_INVALID); +} diff --git a/src/rpc/daemon_ipc_server.cpp b/src/rpc/daemon_ipc_server.cpp deleted file mode 100644 index 54f4232da..000000000 --- a/src/rpc/daemon_ipc_server.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2014, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers - -#include "json_rpc_http_server.h" - -#include - -namespace IPC -{ - Daemon_ipc_server::Daemon_ipc_server(const std::string &ip, const std::string &port, - nodetool::node_server > *p_p2p, - cryptonote::core *p_core) - { - if (m_is_running) - { - return false; - } - p2p = p_p2p; - core = p_core; - m_ip = ip; - m_port = port; - zmq::context_t context(1); - socket = new zmq::socket_t(context, ZMQ_REQ); - return true; - } - - void Daemon_ipc_server::start() - { - socket->bind(std::string("tcp://" + m_ip + ":" + m_port); - m_is_running = true; - // Start a new thread so it doesn't block. - server_thread = new boost::thread(&Daemon_ipc_server::poll, this); - } - - Daemon_ipc_server::~Daemon_ipc_server() - { - stop(); - } - - bool Daemon_ipc_server::check_core_busy() - { - if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return false; - } - return true; - } - - /*! - * \brief Stops the server - */ - void Daemon_ipc_server::stop() - { - m_is_running = false; - server_thread->join(); - delete server_thread; - } - - void Daemon_ipc_server::getblocks(std::string &response) - { - - } - void Daemon_ipc_server::sendtransactions(std::string &response); - void Daemon_ipc_server::get_o_indexes(std::string &response); - - void handle_request(const std::string &request_string, std::string &response) - { - if (check_core_busy()) - { - response = ""; - } - if (request_string == "getblocks") - { - getblocks(response); - } - else if (request_string == "sendrawtransaction") - { - sendrawtransaction(response); - } - else if (request_string == "get_o_indexes") - { - get_o_indexes(response); - } - else - { - response = ""; - } - } - - /*! - * \brief Repeatedly loops processing requests if any. - */ - void Daemon_ipc_server::poll() - { - // Loop until the server is running and poll. - while (m_is_running) { - zmq::message_t request; - // Wait for next request from client - socket->recv(&request); - std::string request_string((char*)request.data(), request.size()); - std::string response; - handle_request(request_string, response); - zmq::message_t reply(response.length()); - memcpy((void*)reply.data(), response.c_str(), response.length()); - socket->send(reply); - } - } -} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index af018afb5..2d47db882 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1458,7 +1458,8 @@ int main(int argc, char* argv[]) LOG_ERROR("Failed to store wallet: " << e.what()); return 1; } - }else + } + else { //runs wallet with console interface r = w.init(vm); diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index a8fe78414..9511bb075 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -207,5 +207,6 @@ namespace cryptonote std::unique_ptr m_wallet; epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; + wap_client_t *client; }; } diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index 6107e2136..7a42b2de3 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -48,10 +48,13 @@ bitmonero_add_library(wallet ${wallet_private_headers}) target_link_libraries(wallet LINK_PUBLIC + client_ipc cryptonote_core mnemonics LINK_PRIVATE ${Boost_SERIALIZATION_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_THREAD_LIBRARY} + ${ZMQ_LIB} + ${CZMQ_LIB} ${EXTRA_LIBRARIES}) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index da8898132..c92e5d059 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -83,7 +83,8 @@ namespace tools const size_t MAX_SPLIT_ATTEMPTS = 30; //---------------------------------------------------------------------------------------------------- -void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) +void wallet2::init(const std::string& daemon_address, + uint64_t upper_transaction_size_limit) { m_upper_transaction_size_limit = upper_transaction_size_limit; m_daemon_address = daemon_address; @@ -456,6 +457,9 @@ void wallet2::detach_blockchain(uint64_t height) //---------------------------------------------------------------------------------------------------- bool wallet2::deinit() { + // Great, it all works. Now to shutdown, we use the destroy method, + // which does a proper deconnect handshake internally: + wap_client_destroy (&client); return true; } //---------------------------------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 073fff58b..ed72c76ed 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -46,6 +46,8 @@ #include "common/unordered_containers_boost_serialization.h" #include "crypto/chacha8.h" #include "crypto/hash.h" +#include "wap_library.h" +#include "wap_classes.h" #include "wallet_errors.h" @@ -82,7 +84,13 @@ namespace tools { wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false) {}; public: - wallet2(bool testnet = false) : m_run(true), m_callback(0), m_testnet(testnet), is_old_file_format(false) {}; + wallet2(bool testnet = false) : m_run(true), m_callback(0), m_testnet(testnet) { + client = wap_client_new ("ipc://@/monero", 200, "wallet identity"); + assert (client); + int rc = wap_client_start (client, 25); + std::cout << "\n\n Response: " << (int)wap_client_curr_height(client) << "\n\n"; + assert (rc == 0); + }; struct transfer_details { uint64_t m_block_height; @@ -165,7 +173,9 @@ namespace tools // free block size. TODO: fix this so that it actually takes // into account the current median block size rather than // the minimum block size. - void init(const std::string& daemon_address = "http://localhost:8080", uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); + void init(const std::string& daemon_address = "http://localhost:8080", + uint64_t upper_transaction_size_limit = ((CRYPTONOTE_BLOCK_GRANTED_FULL_REWARD_ZONE * 125) / 100) - + CRYPTONOTE_COINBASE_BLOB_RESERVED_SIZE); bool deinit(); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -298,6 +308,7 @@ namespace tools bool m_testnet; std::string seed_language; /*!< Language of the mnemonics (seed). */ bool is_old_file_format; /*!< Whether the wallet file is of an old file format */ + wap_client_t *client; }; } BOOST_CLASS_VERSION(tools::wallet2, 7) diff --git a/tests/functional_tests/transactions_flow_test.cpp b/tests/functional_tests/transactions_flow_test.cpp index 5d88b65f2..319165ac0 100644 --- a/tests/functional_tests/transactions_flow_test.cpp +++ b/tests/functional_tests/transactions_flow_test.cpp @@ -137,8 +137,6 @@ bool transactions_flow_test(std::string& working_folder, return false; } - w1.init(daemon_addr_a); - size_t blocks_fetched = 0; bool received_money; bool ok; From 9b850f28dec12f6159b23a76a04836ce502f44ad Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 17 Jan 2015 16:53:50 +0530 Subject: [PATCH 25/45] Uncomment 0MQ server init code in daemon --- src/ipc/daemon_ipc_handlers.cpp | 4 ++-- src/wallet/wallet2.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 23042267c..b10c327a4 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -57,9 +57,9 @@ namespace IPC { p2p = p_p2p; core = p_core; - /*server = zactor_new (wap_server, NULL); + server = zactor_new (wap_server, NULL); zsock_send (server, "ss", "BIND", "ipc://@/monero"); - zsock_send (server, "sss", "SET", "server/timeout", "5000");*/ + zsock_send (server, "sss", "SET", "server/timeout", "5000"); } void start_mining(wap_proto_t *message) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 9f3be4a06..db2c97d18 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -86,7 +86,9 @@ namespace tools public: wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet) { client = wap_client_new ("ipc://@/monero", 200, "wallet identity"); - assert (client); + if (!client) { + // TODO: Daemon not up. + } int rc = wap_client_start (client, 25); assert (rc == 0); }; From 3fba3fec571565c35b70db94aebec29012df5659 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 24 Jan 2015 16:43:33 +0530 Subject: [PATCH 26/45] 0MQ end-to-end test works! --- src/ipc/daemon_ipc_handlers.cpp | 2 +- src/ipc/include/wap_client_engine.inc | 29 +++++++++++++++------------ src/wallet/wallet2.h | 1 + 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index b10c327a4..e3175c9db 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -70,7 +70,7 @@ namespace IPC void getblocks(wap_proto_t *message) { - + } void sendtransactions(wap_proto_t *message) diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index c8dc78a7f..995ae8e88 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -56,7 +56,7 @@ typedef enum { start_ok_event = 16, stop_ok_event = 17, close_ok_event = 18, - connection_pong_event = 19, + ping_ok_event = 19, error_event = 20, command_invalid_event = 21, other_event = 22 @@ -102,7 +102,7 @@ s_event_name [] = { "START_OK", "STOP_OK", "CLOSE_OK", - "connection_pong", + "PING_OK", "ERROR", "command_invalid", "other" @@ -407,6 +407,9 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_CLOSE_OK: return close_ok_event; break; + case WAP_PROTO_PING_OK: + return ping_ok_event; + break; case WAP_PROTO_ERROR: return error_event; break; @@ -525,7 +528,7 @@ s_client_execute (s_client_t *self, event_t event) } } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -668,7 +671,7 @@ s_client_execute (s_client_t *self, event_t event) } } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -698,7 +701,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -728,7 +731,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -758,7 +761,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -788,7 +791,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -818,7 +821,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -848,7 +851,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -897,7 +900,7 @@ s_client_execute (s_client_t *self, event_t event) } } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -916,7 +919,7 @@ s_client_execute (s_client_t *self, event_t event) break; case defaults_state: - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { @@ -987,7 +990,7 @@ s_client_execute (s_client_t *self, event_t event) self->state = connected_state; } else - if (self->event == connection_pong_event) { + if (self->event == ping_ok_event) { } else if (self->event == error_event) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index db2c97d18..271229976 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -90,6 +90,7 @@ namespace tools // TODO: Daemon not up. } int rc = wap_client_start (client, 25); + std::cout << "\n\n Response: " << (int)wap_client_curr_height(client) << std::endl; assert (rc == 0); }; struct transfer_details From d75b5dd60b3e717c4cfc413a2b15274ef8f933d5 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 7 Feb 2015 02:29:59 +0530 Subject: [PATCH 27/45] start_mining and get_blocks IPC --- src/common/dns_utils.h | 2 + src/daemon/daemon.cpp | 2 +- src/ipc/daemon_ipc_handlers.cpp | 96 ++++++++++++++++++- src/ipc/include/daemon_ipc_handlers.h | 17 +++- src/ipc/include/wap_client.h | 16 ++-- src/ipc/include/wap_client_engine.inc | 44 ++++----- src/ipc/include/wap_proto.h | 34 ++++--- src/ipc/wap_client/wap_client.c | 10 +- src/ipc/wap_proto.c | 133 ++++++++++++++++---------- src/ipc/wap_server/wap_server.c | 3 +- src/wallet/wallet2.cpp | 51 +++++++--- src/wallet/wallet2.h | 22 ++++- src/wallet/wallet_errors.h | 7 ++ 13 files changed, 305 insertions(+), 132 deletions(-) diff --git a/src/common/dns_utils.h b/src/common/dns_utils.h index a16c7eff7..86c786ec3 100644 --- a/src/common/dns_utils.h +++ b/src/common/dns_utils.h @@ -26,6 +26,8 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#pragma once + #include #include diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index d99d92ecf..6ac98b4f1 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -247,7 +247,7 @@ int main(int argc, char* argv[]) LOG_PRINT_L0("Protocol initialized OK"); LOG_PRINT_L0("Initializing core IPC server..."); - IPC::Daemon::init(&ccore, &p2psrv); + IPC::Daemon::init(&ccore, &p2psrv, testnet_mode); LOG_PRINT_L0("Initializing core RPC server..."); RPC::Daemon::init(&ccore, &p2psrv, testnet_mode); std::string ip_address, port; diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index e3175c9db..d12efde86 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -38,6 +38,7 @@ namespace nodetool::node_server > *p2p; /*!< Pointer to p2p node server */ zactor_t *server; + bool testnet; bool check_core_busy() { if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) @@ -53,10 +54,12 @@ namespace IPC namespace Daemon { void init(cryptonote::core *p_core, - nodetool::node_server > *p_p2p) + nodetool::node_server > *p_p2p, + bool p_testnet) { p2p = p_p2p; core = p_core; + testnet = p_testnet; server = zactor_new (wap_server, NULL); zsock_send (server, "ss", "BIND", "ipc://@/monero"); zsock_send (server, "sss", "SET", "server/timeout", "5000"); @@ -64,13 +67,96 @@ namespace IPC void start_mining(wap_proto_t *message) { - uint64_t start_height = wap_proto_start_height(message); - wap_proto_set_curr_height(message, 2 * start_height); + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + cryptonote::account_public_address adr; + const char *address = wap_proto_address(message); + if (!get_account_address_from_str(adr, testnet, std::string(address))) + { + wap_proto_set_status(message, STATUS_WRONG_ADDRESS); + return; + } + + boost::thread::attributes attrs; + attrs.set_stack_size(THREAD_STACK_SIZE); + + uint64_t threads_count = wap_proto_thread_count(message); + if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) + { + wap_proto_set_status(message, STATUS_MINING_NOT_STARTED); + return; + } + wap_proto_set_status(message, STATUS_OK); } - void getblocks(wap_proto_t *message) + void retrieve_blocks(wap_proto_t *message) { - + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + uint64_t start_height = wap_proto_start_height(message); + zlist_t *z_block_ids = wap_proto_block_ids(message); + + std::list block_ids; + char *block_id = (char*)zlist_first(z_block_ids); + while (block_id) { + crypto::hash hash; + memcpy(hash.data, block_id, crypto::HASH_SIZE); + block_ids.push_back(hash); + block_id = (char*)zlist_next(z_block_ids); + } + + std::list > > bs; + uint64_t result_current_height = 0; + uint64_t result_start_height = 0; + if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, + result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + + rapidjson::Document result_json; + result_json.SetObject(); + rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator(); + rapidjson::Value block_json(rapidjson::kArrayType); + std::string blob; + BOOST_FOREACH(auto &b, bs) + { + rapidjson::Value this_block(rapidjson::kObjectType); + blob = block_to_blob(b.first); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(blob.c_str(), blob.length(), allocator); + this_block.AddMember("block", string_value.Move(), allocator); + rapidjson::Value txs_blocks(rapidjson::kArrayType); + BOOST_FOREACH(auto &t, b.second) + { + rapidjson::Value string_value(rapidjson::kStringType); + blob = cryptonote::tx_to_blob(t); + string_value.SetString(blob.c_str(), blob.length(), allocator); + txs_blocks.PushBack(string_value.Move(), allocator); + } + this_block.AddMember("txs", txs_blocks, allocator); + block_json.PushBack(this_block, allocator); + } + + result_json.AddMember("blocks", block_json, allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + result_json.Accept(writer); + std::string block_string = buffer.GetString(); + zmsg_t *block_data = zmsg_new(); + zframe_t *frame = zframe_new(block_string.c_str(), block_string.length()); + zmsg_prepend(block_data, &frame); + wap_proto_set_start_height(message, result_start_height); + wap_proto_set_curr_height(message, result_current_height); + wap_proto_set_status(message, STATUS_OK); + wap_proto_set_block_data(message, &block_data); + } void sendtransactions(wap_proto_t *message) diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 5ef0aa350..7450a2616 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -45,17 +45,30 @@ using namespace epee; #include "crypto/hash.h" #include "wap_library.h" #include "wap_classes.h" +#include "net/http_server_impl_base.h" +#include "cryptonote_core/cryptonote_basic_impl.h" + +#include "rapidjson/document.h" +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" namespace IPC { + const uint64_t STATUS_OK = 0; + const uint64_t STATUS_CORE_BUSY = 1; + const uint64_t STATUS_WRONG_ADDRESS = 2; + const uint64_t STATUS_MINING_NOT_STARTED = 3; + const uint64_t STATUS_WRONG_BLOCK_ID_LENGTH = 4; + const uint64_t STATUS_INTERNAL_ERROR = 5; namespace Daemon { void start_mining(wap_proto_t *message); - void get_blocks(wap_proto_t *message); + void retrieve_blocks(wap_proto_t *message); void send_transactions(wap_proto_t *message); void get_o_indexes(wap_proto_t *message); void init(cryptonote::core *p_core, - nodetool::node_server > *p_p2p); + nodetool::node_server > *p_p2p, + bool p_testnet); } } diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 83b66b445..8c7688bba 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -57,7 +57,7 @@ WAP_EXPORT zsock_t * // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p); + wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p, uint64_t start_height); // Send a raw transaction to the daemon. // Returns >= 0 if successful, -1 if interrupted. @@ -77,7 +77,7 @@ WAP_EXPORT int // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_start (wap_client_t *self, uint64_t start_height); + wap_client_start (wap_client_t *self, const char *address, uint64_t thread_count); // Send stop command to server. // Returns >= 0 if successful, -1 if interrupted. @@ -93,17 +93,13 @@ WAP_EXPORT const char * wap_client_reason (wap_client_t *self); // Return last received start_height -WAP_EXPORT uint32_t +WAP_EXPORT uint64_t wap_client_start_height (wap_client_t *self); // Return last received curr_height -WAP_EXPORT uint32_t +WAP_EXPORT uint64_t wap_client_curr_height (wap_client_t *self); -// Return last received block_status -WAP_EXPORT const char * - wap_client_block_status (wap_client_t *self); - // Return last received block_data WAP_EXPORT zmsg_t * wap_client_block_data (wap_client_t *self); @@ -116,6 +112,10 @@ WAP_EXPORT const char * WAP_EXPORT zchunk_t * wap_client_tx_data (wap_client_t *self); +// Self test of this class +WAP_EXPORT void + wap_client_test (bool verbose); + // To enable verbose tracing (animation) of wap_client instances, set // this to true. This lets you trace from and including construction. WAP_EXPORT extern volatile int diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 995ae8e88..66e5e3535 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -120,9 +120,11 @@ struct _client_args_t { uint32_t timeout; char *identity; zlist_t *block_ids; + uint64_t start_height; zchunk_t *tx_data; char *tx_id; - uint64_t start_height; + char *address; + uint64_t thread_count; }; typedef struct { @@ -252,6 +254,7 @@ s_client_destroy (s_client_t **self_p) zlist_destroy (&self->args.block_ids); zchunk_destroy (&self->args.tx_data); zstr_free (&self->args.tx_id); + zstr_free (&self->args.address); client_terminate (&self->client); wap_proto_destroy (&self->message); zsock_destroy (&self->msgpipe); @@ -1069,7 +1072,7 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) else if (streq (method, "BLOCKS")) { zlist_destroy (&self->args.block_ids); - zsock_recv (self->cmdpipe, "p", &self->args.block_ids); + zsock_recv (self->cmdpipe, "pi", &self->args.block_ids, &self->args.start_height); s_client_execute (self, blocks_event); } else @@ -1090,7 +1093,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) } else if (streq (method, "START")) { - zsock_recv (self->cmdpipe, "i", &self->args.start_height); + zstr_free (&self->args.address); + zsock_recv (self->cmdpipe, "si", &self->args.address, &self->args.thread_count); s_client_execute (self, start_event); } else @@ -1207,9 +1211,8 @@ struct _wap_client_t { zsock_t *msgpipe; // Pipe for async message flow int status; // Returned by actor reply char *reason; // Returned by actor reply - uint32_t start_height; // Returned by actor reply - uint32_t curr_height; // Returned by actor reply - char *block_status; // Returned by actor reply + uint64_t start_height; // Returned by actor reply + uint64_t curr_height; // Returned by actor reply zmsg_t *block_data; // Returned by actor reply char *tx_id; // Returned by actor reply zchunk_t *tx_data; // Returned by actor reply @@ -1268,7 +1271,6 @@ wap_client_destroy (wap_client_t **self_p) zactor_destroy (&self->actor); zsock_destroy (&self->msgpipe); zstr_free (&self->reason); - zstr_free (&self->block_status); zmsg_destroy (&self->block_data); zstr_free (&self->tx_id); zchunk_destroy (&self->tx_data); @@ -1335,9 +1337,8 @@ s_accept_reply (wap_client_t *self, ...) } else if (streq (reply, "BLOCKS OK")) { - zstr_free (&self->block_status); zmsg_destroy (&self->block_data); - zsock_recv (self->actor, "iiisp", &self->status, &self->start_height, &self->curr_height, &self->block_status, &self->block_data); + zsock_recv (self->actor, "iiip", &self->status, &self->start_height, &self->curr_height, &self->block_data); } else if (streq (reply, "PUT OK")) { @@ -1355,7 +1356,7 @@ s_accept_reply (wap_client_t *self, ...) } else if (streq (reply, "START OK")) { - zsock_recv (self->actor, "ii", &self->status, &self->curr_height); + zsock_recv (self->actor, "i", &self->status); } else if (streq (reply, "STOP OK")) { @@ -1415,10 +1416,10 @@ wap_client_destructor (wap_client_t *self) // Returns >= 0 if successful, -1 if interrupted. int -wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p) +wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p, uint64_t start_height) { assert (self); - zsock_send (self->actor, "sp", "BLOCKS", *block_ids_p); + zsock_send (self->actor, "spi", "BLOCKS", *block_ids_p, start_height); *block_ids_p = NULL; // Take ownership of block_ids if (s_accept_reply (self, "BLOCKS OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1477,10 +1478,10 @@ wap_client_save (wap_client_t *self) // Returns >= 0 if successful, -1 if interrupted. int -wap_client_start (wap_client_t *self, uint64_t start_height) +wap_client_start (wap_client_t *self, const char *address, uint64_t thread_count) { assert (self); - zsock_send (self->actor, "si", "START", start_height); + zsock_send (self->actor, "ssi", "START", address, thread_count); if (s_accept_reply (self, "START OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; @@ -1527,7 +1528,7 @@ wap_client_reason (wap_client_t *self) // --------------------------------------------------------------------------- // Return last received start_height -uint32_t +uint64_t wap_client_start_height (wap_client_t *self) { assert (self); @@ -1538,7 +1539,7 @@ wap_client_start_height (wap_client_t *self) // --------------------------------------------------------------------------- // Return last received curr_height -uint32_t +uint64_t wap_client_curr_height (wap_client_t *self) { assert (self); @@ -1546,17 +1547,6 @@ wap_client_curr_height (wap_client_t *self) } -// --------------------------------------------------------------------------- -// Return last received block_status - -const char * -wap_client_block_status (wap_client_t *self) -{ - assert (self); - return self->block_status; -} - - // --------------------------------------------------------------------------- // Return last received block_data diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 8d8611231..c25a44f97 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -33,11 +33,12 @@ BLOCKS - Wallet requests a set of blocks from the daemon. Daemon replies with BLOCKS-OK, or ERROR if the request is invalid. block_ids strings + start_height number 8 BLOCKS_OK - Daemon returns a set of blocks to the wallet. + status number 8 start_height number 8 curr_height number 8 - block_status string block_data msg Frames of block data PUT - Wallet sends a raw transaction to the daemon. Daemon replies with @@ -60,10 +61,11 @@ with GET-OK, or ERROR. START - Wallet asks daemon to start mining. Daemon replies with START-OK, or ERROR. - start_height number 8 + address string + thread_count number 8 START_OK - Daemon replies to a start mining request. - curr_height number 8 + status number 8 STOP - Wallet asks daemon to start mining. Daemon replies with START-OK, or ERROR. @@ -186,18 +188,18 @@ uint64_t void wap_proto_set_start_height (wap_proto_t *self, uint64_t start_height); +// Get/set the status field +uint64_t + wap_proto_status (wap_proto_t *self); +void + wap_proto_set_status (wap_proto_t *self, uint64_t status); + // Get/set the curr_height field uint64_t wap_proto_curr_height (wap_proto_t *self); void wap_proto_set_curr_height (wap_proto_t *self, uint64_t curr_height); -// Get/set the block_status field -const char * - wap_proto_block_status (wap_proto_t *self); -void - wap_proto_set_block_status (wap_proto_t *self, const char *value); - // Get a copy of the block_data field zmsg_t * wap_proto_block_data (wap_proto_t *self); @@ -224,11 +226,17 @@ const char * void wap_proto_set_tx_id (wap_proto_t *self, const char *value); -// Get/set the status field -uint16_t - wap_proto_status (wap_proto_t *self); +// Get/set the address field +const char * + wap_proto_address (wap_proto_t *self); void - wap_proto_set_status (wap_proto_t *self, uint16_t status); + wap_proto_set_address (wap_proto_t *self, const char *value); + +// Get/set the thread_count field +uint64_t + wap_proto_thread_count (wap_proto_t *self); +void + wap_proto_set_thread_count (wap_proto_t *self, uint64_t thread_count); // Get/set the reason field const char * diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index d95b4b10c..c377709ac 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -118,6 +118,7 @@ static void prepare_blocks_command (client_t *self) { wap_proto_set_block_ids (self->message, &self->args->block_ids); + wap_proto_set_start_height (self->message, self->args->start_height); } @@ -128,10 +129,9 @@ prepare_blocks_command (client_t *self) static void signal_have_blocks_ok (client_t *self) { - zsock_send (self->cmdpipe, "siiisp", "BLOCKS OK", 0, + zsock_send (self->cmdpipe, "siiip", "BLOCKS OK", wap_proto_status(self->message), wap_proto_start_height (self->message), wap_proto_curr_height (self->message), - wap_proto_block_status (self->message), wap_proto_get_block_data (self->message)); } @@ -143,7 +143,8 @@ signal_have_blocks_ok (client_t *self) static void prepare_start_command (client_t *self) { - wap_proto_set_start_height (self->message, self->args->start_height); + wap_proto_set_address (self->message, self->args->address); + wap_proto_set_thread_count (self->message, self->args->thread_count); } // --------------------------------------------------------------------------- @@ -221,8 +222,7 @@ signal_have_save_ok (client_t *self) static void signal_have_start_ok (client_t *self) { - zsock_send (self->cmdpipe, "sii", "START OK", 0, - wap_proto_curr_height (self->message)); + zsock_send(self->cmdpipe, "si", "START OK", wap_proto_status(self->message)); } diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 38c680bbb..d6586c838 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -37,12 +37,13 @@ struct _wap_proto_t { char identity [256]; // Wallet identity zlist_t *block_ids; // uint64_t start_height; // + uint64_t status; // uint64_t curr_height; // - char block_status [256]; // zmsg_t *block_data; // Frames of block data zchunk_t *tx_data; // Transaction data char tx_id [256]; // Transaction ID - uint16_t status; // Error status + char address [256]; // + uint64_t thread_count; // char reason [256]; // Printable explanation }; @@ -307,12 +308,13 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) free (string); } } + GET_NUMBER8 (self->start_height); break; case WAP_PROTO_BLOCKS_OK: + GET_NUMBER8 (self->status); GET_NUMBER8 (self->start_height); GET_NUMBER8 (self->curr_height); - GET_STRING (self->block_status); // Get zero or more remaining frames zmsg_destroy (&self->block_data); if (zsock_rcvmore (input)) @@ -362,11 +364,12 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) break; case WAP_PROTO_START: - GET_NUMBER8 (self->start_height); + GET_STRING (self->address); + GET_NUMBER8 (self->thread_count); break; case WAP_PROTO_START_OK: - GET_NUMBER8 (self->curr_height); + GET_NUMBER8 (self->status); break; case WAP_PROTO_STOP: @@ -437,11 +440,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) block_ids = (char *) zlist_next (self->block_ids); } } + frame_size += 8; // start_height break; case WAP_PROTO_BLOCKS_OK: + frame_size += 8; // status frame_size += 8; // start_height frame_size += 8; // curr_height - frame_size += 1 + strlen (self->block_status); break; case WAP_PROTO_PUT: frame_size += 4; // Size is 4 octets @@ -460,10 +464,11 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += zchunk_size (self->tx_data); break; case WAP_PROTO_START: - frame_size += 8; // start_height + frame_size += 1 + strlen (self->address); + frame_size += 8; // thread_count break; case WAP_PROTO_START_OK: - frame_size += 8; // curr_height + frame_size += 8; // status break; case WAP_PROTO_ERROR: frame_size += 2; // status @@ -497,12 +502,13 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) } else PUT_NUMBER4 (0); // Empty string array + PUT_NUMBER8 (self->start_height); break; case WAP_PROTO_BLOCKS_OK: + PUT_NUMBER8 (self->status); PUT_NUMBER8 (self->start_height); PUT_NUMBER8 (self->curr_height); - PUT_STRING (self->block_status); nbr_frames += self->block_data? zmsg_size (self->block_data): 1; send_block_data = true; break; @@ -540,11 +546,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_START: - PUT_NUMBER8 (self->start_height); + PUT_STRING (self->address); + PUT_NUMBER8 (self->thread_count); break; case WAP_PROTO_START_OK: - PUT_NUMBER8 (self->curr_height); + PUT_NUMBER8 (self->status); break; case WAP_PROTO_ERROR: @@ -604,16 +611,14 @@ wap_proto_print (wap_proto_t *self) block_ids = (char *) zlist_next (self->block_ids); } } + zsys_debug (" start_height=%ld", (long) self->start_height); break; case WAP_PROTO_BLOCKS_OK: zsys_debug ("WAP_PROTO_BLOCKS_OK:"); + zsys_debug (" status=%ld", (long) self->status); zsys_debug (" start_height=%ld", (long) self->start_height); zsys_debug (" curr_height=%ld", (long) self->curr_height); - if (self->block_status) - zsys_debug (" block_status='%s'", self->block_status); - else - zsys_debug (" block_status="); zsys_debug (" block_data="); if (self->block_data) zmsg_print (self->block_data); @@ -657,12 +662,16 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_START: zsys_debug ("WAP_PROTO_START:"); - zsys_debug (" start_height=%ld", (long) self->start_height); + if (self->address) + zsys_debug (" address='%s'", self->address); + else + zsys_debug (" address="); + zsys_debug (" thread_count=%ld", (long) self->thread_count); break; case WAP_PROTO_START_OK: zsys_debug ("WAP_PROTO_START_OK:"); - zsys_debug (" curr_height=%ld", (long) self->curr_height); + zsys_debug (" status=%ld", (long) self->status); break; case WAP_PROTO_STOP: @@ -881,6 +890,24 @@ wap_proto_set_start_height (wap_proto_t *self, uint64_t start_height) } +// -------------------------------------------------------------------------- +// Get/set the status field + +uint64_t +wap_proto_status (wap_proto_t *self) +{ + assert (self); + return self->status; +} + +void +wap_proto_set_status (wap_proto_t *self, uint64_t status) +{ + assert (self); + self->status = status; +} + + // -------------------------------------------------------------------------- // Get/set the curr_height field @@ -899,28 +926,6 @@ wap_proto_set_curr_height (wap_proto_t *self, uint64_t curr_height) } -// -------------------------------------------------------------------------- -// Get/set the block_status field - -const char * -wap_proto_block_status (wap_proto_t *self) -{ - assert (self); - return self->block_status; -} - -void -wap_proto_set_block_status (wap_proto_t *self, const char *value) -{ - assert (self); - assert (value); - if (value == self->block_status) - return; - strncpy (self->block_status, value, 255); - self->block_status [255] = 0; -} - - // -------------------------------------------------------------------------- // Get the block_data field without transferring ownership @@ -1010,20 +1015,42 @@ wap_proto_set_tx_id (wap_proto_t *self, const char *value) // -------------------------------------------------------------------------- -// Get/set the status field +// Get/set the address field -uint16_t -wap_proto_status (wap_proto_t *self) +const char * +wap_proto_address (wap_proto_t *self) { assert (self); - return self->status; + return self->address; } void -wap_proto_set_status (wap_proto_t *self, uint16_t status) +wap_proto_set_address (wap_proto_t *self, const char *value) { assert (self); - self->status = status; + assert (value); + if (value == self->address) + return; + strncpy (self->address, value, 255); + self->address [255] = 0; +} + + +// -------------------------------------------------------------------------- +// Get/set the thread_count field + +uint64_t +wap_proto_thread_count (wap_proto_t *self) +{ + assert (self); + return self->thread_count; +} + +void +wap_proto_set_thread_count (wap_proto_t *self, uint64_t thread_count) +{ + assert (self); + self->thread_count = thread_count; } @@ -1104,6 +1131,7 @@ wap_proto_test (bool verbose) zlist_append (blocks_block_ids, "Name: Brutus"); zlist_append (blocks_block_ids, "Age: 43"); wap_proto_set_block_ids (self, &blocks_block_ids); + wap_proto_set_start_height (self, 123); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1116,12 +1144,13 @@ wap_proto_test (bool verbose) assert (streq ((char *) zlist_first (block_ids), "Name: Brutus")); assert (streq ((char *) zlist_next (block_ids), "Age: 43")); zlist_destroy (&block_ids); + assert (wap_proto_start_height (self) == 123); } wap_proto_set_id (self, WAP_PROTO_BLOCKS_OK); + wap_proto_set_status (self, 123); wap_proto_set_start_height (self, 123); wap_proto_set_curr_height (self, 123); - wap_proto_set_block_status (self, "Life is short but Now lasts for ever"); zmsg_t *blocks_ok_block_data = zmsg_new (); wap_proto_set_block_data (self, &blocks_ok_block_data); zmsg_addstr (wap_proto_block_data (self), "Hello, World"); @@ -1132,9 +1161,9 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); assert (wap_proto_start_height (self) == 123); assert (wap_proto_curr_height (self) == 123); - assert (streq (wap_proto_block_status (self), "Life is short but Now lasts for ever")); assert (zmsg_size (wap_proto_block_data (self)) == 1); } wap_proto_set_id (self, WAP_PROTO_PUT); @@ -1209,7 +1238,8 @@ wap_proto_test (bool verbose) } wap_proto_set_id (self, WAP_PROTO_START); - wap_proto_set_start_height (self, 123); + wap_proto_set_address (self, "Life is short but Now lasts for ever"); + wap_proto_set_thread_count (self, 123); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1217,11 +1247,12 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (wap_proto_start_height (self) == 123); + assert (streq (wap_proto_address (self), "Life is short but Now lasts for ever")); + assert (wap_proto_thread_count (self) == 123); } wap_proto_set_id (self, WAP_PROTO_START_OK); - wap_proto_set_curr_height (self, 123); + wap_proto_set_status (self, 123); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1229,7 +1260,7 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (wap_proto_curr_height (self) == 123); + assert (wap_proto_status (self) == 123); } wap_proto_set_id (self, WAP_PROTO_STOP); diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index f3ccdce4a..a1aa95a0c 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -150,7 +150,7 @@ register_wallet (client_t *self) static void retrieve_blocks (client_t *self) { - + IPC::Daemon::retrieve_blocks(self->message); } @@ -185,7 +185,6 @@ start_mining_process (client_t *self) { IPC::Daemon::start_mining(self->message); printf("\n\n Request: %d \n\n", (int)wap_proto_start_height(self->message)); - // wap_proto_set_curr_height(self->message, 100); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 295d8b0a1..e7c6d842f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -314,24 +314,49 @@ void wallet2::get_short_chain_history(std::list& ids) if(!genesis_included) ids.push_back(m_blockchain[0]); } +void wallet2::get_blocks_from_zmq_msg(zmsg_t *msg, std::list &blocks) { + zframe_t *frame = zmsg_first(msg); + THROW_WALLET_EXCEPTION_IF(!frame, error::get_blocks_error, "getblocks"); + size_t size = zframe_size(frame); + char *block_data = reinterpret_cast(zframe_data(frame)); + + rapidjson::Document json; + int _i = 0; + THROW_WALLET_EXCEPTION_IF(json.Parse(block_data, size).HasParseError(), error::get_blocks_error, "getblocks"); + for (rapidjson::SizeType i = 0; i < json["blocks"].Size(); i++) { + block_complete_entry block_entry; + std::string block_string(json["blocks"][i]["block"].GetString(), json["blocks"][i]["block"].GetStringLength()); + block_entry.block = block_string; + for (rapidjson::SizeType j = 0; j < json["blocks"][i]["txs"].Size(); j++) { + block_entry.txs.push_back(std::string(json["blocks"][i]["txs"][j].GetString(), json["blocks"][i]["txs"][j].GetStringLength())); + } + blocks.push_back(block_entry); + } +} //---------------------------------------------------------------------------------------------------- void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added) { blocks_added = 0; - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::request req = AUTO_VAL_INIT(req); - cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response res = AUTO_VAL_INIT(res); - get_short_chain_history(req.block_ids); - req.start_height = start_height; - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getblocks.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getblocks.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_blocks_error, res.status); + std::list block_ids; + get_short_chain_history(block_ids); + zlist_t *list = zlist_new(); + for (std::list::iterator it = block_ids.begin(); it != block_ids.end(); it++) { + zlist_append(list, it->data); + } + int rc = wap_client_blocks(client, &list, start_height); + THROW_WALLET_EXCEPTION_IF(rc != 0, error::no_connection_to_daemon, "getblocks"); - size_t current_index = res.start_height; - BOOST_FOREACH(auto& bl_entry, res.blocks) + uint64_t status = wap_client_status(client); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getblocks"); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "getblocks"); + THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_blocks_error, "getblocks"); + std::list blocks; + get_blocks_from_zmq_msg(wap_client_block_data(client), blocks); + uint64_t current_index = wap_client_start_height(client); + BOOST_FOREACH(auto& bl_entry, blocks) { cryptonote::block bl; - r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); + bool r = cryptonote::parse_and_validate_block_from_blob(bl_entry.block, bl); THROW_WALLET_EXCEPTION_IF(!r, error::block_parse_error, bl_entry.block); crypto::hash bl_id = get_block_hash(bl); @@ -343,9 +368,9 @@ void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added) else if(bl_id != m_blockchain[current_index]) { //split detected here !!! - THROW_WALLET_EXCEPTION_IF(current_index == res.start_height, error::wallet_internal_error, + THROW_WALLET_EXCEPTION_IF(current_index == start_height, error::wallet_internal_error, "wrong daemon response: split starts from the first block in response " + string_tools::pod_to_hex(bl_id) + - " (height " + std::to_string(res.start_height) + "), local block id at this height: " + + " (height " + std::to_string(start_height) + "), local block id at this height: " + string_tools::pod_to_hex(m_blockchain[current_index])); detach_blockchain(current_index); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 271229976..14b662956 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -46,10 +46,9 @@ #include "common/unordered_containers_boost_serialization.h" #include "crypto/chacha8.h" #include "crypto/hash.h" -#include "wap_library.h" -#include "wap_classes.h" #include "wallet_errors.h" +#include "daemon_ipc_handlers.h" #include #define DEFAULT_TX_SPENDABLE_AGE 10 @@ -89,9 +88,21 @@ namespace tools if (!client) { // TODO: Daemon not up. } - int rc = wap_client_start (client, 25); - std::cout << "\n\n Response: " << (int)wap_client_curr_height(client) << std::endl; - assert (rc == 0); + /*zlist_t *list = zlist_new(); + char cheese[32]; + const char *foo = "771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148"; + for (int i = 0; i < 63; i += 2) { + std::string x = ""; + x += foo[i]; + x += foo[i + 1]; + cheese[i / 2] = (char)stoi(x, NULL, 16); + } + zlist_append(list, cheese); + uint64_t start_height = 1; + int rc = wap_client_blocks(client, &list, start_height); + // int rc = wap_client_start(client, 25); + // std::cout << "\n\n Response: " << (int)wap_client_curr_height(client) << std::endl; + assert (rc == 0);*/ }; struct transfer_details { @@ -283,6 +294,7 @@ namespace tools bool is_tx_spendtime_unlocked(uint64_t unlock_time) const; bool is_transfer_unlocked(const transfer_details& td) const; bool clear(); + void get_blocks_from_zmq_msg(zmsg_t *msg, std::list &blocks); void pull_blocks(uint64_t start_height, size_t& blocks_added); uint64_t select_transfers(uint64_t needed_money, bool add_dust, uint64_t dust, std::list& selected_transfers); bool prepare_file_names(const std::string& file_path); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index bf520c36c..7809c3698 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -565,6 +565,13 @@ namespace tools { } }; + struct daemon_internal_error : public wallet_rpc_error + { + explicit daemon_internal_error(std::string&& loc, const std::string& request) + : wallet_rpc_error(std::move(loc), "daemon had an internal error processing the request", request) + { + } + }; //---------------------------------------------------------------------------------------------------- struct no_connection_to_daemon : public wallet_rpc_error { From f810f5aa9bacd39b3e8c0bfc3d268341ab49e5c2 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 14 Feb 2015 18:44:00 +0530 Subject: [PATCH 28/45] Size prepend strings --- src/ipc/daemon_ipc_handlers.cpp | 2 +- src/wallet/wallet2.cpp | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index d12efde86..141ae234c 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -104,7 +104,7 @@ namespace IPC char *block_id = (char*)zlist_first(z_block_ids); while (block_id) { crypto::hash hash; - memcpy(hash.data, block_id, crypto::HASH_SIZE); + memcpy(hash.data, block_id + 1, crypto::HASH_SIZE); block_ids.push_back(hash); block_id = (char*)zlist_next(z_block_ids); } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e7c6d842f..9644384d1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -339,11 +339,22 @@ void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added) blocks_added = 0; std::list block_ids; get_short_chain_history(block_ids); + std::list size_prepended_block_ids; zlist_t *list = zlist_new(); for (std::list::iterator it = block_ids.begin(); it != block_ids.end(); it++) { - zlist_append(list, it->data); + char *block_id = new char[crypto::HASH_SIZE + 1]; + block_id[0] = crypto::HASH_SIZE; + memcpy(block_id + 1, it->data, crypto::HASH_SIZE); + size_prepended_block_ids.push_back(block_id); + } + for (std::list::iterator it = size_prepended_block_ids.begin(); it != size_prepended_block_ids.end(); it++) { + zlist_append(list, *it); } int rc = wap_client_blocks(client, &list, start_height); + for (std::list::iterator it = size_prepended_block_ids.begin(); it != size_prepended_block_ids.end(); it++) { + delete *it; + } + zlist_destroy(&list); THROW_WALLET_EXCEPTION_IF(rc != 0, error::no_connection_to_daemon, "getblocks"); uint64_t status = wap_client_status(client); From a3ea924f89fbe5a41a38f0511471974c9b2dad7f Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 16 Feb 2015 16:33:36 +0530 Subject: [PATCH 29/45] Output indexes (throws) --- src/ipc/daemon_ipc_handlers.cpp | 24 +++- src/ipc/include/daemon_ipc_handlers.h | 4 +- src/ipc/include/wap_client.h | 13 ++- src/ipc/include/wap_client_engine.inc | 153 ++++++++++++++++++++------ src/ipc/include/wap_proto.h | 45 +++++--- src/ipc/include/wap_server_engine.inc | 43 ++++++-- src/ipc/wap_client/wap_client.c | 19 ++++ src/ipc/wap_proto.c | 121 ++++++++++++++++++++ src/ipc/wap_server/wap_server.c | 20 ++++ src/wallet/wallet2.cpp | 31 +++++- 10 files changed, 406 insertions(+), 67 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 141ae234c..0c1fbe58d 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -159,14 +159,32 @@ namespace IPC } - void sendtransactions(wap_proto_t *message) + void send_raw_transaction(wap_proto_t *message) { } - void get_o_indexes(wap_proto_t *message) + void get_output_indexes(wap_proto_t *message) { - + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + const char *tx_id = wap_proto_tx_id(message); + crypto::hash hash; + memcpy(hash.data, tx_id + 1, crypto::HASH_SIZE); + std::vector output_indexes; + bool r = core->get_tx_outputs_gindexs(hash, output_indexes); + if (!r) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + // Spec guarantees that vector elements are contiguous. So coversion to uint64_t[] is easy. + uint64_t *indexes = &output_indexes[0]; + zframe_t *frame = zframe_new(indexes, sizeof(uint64_t) * output_indexes.size()); + wap_proto_set_o_indexes(message, &frame); + wap_proto_set_status(message, 100); } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 7450a2616..b46df43ed 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -64,8 +64,8 @@ namespace IPC { void start_mining(wap_proto_t *message); void retrieve_blocks(wap_proto_t *message); - void send_transactions(wap_proto_t *message); - void get_o_indexes(wap_proto_t *message); + void send_raw_transaction(wap_proto_t *message); + void get_output_indexes(wap_proto_t *message); void init(cryptonote::core *p_core, nodetool::node_server > *p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 8c7688bba..8412157da 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -74,6 +74,11 @@ WAP_EXPORT int WAP_EXPORT int wap_client_save (wap_client_t *self); +// Ask for tx output indexes. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_output_indexes (wap_client_t *self, const char *tx_id); + // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int @@ -104,14 +109,14 @@ WAP_EXPORT uint64_t WAP_EXPORT zmsg_t * wap_client_block_data (wap_client_t *self); -// Return last received tx_id -WAP_EXPORT const char * - wap_client_tx_id (wap_client_t *self); - // Return last received tx_data WAP_EXPORT zchunk_t * wap_client_tx_data (wap_client_t *self); +// Return last received o_indexes +WAP_EXPORT zframe_t * + wap_client_o_indexes (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 66e5e3535..0889a375c 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -30,10 +30,11 @@ typedef enum { expect_save_ok_state = 7, expect_start_ok_state = 8, expect_stop_ok_state = 9, - expect_close_ok_state = 10, - defaults_state = 11, - have_error_state = 12, - reexpect_open_ok_state = 13 + expect_output_indexes_ok_state = 10, + expect_close_ok_state = 11, + defaults_state = 12, + have_error_state = 13, + reexpect_open_ok_state = 14 } state_t; typedef enum { @@ -48,18 +49,20 @@ typedef enum { save_event = 8, start_event = 9, stop_event = 10, - destructor_event = 11, - blocks_ok_event = 12, - get_ok_event = 13, - put_ok_event = 14, - save_ok_event = 15, - start_ok_event = 16, - stop_ok_event = 17, - close_ok_event = 18, - ping_ok_event = 19, - error_event = 20, - command_invalid_event = 21, - other_event = 22 + output_indexes_event = 11, + destructor_event = 12, + blocks_ok_event = 13, + get_ok_event = 14, + put_ok_event = 15, + save_ok_event = 16, + start_ok_event = 17, + stop_ok_event = 18, + output_indexes_ok_event = 19, + close_ok_event = 20, + ping_ok_event = 21, + error_event = 22, + command_invalid_event = 23, + other_event = 24 } event_t; // Names for state machine logging and error reporting @@ -75,6 +78,7 @@ s_state_name [] = { "expect save ok", "expect start ok", "expect stop ok", + "expect output indexes ok", "expect close ok", "defaults", "have error", @@ -94,6 +98,7 @@ s_event_name [] = { "SAVE", "START", "STOP", + "OUTPUT_INDEXES", "destructor", "BLOCKS_OK", "GET_OK", @@ -101,6 +106,7 @@ s_event_name [] = { "SAVE_OK", "START_OK", "STOP_OK", + "OUTPUT_INDEXES_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -184,6 +190,8 @@ static void prepare_save_command (client_t *self); static void prepare_start_command (client_t *self); +static void + prepare_get_output_indexes_command (client_t *self); static void signal_have_blocks_ok (client_t *self); static void @@ -196,6 +204,8 @@ static void signal_have_start_ok (client_t *self); static void signal_have_stop_ok (client_t *self); +static void + signal_have_output_indexes_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -383,6 +393,12 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_PUT_OK: return put_ok_event; break; + case WAP_PROTO_OUTPUT_INDEXES: + return output_indexes_event; + break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + return output_indexes_ok_event; + break; case WAP_PROTO_GET: return get_event; break; @@ -652,6 +668,24 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_stop_ok_state; } else + if (self->event == output_indexes_event) { + if (!self->exception) { + // prepare get output indexes command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get output indexes command"); + prepare_get_output_indexes_command (&self->client); + } + if (!self->exception) { + // send OUTPUT_INDEXES + if (wap_client_verbose) + zsys_debug ("wap_client: $ send OUTPUT_INDEXES"); + wap_proto_set_id (self->message, WAP_PROTO_OUTPUT_INDEXES); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_output_indexes_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -872,6 +906,36 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_output_indexes_ok_state: + if (self->event == output_indexes_ok_event) { + if (!self->exception) { + // signal have output indexes ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have output indexes ok"); + signal_have_output_indexes_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else { + // Handle unexpected protocol events + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1092,6 +1156,12 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) s_client_execute (self, save_event); } else + if (streq (method, "OUTPUT INDEXES")) { + zstr_free (&self->args.tx_id); + zsock_recv (self->cmdpipe, "s", &self->args.tx_id); + s_client_execute (self, output_indexes_event); + } + else if (streq (method, "START")) { zstr_free (&self->args.address); zsock_recv (self->cmdpipe, "si", &self->args.address, &self->args.thread_count); @@ -1214,8 +1284,8 @@ struct _wap_client_t { uint64_t start_height; // Returned by actor reply uint64_t curr_height; // Returned by actor reply zmsg_t *block_data; // Returned by actor reply - char *tx_id; // Returned by actor reply zchunk_t *tx_data; // Returned by actor reply + zframe_t *o_indexes; // Returned by actor reply }; @@ -1272,8 +1342,8 @@ wap_client_destroy (wap_client_t **self_p) zsock_destroy (&self->msgpipe); zstr_free (&self->reason); zmsg_destroy (&self->block_data); - zstr_free (&self->tx_id); zchunk_destroy (&self->tx_data); + zframe_destroy (&self->o_indexes); free (self); *self_p = NULL; } @@ -1342,8 +1412,7 @@ s_accept_reply (wap_client_t *self, ...) } else if (streq (reply, "PUT OK")) { - zstr_free (&self->tx_id); - zsock_recv (self->actor, "is", &self->status, &self->tx_id); + zsock_recv (self->actor, "i", &self->status); } else if (streq (reply, "GET OK")) { @@ -1355,6 +1424,11 @@ s_accept_reply (wap_client_t *self, ...) zsock_recv (self->actor, "i", &self->status); } else + if (streq (reply, "OUTPUT INDEXES OK")) { + zframe_destroy (&self->o_indexes); + zsock_recv (self->actor, "ip", &self->status, &self->o_indexes); + } + else if (streq (reply, "START OK")) { zsock_recv (self->actor, "i", &self->status); } @@ -1473,6 +1547,21 @@ wap_client_save (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Ask for tx output indexes. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_output_indexes (wap_client_t *self, const char *tx_id) +{ + assert (self); + zsock_send (self->actor, "ss", "OUTPUT INDEXES", tx_id); + if (s_accept_reply (self, "OUTPUT INDEXES OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. @@ -1558,17 +1647,6 @@ wap_client_block_data (wap_client_t *self) } -// --------------------------------------------------------------------------- -// Return last received tx_id - -const char * -wap_client_tx_id (wap_client_t *self) -{ - assert (self); - return self->tx_id; -} - - // --------------------------------------------------------------------------- // Return last received tx_data @@ -1578,3 +1656,16 @@ wap_client_tx_data (wap_client_t *self) assert (self); return self->tx_data; } + + +// --------------------------------------------------------------------------- +// Return last received o_indexes + +zframe_t * +wap_client_o_indexes (wap_client_t *self) +{ +printf("here1\n"); + assert (self); +printf("here2\n"); + return self->o_indexes; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index c25a44f97..f7b925f3d 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -46,8 +46,15 @@ PUT-OK, or ERROR. tx_data chunk Transaction data PUT_OK - Daemon confirms that it accepted the raw transaction. + status number 8 Transaction ID + + OUTPUT_INDEXES - Ask for tx output indexes. tx_id string Transaction ID + OUTPUT_INDEXES_OK - Daemon returns tx output indexes. + status number 8 Status + o_indexes frame Output Indexes + GET - Wallet requests transaction data from the daemon. Daemon replies with GET-OK, or ERROR. tx_id string Transaction ID @@ -104,19 +111,21 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_BLOCKS_OK 4 #define WAP_PROTO_PUT 5 #define WAP_PROTO_PUT_OK 6 -#define WAP_PROTO_GET 7 -#define WAP_PROTO_GET_OK 8 -#define WAP_PROTO_SAVE 9 -#define WAP_PROTO_SAVE_OK 10 -#define WAP_PROTO_START 11 -#define WAP_PROTO_START_OK 12 -#define WAP_PROTO_STOP 13 -#define WAP_PROTO_STOP_OK 14 -#define WAP_PROTO_CLOSE 15 -#define WAP_PROTO_CLOSE_OK 16 -#define WAP_PROTO_PING 17 -#define WAP_PROTO_PING_OK 18 -#define WAP_PROTO_ERROR 19 +#define WAP_PROTO_OUTPUT_INDEXES 7 +#define WAP_PROTO_OUTPUT_INDEXES_OK 8 +#define WAP_PROTO_GET 9 +#define WAP_PROTO_GET_OK 10 +#define WAP_PROTO_SAVE 11 +#define WAP_PROTO_SAVE_OK 12 +#define WAP_PROTO_START 13 +#define WAP_PROTO_START_OK 14 +#define WAP_PROTO_STOP 15 +#define WAP_PROTO_STOP_OK 16 +#define WAP_PROTO_CLOSE 17 +#define WAP_PROTO_CLOSE_OK 18 +#define WAP_PROTO_PING 19 +#define WAP_PROTO_PING_OK 20 +#define WAP_PROTO_ERROR 21 #include @@ -226,6 +235,16 @@ const char * void wap_proto_set_tx_id (wap_proto_t *self, const char *value); +// Get a copy of the o_indexes field +zframe_t * + wap_proto_o_indexes (wap_proto_t *self); +// Get the o_indexes field and transfer ownership to caller +zframe_t * + wap_proto_get_o_indexes (wap_proto_t *self); +// Set the o_indexes field, transferring ownership from caller +void + wap_proto_set_o_indexes (wap_proto_t *self, zframe_t **frame_p); + // Get/set the address field const char * wap_proto_address (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index a7fc04154..d01943b83 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -37,11 +37,12 @@ typedef enum { save_event = 6, start_event = 7, stop_event = 8, - close_event = 9, - ping_event = 10, - expired_event = 11, - exception_event = 12, - settled_event = 13 + output_indexes_event = 9, + close_event = 10, + ping_event = 11, + expired_event = 12, + exception_event = 13, + settled_event = 14 } event_t; // Names for state machine logging and error reporting @@ -65,6 +66,7 @@ s_event_name [] = { "SAVE", "START", "STOP", + "OUTPUT_INDEXES", "CLOSE", "PING", "expired", @@ -137,13 +139,15 @@ static void static void retrieve_blocks (client_t *self); static void - store_transaction (client_t *self); + send_transaction (client_t *self); static void retrieve_transaction (client_t *self); static void start_mining_process (client_t *self); static void stop_mining_process (client_t *self); +static void + output_indexes (client_t *self); static void deregister_wallet (client_t *self); static void @@ -337,6 +341,9 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_PUT: return put_event; break; + case WAP_PROTO_OUTPUT_INDEXES: + return output_indexes_event; + break; case WAP_PROTO_GET: return get_event; break; @@ -584,10 +591,10 @@ s_client_execute (s_client_t *self, event_t event) else if (self->event == put_event) { if (!self->exception) { - // store transaction + // send transaction if (self->server->verbose) - zsys_debug ("%s: $ store transaction", self->log_prefix); - store_transaction (&self->client); + zsys_debug ("%s: $ send transaction", self->log_prefix); + send_transaction (&self->client); } if (!self->exception) { // send PUT_OK @@ -666,6 +673,24 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == output_indexes_event) { + if (!self->exception) { + // output indexes + if (self->server->verbose) + zsys_debug ("%s: $ output indexes", self->log_prefix); + output_indexes (&self->client); + } + if (!self->exception) { + // send OUTPUT_INDEXES_OK + if (self->server->verbose) + zsys_debug ("%s: $ send OUTPUT_INDEXES_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_OUTPUT_INDEXES_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index c377709ac..095c96974 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -121,6 +121,15 @@ prepare_blocks_command (client_t *self) wap_proto_set_start_height (self->message, self->args->start_height); } +// --------------------------------------------------------------------------- +// prepare_get_output_indexes_command +// + +static void +prepare_get_output_indexes_command (client_t *self) +{ + wap_proto_set_tx_id (self->message, self->args->tx_id); +} // --------------------------------------------------------------------------- // signal_have_blocks_ok @@ -236,6 +245,16 @@ signal_have_stop_ok (client_t *self) zsock_send (self->cmdpipe, "si", "STOP OK", 0); } +// --------------------------------------------------------------------------- +// signal_have_output_indexes_ok +// + +static void +signal_have_output_indexes_ok (client_t *self) +{ + zsock_send(self->cmdpipe, "sip", "OUTPUT INDEXES OK", wap_proto_status(self->message), + wap_proto_o_indexes(self->message)); +} // --------------------------------------------------------------------------- // signal_success diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index d6586c838..932c2ff22 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -42,6 +42,7 @@ struct _wap_proto_t { zmsg_t *block_data; // Frames of block data zchunk_t *tx_data; // Transaction data char tx_id [256]; // Transaction ID + zframe_t *o_indexes; // Output Indexes char address [256]; // uint64_t thread_count; // char reason [256]; // Printable explanation @@ -224,6 +225,7 @@ wap_proto_destroy (wap_proto_t **self_p) zlist_destroy (&self->block_ids); zmsg_destroy (&self->block_data); zchunk_destroy (&self->tx_data); + zframe_destroy (&self->o_indexes); // Free object itself free (self); @@ -337,9 +339,24 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) break; case WAP_PROTO_PUT_OK: + GET_NUMBER8 (self->status); + break; + + case WAP_PROTO_OUTPUT_INDEXES: GET_STRING (self->tx_id); break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + GET_NUMBER8 (self->status); + // Get next frame off socket + if (!zsock_rcvmore (input)) { + zsys_warning ("wap_proto: o_indexes is missing"); + goto malformed; + } + zframe_destroy (&self->o_indexes); + self->o_indexes = zframe_recv (input); + break; + case WAP_PROTO_GET: GET_STRING (self->tx_id); break; @@ -453,8 +470,14 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += zchunk_size (self->tx_data); break; case WAP_PROTO_PUT_OK: + frame_size += 8; // status + break; + case WAP_PROTO_OUTPUT_INDEXES: frame_size += 1 + strlen (self->tx_id); break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + frame_size += 8; // status + break; case WAP_PROTO_GET: frame_size += 1 + strlen (self->tx_id); break; @@ -526,9 +549,18 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_PUT_OK: + PUT_NUMBER8 (self->status); + break; + + case WAP_PROTO_OUTPUT_INDEXES: PUT_STRING (self->tx_id); break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + PUT_NUMBER8 (self->status); + nbr_frames++; + break; + case WAP_PROTO_GET: PUT_STRING (self->tx_id); break; @@ -563,6 +595,14 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) // Now send the data frame zmq_msg_send (&frame, zsock_resolve (output), --nbr_frames? ZMQ_SNDMORE: 0); + // Now send any frame fields, in order + if (self->id == WAP_PROTO_OUTPUT_INDEXES_OK) { + // If o_indexes isn't set, send an empty frame + if (self->o_indexes) + zframe_send (&self->o_indexes, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + else + zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); + } // Now send the block_data if necessary if (send_block_data) { if (self->block_data) { @@ -633,12 +673,27 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_PUT_OK: zsys_debug ("WAP_PROTO_PUT_OK:"); + zsys_debug (" status=%ld", (long) self->status); + break; + + case WAP_PROTO_OUTPUT_INDEXES: + zsys_debug ("WAP_PROTO_OUTPUT_INDEXES:"); if (self->tx_id) zsys_debug (" tx_id='%s'", self->tx_id); else zsys_debug (" tx_id="); break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + zsys_debug ("WAP_PROTO_OUTPUT_INDEXES_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" o_indexes="); + if (self->o_indexes) + zframe_print (self->o_indexes, NULL); + else + zsys_debug ("(NULL)"); + break; + case WAP_PROTO_GET: zsys_debug ("WAP_PROTO_GET:"); if (self->tx_id) @@ -772,6 +827,12 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_PUT_OK: return ("PUT_OK"); break; + case WAP_PROTO_OUTPUT_INDEXES: + return ("OUTPUT_INDEXES"); + break; + case WAP_PROTO_OUTPUT_INDEXES_OK: + return ("OUTPUT_INDEXES_OK"); + break; case WAP_PROTO_GET: return ("GET"); break; @@ -1014,6 +1075,39 @@ wap_proto_set_tx_id (wap_proto_t *self, const char *value) } +// -------------------------------------------------------------------------- +// Get the o_indexes field without transferring ownership + +zframe_t * +wap_proto_o_indexes (wap_proto_t *self) +{ + assert (self); + return self->o_indexes; +} + +// Get the o_indexes field and transfer ownership to caller + +zframe_t * +wap_proto_get_o_indexes (wap_proto_t *self) +{ + zframe_t *o_indexes = self->o_indexes; + self->o_indexes = NULL; + return o_indexes; +} + +// Set the o_indexes field, transferring ownership from caller + +void +wap_proto_set_o_indexes (wap_proto_t *self, zframe_t **frame_p) +{ + assert (self); + assert (frame_p); + zframe_destroy (&self->o_indexes); + self->o_indexes = *frame_p; + *frame_p = NULL; +} + + // -------------------------------------------------------------------------- // Get/set the address field @@ -1181,6 +1275,18 @@ wap_proto_test (bool verbose) } wap_proto_set_id (self, WAP_PROTO_PUT_OK); + wap_proto_set_status (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_OUTPUT_INDEXES); + wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); // Send twice wap_proto_send (self, output); @@ -1191,6 +1297,21 @@ wap_proto_test (bool verbose) assert (wap_proto_routing_id (self)); assert (streq (wap_proto_tx_id (self), "Life is short but Now lasts for ever")); } + wap_proto_set_id (self, WAP_PROTO_OUTPUT_INDEXES_OK); + + wap_proto_set_status (self, 123); + zframe_t *output_indexes_ok_o_indexes = zframe_new ("Captcha Diem", 12); + wap_proto_set_o_indexes (self, &output_indexes_ok_o_indexes); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (zframe_streq (wap_proto_o_indexes (self), "Captcha Diem")); + } wap_proto_set_id (self, WAP_PROTO_GET); wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index a1aa95a0c..9a71bee6f 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -198,6 +198,26 @@ stop_mining_process (client_t *self) } +// --------------------------------------------------------------------------- +// output_indexes +// + +static void +output_indexes (client_t *self) +{ + IPC::Daemon::get_output_indexes(self->message); +} + +// --------------------------------------------------------------------------- +// send_transaction +// + +static void +send_transaction (client_t *self) +{ + IPC::Daemon::send_raw_transaction(self->message); +} + // --------------------------------------------------------------------------- // deregister_wallet diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 9644384d1..ffe235883 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -175,14 +175,35 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ //usually we have only one transfer for user in transaction cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); - req.txid = get_transaction_hash(tx); - bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); + crypto::hash tx_id = get_transaction_hash(tx); + //req.txid = get_transaction_hash(tx); + /*bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status); THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size())); + "transactions outputs size=" + std::to_string(tx.vout.size()) + + " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));*/ + + char *size_prepended_tx_id = new char[crypto::HASH_SIZE + 1]; + size_prepended_tx_id[0] = crypto::HASH_SIZE; + memcpy(size_prepended_tx_id + 1, tx_id.data, crypto::HASH_SIZE); + int rc = wap_client_output_indexes(client, size_prepended_tx_id); + delete size_prepended_tx_id; + THROW_WALLET_EXCEPTION_IF(rc != 0, error::no_connection_to_daemon, "get_output_indexes"); + uint64_t status = wap_client_status(client); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_output_indexes"); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "get_output_indexes"); + THROW_WALLET_EXCEPTION_IF(status != 100, error::get_out_indices_error, "get_output_indexes"); + + zframe_t *frame = wap_client_o_indexes(client); + THROW_WALLET_EXCEPTION_IF(!frame, error::get_out_indices_error, "get_output_indexes"); + size_t size = zframe_size(frame) / sizeof(uint64_t); + uint64_t *o_indexes_array = reinterpret_cast(zframe_data(frame)); + std::vector o_indexes; + for (uint64_t i = 0; i < size; i++) { + o_indexes.push_back(o_indexes_array[i]); + } BOOST_FOREACH(size_t o, outs) { @@ -193,7 +214,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ transfer_details& td = m_transfers.back(); td.m_block_height = height; td.m_internal_output_index = o; - td.m_global_output_index = res.o_indexes[o]; + td.m_global_output_index = o_indexes[o]; td.m_tx = tx; td.m_spent = false; cryptonote::keypair in_ephemeral; From 77c6e85cbbced87c2c16073e4784934742231796 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 21 Mar 2015 11:56:33 +0530 Subject: [PATCH 30/45] Get output indexes works --- src/ipc/CMakeLists.txt | 4 +++- src/ipc/daemon_ipc_handlers.cpp | 2 +- src/ipc/include/wap_client_engine.inc | 2 -- src/ipc/wap_client/wap_client.c | 5 +++-- src/wallet/wallet2.cpp | 7 ++++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ipc/CMakeLists.txt b/src/ipc/CMakeLists.txt index 6e2836cb3..40c04b62d 100644 --- a/src/ipc/CMakeLists.txt +++ b/src/ipc/CMakeLists.txt @@ -47,7 +47,8 @@ bitmonero_add_library(server_ipc target_link_libraries(server_ipc LINK_PRIVATE cryptonote_core - ${ZMQ_LIB}) + ${ZMQ_LIB} + ${CZMQ_LIB}) bitmonero_add_library(client_ipc ${client_ipc_sources} @@ -55,4 +56,5 @@ bitmonero_add_library(client_ipc target_link_libraries(client_ipc LINK_PRIVATE cryptonote_core + ${ZMQ_LIB} ${CZMQ_LIB}) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 0c1fbe58d..a6ad9d621 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -184,7 +184,7 @@ namespace IPC uint64_t *indexes = &output_indexes[0]; zframe_t *frame = zframe_new(indexes, sizeof(uint64_t) * output_indexes.size()); wap_proto_set_o_indexes(message, &frame); - wap_proto_set_status(message, 100); + wap_proto_set_status(message, STATUS_OK); } } } diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 0889a375c..388029038 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -1664,8 +1664,6 @@ wap_client_tx_data (wap_client_t *self) zframe_t * wap_client_o_indexes (wap_client_t *self) { -printf("here1\n"); assert (self); -printf("here2\n"); return self->o_indexes; } diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 095c96974..c0b465a44 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -252,8 +252,9 @@ signal_have_stop_ok (client_t *self) static void signal_have_output_indexes_ok (client_t *self) { - zsock_send(self->cmdpipe, "sip", "OUTPUT INDEXES OK", wap_proto_status(self->message), - wap_proto_o_indexes(self->message)); + zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK", + wap_proto_status (self->message), + wap_proto_get_o_indexes (self->message)); } // --------------------------------------------------------------------------- diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index ffe235883..8498e1742 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -194,7 +194,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ uint64_t status = wap_client_status(client); THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_output_indexes"); THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "get_output_indexes"); - THROW_WALLET_EXCEPTION_IF(status != 100, error::get_out_indices_error, "get_output_indexes"); + THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_out_indices_error, "get_output_indexes"); zframe_t *frame = wap_client_o_indexes(client); THROW_WALLET_EXCEPTION_IF(!frame, error::get_out_indices_error, "get_output_indexes"); @@ -342,7 +342,6 @@ void wallet2::get_blocks_from_zmq_msg(zmsg_t *msg, std::list(zframe_data(frame)); rapidjson::Document json; - int _i = 0; THROW_WALLET_EXCEPTION_IF(json.Parse(block_data, size).HasParseError(), error::get_blocks_error, "getblocks"); for (rapidjson::SizeType i = 0; i < json["blocks"].Size(); i++) { block_complete_entry block_entry; @@ -383,7 +382,9 @@ void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added) THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "getblocks"); THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_blocks_error, "getblocks"); std::list blocks; - get_blocks_from_zmq_msg(wap_client_block_data(client), blocks); + zmsg_t *msg = wap_client_block_data(client); + get_blocks_from_zmq_msg(msg, blocks); + uint64_t current_index = wap_client_start_height(client); BOOST_FOREACH(auto& bl_entry, blocks) { From 322900b374118fbd8f2cd8587460093a577ab89e Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 23 Mar 2015 19:59:32 +0530 Subject: [PATCH 31/45] Update generated code from fix in zproto --- src/ipc/include/wap_client.h | 25 ++-- src/ipc/include/wap_client_engine.inc | 172 +++++++++++++++++++------- src/ipc/include/wap_proto.h | 4 +- src/ipc/include/wap_server.h | 18 ++- src/ipc/include/wap_server_engine.inc | 34 +++-- src/ipc/wap_client/wap_client.c | 12 +- src/ipc/wap_proto.c | 41 ++++-- src/wallet/wallet2.h | 3 +- 8 files changed, 215 insertions(+), 94 deletions(-) diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 8412157da..b3a47bcee 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -16,8 +16,10 @@ ========================================================================= */ -#ifndef __WAP_CLIENT_H_INCLUDED__ -#define __WAP_CLIENT_H_INCLUDED__ +#ifndef WAP_CLIENT_H_INCLUDED +#define WAP_CLIENT_H_INCLUDED + +#include #ifdef __cplusplus extern "C" { @@ -30,14 +32,12 @@ typedef struct _wap_client_t wap_client_t; #endif // @interface -// Create a new wap_client -// Connect to server endpoint, with specified timeout in msecs (zero means wait -// forever). Constructor succeeds if connection is successful. The caller may -// specify its address. +// Create a new wap_client, return the reference if successful, or NULL +// if construction failed due to lack of available memory. WAP_EXPORT wap_client_t * - wap_client_new (const char *endpoint, uint32_t timeout, const char *identity); + wap_client_new (void); -// Destroy the wap_client +// Destroy the wap_client and free all memory used by the object. WAP_EXPORT void wap_client_destroy (wap_client_t **self_p); @@ -54,6 +54,13 @@ WAP_EXPORT zactor_t * WAP_EXPORT zsock_t * wap_client_msgpipe (wap_client_t *self); +// Connect to server endpoint, with specified timeout in msecs (zero means wait +// forever). Constructor succeeds if connection is successful. The caller may +// specify its address. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_connect (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity); + // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int @@ -120,7 +127,7 @@ WAP_EXPORT zframe_t * // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); - + // To enable verbose tracing (animation) of wap_client instances, set // this to true. This lets you trace from and including construction. WAP_EXPORT extern volatile int diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 388029038..3d1f4f0b0 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -39,7 +39,7 @@ typedef enum { typedef enum { NULL_event = 0, - constructor_event = 1, + connect_event = 1, bad_endpoint_event = 2, open_ok_event = 3, expired_event = 4, @@ -88,7 +88,7 @@ s_state_name [] = { static char * s_event_name [] = { "(NONE)", - "constructor", + "connect", "bad_endpoint", "OPEN_OK", "expired", @@ -113,7 +113,7 @@ s_event_name [] = { "command_invalid", "other" }; - + // --------------------------------------------------------------------------- // Context for the client. This embeds the application-level client context @@ -142,6 +142,7 @@ typedef struct { wap_proto_t *message; // Message received or sent client_args_t args; // Method arguments structure bool terminated; // True if client is shutdown + bool fsm_stopped; // "terminate" action called size_t timeout; // inactivity timeout, msecs state_t state; // Current state event_t event; // Current event @@ -216,7 +217,7 @@ static void // Global tracing/animation indicator; we can't use a client method as // that only works after construction (which we often want to trace). volatile int wap_client_verbose = false; - + // Create a new client connection static s_client_t * @@ -441,7 +442,7 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) // Execute state machine as long as we have events; if event is NULL_event, -// or state machine is terminated, do nothing. +// or state machine is stopped, do nothing. static void s_client_execute (s_client_t *self, event_t event) @@ -452,7 +453,9 @@ s_client_execute (s_client_t *self, event_t event) zloop_timer_end (self->loop, self->wakeup_timer); self->wakeup_timer = 0; } - while (!self->terminated && self->next_event != NULL_event) { + while (!self->terminated // Actor is dying + && !self->fsm_stopped // FSM has finished + && self->next_event != NULL_event) { self->event = self->next_event; self->next_event = NULL_event; self->exception = NULL_event; @@ -462,7 +465,7 @@ s_client_execute (s_client_t *self, event_t event) } switch (self->state) { case start_state: - if (self->event == constructor_event) { + if (self->event == connect_event) { if (!self->exception) { // connect to server endpoint if (wap_client_verbose) @@ -503,7 +506,7 @@ s_client_execute (s_client_t *self, event_t event) // terminate if (wap_client_verbose) zsys_debug ("wap_client: $ terminate"); - self->terminated = true; + self->fsm_stopped = true; } } else { @@ -543,11 +546,14 @@ s_client_execute (s_client_t *self, event_t event) // terminate if (wap_client_verbose) zsys_debug ("wap_client: $ terminate"); - self->terminated = true; + self->fsm_stopped = true; } } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -562,6 +568,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -709,6 +718,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -723,6 +735,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -739,6 +754,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -753,6 +771,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -769,6 +790,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -783,6 +807,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -799,6 +826,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -813,6 +843,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -829,6 +862,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -843,6 +879,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -859,6 +898,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -873,6 +915,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -889,6 +934,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -903,6 +951,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -919,6 +970,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -933,6 +987,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -948,7 +1005,7 @@ s_client_execute (s_client_t *self, event_t event) // terminate if (wap_client_verbose) zsys_debug ("wap_client: $ terminate"); - self->terminated = true; + self->fsm_stopped = true; } } else @@ -963,11 +1020,14 @@ s_client_execute (s_client_t *self, event_t event) // terminate if (wap_client_verbose) zsys_debug ("wap_client: $ terminate"); - self->terminated = true; + self->fsm_stopped = true; } } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -982,11 +1042,17 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; case defaults_state: if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -1001,6 +1067,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; @@ -1034,7 +1103,7 @@ s_client_execute (s_client_t *self, event_t event) // terminate if (wap_client_verbose) zsys_debug ("wap_client: $ terminate"); - self->terminated = true; + self->fsm_stopped = true; } } else { @@ -1058,6 +1127,9 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ ping_ok"); } else if (self->event == error_event) { @@ -1072,6 +1144,9 @@ s_client_execute (s_client_t *self, event_t event) } else { // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); } break; } @@ -1123,11 +1198,11 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "$TERM")) self->terminated = true; // Shutdown the engine else - if (streq (method, "CONSTRUCTOR")) { + if (streq (method, "CONNECT")) { zstr_free (&self->args.endpoint); zstr_free (&self->args.identity); - zsock_recv (self->cmdpipe, "sis", &self->args.endpoint, &self->args.timeout, &self->args.identity); - s_client_execute (self, constructor_event); + zsock_recv (self->cmdpipe, "s4s", &self->args.endpoint, &self->args.timeout, &self->args.identity); + s_client_execute (self, connect_event); } else if (streq (method, "DESTRUCTOR")) { @@ -1136,7 +1211,7 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) else if (streq (method, "BLOCKS")) { zlist_destroy (&self->args.block_ids); - zsock_recv (self->cmdpipe, "pi", &self->args.block_ids, &self->args.start_height); + zsock_recv (self->cmdpipe, "p8", &self->args.block_ids, &self->args.start_height); s_client_execute (self, blocks_event); } else @@ -1164,7 +1239,7 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) else if (streq (method, "START")) { zstr_free (&self->args.address); - zsock_recv (self->cmdpipe, "si", &self->args.address, &self->args.thread_count); + zsock_recv (self->cmdpipe, "s8", &self->args.address, &self->args.thread_count); s_client_execute (self, start_event); } else @@ -1199,8 +1274,11 @@ s_client_handle_msgpipe (zloop_t *loop, zsock_t *reader, void *argument) if (wap_client_verbose) zsys_debug ("wap_client: API message=%s", method); - // Front-end shuts down msgpipe before cmdpipe - if (streq (method, "$TERM")) + // Front-end shuts down msgpipe before cmdpipe, this little + // handshake just ensures all traffic on the msgpipe has been + // flushed before the calling thread continues with destroying + // the actor. + if (streq (method, "$FLUSH")) zsock_signal (self->cmdpipe, 0); // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->msgpipe)) { @@ -1256,7 +1334,7 @@ wap_client (zsock_t *cmdpipe, void *msgpipe) s_client_t *self = s_client_new (cmdpipe, (zsock_t *) msgpipe); if (self) { zsock_signal (cmdpipe, 0); - + // Set up handler for the sockets the client uses engine_handle_socket ((client_t *) self, self->cmdpipe, s_client_handle_cmdpipe); engine_handle_socket ((client_t *) self, self->msgpipe, s_client_handle_msgpipe); @@ -1291,25 +1369,16 @@ struct _wap_client_t { // --------------------------------------------------------------------------- // Create a new wap_client -// Connect to server endpoint, with specified timeout in msecs (zero means wait -// forever). Constructor succeeds if connection is successful. The caller may -// specify its address. - -static int -wap_client_constructor (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity); WAP_EXPORT wap_client_t * -wap_client_new (const char *endpoint, uint32_t timeout, const char *identity) +wap_client_new (void) { wap_client_t *self = (wap_client_t *) zmalloc (sizeof (wap_client_t)); if (self) { zsock_t *backend; self->msgpipe = zsys_create_pipe (&backend); - self->actor = zactor_new (wap_client, backend); - if (self->actor) - self->status = wap_client_constructor (self, endpoint, timeout, identity); - if (self->status == -1) - zactor_destroy (&self->actor); + if (self->msgpipe) + self->actor = zactor_new (wap_client, backend); if (!self->actor) wap_client_destroy (&self); } @@ -1332,10 +1401,12 @@ wap_client_destroy (wap_client_t **self_p) if (*self_p) { wap_client_t *self = *self_p; if (self->actor && !zsys_interrupted) { - // Shut down msgpipe first so that client can do clean shutdown, - // sending any pending messages and handshaking goodbye to server - zstr_send (self->msgpipe, "$TERM"); - zsock_wait (self->actor); + // Before destroying the actor we have to flush any pending + // traffic on the msgpipe, otherwise it gets lost in a fire and + // forget scenario. We do this by sending $FLUSH to the msgpipe + // and waiting for a signal back on the cmdpipe. + if (zstr_send (self->msgpipe, "$FLUSH") == 0) + zsock_wait (self->actor); wap_client_destructor (self); } zactor_destroy (&self->actor); @@ -1391,7 +1462,7 @@ s_accept_reply (wap_client_t *self, ...) char *reply = zstr_recv (self->actor); if (!reply) break; // Interrupted or timed-out - + va_list args; va_start (args, self); char *filter = va_arg (args, char *); @@ -1408,11 +1479,11 @@ s_accept_reply (wap_client_t *self, ...) else if (streq (reply, "BLOCKS OK")) { zmsg_destroy (&self->block_data); - zsock_recv (self->actor, "iiip", &self->status, &self->start_height, &self->curr_height, &self->block_data); + zsock_recv (self->actor, "888p", &self->status, &self->start_height, &self->curr_height, &self->block_data); } else if (streq (reply, "PUT OK")) { - zsock_recv (self->actor, "i", &self->status); + zsock_recv (self->actor, "8", &self->status); } else if (streq (reply, "GET OK")) { @@ -1426,11 +1497,11 @@ s_accept_reply (wap_client_t *self, ...) else if (streq (reply, "OUTPUT INDEXES OK")) { zframe_destroy (&self->o_indexes); - zsock_recv (self->actor, "ip", &self->status, &self->o_indexes); + zsock_recv (self->actor, "8p", &self->status, &self->o_indexes); } else if (streq (reply, "START OK")) { - zsock_recv (self->actor, "i", &self->status); + zsock_recv (self->actor, "8", &self->status); } else if (streq (reply, "STOP OK")) { @@ -1458,11 +1529,12 @@ s_accept_reply (wap_client_t *self, ...) // specify its address. // Returns >= 0 if successful, -1 if interrupted. -static int -wap_client_constructor (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity) +int +wap_client_connect (wap_client_t *self, const char *endpoint, uint32_t timeout, const char *identity) { assert (self); - zsock_send (self->actor, "ssis", "CONSTRUCTOR", endpoint, timeout, identity); + + zsock_send (self->actor, "ss4s", "CONNECT", endpoint, timeout, identity); if (s_accept_reply (self, "SUCCESS", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; @@ -1478,6 +1550,7 @@ int wap_client_destructor (wap_client_t *self) { assert (self); + zsock_send (self->actor, "s", "DESTRUCTOR"); if (s_accept_reply (self, "SUCCESS", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1493,7 +1566,8 @@ int wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p, uint64_t start_height) { assert (self); - zsock_send (self->actor, "spi", "BLOCKS", *block_ids_p, start_height); + + zsock_send (self->actor, "sp8", "BLOCKS", *block_ids_p, start_height); *block_ids_p = NULL; // Take ownership of block_ids if (s_accept_reply (self, "BLOCKS OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1509,6 +1583,7 @@ int wap_client_put (wap_client_t *self, zchunk_t **tx_data_p) { assert (self); + zsock_send (self->actor, "sp", "PUT", *tx_data_p); *tx_data_p = NULL; // Take ownership of tx_data if (s_accept_reply (self, "PUT OK", "FAILURE", NULL)) @@ -1525,6 +1600,7 @@ int wap_client_get (wap_client_t *self, const char *tx_id) { assert (self); + zsock_send (self->actor, "ss", "GET", tx_id); if (s_accept_reply (self, "GET OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1540,6 +1616,7 @@ int wap_client_save (wap_client_t *self) { assert (self); + zsock_send (self->actor, "s", "SAVE"); if (s_accept_reply (self, "SAVE OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1555,6 +1632,7 @@ int wap_client_output_indexes (wap_client_t *self, const char *tx_id) { assert (self); + zsock_send (self->actor, "ss", "OUTPUT INDEXES", tx_id); if (s_accept_reply (self, "OUTPUT INDEXES OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out @@ -1570,7 +1648,8 @@ int wap_client_start (wap_client_t *self, const char *address, uint64_t thread_count) { assert (self); - zsock_send (self->actor, "ssi", "START", address, thread_count); + + zsock_send (self->actor, "ss8", "START", address, thread_count); if (s_accept_reply (self, "START OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; @@ -1585,6 +1664,7 @@ int wap_client_stop (wap_client_t *self) { assert (self); + zsock_send (self->actor, "s", "STOP"); if (s_accept_reply (self, "STOP OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index f7b925f3d..115e21254 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -18,8 +18,8 @@ ========================================================================= */ -#ifndef __WAP_PROTO_H_INCLUDED__ -#define __WAP_PROTO_H_INCLUDED__ +#ifndef WAP_PROTO_H_INCLUDED +#define WAP_PROTO_H_INCLUDED /* These are the wap_proto messages: diff --git a/src/ipc/include/wap_server.h b/src/ipc/include/wap_server.h index 73a84f475..64be3416c 100644 --- a/src/ipc/include/wap_server.h +++ b/src/ipc/include/wap_server.h @@ -16,8 +16,10 @@ ========================================================================= */ -#ifndef __WAP_SERVER_H_INCLUDED__ -#define __WAP_SERVER_H_INCLUDED__ +#ifndef WAP_SERVER_H_INCLUDED +#define WAP_SERVER_H_INCLUDED + +#include #ifdef __cplusplus extern "C" { @@ -29,11 +31,11 @@ extern "C" { // Create new wap_server instance, passing logging prefix: // // zactor_t *wap_server = zactor_new (wap_server, "myname"); -// +// // Destroy wap_server instance // // zactor_destroy (&wap_server); -// +// // Enable verbose logging of commands and activity: // // zstr_send (wap_server, "VERBOSE"); @@ -54,12 +56,16 @@ extern "C" { // Specify configuration file to load, overwriting any previous loaded // configuration file or options: // -// zstr_sendx (wap_server, "CONFIGURE", filename, NULL); +// zstr_sendx (wap_server, "LOAD", filename, NULL); // // Set configuration path value: // // zstr_sendx (wap_server, "SET", path, value, NULL); -// +// +// Save configuration data to config file on disk: +// +// zstr_sendx (wap_server, "SAVE", filename, NULL); +// // Send zmsg_t instance to wap_server: // // zactor_send (wap_server, &msg); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index d01943b83..54c63ae48 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -378,7 +378,7 @@ s_client_new (s_server_t *server, zframe_t *routing_id) s_client_t *self = (s_client_t *) zmalloc (sizeof (s_client_t)); assert (self); assert ((s_client_t *) &self->client == self); - + self->server = server; self->hashkey = zframe_strhex (routing_id); self->routing_id = zframe_dup (routing_id); @@ -1002,17 +1002,17 @@ static void s_server_config_global (s_server_t *self) { // Built-in server configuration options - // + // // If we didn't already set verbose, check if the config tree wants it if (!self->verbose && atoi (zconfig_resolve (self->config, "server/verbose", "0"))) self->verbose = true; - + // Default client timeout is 60 seconds self->timeout = atoi ( zconfig_resolve (self->config, "server/timeout", "60000")); zloop_set_ticket_delay (self->loop, self->timeout); - + // Do we want to run server in the background? int background = atoi ( zconfig_resolve (self->config, "server/background", "0")); @@ -1097,12 +1097,15 @@ s_server_config_service (s_server_t *self) char *mechanism = zconfig_resolve (section, "mechanism", "null"); char *domain = zconfig_resolve (section, "domain", NULL); if (streq (mechanism, "null")) { + zsys_notice ("server is using NULL security"); if (domain) zsock_set_zap_domain (self->router, NULL); } else - if (streq (mechanism, "plain")) + if (streq (mechanism, "plain")) { + zsys_notice ("server is using PLAIN security"); zsock_set_plain_server (self->router, 1); + } else zsys_warning ("mechanism=%s is not supported", mechanism); } @@ -1148,20 +1151,20 @@ s_server_handle_pipe (zloop_t *loop, zsock_t *reader, void *argument) zstr_sendm (self->pipe, "PORT"); zstr_sendf (self->pipe, "%d", self->port); } - else - if (streq (method, "CONFIGURE")) { - char *config_file = zmsg_popstr (msg); + else // Deprecated method name + if (streq (method, "LOAD") || streq (method, "CONFIGURE")) { + char *filename = zmsg_popstr (msg); zconfig_destroy (&self->config); - self->config = zconfig_load (config_file); + self->config = zconfig_load (filename); if (self->config) { s_server_config_service (self); self->server.config = self->config; } else { - zsys_warning ("cannot load config file '%s'\n", config_file); + zsys_warning ("cannot load config file '%s'", filename); self->config = zconfig_new ("root", NULL); } - free (config_file); + free (filename); } else if (streq (method, "SET")) { @@ -1176,6 +1179,13 @@ s_server_handle_pipe (zloop_t *loop, zsock_t *reader, void *argument) free (path); free (value); } + else + if (streq (method, "SAVE")) { + char *filename = zmsg_popstr (msg); + if (zconfig_save (self->config, filename)) + zsys_warning ("cannot save config file '%s'", filename); + free (filename); + } else { // Execute custom method zmsg_t *reply = server_method (&self->server, method, msg); @@ -1211,7 +1221,7 @@ s_server_handle_protocol (zloop_t *loop, zsock_t *reader, void *argument) // Any input from client counts as activity if (client->ticket) zloop_ticket_reset (self->loop, client->ticket); - + // Pass to client state machine s_client_execute (client, s_protocol_event (self->message)); } diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index c0b465a44..8ac3a18b5 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -138,7 +138,7 @@ prepare_get_output_indexes_command (client_t *self) static void signal_have_blocks_ok (client_t *self) { - zsock_send (self->cmdpipe, "siiip", "BLOCKS OK", wap_proto_status(self->message), + zsock_send (self->cmdpipe, "s888p", "BLOCKS OK", wap_proto_status(self->message), wap_proto_start_height (self->message), wap_proto_curr_height (self->message), wap_proto_get_block_data (self->message)); @@ -174,7 +174,7 @@ prepare_put_command (client_t *self) static void signal_have_put_ok (client_t *self) { - zsock_send (self->cmdpipe, "sis", "PUT OK", 0, + zsock_send (self->cmdpipe, "s8s", "PUT OK", 0, wap_proto_tx_id (self->message)); } @@ -197,7 +197,7 @@ prepare_get_command (client_t *self) static void signal_have_get_ok (client_t *self) { - zsock_send (self->cmdpipe, "sip", "GET OK", 0, + zsock_send (self->cmdpipe, "s8p", "GET OK", 0, wap_proto_get_tx_data (self->message)); } @@ -220,7 +220,7 @@ prepare_save_command (client_t *self) static void signal_have_save_ok (client_t *self) { - zsock_send (self->cmdpipe, "si", "SAVE OK", 0); + zsock_send (self->cmdpipe, "s8", "SAVE OK", 0); } @@ -231,7 +231,7 @@ signal_have_save_ok (client_t *self) static void signal_have_start_ok (client_t *self) { - zsock_send(self->cmdpipe, "si", "START OK", wap_proto_status(self->message)); + zsock_send(self->cmdpipe, "s8", "START OK", wap_proto_status(self->message)); } @@ -242,7 +242,7 @@ signal_have_start_ok (client_t *self) static void signal_have_stop_ok (client_t *self) { - zsock_send (self->cmdpipe, "si", "STOP OK", 0); + zsock_send (self->cmdpipe, "s8", "STOP OK", 0); } // --------------------------------------------------------------------------- diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 932c2ff22..678309e80 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -34,18 +34,30 @@ struct _wap_proto_t { int id; // wap_proto message ID byte *needle; // Read/write pointer for serialization byte *ceiling; // Valid upper limit for read pointer - char identity [256]; // Wallet identity - zlist_t *block_ids; // - uint64_t start_height; // - uint64_t status; // - uint64_t curr_height; // - zmsg_t *block_data; // Frames of block data - zchunk_t *tx_data; // Transaction data - char tx_id [256]; // Transaction ID - zframe_t *o_indexes; // Output Indexes - char address [256]; // - uint64_t thread_count; // - char reason [256]; // Printable explanation + /* Wallet identity */ + char identity [256]; + /* */ + zlist_t *block_ids; + /* */ + uint64_t start_height; + /* */ + uint64_t status; + /* */ + uint64_t curr_height; + /* Frames of block data */ + zmsg_t *block_data; + /* Transaction data */ + zchunk_t *tx_data; + /* Transaction ID */ + char tx_id [256]; + /* Output Indexes */ + zframe_t *o_indexes; + /* */ + char address [256]; + /* */ + uint64_t thread_count; + /* Printable explanation */ + char reason [256]; }; // -------------------------------------------------------------------------- @@ -333,6 +345,7 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) zsys_warning ("wap_proto: tx_data is missing data"); goto malformed; } + zchunk_destroy (&self->tx_data); self->tx_data = zchunk_new (self->needle, chunk_size); self->needle += chunk_size; } @@ -369,6 +382,7 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) zsys_warning ("wap_proto: tx_data is missing data"); goto malformed; } + zchunk_destroy (&self->tx_data); self->tx_data = zchunk_new (self->needle, chunk_size); self->needle += chunk_size; } @@ -1179,6 +1193,9 @@ wap_proto_test (bool verbose) { printf (" * wap_proto: "); + // Silence an "unused" warning by "using" the verbose variable + if (verbose) {;} + // @selftest // Simple create/destroy test wap_proto_t *self = wap_proto_new (); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 14b662956..1d41dbdcc 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -84,7 +84,8 @@ namespace tools wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false) {}; public: wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet) { - client = wap_client_new ("ipc://@/monero", 200, "wallet identity"); + client = wap_client_new (); + wap_client_connect (client, "ipc://@/monero", 200, "wallet identity"); if (!client) { // TODO: Daemon not up. } From bcbc24a45622d8c49d1fef838ffb95674ed3dbea Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Tue, 31 Mar 2015 21:50:38 +0530 Subject: [PATCH 32/45] Commit tx IPC --- src/ipc/daemon_ipc_handlers.cpp | 117 ++++++++ src/ipc/include/daemon_ipc_handlers.h | 5 + src/ipc/include/wap_client.h | 17 +- src/ipc/include/wap_client_engine.inc | 404 ++++++++++++++++++++++---- src/ipc/include/wap_proto.h | 90 ++++-- src/ipc/include/wap_server_engine.inc | 37 ++- src/ipc/wap_client/wap_client.c | 53 +++- src/ipc/wap_proto.c | 395 ++++++++++++++++++++----- src/ipc/wap_server/wap_server.c | 10 + src/simplewallet/simplewallet.cpp | 13 +- src/wallet/wallet2.cpp | 16 +- src/wallet/wallet2.h | 41 ++- src/wallet/wallet_errors.h | 6 +- 13 files changed, 1031 insertions(+), 173 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index a6ad9d621..31bf78926 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -161,7 +161,49 @@ namespace IPC void send_raw_transaction(wap_proto_t *message) { + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + std::string tx_blob; + zchunk_t *tx_as_hex_chunk = wap_proto_tx_as_hex(message); + char *tx_as_hex = (char*)zchunk_data(tx_as_hex_chunk); + std::string tx_as_hex_string(tx_as_hex, zchunk_size(tx_as_hex_chunk)); + if (!string_tools::parse_hexstr_to_binbuff(tx_as_hex_string, tx_blob)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to parse tx from hexbuff: " << tx_as_hex_string); + wap_proto_set_status(message, STATUS_INVALID_TX); + return; + } + cryptonote::cryptonote_connection_context fake_context = AUTO_VAL_INIT(fake_context); + cryptonote::tx_verification_context tvc = AUTO_VAL_INIT(tvc); + if (!core->handle_incoming_tx(tx_blob, tvc, false)) + { + LOG_PRINT_L0("[on_send_raw_tx]: Failed to process tx"); + wap_proto_set_status(message, STATUS_INVALID_TX); + return; + } + + if (tvc.m_verifivation_failed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx verification failed"); + wap_proto_set_status(message, STATUS_TX_VERIFICATION_FAILED); + return; + } + + if (!tvc.m_should_be_relayed) + { + LOG_PRINT_L0("[on_send_raw_tx]: tx accepted, but not relayed"); + wap_proto_set_status(message, STATUS_TX_NOT_RELAYED); + return; + } + + cryptonote::NOTIFY_NEW_TRANSACTIONS::request r; + r.txs.push_back(tx_blob); + core->get_protocol()->relay_transactions(r, fake_context); + //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + wap_proto_set_status(message, STATUS_OK); } void get_output_indexes(wap_proto_t *message) @@ -186,5 +228,80 @@ namespace IPC wap_proto_set_o_indexes(message, &frame); wap_proto_set_status(message, STATUS_OK); } + + void get_random_outs(wap_proto_t *message) { + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + // The core does its stuff with old style RPC objects. + // So we construct and read from those objects. + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req; + uint64_t outs_count = wap_proto_outs_count(message); + req.outs_count = outs_count; + zframe_t *amounts_frame = wap_proto_amounts(message); + uint64_t amounts_count = zframe_size(amounts_frame) / sizeof(uint64_t); + uint64_t *amounts = (uint64_t*)zframe_data(amounts_frame); + for (unsigned int i = 0; i < amounts_count; i++) { + req.amounts.push_back(amounts[i]); + } + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res; + if (!core->get_random_outs_for_amounts(req, res)) + { + wap_proto_set_status(message, STATUS_RANDOM_OUTS_FAILED); + } + + // We have to convert the result into a JSON string. + rapidjson::Document result_json; + result_json.SetObject(); + rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator(); + rapidjson::Value outputs_json(rapidjson::kArrayType); + + typedef cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount outs_for_amount; + typedef cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry out_entry; + for (unsigned int i = 0; i < res.outs.size(); i++) { + rapidjson::Value output(rapidjson::kObjectType); + outs_for_amount out = res.outs[i]; + rapidjson::Value output_entries(rapidjson::kArrayType); + for (std::list::iterator it = out.outs.begin(); it != out.outs.end(); it++) { + rapidjson::Value output_entry(rapidjson::kObjectType); + out_entry entry = *it; + output_entry.AddMember("global_amount_index", entry.global_amount_index, allocator); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(entry.out_key.data, 32, allocator); + output_entry.AddMember("out_key", string_value.Move(), allocator); + output_entries.PushBack(output_entry, allocator); + } + output.AddMember("amount", out.amount, allocator); + output.AddMember("outs", output_entries, allocator); + outputs_json.PushBack(output, allocator); + } + result_json.AddMember("outputs", outputs_json, allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + result_json.Accept(writer); + std::string block_string = buffer.GetString(); + +std::cout << block_string << std::endl; + + zframe_t *frame = zframe_new(block_string.c_str(), block_string.length()); + wap_proto_set_random_outputs(message, &frame); + + std::stringstream ss; + std::for_each(res.outs.begin(), res.outs.end(), [&](outs_for_amount& ofa) + { + ss << "[" << ofa.amount << "]:"; + CHECK_AND_ASSERT_MES(ofa.outs.size(), ;, "internal error: ofa.outs.size() is empty for amount " << ofa.amount); + std::for_each(ofa.outs.begin(), ofa.outs.end(), [&](out_entry& oe) + { + ss << oe.global_amount_index << " "; + }); + ss << ENDL; + }); + std::string s = ss.str(); + LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index b46df43ed..9e716d5fd 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -60,12 +60,17 @@ namespace IPC const uint64_t STATUS_MINING_NOT_STARTED = 3; const uint64_t STATUS_WRONG_BLOCK_ID_LENGTH = 4; const uint64_t STATUS_INTERNAL_ERROR = 5; + const uint64_t STATUS_INVALID_TX = 6; + const uint64_t STATUS_TX_VERIFICATION_FAILED = 7; + const uint64_t STATUS_TX_NOT_RELAYED = 8; + const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; namespace Daemon { void start_mining(wap_proto_t *message); void retrieve_blocks(wap_proto_t *message); void send_raw_transaction(wap_proto_t *message); void get_output_indexes(wap_proto_t *message); + void get_random_outs(wap_proto_t *message); void init(cryptonote::core *p_core, nodetool::node_server > *p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index b3a47bcee..4d37830bc 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -54,6 +54,12 @@ WAP_EXPORT zactor_t * WAP_EXPORT zsock_t * wap_client_msgpipe (wap_client_t *self); +// Return true if client is currently connected, else false. Note that the +// client will automatically re-connect if the server dies and restarts after +// a successful first connection. +WAP_EXPORT bool + wap_client_connected (wap_client_t *self); + // Connect to server endpoint, with specified timeout in msecs (zero means wait // forever). Constructor succeeds if connection is successful. The caller may // specify its address. @@ -69,7 +75,7 @@ WAP_EXPORT int // Send a raw transaction to the daemon. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_put (wap_client_t *self, zchunk_t **tx_data_p); + wap_client_put (wap_client_t *self, zchunk_t **tx_as_hex_p); // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. @@ -86,6 +92,11 @@ WAP_EXPORT int WAP_EXPORT int wap_client_output_indexes (wap_client_t *self, const char *tx_id); +// Ask for tx output indexes. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amounts_p); + // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int @@ -124,6 +135,10 @@ WAP_EXPORT zchunk_t * WAP_EXPORT zframe_t * wap_client_o_indexes (wap_client_t *self); +// Return last received random_outputs +WAP_EXPORT zframe_t * + wap_client_random_outputs (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 3d1f4f0b0..8c39bfd0e 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -31,10 +31,11 @@ typedef enum { expect_start_ok_state = 8, expect_stop_ok_state = 9, expect_output_indexes_ok_state = 10, - expect_close_ok_state = 11, - defaults_state = 12, - have_error_state = 13, - reexpect_open_ok_state = 14 + expect_random_outs_ok_state = 11, + expect_close_ok_state = 12, + defaults_state = 13, + have_error_state = 14, + reexpect_open_ok_state = 15 } state_t; typedef enum { @@ -50,19 +51,22 @@ typedef enum { start_event = 9, stop_event = 10, output_indexes_event = 11, - destructor_event = 12, - blocks_ok_event = 13, - get_ok_event = 14, - put_ok_event = 15, - save_ok_event = 16, - start_ok_event = 17, - stop_ok_event = 18, - output_indexes_ok_event = 19, - close_ok_event = 20, - ping_ok_event = 21, - error_event = 22, - command_invalid_event = 23, - other_event = 24 + random_outs_event = 12, + destructor_event = 13, + blocks_ok_event = 14, + get_ok_event = 15, + put_ok_event = 16, + save_ok_event = 17, + start_ok_event = 18, + stop_ok_event = 19, + output_indexes_ok_event = 20, + random_outs_ok_event = 21, + close_ok_event = 22, + ping_ok_event = 23, + error_event = 24, + exception_event = 25, + command_invalid_event = 26, + other_event = 27 } event_t; // Names for state machine logging and error reporting @@ -79,6 +83,7 @@ s_state_name [] = { "expect start ok", "expect stop ok", "expect output indexes ok", + "expect random outs ok", "expect close ok", "defaults", "have error", @@ -99,6 +104,7 @@ s_event_name [] = { "START", "STOP", "OUTPUT_INDEXES", + "RANDOM_OUTS", "destructor", "BLOCKS_OK", "GET_OK", @@ -107,9 +113,11 @@ s_event_name [] = { "START_OK", "STOP_OK", "OUTPUT_INDEXES_OK", + "RANDOM_OUTS_OK", "CLOSE_OK", "PING_OK", "ERROR", + "exception", "command_invalid", "other" }; @@ -127,8 +135,10 @@ struct _client_args_t { char *identity; zlist_t *block_ids; uint64_t start_height; - zchunk_t *tx_data; + zchunk_t *tx_as_hex; char *tx_id; + uint64_t outs_count; + zframe_t *amounts; char *address; uint64_t thread_count; }; @@ -141,6 +151,7 @@ typedef struct { zloop_t *loop; // Listen to pipe and dealer wap_proto_t *message; // Message received or sent client_args_t args; // Method arguments structure + bool connected; // True if client is connected bool terminated; // True if client is shutdown bool fsm_stopped; // "terminate" action called size_t timeout; // inactivity timeout, msecs @@ -178,7 +189,7 @@ static void static void signal_success (client_t *self); static void - use_heartbeat_timer (client_t *self); + client_is_connected (client_t *self); static void signal_server_not_present (client_t *self); static void @@ -193,6 +204,10 @@ static void prepare_start_command (client_t *self); static void prepare_get_output_indexes_command (client_t *self); +static void + prepare_get_random_outs_command (client_t *self); +static void + check_if_connection_is_dead (client_t *self); static void signal_have_blocks_ok (client_t *self); static void @@ -207,6 +222,8 @@ static void signal_have_stop_ok (client_t *self); static void signal_have_output_indexes_ok (client_t *self); +static void + signal_have_random_outs_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -263,8 +280,9 @@ s_client_destroy (s_client_t **self_p) zstr_free (&self->args.endpoint); zstr_free (&self->args.identity); zlist_destroy (&self->args.block_ids); - zchunk_destroy (&self->args.tx_data); + zchunk_destroy (&self->args.tx_as_hex); zstr_free (&self->args.tx_id); + zframe_destroy (&self->args.amounts); zstr_free (&self->args.address); client_terminate (&self->client); wap_proto_destroy (&self->message); @@ -322,9 +340,11 @@ engine_set_wakeup_event (client_t *client, size_t delay, event_t event) } } -// Set timeout for next protocol read. By default, will wait forever -// or until the process is interrupted. The timeout is in milliseconds. -// The state machine must handle the "expired" event. +// Set heartbeat timeout. By default, the timeout is zero, meaning +// infinite. Setting a non-zero timeout causes the state machine to +// receive an "expired" event if is no incoming traffic for that many +// milliseconds. This cycles over and over until/unless the code sets +// a zero timeout. The state machine must handle the "expired" event. static void engine_set_timeout (client_t *client, size_t timeout) @@ -332,6 +352,10 @@ engine_set_timeout (client_t *client, size_t timeout) if (client) { s_client_t *self = (s_client_t *) client; self->timeout = timeout; + if (self->expiry_timer) { + zloop_timer_end (self->loop, self->expiry_timer); + self->expiry_timer = 0; + } if (self->timeout) self->expiry_timer = zloop_timer ( self->loop, self->timeout, 1, s_client_handle_timeout, self); @@ -356,6 +380,18 @@ engine_handle_socket (client_t *client, zsock_t *sock, zloop_reader_fn handler) } } +// Set connected to true/false. The client must call this if it wants +// to provide the API with the connected status. + +static void +engine_set_connected (client_t *client, bool connected) +{ + if (client) { + s_client_t *self = (s_client_t *) client; + self->connected = connected; + } +} + // Pedantic compilers don't like unused functions, so we call the whole // API, passing null references. It's nasty and horrid and sufficient. @@ -400,6 +436,12 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_OUTPUT_INDEXES_OK: return output_indexes_ok_event; break; + case WAP_PROTO_RANDOM_OUTS: + return random_outs_event; + break; + case WAP_PROTO_RANDOM_OUTS_OK: + return random_outs_ok_event; + break; case WAP_PROTO_GET: return get_event; break; @@ -526,10 +568,10 @@ s_client_execute (s_client_t *self, event_t event) signal_success (&self->client); } if (!self->exception) { - // use heartbeat timer + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ use heartbeat timer"); - use_heartbeat_timer (&self->client); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); } if (!self->exception) self->state = connected_state; @@ -551,9 +593,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -566,6 +611,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -695,6 +746,24 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_output_indexes_ok_state; } else + if (self->event == random_outs_event) { + if (!self->exception) { + // prepare get random outs command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get random outs command"); + prepare_get_random_outs_command (&self->client); + } + if (!self->exception) { + // send RANDOM_OUTS + if (wap_client_verbose) + zsys_debug ("wap_client: $ send RANDOM_OUTS"); + wap_proto_set_id (self->message, WAP_PROTO_RANDOM_OUTS); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_random_outs_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -708,6 +777,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == expired_event) { + if (!self->exception) { + // check if connection is dead + if (wap_client_verbose) + zsys_debug ("wap_client: $ check if connection is dead"); + check_if_connection_is_dead (&self->client); + } if (!self->exception) { // send PING if (wap_client_verbose) @@ -718,9 +793,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -733,6 +811,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -754,9 +838,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -769,6 +856,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -790,9 +883,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -805,6 +901,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -826,9 +928,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -841,6 +946,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -862,9 +973,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -877,6 +991,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -898,9 +1018,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -913,6 +1036,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -934,9 +1063,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -949,6 +1081,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -970,9 +1108,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -985,6 +1126,57 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_random_outs_ok_state: + if (self->event == random_outs_ok_event) { + if (!self->exception) { + // signal have random outs ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have random outs ok"); + signal_have_random_outs_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -1025,9 +1217,12 @@ s_client_execute (s_client_t *self, event_t event) } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -1040,6 +1235,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -1050,9 +1251,12 @@ s_client_execute (s_client_t *self, event_t event) case defaults_state: if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -1065,6 +1269,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -1117,19 +1327,22 @@ s_client_execute (s_client_t *self, event_t event) case reexpect_open_ok_state: if (self->event == open_ok_event) { if (!self->exception) { - // use heartbeat timer + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ use heartbeat timer"); - use_heartbeat_timer (&self->client); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); } if (!self->exception) self->state = connected_state; } else if (self->event == ping_ok_event) { - // No action - just logging + if (!self->exception) { + // client is connected if (wap_client_verbose) - zsys_debug ("wap_client: $ ping_ok"); + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } } else if (self->event == error_event) { @@ -1142,6 +1355,12 @@ s_client_execute (s_client_t *self, event_t event) if (!self->exception) self->state = have_error_state; } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } else { // Handle unexpected protocol events // No action - just logging @@ -1169,7 +1388,13 @@ s_client_handle_timeout (zloop_t *loop, int timer_id, void *argument) { s_client_t *self = (s_client_t *) argument; s_client_execute (self, expired_event); - return self->terminated? -1: 0; + if (self->terminated) + return -1; + + if (self->timeout > 0) + self->expiry_timer = zloop_timer ( + loop, self->timeout, 1, s_client_handle_timeout, self); + return 0; } // zloop callback when client wakeup timer expires @@ -1198,6 +1423,9 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "$TERM")) self->terminated = true; // Shutdown the engine else + if (streq (method, "$CONNECTED")) + zsock_send (self->cmdpipe, "i", self->connected); + else if (streq (method, "CONNECT")) { zstr_free (&self->args.endpoint); zstr_free (&self->args.identity); @@ -1216,8 +1444,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) } else if (streq (method, "PUT")) { - zchunk_destroy (&self->args.tx_data); - zsock_recv (self->cmdpipe, "p", &self->args.tx_data); + zchunk_destroy (&self->args.tx_as_hex); + zsock_recv (self->cmdpipe, "p", &self->args.tx_as_hex); s_client_execute (self, put_event); } else @@ -1237,6 +1465,12 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) s_client_execute (self, output_indexes_event); } else + if (streq (method, "RANDOM OUTS")) { + zframe_destroy (&self->args.amounts); + zsock_recv (self->cmdpipe, "8p", &self->args.outs_count, &self->args.amounts); + s_client_execute (self, random_outs_event); + } + else if (streq (method, "START")) { zstr_free (&self->args.address); zsock_recv (self->cmdpipe, "s8", &self->args.address, &self->args.thread_count); @@ -1357,6 +1591,7 @@ wap_client (zsock_t *cmdpipe, void *msgpipe) struct _wap_client_t { zactor_t *actor; // Client actor zsock_t *msgpipe; // Pipe for async message flow + bool connected; // Client currently connected or not int status; // Returned by actor reply char *reason; // Returned by actor reply uint64_t start_height; // Returned by actor reply @@ -1364,6 +1599,7 @@ struct _wap_client_t { zmsg_t *block_data; // Returned by actor reply zchunk_t *tx_data; // Returned by actor reply zframe_t *o_indexes; // Returned by actor reply + zframe_t *random_outputs; // Returned by actor reply }; @@ -1415,6 +1651,7 @@ wap_client_destroy (wap_client_t **self_p) zmsg_destroy (&self->block_data); zchunk_destroy (&self->tx_data); zframe_destroy (&self->o_indexes); + zframe_destroy (&self->random_outputs); free (self); *self_p = NULL; } @@ -1448,6 +1685,22 @@ wap_client_msgpipe (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Return true if client is currently connected, else false. Note that the +// client will automatically re-connect if the server dies and restarts after +// a successful first connection. + +bool +wap_client_connected (wap_client_t *self) +{ + assert (self); + bool connected; + zsock_send (self->actor, "s", "$CONNECTED"); + zsock_recv (self->actor, "i", &connected); + return connected; +} + + // --------------------------------------------------------------------------- // Get valid reply from actor; discard replies that does not match. Current // implementation filters on first frame of message. Blocks until a valid @@ -1500,6 +1753,11 @@ s_accept_reply (wap_client_t *self, ...) zsock_recv (self->actor, "8p", &self->status, &self->o_indexes); } else + if (streq (reply, "RANDOM OUTS OK")) { + zframe_destroy (&self->random_outputs); + zsock_recv (self->actor, "8p", &self->status, &self->random_outputs); + } + else if (streq (reply, "START OK")) { zsock_recv (self->actor, "8", &self->status); } @@ -1580,12 +1838,12 @@ wap_client_blocks (wap_client_t *self, zlist_t **block_ids_p, uint64_t start_hei // Returns >= 0 if successful, -1 if interrupted. int -wap_client_put (wap_client_t *self, zchunk_t **tx_data_p) +wap_client_put (wap_client_t *self, zchunk_t **tx_as_hex_p) { assert (self); - zsock_send (self->actor, "sp", "PUT", *tx_data_p); - *tx_data_p = NULL; // Take ownership of tx_data + zsock_send (self->actor, "sp", "PUT", *tx_as_hex_p); + *tx_as_hex_p = NULL; // Take ownership of tx_as_hex if (s_accept_reply (self, "PUT OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; @@ -1640,6 +1898,23 @@ wap_client_output_indexes (wap_client_t *self, const char *tx_id) } +// --------------------------------------------------------------------------- +// Ask for tx output indexes. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amounts_p) +{ + assert (self); + + zsock_send (self->actor, "s8p", "RANDOM OUTS", outs_count, *amounts_p); + *amounts_p = NULL; // Take ownership of amounts + if (s_accept_reply (self, "RANDOM OUTS OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. @@ -1747,3 +2022,14 @@ wap_client_o_indexes (wap_client_t *self) assert (self); return self->o_indexes; } + + +// --------------------------------------------------------------------------- +// Return last received random_outputs + +zframe_t * +wap_client_random_outputs (wap_client_t *self) +{ + assert (self); + return self->random_outputs; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 115e21254..eb3cadd63 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -1,6 +1,6 @@ /* ========================================================================= wap_proto - Wallet Access Protocol - + Codec header for wap_proto. ** WARNING ************************************************************* @@ -43,7 +43,7 @@ BLOCKS-OK, or ERROR if the request is invalid. PUT - Wallet sends a raw transaction to the daemon. Daemon replies with PUT-OK, or ERROR. - tx_data chunk Transaction data + tx_as_hex chunk Transaction as hex PUT_OK - Daemon confirms that it accepted the raw transaction. status number 8 Transaction ID @@ -55,6 +55,14 @@ PUT-OK, or ERROR. status number 8 Status o_indexes frame Output Indexes + RANDOM_OUTS - Get random outputs for amounts. + outs_count number 8 Outs count + amounts frame Amounts + + RANDOM_OUTS_OK - Daemon returns random outputs for amounts. + status number 8 Status + random_outputs frame Outputs + GET - Wallet requests transaction data from the daemon. Daemon replies with GET-OK, or ERROR. tx_id string Transaction ID @@ -113,19 +121,21 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_PUT_OK 6 #define WAP_PROTO_OUTPUT_INDEXES 7 #define WAP_PROTO_OUTPUT_INDEXES_OK 8 -#define WAP_PROTO_GET 9 -#define WAP_PROTO_GET_OK 10 -#define WAP_PROTO_SAVE 11 -#define WAP_PROTO_SAVE_OK 12 -#define WAP_PROTO_START 13 -#define WAP_PROTO_START_OK 14 -#define WAP_PROTO_STOP 15 -#define WAP_PROTO_STOP_OK 16 -#define WAP_PROTO_CLOSE 17 -#define WAP_PROTO_CLOSE_OK 18 -#define WAP_PROTO_PING 19 -#define WAP_PROTO_PING_OK 20 -#define WAP_PROTO_ERROR 21 +#define WAP_PROTO_RANDOM_OUTS 9 +#define WAP_PROTO_RANDOM_OUTS_OK 10 +#define WAP_PROTO_GET 11 +#define WAP_PROTO_GET_OK 12 +#define WAP_PROTO_SAVE 13 +#define WAP_PROTO_SAVE_OK 14 +#define WAP_PROTO_START 15 +#define WAP_PROTO_START_OK 16 +#define WAP_PROTO_STOP 17 +#define WAP_PROTO_STOP_OK 18 +#define WAP_PROTO_CLOSE 19 +#define WAP_PROTO_CLOSE_OK 20 +#define WAP_PROTO_PING 21 +#define WAP_PROTO_PING_OK 22 +#define WAP_PROTO_ERROR 23 #include @@ -156,7 +166,7 @@ int // Send the wap_proto to the output socket, does not destroy it int wap_proto_send (wap_proto_t *self, zsock_t *output); - + // Print contents of message to stdout void wap_proto_print (wap_proto_t *self); @@ -219,15 +229,15 @@ zmsg_t * void wap_proto_set_block_data (wap_proto_t *self, zmsg_t **msg_p); -// Get a copy of the tx_data field +// Get a copy of the tx_as_hex field zchunk_t * - wap_proto_tx_data (wap_proto_t *self); -// Get the tx_data field and transfer ownership to caller + wap_proto_tx_as_hex (wap_proto_t *self); +// Get the tx_as_hex field and transfer ownership to caller zchunk_t * - wap_proto_get_tx_data (wap_proto_t *self); -// Set the tx_data field, transferring ownership from caller + wap_proto_get_tx_as_hex (wap_proto_t *self); +// Set the tx_as_hex field, transferring ownership from caller void - wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p); + wap_proto_set_tx_as_hex (wap_proto_t *self, zchunk_t **chunk_p); // Get/set the tx_id field const char * @@ -245,6 +255,42 @@ zframe_t * void wap_proto_set_o_indexes (wap_proto_t *self, zframe_t **frame_p); +// Get/set the outs_count field +uint64_t + wap_proto_outs_count (wap_proto_t *self); +void + wap_proto_set_outs_count (wap_proto_t *self, uint64_t outs_count); + +// Get a copy of the amounts field +zframe_t * + wap_proto_amounts (wap_proto_t *self); +// Get the amounts field and transfer ownership to caller +zframe_t * + wap_proto_get_amounts (wap_proto_t *self); +// Set the amounts field, transferring ownership from caller +void + wap_proto_set_amounts (wap_proto_t *self, zframe_t **frame_p); + +// Get a copy of the random_outputs field +zframe_t * + wap_proto_random_outputs (wap_proto_t *self); +// Get the random_outputs field and transfer ownership to caller +zframe_t * + wap_proto_get_random_outputs (wap_proto_t *self); +// Set the random_outputs field, transferring ownership from caller +void + wap_proto_set_random_outputs (wap_proto_t *self, zframe_t **frame_p); + +// Get a copy of the tx_data field +zchunk_t * + wap_proto_tx_data (wap_proto_t *self); +// Get the tx_data field and transfer ownership to caller +zchunk_t * + wap_proto_get_tx_data (wap_proto_t *self); +// Set the tx_data field, transferring ownership from caller +void + wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p); + // Get/set the address field const char * wap_proto_address (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index 54c63ae48..91d74a355 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -38,11 +38,12 @@ typedef enum { start_event = 7, stop_event = 8, output_indexes_event = 9, - close_event = 10, - ping_event = 11, - expired_event = 12, - exception_event = 13, - settled_event = 14 + random_outs_event = 10, + close_event = 11, + ping_event = 12, + expired_event = 13, + exception_event = 14, + settled_event = 15 } event_t; // Names for state machine logging and error reporting @@ -67,6 +68,7 @@ s_event_name [] = { "START", "STOP", "OUTPUT_INDEXES", + "RANDOM_OUTS", "CLOSE", "PING", "expired", @@ -148,6 +150,8 @@ static void stop_mining_process (client_t *self); static void output_indexes (client_t *self); +static void + random_outs (client_t *self); static void deregister_wallet (client_t *self); static void @@ -344,6 +348,9 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_OUTPUT_INDEXES: return output_indexes_event; break; + case WAP_PROTO_RANDOM_OUTS: + return random_outs_event; + break; case WAP_PROTO_GET: return get_event; break; @@ -691,6 +698,24 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == random_outs_event) { + if (!self->exception) { + // random outs + if (self->server->verbose) + zsys_debug ("%s: $ random outs", self->log_prefix); + random_outs (&self->client); + } + if (!self->exception) { + // send RANDOM_OUTS_OK + if (self->server->verbose) + zsys_debug ("%s: $ send RANDOM_OUTS_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_RANDOM_OUTS_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK @@ -1092,6 +1117,7 @@ s_server_config_service (s_server_t *self) if (zsock_bind (self->router, "%s", endpoint) == -1) zsys_warning ("could not bind to %s (%s)", endpoint, zmq_strerror (zmq_errno ())); } +#if (ZMQ_VERSION_MAJOR >= 4) else if (streq (zconfig_name (section), "security")) { char *mechanism = zconfig_resolve (section, "mechanism", "null"); @@ -1109,6 +1135,7 @@ s_server_config_service (s_server_t *self) else zsys_warning ("mechanism=%s is not supported", mechanism); } +#endif section = zconfig_next (section); } s_server_config_global (self); diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 8ac3a18b5..50d737439 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -37,6 +37,7 @@ typedef struct { // Own properties int heartbeat_timer; // Timeout for heartbeats to server + int retries; // How many heartbeats we've tried } client_t; // Include the generated client engine @@ -97,6 +98,32 @@ use_connect_timeout (client_t *self) engine_set_timeout (self, self->args->timeout); } +// --------------------------------------------------------------------------- +// client_is_connected +// + +static void +client_is_connected (client_t *self) +{ + self->retries = 0; + engine_set_connected (self, true); + engine_set_timeout (self, self->heartbeat_timer); +} + +// --------------------------------------------------------------------------- +// check_if_connection_is_dead +// + +static void +check_if_connection_is_dead (client_t *self) +{ + // We send at most 3 heartbeats before expiring the server + if (++self->retries >= 3) { + engine_set_timeout (self, 0); + engine_set_connected (self, false); + engine_set_exception (self, exception_event); + } +} // --------------------------------------------------------------------------- // use_heartbeat_timer @@ -163,7 +190,7 @@ prepare_start_command (client_t *self) static void prepare_put_command (client_t *self) { - wap_proto_set_tx_data (self->message, &self->args->tx_data); + wap_proto_set_tx_as_hex (self->message, &self->args->tx_as_hex); } @@ -174,7 +201,7 @@ prepare_put_command (client_t *self) static void signal_have_put_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8s", "PUT OK", 0, + zsock_send (self->cmdpipe, "s8s", "PUT OK", wap_proto_status(self->message), wap_proto_tx_id (self->message)); } @@ -325,3 +352,25 @@ signal_server_not_present (client_t *self) zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Server is not reachable"); } +// --------------------------------------------------------------------------- +// prepare_get_random_outs_command +// + +static void +prepare_get_random_outs_command (client_t *self) +{ + wap_proto_set_amounts (self->message, &self->args->amounts); +} + + +// --------------------------------------------------------------------------- +// signal_have_random_outs_ok +// + +static void +signal_have_random_outs_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8p", "RANDOM OUTS OK", + wap_proto_status (self->message), + wap_proto_get_random_outputs (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 678309e80..16dd0a964 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -34,29 +34,37 @@ struct _wap_proto_t { int id; // wap_proto message ID byte *needle; // Read/write pointer for serialization byte *ceiling; // Valid upper limit for read pointer - /* Wallet identity */ + // Wallet identity char identity [256]; - /* */ + // block_ids zlist_t *block_ids; - /* */ + // start_height uint64_t start_height; - /* */ + // status uint64_t status; - /* */ + // curr_height uint64_t curr_height; - /* Frames of block data */ + // Frames of block data zmsg_t *block_data; - /* Transaction data */ - zchunk_t *tx_data; - /* Transaction ID */ + // Transaction as hex + zchunk_t *tx_as_hex; + // Transaction ID char tx_id [256]; - /* Output Indexes */ + // Output Indexes zframe_t *o_indexes; - /* */ + // Outs count + uint64_t outs_count; + // Amounts + zframe_t *amounts; + // Outputs + zframe_t *random_outputs; + // Transaction data + zchunk_t *tx_data; + // address char address [256]; - /* */ + // thread_count uint64_t thread_count; - /* Printable explanation */ + // Printable explanation char reason [256]; }; @@ -236,8 +244,11 @@ wap_proto_destroy (wap_proto_t **self_p) if (self->block_ids) zlist_destroy (&self->block_ids); zmsg_destroy (&self->block_data); - zchunk_destroy (&self->tx_data); + zchunk_destroy (&self->tx_as_hex); zframe_destroy (&self->o_indexes); + zframe_destroy (&self->amounts); + zframe_destroy (&self->random_outputs); + zchunk_destroy (&self->tx_data); // Free object itself free (self); @@ -254,7 +265,7 @@ int wap_proto_recv (wap_proto_t *self, zsock_t *input) { assert (input); - + if (zsock_type (input) == ZMQ_ROUTER) { zframe_destroy (&self->routing_id); self->routing_id = zframe_recv (input); @@ -273,7 +284,7 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) // Get and check protocol signature self->needle = (byte *) zmq_msg_data (&frame); self->ceiling = self->needle + zmq_msg_size (&frame); - + uint16_t signature; GET_NUMBER2 (signature); if (signature != (0xAAA0 | 0)) { @@ -342,11 +353,11 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) size_t chunk_size; GET_NUMBER4 (chunk_size); if (self->needle + chunk_size > (self->ceiling)) { - zsys_warning ("wap_proto: tx_data is missing data"); + zsys_warning ("wap_proto: tx_as_hex is missing data"); goto malformed; } - zchunk_destroy (&self->tx_data); - self->tx_data = zchunk_new (self->needle, chunk_size); + zchunk_destroy (&self->tx_as_hex); + self->tx_as_hex = zchunk_new (self->needle, chunk_size); self->needle += chunk_size; } break; @@ -370,6 +381,28 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) self->o_indexes = zframe_recv (input); break; + case WAP_PROTO_RANDOM_OUTS: + GET_NUMBER8 (self->outs_count); + // Get next frame off socket + if (!zsock_rcvmore (input)) { + zsys_warning ("wap_proto: amounts is missing"); + goto malformed; + } + zframe_destroy (&self->amounts); + self->amounts = zframe_recv (input); + break; + + case WAP_PROTO_RANDOM_OUTS_OK: + GET_NUMBER8 (self->status); + // Get next frame off socket + if (!zsock_rcvmore (input)) { + zsys_warning ("wap_proto: random_outputs is missing"); + goto malformed; + } + zframe_destroy (&self->random_outputs); + self->random_outputs = zframe_recv (input); + break; + case WAP_PROTO_GET: GET_STRING (self->tx_id); break; @@ -480,8 +513,8 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_PUT: frame_size += 4; // Size is 4 octets - if (self->tx_data) - frame_size += zchunk_size (self->tx_data); + if (self->tx_as_hex) + frame_size += zchunk_size (self->tx_as_hex); break; case WAP_PROTO_PUT_OK: frame_size += 8; // status @@ -492,6 +525,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_OUTPUT_INDEXES_OK: frame_size += 8; // status break; + case WAP_PROTO_RANDOM_OUTS: + frame_size += 8; // outs_count + break; + case WAP_PROTO_RANDOM_OUTS_OK: + frame_size += 8; // status + break; case WAP_PROTO_GET: frame_size += 1 + strlen (self->tx_id); break; @@ -520,7 +559,7 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER1 (self->id); bool send_block_data = false; size_t nbr_frames = 1; // Total number of frames to send - + switch (self->id) { case WAP_PROTO_OPEN: PUT_STRING ("WAP"); @@ -551,12 +590,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_PUT: - if (self->tx_data) { - PUT_NUMBER4 (zchunk_size (self->tx_data)); + if (self->tx_as_hex) { + PUT_NUMBER4 (zchunk_size (self->tx_as_hex)); memcpy (self->needle, - zchunk_data (self->tx_data), - zchunk_size (self->tx_data)); - self->needle += zchunk_size (self->tx_data); + zchunk_data (self->tx_as_hex), + zchunk_size (self->tx_as_hex)); + self->needle += zchunk_size (self->tx_as_hex); } else PUT_NUMBER4 (0); // Empty chunk @@ -575,6 +614,16 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) nbr_frames++; break; + case WAP_PROTO_RANDOM_OUTS: + PUT_NUMBER8 (self->outs_count); + nbr_frames++; + break; + + case WAP_PROTO_RANDOM_OUTS_OK: + PUT_NUMBER8 (self->status); + nbr_frames++; + break; + case WAP_PROTO_GET: PUT_STRING (self->tx_id); break; @@ -608,7 +657,7 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) } // Now send the data frame zmq_msg_send (&frame, zsock_resolve (output), --nbr_frames? ZMQ_SNDMORE: 0); - + // Now send any frame fields, in order if (self->id == WAP_PROTO_OUTPUT_INDEXES_OK) { // If o_indexes isn't set, send an empty frame @@ -617,6 +666,22 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) else zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); } + // Now send any frame fields, in order + if (self->id == WAP_PROTO_RANDOM_OUTS) { + // If amounts isn't set, send an empty frame + if (self->amounts) + zframe_send (&self->amounts, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + else + zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); + } + // Now send any frame fields, in order + if (self->id == WAP_PROTO_RANDOM_OUTS_OK) { + // If random_outputs isn't set, send an empty frame + if (self->random_outputs) + zframe_send (&self->random_outputs, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + else + zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); + } // Now send the block_data if necessary if (send_block_data) { if (self->block_data) { @@ -650,11 +715,11 @@ wap_proto_print (wap_proto_t *self) else zsys_debug (" identity="); break; - + case WAP_PROTO_OPEN_OK: zsys_debug ("WAP_PROTO_OPEN_OK:"); break; - + case WAP_PROTO_BLOCKS: zsys_debug ("WAP_PROTO_BLOCKS:"); zsys_debug (" block_ids="); @@ -667,7 +732,7 @@ wap_proto_print (wap_proto_t *self) } zsys_debug (" start_height=%ld", (long) self->start_height); break; - + case WAP_PROTO_BLOCKS_OK: zsys_debug ("WAP_PROTO_BLOCKS_OK:"); zsys_debug (" status=%ld", (long) self->status); @@ -679,17 +744,17 @@ wap_proto_print (wap_proto_t *self) else zsys_debug ("(NULL)"); break; - + case WAP_PROTO_PUT: zsys_debug ("WAP_PROTO_PUT:"); - zsys_debug (" tx_data=[ ... ]"); + zsys_debug (" tx_as_hex=[ ... ]"); break; - + case WAP_PROTO_PUT_OK: zsys_debug ("WAP_PROTO_PUT_OK:"); zsys_debug (" status=%ld", (long) self->status); break; - + case WAP_PROTO_OUTPUT_INDEXES: zsys_debug ("WAP_PROTO_OUTPUT_INDEXES:"); if (self->tx_id) @@ -697,7 +762,7 @@ wap_proto_print (wap_proto_t *self) else zsys_debug (" tx_id="); break; - + case WAP_PROTO_OUTPUT_INDEXES_OK: zsys_debug ("WAP_PROTO_OUTPUT_INDEXES_OK:"); zsys_debug (" status=%ld", (long) self->status); @@ -707,7 +772,27 @@ wap_proto_print (wap_proto_t *self) else zsys_debug ("(NULL)"); break; - + + case WAP_PROTO_RANDOM_OUTS: + zsys_debug ("WAP_PROTO_RANDOM_OUTS:"); + zsys_debug (" outs_count=%ld", (long) self->outs_count); + zsys_debug (" amounts="); + if (self->amounts) + zframe_print (self->amounts, NULL); + else + zsys_debug ("(NULL)"); + break; + + case WAP_PROTO_RANDOM_OUTS_OK: + zsys_debug ("WAP_PROTO_RANDOM_OUTS_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" random_outputs="); + if (self->random_outputs) + zframe_print (self->random_outputs, NULL); + else + zsys_debug ("(NULL)"); + break; + case WAP_PROTO_GET: zsys_debug ("WAP_PROTO_GET:"); if (self->tx_id) @@ -715,20 +800,20 @@ wap_proto_print (wap_proto_t *self) else zsys_debug (" tx_id="); break; - + case WAP_PROTO_GET_OK: zsys_debug ("WAP_PROTO_GET_OK:"); zsys_debug (" tx_data=[ ... ]"); break; - + case WAP_PROTO_SAVE: zsys_debug ("WAP_PROTO_SAVE:"); break; - + case WAP_PROTO_SAVE_OK: zsys_debug ("WAP_PROTO_SAVE_OK:"); break; - + case WAP_PROTO_START: zsys_debug ("WAP_PROTO_START:"); if (self->address) @@ -737,36 +822,36 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" address="); zsys_debug (" thread_count=%ld", (long) self->thread_count); break; - + case WAP_PROTO_START_OK: zsys_debug ("WAP_PROTO_START_OK:"); zsys_debug (" status=%ld", (long) self->status); break; - + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; - + case WAP_PROTO_STOP_OK: zsys_debug ("WAP_PROTO_STOP_OK:"); break; - + case WAP_PROTO_CLOSE: zsys_debug ("WAP_PROTO_CLOSE:"); break; - + case WAP_PROTO_CLOSE_OK: zsys_debug ("WAP_PROTO_CLOSE_OK:"); break; - + case WAP_PROTO_PING: zsys_debug ("WAP_PROTO_PING:"); break; - + case WAP_PROTO_PING_OK: zsys_debug ("WAP_PROTO_PING_OK:"); break; - + case WAP_PROTO_ERROR: zsys_debug ("WAP_PROTO_ERROR:"); zsys_debug (" status=%ld", (long) self->status); @@ -775,7 +860,7 @@ wap_proto_print (wap_proto_t *self) else zsys_debug (" reason="); break; - + } } @@ -847,6 +932,12 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_OUTPUT_INDEXES_OK: return ("OUTPUT_INDEXES_OK"); break; + case WAP_PROTO_RANDOM_OUTS: + return ("RANDOM_OUTS"); + break; + case WAP_PROTO_RANDOM_OUTS_OK: + return ("RANDOM_OUTS_OK"); + break; case WAP_PROTO_GET: return ("GET"); break; @@ -1035,34 +1126,34 @@ wap_proto_set_block_data (wap_proto_t *self, zmsg_t **msg_p) // -------------------------------------------------------------------------- -// Get the tx_data field without transferring ownership +// Get the tx_as_hex field without transferring ownership zchunk_t * -wap_proto_tx_data (wap_proto_t *self) +wap_proto_tx_as_hex (wap_proto_t *self) { assert (self); - return self->tx_data; + return self->tx_as_hex; } -// Get the tx_data field and transfer ownership to caller +// Get the tx_as_hex field and transfer ownership to caller zchunk_t * -wap_proto_get_tx_data (wap_proto_t *self) +wap_proto_get_tx_as_hex (wap_proto_t *self) { - zchunk_t *tx_data = self->tx_data; - self->tx_data = NULL; - return tx_data; + zchunk_t *tx_as_hex = self->tx_as_hex; + self->tx_as_hex = NULL; + return tx_as_hex; } -// Set the tx_data field, transferring ownership from caller +// Set the tx_as_hex field, transferring ownership from caller void -wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p) +wap_proto_set_tx_as_hex (wap_proto_t *self, zchunk_t **chunk_p) { assert (self); assert (chunk_p); - zchunk_destroy (&self->tx_data); - self->tx_data = *chunk_p; + zchunk_destroy (&self->tx_as_hex); + self->tx_as_hex = *chunk_p; *chunk_p = NULL; } @@ -1122,6 +1213,123 @@ wap_proto_set_o_indexes (wap_proto_t *self, zframe_t **frame_p) } +// -------------------------------------------------------------------------- +// Get/set the outs_count field + +uint64_t +wap_proto_outs_count (wap_proto_t *self) +{ + assert (self); + return self->outs_count; +} + +void +wap_proto_set_outs_count (wap_proto_t *self, uint64_t outs_count) +{ + assert (self); + self->outs_count = outs_count; +} + + +// -------------------------------------------------------------------------- +// Get the amounts field without transferring ownership + +zframe_t * +wap_proto_amounts (wap_proto_t *self) +{ + assert (self); + return self->amounts; +} + +// Get the amounts field and transfer ownership to caller + +zframe_t * +wap_proto_get_amounts (wap_proto_t *self) +{ + zframe_t *amounts = self->amounts; + self->amounts = NULL; + return amounts; +} + +// Set the amounts field, transferring ownership from caller + +void +wap_proto_set_amounts (wap_proto_t *self, zframe_t **frame_p) +{ + assert (self); + assert (frame_p); + zframe_destroy (&self->amounts); + self->amounts = *frame_p; + *frame_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the random_outputs field without transferring ownership + +zframe_t * +wap_proto_random_outputs (wap_proto_t *self) +{ + assert (self); + return self->random_outputs; +} + +// Get the random_outputs field and transfer ownership to caller + +zframe_t * +wap_proto_get_random_outputs (wap_proto_t *self) +{ + zframe_t *random_outputs = self->random_outputs; + self->random_outputs = NULL; + return random_outputs; +} + +// Set the random_outputs field, transferring ownership from caller + +void +wap_proto_set_random_outputs (wap_proto_t *self, zframe_t **frame_p) +{ + assert (self); + assert (frame_p); + zframe_destroy (&self->random_outputs); + self->random_outputs = *frame_p; + *frame_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the tx_data field without transferring ownership + +zchunk_t * +wap_proto_tx_data (wap_proto_t *self) +{ + assert (self); + return self->tx_data; +} + +// Get the tx_data field and transfer ownership to caller + +zchunk_t * +wap_proto_get_tx_data (wap_proto_t *self) +{ + zchunk_t *tx_data = self->tx_data; + self->tx_data = NULL; + return tx_data; +} + +// Set the tx_data field, transferring ownership from caller + +void +wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->tx_data); + self->tx_data = *chunk_p; + *chunk_p = NULL; +} + + // -------------------------------------------------------------------------- // Get/set the address field @@ -1191,7 +1399,7 @@ wap_proto_set_reason (wap_proto_t *self, const char *value) int wap_proto_test (bool verbose) { - printf (" * wap_proto: "); + printf (" * wap_proto:"); // Silence an "unused" warning by "using" the verbose variable if (verbose) {;} @@ -1203,13 +1411,16 @@ wap_proto_test (bool verbose) wap_proto_destroy (&self); // Create pair of sockets we can send through - zsock_t *input = zsock_new (ZMQ_ROUTER); - assert (input); - zsock_connect (input, "inproc://selftest-wap_proto"); - + // We must bind before connect if we wish to remain compatible with ZeroMQ < v4 zsock_t *output = zsock_new (ZMQ_DEALER); assert (output); - zsock_bind (output, "inproc://selftest-wap_proto"); + int rc = zsock_bind (output, "inproc://selftest-wap_proto"); + assert (rc == 0); + + zsock_t *input = zsock_new (ZMQ_ROUTER); + assert (input); + rc = zsock_connect (input, "inproc://selftest-wap_proto"); + assert (rc == 0); // Encode/send/decode and verify each message type int instance; @@ -1255,6 +1466,7 @@ wap_proto_test (bool verbose) assert (streq ((char *) zlist_first (block_ids), "Name: Brutus")); assert (streq ((char *) zlist_next (block_ids), "Age: 43")); zlist_destroy (&block_ids); + zlist_destroy (&blocks_block_ids); assert (wap_proto_start_height (self) == 123); } wap_proto_set_id (self, WAP_PROTO_BLOCKS_OK); @@ -1264,7 +1476,7 @@ wap_proto_test (bool verbose) wap_proto_set_curr_height (self, 123); zmsg_t *blocks_ok_block_data = zmsg_new (); wap_proto_set_block_data (self, &blocks_ok_block_data); - zmsg_addstr (wap_proto_block_data (self), "Hello, World"); + zmsg_addstr (wap_proto_block_data (self), "Captcha Diem"); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1276,11 +1488,15 @@ wap_proto_test (bool verbose) assert (wap_proto_start_height (self) == 123); assert (wap_proto_curr_height (self) == 123); assert (zmsg_size (wap_proto_block_data (self)) == 1); + char *content = zmsg_popstr (wap_proto_block_data (self)); + assert (streq (content, "Captcha Diem")); + zstr_free (&content); + zmsg_destroy (&blocks_ok_block_data); } wap_proto_set_id (self, WAP_PROTO_PUT); - zchunk_t *put_tx_data = zchunk_new ("Captcha Diem", 12); - wap_proto_set_tx_data (self, &put_tx_data); + zchunk_t *put_tx_as_hex = zchunk_new ("Captcha Diem", 12); + wap_proto_set_tx_as_hex (self, &put_tx_as_hex); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1288,7 +1504,8 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (memcmp (zchunk_data (wap_proto_tx_data (self)), "Captcha Diem", 12) == 0); + assert (memcmp (zchunk_data (wap_proto_tx_as_hex (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&put_tx_as_hex); } wap_proto_set_id (self, WAP_PROTO_PUT_OK); @@ -1328,6 +1545,39 @@ wap_proto_test (bool verbose) assert (wap_proto_routing_id (self)); assert (wap_proto_status (self) == 123); assert (zframe_streq (wap_proto_o_indexes (self), "Captcha Diem")); + zframe_destroy (&output_indexes_ok_o_indexes); + } + wap_proto_set_id (self, WAP_PROTO_RANDOM_OUTS); + + wap_proto_set_outs_count (self, 123); + zframe_t *random_outs_amounts = zframe_new ("Captcha Diem", 12); + wap_proto_set_amounts (self, &random_outs_amounts); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_outs_count (self) == 123); + assert (zframe_streq (wap_proto_amounts (self), "Captcha Diem")); + zframe_destroy (&random_outs_amounts); + } + wap_proto_set_id (self, WAP_PROTO_RANDOM_OUTS_OK); + + wap_proto_set_status (self, 123); + zframe_t *random_outs_ok_random_outputs = zframe_new ("Captcha Diem", 12); + wap_proto_set_random_outputs (self, &random_outs_ok_random_outputs); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (zframe_streq (wap_proto_random_outputs (self), "Captcha Diem")); + zframe_destroy (&random_outs_ok_random_outputs); } wap_proto_set_id (self, WAP_PROTO_GET); @@ -1353,6 +1603,7 @@ wap_proto_test (bool verbose) wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); assert (memcmp (zchunk_data (wap_proto_tx_data (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_ok_tx_data); } wap_proto_set_id (self, WAP_PROTO_SAVE); diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 9a71bee6f..71cce48df 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -261,3 +261,13 @@ signal_command_not_valid (client_t *self) { wap_proto_set_status (self->message, WAP_PROTO_COMMAND_INVALID); } + +// --------------------------------------------------------------------------- +// random_outs +// + +static void +random_outs (client_t *self) +{ + IPC::Daemon::get_random_outs(self->message); +} diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index bf996e55a..4e48142fa 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1065,9 +1065,10 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args_) { - if (!try_connect_to_daemon()) - return true; + /*if (!try_connect_to_daemon()) + return true;*/ +std::cout << "1\n"; std::vector local_args = args_; if(local_args.size() < 3) { @@ -1075,6 +1076,7 @@ bool simple_wallet::transfer(const std::vector &args_) return true; } +std::cout << "2\n"; size_t fake_outs_count; if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) { @@ -1083,6 +1085,7 @@ bool simple_wallet::transfer(const std::vector &args_) } local_args.erase(local_args.begin()); +std::cout << "3\n"; std::vector extra; if (1 == local_args.size() % 2) { @@ -1105,6 +1108,7 @@ bool simple_wallet::transfer(const std::vector &args_) } } +std::cout << "4\n"; vector dsts; for (size_t i = 0; i < local_args.size(); i += 2) { @@ -1179,14 +1183,17 @@ bool simple_wallet::transfer(const std::vector &args_) dsts.push_back(de); } +std::cout << "5\n"; try { // figure out what tx will be necessary auto ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra); +std::cout << "5a\n"; // if more than one tx necessary, prompt user to confirm if (ptx_vector.size() > 1) { +std::cout << "5b\n"; std::string prompt_str = "Your transaction needs to be split into "; prompt_str += std::to_string(ptx_vector.size()); prompt_str += " transactions. This will result in a transaction fee being applied to each transaction"; @@ -1202,6 +1209,7 @@ bool simple_wallet::transfer(const std::vector &args_) } } +std::cout << "6\n"; // actually commit the transactions while (!ptx_vector.empty()) { @@ -1219,6 +1227,7 @@ bool simple_wallet::transfer(const std::vector &args_) } catch (const tools::error::no_connection_to_daemon&) { +std::cout << "7\n"; fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running."; } catch (const tools::error::wallet_rpc_error& e) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 8498e1742..c0f913dc3 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1130,13 +1130,21 @@ std::string wallet2::address_from_txt_record(const std::string& s) void wallet2::commit_tx(pending_tx& ptx) { using namespace cryptonote; - COMMAND_RPC_SEND_RAW_TX::request req; + /*COMMAND_RPC_SEND_RAW_TX::request req; req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status);*/ + + std::string tx_as_hex_string = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); + zchunk_t *tx_as_hex = zchunk_new((void*)tx_as_hex_string.c_str(), tx_as_hex_string.length()); + int rc = wap_client_put(client, &tx_as_hex); + uint64_t status = wap_client_status(client); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "sendrawtransaction"); + THROW_WALLET_EXCEPTION_IF((status == IPC::STATUS_INVALID_TX) || (status == IPC::STATUS_TX_VERIFICATION_FAILED) || + (status == IPC::STATUS_TX_NOT_RELAYED), error::tx_rejected, ptx.tx, status); add_unconfirmed_tx(ptx.tx, ptx.change_dts.amount); @@ -1171,6 +1179,7 @@ std::vector wallet2::create_transactions(std::vector wallet2::create_transactions(std::vector amounts; BOOST_FOREACH(transfer_container::iterator it, selected_transfers) { THROW_WALLET_EXCEPTION_IF(it->m_tx.vout.size() <= it->m_internal_output_index, error::wallet_internal_error, "m_internal_output_index = " + std::to_string(it->m_internal_output_index) + " is greater or equal to outputs count = " + std::to_string(it->m_tx.vout.size())); - req.amounts.push_back(it->amount()); + amounts.push_back(it->amount()); } - bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); + zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t)); + int rc = wap_client_random_outs(client, outs_count, &amounts_frame); + /*bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error, "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size())); + std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));*/ + + uint64_t status = wap_client_status(client); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts"); + // TODO: Use a code to string mapping of errors + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_RANDOM_OUTS_FAILED, error::get_random_outs_error, "IPC::STATUS_RANDOM_OUTS_FAILED"); + THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_random_outs_error, "!IPC:STATUS_OK"); + + // Convert ZMQ response back into RPC response object. + zframe_t *outputs_frame = wap_client_random_outputs(client); + uint64_t frame_size = zframe_size(outputs_frame); + char *frame_data = reinterpret_cast(zframe_data(outputs_frame)); +std::string tmp(frame_data, frame_size); +std::cout << tmp << std::endl; + rapidjson::Document json; + THROW_WALLET_EXCEPTION_IF(json.Parse(frame_data, frame_size).HasParseError(), error::get_random_outs_error, "Couldn't JSON parse random outputs."); + for (rapidjson::SizeType i = 0; i < json["outputs"].Size(); i++) { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount output; + output.amount = json["outputs"][i]["amount"].GetInt(); + for (rapidjson::SizeType j = 0; j < json["outputs"][i]["outs"].Size(); j++) { + COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry entry; + entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt(); + std::string out_key(json["outputs"][i]["outs"][j]["out_key"].GetString(), json["outputs"][i]["outs"][j]["out_key"].GetStringLength()); + memcpy(entry.out_key.data, out_key.c_str(), 32); + output.outs.push_back(entry); + } + daemon_resp.outs.push_back(output); + } std::vector scanty_outs; BOOST_FOREACH(COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount& amount_outs, daemon_resp.outs) diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index 7809c3698..95120269d 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -439,7 +439,7 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_rejected : public transfer_error { - explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, const std::string& status) + explicit tx_rejected(std::string&& loc, const cryptonote::transaction& tx, uint64_t status) : transfer_error(std::move(loc), "transaction was rejected by daemon") , m_tx(tx) , m_status(status) @@ -447,7 +447,7 @@ namespace tools } const cryptonote::transaction& tx() const { return m_tx; } - const std::string& status() const { return m_status; } + uint64_t status() const { return m_status; } std::string to_string() const { @@ -460,7 +460,7 @@ namespace tools private: cryptonote::transaction m_tx; - std::string m_status; + uint64_t m_status; }; //---------------------------------------------------------------------------------------------------- struct tx_sum_overflow : public transfer_error From 6f7dd3bf3b4b7c1902008a9982f3ca84bbbbb092 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Tue, 7 Apr 2015 02:48:20 +0530 Subject: [PATCH 33/45] Removed net_skeleton --- external/net_skeleton | 1 - 1 file changed, 1 deletion(-) delete mode 160000 external/net_skeleton diff --git a/external/net_skeleton b/external/net_skeleton deleted file mode 160000 index f7b138a4c..000000000 --- a/external/net_skeleton +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f7b138a4ce9299651fa30b5fdfeb68811317d3c0 From 87a9de91686badd86ac7f663e9a7e3bb16c03141 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Tue, 7 Apr 2015 02:50:09 +0530 Subject: [PATCH 34/45] Added net_skeleton --- external/net_skeleton/.jigplugins.txt | 2 + external/net_skeleton/LICENSE | 15 + external/net_skeleton/README.adoc | 458 ++++ external/net_skeleton/examples/Makefile | 15 + .../examples/json_rpc_server/Makefile | 14 + .../json_rpc_server/json_rpc_server.c | 67 + .../net_skeleton/examples/netcat/Makefile | 14 + external/net_skeleton/examples/netcat/nc | Bin 0 -> 67011 bytes external/net_skeleton/examples/netcat/nc.c | 140 + .../examples/publish_subscribe/Makefile | 14 + .../publish_subscribe/publish_subscribe | Bin 0 -> 65633 bytes .../publish_subscribe/publish_subscribe.c | 112 + .../examples/restful_client/Makefile | 14 + .../examples/restful_client/restful_client | Bin 0 -> 61243 bytes .../examples/restful_client/restful_client.c | 54 + .../examples/restful_server/Makefile | 14 + .../examples/restful_server/index.html | 67 + .../restful_server/json_rpc_http_server.cpp | 75 + .../restful_server/json_rpc_http_server.h | 30 + .../examples/restful_server/restful_server | Bin 0 -> 61296 bytes .../examples/restful_server/restful_server.c | 54 + .../examples/tcp_echo_server/Makefile | 14 + .../examples/tcp_echo_server/echo_server | Bin 0 -> 61106 bytes .../examples/tcp_echo_server/echo_server.c | 50 + .../examples/websocket_chat/Makefile | 12 + .../examples/websocket_chat/index.html | 85 + .../examples/websocket_chat/websocket_chat.c | 79 + external/net_skeleton/modules/Makefile | 17 + external/net_skeleton/modules/http.c | 485 ++++ external/net_skeleton/modules/http.h | 78 + external/net_skeleton/modules/json-rpc.c | 141 + external/net_skeleton/modules/json-rpc.h | 66 + external/net_skeleton/modules/sha1.c | 141 + external/net_skeleton/modules/sha1.h | 26 + external/net_skeleton/modules/skeleton.c | 957 +++++++ external/net_skeleton/modules/skeleton.h | 263 ++ external/net_skeleton/modules/util.c | 166 ++ external/net_skeleton/modules/util.h | 30 + external/net_skeleton/net_skeleton.c | 2376 +++++++++++++++++ external/net_skeleton/net_skeleton.h | 537 ++++ .../scripts/embed_net_skeleton.sh | 28 + .../scripts/generate_ssl_certificates.sh | 40 + external/net_skeleton/test/.gitignore | 10 + external/net_skeleton/test/Makefile | 46 + external/net_skeleton/test/ca.pem | 49 + external/net_skeleton/test/client.pem | 45 + external/net_skeleton/test/server.pem | 45 + external/net_skeleton/test/unit_test.c | 558 ++++ 48 files changed, 7503 insertions(+) create mode 100644 external/net_skeleton/.jigplugins.txt create mode 100644 external/net_skeleton/LICENSE create mode 100644 external/net_skeleton/README.adoc create mode 100644 external/net_skeleton/examples/Makefile create mode 100644 external/net_skeleton/examples/json_rpc_server/Makefile create mode 100644 external/net_skeleton/examples/json_rpc_server/json_rpc_server.c create mode 100644 external/net_skeleton/examples/netcat/Makefile create mode 100644 external/net_skeleton/examples/netcat/nc create mode 100644 external/net_skeleton/examples/netcat/nc.c create mode 100644 external/net_skeleton/examples/publish_subscribe/Makefile create mode 100644 external/net_skeleton/examples/publish_subscribe/publish_subscribe create mode 100644 external/net_skeleton/examples/publish_subscribe/publish_subscribe.c create mode 100644 external/net_skeleton/examples/restful_client/Makefile create mode 100644 external/net_skeleton/examples/restful_client/restful_client create mode 100644 external/net_skeleton/examples/restful_client/restful_client.c create mode 100644 external/net_skeleton/examples/restful_server/Makefile create mode 100644 external/net_skeleton/examples/restful_server/index.html create mode 100644 external/net_skeleton/examples/restful_server/json_rpc_http_server.cpp create mode 100644 external/net_skeleton/examples/restful_server/json_rpc_http_server.h create mode 100644 external/net_skeleton/examples/restful_server/restful_server create mode 100644 external/net_skeleton/examples/restful_server/restful_server.c create mode 100644 external/net_skeleton/examples/tcp_echo_server/Makefile create mode 100644 external/net_skeleton/examples/tcp_echo_server/echo_server create mode 100644 external/net_skeleton/examples/tcp_echo_server/echo_server.c create mode 100644 external/net_skeleton/examples/websocket_chat/Makefile create mode 100644 external/net_skeleton/examples/websocket_chat/index.html create mode 100644 external/net_skeleton/examples/websocket_chat/websocket_chat.c create mode 100644 external/net_skeleton/modules/Makefile create mode 100644 external/net_skeleton/modules/http.c create mode 100644 external/net_skeleton/modules/http.h create mode 100644 external/net_skeleton/modules/json-rpc.c create mode 100644 external/net_skeleton/modules/json-rpc.h create mode 100644 external/net_skeleton/modules/sha1.c create mode 100644 external/net_skeleton/modules/sha1.h create mode 100644 external/net_skeleton/modules/skeleton.c create mode 100644 external/net_skeleton/modules/skeleton.h create mode 100644 external/net_skeleton/modules/util.c create mode 100644 external/net_skeleton/modules/util.h create mode 100644 external/net_skeleton/net_skeleton.c create mode 100644 external/net_skeleton/net_skeleton.h create mode 100644 external/net_skeleton/scripts/embed_net_skeleton.sh create mode 100644 external/net_skeleton/scripts/generate_ssl_certificates.sh create mode 100644 external/net_skeleton/test/.gitignore create mode 100644 external/net_skeleton/test/Makefile create mode 100644 external/net_skeleton/test/ca.pem create mode 100644 external/net_skeleton/test/client.pem create mode 100644 external/net_skeleton/test/server.pem create mode 100644 external/net_skeleton/test/unit_test.c diff --git a/external/net_skeleton/.jigplugins.txt b/external/net_skeleton/.jigplugins.txt new file mode 100644 index 000000000..54c0e5d0b --- /dev/null +++ b/external/net_skeleton/.jigplugins.txt @@ -0,0 +1,2 @@ +http://github.com/robmadole/jig-plugins@whitespace +http://github.com/robmadole/jig-plugins@woops diff --git a/external/net_skeleton/LICENSE b/external/net_skeleton/LICENSE new file mode 100644 index 000000000..59958c5d5 --- /dev/null +++ b/external/net_skeleton/LICENSE @@ -0,0 +1,15 @@ +Copyright (c) 2014 Cesanta Software Limited +All rights reserved + +This software is dual-licensed: you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. For the terms of this +license, see . + +You are free to use this software under the terms of the GNU General +Public License, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU General Public License for more details. + +Alternatively, you can license this software under a commercial +license, as set out in . diff --git a/external/net_skeleton/README.adoc b/external/net_skeleton/README.adoc new file mode 100644 index 000000000..7afcb15ac --- /dev/null +++ b/external/net_skeleton/README.adoc @@ -0,0 +1,458 @@ += Networking library for C/C++ + +:buildstatus-uri: https://www.codeship.io/projects/72674aa0-1cbd-0132-0050-4a361eed21f8 +:buildstatus-badge: https://www.codeship.io/projects/72674aa0-1cbd-0132-0050-4a361eed21f8/status?branch=master + +ifdef::env-github[] +image:{buildstatus-badge}[Build Status,link={buildstatus-uri}] +endif::[] + +image::https://drone.io/github.com/cesanta/net_skeleton/status.png[Build Status,link=https://drone.io/github.com/cesanta/net_skeleton/latest] + + +Net Skeleton is a multi-protocol networking library written in C. +It provides easy to use event-driven interface that allows to implement +network protocols or scalable network applications with little effort. +Net Skeleton releives developers from the burden of network programming +complexity and let them concentrate on the logic, saving time and money. + +Net Skeleton has built-in support for several protocols, like +HTTP and Websocket, and is ideal for embedded environments. Net Skeleton +has been designed as an open source platform for connecting devices and +bringing them online. + +== Features + +* Cross-platform: works on Linux/UNIX, QNX, eCos, Windows, Android, iPhone, etc +* Single-threaded, asynchronous, non-blocking core with simple event-based API +* Builtin protocols: + ** plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way) + ** HTTP client, HTTP server + ** Websocket client, Websocket server + ** JSON-RPC client, JSON-RPC server +* Tiny static and run-time footprint +* Source code is both ISO C and ISO C++ compliant +* Extensively tested and production-ready, trusted by many blue chip businesses + +== Concept + +Net Skeleton is a non-blocking, asyncronous event manager described by +`struct ns_mgr` structure. That structure holds active connections. +Connections could be either *listening*, *client* or *accepted*. +Client connections are created by +`ns_connect()` call. Listening connections are created by `ns_bind()` call. +Accepted connections are those that incoming on a listening connection. +Each connection is described by `struct ns_connection` structure, which has +a number of fields like socket, event handler function, send/receive buffer, +flags, et cetera. + +`ns_mgr_poll()` should be called in an infinite event loop. +`ns_mgr_poll()` iterates over all sockets, accepts new connections, +sends and receives data, closes connections, and calls an event handler +function for each of those events. + +Each connection has send and receive buffer, `struct ns_connection::send_iobuf` +and `struct ns_connection::recv_iobuf` respectively. When data arrives, +Net Skeleton appends received data to the `recv_iobuf` and +triggers `NS_RECV` event. User may send data back (`ns_send()` or +`ns_printf()`), which appends data to the `send_iobuf`. When Net Skeleton +successfully writes data to the socket, it discards it from `send_iobuf` and +sends `NS_SEND` event. When connection is closed, `NS_CLOSE` event is sent. + +image::http://cesanta.com/images/net_skeleton/iobuf.png[] + +== Using Net Skeleton + +1. Define an event handler function. +2. Initialize mgr by calling `ns_mgr_init()`. +3. Create *listening connections* with `ns_bind()` and/or *client connections* +with `ns_connect()`. Note that many connections can be created within a +single manager. Connections can be created at any time, including within +an event handler function. +4. Call `ns_mgr_poll()` in a loop. + +[source,c] +---- +#include "net_skeleton.h" + +// This event handler implements TCP echo server +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { // 1 + struct iobuf *io = &nc->recv_iobuf; + + switch (ev) { + case NS_RECV: + ns_send(nc, io->buf, io->len); // Echo received data back + iobuf_remove(io, io->len); // Discard data from recv buffer + break; + default: + break; + } +} + +int main(void) { + struct ns_mgr mgr; + ns_mgr_init(&mgr, NULL); // 2 + ns_bind(&mgr, "1234", ev_handler, NULL); // 3 + + // 4 - an event loop + for (;;) { + ns_mgr_poll(&mgr, 1000); + } + + ns_mgr_free(&mgr); + return 0; +} +---- + + +Net Skeleton accepts incoming connections, reads and writes data, and +calls specified event handler for each connection when appropriate. An +event handler should examine received data, set connection flags if needed, +and send data back to the client by `ns_send()` or `ns_printf()`. Here is a +typical event flow for the accepted connection: +`NS_ACCEPT` -> `NS_RECV` -> .... -> `NS_CLOSE`. Below is a complete list +of events triggered by Net Skeleton: + +NS_ACCEPT:: sent when new server connection is accepted by a +listening connection. `void *ev_data` is `union socket_address` +of the remote peer. +NS_CONNECT:: sent when a new client connection created by `ns_connect()` either +failed or succeeded. `void *ev_data` is `int *success`. If `success` is 0 +then connection has been established, otherwise it contains error code. Example +code to check connection status: + +[source,c] +---- +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { + int connect_status; + + switch (ev) { + case NS_CONNECT: + connect_status = * (int *) ev_data; + if (connect_status == 0) { + /* Success */ + } else { + /* Error */ + printf("connect() error: %s\n", strerror(connect_status)); + } + break; + ... +---- + +NS_RECV:: New data is received and appended to the end of `recv_iobuf`. +`void *ev_data` is `int *num_received_bytes`. + +WARNING: Net Skeleton uses `realloc()` to expand receive buffer. +It is user's responsibility to discard processed +data from the beginning of receive buffer, note the `iobuf_remove()` +call in the example above. + +NS_SEND:: Net Skeleton has written data to the remote peer and discarded +written data from the `send_iobuf`. `void *ev_data` is `int *num_sent_bytes` + +NS_POLL:: Sent to all connections on each invocation of `ns_server_poll()` + +An event handler can set `struct ns_connection::flags` attribute to control +the behavior of the connection. Below is a list of connection flags: + +* `NSF_FINISHED_SENDING_DATA` tells Net Skeleton that all data has been + appended to the `send_iobuf`. As soon as Net Skeleton sends it to the + socket, the connection will be closed. +* `NSF_BUFFER_BUT_DONT_SEND` tells Net Skeleton to append data to the + `send_iobuf` but hold on sending it, because the data will be modified + later and then will be sent by clearing `NSF_BUFFER_BUT_DONT_SEND` flag. +* `NSF_SSL_HANDSHAKE_DONE` SSL only, set when SSL handshake is done +* `NSF_CONNECTING` set when connection is in connecting state after + `ns_connect()` call but connect did not finish yet +* `NSF_CLOSE_IMMEDIATELY` tells Net Skeleton to close the connection + immediately, usually after some error +* `NSF_LISTENING` set for all listening connections +* `NSF_UDP` set if connection is UDP +* `NSF_IS_WEBSOCKET` set by Net Skeleton if connection is a Websocket connection +* `NSF_WEBSOCKET_NO_DEFRAG` should be set by a user if user wants to switch + off automatic frame defragmentation +* `NSF_USER_1`, `NSF_USER_2`, `NSF_USER_3`, `NSF_USER_4` could be + used by a developer to store application-specific state + +== Plain TCP/UDP/SSL API + +CAUTION: Net skeleton manager instance is single threaded. It does not protect +it's data structures by mutexes, therefore all functions that are dealing +with particular event manager should be called from the same thread, +with exception of `mg_broadcast()` function. It is fine to have different +event managers handled by different threads. + +=== Structures + +- `struct ns_connection` Describes a connection between two peers +- `struct ns_mgr` Container for a bunch of connections +- `struct iobuf` Describes piece of data + +=== Functions for net skeleton manager + +void ns_mgr_init(struct ns_mgr *, void *user_data):: + Initializes net skeleton manager. + +void ns_mgr_free(struct ns_mgr *):: + +De-initializes skeleton manager, closes and deallocates all active connections. + +time_t ns_mgr_poll(struct ns_mgr *, int milliseconds):: + +This function performs the actual IO, and must be called in a loop +(an event loop). Returns number current timestamp. + +void ns_broadcast(struct ns_mgr *, ns_event_handler_t cb, void *msg, size_t len):: + +Must be called from a different thread. Passes a message of a given length to +all connections. Skeleton manager has a socketpair, `struct ns_mgr::ctl`, +where `ns_broadcast()` pushes the message. +`ns_mgr_poll()` wakes up, reads a message from the socket pair, and calls +specified callback for each connection. Thus the callback function executes +in event manager thread. Note that `ns_broadcast()` is the only function +that can be, and must be, called from a different thread. + +void ns_next(struct ns_mgr *, struct ns_connection *):: + +Iterates over all active connections. Returns next connection from the list +of active connections, or `NULL` if there is no more connections. Below +is the iteration idiom: +[source,c] +---- +for (c = ns_next(srv, NULL); c != NULL; c = ns_next(srv, c)) { + // Do something with connection `c` +} +---- + + +=== Functions for adding new connections + +struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t sock, ns_event_handler_t ev_handler):: + +Create a connection, associate it with the given socket and event handler, and +add to the manager. + +struct ns_connection *ns_connect(struct ns_mgr *server, const char *addr, ns_event_handler_t ev_handler):: + +Connect to a remote host. If successful, `NS_CONNECT` event will be delivered +to the new connection. `addr` format is the same as for the `ns_bind()` call, +just an IP address becomes mandatory: `[PROTO://]HOST:PORT` +`PROTO` could be `tcp://` or `udp://`. If `HOST` is not an IP +address, Net Skeleton will resolve it - beware that standard blocking resolver +will be used. It is a good practice to pre-resolve hosts beforehands and +use only IP addresses to avoid blockin an IO thread. +Returns: new client connection, or `NULL` on error. + +struct ns_connection *ns_bind(struct ns_mgr *, const char *addr, ns_event_handler_t ev_handler):: + +Start listening on the given port. `addr` could be a port number, +e.g. `"3128"`, or IP address with a port number, e.g. `"127.0.0.1:3128"`. +Also, a protocol prefix could be specified, valid prefixes are `tcp://` or +`udp://`. + +Note that for UDP listening connections, only `NS_RECV` and `NS_CLOSE` +are triggered. + +If IP address is specified, Net Skeleton binds to a specific interface only. +Also, port could be `"0"`, in which case a random non-occupied port number +will be chosen. Return value: a listening connection on success, or +`NULL` on error. + +const char *ns_set_ssl(struct ns_connection *nc, const char *cert, const char *ca_cert):: +Enable SSL for a given connection. Connection must be TCP. For listening +connection, `cert` is a path to a server certificate, and is mandatory. +`ca_cert` if non-NULL, specifies CA certificate for client authentication, +enables two-way SSL. For client connections, both `cert` and `ca_cert` are +optional and can be set to NULL. All certificates +must be in PEM format. PEM file for server certificate should contain +both certificate and the private key concatenated together. +Returns: NULL if there is no error, or error string if there was error. + +Snippet below shows how to generate self-signed SSL certificate using OpenSSL: +[source,sh] +---- +openssl req -x509 -nodes -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 +cat cert.pem key.pem > my_ssl_cert.pem +---- + +=== Functions for sending data + +int ns_send(struct ns_connection *, const void *buf, int len):: +int ns_printf(struct ns_connection *, const char *fmt, ...):: +int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap):: + +These functions are for sending un-formatted and formatted data to the +connection. Number of written bytes is returned. Note that these sending +functions do not actually push data to the sockets, they just append data +to the output buffer. The exception is UDP connections. For UDP, data is +sent immediately, and returned value indicates an actual number of bytes +sent to the socket. + +=== Utility functions + +void *ns_start_thread(void *(*thread_function)(void *), void *param):: + Starts a new thread + +int ns_socketpair2(sock_t [2], int proto):: + Create a socket pair. `proto` can be either `SOCK_STREAM` or `SOCK_DGRAM`. + Return 0 on failure, 1 on success. + +void ns_set_close_on_exec(sock_t):: + Set close-on-exec bit for a given socket. + +void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags):: + Converts socket's local or remote address into string. `flags` parameter + is a bit mask that controls the behavior. If bit 2 is set (`flags & 4`) then + the remote address is stringified, otherwise local address is stringified. + If bit 0 is set, then IP + address is printed. If bit 1 is set, then port number is printed. If both + port number and IP address are printed, they are separated by `:`. + +int ns_hexdump(const void *buf, int len, char *dst, int dst_len):: + Takes a memory buffer `buf` of length `len` and creates a hex dump of that + buffer in `dst`. + +int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len):: + Converts domain name into IP address. This is a blocking call. Returns 1 + on success, 0 on failure. + +int ns_stat(const char *path, ns_stat_t *st):: + Perform a 64-bit `stat()` call against given file. `path` should be + UTF8 encoded. Return value is the same as for `stat()` syscall. + +FILE *ns_fopen(const char *path, const char *mode):: + Open given file and return a file stream. `path` and `mode` should be + UTF8 encoded. Return value is the same as for `fopen()` call. + +int ns_open(const char *path, int flag, int mode):: + Open given file and return file descriptor. `path` should be UTF8 encoded. + Return value is the same as for `open()` syscall. + +=== HTTP/Websocket API + +void ns_set_protocol_http_websocket(struct ns_connection *):: + Attach built-in HTTP event handler to the given connection. User-defined + event handler will receive following extra events: + - NS_HTTP_REQUEST: HTTP request has arrived. Parsed HTTP request is passed as + `struct http_message` through the handler's `void *ev_data` pointer. + - NS_HTTP_REPLY: HTTP reply has arrived. Parsed HTTP reply is passed as + `struct http_message` through the handler's `void *ev_data` pointer. + - NS_WEBSOCKET_HANDSHAKE_REQUEST: server has received websocket handshake + request. `ev_data` contains parsed HTTP request. + - NS_WEBSOCKET_HANDSHAKE_DONE: server has completed Websocket handshake. + `ev_data` is `NULL`. + - NS_WEBSOCKET_FRAME: new websocket frame has arrived. `ev_data` is + `struct websocket_message *` + +void ns_send_websocket_handshake(struct ns_connection *nc, const char *uri, const char *extra_headers):: + Sends websocket handshake to the server. `nc` must be a valid connection, connected to a server, `uri` is an URI on the server, `extra_headers` is + extra HTTP headers to send or `NULL`. + This function is to be used by websocket client. + +void ns_send_websocket_frame(struct ns_connection *nc, int op, const void *data, size_t data_len):: + Send websocket frame to the remote end. `op` specifies frame's type , one of: + - WEBSOCKET_OP_CONTINUE + - WEBSOCKET_OP_TEXT + - WEBSOCKET_OP_BINARY + - WEBSOCKET_OP_CLOSE + - WEBSOCKET_OP_PING + - WEBSOCKET_OP_PONG + `data` and `data_len` contain frame data. + +void ns_send_websocket_framev(struct ns_connection *nc, int op, const struct ns_str *frames, int num_frames); + Send multiple websocket frames. Like `ns_send_websocket_frame()`, but sends + multiple frames at once. + +void ns_printf_websocket_frame(struct ns_connection *nc, int op, const char *fmt, ...):: + Send websocket frame to the remote end. Like `ns_send_websocket_frame()`, + but allows to create formatted message with `printf()`-like semantics. + +struct ns_str *ns_get_http_header(struct http_message *, const char *):: + Returns HTTP header if it is present in the HTTP message, or `NULL`. + +int ns_parse_http(const char *s, int n, struct http_message *req):: + Parses HTTP message. Return number of bytes parsed. If HTTP message is + incomplete, `0` is returned. On parse error, negative number is returned. + +int ns_get_http_var(const struct ns_str *buf, const char *name, char *dst, size_t dst_len):: + Fetch an HTTP form variable `name` from a `buf` into a buffer specified by + `dst`, `dst_len`. Destination is always zero-terminated. Return length + of a fetched variable. If not found, 0 is returned. `buf` must be + valid url-encoded buffer. If destination is too small, `-1` is returned. + +void ns_serve_http(struct ns_connection *nc, struct http_message *request, struct ns_serve_http_opts options):: + Serve given HTTP request according to the `options`. + Example code snippet: + +[source,c] +.web_server.c +---- +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + struct ns_serve_http_opts opts = { .document_root = "/var/www" }; // C99 syntax + + switch (ev) { + case NS_HTTP_REQUEST: + ns_serve_http(nc, hm, opts); + break; + default: + break; + } +} +---- + +=== JSON-RPC API + +JSON-RPC module is implemented using +https://github.com/cesanta/frozen[Frozen JSON parser/generator]. So for +JSON-related functionality refer to Frozen documentation. + +int ns_rpc_parse_reply(const char *buf, int len, struct json_token *toks, int max_toks, struct ns_rpc_reply *reply, struct ns_rpc_error *error):: +Parse JSON-RPC reply contained in `buf`, `len` into JSON tokens array +`toks`, `max_toks`. If buffer contains valid reply, `reply` structure is +populated. The result of RPC call is located in `reply.result`. On error, +`error` structure is populated. Returns: the result of calling +`parse_json(buf, len, toks, max_toks)`. + +int ns_rpc_create_request(char *buf, int len, const char *method, const char *id, const char *params_fmt, ...):: +Create JSON-RPC request in a given buffer. Return length of the request, which +can be larger then `len` that indicates an overflow. + +int ns_rpc_create_reply(char *buf, int len, const struct ns_rpc_request *req, const char *result_fmt, ...):: +Create JSON-RPC reply in a given buffer. Return length of the reply, which +can be larger then `len` that indicates an overflow. + +int ns_rpc_create_error(char *, int, struct ns_rpc_request *req, int, const char *, const char *, ...):: +Create JSON-RPC error in a given buffer. Return length of the error, which +can be larger then `len` that indicates an overflow. + +int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code):: +Create JSON-RPC error in a given buffer. Return length of the error, which +can be larger then `len` that indicates an overflow. `code` could be one of: +`JSON_RPC_PARSE_ERROR`, `JSON_RPC_INVALID_REQUEST_ERROR`, +`JSON_RPC_METHOD_NOT_FOUND_ERROR`, `JSON_RPC_INVALID_PARAMS_ERROR`, +`JSON_RPC_INTERNAL_ERROR`, `JSON_RPC_SERVER_ERROR`. + +int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len, const char **methods, ns_rpc_handler_t *handlers):: +Parses JSON-RPC request contained in `buf`, `len`. Then, dispatches the request +to the correct handler method. Valid method names should be specified in NULL +terminated array `methods`, and corresponding handlers in `handlers`. +Result is put in `dst`, `dst_len`. Return: length of the result, which +can be larger then `dst_len` that indicates an overflow. + +== Examples + +* link:examples/echo_server[examples/echo_server]: + a simple TCP echo server. It accepts incoming connections + and echoes back any data that it receives +* link:examples/publish_subscribe[examples/publish_subscribe]: + implements pubsub pattern for TCP communication +* link:examples/netcat[examples/netcat]: + an implementation of Netcat utility with traffic hexdump and SSL support + +== License + +Net Skeleton is released under +http://www.gnu.org/licenses/old-licenses/gpl-2.0.html[GNU GPL v.2]. +Businesses have an option to get non-restrictive, royalty-free commercial +license and professional support from http://cesanta.com[Cesanta Software]. diff --git a/external/net_skeleton/examples/Makefile b/external/net_skeleton/examples/Makefile new file mode 100644 index 000000000..8b1313a6b --- /dev/null +++ b/external/net_skeleton/examples/Makefile @@ -0,0 +1,15 @@ +# Copyright (c) 2014 Cesanta Software +# All rights reserved + +SUBDIRS = $(sort $(dir $(wildcard */))) +X = $(SUBDIRS) + +.PHONY: $(SUBDIRS) + +all: $(SUBDIRS) + +$(SUBDIRS): + @$(MAKE) -C $@ + +clean: + for d in $(SUBDIRS) ; do $(MAKE) -C $$d clean ; done diff --git a/external/net_skeleton/examples/json_rpc_server/Makefile b/external/net_skeleton/examples/json_rpc_server/Makefile new file mode 100644 index 000000000..943450991 --- /dev/null +++ b/external/net_skeleton/examples/json_rpc_server/Makefile @@ -0,0 +1,14 @@ +PROG = json_rpc_server +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/json_rpc_server/json_rpc_server.c b/external/net_skeleton/examples/json_rpc_server/json_rpc_server.c new file mode 100644 index 000000000..5adf5a04b --- /dev/null +++ b/external/net_skeleton/examples/json_rpc_server/json_rpc_server.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * + * To test this server, do + * $ curl -d '{"id":1,method:"sum",params:[22,33]}' 127.0.0.1:8000 + */ + +#include "net_skeleton.h" + +static const char *s_http_port = "8000"; + +static int rpc_sum(char *buf, int len, struct ns_rpc_request *req) { + double sum = 0; + int i; + + if (req->params[0].type != JSON_TYPE_ARRAY) { + return ns_rpc_create_std_error(buf, len, req, + JSON_RPC_INVALID_PARAMS_ERROR); + } + + for (i = 0; i < req->params[0].num_desc; i++) { + if (req->params[i + 1].type != JSON_TYPE_NUMBER) { + return ns_rpc_create_std_error(buf, len, req, + JSON_RPC_INVALID_PARAMS_ERROR); + } + sum += strtod(req->params[i + 1].ptr, NULL); + } + return ns_rpc_create_reply(buf, len, req, "f", sum); +} + +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + static const char *methods[] = { "sum", NULL }; + static ns_rpc_handler_t handlers[] = { rpc_sum, NULL }; + char buf[100]; + + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + methods, 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; + } +} + +int main(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + + ns_mgr_init(&mgr, NULL); + nc = ns_bind(&mgr, s_http_port, ev_handler); + ns_set_protocol_http_websocket(nc); + + printf("Starting JSON-RPC server on port %s\n", s_http_port); + for (;;) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return 0; +} diff --git a/external/net_skeleton/examples/netcat/Makefile b/external/net_skeleton/examples/netcat/Makefile new file mode 100644 index 000000000..d1bcfbbb4 --- /dev/null +++ b/external/net_skeleton/examples/netcat/Makefile @@ -0,0 +1,14 @@ +PROG = nc +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread -DNS_ENABLE_SSL -lssl $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /DNS_ENABLE_SSL /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/netcat/nc b/external/net_skeleton/examples/netcat/nc new file mode 100644 index 0000000000000000000000000000000000000000..3903aacd4e0d1f51acb1815e7f5617191c23d683 GIT binary patch literal 67011 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
  • &zo^w34v{bDaN-pOx{>$wxrdDs}sHkB6TX$t__WdaD9@C_SXzsWw} z`c0sBc=m>)Fe_dj86D^yn!Vu`w!Ge9*&BW>;4I%G(Q*Ne^_?5&z0Wr%&^tmMmyF)3 zuEf#yc>EYGLU14~)7_QS(&o z0PN#a)b#p_dKllbcNhN#Utq8d5p74w_GUkNjoFm@m9xdHOCR~deisJ;6Z-ZP^i)xn zz5yfnHzs7Zw7l<{BH()bv~fH#F4q zLnP4VkY5`odbtwO!c#*9_1JuNwP4^w5`Z8D%?0O3L+6C?VKrWY!@4ifG+A5*)?jy# zXmBl3%1vl>2itM>0tqSC@5Zv1_{z>GFJl(*&dlp~q6*btp#`c$otb^9-kur08Bip9 zbLMH%g*c{*5}SbIMMrSPNOmh-d+2cXP=TLDiux3|##G?%kYzYk%sItaf4luCjE<<6 zmcum}tkF{cuyn|+J;sA=k@Nbp!mFJAo~i$DXXP0#XR-_JjQ)#qaDSJIXqHM3&DHi2 zdY980^bqEyT8j(*QCbv!suoZ66|35MO{@Xt)ZCnvS6B?)rTyy*sAYC;PRwWZQR6U9 zD%s`|!SFt6J=(bj&a_>eDZI@FeE1n439BSsOLD)dXASF5a1kH7`Qtfue`y?k|Az1C z<)bI{U41UZ4(Pl3@C>FI64sDXAf&&pZ;Lc>30pp@w|`m>Z|{I37H=;zO6!}eQIE~NYn9G0f0j_Bci*3cyHMfCjc*6~ zbC2*_Lb<(XDXN8Lbe|V+oy5Z0Nl3_koby)OAQdKb+WN9k@ms7fYX9;nkm_45xDRqX z`bXu;+V>Wr+FFz6=f5kJtQ|ELz7O5zacdmMi43N)<{eD!?l!rD2?fn+364#?l1UM9 zm9USKBfv;Ehw3sqU=5DcUlFO^Ma~>>VEDj2A+`T%7NkQ8@`wlwvxk%oL}>N;-hyYG zsNQXn-{K}R$mYm_BSuFunjgl~6xeB|XgSZ=X+m-W2l>H9D6SJaPlShxz{pJEgevX` z(pBU;sF-@v9=#3koXl^*R5%lR$eC&nS+4_N0IX0~_d}rvVqepjyJ=dhY)dPi@9D#v zTK^v3WH9%#fDa1Up<-F|a)(Mny|{CUiok}z5r*lYZWaz7%N6OXWgre&6bUBc7fYlK z?Lf*Ea7uua&*`pKOvc`kz#W*OHNFHIgAXSzDa6=2j7Z@|VZFN(#T_(j5$lRoM_^Z} zOKKYpm8>W$;Oa`lK<|p|C!SNINQ(w1US!V>ABN$K%}qM#�uCQ?q6=VOtUqikP*I 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.len, hm->body.p); + nc->flags |= NSF_FINISHED_SENDING_DATA; + s_exit = 1; + break; + default: + break; + } +} + +int main(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + + ns_mgr_init(&mgr, NULL); + nc = ns_connect(&mgr, s_target_address, ev_handler); + ns_set_protocol_http_websocket(nc); + + printf("Starting RESTful client against %s\n", s_target_address); + while (s_exit == 0) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return 0; +} diff --git a/external/net_skeleton/examples/restful_server/Makefile b/external/net_skeleton/examples/restful_server/Makefile new file mode 100644 index 000000000..9d3dfa556 --- /dev/null +++ b/external/net_skeleton/examples/restful_server/Makefile @@ -0,0 +1,14 @@ +PROG = restful_server +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/restful_server/index.html b/external/net_skeleton/examples/restful_server/index.html new file mode 100644 index 000000000..5486a1b6a --- /dev/null +++ b/external/net_skeleton/examples/restful_server/index.html @@ -0,0 +1,67 @@ + + + + + RESTful API demo + + + + + + + +
    +

    RESTful API demo.

    + +

    + This page demonstrates how Net Skeleton could be used to implement + RESTful APIs. Start typing numbers into the text fields below. + Browser sends two numbers to /api/v1/sum URI. + Web server calclulates the sum and returns the result. +

    + +
    + +
    + +
    +   +
    + +
    + + diff --git a/external/net_skeleton/examples/restful_server/json_rpc_http_server.cpp b/external/net_skeleton/examples/restful_server/json_rpc_http_server.cpp new file mode 100644 index 000000000..563faf786 --- /dev/null +++ b/external/net_skeleton/examples/restful_server/json_rpc_http_server.cpp @@ -0,0 +1,75 @@ +#include "json_rpc_http_server.h" + +#include + +namespace RPC +{ + Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port) + { + m_ip = ip; + m_port = port; + m_is_running = false; + m_method_count = 0; + } + + Json_rpc_http_server::~Json_rpc_http_server() + { + stop(); + } + + void Json_rpc_http_server::start() + { + m_is_running = true; + server_thread = new boost::thread(poll); + } + + void Json_rpc_http_server::poll() + { + ns_mgr_init(&mgr, NULL); + nc = ns_bind(&mgr, s_http_port, ev_handler); + ns_set_protocol_http_websocket(nc); + while (m_is_running) { + ns_mgr_poll(&mgr, 1000); + } + } + + void Json_rpc_http_server::stop() + { + m_is_running = false; + server_thread->join(); + delete server_thread; + ns_mgr_free(&mgr); + } + + void ev_handler(struct ns_connection *nc, int ev, void *ev_data) + { + struct http_message *hm = (struct http_message *) ev_data; + char buf[100]; + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + m_method_names, m_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; + } + } + + bool add_handler(const std::string &method_name, + void (*hander)(char *buf, int len, struct ns_rpc_request *req)) + { + if (m_method_count == MAX_METHODS) + { + return false; + } + m_method_names[m_method_count] = new char[method_name.length() + 1]; + strcpy(m_method_names[m_method_count], method_name.c_str()); + m_handlers[m_method_count] = hander; + m_method_count++; + return true; + } +} diff --git a/external/net_skeleton/examples/restful_server/json_rpc_http_server.h b/external/net_skeleton/examples/restful_server/json_rpc_http_server.h new file mode 100644 index 000000000..25774e1c5 --- /dev/null +++ b/external/net_skeleton/examples/restful_server/json_rpc_http_server.h @@ -0,0 +1,30 @@ +#include "net_skeleton/net_skeleton.h" +#include +#include + +#define MAX_METHODS + +namespace RPC +{ + class Json_rpc_http_server + { + struct ns_mgr mgr; + struct ns_connection *nc; + Boost::thread *server_thread; + void ev_handler(struct ns_connection *nc, int ev, void *ev_data); + void poll(); + std::string m_ip; + std::string m_port; + bool m_is_running; + char *m_method_names[MAX_METHODS]; + ns_rpc_handler_t m_handlers[MAX_METHODS]; + int m_method_count; + public: + Json_rpc_http_server(const std::string &ip, const std::string &port); + ~Json_rpc_http_server(); + void start(); + void stop(); + bool add_handler(const std::string &method_name, + void (*hander)(char *buf, int len, struct ns_rpc_request *req)); + }; +} diff --git a/external/net_skeleton/examples/restful_server/restful_server b/external/net_skeleton/examples/restful_server/restful_server new file mode 100644 index 0000000000000000000000000000000000000000..52f13a5ca554cec81657b9ebaa760be21581dc5c GIT binary patch literal 61296 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
    Xzrc{Zfxpe(3z*ls;ZR+2qv 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 literal 0 HcmV?d00001 diff --git a/external/net_skeleton/examples/restful_server/restful_server.c b/external/net_skeleton/examples/restful_server/restful_server.c new file mode 100644 index 000000000..77c40b543 --- /dev/null +++ b/external/net_skeleton/examples/restful_server/restful_server.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "net_skeleton.h" + +static const char *s_http_port = "8000"; +static struct ns_serve_http_opts s_http_server_opts = { "." }; + +static void handle_sum_call(struct ns_connection *nc, struct http_message *hm) { + char n1[100], n2[100]; + + /* Get form variables */ + ns_get_http_var(&hm->body, "n1", n1, sizeof(n1)); + ns_get_http_var(&hm->body, "n2", n2, sizeof(n2)); + + ns_printf(nc, "%s", "HTTP/1.0 200 OK\n\n"); + ns_printf(nc, "{ \"result\": %lf }", strtod(n1, NULL) + strtod(n2, NULL)); +} + +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + + switch (ev) { + case NS_HTTP_REQUEST: + if (ns_vcmp(&hm->uri, "/api/v1/sum") == 0) { + handle_sum_call(nc, hm); /* Handle RESTful call */ + } else { + ns_serve_http(nc, hm, s_http_server_opts); /* Serve static content */ + } + nc->flags |= NSF_FINISHED_SENDING_DATA; + break; + default: + break; + } +} + +int main(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + + ns_mgr_init(&mgr, NULL); + nc = ns_bind(&mgr, s_http_port, ev_handler); + ns_set_protocol_http_websocket(nc); + + printf("Starting RESTful server on port %s\n", s_http_port); + for (;;) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return 0; +} diff --git a/external/net_skeleton/examples/tcp_echo_server/Makefile b/external/net_skeleton/examples/tcp_echo_server/Makefile new file mode 100644 index 000000000..fdecb884f --- /dev/null +++ b/external/net_skeleton/examples/tcp_echo_server/Makefile @@ -0,0 +1,14 @@ +PROG = echo_server +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/tcp_echo_server/echo_server b/external/net_skeleton/examples/tcp_echo_server/echo_server new file mode 100644 index 0000000000000000000000000000000000000000..b9ddb79dbd738667f00932380cede3b5ebaf8b16 GIT binary patch literal 61106 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 literal 0 HcmV?d00001 diff --git a/external/net_skeleton/examples/tcp_echo_server/echo_server.c b/external/net_skeleton/examples/tcp_echo_server/echo_server.c new file mode 100644 index 000000000..79a5800cb --- /dev/null +++ b/external/net_skeleton/examples/tcp_echo_server/echo_server.c @@ -0,0 +1,50 @@ +// Copyright (c) 2014 Cesanta Software Limited +// All rights reserved +// +// This software is dual-licensed: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. For the terms of this +// license, see . +// +// You are free to use this software under the terms of the GNU General +// Public License, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// Alternatively, you can license this software under a commercial +// license, as set out in . +// +// $Date: 2014-09-28 05:04:41 UTC $ + +#include "net_skeleton.h" + +static void ev_handler(struct ns_connection *nc, int ev, void *p) { + struct iobuf *io = &nc->recv_iobuf; + (void) p; + + switch (ev) { + case NS_RECV: + ns_send(nc, io->buf, io->len); // Echo message back + iobuf_remove(io, io->len); // Discard message from recv buffer + break; + default: + break; + } +} + +int main(void) { + struct ns_mgr mgr; + const char *port1 = "1234", *port2 = "127.0.0.1:17000"; + + ns_mgr_init(&mgr, NULL); + ns_bind(&mgr, port1, ev_handler); + ns_bind(&mgr, port2, ev_handler); + + printf("Starting echo mgr on ports %s, %s\n", port1, port2); + for (;;) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return 0; +} diff --git a/external/net_skeleton/examples/websocket_chat/Makefile b/external/net_skeleton/examples/websocket_chat/Makefile new file mode 100644 index 000000000..80dc92e46 --- /dev/null +++ b/external/net_skeleton/examples/websocket_chat/Makefile @@ -0,0 +1,12 @@ +# Copyright (c) 2014 Cesanta Software +# All rights reserved + +PROG = websocket_chat +CFLAGS = -W -Wall -I../.. -pthread -g -O0 $(CFLAGS_EXTRA) +SOURCES = $(PROG).c ../../net_skeleton.c + +$(PROG): $(SOURCES) + $(CC) -o $(PROG) $(SOURCES) $(CFLAGS) + +clean: + rm -rf $(PROG) *.exe *.dSYM *.obj *.exp .*o *.lib diff --git a/external/net_skeleton/examples/websocket_chat/index.html b/external/net_skeleton/examples/websocket_chat/index.html new file mode 100644 index 000000000..a74585cbc --- /dev/null +++ b/external/net_skeleton/examples/websocket_chat/index.html @@ -0,0 +1,85 @@ + + + + + WebSocket Test + + + + + + + + + diff --git a/external/net_skeleton/examples/websocket_chat/websocket_chat.c b/external/net_skeleton/examples/websocket_chat/websocket_chat.c new file mode 100644 index 000000000..d200228df --- /dev/null +++ b/external/net_skeleton/examples/websocket_chat/websocket_chat.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "net_skeleton.h" + +static int s_signal_received = 0; +static const char *s_http_port = "8000"; +static struct ns_serve_http_opts s_http_server_opts = { "." }; + +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); // Reinstantiate signal handler + s_signal_received = sig_num; +} + +static int is_websocket(const struct ns_connection *nc) { + return nc->flags & NSF_IS_WEBSOCKET; +} + +static void broadcast(struct ns_connection *nc, const char *msg, size_t len) { + struct ns_connection *c; + char buf[500]; + + snprintf(buf, sizeof(buf), "%p %.*s", nc, (int) len, msg); + for (c = ns_next(nc->mgr, NULL); c != NULL; c = ns_next(nc->mgr, c)) { + ns_send_websocket_frame(c, WEBSOCKET_OP_TEXT, buf, strlen(buf)); + } +} + +static void ev_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + struct websocket_message *wm = (struct websocket_message *) ev_data; + + switch (ev) { + case NS_HTTP_REQUEST: + /* Usual HTTP request - serve static files */ + ns_serve_http(nc, hm, s_http_server_opts); + nc->flags |= NSF_FINISHED_SENDING_DATA; + break; + case NS_WEBSOCKET_HANDSHAKE_DONE: + /* New websocket connection. Tell everybody. */ + broadcast(nc, "joined", 6); + break; + case NS_WEBSOCKET_FRAME: + /* New websocket message. Tell everybody. */ + broadcast(nc, (char *) wm->data, wm->size); + break; + case NS_CLOSE: + /* Disconnect. Tell everybody. */ + if (is_websocket(nc)) { + broadcast(nc, "left", 4); + } + break; + default: + break; + } +} + +int main(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + + ns_mgr_init(&mgr, NULL); + + nc = ns_bind(&mgr, s_http_port, ev_handler); + ns_set_protocol_http_websocket(nc); + + printf("Started on port %s\n", s_http_port); + while (s_signal_received == 0) { + ns_mgr_poll(&mgr, 200); + } + ns_mgr_free(&mgr); + + return 0; +} diff --git a/external/net_skeleton/modules/Makefile b/external/net_skeleton/modules/Makefile new file mode 100644 index 000000000..571c6d251 --- /dev/null +++ b/external/net_skeleton/modules/Makefile @@ -0,0 +1,17 @@ +FR = ../../frozen +HEADERS = skeleton.h $(FR)/frozen.h sha1.h util.h http.h json-rpc.h +SOURCES = skeleton.c $(FR)/frozen.c http.c sha1.c util.c json-rpc.c + +all: ../net_skeleton.c ../net_skeleton.h + +../net_skeleton.h: Makefile $(HEADERS) + $(MAKE) -s --no-print-directory merge_net_skeleton.h >$@ + +../net_skeleton.c: Makefile $(SOURCES) + $(MAKE) -s --no-print-directory merge_net_skeleton.c >$@ + +merge_net_skeleton.h: Makefile $(HEADERS) + @cat $(HEADERS) + +merge_net_skeleton.c: Makefile $(SOURCES) + @(echo '#include "net_skeleton.h"'; (cat $(SOURCES) | sed '/^#include "/d')) diff --git a/external/net_skeleton/modules/http.c b/external/net_skeleton/modules/http.c new file mode 100644 index 000000000..7b0bd6fd5 --- /dev/null +++ b/external/net_skeleton/modules/http.c @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_DISABLE_HTTP_WEBSOCKET + +#include "net_skeleton.h" +#include "sha1.h" +#include "util.h" +#include "http.h" + +/* + * Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n + */ +static int get_request_len(const char *s, int buf_len) { + const unsigned char *buf = (unsigned char *) s; + int i; + + for (i = 0; i < buf_len; i++) { + if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { + return -1; + } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { + return i + 2; + } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && + buf[i + 2] == '\n') { + return i + 3; + } + } + + return 0; +} + +int ns_parse_http(const char *s, int n, struct http_message *req) { + const char *end; + int len, i; + + if ((len = get_request_len(s, n)) <= 0) return len; + + memset(req, 0, sizeof(*req)); + req->message.p = s; + req->body.p = s + len; + req->message.len = req->body.len = (size_t) ~0; + end = s + len; + + /* Request is fully buffered. Skip leading whitespaces. */ + while (s < end && isspace(* (unsigned char *) s)) s++; + + /* Parse request line: method, URI, proto */ + s = ns_skip(s, end, " ", &req->method); + s = ns_skip(s, end, " ", &req->uri); + s = ns_skip(s, end, "\r\n", &req->proto); + if (req->uri.p <= req->method.p || req->proto.p <= req->uri.p) return -1; + + for (i = 0; i < (int) ARRAY_SIZE(req->header_names); i++) { + struct ns_str *k = &req->header_names[i], *v = &req->header_values[i]; + + s = ns_skip(s, end, ": ", k); + s = ns_skip(s, end, "\r\n", v); + + while (v->len > 0 && v->p[v->len - 1] == ' ') { + v->len--; /* Trim trailing spaces in header value */ + } + + if (k->len == 0 || v->len == 0) { + k->p = v->p = NULL; + break; + } + + if (!ns_ncasecmp(k->p, "Content-Length", 14)) { + req->body.len = to64(v->p); + req->message.len = len + req->body.len; + } + } + + if (req->body.len == (size_t) ~0 && ns_vcasecmp(&req->method, "GET") == 0) { + req->body.len = 0; + req->message.len = len; + } + + return len; +} + +struct ns_str *ns_get_http_header(struct http_message *hm, const char *name) { + size_t i, len = strlen(name); + + for (i = 0; i < ARRAY_SIZE(hm->header_names); i++) { + struct ns_str *h = &hm->header_names[i], *v = &hm->header_values[i]; + if (h->p != NULL && h->len == len && !ns_ncasecmp(h->p, name, len)) return v; + } + + return NULL; +} + +static int is_ws_fragment(unsigned char flags) { + return (flags & 0x80) == 0 || (flags & 0x0f) == 0; +} + +static int is_ws_first_fragment(unsigned char flags) { + return (flags & 0x80) == 0 && (flags & 0x0f) != 0; +} + +static int deliver_websocket_data(struct ns_connection *nc) { + /* Using unsigned char *, cause of integer arithmetic below */ + uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_iobuf.len, + len, mask_len = 0, header_len = 0; + unsigned char *p = (unsigned char *) nc->recv_iobuf.buf, + *buf = p, *e = p + buf_len; + unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */ + int ok, reass = buf_len > 0 && is_ws_fragment(p[0]) && + !(nc->flags & NSF_WEBSOCKET_NO_DEFRAG); + + /* If that's a continuation frame that must be reassembled, handle it */ + if (reass && !is_ws_first_fragment(p[0]) && buf_len >= 1 + sizeof(*sizep) && + buf_len >= 1 + sizeof(*sizep) + *sizep) { + buf += 1 + sizeof(*sizep) + *sizep; + buf_len -= 1 + sizeof(*sizep) + *sizep; + } + + if (buf_len >= 2) { + len = buf[1] & 127; + mask_len = buf[1] & 128 ? 4 : 0; + if (len < 126 && buf_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && buf_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ntohs(* (uint16_t *) &buf[2]); + } else if (buf_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) ntohl(* (uint32_t *) &buf[2])) << 32) + + ntohl(* (uint32_t *) &buf[6]); + } + } + + frame_len = header_len + data_len; + ok = frame_len > 0 && frame_len <= buf_len; + + if (ok) { + struct websocket_message wsm; + + wsm.size = (size_t) data_len; + wsm.data = buf + header_len; + wsm.flags = buf[0]; + + /* Apply mask if necessary */ + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4]; + } + } + + if (reass) { + /* On first fragmented frame, nullify size */ + if (is_ws_first_fragment(wsm.flags)) { + iobuf_resize(&nc->recv_iobuf, nc->recv_iobuf.size + sizeof(*sizep)); + p[0] &= ~0x0f; /* Next frames will be treated as continuation */ + buf = p + 1 + sizeof(*sizep); + *sizep = 0; /* TODO(lsm): fix. this can stomp over frame data */ + } + + /* Append this frame to the reassembled buffer */ + memmove(buf, wsm.data, e - wsm.data); + (*sizep) += wsm.size; + nc->recv_iobuf.len -= wsm.data - buf; + + /* On last fragmented frame - call user handler and remove data */ + if (wsm.flags & 0x80) { + wsm.data = p + 1 + sizeof(*sizep); + wsm.size = *sizep; + nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm); + iobuf_remove(&nc->recv_iobuf, 1 + sizeof(*sizep) + *sizep); + } + } else { + /* TODO(lsm): properly handle OOB control frames during defragmentation */ + nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm); /* Call handler */ + iobuf_remove(&nc->recv_iobuf, (size_t) frame_len); /* Cleanup frame */ + } + } + + return ok; +} + +static void ns_send_ws_header(struct ns_connection *nc, int op, size_t len) { + int header_len; + unsigned char header[10]; + + header[0] = 0x80 + (op & 0x0f); + if (len < 126) { + header[1] = len; + header_len = 2; + } else if (len < 65535) { + header[1] = 126; + * (uint16_t *) &header[2] = htons((uint16_t) len); + header_len = 4; + } else { + header[1] = 127; + * (uint32_t *) &header[2] = htonl((uint32_t) ((uint64_t) len >> 32)); + * (uint32_t *) &header[6] = htonl((uint32_t) (len & 0xffffffff)); + header_len = 10; + } + ns_send(nc, header, header_len); +} + +void ns_send_websocket_frame(struct ns_connection *nc, int op, + const void *data, size_t len) { + ns_send_ws_header(nc, op, len); + ns_send(nc, data, len); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= NSF_FINISHED_SENDING_DATA; + } +} + +void ns_send_websocket_framev(struct ns_connection *nc, int op, + const struct ns_str *strv, int strvcnt) { + int i; + int len = 0; + for (i=0; iflags |= NSF_FINISHED_SENDING_DATA; + } +} + +void ns_printf_websocket_frame(struct ns_connection *nc, int op, + const char *fmt, ...) { + char mem[4192], *buf = mem; + va_list ap; + int len; + + va_start(ap, fmt); + if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + ns_send_websocket_frame(nc, op, buf, len); + } + va_end(ap); + + if (buf != mem && buf != NULL) { + free(buf); + } +} + +static void websocket_handler(struct ns_connection *nc, int ev, void *ev_data) { + nc->handler(nc, ev, ev_data); + + switch (ev) { + case NS_RECV: + do { } while (deliver_websocket_data(nc)); + break; + default: + break; + } +} + +static void ws_handshake(struct ns_connection *nc, const struct ns_str *key) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + char buf[500], sha[20], b64_sha[sizeof(sha) * 2]; + SHA1_CTX sha_ctx; + + snprintf(buf, sizeof(buf), "%.*s%s", (int) key->len, key->p, magic); + + SHA1Init(&sha_ctx); + SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); + SHA1Final((unsigned char *) sha, &sha_ctx); + + ns_base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); + ns_printf(nc, "%s%s%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); +} + +static void http_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct iobuf *io = &nc->recv_iobuf; + struct http_message hm; + struct ns_str *vec; + int req_len; + + /* + * For HTTP messages without Content-Length, always send HTTP message + * before NS_CLOSE message. + */ + if (ev == NS_CLOSE && io->len > 0 && + ns_parse_http(io->buf, io->len, &hm) > 0) { + hm.body.len = io->buf + io->len - hm.body.p; + nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm); + } + + nc->handler(nc, ev, ev_data); + + if (ev == NS_RECV) { + req_len = ns_parse_http(io->buf, io->len, &hm); + if (req_len < 0 || (req_len == 0 && io->len >= NS_MAX_HTTP_REQUEST_SIZE)) { + nc->flags |= NSF_CLOSE_IMMEDIATELY; + } else if (req_len == 0) { + /* Do nothing, request is not yet fully buffered */ + } else if (nc->listener == NULL && + ns_get_http_header(&hm, "Sec-WebSocket-Accept")) { + /* We're websocket client, got handshake response from server. */ + /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ + iobuf_remove(io, req_len); + nc->proto_handler = websocket_handler; + nc->flags |= NSF_IS_WEBSOCKET; + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL); + websocket_handler(nc, NS_RECV, ev_data); + } else if (nc->listener != NULL && + (vec = ns_get_http_header(&hm, "Sec-WebSocket-Key")) != NULL) { + /* This is a websocket request. Switch protocol handlers. */ + iobuf_remove(io, req_len); + nc->proto_handler = websocket_handler; + nc->flags |= NSF_IS_WEBSOCKET; + + /* Send handshake */ + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_REQUEST, &hm); + if (!(nc->flags & NSF_CLOSE_IMMEDIATELY)) { + if (nc->send_iobuf.len == 0) { + ws_handshake(nc, vec); + } + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL); + websocket_handler(nc, NS_RECV, ev_data); + } + } else if (hm.message.len <= io->len) { + /* Whole HTTP message is fully buffered, call event handler */ + nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm); + iobuf_remove(io, hm.message.len); + } + } +} + +void ns_set_protocol_http_websocket(struct ns_connection *nc) { + nc->proto_handler = http_handler; +} + +void ns_send_websocket_handshake(struct ns_connection *nc, const char *uri, + const char *extra_headers) { + unsigned long random = (unsigned long) uri; + char key[sizeof(random) * 2]; + + ns_base64_encode((unsigned char *) &random, sizeof(random), key); + ns_printf(nc, "GET %s HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: %s\r\n" + "%s\r\n", + uri, key, extra_headers == NULL ? "" : extra_headers); +} + +void ns_send_http_file(struct ns_connection *nc, const char *path, + ns_stat_t *st) { + char buf[BUFSIZ]; + size_t n; + FILE *fp; + + if ((fp = fopen(path, "rb")) != NULL) { + ns_printf(nc, "HTTP/1.1 200 OK\r\n" + "Content-Length: %lu\r\n\r\n", (unsigned long) st->st_size); + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + ns_send(nc, buf, n); + } + fclose(fp); + } else { + ns_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n" + "Content-Length: 0\r\n\r\n"); + } +} + +static void remove_double_dots(char *s) { + char *p = s; + + while (*s != '\0') { + *p++ = *s++; + if (s[-1] == '/' || s[-1] == '\\') { + while (s[0] != '\0') { + if (s[0] == '/' || s[0] == '\\') { + s++; + } else if (s[0] == '.' && s[1] == '.') { + s += 2; + } else { + break; + } + } + } + } + *p = '\0'; +} + +int ns_url_decode(const char *src, int src_len, char *dst, + int dst_len, int is_form_url_encoded) { + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%' && i < src_len - 2 && + isxdigit(* (const unsigned char *) (src + i + 1)) && + isxdigit(* (const unsigned char *) (src + i + 2))) { + a = tolower(* (const unsigned char *) (src + i + 1)); + b = tolower(* (const unsigned char *) (src + i + 2)); + dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +int ns_get_http_var(const struct ns_str *buf, const char *name, + char *dst, size_t dst_len) { + const char *p, *e, *s; + size_t name_len; + int len; + + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (buf->p == NULL || name == NULL || buf->len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = buf->p + buf->len; + len = -1; + dst[0] = '\0'; + + for (p = buf->p; p + name_len < e; p++) { + if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && + !ns_ncasecmp(name, p, name_len)) { + p += name_len + 1; + s = (const char *) memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + len = ns_url_decode(p, (size_t)(s - p), dst, dst_len, 1); + if (len == -1) { + len = -2; + } + break; + } + } + } + + return len; +} + +void ns_serve_http(struct ns_connection *nc, struct http_message *hm, + struct ns_serve_http_opts opts) { + char path[NS_MAX_PATH]; + ns_stat_t st; + + snprintf(path, sizeof(path), "%s/%.*s", opts.document_root, + (int) hm->uri.len, hm->uri.p); + remove_double_dots(path); + + if (ns_stat(path, &st) != 0) { + ns_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); + } else if (S_ISDIR(st.st_mode)) { + strncat(path, "/index.html", sizeof(path) - (strlen(path) + 1)); + if (ns_stat(path, &st) == 0) { + ns_send_http_file(nc, path, &st); + } else { + ns_printf(nc, "%s", "HTTP/1.1 403 Access Denied\r\n" + "Content-Length: 0\r\n\r\n"); + } + } else { + ns_send_http_file(nc, path, &st); + } +} +#endif /* NS_DISABLE_HTTP_WEBSOCKET */ diff --git a/external/net_skeleton/modules/http.h b/external/net_skeleton/modules/http.h new file mode 100644 index 000000000..bd5a43719 --- /dev/null +++ b/external/net_skeleton/modules/http.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_HTTP_HEADER_DEFINED +#define NS_HTTP_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define NS_MAX_HTTP_HEADERS 40 +#define NS_MAX_HTTP_REQUEST_SIZE 8192 +#define NS_MAX_PATH 1024 + +struct http_message { + struct ns_str message; /* Whole message: request line + headers + body */ + + /* HTTP Request line (or HTTP response line) */ + struct ns_str method; /* "GET" */ + struct ns_str uri; /* "/my_file.html" */ + struct ns_str proto; /* "HTTP/1.1" */ + + /* Headers */ + struct ns_str header_names[NS_MAX_HTTP_HEADERS]; + struct ns_str header_values[NS_MAX_HTTP_HEADERS]; + + /* Message body */ + struct ns_str body; /* Zero-length for requests with no body */ +}; + +struct websocket_message { + unsigned char *data; + size_t size; + unsigned char flags; +}; + +/* HTTP and websocket events. void *ev_data is described in a comment. */ +#define NS_HTTP_REQUEST 100 /* struct http_message * */ +#define NS_HTTP_REPLY 101 /* struct http_message * */ + +#define NS_WEBSOCKET_HANDSHAKE_REQUEST 111 /* NULL */ +#define NS_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ +#define NS_WEBSOCKET_FRAME 113 /* struct websocket_message * */ + +void ns_set_protocol_http_websocket(struct ns_connection *); +void ns_send_websocket_handshake(struct ns_connection *, const char *, + const char *); +void ns_send_websocket_frame(struct ns_connection *, int, const void *, size_t); +void ns_send_websocket_framev(struct ns_connection *, int, const struct ns_str *, int); + +void ns_printf_websocket_frame(struct ns_connection *, int, const char *, ...); + +/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ +#define WEBSOCKET_OP_CONTINUE 0 +#define WEBSOCKET_OP_TEXT 1 +#define WEBSOCKET_OP_BINARY 2 +#define WEBSOCKET_OP_CLOSE 8 +#define WEBSOCKET_OP_PING 9 +#define WEBSOCKET_OP_PONG 10 + +/* Utility functions */ +struct ns_str *ns_get_http_header(struct http_message *, const char *); +int ns_parse_http(const char *s, int n, struct http_message *req); +int ns_get_http_var(const struct ns_str *, const char *, char *dst, size_t); + + +struct ns_serve_http_opts { + const char *document_root; +}; +void ns_serve_http(struct ns_connection *, struct http_message *, + struct ns_serve_http_opts); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_HTTP_HEADER_DEFINED */ diff --git a/external/net_skeleton/modules/json-rpc.c b/external/net_skeleton/modules/json-rpc.c new file mode 100644 index 000000000..6455ac1b6 --- /dev/null +++ b/external/net_skeleton/modules/json-rpc.c @@ -0,0 +1,141 @@ +/* Copyright (c) 2014 Cesanta Software Limited */ +/* All rights reserved */ + +#ifndef NS_DISABLE_JSON_RPC + +#include "net_skeleton.h" +#include "json-rpc.h" + +int ns_rpc_create_reply(char *buf, int len, const struct ns_rpc_request *req, + const char *result_fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:V,s:", + "jsonrpc", "2.0", "id", + req->id == NULL ? "null" : req->id->ptr, + req->id == NULL ? 4 : req->id->len, + "result"); + va_start(ap, result_fmt); + n += json_emit_va(buf + n, len - n, result_fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}"); + + return n; +} + +int ns_rpc_create_request(char *buf, int len, const char *method, + const char *id, const char *params_fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:s,s:s,s:", + "jsonrpc", "2.0", "id", id, "method", method, "params"); + va_start(ap, params_fmt); + n += json_emit_va(buf + n, len - n, params_fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}"); + + return n; +} + +int ns_rpc_create_error(char *buf, int len, struct ns_rpc_request *req, + int code, const char *message, const char *fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:V,s:{s:i,s:s,s:", + "jsonrpc", "2.0", "id", + req->id == NULL ? "null" : req->id->ptr, + req->id == NULL ? 4 : req->id->len, + "error", "code", code, + "message", message, "data"); + va_start(ap, fmt); + n += json_emit_va(buf + n, len - n, fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}}"); + + return n; +} + +int ns_rpc_create_std_error(char *buf, int len, struct ns_rpc_request *req, + int code) { + const char *message = NULL; + + switch (code) { + case JSON_RPC_PARSE_ERROR: message = "parse error"; break; + case JSON_RPC_INVALID_REQUEST_ERROR: message = "invalid request"; break; + case JSON_RPC_METHOD_NOT_FOUND_ERROR: message = "method not found"; break; + case JSON_RPC_INVALID_PARAMS_ERROR: message = "invalid parameters"; break; + case JSON_RPC_SERVER_ERROR: message = "server error"; break; + default: message = "unspecified error"; break; + } + + return ns_rpc_create_error(buf, len, req, code, message, "N"); +} + +int ns_rpc_dispatch(const char *buf, int len, char *dst, int dst_len, + const char **methods, ns_rpc_handler_t *handlers) { + struct json_token tokens[200]; + struct ns_rpc_request req; + int i, n; + + memset(&req, 0, sizeof(req)); + n = parse_json(buf, len, tokens, sizeof(tokens) / sizeof(tokens[0])); + if (n <= 0) { + int err_code = (n == JSON_STRING_INVALID) ? + JSON_RPC_PARSE_ERROR : JSON_RPC_SERVER_ERROR; + return ns_rpc_create_std_error(dst, dst_len, &req, err_code); + } + + req.message = tokens; + req.id = find_json_token(tokens, "id"); + req.method = find_json_token(tokens, "method"); + req.params = find_json_token(tokens, "params"); + + if (req.id == NULL || req.method == NULL) { + return ns_rpc_create_std_error(dst, dst_len, &req, + JSON_RPC_INVALID_REQUEST_ERROR); + } + + for (i = 0; methods[i] != NULL; i++) { + int mlen = strlen(methods[i]); + if (mlen == req.method->len && + memcmp(methods[i], req.method->ptr, mlen) == 0) break; + } + + if (methods[i] == NULL) { + return ns_rpc_create_std_error(dst, dst_len, &req, + JSON_RPC_METHOD_NOT_FOUND_ERROR); + } + + return handlers[i](dst, dst_len, &req); +} + +int ns_rpc_parse_reply(const char *buf, int len, + struct json_token *toks, int max_toks, + struct ns_rpc_reply *rep, struct ns_rpc_error *er) { + int n = parse_json(buf, len, toks, max_toks); + + memset(rep, 0, sizeof(*rep)); + memset(er, 0, sizeof(*er)); + + if (n > 0) { + if ((rep->result = find_json_token(toks, "result")) != NULL) { + rep->message = toks; + rep->id = find_json_token(toks, "id"); + } else { + er->message = toks; + er->id = find_json_token(toks, "id"); + er->error_code = find_json_token(toks, "error.code"); + er->error_message = find_json_token(toks, "error.message"); + er->error_data = find_json_token(toks, "error.data"); + } + } + return n; +} + +#endif /* NS_DISABLE_JSON_RPC */ diff --git a/external/net_skeleton/modules/json-rpc.h b/external/net_skeleton/modules/json-rpc.h new file mode 100644 index 000000000..51d546f8d --- /dev/null +++ b/external/net_skeleton/modules/json-rpc.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_JSON_RPC_HEADER_DEFINED +#define NS_JSON_RPC_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* JSON-RPC standard error codes */ +#define JSON_RPC_PARSE_ERROR (-32700) +#define JSON_RPC_INVALID_REQUEST_ERROR (-32600) +#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601) +#define JSON_RPC_INVALID_PARAMS_ERROR (-32602) +#define JSON_RPC_INTERNAL_ERROR (-32603) +#define JSON_RPC_SERVER_ERROR (-32000) + +struct ns_rpc_request { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *method; /* Method name */ + struct json_token *params; /* Method params */ +}; + +struct ns_rpc_reply { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *result; /* Remote call result */ +}; + +struct ns_rpc_error { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *error_code; /* error.code */ + struct json_token *error_message; /* error.message */ + struct json_token *error_data; /* error.data, can be NULL */ +}; + +int ns_rpc_parse_request(const char *buf, int len, struct ns_rpc_request *req); + +int ns_rpc_parse_reply(const char *buf, int len, + struct json_token *toks, int max_toks, + struct ns_rpc_reply *, struct ns_rpc_error *); + +int ns_rpc_create_request(char *, int, const char *method, + const char *id, const char *params_fmt, ...); + +int ns_rpc_create_reply(char *, int, const struct ns_rpc_request *req, + const char *result_fmt, ...); + +int ns_rpc_create_error(char *, int, struct ns_rpc_request *req, + int, const char *, const char *, ...); + +int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code); + +typedef int (*ns_rpc_handler_t)(char *buf, int len, struct ns_rpc_request *); +int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len, + const char **methods, ns_rpc_handler_t *handlers); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_JSON_RPC_HEADER_DEFINED */ diff --git a/external/net_skeleton/modules/sha1.c b/external/net_skeleton/modules/sha1.c new file mode 100644 index 000000000..5ec9ca773 --- /dev/null +++ b/external/net_skeleton/modules/sha1.c @@ -0,0 +1,141 @@ +/* Copyright(c) By Steve Reid */ +/* 100% Public Domain */ + +#ifndef NS_DISABLE_SHA1 + +#include +#include "sha1.h" + +static int is_big_endian(void) { + static const int n = 1; + return ((char *) &n)[0] == 0; +} + +#define SHA1HANDSOFF +#if defined(__sun) +#include "solarisfixes.h" +#endif + +union char64long16 { unsigned char c[64]; uint32_t l[16]; }; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static uint32_t blk0(union char64long16 *block, int i) { + /* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */ + if (!is_big_endian()) { + block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | + (rol(block->l[i], 8) & 0x00FF00FF); + } + return block->l[i]; +} + +/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ +#undef blk +#undef R0 +#undef R1 +#undef R2 +#undef R3 +#undef R4 + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { + uint32_t a, b, c, d, e; + union char64long16 block[1]; + + memcpy(block, buffer, 64); + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Erase working structures. The order of operations is important, + * used to ensure that compiler doesn't optimize those out. */ + memset(block, 0, sizeof(block)); + a = b = c = d = e = 0; + (void) a; (void) b; (void) c; (void) d; (void) e; +} + +void SHA1Init(SHA1_CTX *context) { + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len) { + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { + unsigned i; + unsigned char finalcount[8], c; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); + } + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +#endif /* NS_DISABLE_SHA1 */ diff --git a/external/net_skeleton/modules/sha1.h b/external/net_skeleton/modules/sha1.h new file mode 100644 index 000000000..19cd49185 --- /dev/null +++ b/external/net_skeleton/modules/sha1.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if !defined(NS_SHA1_HEADER_INCLUDED) && !defined(NS_DISABLE_SHA1) +#define NS_SHA1_HEADER_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Init(SHA1_CTX *); +void SHA1Update(SHA1_CTX *, const unsigned char *data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX *); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_SHA1_HEADER_INCLUDED */ diff --git a/external/net_skeleton/modules/skeleton.c b/external/net_skeleton/modules/skeleton.c new file mode 100644 index 000000000..83c924233 --- /dev/null +++ b/external/net_skeleton/modules/skeleton.c @@ -0,0 +1,957 @@ +/* Copyright (c) 2014 Cesanta Software Limited +* All rights reserved +* +* This software is dual-licensed: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. For the terms of this +* license, see . +* +* You are free to use this software under the terms of the GNU General +* Public License, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* Alternatively, you can license this software under a commercial +* license, as set out in . +* +* $Date: 2014-09-28 05:04:41 UTC $ +*/ + +#include "net_skeleton.h" + +#ifndef NS_MALLOC +#define NS_MALLOC malloc +#endif + +#ifndef NS_REALLOC +#define NS_REALLOC realloc +#endif + +#ifndef NS_FREE +#define NS_FREE free +#endif + +#define NS_UDP_RECEIVE_BUFFER_SIZE 2000 +#define NS_VPRINTF_BUFFER_SIZE 500 + +struct ctl_msg { + ns_event_handler_t callback; + char message[1024 * 8]; +}; + +void iobuf_resize(struct iobuf *io, size_t new_size) { + char *p; + if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && + (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { + io->size = new_size; + io->buf = p; + } +} + +void iobuf_init(struct iobuf *iobuf, size_t initial_size) { + iobuf->len = iobuf->size = 0; + iobuf->buf = NULL; + iobuf_resize(iobuf, initial_size); +} + +void iobuf_free(struct iobuf *iobuf) { + if (iobuf != NULL) { + NS_FREE(iobuf->buf); + iobuf_init(iobuf, 0); + } +} + +size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) { + char *p = NULL; + + assert(io != NULL); + assert(io->len <= io->size); + + if (len <= 0) { + } else if (io->len + len <= io->size) { + memcpy(io->buf + io->len, buf, len); + io->len += len; + } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) { + io->buf = p; + memcpy(io->buf + io->len, buf, len); + io->len += len; + io->size = io->len; + } else { + len = 0; + } + + return len; +} + +void iobuf_remove(struct iobuf *io, size_t n) { + if (n > 0 && n <= io->len) { + memmove(io->buf, io->buf + n, io->len - n); + io->len -= n; + } +} + +static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { + if (nc->flags & NSF_UDP) { + long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); + return n < 0 ? 0 : n; + } else { + return iobuf_append(&nc->send_iobuf, buf, len); + } +} + +#ifndef NS_DISABLE_THREADS +void *ns_start_thread(void *(*f)(void *), void *p) { +#ifdef _WIN32 + return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p); +#else + pthread_t thread_id = (pthread_t) 0; + pthread_attr_t attr; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1 + (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE); +#endif + + pthread_create(&thread_id, &attr, f, p); + pthread_attr_destroy(&attr); + + return (void *) thread_id; +#endif +} +#endif /* NS_DISABLE_THREADS */ + +static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) { + c->next = mgr->active_connections; + mgr->active_connections = c; + c->prev = NULL; + if (c->next != NULL) c->next->prev = c; +} + +static void ns_remove_conn(struct ns_connection *conn) { + if (conn->prev == NULL) conn->mgr->active_connections = conn->next; + if (conn->prev) conn->prev->next = conn->next; + if (conn->next) conn->next->prev = conn->prev; +} + +/* Print message to buffer. If buffer is large enough to hold the message, + * return buffer. If buffer is to small, allocate large enough buffer on heap, + * and return allocated buffer. */ +int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { + va_list ap_copy; + int len; + + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + + if (len < 0) { + /* eCos and Windows are not standard-compliant and return -1 when + * the buffer is too small. Keep allocating larger buffers until we + * succeed or out of memory. */ + *buf = NULL; + while (len < 0) { + NS_FREE(*buf); + size *= 2; + if ((*buf = (char *) NS_MALLOC(size)) == NULL) break; + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + } + } else if (len > (int) size) { + /* Standard-compliant code path. Allocate a buffer that is large enough. */ + if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) { + len = -1; + } else { + va_copy(ap_copy, ap); + len = vsnprintf(*buf, len + 1, fmt, ap_copy); + va_end(ap_copy); + } + } + + return len; +} + +int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) { + char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + + if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + ns_out(nc, buf, len); + } + if (buf != mem && buf != NULL) { + NS_FREE(buf); + } + + return len; +} + +int ns_printf(struct ns_connection *conn, const char *fmt, ...) { + int len; + va_list ap; + va_start(ap, fmt); + len = ns_vprintf(conn, fmt, ap); + va_end(ap); + return len; +} + +static void hexdump(struct ns_connection *nc, const char *path, + int num_bytes, int ev) { + const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; + FILE *fp; + char *buf, src[60], dst[60]; + int buf_size = num_bytes * 5 + 100; + + if ((fp = fopen(path, "a")) != NULL) { + ns_sock_to_str(nc->sock, src, sizeof(src), 3); + ns_sock_to_str(nc->sock, dst, sizeof(dst), 7); + fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL), + nc->user_data, src, + ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : + ev == NS_ACCEPT ? "" : "XX", + dst, num_bytes); + if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) { + ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - + (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); + fprintf(fp, "%s", buf); + NS_FREE(buf); + } + fclose(fp); + } +} + +static void ns_call(struct ns_connection *nc, int ev, void *ev_data) { + if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { + int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) ev_data : 0; + hexdump(nc, nc->mgr->hexdump_file, len, ev); + } + + /* + * If protocol handler is specified, call it. Otherwise, call user-specified + * event handler. + */ + (nc->proto_handler ? nc->proto_handler : nc->handler)(nc, ev, ev_data); +} + +static void ns_destroy_conn(struct ns_connection *conn) { + closesocket(conn->sock); + iobuf_free(&conn->recv_iobuf); + iobuf_free(&conn->send_iobuf); +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + SSL_free(conn->ssl); + } + if (conn->ssl_ctx != NULL) { + SSL_CTX_free(conn->ssl_ctx); + } +#endif + NS_FREE(conn); +} + +static void ns_close_conn(struct ns_connection *conn) { + DBG(("%p %d", conn, conn->flags)); + ns_call(conn, NS_CLOSE, NULL); + ns_remove_conn(conn); + ns_destroy_conn(conn); +} + +void ns_set_close_on_exec(sock_t sock) { +#ifdef _WIN32 + (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); +#else + fcntl(sock, F_SETFD, FD_CLOEXEC); +#endif +} + +static void ns_set_non_blocking_mode(sock_t sock) { +#ifdef _WIN32 + unsigned long on = 1; + ioctlsocket(sock, FIONBIO, &on); +#else + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif +} + +#ifndef NS_DISABLE_SOCKETPAIR +int ns_socketpair2(sock_t sp[2], int sock_type) { + union socket_address sa; + sock_t sock; + socklen_t len = sizeof(sa.sin); + int ret = 0; + + sock = sp[0] = sp[1] = INVALID_SOCKET; + + (void) memset(&sa, 0, sizeof(sa)); + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(0); + sa.sin.sin_addr.s_addr = htonl(0x7f000001); + + if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (bind(sock, &sa.sa, len) != 0) { + } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { + } else if (getsockname(sock, &sa.sa, &len) != 0) { + } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (connect(sp[0], &sa.sa, len) != 0) { + } else if (sock_type == SOCK_DGRAM && + (getsockname(sp[0], &sa.sa, &len) != 0 || + connect(sock, &sa.sa, len) != 0)) { + } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : + accept(sock, &sa.sa, &len))) == INVALID_SOCKET) { + } else { + ns_set_close_on_exec(sp[0]); + ns_set_close_on_exec(sp[1]); + if (sock_type == SOCK_STREAM) closesocket(sock); + ret = 1; + } + + if (!ret) { + if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); + if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); + if (sock != INVALID_SOCKET) closesocket(sock); + sock = sp[0] = sp[1] = INVALID_SOCKET; + } + + return ret; +} + +int ns_socketpair(sock_t sp[2]) { + return ns_socketpair2(sp, SOCK_STREAM); +} +#endif /* NS_DISABLE_SOCKETPAIR */ + +/* TODO(lsm): use non-blocking resolver */ +static int ns_resolve2(const char *host, struct in_addr *ina) { + struct hostent *he; + if ((he = gethostbyname(host)) == NULL) { + DBG(("gethostbyname(%s) failed: %s", host, strerror(errno))); + } else { + memcpy(ina, he->h_addr_list[0], sizeof(*ina)); + return 1; + } + return 0; +} + +/* Resolve FDQN "host", store IP address in the "ip". + * Return > 0 (IP address length) on success. */ +int ns_resolve(const char *host, char *buf, size_t n) { + struct in_addr ad; + return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; +} + +/* Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT] */ +static int ns_parse_address(const char *str, union socket_address *sa, int *p) { + unsigned int a, b, c, d, port = 0; + int len = 0; + char host[200]; +#ifdef NS_ENABLE_IPV6 + char buf[100]; +#endif + + /* MacOS needs that. If we do not zero it, subsequent bind() will fail. */ + /* Also, all-zeroes in the socket address means binding to all addresses */ + /* for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */ + memset(sa, 0, sizeof(*sa)); + sa->sin.sin_family = AF_INET; + + *p = SOCK_STREAM; + + if (memcmp(str, "udp://", 6) == 0) { + str += 6; + *p = SOCK_DGRAM; + } else if (memcmp(str, "tcp://", 6) == 0) { + str += 6; + } + + if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { + /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ + sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); + sa->sin.sin_port = htons((uint16_t) port); +#ifdef NS_ENABLE_IPV6 + } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && + inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { + /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ + sa->sin6.sin6_family = AF_INET6; + sa->sin6.sin6_port = htons((uint16_t) port); +#endif + } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) { + sa->sin.sin_port = htons((uint16_t) port); + ns_resolve2(host, &sa->sin.sin_addr); + } else if (sscanf(str, "%u%n", &port, &len) == 1) { + /* If only port is specified, bind to IPv4, INADDR_ANY */ + sa->sin.sin_port = htons((uint16_t) port); + } + + return port < 0xffff && str[len] == '\0' ? len : 0; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t ns_open_listening_socket(union socket_address *sa, int proto) { + socklen_t sa_len = (sa->sa.sa_family == AF_INET) ? + sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = INVALID_SOCKET; + int on = 1; + + if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET && + +#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) + /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx */ + !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (void *) &on, sizeof(on)) && +#endif + +#if 1 || !defined(_WIN32) || defined(SO_EXCLUSIVEADDRUSE) + /* + * SO_RESUSEADDR is not enabled on Windows because the semantics of + * SO_REUSEADDR on UNIX and Windows is different. On Windows, + * SO_REUSEADDR allows to bind a socket to a port without error even if + * the port is already open by another program. This is not the behavior + * SO_REUSEADDR was designed for, and leads to hard-to-track failure + * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + * SO_EXCLUSIVEADDRUSE is supported and set on a socket. + */ + !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && +#endif + + !bind(sock, &sa->sa, sa_len) && + (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { + ns_set_non_blocking_mode(sock); + /* In case port was set to 0, get the real port number */ + (void) getsockname(sock, &sa->sa, &sa_len); + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + + return sock; +} + +#ifdef NS_ENABLE_SSL +/* Certificate generation script is at */ +/* https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh */ + +static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) { + if (ctx == NULL) { + return -1; + } else if (cert == NULL || cert[0] == '\0') { + return 0; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2; +} + +static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { + if (ctx == NULL) { + return -1; + } else if (pem_file == NULL || pem_file[0] == '\0') { + return 0; + } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 || + SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) { + return -2; + } else { + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_use_certificate_chain_file(ctx, pem_file); + return 0; + } +} + +const char *ns_set_ssl(struct ns_connection *nc, const char *cert, + const char *ca_cert) { + const char *result = NULL; + + if ((nc->flags & NSF_LISTENING) && + (nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { + result = "SSL_CTX_new() failed"; + } else if (!(nc->flags & NSF_LISTENING) && + (nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + result = "SSL_CTX_new() failed"; + } else if (ns_use_cert(nc->ssl_ctx, cert) != 0) { + result = "Invalid ssl cert"; + } else if (ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) { + result = "Invalid CA cert"; + } else if (!(nc->flags & NSF_LISTENING) && + (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) { + result = "SSL_new() failed"; + } else if (!(nc->flags & NSF_LISTENING)) { + SSL_set_fd(nc->ssl, nc->sock); + } + return result; +} + +static int ns_ssl_err(struct ns_connection *conn, int res) { + int ssl_err = SSL_get_error(conn->ssl, res); + if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ; + if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE; + return ssl_err; +} +#endif /* NS_ENABLE_SSL */ + +struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, + ns_event_handler_t callback) { + union socket_address sa; + struct ns_connection *nc = NULL; + int proto; + sock_t sock; + + ns_parse_address(str, &sa, &proto); + if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { + DBG(("Failed to open listener: %d", errno)); + } else if ((nc = ns_add_sock(srv, sock, callback)) == NULL) { + DBG(("Failed to ns_add_sock")); + closesocket(sock); + } else { + nc->sa = sa; + nc->flags |= NSF_LISTENING; + nc->handler = callback; + + if (proto == SOCK_DGRAM) { + nc->flags |= NSF_UDP; + } + + DBG(("%p sock %d/%d", nc, sock, proto)); + } + + return nc; +} + +static struct ns_connection *accept_conn(struct ns_connection *ls) { + struct ns_connection *c = NULL; + union socket_address sa; + socklen_t len = sizeof(sa); + sock_t sock = INVALID_SOCKET; + + /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ + if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { + } else if ((c = ns_add_sock(ls->mgr, sock, ls->handler)) == NULL) { + closesocket(sock); +#ifdef NS_ENABLE_SSL + } else if (ls->ssl_ctx != NULL && + ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL || + SSL_set_fd(c->ssl, sock) != 1)) { + DBG(("SSL error")); + ns_close_conn(c); + c = NULL; +#endif + } else { + c->listener = ls; + c->proto_data = ls->proto_data; + c->proto_handler = ls->proto_handler; + c->user_data = ls->user_data; + ns_call(c, NS_ACCEPT, &sa); + DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); + } + + return c; +} + +static int ns_is_error(int n) { + return n == 0 || + (n < 0 && errno != EINTR && errno != EINPROGRESS && + errno != EAGAIN && errno != EWOULDBLOCK +#ifdef _WIN32 + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK +#endif + ); +} + +void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { + union socket_address sa; + socklen_t slen = sizeof(sa); + + if (buf != NULL && len > 0) { + buf[0] = '\0'; + memset(&sa, 0, sizeof(sa)); + if (flags & 4) { + getpeername(sock, &sa.sa, &slen); + } else { + getsockname(sock, &sa.sa, &slen); + } + if (flags & 1) { +#if defined(NS_ENABLE_IPV6) + inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ? + (void *) &sa.sin.sin_addr : + (void *) &sa.sin6.sin6_addr, buf, len); +#elif defined(_WIN32) + /* Only Windoze Vista (and newer) have inet_ntop() */ + strncpy(buf, inet_ntoa(sa.sin.sin_addr), len); +#else + inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len); +#endif + } + if (flags & 2) { + snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d", + flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port)); + } + } +} + +int ns_hexdump(const void *buf, int len, char *dst, int dst_len) { + const unsigned char *p = (const unsigned char *) buf; + char ascii[17] = ""; + int i, idx, n = 0; + + for (i = 0; i < len; i++) { + idx = i % 16; + if (idx == 0) { + if (i > 0) n += snprintf(dst + n, dst_len - n, " %s\n", ascii); + n += snprintf(dst + n, dst_len - n, "%04x ", i); + } + n += snprintf(dst + n, dst_len - n, " %02x", p[i]); + ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; + ascii[idx + 1] = '\0'; + } + + while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", " "); + n += snprintf(dst + n, dst_len - n, " %s\n\n", ascii); + + return n; +} + +static void ns_read_from_socket(struct ns_connection *conn) { + char buf[2048]; + int n = 0; + + if (conn->flags & NSF_CONNECTING) { + int ok = 1, ret; + socklen_t len = sizeof(ok); + + ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); +#ifdef NS_ENABLE_SSL + if (ret == 0 && ok == 0 && conn->ssl != NULL) { + int res = SSL_connect(conn->ssl); + int ssl_err = ns_ssl_err(conn, res); + if (res == 1) { + conn->flags |= NSF_SSL_HANDSHAKE_DONE; + } else if (ssl_err == SSL_ERROR_WANT_READ || + ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + ok = 1; + } + } +#endif + conn->flags &= ~NSF_CONNECTING; + DBG(("%p ok=%d", conn, ok)); + if (ok != 0) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + ns_call(conn, NS_CONNECT, &ok); + return; + } + +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { + /* SSL library may have more bytes ready to read then we ask to read. + * Therefore, read in a loop until we read everything. Without the loop, + * we skip to the next select() cycle which can just timeout. */ + while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { + DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n)); + iobuf_append(&conn->recv_iobuf, buf, n); + ns_call(conn, NS_RECV, &n); + } + ns_ssl_err(conn, n); + } else { + int res = SSL_accept(conn->ssl); + int ssl_err = ns_ssl_err(conn, res); + if (res == 1) { + conn->flags |= NSF_SSL_HANDSHAKE_DONE; + } else if (ssl_err == SSL_ERROR_WANT_READ || + ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + return; + } + } else +#endif + { + while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) { + DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n)); + iobuf_append(&conn->recv_iobuf, buf, n); + ns_call(conn, NS_RECV, &n); + } + } + + if (ns_is_error(n)) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } +} + +static void ns_write_to_socket(struct ns_connection *conn) { + struct iobuf *io = &conn->send_iobuf; + int n = 0; + +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + n = SSL_write(conn->ssl, io->buf, io->len); + if (n <= 0) { + int ssl_err = ns_ssl_err(conn, n); + if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + } + } else +#endif + { n = (int) send(conn->sock, io->buf, io->len, 0); } + + DBG(("%p %d -> %d bytes", conn, conn->flags, n)); + + ns_call(conn, NS_SEND, &n); + if (ns_is_error(n)) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } else if (n > 0) { + iobuf_remove(io, n); + } +} + +int ns_send(struct ns_connection *conn, const void *buf, int len) { + return (int) ns_out(conn, buf, len); +} + +static void ns_handle_udp(struct ns_connection *ls) { + struct ns_connection nc; + char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; + int n; + socklen_t s_len = sizeof(nc.sa); + + memset(&nc, 0, sizeof(nc)); + n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); + if (n <= 0) { + DBG(("%p recvfrom: %s", ls, strerror(errno))); + } else { + nc.mgr = ls->mgr; + nc.recv_iobuf.buf = buf; + nc.recv_iobuf.len = nc.recv_iobuf.size = n; + nc.sock = ls->sock; + nc.handler = ls->handler; + nc.user_data = ls->user_data; + nc.proto_data = ls->proto_data; + nc.proto_handler = ls->proto_handler; + nc.mgr = ls->mgr; + nc.listener = ls; + nc.flags = NSF_UDP; + DBG(("%p %d bytes received", ls, n)); + ns_call(&nc, NS_RECV, &n); + } +} + +static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { + if (sock != INVALID_SOCKET) { + FD_SET(sock, set); + if (*max_fd == INVALID_SOCKET || sock > *max_fd) { + *max_fd = sock; + } + } +} + +time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { + struct ns_connection *nc, *tmp; + struct timeval tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + time_t current_time = time(NULL); + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); + ns_add_to_set(mgr->ctl[1], &read_set, &max_fd); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if (!(nc->flags & (NSF_LISTENING | NSF_CONNECTING))) { + ns_call(nc, NS_POLL, ¤t_time); + } + if (!(nc->flags & NSF_WANT_WRITE)) { + /*DBG(("%p read_set", nc)); */ + ns_add_to_set(nc->sock, &read_set, &max_fd); + } + if (((nc->flags & NSF_CONNECTING) && !(nc->flags & NSF_WANT_READ)) || + (nc->send_iobuf.len > 0 && !(nc->flags & NSF_CONNECTING) && + !(nc->flags & NSF_BUFFER_BUT_DONT_SEND))) { + /*DBG(("%p write_set", nc)); */ + ns_add_to_set(nc->sock, &write_set, &max_fd); + ns_add_to_set(nc->sock, &err_set, &max_fd); + } + if (nc->flags & NSF_CLOSE_IMMEDIATELY) { + ns_close_conn(nc); + } + } + + tv.tv_sec = milli / 1000; + tv.tv_usec = (milli % 1000) * 1000; + + if (select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv) > 0) { + /* select() might have been waiting for a long time, reset current_time + * now to prevent last_io_time being set to the past. */ + current_time = time(NULL); + + /* Read wakeup messages */ + if (mgr->ctl[1] != INVALID_SOCKET && + FD_ISSET(mgr->ctl[1], &read_set)) { + struct ctl_msg ctl_msg; + int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); + send(mgr->ctl[1], ctl_msg.message, 1, 0); + if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { + struct ns_connection *c; + for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) { + ctl_msg.callback(c, NS_POLL, ctl_msg.message); + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + + /* Windows reports failed connect() requests in err_set */ + if (FD_ISSET(nc->sock, &err_set) && (nc->flags & NSF_CONNECTING)) { + nc->last_io_time = current_time; + ns_read_from_socket(nc); + } + + if (FD_ISSET(nc->sock, &read_set)) { + nc->last_io_time = current_time; + if (nc->flags & NSF_LISTENING) { + if (nc->flags & NSF_UDP) { + ns_handle_udp(nc); + } else { + /* We're not looping here, and accepting just one connection at + * a time. The reason is that eCos does not respect non-blocking + * flag on a listening socket and hangs in a loop. */ + accept_conn(nc); + } + } else { + ns_read_from_socket(nc); + } + } + + if (FD_ISSET(nc->sock, &write_set)) { + nc->last_io_time = current_time; + if (nc->flags & NSF_CONNECTING) { + ns_read_from_socket(nc); + } else if (!(nc->flags & NSF_BUFFER_BUT_DONT_SEND)) { + ns_write_to_socket(nc); + } + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & NSF_CLOSE_IMMEDIATELY) || + (nc->send_iobuf.len == 0 && + (nc->flags & NSF_FINISHED_SENDING_DATA))) { + ns_close_conn(nc); + } + } + + return current_time; +} + +struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, + ns_event_handler_t callback) { + sock_t sock = INVALID_SOCKET; + struct ns_connection *nc = NULL; + union socket_address sa; + int rc, proto; + + ns_parse_address(address, &sa, &proto); + if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { + return NULL; + } + ns_set_non_blocking_mode(sock); + rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); + + if (rc != 0 && ns_is_error(rc)) { + closesocket(sock); + return NULL; + } else if ((nc = ns_add_sock(mgr, sock, callback)) == NULL) { + closesocket(sock); + return NULL; + } + + nc->sa = sa; /* Important, cause UDP conns will use sendto() */ + nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; + + return nc; +} + +struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, + ns_event_handler_t callback) { + struct ns_connection *conn; + if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { + memset(conn, 0, sizeof(*conn)); + ns_set_non_blocking_mode(sock); + ns_set_close_on_exec(sock); + conn->sock = sock; + conn->handler = callback; + conn->mgr = s; + conn->last_io_time = time(NULL); + ns_add_conn(s, conn); + DBG(("%p %d", conn, sock)); + } + return conn; +} + +struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) { + return conn == NULL ? s->active_connections : conn->next; +} + +void ns_broadcast(struct ns_mgr *mgr, ns_event_handler_t cb,void *data, size_t len) { + struct ctl_msg ctl_msg; + if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && + len < sizeof(ctl_msg.message)) { + ctl_msg.callback = cb; + memcpy(ctl_msg.message, data, len); + send(mgr->ctl[0], (char *) &ctl_msg, + offsetof(struct ctl_msg, message) + len, 0); + recv(mgr->ctl[0], (char *) &len, 1, 0); + } +} + +void ns_mgr_init(struct ns_mgr *s, void *user_data) { + memset(s, 0, sizeof(*s)); + s->ctl[0] = s->ctl[1] = INVALID_SOCKET; + s->user_data = user_data; + +#ifdef _WIN32 + { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } +#else + /* Ignore SIGPIPE signal, so if client cancels the request, it + * won't kill the whole process. */ + signal(SIGPIPE, SIG_IGN); +#endif + +#ifndef NS_DISABLE_SOCKETPAIR + do { + ns_socketpair2(s->ctl, SOCK_DGRAM); + } while (s->ctl[0] == INVALID_SOCKET); +#endif + +#ifdef NS_ENABLE_SSL + {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }} +#endif +} + +void ns_mgr_free(struct ns_mgr *s) { + struct ns_connection *conn, *tmp_conn; + + DBG(("%p", s)); + if (s == NULL) return; + /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ + ns_mgr_poll(s, 0); + + if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]); + if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]); + s->ctl[0] = s->ctl[1] = INVALID_SOCKET; + + for (conn = s->active_connections; conn != NULL; conn = tmp_conn) { + tmp_conn = conn->next; + ns_close_conn(conn); + } +} diff --git a/external/net_skeleton/modules/skeleton.h b/external/net_skeleton/modules/skeleton.h new file mode 100644 index 000000000..2e0972017 --- /dev/null +++ b/external/net_skeleton/modules/skeleton.h @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef NS_SKELETON_HEADER_INCLUDED +#define NS_SKELETON_HEADER_INCLUDED + +#define NS_SKELETON_VERSION "2.2.0" + +#undef UNICODE /* Use ANSI WinAPI functions */ +#undef _UNICODE /* Use multibyte encoding on Windows */ +#define _MBCS /* Use multibyte encoding on Windows */ +#define _INTEGRAL_MAX_BITS 64 /* Enable _stati64() on Windows */ +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ +#undef WIN32_LEAN_AND_MEAN /* Let windows.h always include winsock2.h */ +#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ +#define __STDC_FORMAT_MACROS /* wants this for C++ */ +#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE /* Enable fseeko() and ftello() functions */ +#endif +#define _FILE_OFFSET_BITS 64 /* Enable 64-bit file offsets */ + +#ifdef _MSC_VER +#pragma warning (disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning (disable : 4204) /* missing c99 support */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x,y) (x) = (y) +#endif +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#endif +#include +#include +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define sleep(x) Sleep((x) * 1000) +#define to64(x) _atoi64(x) +typedef int socklen_t; +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +typedef SOCKET sock_t; +#ifdef __MINGW32__ +typedef struct stat ns_stat_t; +#else +typedef struct _stati64 ns_stat_t; +#endif +#ifndef S_ISDIR +#define S_ISDIR(x) ((x) & _S_IFDIR) +#endif +#else /* not _WIN32 */ +#include +#include +#include +#include +#include +#include +#include /* For inet_pton() when NS_ENABLE_IPV6 is defined */ +#include +#include +#include +#define closesocket(x) close(x) +#define __cdecl +#define INVALID_SOCKET (-1) +#ifdef __APPLE__ +int64_t strtoll(const char * str, char ** endptr, int base); +#endif +#define to64(x) strtoll(x, NULL, 10) +typedef int sock_t; +typedef struct stat ns_stat_t; +#endif /* _WIN32 */ + +#ifdef NS_ENABLE_DEBUG +#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ + fflush(stdout); } while(0) +#else +#define DBG(x) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +#ifdef NS_ENABLE_SSL +#ifdef __APPLE__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +#include +#else +typedef void *SSL; +typedef void *SSL_CTX; +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +union socket_address { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef NS_ENABLE_IPV6 + struct sockaddr_in6 sin6; +#else + struct sockaddr sin6; +#endif +}; + +/* Describes chunk of memory */ +struct ns_str { + const char *p; + size_t len; +}; + +/* IO buffers interface */ +struct iobuf { + char *buf; + size_t len; + size_t size; +}; + +void iobuf_init(struct iobuf *, size_t initial_size); +void iobuf_free(struct iobuf *); +size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); +void iobuf_remove(struct iobuf *, size_t data_size); +void iobuf_resize(struct iobuf *, size_t new_size); + +/* Callback function (event handler) prototype, must be defined by user. */ +/* Net skeleton will call event handler, passing events defined above. */ +struct ns_connection; +typedef void (*ns_event_handler_t)(struct ns_connection *, int ev, void *); + +/* Events. Meaning of event parameter (evp) is given in the comment. */ +#define NS_POLL 0 /* Sent to each connection on each call to ns_mgr_poll() */ +#define NS_ACCEPT 1 /* New connection accept()-ed. union socket_address *addr */ +#define NS_CONNECT 2 /* connect() succeeded or failed. int *success_status */ +#define NS_RECV 3 /* Data has benn received. int *num_bytes */ +#define NS_SEND 4 /* Data has been written to a socket. int *num_bytes */ +#define NS_CLOSE 5 /* Connection is closed. NULL */ + +struct ns_mgr { + struct ns_connection *active_connections; + const char *hexdump_file; /* Debug hexdump file path */ + sock_t ctl[2]; /* Socketpair for mg_wakeup() */ + void *user_data; /* User data */ +}; + +struct ns_connection { + struct ns_connection *next, *prev; /* ns_mgr::active_connections linkage */ + struct ns_connection *listener; /* Set only for accept()-ed connections */ + struct ns_mgr *mgr; + + sock_t sock; /* Socket */ + union socket_address sa; /* Peer address */ + struct iobuf recv_iobuf; /* Received data */ + struct iobuf send_iobuf; /* Data scheduled for sending */ + SSL *ssl; + SSL_CTX *ssl_ctx; + time_t last_io_time; /* Timestamp of the last socket IO */ + ns_event_handler_t proto_handler; /* Protocol-specific event handler */ + void *proto_data; /* Protocol-specific data */ + ns_event_handler_t handler; /* Event handler function */ + void *user_data; /* User-specific data */ + + unsigned long flags; +#define NSF_FINISHED_SENDING_DATA (1 << 0) +#define NSF_BUFFER_BUT_DONT_SEND (1 << 1) +#define NSF_SSL_HANDSHAKE_DONE (1 << 2) +#define NSF_CONNECTING (1 << 3) +#define NSF_CLOSE_IMMEDIATELY (1 << 4) +#define NSF_WANT_READ (1 << 5) +#define NSF_WANT_WRITE (1 << 6) /* NOTE(lsm): proto-specific */ +#define NSF_LISTENING (1 << 7) /* NOTE(lsm): proto-specific */ +#define NSF_UDP (1 << 8) +#define NSF_IS_WEBSOCKET (1 << 9) /* NOTE(lsm): proto-specific */ +#define NSF_WEBSOCKET_NO_DEFRAG (1 << 10) /* NOTE(lsm): proto-specific */ + +#define NSF_USER_1 (1 << 20) +#define NSF_USER_2 (1 << 21) +#define NSF_USER_3 (1 << 22) +#define NSF_USER_4 (1 << 23) +#define NSF_USER_5 (1 << 24) +#define NSF_USER_6 (1 << 25) +}; + +void ns_mgr_init(struct ns_mgr *, void *user_data); +void ns_mgr_free(struct ns_mgr *); +time_t ns_mgr_poll(struct ns_mgr *, int milli); +void ns_broadcast(struct ns_mgr *, ns_event_handler_t, void *, size_t); + +struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); +struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, ns_event_handler_t); +struct ns_connection *ns_bind(struct ns_mgr *, const char *, ns_event_handler_t); +struct ns_connection *ns_connect(struct ns_mgr *, const char *, ns_event_handler_t); +const char *ns_set_ssl(struct ns_connection *nc, const char *, const char *); + +int ns_send(struct ns_connection *, const void *buf, int len); +int ns_printf(struct ns_connection *, const char *fmt, ...); +int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); + +/* Utility functions */ +void *ns_start_thread(void *(*f)(void *), void *p); +int ns_socketpair(sock_t [2]); +int ns_socketpair2(sock_t [2], int sock_type); /* SOCK_STREAM or SOCK_DGRAM */ +void ns_set_close_on_exec(sock_t); +void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); +int ns_hexdump(const void *buf, int len, char *dst, int dst_len); +int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); +int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NS_SKELETON_HEADER_INCLUDED */ diff --git a/external/net_skeleton/modules/util.c b/external/net_skeleton/modules/util.c new file mode 100644 index 000000000..0e434a7bd --- /dev/null +++ b/external/net_skeleton/modules/util.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#include "net_skeleton.h" +#include "util.h" + +const char *ns_skip(const char *s, const char *end, + const char *delims, struct ns_str *v) { + v->p = s; + while (s < end && strchr(delims, * (unsigned char *) s) == NULL) s++; + v->len = s - v->p; + while (s < end && strchr(delims, * (unsigned char *) s) != NULL) s++; + return s; +} + +static int lowercase(const char *s) { + return tolower(* (const unsigned char *) s); +} + +int ns_ncasecmp(const char *s1, const char *s2, size_t len) { + int diff = 0; + + if (len > 0) + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + + return diff; +} + +int ns_vcasecmp(const struct ns_str *str2, const char *str1) { + size_t n1 = strlen(str1), n2 = str2->len; + return n1 == n2 ? ns_ncasecmp(str1, str2->p, n1) : n1 > n2 ? 1 : -1; +} + +int ns_vcmp(const struct ns_str *str2, const char *str1) { + size_t n1 = strlen(str1), n2 = str2->len; + return n1 == n2 ? memcmp(str1, str2->p, n2) : n1 > n2 ? 1 : -1; +} + +#ifdef _WIN32 +static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { + char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; + + strncpy(buf, path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + /* Trim trailing slashes. Leave backslash for paths like "X:\" */ + p = buf + strlen(buf) - 1; + while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; + + /* + * Convert to Unicode and back. If doubly-converted string does not + * match the original, something is fishy, reject. + */ + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); + WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } +} +#endif /* _WIN32 */ + +int ns_stat(const char *path, ns_stat_t *st) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); + return _wstati64(wpath, st); +#else + return stat(path, st); +#endif +} + +FILE *ns_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE], wmode[10]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + to_wchar(mode, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wpath, wmode); +#else + return fopen(path, mode); +#endif +} + +int ns_open(const char *path, int flag, int mode) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + return _wopen(wpath, flag, mode); +#else + return open(path, flag, mode); +#endif +} + +void ns_base64_encode(const unsigned char *src, int src_len, char *dst) { + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +/* Convert one byte of encoded base64 input stream to 6-bit chunk */ +static unsigned char from_b64(unsigned char ch) { + /* Inverse lookup map */ + static const unsigned char tab[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, /* 0 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 8 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 16 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 24 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 32 */ + 255, 255, 255, 62, 255, 255, 255, 63, /* 40 */ + 52, 53, 54, 55, 56, 57, 58, 59, /* 48 */ + 60, 61, 255, 255, 255, 200, 255, 255, /* 56 '=' is 200, on index 61 */ + 255, 0, 1, 2, 3, 4, 5, 6, /* 64 */ + 7, 8, 9, 10, 11, 12, 13, 14, /* 72 */ + 15, 16, 17, 18, 19, 20, 21, 22, /* 80 */ + 23, 24, 25, 255, 255, 255, 255, 255, /* 88 */ + 255, 26, 27, 28, 29, 30, 31, 32, /* 96 */ + 33, 34, 35, 36, 37, 38, 39, 40, /* 104 */ + 41, 42, 43, 44, 45, 46, 47, 48, /* 112 */ + 49, 50, 51, 255, 255, 255, 255, 255, /* 120 */ + }; + return tab[ch & 127]; +} + +void ns_base64_decode(const unsigned char *s, int len, char *dst) { + unsigned char a, b, c, d; + while (len >= 4 && + (a = from_b64(s[0])) != 255 && + (b = from_b64(s[1])) != 255 && + (c = from_b64(s[2])) != 255 && + (d = from_b64(s[3])) != 255) { + if (a == 200 || b == 200) break; /* '=' can't be there */ + *dst++ = a << 2 | b >> 4; + if (c == 200) break; + *dst++ = b << 4 | c >> 2; + if (d == 200) break; + *dst++ = c << 6 | d; + s += 4; + len -=4; + } + *dst = 0; +} diff --git a/external/net_skeleton/modules/util.h b/external/net_skeleton/modules/util.h new file mode 100644 index 000000000..11246e007 --- /dev/null +++ b/external/net_skeleton/modules/util.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_UTIL_HEADER_DEFINED +#define NS_UTIL_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MAX_PATH_SIZE +#define MAX_PATH_SIZE 500 +#endif + +const char *ns_skip(const char *, const char *, const char *, struct ns_str *); +int ns_ncasecmp(const char *s1, const char *s2, size_t len); +int ns_vcmp(const struct ns_str *str2, const char *str1); +int ns_vcasecmp(const struct ns_str *str2, const char *str1); +void ns_base64_decode(const unsigned char *s, int len, char *dst); +void ns_base64_encode(const unsigned char *src, int src_len, char *dst); +int ns_stat(const char *path, ns_stat_t *st); +FILE *ns_fopen(const char *path, const char *mode); +int ns_open(const char *path, int flag, int mode); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_UTIL_HEADER_DEFINED */ diff --git a/external/net_skeleton/net_skeleton.c b/external/net_skeleton/net_skeleton.c new file mode 100644 index 000000000..bc7c5c8b4 --- /dev/null +++ b/external/net_skeleton/net_skeleton.c @@ -0,0 +1,2376 @@ +#include "net_skeleton.h" +/* Copyright (c) 2014 Cesanta Software Limited +* All rights reserved +* +* This software is dual-licensed: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. For the terms of this +* license, see . +* +* You are free to use this software under the terms of the GNU General +* Public License, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* See the GNU General Public License for more details. +* +* Alternatively, you can license this software under a commercial +* license, as set out in . +* +* $Date: 2014-09-28 05:04:41 UTC $ +*/ + + +#ifndef NS_MALLOC +#define NS_MALLOC malloc +#endif + +#ifndef NS_REALLOC +#define NS_REALLOC realloc +#endif + +#ifndef NS_FREE +#define NS_FREE free +#endif + +#define NS_UDP_RECEIVE_BUFFER_SIZE 2000 +#define NS_VPRINTF_BUFFER_SIZE 500 + +struct ctl_msg { + ns_event_handler_t callback; + char message[1024 * 8]; +}; + +void iobuf_resize(struct iobuf *io, size_t new_size) { + char *p; + if ((new_size > io->size || (new_size < io->size && new_size >= io->len)) && + (p = (char *) NS_REALLOC(io->buf, new_size)) != NULL) { + io->size = new_size; + io->buf = p; + } +} + +void iobuf_init(struct iobuf *iobuf, size_t initial_size) { + iobuf->len = iobuf->size = 0; + iobuf->buf = NULL; + iobuf_resize(iobuf, initial_size); +} + +void iobuf_free(struct iobuf *iobuf) { + if (iobuf != NULL) { + NS_FREE(iobuf->buf); + iobuf_init(iobuf, 0); + } +} + +size_t iobuf_append(struct iobuf *io, const void *buf, size_t len) { + char *p = NULL; + + assert(io != NULL); + assert(io->len <= io->size); + + if (len <= 0) { + } else if (io->len + len <= io->size) { + memcpy(io->buf + io->len, buf, len); + io->len += len; + } else if ((p = (char *) NS_REALLOC(io->buf, io->len + len)) != NULL) { + io->buf = p; + memcpy(io->buf + io->len, buf, len); + io->len += len; + io->size = io->len; + } else { + len = 0; + } + + return len; +} + +void iobuf_remove(struct iobuf *io, size_t n) { + if (n > 0 && n <= io->len) { + memmove(io->buf, io->buf + n, io->len - n); + io->len -= n; + } +} + +static size_t ns_out(struct ns_connection *nc, const void *buf, size_t len) { + if (nc->flags & NSF_UDP) { + long n = sendto(nc->sock, buf, len, 0, &nc->sa.sa, sizeof(nc->sa.sin)); + DBG(("%p %d send %ld (%d %s)", nc, nc->sock, n, errno, strerror(errno))); + return n < 0 ? 0 : n; + } else { + return iobuf_append(&nc->send_iobuf, buf, len); + } +} + +#ifndef NS_DISABLE_THREADS +void *ns_start_thread(void *(*f)(void *), void *p) { +#ifdef _WIN32 + return (void *) _beginthread((void (__cdecl *)(void *)) f, 0, p); +#else + pthread_t thread_id = (pthread_t) 0; + pthread_attr_t attr; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + +#if defined(NS_STACK_SIZE) && NS_STACK_SIZE > 1 + (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE); +#endif + + pthread_create(&thread_id, &attr, f, p); + pthread_attr_destroy(&attr); + + return (void *) thread_id; +#endif +} +#endif /* NS_DISABLE_THREADS */ + +static void ns_add_conn(struct ns_mgr *mgr, struct ns_connection *c) { + c->next = mgr->active_connections; + mgr->active_connections = c; + c->prev = NULL; + if (c->next != NULL) c->next->prev = c; +} + +static void ns_remove_conn(struct ns_connection *conn) { + if (conn->prev == NULL) conn->mgr->active_connections = conn->next; + if (conn->prev) conn->prev->next = conn->next; + if (conn->next) conn->next->prev = conn->prev; +} + +/* Print message to buffer. If buffer is large enough to hold the message, + * return buffer. If buffer is to small, allocate large enough buffer on heap, + * and return allocated buffer. */ +int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) { + va_list ap_copy; + int len; + + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + + if (len < 0) { + /* eCos and Windows are not standard-compliant and return -1 when + * the buffer is too small. Keep allocating larger buffers until we + * succeed or out of memory. */ + *buf = NULL; + while (len < 0) { + NS_FREE(*buf); + size *= 2; + if ((*buf = (char *) NS_MALLOC(size)) == NULL) break; + va_copy(ap_copy, ap); + len = vsnprintf(*buf, size, fmt, ap_copy); + va_end(ap_copy); + } + } else if (len > (int) size) { + /* Standard-compliant code path. Allocate a buffer that is large enough. */ + if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) { + len = -1; + } else { + va_copy(ap_copy, ap); + len = vsnprintf(*buf, len + 1, fmt, ap_copy); + va_end(ap_copy); + } + } + + return len; +} + +int ns_vprintf(struct ns_connection *nc, const char *fmt, va_list ap) { + char mem[NS_VPRINTF_BUFFER_SIZE], *buf = mem; + int len; + + if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + ns_out(nc, buf, len); + } + if (buf != mem && buf != NULL) { + NS_FREE(buf); + } + + return len; +} + +int ns_printf(struct ns_connection *conn, const char *fmt, ...) { + int len; + va_list ap; + va_start(ap, fmt); + len = ns_vprintf(conn, fmt, ap); + va_end(ap); + return len; +} + +static void hexdump(struct ns_connection *nc, const char *path, + int num_bytes, int ev) { + const struct iobuf *io = ev == NS_SEND ? &nc->send_iobuf : &nc->recv_iobuf; + FILE *fp; + char *buf, src[60], dst[60]; + int buf_size = num_bytes * 5 + 100; + + if ((fp = fopen(path, "a")) != NULL) { + ns_sock_to_str(nc->sock, src, sizeof(src), 3); + ns_sock_to_str(nc->sock, dst, sizeof(dst), 7); + fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) time(NULL), + nc->user_data, src, + ev == NS_RECV ? "<-" : ev == NS_SEND ? "->" : + ev == NS_ACCEPT ? "" : "XX", + dst, num_bytes); + if (num_bytes > 0 && (buf = (char *) NS_MALLOC(buf_size)) != NULL) { + ns_hexdump(io->buf + (ev == NS_SEND ? 0 : io->len) - + (ev == NS_SEND ? 0 : num_bytes), num_bytes, buf, buf_size); + fprintf(fp, "%s", buf); + NS_FREE(buf); + } + fclose(fp); + } +} + +static void ns_call(struct ns_connection *nc, int ev, void *ev_data) { + if (nc->mgr->hexdump_file != NULL && ev != NS_POLL) { + int len = (ev == NS_RECV || ev == NS_SEND) ? * (int *) ev_data : 0; + hexdump(nc, nc->mgr->hexdump_file, len, ev); + } + + /* + * If protocol handler is specified, call it. Otherwise, call user-specified + * event handler. + */ + (nc->proto_handler ? nc->proto_handler : nc->handler)(nc, ev, ev_data); +} + +static void ns_destroy_conn(struct ns_connection *conn) { + closesocket(conn->sock); + iobuf_free(&conn->recv_iobuf); + iobuf_free(&conn->send_iobuf); +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + SSL_free(conn->ssl); + } + if (conn->ssl_ctx != NULL) { + SSL_CTX_free(conn->ssl_ctx); + } +#endif + NS_FREE(conn); +} + +static void ns_close_conn(struct ns_connection *conn) { + DBG(("%p %d", conn, conn->flags)); + ns_call(conn, NS_CLOSE, NULL); + ns_remove_conn(conn); + ns_destroy_conn(conn); +} + +void ns_set_close_on_exec(sock_t sock) { +#ifdef _WIN32 + (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0); +#else + fcntl(sock, F_SETFD, FD_CLOEXEC); +#endif +} + +static void ns_set_non_blocking_mode(sock_t sock) { +#ifdef _WIN32 + unsigned long on = 1; + ioctlsocket(sock, FIONBIO, &on); +#else + int flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags | O_NONBLOCK); +#endif +} + +#ifndef NS_DISABLE_SOCKETPAIR +int ns_socketpair2(sock_t sp[2], int sock_type) { + union socket_address sa; + sock_t sock; + socklen_t len = sizeof(sa.sin); + int ret = 0; + + sock = sp[0] = sp[1] = INVALID_SOCKET; + + (void) memset(&sa, 0, sizeof(sa)); + sa.sin.sin_family = AF_INET; + sa.sin.sin_port = htons(0); + sa.sin.sin_addr.s_addr = htonl(0x7f000001); + + if ((sock = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (bind(sock, &sa.sa, len) != 0) { + } else if (sock_type == SOCK_STREAM && listen(sock, 1) != 0) { + } else if (getsockname(sock, &sa.sa, &len) != 0) { + } else if ((sp[0] = socket(AF_INET, sock_type, 0)) == INVALID_SOCKET) { + } else if (connect(sp[0], &sa.sa, len) != 0) { + } else if (sock_type == SOCK_DGRAM && + (getsockname(sp[0], &sa.sa, &len) != 0 || + connect(sock, &sa.sa, len) != 0)) { + } else if ((sp[1] = (sock_type == SOCK_DGRAM ? sock : + accept(sock, &sa.sa, &len))) == INVALID_SOCKET) { + } else { + ns_set_close_on_exec(sp[0]); + ns_set_close_on_exec(sp[1]); + if (sock_type == SOCK_STREAM) closesocket(sock); + ret = 1; + } + + if (!ret) { + if (sp[0] != INVALID_SOCKET) closesocket(sp[0]); + if (sp[1] != INVALID_SOCKET) closesocket(sp[1]); + if (sock != INVALID_SOCKET) closesocket(sock); + sock = sp[0] = sp[1] = INVALID_SOCKET; + } + + return ret; +} + +int ns_socketpair(sock_t sp[2]) { + return ns_socketpair2(sp, SOCK_STREAM); +} +#endif /* NS_DISABLE_SOCKETPAIR */ + +/* TODO(lsm): use non-blocking resolver */ +static int ns_resolve2(const char *host, struct in_addr *ina) { + struct hostent *he; + if ((he = gethostbyname(host)) == NULL) { + DBG(("gethostbyname(%s) failed: %s", host, strerror(errno))); + } else { + memcpy(ina, he->h_addr_list[0], sizeof(*ina)); + return 1; + } + return 0; +} + +/* Resolve FDQN "host", store IP address in the "ip". + * Return > 0 (IP address length) on success. */ +int ns_resolve(const char *host, char *buf, size_t n) { + struct in_addr ad; + return ns_resolve2(host, &ad) ? snprintf(buf, n, "%s", inet_ntoa(ad)) : 0; +} + +/* Address format: [PROTO://][IP_ADDRESS:]PORT[:CERT][:CA_CERT] */ +static int ns_parse_address(const char *str, union socket_address *sa, int *p) { + unsigned int a, b, c, d, port = 0; + int len = 0; + char host[200]; +#ifdef NS_ENABLE_IPV6 + char buf[100]; +#endif + + /* MacOS needs that. If we do not zero it, subsequent bind() will fail. */ + /* Also, all-zeroes in the socket address means binding to all addresses */ + /* for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */ + memset(sa, 0, sizeof(*sa)); + sa->sin.sin_family = AF_INET; + + *p = SOCK_STREAM; + + if (memcmp(str, "udp://", 6) == 0) { + str += 6; + *p = SOCK_DGRAM; + } else if (memcmp(str, "tcp://", 6) == 0) { + str += 6; + } + + if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) { + /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ + sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); + sa->sin.sin_port = htons((uint16_t) port); +#ifdef NS_ENABLE_IPV6 + } else if (sscanf(str, "[%99[^]]]:%u%n", buf, &port, &len) == 2 && + inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) { + /* IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080 */ + sa->sin6.sin6_family = AF_INET6; + sa->sin6.sin6_port = htons((uint16_t) port); +#endif + } else if (sscanf(str, "%199[^ :]:%u%n", host, &port, &len) == 2) { + sa->sin.sin_port = htons((uint16_t) port); + ns_resolve2(host, &sa->sin.sin_addr); + } else if (sscanf(str, "%u%n", &port, &len) == 1) { + /* If only port is specified, bind to IPv4, INADDR_ANY */ + sa->sin.sin_port = htons((uint16_t) port); + } + + return port < 0xffff && str[len] == '\0' ? len : 0; +} + +/* 'sa' must be an initialized address to bind to */ +static sock_t ns_open_listening_socket(union socket_address *sa, int proto) { + socklen_t sa_len = (sa->sa.sa_family == AF_INET) ? + sizeof(sa->sin) : sizeof(sa->sin6); + sock_t sock = INVALID_SOCKET; + int on = 1; + + if ((sock = socket(sa->sa.sa_family, proto, 0)) != INVALID_SOCKET && + +#if defined(_WIN32) && defined(SO_EXCLUSIVEADDRUSE) + /* http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx */ + !setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, + (void *) &on, sizeof(on)) && +#endif + +#if 1 || !defined(_WIN32) || defined(SO_EXCLUSIVEADDRUSE) + /* + * SO_RESUSEADDR is not enabled on Windows because the semantics of + * SO_REUSEADDR on UNIX and Windows is different. On Windows, + * SO_REUSEADDR allows to bind a socket to a port without error even if + * the port is already open by another program. This is not the behavior + * SO_REUSEADDR was designed for, and leads to hard-to-track failure + * scenarios. Therefore, SO_REUSEADDR was disabled on Windows unless + * SO_EXCLUSIVEADDRUSE is supported and set on a socket. + */ + !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) && +#endif + + !bind(sock, &sa->sa, sa_len) && + (proto == SOCK_DGRAM || listen(sock, SOMAXCONN) == 0)) { + ns_set_non_blocking_mode(sock); + /* In case port was set to 0, get the real port number */ + (void) getsockname(sock, &sa->sa, &sa_len); + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + sock = INVALID_SOCKET; + } + + return sock; +} + +#ifdef NS_ENABLE_SSL +/* Certificate generation script is at */ +/* https://github.com/cesanta/net_skeleton/blob/master/scripts/gen_certs.sh */ + +static int ns_use_ca_cert(SSL_CTX *ctx, const char *cert) { + if (ctx == NULL) { + return -1; + } else if (cert == NULL || cert[0] == '\0') { + return 0; + } + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0); + return SSL_CTX_load_verify_locations(ctx, cert, NULL) == 1 ? 0 : -2; +} + +static int ns_use_cert(SSL_CTX *ctx, const char *pem_file) { + if (ctx == NULL) { + return -1; + } else if (pem_file == NULL || pem_file[0] == '\0') { + return 0; + } else if (SSL_CTX_use_certificate_file(ctx, pem_file, 1) == 0 || + SSL_CTX_use_PrivateKey_file(ctx, pem_file, 1) == 0) { + return -2; + } else { + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_use_certificate_chain_file(ctx, pem_file); + return 0; + } +} + +const char *ns_set_ssl(struct ns_connection *nc, const char *cert, + const char *ca_cert) { + const char *result = NULL; + + if ((nc->flags & NSF_LISTENING) && + (nc->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { + result = "SSL_CTX_new() failed"; + } else if (!(nc->flags & NSF_LISTENING) && + (nc->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + result = "SSL_CTX_new() failed"; + } else if (ns_use_cert(nc->ssl_ctx, cert) != 0) { + result = "Invalid ssl cert"; + } else if (ns_use_ca_cert(nc->ssl_ctx, ca_cert) != 0) { + result = "Invalid CA cert"; + } else if (!(nc->flags & NSF_LISTENING) && + (nc->ssl = SSL_new(nc->ssl_ctx)) == NULL) { + result = "SSL_new() failed"; + } else if (!(nc->flags & NSF_LISTENING)) { + SSL_set_fd(nc->ssl, nc->sock); + } + return result; +} + +static int ns_ssl_err(struct ns_connection *conn, int res) { + int ssl_err = SSL_get_error(conn->ssl, res); + if (ssl_err == SSL_ERROR_WANT_READ) conn->flags |= NSF_WANT_READ; + if (ssl_err == SSL_ERROR_WANT_WRITE) conn->flags |= NSF_WANT_WRITE; + return ssl_err; +} +#endif /* NS_ENABLE_SSL */ + +struct ns_connection *ns_bind(struct ns_mgr *srv, const char *str, + ns_event_handler_t callback) { + union socket_address sa; + struct ns_connection *nc = NULL; + int proto; + sock_t sock; + + ns_parse_address(str, &sa, &proto); + if ((sock = ns_open_listening_socket(&sa, proto)) == INVALID_SOCKET) { + DBG(("Failed to open listener: %d", errno)); + } else if ((nc = ns_add_sock(srv, sock, callback)) == NULL) { + DBG(("Failed to ns_add_sock")); + closesocket(sock); + } else { + nc->sa = sa; + nc->flags |= NSF_LISTENING; + nc->handler = callback; + + if (proto == SOCK_DGRAM) { + nc->flags |= NSF_UDP; + } + + DBG(("%p sock %d/%d", nc, sock, proto)); + } + + return nc; +} + +static struct ns_connection *accept_conn(struct ns_connection *ls) { + struct ns_connection *c = NULL; + union socket_address sa; + socklen_t len = sizeof(sa); + sock_t sock = INVALID_SOCKET; + + /* NOTE(lsm): on Windows, sock is always > FD_SETSIZE */ + if ((sock = accept(ls->sock, &sa.sa, &len)) == INVALID_SOCKET) { + } else if ((c = ns_add_sock(ls->mgr, sock, ls->handler)) == NULL) { + closesocket(sock); +#ifdef NS_ENABLE_SSL + } else if (ls->ssl_ctx != NULL && + ((c->ssl = SSL_new(ls->ssl_ctx)) == NULL || + SSL_set_fd(c->ssl, sock) != 1)) { + DBG(("SSL error")); + ns_close_conn(c); + c = NULL; +#endif + } else { + c->listener = ls; + c->proto_data = ls->proto_data; + c->proto_handler = ls->proto_handler; + c->user_data = ls->user_data; + ns_call(c, NS_ACCEPT, &sa); + DBG(("%p %d %p %p", c, c->sock, c->ssl_ctx, c->ssl)); + } + + return c; +} + +static int ns_is_error(int n) { + return n == 0 || + (n < 0 && errno != EINTR && errno != EINPROGRESS && + errno != EAGAIN && errno != EWOULDBLOCK +#ifdef _WIN32 + && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK +#endif + ); +} + +void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags) { + union socket_address sa; + socklen_t slen = sizeof(sa); + + if (buf != NULL && len > 0) { + buf[0] = '\0'; + memset(&sa, 0, sizeof(sa)); + if (flags & 4) { + getpeername(sock, &sa.sa, &slen); + } else { + getsockname(sock, &sa.sa, &slen); + } + if (flags & 1) { +#if defined(NS_ENABLE_IPV6) + inet_ntop(sa.sa.sa_family, sa.sa.sa_family == AF_INET ? + (void *) &sa.sin.sin_addr : + (void *) &sa.sin6.sin6_addr, buf, len); +#elif defined(_WIN32) + /* Only Windoze Vista (and newer) have inet_ntop() */ + strncpy(buf, inet_ntoa(sa.sin.sin_addr), len); +#else + inet_ntop(sa.sa.sa_family, (void *) &sa.sin.sin_addr, buf,(socklen_t)len); +#endif + } + if (flags & 2) { + snprintf(buf + strlen(buf), len - (strlen(buf) + 1), "%s%d", + flags & 1 ? ":" : "", (int) ntohs(sa.sin.sin_port)); + } + } +} + +int ns_hexdump(const void *buf, int len, char *dst, int dst_len) { + const unsigned char *p = (const unsigned char *) buf; + char ascii[17] = ""; + int i, idx, n = 0; + + for (i = 0; i < len; i++) { + idx = i % 16; + if (idx == 0) { + if (i > 0) n += snprintf(dst + n, dst_len - n, " %s\n", ascii); + n += snprintf(dst + n, dst_len - n, "%04x ", i); + } + n += snprintf(dst + n, dst_len - n, " %02x", p[i]); + ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]; + ascii[idx + 1] = '\0'; + } + + while (i++ % 16) n += snprintf(dst + n, dst_len - n, "%s", " "); + n += snprintf(dst + n, dst_len - n, " %s\n\n", ascii); + + return n; +} + +static void ns_read_from_socket(struct ns_connection *conn) { + char buf[2048]; + int n = 0; + + if (conn->flags & NSF_CONNECTING) { + int ok = 1, ret; + socklen_t len = sizeof(ok); + + ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len); +#ifdef NS_ENABLE_SSL + if (ret == 0 && ok == 0 && conn->ssl != NULL) { + int res = SSL_connect(conn->ssl); + int ssl_err = ns_ssl_err(conn, res); + if (res == 1) { + conn->flags |= NSF_SSL_HANDSHAKE_DONE; + } else if (ssl_err == SSL_ERROR_WANT_READ || + ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + ok = 1; + } + } +#endif + conn->flags &= ~NSF_CONNECTING; + DBG(("%p ok=%d", conn, ok)); + if (ok != 0) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + ns_call(conn, NS_CONNECT, &ok); + return; + } + +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + if (conn->flags & NSF_SSL_HANDSHAKE_DONE) { + /* SSL library may have more bytes ready to read then we ask to read. + * Therefore, read in a loop until we read everything. Without the loop, + * we skip to the next select() cycle which can just timeout. */ + while ((n = SSL_read(conn->ssl, buf, sizeof(buf))) > 0) { + DBG(("%p %d <- %d bytes (SSL)", conn, conn->flags, n)); + iobuf_append(&conn->recv_iobuf, buf, n); + ns_call(conn, NS_RECV, &n); + } + ns_ssl_err(conn, n); + } else { + int res = SSL_accept(conn->ssl); + int ssl_err = ns_ssl_err(conn, res); + if (res == 1) { + conn->flags |= NSF_SSL_HANDSHAKE_DONE; + } else if (ssl_err == SSL_ERROR_WANT_READ || + ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + return; + } + } else +#endif + { + while ((n = (int) recv(conn->sock, buf, sizeof(buf), 0)) > 0) { + DBG(("%p %d <- %d bytes (PLAIN)", conn, conn->flags, n)); + iobuf_append(&conn->recv_iobuf, buf, n); + ns_call(conn, NS_RECV, &n); + } + } + + if (ns_is_error(n)) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } +} + +static void ns_write_to_socket(struct ns_connection *conn) { + struct iobuf *io = &conn->send_iobuf; + int n = 0; + +#ifdef NS_ENABLE_SSL + if (conn->ssl != NULL) { + n = SSL_write(conn->ssl, io->buf, io->len); + if (n <= 0) { + int ssl_err = ns_ssl_err(conn, n); + if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE) { + return; /* Call us again */ + } else { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } + } + } else +#endif + { n = (int) send(conn->sock, io->buf, io->len, 0); } + + DBG(("%p %d -> %d bytes", conn, conn->flags, n)); + + ns_call(conn, NS_SEND, &n); + if (ns_is_error(n)) { + conn->flags |= NSF_CLOSE_IMMEDIATELY; + } else if (n > 0) { + iobuf_remove(io, n); + } +} + +int ns_send(struct ns_connection *conn, const void *buf, int len) { + return (int) ns_out(conn, buf, len); +} + +static void ns_handle_udp(struct ns_connection *ls) { + struct ns_connection nc; + char buf[NS_UDP_RECEIVE_BUFFER_SIZE]; + int n; + socklen_t s_len = sizeof(nc.sa); + + memset(&nc, 0, sizeof(nc)); + n = recvfrom(ls->sock, buf, sizeof(buf), 0, &nc.sa.sa, &s_len); + if (n <= 0) { + DBG(("%p recvfrom: %s", ls, strerror(errno))); + } else { + nc.mgr = ls->mgr; + nc.recv_iobuf.buf = buf; + nc.recv_iobuf.len = nc.recv_iobuf.size = n; + nc.sock = ls->sock; + nc.handler = ls->handler; + nc.user_data = ls->user_data; + nc.proto_data = ls->proto_data; + nc.proto_handler = ls->proto_handler; + nc.mgr = ls->mgr; + nc.listener = ls; + nc.flags = NSF_UDP; + DBG(("%p %d bytes received", ls, n)); + ns_call(&nc, NS_RECV, &n); + } +} + +static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) { + if (sock != INVALID_SOCKET) { + FD_SET(sock, set); + if (*max_fd == INVALID_SOCKET || sock > *max_fd) { + *max_fd = sock; + } + } +} + +time_t ns_mgr_poll(struct ns_mgr *mgr, int milli) { + struct ns_connection *nc, *tmp; + struct timeval tv; + fd_set read_set, write_set, err_set; + sock_t max_fd = INVALID_SOCKET; + time_t current_time = time(NULL); + + FD_ZERO(&read_set); + FD_ZERO(&write_set); + FD_ZERO(&err_set); + ns_add_to_set(mgr->ctl[1], &read_set, &max_fd); + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if (!(nc->flags & (NSF_LISTENING | NSF_CONNECTING))) { + ns_call(nc, NS_POLL, ¤t_time); + } + if (!(nc->flags & NSF_WANT_WRITE)) { + /*DBG(("%p read_set", nc)); */ + ns_add_to_set(nc->sock, &read_set, &max_fd); + } + if (((nc->flags & NSF_CONNECTING) && !(nc->flags & NSF_WANT_READ)) || + (nc->send_iobuf.len > 0 && !(nc->flags & NSF_CONNECTING) && + !(nc->flags & NSF_BUFFER_BUT_DONT_SEND))) { + /*DBG(("%p write_set", nc)); */ + ns_add_to_set(nc->sock, &write_set, &max_fd); + ns_add_to_set(nc->sock, &err_set, &max_fd); + } + if (nc->flags & NSF_CLOSE_IMMEDIATELY) { + ns_close_conn(nc); + } + } + + tv.tv_sec = milli / 1000; + tv.tv_usec = (milli % 1000) * 1000; + + if (select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv) > 0) { + /* select() might have been waiting for a long time, reset current_time + * now to prevent last_io_time being set to the past. */ + current_time = time(NULL); + + /* Read wakeup messages */ + if (mgr->ctl[1] != INVALID_SOCKET && + FD_ISSET(mgr->ctl[1], &read_set)) { + struct ctl_msg ctl_msg; + int len = (int) recv(mgr->ctl[1], (char *) &ctl_msg, sizeof(ctl_msg), 0); + send(mgr->ctl[1], ctl_msg.message, 1, 0); + if (len >= (int) sizeof(ctl_msg.callback) && ctl_msg.callback != NULL) { + struct ns_connection *c; + for (c = ns_next(mgr, NULL); c != NULL; c = ns_next(mgr, c)) { + ctl_msg.callback(c, NS_POLL, ctl_msg.message); + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + + /* Windows reports failed connect() requests in err_set */ + if (FD_ISSET(nc->sock, &err_set) && (nc->flags & NSF_CONNECTING)) { + nc->last_io_time = current_time; + ns_read_from_socket(nc); + } + + if (FD_ISSET(nc->sock, &read_set)) { + nc->last_io_time = current_time; + if (nc->flags & NSF_LISTENING) { + if (nc->flags & NSF_UDP) { + ns_handle_udp(nc); + } else { + /* We're not looping here, and accepting just one connection at + * a time. The reason is that eCos does not respect non-blocking + * flag on a listening socket and hangs in a loop. */ + accept_conn(nc); + } + } else { + ns_read_from_socket(nc); + } + } + + if (FD_ISSET(nc->sock, &write_set)) { + nc->last_io_time = current_time; + if (nc->flags & NSF_CONNECTING) { + ns_read_from_socket(nc); + } else if (!(nc->flags & NSF_BUFFER_BUT_DONT_SEND)) { + ns_write_to_socket(nc); + } + } + } + } + + for (nc = mgr->active_connections; nc != NULL; nc = tmp) { + tmp = nc->next; + if ((nc->flags & NSF_CLOSE_IMMEDIATELY) || + (nc->send_iobuf.len == 0 && + (nc->flags & NSF_FINISHED_SENDING_DATA))) { + ns_close_conn(nc); + } + } + + return current_time; +} + +struct ns_connection *ns_connect(struct ns_mgr *mgr, const char *address, + ns_event_handler_t callback) { + sock_t sock = INVALID_SOCKET; + struct ns_connection *nc = NULL; + union socket_address sa; + int rc, proto; + + ns_parse_address(address, &sa, &proto); + if ((sock = socket(AF_INET, proto, 0)) == INVALID_SOCKET) { + return NULL; + } + ns_set_non_blocking_mode(sock); + rc = (proto == SOCK_DGRAM) ? 0 : connect(sock, &sa.sa, sizeof(sa.sin)); + + if (rc != 0 && ns_is_error(rc)) { + closesocket(sock); + return NULL; + } else if ((nc = ns_add_sock(mgr, sock, callback)) == NULL) { + closesocket(sock); + return NULL; + } + + nc->sa = sa; /* Important, cause UDP conns will use sendto() */ + nc->flags = (proto == SOCK_DGRAM) ? NSF_UDP : NSF_CONNECTING; + + return nc; +} + +struct ns_connection *ns_add_sock(struct ns_mgr *s, sock_t sock, + ns_event_handler_t callback) { + struct ns_connection *conn; + if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) { + memset(conn, 0, sizeof(*conn)); + ns_set_non_blocking_mode(sock); + ns_set_close_on_exec(sock); + conn->sock = sock; + conn->handler = callback; + conn->mgr = s; + conn->last_io_time = time(NULL); + ns_add_conn(s, conn); + DBG(("%p %d", conn, sock)); + } + return conn; +} + +struct ns_connection *ns_next(struct ns_mgr *s, struct ns_connection *conn) { + return conn == NULL ? s->active_connections : conn->next; +} + +void ns_broadcast(struct ns_mgr *mgr, ns_event_handler_t cb,void *data, size_t len) { + struct ctl_msg ctl_msg; + if (mgr->ctl[0] != INVALID_SOCKET && data != NULL && + len < sizeof(ctl_msg.message)) { + ctl_msg.callback = cb; + memcpy(ctl_msg.message, data, len); + send(mgr->ctl[0], (char *) &ctl_msg, + offsetof(struct ctl_msg, message) + len, 0); + recv(mgr->ctl[0], (char *) &len, 1, 0); + } +} + +void ns_mgr_init(struct ns_mgr *s, void *user_data) { + memset(s, 0, sizeof(*s)); + s->ctl[0] = s->ctl[1] = INVALID_SOCKET; + s->user_data = user_data; + +#ifdef _WIN32 + { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } +#else + /* Ignore SIGPIPE signal, so if client cancels the request, it + * won't kill the whole process. */ + signal(SIGPIPE, SIG_IGN); +#endif + +#ifndef NS_DISABLE_SOCKETPAIR + do { + ns_socketpair2(s->ctl, SOCK_DGRAM); + } while (s->ctl[0] == INVALID_SOCKET); +#endif + +#ifdef NS_ENABLE_SSL + {static int init_done; if (!init_done) { SSL_library_init(); init_done++; }} +#endif +} + +void ns_mgr_free(struct ns_mgr *s) { + struct ns_connection *conn, *tmp_conn; + + DBG(("%p", s)); + if (s == NULL) return; + /* Do one last poll, see https://github.com/cesanta/mongoose/issues/286 */ + ns_mgr_poll(s, 0); + + if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]); + if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]); + s->ctl[0] = s->ctl[1] = INVALID_SOCKET; + + for (conn = s->active_connections; conn != NULL; conn = tmp_conn) { + tmp_conn = conn->next; + ns_close_conn(conn); + } +} +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013 Cesanta Software Limited + * All rights reserved + * + * This library is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this library under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this library under a commercial + * license, as set out in . + */ + +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ + +#include +#include +#include +#include + +#ifdef _WIN32 +#define snprintf _snprintf +#endif + +#ifndef FROZEN_REALLOC +#define FROZEN_REALLOC realloc +#endif + +#ifndef FROZEN_FREE +#define FROZEN_FREE free +#endif + +struct frozen { + const char *end; + const char *cur; + struct json_token *tokens; + int max_tokens; + int num_tokens; + int do_realloc; +}; + +static int parse_object(struct frozen *f); +static int parse_value(struct frozen *f); + +#define EXPECT(cond, err_code) do { if (!(cond)) return (err_code); } while (0) +#define TRY(expr) do { int _n = expr; if (_n < 0) return _n; } while (0) +#define END_OF_STRING (-1) + +static int left(const struct frozen *f) { + return f->end - f->cur; +} + +static int is_space(int ch) { + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +static void skip_whitespaces(struct frozen *f) { + while (f->cur < f->end && is_space(*f->cur)) f->cur++; +} + +static int cur(struct frozen *f) { + skip_whitespaces(f); + return f->cur >= f->end ? END_OF_STRING : * (unsigned char *) f->cur; +} + +static int test_and_skip(struct frozen *f, int expected) { + int ch = cur(f); + if (ch == expected) { f->cur++; return 0; } + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +} + +static int is_alpha(int ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +static int is_digit(int ch) { + return ch >= '0' && ch <= '9'; +} + +static int is_hex_digit(int ch) { + return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); +} + +static int get_escape_len(const char *s, int len) { + switch (*s) { + case 'u': + return len < 6 ? JSON_STRING_INCOMPLETE : + is_hex_digit(s[1]) && is_hex_digit(s[2]) && + is_hex_digit(s[3]) && is_hex_digit(s[4]) ? 5 : JSON_STRING_INVALID; + case '"': case '\\': case '/': case 'b': + case 'f': case 'n': case 'r': case 't': + return len < 2 ? JSON_STRING_INCOMPLETE : 1; + default: + return JSON_STRING_INVALID; + } +} + +static int capture_ptr(struct frozen *f, const char *ptr, enum json_type type) { + if (f->do_realloc && f->num_tokens >= f->max_tokens) { + int new_size = f->max_tokens == 0 ? 100 : f->max_tokens * 2; + void *p = FROZEN_REALLOC(f->tokens, new_size * sizeof(f->tokens[0])); + if (p == NULL) return JSON_TOKEN_ARRAY_TOO_SMALL; + f->max_tokens = new_size; + f->tokens = (struct json_token *) p; + } + if (f->tokens == NULL || f->max_tokens == 0) return 0; + if (f->num_tokens >= f->max_tokens) return JSON_TOKEN_ARRAY_TOO_SMALL; + f->tokens[f->num_tokens].ptr = ptr; + f->tokens[f->num_tokens].type = type; + f->num_tokens++; + return 0; +} + +static int capture_len(struct frozen *f, int token_index, const char *ptr) { + if (f->tokens == 0 || f->max_tokens == 0) return 0; + EXPECT(token_index >= 0 && token_index < f->max_tokens, JSON_STRING_INVALID); + f->tokens[token_index].len = ptr - f->tokens[token_index].ptr; + f->tokens[token_index].num_desc = (f->num_tokens - 1) - token_index; + return 0; +} + +/* identifier = letter { letter | digit | '_' } */ +static int parse_identifier(struct frozen *f) { + EXPECT(is_alpha(cur(f)), JSON_STRING_INVALID); + TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING)); + while (f->cur < f->end && + (*f->cur == '_' || is_alpha(*f->cur) || is_digit(*f->cur))) { + f->cur++; + } + capture_len(f, f->num_tokens - 1, f->cur); + return 0; +} + +static int get_utf8_char_len(unsigned char ch) { + if ((ch & 0x80) == 0) return 1; + switch (ch & 0xf0) { + case 0xf0: return 4; + case 0xe0: return 3; + default: return 2; + } +} + +/* string = '"' { quoted_printable_chars } '"' */ +static int parse_string(struct frozen *f) { + int n, ch = 0, len = 0; + TRY(test_and_skip(f, '"')); + TRY(capture_ptr(f, f->cur, JSON_TYPE_STRING)); + for (; f->cur < f->end; f->cur += len) { + ch = * (unsigned char *) f->cur; + len = get_utf8_char_len((unsigned char) ch); + EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ + EXPECT(len < left(f), JSON_STRING_INCOMPLETE); + if (ch == '\\') { + EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n); + len += n; + } else if (ch == '"') { + capture_len(f, f->num_tokens - 1, f->cur); + f->cur++; + break; + }; + } + return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; +} + +/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ +static int parse_number(struct frozen *f) { + int ch = cur(f); + TRY(capture_ptr(f, f->cur, JSON_TYPE_NUMBER)); + if (ch == '-') f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + if (f->cur < f->end && f->cur[0] == '.') { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + } + if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(is_digit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && is_digit(f->cur[0])) f->cur++; + } + capture_len(f, f->num_tokens - 1, f->cur); + return 0; +} + +/* array = '[' [ value { ',' value } ] ']' */ +static int parse_array(struct frozen *f) { + int ind; + TRY(test_and_skip(f, '[')); + TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_ARRAY)); + ind = f->num_tokens - 1; + while (cur(f) != ']') { + TRY(parse_value(f)); + if (cur(f) == ',') f->cur++; + } + TRY(test_and_skip(f, ']')); + capture_len(f, ind, f->cur); + return 0; +} + +static int compare(const char *s, const char *str, int len) { + int i = 0; + while (i < len && s[i] == str[i]) i++; + return i == len ? 1 : 0; +} + +static int expect(struct frozen *f, const char *s, int len, enum json_type t) { + int i, n = left(f); + + TRY(capture_ptr(f, f->cur, t)); + for (i = 0; i < len; i++) { + if (i >= n) return JSON_STRING_INCOMPLETE; + if (f->cur[i] != s[i]) return JSON_STRING_INVALID; + } + f->cur += len; + TRY(capture_len(f, f->num_tokens - 1, f->cur)); + + return 0; +} + +/* value = 'null' | 'true' | 'false' | number | string | array | object */ +static int parse_value(struct frozen *f) { + int ch = cur(f); + + switch (ch) { + case '"': TRY(parse_string(f)); break; + case '{': TRY(parse_object(f)); break; + case '[': TRY(parse_array(f)); break; + case 'n': TRY(expect(f, "null", 4, JSON_TYPE_NULL)); break; + case 't': TRY(expect(f, "true", 4, JSON_TYPE_TRUE)); break; + case 'f': TRY(expect(f, "false", 5, JSON_TYPE_FALSE)); break; + case '-': case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + TRY(parse_number(f)); + break; + default: + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + + return 0; +} + +/* key = identifier | string */ +static int parse_key(struct frozen *f) { + int ch = cur(f); +#if 0 + printf("%s 1 [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur); +#endif + if (is_alpha(ch)) { + TRY(parse_identifier(f)); + } else if (ch == '"') { + TRY(parse_string(f)); + } else { + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + return 0; +} + +/* pair = key ':' value */ +static int parse_pair(struct frozen *f) { + TRY(parse_key(f)); + TRY(test_and_skip(f, ':')); + TRY(parse_value(f)); + return 0; +} + +/* object = '{' pair { ',' pair } '}' */ +static int parse_object(struct frozen *f) { + int ind; + TRY(test_and_skip(f, '{')); + TRY(capture_ptr(f, f->cur - 1, JSON_TYPE_OBJECT)); + ind = f->num_tokens - 1; + while (cur(f) != '}') { + TRY(parse_pair(f)); + if (cur(f) == ',') f->cur++; + } + TRY(test_and_skip(f, '}')); + capture_len(f, ind, f->cur); + return 0; +} + +static int doit(struct frozen *f) { + if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; + if (f->end == f->cur) return JSON_STRING_INCOMPLETE; + TRY(parse_object(f)); + TRY(capture_ptr(f, f->cur, JSON_TYPE_EOF)); + capture_len(f, f->num_tokens, f->cur); + return 0; +} + +/* json = object */ +int parse_json(const char *s, int s_len, struct json_token *arr, int arr_len) { + struct frozen frozen; + + memset(&frozen, 0, sizeof(frozen)); + frozen.end = s + s_len; + frozen.cur = s; + frozen.tokens = arr; + frozen.max_tokens = arr_len; + + TRY(doit(&frozen)); + + return frozen.cur - s; +} + +struct json_token *parse_json2(const char *s, int s_len) { + struct frozen frozen; + + memset(&frozen, 0, sizeof(frozen)); + frozen.end = s + s_len; + frozen.cur = s; + frozen.do_realloc = 1; + + if (doit(&frozen) < 0) { + FROZEN_FREE((void *) frozen.tokens); + frozen.tokens = NULL; + } + return frozen.tokens; +} + +static int path_part_len(const char *p) { + int i = 0; + while (p[i] != '\0' && p[i] != '[' && p[i] != '.') i++; + return i; +} + +struct json_token *find_json_token(struct json_token *toks, const char *path) { + while (path != 0 && path[0] != '\0') { + int i, ind2 = 0, ind = -1, skip = 2, n = path_part_len(path); + if (path[0] == '[') { + if (toks->type != JSON_TYPE_ARRAY || !is_digit(path[1])) return 0; + for (ind = 0, n = 1; path[n] != ']' && path[n] != '\0'; n++) { + if (!is_digit(path[n])) return 0; + ind *= 10; + ind += path[n] - '0'; + } + if (path[n++] != ']') return 0; + skip = 1; /* In objects, we skip 2 elems while iterating, in arrays 1. */ + } else if (toks->type != JSON_TYPE_OBJECT) return 0; + toks++; + for (i = 0; i < toks[-1].num_desc; i += skip, ind2++) { + /* ind == -1 indicated that we're iterating an array, not object */ + if (ind == -1 && toks[i].type != JSON_TYPE_STRING) return 0; + if (ind2 == ind || + (ind == -1 && toks[i].len == n && compare(path, toks[i].ptr, n))) { + i += skip - 1; + break; + }; + if (toks[i - 1 + skip].type == JSON_TYPE_ARRAY || + toks[i - 1 + skip].type == JSON_TYPE_OBJECT) { + i += toks[i - 1 + skip].num_desc; + } + } + if (i == toks[-1].num_desc) return 0; + path += n; + if (path[0] == '.') path++; + if (path[0] == '\0') return &toks[i]; + toks += i; + } + return 0; +} + +int json_emit_long(char *buf, int buf_len, long int value) { + char tmp[20]; + int n = snprintf(tmp, sizeof(tmp), "%ld", value); + strncpy(buf, tmp, buf_len > 0 ? buf_len : 0); + return n; +} + +int json_emit_double(char *buf, int buf_len, double value) { + char tmp[20]; + int n = snprintf(tmp, sizeof(tmp), "%g", value); + strncpy(buf, tmp, buf_len > 0 ? buf_len : 0); + return n; +} + +int json_emit_quoted_str(char *s, int s_len, const char *str, int len) { + const char *begin = s, *end = s + s_len, *str_end = str + len; + char ch; + +#define EMIT(x) do { if (s < end) *s = x; s++; } while (0) + + EMIT('"'); + while (str < str_end) { + ch = *str++; + switch (ch) { + case '"': EMIT('\\'); EMIT('"'); break; + case '\\': EMIT('\\'); EMIT('\\'); break; + case '\b': EMIT('\\'); EMIT('b'); break; + case '\f': EMIT('\\'); EMIT('f'); break; + case '\n': EMIT('\\'); EMIT('n'); break; + case '\r': EMIT('\\'); EMIT('r'); break; + case '\t': EMIT('\\'); EMIT('t'); break; + default: EMIT(ch); + } + } + EMIT('"'); + if (s < end) { + *s = '\0'; + } + + return s - begin; +} + +int json_emit_unquoted_str(char *buf, int buf_len, const char *str, int len) { + if (buf_len > 0 && len > 0) { + int n = len < buf_len ? len : buf_len; + memcpy(buf, str, n); + if (n < buf_len) { + buf[n] = '\0'; + } + } + return len; +} + +int json_emit_va(char *s, int s_len, const char *fmt, va_list ap) { + const char *end = s + s_len, *str, *orig = s; + size_t len; + + while (*fmt != '\0') { + switch (*fmt) { + case '[': case ']': case '{': case '}': case ',': case ':': + case ' ': case '\r': case '\n': case '\t': + if (s < end) { + *s = *fmt; + } + s++; + break; + case 'i': + s += json_emit_long(s, end - s, va_arg(ap, long)); + break; + case 'f': + s += json_emit_double(s, end - s, va_arg(ap, double)); + break; + case 'v': + str = va_arg(ap, char *); + len = va_arg(ap, size_t); + s += json_emit_quoted_str(s, end - s, str, len); + break; + case 'V': + str = va_arg(ap, char *); + len = va_arg(ap, size_t); + s += json_emit_unquoted_str(s, end - s, str, len); + break; + case 's': + str = va_arg(ap, char *); + s += json_emit_quoted_str(s, end - s, str, strlen(str)); + break; + case 'S': + str = va_arg(ap, char *); + s += json_emit_unquoted_str(s, end - s, str, strlen(str)); + break; + case 'T': + s += json_emit_unquoted_str(s, end - s, "true", 4); + break; + case 'F': + s += json_emit_unquoted_str(s, end - s, "false", 5); + break; + case 'N': + s += json_emit_unquoted_str(s, end - s, "null", 4); + break; + default: + return 0; + } + fmt++; + } + + /* Best-effort to 0-terminate generated string */ + if (s < end) { + *s = '\0'; + } + + return s - orig; +} + +int json_emit(char *buf, int buf_len, const char *fmt, ...) { + int len; + va_list ap; + + va_start(ap, fmt); + len = json_emit_va(buf, buf_len, fmt, ap); + va_end(ap); + + return len; +} +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_DISABLE_HTTP_WEBSOCKET + + +/* + * Check whether full request is buffered. Return: + * -1 if request is malformed + * 0 if request is not yet fully buffered + * >0 actual request length, including last \r\n\r\n + */ +static int get_request_len(const char *s, int buf_len) { + const unsigned char *buf = (unsigned char *) s; + int i; + + for (i = 0; i < buf_len; i++) { + if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) { + return -1; + } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') { + return i + 2; + } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' && + buf[i + 2] == '\n') { + return i + 3; + } + } + + return 0; +} + +int ns_parse_http(const char *s, int n, struct http_message *req) { + const char *end; + int len, i; + + if ((len = get_request_len(s, n)) <= 0) return len; + + memset(req, 0, sizeof(*req)); + req->message.p = s; + req->body.p = s + len; + req->message.len = req->body.len = (size_t) ~0; + end = s + len; + + /* Request is fully buffered. Skip leading whitespaces. */ + while (s < end && isspace(* (unsigned char *) s)) s++; + + /* Parse request line: method, URI, proto */ + s = ns_skip(s, end, " ", &req->method); + s = ns_skip(s, end, " ", &req->uri); + s = ns_skip(s, end, "\r\n", &req->proto); + if (req->uri.p <= req->method.p || req->proto.p <= req->uri.p) return -1; + + for (i = 0; i < (int) ARRAY_SIZE(req->header_names); i++) { + struct ns_str *k = &req->header_names[i], *v = &req->header_values[i]; + + s = ns_skip(s, end, ": ", k); + s = ns_skip(s, end, "\r\n", v); + + while (v->len > 0 && v->p[v->len - 1] == ' ') { + v->len--; /* Trim trailing spaces in header value */ + } + + if (k->len == 0 || v->len == 0) { + k->p = v->p = NULL; + break; + } + + if (!ns_ncasecmp(k->p, "Content-Length", 14)) { + req->body.len = to64(v->p); + req->message.len = len + req->body.len; + } + } + + if (req->body.len == (size_t) ~0 && ns_vcasecmp(&req->method, "GET") == 0) { + req->body.len = 0; + req->message.len = len; + } + + return len; +} + +struct ns_str *ns_get_http_header(struct http_message *hm, const char *name) { + size_t i, len = strlen(name); + + for (i = 0; i < ARRAY_SIZE(hm->header_names); i++) { + struct ns_str *h = &hm->header_names[i], *v = &hm->header_values[i]; + if (h->p != NULL && h->len == len && !ns_ncasecmp(h->p, name, len)) return v; + } + + return NULL; +} + +static int is_ws_fragment(unsigned char flags) { + return (flags & 0x80) == 0 || (flags & 0x0f) == 0; +} + +static int is_ws_first_fragment(unsigned char flags) { + return (flags & 0x80) == 0 && (flags & 0x0f) != 0; +} + +static int deliver_websocket_data(struct ns_connection *nc) { + /* Using unsigned char *, cause of integer arithmetic below */ + uint64_t i, data_len = 0, frame_len = 0, buf_len = nc->recv_iobuf.len, + len, mask_len = 0, header_len = 0; + unsigned char *p = (unsigned char *) nc->recv_iobuf.buf, + *buf = p, *e = p + buf_len; + unsigned *sizep = (unsigned *) &p[1]; /* Size ptr for defragmented frames */ + int ok, reass = buf_len > 0 && is_ws_fragment(p[0]) && + !(nc->flags & NSF_WEBSOCKET_NO_DEFRAG); + + /* If that's a continuation frame that must be reassembled, handle it */ + if (reass && !is_ws_first_fragment(p[0]) && buf_len >= 1 + sizeof(*sizep) && + buf_len >= 1 + sizeof(*sizep) + *sizep) { + buf += 1 + sizeof(*sizep) + *sizep; + buf_len -= 1 + sizeof(*sizep) + *sizep; + } + + if (buf_len >= 2) { + len = buf[1] & 127; + mask_len = buf[1] & 128 ? 4 : 0; + if (len < 126 && buf_len >= mask_len) { + data_len = len; + header_len = 2 + mask_len; + } else if (len == 126 && buf_len >= 4 + mask_len) { + header_len = 4 + mask_len; + data_len = ntohs(* (uint16_t *) &buf[2]); + } else if (buf_len >= 10 + mask_len) { + header_len = 10 + mask_len; + data_len = (((uint64_t) ntohl(* (uint32_t *) &buf[2])) << 32) + + ntohl(* (uint32_t *) &buf[6]); + } + } + + frame_len = header_len + data_len; + ok = frame_len > 0 && frame_len <= buf_len; + + if (ok) { + struct websocket_message wsm; + + wsm.size = (size_t) data_len; + wsm.data = buf + header_len; + wsm.flags = buf[0]; + + /* Apply mask if necessary */ + if (mask_len > 0) { + for (i = 0; i < data_len; i++) { + buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4]; + } + } + + if (reass) { + /* On first fragmented frame, nullify size */ + if (is_ws_first_fragment(wsm.flags)) { + iobuf_resize(&nc->recv_iobuf, nc->recv_iobuf.size + sizeof(*sizep)); + p[0] &= ~0x0f; /* Next frames will be treated as continuation */ + buf = p + 1 + sizeof(*sizep); + *sizep = 0; /* TODO(lsm): fix. this can stomp over frame data */ + } + + /* Append this frame to the reassembled buffer */ + memmove(buf, wsm.data, e - wsm.data); + (*sizep) += wsm.size; + nc->recv_iobuf.len -= wsm.data - buf; + + /* On last fragmented frame - call user handler and remove data */ + if (wsm.flags & 0x80) { + wsm.data = p + 1 + sizeof(*sizep); + wsm.size = *sizep; + nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm); + iobuf_remove(&nc->recv_iobuf, 1 + sizeof(*sizep) + *sizep); + } + } else { + /* TODO(lsm): properly handle OOB control frames during defragmentation */ + nc->handler(nc, NS_WEBSOCKET_FRAME, &wsm); /* Call handler */ + iobuf_remove(&nc->recv_iobuf, (size_t) frame_len); /* Cleanup frame */ + } + } + + return ok; +} + +static void ns_send_ws_header(struct ns_connection *nc, int op, size_t len) { + int header_len; + unsigned char header[10]; + + header[0] = 0x80 + (op & 0x0f); + if (len < 126) { + header[1] = len; + header_len = 2; + } else if (len < 65535) { + header[1] = 126; + * (uint16_t *) &header[2] = htons((uint16_t) len); + header_len = 4; + } else { + header[1] = 127; + * (uint32_t *) &header[2] = htonl((uint32_t) ((uint64_t) len >> 32)); + * (uint32_t *) &header[6] = htonl((uint32_t) (len & 0xffffffff)); + header_len = 10; + } + ns_send(nc, header, header_len); +} + +void ns_send_websocket_frame(struct ns_connection *nc, int op, + const void *data, size_t len) { + ns_send_ws_header(nc, op, len); + ns_send(nc, data, len); + + if (op == WEBSOCKET_OP_CLOSE) { + nc->flags |= NSF_FINISHED_SENDING_DATA; + } +} + +void ns_send_websocket_framev(struct ns_connection *nc, int op, + const struct ns_str *strv, int strvcnt) { + int i; + int len = 0; + for (i=0; iflags |= NSF_FINISHED_SENDING_DATA; + } +} + +void ns_printf_websocket_frame(struct ns_connection *nc, int op, + const char *fmt, ...) { + char mem[4192], *buf = mem; + va_list ap; + int len; + + va_start(ap, fmt); + if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) { + ns_send_websocket_frame(nc, op, buf, len); + } + va_end(ap); + + if (buf != mem && buf != NULL) { + free(buf); + } +} + +static void websocket_handler(struct ns_connection *nc, int ev, void *ev_data) { + nc->handler(nc, ev, ev_data); + + switch (ev) { + case NS_RECV: + do { } while (deliver_websocket_data(nc)); + break; + default: + break; + } +} + +static void ws_handshake(struct ns_connection *nc, const struct ns_str *key) { + static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + char buf[500], sha[20], b64_sha[sizeof(sha) * 2]; + SHA1_CTX sha_ctx; + + snprintf(buf, sizeof(buf), "%.*s%s", (int) key->len, key->p, magic); + + SHA1Init(&sha_ctx); + SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf)); + SHA1Final((unsigned char *) sha, &sha_ctx); + + ns_base64_encode((unsigned char *) sha, sizeof(sha), b64_sha); + ns_printf(nc, "%s%s%s", + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); +} + +static void http_handler(struct ns_connection *nc, int ev, void *ev_data) { + struct iobuf *io = &nc->recv_iobuf; + struct http_message hm; + struct ns_str *vec; + int req_len; + + /* + * For HTTP messages without Content-Length, always send HTTP message + * before NS_CLOSE message. + */ + if (ev == NS_CLOSE && io->len > 0 && + ns_parse_http(io->buf, io->len, &hm) > 0) { + hm.body.len = io->buf + io->len - hm.body.p; + nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm); + } + + nc->handler(nc, ev, ev_data); + + if (ev == NS_RECV) { + req_len = ns_parse_http(io->buf, io->len, &hm); + if (req_len < 0 || (req_len == 0 && io->len >= NS_MAX_HTTP_REQUEST_SIZE)) { + nc->flags |= NSF_CLOSE_IMMEDIATELY; + } else if (req_len == 0) { + /* Do nothing, request is not yet fully buffered */ + } else if (nc->listener == NULL && + ns_get_http_header(&hm, "Sec-WebSocket-Accept")) { + /* We're websocket client, got handshake response from server. */ + /* TODO(lsm): check the validity of accept Sec-WebSocket-Accept */ + iobuf_remove(io, req_len); + nc->proto_handler = websocket_handler; + nc->flags |= NSF_IS_WEBSOCKET; + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL); + websocket_handler(nc, NS_RECV, ev_data); + } else if (nc->listener != NULL && + (vec = ns_get_http_header(&hm, "Sec-WebSocket-Key")) != NULL) { + /* This is a websocket request. Switch protocol handlers. */ + iobuf_remove(io, req_len); + nc->proto_handler = websocket_handler; + nc->flags |= NSF_IS_WEBSOCKET; + + /* Send handshake */ + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_REQUEST, &hm); + if (!(nc->flags & NSF_CLOSE_IMMEDIATELY)) { + if (nc->send_iobuf.len == 0) { + ws_handshake(nc, vec); + } + nc->handler(nc, NS_WEBSOCKET_HANDSHAKE_DONE, NULL); + websocket_handler(nc, NS_RECV, ev_data); + } + } else if (hm.message.len <= io->len) { + /* Whole HTTP message is fully buffered, call event handler */ + nc->handler(nc, nc->listener ? NS_HTTP_REQUEST : NS_HTTP_REPLY, &hm); + iobuf_remove(io, hm.message.len); + } + } +} + +void ns_set_protocol_http_websocket(struct ns_connection *nc) { + nc->proto_handler = http_handler; +} + +void ns_send_websocket_handshake(struct ns_connection *nc, const char *uri, + const char *extra_headers) { + unsigned long random = (unsigned long) uri; + char key[sizeof(random) * 2]; + + ns_base64_encode((unsigned char *) &random, sizeof(random), key); + ns_printf(nc, "GET %s HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Key: %s\r\n" + "%s\r\n", + uri, key, extra_headers == NULL ? "" : extra_headers); +} + +void ns_send_http_file(struct ns_connection *nc, const char *path, + ns_stat_t *st) { + char buf[BUFSIZ]; + size_t n; + FILE *fp; + + if ((fp = fopen(path, "rb")) != NULL) { + ns_printf(nc, "HTTP/1.1 200 OK\r\n" + "Content-Length: %lu\r\n\r\n", (unsigned long) st->st_size); + while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) { + ns_send(nc, buf, n); + } + fclose(fp); + } else { + ns_printf(nc, "%s", "HTTP/1.1 500 Server Error\r\n" + "Content-Length: 0\r\n\r\n"); + } +} + +static void remove_double_dots(char *s) { + char *p = s; + + while (*s != '\0') { + *p++ = *s++; + if (s[-1] == '/' || s[-1] == '\\') { + while (s[0] != '\0') { + if (s[0] == '/' || s[0] == '\\') { + s++; + } else if (s[0] == '.' && s[1] == '.') { + s += 2; + } else { + break; + } + } + } + } + *p = '\0'; +} + +int ns_url_decode(const char *src, int src_len, char *dst, + int dst_len, int is_form_url_encoded) { + int i, j, a, b; +#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') + + for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { + if (src[i] == '%' && i < src_len - 2 && + isxdigit(* (const unsigned char *) (src + i + 1)) && + isxdigit(* (const unsigned char *) (src + i + 2))) { + a = tolower(* (const unsigned char *) (src + i + 1)); + b = tolower(* (const unsigned char *) (src + i + 2)); + dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b)); + i += 2; + } else if (is_form_url_encoded && src[i] == '+') { + dst[j] = ' '; + } else { + dst[j] = src[i]; + } + } + + dst[j] = '\0'; /* Null-terminate the destination */ + + return i >= src_len ? j : -1; +} + +int ns_get_http_var(const struct ns_str *buf, const char *name, + char *dst, size_t dst_len) { + const char *p, *e, *s; + size_t name_len; + int len; + + if (dst == NULL || dst_len == 0) { + len = -2; + } else if (buf->p == NULL || name == NULL || buf->len == 0) { + len = -1; + dst[0] = '\0'; + } else { + name_len = strlen(name); + e = buf->p + buf->len; + len = -1; + dst[0] = '\0'; + + for (p = buf->p; p + name_len < e; p++) { + if ((p == buf->p || p[-1] == '&') && p[name_len] == '=' && + !ns_ncasecmp(name, p, name_len)) { + p += name_len + 1; + s = (const char *) memchr(p, '&', (size_t)(e - p)); + if (s == NULL) { + s = e; + } + len = ns_url_decode(p, (size_t)(s - p), dst, dst_len, 1); + if (len == -1) { + len = -2; + } + break; + } + } + } + + return len; +} + +void ns_serve_http(struct ns_connection *nc, struct http_message *hm, + struct ns_serve_http_opts opts) { + char path[NS_MAX_PATH]; + ns_stat_t st; + + snprintf(path, sizeof(path), "%s/%.*s", opts.document_root, + (int) hm->uri.len, hm->uri.p); + remove_double_dots(path); + + if (ns_stat(path, &st) != 0) { + ns_printf(nc, "%s", "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"); + } else if (S_ISDIR(st.st_mode)) { + strncat(path, "/index.html", sizeof(path) - (strlen(path) + 1)); + if (ns_stat(path, &st) == 0) { + ns_send_http_file(nc, path, &st); + } else { + ns_printf(nc, "%s", "HTTP/1.1 403 Access Denied\r\n" + "Content-Length: 0\r\n\r\n"); + } + } else { + ns_send_http_file(nc, path, &st); + } +} +#endif /* NS_DISABLE_HTTP_WEBSOCKET */ +/* Copyright(c) By Steve Reid */ +/* 100% Public Domain */ + +#ifndef NS_DISABLE_SHA1 + +#include + +static int is_big_endian(void) { + static const int n = 1; + return ((char *) &n)[0] == 0; +} + +#define SHA1HANDSOFF +#if defined(__sun) +#endif + +union char64long16 { unsigned char c[64]; uint32_t l[16]; }; + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static uint32_t blk0(union char64long16 *block, int i) { + /* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */ + if (!is_big_endian()) { + block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | + (rol(block->l[i], 8) & 0x00FF00FF); + } + return block->l[i]; +} + +/* Avoid redefine warning (ARM /usr/include/sys/ucontext.h define R0~R4) */ +#undef blk +#undef R0 +#undef R1 +#undef R2 +#undef R3 +#undef R4 + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { + uint32_t a, b, c, d, e; + union char64long16 block[1]; + + memcpy(block, buffer, 64); + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Erase working structures. The order of operations is important, + * used to ensure that compiler doesn't optimize those out. */ + memset(block, 0, sizeof(block)); + a = b = c = d = e = 0; + (void) a; (void) b; (void) c; (void) d; (void) e; +} + +void SHA1Init(SHA1_CTX *context) { + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +void SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len) { + uint32_t i, j; + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1]++; + context->count[1] += (len>>29); + j = (j >> 3) & 63; + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + +void SHA1Final(unsigned char digest[20], SHA1_CTX *context) { + unsigned i; + unsigned char finalcount[8], c; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); + } + c = 0200; + SHA1Update(context, &c, 1); + while ((context->count[0] & 504) != 448) { + c = 0000; + SHA1Update(context, &c, 1); + } + SHA1Update(context, finalcount, 8); + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + memset(context, '\0', sizeof(*context)); + memset(&finalcount, '\0', sizeof(finalcount)); +} +#endif /* NS_DISABLE_SHA1 */ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + + +const char *ns_skip(const char *s, const char *end, + const char *delims, struct ns_str *v) { + v->p = s; + while (s < end && strchr(delims, * (unsigned char *) s) == NULL) s++; + v->len = s - v->p; + while (s < end && strchr(delims, * (unsigned char *) s) != NULL) s++; + return s; +} + +static int lowercase(const char *s) { + return tolower(* (const unsigned char *) s); +} + +int ns_ncasecmp(const char *s1, const char *s2, size_t len) { + int diff = 0; + + if (len > 0) + do { + diff = lowercase(s1++) - lowercase(s2++); + } while (diff == 0 && s1[-1] != '\0' && --len > 0); + + return diff; +} + +int ns_vcasecmp(const struct ns_str *str2, const char *str1) { + size_t n1 = strlen(str1), n2 = str2->len; + return n1 == n2 ? ns_ncasecmp(str1, str2->p, n1) : n1 > n2 ? 1 : -1; +} + +int ns_vcmp(const struct ns_str *str2, const char *str1) { + size_t n1 = strlen(str1), n2 = str2->len; + return n1 == n2 ? memcmp(str1, str2->p, n2) : n1 > n2 ? 1 : -1; +} + +#ifdef _WIN32 +static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) { + char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p; + + strncpy(buf, path, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + + /* Trim trailing slashes. Leave backslash for paths like "X:\" */ + p = buf + strlen(buf) - 1; + while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0'; + + /* + * Convert to Unicode and back. If doubly-converted string does not + * match the original, something is fishy, reject. + */ + memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len); + WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2), + NULL, NULL); + if (strcmp(buf, buf2) != 0) { + wbuf[0] = L'\0'; + } +} +#endif /* _WIN32 */ + +int ns_stat(const char *path, ns_stat_t *st) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st))); + return _wstati64(wpath, st); +#else + return stat(path, st); +#endif +} + +FILE *ns_fopen(const char *path, const char *mode) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE], wmode[10]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + to_wchar(mode, wmode, ARRAY_SIZE(wmode)); + return _wfopen(wpath, wmode); +#else + return fopen(path, mode); +#endif +} + +int ns_open(const char *path, int flag, int mode) { +#ifdef _WIN32 + wchar_t wpath[MAX_PATH_SIZE]; + to_wchar(path, wpath, ARRAY_SIZE(wpath)); + return _wopen(wpath, flag, mode); +#else + return open(path, flag, mode); +#endif +} + +void ns_base64_encode(const unsigned char *src, int src_len, char *dst) { + static const char *b64 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int i, j, a, b, c; + + for (i = j = 0; i < src_len; i += 3) { + a = src[i]; + b = i + 1 >= src_len ? 0 : src[i + 1]; + c = i + 2 >= src_len ? 0 : src[i + 2]; + + dst[j++] = b64[a >> 2]; + dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; + if (i + 1 < src_len) { + dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; + } + if (i + 2 < src_len) { + dst[j++] = b64[c & 63]; + } + } + while (j % 4 != 0) { + dst[j++] = '='; + } + dst[j++] = '\0'; +} + +/* Convert one byte of encoded base64 input stream to 6-bit chunk */ +static unsigned char from_b64(unsigned char ch) { + /* Inverse lookup map */ + static const unsigned char tab[128] = { + 255, 255, 255, 255, 255, 255, 255, 255, /* 0 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 8 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 16 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 24 */ + 255, 255, 255, 255, 255, 255, 255, 255, /* 32 */ + 255, 255, 255, 62, 255, 255, 255, 63, /* 40 */ + 52, 53, 54, 55, 56, 57, 58, 59, /* 48 */ + 60, 61, 255, 255, 255, 200, 255, 255, /* 56 '=' is 200, on index 61 */ + 255, 0, 1, 2, 3, 4, 5, 6, /* 64 */ + 7, 8, 9, 10, 11, 12, 13, 14, /* 72 */ + 15, 16, 17, 18, 19, 20, 21, 22, /* 80 */ + 23, 24, 25, 255, 255, 255, 255, 255, /* 88 */ + 255, 26, 27, 28, 29, 30, 31, 32, /* 96 */ + 33, 34, 35, 36, 37, 38, 39, 40, /* 104 */ + 41, 42, 43, 44, 45, 46, 47, 48, /* 112 */ + 49, 50, 51, 255, 255, 255, 255, 255, /* 120 */ + }; + return tab[ch & 127]; +} + +void ns_base64_decode(const unsigned char *s, int len, char *dst) { + unsigned char a, b, c, d; + while (len >= 4 && + (a = from_b64(s[0])) != 255 && + (b = from_b64(s[1])) != 255 && + (c = from_b64(s[2])) != 255 && + (d = from_b64(s[3])) != 255) { + if (a == 200 || b == 200) break; /* '=' can't be there */ + *dst++ = a << 2 | b >> 4; + if (c == 200) break; + *dst++ = b << 4 | c >> 2; + if (d == 200) break; + *dst++ = c << 6 | d; + s += 4; + len -=4; + } + *dst = 0; +} +/* Copyright (c) 2014 Cesanta Software Limited */ +/* All rights reserved */ + +#ifndef NS_DISABLE_JSON_RPC + + +int ns_rpc_create_reply(char *buf, int len, const struct ns_rpc_request *req, + const char *result_fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:V,s:", + "jsonrpc", "2.0", "id", + req->id == NULL ? "null" : req->id->ptr, + req->id == NULL ? 4 : req->id->len, + "result"); + va_start(ap, result_fmt); + n += json_emit_va(buf + n, len - n, result_fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}"); + + return n; +} + +int ns_rpc_create_request(char *buf, int len, const char *method, + const char *id, const char *params_fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:s,s:s,s:", + "jsonrpc", "2.0", "id", id, "method", method, "params"); + va_start(ap, params_fmt); + n += json_emit_va(buf + n, len - n, params_fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}"); + + return n; +} + +int ns_rpc_create_error(char *buf, int len, struct ns_rpc_request *req, + int code, const char *message, const char *fmt, ...) { + va_list ap; + int n = 0; + + n += json_emit(buf + n, len - n, "{s:s,s:V,s:{s:i,s:s,s:", + "jsonrpc", "2.0", "id", + req->id == NULL ? "null" : req->id->ptr, + req->id == NULL ? 4 : req->id->len, + "error", "code", code, + "message", message, "data"); + va_start(ap, fmt); + n += json_emit_va(buf + n, len - n, fmt, ap); + va_end(ap); + + n += json_emit(buf + n, len - n, "}}"); + + return n; +} + +int ns_rpc_create_std_error(char *buf, int len, struct ns_rpc_request *req, + int code) { + const char *message = NULL; + + switch (code) { + case JSON_RPC_PARSE_ERROR: message = "parse error"; break; + case JSON_RPC_INVALID_REQUEST_ERROR: message = "invalid request"; break; + case JSON_RPC_METHOD_NOT_FOUND_ERROR: message = "method not found"; break; + case JSON_RPC_INVALID_PARAMS_ERROR: message = "invalid parameters"; break; + case JSON_RPC_SERVER_ERROR: message = "server error"; break; + default: message = "unspecified error"; break; + } + + return ns_rpc_create_error(buf, len, req, code, message, "N"); +} + +int ns_rpc_dispatch(const char *buf, int len, char *dst, int dst_len, + const char **methods, ns_rpc_handler_t *handlers) { + struct json_token tokens[200]; + struct ns_rpc_request req; + int i, n; + + memset(&req, 0, sizeof(req)); + n = parse_json(buf, len, tokens, sizeof(tokens) / sizeof(tokens[0])); + if (n <= 0) { + int err_code = (n == JSON_STRING_INVALID) ? + JSON_RPC_PARSE_ERROR : JSON_RPC_SERVER_ERROR; + return ns_rpc_create_std_error(dst, dst_len, &req, err_code); + } + + req.message = tokens; + req.id = find_json_token(tokens, "id"); + req.method = find_json_token(tokens, "method"); + req.params = find_json_token(tokens, "params"); + + if (req.id == NULL || req.method == NULL) { + return ns_rpc_create_std_error(dst, dst_len, &req, + JSON_RPC_INVALID_REQUEST_ERROR); + } + + for (i = 0; methods[i] != NULL; i++) { + int mlen = strlen(methods[i]); + if (mlen == req.method->len && + memcmp(methods[i], req.method->ptr, mlen) == 0) break; + } + + if (methods[i] == NULL) { + return ns_rpc_create_std_error(dst, dst_len, &req, + JSON_RPC_METHOD_NOT_FOUND_ERROR); + } + + return handlers[i](dst, dst_len, &req); +} + +int ns_rpc_parse_reply(const char *buf, int len, + struct json_token *toks, int max_toks, + struct ns_rpc_reply *rep, struct ns_rpc_error *er) { + int n = parse_json(buf, len, toks, max_toks); + + memset(rep, 0, sizeof(*rep)); + memset(er, 0, sizeof(*er)); + + if (n > 0) { + if ((rep->result = find_json_token(toks, "result")) != NULL) { + rep->message = toks; + rep->id = find_json_token(toks, "id"); + } else { + er->message = toks; + er->id = find_json_token(toks, "id"); + er->error_code = find_json_token(toks, "error.code"); + er->error_message = find_json_token(toks, "error.message"); + er->error_data = find_json_token(toks, "error.data"); + } + } + return n; +} + +#endif /* NS_DISABLE_JSON_RPC */ diff --git a/external/net_skeleton/net_skeleton.h b/external/net_skeleton/net_skeleton.h new file mode 100644 index 000000000..58e8af201 --- /dev/null +++ b/external/net_skeleton/net_skeleton.h @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#ifndef NS_SKELETON_HEADER_INCLUDED +#define NS_SKELETON_HEADER_INCLUDED + +#define NS_SKELETON_VERSION "2.2.0" + +#undef UNICODE /* Use ANSI WinAPI functions */ +#undef _UNICODE /* Use multibyte encoding on Windows */ +#define _MBCS /* Use multibyte encoding on Windows */ +#define _INTEGRAL_MAX_BITS 64 /* Enable _stati64() on Windows */ +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ +#undef WIN32_LEAN_AND_MEAN /* Let windows.h always include winsock2.h */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ +#endif +#define __STDC_FORMAT_MACROS /* wants this for C++ */ +#define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ +#ifndef _LARGEFILE_SOURCE +#define _LARGEFILE_SOURCE /* Enable fseeko() and ftello() functions */ +#endif +#define _FILE_OFFSET_BITS 64 /* Enable 64-bit file offsets */ + +#ifdef _MSC_VER +#pragma warning (disable : 4127) /* FD_SET() emits warning, disable it */ +#pragma warning (disable : 4204) /* missing c99 support */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef va_copy +#ifdef __va_copy +#define va_copy __va_copy +#else +#define va_copy(x,y) (x) = (y) +#endif +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#pragma comment(lib, "ws2_32.lib") /* Linking with winsock library */ +#endif +#include +#include +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef __func__ +#define STRX(x) #x +#define STR(x) STRX(x) +#define __func__ __FILE__ ":" STR(__LINE__) +#endif +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define sleep(x) Sleep((x) * 1000) +#define to64(x) _atoi64(x) +typedef int socklen_t; +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned short uint16_t; +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +typedef SOCKET sock_t; +#ifdef __MINGW32__ +typedef struct stat ns_stat_t; +#else +typedef struct _stati64 ns_stat_t; +#endif +#ifndef S_ISDIR +#define S_ISDIR(x) ((x) & _S_IFDIR) +#endif +#else /* not _WIN32 */ +#include +#include +#include +#include +#include +#include +#include /* For inet_pton() when NS_ENABLE_IPV6 is defined */ +#include +#include +#include +#ifndef closesocket +#define closesocket(x) close(x) +#endif +#define __cdecl +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#ifdef __APPLE__ +int64_t strtoll(const char * str, char ** endptr, int base); +#endif +#define to64(x) strtoll(x, NULL, 10) +typedef int sock_t; +typedef struct stat ns_stat_t; +#endif /* _WIN32 */ + +#ifdef NS_ENABLE_DEBUG +#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \ + fflush(stdout); } while(0) +#else +#define DBG(x) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) +#endif + +#ifdef NS_ENABLE_SSL +#ifdef __APPLE__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +#include +#else +typedef void *SSL; +typedef void *SSL_CTX; +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +union socket_address { + struct sockaddr sa; + struct sockaddr_in sin; +#ifdef NS_ENABLE_IPV6 + struct sockaddr_in6 sin6; +#else + struct sockaddr sin6; +#endif +}; + +/* Describes chunk of memory */ +struct ns_str { + const char *p; + size_t len; +}; + +/* IO buffers interface */ +struct iobuf { + char *buf; + size_t len; + size_t size; +}; + +void iobuf_init(struct iobuf *, size_t initial_size); +void iobuf_free(struct iobuf *); +size_t iobuf_append(struct iobuf *, const void *data, size_t data_size); +void iobuf_remove(struct iobuf *, size_t data_size); +void iobuf_resize(struct iobuf *, size_t new_size); + +/* Callback function (event handler) prototype, must be defined by user. */ +/* Net skeleton will call event handler, passing events defined above. */ +struct ns_connection; +typedef void (*ns_event_handler_t)(struct ns_connection *, int ev, void *); + +/* Events. Meaning of event parameter (evp) is given in the comment. */ +#define NS_POLL 0 /* Sent to each connection on each call to ns_mgr_poll() */ +#define NS_ACCEPT 1 /* New connection accept()-ed. union socket_address *addr */ +#define NS_CONNECT 2 /* connect() succeeded or failed. int *success_status */ +#define NS_RECV 3 /* Data has benn received. int *num_bytes */ +#define NS_SEND 4 /* Data has been written to a socket. int *num_bytes */ +#define NS_CLOSE 5 /* Connection is closed. NULL */ + +struct ns_mgr { + struct ns_connection *active_connections; + const char *hexdump_file; /* Debug hexdump file path */ + sock_t ctl[2]; /* Socketpair for mg_wakeup() */ + void *user_data; /* User data */ +}; + +struct ns_connection { + struct ns_connection *next, *prev; /* ns_mgr::active_connections linkage */ + struct ns_connection *listener; /* Set only for accept()-ed connections */ + struct ns_mgr *mgr; + + sock_t sock; /* Socket */ + union socket_address sa; /* Peer address */ + struct iobuf recv_iobuf; /* Received data */ + struct iobuf send_iobuf; /* Data scheduled for sending */ + SSL *ssl; + SSL_CTX *ssl_ctx; + time_t last_io_time; /* Timestamp of the last socket IO */ + ns_event_handler_t proto_handler; /* Protocol-specific event handler */ + void *proto_data; /* Protocol-specific data */ + ns_event_handler_t handler; /* Event handler function */ + void *user_data; /* User-specific data */ + + unsigned long flags; +#define NSF_FINISHED_SENDING_DATA (1 << 0) +#define NSF_BUFFER_BUT_DONT_SEND (1 << 1) +#define NSF_SSL_HANDSHAKE_DONE (1 << 2) +#define NSF_CONNECTING (1 << 3) +#define NSF_CLOSE_IMMEDIATELY (1 << 4) +#define NSF_WANT_READ (1 << 5) +#define NSF_WANT_WRITE (1 << 6) /* NOTE(lsm): proto-specific */ +#define NSF_LISTENING (1 << 7) /* NOTE(lsm): proto-specific */ +#define NSF_UDP (1 << 8) +#define NSF_IS_WEBSOCKET (1 << 9) /* NOTE(lsm): proto-specific */ +#define NSF_WEBSOCKET_NO_DEFRAG (1 << 10) /* NOTE(lsm): proto-specific */ + +#define NSF_USER_1 (1 << 20) +#define NSF_USER_2 (1 << 21) +#define NSF_USER_3 (1 << 22) +#define NSF_USER_4 (1 << 23) +#define NSF_USER_5 (1 << 24) +#define NSF_USER_6 (1 << 25) +}; + +void ns_mgr_init(struct ns_mgr *, void *user_data); +void ns_mgr_free(struct ns_mgr *); +time_t ns_mgr_poll(struct ns_mgr *, int milli); +void ns_broadcast(struct ns_mgr *, ns_event_handler_t, void *, size_t); + +struct ns_connection *ns_next(struct ns_mgr *, struct ns_connection *); +struct ns_connection *ns_add_sock(struct ns_mgr *, sock_t, ns_event_handler_t); +struct ns_connection *ns_bind(struct ns_mgr *, const char *, ns_event_handler_t); +struct ns_connection *ns_connect(struct ns_mgr *, const char *, ns_event_handler_t); +const char *ns_set_ssl(struct ns_connection *nc, const char *, const char *); + +int ns_send(struct ns_connection *, const void *buf, int len); +int ns_printf(struct ns_connection *, const char *fmt, ...); +int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap); + +/* Utility functions */ +void *ns_start_thread(void *(*f)(void *), void *p); +int ns_socketpair(sock_t [2]); +int ns_socketpair2(sock_t [2], int sock_type); /* SOCK_STREAM or SOCK_DGRAM */ +void ns_set_close_on_exec(sock_t); +void ns_sock_to_str(sock_t sock, char *buf, size_t len, int flags); +int ns_hexdump(const void *buf, int len, char *dst, int dst_len); +int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap); +int ns_resolve(const char *domain_name, char *ip_addr_buf, size_t buf_len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* NS_SKELETON_HEADER_INCLUDED */ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2013 Cesanta Software Limited + * All rights reserved + * + * This library is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this library under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this library under a commercial + * license, as set out in . + */ + +#ifndef FROZEN_HEADER_INCLUDED +#define FROZEN_HEADER_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +enum json_type { + JSON_TYPE_EOF = 0, /* End of parsed tokens marker */ + JSON_TYPE_STRING = 1, + JSON_TYPE_NUMBER = 2, + JSON_TYPE_OBJECT = 3, + JSON_TYPE_TRUE = 4, + JSON_TYPE_FALSE = 5, + JSON_TYPE_NULL = 6, + JSON_TYPE_ARRAY = 7 +}; + +struct json_token { + const char *ptr; /* Points to the beginning of the token */ + int len; /* Token length */ + int num_desc; /* For arrays and object, total number of descendants */ + enum json_type type; /* Type of the token, possible values above */ +}; + +/* Error codes */ +#define JSON_STRING_INVALID -1 +#define JSON_STRING_INCOMPLETE -2 +#define JSON_TOKEN_ARRAY_TOO_SMALL -3 + +int parse_json(const char *json_string, int json_string_length, + struct json_token *tokens_array, int size_of_tokens_array); +struct json_token *parse_json2(const char *json_string, int string_length); +struct json_token *find_json_token(struct json_token *toks, const char *path); + +int json_emit_long(char *buf, int buf_len, long value); +int json_emit_double(char *buf, int buf_len, double value); +int json_emit_quoted_str(char *buf, int buf_len, const char *str, int len); +int json_emit_unquoted_str(char *buf, int buf_len, const char *str, int len); +int json_emit(char *buf, int buf_len, const char *fmt, ...); +int json_emit_va(char *buf, int buf_len, const char *fmt, va_list); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* FROZEN_HEADER_INCLUDED */ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#if !defined(NS_SHA1_HEADER_INCLUDED) && !defined(NS_DISABLE_SHA1) +#define NS_SHA1_HEADER_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +void SHA1Init(SHA1_CTX *); +void SHA1Update(SHA1_CTX *, const unsigned char *data, uint32_t len); +void SHA1Final(unsigned char digest[20], SHA1_CTX *); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_SHA1_HEADER_INCLUDED */ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_UTIL_HEADER_DEFINED +#define NS_UTIL_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef MAX_PATH_SIZE +#define MAX_PATH_SIZE 500 +#endif + +const char *ns_skip(const char *, const char *, const char *, struct ns_str *); +int ns_ncasecmp(const char *s1, const char *s2, size_t len); +int ns_vcmp(const struct ns_str *str2, const char *str1); +int ns_vcasecmp(const struct ns_str *str2, const char *str1); +void ns_base64_decode(const unsigned char *s, int len, char *dst); +void ns_base64_encode(const unsigned char *src, int src_len, char *dst); +int ns_stat(const char *path, ns_stat_t *st); +FILE *ns_fopen(const char *path, const char *mode); +int ns_open(const char *path, int flag, int mode); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_UTIL_HEADER_DEFINED */ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_HTTP_HEADER_DEFINED +#define NS_HTTP_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define NS_MAX_HTTP_HEADERS 40 +#define NS_MAX_HTTP_REQUEST_SIZE 8192 +#define NS_MAX_PATH 1024 + +struct http_message { + struct ns_str message; /* Whole message: request line + headers + body */ + + /* HTTP Request line (or HTTP response line) */ + struct ns_str method; /* "GET" */ + struct ns_str uri; /* "/my_file.html" */ + struct ns_str proto; /* "HTTP/1.1" */ + + /* Headers */ + struct ns_str header_names[NS_MAX_HTTP_HEADERS]; + struct ns_str header_values[NS_MAX_HTTP_HEADERS]; + + /* Message body */ + struct ns_str body; /* Zero-length for requests with no body */ +}; + +struct websocket_message { + unsigned char *data; + size_t size; + unsigned char flags; +}; + +/* HTTP and websocket events. void *ev_data is described in a comment. */ +#define NS_HTTP_REQUEST 100 /* struct http_message * */ +#define NS_HTTP_REPLY 101 /* struct http_message * */ + +#define NS_WEBSOCKET_HANDSHAKE_REQUEST 111 /* NULL */ +#define NS_WEBSOCKET_HANDSHAKE_DONE 112 /* NULL */ +#define NS_WEBSOCKET_FRAME 113 /* struct websocket_message * */ + +void ns_set_protocol_http_websocket(struct ns_connection *); +void ns_send_websocket_handshake(struct ns_connection *, const char *, + const char *); +void ns_send_websocket_frame(struct ns_connection *, int, const void *, size_t); +void ns_send_websocket_framev(struct ns_connection *, int, const struct ns_str *, int); + +void ns_printf_websocket_frame(struct ns_connection *, int, const char *, ...); + +/* Websocket opcodes, from http://tools.ietf.org/html/rfc6455 */ +#define WEBSOCKET_OP_CONTINUE 0 +#define WEBSOCKET_OP_TEXT 1 +#define WEBSOCKET_OP_BINARY 2 +#define WEBSOCKET_OP_CLOSE 8 +#define WEBSOCKET_OP_PING 9 +#define WEBSOCKET_OP_PONG 10 + +/* Utility functions */ +struct ns_str *ns_get_http_header(struct http_message *, const char *); +int ns_parse_http(const char *s, int n, struct http_message *req); +int ns_get_http_var(const struct ns_str *, const char *, char *dst, size_t); + + +struct ns_serve_http_opts { + const char *document_root; +}; +void ns_serve_http(struct ns_connection *, struct http_message *, + struct ns_serve_http_opts); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_HTTP_HEADER_DEFINED */ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + */ + +#ifndef NS_JSON_RPC_HEADER_DEFINED +#define NS_JSON_RPC_HEADER_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* JSON-RPC standard error codes */ +#define JSON_RPC_PARSE_ERROR (-32700) +#define JSON_RPC_INVALID_REQUEST_ERROR (-32600) +#define JSON_RPC_METHOD_NOT_FOUND_ERROR (-32601) +#define JSON_RPC_INVALID_PARAMS_ERROR (-32602) +#define JSON_RPC_INTERNAL_ERROR (-32603) +#define JSON_RPC_SERVER_ERROR (-32000) + +struct ns_rpc_request { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *method; /* Method name */ + struct json_token *params; /* Method params */ +}; + +struct ns_rpc_reply { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *result; /* Remote call result */ +}; + +struct ns_rpc_error { + struct json_token *message; /* Whole RPC message */ + struct json_token *id; /* Message ID */ + struct json_token *error_code; /* error.code */ + struct json_token *error_message; /* error.message */ + struct json_token *error_data; /* error.data, can be NULL */ +}; + +int ns_rpc_parse_request(const char *buf, int len, struct ns_rpc_request *req); + +int ns_rpc_parse_reply(const char *buf, int len, + struct json_token *toks, int max_toks, + struct ns_rpc_reply *, struct ns_rpc_error *); + +int ns_rpc_create_request(char *, int, const char *method, + const char *id, const char *params_fmt, ...); + +int ns_rpc_create_reply(char *, int, const struct ns_rpc_request *req, + const char *result_fmt, ...); + +int ns_rpc_create_error(char *, int, struct ns_rpc_request *req, + int, const char *, const char *, ...); + +int ns_rpc_create_std_error(char *, int, struct ns_rpc_request *, int code); + +typedef int (*ns_rpc_handler_t)(char *buf, int len, struct ns_rpc_request *); +int ns_rpc_dispatch(const char *buf, int, char *dst, int dst_len, + const char **methods, ns_rpc_handler_t *handlers); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* NS_JSON_RPC_HEADER_DEFINED */ diff --git a/external/net_skeleton/scripts/embed_net_skeleton.sh b/external/net_skeleton/scripts/embed_net_skeleton.sh new file mode 100644 index 000000000..973ced33e --- /dev/null +++ b/external/net_skeleton/scripts/embed_net_skeleton.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# This script embeds net_skeleton directly into the source file. +# The source file must have a placeholder for the net_skeleton code, +# two following lines: + +# // net_skeleton start +# // net_skeleton end +# +# Net skeleton code will be inserted between those two lines. + +if ! test -f "$1" ; then + echo "Usage: $0 " + exit 1 +fi + +D=`dirname $0` + +F1=$D/../net_skeleton.h +F2=$D/../net_skeleton.c + +sed '/#include "net_skeleton.h"/d' $F2 > /tmp/$$ +F2=/tmp/$$ + +A='\/\/ net_skeleton start' +B='\/\/ net_skeleton end' + +sed -i .$$.bak -e "/$A/,/$B/ { /$A/{ n; r $F1" -e "r $F2" -e "}; /$B/!d; }" "$1" diff --git a/external/net_skeleton/scripts/generate_ssl_certificates.sh b/external/net_skeleton/scripts/generate_ssl_certificates.sh new file mode 100644 index 000000000..1d94a6123 --- /dev/null +++ b/external/net_skeleton/scripts/generate_ssl_certificates.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# This script generates self-signed certificates: CA, client and server. + +set -e +set -x + +# Change these if needed +BITS=2048 +DAYS=3650 + +if test -z "$1" ; then + echo "Usage: ./$0 " + exit 1 +fi + +MY_DOMAIN="$1" + +CAS="/CN=$MY_DOMAIN/O=$MY_DOMAIN/C=IE/L=Dublin" +SUBJ=${SUBJ:="/CN=$MY_DOMAIN/O=$MY_DOMAIN/C=IE/L=Galway"} +SERIAL=$(date +%s) + +echo $SERIAL > ca.srl +openssl genrsa -out ca.key $BITS +openssl req -new -x509 -key ca.key -out ca.crt -nodes -days $DAYS -subj "$CAS" +cat ca.key ca.crt > ca.pem + +openssl genrsa -out server.key $BITS +openssl req -key server.key -new -out server.req -days $DAYS -subj "$SUBJ" +openssl x509 -req -in server.req -CA ca.pem -CAkey ca.pem -out server.crt -days $DAYS +cat server.key server.crt > server.pem + +openssl genrsa -out client.key $BITS +openssl req -new -key client.key -out client.req -days $DAYS -subj "$SUBJ" +openssl x509 -req -in client.req -CA ca.pem -CAkey ca.pem -out client.crt -days $DAYS +cat client.key client.crt > client.pem + +rm ca.{crt,key,srl} client.{crt,key,req} server.{crt,key,req} +mv ca.pem ${MY_DOMAIN}_ca.pem +mv client.pem ${MY_DOMAIN}_client.pem +mv server.pem ${MY_DOMAIN}_server.pem \ No newline at end of file diff --git a/external/net_skeleton/test/.gitignore b/external/net_skeleton/test/.gitignore new file mode 100644 index 000000000..1e61043bb --- /dev/null +++ b/external/net_skeleton/test/.gitignore @@ -0,0 +1,10 @@ +unit_test +unit_test_ansi +unit_test_c99 +unit_test.c.gcov +unit_test.gcno +unit_test.gcda +*.gcov +unit_test.dSYM +net_skeleton.gcno +net_skeleton.gcda diff --git a/external/net_skeleton/test/Makefile b/external/net_skeleton/test/Makefile new file mode 100644 index 000000000..e30cb3d35 --- /dev/null +++ b/external/net_skeleton/test/Makefile @@ -0,0 +1,46 @@ +PROG = unit_test +PROF = -fprofile-arcs -ftest-coverage -g -O0 +CFLAGS = -W -Wall -Werror -I.. -DNS_ENABLE_SSL -DNS_ENABLE_IPV6 -pthread $(PROF) $(CFLAGS_EXTRA) +PEDANTIC=$(shell gcc --version 2>/dev/null | grep -q clang && echo -pedantic) +SOURCES = $(PROG).c ../net_skeleton.c + +# http://crossgcc.rts-software.org/doku.php?id=compiling_for_win32 +MINGW_GCC=/usr/local/gcc-4.8.0-qt-4.8.4-for-mingw32/win32-gcc/bin/i586-mingw32-gcc + +all: clean ismerged $(PROG)_ansi $(PROG)_c99 $(PROG) + +.PHONY: clean clean_coverage $(PROG) $(PROG)_ansi $(PROG)_c99 $(PROG).exe $(PROG)_mingw.exe + +ismerged: + $(MAKE) -C ../modules -s --no-print-directory merge_net_skeleton.c | diff ../net_skeleton.c - + $(MAKE) -C ../modules -s --no-print-directory merge_net_skeleton.h | diff ../net_skeleton.h - + +$(PROG): Makefile + $(MAKE) clean_coverage + g++ -x c++ $(SOURCES) -o $(PROG) $(CFLAGS) -lssl && ./$(PROG) + gcov -b $(PROG).c + +$(PROG)_ansi: Makefile + $(MAKE) clean_coverage + gcc $(PEDANTIC) -ansi $(SOURCES) -o $(PROG)_ansi $(CFLAGS) -lssl && ./$(PROG)_ansi + gcov -b $(PROG).c + +$(PROG)_c99: Makefile + $(MAKE) clean_coverage + gcc $(PEDANTIC) -std=c99 $(SOURCES) -o $(PROG)_c99 $(CFLAGS) -lssl && ./$(PROG)_c99 + +$(PROG)_mingw.exe: Makefile + $(MAKE) clean_coverage + $(MINGW_GCC) $(SOURCES) -o $(PROG)_mingw.exe -W -Wall -Werror -I.. + +$(PROG).exe: + wine cl $(SOURCES) /MD /I.. /Zi $(CFLAGS_EXTRA) && echo "Compiled OK\n" + +win: $(PROG).exe + wine $(PROG).exe + +clean: clean_coverage + rm -rf $(PROG) $(PROG)_ansi $(PROG)_c99 *.txt *.exe *.obj *.o a.out *.pdb *.opt + +clean_coverage: + rm -rf *.gc* *.dSYM diff --git a/external/net_skeleton/test/ca.pem b/external/net_skeleton/test/ca.pem new file mode 100644 index 000000000..88da4b5b0 --- /dev/null +++ b/external/net_skeleton/test/ca.pem @@ -0,0 +1,49 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAv3/TSSi5hZDwMKG43eqe+GzR1lRMXVYt9I1Mr987v1DT99xR +Dcpfo/3aj6B/V/G67oPz+zbVZN/ZPvvA1Z82T7ixcBFsGIXgEWzxUm1UCUf51ftl +MlOaf24cdyegi0y8hRdkWLoC7w0vuMfrgR6cmpbI2LSDSMaXXX2qDoofQsFUYaJN +Nn3uqRK0ixs/jzbzbAT9q2BWYwySUX4VEgADpmi0FyANDjEhmdktxQW9l6IGGzF8 +M9mA053hIgZwo+9qf9X3nfNUTWMvisMQtxm0mRYgvD53Oix08VLs6bodNTVOLQoc +H0uH3CTs+H3Z0CkcZaAJe/kwCLFhls9ee3M0nQIDAQABAoIBAQCsADPWUi3QOg6C +n79cE5AVsigHSlAMxYshTIjErs0LWZ4J0mk66bpdoXTd7Fp0szojYYGS8f1ZTXXj +jFv3g7lUgZ9d+UgN/rDy9dcLIgeJDozoFZUfTthF/LC0lXMtqw7ou8n1p51a+Y0T +ev2cS9J9R1G+0uPYSgdKgcRsqsLJQS4fu5CAk9d0aeTTl009uxcn9yfTUjwOaR5J +PuNmunAEvhE/DGSkt5oNXo7t8Q2L3mYSM0MwKdDFqoQdZAV6TMTv22Mjb6SxOOnJ +r5gNK2BmM6oNPWvzY0PoI0LcLgFNDWIMqIq4mg73MdzszakaNRDlOYtLAuKbTF3Q +SDq8OkZBAoGBAOn6B5jBxSa+5GcIIeGqtiRhDMExyblt3Gk2gaw6UIZpvVDXPWWm +r0tkGJrYecmqesN7DGmmdkyx8KONF+yzYLxSsIEGNesvVYe6PXTDZYYI57As4Z4W +DFlCDt2FaKuMXxyOlUCiXg94z8IJBJ2ldCmmG34gBSvuFe6V5x4XE3crAoGBANGG +P7AWw6uygfjog6B2dFT6n+9UhpyJlqwfPi5eD9V5JXtWlH6xWi3dRfuYAIafg95I +W8/OZGHrj44gNCgYjvZHud+H3NPJPZ7lftoay5KeShBAa/pCd67OMxp1SvvONYcp +7TSwm5s+hOJvQOpw2wg0cXnfrxGKpGLOFaRddp9XAoGAFdeXefUs2G8dl1i1AQIU +utSsgiSJtlvBJblG5bMT7VhVqgRN4P1sg9c2TM5EoETf7PvBruMxS/uYgUwcnaYp +M6tser7/rZLfoyoJrqrHAXo3VsT50u4v/O0jwh5AJTOXdW0CFeSSb1NR4cVBvw3B +CFpPWrjWgsFZHsqzpqV01b0CgYEAkDft4pDowmgumlvBLlQaotuX9q6hsWHrOjKP +JG9OSswGhq0DrWj5/5PNNe5cfk2SARChUZpo8hWoTFXSUL8GuHKKeFgWIhjkt1iU +RiAne5ZEuIb/S9UweDwqZM3TfRtlMNIlGh1uHh+cbBfUAQsJWM5wRUk4QcTCfdgI +gYhrvCUCgYBB6u8Q49RjrTBxWK8bcZOjVhYNrd3xrCunFVMt2QAXGGrRaXpqUMnp +xNUmGe9vGux+s0TRguZcLEX3vX+wFyBfFKwZY9hSU7PFY/da8echpu37JasKvAov +5+5XWI5RgF+SFVk+Q7St2BlSJa/vBAH8vtrX9Dt/hN/VSo2mAPAyMQ== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDjjCCAnagAwIBAgIJAIOEuwkahzkOMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNV +BAMTAm5zMQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1Ymxp +bjAeFw0xNDA4MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5z +MQswCQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9/00kouYWQ8DChuN3qnvhs0dZU +TF1WLfSNTK/fO79Q0/fcUQ3KX6P92o+gf1fxuu6D8/s21WTf2T77wNWfNk+4sXAR +bBiF4BFs8VJtVAlH+dX7ZTJTmn9uHHcnoItMvIUXZFi6Au8NL7jH64EenJqWyNi0 +g0jGl119qg6KH0LBVGGiTTZ97qkStIsbP48282wE/atgVmMMklF+FRIAA6ZotBcg +DQ4xIZnZLcUFvZeiBhsxfDPZgNOd4SIGcKPvan/V953zVE1jL4rDELcZtJkWILw+ +dzosdPFS7Om6HTU1Ti0KHB9Lh9wk7Ph92dApHGWgCXv5MAixYZbPXntzNJ0CAwEA +AaOBmjCBlzAdBgNVHQ4EFgQUsz/nOHpjMkV8pk9dFpy41batoTcwaAYDVR0jBGEw +X4AUsz/nOHpjMkV8pk9dFpy41batoTehPKQ6MDgxCzAJBgNVBAMTAm5zMQswCQYD +VQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpboIJAIOEuwkahzkO +MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAEDOtAl7bgAXgcL3HRlV +H71tkUaok589PIqsTE4d8s8tFBZ92CyWD8ZPU46HbbyJXMFoFxiN7PvCzOBlgoZM +r80HJWZc9tSlqK0NIbIyk1aeM06+F8qB+8/vw/spIkdYzDv3avwyOrc6fFnEzbwz +5BFFrF2G9JajRKAP5snAV9iM8I2TD4l+w75MXXl7/DBEohdMBsTeDrrXj4q4sgoB +L/yLeCoK6inkMTU5DwcGbiqvNnZA+9T654qlAlKjPMObGGPphK5/QKcOnV7Qtdju +DHzDsDimdVbz9G1cxXs/AI/35GD7IDTdNTtmBhkf4/tsQ7Ua80xpIowb1fFUHmo1 +UAo= +-----END CERTIFICATE----- diff --git a/external/net_skeleton/test/client.pem b/external/net_skeleton/test/client.pem new file mode 100644 index 000000000..f0a7885e9 --- /dev/null +++ b/external/net_skeleton/test/client.pem @@ -0,0 +1,45 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAwV5xaK7ez2/TX7vSgJ0a3YbZj2l1VQ2rMzqO1Id01xlWbF/U +rebwhAdVtWcT9R6RaBTPDGaILkV38u77M2BxIHX4MSnR6WezoA2bGMgvt3+tq2N6 +q+xkj57vwBEqedBjscVtFkoWtsX8pKwtNlMB1NvTa8p5+BNsvpvzaDX+51+FotId +wvieQfQYgFg36HpOtOyyIV31rZ/5+qtoce8gU6wApHxmovTnQPoduNM6fOUJCHDd +Lz90EeBREtoTVgoWcKvQoCEwJQSBmeDZgkA8Q1OYmbYoS12tIyi8rTkseRj5BvPH +iXfNmHFKliAjvlsml5qI44I9DoagPubTf6qR5wIDAQABAoIBACZ6VZTgH0Ql22jU +ZhnjqUHloIsyEAABvUxvXZaa8bwPtavREfAc4UVUdFCpl0YSdBrC8URlbrnOZwT3 +WxMpILm139JgoP2R/iNeMbunsh8QkA1nuTRW0NfnZ4vPnqUou33XbFKgIY7zLMfT +3xdNQzMJHzP20Xh03RG81J2rCPMfLScTRo2XxcSxmhhS/p2WLk6pnmMHiNgYGGwX +gcdK5lIVjMMNxgcltC30x90v0o0GDRM8/+wua+/vfn8rr3iudv9IHzL8xIzpi6NY +CXJ8Kxd6Jtgsr3Boj5i6Mqi3Q/Trxt+rIA4bKAFXxwcp4+GmRIJtQFFiTWXpLCPC +tLT4CHECgYEA7iCbrGjWHJ4QsUWUGrGlw1/sQ0SIv9BdZm8RydHzpRVtQOi+YOuU +i6raVaXWzUBKgKcs/htVjAMTiePs/yhlU/MGXivz6uTX/nrD7ISJImmK2K50hgUe ++UBnFKmBMVaNxD9RFWPJkfmNXfW7nBkqSa9CxlBcYPuOcPtZDqRl+gkCgYEAz+HX +8wh3SHKb1cAI+o4caclpUTpGa9/zW4k+7gOh72WCKaqxTNvBvNyZGdXc9t5ToDSf +xxsDXWG10lcHBIGLj4QBEoSWp9I43lid5swY3mCo7CjTl+1l03IfDNaC6CYQFp5p +ZnKlsQUwR38t/uiyZpnnicCAZjqIfJbeQ5jD6G8CgYB8ufmwQa08ihJmN/KOVNRl +VF31EfWquqHhYHXpxx2eL23tXLszGtHQoioASIANPAqJ/oaTho+1aXsXc5oUP/1r +DlUciFsXgswb0APFY9pMewmt2xrPg+koVvJnIS25QQO6cguvb3gKDLNeLrMY3RmI +RNNt+nOYnMqMJSsNf1CmuQKBgQCiCZxWaCbyZcNqncFh7BvhqYlaM15o/6ulkhln +VZWIEUugRtjk2/bry9fa94TBORNeMSbKABhjVaJwTj2+GWw7dd2QHaGBNq/1QIX0 +POq1jAqf6kLkjbttUes6CosHgYPQ3bGylXLpxO2ZDV1A8Qj+SMDd8xsilEWHN+IQ +NqeeKQKBgQDe4c7VVG+WvRRKshTh8+tjzc9nXKE2AWgwnw729SMFZO/WqX2FPp2C +7C99XJTVBsCBy8VzuyaojeTKkag0YL3v6UTZYUeyu0YTHGQ33WVPaqdCAo840nmG +ttwHVqshB9c67HHiYOOFt1VmT3xW6x6yympUyRqR0L+BZ1wOS3h2vQ== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIC6DCCAdACBRQJQlZlMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw +CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4 +MzAxOTA3NDRaFw0yNDA4MjcxOTA3NDRaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK +EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMFecWiu3s9v01+70oCdGt2G2Y9pdVUNqzM6jtSH +dNcZVmxf1K3m8IQHVbVnE/UekWgUzwxmiC5Fd/Lu+zNgcSB1+DEp0elns6ANmxjI +L7d/ratjeqvsZI+e78ARKnnQY7HFbRZKFrbF/KSsLTZTAdTb02vKefgTbL6b82g1 +/udfhaLSHcL4nkH0GIBYN+h6TrTssiFd9a2f+fqraHHvIFOsAKR8ZqL050D6HbjT +OnzlCQhw3S8/dBHgURLaE1YKFnCr0KAhMCUEgZng2YJAPENTmJm2KEtdrSMovK05 +LHkY+Qbzx4l3zZhxSpYgI75bJpeaiOOCPQ6GoD7m03+qkecCAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAJ+wZ/IgAF5LIu0yOfJlaFRJLunKHZENigiVjYvkTdM7NI3O2 +1AZGY4O8H5Fs3YT5ZY3vas/n6IwWTk3o/JSPXojMFo82XkbI1k2cm3oLtwgEGN3p +s5yFsjZE3H7fQJ9wHIzESBPHFY6dwwgMsNENuAM2zkwFpbAkisKhjK+EyUCXauok +7zJY6RVPMaNojsje4iE/SBtSOnK/9WDBAgpCznHrSChJmKs4FsU7ZTO+Dg+0vQln +l8/yBcEGAFe0GA2D9NvZKH5IoNmitvtU9zdNDK4dzC3Q+C28IjW5jE8peDFtdGs1 +P0u4kRxmb4UH1DchgoWlZjL2lSFScJ7L4xY2aQ== +-----END CERTIFICATE----- diff --git a/external/net_skeleton/test/server.pem b/external/net_skeleton/test/server.pem new file mode 100644 index 000000000..5ae2c837c --- /dev/null +++ b/external/net_skeleton/test/server.pem @@ -0,0 +1,45 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1mONQ0hAXOL9lb15Pz4fqXXNHREsF3a7/NoMJdQDclx0+a32 +MhuHcO6R7Fhsc0mZMuzbmAFLMmIIgXPMKQBZLoA12yCBlZPyKFoWUhFrLa3gUjO6 +CZlBKqkUVEACpVrQ41ihapeeUHa0uryt3tXwMn2/853yzi1uciGYi4ULTy3yTE/n +qRIVJLiBDSC9WNFLg26f/W4YRW7tANOk2b/W/Ws9B/n7vNDgHG7Lpd38YTpFhhXT +n3xlt/VcczkQhW79Moh6/lY6sLg6H15EjHKHeTn8t9BRm+qYi/CvC258YF/Qz/qK +agSsLT/3FrQ6+aQgg/Eyao0IWAql49PQNxuwPQIDAQABAoIBAQC5y3S1BnyhAyb5 +Ckd1g4U0+x5TPnqTqxanvuAgOGj0RyQo7ZYbPrhWKqrTxJ3YG8Rk2dhFF3nvo/3z +EkOwlNi07++8g6NJ2flW9xu469eSsslg8+saPnK3Yeh4SzD/1ICLRlg9ZECTQwzF +eJbGM2oCl/AuVIgEHmNFDdCBuT9f0b7j3/Z3aK3lKzqzBYQgZ5fd8UxT+Kn4oAuS +cLr3lQT1s6xZOAYn7O2GvXEC+yMMbvm0a97MdwSpQez1WcE9YxtCgAWwn5EmSXlh +296iLtbaM1wgYOykJUOUoSgijf8pUfotk4Zj/y1KPHXePgAlyGCtE1zasiYb5K+5 +LuajD++BAoGBAPpKWLNzQBwQLiFaJgt6bLOxlEUR+EnjdFePDPJtyCCCiKJiKO5c +Z5s/FT1JDQawouhjQwYqT48hbGBPjWRHkSkzB7+cg6FVSKkQRYX2TsSFvN+KCu32 +oSgDV9cFo68v1csoZIQ41TtHC82db4OTv9MPUe3Glujnep1TOTwspAM1AoGBANtH +i+HWKOxOm7f/R2VX1ys9UjkAK+msac512XWSLAzBs7NFnB7iJ7m3Bh3ydb1ZiTgW +l6bIdoT8TLPYNIXJ6uohhxPU5h3v81PHqIuJMBtmHCQjq3nxeH9mOsfjOFvS1cQa +At45F9pK/5sQpOkkaBGSv8jXUFIKBEDBErourVHpAoGAK0gSAK4sZu3xXDkfnRqF +k6lgr3UFD5nys3V8UqvjUKPiBtqco2N9Ux5ciOWKCB8hfLg1jephKaoo+JqpI68w +jgRSEbN6G7sIvpueuiS2yEssNyfC7hWZFrdFSFykSpYmDWSlxSuizAZkJyFTeFhj +cpcSnuCZlhr5XB1ZJ2u8zQUCgYEAke5QgpCDFZjO+ynR+vj1gppBwRuDHfUXSUaW +3S7VT/wNOq6F0uvRYkASuxVkFAqlToWCkYVxktlRtpKZibwyMXT0r1cNejj5Z/VF +Du/S6zkOW2K9uN7hwW9oiSSHmlx61RI2fGvkmus0pp7yERKgi6ltJx1cH+z4nZug +efWcdRkCgYBy+XdmsxgNZOunlSC6VZiD0Ve/VFrCtKPWUivKDAZZPKl0T/1tbTwb +I/N4zTF82jx88rDz+6jN5nOy9qbSR5TeCy6WlBesTvXm49awr5jSK3WkcLgmO+JI +Zr2ozCBhUG6RvVsUPp2kXEsmwZMV/e9faFAlIXeJhKum6hZmfOgodg== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIC6DCCAdACBRQJQlZkMA0GCSqGSIb3DQEBBQUAMDgxCzAJBgNVBAMTAm5zMQsw +CQYDVQQKEwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkR1YmxpbjAeFw0xNDA4 +MzAxOTA3NDNaFw0yNDA4MjcxOTA3NDNaMDgxCzAJBgNVBAMTAm5zMQswCQYDVQQK +EwJuczELMAkGA1UEBhMCSUUxDzANBgNVBAcTBkdhbHdheTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANZjjUNIQFzi/ZW9eT8+H6l1zR0RLBd2u/zaDCXU +A3JcdPmt9jIbh3DukexYbHNJmTLs25gBSzJiCIFzzCkAWS6ANdsggZWT8ihaFlIR +ay2t4FIzugmZQSqpFFRAAqVa0ONYoWqXnlB2tLq8rd7V8DJ9v/Od8s4tbnIhmIuF +C08t8kxP56kSFSS4gQ0gvVjRS4Nun/1uGEVu7QDTpNm/1v1rPQf5+7zQ4Bxuy6Xd +/GE6RYYV0598Zbf1XHM5EIVu/TKIev5WOrC4Oh9eRIxyh3k5/LfQUZvqmIvwrwtu +fGBf0M/6imoErC0/9xa0OvmkIIPxMmqNCFgKpePT0DcbsD0CAwEAATANBgkqhkiG +9w0BAQUFAAOCAQEAoVXK97WA24tp3JyPBJKr28gFSUtOBNDPdY8atWaqw7PwUIIM +qhs3BTag96tgSoaISRwRphz2LM1Cl+QlItYXySAnxPKrUsA0S6DlxnA6Hq3s2wTR +6yIT7oDUDKcWkVQcQmuNGdfxCvZXkCih9lnQn++xHcuVn9mZmjXW2xk42ljDTZCp +CM29betpcmuho6sFXsBhY7WjQWg7UpRZat0bOwleS4fsePebMKrnr/6cq4bVw59U +XvhSFBlLoGMYteJ82fOYH6pUO1hiPr6ww5d819LPcJEcRpcxCdQZqIq680Kp7+GY +0wkyOYr0gkNwWVP7IUZ0FExaQ/s54g71Kd0OgA== +-----END CERTIFICATE----- diff --git a/external/net_skeleton/test/unit_test.c b/external/net_skeleton/test/unit_test.c new file mode 100644 index 000000000..c433f9f16 --- /dev/null +++ b/external/net_skeleton/test/unit_test.c @@ -0,0 +1,558 @@ +/* + * Copyright (c) 2014 Cesanta Software Limited + * All rights reserved + * This software is dual-licensed: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. For the terms of this + * license, see . + * + * You are free to use this software under the terms of the GNU General + * Public License, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * Alternatively, you can license this software under a commercial + * license, as set out in . + */ + +#include "net_skeleton.h" + +#define FAIL(str, line) do { \ + printf("%s:%d:1 [%s]\n", __FILE__, line, str); \ + return str; \ +} while (0) + +#define ASSERT(expr) do { \ + static_num_tests++; \ + if (!(expr)) FAIL(#expr, __LINE__); \ +} while (0) + +#define RUN_TEST(test) do { const char *msg = test(); \ + if (msg) return msg; } while (0) + +#define HTTP_PORT "45772" +#define LOOPBACK_IP "127.0.0.1" +#define LISTENING_ADDR LOOPBACK_IP ":" HTTP_PORT + +static int static_num_tests = 0; + +static const char *test_iobuf(void) { + struct iobuf io; + + iobuf_init(&io, 0); + ASSERT(io.buf == NULL && io.len == 0 && io.size == 0); + iobuf_free(&io); + ASSERT(io.buf == NULL && io.len == 0 && io.size == 0); + + iobuf_init(&io, 10); + ASSERT(io.buf != NULL && io.len == 0 && io.size == 10); + iobuf_free(&io); + ASSERT(io.buf == NULL && io.len == 0 && io.size == 0); + + return NULL; +} + +static void poll_mgr(struct ns_mgr *mgr, int num_iterations) { + while (num_iterations-- > 0) { + ns_mgr_poll(mgr, 1); + } +} + +static void eh1(struct ns_connection *nc, int ev, void *ev_data) { + struct iobuf *io = &nc->recv_iobuf; + + switch (ev) { + case NS_CONNECT: + ns_printf(nc, "%d %s there", * (int *) ev_data, "hi"); + break; + case NS_RECV: + if (nc->listener != NULL) { + ns_printf(nc, "%d", (int) io->len); + iobuf_remove(io, io->len); + } else if (io->len == 2 && memcmp(io->buf, "10", 2) == 0) { + sprintf((char *) nc->user_data, "%s", "ok!"); + nc->flags |= NSF_CLOSE_IMMEDIATELY; + } + break; + default: + break; + } +} + +#define S_PEM "server.pem" +#define C_PEM "client.pem" +#define CA_PEM "ca.pem" + +static const char *test_mgr_with_ssl(int use_ssl) { + char addr[100] = "127.0.0.1:0", ip[sizeof(addr)], buf[100] = ""; + struct ns_mgr mgr; + struct ns_connection *nc; + int port, port2; + + ns_mgr_init(&mgr, NULL); + /* mgr.hexdump_file = "/dev/stdout"; */ + + ASSERT((nc = ns_bind(&mgr, addr, eh1)) != NULL); + port2 = htons(nc->sa.sin.sin_port); + ASSERT(port2 > 0); + if (use_ssl) { + ASSERT(ns_set_ssl(nc, S_PEM, CA_PEM) == NULL); + } + + ns_sock_to_str(nc->sock, addr, sizeof(addr), 3); + ASSERT(sscanf(addr, "%[^:]:%d", ip, &port) == 2); + ASSERT(strcmp(ip, "127.0.0.1") == 0); + ASSERT(port == port2); + + ASSERT((nc = ns_connect(&mgr, addr, eh1)) != NULL); + if (use_ssl) { + ASSERT(ns_set_ssl(nc, C_PEM, CA_PEM) == NULL); + } + nc->user_data = buf; + poll_mgr(&mgr, 50); + + ASSERT(strcmp(buf, "ok!") == 0); + + ns_mgr_free(&mgr); + return NULL; +} + +static const char *test_mgr(void) { + return test_mgr_with_ssl(0); +} + +#ifdef NS_ENABLE_SSL +static const char *test_ssl(void) { + return test_mgr_with_ssl(1); +} +#endif + +static const char *test_to64(void) { + ASSERT(to64("0") == 0); + ASSERT(to64("") == 0); + ASSERT(to64("123") == 123); + ASSERT(to64("-34") == -34); + ASSERT(to64("3566626116") == 3566626116U); + return NULL; +} + +#if 0 +static const char *test_parse_address(void) { + static const char *valid[] = { + "1", "1.2.3.4:1", "tcp://123", "udp://0.0.0.0:99", "ssl://17", + "ssl://900:a.pem:b.pem", "ssl://1.2.3.4:9000:aa.pem", +#if defined(NS_ENABLE_IPV6) + "udp://[::1]:123", "[3ffe:2a00:100:7031::1]:900", +#endif + NULL + }; + static const int protos[] = {SOCK_STREAM, SOCK_STREAM, SOCK_STREAM, + SOCK_DGRAM, SOCK_STREAM, SOCK_STREAM, SOCK_STREAM, SOCK_DGRAM, SOCK_STREAM}; + static const int use_ssls[] = {0, 0, 0, 0, 1, 1, 1, 0, 0}; + static const char *invalid[] = { + "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p", "blah://12", NULL + }; + union socket_address sa; + char cert[100], ca[100]; + int i, proto, use_ssl; + + for (i = 0; valid[i] != NULL; i++) { + ASSERT(ns_parse_address(valid[i], &sa, &proto, &use_ssl, cert, ca) != 0); + ASSERT(proto == protos[i]); + ASSERT(use_ssl == use_ssls[i]); + } + + for (i = 0; invalid[i] != NULL; i++) { + ASSERT(ns_parse_address(invalid[i], &sa, &proto, &use_ssl, cert, ca) == 0); + } + ASSERT(ns_parse_address("0", &sa, &proto, &use_ssl, cert, ca) != 0); + + return NULL; +} +#endif + +static int avt(char **buf, size_t buf_size, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = ns_avprintf(buf, buf_size, fmt, ap); + va_end(ap); + return result; +} + +static const char *test_alloc_vprintf(void) { + char buf[5], *p = buf; + + ASSERT(avt(&p, sizeof(buf), "%d", 123) == 3); + ASSERT(p == buf); + ASSERT(strcmp(p, "123") == 0); + + ASSERT(avt(&p, sizeof(buf), "%d", 123456789) == 9); + ASSERT(p != buf); + ASSERT(strcmp(p, "123456789") == 0); + free(p); + + return NULL; +} + +static const char *test_socketpair(void) { + sock_t sp[2]; + static const char foo[] = "hi there"; + char buf[20]; + + ASSERT(ns_socketpair2(sp, SOCK_DGRAM) == 1); + ASSERT(sizeof(foo) < sizeof(buf)); + + /* Send string in one direction */ + ASSERT(send(sp[0], foo, sizeof(foo), 0) == sizeof(foo)); + ASSERT(recv(sp[1], buf, sizeof(buf), 0) == sizeof(foo)); + ASSERT(strcmp(buf, "hi there") == 0); + + /* Now in opposite direction */ + ASSERT(send(sp[1], foo, sizeof(foo), 0) == sizeof(foo)); + ASSERT(recv(sp[0], buf, sizeof(buf), 0) == sizeof(foo)); + ASSERT(strcmp(buf, "hi there") == 0); + + closesocket(sp[0]); + closesocket(sp[1]); + + return NULL; +} + +static void eh2(struct ns_connection *nc, int ev, void *p) { + (void) p; + switch (ev) { + case NS_RECV: + strcpy((char *) nc->user_data, nc->recv_iobuf.buf); + break; + default: + break; + } +} + +static void *thread_func(void *param) { + sock_t sock = * (sock_t *) param; + send(sock, ":-)", 4, 0); + return NULL; +} + +static const char *test_thread(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + sock_t sp[2]; + char buf[20]; + + ASSERT(ns_socketpair(sp) == 1); + ns_start_thread(thread_func, &sp[1]); + + ns_mgr_init(&mgr, NULL); + ASSERT((nc = ns_add_sock(&mgr, sp[0], eh2)) != NULL); + nc->user_data = buf; + poll_mgr(&mgr, 50); + ASSERT(strcmp(buf, ":-)") == 0); + ns_mgr_free(&mgr); + closesocket(sp[1]); + + return NULL; +} + +static void eh3(struct ns_connection *nc, int ev, void *p) { + struct iobuf *io = &nc->recv_iobuf; + (void) p; + + if (ev == NS_RECV) { + memcpy((char *) nc->mgr->user_data, io->buf, io->len); + } +} + +static const char *test_udp(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + const char *address = "udp://127.0.0.1:7878"; + char buf[20] = ""; + + ns_mgr_init(&mgr, buf); + ASSERT(ns_bind(&mgr, address, eh3) != NULL); + ASSERT((nc = ns_connect(&mgr, address, eh3)) != NULL); + ns_printf(nc, "%s", "boo!"); + + { int i; for (i = 0; i < 50; i++) ns_mgr_poll(&mgr, 1); } + ASSERT(memcmp(buf, "boo!", 4) == 0); + ns_mgr_free(&mgr); + + return NULL; +} + +static const char *test_parse_http_message(void) { + static const char *a = "GET / HTTP/1.0\n\n"; + static const char *b = "GET /blah HTTP/1.0\r\nFoo: bar \r\n\r\n"; + static const char *c = "get b c\nz: k \nb: t\nvvv\n\n xx"; + static const char *d = "a b c\nContent-Length: 21 \nb: t\nvvv\n\n"; + struct ns_str *v; + struct http_message req; + + ASSERT(ns_parse_http("\b23", 3, &req) == -1); + ASSERT(ns_parse_http("get\n\n", 5, &req) == -1); + ASSERT(ns_parse_http(a, strlen(a) - 1, &req) == 0); + ASSERT(ns_parse_http(a, strlen(a), &req) == (int) strlen(a)); + + ASSERT(ns_parse_http(b, strlen(b), &req) == (int) strlen(b)); + ASSERT(req.header_names[0].len == 3); + ASSERT(req.header_values[0].len == 3); + ASSERT(req.header_names[1].p == NULL); + + ASSERT(ns_parse_http(c, strlen(c), &req) == (int) strlen(c) - 3); + ASSERT(req.header_names[2].p == NULL); + ASSERT(req.header_names[0].p != NULL); + ASSERT(req.header_names[1].p != NULL); + ASSERT(memcmp(req.header_values[1].p, "t", 1) == 0); + ASSERT(req.header_names[1].len == 1); + ASSERT(req.body.len == 0); + + ASSERT(ns_parse_http(d, strlen(d), &req) == (int) strlen(d)); + ASSERT(req.body.len == 21); + ASSERT(req.message.len == 21 + strlen(d)); + ASSERT(ns_get_http_header(&req, "foo") == NULL); + ASSERT((v = ns_get_http_header(&req, "contENT-Length")) != NULL); + ASSERT(v->len == 2 && memcmp(v->p, "21", 2) == 0); + + return NULL; +} + +static void cb1(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + + if (ev == NS_HTTP_REQUEST) { + ns_printf(nc, "HTTP/1.0 200 OK\n\n[%.*s %d]", + (int) hm->uri.len, hm->uri.p, (int) hm->body.len); + nc->flags |= NSF_FINISHED_SENDING_DATA; + } +} + +static void cb2(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + + if (ev == NS_HTTP_REPLY) { + memcpy(nc->user_data, hm->body.p, hm->body.len); + nc->flags |= NSF_CLOSE_IMMEDIATELY; + } +} + +static const char *test_http(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + const char *local_addr = "127.0.0.1:7777"; + char buf[20] = ""; + + ns_mgr_init(&mgr, NULL); + ASSERT((nc = ns_bind(&mgr, local_addr, cb1)) != NULL); + ns_set_protocol_http_websocket(nc); + + /* Valid HTTP request. Pass test buffer to the callback. */ + ASSERT((nc = ns_connect(&mgr, local_addr, cb2)) != NULL); + ns_set_protocol_http_websocket(nc); + nc->user_data = buf; + ns_printf(nc, "%s", "POST /foo HTTP/1.0\nContent-Length: 10\n\n" + "0123456789"); + + /* Invalid HTTP request */ + ASSERT((nc = ns_connect(&mgr, local_addr, cb2)) != NULL); + ns_set_protocol_http_websocket(nc); + ns_printf(nc, "%s", "bl\x03\n\n"); + poll_mgr(&mgr, 50); + ns_mgr_free(&mgr); + + /* Check that test buffer has been filled by the callback properly. */ + ASSERT(strcmp(buf, "[/foo 10]") == 0); + + return NULL; +} + +static void cb3(struct ns_connection *nc, int ev, void *ev_data) { + struct websocket_message *wm = (struct websocket_message *) ev_data; + + if (ev == NS_WEBSOCKET_FRAME) { + const char *reply = wm->size == 2 && !memcmp(wm->data, "hi", 2) ? "A": "B"; + ns_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", reply); + } +} + +static void cb4(struct ns_connection *nc, int ev, void *ev_data) { + struct websocket_message *wm = (struct websocket_message *) ev_data; + + if (ev == NS_WEBSOCKET_FRAME) { + memcpy(nc->user_data, wm->data, wm->size); + ns_send_websocket_frame(nc, WEBSOCKET_OP_CLOSE, NULL, 0); + } else if (ev == NS_WEBSOCKET_HANDSHAKE_DONE) { + /* Send "hi" to server. server must reply "A". */ + ns_printf_websocket_frame(nc, WEBSOCKET_OP_TEXT, "%s", "hi"); + } +} + +static const char *test_websocket(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + const char *local_addr = "127.0.0.1:7778"; + char buf[20] = ""; + + ns_mgr_init(&mgr, NULL); + /* mgr.hexdump_file = "/dev/stdout"; */ + ASSERT((nc = ns_bind(&mgr, local_addr, cb3)) != NULL); + ns_set_protocol_http_websocket(nc); + + /* Websocket request */ + ASSERT((nc = ns_connect(&mgr, local_addr, cb4)) != NULL); + ns_set_protocol_http_websocket(nc); + nc->user_data = buf; + ns_send_websocket_handshake(nc, "/ws", NULL); + poll_mgr(&mgr, 50); + ns_mgr_free(&mgr); + + /* Check that test buffer has been filled by the callback properly. */ + ASSERT(strcmp(buf, "A") == 0); + + return NULL; +} + +static int rpc_sum(char *buf, int len, struct ns_rpc_request *req) { + double sum = 0; + int i; + + if (req->params[0].type != JSON_TYPE_ARRAY) { + return ns_rpc_create_std_error(buf, len, req, + JSON_RPC_INVALID_PARAMS_ERROR); + } + + for (i = 0; i < req->params[0].num_desc; i++) { + if (req->params[i + 1].type != JSON_TYPE_NUMBER) { + return ns_rpc_create_std_error(buf, len, req, + JSON_RPC_INVALID_PARAMS_ERROR); + } + sum += strtod(req->params[i + 1].ptr, NULL); + } + return ns_rpc_create_reply(buf, len, req, "f", sum); +} + +static void rpc_server(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + static const char *methods[] = { "sum", NULL }; + static ns_rpc_handler_t handlers[] = { rpc_sum, NULL }; + char buf[100]; + + switch (ev) { + case NS_HTTP_REQUEST: + ns_rpc_dispatch(hm->body.p, hm->body.len, buf, sizeof(buf), + methods, 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; + } +} + +static void rpc_client(struct ns_connection *nc, int ev, void *ev_data) { + struct http_message *hm = (struct http_message *) ev_data; + struct ns_rpc_reply rpc_reply; + struct ns_rpc_error rpc_error; + struct json_token toks[20]; + char buf[100]; + + switch (ev) { + case NS_CONNECT: + ns_rpc_create_request(buf, sizeof(buf), "sum", "1", "[f,f,f]", + 1.0, 2.0, 13.0); + ns_printf(nc, "POST / HTTP/1.0\r\nContent-Type: application/json\r\n" + "Content-Length: %d\r\n\r\n%s", (int) strlen(buf), buf); + case NS_HTTP_REPLY: + ns_rpc_parse_reply(hm->body.p, hm->body.len, + toks, sizeof(toks) / sizeof(toks[0]), + &rpc_reply, &rpc_error); + sprintf((char *) nc->user_data, "%.*s", + rpc_reply.result == NULL ? 1 : rpc_reply.result->len, + rpc_reply.result == NULL ? "?" : rpc_reply.result->ptr); + break; + default: + break; + } +} + +static const char *test_rpc(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + const char *local_addr = "127.0.0.1:7779"; + char buf[100] = ""; + + ns_mgr_init(&mgr, NULL); + + ASSERT((nc = ns_bind(&mgr, local_addr, rpc_server)) != NULL); + ns_set_protocol_http_websocket(nc); + + ASSERT((nc = ns_connect(&mgr, local_addr, rpc_client)) != NULL); + ns_set_protocol_http_websocket(nc); + nc->user_data = buf; + + poll_mgr(&mgr, 50); + ns_mgr_free(&mgr); + + ASSERT(strcmp(buf, "16") == 0); + + return NULL; +} + +static void cb5(struct ns_connection *nc, int ev, void *ev_data) { + switch (ev) { + case NS_CONNECT: + sprintf((char *) nc->user_data, "%d", * (int *) ev_data); + break; + default: + break; + } +} + +static const char *test_connect_fail(void) { + struct ns_mgr mgr; + struct ns_connection *nc; + char buf[100] = "0"; + + ns_mgr_init(&mgr, NULL); + ASSERT((nc = ns_connect(&mgr, "127.0.0.1:33211", cb5)) != NULL); + nc->user_data = buf; + poll_mgr(&mgr, 50); + ns_mgr_free(&mgr); + + /* printf("failed connect status: [%s]\n", buf); */ + ASSERT(strcmp(buf, "0") != 0); + + return NULL; +} + +static const char *run_all_tests(void) { + RUN_TEST(test_iobuf); +#if 0 + RUN_TEST(test_parse_address); +#endif + RUN_TEST(test_connect_fail); + RUN_TEST(test_to64); + RUN_TEST(test_alloc_vprintf); + RUN_TEST(test_socketpair); + RUN_TEST(test_thread); + RUN_TEST(test_mgr); + RUN_TEST(test_parse_http_message); + RUN_TEST(test_http); + RUN_TEST(test_websocket); + RUN_TEST(test_rpc); +#ifdef NS_ENABLE_SSL + RUN_TEST(test_ssl); +#endif + RUN_TEST(test_udp); + return NULL; +} + +int __cdecl main(void) { + const char *fail_msg = run_all_tests(); + printf("%s, tests run: %d\n", fail_msg ? "FAIL" : "PASS", static_num_tests); + return fail_msg == NULL ? EXIT_SUCCESS : EXIT_FAILURE; +} From f5191af24b2922826541577bd0d4ecffe8b45f54 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 11 Apr 2015 23:53:57 +0530 Subject: [PATCH 35/45] Wallet seems functional with new IPC --- src/ipc/daemon_ipc_handlers.cpp | 8 +- src/ipc/include/wap_client.h | 4 +- src/ipc/include/wap_client_engine.inc | 22 +++--- src/ipc/include/wap_proto.h | 14 ++-- src/ipc/wap_client/wap_client.c | 5 +- src/ipc/wap_proto.c | 104 +++++++++++++++++++------- src/wallet/wallet2.cpp | 12 +-- src/wallet/wallet2.h | 23 +----- 8 files changed, 113 insertions(+), 79 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 9b7acc3c8..d649def04 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -216,10 +216,11 @@ namespace IPC wap_proto_set_status(message, STATUS_CORE_BUSY); return; } - const char *tx_id = wap_proto_tx_id(message); + zchunk_t *tx_id = wap_proto_tx_id(message); crypto::hash hash; - memcpy(hash.data, tx_id + 1, crypto::HASH_SIZE); + memcpy(hash.data, zchunk_data(tx_id), crypto::HASH_SIZE); std::vector output_indexes; + bool r = core->get_tx_outputs_gindexs(hash, output_indexes); if (!r) { @@ -249,6 +250,7 @@ namespace IPC for (unsigned int i = 0; i < amounts_count; i++) { req.amounts.push_back(amounts[i]); } + cryptonote::COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response res; if (!core->get_random_outs_for_amounts(req, res)) { @@ -287,8 +289,6 @@ namespace IPC result_json.Accept(writer); std::string block_string = buffer.GetString(); -std::cout << block_string << std::endl; - zframe_t *frame = zframe_new(block_string.c_str(), block_string.length()); wap_proto_set_random_outputs(message, &frame); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 4d37830bc..33f9ac3e0 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -80,7 +80,7 @@ WAP_EXPORT int // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_get (wap_client_t *self, const char *tx_id); + wap_client_get (wap_client_t *self, zchunk_t **tx_id_p); // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. @@ -90,7 +90,7 @@ WAP_EXPORT int // Ask for tx output indexes. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_output_indexes (wap_client_t *self, const char *tx_id); + wap_client_output_indexes (wap_client_t *self, zchunk_t **tx_id_p); // Ask for tx output indexes. // Returns >= 0 if successful, -1 if interrupted. diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 8c39bfd0e..08d999458 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -136,7 +136,7 @@ struct _client_args_t { zlist_t *block_ids; uint64_t start_height; zchunk_t *tx_as_hex; - char *tx_id; + zchunk_t *tx_id; uint64_t outs_count; zframe_t *amounts; char *address; @@ -281,7 +281,7 @@ s_client_destroy (s_client_t **self_p) zstr_free (&self->args.identity); zlist_destroy (&self->args.block_ids); zchunk_destroy (&self->args.tx_as_hex); - zstr_free (&self->args.tx_id); + zchunk_destroy (&self->args.tx_id); zframe_destroy (&self->args.amounts); zstr_free (&self->args.address); client_terminate (&self->client); @@ -1450,8 +1450,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) } else if (streq (method, "GET")) { - zstr_free (&self->args.tx_id); - zsock_recv (self->cmdpipe, "s", &self->args.tx_id); + zchunk_destroy (&self->args.tx_id); + zsock_recv (self->cmdpipe, "p", &self->args.tx_id); s_client_execute (self, get_event); } else @@ -1460,8 +1460,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) } else if (streq (method, "OUTPUT INDEXES")) { - zstr_free (&self->args.tx_id); - zsock_recv (self->cmdpipe, "s", &self->args.tx_id); + zchunk_destroy (&self->args.tx_id); + zsock_recv (self->cmdpipe, "p", &self->args.tx_id); s_client_execute (self, output_indexes_event); } else @@ -1855,11 +1855,12 @@ wap_client_put (wap_client_t *self, zchunk_t **tx_as_hex_p) // Returns >= 0 if successful, -1 if interrupted. int -wap_client_get (wap_client_t *self, const char *tx_id) +wap_client_get (wap_client_t *self, zchunk_t **tx_id_p) { assert (self); - zsock_send (self->actor, "ss", "GET", tx_id); + zsock_send (self->actor, "sp", "GET", *tx_id_p); + *tx_id_p = NULL; // Take ownership of tx_id if (s_accept_reply (self, "GET OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; @@ -1887,11 +1888,12 @@ wap_client_save (wap_client_t *self) // Returns >= 0 if successful, -1 if interrupted. int -wap_client_output_indexes (wap_client_t *self, const char *tx_id) +wap_client_output_indexes (wap_client_t *self, zchunk_t **tx_id_p) { assert (self); - zsock_send (self->actor, "ss", "OUTPUT INDEXES", tx_id); + zsock_send (self->actor, "sp", "OUTPUT INDEXES", *tx_id_p); + *tx_id_p = NULL; // Take ownership of tx_id if (s_accept_reply (self, "OUTPUT INDEXES OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index eb3cadd63..38e34ad8b 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -49,7 +49,7 @@ PUT-OK, or ERROR. status number 8 Transaction ID OUTPUT_INDEXES - Ask for tx output indexes. - tx_id string Transaction ID + tx_id chunk Transaction ID OUTPUT_INDEXES_OK - Daemon returns tx output indexes. status number 8 Status @@ -65,7 +65,7 @@ PUT-OK, or ERROR. GET - Wallet requests transaction data from the daemon. Daemon replies with GET-OK, or ERROR. - tx_id string Transaction ID + tx_id chunk Transaction ID GET_OK - Daemon replies with transaction data. tx_data chunk Transaction data @@ -239,11 +239,15 @@ zchunk_t * void wap_proto_set_tx_as_hex (wap_proto_t *self, zchunk_t **chunk_p); -// Get/set the tx_id field -const char * +// Get a copy of the tx_id field +zchunk_t * wap_proto_tx_id (wap_proto_t *self); +// Get the tx_id field and transfer ownership to caller +zchunk_t * + wap_proto_get_tx_id (wap_proto_t *self); +// Set the tx_id field, transferring ownership from caller void - wap_proto_set_tx_id (wap_proto_t *self, const char *value); + wap_proto_set_tx_id (wap_proto_t *self, zchunk_t **chunk_p); // Get a copy of the o_indexes field zframe_t * diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 50d737439..d706f5457 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -155,7 +155,7 @@ prepare_blocks_command (client_t *self) static void prepare_get_output_indexes_command (client_t *self) { - wap_proto_set_tx_id (self->message, self->args->tx_id); + wap_proto_set_tx_id (self->message, &self->args->tx_id); } // --------------------------------------------------------------------------- @@ -213,7 +213,7 @@ signal_have_put_ok (client_t *self) static void prepare_get_command (client_t *self) { - wap_proto_set_tx_id (self->message, self->args->tx_id); + wap_proto_set_tx_id (self->message, &self->args->tx_id); } @@ -359,6 +359,7 @@ signal_server_not_present (client_t *self) static void prepare_get_random_outs_command (client_t *self) { + wap_proto_set_outs_count(self->message, self->args->outs_count); wap_proto_set_amounts (self->message, &self->args->amounts); } diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 16dd0a964..7681536b9 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -49,7 +49,7 @@ struct _wap_proto_t { // Transaction as hex zchunk_t *tx_as_hex; // Transaction ID - char tx_id [256]; + zchunk_t *tx_id; // Output Indexes zframe_t *o_indexes; // Outs count @@ -245,6 +245,7 @@ wap_proto_destroy (wap_proto_t **self_p) zlist_destroy (&self->block_ids); zmsg_destroy (&self->block_data); zchunk_destroy (&self->tx_as_hex); + zchunk_destroy (&self->tx_id); zframe_destroy (&self->o_indexes); zframe_destroy (&self->amounts); zframe_destroy (&self->random_outputs); @@ -367,7 +368,17 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) break; case WAP_PROTO_OUTPUT_INDEXES: - GET_STRING (self->tx_id); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: tx_id is missing data"); + goto malformed; + } + zchunk_destroy (&self->tx_id); + self->tx_id = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } break; case WAP_PROTO_OUTPUT_INDEXES_OK: @@ -404,7 +415,17 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) break; case WAP_PROTO_GET: - GET_STRING (self->tx_id); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: tx_id is missing data"); + goto malformed; + } + zchunk_destroy (&self->tx_id); + self->tx_id = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } break; case WAP_PROTO_GET_OK: @@ -520,7 +541,9 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += 8; // status break; case WAP_PROTO_OUTPUT_INDEXES: - frame_size += 1 + strlen (self->tx_id); + frame_size += 4; // Size is 4 octets + if (self->tx_id) + frame_size += zchunk_size (self->tx_id); break; case WAP_PROTO_OUTPUT_INDEXES_OK: frame_size += 8; // status @@ -532,7 +555,9 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += 8; // status break; case WAP_PROTO_GET: - frame_size += 1 + strlen (self->tx_id); + frame_size += 4; // Size is 4 octets + if (self->tx_id) + frame_size += zchunk_size (self->tx_id); break; case WAP_PROTO_GET_OK: frame_size += 4; // Size is 4 octets @@ -606,7 +631,15 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_OUTPUT_INDEXES: - PUT_STRING (self->tx_id); + if (self->tx_id) { + PUT_NUMBER4 (zchunk_size (self->tx_id)); + memcpy (self->needle, + zchunk_data (self->tx_id), + zchunk_size (self->tx_id)); + self->needle += zchunk_size (self->tx_id); + } + else + PUT_NUMBER4 (0); // Empty chunk break; case WAP_PROTO_OUTPUT_INDEXES_OK: @@ -625,7 +658,15 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_GET: - PUT_STRING (self->tx_id); + if (self->tx_id) { + PUT_NUMBER4 (zchunk_size (self->tx_id)); + memcpy (self->needle, + zchunk_data (self->tx_id), + zchunk_size (self->tx_id)); + self->needle += zchunk_size (self->tx_id); + } + else + PUT_NUMBER4 (0); // Empty chunk break; case WAP_PROTO_GET_OK: @@ -757,10 +798,7 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_OUTPUT_INDEXES: zsys_debug ("WAP_PROTO_OUTPUT_INDEXES:"); - if (self->tx_id) - zsys_debug (" tx_id='%s'", self->tx_id); - else - zsys_debug (" tx_id="); + zsys_debug (" tx_id=[ ... ]"); break; case WAP_PROTO_OUTPUT_INDEXES_OK: @@ -795,10 +833,7 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_GET: zsys_debug ("WAP_PROTO_GET:"); - if (self->tx_id) - zsys_debug (" tx_id='%s'", self->tx_id); - else - zsys_debug (" tx_id="); + zsys_debug (" tx_id=[ ... ]"); break; case WAP_PROTO_GET_OK: @@ -1159,24 +1194,35 @@ wap_proto_set_tx_as_hex (wap_proto_t *self, zchunk_t **chunk_p) // -------------------------------------------------------------------------- -// Get/set the tx_id field +// Get the tx_id field without transferring ownership -const char * +zchunk_t * wap_proto_tx_id (wap_proto_t *self) { assert (self); return self->tx_id; } +// Get the tx_id field and transfer ownership to caller + +zchunk_t * +wap_proto_get_tx_id (wap_proto_t *self) +{ + zchunk_t *tx_id = self->tx_id; + self->tx_id = NULL; + return tx_id; +} + +// Set the tx_id field, transferring ownership from caller + void -wap_proto_set_tx_id (wap_proto_t *self, const char *value) +wap_proto_set_tx_id (wap_proto_t *self, zchunk_t **chunk_p) { assert (self); - assert (value); - if (value == self->tx_id) - return; - strncpy (self->tx_id, value, 255); - self->tx_id [255] = 0; + assert (chunk_p); + zchunk_destroy (&self->tx_id); + self->tx_id = *chunk_p; + *chunk_p = NULL; } @@ -1521,7 +1567,8 @@ wap_proto_test (bool verbose) } wap_proto_set_id (self, WAP_PROTO_OUTPUT_INDEXES); - wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); + zchunk_t *output_indexes_tx_id = zchunk_new ("Captcha Diem", 12); + wap_proto_set_tx_id (self, &output_indexes_tx_id); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1529,7 +1576,8 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (streq (wap_proto_tx_id (self), "Life is short but Now lasts for ever")); + assert (memcmp (zchunk_data (wap_proto_tx_id (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&output_indexes_tx_id); } wap_proto_set_id (self, WAP_PROTO_OUTPUT_INDEXES_OK); @@ -1581,7 +1629,8 @@ wap_proto_test (bool verbose) } wap_proto_set_id (self, WAP_PROTO_GET); - wap_proto_set_tx_id (self, "Life is short but Now lasts for ever"); + zchunk_t *get_tx_id = zchunk_new ("Captcha Diem", 12); + wap_proto_set_tx_id (self, &get_tx_id); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1589,7 +1638,8 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (streq (wap_proto_tx_id (self), "Life is short but Now lasts for ever")); + assert (memcmp (zchunk_data (wap_proto_tx_id (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_tx_id); } wap_proto_set_id (self, WAP_PROTO_GET_OK); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index d89860e0c..0d6ad02b9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -185,11 +185,9 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ "transactions outputs size=" + std::to_string(tx.vout.size()) + " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));*/ - char *size_prepended_tx_id = new char[crypto::HASH_SIZE + 1]; - size_prepended_tx_id[0] = crypto::HASH_SIZE; - memcpy(size_prepended_tx_id + 1, tx_id.data, crypto::HASH_SIZE); - int rc = wap_client_output_indexes(ipc_client, size_prepended_tx_id); - delete size_prepended_tx_id; + zchunk_t *tx_id_chunk = zchunk_new(tx_id.data, crypto::HASH_SIZE); + int rc = wap_client_output_indexes(ipc_client, &tx_id_chunk); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_output_indexes"); uint64_t status = wap_client_status(ipc_client); THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_output_indexes"); @@ -1178,7 +1176,6 @@ std::vector wallet2::create_transactions(std::vector(zframe_data(outputs_frame)); -std::string tmp(frame_data, frame_size); -std::cout << tmp << std::endl; rapidjson::Document json; THROW_WALLET_EXCEPTION_IF(json.Parse(frame_data, frame_size).HasParseError(), error::get_random_outs_error, "Couldn't JSON parse random outputs."); for (rapidjson::SizeType i = 0; i < json["outputs"].Size(); i++) { COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::outs_for_amount output; - output.amount = json["outputs"][i]["amount"].GetInt(); + output.amount = json["outputs"][i]["amount"].GetInt64(); for (rapidjson::SizeType j = 0; j < json["outputs"][i]["outs"].Size(); j++) { COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::out_entry entry; - entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt(); + entry.global_amount_index = json["outputs"][i]["outs"][j]["global_amount_index"].GetInt64(); std::string out_key(json["outputs"][i]["outs"][j]["out_key"].GetString(), json["outputs"][i]["outs"][j]["out_key"].GetStringLength()); memcpy(entry.out_key.data, out_key.c_str(), 32); output.outs.push_back(entry); From 015847b0d0f0a93745e46a47de6753b8a8f1195d Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 12 Apr 2015 10:11:21 +0530 Subject: [PATCH 36/45] Manage inability to connect to daemon correctly. --- src/simplewallet/simplewallet.cpp | 10 +------ src/wallet/wallet2.cpp | 45 ++++++++++++++++--------------- src/wallet/wallet2.h | 18 +++++-------- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 1d32e930d..5c36c89c5 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -1069,13 +1069,12 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args_) { + // TODO: Find a way to check if daemon is connectible via 0MQ. /*if (!try_connect_to_daemon()) return true;*/ -std::cout << "1\n"; std::vector local_args = args_; -std::cout << "2\n"; size_t fake_outs_count; if(local_args.size() > 0) { if(!epee::string_tools::get_xtype_from_string(fake_outs_count, local_args[0])) @@ -1094,7 +1093,6 @@ std::cout << "2\n"; return true; } -std::cout << "3\n"; std::vector extra; if (1 == local_args.size() % 2) { @@ -1117,7 +1115,6 @@ std::cout << "3\n"; } } -std::cout << "4\n"; vector dsts; for (size_t i = 0; i < local_args.size(); i += 2) { @@ -1192,17 +1189,14 @@ std::cout << "4\n"; dsts.push_back(de); } -std::cout << "5\n"; try { // figure out what tx will be necessary auto ptx_vector = m_wallet->create_transactions(dsts, fake_outs_count, 0 /* unlock_time */, 0 /* unused fee arg*/, extra); -std::cout << "5a\n"; // if more than one tx necessary, prompt user to confirm if (ptx_vector.size() > 1) { -std::cout << "5b\n"; std::string prompt_str = "Your transaction needs to be split into "; prompt_str += std::to_string(ptx_vector.size()); prompt_str += " transactions. This will result in a transaction fee being applied to each transaction"; @@ -1218,7 +1212,6 @@ std::cout << "5b\n"; } } -std::cout << "6\n"; // actually commit the transactions while (!ptx_vector.empty()) { @@ -1236,7 +1229,6 @@ std::cout << "6\n"; } catch (const tools::error::no_connection_to_daemon&) { -std::cout << "7\n"; fail_msg_writer() << "no connection to daemon. Please, make sure daemon is running."; } catch (const tools::error::wallet_rpc_error& e) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 0d6ad02b9..5838f9ca1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -171,19 +171,14 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ if(!outs.empty() && tx_money_got_in_outs) { + connect_to_daemon(); + THROW_WALLET_EXCEPTION_IF(ipc_client == NULL, error::no_connection_to_daemon, "get_output_indexes"); + //good news - got money! take care about it //usually we have only one transfer for user in transaction cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request req = AUTO_VAL_INIT(req); cryptonote::COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response res = AUTO_VAL_INIT(res); crypto::hash tx_id = get_transaction_hash(tx); - //req.txid = get_transaction_hash(tx); - /*bool r = net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/get_o_indexes.bin", req, res, m_http_client, WALLET_RCP_CONNECTION_TIMEOUT); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "get_o_indexes.bin"); - THROW_WALLET_EXCEPTION_IF(res.status != CORE_RPC_STATUS_OK, error::get_out_indices_error, res.status); - THROW_WALLET_EXCEPTION_IF(res.o_indexes.size() != tx.vout.size(), error::wallet_internal_error, - "transactions outputs size=" + std::to_string(tx.vout.size()) + - " not match with COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES response size=" + std::to_string(res.o_indexes.size()));*/ zchunk_t *tx_id_chunk = zchunk_new(tx_id.data, crypto::HASH_SIZE); int rc = wap_client_output_indexes(ipc_client, &tx_id_chunk); @@ -354,6 +349,8 @@ void wallet2::get_blocks_from_zmq_msg(zmsg_t *msg, std::list block_ids; get_short_chain_history(block_ids); @@ -373,12 +370,12 @@ void wallet2::pull_blocks(uint64_t start_height, size_t& blocks_added) delete *it; } zlist_destroy(&list); - THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "getblocks"); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_blocks"); uint64_t status = wap_client_status(ipc_client); - THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getblocks"); - THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "getblocks"); - THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_blocks_error, "getblocks"); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "get_blocks"); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_INTERNAL_ERROR, error::daemon_internal_error, "get_blocks"); + THROW_WALLET_EXCEPTION_IF(status != IPC::STATUS_OK, error::get_blocks_error, "get_blocks"); std::list blocks; zmsg_t *msg = wap_client_block_data(ipc_client); get_blocks_from_zmq_msg(msg, blocks); @@ -1127,19 +1124,14 @@ std::string wallet2::address_from_txt_record(const std::string& s) void wallet2::commit_tx(pending_tx& ptx) { using namespace cryptonote; - /*COMMAND_RPC_SEND_RAW_TX::request req; - req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); - COMMAND_RPC_SEND_RAW_TX::response daemon_send_resp; - bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/sendrawtransaction", req, daemon_send_resp, m_http_client, 200000); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "sendrawtransaction"); - THROW_WALLET_EXCEPTION_IF(daemon_send_resp.status != CORE_RPC_STATUS_OK, error::tx_rejected, ptx.tx, daemon_send_resp.status);*/ + connect_to_daemon(); + THROW_WALLET_EXCEPTION_IF(ipc_client == NULL, error::no_connection_to_daemon, "send_raw_transaction"); std::string tx_as_hex_string = epee::string_tools::buff_to_hex_nodelimer(tx_to_blob(ptx.tx)); zchunk_t *tx_as_hex = zchunk_new((void*)tx_as_hex_string.c_str(), tx_as_hex_string.length()); int rc = wap_client_put(ipc_client, &tx_as_hex); uint64_t status = wap_client_status(ipc_client); - THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "sendrawtransaction"); + THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "send_raw_transaction"); THROW_WALLET_EXCEPTION_IF((status == IPC::STATUS_INVALID_TX) || (status == IPC::STATUS_TX_VERIFICATION_FAILED) || (status == IPC::STATUS_TX_NOT_RELAYED), error::tx_rejected, ptx.tx, status); @@ -1281,6 +1273,17 @@ void wallet2::generate_genesis(cryptonote::block& b) { } void wallet2::stop_ipc_client() { - wap_client_destroy(&ipc_client); + if (ipc_client) { + wap_client_destroy(&ipc_client); + } +} + +void wallet2::connect_to_daemon() { + if (ipc_client) { + // TODO: Instead, check if daemon is reachable. + return; + } + ipc_client = wap_client_new(); + wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); } } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index ee4986899..a74d8cd62 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -84,11 +84,11 @@ namespace tools wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false) {}; public: wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet) { - ipc_client = wap_client_new(); - wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); + connect_to_daemon(); if (!ipc_client) { std::cout << "Couldn't connect to daemon\n\n"; - // TODO: Daemon not up. + // Let ipc_client remain null. All request sending code will verify that + // it's not null and otherwise throw. } }; struct transfer_details @@ -290,6 +290,7 @@ namespace tools void add_unconfirmed_tx(const cryptonote::transaction& tx, uint64_t change_amount); void generate_genesis(cryptonote::block& b); void check_genesis(const crypto::hash& genesis_hash); //throws + void connect_to_daemon(); cryptonote::account_base m_account; std::string m_daemon_address; @@ -458,8 +459,8 @@ namespace tools COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response daemon_resp = AUTO_VAL_INIT(daemon_resp); if(fake_outputs_count) { - // COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req = AUTO_VAL_INIT(req); - // req.outs_count = fake_outputs_count + 1;// add one to make possible (if need) to skip real output key + connect_to_daemon(); + THROW_WALLET_EXCEPTION_IF(ipc_client == NULL, error::no_connection_to_daemon, "get_random_outs"); uint64_t outs_count = fake_outputs_count + 1; std::vector amounts; BOOST_FOREACH(transfer_container::iterator it, selected_transfers) @@ -472,13 +473,6 @@ namespace tools zframe_t *amounts_frame = zframe_new(&amounts[0], amounts.size() * sizeof(uint64_t)); int rc = wap_client_random_outs(ipc_client, outs_count, &amounts_frame); - /*bool r = epee::net_utils::invoke_http_bin_remote_command2(m_daemon_address + "/getrandom_outs.bin", req, daemon_resp, m_http_client, 200000); - THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "getrandom_outs.bin"); - THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::get_random_outs_error, daemon_resp.status); - THROW_WALLET_EXCEPTION_IF(daemon_resp.outs.size() != selected_transfers.size(), error::wallet_internal_error, - "daemon returned wrong response for getrandom_outs.bin, wrong amounts count = " + - std::to_string(daemon_resp.outs.size()) + ", expected " + std::to_string(selected_transfers.size()));*/ uint64_t status = wap_client_status(ipc_client); THROW_WALLET_EXCEPTION_IF(status == IPC::STATUS_CORE_BUSY, error::daemon_busy, "getrandomouts"); From 9eb15fffa5443ed5d61f0ccb79ecde8c0ed554a6 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Thu, 14 May 2015 22:23:38 +0530 Subject: [PATCH 37/45] Start and stop mining IPC --- src/ipc/daemon_ipc_handlers.cpp | 21 ++++- src/ipc/include/daemon_ipc_handlers.h | 2 + src/ipc/include/wap_client.h | 2 +- src/ipc/include/wap_client_engine.inc | 18 ++-- src/ipc/include/wap_proto.h | 12 ++- src/ipc/wap_client/wap_client.c | 2 +- src/ipc/wap_proto.c | 131 ++++++++++++++------------ src/ipc/wap_server/wap_server.c | 3 +- src/simplewallet/simplewallet.cpp | 41 ++++---- src/wallet/wallet2.cpp | 27 +++--- src/wallet/wallet2.h | 7 ++ 11 files changed, 156 insertions(+), 110 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index d649def04..90e110cae 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -76,8 +76,11 @@ namespace IPC return; } cryptonote::account_public_address adr; - const char *address = wap_proto_address(message); - if (!get_account_address_from_str(adr, testnet, std::string(address))) + zchunk_t *address_chunk = wap_proto_address(message); + char *address = (char*)zchunk_data(address_chunk); + std::string address_string(address, zchunk_size(address_chunk)); + + if (!get_account_address_from_str(adr, testnet, std::string(address_string))) { wap_proto_set_status(message, STATUS_WRONG_ADDRESS); return; @@ -86,8 +89,8 @@ namespace IPC boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - uint64_t threads_count = wap_proto_thread_count(message); - if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) + uint64_t thread_count = wap_proto_thread_count(message); + if (!core->get_miner().start(adr, static_cast(thread_count), attrs)) { wap_proto_set_status(message, STATUS_MINING_NOT_STARTED); return; @@ -95,6 +98,16 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + void stop_mining(wap_proto_t *message) + { + if (!core->get_miner().stop()) + { + wap_proto_set_status(message, STATUS_MINING_NOT_STOPPED); + return; + } + wap_proto_set_status(message, STATUS_OK); + } + void retrieve_blocks(wap_proto_t *message) { if (!check_core_busy()) { diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 799fe9969..fe19166ce 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -64,9 +64,11 @@ namespace IPC const uint64_t STATUS_TX_VERIFICATION_FAILED = 7; const uint64_t STATUS_TX_NOT_RELAYED = 8; const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; + const uint64_t STATUS_MINING_NOT_STOPPED = 10; namespace Daemon { void start_mining(wap_proto_t *message); + void stop_mining(wap_proto_t *message); void retrieve_blocks(wap_proto_t *message); void send_raw_transaction(wap_proto_t *message); void get_output_indexes(wap_proto_t *message); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 33f9ac3e0..5ef8dfa6b 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -100,7 +100,7 @@ WAP_EXPORT int // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_start (wap_client_t *self, const char *address, uint64_t thread_count); + wap_client_start (wap_client_t *self, zchunk_t **address_p, uint64_t thread_count); // Send stop command to server. // Returns >= 0 if successful, -1 if interrupted. diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 08d999458..6f10bc0e0 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -139,7 +139,7 @@ struct _client_args_t { zchunk_t *tx_id; uint64_t outs_count; zframe_t *amounts; - char *address; + zchunk_t *address; uint64_t thread_count; }; @@ -283,7 +283,7 @@ s_client_destroy (s_client_t **self_p) zchunk_destroy (&self->args.tx_as_hex); zchunk_destroy (&self->args.tx_id); zframe_destroy (&self->args.amounts); - zstr_free (&self->args.address); + zchunk_destroy (&self->args.address); client_terminate (&self->client); wap_proto_destroy (&self->message); zsock_destroy (&self->msgpipe); @@ -403,6 +403,7 @@ s_satisfy_pedantic_compilers (void) engine_set_timeout (NULL, 0); engine_set_wakeup_event (NULL, 0, NULL_event); engine_handle_socket (NULL, 0, NULL); + engine_set_connected (NULL, 0); } @@ -1472,8 +1473,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) } else if (streq (method, "START")) { - zstr_free (&self->args.address); - zsock_recv (self->cmdpipe, "s8", &self->args.address, &self->args.thread_count); + zchunk_destroy (&self->args.address); + zsock_recv (self->cmdpipe, "p8", &self->args.address, &self->args.thread_count); s_client_execute (self, start_event); } else @@ -1694,10 +1695,10 @@ bool wap_client_connected (wap_client_t *self) { assert (self); - bool connected; + int connected; zsock_send (self->actor, "s", "$CONNECTED"); zsock_recv (self->actor, "i", &connected); - return connected; + return (bool) connected; } @@ -1922,11 +1923,12 @@ wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amou // Returns >= 0 if successful, -1 if interrupted. int -wap_client_start (wap_client_t *self, const char *address, uint64_t thread_count) +wap_client_start (wap_client_t *self, zchunk_t **address_p, uint64_t thread_count) { assert (self); - zsock_send (self->actor, "ss8", "START", address, thread_count); + zsock_send (self->actor, "sp8", "START", *address_p, thread_count); + *address_p = NULL; // Take ownership of address if (s_accept_reply (self, "START OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 38e34ad8b..edb72ada2 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -76,7 +76,7 @@ with GET-OK, or ERROR. START - Wallet asks daemon to start mining. Daemon replies with START-OK, or ERROR. - address string + address chunk thread_count number 8 START_OK - Daemon replies to a start mining request. @@ -295,11 +295,15 @@ zchunk_t * void wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p); -// Get/set the address field -const char * +// Get a copy of the address field +zchunk_t * wap_proto_address (wap_proto_t *self); +// Get the address field and transfer ownership to caller +zchunk_t * + wap_proto_get_address (wap_proto_t *self); +// Set the address field, transferring ownership from caller void - wap_proto_set_address (wap_proto_t *self, const char *value); + wap_proto_set_address (wap_proto_t *self, zchunk_t **chunk_p); // Get/set the thread_count field uint64_t diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index d706f5457..f8bb27197 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -179,7 +179,7 @@ signal_have_blocks_ok (client_t *self) static void prepare_start_command (client_t *self) { - wap_proto_set_address (self->message, self->args->address); + wap_proto_set_address (self->message, &self->args->address); wap_proto_set_thread_count (self->message, self->args->thread_count); } diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 7681536b9..805a65386 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -34,38 +34,22 @@ struct _wap_proto_t { int id; // wap_proto message ID byte *needle; // Read/write pointer for serialization byte *ceiling; // Valid upper limit for read pointer - // Wallet identity - char identity [256]; - // block_ids - zlist_t *block_ids; - // start_height - uint64_t start_height; - // status - uint64_t status; - // curr_height - uint64_t curr_height; - // Frames of block data - zmsg_t *block_data; - // Transaction as hex - zchunk_t *tx_as_hex; - // Transaction ID - zchunk_t *tx_id; - // Output Indexes - zframe_t *o_indexes; - // Outs count - uint64_t outs_count; - // Amounts - zframe_t *amounts; - // Outputs - zframe_t *random_outputs; - // Transaction data - zchunk_t *tx_data; - // address - char address [256]; - // thread_count - uint64_t thread_count; - // Printable explanation - char reason [256]; + char identity [256]; // Wallet identity + zlist_t *block_ids; // block_ids + uint64_t start_height; // start_height + uint64_t status; // status + uint64_t curr_height; // curr_height + zmsg_t *block_data; // Frames of block data + zchunk_t *tx_as_hex; // Transaction as hex + zchunk_t *tx_id; // Transaction ID + zframe_t *o_indexes; // Output Indexes + uint64_t outs_count; // Outs count + zframe_t *amounts; // Amounts + zframe_t *random_outputs; // Outputs + zchunk_t *tx_data; // Transaction data + zchunk_t *address; // address + uint64_t thread_count; // thread_count + char reason [256]; // Printable explanation }; // -------------------------------------------------------------------------- @@ -250,6 +234,7 @@ wap_proto_destroy (wap_proto_t **self_p) zframe_destroy (&self->amounts); zframe_destroy (&self->random_outputs); zchunk_destroy (&self->tx_data); + zchunk_destroy (&self->address); // Free object itself free (self); @@ -449,7 +434,17 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) break; case WAP_PROTO_START: - GET_STRING (self->address); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: address is missing data"); + goto malformed; + } + zchunk_destroy (&self->address); + self->address = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } GET_NUMBER8 (self->thread_count); break; @@ -565,7 +560,9 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += zchunk_size (self->tx_data); break; case WAP_PROTO_START: - frame_size += 1 + strlen (self->address); + frame_size += 4; // Size is 4 octets + if (self->address) + frame_size += zchunk_size (self->address); frame_size += 8; // thread_count break; case WAP_PROTO_START_OK: @@ -582,7 +579,7 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) self->needle = (byte *) zmq_msg_data (&frame); PUT_NUMBER2 (0xAAA0 | 0); PUT_NUMBER1 (self->id); - bool send_block_data = false; + bool have_block_data = false; size_t nbr_frames = 1; // Total number of frames to send switch (self->id) { @@ -611,7 +608,7 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->start_height); PUT_NUMBER8 (self->curr_height); nbr_frames += self->block_data? zmsg_size (self->block_data): 1; - send_block_data = true; + have_block_data = true; break; case WAP_PROTO_PUT: @@ -682,7 +679,15 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) break; case WAP_PROTO_START: - PUT_STRING (self->address); + if (self->address) { + PUT_NUMBER4 (zchunk_size (self->address)); + memcpy (self->needle, + zchunk_data (self->address), + zchunk_size (self->address)); + self->needle += zchunk_size (self->address); + } + else + PUT_NUMBER4 (0); // Empty chunk PUT_NUMBER8 (self->thread_count); break; @@ -724,7 +729,7 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); } // Now send the block_data if necessary - if (send_block_data) { + if (have_block_data) { if (self->block_data) { zframe_t *frame = zmsg_first (self->block_data); while (frame) { @@ -751,10 +756,7 @@ wap_proto_print (wap_proto_t *self) zsys_debug ("WAP_PROTO_OPEN:"); zsys_debug (" protocol=wap"); zsys_debug (" version=1"); - if (self->identity) - zsys_debug (" identity='%s'", self->identity); - else - zsys_debug (" identity="); + zsys_debug (" identity='%s'", self->identity); break; case WAP_PROTO_OPEN_OK: @@ -851,10 +853,7 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_START: zsys_debug ("WAP_PROTO_START:"); - if (self->address) - zsys_debug (" address='%s'", self->address); - else - zsys_debug (" address="); + zsys_debug (" address=[ ... ]"); zsys_debug (" thread_count=%ld", (long) self->thread_count); break; @@ -890,10 +889,7 @@ wap_proto_print (wap_proto_t *self) case WAP_PROTO_ERROR: zsys_debug ("WAP_PROTO_ERROR:"); zsys_debug (" status=%ld", (long) self->status); - if (self->reason) - zsys_debug (" reason='%s'", self->reason); - else - zsys_debug (" reason="); + zsys_debug (" reason='%s'", self->reason); break; } @@ -1377,24 +1373,35 @@ wap_proto_set_tx_data (wap_proto_t *self, zchunk_t **chunk_p) // -------------------------------------------------------------------------- -// Get/set the address field +// Get the address field without transferring ownership -const char * +zchunk_t * wap_proto_address (wap_proto_t *self) { assert (self); return self->address; } +// Get the address field and transfer ownership to caller + +zchunk_t * +wap_proto_get_address (wap_proto_t *self) +{ + zchunk_t *address = self->address; + self->address = NULL; + return address; +} + +// Set the address field, transferring ownership from caller + void -wap_proto_set_address (wap_proto_t *self, const char *value) +wap_proto_set_address (wap_proto_t *self, zchunk_t **chunk_p) { assert (self); - assert (value); - if (value == self->address) - return; - strncpy (self->address, value, 255); - self->address [255] = 0; + assert (chunk_p); + zchunk_destroy (&self->address); + self->address = *chunk_p; + *chunk_p = NULL; } @@ -1455,7 +1462,6 @@ wap_proto_test (bool verbose) wap_proto_t *self = wap_proto_new (); assert (self); wap_proto_destroy (&self); - // Create pair of sockets we can send through // We must bind before connect if we wish to remain compatible with ZeroMQ < v4 zsock_t *output = zsock_new (ZMQ_DEALER); @@ -1468,6 +1474,7 @@ wap_proto_test (bool verbose) rc = zsock_connect (input, "inproc://selftest-wap_proto"); assert (rc == 0); + // Encode/send/decode and verify each message type int instance; self = wap_proto_new (); @@ -1677,7 +1684,8 @@ wap_proto_test (bool verbose) } wap_proto_set_id (self, WAP_PROTO_START); - wap_proto_set_address (self, "Life is short but Now lasts for ever"); + zchunk_t *start_address = zchunk_new ("Captcha Diem", 12); + wap_proto_set_address (self, &start_address); wap_proto_set_thread_count (self, 123); // Send twice wap_proto_send (self, output); @@ -1686,7 +1694,8 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); - assert (streq (wap_proto_address (self), "Life is short but Now lasts for ever")); + assert (memcmp (zchunk_data (wap_proto_address (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&start_address); assert (wap_proto_thread_count (self) == 123); } wap_proto_set_id (self, WAP_PROTO_START_OK); diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 71cce48df..7b6520498 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -184,7 +184,6 @@ static void start_mining_process (client_t *self) { IPC::Daemon::start_mining(self->message); - printf("\n\n Request: %d \n\n", (int)wap_proto_start_height(self->message)); } @@ -195,7 +194,7 @@ start_mining_process (client_t *self) static void stop_mining_process (client_t *self) { - + IPC::Daemon::stop_mining(self->message); } // --------------------------------------------------------------------------- diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 5c36c89c5..05ed03a66 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -756,20 +756,24 @@ bool simple_wallet::start_mining(const std::vector& args) return true; COMMAND_RPC_START_MINING::request req; - req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + // req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + std::string miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); + uint64_t threads_count; bool ok = true; size_t max_mining_threads_count = (std::max)(std::thread::hardware_concurrency(), static_cast(2)); if (0 == args.size()) { - req.threads_count = 1; + // req.threads_count = 1; + threads_count = 1; } else if (1 == args.size()) { uint16_t num = 1; ok = string_tools::get_xtype_from_string(num, args[0]); ok = ok && (1 <= num && num <= max_mining_threads_count); - req.threads_count = num; + // req.threads_count = num; + threads_count = num; } else { @@ -783,13 +787,16 @@ bool simple_wallet::start_mining(const std::vector& args) return true; } - COMMAND_RPC_START_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); - std::string err = interpret_rpc_response(r, res.status); - if (err.empty()) + // COMMAND_RPC_START_MINING::response res; + // bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/start_mining", req, res, m_http_client); + // std::string err = interpret_rpc_response(r, res.status); + + uint64_t status = m_wallet->start_mining(miner_address, threads_count); + // res has to be true since we have checked before. + if (status == IPC::STATUS_OK) success_msg_writer() << "Mining started in daemon"; else - fail_msg_writer() << "mining has NOT been started: " << err; + fail_msg_writer() << "mining has NOT been started: " << status; return true; } //---------------------------------------------------------------------------------------------------- @@ -798,14 +805,15 @@ bool simple_wallet::stop_mining(const std::vector& args) if (!try_connect_to_daemon()) return true; - COMMAND_RPC_STOP_MINING::request req; - COMMAND_RPC_STOP_MINING::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); - std::string err = interpret_rpc_response(r, res.status); - if (err.empty()) + // COMMAND_RPC_STOP_MINING::request req; + // COMMAND_RPC_STOP_MINING::response res; + // bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/stop_mining", req, res, m_http_client); + // std::string err = interpret_rpc_response(r, res.status); + uint64_t status = m_wallet->stop_mining(); + if (status == IPC::STATUS_OK) success_msg_writer() << "Mining stopped in daemon"; else - fail_msg_writer() << "mining has NOT been stopped: " << err; + fail_msg_writer() << "mining has NOT been stopped: " << status; return true; } //---------------------------------------------------------------------------------------------------- @@ -1069,9 +1077,8 @@ bool simple_wallet::show_blockchain_height(const std::vector& args) //---------------------------------------------------------------------------------------------------- bool simple_wallet::transfer(const std::vector &args_) { - // TODO: Find a way to check if daemon is connectible via 0MQ. - /*if (!try_connect_to_daemon()) - return true;*/ + if (!try_connect_to_daemon()) + return true; std::vector local_args = args_; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5838f9ca1..1801333ba 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -762,18 +762,7 @@ bool wallet2::prepare_file_names(const std::string& file_path) //---------------------------------------------------------------------------------------------------- bool wallet2::check_connection() { - if(m_http_client.is_connected()) - return true; - - net_utils::http::url_content u; - net_utils::parse_url(m_daemon_address, u); - - if(!u.port) - { - u.port = m_testnet ? config::testnet::RPC_DEFAULT_PORT : config::RPC_DEFAULT_PORT; - } - - return m_http_client.connect(u.host, std::to_string(u.port), WALLET_RCP_CONNECTION_TIMEOUT); + return ipc_client && wap_client_connected(ipc_client); } //---------------------------------------------------------------------------------------------------- void wallet2::load(const std::string& wallet_, const std::string& password) @@ -1286,4 +1275,18 @@ void wallet2::connect_to_daemon() { ipc_client = wap_client_new(); wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); } + +uint64_t wallet2::start_mining(const std::string &address, uint64_t thread_count) { + zchunk_t *address_chunk = zchunk_new((void*)address.c_str(), address.length()); + int rc = wap_client_start(ipc_client, &address_chunk, thread_count); + zchunk_destroy(&address_chunk); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "start_mining"); + return wap_client_status(ipc_client); +} + +uint64_t wallet2::stop_mining() { + int rc = wap_client_stop(ipc_client); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "stop_mining"); + return wap_client_status(ipc_client); +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a74d8cd62..202c0c0c9 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -84,6 +84,7 @@ namespace tools wallet2(const wallet2&) : m_run(true), m_callback(0), m_testnet(false) {}; public: wallet2(bool testnet = false, bool restricted = false) : m_run(true), m_callback(0), m_testnet(testnet) { + ipc_client = NULL; connect_to_daemon(); if (!ipc_client) { std::cout << "Couldn't connect to daemon\n\n"; @@ -91,6 +92,9 @@ namespace tools // it's not null and otherwise throw. } }; + ~wallet2() { + stop_ipc_client(); + }; struct transfer_details { uint64_t m_block_height; @@ -261,6 +265,9 @@ namespace tools static std::vector addresses_from_url(const std::string& url, bool& dnssec_valid); static std::string address_from_txt_record(const std::string& s); + + uint64_t start_mining(const std::string &address, uint64_t thread_count); + uint64_t stop_mining(); private: /*! * \brief Stores wallet information to wallet file. From 44c3ad69b4f36a08448cf8d5d2e0a7be32436991 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sun, 24 May 2015 23:10:15 +0530 Subject: [PATCH 38/45] monero-rpc-deprecated basic. get_info, save_bc, start, stop mining --- src/daemon/daemon.cpp | 22 +- src/ipc/daemon_ipc_handlers.cpp | 42 + src/ipc/include/daemon_ipc_handlers.h | 4 + src/ipc/include/wap_client.h | 52 +- src/ipc/include/wap_client_engine.inc | 410 +++++++- src/ipc/include/wap_proto.h | 118 ++- src/ipc/include/wap_server_engine.inc | 84 +- src/ipc/wap_client/wap_client.c | 47 +- src/ipc/wap_proto.c | 393 +++++++- src/ipc/wap_server/wap_server.c | 30 + src/rpc/CMakeLists.txt | 24 +- src/rpc/daemon_deprecated_rpc.cpp | 333 +++++++ src/rpc/daemon_deprecated_rpc.h | 48 + src/rpc/daemon_json_rpc_handlers.cpp | 1197 ----------------------- src/rpc/daemon_json_rpc_handlers.h | 75 -- src/rpc/json_rpc.cpp | 26 + src/rpc/json_rpc_http_server.cpp | 9 +- src/rpc/json_rpc_http_server.h | 3 +- src/simplewallet/simplewallet.cpp | 32 +- src/simplewallet/simplewallet.h | 1 - src/wallet/wallet2.cpp | 16 +- src/wallet/wallet2.h | 2 + src/wallet/wallet_json_rpc_handlers.cpp | 12 +- 23 files changed, 1558 insertions(+), 1422 deletions(-) create mode 100644 src/rpc/daemon_deprecated_rpc.cpp create mode 100644 src/rpc/daemon_deprecated_rpc.h delete mode 100644 src/rpc/daemon_json_rpc_handlers.cpp delete mode 100644 src/rpc/daemon_json_rpc_handlers.h create mode 100644 src/rpc/json_rpc.cpp diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index f35f58b80..9e7f2f210 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -34,7 +34,7 @@ #include "daemon/core.h" #include "daemon/p2p.h" #include "daemon/protocol.h" -#include "daemon/rpc.h" +// #include "daemon/rpc.h" #include "daemon/command_server.h" #include "misc_log_ex.h" #include "version.h" @@ -57,7 +57,7 @@ private: public: t_core core; t_p2p p2p; - t_rpc rpc; + // t_rpc rpc; bool testnet_mode; t_internals( @@ -66,7 +66,7 @@ public: : core{vm} , protocol{vm, core} , p2p{vm, protocol} - , rpc{vm, core, p2p} + // , rpc{vm, core, p2p} { // Handle circular dependencies protocol.set_p2p_endpoint(p2p.get()); @@ -79,7 +79,7 @@ void t_daemon::init_options(boost::program_options::options_description & option { t_core::init_options(option_spec); t_p2p::init_options(option_spec); - t_rpc::init_options(option_spec); + // t_rpc::init_options(option_spec); } t_daemon::t_daemon( @@ -122,14 +122,14 @@ bool t_daemon::run(bool interactive) try { mp_internals->core.run(); - mp_internals->rpc.run(); + // mp_internals->rpc.run(); - daemonize::t_command_server* rpc_commands; + // daemonize::t_command_server* rpc_commands; if (interactive) { - rpc_commands = new daemonize::t_command_server(0, 0, false, mp_internals->rpc.get_server()); - rpc_commands->start_handling(); + // rpc_commands = new daemonize::t_command_server(0, 0, false, mp_internals->rpc.get_server()); + // rpc_commands->start_handling(); IPC::Daemon::init(mp_internals->core.get(), mp_internals->p2p.get(), mp_internals->testnet_mode); } @@ -138,11 +138,11 @@ bool t_daemon::run(bool interactive) if (interactive) { - rpc_commands->stop_handling(); + // rpc_commands->stop_handling(); IPC::Daemon::stop(); } - mp_internals->rpc.stop(); + // mp_internals->rpc.stop(); LOG_PRINT("Node stopped.", LOG_LEVEL_0); return true; } @@ -165,7 +165,7 @@ void t_daemon::stop() throw std::runtime_error{"Can't stop stopped daemon"}; } mp_internals->p2p.stop(); - mp_internals->rpc.stop(); + // mp_internals->rpc.stop(); mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return } diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 90e110cae..4aba57adb 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -320,5 +320,47 @@ namespace IPC LOG_PRINT_L2("COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS: " << ENDL << s); wap_proto_set_status(message, STATUS_OK); } + + void get_height(wap_proto_t *message) { + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + wap_proto_set_height(message, core->get_current_blockchain_height()); + wap_proto_set_status(message, STATUS_OK); + } + + void save_bc(wap_proto_t *message) { + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + if (!core->get_blockchain_storage().store_blockchain()) { + wap_proto_set_status(message, STATUS_ERROR_STORING_BLOCKCHAIN); + return; + } + wap_proto_set_status(message, STATUS_OK); + } + + void get_info(wap_proto_t *message) { + if (!check_core_busy()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + uint64_t height = core->get_current_blockchain_height(); + wap_proto_set_height(message, height); + wap_proto_set_target_height(message, core->get_target_blockchain_height()); + wap_proto_set_difficulty(message, core->get_blockchain_storage().get_difficulty_for_next_block()); + wap_proto_set_tx_count(message, core->get_blockchain_storage().get_total_transactions() - height); + wap_proto_set_tx_pool_size(message, core->get_pool_transactions_count()); + wap_proto_set_alt_blocks_count(message, core->get_blockchain_storage().get_alternative_blocks_count()); + uint64_t outgoing_connections_count = p2p->get_outgoing_connections_count(); + wap_proto_set_outgoing_connections_count(message, outgoing_connections_count); + uint64_t total_connections = p2p->get_connections_count(); + wap_proto_set_incoming_connections_count(message, total_connections - outgoing_connections_count); + wap_proto_set_white_peerlist_size(message, p2p->get_peerlist_manager().get_white_peers_count()); + wap_proto_set_grey_peerlist_size(message, p2p->get_peerlist_manager().get_gray_peers_count()); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index fe19166ce..eba8b2e03 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -65,6 +65,7 @@ namespace IPC const uint64_t STATUS_TX_NOT_RELAYED = 8; const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; const uint64_t STATUS_MINING_NOT_STOPPED = 10; + const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 11; namespace Daemon { void start_mining(wap_proto_t *message); @@ -73,6 +74,9 @@ namespace IPC void send_raw_transaction(wap_proto_t *message); void get_output_indexes(wap_proto_t *message); void get_random_outs(wap_proto_t *message); + void get_height(wap_proto_t *message); + void save_bc(wap_proto_t *message); + void get_info(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 5ef8dfa6b..c4f2a1a76 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -85,7 +85,7 @@ WAP_EXPORT int // Request a set of blocks from the server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int - wap_client_save (wap_client_t *self); + wap_client_save_bc (wap_client_t *self); // Ask for tx output indexes. // Returns >= 0 if successful, -1 if interrupted. @@ -97,6 +97,16 @@ WAP_EXPORT int WAP_EXPORT int wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amounts_p); +// Ask for height. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_height (wap_client_t *self); + +// Ask for height. +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_info (wap_client_t *self); + // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. WAP_EXPORT int @@ -139,6 +149,46 @@ WAP_EXPORT zframe_t * WAP_EXPORT zframe_t * wap_client_random_outputs (wap_client_t *self); +// Return last received height +WAP_EXPORT uint64_t + wap_client_height (wap_client_t *self); + +// Return last received target_height +WAP_EXPORT uint64_t + wap_client_target_height (wap_client_t *self); + +// Return last received difficulty +WAP_EXPORT uint64_t + wap_client_difficulty (wap_client_t *self); + +// Return last received tx_count +WAP_EXPORT uint64_t + wap_client_tx_count (wap_client_t *self); + +// Return last received tx_pool_size +WAP_EXPORT uint64_t + wap_client_tx_pool_size (wap_client_t *self); + +// Return last received alt_blocks_count +WAP_EXPORT uint64_t + wap_client_alt_blocks_count (wap_client_t *self); + +// Return last received outgoing_connections_count +WAP_EXPORT uint64_t + wap_client_outgoing_connections_count (wap_client_t *self); + +// Return last received incoming_connections_count +WAP_EXPORT uint64_t + wap_client_incoming_connections_count (wap_client_t *self); + +// Return last received white_peerlist_size +WAP_EXPORT uint64_t + wap_client_white_peerlist_size (wap_client_t *self); + +// Return last received grey_peerlist_size +WAP_EXPORT uint64_t + wap_client_grey_peerlist_size (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 6f10bc0e0..d1fad0938 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -27,15 +27,17 @@ typedef enum { expect_blocks_ok_state = 4, expect_get_ok_state = 5, expect_put_ok_state = 6, - expect_save_ok_state = 7, + expect_save_bc_ok_state = 7, expect_start_ok_state = 8, expect_stop_ok_state = 9, expect_output_indexes_ok_state = 10, expect_random_outs_ok_state = 11, - expect_close_ok_state = 12, - defaults_state = 13, - have_error_state = 14, - reexpect_open_ok_state = 15 + expect_get_height_ok_state = 12, + expect_get_info_ok_state = 13, + expect_close_ok_state = 14, + defaults_state = 15, + have_error_state = 16, + reexpect_open_ok_state = 17 } state_t; typedef enum { @@ -47,26 +49,30 @@ typedef enum { blocks_event = 5, get_event = 6, put_event = 7, - save_event = 8, + save_bc_event = 8, start_event = 9, stop_event = 10, output_indexes_event = 11, random_outs_event = 12, - destructor_event = 13, - blocks_ok_event = 14, - get_ok_event = 15, - put_ok_event = 16, - save_ok_event = 17, - start_ok_event = 18, - stop_ok_event = 19, - output_indexes_ok_event = 20, - random_outs_ok_event = 21, - close_ok_event = 22, - ping_ok_event = 23, - error_event = 24, - exception_event = 25, - command_invalid_event = 26, - other_event = 27 + get_height_event = 13, + get_info_event = 14, + destructor_event = 15, + blocks_ok_event = 16, + get_ok_event = 17, + put_ok_event = 18, + save_bc_ok_event = 19, + start_ok_event = 20, + stop_ok_event = 21, + output_indexes_ok_event = 22, + random_outs_ok_event = 23, + get_height_ok_event = 24, + get_info_ok_event = 25, + close_ok_event = 26, + ping_ok_event = 27, + error_event = 28, + exception_event = 29, + command_invalid_event = 30, + other_event = 31 } event_t; // Names for state machine logging and error reporting @@ -79,11 +85,13 @@ s_state_name [] = { "expect blocks ok", "expect get ok", "expect put ok", - "expect save ok", + "expect save bc ok", "expect start ok", "expect stop ok", "expect output indexes ok", "expect random outs ok", + "expect get height ok", + "expect get info ok", "expect close ok", "defaults", "have error", @@ -100,20 +108,24 @@ s_event_name [] = { "BLOCKS", "GET", "PUT", - "SAVE", + "SAVE_BC", "START", "STOP", "OUTPUT_INDEXES", "RANDOM_OUTS", + "GET_HEIGHT", + "GET_INFO", "destructor", "BLOCKS_OK", "GET_OK", "PUT_OK", - "SAVE_OK", + "SAVE_BC_OK", "START_OK", "STOP_OK", "OUTPUT_INDEXES_OK", "RANDOM_OUTS_OK", + "GET_HEIGHT_OK", + "GET_INFO_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -198,8 +210,6 @@ static void prepare_get_command (client_t *self); static void prepare_put_command (client_t *self); -static void - prepare_save_command (client_t *self); static void prepare_start_command (client_t *self); static void @@ -215,7 +225,7 @@ static void static void signal_have_put_ok (client_t *self); static void - signal_have_save_ok (client_t *self); + signal_have_save_bc_ok (client_t *self); static void signal_have_start_ok (client_t *self); static void @@ -224,6 +234,10 @@ static void signal_have_output_indexes_ok (client_t *self); static void signal_have_random_outs_ok (client_t *self); +static void + signal_have_get_height_ok (client_t *self); +static void + signal_have_get_info_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -443,17 +457,23 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_RANDOM_OUTS_OK: return random_outs_ok_event; break; + case WAP_PROTO_GET_HEIGHT: + return get_height_event; + break; + case WAP_PROTO_GET_HEIGHT_OK: + return get_height_ok_event; + break; case WAP_PROTO_GET: return get_event; break; case WAP_PROTO_GET_OK: return get_ok_event; break; - case WAP_PROTO_SAVE: - return save_event; + case WAP_PROTO_SAVE_BC: + return save_bc_event; break; - case WAP_PROTO_SAVE_OK: - return save_ok_event; + case WAP_PROTO_SAVE_BC_OK: + return save_bc_ok_event; break; case WAP_PROTO_START: return start_event; @@ -461,6 +481,12 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_START_OK: return start_ok_event; break; + case WAP_PROTO_GET_INFO: + return get_info_event; + break; + case WAP_PROTO_GET_INFO_OK: + return get_info_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -681,22 +707,16 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_put_ok_state; } else - if (self->event == save_event) { + if (self->event == save_bc_event) { if (!self->exception) { - // prepare save command + // send SAVE_BC if (wap_client_verbose) - zsys_debug ("wap_client: $ prepare save command"); - prepare_save_command (&self->client); - } - if (!self->exception) { - // send SAVE - if (wap_client_verbose) - zsys_debug ("wap_client: $ send SAVE"); - wap_proto_set_id (self->message, WAP_PROTO_SAVE); + zsys_debug ("wap_client: $ send SAVE_BC"); + wap_proto_set_id (self->message, WAP_PROTO_SAVE_BC); wap_proto_send (self->message, self->dealer); } if (!self->exception) - self->state = expect_save_ok_state; + self->state = expect_save_bc_ok_state; } else if (self->event == start_event) { @@ -765,6 +785,30 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_random_outs_ok_state; } else + if (self->event == get_height_event) { + if (!self->exception) { + // send GET_HEIGHT + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_HEIGHT"); + wap_proto_set_id (self->message, WAP_PROTO_GET_HEIGHT); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_height_ok_state; + } + else + if (self->event == get_info_event) { + if (!self->exception) { + // send GET_INFO + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_INFO"); + wap_proto_set_id (self->message, WAP_PROTO_GET_INFO); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_info_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -961,13 +1005,13 @@ s_client_execute (s_client_t *self, event_t event) } break; - case expect_save_ok_state: - if (self->event == save_ok_event) { + case expect_save_bc_ok_state: + if (self->event == save_bc_ok_event) { if (!self->exception) { - // signal have save ok + // signal have save bc ok if (wap_client_verbose) - zsys_debug ("wap_client: $ signal have save ok"); - signal_have_save_ok (&self->client); + zsys_debug ("wap_client: $ signal have save bc ok"); + signal_have_save_bc_ok (&self->client); } if (!self->exception) self->state = connected_state; @@ -1186,6 +1230,96 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_get_height_ok_state: + if (self->event == get_height_ok_event) { + if (!self->exception) { + // signal have get height ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get height ok"); + signal_have_get_height_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_get_info_ok_state: + if (self->event == get_info_ok_event) { + if (!self->exception) { + // signal have get info ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get info ok"); + signal_have_get_info_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1456,8 +1590,8 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) s_client_execute (self, get_event); } else - if (streq (method, "SAVE")) { - s_client_execute (self, save_event); + if (streq (method, "SAVE BC")) { + s_client_execute (self, save_bc_event); } else if (streq (method, "OUTPUT INDEXES")) { @@ -1472,6 +1606,14 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) s_client_execute (self, random_outs_event); } else + if (streq (method, "GET HEIGHT")) { + s_client_execute (self, get_height_event); + } + else + if (streq (method, "GET INFO")) { + s_client_execute (self, get_info_event); + } + else if (streq (method, "START")) { zchunk_destroy (&self->args.address); zsock_recv (self->cmdpipe, "p8", &self->args.address, &self->args.thread_count); @@ -1601,6 +1743,16 @@ struct _wap_client_t { zchunk_t *tx_data; // Returned by actor reply zframe_t *o_indexes; // Returned by actor reply zframe_t *random_outputs; // Returned by actor reply + uint64_t height; // Returned by actor reply + uint64_t target_height; // Returned by actor reply + uint64_t difficulty; // Returned by actor reply + uint64_t tx_count; // Returned by actor reply + uint64_t tx_pool_size; // Returned by actor reply + uint64_t alt_blocks_count; // Returned by actor reply + uint64_t outgoing_connections_count; // Returned by actor reply + uint64_t incoming_connections_count; // Returned by actor reply + uint64_t white_peerlist_size; // Returned by actor reply + uint64_t grey_peerlist_size; // Returned by actor reply }; @@ -1745,8 +1897,8 @@ s_accept_reply (wap_client_t *self, ...) zsock_recv (self->actor, "ip", &self->status, &self->tx_data); } else - if (streq (reply, "SAVE OK")) { - zsock_recv (self->actor, "i", &self->status); + if (streq (reply, "SAVE BC OK")) { + zsock_recv (self->actor, "8", &self->status); } else if (streq (reply, "OUTPUT INDEXES OK")) { @@ -1759,6 +1911,14 @@ s_accept_reply (wap_client_t *self, ...) zsock_recv (self->actor, "8p", &self->status, &self->random_outputs); } else + if (streq (reply, "GET HEIGHT OK")) { + zsock_recv (self->actor, "88", &self->status, &self->height); + } + else + if (streq (reply, "GET INFO OK")) { + zsock_recv (self->actor, "88888888888", &self->status, &self->height, &self->target_height, &self->difficulty, &self->tx_count, &self->tx_pool_size, &self->alt_blocks_count, &self->outgoing_connections_count, &self->incoming_connections_count, &self->white_peerlist_size, &self->grey_peerlist_size); + } + else if (streq (reply, "START OK")) { zsock_recv (self->actor, "8", &self->status); } @@ -1873,12 +2033,12 @@ wap_client_get (wap_client_t *self, zchunk_t **tx_id_p) // Returns >= 0 if successful, -1 if interrupted. int -wap_client_save (wap_client_t *self) +wap_client_save_bc (wap_client_t *self) { assert (self); - zsock_send (self->actor, "s", "SAVE"); - if (s_accept_reply (self, "SAVE OK", "FAILURE", NULL)) + zsock_send (self->actor, "s", "SAVE BC"); + if (s_accept_reply (self, "SAVE BC OK", "FAILURE", NULL)) return -1; // Interrupted or timed-out return self->status; } @@ -1918,6 +2078,38 @@ wap_client_random_outs (wap_client_t *self, uint64_t outs_count, zframe_t **amou } +// --------------------------------------------------------------------------- +// Ask for height. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_height (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "GET HEIGHT"); + if (s_accept_reply (self, "GET HEIGHT OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Ask for height. +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_info (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "GET INFO"); + if (s_accept_reply (self, "GET INFO OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Send start command to server. // Returns >= 0 if successful, -1 if interrupted. @@ -2037,3 +2229,113 @@ wap_client_random_outputs (wap_client_t *self) assert (self); return self->random_outputs; } + + +// --------------------------------------------------------------------------- +// Return last received height + +uint64_t +wap_client_height (wap_client_t *self) +{ + assert (self); + return self->height; +} + + +// --------------------------------------------------------------------------- +// Return last received target_height + +uint64_t +wap_client_target_height (wap_client_t *self) +{ + assert (self); + return self->target_height; +} + + +// --------------------------------------------------------------------------- +// Return last received difficulty + +uint64_t +wap_client_difficulty (wap_client_t *self) +{ + assert (self); + return self->difficulty; +} + + +// --------------------------------------------------------------------------- +// Return last received tx_count + +uint64_t +wap_client_tx_count (wap_client_t *self) +{ + assert (self); + return self->tx_count; +} + + +// --------------------------------------------------------------------------- +// Return last received tx_pool_size + +uint64_t +wap_client_tx_pool_size (wap_client_t *self) +{ + assert (self); + return self->tx_pool_size; +} + + +// --------------------------------------------------------------------------- +// Return last received alt_blocks_count + +uint64_t +wap_client_alt_blocks_count (wap_client_t *self) +{ + assert (self); + return self->alt_blocks_count; +} + + +// --------------------------------------------------------------------------- +// Return last received outgoing_connections_count + +uint64_t +wap_client_outgoing_connections_count (wap_client_t *self) +{ + assert (self); + return self->outgoing_connections_count; +} + + +// --------------------------------------------------------------------------- +// Return last received incoming_connections_count + +uint64_t +wap_client_incoming_connections_count (wap_client_t *self) +{ + assert (self); + return self->incoming_connections_count; +} + + +// --------------------------------------------------------------------------- +// Return last received white_peerlist_size + +uint64_t +wap_client_white_peerlist_size (wap_client_t *self) +{ + assert (self); + return self->white_peerlist_size; +} + + +// --------------------------------------------------------------------------- +// Return last received grey_peerlist_size + +uint64_t +wap_client_grey_peerlist_size (wap_client_t *self) +{ + assert (self); + return self->grey_peerlist_size; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index edb72ada2..7b7ad0ae1 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -63,6 +63,12 @@ PUT-OK, or ERROR. status number 8 Status random_outputs frame Outputs + GET_HEIGHT - Get height. + + GET_HEIGHT_OK - Daemon returns height. + status number 8 Status + height number 8 Height + GET - Wallet requests transaction data from the daemon. Daemon replies with GET-OK, or ERROR. tx_id chunk Transaction ID @@ -70,9 +76,10 @@ with GET-OK, or ERROR. GET_OK - Daemon replies with transaction data. tx_data chunk Transaction data - SAVE - save_bc command. Details tbd. + SAVE_BC - save_bc command. Details tbd. - SAVE_OK - Daemon replies to a save_bc command. + SAVE_BC_OK - Daemon replies to a save_bc command. + status number 8 Status START - Wallet asks daemon to start mining. Daemon replies with START-OK, or ERROR. @@ -82,7 +89,22 @@ ERROR. START_OK - Daemon replies to a start mining request. status number 8 - STOP - Wallet asks daemon to start mining. Daemon replies with START-OK, or + GET_INFO - getinfo IPC + + GET_INFO_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + height number 8 Height + target_height number 8 Target Height + difficulty number 8 Difficulty + tx_count number 8 TX Count + tx_pool_size number 8 TX Pool Size + alt_blocks_count number 8 Alt Blocks Count + outgoing_connections_count number 8 Outgoing Connections Count + incoming_connections_count number 8 Incoming Connections Count + white_peerlist_size number 8 White Peerlist Size + grey_peerlist_size number 8 Grey Peerlist Size + + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. STOP_OK - Daemon replies to a stop mining request. @@ -123,19 +145,23 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_OUTPUT_INDEXES_OK 8 #define WAP_PROTO_RANDOM_OUTS 9 #define WAP_PROTO_RANDOM_OUTS_OK 10 -#define WAP_PROTO_GET 11 -#define WAP_PROTO_GET_OK 12 -#define WAP_PROTO_SAVE 13 -#define WAP_PROTO_SAVE_OK 14 -#define WAP_PROTO_START 15 -#define WAP_PROTO_START_OK 16 -#define WAP_PROTO_STOP 17 -#define WAP_PROTO_STOP_OK 18 -#define WAP_PROTO_CLOSE 19 -#define WAP_PROTO_CLOSE_OK 20 -#define WAP_PROTO_PING 21 -#define WAP_PROTO_PING_OK 22 -#define WAP_PROTO_ERROR 23 +#define WAP_PROTO_GET_HEIGHT 11 +#define WAP_PROTO_GET_HEIGHT_OK 12 +#define WAP_PROTO_GET 13 +#define WAP_PROTO_GET_OK 14 +#define WAP_PROTO_SAVE_BC 15 +#define WAP_PROTO_SAVE_BC_OK 16 +#define WAP_PROTO_START 17 +#define WAP_PROTO_START_OK 18 +#define WAP_PROTO_GET_INFO 19 +#define WAP_PROTO_GET_INFO_OK 20 +#define WAP_PROTO_STOP 21 +#define WAP_PROTO_STOP_OK 22 +#define WAP_PROTO_CLOSE 23 +#define WAP_PROTO_CLOSE_OK 24 +#define WAP_PROTO_PING 25 +#define WAP_PROTO_PING_OK 26 +#define WAP_PROTO_ERROR 27 #include @@ -285,6 +311,12 @@ zframe_t * void wap_proto_set_random_outputs (wap_proto_t *self, zframe_t **frame_p); +// Get/set the height field +uint64_t + wap_proto_height (wap_proto_t *self); +void + wap_proto_set_height (wap_proto_t *self, uint64_t height); + // Get a copy of the tx_data field zchunk_t * wap_proto_tx_data (wap_proto_t *self); @@ -311,6 +343,60 @@ uint64_t void wap_proto_set_thread_count (wap_proto_t *self, uint64_t thread_count); +// Get/set the target_height field +uint64_t + wap_proto_target_height (wap_proto_t *self); +void + wap_proto_set_target_height (wap_proto_t *self, uint64_t target_height); + +// Get/set the difficulty field +uint64_t + wap_proto_difficulty (wap_proto_t *self); +void + wap_proto_set_difficulty (wap_proto_t *self, uint64_t difficulty); + +// Get/set the tx_count field +uint64_t + wap_proto_tx_count (wap_proto_t *self); +void + wap_proto_set_tx_count (wap_proto_t *self, uint64_t tx_count); + +// Get/set the tx_pool_size field +uint64_t + wap_proto_tx_pool_size (wap_proto_t *self); +void + wap_proto_set_tx_pool_size (wap_proto_t *self, uint64_t tx_pool_size); + +// Get/set the alt_blocks_count field +uint64_t + wap_proto_alt_blocks_count (wap_proto_t *self); +void + wap_proto_set_alt_blocks_count (wap_proto_t *self, uint64_t alt_blocks_count); + +// Get/set the outgoing_connections_count field +uint64_t + wap_proto_outgoing_connections_count (wap_proto_t *self); +void + wap_proto_set_outgoing_connections_count (wap_proto_t *self, uint64_t outgoing_connections_count); + +// Get/set the incoming_connections_count field +uint64_t + wap_proto_incoming_connections_count (wap_proto_t *self); +void + wap_proto_set_incoming_connections_count (wap_proto_t *self, uint64_t incoming_connections_count); + +// Get/set the white_peerlist_size field +uint64_t + wap_proto_white_peerlist_size (wap_proto_t *self); +void + wap_proto_set_white_peerlist_size (wap_proto_t *self, uint64_t white_peerlist_size); + +// Get/set the grey_peerlist_size field +uint64_t + wap_proto_grey_peerlist_size (wap_proto_t *self); +void + wap_proto_set_grey_peerlist_size (wap_proto_t *self, uint64_t grey_peerlist_size); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index 91d74a355..cc4a6a1e4 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -34,16 +34,18 @@ typedef enum { blocks_event = 3, put_event = 4, get_event = 5, - save_event = 6, + save_bc_event = 6, start_event = 7, stop_event = 8, output_indexes_event = 9, random_outs_event = 10, - close_event = 11, - ping_event = 12, - expired_event = 13, - exception_event = 14, - settled_event = 15 + get_height_event = 11, + get_info_event = 12, + close_event = 13, + ping_event = 14, + expired_event = 15, + exception_event = 16, + settled_event = 17 } event_t; // Names for state machine logging and error reporting @@ -64,11 +66,13 @@ s_event_name [] = { "BLOCKS", "PUT", "GET", - "SAVE", + "SAVE_BC", "START", "STOP", "OUTPUT_INDEXES", "RANDOM_OUTS", + "GET_HEIGHT", + "GET_INFO", "CLOSE", "PING", "expired", @@ -144,6 +148,8 @@ static void send_transaction (client_t *self); static void retrieve_transaction (client_t *self); +static void + save_bc (client_t *self); static void start_mining_process (client_t *self); static void @@ -152,6 +158,10 @@ static void output_indexes (client_t *self); static void random_outs (client_t *self); +static void + height (client_t *self); +static void + getinfo (client_t *self); static void deregister_wallet (client_t *self); static void @@ -351,15 +361,21 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_RANDOM_OUTS: return random_outs_event; break; + case WAP_PROTO_GET_HEIGHT: + return get_height_event; + break; case WAP_PROTO_GET: return get_event; break; - case WAP_PROTO_SAVE: - return save_event; + case WAP_PROTO_SAVE_BC: + return save_bc_event; break; case WAP_PROTO_START: return start_event; break; + case WAP_PROTO_GET_INFO: + return get_info_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -632,13 +648,19 @@ s_client_execute (s_client_t *self, event_t event) } } else - if (self->event == save_event) { + if (self->event == save_bc_event) { if (!self->exception) { - // send SAVE_OK + // save bc if (self->server->verbose) - zsys_debug ("%s: $ send SAVE_OK", + zsys_debug ("%s: $ save bc", self->log_prefix); + save_bc (&self->client); + } + if (!self->exception) { + // send SAVE_BC_OK + if (self->server->verbose) + zsys_debug ("%s: $ send SAVE_BC_OK", self->log_prefix); - wap_proto_set_id (self->server->message, WAP_PROTO_SAVE_OK); + wap_proto_set_id (self->server->message, WAP_PROTO_SAVE_BC_OK); wap_proto_set_routing_id (self->server->message, self->routing_id); wap_proto_send (self->server->message, self->server->router); } @@ -716,6 +738,42 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == get_height_event) { + if (!self->exception) { + // height + if (self->server->verbose) + zsys_debug ("%s: $ height", self->log_prefix); + height (&self->client); + } + if (!self->exception) { + // send GET_HEIGHT_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_HEIGHT_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_HEIGHT_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == get_info_event) { + if (!self->exception) { + // getinfo + if (self->server->verbose) + zsys_debug ("%s: $ getinfo", self->log_prefix); + getinfo (&self->client); + } + if (!self->exception) { + // send GET_INFO_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_INFO_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_INFO_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index f8bb27197..cb4be741e 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -229,25 +229,14 @@ signal_have_get_ok (client_t *self) } -// --------------------------------------------------------------------------- -// prepare_save_command -// - -static void -prepare_save_command (client_t *self) -{ -} - - - // --------------------------------------------------------------------------- // signal_have_save_ok // static void -signal_have_save_ok (client_t *self) +signal_have_save_bc_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8", "SAVE OK", 0); + zsock_send (self->cmdpipe, "s8", "SAVE BC OK", wap_proto_status(self->message)); } @@ -272,6 +261,17 @@ signal_have_stop_ok (client_t *self) zsock_send (self->cmdpipe, "s8", "STOP OK", 0); } +// --------------------------------------------------------------------------- +// signal_have_get_height_ok +// + +static void +signal_have_get_height_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0, + wap_proto_height (self->message)); +} + // --------------------------------------------------------------------------- // signal_have_output_indexes_ok // @@ -375,3 +375,24 @@ signal_have_random_outs_ok (client_t *self) wap_proto_status (self->message), wap_proto_get_random_outputs (self->message)); } + +// --------------------------------------------------------------------------- +// signal_have_get_info_ok +// + +static void +signal_have_get_info_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s88888888888", "GET INFO OK", + wap_proto_status (self->message), + wap_proto_height (self->message), + wap_proto_target_height (self->message), + wap_proto_difficulty (self->message), + wap_proto_tx_count (self->message), + wap_proto_tx_pool_size (self->message), + wap_proto_alt_blocks_count (self->message), + wap_proto_outgoing_connections_count (self->message), + wap_proto_incoming_connections_count (self->message), + wap_proto_white_peerlist_size (self->message), + wap_proto_grey_peerlist_size (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 805a65386..0d4c9c16a 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -46,9 +46,19 @@ struct _wap_proto_t { uint64_t outs_count; // Outs count zframe_t *amounts; // Amounts zframe_t *random_outputs; // Outputs + uint64_t height; // Height zchunk_t *tx_data; // Transaction data zchunk_t *address; // address uint64_t thread_count; // thread_count + uint64_t target_height; // Target Height + uint64_t difficulty; // Difficulty + uint64_t tx_count; // TX Count + uint64_t tx_pool_size; // TX Pool Size + uint64_t alt_blocks_count; // Alt Blocks Count + uint64_t outgoing_connections_count; // Outgoing Connections Count + uint64_t incoming_connections_count; // Incoming Connections Count + uint64_t white_peerlist_size; // White Peerlist Size + uint64_t grey_peerlist_size; // Grey Peerlist Size char reason [256]; // Printable explanation }; @@ -399,6 +409,14 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) self->random_outputs = zframe_recv (input); break; + case WAP_PROTO_GET_HEIGHT: + break; + + case WAP_PROTO_GET_HEIGHT_OK: + GET_NUMBER8 (self->status); + GET_NUMBER8 (self->height); + break; + case WAP_PROTO_GET: { size_t chunk_size; @@ -427,10 +445,11 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) } break; - case WAP_PROTO_SAVE: + case WAP_PROTO_SAVE_BC: break; - case WAP_PROTO_SAVE_OK: + case WAP_PROTO_SAVE_BC_OK: + GET_NUMBER8 (self->status); break; case WAP_PROTO_START: @@ -452,6 +471,23 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) GET_NUMBER8 (self->status); break; + case WAP_PROTO_GET_INFO: + break; + + case WAP_PROTO_GET_INFO_OK: + GET_NUMBER8 (self->status); + GET_NUMBER8 (self->height); + GET_NUMBER8 (self->target_height); + GET_NUMBER8 (self->difficulty); + GET_NUMBER8 (self->tx_count); + GET_NUMBER8 (self->tx_pool_size); + GET_NUMBER8 (self->alt_blocks_count); + GET_NUMBER8 (self->outgoing_connections_count); + GET_NUMBER8 (self->incoming_connections_count); + GET_NUMBER8 (self->white_peerlist_size); + GET_NUMBER8 (self->grey_peerlist_size); + break; + case WAP_PROTO_STOP: break; @@ -549,6 +585,10 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_RANDOM_OUTS_OK: frame_size += 8; // status break; + case WAP_PROTO_GET_HEIGHT_OK: + frame_size += 8; // status + frame_size += 8; // height + break; case WAP_PROTO_GET: frame_size += 4; // Size is 4 octets if (self->tx_id) @@ -559,6 +599,9 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) if (self->tx_data) frame_size += zchunk_size (self->tx_data); break; + case WAP_PROTO_SAVE_BC_OK: + frame_size += 8; // status + break; case WAP_PROTO_START: frame_size += 4; // Size is 4 octets if (self->address) @@ -568,6 +611,19 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_START_OK: frame_size += 8; // status break; + case WAP_PROTO_GET_INFO_OK: + frame_size += 8; // status + frame_size += 8; // height + frame_size += 8; // target_height + frame_size += 8; // difficulty + frame_size += 8; // tx_count + frame_size += 8; // tx_pool_size + frame_size += 8; // alt_blocks_count + frame_size += 8; // outgoing_connections_count + frame_size += 8; // incoming_connections_count + frame_size += 8; // white_peerlist_size + frame_size += 8; // grey_peerlist_size + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -654,6 +710,11 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) nbr_frames++; break; + case WAP_PROTO_GET_HEIGHT_OK: + PUT_NUMBER8 (self->status); + PUT_NUMBER8 (self->height); + break; + case WAP_PROTO_GET: if (self->tx_id) { PUT_NUMBER4 (zchunk_size (self->tx_id)); @@ -678,6 +739,10 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER4 (0); // Empty chunk break; + case WAP_PROTO_SAVE_BC_OK: + PUT_NUMBER8 (self->status); + break; + case WAP_PROTO_START: if (self->address) { PUT_NUMBER4 (zchunk_size (self->address)); @@ -695,6 +760,20 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->status); break; + case WAP_PROTO_GET_INFO_OK: + PUT_NUMBER8 (self->status); + PUT_NUMBER8 (self->height); + PUT_NUMBER8 (self->target_height); + PUT_NUMBER8 (self->difficulty); + PUT_NUMBER8 (self->tx_count); + PUT_NUMBER8 (self->tx_pool_size); + PUT_NUMBER8 (self->alt_blocks_count); + PUT_NUMBER8 (self->outgoing_connections_count); + PUT_NUMBER8 (self->incoming_connections_count); + PUT_NUMBER8 (self->white_peerlist_size); + PUT_NUMBER8 (self->grey_peerlist_size); + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -833,6 +912,16 @@ wap_proto_print (wap_proto_t *self) zsys_debug ("(NULL)"); break; + case WAP_PROTO_GET_HEIGHT: + zsys_debug ("WAP_PROTO_GET_HEIGHT:"); + break; + + case WAP_PROTO_GET_HEIGHT_OK: + zsys_debug ("WAP_PROTO_GET_HEIGHT_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" height=%ld", (long) self->height); + break; + case WAP_PROTO_GET: zsys_debug ("WAP_PROTO_GET:"); zsys_debug (" tx_id=[ ... ]"); @@ -843,12 +932,13 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" tx_data=[ ... ]"); break; - case WAP_PROTO_SAVE: - zsys_debug ("WAP_PROTO_SAVE:"); + case WAP_PROTO_SAVE_BC: + zsys_debug ("WAP_PROTO_SAVE_BC:"); break; - case WAP_PROTO_SAVE_OK: - zsys_debug ("WAP_PROTO_SAVE_OK:"); + case WAP_PROTO_SAVE_BC_OK: + zsys_debug ("WAP_PROTO_SAVE_BC_OK:"); + zsys_debug (" status=%ld", (long) self->status); break; case WAP_PROTO_START: @@ -862,6 +952,25 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" status=%ld", (long) self->status); break; + case WAP_PROTO_GET_INFO: + zsys_debug ("WAP_PROTO_GET_INFO:"); + break; + + case WAP_PROTO_GET_INFO_OK: + zsys_debug ("WAP_PROTO_GET_INFO_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" height=%ld", (long) self->height); + zsys_debug (" target_height=%ld", (long) self->target_height); + zsys_debug (" difficulty=%ld", (long) self->difficulty); + zsys_debug (" tx_count=%ld", (long) self->tx_count); + zsys_debug (" tx_pool_size=%ld", (long) self->tx_pool_size); + zsys_debug (" alt_blocks_count=%ld", (long) self->alt_blocks_count); + zsys_debug (" outgoing_connections_count=%ld", (long) self->outgoing_connections_count); + zsys_debug (" incoming_connections_count=%ld", (long) self->incoming_connections_count); + zsys_debug (" white_peerlist_size=%ld", (long) self->white_peerlist_size); + zsys_debug (" grey_peerlist_size=%ld", (long) self->grey_peerlist_size); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -969,17 +1078,23 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_RANDOM_OUTS_OK: return ("RANDOM_OUTS_OK"); break; + case WAP_PROTO_GET_HEIGHT: + return ("GET_HEIGHT"); + break; + case WAP_PROTO_GET_HEIGHT_OK: + return ("GET_HEIGHT_OK"); + break; case WAP_PROTO_GET: return ("GET"); break; case WAP_PROTO_GET_OK: return ("GET_OK"); break; - case WAP_PROTO_SAVE: - return ("SAVE"); + case WAP_PROTO_SAVE_BC: + return ("SAVE_BC"); break; - case WAP_PROTO_SAVE_OK: - return ("SAVE_OK"); + case WAP_PROTO_SAVE_BC_OK: + return ("SAVE_BC_OK"); break; case WAP_PROTO_START: return ("START"); @@ -987,6 +1102,12 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_START_OK: return ("START_OK"); break; + case WAP_PROTO_GET_INFO: + return ("GET_INFO"); + break; + case WAP_PROTO_GET_INFO_OK: + return ("GET_INFO_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -1339,6 +1460,24 @@ wap_proto_set_random_outputs (wap_proto_t *self, zframe_t **frame_p) } +// -------------------------------------------------------------------------- +// Get/set the height field + +uint64_t +wap_proto_height (wap_proto_t *self) +{ + assert (self); + return self->height; +} + +void +wap_proto_set_height (wap_proto_t *self, uint64_t height) +{ + assert (self); + self->height = height; +} + + // -------------------------------------------------------------------------- // Get the tx_data field without transferring ownership @@ -1423,6 +1562,168 @@ wap_proto_set_thread_count (wap_proto_t *self, uint64_t thread_count) } +// -------------------------------------------------------------------------- +// Get/set the target_height field + +uint64_t +wap_proto_target_height (wap_proto_t *self) +{ + assert (self); + return self->target_height; +} + +void +wap_proto_set_target_height (wap_proto_t *self, uint64_t target_height) +{ + assert (self); + self->target_height = target_height; +} + + +// -------------------------------------------------------------------------- +// Get/set the difficulty field + +uint64_t +wap_proto_difficulty (wap_proto_t *self) +{ + assert (self); + return self->difficulty; +} + +void +wap_proto_set_difficulty (wap_proto_t *self, uint64_t difficulty) +{ + assert (self); + self->difficulty = difficulty; +} + + +// -------------------------------------------------------------------------- +// Get/set the tx_count field + +uint64_t +wap_proto_tx_count (wap_proto_t *self) +{ + assert (self); + return self->tx_count; +} + +void +wap_proto_set_tx_count (wap_proto_t *self, uint64_t tx_count) +{ + assert (self); + self->tx_count = tx_count; +} + + +// -------------------------------------------------------------------------- +// Get/set the tx_pool_size field + +uint64_t +wap_proto_tx_pool_size (wap_proto_t *self) +{ + assert (self); + return self->tx_pool_size; +} + +void +wap_proto_set_tx_pool_size (wap_proto_t *self, uint64_t tx_pool_size) +{ + assert (self); + self->tx_pool_size = tx_pool_size; +} + + +// -------------------------------------------------------------------------- +// Get/set the alt_blocks_count field + +uint64_t +wap_proto_alt_blocks_count (wap_proto_t *self) +{ + assert (self); + return self->alt_blocks_count; +} + +void +wap_proto_set_alt_blocks_count (wap_proto_t *self, uint64_t alt_blocks_count) +{ + assert (self); + self->alt_blocks_count = alt_blocks_count; +} + + +// -------------------------------------------------------------------------- +// Get/set the outgoing_connections_count field + +uint64_t +wap_proto_outgoing_connections_count (wap_proto_t *self) +{ + assert (self); + return self->outgoing_connections_count; +} + +void +wap_proto_set_outgoing_connections_count (wap_proto_t *self, uint64_t outgoing_connections_count) +{ + assert (self); + self->outgoing_connections_count = outgoing_connections_count; +} + + +// -------------------------------------------------------------------------- +// Get/set the incoming_connections_count field + +uint64_t +wap_proto_incoming_connections_count (wap_proto_t *self) +{ + assert (self); + return self->incoming_connections_count; +} + +void +wap_proto_set_incoming_connections_count (wap_proto_t *self, uint64_t incoming_connections_count) +{ + assert (self); + self->incoming_connections_count = incoming_connections_count; +} + + +// -------------------------------------------------------------------------- +// Get/set the white_peerlist_size field + +uint64_t +wap_proto_white_peerlist_size (wap_proto_t *self) +{ + assert (self); + return self->white_peerlist_size; +} + +void +wap_proto_set_white_peerlist_size (wap_proto_t *self, uint64_t white_peerlist_size) +{ + assert (self); + self->white_peerlist_size = white_peerlist_size; +} + + +// -------------------------------------------------------------------------- +// Get/set the grey_peerlist_size field + +uint64_t +wap_proto_grey_peerlist_size (wap_proto_t *self) +{ + assert (self); + return self->grey_peerlist_size; +} + +void +wap_proto_set_grey_peerlist_size (wap_proto_t *self, uint64_t grey_peerlist_size) +{ + assert (self); + self->grey_peerlist_size = grey_peerlist_size; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -1634,6 +1935,30 @@ wap_proto_test (bool verbose) assert (zframe_streq (wap_proto_random_outputs (self), "Captcha Diem")); zframe_destroy (&random_outs_ok_random_outputs); } + wap_proto_set_id (self, WAP_PROTO_GET_HEIGHT); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_GET_HEIGHT_OK); + + wap_proto_set_status (self, 123); + wap_proto_set_height (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (wap_proto_height (self) == 123); + } wap_proto_set_id (self, WAP_PROTO_GET); zchunk_t *get_tx_id = zchunk_new ("Captcha Diem", 12); @@ -1662,7 +1987,7 @@ wap_proto_test (bool verbose) assert (memcmp (zchunk_data (wap_proto_tx_data (self)), "Captcha Diem", 12) == 0); zchunk_destroy (&get_ok_tx_data); } - wap_proto_set_id (self, WAP_PROTO_SAVE); + wap_proto_set_id (self, WAP_PROTO_SAVE_BC); // Send twice wap_proto_send (self, output); @@ -1672,8 +1997,9 @@ wap_proto_test (bool verbose) wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); } - wap_proto_set_id (self, WAP_PROTO_SAVE_OK); + wap_proto_set_id (self, WAP_PROTO_SAVE_BC_OK); + wap_proto_set_status (self, 123); // Send twice wap_proto_send (self, output); wap_proto_send (self, output); @@ -1681,6 +2007,7 @@ wap_proto_test (bool verbose) for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); } wap_proto_set_id (self, WAP_PROTO_START); @@ -1710,6 +2037,48 @@ wap_proto_test (bool verbose) assert (wap_proto_routing_id (self)); assert (wap_proto_status (self) == 123); } + wap_proto_set_id (self, WAP_PROTO_GET_INFO); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_GET_INFO_OK); + + wap_proto_set_status (self, 123); + wap_proto_set_height (self, 123); + wap_proto_set_target_height (self, 123); + wap_proto_set_difficulty (self, 123); + wap_proto_set_tx_count (self, 123); + wap_proto_set_tx_pool_size (self, 123); + wap_proto_set_alt_blocks_count (self, 123); + wap_proto_set_outgoing_connections_count (self, 123); + wap_proto_set_incoming_connections_count (self, 123); + wap_proto_set_white_peerlist_size (self, 123); + wap_proto_set_grey_peerlist_size (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (wap_proto_height (self) == 123); + assert (wap_proto_target_height (self) == 123); + assert (wap_proto_difficulty (self) == 123); + assert (wap_proto_tx_count (self) == 123); + assert (wap_proto_tx_pool_size (self) == 123); + assert (wap_proto_alt_blocks_count (self) == 123); + assert (wap_proto_outgoing_connections_count (self) == 123); + assert (wap_proto_incoming_connections_count (self) == 123); + assert (wap_proto_white_peerlist_size (self) == 123); + assert (wap_proto_grey_peerlist_size (self) == 123); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 7b6520498..9f108763f 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -270,3 +270,33 @@ random_outs (client_t *self) { IPC::Daemon::get_random_outs(self->message); } + +// --------------------------------------------------------------------------- +// height +// + +static void +height (client_t *self) +{ + IPC::Daemon::get_height(self->message); +} + +// --------------------------------------------------------------------------- +// save_bc +// + +static void +save_bc (client_t *self) +{ + IPC::Daemon::save_bc(self->message); +} + +// --------------------------------------------------------------------------- +// getinfo +// + +static void +getinfo (client_t *self) +{ + IPC::Daemon::get_info(self->message); +} diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index 6aa90c9ea..ce95d424c 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -28,8 +28,7 @@ set(rpc_sources core_rpc_server.cpp - json_rpc_http_server.cpp - daemon_json_rpc_handlers.cpp) + json_rpc_http_server.cpp) set(rpc_headers) @@ -55,3 +54,24 @@ target_link_libraries(rpc ${EXTRA_LIBRARIES}) add_dependencies(rpc version) + +set(translator_rpc_sources + daemon_deprecated_rpc.cpp + json_rpc_http_server.cpp + json_rpc.cpp) +bitmonero_add_executable(rpc_translator + ${translator_rpc_sources} +) +target_link_libraries(rpc_translator + LINK_PRIVATE + client_ipc + cryptonote_core + cryptonote_protocol + ${EXTRA_LIBRARIES} + ${NET_SKELETON_LIBRARY} + ${ZMQ_LIB} + ${CZMQ_LIB} + ${CMAKE_THREAD_LIBS_INIT}) +set_property(TARGET rpc_translator + PROPERTY + OUTPUT_NAME "monero-rpc-deprecated") diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp new file mode 100644 index 000000000..a6154c8ac --- /dev/null +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -0,0 +1,333 @@ +/*! + * \file rpc_translator.cpp + * \brief Implementations of JSON RPC handlers (Daemon) + */ + +// 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 "daemon_deprecated_rpc.h" +#include + +#define MAX_RESPONSE_SIZE 2000 + +/*! + * \namespace + * \brief Anonymous namespace to keep things in the scope of this file. + */ +namespace +{ + int daemon_connection_error = -326701; + int parse_error = -32700; + int invalid_request = -32600; + int invalid_params = -32602; + int internal_error = -32603; + + RPC::Json_rpc_http_server *server = NULL; + wap_client_t *ipc_client = NULL; + + const char* STATUS_OK = "OK"; + + bool check_connection_to_daemon() + { + return ipc_client && wap_client_connected(ipc_client); + } + + void connect_to_daemon() { + if (check_connection_to_daemon()) { + return; + } + ipc_client = wap_client_new(); + wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); + } + + /*! + * \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(); + response_json.AddMember("jsonrpc", "2.0" , response_json.GetAllocator()); + 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(); + } + /*! + * \brief Implementation of 'getheight' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getheight(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_get_height(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + uint64_t height = wap_client_height(ipc_client); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("height", height, response_json.GetAllocator()); + result_json.AddMember("status", "OK", response_json.GetAllocator()); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'startmining' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int startmining(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect miner_address", "{}"); + } + if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect threads_count", "{}"); + } + + std::string miner_address = request_json["miner_address"].GetString(); + uint64_t threads_count = request_json["threads_count"].GetUint(); + + zchunk_t *address_chunk = zchunk_new((void*)miner_address.c_str(), miner_address.length()); + int rc = wap_client_start(ipc_client, &address_chunk, threads_count); + zchunk_destroy(&address_chunk); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + uint64_t status = wap_client_status(ipc_client); + + if (status == IPC::STATUS_WRONG_ADDRESS) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Failed, wrong address", "{}"); + } + if (status == IPC::STATUS_MINING_NOT_STARTED) + { + return ns_rpc_create_error(buf, len, req, invalid_request, + "Failed, mining not started", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } + + /*! + * \brief Implementation of 'stopmining' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int stopmining(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_stop(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (wap_client_status(ipc_client) != IPC::STATUS_OK) + { + return ns_rpc_create_error(buf, len, req, invalid_request, + "Failed, mining not stopped", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } + + /*! + * \brief Implementation of 'getinfo' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getinfo(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_get_info(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (wap_client_status(ipc_client) != IPC::STATUS_OK) + { + return ns_rpc_create_error(buf, len, req, invalid_request, + "Failed to get info", "{}"); + } + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("height", wap_client_height(ipc_client), response_json.GetAllocator()); + result_json.AddMember("target_height", wap_client_target_height(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("difficulty", wap_client_difficulty(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("tx_count", wap_client_tx_count(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("tx_pool_size", wap_client_tx_pool_size(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("alt_blocks_count", wap_client_alt_blocks_count(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("outgoing_connections_count", wap_client_outgoing_connections_count(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("incoming_connections_count", wap_client_incoming_connections_count(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("white_peerlist_size", wap_client_white_peerlist_size(ipc_client), + response_json.GetAllocator()); + result_json.AddMember("grey_peerlist_size", wap_client_grey_peerlist_size(ipc_client), + response_json.GetAllocator()); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + // Contains a list of method names. + const char *method_names[] = { + "getheight", + "startmining", + "stopmining", + "getinfo", + NULL + }; + + // Contains a list of function pointers. These must map 1-1 by index with `method_names`. + ns_rpc_handler_t handlers[] = { + getheight, + startmining, + stopmining, + getinfo, + NULL + }; + + /*! + * \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; + } + } +} + +/*! + * \namespace RPC + * \brief RPC related utilities + */ +namespace RPC +{ + /*! + * \namespace Daemon + * \brief RPC relevant to daemon + */ + namespace DaemonDeprecated + { + + int start() { + server = new RPC::Json_rpc_http_server("127.0.0.1", "9997", "daemon_json_rpc", &ev_handler); + if (!server->start()) { + return FAILURE_HTTP_SERVER; + } + std::cout << "Started Daemon server at 127.0.0.1/daemon_json_rpc:9997\n"; + ipc_client = wap_client_new(); + wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); + if (!check_connection_to_daemon()) { + return FAILURE_DAEMON_NOT_RUNNING; + } + return SUCCESS; + } + + void stop() { + if (server) { + server->stop(); + delete server; + } + std::cout << "HTTP done\n\n"; + if (ipc_client) { + wap_client_destroy(&ipc_client); + } + std::cout << "IPC done\n\n"; + } + } +} diff --git a/src/rpc/daemon_deprecated_rpc.h b/src/rpc/daemon_deprecated_rpc.h new file mode 100644 index 000000000..6ea1e820c --- /dev/null +++ b/src/rpc/daemon_deprecated_rpc.h @@ -0,0 +1,48 @@ +/*! + * \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 "ipc/include/wallet.h" +#include "ipc/include/daemon_ipc_handlers.h" + +#include + +/*! + * \namespace RPC + * \brief RPC related utilities + */ +namespace RPC +{ + /*! + * \namespace Daemon + * \brief RPC relevant to daemon + */ + namespace DaemonDeprecated + { + const int SUCCESS = 0; + const int FAILURE_DAEMON_NOT_RUNNING = 1; + const int FAILURE_HTTP_SERVER = 2; + int start(); + void stop(); + } +} + +#endif diff --git a/src/rpc/daemon_json_rpc_handlers.cpp b/src/rpc/daemon_json_rpc_handlers.cpp deleted file mode 100644 index bcfacc7bf..000000000 --- a/src/rpc/daemon_json_rpc_handlers.cpp +++ /dev/null @@ -1,1197 +0,0 @@ -/*! - * \file daemon_json_rpc_handlers.cpp - * \brief Implementations of JSON RPC handlers (Daemon) - */ - -// 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 "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, \ - CORE_RPC_STATUS_BUSY, "{}"); } - -#define MAX_RESPONSE_SIZE 2000 - -/*! - * \namespace - * \brief Anonymous namespace to keep things in the scope of this file. - */ -namespace -{ - cryptonote::core *core; - nodetool::node_server > *p2p; - bool testnet; - - const command_line::arg_descriptor arg_rpc_bind_ip = { - "rpc-bind-ip", - "IP for RPC server", - "127.0.0.1" - }; - - const command_line::arg_descriptor arg_rpc_bind_port = { - "rpc-bind-port", - "Port for RPC server", - std::to_string(config::RPC_DEFAULT_PORT) - }; - - const command_line::arg_descriptor arg_testnet_rpc_bind_port = { - "testnet-rpc-bind-port", - "Port for testnet RPC server", - std::to_string(config::testnet::RPC_DEFAULT_PORT) - }; - - /*! - * \brief Tells if core is busy - * \return True if core is busy, false otherwise. - */ - bool check_core_busy() - { - if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) - { - return true; - } - return false; - } - - /*! - * \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(); - } - - /*! - * \brief equivalent of strstr, but with arbitrary bytes (ie, NULs) - * - * This does not differentiate between "not found" and "found at offset 0" - * \param start_buff String to search - * \param buflen length of string - * \param pat Pattern to search - * \param patlen Length of pattern - * \return Position of match - */ - uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) - { - const void *buf = start_buff; - const void *end = (const char*)buf + buflen; - if (patlen > buflen || patlen == 0) - { - return 0; - } - while (buflen > 0 && (buf = memchr(buf,((const char*)pat)[0], buflen - patlen + 1))) - { - if (memcmp(buf,pat,patlen) == 0) - return (const char*)buf - (const char*)start_buff; - buf = (const char*)buf + 1; - buflen = (const char*)end - (const char*)buf; - } - return 0; - } - - uint64_t get_block_reward(const cryptonote::block& blk) - { - uint64_t reward = 0; - BOOST_FOREACH(const cryptonote::tx_out& out, blk.miner_tx.vout) - { - reward += out.amount; - } - return reward; - } - - void fill_block_header_response(const cryptonote::block& blk, bool orphan_status, uint64_t height, - const crypto::hash& hash, rapidjson::Value &header_response, rapidjson::Document &root_doc) - { - rapidjson::Value string_value(rapidjson::kStringType); - header_response.AddMember("major_version", blk.major_version, root_doc.GetAllocator()); - header_response.AddMember("minor_version", blk.minor_version, root_doc.GetAllocator()); - header_response.AddMember("timestamp", blk.timestamp, root_doc.GetAllocator()); - std::string string_prev_hash = epee::string_tools::pod_to_hex(blk.prev_id); - string_value.SetString(string_prev_hash.c_str(), string_prev_hash.length()); - header_response.AddMember("prev_hash", string_value, root_doc.GetAllocator()); - header_response.AddMember("nonce", blk.nonce, root_doc.GetAllocator()); - header_response.AddMember("orphan_status", orphan_status, root_doc.GetAllocator()); - header_response.AddMember("height", height, root_doc.GetAllocator()); - header_response.AddMember("depth", core->get_current_blockchain_height() - height - 1, - root_doc.GetAllocator()); - std::string string_hash = epee::string_tools::pod_to_hex(hash); - string_value.SetString(string_hash.c_str(), string_hash.length()); - header_response.AddMember("hash", string_value, root_doc.GetAllocator()); - header_response.AddMember("difficulty", core->get_blockchain_storage().block_difficulty(height), - root_doc.GetAllocator()); - header_response.AddMember("reward", get_block_reward(blk), root_doc.GetAllocator()); - } - - // The actual RPC method implementations start here. - - /*! - * \brief Implementation of 'getheight' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getheight(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - uint64_t height = core->get_current_blockchain_height(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("height", height, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblocks' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblocks(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("start_height") || !request_json["start_height"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect start_height", "{}"); - } - if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect block_ids", "{}"); - } - - uint64_t start_height = request_json["start_height"].GetUint(); - std::list block_ids; - int ii = 0; - for (rapidjson::Value::ConstValueIterator it = request_json["block_ids"].Begin(); - it != request_json["block_ids"].End(); it++, ii++) - { - crypto::hash hash; - if (!it->IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Wrong type in block_ids", "{}"); - } - if (strlen(it->GetString()) > crypto::HASH_SIZE) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Block ID exceeds length.", "{}"); - } - strcpy(hash.data, it->GetString()); - block_ids.push_back(hash); - } - - std::list > > bs; - uint64_t result_current_height = 0; - uint64_t result_start_height = 0; - if (!core->find_blockchain_supplement(start_height, block_ids, bs, result_current_height, - result_start_height, COMMAND_RPC_GET_BLOCKS_FAST_MAX_COUNT)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed", "{}"); - } - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Value block_json(rapidjson::kArrayType); - rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); - BOOST_FOREACH(auto &b, bs) - { - rapidjson::Value this_block(rapidjson::kObjectType); - std::string blob = block_to_blob(b.first); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(blob.c_str(), blob.length()); - this_block.AddMember("block", string_value, allocator); - rapidjson::Value txs_blocks(rapidjson::kArrayType); - BOOST_FOREACH(auto &t, b.second) - { - blob = cryptonote::tx_to_blob(t); - string_value.SetString(blob.c_str(), blob.length()); - txs_blocks.PushBack(string_value.Move(), allocator); - } - this_block.AddMember("txs", txs_blocks, allocator); - block_json.PushBack(this_block, allocator); - } - - result_json.AddMember("start_height", result_start_height, allocator); - result_json.AddMember("current_height", result_current_height, allocator); - result_json.AddMember("status", CORE_RPC_STATUS_OK, allocator); - result_json.AddMember("blocks", block_json, allocator); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'gettransactions' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int gettransactions(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("txs_hashes") || !request_json["txs_hashes"].IsArray()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect txs_hashes", "{}"); - } - - std::list txs_hashes; - for (rapidjson::Value::ConstValueIterator it = request_json["txs_hashes"].Begin(); - it != request_json["txs_hashes"].End(); it++) - { - if (!it->IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Wrong type in txs_hashes", "{}"); - } - txs_hashes.push_back(std::string(it->GetString())); - } - std::vector vh; - BOOST_FOREACH(const auto& tx_hex_str, txs_hashes) - { - cryptonote::blobdata b; - if (!epee::string_tools::parse_hexstr_to_binbuff(tx_hex_str, b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed to parse hex representation of transaction hash", "{}"); - } - if (b.size() != sizeof(crypto::hash)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed, size of data mismatch", "{}"); - } - vh.push_back(*reinterpret_cast(b.data())); - } - std::list missed_txs; - std::list txs; - bool r = core->get_transactions(vh, txs, missed_txs); - if (!r) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed", "{}"); - } - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); - - rapidjson::Value txs_as_hex(rapidjson::kArrayType); - BOOST_FOREACH(auto &tx, txs) - { - cryptonote::blobdata blob = t_serializable_object_to_blob(tx); - std::string string_blob = epee::string_tools::buff_to_hex_nodelimer(blob); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(string_blob.c_str(), string_blob.length()); - txs_as_hex.PushBack(string_value, allocator); - } - result_json.AddMember("txs_as_hex", txs_as_hex, response_json.GetAllocator()); - - rapidjson::Value missed_tx(rapidjson::kArrayType); - BOOST_FOREACH(const auto &miss_tx, missed_txs) - { - std::string string_blob = epee::string_tools::pod_to_hex(miss_tx); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(string_blob.c_str(), string_blob.length()); - missed_tx.PushBack(string_value, allocator); - } - result_json.AddMember("missed_tx", missed_tx, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'startmining' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int startmining(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("miner_address") || !request_json["miner_address"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect miner_address", "{}"); - } - if (!request_json.HasMember("threads_count") || !request_json["threads_count"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect threads_count", "{}"); - } - - std::string miner_address = request_json["miner_address"].GetString(); - uint64_t threads_count = request_json["threads_count"].GetUint(); - - cryptonote::account_public_address adr; - if (!cryptonote::get_account_address_from_str(adr, testnet, miner_address)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed, wrong address", "{}"); - } - - boost::thread::attributes attrs; - attrs.set_stack_size(THREAD_STACK_SIZE); - if (!core->get_miner().start(adr, static_cast(threads_count), attrs)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Failed, mining not started", "{}"); - } - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - /*! - * \brief Implementation of 'stopmining' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int stopmining(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (!core->get_miner().stop()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Failed, mining not stopped", "{}"); - } - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - /*! - * \brief Implementation of 'miningstatus' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int miningstatus(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - bool active = core->get_miner().is_mining(); - result_json.AddMember("active", active, response_json.GetAllocator()); - - if (active) - { - uint64_t speed = core->get_miner().get_speed(); - uint64_t threads_count = core->get_miner().get_threads_count(); - const cryptonote::account_public_address &mining_address = core->get_miner().get_mining_address(); - std::string address = cryptonote::get_account_address_as_str(testnet, mining_address); - result_json.AddMember("speed", speed, response_json.GetAllocator()); - result_json.AddMember("threads_count", threads_count, response_json.GetAllocator()); - rapidjson::Value string_address(rapidjson::kStringType); - string_address.SetString(address.c_str(), address.length()); - result_json.AddMember("address", string_address, response_json.GetAllocator()); - } - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblockcount' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblockcount(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("count", core->get_current_blockchain_height(), response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblockhash' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblockhash(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect height", "{}"); - } - uint64_t height = request_json["height"].GetUint(); - if (core->get_current_blockchain_height() <= height) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Height specified is too big.", "{}"); - } - std::string hash = epee::string_tools::pod_to_hex(core->get_block_id_by_height(height)); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(hash.c_str(), hash.length()); - result_json.AddMember("hash", string_value, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblocktemplate' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - - if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect reserve_size", "{}"); - } - if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect wallet_address", "{}"); - } - - uint64_t reserve_size = request_json["reserve_size"].GetUint(); - std::string wallet_address = request_json["wallet_address"].GetString(); - - if (reserve_size > 255) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "To big reserved size, maximum 255", "{}"); - } - - cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); - if (!wallet_address.size() || !cryptonote::get_account_address_from_str(acc, testnet, wallet_address)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Failed to parse wallet address", "{}"); - } - - cryptonote::block b = AUTO_VAL_INIT(b); - cryptonote::blobdata blob_reserve; - blob_reserve.resize(reserve_size, 0); - - uint64_t difficulty, height, reserved_offset; - if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) - { - LOG_ERROR("Failed to create block template"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed to create block template", "{}"); - } - - cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); - crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); - if (tx_pub_key == cryptonote::null_pkey) - { - LOG_ERROR("Failed to tx pub key in coinbase extra"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed to create block template", "{}"); - } - - reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); - if (!reserved_offset) - { - LOG_ERROR("Failed to find tx pub key in blockblob"); - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Internal error: failed to create block template", "{}"); - } - - reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) - if (reserved_offset + reserve_size > block_blob.size()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Internal error: failed to create block template", "{}"); - } - cryptonote::blobdata blocktemplate_blob = epee::string_tools::buff_to_hex_nodelimer(block_blob); - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("difficulty", difficulty, response_json.GetAllocator()); - result_json.AddMember("height", height, response_json.GetAllocator()); - result_json.AddMember("reserved_offset", reserved_offset, response_json.GetAllocator()); - rapidjson::Value string_value(rapidjson::kStringType); - string_value.SetString(blocktemplate_blob.c_str(), blocktemplate_blob.length()); - result_json.AddMember("blocktemplate_blob", string_value, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'submitblock' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int submitblock(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - if (!request_json.HasMember("block") || !request_json["block"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect block", "{}"); - } - - std::string string_blockblob = request_json["block"].GetString(); - cryptonote::blobdata blockblob; - if (!epee::string_tools::parse_hexstr_to_binbuff(string_blockblob, blockblob)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Incorrect block", "{}"); - } - // Fixing of high orphan issue for most pools - cryptonote::block b = AUTO_VAL_INIT(b); - if (!parse_and_validate_block_from_blob(blockblob, b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Wrong block blob", "{}"); - } - // Fix from Boolberry neglects to check block - // size, do that with the function below - if (!core->check_incoming_block_size(blockblob)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Block blob size is too big, rejecting block", "{}"); - } - if (!core->handle_block_found(b)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Block not accepted.", "{}"); - } - - return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", CORE_RPC_STATUS_OK); - } - - /*! - * \brief Implementation of 'getlastblockheader' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getlastblockheader(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - uint64_t last_block_height; - crypto::hash last_block_hash; - bool have_last_block_hash = core->get_blockchain_top(last_block_height, last_block_hash); - - if (!have_last_block_hash) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Internal error: can't get last block hash", "{}"); - } - - cryptonote::block last_block; - bool have_last_block = core->get_block_by_hash(last_block_hash, last_block); - if (!have_last_block) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_request, - "Internal error: can't get last block hash", "{}"); - } - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - fill_block_header_response(last_block, false, last_block_height, last_block_hash, - result_json, response_json); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblockheaderbyhash' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblockheaderbyhash(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - if (!request_json.HasMember("hash") || !request_json["hash"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect hash", "{}"); - } - - std::string hash = request_json["hash"].GetString(); - crypto::hash block_hash; - bool hash_parsed = parse_hash256(hash, block_hash); - if (!hash_parsed) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - std::string("Failed to parse hex representation of block hash. Hex = " + hash + '.').c_str(), "{}"); - } - - cryptonote::block blk; - bool have_block = core->get_block_by_hash(block_hash, blk); - if (!have_block) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - std::string("Internal error: can't get block by hash. Hash = " + hash + '.').c_str(), "{}"); - } - - if (blk.miner_tx.vin.front().type() != typeid(cryptonote::txin_gen)) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - std::string("Internal error: coinbase transaction in the block has the wrong type").c_str(), "{}"); - } - - uint64_t block_height = boost::get(blk.miner_tx.vin.front()).height; - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - fill_block_header_response(blk, false, block_height, block_hash, result_json, response_json); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getblockheaderbyheight' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getblockheaderbyheight(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - if (!request_json.HasMember("height") || !request_json["height"].IsUint64()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect height", "{}"); - } - - uint64_t height = request_json["height"].GetUint(); - if (core->get_current_blockchain_height() <= height) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - std::string(std::string("To big height: ") + std::to_string(height) + - ", current blockchain height = " + std::to_string(core->get_current_blockchain_height())).c_str(), "{}"); - } - crypto::hash block_hash = core->get_block_id_by_height(height); - cryptonote::block blk; - bool have_block = core->get_block_by_hash(block_hash, blk); - if (!have_block) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - std::string("Internal error: can't get block by height. Height = " + std::to_string(height) + '.').c_str(), "{}"); - } - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - fill_block_header_response(blk, false, height, block_hash, result_json, response_json); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getconnections' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getconnections(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - - std::list connections = p2p->get_payload_object().get_connections(); - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - rapidjson::Value connections_json(rapidjson::kArrayType); - for (std::list::iterator it = connections.begin(); - it != connections.end(); it++) - { - rapidjson::Value connection; - connection.SetObject(); - rapidjson::Value string_value; - connection.AddMember("incoming", it->incoming, response_json.GetAllocator()); - string_value.SetString(it->ip.c_str(), it->ip.length()); - connection.AddMember("ip", string_value, response_json.GetAllocator()); - string_value.SetString(it->port.c_str(), it->port.length()); - connection.AddMember("port", string_value, response_json.GetAllocator()); - string_value.SetString(it->peer_id.c_str(), it->peer_id.length()); - connection.AddMember("peer_id", string_value, response_json.GetAllocator()); - connection.AddMember("recv_count", it->recv_count, response_json.GetAllocator()); - connection.AddMember("recv_idle_time", it->recv_idle_time, response_json.GetAllocator()); - connection.AddMember("send_count", it->send_count, response_json.GetAllocator()); - connection.AddMember("send_idle_time", it->send_idle_time, response_json.GetAllocator()); - string_value.SetString(it->state.c_str(), it->state.length()); - connection.AddMember("state", string_value, response_json.GetAllocator()); - connection.AddMember("live_time", it->live_time, response_json.GetAllocator()); - connections_json.PushBack(connection, response_json.GetAllocator()); - } - result_json.AddMember("connections", connections_json, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getinfo' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getinfo(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - uint64_t height = core->get_current_blockchain_height(); - result_json.AddMember("height", height, response_json.GetAllocator()); - result_json.AddMember("target_height", core->get_target_blockchain_height(), - response_json.GetAllocator()); - result_json.AddMember("difficulty", core->get_blockchain_storage().get_difficulty_for_next_block(), - response_json.GetAllocator()); - result_json.AddMember("tx_count", core->get_blockchain_storage().get_total_transactions() - height, - response_json.GetAllocator()); - result_json.AddMember("tx_pool_size", core->get_pool_transactions_count(), - response_json.GetAllocator()); - result_json.AddMember("alt_blocks_count", (uint64_t)core->get_blockchain_storage().get_alternative_blocks_count(), - response_json.GetAllocator()); - uint64_t total_conn = p2p->get_connections_count(); - uint64_t outgoing_connections_count = p2p->get_outgoing_connections_count(); - result_json.AddMember("outgoing_connections_count", outgoing_connections_count, - response_json.GetAllocator()); - result_json.AddMember("incoming_connections_count", total_conn - outgoing_connections_count, - response_json.GetAllocator()); - result_json.AddMember("white_peerlist_size", p2p->get_peerlist_manager().get_white_peers_count(), - response_json.GetAllocator()); - result_json.AddMember("grey_peerlist_size", p2p->get_peerlist_manager().get_gray_peers_count(), - response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - /*! - * \brief Implementation of 'getindexes' method. - * \param buf Buffer to fill in response. - * \param len Max length of response. - * \param req net_skeleton RPC request - * \return Actual response length. - */ - int getindexes(char *buf, int len, struct ns_rpc_request *req) - { - CHECK_CORE_BUSY(); - if (req->params == NULL) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Parameters missing.", "{}"); - } - rapidjson::Document request_json; - char request_buf[1000]; - strncpy(request_buf, req->params[0].ptr, req->params[0].len); - request_buf[req->params[0].len] = '\0'; - - if (request_json.Parse(request_buf).HasParseError()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::parse_error, - "Invalid JSON passed", "{}"); - } - if (!request_json.HasMember("txid") || !request_json["txid"].IsString()) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "Incorrect txid", "{}"); - } - std::string txid_string = request_json["txid"].GetString(); - if (txid_string.length() < crypto::HASH_SIZE) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::invalid_params, - "txid is not of correct length", "{}"); - } - crypto::hash txid; - strncpy(txid.data, txid_string.c_str(), crypto::HASH_SIZE); - std::vector o_indexes; - - rapidjson::Document response_json; - rapidjson::Value result_json; - result_json.SetObject(); - - rapidjson::Value o_indexes_json(rapidjson::kArrayType); - bool r = core->get_tx_outputs_gindexs(txid, o_indexes); - if (!r) - { - return ns_rpc_create_error(buf, len, req, RPC::Json_rpc_http_server::internal_error, - "Failed", "{}"); - } - for (std::vector::iterator it = o_indexes.begin(); it != o_indexes.end(); it++) - { - o_indexes_json.PushBack(*it, response_json.GetAllocator()); - } - result_json.AddMember("o_indexes", o_indexes_json, response_json.GetAllocator()); - result_json.AddMember("status", CORE_RPC_STATUS_OK, response_json.GetAllocator()); - - std::string response; - construct_response_string(req, result_json, response_json, response); - size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; - strncpy(buf, response.c_str(), copy_length); - return response.length(); - } - - // Contains a list of method names. - const char *method_names[] = { - "getheight", - "getblocks", - "gettransactions", - "startmining", - "stopmining", - "miningstatus", - "getblockcount", - "getblockhash", - "getblocktemplate", - "submitblock", - "getlastblockheader", - "getblockheaderbyhash", - "getblockheaderbyheight", - "getconnections", - "getinfo", - "getindexes", - NULL - }; - - // Contains a list of function pointers. These must map 1-1 by index with `method_names`. - ns_rpc_handler_t handlers[] = { - getheight, - getblocks, - gettransactions, - startmining, - stopmining, - miningstatus, - getblockcount, - getblockhash, - getblocktemplate, - submitblock, - getlastblockheader, - getblockheaderbyhash, - getblockheaderbyheight, - getconnections, - getinfo, - getindexes, - NULL - }; -} - -/*! - * \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) - { - 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); - } - - /*! - * \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); - } - - /*! - * \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 deleted file mode 100644 index bc9bb3065..000000000 --- a/src/rpc/daemon_json_rpc_handlers.h +++ /dev/null @@ -1,75 +0,0 @@ -/*! - * \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.cpp b/src/rpc/json_rpc.cpp new file mode 100644 index 000000000..142cb75c4 --- /dev/null +++ b/src/rpc/json_rpc.cpp @@ -0,0 +1,26 @@ +#include "daemon_deprecated_rpc.h" +#include +#include + +static bool execute = true; +void trap(int signal) { + RPC::DaemonDeprecated::stop(); + execute = false; +} + +int main() { + int res = RPC::DaemonDeprecated::start(); + if (res == RPC::DaemonDeprecated::FAILURE_HTTP_SERVER) { + std::cerr << "Couldn't start HTTP server\n"; + execute = false; + } else if (res == RPC::DaemonDeprecated::FAILURE_DAEMON_NOT_RUNNING) { + std::cerr << "Couldn't connect to daemon\n"; + execute = false; + } + signal(SIGINT, &trap); + while (execute) { + + } + std::cout << "out!\n"; + return 0; +} diff --git a/src/rpc/json_rpc_http_server.cpp b/src/rpc/json_rpc_http_server.cpp index 86edc7d10..3570d058c 100644 --- a/src/rpc/json_rpc_http_server.cpp +++ b/src/rpc/json_rpc_http_server.cpp @@ -13,10 +13,6 @@ */ namespace RPC { - int Json_rpc_http_server::parse_error = -32700; - int Json_rpc_http_server::invalid_request = -32600; - int Json_rpc_http_server::invalid_params = -32602; - int Json_rpc_http_server::internal_error = -32603; /** * \brief Constructor @@ -25,10 +21,11 @@ namespace RPC * \param ev_handler Event handler function pointer */ Json_rpc_http_server::Json_rpc_http_server(const std::string &ip, const std::string &port, - void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)) + const std::string &path, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)) { m_ip = ip; m_port = port; + m_path = path; m_is_running = false; m_ev_handler = ev_handler; } @@ -53,7 +50,7 @@ namespace RPC } m_is_running = true; ns_mgr_init(&mgr, NULL); - nc = ns_bind(&mgr, (m_ip + ":" + m_port).c_str(), m_ev_handler); + nc = ns_bind(&mgr, (m_ip + ":" + m_port + "/" + m_path).c_str(), m_ev_handler); if (!nc) { return false; diff --git a/src/rpc/json_rpc_http_server.h b/src/rpc/json_rpc_http_server.h index a262f6061..4cc728313 100644 --- a/src/rpc/json_rpc_http_server.h +++ b/src/rpc/json_rpc_http_server.h @@ -33,6 +33,7 @@ namespace RPC void poll(); std::string m_ip; /*!< IP address where its listening */ std::string m_port; /*!< Port where its listening */ + std::string m_path; /*!< Path */ bool m_is_running; /*!< Whether the server is currently running */ void (*m_ev_handler)(struct ns_connection *nc, int ev, void *ev_data); /*!< Server event handler function pointer */ public: @@ -43,7 +44,7 @@ namespace RPC * \param port Port number to bind * \param ev_handler Event handler function pointer */ - Json_rpc_http_server(const std::string &ip, const std::string &port, + Json_rpc_http_server(const std::string &ip, const std::string &port, const std::string &path, void (*ev_handler)(struct ns_connection *nc, int ev, void *ev_data)); /** diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index 05ed03a66..bdb31413a 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -755,7 +755,7 @@ bool simple_wallet::start_mining(const std::vector& args) if (!try_connect_to_daemon()) return true; - COMMAND_RPC_START_MINING::request req; + // COMMAND_RPC_START_MINING::request req; // req.miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); std::string miner_address = m_wallet->get_account().get_public_address_str(m_wallet->testnet()); uint64_t threads_count; @@ -822,14 +822,15 @@ bool simple_wallet::save_bc(const std::vector& args) if (!try_connect_to_daemon()) return true; - COMMAND_RPC_SAVE_BC::request req; - COMMAND_RPC_SAVE_BC::response res; - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client); - std::string err = interpret_rpc_response(r, res.status); - if (err.empty()) + // COMMAND_RPC_SAVE_BC::request req; + // COMMAND_RPC_SAVE_BC::response res; + // bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/save_bc", req, res, m_http_client); + // std::string err = interpret_rpc_response(r, res.status); + uint64_t status = m_wallet->save_bc(); + if (status == IPC::STATUS_OK) success_msg_writer() << "Blockchain saved"; else - fail_msg_writer() << "Blockchain can't be saved: " << err; + fail_msg_writer() << "Blockchain can't be saved: " << status; return true; } //---------------------------------------------------------------------------------------------------- @@ -1053,11 +1054,18 @@ bool simple_wallet::show_payments(const std::vector &args) //---------------------------------------------------------------------------------------------------- uint64_t simple_wallet::get_daemon_blockchain_height(std::string& err) { - COMMAND_RPC_GET_HEIGHT::request req; - COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized(); - bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); - err = interpret_rpc_response(r, res.status); - return res.height; + // COMMAND_RPC_GET_HEIGHT::request req; + // COMMAND_RPC_GET_HEIGHT::response res = boost::value_initialized(); + // bool r = net_utils::invoke_http_json_remote_command2(m_daemon_address + "/getheight", req, res, m_http_client); + // err = interpret_rpc_response(r, res.status); + uint64_t height; + uint64_t status = m_wallet->get_height(height); + // res has to be true since we have checked before. + if (status != IPC::STATUS_OK) { + // TODO: map proper error messages to codes. + err = "Couldn't get blockchain height."; + } + return height; } //---------------------------------------------------------------------------------------------------- bool simple_wallet::show_blockchain_height(const std::vector& args) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index 9511bb075..a8fe78414 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -207,6 +207,5 @@ namespace cryptonote std::unique_ptr m_wallet; epee::net_utils::http::http_simple_client m_http_client; refresh_progress_reporter_t m_refresh_progress_reporter; - wap_client_t *client; }; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 1801333ba..69b38ee0c 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1268,8 +1268,7 @@ void wallet2::stop_ipc_client() { } void wallet2::connect_to_daemon() { - if (ipc_client) { - // TODO: Instead, check if daemon is reachable. + if (check_connection()) { return; } ipc_client = wap_client_new(); @@ -1289,4 +1288,17 @@ uint64_t wallet2::stop_mining() { THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "stop_mining"); return wap_client_status(ipc_client); } + +uint64_t wallet2::get_height(uint64_t &height) { + int rc = wap_client_get_height(ipc_client); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "get_height"); + height = wap_client_height(ipc_client); + return wap_client_status(ipc_client); +} + +uint64_t wallet2::save_bc() { + int rc = wap_client_save_bc(ipc_client); + THROW_WALLET_EXCEPTION_IF(rc < 0, error::no_connection_to_daemon, "save_bc"); + return wap_client_status(ipc_client); +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 202c0c0c9..a1c67ca8d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -268,6 +268,8 @@ namespace tools uint64_t start_mining(const std::string &address, uint64_t thread_count); uint64_t stop_mining(); + uint64_t get_height(uint64_t &height); + uint64_t save_bc(); private: /*! * \brief Stores wallet information to wallet file. diff --git a/src/wallet/wallet_json_rpc_handlers.cpp b/src/wallet/wallet_json_rpc_handlers.cpp index 5334ebbf7..04986eb8a 100644 --- a/src/wallet/wallet_json_rpc_handlers.cpp +++ b/src/wallet/wallet_json_rpc_handlers.cpp @@ -39,7 +39,7 @@ namespace void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json, rapidjson::Document &response_json, std::string &response) { - response_json.SetObject(); + /*response_json.SetObject(); rapidjson::Value string_value(rapidjson::kStringType); // If ID was present in request use it else use "null". if (req->id != NULL) @@ -58,7 +58,7 @@ namespace rapidjson::Writer writer(buffer); response_json.Accept(writer); // Write string to `response`. - response = buffer.GetString(); + response = buffer.GetString();*/ } /*! @@ -70,7 +70,7 @@ namespace */ int getbalance(char *buf, int len, struct ns_rpc_request *req) { - uint64_t balance, unlocked_balance; + /*uint64_t balance, unlocked_balance; try { balance = wallet->balance(); @@ -91,7 +91,7 @@ namespace construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); - return response.length(); + return response.length();*/ } /*! @@ -103,7 +103,7 @@ namespace */ int getaddress(char *buf, int len, struct ns_rpc_request *req) { - std::string address; + /*std::string address; try { address = wallet->get_account().get_public_address_str(wallet->testnet()); @@ -124,7 +124,7 @@ namespace construct_response_string(req, result_json, response_json, response); size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; strncpy(buf, response.c_str(), copy_length); - return response.length(); + return response.length();*/ } // Contains a list of method names. From eec95a71d5d184d61115fdf53c5c083155b278ee Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Wed, 10 Jun 2015 20:47:33 +0530 Subject: [PATCH 39/45] get_peer_list IPC --- src/ipc/daemon_ipc_handlers.cpp | 54 +++++++++ src/ipc/include/daemon_ipc_handlers.h | 1 + src/ipc/include/wap_client.h | 13 ++ src/ipc/include/wap_client_engine.inc | 167 +++++++++++++++++++++---- src/ipc/include/wap_proto.h | 43 +++++-- src/ipc/include/wap_server_engine.inc | 35 +++++- src/ipc/wap_client/wap_client.c | 13 ++ src/ipc/wap_proto.c | 168 ++++++++++++++++++++++++++ src/ipc/wap_server/wap_server.c | 10 ++ src/rpc/daemon_deprecated_rpc.cpp | 57 ++++++++- 10 files changed, 525 insertions(+), 36 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 4aba57adb..21e7e22ad 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -28,6 +28,8 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +//TODO: Recheck memory leaks + #include "daemon_ipc_handlers.h" #include @@ -362,5 +364,57 @@ namespace IPC wap_proto_set_grey_peerlist_size(message, p2p->get_peerlist_manager().get_gray_peers_count()); wap_proto_set_status(message, STATUS_OK); } + + void get_peer_list(wap_proto_t *message) { + std::list white_list; + std::list gray_list; + p2p->get_peerlist_manager().get_peerlist_full(white_list, gray_list); + + rapidjson::Document white_list_json; + white_list_json.SetObject(); + rapidjson::Document::AllocatorType &white_list_allocator = white_list_json.GetAllocator(); + rapidjson::Value white_peers(rapidjson::kArrayType); + + for (auto & entry : white_list) { + rapidjson::Value output(rapidjson::kObjectType); + output.AddMember("id", entry.id, white_list_allocator); + output.AddMember("ip", entry.adr.ip, white_list_allocator); + output.AddMember("port", entry.adr.port, white_list_allocator); + output.AddMember("last_seen", entry.last_seen, white_list_allocator); + white_peers.PushBack(output, white_list_allocator); + } + white_list_json.AddMember("peers", white_peers, white_list_allocator); + + rapidjson::Document gray_list_json; + gray_list_json.SetObject(); + rapidjson::Document::AllocatorType &gray_list_allocator = gray_list_json.GetAllocator(); + rapidjson::Value gray_peers(rapidjson::kArrayType); + + for (auto & entry : gray_list) { + rapidjson::Value output(rapidjson::kObjectType); + output.AddMember("id", entry.id, gray_list_allocator); + output.AddMember("ip", entry.adr.ip, gray_list_allocator); + output.AddMember("port", entry.adr.port, gray_list_allocator); + output.AddMember("last_seen", entry.last_seen, gray_list_allocator); + gray_peers.PushBack(output, gray_list_allocator); + } + gray_list_json.AddMember("peers", gray_peers, gray_list_allocator); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + white_list_json.Accept(writer); + std::string white_list_string = buffer.GetString(); + + zframe_t *white_list_frame = zframe_new(white_list_string.c_str(), white_list_string.length()); + + buffer.Clear(); + gray_list_json.Accept(writer); + std::string gray_list_string = buffer.GetString(); + zframe_t *gray_list_frame = zframe_new(gray_list_string.c_str(), gray_list_string.length()); + + wap_proto_set_white_list(message, &white_list_frame); + wap_proto_set_gray_list(message, &gray_list_frame); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index eba8b2e03..6dc32bac5 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -77,6 +77,7 @@ namespace IPC void get_height(wap_proto_t *message); void save_bc(wap_proto_t *message); void get_info(wap_proto_t *message); + void get_peer_list(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index c4f2a1a76..fa67e945b 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -117,6 +117,11 @@ WAP_EXPORT int WAP_EXPORT int wap_client_stop (wap_client_t *self); +// Get peer list +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_peer_list (wap_client_t *self); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); @@ -189,6 +194,14 @@ WAP_EXPORT uint64_t WAP_EXPORT uint64_t wap_client_grey_peerlist_size (wap_client_t *self); +// Return last received white_list +WAP_EXPORT zframe_t * + wap_client_white_list (wap_client_t *self); + +// Return last received gray_list +WAP_EXPORT zframe_t * + wap_client_gray_list (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index d1fad0938..17d2c4b9a 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -34,10 +34,11 @@ typedef enum { expect_random_outs_ok_state = 11, expect_get_height_ok_state = 12, expect_get_info_ok_state = 13, - expect_close_ok_state = 14, - defaults_state = 15, - have_error_state = 16, - reexpect_open_ok_state = 17 + expect_get_peer_list_ok_state = 14, + expect_close_ok_state = 15, + defaults_state = 16, + have_error_state = 17, + reexpect_open_ok_state = 18 } state_t; typedef enum { @@ -56,23 +57,25 @@ typedef enum { random_outs_event = 12, get_height_event = 13, get_info_event = 14, - destructor_event = 15, - blocks_ok_event = 16, - get_ok_event = 17, - put_ok_event = 18, - save_bc_ok_event = 19, - start_ok_event = 20, - stop_ok_event = 21, - output_indexes_ok_event = 22, - random_outs_ok_event = 23, - get_height_ok_event = 24, - get_info_ok_event = 25, - close_ok_event = 26, - ping_ok_event = 27, - error_event = 28, - exception_event = 29, - command_invalid_event = 30, - other_event = 31 + get_peer_list_event = 15, + destructor_event = 16, + blocks_ok_event = 17, + get_ok_event = 18, + put_ok_event = 19, + save_bc_ok_event = 20, + start_ok_event = 21, + stop_ok_event = 22, + output_indexes_ok_event = 23, + random_outs_ok_event = 24, + get_height_ok_event = 25, + get_info_ok_event = 26, + get_peer_list_ok_event = 27, + close_ok_event = 28, + ping_ok_event = 29, + error_event = 30, + exception_event = 31, + command_invalid_event = 32, + other_event = 33 } event_t; // Names for state machine logging and error reporting @@ -92,6 +95,7 @@ s_state_name [] = { "expect random outs ok", "expect get height ok", "expect get info ok", + "expect get peer list ok", "expect close ok", "defaults", "have error", @@ -115,6 +119,7 @@ s_event_name [] = { "RANDOM_OUTS", "GET_HEIGHT", "GET_INFO", + "GET_PEER_LIST", "destructor", "BLOCKS_OK", "GET_OK", @@ -126,6 +131,7 @@ s_event_name [] = { "RANDOM_OUTS_OK", "GET_HEIGHT_OK", "GET_INFO_OK", + "GET_PEER_LIST_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -238,6 +244,8 @@ static void signal_have_get_height_ok (client_t *self); static void signal_have_get_info_ok (client_t *self); +static void + signal_have_get_peer_list_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -487,6 +495,12 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_GET_INFO_OK: return get_info_ok_event; break; + case WAP_PROTO_GET_PEER_LIST: + return get_peer_list_event; + break; + case WAP_PROTO_GET_PEER_LIST_OK: + return get_peer_list_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -809,6 +823,18 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_get_info_ok_state; } else + if (self->event == get_peer_list_event) { + if (!self->exception) { + // send GET_PEER_LIST + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_PEER_LIST"); + wap_proto_set_id (self->message, WAP_PROTO_GET_PEER_LIST); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_peer_list_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1320,6 +1346,51 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_get_peer_list_ok_state: + if (self->event == get_peer_list_ok_event) { + if (!self->exception) { + // signal have get peer list ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get peer list ok"); + signal_have_get_peer_list_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1623,6 +1694,10 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "STOP")) { s_client_execute (self, stop_event); } + else + if (streq (method, "GET PEER LIST")) { + s_client_execute (self, get_peer_list_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -1753,6 +1828,8 @@ struct _wap_client_t { uint64_t incoming_connections_count; // Returned by actor reply uint64_t white_peerlist_size; // Returned by actor reply uint64_t grey_peerlist_size; // Returned by actor reply + zframe_t *white_list; // Returned by actor reply + zframe_t *gray_list; // Returned by actor reply }; @@ -1805,6 +1882,8 @@ wap_client_destroy (wap_client_t **self_p) zchunk_destroy (&self->tx_data); zframe_destroy (&self->o_indexes); zframe_destroy (&self->random_outputs); + zframe_destroy (&self->white_list); + zframe_destroy (&self->gray_list); free (self); *self_p = NULL; } @@ -1924,7 +2003,13 @@ s_accept_reply (wap_client_t *self, ...) } else if (streq (reply, "STOP OK")) { - zsock_recv (self->actor, "i", &self->status); + zsock_recv (self->actor, "8", &self->status); + } + else + if (streq (reply, "GET PEER LIST OK")) { + zframe_destroy (&self->white_list); + zframe_destroy (&self->gray_list); + zsock_recv (self->actor, "8pp", &self->status, &self->white_list, &self->gray_list); } break; } @@ -2143,6 +2228,22 @@ wap_client_stop (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Get peer list +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_peer_list (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "GET PEER LIST"); + if (s_accept_reply (self, "GET PEER LIST OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status @@ -2339,3 +2440,25 @@ wap_client_grey_peerlist_size (wap_client_t *self) assert (self); return self->grey_peerlist_size; } + + +// --------------------------------------------------------------------------- +// Return last received white_list + +zframe_t * +wap_client_white_list (wap_client_t *self) +{ + assert (self); + return self->white_list; +} + + +// --------------------------------------------------------------------------- +// Return last received gray_list + +zframe_t * +wap_client_gray_list (wap_client_t *self) +{ + assert (self); + return self->gray_list; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 7b7ad0ae1..a7ebab735 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -104,6 +104,13 @@ ERROR. white_peerlist_size number 8 White Peerlist Size grey_peerlist_size number 8 Grey Peerlist Size + GET_PEER_LIST - get_peer_list IPC + + GET_PEER_LIST_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + white_list frame White list + gray_list frame Gray list + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -155,13 +162,15 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_START_OK 18 #define WAP_PROTO_GET_INFO 19 #define WAP_PROTO_GET_INFO_OK 20 -#define WAP_PROTO_STOP 21 -#define WAP_PROTO_STOP_OK 22 -#define WAP_PROTO_CLOSE 23 -#define WAP_PROTO_CLOSE_OK 24 -#define WAP_PROTO_PING 25 -#define WAP_PROTO_PING_OK 26 -#define WAP_PROTO_ERROR 27 +#define WAP_PROTO_GET_PEER_LIST 21 +#define WAP_PROTO_GET_PEER_LIST_OK 22 +#define WAP_PROTO_STOP 23 +#define WAP_PROTO_STOP_OK 24 +#define WAP_PROTO_CLOSE 25 +#define WAP_PROTO_CLOSE_OK 26 +#define WAP_PROTO_PING 27 +#define WAP_PROTO_PING_OK 28 +#define WAP_PROTO_ERROR 29 #include @@ -397,6 +406,26 @@ uint64_t void wap_proto_set_grey_peerlist_size (wap_proto_t *self, uint64_t grey_peerlist_size); +// Get a copy of the white_list field +zframe_t * + wap_proto_white_list (wap_proto_t *self); +// Get the white_list field and transfer ownership to caller +zframe_t * + wap_proto_get_white_list (wap_proto_t *self); +// Set the white_list field, transferring ownership from caller +void + wap_proto_set_white_list (wap_proto_t *self, zframe_t **frame_p); + +// Get a copy of the gray_list field +zframe_t * + wap_proto_gray_list (wap_proto_t *self); +// Get the gray_list field and transfer ownership to caller +zframe_t * + wap_proto_get_gray_list (wap_proto_t *self); +// Set the gray_list field, transferring ownership from caller +void + wap_proto_set_gray_list (wap_proto_t *self, zframe_t **frame_p); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index cc4a6a1e4..8442f3422 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -41,11 +41,12 @@ typedef enum { random_outs_event = 10, get_height_event = 11, get_info_event = 12, - close_event = 13, - ping_event = 14, - expired_event = 15, - exception_event = 16, - settled_event = 17 + get_peer_list_event = 13, + close_event = 14, + ping_event = 15, + expired_event = 16, + exception_event = 17, + settled_event = 18 } event_t; // Names for state machine logging and error reporting @@ -73,6 +74,7 @@ s_event_name [] = { "RANDOM_OUTS", "GET_HEIGHT", "GET_INFO", + "GET_PEER_LIST", "CLOSE", "PING", "expired", @@ -162,6 +164,8 @@ static void height (client_t *self); static void getinfo (client_t *self); +static void + get_peer_list (client_t *self); static void deregister_wallet (client_t *self); static void @@ -376,6 +380,9 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_GET_INFO: return get_info_event; break; + case WAP_PROTO_GET_PEER_LIST: + return get_peer_list_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -774,6 +781,24 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == get_peer_list_event) { + if (!self->exception) { + // get peer list + if (self->server->verbose) + zsys_debug ("%s: $ get peer list", self->log_prefix); + get_peer_list (&self->client); + } + if (!self->exception) { + // send GET_PEER_LIST_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_PEER_LIST_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_PEER_LIST_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index cb4be741e..00b717fe7 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -396,3 +396,16 @@ signal_have_get_info_ok (client_t *self) wap_proto_white_peerlist_size (self->message), wap_proto_grey_peerlist_size (self->message)); } + +// --------------------------------------------------------------------------- +// signal_have_get_get_peer_list_ok +// + +static void +signal_have_get_peer_list_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8pp", "GET PEER LIST OK", + wap_proto_status (self->message), + wap_proto_get_white_list (self->message), + wap_proto_get_gray_list (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 0d4c9c16a..49b77c7d5 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -59,6 +59,8 @@ struct _wap_proto_t { uint64_t incoming_connections_count; // Incoming Connections Count uint64_t white_peerlist_size; // White Peerlist Size uint64_t grey_peerlist_size; // Grey Peerlist Size + zframe_t *white_list; // White list + zframe_t *gray_list; // Gray list char reason [256]; // Printable explanation }; @@ -245,6 +247,8 @@ wap_proto_destroy (wap_proto_t **self_p) zframe_destroy (&self->random_outputs); zchunk_destroy (&self->tx_data); zchunk_destroy (&self->address); + zframe_destroy (&self->white_list); + zframe_destroy (&self->gray_list); // Free object itself free (self); @@ -488,6 +492,27 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) GET_NUMBER8 (self->grey_peerlist_size); break; + case WAP_PROTO_GET_PEER_LIST: + break; + + case WAP_PROTO_GET_PEER_LIST_OK: + GET_NUMBER8 (self->status); + // Get next frame off socket + if (!zsock_rcvmore (input)) { + zsys_warning ("wap_proto: white_list is missing"); + goto malformed; + } + zframe_destroy (&self->white_list); + self->white_list = zframe_recv (input); + // Get next frame off socket + if (!zsock_rcvmore (input)) { + zsys_warning ("wap_proto: gray_list is missing"); + goto malformed; + } + zframe_destroy (&self->gray_list); + self->gray_list = zframe_recv (input); + break; + case WAP_PROTO_STOP: break; @@ -624,6 +649,9 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) frame_size += 8; // white_peerlist_size frame_size += 8; // grey_peerlist_size break; + case WAP_PROTO_GET_PEER_LIST_OK: + frame_size += 8; // status + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -774,6 +802,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->grey_peerlist_size); break; + case WAP_PROTO_GET_PEER_LIST_OK: + PUT_NUMBER8 (self->status); + nbr_frames++; + nbr_frames++; + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -807,6 +841,19 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) else zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); } + // Now send any frame fields, in order + if (self->id == WAP_PROTO_GET_PEER_LIST_OK) { + // If white_list isn't set, send an empty frame + if (self->white_list) + zframe_send (&self->white_list, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + else + zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); + // If gray_list isn't set, send an empty frame + if (self->gray_list) + zframe_send (&self->gray_list, output, ZFRAME_REUSE + (--nbr_frames? ZFRAME_MORE: 0)); + else + zmq_send (zsock_resolve (output), NULL, 0, (--nbr_frames? ZMQ_SNDMORE: 0)); + } // Now send the block_data if necessary if (have_block_data) { if (self->block_data) { @@ -971,6 +1018,25 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" grey_peerlist_size=%ld", (long) self->grey_peerlist_size); break; + case WAP_PROTO_GET_PEER_LIST: + zsys_debug ("WAP_PROTO_GET_PEER_LIST:"); + break; + + case WAP_PROTO_GET_PEER_LIST_OK: + zsys_debug ("WAP_PROTO_GET_PEER_LIST_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" white_list="); + if (self->white_list) + zframe_print (self->white_list, NULL); + else + zsys_debug ("(NULL)"); + zsys_debug (" gray_list="); + if (self->gray_list) + zframe_print (self->gray_list, NULL); + else + zsys_debug ("(NULL)"); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1108,6 +1174,12 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_GET_INFO_OK: return ("GET_INFO_OK"); break; + case WAP_PROTO_GET_PEER_LIST: + return ("GET_PEER_LIST"); + break; + case WAP_PROTO_GET_PEER_LIST_OK: + return ("GET_PEER_LIST_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -1724,6 +1796,72 @@ wap_proto_set_grey_peerlist_size (wap_proto_t *self, uint64_t grey_peerlist_size } +// -------------------------------------------------------------------------- +// Get the white_list field without transferring ownership + +zframe_t * +wap_proto_white_list (wap_proto_t *self) +{ + assert (self); + return self->white_list; +} + +// Get the white_list field and transfer ownership to caller + +zframe_t * +wap_proto_get_white_list (wap_proto_t *self) +{ + zframe_t *white_list = self->white_list; + self->white_list = NULL; + return white_list; +} + +// Set the white_list field, transferring ownership from caller + +void +wap_proto_set_white_list (wap_proto_t *self, zframe_t **frame_p) +{ + assert (self); + assert (frame_p); + zframe_destroy (&self->white_list); + self->white_list = *frame_p; + *frame_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the gray_list field without transferring ownership + +zframe_t * +wap_proto_gray_list (wap_proto_t *self) +{ + assert (self); + return self->gray_list; +} + +// Get the gray_list field and transfer ownership to caller + +zframe_t * +wap_proto_get_gray_list (wap_proto_t *self) +{ + zframe_t *gray_list = self->gray_list; + self->gray_list = NULL; + return gray_list; +} + +// Set the gray_list field, transferring ownership from caller + +void +wap_proto_set_gray_list (wap_proto_t *self, zframe_t **frame_p) +{ + assert (self); + assert (frame_p); + zframe_destroy (&self->gray_list); + self->gray_list = *frame_p; + *frame_p = NULL; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -2079,6 +2217,36 @@ wap_proto_test (bool verbose) assert (wap_proto_white_peerlist_size (self) == 123); assert (wap_proto_grey_peerlist_size (self) == 123); } + wap_proto_set_id (self, WAP_PROTO_GET_PEER_LIST); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_GET_PEER_LIST_OK); + + wap_proto_set_status (self, 123); + zframe_t *get_peer_list_ok_white_list = zframe_new ("Captcha Diem", 12); + wap_proto_set_white_list (self, &get_peer_list_ok_white_list); + zframe_t *get_peer_list_ok_gray_list = zframe_new ("Captcha Diem", 12); + wap_proto_set_gray_list (self, &get_peer_list_ok_gray_list); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (zframe_streq (wap_proto_white_list (self), "Captcha Diem")); + zframe_destroy (&get_peer_list_ok_white_list); + assert (zframe_streq (wap_proto_gray_list (self), "Captcha Diem")); + zframe_destroy (&get_peer_list_ok_gray_list); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 9f108763f..cba82210d 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -300,3 +300,13 @@ getinfo (client_t *self) { IPC::Daemon::get_info(self->message); } + +// --------------------------------------------------------------------------- +// get_peer_list +// + +static void +get_peer_list (client_t *self) +{ + IPC::Daemon::get_peer_list(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index a6154c8ac..465898b70 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -18,7 +18,7 @@ #include "daemon_deprecated_rpc.h" #include -#define MAX_RESPONSE_SIZE 2000 +#define MAX_RESPONSE_SIZE 100000 /*! * \namespace @@ -56,7 +56,7 @@ namespace * 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_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, @@ -246,12 +246,64 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getpeerlist' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getpeerlist(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_get_peer_list(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + rapidjson::Value result_json; + result_json.SetObject(); + + zframe_t *white_list_frame = wap_client_white_list(ipc_client); + rapidjson::Document white_list_json; + char *data = reinterpret_cast(zframe_data(white_list_frame)); + size_t size = zframe_size(white_list_frame); + + if (white_list_json.Parse(data, size).HasParseError()) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Couldn't parse JSON sent by daemon.", "{}"); + } + + result_json.AddMember("white_list", white_list_json["peers"], allocator); + + zframe_t *gray_list_frame = wap_client_gray_list(ipc_client); + rapidjson::Document gray_list_json; + data = reinterpret_cast(zframe_data(gray_list_frame)); + size = zframe_size(gray_list_frame); + + if (gray_list_json.Parse(data, size).HasParseError()) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Couldn't parse JSON sent by daemon.", "{}"); + } + result_json.AddMember("gray_list", gray_list_json["peers"], allocator); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", "startmining", "stopmining", "getinfo", + "getpeerlist", NULL }; @@ -261,6 +313,7 @@ namespace startmining, stopmining, getinfo, + getpeerlist, NULL }; From 919c067d7244593ae1e73998c62c71b00f4a3c5a Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Fri, 12 Jun 2015 00:19:48 +0530 Subject: [PATCH 40/45] get_mining_status IPC --- src/ipc/daemon_ipc_handlers.cpp | 28 ++++ src/ipc/include/daemon_ipc_handlers.h | 1 + src/ipc/include/wap_client.h | 21 +++ src/ipc/include/wap_client_engine.inc | 189 +++++++++++++++++++++++--- src/ipc/include/wap_proto.h | 37 ++++- src/ipc/include/wap_server_engine.inc | 35 ++++- src/ipc/wap_client/wap_client.c | 15 ++ src/ipc/wap_proto.c | 135 ++++++++++++++++++ src/ipc/wap_server/wap_server.c | 10 ++ src/rpc/daemon_deprecated_rpc.cpp | 38 ++++++ 10 files changed, 475 insertions(+), 34 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 21e7e22ad..0f98395d8 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -49,6 +49,14 @@ namespace } return true; } + bool check_core_ready() + { + if (p2p->get_payload_object().is_synchronized()) + { + return false; + } + return check_core_busy(); + } } namespace IPC @@ -416,5 +424,25 @@ namespace IPC wap_proto_set_gray_list(message, &gray_list_frame); wap_proto_set_status(message, STATUS_OK); } + + void get_mining_status(wap_proto_t *message) { + if (!check_core_ready()) { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + const cryptonote::miner& lMiner = core->get_miner(); + wap_proto_set_active(message, lMiner.is_mining() ? 1 : 0); + + if (lMiner.is_mining()) { + wap_proto_set_speed(message, lMiner.get_speed()); + wap_proto_set_thread_count(message, lMiner.get_threads_count()); + const cryptonote::account_public_address& lMiningAdr = lMiner.get_mining_address(); + std::string address = get_account_address_as_str(testnet, lMiningAdr); + zchunk_t *address_chunk = zchunk_new((void*)address.c_str(), address.length()); + wap_proto_set_address(message, &address_chunk); + } + + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 6dc32bac5..d3e49e1e5 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -78,6 +78,7 @@ namespace IPC void save_bc(wap_proto_t *message); void get_info(wap_proto_t *message); void get_peer_list(wap_proto_t *message); + void get_mining_status(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index fa67e945b..9957855f1 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -122,6 +122,11 @@ WAP_EXPORT int WAP_EXPORT int wap_client_get_peer_list (wap_client_t *self); +// Get mining status +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_mining_status (wap_client_t *self); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); @@ -202,6 +207,22 @@ WAP_EXPORT zframe_t * WAP_EXPORT zframe_t * wap_client_gray_list (wap_client_t *self); +// Return last received active +WAP_EXPORT uint8_t + wap_client_active (wap_client_t *self); + +// Return last received speed +WAP_EXPORT uint64_t + wap_client_speed (wap_client_t *self); + +// Return last received thread_count +WAP_EXPORT uint64_t + wap_client_thread_count (wap_client_t *self); + +// Return last received address +WAP_EXPORT zchunk_t * + wap_client_address (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 17d2c4b9a..410f76117 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -35,10 +35,11 @@ typedef enum { expect_get_height_ok_state = 12, expect_get_info_ok_state = 13, expect_get_peer_list_ok_state = 14, - expect_close_ok_state = 15, - defaults_state = 16, - have_error_state = 17, - reexpect_open_ok_state = 18 + expect_get_mining_status_ok_state = 15, + expect_close_ok_state = 16, + defaults_state = 17, + have_error_state = 18, + reexpect_open_ok_state = 19 } state_t; typedef enum { @@ -58,24 +59,26 @@ typedef enum { get_height_event = 13, get_info_event = 14, get_peer_list_event = 15, - destructor_event = 16, - blocks_ok_event = 17, - get_ok_event = 18, - put_ok_event = 19, - save_bc_ok_event = 20, - start_ok_event = 21, - stop_ok_event = 22, - output_indexes_ok_event = 23, - random_outs_ok_event = 24, - get_height_ok_event = 25, - get_info_ok_event = 26, - get_peer_list_ok_event = 27, - close_ok_event = 28, - ping_ok_event = 29, - error_event = 30, - exception_event = 31, - command_invalid_event = 32, - other_event = 33 + get_mining_status_event = 16, + destructor_event = 17, + blocks_ok_event = 18, + get_ok_event = 19, + put_ok_event = 20, + save_bc_ok_event = 21, + start_ok_event = 22, + stop_ok_event = 23, + output_indexes_ok_event = 24, + random_outs_ok_event = 25, + get_height_ok_event = 26, + get_info_ok_event = 27, + get_peer_list_ok_event = 28, + get_mining_status_ok_event = 29, + close_ok_event = 30, + ping_ok_event = 31, + error_event = 32, + exception_event = 33, + command_invalid_event = 34, + other_event = 35 } event_t; // Names for state machine logging and error reporting @@ -96,6 +99,7 @@ s_state_name [] = { "expect get height ok", "expect get info ok", "expect get peer list ok", + "expect get mining status ok", "expect close ok", "defaults", "have error", @@ -120,6 +124,7 @@ s_event_name [] = { "GET_HEIGHT", "GET_INFO", "GET_PEER_LIST", + "GET_MINING_STATUS", "destructor", "BLOCKS_OK", "GET_OK", @@ -132,6 +137,7 @@ s_event_name [] = { "GET_HEIGHT_OK", "GET_INFO_OK", "GET_PEER_LIST_OK", + "GET_MINING_STATUS_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -246,6 +252,8 @@ static void signal_have_get_info_ok (client_t *self); static void signal_have_get_peer_list_ok (client_t *self); +static void + signal_have_get_mining_status_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -501,6 +509,12 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_GET_PEER_LIST_OK: return get_peer_list_ok_event; break; + case WAP_PROTO_GET_MINING_STATUS: + return get_mining_status_event; + break; + case WAP_PROTO_GET_MINING_STATUS_OK: + return get_mining_status_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -835,6 +849,18 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_get_peer_list_ok_state; } else + if (self->event == get_mining_status_event) { + if (!self->exception) { + // send GET_MINING_STATUS + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_MINING_STATUS"); + wap_proto_set_id (self->message, WAP_PROTO_GET_MINING_STATUS); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_mining_status_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1391,6 +1417,51 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_get_mining_status_ok_state: + if (self->event == get_mining_status_ok_event) { + if (!self->exception) { + // signal have get mining status ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get mining status ok"); + signal_have_get_mining_status_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1698,6 +1769,10 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "GET PEER LIST")) { s_client_execute (self, get_peer_list_event); } + else + if (streq (method, "GET MINING STATUS")) { + s_client_execute (self, get_mining_status_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -1830,6 +1905,10 @@ struct _wap_client_t { uint64_t grey_peerlist_size; // Returned by actor reply zframe_t *white_list; // Returned by actor reply zframe_t *gray_list; // Returned by actor reply + uint8_t active; // Returned by actor reply + uint64_t speed; // Returned by actor reply + uint64_t thread_count; // Returned by actor reply + zchunk_t *address; // Returned by actor reply }; @@ -1884,6 +1963,7 @@ wap_client_destroy (wap_client_t **self_p) zframe_destroy (&self->random_outputs); zframe_destroy (&self->white_list); zframe_destroy (&self->gray_list); + zchunk_destroy (&self->address); free (self); *self_p = NULL; } @@ -2011,6 +2091,11 @@ s_accept_reply (wap_client_t *self, ...) zframe_destroy (&self->gray_list); zsock_recv (self->actor, "8pp", &self->status, &self->white_list, &self->gray_list); } + else + if (streq (reply, "GET MINING STATUS OK")) { + zchunk_destroy (&self->address); + zsock_recv (self->actor, "8188p", &self->status, &self->active, &self->speed, &self->thread_count, &self->address); + } break; } filter = va_arg (args, char *); @@ -2244,6 +2329,22 @@ wap_client_get_peer_list (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Get mining status +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_mining_status (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "GET MINING STATUS"); + if (s_accept_reply (self, "GET MINING STATUS OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status @@ -2462,3 +2563,47 @@ wap_client_gray_list (wap_client_t *self) assert (self); return self->gray_list; } + + +// --------------------------------------------------------------------------- +// Return last received active + +uint8_t +wap_client_active (wap_client_t *self) +{ + assert (self); + return self->active; +} + + +// --------------------------------------------------------------------------- +// Return last received speed + +uint64_t +wap_client_speed (wap_client_t *self) +{ + assert (self); + return self->speed; +} + + +// --------------------------------------------------------------------------- +// Return last received thread_count + +uint64_t +wap_client_thread_count (wap_client_t *self) +{ + assert (self); + return self->thread_count; +} + + +// --------------------------------------------------------------------------- +// Return last received address + +zchunk_t * +wap_client_address (wap_client_t *self) +{ + assert (self); + return self->address; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index a7ebab735..9ab3bd05a 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -111,6 +111,15 @@ ERROR. white_list frame White list gray_list frame Gray list + GET_MINING_STATUS - get_mining_status IPC + + GET_MINING_STATUS_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + active number 1 Active + speed number 8 Speed + thread_count number 8 Threads count + address chunk Address + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -164,13 +173,15 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_GET_INFO_OK 20 #define WAP_PROTO_GET_PEER_LIST 21 #define WAP_PROTO_GET_PEER_LIST_OK 22 -#define WAP_PROTO_STOP 23 -#define WAP_PROTO_STOP_OK 24 -#define WAP_PROTO_CLOSE 25 -#define WAP_PROTO_CLOSE_OK 26 -#define WAP_PROTO_PING 27 -#define WAP_PROTO_PING_OK 28 -#define WAP_PROTO_ERROR 29 +#define WAP_PROTO_GET_MINING_STATUS 23 +#define WAP_PROTO_GET_MINING_STATUS_OK 24 +#define WAP_PROTO_STOP 25 +#define WAP_PROTO_STOP_OK 26 +#define WAP_PROTO_CLOSE 27 +#define WAP_PROTO_CLOSE_OK 28 +#define WAP_PROTO_PING 29 +#define WAP_PROTO_PING_OK 30 +#define WAP_PROTO_ERROR 31 #include @@ -426,6 +437,18 @@ zframe_t * void wap_proto_set_gray_list (wap_proto_t *self, zframe_t **frame_p); +// Get/set the active field +byte + wap_proto_active (wap_proto_t *self); +void + wap_proto_set_active (wap_proto_t *self, byte active); + +// Get/set the speed field +uint64_t + wap_proto_speed (wap_proto_t *self); +void + wap_proto_set_speed (wap_proto_t *self, uint64_t speed); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index 8442f3422..b3d1ba8ac 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -42,11 +42,12 @@ typedef enum { get_height_event = 11, get_info_event = 12, get_peer_list_event = 13, - close_event = 14, - ping_event = 15, - expired_event = 16, - exception_event = 17, - settled_event = 18 + get_mining_status_event = 14, + close_event = 15, + ping_event = 16, + expired_event = 17, + exception_event = 18, + settled_event = 19 } event_t; // Names for state machine logging and error reporting @@ -75,6 +76,7 @@ s_event_name [] = { "GET_HEIGHT", "GET_INFO", "GET_PEER_LIST", + "GET_MINING_STATUS", "CLOSE", "PING", "expired", @@ -166,6 +168,8 @@ static void getinfo (client_t *self); static void get_peer_list (client_t *self); +static void + get_mining_status (client_t *self); static void deregister_wallet (client_t *self); static void @@ -383,6 +387,9 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_GET_PEER_LIST: return get_peer_list_event; break; + case WAP_PROTO_GET_MINING_STATUS: + return get_mining_status_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -799,6 +806,24 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == get_mining_status_event) { + if (!self->exception) { + // get mining status + if (self->server->verbose) + zsys_debug ("%s: $ get mining status", self->log_prefix); + get_mining_status (&self->client); + } + if (!self->exception) { + // send GET_MINING_STATUS_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_MINING_STATUS_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_MINING_STATUS_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 00b717fe7..9429bc998 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -409,3 +409,18 @@ signal_have_get_peer_list_ok (client_t *self) wap_proto_get_white_list (self->message), wap_proto_get_gray_list (self->message)); } + +// --------------------------------------------------------------------------- +// signal_have_get_mining_ok +// + +static void +signal_have_get_mining_status_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8188p", "GET MINING STATUS OK", + wap_proto_status (self->message), + wap_proto_active (self->message), + wap_proto_speed (self->message), + wap_proto_thread_count (self->message), + wap_proto_get_address (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 49b77c7d5..e3bfa6b94 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -61,6 +61,8 @@ struct _wap_proto_t { uint64_t grey_peerlist_size; // Grey Peerlist Size zframe_t *white_list; // White list zframe_t *gray_list; // Gray list + byte active; // Active + uint64_t speed; // Speed char reason [256]; // Printable explanation }; @@ -513,6 +515,27 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) self->gray_list = zframe_recv (input); break; + case WAP_PROTO_GET_MINING_STATUS: + break; + + case WAP_PROTO_GET_MINING_STATUS_OK: + GET_NUMBER8 (self->status); + GET_NUMBER1 (self->active); + GET_NUMBER8 (self->speed); + GET_NUMBER8 (self->thread_count); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: address is missing data"); + goto malformed; + } + zchunk_destroy (&self->address); + self->address = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + case WAP_PROTO_STOP: break; @@ -652,6 +675,15 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_GET_PEER_LIST_OK: frame_size += 8; // status break; + case WAP_PROTO_GET_MINING_STATUS_OK: + frame_size += 8; // status + frame_size += 1; // active + frame_size += 8; // speed + frame_size += 8; // thread_count + frame_size += 4; // Size is 4 octets + if (self->address) + frame_size += zchunk_size (self->address); + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -808,6 +840,22 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) nbr_frames++; break; + case WAP_PROTO_GET_MINING_STATUS_OK: + PUT_NUMBER8 (self->status); + PUT_NUMBER1 (self->active); + PUT_NUMBER8 (self->speed); + PUT_NUMBER8 (self->thread_count); + if (self->address) { + PUT_NUMBER4 (zchunk_size (self->address)); + memcpy (self->needle, + zchunk_data (self->address), + zchunk_size (self->address)); + self->needle += zchunk_size (self->address); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -1037,6 +1085,19 @@ wap_proto_print (wap_proto_t *self) zsys_debug ("(NULL)"); break; + case WAP_PROTO_GET_MINING_STATUS: + zsys_debug ("WAP_PROTO_GET_MINING_STATUS:"); + break; + + case WAP_PROTO_GET_MINING_STATUS_OK: + zsys_debug ("WAP_PROTO_GET_MINING_STATUS_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" active=%ld", (long) self->active); + zsys_debug (" speed=%ld", (long) self->speed); + zsys_debug (" thread_count=%ld", (long) self->thread_count); + zsys_debug (" address=[ ... ]"); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1180,6 +1241,12 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_GET_PEER_LIST_OK: return ("GET_PEER_LIST_OK"); break; + case WAP_PROTO_GET_MINING_STATUS: + return ("GET_MINING_STATUS"); + break; + case WAP_PROTO_GET_MINING_STATUS_OK: + return ("GET_MINING_STATUS_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -1862,6 +1929,42 @@ wap_proto_set_gray_list (wap_proto_t *self, zframe_t **frame_p) } +// -------------------------------------------------------------------------- +// Get/set the active field + +byte +wap_proto_active (wap_proto_t *self) +{ + assert (self); + return self->active; +} + +void +wap_proto_set_active (wap_proto_t *self, byte active) +{ + assert (self); + self->active = active; +} + + +// -------------------------------------------------------------------------- +// Get/set the speed field + +uint64_t +wap_proto_speed (wap_proto_t *self) +{ + assert (self); + return self->speed; +} + +void +wap_proto_set_speed (wap_proto_t *self, uint64_t speed) +{ + assert (self); + self->speed = speed; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -2247,6 +2350,38 @@ wap_proto_test (bool verbose) assert (zframe_streq (wap_proto_gray_list (self), "Captcha Diem")); zframe_destroy (&get_peer_list_ok_gray_list); } + wap_proto_set_id (self, WAP_PROTO_GET_MINING_STATUS); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_GET_MINING_STATUS_OK); + + wap_proto_set_status (self, 123); + wap_proto_set_active (self, 123); + wap_proto_set_speed (self, 123); + wap_proto_set_thread_count (self, 123); + zchunk_t *get_mining_status_ok_address = zchunk_new ("Captcha Diem", 12); + wap_proto_set_address (self, &get_mining_status_ok_address); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (wap_proto_active (self) == 123); + assert (wap_proto_speed (self) == 123); + assert (wap_proto_thread_count (self) == 123); + assert (memcmp (zchunk_data (wap_proto_address (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_mining_status_ok_address); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index cba82210d..39d37c290 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -310,3 +310,13 @@ get_peer_list (client_t *self) { IPC::Daemon::get_peer_list(self->message); } + +// --------------------------------------------------------------------------- +// get_mining_status +// + +static void +get_mining_status (client_t *self) +{ + IPC::Daemon::get_mining_status(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 465898b70..904be57c6 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -297,6 +297,42 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getminingstatus' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getminingstatus(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_get_mining_status(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + rapidjson::Value result_json; + result_json.SetObject(); + + result_json.AddMember("speed", wap_client_speed(ipc_client), allocator); + result_json.AddMember("active", (wap_client_active(ipc_client) == 1), allocator); + result_json.AddMember("threads_count", wap_client_thread_count(ipc_client), allocator); + zchunk_t *address_chunk = wap_client_address(ipc_client); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString((char*)(zchunk_data(address_chunk)), zchunk_size(address_chunk)); + result_json.AddMember("address", string_value, allocator); + + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -304,6 +340,7 @@ namespace "stopmining", "getinfo", "getpeerlist", + "getminingstatus", NULL }; @@ -314,6 +351,7 @@ namespace stopmining, getinfo, getpeerlist, + getminingstatus, NULL }; From 65c98639e738834898363ee14724fc67f251020f Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Fri, 12 Jun 2015 18:27:03 +0530 Subject: [PATCH 41/45] set_log_hash_rate, set_log_level IPC and deprecated RPC --- src/ipc/daemon_ipc_handlers.cpp | 29 +++ src/ipc/include/daemon_ipc_handlers.h | 4 + src/ipc/include/wap_client.h | 10 + src/ipc/include/wap_client_engine.inc | 256 +++++++++++++++++++++++--- src/ipc/include/wap_proto.h | 42 ++++- src/ipc/include/wap_server_engine.inc | 60 +++++- src/ipc/wap_client/wap_client.c | 42 +++++ src/ipc/wap_proto.c | 162 ++++++++++++++++ src/ipc/wap_server/wap_server.c | 22 +++ src/rpc/daemon_deprecated_rpc.cpp | 105 ++++++++++- 10 files changed, 695 insertions(+), 37 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 0f98395d8..caf1b3e83 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -444,5 +444,34 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + + void set_log_hash_rate(wap_proto_t *message) { + if (core->get_miner().is_mining()) + { + core->get_miner().do_print_hashrate(wap_proto_visible(message)); + wap_proto_set_status(message, STATUS_OK); + } + else + { + wap_proto_set_status(message, STATUS_NOT_MINING); + } + } + + void set_log_level(wap_proto_t *message) { + // zproto supports only unsigned integers afaik. so the log level is sent as + // one and casted to signed int here. + int8_t level = (int8_t)wap_proto_level(message); + if (level < LOG_LEVEL_MIN || level > LOG_LEVEL_MAX) + { + wap_proto_set_status(message, STATUS_INVALID_LOG_LEVEL); + } + else + { + epee::log_space::log_singletone::get_set_log_detalisation_level(true, level); + int otshell_utils_log_level = 100 - (level * 20); + gCurrentLogger.setDebugLevel(otshell_utils_log_level); + wap_proto_set_status(message, STATUS_OK); + } + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index d3e49e1e5..a89159d4a 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -65,6 +65,8 @@ namespace IPC const uint64_t STATUS_TX_NOT_RELAYED = 8; const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; const uint64_t STATUS_MINING_NOT_STOPPED = 10; + const uint64_t STATUS_NOT_MINING = 11; + const uint64_t STATUS_INVALID_LOG_LEVEL = 11; const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 11; namespace Daemon { @@ -79,6 +81,8 @@ namespace IPC void get_info(wap_proto_t *message); void get_peer_list(wap_proto_t *message); void get_mining_status(wap_proto_t *message); + void set_log_hash_rate(wap_proto_t *message); + void set_log_level(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 9957855f1..1e1a58cf7 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -127,6 +127,16 @@ WAP_EXPORT int WAP_EXPORT int wap_client_get_mining_status (wap_client_t *self); +// Set log hash rate +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_set_log_hash_rate (wap_client_t *self, uint8_t visible); + +// Set log hash rate +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_set_log_level (wap_client_t *self, uint8_t level); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 410f76117..14d522b05 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -36,10 +36,12 @@ typedef enum { expect_get_info_ok_state = 13, expect_get_peer_list_ok_state = 14, expect_get_mining_status_ok_state = 15, - expect_close_ok_state = 16, - defaults_state = 17, - have_error_state = 18, - reexpect_open_ok_state = 19 + expect_set_log_hash_rate_ok_state = 16, + expect_set_log_level_ok_state = 17, + expect_close_ok_state = 18, + defaults_state = 19, + have_error_state = 20, + reexpect_open_ok_state = 21 } state_t; typedef enum { @@ -60,25 +62,29 @@ typedef enum { get_info_event = 14, get_peer_list_event = 15, get_mining_status_event = 16, - destructor_event = 17, - blocks_ok_event = 18, - get_ok_event = 19, - put_ok_event = 20, - save_bc_ok_event = 21, - start_ok_event = 22, - stop_ok_event = 23, - output_indexes_ok_event = 24, - random_outs_ok_event = 25, - get_height_ok_event = 26, - get_info_ok_event = 27, - get_peer_list_ok_event = 28, - get_mining_status_ok_event = 29, - close_ok_event = 30, - ping_ok_event = 31, - error_event = 32, - exception_event = 33, - command_invalid_event = 34, - other_event = 35 + set_log_hash_rate_event = 17, + set_log_level_event = 18, + destructor_event = 19, + blocks_ok_event = 20, + get_ok_event = 21, + put_ok_event = 22, + save_bc_ok_event = 23, + start_ok_event = 24, + stop_ok_event = 25, + output_indexes_ok_event = 26, + random_outs_ok_event = 27, + get_height_ok_event = 28, + get_info_ok_event = 29, + get_peer_list_ok_event = 30, + get_mining_status_ok_event = 31, + set_log_hash_rate_ok_event = 32, + set_log_level_ok_event = 33, + close_ok_event = 34, + ping_ok_event = 35, + error_event = 36, + exception_event = 37, + command_invalid_event = 38, + other_event = 39 } event_t; // Names for state machine logging and error reporting @@ -100,6 +106,8 @@ s_state_name [] = { "expect get info ok", "expect get peer list ok", "expect get mining status ok", + "expect set log hash rate ok", + "expect set log level ok", "expect close ok", "defaults", "have error", @@ -125,6 +133,8 @@ s_event_name [] = { "GET_INFO", "GET_PEER_LIST", "GET_MINING_STATUS", + "SET_LOG_HASH_RATE", + "SET_LOG_LEVEL", "destructor", "BLOCKS_OK", "GET_OK", @@ -138,6 +148,8 @@ s_event_name [] = { "GET_INFO_OK", "GET_PEER_LIST_OK", "GET_MINING_STATUS_OK", + "SET_LOG_HASH_RATE_OK", + "SET_LOG_LEVEL_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -165,6 +177,8 @@ struct _client_args_t { zframe_t *amounts; zchunk_t *address; uint64_t thread_count; + uint8_t visible; + uint8_t level; }; typedef struct { @@ -228,6 +242,10 @@ static void prepare_get_output_indexes_command (client_t *self); static void prepare_get_random_outs_command (client_t *self); +static void + prepare_set_log_hash_rate_command (client_t *self); +static void + prepare_set_log_level_command (client_t *self); static void check_if_connection_is_dead (client_t *self); static void @@ -254,6 +272,10 @@ static void signal_have_get_peer_list_ok (client_t *self); static void signal_have_get_mining_status_ok (client_t *self); +static void + signal_have_set_log_hash_rate_ok (client_t *self); +static void + signal_have_set_log_level_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -515,6 +537,18 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_GET_MINING_STATUS_OK: return get_mining_status_ok_event; break; + case WAP_PROTO_SET_LOG_HASH_RATE: + return set_log_hash_rate_event; + break; + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + return set_log_hash_rate_ok_event; + break; + case WAP_PROTO_SET_LOG_LEVEL: + return set_log_level_event; + break; + case WAP_PROTO_SET_LOG_LEVEL_OK: + return set_log_level_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -861,6 +895,42 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_get_mining_status_ok_state; } else + if (self->event == set_log_hash_rate_event) { + if (!self->exception) { + // prepare set log hash rate command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare set log hash rate command"); + prepare_set_log_hash_rate_command (&self->client); + } + if (!self->exception) { + // send SET_LOG_HASH_RATE + if (wap_client_verbose) + zsys_debug ("wap_client: $ send SET_LOG_HASH_RATE"); + wap_proto_set_id (self->message, WAP_PROTO_SET_LOG_HASH_RATE); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_set_log_hash_rate_ok_state; + } + else + if (self->event == set_log_level_event) { + if (!self->exception) { + // prepare set log level command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare set log level command"); + prepare_set_log_level_command (&self->client); + } + if (!self->exception) { + // send SET_LOG_LEVEL + if (wap_client_verbose) + zsys_debug ("wap_client: $ send SET_LOG_LEVEL"); + wap_proto_set_id (self->message, WAP_PROTO_SET_LOG_LEVEL); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_set_log_level_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1462,6 +1532,96 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_set_log_hash_rate_ok_state: + if (self->event == set_log_hash_rate_ok_event) { + if (!self->exception) { + // signal have set log hash rate ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have set log hash rate ok"); + signal_have_set_log_hash_rate_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_set_log_level_ok_state: + if (self->event == set_log_level_ok_event) { + if (!self->exception) { + // signal have set log level ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have set log level ok"); + signal_have_set_log_level_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1773,6 +1933,16 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "GET MINING STATUS")) { s_client_execute (self, get_mining_status_event); } + else + if (streq (method, "SET LOG HASH RATE")) { + zsock_recv (self->cmdpipe, "1", &self->args.visible); + s_client_execute (self, set_log_hash_rate_event); + } + else + if (streq (method, "SET LOG LEVEL")) { + zsock_recv (self->cmdpipe, "1", &self->args.level); + s_client_execute (self, set_log_level_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -2096,6 +2266,14 @@ s_accept_reply (wap_client_t *self, ...) zchunk_destroy (&self->address); zsock_recv (self->actor, "8188p", &self->status, &self->active, &self->speed, &self->thread_count, &self->address); } + else + if (streq (reply, "SET LOG HASH RATE OK")) { + zsock_recv (self->actor, "8", &self->status); + } + else + if (streq (reply, "SET LOG LEVEL OK")) { + zsock_recv (self->actor, "8", &self->status); + } break; } filter = va_arg (args, char *); @@ -2345,6 +2523,38 @@ wap_client_get_mining_status (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Set log hash rate +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_set_log_hash_rate (wap_client_t *self, uint8_t visible) +{ + assert (self); + + zsock_send (self->actor, "s1", "SET LOG HASH RATE", visible); + if (s_accept_reply (self, "SET LOG HASH RATE OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Set log hash rate +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_set_log_level (wap_client_t *self, uint8_t level) +{ + assert (self); + + zsock_send (self->actor, "s1", "SET LOG LEVEL", level); + if (s_accept_reply (self, "SET LOG LEVEL OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 9ab3bd05a..78513f5ad 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -120,6 +120,18 @@ ERROR. thread_count number 8 Threads count address chunk Address + SET_LOG_HASH_RATE - set_log_hash_rate IPC + visible number 1 Visible + + SET_LOG_HASH_RATE_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + + SET_LOG_LEVEL - set_log_level IPC + level number 1 Level + + SET_LOG_LEVEL_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -175,13 +187,17 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_GET_PEER_LIST_OK 22 #define WAP_PROTO_GET_MINING_STATUS 23 #define WAP_PROTO_GET_MINING_STATUS_OK 24 -#define WAP_PROTO_STOP 25 -#define WAP_PROTO_STOP_OK 26 -#define WAP_PROTO_CLOSE 27 -#define WAP_PROTO_CLOSE_OK 28 -#define WAP_PROTO_PING 29 -#define WAP_PROTO_PING_OK 30 -#define WAP_PROTO_ERROR 31 +#define WAP_PROTO_SET_LOG_HASH_RATE 25 +#define WAP_PROTO_SET_LOG_HASH_RATE_OK 26 +#define WAP_PROTO_SET_LOG_LEVEL 27 +#define WAP_PROTO_SET_LOG_LEVEL_OK 28 +#define WAP_PROTO_STOP 29 +#define WAP_PROTO_STOP_OK 30 +#define WAP_PROTO_CLOSE 31 +#define WAP_PROTO_CLOSE_OK 32 +#define WAP_PROTO_PING 33 +#define WAP_PROTO_PING_OK 34 +#define WAP_PROTO_ERROR 35 #include @@ -449,6 +465,18 @@ uint64_t void wap_proto_set_speed (wap_proto_t *self, uint64_t speed); +// Get/set the visible field +byte + wap_proto_visible (wap_proto_t *self); +void + wap_proto_set_visible (wap_proto_t *self, byte visible); + +// Get/set the level field +byte + wap_proto_level (wap_proto_t *self); +void + wap_proto_set_level (wap_proto_t *self, byte level); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index b3d1ba8ac..5e746407f 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -43,11 +43,13 @@ typedef enum { get_info_event = 12, get_peer_list_event = 13, get_mining_status_event = 14, - close_event = 15, - ping_event = 16, - expired_event = 17, - exception_event = 18, - settled_event = 19 + set_log_hash_rate_event = 15, + set_log_level_event = 16, + close_event = 17, + ping_event = 18, + expired_event = 19, + exception_event = 20, + settled_event = 21 } event_t; // Names for state machine logging and error reporting @@ -77,6 +79,8 @@ s_event_name [] = { "GET_INFO", "GET_PEER_LIST", "GET_MINING_STATUS", + "SET_LOG_HASH_RATE", + "SET_LOG_LEVEL", "CLOSE", "PING", "expired", @@ -170,6 +174,10 @@ static void get_peer_list (client_t *self); static void get_mining_status (client_t *self); +static void + set_log_hash_rate (client_t *self); +static void + set_log_level (client_t *self); static void deregister_wallet (client_t *self); static void @@ -390,6 +398,12 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_GET_MINING_STATUS: return get_mining_status_event; break; + case WAP_PROTO_SET_LOG_HASH_RATE: + return set_log_hash_rate_event; + break; + case WAP_PROTO_SET_LOG_LEVEL: + return set_log_level_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -824,6 +838,42 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == set_log_hash_rate_event) { + if (!self->exception) { + // set log hash rate + if (self->server->verbose) + zsys_debug ("%s: $ set log hash rate", self->log_prefix); + set_log_hash_rate (&self->client); + } + if (!self->exception) { + // send SET_LOG_HASH_RATE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send SET_LOG_HASH_RATE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_SET_LOG_HASH_RATE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == set_log_level_event) { + if (!self->exception) { + // set log level + if (self->server->verbose) + zsys_debug ("%s: $ set log level", self->log_prefix); + set_log_level (&self->client); + } + if (!self->exception) { + // send SET_LOG_LEVEL_OK + if (self->server->verbose) + zsys_debug ("%s: $ send SET_LOG_LEVEL_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_SET_LOG_LEVEL_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 9429bc998..e985b54eb 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -424,3 +424,45 @@ signal_have_get_mining_status_ok (client_t *self) wap_proto_thread_count (self->message), wap_proto_get_address (self->message)); } + +// --------------------------------------------------------------------------- +// prepare_set_hash_log_rate_command +// + +static void +prepare_set_log_hash_rate_command (client_t *self) +{ + wap_proto_set_visible (self->message, self->args->visible); +} + +// --------------------------------------------------------------------------- +// signal_have_set_log_hash_rate_ok +// + +static void +signal_have_set_log_hash_rate_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8", "SET LOG HASH RATE OK", + wap_proto_status (self->message)); +} + +// --------------------------------------------------------------------------- +// prepare_set_log_level_command +// + +static void +prepare_set_log_level_command (client_t *self) +{ + wap_proto_set_level (self->message, self->args->level); +} + +// --------------------------------------------------------------------------- +// signal_have_set_log_level_ok +// + +static void +signal_have_set_log_level_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8", "SET LOG LEVEL OK", + wap_proto_status (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index e3bfa6b94..849da4f81 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -63,6 +63,8 @@ struct _wap_proto_t { zframe_t *gray_list; // Gray list byte active; // Active uint64_t speed; // Speed + byte visible; // Visible + byte level; // Level char reason [256]; // Printable explanation }; @@ -536,6 +538,22 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) } break; + case WAP_PROTO_SET_LOG_HASH_RATE: + GET_NUMBER1 (self->visible); + break; + + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + GET_NUMBER8 (self->status); + break; + + case WAP_PROTO_SET_LOG_LEVEL: + GET_NUMBER1 (self->level); + break; + + case WAP_PROTO_SET_LOG_LEVEL_OK: + GET_NUMBER8 (self->status); + break; + case WAP_PROTO_STOP: break; @@ -684,6 +702,18 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) if (self->address) frame_size += zchunk_size (self->address); break; + case WAP_PROTO_SET_LOG_HASH_RATE: + frame_size += 1; // visible + break; + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + frame_size += 8; // status + break; + case WAP_PROTO_SET_LOG_LEVEL: + frame_size += 1; // level + break; + case WAP_PROTO_SET_LOG_LEVEL_OK: + frame_size += 8; // status + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -856,6 +886,22 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER4 (0); // Empty chunk break; + case WAP_PROTO_SET_LOG_HASH_RATE: + PUT_NUMBER1 (self->visible); + break; + + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + PUT_NUMBER8 (self->status); + break; + + case WAP_PROTO_SET_LOG_LEVEL: + PUT_NUMBER1 (self->level); + break; + + case WAP_PROTO_SET_LOG_LEVEL_OK: + PUT_NUMBER8 (self->status); + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -1098,6 +1144,26 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" address=[ ... ]"); break; + case WAP_PROTO_SET_LOG_HASH_RATE: + zsys_debug ("WAP_PROTO_SET_LOG_HASH_RATE:"); + zsys_debug (" visible=%ld", (long) self->visible); + break; + + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + zsys_debug ("WAP_PROTO_SET_LOG_HASH_RATE_OK:"); + zsys_debug (" status=%ld", (long) self->status); + break; + + case WAP_PROTO_SET_LOG_LEVEL: + zsys_debug ("WAP_PROTO_SET_LOG_LEVEL:"); + zsys_debug (" level=%ld", (long) self->level); + break; + + case WAP_PROTO_SET_LOG_LEVEL_OK: + zsys_debug ("WAP_PROTO_SET_LOG_LEVEL_OK:"); + zsys_debug (" status=%ld", (long) self->status); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1247,6 +1313,18 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_GET_MINING_STATUS_OK: return ("GET_MINING_STATUS_OK"); break; + case WAP_PROTO_SET_LOG_HASH_RATE: + return ("SET_LOG_HASH_RATE"); + break; + case WAP_PROTO_SET_LOG_HASH_RATE_OK: + return ("SET_LOG_HASH_RATE_OK"); + break; + case WAP_PROTO_SET_LOG_LEVEL: + return ("SET_LOG_LEVEL"); + break; + case WAP_PROTO_SET_LOG_LEVEL_OK: + return ("SET_LOG_LEVEL_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -1965,6 +2043,42 @@ wap_proto_set_speed (wap_proto_t *self, uint64_t speed) } +// -------------------------------------------------------------------------- +// Get/set the visible field + +byte +wap_proto_visible (wap_proto_t *self) +{ + assert (self); + return self->visible; +} + +void +wap_proto_set_visible (wap_proto_t *self, byte visible) +{ + assert (self); + self->visible = visible; +} + + +// -------------------------------------------------------------------------- +// Get/set the level field + +byte +wap_proto_level (wap_proto_t *self) +{ + assert (self); + return self->level; +} + +void +wap_proto_set_level (wap_proto_t *self, byte level) +{ + assert (self); + self->level = level; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -2382,6 +2496,54 @@ wap_proto_test (bool verbose) assert (memcmp (zchunk_data (wap_proto_address (self)), "Captcha Diem", 12) == 0); zchunk_destroy (&get_mining_status_ok_address); } + wap_proto_set_id (self, WAP_PROTO_SET_LOG_HASH_RATE); + + wap_proto_set_visible (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_visible (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_SET_LOG_HASH_RATE_OK); + + wap_proto_set_status (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_SET_LOG_LEVEL); + + wap_proto_set_level (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_level (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_SET_LOG_LEVEL_OK); + + wap_proto_set_status (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 39d37c290..8e0eef5cf 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -320,3 +320,25 @@ get_mining_status (client_t *self) { IPC::Daemon::get_mining_status(self->message); } + +// --------------------------------------------------------------------------- +// set_log_hash_rate +// + +static void +set_log_hash_rate (client_t *self) +{ + IPC::Daemon::set_log_hash_rate(self->message); +} + + + +// --------------------------------------------------------------------------- +// set_log_level +// + +static void +set_log_level (client_t *self) +{ + IPC::Daemon::set_log_level(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 904be57c6..8b3ed389d 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -31,6 +31,7 @@ namespace int invalid_request = -32600; int invalid_params = -32602; int internal_error = -32603; + int not_mining_error = -32604; RPC::Json_rpc_http_server *server = NULL; wap_client_t *ipc_client = NULL; @@ -238,6 +239,7 @@ namespace response_json.GetAllocator()); result_json.AddMember("grey_peerlist_size", wap_client_grey_peerlist_size(ipc_client), response_json.GetAllocator()); + result_json.AddMember("status", "OK", response_json.GetAllocator()); std::string response; construct_response_string(req, result_json, response_json, response); @@ -289,6 +291,7 @@ namespace "Couldn't parse JSON sent by daemon.", "{}"); } result_json.AddMember("gray_list", gray_list_json["peers"], allocator); + result_json.AddMember("status", "OK", allocator); std::string response; construct_response_string(req, result_json, response_json, response); @@ -325,6 +328,7 @@ namespace rapidjson::Value string_value(rapidjson::kStringType); string_value.SetString((char*)(zchunk_data(address_chunk)), zchunk_size(address_chunk)); result_json.AddMember("address", string_value, allocator); + result_json.AddMember("status", "OK", allocator); std::string response; construct_response_string(req, result_json, response_json, response); @@ -333,6 +337,101 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'setloghashrate' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int setloghashrate(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("visible") || !request_json["visible"].IsBool()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'visible' field", "{}"); + } + + bool visible = request_json["visible"].GetBool(); + // 0MQ server expects an integer. 1 is true, 0 is false. + int rc = wap_client_set_log_hash_rate(ipc_client, visible ? 1 : 0); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + if (wap_client_status(ipc_client) == IPC::STATUS_NOT_MINING) { + return ns_rpc_create_error(buf, len, req, not_mining_error, + "Not mining", "{}"); + } + + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } + + /*! + * \brief Implementation of 'setloglevel' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int setloglevel(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("level") || !request_json["level"].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'level' field", "{}"); + } + + int level = request_json["level"].GetInt(); + int rc = wap_client_set_log_level(ipc_client, level); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + if (wap_client_status(ipc_client) == IPC::STATUS_INVALID_LOG_LEVEL) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Invalid log level", "{}"); + } + + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -341,6 +440,8 @@ namespace "getinfo", "getpeerlist", "getminingstatus", + "setloghashrate", + "setloglevel", NULL }; @@ -352,6 +453,8 @@ namespace getinfo, getpeerlist, getminingstatus, + setloghashrate, + setloglevel, NULL }; @@ -414,11 +517,9 @@ namespace RPC server->stop(); delete server; } - std::cout << "HTTP done\n\n"; if (ipc_client) { wap_client_destroy(&ipc_client); } - std::cout << "IPC done\n\n"; } } } From 26f7c3d5a9a63026a797e5ff7904498332cb0068 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Sat, 13 Jun 2015 16:46:13 +0530 Subject: [PATCH 42/45] get_block_count, start_save_graph, stop_save_graph IPC --- src/ipc/daemon_ipc_handlers.cpp | 10 ++ src/ipc/include/daemon_ipc_handlers.h | 2 + src/ipc/include/wap_client.h | 10 ++ src/ipc/include/wap_client_engine.inc | 240 +++++++++++++++++++++++--- src/ipc/include/wap_proto.h | 28 ++- src/ipc/include/wap_server_engine.inc | 60 ++++++- src/ipc/wap_client/wap_client.c | 22 +++ src/ipc/wap_proto.c | 102 +++++++++++ src/ipc/wap_server/wap_server.c | 20 +++ src/rpc/daemon_deprecated_rpc.cpp | 69 ++++++++ 10 files changed, 526 insertions(+), 37 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index caf1b3e83..2e83d7601 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -473,5 +473,15 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } } + + void start_save_graph(wap_proto_t *message) { + p2p->set_save_graph(true); + wap_proto_set_status(message, STATUS_OK); + } + + void stop_save_graph(wap_proto_t *message) { + p2p->set_save_graph(false); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index a89159d4a..545712815 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -83,6 +83,8 @@ namespace IPC void get_mining_status(wap_proto_t *message); void set_log_hash_rate(wap_proto_t *message); void set_log_level(wap_proto_t *message); + void start_save_graph(wap_proto_t *message); + void stop_save_graph(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index 1e1a58cf7..c39d6b08d 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -137,6 +137,16 @@ WAP_EXPORT int WAP_EXPORT int wap_client_set_log_level (wap_client_t *self, uint8_t level); +// Start save graph +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_start_save_graph (wap_client_t *self); + +// Stop save graph +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_stop_save_graph (wap_client_t *self); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 14d522b05..3ec540854 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -38,10 +38,12 @@ typedef enum { expect_get_mining_status_ok_state = 15, expect_set_log_hash_rate_ok_state = 16, expect_set_log_level_ok_state = 17, - expect_close_ok_state = 18, - defaults_state = 19, - have_error_state = 20, - reexpect_open_ok_state = 21 + expect_start_save_graph_ok_state = 18, + expect_stop_save_graph_ok_state = 19, + expect_close_ok_state = 20, + defaults_state = 21, + have_error_state = 22, + reexpect_open_ok_state = 23 } state_t; typedef enum { @@ -64,27 +66,31 @@ typedef enum { get_mining_status_event = 16, set_log_hash_rate_event = 17, set_log_level_event = 18, - destructor_event = 19, - blocks_ok_event = 20, - get_ok_event = 21, - put_ok_event = 22, - save_bc_ok_event = 23, - start_ok_event = 24, - stop_ok_event = 25, - output_indexes_ok_event = 26, - random_outs_ok_event = 27, - get_height_ok_event = 28, - get_info_ok_event = 29, - get_peer_list_ok_event = 30, - get_mining_status_ok_event = 31, - set_log_hash_rate_ok_event = 32, - set_log_level_ok_event = 33, - close_ok_event = 34, - ping_ok_event = 35, - error_event = 36, - exception_event = 37, - command_invalid_event = 38, - other_event = 39 + start_save_graph_event = 19, + stop_save_graph_event = 20, + destructor_event = 21, + blocks_ok_event = 22, + get_ok_event = 23, + put_ok_event = 24, + save_bc_ok_event = 25, + start_ok_event = 26, + stop_ok_event = 27, + output_indexes_ok_event = 28, + random_outs_ok_event = 29, + get_height_ok_event = 30, + get_info_ok_event = 31, + get_peer_list_ok_event = 32, + get_mining_status_ok_event = 33, + set_log_hash_rate_ok_event = 34, + set_log_level_ok_event = 35, + start_save_graph_ok_event = 36, + stop_save_graph_ok_event = 37, + close_ok_event = 38, + ping_ok_event = 39, + error_event = 40, + exception_event = 41, + command_invalid_event = 42, + other_event = 43 } event_t; // Names for state machine logging and error reporting @@ -108,6 +114,8 @@ s_state_name [] = { "expect get mining status ok", "expect set log hash rate ok", "expect set log level ok", + "expect start save graph ok", + "expect stop save graph ok", "expect close ok", "defaults", "have error", @@ -135,6 +143,8 @@ s_event_name [] = { "GET_MINING_STATUS", "SET_LOG_HASH_RATE", "SET_LOG_LEVEL", + "START_SAVE_GRAPH", + "STOP_SAVE_GRAPH", "destructor", "BLOCKS_OK", "GET_OK", @@ -150,6 +160,8 @@ s_event_name [] = { "GET_MINING_STATUS_OK", "SET_LOG_HASH_RATE_OK", "SET_LOG_LEVEL_OK", + "START_SAVE_GRAPH_OK", + "STOP_SAVE_GRAPH_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -276,6 +288,10 @@ static void signal_have_set_log_hash_rate_ok (client_t *self); static void signal_have_set_log_level_ok (client_t *self); +static void + signal_have_start_save_graph_ok (client_t *self); +static void + signal_have_stop_save_graph_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -549,6 +565,18 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_SET_LOG_LEVEL_OK: return set_log_level_ok_event; break; + case WAP_PROTO_START_SAVE_GRAPH: + return start_save_graph_event; + break; + case WAP_PROTO_START_SAVE_GRAPH_OK: + return start_save_graph_ok_event; + break; + case WAP_PROTO_STOP_SAVE_GRAPH: + return stop_save_graph_event; + break; + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + return stop_save_graph_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -931,6 +959,30 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_set_log_level_ok_state; } else + if (self->event == start_save_graph_event) { + if (!self->exception) { + // send START_SAVE_GRAPH + if (wap_client_verbose) + zsys_debug ("wap_client: $ send START_SAVE_GRAPH"); + wap_proto_set_id (self->message, WAP_PROTO_START_SAVE_GRAPH); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_start_save_graph_ok_state; + } + else + if (self->event == stop_save_graph_event) { + if (!self->exception) { + // send STOP_SAVE_GRAPH + if (wap_client_verbose) + zsys_debug ("wap_client: $ send STOP_SAVE_GRAPH"); + wap_proto_set_id (self->message, WAP_PROTO_STOP_SAVE_GRAPH); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_stop_save_graph_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1622,6 +1674,96 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_start_save_graph_ok_state: + if (self->event == start_save_graph_ok_event) { + if (!self->exception) { + // signal have start save graph ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have start save graph ok"); + signal_have_start_save_graph_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_stop_save_graph_ok_state: + if (self->event == stop_save_graph_ok_event) { + if (!self->exception) { + // signal have stop save graph ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have stop save graph ok"); + signal_have_stop_save_graph_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -1943,6 +2085,14 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) zsock_recv (self->cmdpipe, "1", &self->args.level); s_client_execute (self, set_log_level_event); } + else + if (streq (method, "START SAVE GRAPH")) { + s_client_execute (self, start_save_graph_event); + } + else + if (streq (method, "STOP SAVE GRAPH")) { + s_client_execute (self, stop_save_graph_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -2274,6 +2424,14 @@ s_accept_reply (wap_client_t *self, ...) if (streq (reply, "SET LOG LEVEL OK")) { zsock_recv (self->actor, "8", &self->status); } + else + if (streq (reply, "START SAVE GRAPH OK")) { + zsock_recv (self->actor, "8", &self->status); + } + else + if (streq (reply, "STOP SAVE GRAPH OK")) { + zsock_recv (self->actor, "8", &self->status); + } break; } filter = va_arg (args, char *); @@ -2555,6 +2713,38 @@ wap_client_set_log_level (wap_client_t *self, uint8_t level) } +// --------------------------------------------------------------------------- +// Start save graph +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_start_save_graph (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "START SAVE GRAPH"); + if (s_accept_reply (self, "START SAVE GRAPH OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Stop save graph +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_stop_save_graph (wap_client_t *self) +{ + assert (self); + + zsock_send (self->actor, "s", "STOP SAVE GRAPH"); + if (s_accept_reply (self, "STOP SAVE GRAPH OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 78513f5ad..15b66f468 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -132,6 +132,16 @@ ERROR. SET_LOG_LEVEL_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) status number 8 Status + START_SAVE_GRAPH - start_save_graph IPC + + START_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + + STOP_SAVE_GRAPH - stop_save_graph IPC + + STOP_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -191,13 +201,17 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_SET_LOG_HASH_RATE_OK 26 #define WAP_PROTO_SET_LOG_LEVEL 27 #define WAP_PROTO_SET_LOG_LEVEL_OK 28 -#define WAP_PROTO_STOP 29 -#define WAP_PROTO_STOP_OK 30 -#define WAP_PROTO_CLOSE 31 -#define WAP_PROTO_CLOSE_OK 32 -#define WAP_PROTO_PING 33 -#define WAP_PROTO_PING_OK 34 -#define WAP_PROTO_ERROR 35 +#define WAP_PROTO_START_SAVE_GRAPH 29 +#define WAP_PROTO_START_SAVE_GRAPH_OK 30 +#define WAP_PROTO_STOP_SAVE_GRAPH 31 +#define WAP_PROTO_STOP_SAVE_GRAPH_OK 32 +#define WAP_PROTO_STOP 33 +#define WAP_PROTO_STOP_OK 34 +#define WAP_PROTO_CLOSE 35 +#define WAP_PROTO_CLOSE_OK 36 +#define WAP_PROTO_PING 37 +#define WAP_PROTO_PING_OK 38 +#define WAP_PROTO_ERROR 39 #include diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index 5e746407f..eaa4f37ce 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -45,11 +45,13 @@ typedef enum { get_mining_status_event = 14, set_log_hash_rate_event = 15, set_log_level_event = 16, - close_event = 17, - ping_event = 18, - expired_event = 19, - exception_event = 20, - settled_event = 21 + start_save_graph_event = 17, + stop_save_graph_event = 18, + close_event = 19, + ping_event = 20, + expired_event = 21, + exception_event = 22, + settled_event = 23 } event_t; // Names for state machine logging and error reporting @@ -81,6 +83,8 @@ s_event_name [] = { "GET_MINING_STATUS", "SET_LOG_HASH_RATE", "SET_LOG_LEVEL", + "START_SAVE_GRAPH", + "STOP_SAVE_GRAPH", "CLOSE", "PING", "expired", @@ -178,6 +182,10 @@ static void set_log_hash_rate (client_t *self); static void set_log_level (client_t *self); +static void + start_save_graph (client_t *self); +static void + stop_save_graph (client_t *self); static void deregister_wallet (client_t *self); static void @@ -404,6 +412,12 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_SET_LOG_LEVEL: return set_log_level_event; break; + case WAP_PROTO_START_SAVE_GRAPH: + return start_save_graph_event; + break; + case WAP_PROTO_STOP_SAVE_GRAPH: + return stop_save_graph_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -874,6 +888,42 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == start_save_graph_event) { + if (!self->exception) { + // start save graph + if (self->server->verbose) + zsys_debug ("%s: $ start save graph", self->log_prefix); + start_save_graph (&self->client); + } + if (!self->exception) { + // send START_SAVE_GRAPH_OK + if (self->server->verbose) + zsys_debug ("%s: $ send START_SAVE_GRAPH_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_START_SAVE_GRAPH_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == stop_save_graph_event) { + if (!self->exception) { + // stop save graph + if (self->server->verbose) + zsys_debug ("%s: $ stop save graph", self->log_prefix); + stop_save_graph (&self->client); + } + if (!self->exception) { + // send STOP_SAVE_GRAPH_OK + if (self->server->verbose) + zsys_debug ("%s: $ send STOP_SAVE_GRAPH_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_STOP_SAVE_GRAPH_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index e985b54eb..8973abe6e 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -466,3 +466,25 @@ signal_have_set_log_level_ok (client_t *self) zsock_send (self->cmdpipe, "s8", "SET LOG LEVEL OK", wap_proto_status (self->message)); } + +// --------------------------------------------------------------------------- +// signal_have_start_save_graph_ok +// + +static void +signal_have_start_save_graph_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8", "START SAVE GRAPH OK", + wap_proto_status (self->message)); +} + +// --------------------------------------------------------------------------- +// signal_have_stop_save_graph_ok +// + +static void +signal_have_stop_save_graph_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8", "STOP SAVE GRAPH OK", + wap_proto_status (self->message)); +} diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 849da4f81..6a6f0af26 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -554,6 +554,20 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) GET_NUMBER8 (self->status); break; + case WAP_PROTO_START_SAVE_GRAPH: + break; + + case WAP_PROTO_START_SAVE_GRAPH_OK: + GET_NUMBER8 (self->status); + break; + + case WAP_PROTO_STOP_SAVE_GRAPH: + break; + + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + GET_NUMBER8 (self->status); + break; + case WAP_PROTO_STOP: break; @@ -714,6 +728,12 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_SET_LOG_LEVEL_OK: frame_size += 8; // status break; + case WAP_PROTO_START_SAVE_GRAPH_OK: + frame_size += 8; // status + break; + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + frame_size += 8; // status + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -902,6 +922,14 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->status); break; + case WAP_PROTO_START_SAVE_GRAPH_OK: + PUT_NUMBER8 (self->status); + break; + + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + PUT_NUMBER8 (self->status); + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -1164,6 +1192,24 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" status=%ld", (long) self->status); break; + case WAP_PROTO_START_SAVE_GRAPH: + zsys_debug ("WAP_PROTO_START_SAVE_GRAPH:"); + break; + + case WAP_PROTO_START_SAVE_GRAPH_OK: + zsys_debug ("WAP_PROTO_START_SAVE_GRAPH_OK:"); + zsys_debug (" status=%ld", (long) self->status); + break; + + case WAP_PROTO_STOP_SAVE_GRAPH: + zsys_debug ("WAP_PROTO_STOP_SAVE_GRAPH:"); + break; + + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + zsys_debug ("WAP_PROTO_STOP_SAVE_GRAPH_OK:"); + zsys_debug (" status=%ld", (long) self->status); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1325,6 +1371,18 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_SET_LOG_LEVEL_OK: return ("SET_LOG_LEVEL_OK"); break; + case WAP_PROTO_START_SAVE_GRAPH: + return ("START_SAVE_GRAPH"); + break; + case WAP_PROTO_START_SAVE_GRAPH_OK: + return ("START_SAVE_GRAPH_OK"); + break; + case WAP_PROTO_STOP_SAVE_GRAPH: + return ("STOP_SAVE_GRAPH"); + break; + case WAP_PROTO_STOP_SAVE_GRAPH_OK: + return ("STOP_SAVE_GRAPH_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -2539,6 +2597,50 @@ wap_proto_test (bool verbose) wap_proto_send (self, output); wap_proto_send (self, output); + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_START_SAVE_GRAPH); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_START_SAVE_GRAPH_OK); + + wap_proto_set_status (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_STOP_SAVE_GRAPH); + + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + } + wap_proto_set_id (self, WAP_PROTO_STOP_SAVE_GRAPH_OK); + + wap_proto_set_status (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + for (instance = 0; instance < 2; instance++) { wap_proto_recv (self, input); assert (wap_proto_routing_id (self)); diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 8e0eef5cf..699a9c11a 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -342,3 +342,23 @@ set_log_level (client_t *self) { IPC::Daemon::set_log_level(self->message); } + +// --------------------------------------------------------------------------- +// start_save_graph +// + +static void +start_save_graph (client_t *self) +{ + IPC::Daemon::start_save_graph(self->message); +} + +// --------------------------------------------------------------------------- +// stop_save_graph +// + +static void +stop_save_graph (client_t *self) +{ + IPC::Daemon::stop_save_graph(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 8b3ed389d..338ef2015 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -432,6 +432,69 @@ namespace return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); } + /*! + * \brief Implementation of 'getblockcount' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblockcount(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_get_height(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + uint64_t count = wap_client_height(ipc_client); + rapidjson::Document response_json; + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("count", count, response_json.GetAllocator()); + result_json.AddMember("status", "OK", response_json.GetAllocator()); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'startsavegraph' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int startsavegraph(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_start_save_graph(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } + + /*! + * \brief Implementation of 'stopsavegraph' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int stopsavegraph(char *buf, int len, struct ns_rpc_request *req) + { + connect_to_daemon(); + int rc = wap_client_stop_save_graph(ipc_client); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); + } // Contains a list of method names. const char *method_names[] = { "getheight", @@ -442,6 +505,9 @@ namespace "getminingstatus", "setloghashrate", "setloglevel", + "getblockcount", + "startsavegraph", + "stopsavegraph", NULL }; @@ -455,6 +521,9 @@ namespace getminingstatus, setloghashrate, setloglevel, + getblockcount, + startsavegraph, + stopsavegraph, NULL }; From c7d27d49793466ae152672b11c9a2ee52fa755c7 Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Wed, 17 Jun 2015 19:25:07 +0530 Subject: [PATCH 43/45] get_block_hash, get_block_template IPC + deprecated RPC --- src/ipc/daemon_ipc_handlers.cpp | 103 +++++++ src/ipc/include/daemon_ipc_handlers.h | 8 +- src/ipc/include/wap_client.h | 26 ++ src/ipc/include/wap_client_engine.inc | 320 +++++++++++++++++++-- src/ipc/include/wap_proto.h | 79 ++++- src/ipc/include/wap_server_engine.inc | 60 +++- src/ipc/wap_client/wap_client.c | 161 +++++++---- src/ipc/wap_proto.c | 397 ++++++++++++++++++++++++++ src/ipc/wap_server/wap_server.c | 20 ++ src/rpc/daemon_deprecated_rpc.cpp | 313 ++++++++++++++++++-- 10 files changed, 1363 insertions(+), 124 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 2e83d7601..563d0621f 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -57,6 +57,25 @@ namespace } return check_core_busy(); } + + //------------------------------------------------------------------------------------------------------------------------------ + // equivalent of strstr, but with arbitrary bytes (ie, NULs) + // This does not differentiate between "not found" and "found at offset 0" + // (taken straight from core_rpc_server.cpp) + uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) + { + const void *buf = start_buff; + const void *end = (const char*)buf + buflen; + if (patlen > buflen || patlen == 0) return 0; + while (buflen > 0 && (buf = memchr(buf, ((const char*)pat)[0], buflen-patlen + 1))) + { + if (memcmp(buf,pat,patlen) == 0) + return (const char*)buf - (const char*)start_buff; + buf = (const char*)buf + 1; + buflen = (const char*)end - (const char*)buf; + } + return 0; + } } namespace IPC @@ -483,5 +502,89 @@ namespace IPC p2p->set_save_graph(false); wap_proto_set_status(message, STATUS_OK); } + + void get_block_hash(wap_proto_t *message) { + if (!check_core_busy()) + { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + uint64_t height = wap_proto_height(message); + if (core->get_current_blockchain_height() <= height) + { + wap_proto_set_status(message, STATUS_HEIGHT_TOO_BIG); + return; + } + std::string hash = string_tools::pod_to_hex(core->get_block_id_by_height(height)); + zchunk_t *hash_chunk = zchunk_new((void*)(hash.c_str()), hash.length()); + wap_proto_set_hash(message, &hash_chunk); + wap_proto_set_status(message, STATUS_OK); + } + + void get_block_template(wap_proto_t *message) { + if (!check_core_ready()) + { + wap_proto_set_status(message, STATUS_CORE_BUSY); + return; + } + + uint64_t reserve_size = wap_proto_reserve_size(message); + if (reserve_size > 255) + { + wap_proto_set_status(message, STATUS_RESERVE_SIZE_TOO_BIG); + return; + } + + cryptonote::account_public_address acc = AUTO_VAL_INIT(acc); + + zchunk_t *address_chunk = wap_proto_address(message); + std::string address((char*)zchunk_data(address_chunk), zchunk_size(address_chunk)); + if (!address.size() || !cryptonote::get_account_address_from_str(acc, testnet, address)) + { + wap_proto_set_status(message, STATUS_WRONG_ADDRESS); + return; + } + + cryptonote::block b = AUTO_VAL_INIT(b); + cryptonote::blobdata blob_reserve; + blob_reserve.resize(reserve_size, 0); + uint64_t difficulty = wap_proto_difficulty(message); + uint64_t height; + if (!core->get_block_template(b, acc, difficulty, height, blob_reserve)) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + cryptonote::blobdata block_blob = t_serializable_object_to_blob(b); + crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(b.miner_tx); + if (tx_pub_key == cryptonote::null_pkey) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + uint64_t reserved_offset = slow_memmem((void*)block_blob.data(), block_blob.size(), &tx_pub_key, sizeof(tx_pub_key)); + if (!reserved_offset) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + reserved_offset += sizeof(tx_pub_key) + 3; // 3 bytes: tag for TX_EXTRA_TAG_PUBKEY(1 byte), tag for TX_EXTRA_NONCE(1 byte), counter in TX_EXTRA_NONCE(1 byte) + if (reserved_offset + reserve_size > block_blob.size()) + { + wap_proto_set_status(message, STATUS_INTERNAL_ERROR); + return; + } + wap_proto_set_height(message, height); + wap_proto_set_difficulty(message, difficulty); + wap_proto_set_reserved_offset(message, reserved_offset); + std::string prev_hash = string_tools::pod_to_hex(b.prev_id); + zchunk_t *prev_hash_chunk = zchunk_new((void*)prev_hash.c_str(), prev_hash.length()); + wap_proto_set_prev_hash(message, &prev_hash_chunk); + + cryptonote::blobdata blocktemplate_blob = string_tools::buff_to_hex_nodelimer(block_blob); + zchunk_t *blob_chunk = zchunk_new((void*)blocktemplate_blob.c_str(), blocktemplate_blob.length()); + wap_proto_set_block_template_blob(message, &blob_chunk); + wap_proto_set_status(message, STATUS_OK); + } } } diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 545712815..231a9bae5 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -66,8 +66,10 @@ namespace IPC const uint64_t STATUS_RANDOM_OUTS_FAILED = 9; const uint64_t STATUS_MINING_NOT_STOPPED = 10; const uint64_t STATUS_NOT_MINING = 11; - const uint64_t STATUS_INVALID_LOG_LEVEL = 11; - const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 11; + const uint64_t STATUS_INVALID_LOG_LEVEL = 12; + const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 13; + const uint64_t STATUS_HEIGHT_TOO_BIG = 13; + const uint64_t STATUS_RESERVE_SIZE_TOO_BIG = 14; namespace Daemon { void start_mining(wap_proto_t *message); @@ -85,6 +87,8 @@ namespace IPC void set_log_level(wap_proto_t *message); void start_save_graph(wap_proto_t *message); void stop_save_graph(wap_proto_t *message); + void get_block_hash(wap_proto_t *message); + void get_block_template(wap_proto_t *message); void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); diff --git a/src/ipc/include/wap_client.h b/src/ipc/include/wap_client.h index c39d6b08d..777528e26 100644 --- a/src/ipc/include/wap_client.h +++ b/src/ipc/include/wap_client.h @@ -147,6 +147,16 @@ WAP_EXPORT int WAP_EXPORT int wap_client_stop_save_graph (wap_client_t *self); +// Get block hash +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_block_hash (wap_client_t *self, uint64_t height); + +// Get block template +// Returns >= 0 if successful, -1 if interrupted. +WAP_EXPORT int + wap_client_get_block_template (wap_client_t *self, uint64_t reserve_size, zchunk_t **address_p); + // Return last received status WAP_EXPORT int wap_client_status (wap_client_t *self); @@ -243,6 +253,22 @@ WAP_EXPORT uint64_t WAP_EXPORT zchunk_t * wap_client_address (wap_client_t *self); +// Return last received hash +WAP_EXPORT zchunk_t * + wap_client_hash (wap_client_t *self); + +// Return last received reserved_offset +WAP_EXPORT uint64_t + wap_client_reserved_offset (wap_client_t *self); + +// Return last received prev_hash +WAP_EXPORT zchunk_t * + wap_client_prev_hash (wap_client_t *self); + +// Return last received block_template_blob +WAP_EXPORT zchunk_t * + wap_client_block_template_blob (wap_client_t *self); + // Self test of this class WAP_EXPORT void wap_client_test (bool verbose); diff --git a/src/ipc/include/wap_client_engine.inc b/src/ipc/include/wap_client_engine.inc index 3ec540854..621c5d295 100644 --- a/src/ipc/include/wap_client_engine.inc +++ b/src/ipc/include/wap_client_engine.inc @@ -40,10 +40,12 @@ typedef enum { expect_set_log_level_ok_state = 17, expect_start_save_graph_ok_state = 18, expect_stop_save_graph_ok_state = 19, - expect_close_ok_state = 20, - defaults_state = 21, - have_error_state = 22, - reexpect_open_ok_state = 23 + expect_get_block_hash_ok_state = 20, + expect_get_block_template_ok_state = 21, + expect_close_ok_state = 22, + defaults_state = 23, + have_error_state = 24, + reexpect_open_ok_state = 25 } state_t; typedef enum { @@ -68,29 +70,33 @@ typedef enum { set_log_level_event = 18, start_save_graph_event = 19, stop_save_graph_event = 20, - destructor_event = 21, - blocks_ok_event = 22, - get_ok_event = 23, - put_ok_event = 24, - save_bc_ok_event = 25, - start_ok_event = 26, - stop_ok_event = 27, - output_indexes_ok_event = 28, - random_outs_ok_event = 29, - get_height_ok_event = 30, - get_info_ok_event = 31, - get_peer_list_ok_event = 32, - get_mining_status_ok_event = 33, - set_log_hash_rate_ok_event = 34, - set_log_level_ok_event = 35, - start_save_graph_ok_event = 36, - stop_save_graph_ok_event = 37, - close_ok_event = 38, - ping_ok_event = 39, - error_event = 40, - exception_event = 41, - command_invalid_event = 42, - other_event = 43 + get_block_hash_event = 21, + get_block_template_event = 22, + destructor_event = 23, + blocks_ok_event = 24, + get_ok_event = 25, + put_ok_event = 26, + save_bc_ok_event = 27, + start_ok_event = 28, + stop_ok_event = 29, + output_indexes_ok_event = 30, + random_outs_ok_event = 31, + get_height_ok_event = 32, + get_info_ok_event = 33, + get_peer_list_ok_event = 34, + get_mining_status_ok_event = 35, + set_log_hash_rate_ok_event = 36, + set_log_level_ok_event = 37, + start_save_graph_ok_event = 38, + stop_save_graph_ok_event = 39, + get_block_hash_ok_event = 40, + get_block_template_ok_event = 41, + close_ok_event = 42, + ping_ok_event = 43, + error_event = 44, + exception_event = 45, + command_invalid_event = 46, + other_event = 47 } event_t; // Names for state machine logging and error reporting @@ -116,6 +122,8 @@ s_state_name [] = { "expect set log level ok", "expect start save graph ok", "expect stop save graph ok", + "expect get block hash ok", + "expect get block template ok", "expect close ok", "defaults", "have error", @@ -145,6 +153,8 @@ s_event_name [] = { "SET_LOG_LEVEL", "START_SAVE_GRAPH", "STOP_SAVE_GRAPH", + "GET_BLOCK_HASH", + "GET_BLOCK_TEMPLATE", "destructor", "BLOCKS_OK", "GET_OK", @@ -162,6 +172,8 @@ s_event_name [] = { "SET_LOG_LEVEL_OK", "START_SAVE_GRAPH_OK", "STOP_SAVE_GRAPH_OK", + "GET_BLOCK_HASH_OK", + "GET_BLOCK_TEMPLATE_OK", "CLOSE_OK", "PING_OK", "ERROR", @@ -191,6 +203,8 @@ struct _client_args_t { uint64_t thread_count; uint8_t visible; uint8_t level; + uint64_t height; + uint64_t reserve_size; }; typedef struct { @@ -258,6 +272,10 @@ static void prepare_set_log_hash_rate_command (client_t *self); static void prepare_set_log_level_command (client_t *self); +static void + prepare_get_block_hash_command (client_t *self); +static void + prepare_get_block_template_command (client_t *self); static void check_if_connection_is_dead (client_t *self); static void @@ -292,6 +310,10 @@ static void signal_have_start_save_graph_ok (client_t *self); static void signal_have_stop_save_graph_ok (client_t *self); +static void + signal_have_get_block_hash_ok (client_t *self); +static void + signal_have_get_block_template_ok (client_t *self); static void signal_failure (client_t *self); static void @@ -577,6 +599,18 @@ s_protocol_event (s_client_t *self, wap_proto_t *message) case WAP_PROTO_STOP_SAVE_GRAPH_OK: return stop_save_graph_ok_event; break; + case WAP_PROTO_GET_BLOCK_HASH: + return get_block_hash_event; + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + return get_block_hash_ok_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return get_block_template_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + return get_block_template_ok_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -983,6 +1017,42 @@ s_client_execute (s_client_t *self, event_t event) self->state = expect_stop_save_graph_ok_state; } else + if (self->event == get_block_hash_event) { + if (!self->exception) { + // prepare get block hash command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get block hash command"); + prepare_get_block_hash_command (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_HASH + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_BLOCK_HASH"); + wap_proto_set_id (self->message, WAP_PROTO_GET_BLOCK_HASH); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_block_hash_ok_state; + } + else + if (self->event == get_block_template_event) { + if (!self->exception) { + // prepare get block template command + if (wap_client_verbose) + zsys_debug ("wap_client: $ prepare get block template command"); + prepare_get_block_template_command (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_TEMPLATE + if (wap_client_verbose) + zsys_debug ("wap_client: $ send GET_BLOCK_TEMPLATE"); + wap_proto_set_id (self->message, WAP_PROTO_GET_BLOCK_TEMPLATE); + wap_proto_send (self->message, self->dealer); + } + if (!self->exception) + self->state = expect_get_block_template_ok_state; + } + else if (self->event == destructor_event) { if (!self->exception) { // send CLOSE @@ -1764,6 +1834,96 @@ s_client_execute (s_client_t *self, event_t event) } break; + case expect_get_block_hash_ok_state: + if (self->event == get_block_hash_ok_event) { + if (!self->exception) { + // signal have get block hash ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get block hash ok"); + signal_have_get_block_hash_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + + case expect_get_block_template_ok_state: + if (self->event == get_block_template_ok_event) { + if (!self->exception) { + // signal have get block template ok + if (wap_client_verbose) + zsys_debug ("wap_client: $ signal have get block template ok"); + signal_have_get_block_template_ok (&self->client); + } + if (!self->exception) + self->state = connected_state; + } + else + if (self->event == ping_ok_event) { + if (!self->exception) { + // client is connected + if (wap_client_verbose) + zsys_debug ("wap_client: $ client is connected"); + client_is_connected (&self->client); + } + } + else + if (self->event == error_event) { + if (!self->exception) { + // check status code + if (wap_client_verbose) + zsys_debug ("wap_client: $ check status code"); + check_status_code (&self->client); + } + if (!self->exception) + self->state = have_error_state; + } + else + if (self->event == exception_event) { + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ exception"); + } + else { + // Handle unexpected protocol events + // No action - just logging + if (wap_client_verbose) + zsys_debug ("wap_client: $ *"); + } + break; + case expect_close_ok_state: if (self->event == close_ok_event) { if (!self->exception) { @@ -2093,6 +2253,17 @@ s_client_handle_cmdpipe (zloop_t *loop, zsock_t *reader, void *argument) if (streq (method, "STOP SAVE GRAPH")) { s_client_execute (self, stop_save_graph_event); } + else + if (streq (method, "GET BLOCK HASH")) { + zsock_recv (self->cmdpipe, "8", &self->args.height); + s_client_execute (self, get_block_hash_event); + } + else + if (streq (method, "GET BLOCK TEMPLATE")) { + zchunk_destroy (&self->args.address); + zsock_recv (self->cmdpipe, "8p", &self->args.reserve_size, &self->args.address); + s_client_execute (self, get_block_template_event); + } // Cleanup pipe if any argument frames are still waiting to be eaten if (zsock_rcvmore (self->cmdpipe)) { zsys_error ("wap_client: trailing API command frames (%s)", method); @@ -2229,6 +2400,10 @@ struct _wap_client_t { uint64_t speed; // Returned by actor reply uint64_t thread_count; // Returned by actor reply zchunk_t *address; // Returned by actor reply + zchunk_t *hash; // Returned by actor reply + uint64_t reserved_offset; // Returned by actor reply + zchunk_t *prev_hash; // Returned by actor reply + zchunk_t *block_template_blob; // Returned by actor reply }; @@ -2284,6 +2459,9 @@ wap_client_destroy (wap_client_t **self_p) zframe_destroy (&self->white_list); zframe_destroy (&self->gray_list); zchunk_destroy (&self->address); + zchunk_destroy (&self->hash); + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); free (self); *self_p = NULL; } @@ -2432,6 +2610,17 @@ s_accept_reply (wap_client_t *self, ...) if (streq (reply, "STOP SAVE GRAPH OK")) { zsock_recv (self->actor, "8", &self->status); } + else + if (streq (reply, "GET BLOCK HASH OK")) { + zchunk_destroy (&self->hash); + zsock_recv (self->actor, "8p", &self->status, &self->hash); + } + else + if (streq (reply, "GET BLOCK TEMPLATE OK")) { + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); + zsock_recv (self->actor, "8888pp", &self->status, &self->reserved_offset, &self->height, &self->difficulty, &self->prev_hash, &self->block_template_blob); + } break; } filter = va_arg (args, char *); @@ -2745,6 +2934,39 @@ wap_client_stop_save_graph (wap_client_t *self) } +// --------------------------------------------------------------------------- +// Get block hash +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_block_hash (wap_client_t *self, uint64_t height) +{ + assert (self); + + zsock_send (self->actor, "s8", "GET BLOCK HASH", height); + if (s_accept_reply (self, "GET BLOCK HASH OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + +// --------------------------------------------------------------------------- +// Get block template +// Returns >= 0 if successful, -1 if interrupted. + +int +wap_client_get_block_template (wap_client_t *self, uint64_t reserve_size, zchunk_t **address_p) +{ + assert (self); + + zsock_send (self->actor, "s8p", "GET BLOCK TEMPLATE", reserve_size, *address_p); + *address_p = NULL; // Take ownership of address + if (s_accept_reply (self, "GET BLOCK TEMPLATE OK", "FAILURE", NULL)) + return -1; // Interrupted or timed-out + return self->status; +} + + // --------------------------------------------------------------------------- // Return last received status @@ -3007,3 +3229,47 @@ wap_client_address (wap_client_t *self) assert (self); return self->address; } + + +// --------------------------------------------------------------------------- +// Return last received hash + +zchunk_t * +wap_client_hash (wap_client_t *self) +{ + assert (self); + return self->hash; +} + + +// --------------------------------------------------------------------------- +// Return last received reserved_offset + +uint64_t +wap_client_reserved_offset (wap_client_t *self) +{ + assert (self); + return self->reserved_offset; +} + + +// --------------------------------------------------------------------------- +// Return last received prev_hash + +zchunk_t * +wap_client_prev_hash (wap_client_t *self) +{ + assert (self); + return self->prev_hash; +} + + +// --------------------------------------------------------------------------- +// Return last received block_template_blob + +zchunk_t * +wap_client_block_template_blob (wap_client_t *self) +{ + assert (self); + return self->block_template_blob; +} diff --git a/src/ipc/include/wap_proto.h b/src/ipc/include/wap_proto.h index 15b66f468..a3ede6cdc 100644 --- a/src/ipc/include/wap_proto.h +++ b/src/ipc/include/wap_proto.h @@ -142,6 +142,25 @@ ERROR. STOP_SAVE_GRAPH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) status number 8 Status + GET_BLOCK_HASH - get_block_hash IPC + height number 8 Height + + GET_BLOCK_HASH_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + hash chunk Hash + + GET_BLOCK_TEMPLATE - get_block_template IPC + reserve_size number 8 Reserve size + address chunk Address + + GET_BLOCK_TEMPLATE_OK - This is a codec for a Bitcoin Wallet Access Protocol (RFC tbd) + status number 8 Status + reserved_offset number 8 Rservered Offset + height number 8 Height + difficulty number 8 Difficulty + prev_hash chunk Previous Hash + block_template_blob chunk Block template blob + STOP - Wallet asks daemon to start mining. Daemon replies with STOP-OK, or ERROR. @@ -205,13 +224,17 @@ Daemon will reply with CLOSE-OK or ERROR. #define WAP_PROTO_START_SAVE_GRAPH_OK 30 #define WAP_PROTO_STOP_SAVE_GRAPH 31 #define WAP_PROTO_STOP_SAVE_GRAPH_OK 32 -#define WAP_PROTO_STOP 33 -#define WAP_PROTO_STOP_OK 34 -#define WAP_PROTO_CLOSE 35 -#define WAP_PROTO_CLOSE_OK 36 -#define WAP_PROTO_PING 37 -#define WAP_PROTO_PING_OK 38 -#define WAP_PROTO_ERROR 39 +#define WAP_PROTO_GET_BLOCK_HASH 33 +#define WAP_PROTO_GET_BLOCK_HASH_OK 34 +#define WAP_PROTO_GET_BLOCK_TEMPLATE 35 +#define WAP_PROTO_GET_BLOCK_TEMPLATE_OK 36 +#define WAP_PROTO_STOP 37 +#define WAP_PROTO_STOP_OK 38 +#define WAP_PROTO_CLOSE 39 +#define WAP_PROTO_CLOSE_OK 40 +#define WAP_PROTO_PING 41 +#define WAP_PROTO_PING_OK 42 +#define WAP_PROTO_ERROR 43 #include @@ -491,6 +514,48 @@ byte void wap_proto_set_level (wap_proto_t *self, byte level); +// Get a copy of the hash field +zchunk_t * + wap_proto_hash (wap_proto_t *self); +// Get the hash field and transfer ownership to caller +zchunk_t * + wap_proto_get_hash (wap_proto_t *self); +// Set the hash field, transferring ownership from caller +void + wap_proto_set_hash (wap_proto_t *self, zchunk_t **chunk_p); + +// Get/set the reserve_size field +uint64_t + wap_proto_reserve_size (wap_proto_t *self); +void + wap_proto_set_reserve_size (wap_proto_t *self, uint64_t reserve_size); + +// Get/set the reserved_offset field +uint64_t + wap_proto_reserved_offset (wap_proto_t *self); +void + wap_proto_set_reserved_offset (wap_proto_t *self, uint64_t reserved_offset); + +// Get a copy of the prev_hash field +zchunk_t * + wap_proto_prev_hash (wap_proto_t *self); +// Get the prev_hash field and transfer ownership to caller +zchunk_t * + wap_proto_get_prev_hash (wap_proto_t *self); +// Set the prev_hash field, transferring ownership from caller +void + wap_proto_set_prev_hash (wap_proto_t *self, zchunk_t **chunk_p); + +// Get a copy of the block_template_blob field +zchunk_t * + wap_proto_block_template_blob (wap_proto_t *self); +// Get the block_template_blob field and transfer ownership to caller +zchunk_t * + wap_proto_get_block_template_blob (wap_proto_t *self); +// Set the block_template_blob field, transferring ownership from caller +void + wap_proto_set_block_template_blob (wap_proto_t *self, zchunk_t **chunk_p); + // Get/set the reason field const char * wap_proto_reason (wap_proto_t *self); diff --git a/src/ipc/include/wap_server_engine.inc b/src/ipc/include/wap_server_engine.inc index eaa4f37ce..58c66c6ea 100644 --- a/src/ipc/include/wap_server_engine.inc +++ b/src/ipc/include/wap_server_engine.inc @@ -47,11 +47,13 @@ typedef enum { set_log_level_event = 16, start_save_graph_event = 17, stop_save_graph_event = 18, - close_event = 19, - ping_event = 20, - expired_event = 21, - exception_event = 22, - settled_event = 23 + get_block_hash_event = 19, + get_block_template_event = 20, + close_event = 21, + ping_event = 22, + expired_event = 23, + exception_event = 24, + settled_event = 25 } event_t; // Names for state machine logging and error reporting @@ -85,6 +87,8 @@ s_event_name [] = { "SET_LOG_LEVEL", "START_SAVE_GRAPH", "STOP_SAVE_GRAPH", + "GET_BLOCK_HASH", + "GET_BLOCK_TEMPLATE", "CLOSE", "PING", "expired", @@ -186,6 +190,10 @@ static void start_save_graph (client_t *self); static void stop_save_graph (client_t *self); +static void + get_block_hash (client_t *self); +static void + get_block_template (client_t *self); static void deregister_wallet (client_t *self); static void @@ -418,6 +426,12 @@ s_protocol_event (wap_proto_t *message) case WAP_PROTO_STOP_SAVE_GRAPH: return stop_save_graph_event; break; + case WAP_PROTO_GET_BLOCK_HASH: + return get_block_hash_event; + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return get_block_template_event; + break; case WAP_PROTO_STOP: return stop_event; break; @@ -924,6 +938,42 @@ s_client_execute (s_client_t *self, event_t event) } } else + if (self->event == get_block_hash_event) { + if (!self->exception) { + // get block hash + if (self->server->verbose) + zsys_debug ("%s: $ get block hash", self->log_prefix); + get_block_hash (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_HASH_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_BLOCK_HASH_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_BLOCK_HASH_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else + if (self->event == get_block_template_event) { + if (!self->exception) { + // get block template + if (self->server->verbose) + zsys_debug ("%s: $ get block template", self->log_prefix); + get_block_template (&self->client); + } + if (!self->exception) { + // send GET_BLOCK_TEMPLATE_OK + if (self->server->verbose) + zsys_debug ("%s: $ send GET_BLOCK_TEMPLATE_OK", + self->log_prefix); + wap_proto_set_id (self->server->message, WAP_PROTO_GET_BLOCK_TEMPLATE_OK); + wap_proto_set_routing_id (self->server->message, self->routing_id); + wap_proto_send (self->server->message, self->server->router); + } + } + else if (self->event == close_event) { if (!self->exception) { // send CLOSE_OK diff --git a/src/ipc/wap_client/wap_client.c b/src/ipc/wap_client/wap_client.c index 8973abe6e..40b577691 100644 --- a/src/ipc/wap_client/wap_client.c +++ b/src/ipc/wap_client/wap_client.c @@ -16,9 +16,6 @@ */ #include "wap_classes.h" -// TODO: Change these to match your project's needs -#include "../include/wap_proto.h" -#include "../include/wap_client.h" // Forward reference to method arguments structure typedef struct _client_args_t client_args_t; @@ -98,6 +95,7 @@ use_connect_timeout (client_t *self) engine_set_timeout (self, self->args->timeout); } + // --------------------------------------------------------------------------- // client_is_connected // @@ -110,6 +108,7 @@ client_is_connected (client_t *self) engine_set_timeout (self, self->heartbeat_timer); } + // --------------------------------------------------------------------------- // check_if_connection_is_dead // @@ -125,17 +124,6 @@ check_if_connection_is_dead (client_t *self) } } -// --------------------------------------------------------------------------- -// use_heartbeat_timer -// - -static void -use_heartbeat_timer (client_t *self) -{ - engine_set_timeout (self, self->heartbeat_timer); -} - - // --------------------------------------------------------------------------- // prepare_blocks_command @@ -148,15 +136,6 @@ prepare_blocks_command (client_t *self) wap_proto_set_start_height (self->message, self->args->start_height); } -// --------------------------------------------------------------------------- -// prepare_get_output_indexes_command -// - -static void -prepare_get_output_indexes_command (client_t *self) -{ - wap_proto_set_tx_id (self->message, &self->args->tx_id); -} // --------------------------------------------------------------------------- // signal_have_blocks_ok @@ -165,10 +144,13 @@ prepare_get_output_indexes_command (client_t *self) static void signal_have_blocks_ok (client_t *self) { + zmsg_t *msg = wap_proto_get_block_data (self->message); + assert(msg != 0); + printf("%p <--\n", (void*)msg); zsock_send (self->cmdpipe, "s888p", "BLOCKS OK", wap_proto_status(self->message), wap_proto_start_height (self->message), wap_proto_curr_height (self->message), - wap_proto_get_block_data (self->message)); + msg); } @@ -201,8 +183,8 @@ prepare_put_command (client_t *self) static void signal_have_put_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8s", "PUT OK", wap_proto_status(self->message), - wap_proto_tx_id (self->message)); + zsock_send (self->cmdpipe, "s8", "PUT OK", + wap_proto_status (self->message)); } @@ -224,10 +206,21 @@ prepare_get_command (client_t *self) static void signal_have_get_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8p", "GET OK", 0, + zsock_send (self->cmdpipe, "sip", "GET OK", 0, wap_proto_get_tx_data (self->message)); } +// --------------------------------------------------------------------------- +// signal_have_get_height_ok +// + +static void +signal_have_get_height_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0, + wap_proto_height (self->message)); +} + // --------------------------------------------------------------------------- // signal_have_save_ok @@ -258,31 +251,9 @@ signal_have_start_ok (client_t *self) static void signal_have_stop_ok (client_t *self) { - zsock_send (self->cmdpipe, "s8", "STOP OK", 0); + zsock_send (self->cmdpipe, "si", "STOP OK", 0); } -// --------------------------------------------------------------------------- -// signal_have_get_height_ok -// - -static void -signal_have_get_height_ok (client_t *self) -{ - zsock_send (self->cmdpipe, "si8", "GET HEIGHT OK", 0, - wap_proto_height (self->message)); -} - -// --------------------------------------------------------------------------- -// signal_have_output_indexes_ok -// - -static void -signal_have_output_indexes_ok (client_t *self) -{ - zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK", - wap_proto_status (self->message), - wap_proto_get_o_indexes (self->message)); -} // --------------------------------------------------------------------------- // signal_success @@ -352,6 +323,37 @@ signal_server_not_present (client_t *self) zsock_send (self->cmdpipe, "sis", "FAILURE", -1, "Server is not reachable"); } + + + +// --------------------------------------------------------------------------- +// prepare_get_output_indexes_command +// + +static void +prepare_get_output_indexes_command (client_t *self) +{ + wap_proto_set_tx_id (self->message, &self->args->tx_id); +} + +// --------------------------------------------------------------------------- +// signal_have_output_indexes_ok +// + +static void +signal_have_output_indexes_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8p", "OUTPUT INDEXES OK", + wap_proto_status (self->message), + wap_proto_get_o_indexes (self->message)); +} + +void wap_client_test(bool verbose) { +} + + + + // --------------------------------------------------------------------------- // prepare_get_random_outs_command // @@ -359,7 +361,6 @@ signal_server_not_present (client_t *self) static void prepare_get_random_outs_command (client_t *self) { - wap_proto_set_outs_count(self->message, self->args->outs_count); wap_proto_set_amounts (self->message, &self->args->amounts); } @@ -376,6 +377,8 @@ signal_have_random_outs_ok (client_t *self) wap_proto_get_random_outputs (self->message)); } + + // --------------------------------------------------------------------------- // signal_have_get_info_ok // @@ -397,8 +400,9 @@ signal_have_get_info_ok (client_t *self) wap_proto_grey_peerlist_size (self->message)); } + // --------------------------------------------------------------------------- -// signal_have_get_get_peer_list_ok +// signal_have_get_peer_list_ok // static void @@ -432,7 +436,7 @@ signal_have_get_mining_status_ok (client_t *self) static void prepare_set_log_hash_rate_command (client_t *self) { - wap_proto_set_visible (self->message, self->args->visible); + wap_proto_set_visible (self->message, self->args->visible); } // --------------------------------------------------------------------------- @@ -453,7 +457,7 @@ signal_have_set_log_hash_rate_ok (client_t *self) static void prepare_set_log_level_command (client_t *self) { - wap_proto_set_level (self->message, self->args->level); + wap_proto_set_level (self->message, self->args->level); } // --------------------------------------------------------------------------- @@ -488,3 +492,52 @@ signal_have_stop_save_graph_ok (client_t *self) zsock_send (self->cmdpipe, "s8", "STOP SAVE GRAPH OK", wap_proto_status (self->message)); } + +// --------------------------------------------------------------------------- +// prepare_get_block_hash_command +// + +static void +prepare_get_block_hash_command (client_t *self) +{ + wap_proto_set_height (self->message, self->args->height); +} + +// --------------------------------------------------------------------------- +// signal_have_get_block_hash_ok +// + +static void +signal_have_get_block_hash_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8p", "GET BLOCK HASH OK", + wap_proto_status (self->message), wap_proto_get_hash (self->message)); +} + +// --------------------------------------------------------------------------- +// prepare_get_block_template_command +// + +static void +prepare_get_block_template_command (client_t *self) +{ + wap_proto_set_reserve_size (self->message, self->args->reserve_size); + wap_proto_set_address (self->message, &self->args->address); +} + +// --------------------------------------------------------------------------- +// signal_have_get_block_template_ok +// + +static void +signal_have_get_block_template_ok (client_t *self) +{ + zsock_send (self->cmdpipe, "s8888pp", "GET BLOCK TEMPLATE OK", + wap_proto_status (self->message), + wap_proto_reserved_offset (self->message), + wap_proto_height (self->message), + wap_proto_difficulty (self->message), + wap_proto_get_prev_hash (self->message), + wap_proto_get_block_template_blob (self->message)); +} + diff --git a/src/ipc/wap_proto.c b/src/ipc/wap_proto.c index 6a6f0af26..bc4522d7c 100644 --- a/src/ipc/wap_proto.c +++ b/src/ipc/wap_proto.c @@ -65,6 +65,11 @@ struct _wap_proto_t { uint64_t speed; // Speed byte visible; // Visible byte level; // Level + zchunk_t *hash; // Hash + uint64_t reserve_size; // Reserve size + uint64_t reserved_offset; // Rservered Offset + zchunk_t *prev_hash; // Previous Hash + zchunk_t *block_template_blob; // Block template blob char reason [256]; // Printable explanation }; @@ -253,6 +258,9 @@ wap_proto_destroy (wap_proto_t **self_p) zchunk_destroy (&self->address); zframe_destroy (&self->white_list); zframe_destroy (&self->gray_list); + zchunk_destroy (&self->hash); + zchunk_destroy (&self->prev_hash); + zchunk_destroy (&self->block_template_blob); // Free object itself free (self); @@ -568,6 +576,69 @@ wap_proto_recv (wap_proto_t *self, zsock_t *input) GET_NUMBER8 (self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + GET_NUMBER8 (self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + GET_NUMBER8 (self->status); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: hash is missing data"); + goto malformed; + } + zchunk_destroy (&self->hash); + self->hash = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + GET_NUMBER8 (self->reserve_size); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: address is missing data"); + goto malformed; + } + zchunk_destroy (&self->address); + self->address = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + GET_NUMBER8 (self->status); + GET_NUMBER8 (self->reserved_offset); + GET_NUMBER8 (self->height); + GET_NUMBER8 (self->difficulty); + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: prev_hash is missing data"); + goto malformed; + } + zchunk_destroy (&self->prev_hash); + self->prev_hash = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + { + size_t chunk_size; + GET_NUMBER4 (chunk_size); + if (self->needle + chunk_size > (self->ceiling)) { + zsys_warning ("wap_proto: block_template_blob is missing data"); + goto malformed; + } + zchunk_destroy (&self->block_template_blob); + self->block_template_blob = zchunk_new (self->needle, chunk_size); + self->needle += chunk_size; + } + break; + case WAP_PROTO_STOP: break; @@ -734,6 +805,33 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) case WAP_PROTO_STOP_SAVE_GRAPH_OK: frame_size += 8; // status break; + case WAP_PROTO_GET_BLOCK_HASH: + frame_size += 8; // height + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + frame_size += 8; // status + frame_size += 4; // Size is 4 octets + if (self->hash) + frame_size += zchunk_size (self->hash); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + frame_size += 8; // reserve_size + frame_size += 4; // Size is 4 octets + if (self->address) + frame_size += zchunk_size (self->address); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + frame_size += 8; // status + frame_size += 8; // reserved_offset + frame_size += 8; // height + frame_size += 8; // difficulty + frame_size += 4; // Size is 4 octets + if (self->prev_hash) + frame_size += zchunk_size (self->prev_hash); + frame_size += 4; // Size is 4 octets + if (self->block_template_blob) + frame_size += zchunk_size (self->block_template_blob); + break; case WAP_PROTO_ERROR: frame_size += 2; // status frame_size += 1 + strlen (self->reason); @@ -930,6 +1028,61 @@ wap_proto_send (wap_proto_t *self, zsock_t *output) PUT_NUMBER8 (self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + PUT_NUMBER8 (self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + PUT_NUMBER8 (self->status); + if (self->hash) { + PUT_NUMBER4 (zchunk_size (self->hash)); + memcpy (self->needle, + zchunk_data (self->hash), + zchunk_size (self->hash)); + self->needle += zchunk_size (self->hash); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + PUT_NUMBER8 (self->reserve_size); + if (self->address) { + PUT_NUMBER4 (zchunk_size (self->address)); + memcpy (self->needle, + zchunk_data (self->address), + zchunk_size (self->address)); + self->needle += zchunk_size (self->address); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + PUT_NUMBER8 (self->status); + PUT_NUMBER8 (self->reserved_offset); + PUT_NUMBER8 (self->height); + PUT_NUMBER8 (self->difficulty); + if (self->prev_hash) { + PUT_NUMBER4 (zchunk_size (self->prev_hash)); + memcpy (self->needle, + zchunk_data (self->prev_hash), + zchunk_size (self->prev_hash)); + self->needle += zchunk_size (self->prev_hash); + } + else + PUT_NUMBER4 (0); // Empty chunk + if (self->block_template_blob) { + PUT_NUMBER4 (zchunk_size (self->block_template_blob)); + memcpy (self->needle, + zchunk_data (self->block_template_blob), + zchunk_size (self->block_template_blob)); + self->needle += zchunk_size (self->block_template_blob); + } + else + PUT_NUMBER4 (0); // Empty chunk + break; + case WAP_PROTO_ERROR: PUT_NUMBER2 (self->status); PUT_STRING (self->reason); @@ -1210,6 +1363,33 @@ wap_proto_print (wap_proto_t *self) zsys_debug (" status=%ld", (long) self->status); break; + case WAP_PROTO_GET_BLOCK_HASH: + zsys_debug ("WAP_PROTO_GET_BLOCK_HASH:"); + zsys_debug (" height=%ld", (long) self->height); + break; + + case WAP_PROTO_GET_BLOCK_HASH_OK: + zsys_debug ("WAP_PROTO_GET_BLOCK_HASH_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" hash=[ ... ]"); + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE: + zsys_debug ("WAP_PROTO_GET_BLOCK_TEMPLATE:"); + zsys_debug (" reserve_size=%ld", (long) self->reserve_size); + zsys_debug (" address=[ ... ]"); + break; + + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + zsys_debug ("WAP_PROTO_GET_BLOCK_TEMPLATE_OK:"); + zsys_debug (" status=%ld", (long) self->status); + zsys_debug (" reserved_offset=%ld", (long) self->reserved_offset); + zsys_debug (" height=%ld", (long) self->height); + zsys_debug (" difficulty=%ld", (long) self->difficulty); + zsys_debug (" prev_hash=[ ... ]"); + zsys_debug (" block_template_blob=[ ... ]"); + break; + case WAP_PROTO_STOP: zsys_debug ("WAP_PROTO_STOP:"); break; @@ -1383,6 +1563,18 @@ wap_proto_command (wap_proto_t *self) case WAP_PROTO_STOP_SAVE_GRAPH_OK: return ("STOP_SAVE_GRAPH_OK"); break; + case WAP_PROTO_GET_BLOCK_HASH: + return ("GET_BLOCK_HASH"); + break; + case WAP_PROTO_GET_BLOCK_HASH_OK: + return ("GET_BLOCK_HASH_OK"); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE: + return ("GET_BLOCK_TEMPLATE"); + break; + case WAP_PROTO_GET_BLOCK_TEMPLATE_OK: + return ("GET_BLOCK_TEMPLATE_OK"); + break; case WAP_PROTO_STOP: return ("STOP"); break; @@ -2137,6 +2329,141 @@ wap_proto_set_level (wap_proto_t *self, byte level) } +// -------------------------------------------------------------------------- +// Get the hash field without transferring ownership + +zchunk_t * +wap_proto_hash (wap_proto_t *self) +{ + assert (self); + return self->hash; +} + +// Get the hash field and transfer ownership to caller + +zchunk_t * +wap_proto_get_hash (wap_proto_t *self) +{ + zchunk_t *hash = self->hash; + self->hash = NULL; + return hash; +} + +// Set the hash field, transferring ownership from caller + +void +wap_proto_set_hash (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->hash); + self->hash = *chunk_p; + *chunk_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get/set the reserve_size field + +uint64_t +wap_proto_reserve_size (wap_proto_t *self) +{ + assert (self); + return self->reserve_size; +} + +void +wap_proto_set_reserve_size (wap_proto_t *self, uint64_t reserve_size) +{ + assert (self); + self->reserve_size = reserve_size; +} + + +// -------------------------------------------------------------------------- +// Get/set the reserved_offset field + +uint64_t +wap_proto_reserved_offset (wap_proto_t *self) +{ + assert (self); + return self->reserved_offset; +} + +void +wap_proto_set_reserved_offset (wap_proto_t *self, uint64_t reserved_offset) +{ + assert (self); + self->reserved_offset = reserved_offset; +} + + +// -------------------------------------------------------------------------- +// Get the prev_hash field without transferring ownership + +zchunk_t * +wap_proto_prev_hash (wap_proto_t *self) +{ + assert (self); + return self->prev_hash; +} + +// Get the prev_hash field and transfer ownership to caller + +zchunk_t * +wap_proto_get_prev_hash (wap_proto_t *self) +{ + zchunk_t *prev_hash = self->prev_hash; + self->prev_hash = NULL; + return prev_hash; +} + +// Set the prev_hash field, transferring ownership from caller + +void +wap_proto_set_prev_hash (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->prev_hash); + self->prev_hash = *chunk_p; + *chunk_p = NULL; +} + + +// -------------------------------------------------------------------------- +// Get the block_template_blob field without transferring ownership + +zchunk_t * +wap_proto_block_template_blob (wap_proto_t *self) +{ + assert (self); + return self->block_template_blob; +} + +// Get the block_template_blob field and transfer ownership to caller + +zchunk_t * +wap_proto_get_block_template_blob (wap_proto_t *self) +{ + zchunk_t *block_template_blob = self->block_template_blob; + self->block_template_blob = NULL; + return block_template_blob; +} + +// Set the block_template_blob field, transferring ownership from caller + +void +wap_proto_set_block_template_blob (wap_proto_t *self, zchunk_t **chunk_p) +{ + assert (self); + assert (chunk_p); + zchunk_destroy (&self->block_template_blob); + self->block_template_blob = *chunk_p; + *chunk_p = NULL; +} + + // -------------------------------------------------------------------------- // Get/set the reason field @@ -2646,6 +2973,76 @@ wap_proto_test (bool verbose) assert (wap_proto_routing_id (self)); assert (wap_proto_status (self) == 123); } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_HASH); + + wap_proto_set_height (self, 123); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_height (self) == 123); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_HASH_OK); + + wap_proto_set_status (self, 123); + zchunk_t *get_block_hash_ok_hash = zchunk_new ("Captcha Diem", 12); + wap_proto_set_hash (self, &get_block_hash_ok_hash); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (memcmp (zchunk_data (wap_proto_hash (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_hash_ok_hash); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_TEMPLATE); + + wap_proto_set_reserve_size (self, 123); + zchunk_t *get_block_template_address = zchunk_new ("Captcha Diem", 12); + wap_proto_set_address (self, &get_block_template_address); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_reserve_size (self) == 123); + assert (memcmp (zchunk_data (wap_proto_address (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_address); + } + wap_proto_set_id (self, WAP_PROTO_GET_BLOCK_TEMPLATE_OK); + + wap_proto_set_status (self, 123); + wap_proto_set_reserved_offset (self, 123); + wap_proto_set_height (self, 123); + wap_proto_set_difficulty (self, 123); + zchunk_t *get_block_template_ok_prev_hash = zchunk_new ("Captcha Diem", 12); + wap_proto_set_prev_hash (self, &get_block_template_ok_prev_hash); + zchunk_t *get_block_template_ok_block_template_blob = zchunk_new ("Captcha Diem", 12); + wap_proto_set_block_template_blob (self, &get_block_template_ok_block_template_blob); + // Send twice + wap_proto_send (self, output); + wap_proto_send (self, output); + + for (instance = 0; instance < 2; instance++) { + wap_proto_recv (self, input); + assert (wap_proto_routing_id (self)); + assert (wap_proto_status (self) == 123); + assert (wap_proto_reserved_offset (self) == 123); + assert (wap_proto_height (self) == 123); + assert (wap_proto_difficulty (self) == 123); + assert (memcmp (zchunk_data (wap_proto_prev_hash (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_ok_prev_hash); + assert (memcmp (zchunk_data (wap_proto_block_template_blob (self)), "Captcha Diem", 12) == 0); + zchunk_destroy (&get_block_template_ok_block_template_blob); + } wap_proto_set_id (self, WAP_PROTO_STOP); // Send twice diff --git a/src/ipc/wap_server/wap_server.c b/src/ipc/wap_server/wap_server.c index 699a9c11a..98b2a3fa5 100644 --- a/src/ipc/wap_server/wap_server.c +++ b/src/ipc/wap_server/wap_server.c @@ -362,3 +362,23 @@ stop_save_graph (client_t *self) { IPC::Daemon::stop_save_graph(self->message); } + +// --------------------------------------------------------------------------- +// get_block_hash +// + +static void +get_block_hash (client_t *self) +{ + IPC::Daemon::get_block_hash(self->message); +} + +// --------------------------------------------------------------------------- +// get_block_template +// + +static void +get_block_template (client_t *self) +{ + IPC::Daemon::get_block_template(self->message); +} diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 338ef2015..9d50fc97a 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -15,6 +15,8 @@ // Trivial and error responses may be returned with ns_create_rpc_reply and ns_create_rpc_error // respectively. +// TODO: Add core busy checks to all methods here + #include "daemon_deprecated_rpc.h" #include @@ -26,6 +28,7 @@ */ namespace { + // TODO: put right error codes here int daemon_connection_error = -326701; int parse_error = -32700; int invalid_request = -32600; @@ -43,26 +46,21 @@ namespace return ipc_client && wap_client_connected(ipc_client); } - void connect_to_daemon() { + bool connect_to_daemon() { if (check_connection_to_daemon()) { - return; + return true; } ipc_client = wap_client_new(); wap_client_connect(ipc_client, "ipc://@/monero", 200, "wallet identity"); + return check_connection_to_daemon(); } /*! - * \brief Constructs a response string given a result JSON object. - * - * It also adds boilerplate properties like id, method. + * \brief Initializes a rapidjson response object * \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. + * \param response_json net_skeleton request object to fill */ - void construct_response_string(struct ns_rpc_request *req, rapidjson::Value &result_json, - rapidjson::Document &response_json, std::string &response) - { + void init_response_object(struct ns_rpc_request *req, rapidjson::Document &response_json) { response_json.SetObject(); response_json.AddMember("jsonrpc", "2.0" , response_json.GetAllocator()); rapidjson::Value string_value(rapidjson::kStringType); @@ -78,6 +76,21 @@ namespace 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()); + } + + /*! + * \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) + { + init_response_object(req, response_json); response_json.AddMember("result", result_json, response_json.GetAllocator()); rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); @@ -85,6 +98,30 @@ namespace // Write string to `response`. response = buffer.GetString(); } + + /*! + * \brief Constructs a response string given a result string. + * + * It also adds boilerplate properties like id, method. + * \param req net_skeleton request object + * \param result 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, const std::string &result, + rapidjson::Document &response_json, std::string &response) + { + init_response_object(req, response_json); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString(result.c_str(), result.length()); + response_json.AddMember("result", string_value, response_json.GetAllocator()); + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + response_json.Accept(writer); + // Write string to `response`. + response = buffer.GetString(); + } + /*! * \brief Implementation of 'getheight' method. * \param buf Buffer to fill in response. @@ -94,12 +131,20 @@ namespace */ int getheight(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_height(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } uint64_t height = wap_client_height(ipc_client); rapidjson::Document response_json; rapidjson::Value result_json; @@ -122,7 +167,10 @@ namespace */ int startmining(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -160,7 +208,10 @@ namespace "Couldn't connect to daemon.", "{}"); } uint64_t status = wap_client_status(ipc_client); - + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } if (status == IPC::STATUS_WRONG_ADDRESS) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -183,12 +234,20 @@ namespace */ int stopmining(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_stop(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } if (wap_client_status(ipc_client) != IPC::STATUS_OK) { return ns_rpc_create_error(buf, len, req, invalid_request, @@ -206,13 +265,21 @@ namespace */ int getinfo(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_info(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - if (wap_client_status(ipc_client) != IPC::STATUS_OK) + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status != IPC::STATUS_OK) { return ns_rpc_create_error(buf, len, req, invalid_request, "Failed to get info", "{}"); @@ -257,7 +324,10 @@ namespace */ int getpeerlist(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_peer_list(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -309,13 +379,20 @@ namespace */ int getminingstatus(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_mining_status(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } rapidjson::Document response_json; rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); rapidjson::Value result_json; @@ -346,7 +423,10 @@ namespace */ int setloghashrate(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -376,8 +456,12 @@ namespace return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - - if (wap_client_status(ipc_client) == IPC::STATUS_NOT_MINING) { + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_NOT_MINING) { return ns_rpc_create_error(buf, len, req, not_mining_error, "Not mining", "{}"); } @@ -394,7 +478,10 @@ namespace */ int setloglevel(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } if (req->params == NULL) { return ns_rpc_create_error(buf, len, req, invalid_params, @@ -423,8 +510,12 @@ namespace return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } - - if (wap_client_status(ipc_client) == IPC::STATUS_INVALID_LOG_LEVEL) { + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_INVALID_LOG_LEVEL) { return ns_rpc_create_error(buf, len, req, invalid_params, "Invalid log level", "{}"); } @@ -441,12 +532,20 @@ namespace */ int getblockcount(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_get_height(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, "Couldn't connect to daemon.", "{}"); } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } uint64_t count = wap_client_height(ipc_client); rapidjson::Document response_json; rapidjson::Value result_json; @@ -469,7 +568,10 @@ namespace */ int startsavegraph(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_start_save_graph(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -487,7 +589,10 @@ namespace */ int stopsavegraph(char *buf, int len, struct ns_rpc_request *req) { - connect_to_daemon(); + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } int rc = wap_client_stop_save_graph(ipc_client); if (rc < 0) { return ns_rpc_create_error(buf, len, req, daemon_connection_error, @@ -495,6 +600,152 @@ namespace } return ns_rpc_create_reply(buf, len, req, "{s:s}", "status", STATUS_OK); } + + /*! + * \brief Implementation of 'getblockhash' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblockhash(char *buf, int len, struct ns_rpc_request *req) + { + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.IsArray() || request_json.Size() < 1 || !request_json[(unsigned int)0].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'height' field", "{}"); + } + + + int height = request_json[(unsigned int)0].GetUint(); + int rc = wap_client_get_block_hash(ipc_client, height); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_HEIGHT_TOO_BIG) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Height too big.", "{}"); + } + zchunk_t *hash_chunk = wap_client_hash(ipc_client); + std::string hash((char*)zchunk_data(hash_chunk), zchunk_size(hash_chunk)); + std::string response; + rapidjson::Document response_json; + construct_response_string(req, hash, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + + /*! + * \brief Implementation of 'getblocktemplate' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblocktemplate(char *buf, int len, struct ns_rpc_request *req) + { + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("reserve_size") || !request_json["reserve_size"].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'reserve_size' field", "{}"); + } + if (!request_json.HasMember("wallet_address") || !request_json["wallet_address"].IsString()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'wallet_address' field", "{}"); + } + + uint64_t reserve_size = request_json["reserve_size"].GetUint(); + std::string wallet_address = request_json["wallet_address"].GetString(); + zchunk_t *address_chunk = zchunk_new((void*)wallet_address.c_str(), wallet_address.length()); + int rc = wap_client_get_block_template(ipc_client, reserve_size, &address_chunk); + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_RESERVE_SIZE_TOO_BIG) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Reserve size too big.", "{}"); + } + if (status == IPC::STATUS_WRONG_ADDRESS) { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Wrong address.", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + rapidjson::Value result_json; + result_json.SetObject(); + result_json.AddMember("difficulty", wap_client_difficulty(ipc_client), allocator); + result_json.AddMember("height", wap_client_height(ipc_client), allocator); + result_json.AddMember("reserved_offset", wap_client_reserved_offset(ipc_client), allocator); + zchunk_t *prev_hash_chunk = wap_client_prev_hash(ipc_client); + rapidjson::Value string_value(rapidjson::kStringType); + string_value.SetString((char*)zchunk_data(prev_hash_chunk), zchunk_size(prev_hash_chunk)); + result_json.AddMember("prev_hash", string_value, allocator); + zchunk_t *block_template_chunk = wap_client_prev_hash(ipc_client); + string_value.SetString((char*)zchunk_data(block_template_chunk), zchunk_size(block_template_chunk)); + result_json.AddMember("blocktemplate_blob", string_value, allocator); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -508,6 +759,8 @@ namespace "getblockcount", "startsavegraph", "stopsavegraph", + "getblockhash", + "getblocktemplate", NULL }; @@ -524,6 +777,8 @@ namespace getblockcount, startsavegraph, stopsavegraph, + getblockhash, + getblocktemplate, NULL }; From 4b2c2cc3e9f95918eade3a148c757b08375cfd3b Mon Sep 17 00:00:00 2001 From: Oran Juice Date: Mon, 29 Jun 2015 14:19:41 +0530 Subject: [PATCH 44/45] get_blocks deprecated RPC (buffer overflow for large responses), comments --- src/ipc/daemon_ipc_handlers.cpp | 160 +++++++++++++++++++++-- src/ipc/include/daemon_ipc_handlers.h | 43 ++++++- src/rpc/daemon_deprecated_rpc.cpp | 175 +++++++++++++++++++++++++- src/rpc/daemon_deprecated_rpc.h | 38 ++++++ src/rpc/json_rpc.cpp | 41 ++++++ 5 files changed, 439 insertions(+), 18 deletions(-) diff --git a/src/ipc/daemon_ipc_handlers.cpp b/src/ipc/daemon_ipc_handlers.cpp index 563d0621f..aad5941fc 100644 --- a/src/ipc/daemon_ipc_handlers.cpp +++ b/src/ipc/daemon_ipc_handlers.cpp @@ -28,19 +28,36 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +/*! + * \file daemon_ipc_handlers.cpp + * \brief Implementation of Daemon IPC handlers + * + * Most of this code is borrowed from core_rpc_server.cpp but changed to use 0MQ objects. + */ + //TODO: Recheck memory leaks #include "daemon_ipc_handlers.h" #include +/*! + * \namespace IPC + * \brief Anonymous namepsace to keep things in the scope of this file + */ namespace { cryptonote::core *core; /*!< Pointer to the core */ nodetool::node_server > *p2p; /*!< Pointer to p2p node server */ - zactor_t *server; - bool testnet; + zactor_t *server; /*!< 0MQ server */ + bool testnet; /*!< testnet mode or not */ + + /*! + * \brief Checks if core is busy + * + * \return true if core is busy + */ bool check_core_busy() { if (p2p->get_payload_object().get_core().get_blockchain_storage().is_storing_blockchain()) @@ -49,6 +66,12 @@ namespace } return true; } + + /*! + * \brief Checks if core is ready + * + * \return true if core is ready + */ bool check_core_ready() { if (p2p->get_payload_object().is_synchronized()) @@ -58,10 +81,11 @@ namespace return check_core_busy(); } - //------------------------------------------------------------------------------------------------------------------------------ - // equivalent of strstr, but with arbitrary bytes (ie, NULs) - // This does not differentiate between "not found" and "found at offset 0" - // (taken straight from core_rpc_server.cpp) + /*! + * \brief equivalent of strstr, but with arbitrary bytes (ie, NULs) + * This does not differentiate between "not found" and "found at offset 0" + * (taken straight from core_rpc_server.cpp) + */ uint64_t slow_memmem(const void *start_buff, size_t buflen, const void *pat, size_t patlen) { const void *buf = start_buff; @@ -78,10 +102,26 @@ namespace } } +/*! + * \namespace IPC + * \brief Namespace pertaining to IPC. + */ namespace IPC { + /*! + * \namespace Daemon + * \brief Namespace pertaining to Daemon IPC. + */ namespace Daemon { + /*! + * \brief initializes it with objects necessary to handle IPC requests and starts + * IPC server + * + * \param p_core cryptonote core object + * \param p_p2p p2p object + * \param p_testnet testnet mode or not + */ void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet) @@ -94,10 +134,22 @@ namespace IPC zsock_send (server, "sss", "SET", "server/timeout", "5000"); } + /*! + * \brief stops the IPC server + * + * \param p_core cryptonote core object + * \param p_p2p p2p object + * \param p_testnet testnet mode or not + */ void stop() { zactor_destroy(&server); } + /*! + * \brief start_mining IPC + * + * \param message 0MQ response object to populate + */ void start_mining(wap_proto_t *message) { if (!check_core_busy()) { @@ -127,6 +179,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief stop_mining IPC + * + * \param message 0MQ response object to populate + */ void stop_mining(wap_proto_t *message) { if (!core->get_miner().stop()) @@ -137,6 +194,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_blocks IPC + * + * \param message 0MQ response object to populate + */ void retrieve_blocks(wap_proto_t *message) { if (!check_core_busy()) { @@ -165,6 +227,11 @@ namespace IPC return; } + // We are using JSON to encode blocks. The JSON string will sit in a + // 0MQ frame which gets sent in a zmsg_t object. One could put each block + // a different frame too. + + // First create a rapidjson object and then stringify it. rapidjson::Document result_json; result_json.SetObject(); rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator(); @@ -196,6 +263,7 @@ namespace IPC result_json.Accept(writer); std::string block_string = buffer.GetString(); zmsg_t *block_data = zmsg_new(); + // Put the JSON string in a frame. zframe_t *frame = zframe_new(block_string.c_str(), block_string.length()); zmsg_prepend(block_data, &frame); wap_proto_set_start_height(message, result_start_height); @@ -205,6 +273,11 @@ namespace IPC } + /*! + * \brief send_raw_transaction IPC + * + * \param message 0MQ response object to populate + */ void send_raw_transaction(wap_proto_t *message) { if (!check_core_busy()) { @@ -248,10 +321,15 @@ namespace IPC cryptonote::NOTIFY_NEW_TRANSACTIONS::request r; r.txs.push_back(tx_blob); core->get_protocol()->relay_transactions(r, fake_context); - //TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes + // TODO: make sure that tx has reached other nodes here, probably wait to receive reflections from other nodes wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_output_indexes IPC + * + * \param message 0MQ response object to populate + */ void get_output_indexes(wap_proto_t *message) { if (!check_core_busy()) { @@ -276,6 +354,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_random_outputs IPC + * + * \param message 0MQ response object to populate + */ void get_random_outs(wap_proto_t *message) { if (!check_core_busy()) { wap_proto_set_status(message, STATUS_CORE_BUSY); @@ -299,7 +382,7 @@ namespace IPC wap_proto_set_status(message, STATUS_RANDOM_OUTS_FAILED); } - // We have to convert the result into a JSON string. + // We convert the result into a JSON string and put it into a 0MQ frame. rapidjson::Document result_json; result_json.SetObject(); rapidjson::Document::AllocatorType &allocator = result_json.GetAllocator(); @@ -350,6 +433,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_height IPC + * + * \param message 0MQ response object to populate + */ void get_height(wap_proto_t *message) { if (!check_core_busy()) { wap_proto_set_status(message, STATUS_CORE_BUSY); @@ -359,6 +447,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief save_bc IPC + * + * \param message 0MQ response object to populate + */ void save_bc(wap_proto_t *message) { if (!check_core_busy()) { wap_proto_set_status(message, STATUS_CORE_BUSY); @@ -371,6 +464,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_info IPC + * + * \param message 0MQ response object to populate + */ void get_info(wap_proto_t *message) { if (!check_core_busy()) { wap_proto_set_status(message, STATUS_CORE_BUSY); @@ -392,17 +490,27 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_peer_list IPC + * + * \param message 0MQ response object to populate + */ void get_peer_list(wap_proto_t *message) { std::list white_list; std::list gray_list; p2p->get_peerlist_manager().get_peerlist_full(white_list, gray_list); + // The response is of non-trivial type and so is encoded as JSON. + // Each peer list is going to look like this: + // {"peers": [{"id": ....}, ...]} + rapidjson::Document white_list_json; white_list_json.SetObject(); rapidjson::Document::AllocatorType &white_list_allocator = white_list_json.GetAllocator(); rapidjson::Value white_peers(rapidjson::kArrayType); for (auto & entry : white_list) { + // Each peer object is encoded as JSON rapidjson::Value output(rapidjson::kObjectType); output.AddMember("id", entry.id, white_list_allocator); output.AddMember("ip", entry.adr.ip, white_list_allocator); @@ -418,6 +526,7 @@ namespace IPC rapidjson::Value gray_peers(rapidjson::kArrayType); for (auto & entry : gray_list) { + // Each peer object is encoded as JSON rapidjson::Value output(rapidjson::kObjectType); output.AddMember("id", entry.id, gray_list_allocator); output.AddMember("ip", entry.adr.ip, gray_list_allocator); @@ -444,6 +553,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_mining_status IPC + * + * \param message 0MQ response object to populate + */ void get_mining_status(wap_proto_t *message) { if (!check_core_ready()) { wap_proto_set_status(message, STATUS_CORE_BUSY); @@ -464,6 +578,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief set_log_hash_rate IPC + * + * \param message 0MQ response object to populate + */ void set_log_hash_rate(wap_proto_t *message) { if (core->get_miner().is_mining()) { @@ -476,6 +595,11 @@ namespace IPC } } + /*! + * \brief set_log_hash_rate IPC + * + * \param message 0MQ response object to populate + */ void set_log_level(wap_proto_t *message) { // zproto supports only unsigned integers afaik. so the log level is sent as // one and casted to signed int here. @@ -493,16 +617,31 @@ namespace IPC } } + /*! + * \brief start_save_graph IPC + * + * \param message 0MQ response object to populate + */ void start_save_graph(wap_proto_t *message) { p2p->set_save_graph(true); wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief stop_save_graph IPC + * + * \param message 0MQ response object to populate + */ void stop_save_graph(wap_proto_t *message) { p2p->set_save_graph(false); wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_block_hash IPC + * + * \param message 0MQ response object to populate + */ void get_block_hash(wap_proto_t *message) { if (!check_core_busy()) { @@ -521,6 +660,11 @@ namespace IPC wap_proto_set_status(message, STATUS_OK); } + /*! + * \brief get_block_template IPC + * + * \param message 0MQ response object to populate + */ void get_block_template(wap_proto_t *message) { if (!check_core_ready()) { diff --git a/src/ipc/include/daemon_ipc_handlers.h b/src/ipc/include/daemon_ipc_handlers.h index 231a9bae5..6a9df6b05 100644 --- a/src/ipc/include/daemon_ipc_handlers.h +++ b/src/ipc/include/daemon_ipc_handlers.h @@ -28,6 +28,11 @@ // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers +/*! + * \file daemon_ipc_handlers.h + * \brief Header for Daemon IPC handlers + */ + #ifndef DAEMON_IPC_HANDLERS_H #define DAEMON_IPC_HANDLERS_H @@ -52,8 +57,13 @@ using namespace epee; #include "rapidjson/writer.h" #include "rapidjson/stringbuffer.h" +/*! + * \namespace IPC + * \brief Namespace pertaining to IPC. + */ namespace IPC { + // A bunch of response statuses and error codes const uint64_t STATUS_OK = 0; const uint64_t STATUS_CORE_BUSY = 1; const uint64_t STATUS_WRONG_ADDRESS = 2; @@ -70,16 +80,15 @@ namespace IPC const uint64_t STATUS_ERROR_STORING_BLOCKCHAIN = 13; const uint64_t STATUS_HEIGHT_TOO_BIG = 13; const uint64_t STATUS_RESERVE_SIZE_TOO_BIG = 14; + /*! + * \namespace Daemon + * \brief Namespace pertaining to Daemon IPC. + */ namespace Daemon { + void get_height(wap_proto_t *message); void start_mining(wap_proto_t *message); void stop_mining(wap_proto_t *message); - void retrieve_blocks(wap_proto_t *message); - void send_raw_transaction(wap_proto_t *message); - void get_output_indexes(wap_proto_t *message); - void get_random_outs(wap_proto_t *message); - void get_height(wap_proto_t *message); - void save_bc(wap_proto_t *message); void get_info(wap_proto_t *message); void get_peer_list(wap_proto_t *message); void get_mining_status(wap_proto_t *message); @@ -89,9 +98,31 @@ namespace IPC void stop_save_graph(wap_proto_t *message); void get_block_hash(wap_proto_t *message); void get_block_template(wap_proto_t *message); + void retrieve_blocks(wap_proto_t *message); + void send_raw_transaction(wap_proto_t *message); + void get_output_indexes(wap_proto_t *message); + void get_random_outs(wap_proto_t *message); + void save_bc(wap_proto_t *message); + + /*! + * \brief initializes it with objects necessary to handle IPC requests and starts + * IPC server + * + * \param p_core cryptonote core object + * \param p_p2p p2p object + * \param p_testnet testnet mode or not + */ void init(cryptonote::core &p_core, nodetool::node_server > &p_p2p, bool p_testnet); + + /*! + * \brief stops the IPC server + * + * \param p_core cryptonote core object + * \param p_p2p p2p object + * \param p_testnet testnet mode or not + */ void stop(); } } diff --git a/src/rpc/daemon_deprecated_rpc.cpp b/src/rpc/daemon_deprecated_rpc.cpp index 9d50fc97a..6e85a93a1 100644 --- a/src/rpc/daemon_deprecated_rpc.cpp +++ b/src/rpc/daemon_deprecated_rpc.cpp @@ -1,6 +1,36 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + /*! - * \file rpc_translator.cpp - * \brief Implementations of JSON RPC handlers (Daemon) + * \file daemon_deprecated_rpc.cpp + * \brief Implementations of old JSON RPC handlers (Daemon) */ // NOTE: @@ -41,11 +71,19 @@ namespace const char* STATUS_OK = "OK"; + /*! + * \brief Checks if daemon can be reached via IPC + * \return true if daemon can be reached + */ bool check_connection_to_daemon() { return ipc_client && wap_client_connected(ipc_client); } + /*! + * \brief Checks if daemon can be reached and if not tries to connect to it. + * \return true if daemon is reachable at the end of the function + */ bool connect_to_daemon() { if (check_connection_to_daemon()) { return true; @@ -746,6 +784,125 @@ namespace return response.length(); } + /*! + * \brief Implementation of 'getblocks' method. + * \param buf Buffer to fill in response. + * \param len Max length of response. + * \param req net_skeleton RPC request + * \return Actual response length. + */ + int getblocks(char *buf, int len, struct ns_rpc_request *req) + { + if (!connect_to_daemon()) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + if (req->params == NULL) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Parameters missing.", "{}"); + } + + rapidjson::Document request_json; + char request_buf[1000]; + strncpy(request_buf, req->params[0].ptr, req->params[0].len); + request_buf[req->params[0].len] = '\0'; + if (request_json.Parse(request_buf).HasParseError()) + { + return ns_rpc_create_error(buf, len, req, parse_error, + "Invalid JSON passed", "{}"); + } + + if (!request_json.HasMember("start_height") || !request_json["start_height"].IsNumber()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'start_height' field", "{}"); + } + if (!request_json.HasMember("block_ids") || !request_json["block_ids"].IsArray()) + { + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect 'block_ids' field", "{}"); + } + + uint64_t start_height = request_json["start_height"].GetUint(); + uint64_t block_count = request_json["blocks_ids"].Size(); + zlist_t *list = zlist_new(); + for (int i = 0; i < block_count; i++) { + if (!request_json["blocks_ids"][i].IsString()) { + zlist_destroy(&list); + return ns_rpc_create_error(buf, len, req, invalid_params, + "Incorrect block_id", "{}"); + } + std::string block_id = request_json["blocks_ids"][i].GetString(); + char *size_prepended_block_id = new char[block_id.length() + 1]; + size_prepended_block_id[0] = crypto::HASH_SIZE; + memcpy(size_prepended_block_id + 1, block_id.c_str(), crypto::HASH_SIZE); + zlist_append(list, size_prepended_block_id); + } + int rc = wap_client_blocks(ipc_client, &list, start_height); + zlist_destroy(&list); + + if (rc < 0) { + return ns_rpc_create_error(buf, len, req, daemon_connection_error, + "Couldn't connect to daemon.", "{}"); + } + + uint64_t status = wap_client_status(ipc_client); + if (status == IPC::STATUS_CORE_BUSY) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Core busy.", "{}"); + } + if (status == IPC::STATUS_INTERNAL_ERROR) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Internal error.", "{}"); + } + + rapidjson::Document response_json; + rapidjson::Document::AllocatorType &allocator = response_json.GetAllocator(); + rapidjson::Value result_json; + result_json.SetObject(); + rapidjson::Value blocks(rapidjson::kArrayType); + + zframe_t *frame = zmsg_first(wap_client_block_data(ipc_client)); + if (!frame) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Internal error.", "{}"); + } + size_t size = zframe_size(frame); + char *block_data = reinterpret_cast(zframe_data(frame)); + + rapidjson::Document json; + if (json.Parse(block_data, size).HasParseError()) { + return ns_rpc_create_error(buf, len, req, internal_error, + "Internal error.", "{}"); + } + for (rapidjson::SizeType i = 0; i < json["blocks"].Size(); i++) { + rapidjson::Value block_entry(rapidjson::kObjectType); + std::string block_string(json["blocks"][i]["block"].GetString(), json["blocks"][i]["block"].GetStringLength()); + rapidjson::Value block_string_json(rapidjson::kStringType); + block_string_json.SetString(block_string.c_str(), block_string.length()); + block_entry.AddMember("block", block_string_json, allocator); + rapidjson::Value txs(rapidjson::kArrayType); + for (rapidjson::SizeType j = 0; j < json["blocks"][i]["txs"].Size(); j++) { + rapidjson::Value txs_json(rapidjson::kStringType); + std::string txs_string(json["blocks"][i]["txs"][j].GetString(), json["blocks"][i]["txs"][j].GetStringLength()); + txs_json.SetString(txs_string.c_str(), txs_string.length()); + txs.PushBack(txs_json, allocator); + } + block_entry.AddMember("txs", txs, allocator); + blocks.PushBack(block_entry, allocator); + } + + result_json.AddMember("start_height", wap_client_start_height(ipc_client), allocator); + result_json.AddMember("current_height", wap_client_curr_height(ipc_client), allocator); + result_json.AddMember("blocks", blocks, allocator); + std::string response; + construct_response_string(req, result_json, response_json, response); + size_t copy_length = ((uint32_t)len > response.length()) ? response.length() + 1 : (uint32_t)len; + strncpy(buf, response.c_str(), copy_length); + return response.length(); + } + // Contains a list of method names. const char *method_names[] = { "getheight", @@ -761,6 +918,7 @@ namespace "stopsavegraph", "getblockhash", "getblocktemplate", + "getblocks", NULL }; @@ -779,6 +937,7 @@ namespace stopsavegraph, getblockhash, getblocktemplate, + getblocks, NULL }; @@ -816,12 +975,17 @@ namespace namespace RPC { /*! - * \namespace Daemon - * \brief RPC relevant to daemon + * \namespace DaemonDeprecated + * \brief DaemonDeprecated RPC stuff */ namespace DaemonDeprecated { + /*! + * \brief Starts an HTTP server that listens to old style JSON RPC requests + * and creates an IPC client to be able to talk to the daemon + * \return status code + */ int start() { server = new RPC::Json_rpc_http_server("127.0.0.1", "9997", "daemon_json_rpc", &ev_handler); if (!server->start()) { @@ -836,6 +1000,9 @@ namespace RPC return SUCCESS; } + /*! + * \brief Stops the HTTP server and destroys the IPC client + */ void stop() { if (server) { server->stop(); diff --git a/src/rpc/daemon_deprecated_rpc.h b/src/rpc/daemon_deprecated_rpc.h index 6ea1e820c..3f6a0be9f 100644 --- a/src/rpc/daemon_deprecated_rpc.h +++ b/src/rpc/daemon_deprecated_rpc.h @@ -1,3 +1,33 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + /*! * \file daemon_json_rpc_handlers.h * \brief Header for JSON RPC handlers (Daemon) @@ -40,7 +70,15 @@ namespace RPC const int SUCCESS = 0; const int FAILURE_DAEMON_NOT_RUNNING = 1; const int FAILURE_HTTP_SERVER = 2; + /*! + * \brief Starts an HTTP server that listens to old style JSON RPC requests + * and creates an IPC client to be able to talk to the daemon + * \return status code + */ int start(); + /*! + * \brief Stops the HTTP server and destroys the IPC client + */ void stop(); } } diff --git a/src/rpc/json_rpc.cpp b/src/rpc/json_rpc.cpp index 142cb75c4..816ec25f8 100644 --- a/src/rpc/json_rpc.cpp +++ b/src/rpc/json_rpc.cpp @@ -1,3 +1,44 @@ +// Copyright (c) 2014, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +/*! + * \file json_rpc.cpp + * \brief Monero RPC deprecated + * + * Uses net_skeleton (fossa) as the HTTP server to translate JSON RPC requests + * into 0MQ IPC requests, sends them to the daemon, translates back 0MQ IPC responses + * into JSON RPC responses all as per the old monero JSON RPC API. + * + * Written for backwards compatiblity purposes. + */ + #include "daemon_deprecated_rpc.h" #include #include From b00a56acfe50ab8723ef1e0845c614c2d462a0a9 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Sun, 5 Jul 2015 08:32:55 -0400 Subject: [PATCH 45/45] Fixed issue with issuing legacy RPC daemon commands As the core RPC server is being redone to use ZeroMQ and the legacy RPC is being wrapped around that, RPC IP and Port args still need to stick around for the daemon's remote invocation. This will likely change in the near future. --- src/daemon/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index d1e0cf671..406f7bb10 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -77,7 +77,7 @@ int main(int argc, char const * argv[]) command_line::add_arg(visible_options, command_line::arg_test_drop_download); command_line::add_arg(visible_options, command_line::arg_test_dbg_lock_sleep); command_line::add_arg(visible_options, command_line::arg_test_drop_download_height); - + // Settings bf::path default_log = default_data_dir / std::string(CRYPTONOTE_NAME ".log"); command_line::add_arg(core_settings, daemon_args::arg_log_file, default_log.string()); @@ -90,6 +90,11 @@ int main(int argc, char const * argv[]) // Hidden options command_line::add_arg(hidden_options, daemon_args::arg_command); + // Legacy RPC options (in hidden section, not sure if best place) + command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_rpc_bind_ip); + command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_rpc_bind_port); + command_line::add_arg(hidden_options, cryptonote::core_rpc_server::arg_testnet_rpc_bind_port); + visible_options.add(core_settings); all_options.add(visible_options); all_options.add(hidden_options);
    +

    Websocket PubSub Demonstration

    + +

    + This page demonstrates how net skeleton could be used to implement + + publish–subscribe pattern. Open this page in several browser + windows. Each window initiates persistent + WebSocket + connection with the server, making each browser window a websocket client. + Send messages, and see messages sent by other clients. +

    + +
    +
    + +

    + + +

    +
  • Jc>fPUbxeDmhzjU6{; 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 K_+Di#AioD4?#lQ8 literal 0 HcmV?d00001 diff --git a/external/net_skeleton/examples/netcat/nc.c b/external/net_skeleton/examples/netcat/nc.c new file mode 100644 index 000000000..94cfd222c --- /dev/null +++ b/external/net_skeleton/examples/netcat/nc.c @@ -0,0 +1,140 @@ +// Copyright (c) 2014 Cesanta Software Limited +// All rights reserved +// +// This software is dual-licensed: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. For the terms of this +// license, see . +// +// You are free to use this software under the terms of the GNU General +// Public License, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// Alternatively, you can license this software under a commercial +// license, as set out in . +// +// $Date: 2014-09-28 05:04:41 UTC $ + +// This file implements "netcat" utility with SSL and traffic hexdump. + +#include "net_skeleton.h" + +static int s_received_signal = 0; + +static void signal_handler(int sig_num) { + signal(sig_num, signal_handler); + s_received_signal = sig_num; +} + +static void show_usage_and_exit(const char *prog_name) { + fprintf(stderr, "%s\n", "Copyright (c) 2014 CESANTA SOFTWARE"); + fprintf(stderr, "%s\n", "Usage:"); + fprintf(stderr, " %s\n [-d debug_file] [-l] [tcp|ssl]://[ip:]port[:cert][:ca_cert]", + prog_name); + fprintf(stderr, "%s\n", "Examples:"); + fprintf(stderr, " %s\n -d hexdump.txt ssl://google.com:443", prog_name); + fprintf(stderr, " %s\n -l ssl://443:ssl_cert.pem", prog_name); + fprintf(stderr, " %s\n -l tcp://8080", prog_name); + exit(EXIT_FAILURE); +} + +static void on_stdin_read(struct ns_connection *nc, int ev, void *p) { + int ch = * (int *) p; + + (void) ev; + + if (ch < 0) { + // EOF is received from stdin. Schedule the connection to close + nc->flags |= NSF_FINISHED_SENDING_DATA; + if (nc->send_iobuf.len <= 0) { + nc->flags |= NSF_CLOSE_IMMEDIATELY; + } + } else { + // A character is received from stdin. Send it to the connection. + unsigned char c = (unsigned char) ch; + ns_send(nc, &c, 1); + } +} + +static void *stdio_thread_func(void *param) { + struct ns_mgr *mgr = (struct ns_mgr *) param; + int ch; + + // Read stdin until EOF character by character, sending them to the mgr + while ((ch = getchar()) != EOF) { + ns_broadcast(mgr, on_stdin_read, &ch, sizeof(ch)); + } + s_received_signal = 1; + + return NULL; +} + +static void ev_handler(struct ns_connection *nc, int ev, void *p) { + (void) p; + + switch (ev) { + case NS_ACCEPT: + case NS_CONNECT: + ns_start_thread(stdio_thread_func, nc->mgr); + break; + + case NS_CLOSE: + s_received_signal = 1; + break; + + case NS_RECV: + fwrite(nc->recv_iobuf.buf, 1, nc->recv_iobuf.len, stdout); + iobuf_remove(&nc->recv_iobuf, nc->recv_iobuf.len); + break; + + default: + break; + } +} + +int main(int argc, char *argv[]) { + struct ns_mgr mgr; + int i, is_listening = 0; + const char *address = NULL; + + ns_mgr_init(&mgr, NULL); + + // Parse command line options + for (i = 1; i < argc && argv[i][0] == '-'; i++) { + if (strcmp(argv[i], "-l") == 0) { + is_listening = 1; + } else if (strcmp(argv[i], "-d") == 0 && i + 1 < argc) { + mgr.hexdump_file = argv[++i]; + } else { + show_usage_and_exit(argv[0]); + } + } + + if (i + 1 == argc) { + address = argv[i]; + } else { + show_usage_and_exit(argv[0]); + } + + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGPIPE, SIG_IGN); + + if (is_listening) { + if (ns_bind(&mgr, address, ev_handler) == NULL) { + fprintf(stderr, "ns_bind(%s) failed\n", address); + exit(EXIT_FAILURE); + } + } else if (ns_connect(&mgr, address, ev_handler) == NULL) { + fprintf(stderr, "ns_connect(%s) failed\n", address); + exit(EXIT_FAILURE); + } + + while (s_received_signal == 0) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return EXIT_SUCCESS; +} diff --git a/external/net_skeleton/examples/publish_subscribe/Makefile b/external/net_skeleton/examples/publish_subscribe/Makefile new file mode 100644 index 000000000..04531e981 --- /dev/null +++ b/external/net_skeleton/examples/publish_subscribe/Makefile @@ -0,0 +1,14 @@ +PROG = publish_subscribe +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/publish_subscribe/publish_subscribe b/external/net_skeleton/examples/publish_subscribe/publish_subscribe new file mode 100644 index 0000000000000000000000000000000000000000..fc94b938ad396c97c1d58fdaf10fdceff6e356ed GIT binary patch literal 65633 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 AF8}}l literal 0 HcmV?d00001 diff --git a/external/net_skeleton/examples/publish_subscribe/publish_subscribe.c b/external/net_skeleton/examples/publish_subscribe/publish_subscribe.c new file mode 100644 index 000000000..841458581 --- /dev/null +++ b/external/net_skeleton/examples/publish_subscribe/publish_subscribe.c @@ -0,0 +1,112 @@ +// Copyright (c) 2014 Cesanta Software Limited +// All rights reserved +// +// This software is dual-licensed: you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. For the terms of this +// license, see . +// +// You are free to use this software under the terms of the GNU General +// Public License, but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// Alternatively, you can license this software under a commercial +// license, as set out in . +// +// $Date: 2014-09-28 05:04:41 UTC $ + +#include "net_skeleton.h" + +static void *stdin_thread(void *param) { + int ch, sock = * (int *) param; + while ((ch = getchar()) != EOF) { + unsigned char c = (unsigned char) ch; + send(sock, &c, 1, 0); // Forward all types characters to the socketpair + } + return NULL; +} + +static void server_handler(struct ns_connection *nc, int ev, void *p) { + (void) p; + if (ev == NS_RECV) { + // Push received message to all ncections + struct iobuf *io = &nc->recv_iobuf; + struct ns_connection *c; + + for (c = ns_next(nc->mgr, NULL); c != NULL; c = ns_next(nc->mgr, c)) { + ns_send(c, io->buf, io->len); + } + iobuf_remove(io, io->len); + } +} + +static void client_handler(struct ns_connection *conn, int ev, void *p) { + struct iobuf *io = &conn->recv_iobuf; + (void) p; + + if (ev == NS_CONNECT) { + if (conn->flags & NSF_CLOSE_IMMEDIATELY) { + printf("%s\n", "Error connecting to server!"); + exit(EXIT_FAILURE); + } + printf("%s\n", "Connected to server. Type a message and press enter."); + } else if (ev == NS_RECV) { + if (conn->flags & NSF_USER_1) { + // Received data from the stdin, forward it to the server + struct ns_connection *c = (struct ns_connection *) conn->user_data; + ns_send(c, io->buf, io->len); + iobuf_remove(io, io->len); + } else { + // Received data from server connection, print it + fwrite(io->buf, io->len, 1, stdout); + iobuf_remove(io, io->len); + } + } else if (ev == NS_CLOSE) { + // Connection has closed, most probably cause server has stopped + exit(EXIT_SUCCESS); + } +} + +int main(int argc, char *argv[]) { + struct ns_mgr mgr; + + if (argc != 3) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } else if (strcmp(argv[2], "client") == 0) { + int fds[2]; + struct ns_connection *ioconn, *server_conn; + + ns_mgr_init(&mgr, NULL); + + // Connect to the pubsub server + server_conn = ns_connect(&mgr, argv[1], client_handler); + if (server_conn == NULL) { + fprintf(stderr, "Cannot connect to port %s\n", argv[1]); + exit(EXIT_FAILURE); + } + + // Create a socketpair and give one end to the thread that reads stdin + ns_socketpair(fds); + ns_start_thread(stdin_thread, &fds[1]); + + // The other end of a pair goes inside the server + ioconn = ns_add_sock(&mgr, fds[0], client_handler); + ioconn->flags |= NSF_USER_1; // Mark this so we know this is a stdin + ioconn->user_data = server_conn; + + } else { + // Server code path + ns_mgr_init(&mgr, NULL); + ns_bind(&mgr, argv[1], server_handler); + printf("Starting pubsub server on port %s\n", argv[1]); + } + + for (;;) { + ns_mgr_poll(&mgr, 1000); + } + ns_mgr_free(&mgr); + + return EXIT_SUCCESS; +} diff --git a/external/net_skeleton/examples/restful_client/Makefile b/external/net_skeleton/examples/restful_client/Makefile new file mode 100644 index 000000000..4281f5658 --- /dev/null +++ b/external/net_skeleton/examples/restful_client/Makefile @@ -0,0 +1,14 @@ +PROG = restful_client +SOURCES = $(PROG).c ../../net_skeleton.c +CFLAGS = -W -Wall -I../.. -pthread $(CFLAGS_EXTRA) + +all: $(PROG) + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/external/net_skeleton/examples/restful_client/restful_client b/external/net_skeleton/examples/restful_client/restful_client new file mode 100644 index 0000000000000000000000000000000000000000..f2b124ceabaced1c0b7c3a557b25451cbddd3787 GIT binary patch literal 61243 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