mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-15 09:09:46 +02:00
Merge bitcoin/bitcoin#33259: rpc, logging: add backgroundvalidation to getblockchaininfo
25f69d970arelease note (Pol Espinasa)af629821cftest: add background validation test for getblockchaininfo (Pol Espinasa)a3d6f32a39rpc, log: add backgroundvalidation to getblockchaininfo (Pol Espinasa)5b2e4c4a88log: update progress calculations for background validation (Pol Espinasa) Pull request description: `getblockchaininfo` returns `verificationprogress=1` and `initialblockdownload=false` even if there's background validation. This PR adds information about background validation to rpc `getblockchaininfo` in a similar way to `validationprogress` does. If assume utxo was used the output of a "sync" node performing background validation: ``` $ ./build/bin/bitcoin-cli getblockchaininfo ... "mediantime": 1756933740, "verificationprogress": 1, "initialblockdownload": false, "backgroundvalidation": { "snapshotheight": 880000, "blocks": 527589, "bestblockhash": "0000000000000000002326308420fa5ccd28a9155217f4d1896ab443d84148fa", "mediantime": 1529076654, "chainwork": "0000000000000000000000000000000000000000020c92fab9e5e1d8ed2d8dbc", "verificationprogress": 0.2815790617966284 }, "chainwork": "0000000000000000000000000000000000000000df97866c410b0302954919d2", "size_on_disk": 61198817285, ... ``` If assume utxo was not used the progress is hidden: ``` $ ./build/bin/bitcoin-cli getblockchaininfo ... "mediantime": 1756245700, "verificationprogress": 1, "initialblockdownload": false, "chainwork": "00000000000000000000000000000000000000000000000000000656d6bb052b", "size_on_disk": 3964972194, ... ``` The PR also updates the way we estimate the verification progress returning a 100% on the snapshot block and not on the tip as we will stop doing background validation when reaching it. ACKs for top commit: fjahr: ACK25f69d970adanielabrozzoni: ACK25f69d970aachow101: ACK25f69d970asedited: ACK25f69d970aTree-SHA512: 5e5e08fd39af5f764962b862bc6d8257b0d2175fe920d4b79dc5105578fd4ebe08aee2fe9bfa5c9cad5d7610197a435ebaac0de23e7a5efa740dfea031a8a9d4
This commit is contained in:
4
doc/release-notes-33259.md
Normal file
4
doc/release-notes-33259.md
Normal file
@@ -0,0 +1,4 @@
|
||||
RPC
|
||||
---
|
||||
|
||||
The `getblockchaininfo` RPC now exposes progress for background validation if the `assumeutxo` feature is used. Once a node has synced from snapshot to tip, `verificationprogress` returns 1.0 and `initialblockdownload` false even though the node may still be validating blocks in the background. A new object, `backgroundvalidation`, provides details about the snapshot being validated, including snapshot height, number of blocks processed, best block hash, chainwork, median time, and verification progress.
|
||||
@@ -1374,12 +1374,21 @@ RPCHelpMan getblockchaininfo()
|
||||
{RPCResult::Type::NUM, "headers", "the current number of headers we have validated"},
|
||||
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block"},
|
||||
{RPCResult::Type::STR_HEX, "bits", "nBits: compact representation of the block difficulty target"},
|
||||
{RPCResult::Type::STR_HEX, "target", "The difficulty target"},
|
||||
{RPCResult::Type::STR_HEX, "target", "the difficulty target"},
|
||||
{RPCResult::Type::NUM, "difficulty", "the current difficulty"},
|
||||
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
|
||||
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
|
||||
{RPCResult::Type::NUM_TIME, "time", "the block time expressed in " + UNIX_EPOCH_TIME},
|
||||
{RPCResult::Type::NUM_TIME, "mediantime", "the median block time expressed in " + UNIX_EPOCH_TIME},
|
||||
{RPCResult::Type::NUM, "verificationprogress", "estimate of verification progress [0..1]"},
|
||||
{RPCResult::Type::BOOL, "initialblockdownload", "(debug information) estimate of whether this node is in Initial Block Download mode"},
|
||||
{RPCResult::Type::OBJ, "backgroundvalidation", /*optional=*/true, "state info regarding background validation process",
|
||||
{
|
||||
{RPCResult::Type::NUM, "snapshotheight", "the height of the snapshot block. Background validation verifies the chain from genesis up to this height"},
|
||||
{RPCResult::Type::NUM, "blocks", "the height of the most-work background fully-validated chain. The genesis block has height 0"},
|
||||
{RPCResult::Type::STR, "bestblockhash", "the hash of the currently best block validated in the background"},
|
||||
{RPCResult::Type::NUM_TIME, "mediantime", "the median block time expressed in " + UNIX_EPOCH_TIME},
|
||||
{RPCResult::Type::NUM, "verificationprogress", "estimate of background verification progress [0..1]"},
|
||||
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in background validated chain, in hexadecimal"},
|
||||
}},
|
||||
{RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"},
|
||||
{RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"},
|
||||
{RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"},
|
||||
@@ -1420,6 +1429,19 @@ RPCHelpMan getblockchaininfo()
|
||||
obj.pushKV("mediantime", tip.GetMedianTimePast());
|
||||
obj.pushKV("verificationprogress", chainman.GuessVerificationProgress(&tip));
|
||||
obj.pushKV("initialblockdownload", chainman.IsInitialBlockDownload());
|
||||
auto historical_blocks{chainman.GetHistoricalBlockRange()};
|
||||
if (historical_blocks) {
|
||||
UniValue background_validation(UniValue::VOBJ);
|
||||
const CBlockIndex& btip{*CHECK_NONFATAL(historical_blocks->first)};
|
||||
const CBlockIndex& btarget{*CHECK_NONFATAL(historical_blocks->second)};
|
||||
background_validation.pushKV("snapshotheight", btarget.nHeight);
|
||||
background_validation.pushKV("blocks", btip.nHeight);
|
||||
background_validation.pushKV("bestblockhash", btip.GetBlockHash().GetHex());
|
||||
background_validation.pushKV("mediantime", btip.GetMedianTimePast());
|
||||
background_validation.pushKV("chainwork", btip.nChainWork.GetHex());
|
||||
background_validation.pushKV("verificationprogress", chainman.GetBackgroundVerificationProgress(btip));
|
||||
obj.pushKV("backgroundvalidation", std::move(background_validation));
|
||||
}
|
||||
obj.pushKV("chainwork", tip.nChainWork.GetHex());
|
||||
obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage());
|
||||
obj.pushKV("pruned", chainman.m_blockman.IsPruneMode());
|
||||
|
||||
@@ -2861,7 +2861,8 @@ static void UpdateTipLog(
|
||||
const CBlockIndex* tip,
|
||||
const std::string& func_name,
|
||||
const std::string& prefix,
|
||||
const std::string& warning_messages) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
const std::string& warning_messages,
|
||||
const bool background_validation) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||
{
|
||||
|
||||
AssertLockHeld(::cs_main);
|
||||
@@ -2872,7 +2873,7 @@ static void UpdateTipLog(
|
||||
tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
|
||||
log(tip->nChainWork.getdouble()) / log(2.0), tip->m_chain_tx_count,
|
||||
FormatISO8601DateTime(tip->GetBlockTime()),
|
||||
chainman.GuessVerificationProgress(tip),
|
||||
background_validation ? chainman.GetBackgroundVerificationProgress(*tip) : chainman.GuessVerificationProgress(tip),
|
||||
coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
|
||||
coins_tip.GetCacheSize(),
|
||||
!warning_messages.empty() ? strprintf(" warning='%s'", warning_messages) : "");
|
||||
@@ -2889,7 +2890,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
||||
// Only log every so often so that we don't bury log messages at the tip.
|
||||
constexpr int BACKGROUND_LOG_INTERVAL = 2000;
|
||||
if (pindexNew->nHeight % BACKGROUND_LOG_INTERVAL == 0) {
|
||||
UpdateTipLog(m_chainman, coins_tip, pindexNew, __func__, "[background validation] ", "");
|
||||
UpdateTipLog(m_chainman, coins_tip, pindexNew, __func__, "[background validation] ", "", /*background_validation=*/true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2912,7 +2913,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
||||
}
|
||||
}
|
||||
UpdateTipLog(m_chainman, coins_tip, pindexNew, __func__, "",
|
||||
util::Join(warning_messages, Untranslated(", ")).original);
|
||||
util::Join(warning_messages, Untranslated(", ")).original, /*background_validation=*/false);
|
||||
}
|
||||
|
||||
/** Disconnect m_chain's tip.
|
||||
@@ -5523,6 +5524,19 @@ double ChainstateManager::GuessVerificationProgress(const CBlockIndex* pindex) c
|
||||
return std::min<double>(pindex->m_chain_tx_count / fTxTotal, 1.0);
|
||||
}
|
||||
|
||||
double ChainstateManager::GetBackgroundVerificationProgress(const CBlockIndex& pindex) const
|
||||
{
|
||||
AssertLockHeld(GetMutex());
|
||||
Assert(HistoricalChainstate());
|
||||
auto target_block = HistoricalChainstate()->TargetBlock();
|
||||
|
||||
if (pindex.m_chain_tx_count == 0 || target_block->m_chain_tx_count == 0) {
|
||||
LogDebug(BCLog::VALIDATION, "[background validation] Block %d has unset m_chain_tx_count. Unable to estimate verification progress.", pindex.nHeight);
|
||||
return 0.0;
|
||||
}
|
||||
return static_cast<double>(pindex.m_chain_tx_count) / static_cast<double>(target_block->m_chain_tx_count);
|
||||
}
|
||||
|
||||
Chainstate& ChainstateManager::InitializeChainstate(CTxMemPool* mempool)
|
||||
{
|
||||
AssertLockHeld(::cs_main);
|
||||
|
||||
@@ -1194,9 +1194,15 @@ public:
|
||||
/** Check whether we are doing an initial block download (synchronizing from disk or network) */
|
||||
bool IsInitialBlockDownload() const noexcept;
|
||||
|
||||
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
|
||||
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip).
|
||||
* This is also the case in the assumeutxo context, meaning that the progress reported for
|
||||
* the snapshot chainstate may suggest that all historical blocks have already been verified
|
||||
* even though that may not actually be the case. */
|
||||
double GuessVerificationProgress(const CBlockIndex* pindex) const EXCLUSIVE_LOCKS_REQUIRED(GetMutex());
|
||||
|
||||
/** Guess background verification progress in case assume-utxo was used (as a fraction between 0.0=genesis and 1.0=snapshot blocks). */
|
||||
double GetBackgroundVerificationProgress(const CBlockIndex& pindex) const EXCLUSIVE_LOCKS_REQUIRED(GetMutex());
|
||||
|
||||
/**
|
||||
* Import blocks from an external file
|
||||
*
|
||||
|
||||
@@ -542,6 +542,30 @@ class AssumeutxoTest(BitcoinTestFramework):
|
||||
assert_equal(utxo_info['height'], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(utxo_info['bestblock'], snapshot_hash)
|
||||
|
||||
self.log.info("Check that getblockchaininfo returns information about the background validation process")
|
||||
expected_keys = [
|
||||
"snapshotheight",
|
||||
"blocks",
|
||||
"bestblockhash",
|
||||
"mediantime",
|
||||
"chainwork",
|
||||
"verificationprogress"
|
||||
]
|
||||
res = n1.getblockchaininfo()
|
||||
assert "backgroundvalidation" in res.keys()
|
||||
bv_res = res["backgroundvalidation"]
|
||||
assert_equal(sorted(expected_keys), sorted(bv_res.keys()))
|
||||
assert_equal(bv_res["snapshotheight"], SNAPSHOT_BASE_HEIGHT)
|
||||
assert_equal(bv_res["blocks"], START_HEIGHT)
|
||||
assert_equal(bv_res["bestblockhash"], n1.getblockhash(START_HEIGHT))
|
||||
block = n1.getblockheader(bv_res["bestblockhash"])
|
||||
assert_equal(bv_res["mediantime"], block["mediantime"])
|
||||
assert_equal(bv_res["chainwork"], block["chainwork"])
|
||||
background_tx_count = n1.getchaintxstats(blockhash=bv_res["bestblockhash"])["txcount"]
|
||||
snapshot_tx_count = n1.getchaintxstats(blockhash=snapshot_hash)["txcount"]
|
||||
expected_verification_progress = background_tx_count / snapshot_tx_count
|
||||
assert_approx(bv_res["verificationprogress"], expected_verification_progress, vspan=0.01)
|
||||
|
||||
# find coinbase output at snapshot height on node0 and scan for it on node1,
|
||||
# where the block is not available, but the snapshot was loaded successfully
|
||||
coinbase_tx = n0.getblock(snapshot_hash, verbosity=2)['tx'][0]
|
||||
|
||||
@@ -170,6 +170,9 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
assert res['pruned']
|
||||
assert not res['automatic_pruning']
|
||||
|
||||
# check background validation is not present when we are not using assumeutxo
|
||||
assert "backgroundvalidation" not in res.keys()
|
||||
|
||||
self.restart_node(0, ['-stopatheight=207'])
|
||||
res = self.nodes[0].getblockchaininfo()
|
||||
# should have exact keys
|
||||
|
||||
Reference in New Issue
Block a user