diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp index b818e4c13..3fbadb63d 100644 --- a/src/gtest/test_metrics.cpp +++ b/src/gtest/test_metrics.cpp @@ -107,3 +107,30 @@ TEST(Metrics, EstimateNetHeight) { } RegtestDeactivateBlossom(); } + +TEST(Metrics, NextUpgrade) { + SelectParams(CBaseChainParams::REGTEST); + const Consensus::Params& params = Params().GetConsensus(); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, 100); + + EXPECT_EQ(SecondsLeftToNextEpoch(params, 1).value(), 14850); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 1).value(), DurationFormat::REDUCED), "4 hours"); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 1).value(), DurationFormat::FULL), "4 hours, 7 minutes, 30 seconds"); + + EXPECT_EQ(SecondsLeftToNextEpoch(params, 90).value(), 1500); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 90).value(), DurationFormat::REDUCED), "25 minutes"); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 90).value(), DurationFormat::FULL), "25 minutes, 0 seconds"); + + EXPECT_EQ(SecondsLeftToNextEpoch(params, 99).value(), 150); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 99).value(), DurationFormat::REDUCED), "2 minutes"); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(params, 99).value(), DurationFormat::FULL), "2 minutes, 30 seconds"); + + auto paramsBlossom = RegtestActivateBlossom(true); + EXPECT_EQ(SecondsLeftToNextEpoch(paramsBlossom, 1).value(), 7425); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(paramsBlossom, 1).value(), DurationFormat::REDUCED), "2 hours"); + EXPECT_EQ(DisplayDuration(SecondsLeftToNextEpoch(paramsBlossom, 1).value(), DurationFormat::FULL), "2 hours, 3 minutes, 45 seconds"); + RegtestDeactivateBlossom(); + + UpdateNetworkUpgradeParameters(Consensus::UPGRADE_TESTDUMMY, Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT); +} diff --git a/src/metrics.cpp b/src/metrics.cpp index e6488e6c8..bf7f73ec5 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -203,16 +203,59 @@ void ConnectMetricsScreen() uiInterface.InitMessage.connect(metrics_InitMessage); } +std::string DisplayDuration(int64_t time, DurationFormat format) +{ + int days = time / (24 * 60 * 60); + int hours = (time - (days * 24 * 60 * 60)) / (60 * 60); + int minutes = (time - (((days * 24) + hours) * 60 * 60)) / 60; + int seconds = time - (((((days * 24) + hours) * 60) + minutes) * 60); + + std::string strDuration; + if (format == DurationFormat::REDUCED) { + if (days > 0) { + strDuration = strprintf(_("%d days"), days); + } else if (hours > 0) { + strDuration = strprintf(_("%d hours"), hours); + } else if (minutes > 0) { + strDuration = strprintf(_("%d minutes"), minutes); + } else { + strDuration = strprintf(_("%d seconds"), seconds); + } + } else { + if (days > 0) { + strDuration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds); + } else if (hours > 0) { + strDuration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds); + } else if (minutes > 0) { + strDuration = strprintf(_("%d minutes, %d seconds"), minutes, seconds); + } else { + strDuration = strprintf(_("%d seconds"), seconds); + } + } + return strDuration; +} + +boost::optional SecondsLeftToNextEpoch(const Consensus::Params& params, int currentHeight) +{ + auto nextHeight = NextActivationHeight(currentHeight, params); + if (nextHeight) { + return (nextHeight.get() - currentHeight) * params.PoWTargetSpacing(nextHeight.get() - 1); + } else { + return boost::none; + } +} + int printStats(bool mining) { // Number of lines that are always displayed - int lines = 4; + int lines = 5; int height; int64_t currentHeadersHeight; int64_t currentHeadersTime; size_t connections; int64_t netsolps; + const Consensus::Params& params = Params().GetConsensus(); { LOCK2(cs_main, cs_vNodes); height = chainActive.Height(); @@ -225,12 +268,25 @@ int printStats(bool mining) if (IsInitialBlockDownload(Params())) { int netheight = currentHeadersHeight == -1 || currentHeadersTime == 0 ? - 0 : EstimateNetHeight(Params().GetConsensus(), currentHeadersHeight, currentHeadersTime); + 0 : EstimateNetHeight(params, currentHeadersHeight, currentHeadersTime); int downloadPercent = height * 100 / netheight; std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; } else { std::cout << " " << _("Block height") << " | " << height << std::endl; } + + auto secondsLeft = SecondsLeftToNextEpoch(params, height); + std::string strUpgradeTime; + if (secondsLeft) { + auto nextHeight = NextActivationHeight(height, params).value(); + auto nextBranch = NextEpoch(height, params).value(); + strUpgradeTime = strprintf(_("%s at block height %d, in around %s"), + NetworkUpgradeInfo[nextBranch].strName, nextHeight, DisplayDuration(secondsLeft.value(), DurationFormat::REDUCED)); + } + else { + strUpgradeTime = "Unknown"; + } + std::cout << " " << _("Next upgrade") << " | " << strUpgradeTime << std::endl; std::cout << " " << _("Connections") << " | " << connections << std::endl; std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl; if (mining && miningTimer.running()) { @@ -286,24 +342,9 @@ int printMetrics(size_t cols, bool mining) // Number of lines that are always displayed int lines = 3; - // Calculate uptime - int64_t uptime = GetUptime(); - 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); + // Calculate and display uptime + std::string duration = DisplayDuration(GetUptime(), DurationFormat::FULL); - // Display uptime - std::string duration; - if (days > 0) { - duration = strprintf(_("%d days, %d hours, %d minutes, %d seconds"), days, hours, minutes, seconds); - } else if (hours > 0) { - duration = strprintf(_("%d hours, %d minutes, %d seconds"), hours, minutes, seconds); - } else if (minutes > 0) { - duration = strprintf(_("%d minutes, %d seconds"), minutes, seconds); - } else { - duration = strprintf(_("%d seconds"), seconds); - } std::string strDuration = strprintf(_("Since starting this node %s ago:"), duration); std::cout << strDuration << std::endl; lines += (strDuration.size() / cols); diff --git a/src/metrics.h b/src/metrics.h index 32f51c008..7fbdbd938 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -55,6 +55,11 @@ public: double rate(const AtomicCounter& count); }; +enum DurationFormat { + FULL, + REDUCED +}; + extern AtomicCounter transactionsValidated; extern AtomicCounter ehSolverRuns; extern AtomicCounter solutionTargetChecks; @@ -65,6 +70,8 @@ void TrackMinedBlock(uint256 hash); void MarkStartTime(); double GetLocalSolPS(); int EstimateNetHeight(const Consensus::Params& params, int currentBlockHeight, int64_t currentBlockTime); +boost::optional SecondsLeftToNextEpoch(const Consensus::Params& params, int currentHeight); +std::string DisplayDuration(int64_t time, DurationFormat format); void TriggerRefresh();