diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 3b05f84eee4..4bd4e1d4737 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -565,10 +565,10 @@ static UniValue BIP22ValidationResult(const BlockValidationState& state) return "valid?"; } -static std::string gbt_vb_name(const Consensus::DeploymentPos pos) { - const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - std::string s = vbinfo.name; - if (!vbinfo.gbt_force) { +static std::string gbt_force_name(const std::string& name, bool gbt_force) +{ + std::string s{name}; + if (!gbt_force) { s.insert(s.begin(), '!'); } return s; @@ -898,45 +898,33 @@ static RPCHelpMan getblocktemplate() } UniValue vbavailable(UniValue::VOBJ); - for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { - Consensus::DeploymentPos pos = Consensus::DeploymentPos(j); - ThresholdState state = chainman.m_versionbitscache.State(pindexPrev, consensusParams, pos); - switch (state) { - case ThresholdState::DEFINED: - case ThresholdState::FAILED: - // Not exposed to GBT at all - break; - case ThresholdState::LOCKED_IN: - // Ensure bit is set in block version - block.nVersion |= chainman.m_versionbitscache.Mask(consensusParams, pos); - [[fallthrough]]; - case ThresholdState::STARTED: - { - const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit); - if (setClientRules.find(vbinfo.name) == setClientRules.end()) { - if (!vbinfo.gbt_force) { - // If the client doesn't support this, don't indicate it in the [default] version - block.nVersion &= ~chainman.m_versionbitscache.Mask(consensusParams, pos); - } - } - break; - } - case ThresholdState::ACTIVE: - { - // Add to rules only - const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; - aRules.push_back(gbt_vb_name(pos)); - if (setClientRules.find(vbinfo.name) == setClientRules.end()) { - // Not supported by the client; make sure it's safe to proceed - if (!vbinfo.gbt_force) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", vbinfo.name)); - } - } - break; - } + const auto gbtstatus = chainman.m_versionbitscache.GBTStatus(*pindexPrev, consensusParams); + + for (const auto& [name, info] : gbtstatus.signalling) { + vbavailable.pushKV(gbt_force_name(name, info.gbt_force), info.bit); + if (!info.gbt_force && !setClientRules.count(name)) { + // If the client doesn't support this, don't indicate it in the [default] version + block.nVersion &= ~info.mask; } } + + for (const auto& [name, info] : gbtstatus.locked_in) { + block.nVersion |= info.mask; + vbavailable.pushKV(gbt_force_name(name, info.gbt_force), info.bit); + if (!info.gbt_force && !setClientRules.count(name)) { + // If the client doesn't support this, don't indicate it in the [default] version + block.nVersion &= ~info.mask; + } + } + + for (const auto& [name, info] : gbtstatus.active) { + aRules.push_back(gbt_force_name(name, info.gbt_force)); + if (!info.gbt_force && !setClientRules.count(name)) { + // Not supported by the client; make sure it's safe to proceed + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Support for '%s' rule requires explicit client support", name)); + } + } + result.pushKV("version", block.nVersion); result.pushKV("rules", std::move(aRules)); result.pushKV("vbavailable", std::move(vbavailable)); diff --git a/src/versionbits.cpp b/src/versionbits.cpp index f54f8f6a787..0b416884a67 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include @@ -246,6 +247,37 @@ BIP9Info VersionBitsCache::Info(const CBlockIndex& block_index, const Consensus: return result; } +BIP9GBTStatus VersionBitsCache::GBTStatus(const CBlockIndex& block_index, const Consensus::Params& params) +{ + BIP9GBTStatus result; + + LOCK(m_mutex); + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + auto pos = static_cast(i); + VersionBitsConditionChecker checker(params, pos); + ThresholdState state = checker.GetStateFor(&block_index, m_caches[pos]); + const VBDeploymentInfo& vbdepinfo = VersionBitsDeploymentInfo[pos]; + BIP9GBTStatus::Info gbtinfo{.bit=params.vDeployments[pos].bit, .mask=checker.Mask(), .gbt_force=vbdepinfo.gbt_force}; + + switch (state) { + case DEFINED: + case FAILED: + // Not exposed to GBT + break; + case STARTED: + result.signalling.try_emplace(vbdepinfo.name, gbtinfo); + break; + case LOCKED_IN: + result.locked_in.try_emplace(vbdepinfo.name, gbtinfo); + break; + case ACTIVE: + result.active.try_emplace(vbdepinfo.name, gbtinfo); + break; + } + } + return result; +} + ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) { LOCK(m_mutex); diff --git a/src/versionbits.h b/src/versionbits.h index b435d313821..c9fefad9693 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -66,6 +66,15 @@ struct BIP9Info { std::optional active_since; }; +struct BIP9GBTStatus { + struct Info { + int bit; + uint32_t mask; + bool gbt_force; + }; + std::map> signalling, locked_in, active; +}; + /** * Abstract class that implements BIP9-style threshold logic, and caches results. */ @@ -109,6 +118,8 @@ public: BIP9Info Info(const CBlockIndex& block_index, const Consensus::Params& params, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); + BIP9GBTStatus GBTStatus(const CBlockIndex& block_index, const Consensus::Params& params) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); + /** Get the BIP9 state for a given deployment for the block after pindexPrev. */ ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);