]> Git Repo - VerusCoin.git/blob - src/metrics.cpp
Update to Zcash rc1
[VerusCoin.git] / src / metrics.cpp
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.
4
5 #include "metrics.h"
6
7 #include "chainparams.h"
8 #include "checkpoints.h"
9 #include "main.h"
10 #include "ui_interface.h"
11 #include "util.h"
12 #include "utiltime.h"
13 #include "utilmoneystr.h"
14 #include "utilstrencodings.h"
15
16 #include <boost/thread.hpp>
17 #include <boost/thread/synchronized_value.hpp>
18 #include <string>
19 #ifdef _WIN32
20 #include <io.h>
21 #include <windows.h>
22 #else
23 #include <sys/ioctl.h>
24 #endif
25 #include <unistd.h>
26
27 extern uint64_t ASSETCHAINS_TIMELOCKGTE;
28 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH;
29 int64_t komodo_block_unlocktime(uint32_t nHeight);
30
31 void AtomicTimer::start()
32 {
33     std::unique_lock<std::mutex> lock(mtx);
34     if (threads < 1) {
35         start_time = GetTime();
36     }
37     ++threads;
38 }
39
40 void AtomicTimer::stop()
41 {
42     std::unique_lock<std::mutex> lock(mtx);
43     // Ignore excess calls to stop()
44     if (threads > 0) {
45         --threads;
46         if (threads < 1) {
47             int64_t time_span = GetTime() - start_time;
48             total_time += time_span;
49         }
50     }
51 }
52
53 bool AtomicTimer::running()
54 {
55     std::unique_lock<std::mutex> lock(mtx);
56     return threads > 0;
57 }
58
59 uint64_t AtomicTimer::threadCount()
60 {
61     std::unique_lock<std::mutex> lock(mtx);
62     return threads;
63 }
64
65 double AtomicTimer::rate(const AtomicCounter& count)
66 {
67     std::unique_lock<std::mutex> lock(mtx);
68     int64_t duration = total_time;
69     if (threads > 0) {
70         // Timer is running, so get the latest count
71         duration += GetTime() - start_time;
72     }
73     return duration > 0 ? (double)count.get() / duration : 0;
74 }
75
76 boost::synchronized_value<int64_t> nNodeStartTime;
77 boost::synchronized_value<int64_t> nNextRefresh;
78 int64_t nHashCount;
79 AtomicCounter transactionsValidated;
80 AtomicCounter ehSolverRuns;
81 AtomicCounter solutionTargetChecks;
82 static AtomicCounter minedBlocks;
83 AtomicTimer miningTimer;
84 CCriticalSection cs_metrics;
85
86 double AtomicTimer::rate(const int64_t count)
87 {
88     std::unique_lock<std::mutex> lock(mtx);
89     LOCK(cs_metrics);
90     int64_t duration = total_time;
91     if (threads > 0) {
92         // Timer is running, so get the latest count
93         duration += GetTime() - start_time;
94     }
95     return duration > 0 ? (double)count / duration : 0;
96 }
97
98 static boost::synchronized_value<std::list<uint256>> trackedBlocks;
99
100 static boost::synchronized_value<std::list<std::string>> messageBox;
101 static boost::synchronized_value<std::string> initMessage;
102 static bool loaded = false;
103
104 extern int64_t GetNetworkHashPS(int lookup, int height);
105
106 void TrackMinedBlock(uint256 hash)
107 {
108     LOCK(cs_metrics);
109     minedBlocks.increment();
110     trackedBlocks->push_back(hash);
111 }
112
113 void MarkStartTime()
114 {
115     *nNodeStartTime = GetTime();
116 }
117
118 int64_t GetUptime()
119 {
120     return GetTime() - *nNodeStartTime;
121 }
122
123 double GetLocalSolPS()
124 {
125     if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH)
126     {
127         return miningTimer.rate(nHashCount);
128     }
129     else
130         return miningTimer.rate(solutionTargetChecks);
131 }
132
133 int EstimateNetHeightInner(int height, int64_t tipmediantime,
134                            int heightLastCheckpoint, int64_t timeLastCheckpoint,
135                            int64_t genesisTime, int64_t targetSpacing)
136 {
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)) :
142             height / 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;
150 }
151
152 int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams)
153 {
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);
161 }
162
163 void TriggerRefresh()
164 {
165     *nNextRefresh = GetTime();
166     // Ensure that the refresh has started before we return
167     MilliSleep(200);
168 }
169
170 static bool metrics_ThreadSafeMessageBox(const std::string& message,
171                                       const std::string& caption,
172                                       unsigned int style)
173 {
174     // The SECURE flag has no effect in the metrics UI.
175     style &= ~CClientUIInterface::SECURE;
176
177     std::string strCaption;
178     // Check for usage of predefined caption
179     switch (style) {
180     case CClientUIInterface::MSG_ERROR:
181         strCaption += _("Error");
182         break;
183     case CClientUIInterface::MSG_WARNING:
184         strCaption += _("Warning");
185         break;
186     case CClientUIInterface::MSG_INFORMATION:
187         strCaption += _("Information");
188         break;
189     default:
190         strCaption += caption; // Use supplied caption (can be empty)
191     }
192
193     boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
194     u->push_back(strCaption + ": " + message);
195     if (u->size() > 5) {
196         u->pop_back();
197     }
198
199     TriggerRefresh();
200     return false;
201 }
202
203 static bool metrics_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style)
204 {
205     return metrics_ThreadSafeMessageBox(message, caption, style);
206 }
207
208 static void metrics_InitMessage(const std::string& message)
209 {
210     *initMessage = message;
211 }
212
213 void ConnectMetricsScreen()
214 {
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);
221 }
222
223 int printStats(bool mining)
224 {
225     // Number of lines that are always displayed
226     int lines = 4;
227
228     int height;
229     int64_t tipmediantime;
230     size_t connections;
231     int64_t netsolps;
232     {
233         LOCK2(cs_main, cs_vNodes);
234         height = chainActive.Height();
235         tipmediantime = chainActive.LastTip()->GetMedianTimePast();
236         connections = vNodes.size();
237         netsolps = GetNetworkHashPS(120, -1);
238     }
239     auto localsolps = GetLocalSolPS();
240
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;
245     } else {
246         std::cout << "           " << _("Block height") << " | " << height << std::endl;
247     }
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;
252         lines++;
253     }
254     std::cout << std::endl;
255
256     return lines;
257 }
258
259 int printMiningStatus(bool mining)
260 {
261 #ifdef ENABLE_MINING
262     // Number of lines that are always displayed
263     int lines = 1;
264
265     if (mining) {
266         auto nThreads = miningTimer.threadCount();
267         if (nThreads > 0) {
268             std::cout << strprintf(_("You are mining with the %s solver on %d threads."),
269                                    GetArg("-equihashsolver", "default"), nThreads) << std::endl;
270         } else {
271             bool fvNodesEmpty;
272             {
273                 LOCK(cs_vNodes);
274                 fvNodesEmpty = vNodes.empty();
275             }
276             if (fvNodesEmpty) {
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;
280             } else {
281                 std::cout << _("Mining is paused (a JoinSplit may be in progress).") << std::endl;
282             }
283         }
284         lines++;
285     } else {
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;
288         lines += 2;
289     }
290     std::cout << std::endl;
291
292     return lines;
293 #else // ENABLE_MINING
294     return 0;
295 #endif // !ENABLE_MINING
296 }
297
298 int printMetrics(size_t cols, bool mining)
299 {
300     // Number of lines that are always displayed
301     int lines = 3;
302
303     // Calculate uptime
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);
309
310     // Display uptime
311     std::string duration;
312     if (days > 0) {
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);
318     } else {
319         duration = strprintf(_("%d seconds"), seconds);
320     }
321     std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration);
322     std::cout << strDuration << std::endl;
323     lines += (strDuration.size() / cols);
324
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;
330     } else {
331       std::cout << "- " << _("You have validated no transactions.") << std::endl;
332     }
333
334     if (mining && loaded) {
335         std::cout << "- " << strprintf(_("You have completed %d Equihash solver runs."), ehSolverRuns.get()) << std::endl;
336         lines++;
337
338         int mined = 0;
339         int orphaned = 0;
340         CAmount immature {0};
341         CAmount mature {0};
342         {
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();
347
348             // Update orphans and calculate subsidies
349             std::list<uint256>::iterator it = u->begin();
350             while (it != u->end()) {
351                 auto hash = *it;
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;
358                     }
359
360                     if ((std::max(0, COINBASE_MATURITY - (tipHeight - height)) > 0) ||
361                         (tipHeight < komodo_block_unlocktime(height) && subsidy >= ASSETCHAINS_TIMELOCKGTE)) {
362                         immature += subsidy;
363                     } else {
364                         mature += subsidy;
365                     }
366                     it++;
367                 } else {
368                     it = u->erase(it);
369                 }
370             }
371
372             mined = minedBlocks.get();
373             orphaned = mined - u->size();
374         }
375
376         if (mined > 0) {
377             std::string units = Params().CurrencyUnits();
378             std::cout << "- " << strprintf(_("You have mined %d blocks!"), mined) << std::endl;
379             std::cout << "  "
380                       << strprintf(_("Orphaned: %d blocks, Immature: %u %s, Mature: %u %s"),
381                                      orphaned,
382                                      FormatMoney(immature), units,
383                                      FormatMoney(mature), units)
384                       << std::endl;
385             lines += 2;
386         }
387     }
388     std::cout << std::endl;
389
390     return lines;
391 }
392
393 int printMessageBox(size_t cols)
394 {
395     boost::strict_lock_ptr<std::list<std::string>> u = messageBox.synchronize();
396
397     if (u->size() == 0) {
398         return 0;
399     }
400
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
407         size_t i = 0;
408         size_t j = 0;
409         while (j < msg.size()) {
410             i = msg.find('\n', j);
411             if (i == std::string::npos) {
412                 i = msg.size();
413             } else {
414                 // Newline
415                 lines++;
416             }
417             j = i + 1;
418         }
419     }
420     std::cout << std::endl;
421     return lines;
422 }
423
424 int printInitMessage()
425 {
426     if (loaded) {
427         return 0;
428     }
429
430     std::string msg = *initMessage;
431     std::cout << _("Init message:") << " " << msg << std::endl;
432     std::cout << std::endl;
433
434     if (msg == _("Done loading")) {
435         loaded = true;
436     }
437
438     return 2;
439 }
440
441 #ifdef WIN32
442 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
443
444 bool enableVTMode()
445 {
446     // Set output mode to handle virtual terminal sequences
447     HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
448     if (hOut == INVALID_HANDLE_VALUE) {
449         return false;
450     }
451
452     DWORD dwMode = 0;
453     if (!GetConsoleMode(hOut, &dwMode)) {
454         return false;
455     }
456
457     dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
458     if (!SetConsoleMode(hOut, dwMode)) {
459         return false;
460     }
461     return true;
462 }
463 #endif
464
465 void ThreadShowMetricsScreen()
466 {
467     // Make this thread recognisable as the metrics screen thread
468     RenameThread("zcash-metrics-screen");
469
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);
474
475     if (isScreen) {
476 #ifdef WIN32
477         enableVTMode();
478 #endif
479
480         // Clear screen
481         std::cout << "\e[2J";
482
483         // Print art
484         std::cout << METRICS_ART << std::endl;
485         std::cout << std::endl;
486
487         // Thank you text
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;
490
491         // Privacy notice text
492         std::cout << PrivacyInfo();
493         std::cout << std::endl;
494     }
495
496     while (true) {
497         // Number of lines that are always displayed
498         int lines = 1;
499         int cols = 80;
500
501         // Get current window size
502         if (isTTY) {
503 #ifdef _WIN32
504             CONSOLE_SCREEN_BUFFER_INFO csbi;
505             if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) != 0) {
506                 cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
507             }
508 #else
509             struct winsize w;
510             w.ws_col = 0;
511             if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1 && w.ws_col != 0) {
512                 cols = w.ws_col;
513             }
514 #endif
515         }
516
517         if (isScreen) {
518             // Erase below current position
519             std::cout << "\e[J";
520         }
521
522         // Miner status
523 #ifdef ENABLE_MINING
524         bool mining = GetBoolArg("-gen", false);
525 #else
526         bool mining = false;
527 #endif
528
529         if (loaded) {
530             lines += printStats(mining);
531             lines += printMiningStatus(mining);
532         }
533         lines += printMetrics(cols, mining);
534         lines += printMessageBox(cols);
535         lines += printInitMessage();
536
537         if (isScreen) {
538             // Explain how to exit
539             std::cout << "[";
540 #ifdef WIN32
541             std::cout << _("'zcash-cli.exe stop' to exit");
542 #else
543             std::cout << _("Press Ctrl+C to exit");
544 #endif
545             std::cout << "] [" << _("Set 'showmetrics=0' to hide") << "]" << std::endl;
546         } else {
547             // Print delineator
548             std::cout << "----------------------------------------" << std::endl;
549         }
550
551         *nNextRefresh = GetTime() + nRefresh;
552         while (GetTime() < *nNextRefresh) {
553             boost::this_thread::interruption_point();
554             MilliSleep(200);
555         }
556
557         if (isScreen) {
558             // Return to the top of the updating section
559             std::cout << "\e[" << lines << "A";
560         }
561     }
562 }
This page took 0.054742 seconds and 4 git commands to generate.