diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1269,6 +1269,69 @@ nCheckDepth); } +static void BIP9SoftForkDescPushBack(UniValue &softforks, + const std::string &name, + const Consensus::Params &consensusParams, + Consensus::DeploymentPos id) + EXCLUSIVE_LOCKS_REQUIRED(cs_main) { + // For BIP9 deployments. + // Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are + // hidden. A timeout value of 0 guarantees a softfork will never be + // activated. This is used when merging logic to implement a proposed + // softfork without a specified deployment schedule. + if (consensusParams.vDeployments[id].nTimeout <= 1230768000) { + return; + } + + UniValue bip9(UniValue::VOBJ); + const ThresholdState thresholdState = + VersionBitsTipState(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; + } + if (ThresholdState::STARTED == thresholdState) { + bip9.pushKV("bit", consensusParams.vDeployments[id].bit); + } + bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); + bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); + int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id); + bip9.pushKV("since", since_height); + if (ThresholdState::STARTED == thresholdState) { + UniValue statsUV(UniValue::VOBJ); + BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id); + statsUV.pushKV("period", statsStruct.period); + statsUV.pushKV("threshold", statsStruct.threshold); + statsUV.pushKV("elapsed", statsStruct.elapsed); + statsUV.pushKV("count", statsStruct.count); + statsUV.pushKV("possible", statsStruct.possible); + bip9.pushKV("statistics", statsUV); + } + + UniValue rv(UniValue::VOBJ); + rv.pushKV("type", "bip9"); + rv.pushKV("bip9", bip9); + if (ThresholdState::ACTIVE == thresholdState) { + rv.pushKV("height", since_height); + } + rv.pushKV("active", ThresholdState::ACTIVE == thresholdState); + + softforks.pushKV(name, rv); +} + UniValue getblockchaininfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { @@ -1348,6 +1411,12 @@ } } + UniValue softforks(UniValue::VOBJ); + BIP9SoftForkDescPushBack(softforks, "testdummy", + config.GetChainParams().GetConsensus(), + Consensus::DEPLOYMENT_TESTDUMMY); + obj.pushKV("softforks", softforks); + obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } diff --git a/src/validation.h b/src/validation.h --- a/src/validation.h +++ b/src/validation.h @@ -491,6 +491,27 @@ std::vector *pvChecks = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +/** Get the BIP9 state for a given deployment at the current tip. */ +ThresholdState VersionBitsTipState(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos); + +/** + * Get the numerical statistics for the BIP9 state for a given deployment at the + * current tip. + */ +BIP9Stats VersionBitsTipStatistics(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos); + +/** + * Get the block height at which the BIP9 deployment switched into the state for + * the block building on the current tip. + */ +int VersionBitsTipStateSinceHeight(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos); + +/** Apply the effects of this transaction on the UTXO set represented by view */ +void UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, int nHeight); + /** * Mark all the coins corresponding to a given transaction inputs as spent. */ diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5566,6 +5566,25 @@ return &vinfoBlockFile.at(n); } +ThresholdState VersionBitsTipState(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos) { + LOCK(cs_main); + return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache); +} + +BIP9Stats VersionBitsTipStatistics(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos) { + LOCK(cs_main); + return VersionBitsStatistics(chainActive.Tip(), params, pos); +} + +int VersionBitsTipStateSinceHeight(const Consensus::Params ¶ms, + Consensus::DeploymentPos pos) { + LOCK(cs_main); + return VersionBitsStateSinceHeight(chainActive.Tip(), params, pos, + versionbitscache); +} + static const uint64_t MEMPOOL_DUMP_VERSION = 1; bool LoadMempool(const Config &config, CTxMemPool &pool) { diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -79,6 +79,7 @@ 'mediantime', 'pruned', 'size_on_disk', + 'softforks', 'verificationprogress', 'warnings', ] @@ -92,6 +93,27 @@ # size_on_disk should be > 0 assert_greater_than(res['size_on_disk'], 0) + assert_equal(res['softforks'], { + 'testdummy': { + 'type': 'bip9', + 'bip9': { + 'status': 'started', + 'bit': 28, + 'start_time': 0, + # testdummy does not have a timeout so is set to the max int64 value + 'timeout': 0x7fffffffffffffff, + 'since': 144, + 'statistics': { + 'period': 144, + 'threshold': 108, + 'elapsed': 57, + 'count': 57, + 'possible': True, + }, + }, + 'active': False} + }) + # pruneheight should be greater or equal to 0 assert_greater_than_or_equal(res['pruneheight'], 0)