From 047aec1e0eafd0e5f9a80d841afd309730d8d4f8 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 26 Jun 2017 16:16:08 -0700 Subject: [PATCH 1/3] Add block download progress to metrics UI --- src/gtest/test_metrics.cpp | 25 +++++++++++++++++++++++++ src/metrics.cpp | 35 ++++++++++++++++++++++++++++++++++- src/metrics.h | 3 +++ 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp index c199b323e..8e0aa9e02 100644 --- a/src/gtest/test_metrics.cpp +++ b/src/gtest/test_metrics.cpp @@ -92,3 +92,28 @@ TEST(Metrics, GetLocalSolPS) { SetMockTime(104); EXPECT_EQ(1, GetLocalSolPS()); } + +TEST(Metrics, EstimateNetHeightInner) { + // Ensure that the (rounded) current height is returned if the tip is current + SetMockTime(15000); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + SetMockTime(15150); + EXPECT_EQ(100, EstimateNetHeightInner(101, 14400, 50, 7500, 150)); + + // Ensure that correct estimates are returned if the tip is in the past + SetMockTime(15300); // Tip is 2 blocks behind + EXPECT_EQ(100, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + SetMockTime(15900); // Tip is 6 blocks behind + EXPECT_EQ(110, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + + // More complex calculations: + SetMockTime(20000); + // - Checkpoint spacing: 200 + // -> Average spacing: 175 + // -> estimated height: 127 -> 130 + EXPECT_EQ(130, EstimateNetHeightInner(100, 14250, 50, 5250, 150)); + // - Checkpoint spacing: 50 + // -> Average spacing: 100 + // -> estimated height: 153 -> 150 + EXPECT_EQ(150, EstimateNetHeightInner(100, 14250, 50, 12000, 150)); +} diff --git a/src/metrics.cpp b/src/metrics.cpp index af8052049..b10e94630 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -5,6 +5,7 @@ #include "metrics.h" #include "chainparams.h" +#include "checkpoints.h" #include "main.h" #include "ui_interface.h" #include "util.h" @@ -103,6 +104,30 @@ double GetLocalSolPS() return miningTimer.rate(solutionTargetChecks); } +int EstimateNetHeightInner(int height, int64_t tipmediantime, + int heightLastCheckpoint, int64_t timeLastCheckpoint, + int64_t targetSpacing) +{ + // We average the target spacing with the observed spacing to the last + // checkpoint, and use that to estimate the current network height. + int medianHeight = height - CBlockIndex::nMedianTimeSpan / 2; + double checkpointSpacing = (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint); + double averageSpacing = (targetSpacing + checkpointSpacing) / 2; + int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing); + // Round to nearest ten to reduce noise + return ((netheight + 5) / 10) * 10; +} + +int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParams) +{ + auto checkpointData = chainParams.Checkpoints(); + return EstimateNetHeightInner( + height, tipmediantime, + Checkpoints::GetTotalBlocksEstimate(checkpointData), + checkpointData.nTimeLastCheckpoint, + chainParams.GetConsensus().nPowTargetSpacing); +} + void TriggerRefresh() { *nNextRefresh = GetTime(); @@ -169,17 +194,25 @@ int printStats(bool mining) int lines = 4; int height; + int64_t tipmediantime; size_t connections; int64_t netsolps; { LOCK2(cs_main, cs_vNodes); height = chainActive.Height(); + tipmediantime = chainActive.Tip()->GetMedianTimePast(); connections = vNodes.size(); netsolps = GetNetworkHashPS(120, -1); } auto localsolps = GetLocalSolPS(); - std::cout << " " << _("Block height") << " | " << height << std::endl; + if (IsInitialBlockDownload()) { + int netheight = EstimateNetHeight(height, tipmediantime, Params()); + int downloadPercent = height * 100 / netheight; + std::cout << " " << _("Downloading blocks") << " | " << height << " / ~" << netheight << " (" << downloadPercent << "%)" << std::endl; + } else { + std::cout << " " << _("Block height") << " | " << height << std::endl; + } std::cout << " " << _("Connections") << " | " << connections << std::endl; std::cout << " " << _("Network solution rate") << " | " << netsolps << " Sol/s" << std::endl; if (mining && miningTimer.running()) { diff --git a/src/metrics.h b/src/metrics.h index 701306a4a..7f7efdea6 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -63,6 +63,9 @@ void TrackMinedBlock(uint256 hash); void MarkStartTime(); double GetLocalSolPS(); +int EstimateNetHeightInner(int height, int64_t tipmediantime, + int heightLastCheckpoint, int64_t timeLastCheckpoint, + int64_t targetSpacing); void TriggerRefresh(); From 4a651837be5840e3f023a03a9a91c6537009153b Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 Jul 2017 10:35:46 +0000 Subject: [PATCH 2/3] Correct and extend EstimateNetHeightInner tests Corrections are to the median block times, which were generated by subtracting CBlockIndex::nMedianTimeSpan / 2 from the block height and then multiplying by the target spacing. GetMedianTimePast() takes an array sorted by std::sort() and returns element CBlockIndex::nMedianTimeSpan / 2, meaning that if CBlockIndex::nMedianTimeSpan is odd (which it is), there is an out-by-one error in the subtraction. --- src/gtest/test_metrics.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp index 8e0aa9e02..8f313b5d2 100644 --- a/src/gtest/test_metrics.cpp +++ b/src/gtest/test_metrics.cpp @@ -96,24 +96,40 @@ TEST(Metrics, GetLocalSolPS) { TEST(Metrics, EstimateNetHeightInner) { // Ensure that the (rounded) current height is returned if the tip is current SetMockTime(15000); - EXPECT_EQ(100, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); SetMockTime(15150); - EXPECT_EQ(100, EstimateNetHeightInner(101, 14400, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(101, 14250, 50, 7500, 150)); // Ensure that correct estimates are returned if the tip is in the past SetMockTime(15300); // Tip is 2 blocks behind - EXPECT_EQ(100, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); SetMockTime(15900); // Tip is 6 blocks behind - EXPECT_EQ(110, EstimateNetHeightInner(100, 14250, 50, 7500, 150)); + EXPECT_EQ(110, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); + + // Check estimates during resync + SetMockTime(15000); + EXPECT_EQ(100, EstimateNetHeightInner( 0, 0, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 7, 600, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 8, 600, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(10, 750, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(11, 900, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(20, 2100, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(49, 6450, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(50, 6600, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(51, 6750, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(55, 7350, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(56, 7500, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(57, 7650, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(75, 10350, 50, 7500, 150)); // More complex calculations: SetMockTime(20000); // - Checkpoint spacing: 200 // -> Average spacing: 175 // -> estimated height: 127 -> 130 - EXPECT_EQ(130, EstimateNetHeightInner(100, 14250, 50, 5250, 150)); + EXPECT_EQ(130, EstimateNetHeightInner(100, 14100, 50, 5250, 150)); // - Checkpoint spacing: 50 // -> Average spacing: 100 // -> estimated height: 153 -> 150 - EXPECT_EQ(150, EstimateNetHeightInner(100, 14250, 50, 12000, 150)); + EXPECT_EQ(150, EstimateNetHeightInner(100, 14100, 50, 12000, 150)); } From 92bfde0edf5537576121c20ad8e5466fd9fa1e98 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Fri, 28 Jul 2017 10:50:07 +0000 Subject: [PATCH 3/3] Improve network height estimation --- src/gtest/test_metrics.cpp | 38 +++++++++++++++++++------------------- src/metrics.cpp | 14 ++++++++++---- src/metrics.h | 2 +- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/gtest/test_metrics.cpp b/src/gtest/test_metrics.cpp index 8f313b5d2..143fe46d6 100644 --- a/src/gtest/test_metrics.cpp +++ b/src/gtest/test_metrics.cpp @@ -96,40 +96,40 @@ TEST(Metrics, GetLocalSolPS) { TEST(Metrics, EstimateNetHeightInner) { // Ensure that the (rounded) current height is returned if the tip is current SetMockTime(15000); - EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); SetMockTime(15150); - EXPECT_EQ(100, EstimateNetHeightInner(101, 14250, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(101, 14250, 50, 7500, 0, 150)); // Ensure that correct estimates are returned if the tip is in the past SetMockTime(15300); // Tip is 2 blocks behind - EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); SetMockTime(15900); // Tip is 6 blocks behind - EXPECT_EQ(110, EstimateNetHeightInner(100, 14100, 50, 7500, 150)); + EXPECT_EQ(110, EstimateNetHeightInner(100, 14100, 50, 7500, 0, 150)); // Check estimates during resync SetMockTime(15000); - EXPECT_EQ(100, EstimateNetHeightInner( 0, 0, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner( 7, 600, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner( 8, 600, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(10, 750, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(11, 900, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(20, 2100, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(49, 6450, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(50, 6600, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(51, 6750, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(55, 7350, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(56, 7500, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(57, 7650, 50, 7500, 150)); - EXPECT_EQ(100, EstimateNetHeightInner(75, 10350, 50, 7500, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 0, 0, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 7, 600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner( 8, 600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(10, 750, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(11, 900, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(20, 2100, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(49, 6450, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(50, 6600, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(51, 6750, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(55, 7350, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(56, 7500, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(57, 7650, 50, 7500, 0, 150)); + EXPECT_EQ(100, EstimateNetHeightInner(75, 10350, 50, 7500, 0, 150)); // More complex calculations: SetMockTime(20000); // - Checkpoint spacing: 200 // -> Average spacing: 175 // -> estimated height: 127 -> 130 - EXPECT_EQ(130, EstimateNetHeightInner(100, 14100, 50, 5250, 150)); + EXPECT_EQ(130, EstimateNetHeightInner(100, 14100, 50, 5250, 0, 150)); // - Checkpoint spacing: 50 // -> Average spacing: 100 // -> estimated height: 153 -> 150 - EXPECT_EQ(150, EstimateNetHeightInner(100, 14100, 50, 12000, 150)); + EXPECT_EQ(150, EstimateNetHeightInner(100, 14100, 50, 12000, 0, 150)); } diff --git a/src/metrics.cpp b/src/metrics.cpp index b10e94630..1bac2c9c0 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -106,12 +106,17 @@ double GetLocalSolPS() int EstimateNetHeightInner(int height, int64_t tipmediantime, int heightLastCheckpoint, int64_t timeLastCheckpoint, - int64_t targetSpacing) + int64_t genesisTime, int64_t targetSpacing) { // We average the target spacing with the observed spacing to the last - // checkpoint, and use that to estimate the current network height. - int medianHeight = height - CBlockIndex::nMedianTimeSpan / 2; - double checkpointSpacing = (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint); + // checkpoint (either from below or above depending on the current height), + // and use that to estimate the current network height. + int medianHeight = height > CBlockIndex::nMedianTimeSpan ? + height - (1 + ((CBlockIndex::nMedianTimeSpan - 1) / 2)) : + height / 2; + double checkpointSpacing = medianHeight > heightLastCheckpoint ? + (double (tipmediantime - timeLastCheckpoint)) / (medianHeight - heightLastCheckpoint) : + (double (timeLastCheckpoint - genesisTime)) / heightLastCheckpoint; double averageSpacing = (targetSpacing + checkpointSpacing) / 2; int netheight = medianHeight + ((GetTime() - tipmediantime) / averageSpacing); // Round to nearest ten to reduce noise @@ -125,6 +130,7 @@ int EstimateNetHeight(int height, int64_t tipmediantime, CChainParams chainParam height, tipmediantime, Checkpoints::GetTotalBlocksEstimate(checkpointData), checkpointData.nTimeLastCheckpoint, + chainParams.GenesisBlock().nTime, chainParams.GetConsensus().nPowTargetSpacing); } diff --git a/src/metrics.h b/src/metrics.h index 7f7efdea6..2d60d30ca 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -65,7 +65,7 @@ void MarkStartTime(); double GetLocalSolPS(); int EstimateNetHeightInner(int height, int64_t tipmediantime, int heightLastCheckpoint, int64_t timeLastCheckpoint, - int64_t targetSpacing); + int64_t genesisTime, int64_t targetSpacing); void TriggerRefresh();