From fa76b378e4b218fb4853088328b9b488de18dcd2 Mon Sep 17 00:00:00 2001 From: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Date: Fri, 16 May 2025 13:32:23 +0200 Subject: [PATCH] rpc: Round verificationprogress to exactly 1 for a recent tip This requires a new lock annotation, but all relevant callers already held the lock. --- src/node/interfaces.cpp | 4 +++- src/validation.cpp | 16 +++++++++++++--- src/validation.h | 2 +- test/functional/rpc_blockchain.py | 6 ++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index b5c74a97f6d..641589d7cc0 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -326,7 +326,8 @@ public: } double getVerificationProgress() override { - return chainman().GuessVerificationProgress(WITH_LOCK(chainman().GetMutex(), return chainman().ActiveChain().Tip())); + LOCK(chainman().GetMutex()); + return chainman().GuessVerificationProgress(chainman().ActiveTip()); } bool isInitialBlockDownload() override { @@ -409,6 +410,7 @@ public: std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override { return MakeSignalHandler(::uiInterface.NotifyBlockTip_connect([fn, this](SynchronizationState sync_state, const CBlockIndex* block) { + LOCK(chainman().GetMutex()); fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, chainman().GuessVerificationProgress(block)); })); diff --git a/src/validation.cpp b/src/validation.cpp index a2e1b8c195d..3ace1725522 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5558,10 +5558,9 @@ bool Chainstate::ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) return ret; } -//! 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 m_chain_tx_count might be unset) double ChainstateManager::GuessVerificationProgress(const CBlockIndex* pindex) const { + AssertLockHeld(GetMutex()); const ChainTxData& data{GetParams().TxData()}; if (pindex == nullptr) { return 0.0; @@ -5573,13 +5572,24 @@ double ChainstateManager::GuessVerificationProgress(const CBlockIndex* pindex) c } const int64_t nNow{TicksSinceEpoch(NodeClock::now())}; + const auto block_time{ + (Assume(m_best_header) && std::abs(nNow - pindex->GetBlockTime()) <= Ticks(2h) && + Assume(m_best_header->nHeight >= pindex->nHeight)) ? + // When the header is known to be recent, switch to a height-based + // approach. This ensures the returned value is quantized when + // close to "1.0", because some users expect it to be. This also + // avoids relying too much on the exact miner-set timestamp, which + // may be off. + nNow - (m_best_header->nHeight - pindex->nHeight) * GetConsensus().nPowTargetSpacing : + pindex->GetBlockTime(), + }; double fTxTotal; if (pindex->m_chain_tx_count <= data.tx_count) { fTxTotal = data.tx_count + (nNow - data.nTime) * data.dTxRate; } else { - fTxTotal = pindex->m_chain_tx_count + (nNow - pindex->GetBlockTime()) * data.dTxRate; + fTxTotal = pindex->m_chain_tx_count + (nNow - block_time) * data.dTxRate; } return std::min(pindex->m_chain_tx_count / fTxTotal, 1.0); diff --git a/src/validation.h b/src/validation.h index 5b488ba4b53..5ff23b60532 100644 --- a/src/validation.h +++ b/src/validation.h @@ -1147,7 +1147,7 @@ public: bool IsInitialBlockDownload() const; /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ - double GuessVerificationProgress(const CBlockIndex* pindex) const; + double GuessVerificationProgress(const CBlockIndex* pindex) const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()); /** * Import blocks from an external file diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index a323d5eebc6..5f6edfd94b1 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -286,7 +286,13 @@ class BlockchainTest(BitcoinTestFramework): future = 2 * 60 * 60 self.nodes[0].setmocktime(self.nodes[0].getblockchaininfo()["time"] + future + 1) assert_greater_than(1, self.nodes[0].getblockchaininfo()["verificationprogress"]) + + self.log.info("Check that verificationprogress is exactly 1 for a recent block tip") self.nodes[0].setmocktime(self.nodes[0].getblockchaininfo()["time"] + future) + assert_equal(1, self.nodes[0].getblockchaininfo()["verificationprogress"]) + + self.log.info("Check that verificationprogress is less than 1 as soon as a new header comes in") + self.nodes[0].submitheader(self.generateblock(self.nodes[0], output="raw(55)", transactions=[], submit=False, sync_fun=self.no_op)["hex"]) assert_greater_than(1, self.nodes[0].getblockchaininfo()["verificationprogress"]) def _test_y2106(self):