From 484e0d9d647a812ba4b30cb530ddff4841b88e8c Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 14 Feb 2022 11:17:54 +0100 Subject: [PATCH 1/3] Merge bitcoin/bitcoin#24187: Followups for getdeploymentinfo e5f0356e3ffea10f447998b7549a67e016446e81 rpc/blockchain: rename getdeploymentinfo tip/active_chain_tip to blockindex (Anthony Towns) fbab43f169924c681ef085639b3e4de6c74a4958 rpc/blockchain: a constant craving (Anthony Towns) 5179656ef83a563133e32893f4acfd61d1aebdcb trivial: comment tweaks (Anthony Towns) 32f04e6da9845c218f9bbd8b8329f35ed3678a49 rpc documentation improvements (Anthony Towns) 555eafa7930a84d8ca594bea9fe39f63cfa75076 doc: getdeploymentinfo release notes tweaks (Anthony Towns) Pull request description: Documentation, comments and trivial code changes to followup #23508. ACKs for top commit: Sjors: utACK e5f0356e3ffea10f447998b7549a67e016446e81 Tree-SHA512: 4e854a8453588901edb887504f7bfa100cc32df2e99654a5e5970032a0bd63259ba0582479e15bc09ef4792c6672715007f89eb1a7b2d7e229433a678cde9f44 --- doc/release-notes-24187.md | 11 +++ src/rpc/blockchain.cpp | 156 +++++++++++++++++++++++++++++++------ 2 files changed, 145 insertions(+), 22 deletions(-) create mode 100644 doc/release-notes-24187.md diff --git a/doc/release-notes-24187.md b/doc/release-notes-24187.md new file mode 100644 index 000000000000..f5170f033bc1 --- /dev/null +++ b/doc/release-notes-24187.md @@ -0,0 +1,11 @@ +New RPCs +-------- + +- Information on soft fork status has been moved from `getblockchaininfo` + to the new `getdeploymentinfo` RPC which allows querying soft fork status at any + block, rather than just at the chain tip. Inclusion of soft fork + status in `getblockchaininfo` can currently be restored using the + configuration `-deprecatedrpc=softforks`, but this will be removed in + a future release. Note that in either case, the `status` field + now reflects the status of the current block rather than the next + block. (#23508) \ No newline at end of file diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c33dc4b42b38..b60a1d036547 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1367,7 +1367,7 @@ static RPCHelpMan verifychain() }; } -static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep) +static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const Consensus::Params& params, Consensus::BuriedDeployment dep) { // For buried deployments. @@ -1377,66 +1377,89 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& rv.pushKV("type", "buried"); // getblockchaininfo reports the softfork as active from when the chain height is // one below the activation height - rv.pushKV("active", DeploymentActiveAfter(active_chain_tip, params, dep)); + rv.pushKV("active", DeploymentActiveAfter(blockindex, params, dep)); rv.pushKV("height", params.DeploymentHeight(dep)); softforks.pushKV(DeploymentName(dep), rv); } -static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map& signals, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +static void SoftForkDescPushBack(const CBlockIndex* blockindex, const std::unordered_map& signals, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { // For BIP9 deployments. if (!DeploymentEnabled(consensusParams, id)) return; + if (blockindex == nullptr) return; + + auto get_state_name = [](const ThresholdState state) -> std::string { + switch (state) { + case ThresholdState::DEFINED: return "defined"; + case ThresholdState::STARTED: return "started"; + case ThresholdState::LOCKED_IN: return "locked_in"; + case ThresholdState::ACTIVE: return "active"; + case ThresholdState::FAILED: return "failed"; + } + return "invalid"; + }; UniValue bip9(UniValue::VOBJ); - const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id); - switch (thresholdState) { - case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break; - case ThresholdState::STARTED: bip9.pushKV("status", "started"); break; - case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break; - case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break; - case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break; - } - const bool has_signal = (ThresholdState::STARTED == thresholdState || ThresholdState::LOCKED_IN == thresholdState); + + const ThresholdState next_state = g_versionbitscache.State(blockindex, consensusParams, id); + const ThresholdState current_state = g_versionbitscache.State(blockindex->pprev, consensusParams, id); + + const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state); + + // BIP9 parameters if (has_signal) { bip9.pushKV("bit", consensusParams.vDeployments[id].bit); } bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); + bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height); bip9.pushKV("ehf", consensusParams.vDeployments[id].useEHF); if (auto it = signals.find(consensusParams.vDeployments[id].bit); it != signals.end()) { bip9.pushKV("ehf_height", it->second); } - int64_t since_height = g_versionbitscache.StateSinceHeight(active_chain_tip, consensusParams, id); - bip9.pushKV("since", since_height); + + // BIP9 status + bip9.pushKV("status", get_state_name(current_state)); + bip9.pushKV("since", g_versionbitscache.StateSinceHeight(blockindex->pprev, consensusParams, id)); + bip9.pushKV("status-next", get_state_name(next_state)); + + // BIP9 signalling status, if applicable if (has_signal) { UniValue statsUV(UniValue::VOBJ); - BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id); + BIP9Stats statsStruct = g_versionbitscache.Statistics(blockindex, consensusParams, id); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("elapsed", statsStruct.elapsed); statsUV.pushKV("count", statsStruct.count); - if (ThresholdState::LOCKED_IN != thresholdState) { + if (ThresholdState::LOCKED_IN != current_state) { statsUV.pushKV("threshold", statsStruct.threshold); statsUV.pushKV("possible", statsStruct.possible); } bip9.pushKV("statistics", statsUV); } - if (ThresholdState::LOCKED_IN == thresholdState) { - bip9.pushKV("activation_height", since_height + static_cast(consensusParams.vDeployments[id].nWindowSize)); + if (ThresholdState::LOCKED_IN == current_state) { + bip9.pushKV("activation_height", g_versionbitscache.StateSinceHeight(blockindex->pprev, consensusParams, id) + static_cast(consensusParams.vDeployments[id].nWindowSize)); } bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height); UniValue rv(UniValue::VOBJ); rv.pushKV("type", "bip9"); - rv.pushKV("bip9", bip9); - if (ThresholdState::ACTIVE == thresholdState) { - rv.pushKV("height", since_height); + if (ThresholdState::ACTIVE == next_state) { + rv.pushKV("height", g_versionbitscache.StateSinceHeight(blockindex, consensusParams, id)); } - rv.pushKV("active", ThresholdState::ACTIVE == thresholdState); + rv.pushKV("active", ThresholdState::ACTIVE == next_state); + rv.pushKV("bip9", bip9); softforks.pushKV(DeploymentName(id), rv); } +namespace { +/* TODO: when -deprecatedrpc=softforks is removed, drop these */ +UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams); +extern const std::vector RPCHelpForDeployment; +} + +// used by rest.cpp:rest_chaininfo, so cannot be static RPCHelpMan getblockchaininfo() { return RPCHelpMan{"getblockchaininfo", @@ -1570,6 +1593,95 @@ RPCHelpMan getblockchaininfo() }; } +namespace { +const std::vector RPCHelpForDeployment{ + {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, + {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, + {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)", + { + {RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"}, + {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, + {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, + {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"}, + {RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"}, + {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, + {RPCResult::Type::STR, "status-next", "status of deployment at the next block"}, + {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)", + { + {RPCResult::Type::NUM, "period", "the length in blocks of the signalling period"}, + {RPCResult::Type::NUM, "threshold", /*optional=*/true, "the number of blocks with the version bit set required to activate the feature (only for \"started\" status)"}, + {RPCResult::Type::NUM, "elapsed", "the number of blocks elapsed since the beginning of the current period"}, + {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"}, + {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"}, + }}, + {RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"}, + }}, +}; + +UniValue DeploymentInfo(const CBlockIndex* blockindex, const Consensus::Params& consensusParams) +{ + UniValue softforks(UniValue::VOBJ); + std::unordered_map signals{}; // Empty signals map for now + // Buried deployments + SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB); + SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG); + SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV); + SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CSV); + SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT); + // BIP9 deployments + SoftForkDescPushBack(blockindex, signals, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); + SoftForkDescPushBack(blockindex, signals, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT); + return softforks; +} +} // anon namespace + +static RPCHelpMan getdeploymentinfo() +{ + return RPCHelpMan{"getdeploymentinfo", + "Returns an object containing various state info regarding deployments of consensus changes.", + { + {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"hash of current chain tip"}, "The block hash at which to query deployment state"}, + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR, "hash", "requested block hash (or tip)"}, + {RPCResult::Type::NUM, "height", "requested block height (or tip)"}, + {RPCResult::Type::OBJ, "deployments", "", { + {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment} + }}, + } + }, + RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") }, + [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue + { + const ChainstateManager& chainman = EnsureAnyChainman(request.context); + LOCK(cs_main); + const CChainState& active_chainstate = chainman.ActiveChainstate(); + + const CBlockIndex* blockindex; + if (request.params[0].isNull()) { + blockindex = active_chainstate.m_chain.Tip(); + CHECK_NONFATAL(blockindex); + } else { + const uint256 hash(ParseHashV(request.params[0], "blockhash")); + blockindex = chainman.m_blockman.LookupBlockIndex(hash); + if (!blockindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + } + + const Consensus::Params& consensusParams = Params().GetConsensus(); + + UniValue deploymentinfo(UniValue::VOBJ); + deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString()); + deploymentinfo.pushKV("height", blockindex->nHeight); + deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, consensusParams)); + return deploymentinfo; + }, + }; +} + /** Comparison function for sorting the getchaintips heads. */ struct CompareBlocksByHeight { From 473d46635b52dc7473eeef22c5f0a314501db2dd Mon Sep 17 00:00:00 2001 From: Claude Code Date: Thu, 24 Jul 2025 01:57:08 -0500 Subject: [PATCH 2/3] fix: add missing comment change from bitcoin#24187 Adds missing trivial comment change from commit 5179656ef8: - Update comment in GetStateStatisticsFor from 'Find beginning of period' to 'Find how many blocks are in the current period' This change was part of the original Bitcoin PR but was missing from the Dash backport, causing scope validation issues. --- src/versionbits.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 3f0c7fd779fb..8ccfa6352c8c 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -141,7 +141,7 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI if (pindex == nullptr) return stats; - // Find beginning of period + // Find how many blocks are in the current period const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period)); stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight; From 3f8051c817e7b01aa5f8419e0a40fd6b27d87b79 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 25 Jul 2025 07:27:05 -0500 Subject: [PATCH 3/3] fix: add missing comment change from bitcoin#24187 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds missing trivial comment change from commit 5179656ef8: - Update comment in GetStateStatisticsFor from 'Find beginning of period' to 'Find how many blocks are in the current period' This change was part of the original Bitcoin PR but was missing from the Dash backport, causing scope validation issues. Also removes Bitcoin-specific functionality that doesn't exist in Dash: - Remove DEPLOYMENT_SEGWIT and DEPLOYMENT_TAPROOT references - Remove unregistered getdeploymentinfo function that contains Bitcoin-specific deployments - Remove associated documentation for features not implemented in Dash 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- doc/release-notes-24187.md | 11 ------- src/rpc/blockchain.cpp | 61 -------------------------------------- 2 files changed, 72 deletions(-) delete mode 100644 doc/release-notes-24187.md diff --git a/doc/release-notes-24187.md b/doc/release-notes-24187.md deleted file mode 100644 index f5170f033bc1..000000000000 --- a/doc/release-notes-24187.md +++ /dev/null @@ -1,11 +0,0 @@ -New RPCs --------- - -- Information on soft fork status has been moved from `getblockchaininfo` - to the new `getdeploymentinfo` RPC which allows querying soft fork status at any - block, rather than just at the chain tip. Inclusion of soft fork - status in `getblockchaininfo` can currently be restored using the - configuration `-deprecatedrpc=softforks`, but this will be removed in - a future release. Note that in either case, the `status` field - now reflects the status of the current block rather than the next - block. (#23508) \ No newline at end of file diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index b60a1d036547..0ce6950f11b6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1455,7 +1455,6 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, const std::unord namespace { /* TODO: when -deprecatedrpc=softforks is removed, drop these */ -UniValue DeploymentInfo(const CBlockIndex* tip, const Consensus::Params& consensusParams); extern const std::vector RPCHelpForDeployment; } @@ -1619,68 +1618,8 @@ const std::vector RPCHelpForDeployment{ }}, }; -UniValue DeploymentInfo(const CBlockIndex* blockindex, const Consensus::Params& consensusParams) -{ - UniValue softforks(UniValue::VOBJ); - std::unordered_map signals{}; // Empty signals map for now - // Buried deployments - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_CSV); - SoftForkDescPushBack(blockindex, softforks, consensusParams, Consensus::DEPLOYMENT_SEGWIT); - // BIP9 deployments - SoftForkDescPushBack(blockindex, signals, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); - SoftForkDescPushBack(blockindex, signals, softforks, consensusParams, Consensus::DEPLOYMENT_TAPROOT); - return softforks; -} } // anon namespace -static RPCHelpMan getdeploymentinfo() -{ - return RPCHelpMan{"getdeploymentinfo", - "Returns an object containing various state info regarding deployments of consensus changes.", - { - {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Default{"hash of current chain tip"}, "The block hash at which to query deployment state"}, - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", { - {RPCResult::Type::STR, "hash", "requested block hash (or tip)"}, - {RPCResult::Type::NUM, "height", "requested block height (or tip)"}, - {RPCResult::Type::OBJ, "deployments", "", { - {RPCResult::Type::OBJ, "xxxx", "name of the deployment", RPCHelpForDeployment} - }}, - } - }, - RPCExamples{ HelpExampleCli("getdeploymentinfo", "") + HelpExampleRpc("getdeploymentinfo", "") }, - [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue - { - const ChainstateManager& chainman = EnsureAnyChainman(request.context); - LOCK(cs_main); - const CChainState& active_chainstate = chainman.ActiveChainstate(); - - const CBlockIndex* blockindex; - if (request.params[0].isNull()) { - blockindex = active_chainstate.m_chain.Tip(); - CHECK_NONFATAL(blockindex); - } else { - const uint256 hash(ParseHashV(request.params[0], "blockhash")); - blockindex = chainman.m_blockman.LookupBlockIndex(hash); - if (!blockindex) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); - } - } - - const Consensus::Params& consensusParams = Params().GetConsensus(); - - UniValue deploymentinfo(UniValue::VOBJ); - deploymentinfo.pushKV("hash", blockindex->GetBlockHash().ToString()); - deploymentinfo.pushKV("height", blockindex->nHeight); - deploymentinfo.pushKV("deployments", DeploymentInfo(blockindex, consensusParams)); - return deploymentinfo; - }, - }; -} /** Comparison function for sorting the getchaintips heads. */ struct CompareBlocksByHeight