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>
19 #include <sys/ioctl.h>
22 void AtomicTimer::start()
24 std::unique_lock<std::mutex> lock(mtx);
26 start_time = GetTime();
31 void AtomicTimer::stop()
33 std::unique_lock<std::mutex> lock(mtx);
34 // Ignore excess calls to stop()
38 int64_t time_span = GetTime() - start_time;
39 total_time += time_span;
44 bool AtomicTimer::running()
46 std::unique_lock<std::mutex> lock(mtx);
50 uint64_t AtomicTimer::threadCount()
52 std::unique_lock<std::mutex> lock(mtx);
56 double AtomicTimer::rate(const AtomicCounter& count)
58 std::unique_lock<std::mutex> lock(mtx);
59 int64_t duration = total_time;
61 // Timer is running, so get the latest count
62 duration += GetTime() - start_time;
64 return duration > 0 ? (double)count.get() / duration : 0;
67 CCriticalSection cs_metrics;
69 boost::synchronized_value<int64_t> nNodeStartTime;
70 boost::synchronized_value<int64_t> nNextRefresh;
71 AtomicCounter transactionsValidated;
72 AtomicCounter ehSolverRuns;
73 AtomicCounter solutionTargetChecks;
74 AtomicCounter minedBlocks;
75 AtomicTimer miningTimer;
77 boost::synchronized_value<std::list<uint256>> trackedBlocks;
79 boost::synchronized_value<std::list<std::string>> messageBox;
80 boost::synchronized_value<std::string> initMessage;
83 extern int64_t GetNetworkHashPS(int lookup, int height);
85 void TrackMinedBlock(uint256 hash)
88 minedBlocks.increment();
89 trackedBlocks->push_back(hash);
94 *nNodeStartTime = GetTime();
99 return GetTime() - *nNodeStartTime;
102 double GetLocalSolPS()
104 return miningTimer.rate(solutionTargetChecks);
107 int EstimateNetHeightInner(int height, int64_t tipmediantime,
108 int heightLastCheckpoint, int64_t timeLastCheckpoint,
109 int64_t genesisTime, int64_t targetSpacing)
111 // We average the target spacing with the observed spacing to the last
112 // checkpoint (either from below or above depending on the current height),
113 // and use that to estimate the current network height.
114 int medianHeight = height > CBlockIndex::nMedianTimeSpan ?
115 height - (1 + ((CBlockIndex::nMedianTimeSpan - 1) / 2)) :
117 double checkpointSpacing = medianHeight > heightLastCheckpoint ?
118 (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint) :
119 (double (timeLastCheckpoint - genesisTime)) / heightLastCheckpoint;
120 double averageSpacing = (targetSpacing + checkpointSpacing) / 2;
121 int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing);
122 // Round to nearest ten to reduce noise
123 return ((netheight + 5) / 10) * 10;
126 int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams)
128 auto checkpointData = chainParams.Checkpoints();
129 return EstimateNetHeightInner(
130 height, tipmediantime,
131 Checkpoints::GetTotalBlocksEstimate(checkpointData),
132 checkpointData.nTimeLastCheckpoint,
133 chainParams.GenesisBlock().nTime,
134 chainParams.GetConsensus().nPowTargetSpacing);
137 void TriggerRefresh()
139 *nNextRefresh = GetTime();
140 // Ensure that the refresh has started before we return
144 static bool metrics_ThreadSafeMessageBox(const std::string& message,
145 const std::string& caption,
148 // The SECURE flag has no effect in the metrics UI.
149 style &= ~CClientUIInterface::SECURE;
151 std::string strCaption;
152 // Check for usage of predefined caption
154 case CClientUIInterface::MSG_ERROR:
155 strCaption += _("Error");
157 case CClientUIInterface::MSG_WARNING:
158 strCaption += _("Warning");
160 case CClientUIInterface::MSG_INFORMATION:
161 strCaption += _("Information");
164 strCaption += caption; // Use supplied caption (can be empty)
167 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
168 u->push_back(strCaption + ": " + message);
177 static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
179 return metrics_ThreadSafeMessageBox(message, caption, style);
182 static void metrics_InitMessage(const std::string& message)
184 *initMessage = message;
187 void ConnectMetricsScreen()
189 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
190 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
191 uiInterface.ThreadSafeQuestion.disconnect_all_slots();
192 uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
193 uiInterface.InitMessage.disconnect_all_slots();
194 uiInterface.InitMessage.connect(metrics_InitMessage);
197 int printStats(bool mining)
199 // Number of lines that are always displayed
203 int64_t tipmediantime;
207 LOCK2(cs_main, cs_vNodes);
208 height = chainActive.Height();
209 tipmediantime = chainActive.Tip()->GetMedianTimePast();
210 connections = vNodes.size();
211 netsolps = GetNetworkHashPS(120, -1);
213 auto localsolps = GetLocalSolPS();
215 if (IsInitialBlockDownload()) {
216 int netheight = EstimateNetHeight(height, tipmediantime, Params());
217 int downloadPercent = height * 100 / netheight;
218 std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl;
220 std::cout << " " << _("Block height") << " | " << height << std::endl;
222 std::cout << " " << _("Connections") << " | " << connections << std::endl;
223 std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl;
224 if (mining && miningTimer.running()) {
225 std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl;
228 std::cout << std::endl;
233 int printMiningStatus(bool mining)
236 // Number of lines that are always displayed
240 auto nThreads = miningTimer.threadCount();
242 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
243 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
248 fvNodesEmpty = vNodes.empty();
251 std::cout << _("Mining is paused while waiting for connections.") << std::endl;
252 } else if (IsInitialBlockDownload()) {
253 std::cout << _("Mining is paused while downloading blocks.") << std::endl;
255 std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
260 std::cout << _("You are currently not mining.") << std::endl;
261 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
264 std::cout << std::endl;
267 #else // ENABLE_MINING
269 #endif // !ENABLE_MINING
272 int printMetrics(size_t cols, bool mining)
274 // Number of lines that are always displayed
278 int64_t uptime = GetUptime();
279 int days = uptime / (24 * 60 * 60);
280 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
281 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
282 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
285 std::string duration;
287 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
288 } else if (hours > 0) {
289 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
290 } else if (minutes > 0) {
291 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
293 duration = strprintf(_("%d seconds"), seconds);
295 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
296 std::cout << strDuration << std::endl;
297 lines += (strDuration.size() / cols);
299 int validatedCount = transactionsValidated.get();
300 if (validatedCount > 1) {
301 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
302 } else if (validatedCount == 1) {
303 std::cout << "- " << _("You have validated a transaction!") << std::endl;
305 std::cout << "- " << _("You have validated no transactions.") << std::endl;
308 if (mining && loaded) {
309 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
314 CAmount immature {0};
317 LOCK2(cs_main, cs_metrics);
318 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
319 auto consensusParams = Params().GetConsensus();
320 auto tipHeight = chainActive.Height();
322 // Update orphans and calculate subsidies
323 std::list<uint256>::iterator it = u->begin();
324 while (it != u->end()) {
326 if (mapBlockIndex.count(hash) > 0 &&
327 chainActive.Contains(mapBlockIndex[hash])) {
328 int height = mapBlockIndex[hash]->nHeight;
329 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
330 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
331 subsidy -= subsidy/5;
333 if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
344 mined = minedBlocks.get();
345 orphaned = mined - u->size();
349 std::string units = Params().CurrencyUnits();
350 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
352 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
354 FormatMoney(immature), units,
355 FormatMoney(mature), units)
360 std::cout << std::endl;
365 int printMessageBox(size_t cols)
367 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
369 if (u->size() == 0) {
373 int lines = 2 + u->size();
374 std::cout << _("Messages:") << std::endl;
375 for (auto it = u->cbegin(); it != u->cend(); ++it) {
376 auto msg = FormatParagraph(*it, cols, 2);
377 std::cout << "- " << msg << std::endl;
378 // Handle newlines and wrapped lines
381 while (j < msg.size()) {
382 i = msg.find('\n', j);
383 if (i == std::string::npos) {
392 std::cout << std::endl;
396 int printInitMessage()
402 std::string msg = *initMessage;
403 std::cout << _("Init message:") << " " << msg << std::endl;
404 std::cout << std::endl;
406 if (msg == _("Done loading")) {
413 void ThreadShowMetricsScreen()
415 // Make this thread recognisable as the metrics screen thread
416 RenameThread("zcash-metrics-screen");
418 // Determine whether we should render a persistent UI or rolling metrics
419 bool isTTY = isatty(STDOUT_FILENO);
420 bool isScreen = GetBoolArg("-metricsui", isTTY);
421 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
425 std::cout << "\e[2J";
428 std::cout << METRICS_ART << std::endl;
429 std::cout << std::endl;
432 std::cout << _("Thank you for running a Zcash node!") << std::endl;
433 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
435 // Privacy notice text
436 std::cout << PrivacyInfo();
437 std::cout << std::endl;
441 // Number of lines that are always displayed
445 // Get current window size
449 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
455 // Erase below current position
461 bool mining = GetBoolArg("-gen", false);
467 lines += printStats(mining);
468 lines += printMiningStatus(mining);
470 lines += printMetrics(cols, mining);
471 lines += printMessageBox(cols);
472 lines += printInitMessage();
475 // Explain how to exit
476 std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
479 std::cout << "----------------------------------------" << std::endl;
482 *nNextRefresh = GetTime() + nRefresh;
483 while (GetTime() < *nNextRefresh) {
484 boost::this_thread::interruption_point();
489 // Return to the top of the updating section
490 std::cout << "\e[" << lines << "A";