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"
14 #include <boost/thread.hpp>
15 #include <boost/thread/synchronized_value.hpp>
17 #include <sys/ioctl.h>
20 CCriticalSection cs_metrics;
22 boost::synchronized_value<int64_t> nNodeStartTime;
23 boost::synchronized_value<int64_t> nNextRefresh;
24 AtomicCounter transactionsValidated;
25 AtomicCounter ehSolverRuns;
26 AtomicCounter solutionTargetChecks;
27 AtomicCounter minedBlocks;
29 boost::synchronized_value<std::list<uint256>> trackedBlocks;
31 boost::synchronized_value<std::list<std::string>> messageBox;
32 boost::synchronized_value<std::string> initMessage;
35 extern int64_t GetNetworkHashPS(int lookup, int height);
37 void TrackMinedBlock(uint256 hash)
40 minedBlocks.increment();
41 trackedBlocks->push_back(hash);
46 *nNodeStartTime = GetTime();
51 return GetTime() - *nNodeStartTime;
54 double GetLocalSolPS_INTERNAL(int64_t uptime)
56 return uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0;
59 double GetLocalSolPS()
61 return GetLocalSolPS_INTERNAL(GetUptime());
66 *nNextRefresh = GetTime();
67 // Ensure that the refresh has started before we return
71 static bool metrics_ThreadSafeMessageBox(const std::string& message,
72 const std::string& caption,
75 // The SECURE flag has no effect in the metrics UI.
76 style &= ~CClientUIInterface::SECURE;
78 std::string strCaption;
79 // Check for usage of predefined caption
81 case CClientUIInterface::MSG_ERROR:
82 strCaption += _("Error");
84 case CClientUIInterface::MSG_WARNING:
85 strCaption += _("Warning");
87 case CClientUIInterface::MSG_INFORMATION:
88 strCaption += _("Information");
91 strCaption += caption; // Use supplied caption (can be empty)
94 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
95 u->push_back(strCaption + ": " + message);
104 static void metrics_InitMessage(const std::string& message)
106 *initMessage = message;
109 void ConnectMetricsScreen()
111 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
112 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
113 uiInterface.InitMessage.disconnect_all_slots();
114 uiInterface.InitMessage.connect(metrics_InitMessage);
117 int printNetworkStats()
119 LOCK2(cs_main, cs_vNodes);
121 std::cout << " " << _("Block height") << " | " << chainActive.Height() << std::endl;
122 std::cout << " " << _("Network solution rate") << " | " << GetNetworkHashPS(120, -1) << " Sol/s" << std::endl;
123 std::cout << " " << _("Connections") << " | " << vNodes.size() << std::endl;
124 std::cout << std::endl;
129 int printMiningStatus(bool mining)
131 // Number of lines that are always displayed
135 int nThreads = GetArg("-genproclimit", 1);
137 // In regtest threads defaults to 1
138 if (Params().DefaultMinerThreads())
139 nThreads = Params().DefaultMinerThreads();
141 nThreads = boost::thread::hardware_concurrency();
143 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
144 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
147 std::cout << _("You are currently not mining.") << std::endl;
148 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
151 std::cout << std::endl;
156 int printMetrics(size_t cols, bool mining)
158 // Number of lines that are always displayed
162 int64_t uptime = GetUptime();
163 int days = uptime / (24 * 60 * 60);
164 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
165 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
166 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
169 std::string duration;
171 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
172 } else if (hours > 0) {
173 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
174 } else if (minutes > 0) {
175 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
177 duration = strprintf(_("%d seconds"), seconds);
179 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
180 std::cout << strDuration << std::endl;
181 lines += (strDuration.size() / cols);
183 int validatedCount = transactionsValidated.get();
184 if (validatedCount > 1) {
185 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
186 } else if (validatedCount == 1) {
187 std::cout << "- " << _("You have validated a transaction!") << std::endl;
189 std::cout << "- " << _("You have validated no transactions.") << std::endl;
192 if (mining && loaded) {
193 double solps = GetLocalSolPS_INTERNAL(uptime);
194 std::string strSolps = strprintf("%.4f Sol/s", solps);
195 std::cout << "- " << strprintf(_("You have contributed %s on average to the network solution rate."), strSolps) << std::endl;
196 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
201 CAmount immature {0};
204 LOCK2(cs_main, cs_metrics);
205 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
206 auto consensusParams = Params().GetConsensus();
207 auto tipHeight = chainActive.Height();
209 // Update orphans and calculate subsidies
210 std::list<uint256>::iterator it = u->begin();
211 while (it != u->end()) {
213 if (mapBlockIndex.count(hash) > 0 &&
214 chainActive.Contains(mapBlockIndex[hash])) {
215 int height = mapBlockIndex[hash]->nHeight;
216 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
217 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
218 subsidy -= subsidy/5;
220 if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
231 mined = minedBlocks.get();
232 orphaned = mined - u->size();
236 std::string units = Params().CurrencyUnits();
237 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
239 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
241 FormatMoney(immature), units,
242 FormatMoney(mature), units)
247 std::cout << std::endl;
252 int printMessageBox(size_t cols)
254 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
256 if (u->size() == 0) {
260 int lines = 2 + u->size();
261 std::cout << _("Messages:") << std::endl;
262 for (auto it = u->cbegin(); it != u->cend(); ++it) {
263 std::cout << *it << std::endl;
264 // Handle newlines and wrapped lines
267 while (j < it->size()) {
268 i = it->find('\n', j);
269 if (i == std::string::npos) {
276 lines += ((i-j) / cols);
280 std::cout << std::endl;
284 int printInitMessage()
290 std::string msg = *initMessage;
291 std::cout << _("Init message:") << " " << msg << std::endl;
292 std::cout << std::endl;
294 if (msg == _("Done loading")) {
301 void ThreadShowMetricsScreen()
303 // Make this thread recognisable as the metrics screen thread
304 RenameThread("zcash-metrics-screen");
306 // Determine whether we should render a persistent UI or rolling metrics
307 bool isTTY = isatty(STDOUT_FILENO);
308 bool isScreen = GetBoolArg("-metricsui", isTTY);
309 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
313 std::cout << "\e[2J";
316 std::cout << METRICS_ART << std::endl;
317 std::cout << std::endl;
320 std::cout << _("Thank you for running a Zcash node!") << std::endl;
321 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
322 std::cout << std::endl;
326 // Number of lines that are always displayed
330 // Get current window size
334 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
340 // Erase below current position
345 bool mining = GetBoolArg("-gen", false);
348 lines += printNetworkStats();
350 lines += printMiningStatus(mining);
351 lines += printMetrics(cols, mining);
352 lines += printMessageBox(cols);
353 lines += printInitMessage();
356 // Explain how to exit
357 std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
360 std::cout << "----------------------------------------" << std::endl;
363 *nNextRefresh = GetTime() + nRefresh;
364 while (GetTime() < *nNextRefresh) {
365 boost::this_thread::interruption_point();
370 // Return to the top of the updating section
371 std::cout << "\e[" << lines << "A";