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"
9 #include "ui_interface.h"
12 #include "utilmoneystr.h"
13 #include "utilstrencodings.h"
15 #include <boost/thread.hpp>
16 #include <boost/thread/synchronized_value.hpp>
18 #include <sys/ioctl.h>
21 void AtomicTimer::start()
23 std::unique_lock<std::mutex> lock(mtx);
25 start_time = GetTime();
30 void AtomicTimer::stop()
32 std::unique_lock<std::mutex> lock(mtx);
33 // Ignore excess calls to stop()
37 int64_t time_span = GetTime() - start_time;
38 total_time += time_span;
43 bool AtomicTimer::running()
45 std::unique_lock<std::mutex> lock(mtx);
49 double AtomicTimer::rate(const AtomicCounter& count)
51 std::unique_lock<std::mutex> lock(mtx);
52 int64_t duration = total_time;
54 // Timer is running, so get the latest count
55 duration += GetTime() - start_time;
57 return duration > 0 ? (double)count.get() / duration : 0;
60 CCriticalSection cs_metrics;
62 boost::synchronized_value<int64_t> nNodeStartTime;
63 boost::synchronized_value<int64_t> nNextRefresh;
64 AtomicCounter transactionsValidated;
65 AtomicCounter ehSolverRuns;
66 AtomicCounter solutionTargetChecks;
67 AtomicCounter minedBlocks;
68 AtomicTimer miningTimer;
70 boost::synchronized_value<std::list<uint256>> trackedBlocks;
72 boost::synchronized_value<std::list<std::string>> messageBox;
73 boost::synchronized_value<std::string> initMessage;
76 extern int64_t GetNetworkHashPS(int lookup, int height);
78 void TrackMinedBlock(uint256 hash)
81 minedBlocks.increment();
82 trackedBlocks->push_back(hash);
87 *nNodeStartTime = GetTime();
92 return GetTime() - *nNodeStartTime;
95 double GetLocalSolPS()
97 return miningTimer.rate(solutionTargetChecks);
100 void TriggerRefresh()
102 *nNextRefresh = GetTime();
103 // Ensure that the refresh has started before we return
107 static bool metrics_ThreadSafeMessageBox(const std::string& message,
108 const std::string& caption,
111 // The SECURE flag has no effect in the metrics UI.
112 style &= ~CClientUIInterface::SECURE;
114 std::string strCaption;
115 // Check for usage of predefined caption
117 case CClientUIInterface::MSG_ERROR:
118 strCaption += _("Error");
120 case CClientUIInterface::MSG_WARNING:
121 strCaption += _("Warning");
123 case CClientUIInterface::MSG_INFORMATION:
124 strCaption += _("Information");
127 strCaption += caption; // Use supplied caption (can be empty)
130 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
131 u->push_back(strCaption + ": " + message);
140 static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
142 return metrics_ThreadSafeMessageBox(message, caption, style);
145 static void metrics_InitMessage(const std::string& message)
147 *initMessage = message;
150 void ConnectMetricsScreen()
152 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
153 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
154 uiInterface.ThreadSafeQuestion.disconnect_all_slots();
155 uiInterface.ThreadSafeQuestion.connect(metrics_ThreadSafeQuestion);
156 uiInterface.InitMessage.disconnect_all_slots();
157 uiInterface.InitMessage.connect(metrics_InitMessage);
160 int printStats(bool mining)
162 // Number of lines that are always displayed
169 LOCK2(cs_main, cs_vNodes);
170 height = chainActive.Height();
171 connections = vNodes.size();
172 netsolps = GetNetworkHashPS(120, -1);
174 auto localsolps = GetLocalSolPS();
176 std::cout << " " << _("Block height") << " | " << height << std::endl;
177 std::cout << " " << _("Connections") << " | " << connections << std::endl;
178 std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl;
179 if (mining && miningTimer.running()) {
180 std::cout << " " << _("Local solution rate") << " | " << strprintf("%.4f Sol/s", localsolps) << std::endl;
183 std::cout << std::endl;
188 int printMiningStatus(bool mining)
191 // Number of lines that are always displayed
195 int nThreads = GetArg("-genproclimit", 1);
197 // In regtest threads defaults to 1
198 if (Params().DefaultMinerThreads())
199 nThreads = Params().DefaultMinerThreads();
201 nThreads = boost::thread::hardware_concurrency();
203 if (miningTimer.running()) {
204 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
205 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
210 fvNodesEmpty = vNodes.empty();
213 std::cout << _("Mining is paused while waiting for connections.") << std::endl;
214 } else if (IsInitialBlockDownload()) {
215 std::cout << _("Mining is paused while downloading blocks.") << std::endl;
217 std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
222 std::cout << _("You are currently not mining.") << std::endl;
223 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
226 std::cout << std::endl;
229 #else // ENABLE_MINING
231 #endif // !ENABLE_MINING
234 int printMetrics(size_t cols, bool mining)
236 // Number of lines that are always displayed
240 int64_t uptime = GetUptime();
241 int days = uptime / (24 * 60 * 60);
242 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
243 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
244 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
247 std::string duration;
249 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
250 } else if (hours > 0) {
251 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
252 } else if (minutes > 0) {
253 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
255 duration = strprintf(_("%d seconds"), seconds);
257 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
258 std::cout << strDuration << std::endl;
259 lines += (strDuration.size() / cols);
261 int validatedCount = transactionsValidated.get();
262 if (validatedCount > 1) {
263 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
264 } else if (validatedCount == 1) {
265 std::cout << "- " << _("You have validated a transaction!") << std::endl;
267 std::cout << "- " << _("You have validated no transactions.") << std::endl;
270 if (mining && loaded) {
271 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
276 CAmount immature {0};
279 LOCK2(cs_main, cs_metrics);
280 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
281 auto consensusParams = Params().GetConsensus();
282 auto tipHeight = chainActive.Height();
284 // Update orphans and calculate subsidies
285 std::list<uint256>::iterator it = u->begin();
286 while (it != u->end()) {
288 if (mapBlockIndex.count(hash) > 0 &&
289 chainActive.Contains(mapBlockIndex[hash])) {
290 int height = mapBlockIndex[hash]->nHeight;
291 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
292 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
293 subsidy -= subsidy/5;
295 if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
306 mined = minedBlocks.get();
307 orphaned = mined - u->size();
311 std::string units = Params().CurrencyUnits();
312 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
314 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
316 FormatMoney(immature), units,
317 FormatMoney(mature), units)
322 std::cout << std::endl;
327 int printMessageBox(size_t cols)
329 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
331 if (u->size() == 0) {
335 int lines = 2 + u->size();
336 std::cout << _("Messages:") << std::endl;
337 for (auto it = u->cbegin(); it != u->cend(); ++it) {
338 auto msg = FormatParagraph(*it, cols, 2);
339 std::cout << "- " << msg << std::endl;
340 // Handle newlines and wrapped lines
343 while (j < msg.size()) {
344 i = msg.find('\n', j);
345 if (i == std::string::npos) {
354 std::cout << std::endl;
358 int printInitMessage()
364 std::string msg = *initMessage;
365 std::cout << _("Init message:") << " " << msg << std::endl;
366 std::cout << std::endl;
368 if (msg == _("Done loading")) {
375 void ThreadShowMetricsScreen()
377 // Make this thread recognisable as the metrics screen thread
378 RenameThread("zcash-metrics-screen");
380 // Determine whether we should render a persistent UI or rolling metrics
381 bool isTTY = isatty(STDOUT_FILENO);
382 bool isScreen = GetBoolArg("-metricsui", isTTY);
383 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
387 std::cout << "\e[2J";
390 std::cout << METRICS_ART << std::endl;
391 std::cout << std::endl;
394 std::cout << _("Thank you for running a Zcash node!") << std::endl;
395 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
397 // Privacy notice text
398 std::cout << PrivacyInfo();
399 std::cout << std::endl;
403 // Number of lines that are always displayed
407 // Get current window size
411 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
417 // Erase below current position
423 bool mining = GetBoolArg("-gen", false);
429 lines += printStats(mining);
430 lines += printMiningStatus(mining);
432 lines += printMetrics(cols, mining);
433 lines += printMessageBox(cols);
434 lines += printInitMessage();
437 // Explain how to exit
438 std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
441 std::cout << "----------------------------------------" << std::endl;
444 *nNextRefresh = GetTime() + nRefresh;
445 while (GetTime() < *nNextRefresh) {
446 boost::this_thread::interruption_point();
451 // Return to the top of the updating section
452 std::cout << "\e[" << lines << "A";