2015-05-27 12:08:46 +00:00
// Copyright (c) 2012-2015, The CryptoNote developers, The Bytecoin developers
2014-08-13 10:38:35 +00:00
//
// This file is part of Bytecoin.
//
// Bytecoin is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Bytecoin is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Bytecoin. If not, see <http://www.gnu.org/licenses/>.
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
# include <boost/optional.hpp>
2014-08-13 10:51:37 +00:00
# include <boost/program_options.hpp>
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
# include <System/Dispatcher.h>
# include <System/Event.h>
# include <System/InterruptedException.h>
# include <System/Ipv4Address.h>
# include <System/Ipv4Resolver.h>
# include <System/TcpConnection.h>
# include <System/TcpConnector.h>
# include <System/Timer.h>
# include "Common/command_line.h"
# include "Common/StringTools.h"
2014-03-03 22:07:58 +00:00
# include "crypto/crypto.h"
2014-08-13 10:51:37 +00:00
# include "p2p/p2p_protocol_defs.h"
2015-05-27 12:08:46 +00:00
# include "p2p/LevinProtocol.h"
2014-08-13 10:51:37 +00:00
# include "rpc/core_rpc_server_commands_defs.h"
2015-05-27 12:08:46 +00:00
# include "rpc/HttpClient.h"
2015-07-15 12:23:00 +00:00
# include "serialization/SerializationTools.h"
2014-08-13 10:51:37 +00:00
# include "version.h"
2014-03-03 22:07:58 +00:00
namespace po = boost : : program_options ;
2015-05-27 12:08:46 +00:00
using namespace CryptoNote ;
2014-03-03 22:07:58 +00:00
2015-07-15 12:23:00 +00:00
# ifndef ENDL
# define ENDL std::endl
# endif
2015-05-27 12:08:46 +00:00
namespace {
2014-03-20 11:46:11 +00:00
const command_line : : arg_descriptor < std : : string , true > arg_ip = { " ip " , " set ip " } ;
2015-05-27 12:08:46 +00:00
const command_line : : arg_descriptor < uint16_t > arg_port = { " port " , " set port " } ;
const command_line : : arg_descriptor < uint16_t > arg_rpc_port = { " rpc_port " , " set rpc port " } ;
2014-03-20 11:46:11 +00:00
const command_line : : arg_descriptor < uint32_t , true > arg_timeout = { " timeout " , " set timeout " } ;
2014-03-03 22:07:58 +00:00
const command_line : : arg_descriptor < std : : string > arg_priv_key = { " private_key " , " private key to subscribe debug command " , " " , true } ;
2014-03-20 11:46:11 +00:00
const command_line : : arg_descriptor < uint64_t > arg_peer_id = { " peer_id " , " peer_id if known(if not - will be requested) " , 0 } ;
2014-03-03 22:07:58 +00:00
const command_line : : arg_descriptor < bool > arg_generate_keys = { " generate_keys_pair " , " generate private and public keys pair " } ;
const command_line : : arg_descriptor < bool > arg_request_stat_info = { " request_stat_info " , " request statistics information " } ;
const command_line : : arg_descriptor < bool > arg_request_net_state = { " request_net_state " , " request network state information (peer list, connections count) " } ;
const command_line : : arg_descriptor < bool > arg_get_daemon_info = { " rpc_get_daemon_info " , " request daemon state info vie rpc (--rpc_port option should be set ). " , " " , true } ;
}
2015-05-27 12:08:46 +00:00
struct response_schema {
2014-03-03 22:07:58 +00:00
std : : string status ;
std : : string COMMAND_REQUEST_STAT_INFO_status ;
std : : string COMMAND_REQUEST_NETWORK_STATE_status ;
2015-05-27 12:08:46 +00:00
boost : : optional < COMMAND_REQUEST_STAT_INFO : : response > si_rsp ;
boost : : optional < COMMAND_REQUEST_NETWORK_STATE : : response > ns_rsp ;
2014-03-03 22:07:58 +00:00
} ;
2015-05-27 12:08:46 +00:00
template < typename SystemObj >
void withTimeout ( System : : Dispatcher & dispatcher , SystemObj & obj , unsigned timeout , std : : function < void ( ) > f ) {
System : : Event timeoutEvent ( dispatcher ) ;
System : : Timer timeoutTimer ( dispatcher ) ;
dispatcher . spawn ( [ & ] ( ) {
try {
timeoutTimer . sleep ( std : : chrono : : milliseconds ( timeout ) ) ;
obj . stop ( ) ;
} catch ( std : : exception & ) { }
timeoutEvent . set ( ) ;
} ) ;
try {
f ( ) ;
} catch ( System : : InterruptedException & ) {
timeoutEvent . wait ( ) ;
throw std : : runtime_error ( " Operation timeout " ) ;
} catch ( std : : exception & ) {
timeoutTimer . stop ( ) ;
timeoutEvent . wait ( ) ;
throw ;
}
timeoutTimer . stop ( ) ;
timeoutEvent . wait ( ) ;
}
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 ( ) ) {
2015-07-15 12:23:00 +00:00
ss < < " , " < < ENDL < < " \" si_rsp \" : " < < storeToJson ( rs . si_rsp . get ( ) ) ;
2015-05-27 12:08:46 +00:00
}
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 + + ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
ss < < " ], " < < ENDL ;
ss < < " \" local_peerlist_white \" : [ " < < ENDL ;
i = 0 ;
for ( const peerlist_entry & 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 peerlist_entry & 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 + + ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
ss < < " ] " < < ENDL < < " } " < < ENDL ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
ss < < " } " ;
return ss ;
}
2014-03-03 22:07:58 +00:00
//---------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool print_COMMAND_REQUEST_STAT_INFO ( const COMMAND_REQUEST_STAT_INFO : : response & si ) {
2014-03-03 22:07:58 +00:00
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 ;
}
//---------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool print_COMMAND_REQUEST_NETWORK_STATE ( const COMMAND_REQUEST_NETWORK_STATE : : response & ns ) {
2014-03-03 22:07:58 +00:00
std : : cout < < " ------ COMMAND_REQUEST_NETWORK_STATE ------ " < < ENDL ;
std : : cout < < " Peer id: " < < ns . my_id < < ENDL ;
2015-05-27 12:08:46 +00:00
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 ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
std : : cout < < " Peer list white: " < < ns . my_id < < ENDL ;
2015-05-27 12:08:46 +00:00
for ( const peerlist_entry & pe : ns . local_peerlist_white ) {
std : : cout < < pe . id < < " \t " < < pe . adr < < " \t " < < Common : : timeIntervalToString ( ns . local_time - pe . last_seen ) < < ENDL ;
2014-03-03 22:07:58 +00:00
}
std : : cout < < " Peer list gray: " < < ns . my_id < < ENDL ;
2015-05-27 12:08:46 +00:00
for ( const peerlist_entry & pe : ns . local_peerlist_gray ) {
std : : cout < < pe . id < < " \t " < < pe . adr < < " \t " < < Common : : timeIntervalToString ( ns . local_time - pe . last_seen ) < < ENDL ;
2014-03-03 22:07:58 +00:00
}
return true ;
}
//---------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool handle_get_daemon_info ( po : : variables_map & vm ) {
if ( ! command_line : : has_arg ( vm , arg_rpc_port ) ) {
2014-03-03 22:07:58 +00:00
std : : cout < < " ERROR: rpc port not set " < < ENDL ;
return false ;
}
2015-05-27 12:08:46 +00:00
try {
System : : Dispatcher dispatcher ;
HttpClient httpClient ( dispatcher , command_line : : get_arg ( vm , arg_ip ) , command_line : : get_arg ( vm , arg_rpc_port ) ) ;
2014-03-03 22:07:58 +00:00
2015-07-15 12:23:00 +00:00
CryptoNote : : COMMAND_RPC_GET_INFO : : request req ;
CryptoNote : : COMMAND_RPC_GET_INFO : : response res ;
2015-05-27 12:08:46 +00:00
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 ;
2014-03-03 22:07:58 +00:00
return false ;
}
return true ;
}
//---------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool handle_request_stat ( po : : variables_map & vm , peerid_type peer_id ) {
if ( ! command_line : : has_arg ( vm , arg_priv_key ) ) {
2014-03-03 22:07:58 +00:00
std : : cout < < " { " < < ENDL < < " \" status \" : \" ERROR: " < < " secret key not set \" " < < ENDL < < " } " ;
return false ;
}
2015-05-27 12:08:46 +00:00
2015-07-15 12:23:00 +00:00
crypto : : secret_key prvk ;
2015-05-27 12:08:46 +00:00
if ( ! Common : : podFromHex ( command_line : : get_arg ( vm , arg_priv_key ) , prvk ) ) {
2014-03-03 22:07:58 +00:00
std : : cout < < " { " < < ENDL < < " \" status \" : \" ERROR: " < < " wrong secret key set \" " < < ENDL < < " } " ;
return false ;
}
2015-07-15 12:23:00 +00:00
response_schema rs ;
2015-05-27 12:08:46 +00:00
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 , connector , timeout , [ & ] {
connection = connector . connect ( addr , command_line : : get_arg ( vm , arg_port ) ) ;
} ) ;
2014-03-03 22:07:58 +00:00
rs . status = " OK " ;
2015-05-27 12:08:46 +00:00
LevinProtocol levin ( connection ) ;
if ( ! peer_id ) {
COMMAND_REQUEST_PEER_ID : : request req ;
COMMAND_REQUEST_PEER_ID : : response rsp ;
withTimeout ( dispatcher , connection , timeout , [ & ] {
levin . invoke ( COMMAND_REQUEST_PEER_ID : : ID , req , rsp ) ;
} ) ;
2014-03-03 22:07:58 +00:00
peer_id = rsp . my_id ;
}
2015-07-15 12:23:00 +00:00
proof_of_trust pot ;
2015-05-27 12:08:46 +00:00
pot . peer_id = peer_id ;
pot . time = time ( NULL ) ;
2015-07-15 12:23:00 +00:00
crypto : : public_key pubk ;
2015-05-27 12:08:46 +00:00
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 ) ;
2014-03-03 22:07:58 +00:00
2015-05-27 12:08:46 +00:00
if ( command_line : : get_arg ( vm , arg_request_stat_info ) ) {
2015-07-15 12:23:00 +00:00
COMMAND_REQUEST_STAT_INFO : : request req ;
COMMAND_REQUEST_STAT_INFO : : response res ;
2015-05-27 12:08:46 +00:00
req . tr = pot ;
try {
withTimeout ( dispatcher , connection , 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 ( ) ;
}
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
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 ) ;
2015-07-15 12:23:00 +00:00
COMMAND_REQUEST_NETWORK_STATE : : request req { pot } ;
COMMAND_REQUEST_NETWORK_STATE : : response res ;
2015-05-27 12:08:46 +00:00
try {
withTimeout ( dispatcher , connection , 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 ( ) ;
}
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
} catch ( const std : : exception & e ) {
std : : cout < < " ERROR: " < < e . what ( ) < < std : : endl ;
return false ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
get_response_schema_as_json ( std : : cout , rs ) < < std : : endl ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
//---------------------------------------------------------------------------------------------------------------
2015-05-27 12:08:46 +00:00
bool generate_and_print_keys ( ) {
2015-07-15 12:23:00 +00:00
crypto : : public_key pk ;
crypto : : secret_key sk ;
2014-03-03 22:07:58 +00:00
generate_keys ( pk , sk ) ;
2015-05-27 12:08:46 +00:00
std : : cout < < " PUBLIC KEY: " < < Common : : podToHex ( pk ) < < ENDL
< < " PRIVATE KEY: " < < Common : : podToHex ( sk ) ;
2014-03-03 22:07:58 +00:00
return true ;
}
2015-05-27 12:08:46 +00:00
int main ( int argc , char * argv [ ] ) {
2014-03-03 22:07:58 +00:00
// 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 ;
2015-05-27 12:08:46 +00:00
bool r = command_line : : handle_error_helper ( desc_all , [ & ] ( ) {
2014-03-03 22:07:58 +00:00
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 ;
} ) ;
2015-05-27 12:08:46 +00:00
2014-03-03 22:07:58 +00:00
if ( ! r )
return 1 ;
2015-05-27 12:08:46 +00:00
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 ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
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 ;
2014-03-03 22:07:58 +00:00
}
2015-05-27 12:08:46 +00:00
std : : cerr < < " Not enough arguments. " < < ENDL ;
std : : cerr < < desc_all < < ENDL ;
2014-03-03 22:07:58 +00:00
return 1 ;
}