diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index d95b41657c..1a63aa76a8 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -179,7 +179,7 @@ class NodeImpl : public Node LOCK(::cs_main); tip = ::chainActive.Tip(); } - return GuessVerificationProgress(Params().TxData(), tip); + return GuessVerificationProgress(tip, Params().GetConsensus().nPowTargetSpacing); } bool isInitialBlockDownload() override { return IsInitialBlockDownload(); } bool getReindex() override { return ::fReindex; } @@ -266,7 +266,7 @@ class NodeImpl : public Node { return MakeHandler(::uiInterface.NotifyBlockTip_connect([fn](bool initial_download, const CBlockIndex* block) { fn(initial_download, block->nHeight, block->GetBlockTime(), - GuessVerificationProgress(Params().TxData(), block)); + GuessVerificationProgress(block, Params().GetConsensus().nPowTargetSpacing)); })); } std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override @@ -274,7 +274,7 @@ class NodeImpl : public Node return MakeHandler( ::uiInterface.NotifyHeaderTip_connect([fn](bool initial_download, const CBlockIndex* block) { fn(initial_download, block->nHeight, block->GetBlockTime(), - GuessVerificationProgress(Params().TxData(), block)); + GuessVerificationProgress(block, Params().GetConsensus().nPowTargetSpacing)); })); } }; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 4e19c64ff2..b057183974 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1292,7 +1292,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.pushKV("difficulty", (double)GetDifficulty(chainActive.Tip())); } obj.pushKV("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast()); - obj.pushKV("verificationprogress", GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); + obj.pushKV("verificationprogress", GuessVerificationProgress(chainActive.Tip(), chainparams.GetConsensus().nPowTargetSpacing)); obj.pushKV("initialblockdownload", IsInitialBlockDownload()); if (!g_signed_blocks) { obj.pushKV("chainwork", chainActive.Tip()->nChainWork.GetHex()); diff --git a/src/validation.cpp b/src/validation.cpp index c5fdf03d50..2e54069292 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2394,7 +2394,8 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, (unsigned long)pindexNew->nChainTx, FormatISO8601DateTime(pindexNew->GetBlockTime()), - GuessVerificationProgress(chainParams.TxData(), pindexNew), pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); + GuessVerificationProgress(pindexNew, chainParams.GetConsensus().nPowTargetSpacing), + pcoinsTip->DynamicMemoryUsage() * (1.0 / (1<<20)), pcoinsTip->GetCacheSize()); if (!warningMessages.empty()) LogPrintf(" warning='%s'", warningMessages); /* Continued */ LogPrintf("\n"); @@ -4203,10 +4204,10 @@ bool LoadChainTip(const CChainParams& chainparams) g_chainstate.PruneBlockIndexCandidates(); - LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%f\n", + LogPrintf("Loaded best chain: hashBestChain=%s height=%d date=%s progress=%.3f\n", chainActive.Tip()->GetBlockHash().ToString(), chainActive.Height(), FormatISO8601DateTime(chainActive.Tip()->GetBlockTime()), - GuessVerificationProgress(chainparams.TxData(), chainActive.Tip())); + GuessVerificationProgress(chainActive.Tip(), chainparams.GetConsensus().nPowTargetSpacing)); return true; } @@ -5067,23 +5068,22 @@ bool DumpMempool(void) return true; } -//! Guess how far we are in the verification process at the given block index -//! require cs_main if pindex has not been validated yet (because nChainTx might be unset) -double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) { - if (pindex == nullptr) +//! Guess how far we are in the verification process at the given block index. +//! Since we have signed fixed-interval blocks, estimating progress is a very easy. +//! We can extrapolate the last block time to the current time to estimate how many more blocks +//! we expect. +double GuessVerificationProgress(const CBlockIndex* pindex, int64_t blockInterval) { + if (pindex == NULL || pindex->nHeight < 1) { return 0.0; - - int64_t nNow = time(nullptr); - - double fTxTotal; - - if (pindex->nChainTx <= data.nTxCount) { - fTxTotal = data.nTxCount + (nNow - data.nTime) * data.dTxRate; - } else { - fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; } - return pindex->nChainTx / fTxTotal; + int64_t nNow = GetTime(); + int64_t moreBlocksExpected = (nNow - pindex->GetBlockTime()) / blockInterval; + double progress = (pindex->nHeight + 0.0) / (pindex->nHeight + moreBlocksExpected); + // Round to 3 digits to avoid 0.999999 when finished. + progress = ceil(progress * 1000.0) / 1000.0; + // Avoid higher than one if last block is newer than current time. + return std::min(1.0, progress); } class CMainCleanup diff --git a/src/validation.h b/src/validation.h index 6e3b9e3ec4..f1a0ac6296 100644 --- a/src/validation.h +++ b/src/validation.h @@ -289,7 +289,7 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ -double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex); +double GuessVerificationProgress(const CBlockIndex* pindex, int64_t blockInterval); /** Calculate the amount of disk space the block & undo files currently use */ uint64_t CalculateCurrentUsage(); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 88b1b6c093..697c726a9e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1708,12 +1708,12 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock double progress_end; { LOCK(cs_main); - progress_begin = GuessVerificationProgress(chainParams.TxData(), pindex); + progress_begin = GuessVerificationProgress(pindex, chainParams.GetConsensus().nPowTargetSpacing); if (pindexStop == nullptr) { tip = chainActive.Tip(); - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); + progress_end = GuessVerificationProgress(tip, chainParams.GetConsensus().nPowTargetSpacing); } else { - progress_end = GuessVerificationProgress(chainParams.TxData(), pindexStop); + progress_end = GuessVerificationProgress(pindexStop, chainParams.GetConsensus().nPowTargetSpacing); } } double progress_current = progress_begin; @@ -1748,11 +1748,11 @@ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, CBlock { LOCK(cs_main); pindex = chainActive.Next(pindex); - progress_current = GuessVerificationProgress(chainParams.TxData(), pindex); + progress_current = GuessVerificationProgress(pindex, chainParams.GetConsensus().nPowTargetSpacing); if (pindexStop == nullptr && tip != chainActive.Tip()) { tip = chainActive.Tip(); // in case the tip has changed, update progress max - progress_end = GuessVerificationProgress(chainParams.TxData(), tip); + progress_end = GuessVerificationProgress(tip, chainParams.GetConsensus().nPowTargetSpacing); } } } diff --git a/test/functional/feature_progress.py b/test/functional/feature_progress.py new file mode 100755 index 0000000000..02a0e774cd --- /dev/null +++ b/test/functional/feature_progress.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# +# Test progress code +# + +import time + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + Decimal, +) + +def assert_close(f1, f2): + assert(abs(Decimal(f1)-f2) < 0.1) + +class ProgressTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [["-debug", "-con_npowtargetspacing=1", "-maxtimeadjustment=0"]] * self.num_nodes + + def setup_network(self): + self.setup_nodes() + self.is_network_split = True + self.starttime = int(time.time()) + + def setmocktime(self, ntime): + for node in self.nodes: + node.setmocktime(self.starttime + ntime) + + def run_test(self): + node1 = self.nodes[0] + node2 = self.nodes[1] + self.setmocktime(0) + + blocks = [] + for i in range(10): + self.setmocktime(i) + blocks.extend(node1.generate(1)) + + self.setmocktime(19) + assert_close(0.5, node1.getblockchaininfo()["verificationprogress"]) + + assert(node2.getblockchaininfo()["initialblockdownload"]) + + self.setmocktime(10) + for i in range(10): + node2.submitblock(node1.getblock(blocks[i], False)) + progress = node2.getblockchaininfo()["verificationprogress"] + assert_close(i/10.0, progress) + + assert(not node2.getblockchaininfo()["initialblockdownload"]) + +if __name__ == '__main__': + ProgressTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a9f7ebbff4..3021ce0550 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -73,6 +73,7 @@ 'feature_default_asset_name.py', 'feature_assetsdir.py', 'feature_initial_reissuance_token.py', + 'feature_progress.py', # Longest test should go first, to favor running tests in parallel 'wallet_hd.py', 'wallet_backup.py',