diff options
author | Riccardo Spagni <ric@spagni.net> | 2018-02-16 14:18:38 +0100 |
---|---|---|
committer | Riccardo Spagni <ric@spagni.net> | 2018-02-16 14:18:38 +0100 |
commit | 381faf06c7f9174ab5aaad909d57c66c612533de (patch) | |
tree | fbfbd442ea2290b7efff45042b221b480a1facd0 /src | |
parent | Merge pull request #3161 (diff) | |
parent | Fix in_peers/out_peers RPC operations (diff) | |
download | monero-381faf06c7f9174ab5aaad909d57c66c612533de.tar.xz |
Merge pull request #3163
628b78ae Fix in_peers/out_peers RPC operations (Erik de Castro Lopo)
ece9bcf5 rpc_client: Fix error handling (Erik de Castro Lopo)
8f30350d Fix method name in invoke_http_json_rpc (Erik de Castro Lopo)
32c0f908 Allow the number of incoming connections to be limited (Erik de Castro Lopo)
d609a2c1 Rename delete_connections to delete_out_connections (Erik de Castro Lopo)
b927c0fc Rename connections_count to max_out_connection_count (Erik de Castro Lopo)
Diffstat (limited to 'src')
-rw-r--r-- | src/common/rpc_client.h | 16 | ||||
-rw-r--r-- | src/daemon/command_parser_executor.cpp | 17 | ||||
-rw-r--r-- | src/daemon/command_parser_executor.h | 4 | ||||
-rw-r--r-- | src/daemon/command_server.cpp | 6 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.cpp | 34 | ||||
-rw-r--r-- | src/daemon/rpc_command_executor.h | 4 | ||||
-rw-r--r-- | src/p2p/net_node.cpp | 1 | ||||
-rw-r--r-- | src/p2p/net_node.h | 8 | ||||
-rw-r--r-- | src/p2p/net_node.inl | 79 | ||||
-rw-r--r-- | src/p2p/p2p_protocol_defs.h | 6 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.cpp | 16 | ||||
-rw-r--r-- | src/rpc/core_rpc_server.h | 2 | ||||
-rw-r--r-- | src/rpc/core_rpc_server_commands_defs.h | 24 |
13 files changed, 185 insertions, 32 deletions
diff --git a/src/common/rpc_client.h b/src/common/rpc_client.h index 64c84ed19..9665966ae 100644 --- a/src/common/rpc_client.h +++ b/src/common/rpc_client.h @@ -72,10 +72,10 @@ namespace tools fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { - fail_msg_writer() << "Daemon request failed"; + fail_msg_writer() << "basic_json_rpc_request: Daemon request failed"; return false; } else @@ -95,15 +95,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json_rpc("/json_rpc", method_name, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << " -- json_rpc_request: " << res.status; return false; } else @@ -123,15 +123,15 @@ namespace tools t_http_connection connection(&m_http_client); bool ok = connection.is_open(); - ok = ok && epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); if (!ok) { fail_msg_writer() << "Couldn't connect to daemon: " << m_http_client.get_host() << ":" << m_http_client.get_port(); return false; } - else if (res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? + ok = epee::net_utils::invoke_http_json(relative_url, req, res, m_http_client, t_http_connection::TIMEOUT()); + if (!ok || res.status != CORE_RPC_STATUS_OK) // TODO - handle CORE_RPC_STATUS_BUSY ? { - fail_msg_writer() << fail_msg << " -- " << res.status; + fail_msg_writer() << fail_msg << "-- rpc_request: " << res.status; return false; } else diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 3ec74ff79..09e425dd1 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -428,6 +428,23 @@ bool t_command_parser_executor::out_peers(const std::vector<std::string>& args) return m_executor.out_peers(limit); } +bool t_command_parser_executor::in_peers(const std::vector<std::string>& args) +{ + if (args.empty()) return false; + + unsigned int limit; + try { + limit = std::stoi(args[0]); + } + + catch(const std::exception& ex) { + _erro("stoi exception"); + return false; + } + + return m_executor.in_peers(limit); +} + bool t_command_parser_executor::start_save_graph(const std::vector<std::string>& args) { if (!args.empty()) return false; diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 37e900b8f..2c09a4748 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -108,7 +108,9 @@ public: bool set_limit_down(const std::vector<std::string>& args); bool out_peers(const std::vector<std::string>& args); - + + bool in_peers(const std::vector<std::string>& args); + bool start_save_graph(const std::vector<std::string>& args); bool stop_save_graph(const std::vector<std::string>& args); diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 1f8981fa2..a50dbea69 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -197,6 +197,12 @@ t_command_server::t_command_server( , "Set the <max_number> of out peers." ); m_command_lookup.set_handler( + "in_peers" + , std::bind(&t_command_parser_executor::in_peers, &m_parser, p::_1) + , "in_peers <max_number>" + , "Set the <max_number> of in peers." + ); + m_command_lookup.set_handler( "start_save_graph" , std::bind(&t_command_parser_executor::start_save_graph, &m_parser, p::_1) , "Start saving data for dr monero." diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 2da4f3e6e..0ae0fd2cc 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1262,7 +1262,7 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) if (m_is_rpc) { - if (!m_rpc_client->json_rpc_request(req, res, "out_peers", fail_message.c_str())) + if (!m_rpc_client->rpc_request(req, res, "/out_peers", fail_message.c_str())) { return true; } @@ -1281,6 +1281,38 @@ bool t_rpc_command_executor::out_peers(uint64_t limit) return true; } +bool t_rpc_command_executor::in_peers(uint64_t limit) +{ + cryptonote::COMMAND_RPC_IN_PEERS::request req; + cryptonote::COMMAND_RPC_IN_PEERS::response res; + + epee::json_rpc::error error_resp; + + req.in_peers = limit; + + std::string fail_message = "Unsuccessful"; + + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/in_peers", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_in_peers(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << make_error(fail_message, res.status); + return true; + } + } + + std::cout << "Max number of in peers set to " << limit << std::endl; + + return true; +} + bool t_rpc_command_executor::start_save_graph() { cryptonote::COMMAND_RPC_START_SAVE_GRAPH::request req; diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index f0781180a..fa83d8988 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -122,7 +122,9 @@ public: bool set_limit(int64_t limit_down, int64_t limit_up); bool out_peers(uint64_t limit); - + + bool in_peers(uint64_t limit); + bool start_save_graph(); bool stop_save_graph(); diff --git a/src/p2p/net_node.cpp b/src/p2p/net_node.cpp index 121e72392..994941168 100644 --- a/src/p2p/net_node.cpp +++ b/src/p2p/net_node.cpp @@ -55,6 +55,7 @@ namespace nodetool const command_line::arg_descriptor<bool> arg_no_igd = {"no-igd", "Disable UPnP port mapping"}; const command_line::arg_descriptor<int64_t> arg_out_peers = {"out-peers", "set max number of out peers", -1}; + const command_line::arg_descriptor<int64_t> arg_in_peers = {"in-peers", "set max number of in peers", -1}; const command_line::arg_descriptor<int> arg_tos_flag = {"tos-flag", "set TOS flag", -1}; const command_line::arg_descriptor<int64_t> arg_limit_rate_up = {"limit-rate-up", "set limit-rate-up [kB/s]", -1}; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 20520f83c..568c650cc 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -81,6 +81,7 @@ namespace nodetool node_server(t_payload_net_handler& payload_handler) :m_payload_handler(payload_handler), m_current_number_of_out_peers(0), + m_current_number_of_in_peers(0), m_allow_local_ip(false), m_hide_my_port(false), m_no_igd(false), @@ -117,8 +118,10 @@ namespace nodetool bool log_connections(); virtual uint64_t get_connections_count(); size_t get_outgoing_connections_count(); + size_t get_incoming_connections_count(); peerlist_manager& get_peerlist_manager(){return m_peerlist;} - void delete_connections(size_t count); + void delete_out_connections(size_t count); + void delete_in_connections(size_t count); virtual bool block_host(const epee::net_utils::network_address &adress, time_t seconds = P2P_IP_BLOCKTIME); virtual bool unblock_host(const epee::net_utils::network_address &address); virtual std::map<std::string, time_t> get_blocked_hosts() { CRITICAL_REGION_LOCAL(m_blocked_hosts_lock); return m_blocked_hosts; } @@ -230,6 +233,7 @@ namespace nodetool bool parse_peers_and_add_to_container(const boost::program_options::variables_map& vm, const command_line::arg_descriptor<std::vector<std::string> > & arg, Container& container); bool set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max); + bool set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max); bool set_tos_flag(const boost::program_options::variables_map& vm, int limit); bool set_rate_up_limit(const boost::program_options::variables_map& vm, int64_t limit); @@ -271,6 +275,7 @@ namespace nodetool public: config m_config; // TODO was private, add getters? std::atomic<unsigned int> m_current_number_of_out_peers; + std::atomic<unsigned int> m_current_number_of_in_peers; void set_save_graph(bool save_graph) { @@ -345,6 +350,7 @@ namespace nodetool extern const command_line::arg_descriptor<bool> arg_no_igd; extern const command_line::arg_descriptor<bool> arg_offline; extern const command_line::arg_descriptor<int64_t> arg_out_peers; + extern const command_line::arg_descriptor<int64_t> arg_in_peers; extern const command_line::arg_descriptor<int> arg_tos_flag; extern const command_line::arg_descriptor<int64_t> arg_limit_rate_up; diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 152dba942..3445674f6 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -85,6 +85,7 @@ namespace nodetool command_line::add_arg(desc, arg_p2p_hide_my_port); command_line::add_arg(desc, arg_no_igd); command_line::add_arg(desc, arg_out_peers); + command_line::add_arg(desc, arg_in_peers); command_line::add_arg(desc, arg_tos_flag); command_line::add_arg(desc, arg_limit_rate_up); command_line::add_arg(desc, arg_limit_rate_down); @@ -315,6 +316,9 @@ namespace nodetool if ( !set_max_out_peers(vm, command_line::get_arg(vm, arg_out_peers) ) ) return false; + if ( !set_max_in_peers(vm, command_line::get_arg(vm, arg_in_peers) ) ) + return false; + if ( !set_tos_flag(vm, command_line::get_arg(vm, arg_tos_flag) ) ) return false; @@ -565,14 +569,23 @@ namespace nodetool while (!is_closing && !m_net_server.is_stop_signal_sent()) { // main loop of thread //number_of_peers = m_net_server.get_config_object().get_connections_count(); - unsigned int number_of_peers = 0; + unsigned int number_of_in_peers = 0; + unsigned int number_of_out_peers = 0; m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) { - if (!cntxt.m_is_income) ++number_of_peers; + if (cntxt.m_is_income) + { + ++number_of_in_peers; + } + else + { + ++number_of_out_peers; + } return true; }); // lambda - m_current_number_of_out_peers = number_of_peers; + m_current_number_of_in_peers = number_of_in_peers; + m_current_number_of_out_peers = number_of_out_peers; boost::this_thread::sleep_for(boost::chrono::seconds(1)); } // main loop of thread @@ -871,11 +884,11 @@ namespace nodetool template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::try_to_connect_and_handshake_with_new_peer(const epee::net_utils::network_address& na, bool just_take_peerlist, uint64_t last_seen_stamp, PeerType peer_type, uint64_t first_seen_stamp) { - if (m_current_number_of_out_peers == m_config.m_net_config.connections_count) // out peers limit + if (m_current_number_of_out_peers == m_config.m_net_config.max_out_connection_count) // out peers limit { return false; } - else if (m_current_number_of_out_peers > m_config.m_net_config.connections_count) + else if (m_current_number_of_out_peers > m_config.m_net_config.max_out_connection_count) { m_net_server.get_config_object().del_out_connections(1); m_current_number_of_out_peers --; // atomic variable, update time = 1s @@ -1164,10 +1177,10 @@ namespace nodetool if (!connect_to_peerlist(m_priority_peers)) return false; - size_t expected_white_connections = (m_config.m_net_config.connections_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; + size_t expected_white_connections = (m_config.m_net_config.max_out_connection_count*P2P_DEFAULT_WHITELIST_CONNECTIONS_PERCENT)/100; size_t conn_count = get_outgoing_connections_count(); - if(conn_count < m_config.m_net_config.connections_count) + if(conn_count < m_config.m_net_config.max_out_connection_count) { if(conn_count < expected_white_connections) { @@ -1178,20 +1191,20 @@ namespace nodetool if(!make_expected_connections_count(white, expected_white_connections)) return false; //then do grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; }else { //start from grey list - if(!make_expected_connections_count(gray, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(gray, m_config.m_net_config.max_out_connection_count)) return false; //and then do white list - if(!make_expected_connections_count(white, m_config.m_net_config.connections_count)) + if(!make_expected_connections_count(white, m_config.m_net_config.max_out_connection_count)) return false; } } - if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.connections_count) + if (start_conn_count == get_outgoing_connections_count() && start_conn_count < m_config.m_net_config.max_out_connection_count) { MINFO("Failed to connect to any, trying seeds"); if (!connect_to_seed()) @@ -1253,6 +1266,20 @@ namespace nodetool } //----------------------------------------------------------------------------------- template<class t_payload_net_handler> + size_t node_server<t_payload_net_handler>::get_incoming_connections_count() + { + size_t count = 0; + m_net_server.get_config_object().foreach_connection([&](const p2p_connection_context& cntxt) + { + if(cntxt.m_is_income) + ++count; + return true; + }); + + return count; + } + //----------------------------------------------------------------------------------- + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::idle_worker() { m_peer_handshake_idle_maker_interval.do_call(boost::bind(&node_server<t_payload_net_handler>::peer_sync_idle_maker, this)); @@ -1618,6 +1645,13 @@ namespace nodetool return 1; } + if (m_current_number_of_in_peers >= m_config.m_net_config.max_in_connection_count) // in peers limit + { + LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but already have max incoming connections, so dropping this one."); + drop_connection(context); + return 1; + } + if(!m_payload_handler.process_payload_sync_data(arg.payload_data, context, true)) { LOG_WARNING_CC(context, "COMMAND_HANDSHAKE came, but process_payload_sync_data returned false, dropping connection."); @@ -1779,20 +1813,37 @@ namespace nodetool bool node_server<t_payload_net_handler>::set_max_out_peers(const boost::program_options::variables_map& vm, int64_t max) { if(max == -1) { - m_config.m_net_config.connections_count = P2P_DEFAULT_CONNECTIONS_COUNT; + m_config.m_net_config.max_out_connection_count = P2P_DEFAULT_CONNECTIONS_COUNT; + return true; + } + m_config.m_net_config.max_out_connection_count = max; + return true; + } + + template<class t_payload_net_handler> + bool node_server<t_payload_net_handler>::set_max_in_peers(const boost::program_options::variables_map& vm, int64_t max) + { + if(max == -1) { + m_config.m_net_config.max_in_connection_count = -1; return true; } - m_config.m_net_config.connections_count = max; + m_config.m_net_config.max_in_connection_count = max; return true; } template<class t_payload_net_handler> - void node_server<t_payload_net_handler>::delete_connections(size_t count) + void node_server<t_payload_net_handler>::delete_out_connections(size_t count) { m_net_server.get_config_object().del_out_connections(count); } template<class t_payload_net_handler> + void node_server<t_payload_net_handler>::delete_in_connections(size_t count) + { + m_net_server.get_config_object().del_in_connections(count); + } + + template<class t_payload_net_handler> bool node_server<t_payload_net_handler>::set_tos_flag(const boost::program_options::variables_map& vm, int flag) { if(flag==-1){ diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index 348a8b978..e793e19b6 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -131,13 +131,15 @@ namespace nodetool struct network_config { BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(connections_count) + KV_SERIALIZE(max_out_connection_count) + KV_SERIALIZE(max_in_connection_count) KV_SERIALIZE(handshake_interval) KV_SERIALIZE(packet_max_size) KV_SERIALIZE(config_id) END_KV_SERIALIZE_MAP() - uint32_t connections_count; + uint32_t max_out_connection_count; + uint32_t max_in_connection_count; uint32_t connection_timeout; uint32_t ping_connection_timeout; uint32_t handshake_interval; diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index 140094faa..80d9823d4 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -1596,9 +1596,21 @@ namespace cryptonote PERF_TIMER(on_out_peers); size_t n_connections = m_p2p.get_outgoing_connections_count(); size_t n_delete = (n_connections > req.out_peers) ? n_connections - req.out_peers : 0; - m_p2p.m_config.m_net_config.connections_count = req.out_peers; + m_p2p.m_config.m_net_config.max_out_connection_count = req.out_peers; if (n_delete) - m_p2p.delete_connections(n_delete); + m_p2p.delete_out_connections(n_delete); + res.status = CORE_RPC_STATUS_OK; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res) + { + PERF_TIMER(on_in_peers); + size_t n_connections = m_p2p.get_incoming_connections_count(); + size_t n_delete = (n_connections > req.in_peers) ? n_connections - req.in_peers : 0; + m_p2p.m_config.m_net_config.max_in_connection_count = req.in_peers; + if (n_delete) + m_p2p.delete_in_connections(n_delete); res.status = CORE_RPC_STATUS_OK; return true; } diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 0c7028719..2b0d54bef 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -114,6 +114,7 @@ namespace cryptonote MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT) MAP_URI_AUTO_JON2_IF("/set_limit", on_set_limit, COMMAND_RPC_SET_LIMIT, !m_restricted) MAP_URI_AUTO_JON2_IF("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted) + MAP_URI_AUTO_JON2_IF("/in_peers", on_in_peers, COMMAND_RPC_IN_PEERS, !m_restricted) MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) @@ -183,6 +184,7 @@ namespace cryptonote bool on_get_limit(const COMMAND_RPC_GET_LIMIT::request& req, COMMAND_RPC_GET_LIMIT::response& res); bool on_set_limit(const COMMAND_RPC_SET_LIMIT::request& req, COMMAND_RPC_SET_LIMIT::response& res); bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); + bool on_in_peers(const COMMAND_RPC_IN_PEERS::request& req, COMMAND_RPC_IN_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9b9a8f949..3a878c4cc 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1678,8 +1678,28 @@ namespace cryptonote struct response { - std::string status; - + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_IN_PEERS + { + struct request + { + uint64_t in_peers; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(in_peers) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(status) END_KV_SERIALIZE_MAP() |