diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1865,21 +1865,6 @@ LogPrintf("Skipping checkpoint verification.\n"); } - if (args.IsArgSet("-minimumchainwork")) { - const std::string minChainWorkStr = - args.GetArg("-minimumchainwork", ""); - if (!IsHexNumber(minChainWorkStr)) { - return InitError(strprintf( - Untranslated( - "Invalid non-hex (%s) minimum chain work value specified"), - minChainWorkStr)); - } - nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); - } else { - nMinimumChainWork = - UintToArith256(chainparams.GetConsensus().nMinimumChainWork); - } - // Configure excessive block size. const int64_t nProposedExcessiveBlockSize = args.GetIntArg("-excessiveblocksize", DEFAULT_MAX_BLOCK_SIZE); @@ -2022,6 +2007,16 @@ } } + // Also report errors from parsing before daemonization + { + ChainstateManager::Options chainman_opts_dummy{ + .config = config, + }; + if (const auto error{ApplyArgsManOptions(args, chainman_opts_dummy)}) { + return InitError(*error); + } + } + return true; } @@ -2395,7 +2390,8 @@ .config = config, .adjusted_time_callback = GetAdjustedTime, }; - ApplyArgsManOptions(args, chainman_opts); + // no error can happen, already checked in AppInitParameterInteraction + Assert(!ApplyArgsManOptions(args, chainman_opts)); // cache size calculations CacheSizes cache_sizes = diff --git a/src/kernel/chainstatemanager_opts.h b/src/kernel/chainstatemanager_opts.h --- a/src/kernel/chainstatemanager_opts.h +++ b/src/kernel/chainstatemanager_opts.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H #define BITCOIN_KERNEL_CHAINSTATEMANAGER_OPTS_H +#include #include #include @@ -27,6 +28,9 @@ const Config &config; const std::function adjusted_time_callback{ nullptr}; + //! If set, it will override the minimum work we will assume exists on some + //! valid chain. + std::optional minimum_chain_work{}; //! If set, it will override the block hash whose ancestors we will assume //! to have valid scripts without checking them. std::optional assumed_valid_block{}; diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1841,7 +1841,8 @@ if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < m_chainman.ActiveChain().Tip()->nChainWork || - state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + state->pindexBestKnownBlock->nChainWork < + m_chainman.MinimumChainWork()) { // This peer has nothing interesting. return; } @@ -3423,7 +3424,7 @@ tip->nChainWork - std::min(144 * GetBlockProof(*tip), tip->nChainWork); } - return std::max(near_chaintip_work, arith_uint256(nMinimumChainWork)); + return std::max(near_chaintip_work, m_chainman.MinimumChainWork()); } /** @@ -3771,14 +3772,15 @@ // When nCount < MAX_HEADERS_RESULTS, we know we have no more // headers to fetch from this peer. if (nodestate->pindexBestKnownBlock && - nodestate->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { + nodestate->pindexBestKnownBlock->nChainWork < + m_chainman.MinimumChainWork()) { // This peer has too little work on their headers chain to help // us sync -- disconnect if it is an outbound disconnection // candidate. - // Note: We compare their tip to nMinimumChainWork (rather than + // Note: We compare their tip to the minimum chain work (rather than // m_chainman.ActiveChain().Tip()) because we won't start block // download until we have a headers chain that has at least - // nMinimumChainWork, even if a peer has a chain past our tip, + // the minimum chain work, even if a peer has a chain past our tip, // as an anti-DoS measure. if (pfrom.IsOutboundOrBlockRelayConn()) { LogPrintf("Disconnecting outbound peer %d -- headers " @@ -5169,12 +5171,13 @@ // Note that if we were to be on a chain that forks from the // checkpointed chain, then serving those headers to a peer that has // seen the checkpointed chain would cause that peer to disconnect us. - // Requiring that our chainwork exceed nMinimumChainWork is a protection - // against being fed a bogus chain when we started up for the first time - // and getting partitioned off the honest network for serving that chain - // to others. + // Requiring that our chainwork exceed the minimum chainwork is a + // protection against being fed a bogus chain when we started up for + // the first time and getting partitioned off the honest network for + // serving that chain to others. if (m_chainman.ActiveTip() == nullptr || - (m_chainman.ActiveTip()->nChainWork < nMinimumChainWork && + (m_chainman.ActiveTip()->nChainWork < + m_chainman.MinimumChainWork() && !pfrom.HasPermission(NetPermissionFlags::Download))) { LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because active chain " @@ -5669,9 +5672,9 @@ // (eg disk space). Because we only try to reconstruct blocks when // we're close to caught up (via the CanDirectFetch() requirement // above, combined with the behavior of not requesting blocks until - // we have a chain with at least nMinimumChainWork), and we ignore - // compact blocks with less work than our tip, it is safe to treat - // reconstructed compact blocks as having been requested. + // we have a chain with at least the minimum chain work), and we + // ignore compact blocks with less work than our tip, it is safe to + // treat reconstructed compact blocks as having been requested. ProcessBlock(config, pfrom, pblock, /*force_processing=*/true, /*min_pow_checked=*/true); // hold cs_main for CBlockIndex::IsValid() @@ -7450,7 +7453,8 @@ LOCK(cs_main); CNodeState &state = *State(node.GetId()); if (state.pindexBestKnownBlock != nullptr && - state.pindexBestKnownBlock->nChainWork > nMinimumChainWork) { + state.pindexBestKnownBlock->nChainWork > + m_chainman.MinimumChainWork()) { // Tell our peer we prefer to receive headers rather than inv's // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -169,8 +169,9 @@ } else { LogPrintf("Validating signatures for all blocks.\n"); } - LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); - if (nMinimumChainWork < + LogPrintf("Setting nMinimumChainWork=%s\n", + chainman.MinimumChainWork().GetHex()); + if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); diff --git a/src/node/chainstatemanager_args.h b/src/node/chainstatemanager_args.h --- a/src/node/chainstatemanager_args.h +++ b/src/node/chainstatemanager_args.h @@ -7,11 +7,14 @@ #include +#include + class ArgsManager; +struct bilingual_str; namespace node { -void ApplyArgsManOptions(const ArgsManager &args, - ChainstateManager::Options &opts); +std::optional +ApplyArgsManOptions(const ArgsManager &args, ChainstateManager::Options &opts); } // namespace node #endif // BITCOIN_NODE_CHAINSTATEMANAGER_ARGS_H diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp --- a/src/node/chainstatemanager_args.cpp +++ b/src/node/chainstatemanager_args.cpp @@ -4,14 +4,31 @@ #include +#include +#include +#include +#include #include +#include +#include #include #include +#include namespace node { -void ApplyArgsManOptions(const ArgsManager &args, - ChainstateManager::Options &opts) { +std::optional +ApplyArgsManOptions(const ArgsManager &args, ChainstateManager::Options &opts) { + if (auto value{args.GetArg("-minimumchainwork")}) { + if (!IsHexNumber(*value)) { + return strprintf( + Untranslated( + "Invalid non-hex (%s) minimum chain work value specified"), + *value); + } + opts.minimum_chain_work = UintToArith256(uint256S(*value)); + } + if (auto value{args.GetArg("-assumevalid")}) { opts.assumed_valid_block = BlockHash::fromHex(*value); } @@ -19,5 +36,6 @@ if (auto value{args.GetIntArg("-maxtipage")}) { opts.max_tip_age = std::chrono::seconds{*value}; } + return std::nullopt; } } // namespace node diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -120,11 +120,6 @@ extern bool fCheckBlockIndex; extern bool fCheckpointsEnabled; -/** - * Minimum work we will assume exists on some valid chain. - */ -extern arith_uint256 nMinimumChainWork; - /** Documentation for argument 'checklevel'. */ extern const std::vector CHECKLEVEL_DOC; @@ -1239,6 +1234,9 @@ const Consensus::Params &GetConsensus() const { return m_options.config.GetChainParams().GetConsensus(); } + const arith_uint256 &MinimumChainWork() const { + return *Assert(m_options.minimum_chain_work); + } const BlockHash &AssumedValidBlock() const { return *Assert(m_options.assumed_valid_block); } diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -112,8 +112,6 @@ bool fCheckBlockIndex = false; bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; -arith_uint256 nMinimumChainWork; - BlockValidationOptions::BlockValidationOptions(const Config &config) : excessiveBlockSize(config.GetMaxBlockSize()), checkPoW(true), checkMerkleRoot(true) {} @@ -1171,7 +1169,7 @@ if (m_chain.Tip() == nullptr) { return true; } - if (m_chain.Tip()->nChainWork < nMinimumChainWork) { + if (m_chain.Tip()->nChainWork < m_chainman.MinimumChainWork()) { return true; } if (m_chain.Tip()->Time() < @@ -1767,7 +1765,8 @@ if (it->second.GetAncestor(pindex->nHeight) == pindex && m_chainman.m_best_header->GetAncestor(pindex->nHeight) == pindex && - m_chainman.m_best_header->nChainWork >= nMinimumChainWork) { + 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 @@ -1785,8 +1784,9 @@ // 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 nMinimumChainWork prevents the skipping when denied - // access to any chain at least as good as the expected chain. + // 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, @@ -4387,7 +4387,7 @@ // If our tip is behind, a peer could try to send us // low-work blocks on a fake chain that we would never // request; don't process these. - if (pindex->nChainWork < nMinimumChainWork) { + if (pindex->nChainWork < m_chainman.MinimumChainWork()) { return true; } } @@ -6368,6 +6368,10 @@ * operators that accept nullopt, thus ignoring the intended default value. */ static ChainstateManager::Options &&Flatten(ChainstateManager::Options &&opts) { + if (!opts.minimum_chain_work.has_value()) { + opts.minimum_chain_work = UintToArith256( + opts.config.GetChainParams().GetConsensus().nMinimumChainWork); + } if (!opts.assumed_valid_block.has_value()) { opts.assumed_valid_block = opts.config.GetChainParams().GetConsensus().defaultAssumeValid;