mirror of
https://codeberg.org/anoncontributorxmr/monero.git
synced 2024-11-30 03:03:17 +00:00
New interactive daemon command 'print_net_stats': Global traffic stats
This commit is contained in:
parent
f2f725d8db
commit
c23ea7962d
13 changed files with 182 additions and 2 deletions
|
@ -66,6 +66,8 @@ class network_throttle : public i_network_throttle {
|
||||||
network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer
|
network_time_seconds m_last_sample_time; // time of last history[0] - so we know when to rotate the buffer
|
||||||
network_time_seconds m_start_time; // when we were created
|
network_time_seconds m_start_time; // when we were created
|
||||||
bool m_any_packet_yet; // did we yet got any packet to count
|
bool m_any_packet_yet; // did we yet got any packet to count
|
||||||
|
uint64_t m_total_packets;
|
||||||
|
uint64_t m_total_bytes;
|
||||||
|
|
||||||
std::string m_name; // my name for debug and logs
|
std::string m_name; // my name for debug and logs
|
||||||
std::string m_nameshort; // my name for debug and logs (used in log file name)
|
std::string m_nameshort; // my name for debug and logs (used in log file name)
|
||||||
|
@ -95,6 +97,7 @@ class network_throttle : public i_network_throttle {
|
||||||
virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported
|
virtual size_t get_recommended_size_of_planned_transport() const; ///< what should be the size (bytes) of next data block to be transported
|
||||||
virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame
|
virtual size_t get_recommended_size_of_planned_transport_window(double force_window) const; ///< ditto, but for given windows time frame
|
||||||
virtual double get_current_speed() const;
|
virtual double get_current_speed() const;
|
||||||
|
virtual void get_stats(uint64_t &total_packets, uint64_t &total_bytes) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13
|
virtual network_time_seconds time_to_slot(network_time_seconds t) const { return std::floor( t ); } // convert exact time eg 13.7 to rounded time for slot number in history 13
|
||||||
|
|
|
@ -153,6 +153,7 @@ class i_network_throttle {
|
||||||
|
|
||||||
virtual double get_time_seconds() const =0; // a timer
|
virtual double get_time_seconds() const =0; // a timer
|
||||||
virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0;
|
virtual void logger_handle_net(const std::string &filename, double time, size_t size)=0;
|
||||||
|
virtual void get_stats(uint64_t &total_packets, uint64_t &total_bytes) const =0;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -136,6 +136,8 @@ network_throttle::network_throttle(const std::string &nameshort, const std::stri
|
||||||
m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle
|
m_target_speed = 16 * 1024; // other defaults are probably defined in the command-line parsing code when this class is used e.g. as main global throttle
|
||||||
m_last_sample_time = 0;
|
m_last_sample_time = 0;
|
||||||
m_history.resize(m_window_size);
|
m_history.resize(m_window_size);
|
||||||
|
m_total_packets = 0;
|
||||||
|
m_total_bytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void network_throttle::set_name(const std::string &name)
|
void network_throttle::set_name(const std::string &name)
|
||||||
|
@ -192,6 +194,8 @@ void network_throttle::_handle_trafic_exact(size_t packet_size, size_t orginal_s
|
||||||
calculate_times_struct cts ; calculate_times(packet_size, cts , false, -1);
|
calculate_times_struct cts ; calculate_times(packet_size, cts , false, -1);
|
||||||
calculate_times_struct cts2; calculate_times(packet_size, cts2, false, 5);
|
calculate_times_struct cts2; calculate_times(packet_size, cts2, false, 5);
|
||||||
m_history.front().m_size += packet_size;
|
m_history.front().m_size += packet_size;
|
||||||
|
m_total_packets++;
|
||||||
|
m_total_bytes += packet_size;
|
||||||
|
|
||||||
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
|
std::ostringstream oss; oss << "["; for (auto sample: m_history) oss << sample.m_size << " "; oss << "]" << std::ends;
|
||||||
std::string history_str = oss.str();
|
std::string history_str = oss.str();
|
||||||
|
@ -352,6 +356,12 @@ double network_throttle::get_current_speed() const {
|
||||||
return bytes_transferred / ((m_history.size() - 1) * m_slot_size);
|
return bytes_transferred / ((m_history.size() - 1) * m_slot_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void network_throttle::get_stats(uint64_t &total_packets, uint64_t &total_bytes) const {
|
||||||
|
total_packets = m_total_packets;
|
||||||
|
total_bytes = m_total_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,7 @@ using namespace epee;
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
#undef MONERO_DEFAULT_LOG_CATEGORY
|
#undef MONERO_DEFAULT_LOG_CATEGORY
|
||||||
|
@ -1063,4 +1064,39 @@ std::string get_nix_version_display_string()
|
||||||
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm);
|
||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_human_readable_bytes(uint64_t bytes)
|
||||||
|
{
|
||||||
|
// Use 1024 for "kilo", 1024*1024 for "mega" and so on instead of the more modern and standard-conforming
|
||||||
|
// 1000, 1000*1000 and so on, to be consistent with other Monero code that also uses base 2 units
|
||||||
|
struct byte_map
|
||||||
|
{
|
||||||
|
const char* const format;
|
||||||
|
const std::uint64_t bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr const byte_map sizes[] =
|
||||||
|
{
|
||||||
|
{"%.0f B", 1024},
|
||||||
|
{"%.2f KB", 1024 * 1024},
|
||||||
|
{"%.2f MB", std::uint64_t(1024) * 1024 * 1024},
|
||||||
|
{"%.2f GB", std::uint64_t(1024) * 1024 * 1024 * 1024},
|
||||||
|
{"%.2f TB", std::uint64_t(1024) * 1024 * 1024 * 1024 * 1024}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bytes_less
|
||||||
|
{
|
||||||
|
bool operator()(const byte_map& lhs, const byte_map& rhs) const noexcept
|
||||||
|
{
|
||||||
|
return lhs.bytes < rhs.bytes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto size = std::upper_bound(
|
||||||
|
std::begin(sizes), std::end(sizes) - 1, byte_map{"", bytes}, bytes_less{}
|
||||||
|
);
|
||||||
|
const std::uint64_t divisor = size->bytes / 1024;
|
||||||
|
return (boost::format(size->format) % (double(bytes) / divisor)).str();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,4 +244,6 @@ namespace tools
|
||||||
void closefrom(int fd);
|
void closefrom(int fd);
|
||||||
|
|
||||||
std::string get_human_readable_timestamp(uint64_t ts);
|
std::string get_human_readable_timestamp(uint64_t ts);
|
||||||
|
|
||||||
|
std::string get_human_readable_bytes(uint64_t bytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,13 @@ bool t_command_parser_executor::print_connections(const std::vector<std::string>
|
||||||
return m_executor.print_connections();
|
return m_executor.print_connections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool t_command_parser_executor::print_net_stats(const std::vector<std::string>& args)
|
||||||
|
{
|
||||||
|
if (!args.empty()) return false;
|
||||||
|
|
||||||
|
return m_executor.print_net_stats();
|
||||||
|
}
|
||||||
|
|
||||||
bool t_command_parser_executor::print_blockchain_info(const std::vector<std::string>& args)
|
bool t_command_parser_executor::print_blockchain_info(const std::vector<std::string>& args)
|
||||||
{
|
{
|
||||||
if(!args.size())
|
if(!args.size())
|
||||||
|
|
|
@ -148,6 +148,8 @@ public:
|
||||||
bool prune_blockchain(const std::vector<std::string>& args);
|
bool prune_blockchain(const std::vector<std::string>& args);
|
||||||
|
|
||||||
bool check_blockchain_pruning(const std::vector<std::string>& args);
|
bool check_blockchain_pruning(const std::vector<std::string>& args);
|
||||||
|
|
||||||
|
bool print_net_stats(const std::vector<std::string>& args);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace daemonize
|
} // namespace daemonize
|
||||||
|
|
|
@ -77,6 +77,11 @@ t_command_server::t_command_server(
|
||||||
, std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1)
|
, std::bind(&t_command_parser_executor::print_connections, &m_parser, p::_1)
|
||||||
, "Print the current connections."
|
, "Print the current connections."
|
||||||
);
|
);
|
||||||
|
m_command_lookup.set_handler(
|
||||||
|
"print_net_stats"
|
||||||
|
, std::bind(&t_command_parser_executor::print_net_stats, &m_parser, p::_1)
|
||||||
|
, "Print network statistics."
|
||||||
|
);
|
||||||
m_command_lookup.set_handler(
|
m_command_lookup.set_handler(
|
||||||
"print_bc"
|
"print_bc"
|
||||||
, std::bind(&t_command_parser_executor::print_blockchain_info, &m_parser, p::_1)
|
, std::bind(&t_command_parser_executor::print_blockchain_info, &m_parser, p::_1)
|
||||||
|
|
|
@ -627,6 +627,66 @@ bool t_rpc_command_executor::print_connections() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool t_rpc_command_executor::print_net_stats()
|
||||||
|
{
|
||||||
|
cryptonote::COMMAND_RPC_GET_NET_STATS::request net_stats_req;
|
||||||
|
cryptonote::COMMAND_RPC_GET_NET_STATS::response net_stats_res;
|
||||||
|
cryptonote::COMMAND_RPC_GET_LIMIT::request limit_req;
|
||||||
|
cryptonote::COMMAND_RPC_GET_LIMIT::response limit_res;
|
||||||
|
|
||||||
|
std::string fail_message = "Unsuccessful";
|
||||||
|
|
||||||
|
if (m_is_rpc)
|
||||||
|
{
|
||||||
|
if (!m_rpc_client->json_rpc_request(net_stats_req, net_stats_res, "get_net_stats", fail_message.c_str()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!m_rpc_client->json_rpc_request(limit_req, limit_res, "get_limit", fail_message.c_str()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!m_rpc_server->on_get_net_stats(net_stats_req, net_stats_res) || net_stats_res.status != CORE_RPC_STATUS_OK)
|
||||||
|
{
|
||||||
|
tools::fail_msg_writer() << make_error(fail_message, net_stats_res.status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!m_rpc_server->on_get_limit(limit_req, limit_res) || limit_res.status != CORE_RPC_STATUS_OK)
|
||||||
|
{
|
||||||
|
tools::fail_msg_writer() << make_error(fail_message, limit_res.status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t seconds = (uint64_t)time(NULL) - net_stats_res.start_time;
|
||||||
|
uint64_t average = seconds > 0 ? net_stats_res.total_bytes_in / seconds : 0;
|
||||||
|
uint64_t limit = limit_res.limit_down * 1024; // convert to bytes, as limits are always kB/s
|
||||||
|
double percent = (double)average / (double)limit * 100.0;
|
||||||
|
tools::success_msg_writer() << boost::format("Received %u bytes (%s) in %u packets, average %s/s = %.2f%% of the limit of %s/s")
|
||||||
|
% net_stats_res.total_bytes_in
|
||||||
|
% tools::get_human_readable_bytes(net_stats_res.total_bytes_in)
|
||||||
|
% net_stats_res.total_packets_in
|
||||||
|
% tools::get_human_readable_bytes(average)
|
||||||
|
% percent
|
||||||
|
% tools::get_human_readable_bytes(limit);
|
||||||
|
|
||||||
|
average = seconds > 0 ? net_stats_res.total_bytes_out / seconds : 0;
|
||||||
|
limit = limit_res.limit_up * 1024;
|
||||||
|
percent = (double)average / (double)limit * 100.0;
|
||||||
|
tools::success_msg_writer() << boost::format("Sent %u bytes (%s) in %u packets, average %s/s = %.2f%% of the limit of %s/s")
|
||||||
|
% net_stats_res.total_bytes_out
|
||||||
|
% tools::get_human_readable_bytes(net_stats_res.total_bytes_out)
|
||||||
|
% net_stats_res.total_packets_out
|
||||||
|
% tools::get_human_readable_bytes(average)
|
||||||
|
% percent
|
||||||
|
% tools::get_human_readable_bytes(limit);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index) {
|
bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, uint64_t end_block_index) {
|
||||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req;
|
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::request req;
|
||||||
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res;
|
cryptonote::COMMAND_RPC_GET_BLOCK_HEADERS_RANGE::response res;
|
||||||
|
|
|
@ -160,6 +160,8 @@ public:
|
||||||
bool prune_blockchain();
|
bool prune_blockchain();
|
||||||
|
|
||||||
bool check_blockchain_pruning();
|
bool check_blockchain_pruning();
|
||||||
|
|
||||||
|
bool print_net_stats();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace daemonize
|
} // namespace daemonize
|
||||||
|
|
|
@ -260,6 +260,23 @@ namespace cryptonote
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
bool core_rpc_server::on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx)
|
||||||
|
{
|
||||||
|
PERF_TIMER(on_get_net_stats);
|
||||||
|
// No bootstrap daemon check: Only ever get stats about local server
|
||||||
|
res.start_time = (uint64_t)m_core.get_start_time();
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_in);
|
||||||
|
epee::net_utils::network_throttle_manager::get_global_throttle_in().get_stats(res.total_packets_in, res.total_bytes_in);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
CRITICAL_REGION_LOCAL(epee::net_utils::network_throttle_manager::m_lock_get_global_throttle_out);
|
||||||
|
epee::net_utils::network_throttle_manager::get_global_throttle_out().get_stats(res.total_packets_out, res.total_bytes_out);
|
||||||
|
}
|
||||||
|
res.status = CORE_RPC_STATUS_OK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
class pruned_transaction {
|
class pruned_transaction {
|
||||||
transaction& tx;
|
transaction& tx;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -115,6 +115,7 @@ namespace cryptonote
|
||||||
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
|
MAP_URI_AUTO_JON2_IF("/stop_daemon", on_stop_daemon, COMMAND_RPC_STOP_DAEMON, !m_restricted)
|
||||||
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
|
MAP_URI_AUTO_JON2("/get_info", on_get_info, COMMAND_RPC_GET_INFO)
|
||||||
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
|
MAP_URI_AUTO_JON2("/getinfo", on_get_info, COMMAND_RPC_GET_INFO)
|
||||||
|
MAP_URI_AUTO_JON2_IF("/get_net_stats", on_get_net_stats, COMMAND_RPC_GET_NET_STATS, !m_restricted)
|
||||||
MAP_URI_AUTO_JON2("/get_limit", on_get_limit, COMMAND_RPC_GET_LIMIT)
|
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("/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("/out_peers", on_out_peers, COMMAND_RPC_OUT_PEERS, !m_restricted)
|
||||||
|
@ -179,6 +180,7 @@ namespace cryptonote
|
||||||
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx = NULL);
|
bool on_get_outs_bin(const COMMAND_RPC_GET_OUTPUTS_BIN::request& req, COMMAND_RPC_GET_OUTPUTS_BIN::response& res, const connection_context *ctx = NULL);
|
||||||
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx = NULL);
|
bool on_get_outs(const COMMAND_RPC_GET_OUTPUTS::request& req, COMMAND_RPC_GET_OUTPUTS::response& res, const connection_context *ctx = NULL);
|
||||||
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx = NULL);
|
bool on_get_info(const COMMAND_RPC_GET_INFO::request& req, COMMAND_RPC_GET_INFO::response& res, const connection_context *ctx = NULL);
|
||||||
|
bool on_get_net_stats(const COMMAND_RPC_GET_NET_STATS::request& req, COMMAND_RPC_GET_NET_STATS::response& res, const connection_context *ctx = NULL);
|
||||||
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
|
bool on_save_bc(const COMMAND_RPC_SAVE_BC::request& req, COMMAND_RPC_SAVE_BC::response& res, const connection_context *ctx = NULL);
|
||||||
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
|
bool on_get_peer_list(const COMMAND_RPC_GET_PEER_LIST::request& req, COMMAND_RPC_GET_PEER_LIST::response& res, const connection_context *ctx = NULL);
|
||||||
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
|
bool on_set_log_hash_rate(const COMMAND_RPC_SET_LOG_HASH_RATE::request& req, COMMAND_RPC_SET_LOG_HASH_RATE::response& res, const connection_context *ctx = NULL);
|
||||||
|
|
|
@ -84,7 +84,7 @@ namespace cryptonote
|
||||||
// advance which version they will stop working with
|
// advance which version they will stop working with
|
||||||
// Don't go over 32767 for any of these
|
// Don't go over 32767 for any of these
|
||||||
#define CORE_RPC_VERSION_MAJOR 2
|
#define CORE_RPC_VERSION_MAJOR 2
|
||||||
#define CORE_RPC_VERSION_MINOR 4
|
#define CORE_RPC_VERSION_MINOR 5
|
||||||
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||||
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
||||||
|
|
||||||
|
@ -1013,6 +1013,39 @@ namespace cryptonote
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------
|
||||||
|
struct COMMAND_RPC_GET_NET_STATS
|
||||||
|
{
|
||||||
|
struct request_t
|
||||||
|
{
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
typedef epee::misc_utils::struct_init<request_t> request;
|
||||||
|
|
||||||
|
|
||||||
|
struct response_t
|
||||||
|
{
|
||||||
|
std::string status;
|
||||||
|
uint64_t start_time;
|
||||||
|
uint64_t total_packets_in;
|
||||||
|
uint64_t total_bytes_in;
|
||||||
|
uint64_t total_packets_out;
|
||||||
|
uint64_t total_bytes_out;
|
||||||
|
|
||||||
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
KV_SERIALIZE(status)
|
||||||
|
KV_SERIALIZE(start_time)
|
||||||
|
KV_SERIALIZE(total_packets_in)
|
||||||
|
KV_SERIALIZE(total_bytes_in)
|
||||||
|
KV_SERIALIZE(total_packets_out)
|
||||||
|
KV_SERIALIZE(total_bytes_out)
|
||||||
|
END_KV_SERIALIZE_MAP()
|
||||||
|
};
|
||||||
|
typedef epee::misc_utils::struct_init<response_t> response;
|
||||||
|
};
|
||||||
|
|
||||||
//-----------------------------------------------
|
//-----------------------------------------------
|
||||||
struct COMMAND_RPC_STOP_MINING
|
struct COMMAND_RPC_STOP_MINING
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue