1 // Copyright (c) 2016 The Zcash developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 #include "chainparams.h"
8 #include "checkpoints.h"
10 #include "ui_interface.h"
13 #include "utilmoneystr.h"
14 #include "utilstrencodings.h"
16 #include <boost/thread.hpp>
17 #include <boost/thread/synchronized_value.hpp>
23 #include <sys/ioctl.h>
27 extern uint64_t ASSETCHAINS_TIMELOCKGTE;
28 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH;
29 int64_t komodo_block_unlocktime(uint32_t nHeight);
31 void AtomicTimer::start()
33 std::unique_lock<std::mutex> lock(mtx);
35 start_time = GetTime();
40 void AtomicTimer::stop()
42 std::unique_lock<std::mutex> lock(mtx);
43 // Ignore excess calls to stop()
47 int64_t time_span = GetTime() - start_time;
48 total_time += time_span;
53 bool AtomicTimer::running()
55 std::unique_lock<std::mutex> lock(mtx);
59 uint64_t AtomicTimer::threadCount()
61 std::unique_lock<std::mutex> lock(mtx);
65 double AtomicTimer::rate(const AtomicCounter& count)
67 std::unique_lock<std::mutex> lock(mtx);
68 int64_t duration = total_time;
70 // Timer is running, so get the latest count
71 duration += GetTime() - start_time;
73 return duration > 0 ? (double)count.get() / duration : 0;
76 boost::synchronized_value<int64_t> nNodeStartTime;
77 boost::synchronized_value<int64_t> nNextRefresh;
79 AtomicCounter transactionsValidated;
80 AtomicCounter ehSolverRuns;
81 AtomicCounter solutionTargetChecks;
82 static AtomicCounter minedBlocks;
83 AtomicTimer miningTimer;
84 CCriticalSection cs_metrics;
86 double AtomicTimer::rate(const int64_t count)
88 std::unique_lock<std::mutex> lock(mtx);
90 int64_t duration = total_time;
92 // Timer is running, so get the latest count
93 duration += GetTime() - start_time;
95 return duration > 0 ? (double)count / duration : 0;
98 static boost::synchronized_value<std::list<uint256>> trackedBlocks;
100 static boost::synchronized_value<std::list<std::string>> messageBox;
101 static boost::synchronized_value<std::string> initMessage;
102 static bool loaded = false;
104 extern int64_t GetNetworkHashPS(int lookup, int height);
106 void TrackMinedBlock(uint256 hash)
109 minedBlocks.increment();
110 trackedBlocks->push_back(hash);
115 *nNodeStartTime = GetTime();
120 return GetTime() - *nNodeStartTime;
123 double GetLocalSolPS()
125 if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
127 return miningTimer.rate(nHashCount);
130 return miningTimer.rate(solutionTargetChecks);
133 int EstimateNetHeightInner(int height, int64_t tipmediantime,
134 int heightLastCheckpoint, int64_t timeLastCheckpoint,
135 int64_t genesisTime, int64_t targetSpacing)
137 // We average the target spacing with the observed spacing to the last
138 // checkpoint (either from below or above depending on the current height),
139 // and use that to estimate the current network height.
140 int medianHeight = height > CBlockIndex::nMedianTimeSpan ?
141 height - (1 + ((CBlockIndex::nMedianTimeSpan - 1) / 2)) :
143 double checkpointSpacing = medianHeight > heightLastCheckpoint ?
144 (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint) :
145 (double (timeLastCheckpoint - genesisTime)) / heightLastCheckpoint;
146 double averageSpacing = (targetSpacing + checkpointSpacing) / 2;
147 int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing);
148 // Round to nearest ten to reduce noise
149 return ((netheight + 5) / 10) * 10;
152 int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams)
154 auto checkpointData = chainParams.Checkpoints();
155 return EstimateNetHeightInner(
156 height, tipmediantime,
157 Checkpoints::GetTotalBlocksEstimate(checkpointData),
158 checkpointData.nTimeLastCheckpoint,
159 chainParams.GenesisBlock().nTime,
160 chainParams.GetConsensus().nPowTargetSpacing);
163 void TriggerRefresh()
165 *nNextRefresh = GetTime();
166 // Ensure that the refresh has started before we return
170 static bool metrics_ThreadSafeMessageBox(const std::string& message,
171 const std::string& caption,
174 // The SECURE flag has no effect in the metrics UI.
175 style &= ~CClientUIInterface::SECURE;
177 std::string strCaption;
178 // Check for usage of predefined caption
180 case CClientUIInterface::MSG_ERROR:
181 strCaption += _("Error");
183 case CClientUIInterface::MSG_WARNING:
184 strCaption += _("Warning");
186 case CClientUIInterface::MSG_INFORMATION:
187 strCaption += _("Information");
190 strCaption += caption; // Use supplied caption (can be empty)
193 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
194 u->push_back(strCaption + ": " + message);
203 static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
205 return metrics_ThreadSafeMessageBox(message, caption, style);
208 static void metrics_InitMessage(const std::string& message)
210 *initMessage = message;
213 void ConnectMetricsScreen()
215 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
216 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
217 uiInterface.ThreadSafeQuestion.disconnect_all_slots();
218 uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
219 uiInterface.InitMessage.disconnect_all_slots();
220 uiInterface.InitMessage.connect(metrics_InitMessage);
223 int printStats(bool mining)
225 // Number of lines that are always displayed
229 int64_t tipmediantime;
233 LOCK2(cs_main, cs_vNodes);
234 height = chainActive.Height();
235 tipmediantime = chainActive.LastTip()->GetMedianTimePast();
236 connections = vNodes.size();
237 netsolps = GetNetworkHashPS(120, -1);
239 auto localsolps = GetLocalSolPS();
241 if (IsInitialBlockDownload()) {
242 int netheight = EstimateNetHeight(height, tipmediantime, Params());
243 int downloadPercent = height * 100 / netheight;
244 std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl;
246 std::cout << " " << _("Block height") << " | " << height << std::endl;
248 std::cout << " " << _("Connections") << " | " << connections << std::endl;
249 std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl;
250 if (mining && miningTimer.running()) {
251 std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl;
254 std::cout << std::endl;
259 int printMiningStatus(bool mining)
262 // Number of lines that are always displayed
266 auto nThreads = miningTimer.threadCount();
268 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
269 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
274 fvNodesEmpty = vNodes.empty();
277 std::cout << _("Mining is paused while waiting for connections.") << std::endl;
278 } else if (IsInitialBlockDownload()) {
279 std::cout << _("Mining is paused while downloading blocks.") << std::endl;
281 std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
286 std::cout << _("You are currently not mining.") << std::endl;
287 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
290 std::cout << std::endl;
293 #else // ENABLE_MINING
295 #endif // !ENABLE_MINING
298 int printMetrics(size_t cols, bool mining)
300 // Number of lines that are always displayed
304 int64_t uptime = GetUptime();
305 int days = uptime / (24 * 60 * 60);
306 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
307 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
308 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
311 std::string duration;
313 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
314 } else if (hours > 0) {
315 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
316 } else if (minutes > 0) {
317 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
319 duration = strprintf(_("%d seconds"), seconds);
321 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
322 std::cout << strDuration << std::endl;
323 lines += (strDuration.size() / cols);
325 int validatedCount = transactionsValidated.get();
326 if (validatedCount > 1) {
327 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
328 } else if (validatedCount == 1) {
329 std::cout << "- " << _("You have validated a transaction!") << std::endl;
331 std::cout << "- " << _("You have validated no transactions.") << std::endl;
334 if (mining && loaded) {
335 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
340 CAmount immature {0};
343 LOCK2(cs_main, cs_metrics);
344 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
345 auto consensusParams = Params().GetConsensus();
346 auto tipHeight = chainActive.Height();
348 // Update orphans and calculate subsidies
349 std::list<uint256>::iterator it = u->begin();
350 while (it != u->end()) {
352 if (mapBlockIndex.count(hash) > 0 &&
353 chainActive.Contains(mapBlockIndex[hash])) {
354 int height = mapBlockIndex[hash]->GetHeight();
355 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
356 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
357 subsidy -= subsidy/5;
360 if ((std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) ||
361 (tipHeight < komodo_block_unlocktime(height) && subsidy >= ASSETCHAINS_TIMELOCKGTE)) {
372 mined = minedBlocks.get();
373 orphaned = mined - u->size();
377 std::string units = Params().CurrencyUnits();
378 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
380 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
382 FormatMoney(immature), units,
383 FormatMoney(mature), units)
388 std::cout << std::endl;
393 int printMessageBox(size_t cols)
395 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
397 if (u->size() == 0) {
401 int lines = 2 + u->size();
402 std::cout << _("Messages:") << std::endl;
403 for (auto it = u->cbegin(); it != u->cend(); ++it) {
404 auto msg = FormatParagraph(*it, cols, 2);
405 std::cout << "- " << msg << std::endl;
406 // Handle newlines and wrapped lines
409 while (j < msg.size()) {
410 i = msg.find('\n', j);
411 if (i == std::string::npos) {
420 std::cout << std::endl;
424 int printInitMessage()
430 std::string msg = *initMessage;
431 std::cout << _("Init message:") << " " << msg << std::endl;
432 std::cout << std::endl;
434 if (msg == _("Done loading")) {
442 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
446 // Set output mode to handle virtual terminal sequences
447 HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
448 if (hOut == INVALID_HANDLE_VALUE) {
453 if (!GetConsoleMode(hOut, &dwMode)) {
457 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
458 if (!SetConsoleMode(hOut, dwMode)) {
465 void ThreadShowMetricsScreen()
467 // Make this thread recognisable as the metrics screen thread
468 RenameThread("zcash-metrics-screen");
470 // Determine whether we should render a persistent UI or rolling metrics
471 bool isTTY = isatty(STDOUT_FILENO);
472 bool isScreen = GetBoolArg("-metricsui", isTTY);
473 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
481 std::cout << "\e[2J";
484 std::cout << METRICS_ART << std::endl;
485 std::cout << std::endl;
488 std::cout << _("Thank you for running a Zcash node!") << std::endl;
489 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
491 // Privacy notice text
492 std::cout << PrivacyInfo();
493 std::cout << std::endl;
497 // Number of lines that are always displayed
501 // Get current window size
504 CONSOLE_SCREEN_BUFFER_INFO csbi;
505 if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) != 0) {
506 cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
511 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
518 // Erase below current position
524 bool mining = GetBoolArg("-gen", false);
530 lines += printStats(mining);
531 lines += printMiningStatus(mining);
533 lines += printMetrics(cols, mining);
534 lines += printMessageBox(cols);
535 lines += printInitMessage();
538 // Explain how to exit
541 std::cout << _("'zcash-cli.exe stop' to exit");
543 std::cout << _("Press Ctrl+C to exit");
545 std::cout << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
548 std::cout << "----------------------------------------" << std::endl;
551 *nNextRefresh = GetTime() + nRefresh;
552 while (GetTime() < *nNextRefresh) {
553 boost::this_thread::interruption_point();
558 // Return to the top of the updating section
559 std::cout << "\e[" << lines << "A";