diff --git a/src/Makefile.am b/src/Makefile.am index 7467919d4..8ded268cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -120,6 +120,7 @@ BITCOIN_CORE_H = \ main.h \ memusage.h \ merkleblock.h \ + metrics.h \ miner.h \ mruset.h \ net.h \ @@ -205,6 +206,7 @@ libbitcoin_server_a_SOURCES = \ leveldbwrapper.cpp \ main.cpp \ merkleblock.cpp \ + metrics.cpp \ miner.cpp \ net.cpp \ noui.cpp \ diff --git a/src/init.cpp b/src/init.cpp index 37564d951..a24a17822 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -16,6 +16,7 @@ #include "consensus/validation.h" #include "key.h" #include "main.h" +#include "metrics.h" #include "miner.h" #include "net.h" #include "rpcserver.h" @@ -975,6 +976,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(boost::bind(&TraceThread, "scheduler", serviceLoop)); + if (GetBoolArg("-showmetrics", true) && !fPrintToConsole && !GetBoolArg("-daemon", false)) { + // Start the persistent metrics interface + threadGroup.create_thread(&ThreadShowMetricsScreen); + } + // Initialize Zcash circuit parameters ZC_LoadParams(); // These must be disabled for now, they are buggy and we probably don't diff --git a/src/main.cpp b/src/main.cpp index 699b28b5a..ca8f0f8d7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ #include "consensus/validation.h" #include "init.h" #include "merkleblock.h" +#include "metrics.h" #include "net.h" #include "pow.h" #include "txdb.h" @@ -833,6 +834,11 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in bool CheckTransaction(const CTransaction& tx, CValidationState &state) { + // Don't count coinbase transactions because mining skews the count + if (!tx.IsCoinBase()) { + transactionsValidated.increment(); + } + if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { diff --git a/src/metrics.cpp b/src/metrics.cpp new file mode 100644 index 000000000..ad58ab41f --- /dev/null +++ b/src/metrics.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "metrics.h" + +#include "chainparams.h" +#include "util.h" +#include "utiltime.h" + +#include + +AtomicCounter transactionsValidated; +AtomicCounter ehSolverRuns; +AtomicCounter minedBlocks; + +void ThreadShowMetricsScreen() +{ + // Make this thread recognisable as the metrics screen thread + RenameThread("zcash-metrics-screen"); + + // Clear screen + std::cout << "\e[2J"; + + // Print art + std::cout << METRICS_ART << std::endl; + std::cout << std::endl; + + // Thank you text + std::cout << OFFSET << "Thank you for running a Zcash node!" << std::endl; + std::cout << OFFSET << "By running this node, you're contributing to the social good :)" << std::endl; + std::cout << std::endl; + + // Miner status + bool mining = GetBoolArg("-gen", false); + if (mining) { + int nThreads = GetArg("-genproclimit", 1); + if (nThreads < 0) { + // In regtest threads defaults to 1 + if (Params().DefaultMinerThreads()) + nThreads = Params().DefaultMinerThreads(); + else + nThreads = boost::thread::hardware_concurrency(); + } + std::cout << OFFSET << "You are running " << nThreads << " mining threads." << std::endl; + } else { + std::cout << OFFSET << "You are currently not mining." << std::endl; + std::cout << OFFSET << "To enable mining, add 'gen=1' to your zcash.conf and restart." << std::endl; + } + std::cout << std::endl; + + // Count uptime + int64_t nStart = GetTime(); + + while (true) { + int lines = 4; + + // Erase below current position + std::cout << "\e[J"; + + // Calculate uptime + int64_t uptime = GetTime() - nStart; + int days = uptime / (24 * 60 * 60); + int hours = (uptime - (days * 24 * 60 * 60)) / (60 * 60); + int minutes = (uptime - (((days * 24) + hours) * 60 * 60)) / 60; + int seconds = uptime - (((((days * 24) + hours) * 60) + minutes) * 60); + + // Display uptime + std::cout << OFFSET << "Since starting this node "; + if (days > 0) { + std::cout << days << " days, "; + } + if (hours > 0) { + std::cout << hours << " hours, "; + } + if (minutes > 0) { + std::cout << minutes << " minutes, "; + } + std::cout << seconds << " seconds ago:" << std::endl; + + std::cout << OFFSET << "- You have validated " << transactionsValidated.get() << " transactions." << std::endl; + + if (mining) { + std::cout << OFFSET << "- You have completed " << ehSolverRuns.get() << " Equihash solver runs." << std::endl; + lines++; + + int mined = minedBlocks.get(); + if (mined > 0) { + std::cout << OFFSET << "- You have mined " << mined << " blocks!" << std::endl; + lines++; + } + } + + // Explain how to exit + std::cout << std::endl; + std::cout << "[Hit Ctrl+C to exit] [Set 'showmetrics=0' to hide]" << std::endl;; + + boost::this_thread::interruption_point(); + MilliSleep(1000); + + // Return to the top of the updating section + std::cout << "\e[" << lines << "A"; + } +} diff --git a/src/metrics.h b/src/metrics.h new file mode 100644 index 000000000..f1fa13d58 --- /dev/null +++ b/src/metrics.h @@ -0,0 +1,68 @@ +// Copyright (c) 2016 The Zcash developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +struct AtomicCounter { + std::atomic value; + + AtomicCounter() : value {0} { } + + void increment(){ + ++value; + } + + void decrement(){ + --value; + } + + int get(){ + return value.load(); + } +}; + +extern AtomicCounter transactionsValidated; +extern AtomicCounter ehSolverRuns; +extern AtomicCounter minedBlocks; + +void ThreadShowMetricsScreen(); + +/** + * Heart image: https://commons.wikimedia.org/wiki/File:Heart_coraz%C3%B3n.svg + * License: CC BY-SA 3.0 + * + * Rendering options: + * Zcash: img2txt -W 50 -H 26 -f utf8 -d none -g 0.7 Z-yellow.orange-logo.png + * Heart: img2txt -W 50 -H 26 -f utf8 -d none 2000px-Heart_corazón.svg.png + */ +const std::string METRICS_ART = +"   \n" +"   \n" +"  .;tt;.   .;t: :t;. \n" +"  :8SX8S;;;:::t%8@S;  .X ;S S; X. \n" +"  t%Xt%%ttt@XXXX@::XXXX%XS  t. X X .t \n" +"  8S;tttt%%tt8 @:;::XXXXXS8   X tt X \n" +"  %S:;;;;:XXX@@8 S;8;;tt;XXXX%8  8 8 \n" +"  8S.:::;;% S:XXXXXX     \n" +"  88....:::% %;::XXXX@    \n" +"  S8888....:%;;;;;;;;;   8t;;;::XXX8    \n" +"   S888888...:::;;;;tX8  ;Xtt;;;;;::XS.  . . \n" +"  t888888888....::::8  @:ttttt;;;;:::X  % % \n" +"  888888888888...:SS  %t%%%ttttt;;;;:X  % % \n" +"  88888888888888.t. ;Stttt%%%ttttt;;;S  % % \n" +"  t888888888888S8 8X;;;tttt%%%tttt;;@  @ @ \n" +"   @8888888888%% %%::::;;;tttt%%%tttt.  S S \n" +"  Stt88888888. %SSSSSSSSSStttt%%%tX  S S \n" +"  8ttt8888S %;;tttt%S   @ @ \n" +"  8%ttt88S %;;;;tt%   8 8 \n" +"  %8ttttt@@@XXX@ %%%%%%%S::;;X8  %. .% \n" +"  88tttt888888 X8888....:S8   .; ;. \n" +"  t8@ttt888@8888@888888S8S  t t \n" +"  :888St8888888%S88;  S S \n" +"   .;tt;:   \n" +"   \n" +"   "; + +const std::string OFFSET = " "; diff --git a/src/miner.cpp b/src/miner.cpp index 7693394e7..2a5c070b8 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -12,6 +12,7 @@ #include "consensus/validation.h" #include "hash.h" #include "main.h" +#include "metrics.h" #include "net.h" #include "pow.h" #include "primitives/transaction.h" @@ -437,6 +438,8 @@ static bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& rese if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) return error("ZcashMiner: ProcessNewBlock, block not accepted"); + minedBlocks.increment(); + return true; } @@ -555,8 +558,11 @@ void static BitcoinMiner(CWallet *pwallet) SetThreadPriority(THREAD_PRIORITY_LOWEST); // In regression test mode, stop mining after a block is found. - if (chainparams.MineBlocksOnDemand()) + if (chainparams.MineBlocksOnDemand()) { + // Increment here because throwing skips the call below + ehSolverRuns.increment(); throw boost::thread_interrupted(); + } return true; }; @@ -581,6 +587,7 @@ void static BitcoinMiner(CWallet *pwallet) eq.showbsizes(r); } eq.digitK(0); + ehSolverRuns.increment(); // Convert solution indices to byte array (decompress) and pass it to validBlock method. for (size_t s = 0; s < eq.nsols; s++) { @@ -600,8 +607,11 @@ void static BitcoinMiner(CWallet *pwallet) } else { try { // If we find a valid block, we rebuild - if (EhOptimisedSolve(n, k, curr_state, validBlock, cancelled)) + bool found = EhOptimisedSolve(n, k, curr_state, validBlock, cancelled); + ehSolverRuns.increment(); + if (found) { break; + } } catch (EhSolverCancelledException&) { LogPrint("pow", "Equihash solver cancelled\n"); std::lock_guard lock{m_cs}; diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 7f2d01f88..935d2b201 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -11,6 +11,7 @@ #include "crypto/equihash.h" #include "init.h" #include "main.h" +#include "metrics.h" #include "miner.h" #include "net.h" #include "pow.h" @@ -193,13 +194,17 @@ Value generate(const Array& params, bool fHelp) pblock->nSolution = soln; return CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()); }; - if (EhBasicSolveUncancellable(n, k, curr_state, validBlock)) + bool found = EhBasicSolveUncancellable(n, k, curr_state, validBlock); + ehSolverRuns.increment(); + if (found) { goto endloop; + } } endloop: CValidationState state; if (!ProcessNewBlock(state, NULL, pblock, true, NULL)) throw JSONRPCError(RPC_INTERNAL_ERROR, "ProcessNewBlock, block not accepted"); + minedBlocks.increment(); ++nHeight; blockHashes.push_back(pblock->GetHash().GetHex()); }