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 AtomicCounter transactionsValidated;
24 AtomicCounter ehSolverRuns;
25 AtomicCounter solutionTargetChecks;
26 AtomicCounter minedBlocks;
28 boost::synchronized_value<std::list<uint256>> trackedBlocks;
30 boost::synchronized_value<std::list<std::string>> messageBox;
31 boost::synchronized_value<std::string> initMessage;
34 extern int64_t GetNetworkHashPS(int lookup, int height);
36 void TrackMinedBlock(uint256 hash)
39 minedBlocks.increment();
40 trackedBlocks->push_back(hash);
45 *nNodeStartTime = GetTime();
50 return GetTime() - *nNodeStartTime;
53 double GetLocalSolPS_INTERNAL(int64_t uptime)
55 return uptime > 0 ? (double)solutionTargetChecks.get() / uptime : 0;
58 double GetLocalSolPS()
60 return GetLocalSolPS_INTERNAL(GetUptime());
63 static bool metrics_ThreadSafeMessageBox(const std::string& message,
64 const std::string& caption,
67 std::string strCaption;
68 // Check for usage of predefined caption
70 case CClientUIInterface::MSG_ERROR:
71 strCaption += _("Error");
73 case CClientUIInterface::MSG_WARNING:
74 strCaption += _("Warning");
76 case CClientUIInterface::MSG_INFORMATION:
77 strCaption += _("Information");
80 strCaption += caption; // Use supplied caption (can be empty)
83 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
84 u->push_back(strCaption + ": " + message);
90 static void metrics_InitMessage(const std::string& message)
92 *initMessage = message;
95 void ConnectMetricsScreen()
97 uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
98 uiInterface.ThreadSafeMessageBox.connect(metrics_ThreadSafeMessageBox);
99 uiInterface.InitMessage.disconnect_all_slots();
100 uiInterface.InitMessage.connect(metrics_InitMessage);
103 int printNetworkStats()
105 LOCK2(cs_main, cs_vNodes);
107 std::cout << " " << _("Block height") << " | " << chainActive.Height() << std::endl;
108 std::cout << " " << _("Network solution rate") << " | " << GetNetworkHashPS(120, -1) << " Sol/s" << std::endl;
109 std::cout << " " << _("Connections") << " | " << vNodes.size() << std::endl;
110 std::cout << std::endl;
115 int printMiningStatus(bool mining)
117 // Number of lines that are always displayed
121 int nThreads = GetArg("-genproclimit", 1);
123 // In regtest threads defaults to 1
124 if (Params().DefaultMinerThreads())
125 nThreads = Params().DefaultMinerThreads();
127 nThreads = boost::thread::hardware_concurrency();
129 std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
130 GetArg("-equihashsolver", "default"), nThreads) << std::endl;
133 std::cout << _("You are currently not mining.") << std::endl;
134 std::cout << _("To enable mining, add 'gen=1' to your zcash.conf and restart.") << std::endl;
137 std::cout << std::endl;
142 int printMetrics(size_t cols, bool mining)
144 // Number of lines that are always displayed
148 int64_t uptime = GetUptime();
149 int days = uptime / (24 * 60 * 60);
150 int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60);
151 int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60;
152 int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60);
155 std::string duration;
157 duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds);
158 } else if (hours > 0) {
159 duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds);
160 } else if (minutes > 0) {
161 duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds);
163 duration = strprintf(_("%d seconds"), seconds);
165 std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
166 std::cout << strDuration << std::endl;
167 lines += (strDuration.size() / cols);
169 int validatedCount = transactionsValidated.get();
170 if (validatedCount > 1) {
171 std::cout << "- " << strprintf(_("You have validated %d transactions!"), validatedCount) << std::endl;
172 } else if (validatedCount == 1) {
173 std::cout << "- " << _("You have validated a transaction!") << std::endl;
175 std::cout << "- " << _("You have validated no transactions.") << std::endl;
178 if (mining && loaded) {
179 double solps = GetLocalSolPS_INTERNAL(uptime);
180 std::string strSolps = strprintf("%.4f Sol/s", solps);
181 std::cout << "- " << strprintf(_("You have contributed %s on average to the network solution rate."), strSolps) << std::endl;
182 std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
187 CAmount immature {0};
190 LOCK2(cs_main, cs_metrics);
191 boost::strict_lock_ptr<std::list<uint256>> u = trackedBlocks.synchronize();
192 auto consensusParams = Params().GetConsensus();
193 auto tipHeight = chainActive.Height();
195 // Update orphans and calculate subsidies
196 std::list<uint256>::iterator it = u->begin();
197 while (it != u->end()) {
199 if (mapBlockIndex.count(hash) > 0 &&
200 chainActive.Contains(mapBlockIndex[hash])) {
201 int height = mapBlockIndex[hash]->nHeight;
202 CAmount subsidy = GetBlockSubsidy(height, consensusParams);
203 if ((height > 0) && (height <= consensusParams.GetLastFoundersRewardBlockHeight())) {
204 subsidy -= subsidy/5;
206 if (std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) {
217 mined = minedBlocks.get();
218 orphaned = mined - u->size();
222 std::string units = Params().CurrencyUnits();
223 std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
225 << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
227 FormatMoney(immature), units,
228 FormatMoney(mature), units)
233 std::cout << std::endl;
238 int printMessageBox(size_t cols)
240 boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
242 if (u->size() == 0) {
246 int lines = 2 + u->size();
247 std::cout << _("Messages:") << std::endl;
248 for (auto it = u->cbegin(); it != u->cend(); ++it) {
249 std::cout << *it << std::endl;
250 // Handle wrapped lines
251 lines += (it->size() / cols);
253 std::cout << std::endl;
257 int printInitMessage()
263 std::string msg = *initMessage;
264 std::cout << _("Init message:") << " " << msg << std::endl;
265 std::cout << std::endl;
267 if (msg == _("Done loading")) {
274 void ThreadShowMetricsScreen()
276 // Make this thread recognisable as the metrics screen thread
277 RenameThread("zcash-metrics-screen");
279 // Determine whether we should render a persistent UI or rolling metrics
280 bool isTTY = isatty(STDOUT_FILENO);
281 bool isScreen = GetBoolArg("-metricsui", isTTY);
282 int64_t nRefresh = GetArg("-metricsrefreshtime", isTTY ? 1 : 600);
286 std::cout << "\e[2J";
289 std::cout << METRICS_ART << std::endl;
290 std::cout << std::endl;
293 std::cout << _("Thank you for running a Zcash node!") << std::endl;
294 std::cout << _("You're helping to strengthen the network and contributing to a social good :)") << std::endl;
295 std::cout << std::endl;
299 // Number of lines that are always displayed
303 // Get current window size
307 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
313 // Erase below current position
318 bool mining = GetBoolArg("-gen", false);
321 lines += printNetworkStats();
323 lines += printMiningStatus(mining);
324 lines += printMetrics(cols, mining);
325 lines += printMessageBox(cols);
326 lines += printInitMessage();
329 // Explain how to exit
330 std::cout << "[" << _("Press Ctrl+C to exit") << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
333 std::cout << "----------------------------------------" << std::endl;
336 int64_t nWaitEnd = GetTime() + nRefresh;
337 while (GetTime() < nWaitEnd) {
338 boost::this_thread::interruption_point();
343 // Return to the top of the updating section
344 std::cout << "\e[" << lines << "A";