2016-09-03 09:39:45 -07:00
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include "metrics.h"
# include "chainparams.h"
2016-10-30 14:36:36 -07:00
# include "main.h"
2016-09-03 10:51:54 -07:00
# include "ui_interface.h"
2016-09-03 09:39:45 -07:00
# include "util.h"
# include "utiltime.h"
2016-10-30 14:36:36 -07:00
# include "utilmoneystr.h"
2016-09-03 09:39:45 -07:00
# include <boost/thread.hpp>
2016-09-03 10:51:54 -07:00
# include <boost/thread/synchronized_value.hpp>
# include <string>
2016-09-03 20:40:28 -07:00
# include <sys/ioctl.h>
2016-10-23 19:23:56 -07:00
# include <unistd.h>
2016-09-03 09:39:45 -07:00
2016-11-01 09:59:40 -07:00
CCriticalSection cs_metrics ;
2016-10-25 13:11:41 -07:00
boost : : synchronized_value < int64_t > nNodeStartTime ;
2017-01-03 01:21:00 -08:00
boost : : synchronized_value < int64_t > nNextRefresh ;
2016-09-03 09:39:45 -07:00
AtomicCounter transactionsValidated ;
AtomicCounter ehSolverRuns ;
2016-09-04 15:54:15 -07:00
AtomicCounter solutionTargetChecks ;
2016-09-03 09:39:45 -07:00
AtomicCounter minedBlocks ;
2016-10-30 14:36:36 -07:00
boost : : synchronized_value < std : : list < uint256 > > trackedBlocks ;
2016-09-03 10:51:54 -07:00
boost : : synchronized_value < std : : list < std : : string > > messageBox ;
boost : : synchronized_value < std : : string > initMessage ;
bool loaded = false ;
2016-10-30 15:25:40 -07:00
extern int64_t GetNetworkHashPS ( int lookup , int height ) ;
2016-10-30 14:36:36 -07:00
void TrackMinedBlock ( uint256 hash )
{
2016-11-01 09:59:40 -07:00
LOCK ( cs_metrics ) ;
2016-10-30 14:36:36 -07:00
minedBlocks . increment ( ) ;
trackedBlocks - > push_back ( hash ) ;
}
2016-10-25 13:11:41 -07:00
void MarkStartTime ( )
{
* nNodeStartTime = GetTime ( ) ;
}
int64_t GetUptime ( )
{
return GetTime ( ) - * nNodeStartTime ;
}
double GetLocalSolPS_INTERNAL ( int64_t uptime )
{
return uptime > 0 ? ( double ) solutionTargetChecks . get ( ) / uptime : 0 ;
}
double GetLocalSolPS ( )
{
return GetLocalSolPS_INTERNAL ( GetUptime ( ) ) ;
}
2017-01-03 01:21:00 -08:00
void TriggerRefresh ( )
{
* nNextRefresh = GetTime ( ) ;
// Ensure that the refresh has started before we return
MilliSleep ( 200 ) ;
}
2016-09-03 10:51:54 -07:00
static bool metrics_ThreadSafeMessageBox ( const std : : string & message ,
const std : : string & caption ,
unsigned int style )
{
2017-01-03 01:26:49 -08:00
// The SECURE flag has no effect in the metrics UI.
style & = ~ CClientUIInterface : : SECURE ;
2016-09-03 10:51:54 -07:00
std : : string strCaption ;
// Check for usage of predefined caption
switch ( style ) {
case CClientUIInterface : : MSG_ERROR :
strCaption + = _ ( " Error " ) ;
break ;
case CClientUIInterface : : MSG_WARNING :
strCaption + = _ ( " Warning " ) ;
break ;
case CClientUIInterface : : MSG_INFORMATION :
strCaption + = _ ( " Information " ) ;
break ;
default :
strCaption + = caption ; // Use supplied caption (can be empty)
}
boost : : strict_lock_ptr < std : : list < std : : string > > u = messageBox . synchronize ( ) ;
u - > push_back ( strCaption + " : " + message ) ;
if ( u - > size ( ) > 5 ) {
u - > pop_back ( ) ;
}
2017-01-03 01:21:00 -08:00
TriggerRefresh ( ) ;
return false ;
2016-09-03 10:51:54 -07:00
}
2017-02-23 04:55:14 -08:00
static bool metrics_ThreadSafeQuestion ( const std : : string & /* ignored interactive message */ , const std : : string & message , const std : : string & caption , unsigned int style )
{
return metrics_ThreadSafeMessageBox ( message , caption , style ) ;
}
2016-09-03 10:51:54 -07:00
static void metrics_InitMessage ( const std : : string & message )
{
* initMessage = message ;
}
void ConnectMetricsScreen ( )
{
uiInterface . ThreadSafeMessageBox . disconnect_all_slots ( ) ;
uiInterface . ThreadSafeMessageBox . connect ( metrics_ThreadSafeMessageBox ) ;
2017-02-23 04:55:14 -08:00
uiInterface . ThreadSafeQuestion . disconnect_all_slots ( ) ;
uiInterface . ThreadSafeQuestion . connect ( metrics_ThreadSafeQuestion ) ;
2016-09-03 10:51:54 -07:00
uiInterface . InitMessage . disconnect_all_slots ( ) ;
uiInterface . InitMessage . connect ( metrics_InitMessage ) ;
}
2016-10-30 15:25:40 -07:00
int printNetworkStats ( )
{
LOCK2 ( cs_main , cs_vNodes ) ;
std : : cout < < " " < < _ ( " Block height " ) < < " | " < < chainActive . Height ( ) < < std : : endl ;
std : : cout < < " " < < _ ( " Network solution rate " ) < < " | " < < GetNetworkHashPS ( 120 , - 1 ) < < " Sol/s " < < std : : endl ;
std : : cout < < " " < < _ ( " Connections " ) < < " | " < < vNodes . size ( ) < < std : : endl ;
std : : cout < < std : : endl ;
return 4 ;
}
2016-10-30 14:37:20 -07:00
int printMiningStatus ( bool mining )
2016-09-03 20:18:44 -07:00
{
2016-11-06 11:40:34 -08:00
# ifdef ENABLE_MINING
2016-10-30 14:37:20 -07:00
// Number of lines that are always displayed
int lines = 1 ;
2016-09-03 20:18:44 -07:00
if ( mining ) {
int nThreads = GetArg ( " -genproclimit " , 1 ) ;
if ( nThreads < 0 ) {
// In regtest threads defaults to 1
if ( Params ( ) . DefaultMinerThreads ( ) )
nThreads = Params ( ) . DefaultMinerThreads ( ) ;
else
nThreads = boost : : thread : : hardware_concurrency ( ) ;
}
2016-10-30 15:25:40 -07:00
std : : cout < < strprintf ( _ ( " You are mining with the %s solver on %d threads. " ) ,
GetArg ( " -equihashsolver " , " default " ) , nThreads ) < < std : : endl ;
2016-10-30 14:37:20 -07:00
lines + + ;
2016-09-03 20:18:44 -07:00
} else {
2016-09-03 20:31:50 -07:00
std : : cout < < _ ( " You are currently not mining. " ) < < std : : endl ;
std : : cout < < _ ( " To enable mining, add 'gen=1' to your zcash.conf and restart. " ) < < std : : endl ;
2016-10-30 14:37:20 -07:00
lines + = 2 ;
2016-09-03 20:18:44 -07:00
}
std : : cout < < std : : endl ;
2016-10-30 14:37:20 -07:00
return lines ;
2016-11-06 11:40:34 -08:00
# else // ENABLE_MINING
return 0 ;
# endif // !ENABLE_MINING
2016-09-03 20:18:44 -07:00
}
2016-10-25 13:11:41 -07:00
int printMetrics ( size_t cols , bool mining )
2016-09-03 20:18:44 -07:00
{
// Number of lines that are always displayed
int lines = 3 ;
// Calculate uptime
2016-10-25 13:11:41 -07:00
int64_t uptime = GetUptime ( ) ;
2016-09-03 20:18:44 -07:00
int days = uptime / ( 24 * 60 * 60 ) ;
int hours = ( uptime - ( days * 24 * 60 * 60 ) ) / ( 60 * 60 ) ;
int minutes = ( uptime - ( ( ( days * 24 ) + hours ) * 60 * 60 ) ) / 60 ;
int seconds = uptime - ( ( ( ( ( days * 24 ) + hours ) * 60 ) + minutes ) * 60 ) ;
// Display uptime
2016-09-03 20:31:50 -07:00
std : : string duration ;
2016-09-03 20:18:44 -07:00
if ( days > 0 ) {
2016-09-03 20:31:50 -07:00
duration = strprintf ( _ ( " %d days, %d hours, %d minutes, %d seconds " ) , days , hours , minutes , seconds ) ;
} else if ( hours > 0 ) {
duration = strprintf ( _ ( " %d hours, %d minutes, %d seconds " ) , hours , minutes , seconds ) ;
} else if ( minutes > 0 ) {
duration = strprintf ( _ ( " %d minutes, %d seconds " ) , minutes , seconds ) ;
} else {
duration = strprintf ( _ ( " %d seconds " ) , seconds ) ;
2016-09-03 20:18:44 -07:00
}
2016-09-03 20:40:28 -07:00
std : : string strDuration = strprintf ( _ ( " Since starting this node %s ago: " ) , duration ) ;
std : : cout < < strDuration < < std : : endl ;
lines + = ( strDuration . size ( ) / cols ) ;
2016-09-03 20:18:44 -07:00
2016-11-30 18:34:57 -08:00
int validatedCount = transactionsValidated . get ( ) ;
2016-10-28 10:14:13 -07:00
if ( validatedCount > 1 ) {
std : : cout < < " - " < < strprintf ( _ ( " You have validated %d transactions! " ) , validatedCount ) < < std : : endl ;
} else if ( validatedCount = = 1 ) {
2016-11-30 18:34:57 -08:00
std : : cout < < " - " < < _ ( " You have validated a transaction! " ) < < std : : endl ;
2016-10-28 10:14:13 -07:00
} else {
2016-11-30 18:34:57 -08:00
std : : cout < < " - " < < _ ( " You have validated no transactions. " ) < < std : : endl ;
2016-10-28 10:14:13 -07:00
}
2016-09-03 20:18:44 -07:00
2016-10-30 16:54:05 -07:00
if ( mining & & loaded ) {
2016-10-25 13:11:41 -07:00
double solps = GetLocalSolPS_INTERNAL ( uptime ) ;
2016-10-05 16:45:00 -07:00
std : : string strSolps = strprintf ( " %.4f Sol/s " , solps ) ;
std : : cout < < " - " < < strprintf ( _ ( " You have contributed %s on average to the network solution rate. " ) , strSolps ) < < std : : endl ;
2016-09-03 20:31:50 -07:00
std : : cout < < " - " < < strprintf ( _ ( " You have completed %d Equihash solver runs. " ) , ehSolverRuns . get ( ) ) < < std : : endl ;
2016-09-04 15:54:15 -07:00
lines + = 2 ;
2016-09-03 20:18:44 -07:00
2016-11-01 09:59:40 -07:00
int mined = 0 ;
int orphaned = 0 ;
2016-11-01 18:37:59 -07:00
CAmount immature { 0 } ;
CAmount mature { 0 } ;
2016-11-01 09:59:40 -07:00
{
LOCK2 ( cs_main , cs_metrics ) ;
2016-10-30 14:36:36 -07:00
boost : : strict_lock_ptr < std : : list < uint256 > > u = trackedBlocks . synchronize ( ) ;
auto consensusParams = Params ( ) . GetConsensus ( ) ;
auto tipHeight = chainActive . Height ( ) ;
// Update orphans and calculate subsidies
2016-11-02 07:20:35 -07:00
std : : list < uint256 > : : iterator it = u - > begin ( ) ;
while ( it ! = u - > end ( ) ) {
2016-10-30 14:36:36 -07:00
auto hash = * it ;
if ( mapBlockIndex . count ( hash ) > 0 & &
chainActive . Contains ( mapBlockIndex [ hash ] ) ) {
int height = mapBlockIndex [ hash ] - > nHeight ;
CAmount subsidy = GetBlockSubsidy ( height , consensusParams ) ;
if ( ( height > 0 ) & & ( height < = consensusParams . GetLastFoundersRewardBlockHeight ( ) ) ) {
subsidy - = subsidy / 5 ;
}
if ( std : : max ( 0 , COINBASE_MATURITY - ( tipHeight - height ) ) > 0 ) {
immature + = subsidy ;
} else {
mature + = subsidy ;
}
2016-11-02 07:20:35 -07:00
it + + ;
2016-10-30 14:36:36 -07:00
} else {
it = u - > erase ( it ) ;
}
}
2016-11-01 09:59:40 -07:00
mined = minedBlocks . get ( ) ;
orphaned = mined - u - > size ( ) ;
}
if ( mined > 0 ) {
2016-11-01 18:37:59 -07:00
std : : string units = Params ( ) . CurrencyUnits ( ) ;
2016-09-03 20:31:50 -07:00
std : : cout < < " - " < < strprintf ( _ ( " You have mined %d blocks! " ) , mined ) < < std : : endl ;
2016-10-30 14:36:36 -07:00
std : : cout < < " "
< < strprintf ( _ ( " Orphaned: %d blocks, Immature: %u %s, Mature: %u %s " ) ,
orphaned ,
FormatMoney ( immature ) , units ,
FormatMoney ( mature ) , units )
< < std : : endl ;
lines + = 2 ;
2016-09-03 20:18:44 -07:00
}
}
std : : cout < < std : : endl ;
return lines ;
}
2016-09-03 20:40:28 -07:00
int printMessageBox ( size_t cols )
2016-09-03 10:51:54 -07:00
{
boost : : strict_lock_ptr < std : : list < std : : string > > u = messageBox . synchronize ( ) ;
if ( u - > size ( ) = = 0 ) {
return 0 ;
}
2016-09-03 20:40:28 -07:00
int lines = 2 + u - > size ( ) ;
2016-09-03 20:31:50 -07:00
std : : cout < < _ ( " Messages: " ) < < std : : endl ;
2016-09-03 10:51:54 -07:00
for ( auto it = u - > cbegin ( ) ; it ! = u - > cend ( ) ; + + it ) {
std : : cout < < * it < < std : : endl ;
2017-01-04 23:38:35 -08:00
// Handle newlines and wrapped lines
size_t i = 0 ;
size_t j = 0 ;
while ( j < it - > size ( ) ) {
i = it - > find ( ' \n ' , j ) ;
if ( i = = std : : string : : npos ) {
i = it - > size ( ) ;
} else {
// Newline
lines + + ;
}
// Wrapped lines
lines + = ( ( i - j ) / cols ) ;
j = i + 1 ;
}
2016-09-03 10:51:54 -07:00
}
2016-09-03 20:18:44 -07:00
std : : cout < < std : : endl ;
2016-09-03 20:40:28 -07:00
return lines ;
2016-09-03 10:51:54 -07:00
}
int printInitMessage ( )
{
if ( loaded ) {
return 0 ;
}
std : : string msg = * initMessage ;
2016-09-03 20:31:50 -07:00
std : : cout < < _ ( " Init message: " ) < < " " < < msg < < std : : endl ;
2016-09-03 20:18:44 -07:00
std : : cout < < std : : endl ;
2016-09-03 10:51:54 -07:00
2016-09-03 20:31:50 -07:00
if ( msg = = _ ( " Done loading " ) ) {
2016-09-03 10:51:54 -07:00
loaded = true ;
}
return 2 ;
}
2016-09-03 09:39:45 -07:00
void ThreadShowMetricsScreen ( )
{
// Make this thread recognisable as the metrics screen thread
RenameThread ( " zcash-metrics-screen " ) ;
2016-11-17 19:17:09 -08:00
// Determine whether we should render a persistent UI or rolling metrics
2016-11-17 19:32:21 -08:00
bool isTTY = isatty ( STDOUT_FILENO ) ;
bool isScreen = GetBoolArg ( " -metricsui " , isTTY ) ;
int64_t nRefresh = GetArg ( " -metricsrefreshtime " , isTTY ? 1 : 600 ) ;
2016-09-03 09:39:45 -07:00
2016-11-17 19:17:09 -08:00
if ( isScreen ) {
// Clear screen
std : : cout < < " \ e[2J " ;
2016-09-03 09:39:45 -07:00
2016-11-17 19:17:09 -08:00
// Print art
std : : cout < < METRICS_ART < < std : : endl ;
std : : cout < < std : : endl ;
// Thank you text
std : : cout < < _ ( " Thank you for running a Zcash node! " ) < < std : : endl ;
std : : cout < < _ ( " You're helping to strengthen the network and contributing to a social good :) " ) < < std : : endl ;
2017-03-23 18:13:37 -07:00
// Security warning text
std : : cout < < std : : endl ;
2017-03-24 13:48:27 -07:00
std : : cout < < strprintf ( _ ( " In order to ensure you are adequately protecting your privacy when using Zcash, please see %s " ) , " https://z.cash/support/security/index.html " ) < < std : : endl ;
2016-11-17 19:17:09 -08:00
std : : cout < < std : : endl ;
}
2016-09-03 09:39:45 -07:00
while ( true ) {
2016-09-03 10:51:54 -07:00
// Number of lines that are always displayed
2016-09-03 20:18:44 -07:00
int lines = 1 ;
2016-10-23 08:54:50 -07:00
int cols = 80 ;
2016-09-03 09:39:45 -07:00
2016-09-03 20:40:28 -07:00
// Get current window size
2016-11-17 19:32:21 -08:00
if ( isTTY ) {
2016-10-23 19:23:56 -07:00
struct winsize w ;
w . ws_col = 0 ;
if ( ioctl ( STDOUT_FILENO , TIOCGWINSZ , & w ) ! = - 1 & & w . ws_col ! = 0 ) {
cols = w . ws_col ;
}
2016-10-23 08:54:50 -07:00
}
2016-09-03 20:40:28 -07:00
2016-11-17 19:17:09 -08:00
if ( isScreen ) {
// Erase below current position
std : : cout < < " \ e[J " ;
}
2016-09-03 09:39:45 -07:00
2016-10-30 14:37:20 -07:00
// Miner status
2016-11-06 11:40:34 -08:00
# ifdef ENABLE_MINING
2016-10-30 14:37:20 -07:00
bool mining = GetBoolArg ( " -gen " , false ) ;
2016-11-06 11:40:34 -08:00
# else
bool mining = false ;
# endif
2016-10-30 14:37:20 -07:00
2016-10-30 15:25:40 -07:00
if ( loaded ) {
lines + = printNetworkStats ( ) ;
}
2016-10-30 14:37:20 -07:00
lines + = printMiningStatus ( mining ) ;
2016-10-25 13:11:41 -07:00
lines + = printMetrics ( cols , mining ) ;
2016-10-23 08:54:50 -07:00
lines + = printMessageBox ( cols ) ;
2016-09-03 10:51:54 -07:00
lines + = printInitMessage ( ) ;
2016-11-17 19:17:09 -08:00
if ( isScreen ) {
// Explain how to exit
std : : cout < < " [ " < < _ ( " Press Ctrl+C to exit " ) < < " ] [ " < < _ ( " Set 'showmetrics=0' to hide " ) < < " ] " < < std : : endl ;
} else {
// Print delineator
2016-11-28 21:17:28 -08:00
std : : cout < < " ---------------------------------------- " < < std : : endl ;
2016-11-17 19:17:09 -08:00
}
2016-09-03 09:39:45 -07:00
2017-01-03 01:21:00 -08:00
* nNextRefresh = GetTime ( ) + nRefresh ;
while ( GetTime ( ) < * nNextRefresh ) {
2016-11-17 19:32:21 -08:00
boost : : this_thread : : interruption_point ( ) ;
MilliSleep ( 200 ) ;
}
2016-09-03 09:39:45 -07:00
2016-11-17 19:17:09 -08:00
if ( isScreen ) {
// Return to the top of the updating section
std : : cout < < " \ e[ " < < lines < < " A " ;
}
2016-09-03 09:39:45 -07:00
}
}