mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-20 07:09:15 +01:00
Merge bitcoin/bitcoin#33336: log: print every script verification state change
45bd891465log: split assumevalid ancestry-failure-reason message (Lőrinc)6c13a38ab5log: separate script verification reasons (Lőrinc)f2ea6f04e7refactor: untangle assumevalid decision branches (Lőrinc)9bc298556cvalidation: log initial script verification state (Lőrinc)4fad4e992ctest: add assumevalid scenarios scaffold (Lőrinc)91ac64b0a6log: reword `signature validations` to `script verification` in `assumevalid` log (Lőrinc) Pull request description: ### Summary Users can encounter cases where script checks are unexpectedly enabled (e.g. after reindex, or when `assumevalid`/`minimumchainwork` gates fail). Without an explicit line, they must infer state from the absence of a message, which is incomplete and error-prone. The existing "Assuming ancestors of block …" line does not reliably indicate whether script checks are actually enabled, which makes debugging/benchmarking confusing. ### What this changes We make the initial **script-verification** state explicit and log **why** checks are enabled to avoid confusion. * Always log the first script-verification state on startup, **before** the first `UpdateTip`. * Flatten the nested `assumevalid` conditionals into a linear gating sequence for readability. * Extend the functional test to assert the old behavior with the new reason strings. This is a **logging-only** test change it shouldn't change any other behavior. ### Example output The state (with reason) is logged at startup and whenever the reason changes, e.g.: * `Disabling script verification at block #904336 (000000000000000000014106b2082b1a18aaf3091e8b337c6fed110db8c56620).` * `Enabling script verification at block #912527 (000000000000000000010bb6aa3ecabd7d41738463b6c6621776c2e40dbe738a): block too recent relative to best header.` * `Enabling script verification at block #912684 (00000000000000000001375cf7b90b2b86e559d05ed92ca764d376702ead3858): block height above assumevalid height.` ------ Follow-up to https://github.com/bitcoin/bitcoin/pull/32975#discussion_r2329269037 ACKs for top commit: Eunovo: re-ACK45bd891465achow101: ACK45bd891465hodlinator: re-ACK45bd891465yuvicc: ACK45bd891465andrewtoth: ACK45bd891465ajtowns: ACK45bd891465Tree-SHA512: 58328d7c418a6fe18f1c7fe1dd31955bb6fce8b928b0df693f6200807932eb5933146300af886a80a1d922228d93faf531145186dae55ad4ad1f691970732eca
This commit is contained in:
@@ -2422,34 +2422,43 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool fScriptChecks = true;
|
||||
if (!m_chainman.AssumedValidBlock().IsNull()) {
|
||||
const char* script_check_reason;
|
||||
if (m_chainman.AssumedValidBlock().IsNull()) {
|
||||
script_check_reason = "assumevalid=0 (always verify)";
|
||||
} else {
|
||||
constexpr int64_t TWO_WEEKS_IN_SECONDS{60 * 60 * 24 * 7 * 2};
|
||||
// We've been configured with the hash of a block which has been externally verified to have a valid history.
|
||||
// A suitable default value is included with the software and updated from time to time. Because validity
|
||||
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
|
||||
// This setting doesn't force the selection of any particular chain but makes validating some faster by
|
||||
// effectively caching the result of part of the verification.
|
||||
BlockMap::const_iterator it{m_blockman.m_block_index.find(m_chainman.AssumedValidBlock())};
|
||||
if (it != m_blockman.m_block_index.end()) {
|
||||
if (it->second.GetAncestor(pindex->nHeight) == pindex &&
|
||||
m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex &&
|
||||
m_chainman.m_best_header->nChainWork >= m_chainman.MinimumChainWork()) {
|
||||
// This block is a member of the assumed verified chain and an ancestor of the best header.
|
||||
// Script verification is skipped when connecting blocks under the
|
||||
// assumevalid block. Assuming the assumevalid block is valid this
|
||||
// is safe because block merkle hashes are still computed and checked,
|
||||
// Of course, if an assumed valid block is invalid due to false scriptSigs
|
||||
// this optimization would allow an invalid chain to be accepted.
|
||||
// The equivalent time check discourages hash power from extorting the network via DOS attack
|
||||
// into accepting an invalid block through telling users they must manually set assumevalid.
|
||||
// Requiring a software change or burying the invalid block, regardless of the setting, makes
|
||||
// it hard to hide the implication of the demand. This also avoids having release candidates
|
||||
// that are hardly doing any signature verification at all in testing without having to
|
||||
// artificially set the default assumed verified block further back.
|
||||
// The test against the minimum chain work prevents the skipping when denied access to any chain at
|
||||
// least as good as the expected chain.
|
||||
fScriptChecks = (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, params.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
|
||||
}
|
||||
if (it == m_blockman.m_block_index.end()) {
|
||||
script_check_reason = "assumevalid hash not in headers";
|
||||
} else if (it->second.GetAncestor(pindex->nHeight) != pindex) {
|
||||
script_check_reason = (pindex->nHeight > it->second.nHeight) ? "block height above assumevalid height" : "block not in assumevalid chain";
|
||||
} else if (m_chainman.m_best_header->GetAncestor(pindex->nHeight) != pindex) {
|
||||
script_check_reason = "block not in best header chain";
|
||||
} else if (m_chainman.m_best_header->nChainWork < m_chainman.MinimumChainWork()) {
|
||||
script_check_reason = "best header chainwork below minimumchainwork";
|
||||
} else if (GetBlockProofEquivalentTime(*m_chainman.m_best_header, *pindex, *m_chainman.m_best_header, params.GetConsensus()) <= TWO_WEEKS_IN_SECONDS) {
|
||||
script_check_reason = "block too recent relative to best header";
|
||||
} else {
|
||||
// This block is a member of the assumed verified chain and an ancestor of the best header.
|
||||
// Script verification is skipped when connecting blocks under the
|
||||
// assumevalid block. Assuming the assumevalid block is valid this
|
||||
// is safe because block merkle hashes are still computed and checked,
|
||||
// Of course, if an assumed valid block is invalid due to false scriptSigs
|
||||
// this optimization would allow an invalid chain to be accepted.
|
||||
// The equivalent time check discourages hash power from extorting the network via DOS attack
|
||||
// into accepting an invalid block through telling users they must manually set assumevalid.
|
||||
// Requiring a software change or burying the invalid block, regardless of the setting, makes
|
||||
// it hard to hide the implication of the demand. This also avoids having release candidates
|
||||
// that are hardly doing any signature verification at all in testing without having to
|
||||
// artificially set the default assumed verified block further back.
|
||||
// The test against the minimum chain work prevents the skipping when denied access to any chain at
|
||||
// least as good as the expected chain.
|
||||
script_check_reason = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2562,9 +2571,16 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
||||
Ticks<SecondsDouble>(m_chainman.time_forks),
|
||||
Ticks<MillisecondsDouble>(m_chainman.time_forks) / m_chainman.num_blocks_total);
|
||||
|
||||
if (fScriptChecks != m_prev_script_checks_logged && GetRole() == ChainstateRole::NORMAL) {
|
||||
LogInfo("%s signature validations at block #%d (%s).", fScriptChecks ? "Enabling" : "Disabling", pindex->nHeight, block_hash.ToString());
|
||||
m_prev_script_checks_logged = fScriptChecks;
|
||||
const bool fScriptChecks{!!script_check_reason};
|
||||
if (script_check_reason != m_last_script_check_reason_logged && GetRole() == ChainstateRole::NORMAL) {
|
||||
if (fScriptChecks) {
|
||||
LogInfo("Enabling script verification at block #%d (%s): %s.",
|
||||
pindex->nHeight, block_hash.ToString(), script_check_reason);
|
||||
} else {
|
||||
LogInfo("Disabling script verification at block #%d (%s).",
|
||||
pindex->nHeight, block_hash.ToString());
|
||||
}
|
||||
m_last_script_check_reason_logged = script_check_reason;
|
||||
}
|
||||
|
||||
CBlockUndo blockundo;
|
||||
|
||||
Reference in New Issue
Block a user