From 2b0d291da8f479739ff394dd92801da8c40b9f8e Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Tue, 16 Jun 2020 18:58:56 +1000 Subject: [refactor] Add deploymentstatus.h Provides DeploymentEnabled, DeploymentActiveAt, and DeploymentActiveAfter helpers for checking the status of buried deployments. Can be overloaded so the same syntax works for non-buried deployments, allowing future soft forks to be changed from signalled to buried deployments without having to touch the implementation code. Replaces IsWitnessEnabled and IsScriptWitnessEnabled. --- src/Makefile.am | 1 + src/consensus/params.h | 29 ++++++++++++++++++++++ src/deploymentstatus.h | 33 +++++++++++++++++++++++++ src/init.cpp | 3 ++- src/miner.cpp | 5 ++-- src/net_processing.cpp | 9 ++++--- src/rpc/mining.cpp | 3 ++- src/validation.cpp | 67 ++++++++++++++++++++------------------------------ src/validation.h | 4 --- 9 files changed, 101 insertions(+), 53 deletions(-) create mode 100644 src/deploymentstatus.h diff --git a/src/Makefile.am b/src/Makefile.am index e2ed70556d..a3248f3eab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -145,6 +145,7 @@ BITCOIN_CORE_H = \ core_memusage.h \ cuckoocache.h \ dbwrapper.h \ + deploymentstatus.h \ external_signer.h \ flatfile.h \ fs.h \ diff --git a/src/consensus/params.h b/src/consensus/params.h index 28c95e0884..9b4139d76c 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -11,6 +11,17 @@ namespace Consensus { +enum BuriedDeployment : int16_t +{ + // buried deployments get negative values to avoid overlap with DeploymentPos + DEPLOYMENT_HEIGHTINCB = std::numeric_limits::min(), + DEPLOYMENT_CLTV, + DEPLOYMENT_DERSIG, + DEPLOYMENT_CSV, + DEPLOYMENT_SEGWIT, +}; +constexpr bool ValidDeployment(BuriedDeployment dep) { return DEPLOYMENT_HEIGHTINCB <= dep && dep <= DEPLOYMENT_SEGWIT; } + enum DeploymentPos { DEPLOYMENT_TESTDUMMY, @@ -100,7 +111,25 @@ struct Params { */ bool signet_blocks{false}; std::vector signet_challenge; + + int DeploymentHeight(BuriedDeployment dep) const + { + switch (dep) { + case DEPLOYMENT_HEIGHTINCB: + return BIP34Height; + case DEPLOYMENT_CLTV: + return BIP65Height; + case DEPLOYMENT_DERSIG: + return BIP66Height; + case DEPLOYMENT_CSV: + return CSVHeight; + case DEPLOYMENT_SEGWIT: + return SegwitHeight; + } // no default case, so the compiler can warn about missing cases + return std::numeric_limits::max(); + } }; + } // namespace Consensus #endif // BITCOIN_CONSENSUS_PARAMS_H diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h new file mode 100644 index 0000000000..32190e369d --- /dev/null +++ b/src/deploymentstatus.h @@ -0,0 +1,33 @@ +// Copyright (c) 2020 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_DEPLOYMENTSTATUS_H +#define BITCOIN_DEPLOYMENTSTATUS_H + +#include + +#include + +/** Determine if a deployment is active for the next block */ +inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep); +} + +/** Determine if a deployment is active for this block */ +inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return index.nHeight >= params.DeploymentHeight(dep); +} + +/** Determine if a deployment is enabled (can ever be active) */ +inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::BuriedDeployment dep) +{ + assert(Consensus::ValidDeployment(dep)); + return params.DeploymentHeight(dep) != std::numeric_limits::max(); +} + +#endif // BITCOIN_DEPLOYMENTSTATUS_H diff --git a/src/init.cpp b/src/init.cpp index a88adbaa21..ae96f510bc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1587,7 +1588,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } } - if (chainparams.GetConsensus().SegwitHeight != std::numeric_limits::max()) { + if (DeploymentEnabled(chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) { // Advertise witness capabilities. // The option to not set NODE_WITNESS is only used in the tests and should be removed. nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS); diff --git a/src/miner.cpp b/src/miner.cpp index 0cf303eb3c..c1a992b9e9 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -137,12 +138,12 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc // This is only needed in case the witness softfork activation is reverted // (which would require a very deep reorganization). // Note that the mempool would accept transactions with witness data before - // IsWitnessEnabled, but we would only ever mine blocks after IsWitnessEnabled + // the deployment is active, but we would only ever mine blocks after activation // unless there is a massive block reorganization with the witness softfork // not activated. // TODO: replace this with a call to main to assess validity of a mempool // transaction (which in most cases can be a no-op). - fIncludeWitness = IsWitnessEnabled(pindexPrev, chainparams.GetConsensus()); + fIncludeWitness = DeploymentActiveAfter(pindexPrev, chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT); int nPackagesSelected = 0; int nDescendantsUpdated = 0; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index ab9c41ca2b..315d2ac5cd 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -997,7 +998,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count // We consider the chain that this peer is on invalid. return; } - if (!State(nodeid)->fHaveWitness && IsWitnessEnabled(pindex->pprev, consensusParams)) { + if (!State(nodeid)->fHaveWitness && DeploymentActiveAt(*pindex, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) { // We wouldn't download this block or its descendants from this peer. return; } @@ -1467,7 +1468,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha return; nHighestFastAnnounce = pindex->nHeight; - bool fWitnessEnabled = IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()); + bool fWitnessEnabled = DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT); uint256 hashBlock(pblock->GetHash()); { @@ -2082,7 +2083,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) { if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) && !IsBlockRequested(pindexWalk->GetBlockHash()) && - (!IsWitnessEnabled(pindexWalk->pprev, m_chainparams.GetConsensus()) || State(pfrom.GetId())->fHaveWitness)) { + (!DeploymentActiveAt(*pindexWalk, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) || State(pfrom.GetId())->fHaveWitness)) { // We don't have this block, and it's not yet in flight. vToFetch.push_back(pindexWalk); } @@ -3397,7 +3398,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, return; } - if (IsWitnessEnabled(pindex->pprev, m_chainparams.GetConsensus()) && !nodestate->fSupportsDesiredCmpctVersion) { + if (DeploymentActiveAt(*pindex, m_chainparams.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT) && !nodestate->fSupportsDesiredCmpctVersion) { // Don't bother trying to process compact blocks from v1 peers // after segwit activates. return; diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 327f961196..f054c8b145 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -774,7 +775,7 @@ static RPCHelpMan getblocktemplate() pblock->nNonce = 0; // NOTE: If at some point we support pre-segwit miners post-segwit-activation, this needs to take segwit support into consideration - const bool fPreSegWit = (pindexPrev->nHeight + 1 < consensusParams.SegwitHeight); + const bool fPreSegWit = !DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT); UniValue aCaps(UniValue::VARR); aCaps.push_back("proposal"); diff --git a/src/validation.cpp b/src/validation.cpp index 1b0f881d14..b69faa54eb 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1649,15 +1650,6 @@ public: static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main); -// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for -// mainnet. We no longer need to support disabling the segwit deployment -// except for testing purposes, due to limitations of the functional test -// environment. See test/functional/p2p-segwit.py. -static bool IsScriptWitnessEnabled(const Consensus::Params& params) -{ - return params.SegwitHeight != std::numeric_limits::max(); -} - static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { unsigned int flags = SCRIPT_VERIFY_NONE; @@ -1676,22 +1668,22 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens // Enforce WITNESS rules whenever P2SH is in effect (and the segwit // deployment is defined). - if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) { + if (flags & SCRIPT_VERIFY_P2SH && DeploymentEnabled(consensusparams, Consensus::DEPLOYMENT_SEGWIT)) { flags |= SCRIPT_VERIFY_WITNESS; } - // Start enforcing the DERSIG (BIP66) rule - if (pindex->nHeight >= consensusparams.BIP66Height) { + // Enforce the DERSIG (BIP66) rule + if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_DERSIG)) { flags |= SCRIPT_VERIFY_DERSIG; } - // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule - if (pindex->nHeight >= consensusparams.BIP65Height) { + // Enforce CHECKLOCKTIMEVERIFY (BIP65) + if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_CLTV)) { flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; } - // Start enforcing BIP112 (CHECKSEQUENCEVERIFY) - if (pindex->nHeight >= consensusparams.CSVHeight) { + // Enforce CHECKSEQUENCEVERIFY (BIP112) + if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_CSV)) { flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } @@ -1700,8 +1692,8 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_TAPROOT; } - // Start enforcing BIP147 NULLDUMMY (activated simultaneously with segwit) - if (IsWitnessEnabled(pindex->pprev, consensusparams)) { + // Enforce BIP147 NULLDUMMY (activated simultaneously with segwit) + if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_SEGWIT)) { flags |= SCRIPT_VERIFY_NULLDUMMY; } @@ -1891,9 +1883,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, } } - // Start enforcing BIP68 (sequence locks) + // Enforce BIP68 (sequence locks) int nLockTimeFlags = 0; - if (pindex->nHeight >= m_params.GetConsensus().CSVHeight) { + if (DeploymentActiveAt(*pindex, m_params.GetConsensus(), Consensus::DEPLOYMENT_CSV)) { nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; } @@ -2986,7 +2978,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi pindexNew->nDataPos = pos.nPos; pindexNew->nUndoPos = 0; pindexNew->nStatus |= BLOCK_HAVE_DATA; - if (IsWitnessEnabled(pindexNew->pprev, m_params.GetConsensus())) { + if (DeploymentActiveAt(*pindexNew, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) { pindexNew->nStatus |= BLOCK_OPT_WITNESS; } pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS); @@ -3107,17 +3099,11 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu return true; } -bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) -{ - int height = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; - return (height >= params.SegwitHeight); -} - void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams) { int commitpos = GetWitnessCommitmentIndex(block); static const std::vector nonce(32, 0x00); - if (commitpos != NO_WITNESS_COMMITMENT && IsWitnessEnabled(pindexPrev, consensusParams) && !block.vtx[0]->HasWitness()) { + if (commitpos != NO_WITNESS_COMMITMENT && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT) && !block.vtx[0]->HasWitness()) { CMutableTransaction tx(*block.vtx[0]); tx.vin[0].scriptWitness.stack.resize(1); tx.vin[0].scriptWitness.stack[0] = nonce; @@ -3130,7 +3116,7 @@ std::vector GenerateCoinbaseCommitment(CBlock& block, const CBloc std::vector commitment; int commitpos = GetWitnessCommitmentIndex(block); std::vector ret(32, 0x00); - if (consensusParams.SegwitHeight != std::numeric_limits::max()) { + if (DeploymentEnabled(consensusParams, Consensus::DEPLOYMENT_SEGWIT)) { if (commitpos == NO_WITNESS_COMMITMENT) { uint256 witnessroot = BlockWitnessMerkleRoot(block, nullptr); CHash256().Write(witnessroot).Write(ret).Finalize(witnessroot); @@ -3208,13 +3194,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME) return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future"); - // Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded: - // check for version 2, 3 and 4 upgrades - if((block.nVersion < 2 && nHeight >= consensusParams.BIP34Height) || - (block.nVersion < 3 && nHeight >= consensusParams.BIP66Height) || - (block.nVersion < 4 && nHeight >= consensusParams.BIP65Height)) + // Reject blocks with outdated version + if ((block.nVersion < 2 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB)) || + (block.nVersion < 3 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_DERSIG)) || + (block.nVersion < 4 && DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CLTV))) { return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion), strprintf("rejected nVersion=0x%08x block", block.nVersion)); + } return true; } @@ -3229,9 +3215,9 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat { const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; - // Start enforcing BIP113 (Median Time Past). + // Enforce BIP113 (Median Time Past). int nLockTimeFlags = 0; - if (nHeight >= consensusParams.CSVHeight) { + if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_CSV)) { assert(pindexPrev != nullptr); nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST; } @@ -3248,7 +3234,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat } // Enforce rule that the coinbase starts with serialized block height - if (nHeight >= consensusParams.BIP34Height) + if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB)) { CScript expect = CScript() << nHeight; if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() || @@ -3266,7 +3252,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat // {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are // multiple, the last one is used. bool fHaveWitness = false; - if (nHeight >= consensusParams.SegwitHeight) { + if (DeploymentActiveAfter(pindexPrev, consensusParams, Consensus::DEPLOYMENT_SEGWIT)) { int commitpos = GetWitnessCommitmentIndex(block); if (commitpos != NO_WITNESS_COMMITMENT) { bool malleated = false; @@ -4096,9 +4082,8 @@ bool CChainState::NeedsRedownload() const // At and above m_params.SegwitHeight, segwit consensus rules must be validated CBlockIndex* block{m_chain.Tip()}; - const int segwit_height{m_params.GetConsensus().SegwitHeight}; - while (block != nullptr && block->nHeight >= segwit_height) { + while (block != nullptr && DeploymentActiveAt(*block, m_params.GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) { if (!(block->nStatus & BLOCK_OPT_WITNESS)) { // block is insufficiently validated for a segwit client return true; @@ -5000,7 +4985,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload() // won't ask to rewind the entire assumed-valid chain on startup. - if (index->pprev && ::IsWitnessEnabled(index->pprev, ::Params().GetConsensus())) { + if (index->pprev && DeploymentActiveAt(*index, ::Params().GetConsensus(), Consensus::DEPLOYMENT_SEGWIT)) { index->nStatus |= BLOCK_OPT_WITNESS; } } diff --git a/src/validation.h b/src/validation.h index fc702b7183..50a8d7e575 100644 --- a/src/validation.h +++ b/src/validation.h @@ -345,10 +345,6 @@ bool TestBlockValidity(BlockValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -/** Check whether witness commitments are required for a block, and whether to enforce NULLDUMMY (BIP 147) rules. - * Note that transaction witness validation rules are always enforced when P2SH is enforced. */ -bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); - /** Update uncommitted block structures (currently: only the witness reserved value). This is safe for submitted blocks. */ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams); -- cgit v1.2.3