@ -39,8 +39,6 @@ static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
static const int DEFAULT_HTTP_CLIENT_TIMEOUT = 900 ;
static const bool DEFAULT_NAMED = false ;
static const int CONTINUE_EXECUTION = - 1 ;
static const std : : string ONION { " .onion " } ;
static const size_t ONION_LEN { ONION . size ( ) } ;
/** Default number of blocks to generate for RPC generatetoaddress. */
static const std : : string DEFAULT_NBLOCKS = " 1 " ;
@ -298,30 +296,24 @@ public:
class NetinfoRequestHandler : public BaseRequestHandler
{
private :
bool IsAddrIPv6 ( const std : : string & addr ) const
static constexpr int8_t UNKNOWN_NETWORK { - 1 } ;
static constexpr size_t m_networks_size { 3 } ;
const std : : array < std : : string , m_networks_size > m_networks { { " ipv4 " , " ipv6 " , " onion " } } ;
std : : array < std : : array < uint16_t , m_networks_size + 2 > , 3 > m_counts { { { } } } ; //!< Peer counts by (in/out/total, networks/total/block-relay)
int8_t NetworkStringToId ( const std : : string & str ) const
{
return ! addr . empty ( ) & & addr . front ( ) = = ' [ ' ;
}
bool IsInboundOnion ( const std : : string & addr_local , int mapped_as ) const
{
return mapped_as = = 0 & & addr_local . find ( ONION ) ! = std : : string : : npos ;
}
bool IsOutboundOnion ( const std : : string & addr , int mapped_as ) const
{
const size_t addr_len { addr . size ( ) } ;
const size_t onion_pos { addr . rfind ( ONION ) } ;
return mapped_as = = 0 & & onion_pos ! = std : : string : : npos & & addr_len > ONION_LEN & &
( onion_pos = = addr_len - ONION_LEN | | onion_pos = = addr . find_last_of ( " : " ) - ONION_LEN ) ;
for ( size_t i = 0 ; i < m_networks_size ; + + i ) {
if ( str = = m_networks . at ( i ) ) return i ;
}
return UNKNOWN_NETWORK ;
}
uint8_t m_details_level { 0 } ; //!< Optional user-supplied arg to set dashboard details level
bool DetailsRequested ( ) const { return m_details_level > 0 & & m_details_level < 5 ; }
bool IsAddressSelected ( ) const { return m_details_level = = 2 | | m_details_level = = 4 ; }
bool IsVersionSelected ( ) const { return m_details_level = = 3 | | m_details_level = = 4 ; }
enum struct NetType {
ipv4 ,
ipv6 ,
onion ,
} ;
bool m_is_asmap_on { false } ;
size_t m_max_addr_length { 0 } ;
size_t m_max_id_length { 2 } ;
struct Peer {
int id ;
int mapped_as ;
@ -334,30 +326,24 @@ private:
double min_ping ;
double ping ;
std : : string addr ;
std : : string network ;
std : : string sub_version ;
NetType net_type ;
bool is_block_relay ;
bool is_outbound ;
bool operator < ( const Peer & rhs ) const { return std : : tie ( is_outbound , min_ping ) < std : : tie ( rhs . is_outbound , rhs . min_ping ) ; }
} ;
std : : string NetTypeEnumToString ( NetType t )
{
switch ( t ) {
case NetType : : ipv4 : return " ipv4 " ;
case NetType : : ipv6 : return " ipv6 " ;
case NetType : : onion : return " onion " ;
} // no default case, so the compiler can warn about missing cases
assert ( false ) ;
}
std : : vector < Peer > m_peers ;
std : : string ChainToString ( ) const
{
if ( gArgs . GetChainName ( ) = = CBaseChainParams : : TESTNET ) return " testnet " ;
if ( gArgs . GetChainName ( ) = = CBaseChainParams : : REGTEST ) return " regtest " ;
return " " ;
}
const int64_t m_time_now { GetSystemTimeInSeconds ( ) } ;
public :
const int ID_PEERINFO = 0 ;
const int ID_NETWORKINFO = 1 ;
static constexpr int ID_PEERINFO = 0 ;
static constexpr int ID_NETWORKINFO = 1 ;
UniValue PrepareRequest ( const std : : string & method , const std : : vector < std : : string > & args ) override
{
@ -385,49 +371,25 @@ public:
}
// Count peer connection totals, and if DetailsRequested(), store peer data in a vector of structs.
const int64_t time_now { GetSystemTimeInSeconds ( ) } ;
int ipv4_i { 0 } , ipv6_i { 0 } , onion_i { 0 } , block_relay_i { 0 } , total_i { 0 } ; // inbound conn counters
int ipv4_o { 0 } , ipv6_o { 0 } , onion_o { 0 } , block_relay_o { 0 } , total_o { 0 } ; // outbound conn counters
size_t max_peer_id_length { 2 } , max_addr_length { 0 } ;
bool is_asmap_on { false } ;
std : : vector < Peer > peers ;
const UniValue & getpeerinfo { batch [ ID_PEERINFO ] [ " result " ] } ;
for ( const UniValue & peer : getpeerinfo . getValues ( ) ) {
const std : : string addr { peer [ " addr " ] . get_str ( ) } ;
const std : : string addr_local { peer [ " addrlocal " ] . isNull ( ) ? " " : peer [ " addrlocal " ] . get_str ( ) } ;
const int mapped_as { peer [ " mapped_as " ] . isNull ( ) ? 0 : peer [ " mapped_as " ] . get_int ( ) } ;
for ( const UniValue & peer : batch [ ID_PEERINFO ] [ " result " ] . getValues ( ) ) {
const std : : string network { peer [ " network " ] . get_str ( ) } ;
const int8_t network_id { NetworkStringToId ( network ) } ;
if ( network_id = = UNKNOWN_NETWORK ) continue ;
const bool is_outbound { ! peer [ " inbound " ] . get_bool ( ) } ;
const bool is_block_relay { ! peer [ " relaytxes " ] . get_bool ( ) } ;
const bool is_inbound { peer [ " inbound " ] . get_bool ( ) } ;
NetType net_type { NetType : : ipv4 } ;
if ( is_inbound ) {
if ( IsAddrIPv6 ( addr ) ) {
net_type = NetType : : ipv6 ;
+ + ipv6_i ;
} else if ( IsInboundOnion ( addr_local , mapped_as ) ) {
net_type = NetType : : onion ;
+ + onion_i ;
} else {
+ + ipv4_i ;
}
if ( is_block_relay ) + + block_relay_i ;
} else {
if ( IsAddrIPv6 ( addr ) ) {
net_type = NetType : : ipv6 ;
+ + ipv6_o ;
} else if ( IsOutboundOnion ( addr , mapped_as ) ) {
net_type = NetType : : onion ;
+ + onion_o ;
} else {
+ + ipv4_o ;
}
if ( is_block_relay ) + + block_relay_o ;
+ + m_counts . at ( is_outbound ) . at ( network_id ) ; // in/out by network
+ + m_counts . at ( is_outbound ) . at ( m_networks_size ) ; // in/out overall
+ + m_counts . at ( 2 ) . at ( network_id ) ; // total by network
+ + m_counts . at ( 2 ) . at ( m_networks_size ) ; // total overall
if ( is_block_relay ) {
+ + m_counts . at ( is_outbound ) . at ( m_networks_size + 1 ) ; // in/out block-relay
+ + m_counts . at ( 2 ) . at ( m_networks_size + 1 ) ; // total block-relay
}
if ( DetailsRequested ( ) ) {
// Push data for this peer to the peers vector.
const int peer_id { peer [ " id " ] . get_int ( ) } ;
const int mapped_as { peer [ " mapped_as " ] . isNull ( ) ? 0 : peer [ " mapped_as " ] . get_int ( ) } ;
const int version { peer [ " version " ] . get_int ( ) } ;
const std : : string sub_version { peer [ " subver " ] . get_str ( ) } ;
const int64_t conn_time { peer [ " conntime " ] . get_int64 ( ) } ;
const int64_t last_blck { peer [ " last_block " ] . get_int64 ( ) } ;
const int64_t last_recv { peer [ " lastrecv " ] . get_int64 ( ) } ;
@ -435,10 +397,12 @@ public:
const int64_t last_trxn { peer [ " last_transaction " ] . get_int64 ( ) } ;
const double min_ping { peer [ " minping " ] . isNull ( ) ? - 1 : peer [ " minping " ] . get_real ( ) } ;
const double ping { peer [ " pingtime " ] . isNull ( ) ? - 1 : peer [ " pingtime " ] . get_real ( ) } ;
peers . push_back ( { peer_id , mapped_as , version , conn_time , last_blck , last_recv , last_send , last_trxn , min_ping , ping , addr , sub_version , net_type , is_block_relay , ! is_inbound } ) ;
max_peer_id_length = std : : max ( ToString ( peer_id ) . length ( ) , max_peer_id_length ) ;
max_addr_length = std : : max ( addr . length ( ) + 1 , max_addr_length ) ;
is_asmap_on | = ( mapped_as ! = 0 ) ;
const std : : string addr { peer [ " addr " ] . get_str ( ) } ;
const std : : string sub_version { peer [ " subver " ] . get_str ( ) } ;
m_peers . push_back ( { peer_id , mapped_as , version , conn_time , last_blck , last_recv , last_send , last_trxn , min_ping , ping , addr , network , sub_version , is_block_relay , is_outbound } ) ;
m_max_id_length = std : : max ( ToString ( peer_id ) . length ( ) , m_max_id_length ) ;
m_max_addr_length = std : : max ( addr . length ( ) + 1 , m_max_addr_length ) ;
m_is_asmap_on | = ( mapped_as ! = 0 ) ;
}
}
@ -446,30 +410,30 @@ public:
std : : string result { strprintf ( " %s %s%s - %i%s \n \n " , PACKAGE_NAME , FormatFullVersion ( ) , ChainToString ( ) , networkinfo [ " protocolversion " ] . get_int ( ) , networkinfo [ " subversion " ] . get_str ( ) ) } ;
// Report detailed peer connections list sorted by direction and minimum ping time.
if ( DetailsRequested ( ) & & ! peers. empty ( ) ) {
std : : sort ( peers. begin ( ) , peers. end ( ) ) ;
if ( DetailsRequested ( ) & & ! m_ peers. empty ( ) ) {
std : : sort ( m_ peers. begin ( ) , m_ peers. end ( ) ) ;
result + = " Peer connections sorted by direction and min ping \n <-> relay net mping ping send recv txn blk uptime " ;
if ( is_asmap_on) result + = " asmap " ;
result + = strprintf ( " %*s %-*s%s \n " , m ax_peer _id_length, " id " , IsAddressSelected ( ) ? max_addr_length : 0 , IsAddressSelected ( ) ? " address " : " " , IsVersionSelected ( ) ? " version " : " " ) ;
for ( const Peer & peer : peers) {
if ( m_ is_asmap_on) result + = " asmap " ;
result + = strprintf ( " %*s %-*s%s \n " , m _m ax_id_length, " id " , IsAddressSelected ( ) ? m_ max_addr_length : 0 , IsAddressSelected ( ) ? " address " : " " , IsVersionSelected ( ) ? " version " : " " ) ;
for ( const Peer & peer : m_ peers) {
std : : string version { ToString ( peer . version ) + peer . sub_version } ;
result + = strprintf (
" %3s %5s %5s%6s%7s%5s%5s%5s%5s%7s%*i %*s %-*s%s \n " ,
peer . is_outbound ? " out " : " in " ,
peer . is_block_relay ? " block " : " full " ,
NetTypeEnumToString( peer . net_type ) ,
peer. network ,
peer . min_ping = = - 1 ? " " : ToString ( round ( 1000 * peer . min_ping ) ) ,
peer . ping = = - 1 ? " " : ToString ( round ( 1000 * peer . ping ) ) ,
peer . last_send = = 0 ? " " : ToString ( time_now - peer . last_send ) ,
peer . last_recv = = 0 ? " " : ToString ( time_now - peer . last_recv ) ,
peer . last_trxn = = 0 ? " " : ToString ( ( time_now - peer . last_trxn ) / 60 ) ,
peer . last_blck = = 0 ? " " : ToString ( ( time_now - peer . last_blck ) / 60 ) ,
peer . conn_time = = 0 ? " " : ToString ( ( time_now - peer . conn_time ) / 60 ) ,
is_asmap_on ? 7 : 0 , // variable spacing
is_asmap_on & & peer . mapped_as ! = 0 ? ToString ( peer . mapped_as ) : " " ,
m ax_peer _id_length, // variable spacing
peer . last_send = = 0 ? " " : ToString ( m_ time_now - peer . last_send ) ,
peer . last_recv = = 0 ? " " : ToString ( m_ time_now - peer . last_recv ) ,
peer . last_trxn = = 0 ? " " : ToString ( ( m_ time_now - peer . last_trxn ) / 60 ) ,
peer . last_blck = = 0 ? " " : ToString ( ( m_ time_now - peer . last_blck ) / 60 ) ,
peer . conn_time = = 0 ? " " : ToString ( ( m_ time_now - peer . conn_time ) / 60 ) ,
m_ is_asmap_on ? 7 : 0 , // variable spacing
m_ is_asmap_on & & peer . mapped_as ! = 0 ? ToString ( peer . mapped_as ) : " " ,
m _m ax_id_length, // variable spacing
peer . id ,
IsAddressSelected ( ) ? m ax_addr_length : 0 , // variable spacing
IsAddressSelected ( ) ? m _m ax_addr_length : 0 , // variable spacing
IsAddressSelected ( ) ? peer . addr : " " ,
IsVersionSelected ( ) & & version ! = " 0 " ? version : " " ) ;
}
@ -477,12 +441,11 @@ public:
}
// Report peer connection totals by type.
total_i = ipv4_i + ipv6_i + onion_i ;
total_o = ipv4_o + ipv6_o + onion_o ;
result + = " ipv4 ipv6 onion total block-relay \n " ;
result + = strprintf ( " in %5i %5i %5i %5i %5i \n " , ipv4_i , ipv6_i , onion_i , total_i , block_relay_i ) ;
result + = strprintf ( " out %5i %5i %5i %5i %5i \n " , ipv4_o , ipv6_o , onion_o , total_o , block_relay_o ) ;
result + = strprintf ( " total %5i %5i %5i %5i %5i \n " , ipv4_i + ipv4_o , ipv6_i + ipv6_o , onion_i + onion_o , total_i + total_o , block_relay_i + block_relay_o ) ;
const std : : array < std : : string , 3 > rows { { " in " , " out " , " total " } } ;
for ( size_t i = 0 ; i < m_networks_size ; + + i ) {
result + = strprintf ( " %-5s %5i %5i %5i %5i %5i \n " , rows . at ( i ) , m_counts . at ( i ) . at ( 0 ) , m_counts . at ( i ) . at ( 1 ) , m_counts . at ( i ) . at ( 2 ) , m_counts . at ( i ) . at ( m_networks_size ) , m_counts . at ( i ) . at ( m_networks_size + 1 ) ) ;
}
// Report local addresses, ports, and scores.
result + = " \n Local addresses " ;