// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include "Common/CommandLine.h" #include "Common/StringTools.h" #include "crypto/crypto.h" #include "P2p/P2pProtocolDefinitions.h" #include "P2p/LevinProtocol.h" #include "Rpc/CoreRpcServerCommandsDefinitions.h" #include "Rpc/HttpClient.h" #include "Serialization/SerializationTools.h" #include "version.h" namespace po = boost::program_options; using namespace CryptoNote; #ifndef ENDL #define ENDL std::endl #endif namespace { const command_line::arg_descriptor arg_ip = {"ip", "set ip"}; const command_line::arg_descriptor arg_port = { "port", "set port" }; const command_line::arg_descriptor arg_rpc_port = {"rpc_port", "set rpc port"}; const command_line::arg_descriptor arg_timeout = {"timeout", "set timeout"}; const command_line::arg_descriptor arg_priv_key = {"private_key", "private key to subscribe debug command", "", true}; const command_line::arg_descriptor arg_peer_id = {"peer_id", "peer_id if known(if not - will be requested)", 0}; const command_line::arg_descriptor arg_generate_keys = {"generate_keys_pair", "generate private and public keys pair"}; const command_line::arg_descriptor arg_request_stat_info = {"request_stat_info", "request statistics information"}; const command_line::arg_descriptor arg_request_net_state = {"request_net_state", "request network state information (peer list, connections count)"}; const command_line::arg_descriptor arg_get_daemon_info = {"rpc_get_daemon_info", "request daemon state info vie rpc (--rpc_port option should be set ).", "", true}; } struct response_schema { std::string status; std::string COMMAND_REQUEST_STAT_INFO_status; std::string COMMAND_REQUEST_NETWORK_STATE_status; boost::optional si_rsp; boost::optional ns_rsp; }; void withTimeout(System::Dispatcher& dispatcher, unsigned timeout, std::function f) { std::string result; System::ContextGroup cg(dispatcher); System::ContextGroupTimeout cgTimeout(dispatcher, cg, std::chrono::milliseconds(timeout)); cg.spawn([&] { try { f(); } catch (System::InterruptedException&) { result = "Operation timeout"; } catch (std::exception& e) { result = e.what(); } }); cg.wait(); if (!result.empty()) { throw std::runtime_error(result); } } std::ostream& get_response_schema_as_json(std::ostream& ss, response_schema &rs) { ss << "{" << ENDL << " \"status\": \"" << rs.status << "\"," << ENDL << " \"COMMAND_REQUEST_NETWORK_STATE_status\": \"" << rs.COMMAND_REQUEST_NETWORK_STATE_status << "\"," << ENDL << " \"COMMAND_REQUEST_STAT_INFO_status\": \"" << rs.COMMAND_REQUEST_STAT_INFO_status << "\""; if (rs.si_rsp.is_initialized()) { ss << "," << ENDL << " \"si_rsp\": " << storeToJson(rs.si_rsp.get()); } if (rs.ns_rsp.is_initialized()) { const auto& networkState = rs.ns_rsp.get(); ss << "," << ENDL << " \"ns_rsp\": {" << ENDL << " \"local_time\": " << networkState.local_time << "," << ENDL << " \"my_id\": \"" << networkState.my_id << "\"," << ENDL << " \"connections_list\": [" << ENDL; size_t i = 0; for (const connection_entry &ce : networkState.connections_list) { ss << " {\"peer_id\": \"" << ce.id << "\", \"ip\": \"" << Common::ipAddressToString(ce.adr.ip) << "\", \"port\": " << ce.adr.port << ", \"is_income\": " << ce.is_income << "}"; if (networkState.connections_list.size() - 1 != i) ss << ","; ss << ENDL; i++; } ss << " ]," << ENDL; ss << " \"local_peerlist_white\": [" << ENDL; i = 0; for (const PeerlistEntry &pe : networkState.local_peerlist_white) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_white.size() - 1 != i) ss << ","; ss << ENDL; i++; } ss << " ]," << ENDL; ss << " \"local_peerlist_gray\": [" << ENDL; i = 0; for (const PeerlistEntry &pe : networkState.local_peerlist_gray) { ss << " {\"peer_id\": \"" << pe.id << "\", \"ip\": \"" << Common::ipAddressToString(pe.adr.ip) << "\", \"port\": " << pe.adr.port << ", \"last_seen\": " << networkState.local_time - pe.last_seen << "}"; if (networkState.local_peerlist_gray.size() - 1 != i) ss << ","; ss << ENDL; i++; } ss << " ]" << ENDL << " }" << ENDL; } ss << "}"; return ss; } //--------------------------------------------------------------------------------------------------------------- bool print_COMMAND_REQUEST_STAT_INFO(const COMMAND_REQUEST_STAT_INFO::response &si) { std::cout << " ------ COMMAND_REQUEST_STAT_INFO ------ " << ENDL; std::cout << "Version: " << si.version << ENDL; std::cout << "OS Version: " << si.os_version << ENDL; std::cout << "Connections: " << si.connections_count << ENDL; std::cout << "INC Connections: " << si.incoming_connections_count << ENDL; std::cout << "Tx pool size: " << si.payload_info.tx_pool_size << ENDL; std::cout << "BC height: " << si.payload_info.blockchain_height << ENDL; std::cout << "Mining speed: " << si.payload_info.mining_speed << ENDL; std::cout << "Alternative blocks: " << si.payload_info.alternative_blocks << ENDL; std::cout << "Top block id: " << si.payload_info.top_block_id_str << ENDL; return true; } //--------------------------------------------------------------------------------------------------------------- bool print_COMMAND_REQUEST_NETWORK_STATE(const COMMAND_REQUEST_NETWORK_STATE::response &ns) { std::cout << " ------ COMMAND_REQUEST_NETWORK_STATE ------ " << ENDL; std::cout << "Peer id: " << ns.my_id << ENDL; std::cout << "Active connections:" << ENDL; for (const connection_entry &ce : ns.connections_list) { std::cout << ce.id << "\t" << ce.adr << (ce.is_income ? "(INC)" : "(OUT)") << ENDL; } std::cout << "Peer list white:" << ns.my_id << ENDL; for (const PeerlistEntry &pe : ns.local_peerlist_white) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } std::cout << "Peer list gray:" << ns.my_id << ENDL; for (const PeerlistEntry &pe : ns.local_peerlist_gray) { std::cout << pe.id << "\t" << pe.adr << "\t" << Common::timeIntervalToString(ns.local_time - pe.last_seen) << ENDL; } return true; } //--------------------------------------------------------------------------------------------------------------- bool handle_get_daemon_info(po::variables_map& vm) { if(!command_line::has_arg(vm, arg_rpc_port)) { std::cout << "ERROR: rpc port not set" << ENDL; return false; } try { System::Dispatcher dispatcher; HttpClient httpClient(dispatcher, command_line::get_arg(vm, arg_ip), command_line::get_arg(vm, arg_rpc_port)); CryptoNote::COMMAND_RPC_GET_INFO::request req; CryptoNote::COMMAND_RPC_GET_INFO::response res; invokeJsonCommand(httpClient, "/getinfo", req, res); // TODO: timeout std::cout << "OK" << ENDL << "height: " << res.height << ENDL << "difficulty: " << res.difficulty << ENDL << "tx_count: " << res.tx_count << ENDL << "tx_pool_size: " << res.tx_pool_size << ENDL << "alt_blocks_count: " << res.alt_blocks_count << ENDL << "outgoing_connections_count: " << res.outgoing_connections_count << ENDL << "incoming_connections_count: " << res.incoming_connections_count << ENDL << "white_peerlist_size: " << res.white_peerlist_size << ENDL << "grey_peerlist_size: " << res.grey_peerlist_size << ENDL; } catch (const std::exception& e) { std::cout << "ERROR: " << e.what() << std::endl; return false; } return true; } //--------------------------------------------------------------------------------------------------------------- bool handle_request_stat(po::variables_map& vm, PeerIdType peer_id) { if(!command_line::has_arg(vm, arg_priv_key)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "secret key not set \"" << ENDL << "}"; return false; } Crypto::SecretKey prvk; if (!Common::podFromHex(command_line::get_arg(vm, arg_priv_key), prvk)) { std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "wrong secret key set \"" << ENDL << "}"; return false; } response_schema rs; unsigned timeout = command_line::get_arg(vm, arg_timeout); try { System::Dispatcher dispatcher; System::TcpConnector connector(dispatcher); System::Ipv4Resolver resolver(dispatcher); std::cout << "Connecting to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << ENDL; auto addr = resolver.resolve(command_line::get_arg(vm, arg_ip)); System::TcpConnection connection; withTimeout(dispatcher, timeout, [&] { connection = connector.connect(addr, command_line::get_arg(vm, arg_port)); }); rs.status = "OK"; LevinProtocol levin(connection); if (!peer_id) { COMMAND_REQUEST_PEER_ID::request req; COMMAND_REQUEST_PEER_ID::response rsp; withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_PEER_ID::ID, req, rsp); }); peer_id = rsp.my_id; } proof_of_trust pot; pot.peer_id = peer_id; pot.time = time(NULL); Crypto::PublicKey pubk; Common::podFromHex(P2P_STAT_TRUSTED_PUB_KEY, pubk); Crypto::Hash h = get_proof_of_trust_hash(pot); Crypto::generate_signature(h, pubk, prvk, pot.sign); if (command_line::get_arg(vm, arg_request_stat_info)) { COMMAND_REQUEST_STAT_INFO::request req; COMMAND_REQUEST_STAT_INFO::response res; req.tr = pot; try { withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_STAT_INFO::ID, req, res); }); rs.si_rsp = std::move(res); rs.COMMAND_REQUEST_STAT_INFO_status = "OK"; } catch (const std::exception &e) { std::stringstream ss; ss << "ERROR: Failed to invoke remote command COMMAND_REQUEST_STAT_INFO to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << " - " << e.what(); rs.COMMAND_REQUEST_STAT_INFO_status = ss.str(); } } if (command_line::get_arg(vm, arg_request_net_state)) { ++pot.time; h = get_proof_of_trust_hash(pot); Crypto::generate_signature(h, pubk, prvk, pot.sign); COMMAND_REQUEST_NETWORK_STATE::request req{ pot }; COMMAND_REQUEST_NETWORK_STATE::response res; try { withTimeout(dispatcher, timeout, [&] { levin.invoke(COMMAND_REQUEST_NETWORK_STATE::ID, req, res); }); rs.ns_rsp = std::move(res); rs.COMMAND_REQUEST_NETWORK_STATE_status = "OK"; } catch (const std::exception &e) { std::stringstream ss; ss << "ERROR: Failed to invoke remote command COMMAND_REQUEST_NETWORK_STATE to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << " - " << e.what(); rs.COMMAND_REQUEST_NETWORK_STATE_status = ss.str(); } } } catch (const std::exception& e) { std::cout << "ERROR: " << e.what() << std::endl; return false; } get_response_schema_as_json(std::cout, rs) << std::endl; return true; } //--------------------------------------------------------------------------------------------------------------- bool generate_and_print_keys() { Crypto::PublicKey pk; Crypto::SecretKey sk; generate_keys(pk, sk); std::cout << "PUBLIC KEY: " << Common::podToHex(pk) << ENDL << "PRIVATE KEY: " << Common::podToHex(sk); return true; } int main(int argc, char *argv[]) { // Declare the supported options. po::options_description desc_general("General options"); command_line::add_arg(desc_general, command_line::arg_help); po::options_description desc_params("Connectivity options"); command_line::add_arg(desc_params, arg_ip); command_line::add_arg(desc_params, arg_port); command_line::add_arg(desc_params, arg_rpc_port); command_line::add_arg(desc_params, arg_timeout); command_line::add_arg(desc_params, arg_request_stat_info); command_line::add_arg(desc_params, arg_request_net_state); command_line::add_arg(desc_params, arg_generate_keys); command_line::add_arg(desc_params, arg_peer_id); command_line::add_arg(desc_params, arg_priv_key); command_line::add_arg(desc_params, arg_get_daemon_info); po::options_description desc_all; desc_all.add(desc_general).add(desc_params); po::variables_map vm; bool r = command_line::handle_error_helper(desc_all, [&]() { po::store(command_line::parse_command_line(argc, argv, desc_general, true), vm); if (command_line::get_arg(vm, command_line::arg_help)) { std::cout << desc_all << ENDL; return false; } po::store(command_line::parse_command_line(argc, argv, desc_params, false), vm); po::notify(vm); return true; }); if (!r) return 1; if (command_line::has_arg(vm, arg_request_stat_info) || command_line::has_arg(vm, arg_request_net_state)) { return handle_request_stat(vm, command_line::get_arg(vm, arg_peer_id)) ? 0 : 1; } if (command_line::has_arg(vm, arg_get_daemon_info)) { return handle_get_daemon_info(vm) ? 0 : 1; } if (command_line::has_arg(vm, arg_generate_keys)) { return generate_and_print_keys() ? 0 : 1; } std::cerr << "Not enough arguments." << ENDL; std::cerr << desc_all << ENDL; return 1; }