mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-04 13:55:23 -05:00
[rpc] Tidy up reporting of buried and ongoing softforks
This combines reporting of buried (formally ISM) softfork deployments and BIP9 versionbits softfork deployments into one JSON object in the getblockchaininfo return object.
This commit is contained in:
parent
3a3d8b8357
commit
3862e473f0
5 changed files with 81 additions and 99 deletions
|
@ -1183,54 +1183,49 @@ static UniValue verifychain(const JSONRPCRequest& request)
|
||||||
return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
|
return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implementation of IsSuperMajority with better feedback */
|
static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
|
||||||
{
|
{
|
||||||
|
// For buried deployments.
|
||||||
|
// A buried deployment is one where the height of the activation has been hardcoded into
|
||||||
|
// the client implementation long after the consensus change has activated. See BIP 90.
|
||||||
|
// Buried deployments with activation height value of
|
||||||
|
// std::numeric_limits<int>::max() are disabled and thus hidden.
|
||||||
|
if (height == std::numeric_limits<int>::max()) return;
|
||||||
|
|
||||||
UniValue rv(UniValue::VOBJ);
|
UniValue rv(UniValue::VOBJ);
|
||||||
bool activated = false;
|
rv.pushKV("type", "buried");
|
||||||
switch(version)
|
// getblockchaininfo reports the softfork as active from when the chain height is
|
||||||
{
|
// one below the activation height
|
||||||
case 2:
|
rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height);
|
||||||
activated = pindex->nHeight >= consensusParams.BIP34Height;
|
rv.pushKV("height", height);
|
||||||
break;
|
softforks.pushKV(name, rv);
|
||||||
case 3:
|
|
||||||
activated = pindex->nHeight >= consensusParams.BIP66Height;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
activated = pindex->nHeight >= consensusParams.BIP65Height;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rv.pushKV("status", activated);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
|
static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
UniValue rv(UniValue::VOBJ);
|
// For BIP9 deployments.
|
||||||
rv.pushKV("id", name);
|
// Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
|
||||||
rv.pushKV("version", version);
|
// A timeout value of 0 guarantees a softfork will never be activated.
|
||||||
rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams));
|
// This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
|
||||||
return rv;
|
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
|
||||||
}
|
|
||||||
|
|
||||||
static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
|
UniValue bip9(UniValue::VOBJ);
|
||||||
{
|
|
||||||
UniValue rv(UniValue::VOBJ);
|
|
||||||
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
|
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
|
||||||
switch (thresholdState) {
|
switch (thresholdState) {
|
||||||
case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
|
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
|
||||||
case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
|
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
|
||||||
case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break;
|
case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
|
||||||
case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break;
|
case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
|
||||||
case ThresholdState::FAILED: rv.pushKV("status", "failed"); break;
|
case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
|
||||||
}
|
}
|
||||||
if (ThresholdState::STARTED == thresholdState)
|
if (ThresholdState::STARTED == thresholdState)
|
||||||
{
|
{
|
||||||
rv.pushKV("bit", consensusParams.vDeployments[id].bit);
|
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
|
||||||
}
|
}
|
||||||
rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
|
bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
|
||||||
rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
|
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
|
||||||
rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
|
int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
|
||||||
|
bip9.pushKV("since", since_height);
|
||||||
if (ThresholdState::STARTED == thresholdState)
|
if (ThresholdState::STARTED == thresholdState)
|
||||||
{
|
{
|
||||||
UniValue statsUV(UniValue::VOBJ);
|
UniValue statsUV(UniValue::VOBJ);
|
||||||
|
@ -1240,18 +1235,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
|
||||||
statsUV.pushKV("elapsed", statsStruct.elapsed);
|
statsUV.pushKV("elapsed", statsStruct.elapsed);
|
||||||
statsUV.pushKV("count", statsStruct.count);
|
statsUV.pushKV("count", statsStruct.count);
|
||||||
statsUV.pushKV("possible", statsStruct.possible);
|
statsUV.pushKV("possible", statsStruct.possible);
|
||||||
rv.pushKV("statistics", statsUV);
|
bip9.pushKV("statistics", statsUV);
|
||||||
}
|
}
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
|
UniValue rv(UniValue::VOBJ);
|
||||||
{
|
rv.pushKV("type", "bip9");
|
||||||
// Deployments with timeout value of 0 are hidden.
|
rv.pushKV("bip9", bip9);
|
||||||
// A timeout value of 0 guarantees a softfork will never be activated.
|
if (ThresholdState::ACTIVE == thresholdState) {
|
||||||
// This is used when softfork codes are merged without specifying the deployment schedule.
|
rv.pushKV("height", since_height);
|
||||||
if (consensusParams.vDeployments[id].nTimeout > 0)
|
}
|
||||||
bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id));
|
rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
|
||||||
|
|
||||||
|
softforks.pushKV(name, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue getblockchaininfo(const JSONRPCRequest& request)
|
UniValue getblockchaininfo(const JSONRPCRequest& request)
|
||||||
|
@ -1275,29 +1270,25 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
|
||||||
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
|
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
|
||||||
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
|
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
|
||||||
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
|
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
|
||||||
" \"softforks\": [ (array) status of softforks in progress\n"
|
" \"softforks\": { (object) status of softforks\n"
|
||||||
" {\n"
|
|
||||||
" \"id\": \"xxxx\", (string) name of softfork\n"
|
|
||||||
" \"version\": xx, (numeric) block version\n"
|
|
||||||
" \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
|
|
||||||
" \"status\": xx, (boolean) true if threshold reached\n"
|
|
||||||
" },\n"
|
|
||||||
" }, ...\n"
|
|
||||||
" ],\n"
|
|
||||||
" \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
|
|
||||||
" \"xxxx\" : { (string) name of the softfork\n"
|
" \"xxxx\" : { (string) name of the softfork\n"
|
||||||
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
|
" \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n"
|
||||||
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
|
" \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n"
|
||||||
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
|
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
|
||||||
" \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
|
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
|
||||||
" \"since\": xx, (numeric) height of the first block to which the status applies\n"
|
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
|
||||||
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
|
" \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
|
||||||
" \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
|
" \"since\": xx, (numeric) height of the first block to which the status applies\n"
|
||||||
" \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
|
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n"
|
||||||
" \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
|
" \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
|
||||||
" \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
|
" \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
|
||||||
" \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
|
" \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
|
||||||
" }\n"
|
" \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
|
||||||
|
" \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
|
||||||
|
" }\n"
|
||||||
|
" },\n"
|
||||||
|
" \"height\": \"xxxxxx\", (numeric) height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)\n"
|
||||||
|
" \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
|
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
|
||||||
|
@ -1342,16 +1333,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Consensus::Params& consensusParams = Params().GetConsensus();
|
const Consensus::Params& consensusParams = Params().GetConsensus();
|
||||||
UniValue softforks(UniValue::VARR);
|
UniValue softforks(UniValue::VOBJ);
|
||||||
UniValue bip9_softforks(UniValue::VOBJ);
|
BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height);
|
||||||
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
|
BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height);
|
||||||
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
|
BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height);
|
||||||
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
|
BIP9SoftForkDescPushBack(softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV);
|
||||||
for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
|
BIP9SoftForkDescPushBack(softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT);
|
||||||
BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
|
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
|
||||||
}
|
|
||||||
obj.pushKV("softforks", softforks);
|
obj.pushKV("softforks", softforks);
|
||||||
obj.pushKV("bip9_softforks", bip9_softforks);
|
|
||||||
|
|
||||||
obj.pushKV("warnings", GetWarnings("statusbar"));
|
obj.pushKV("warnings", GetWarnings("statusbar"));
|
||||||
return obj;
|
return obj;
|
||||||
|
|
|
@ -69,14 +69,11 @@ class BIP65Test(BitcoinTestFramework):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
|
||||||
def test_cltv_info(self, *, is_active):
|
def test_cltv_info(self, *, is_active):
|
||||||
assert_equal(
|
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'],
|
||||||
next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'),
|
|
||||||
{
|
{
|
||||||
"id": "bip65",
|
"active": is_active,
|
||||||
"version": 4,
|
"height": CLTV_HEIGHT,
|
||||||
"reject": {
|
"type": "buried",
|
||||||
"status": is_active
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -104,9 +101,9 @@ class BIP65Test(BitcoinTestFramework):
|
||||||
block.hashMerkleRoot = block.calc_merkle_root()
|
block.hashMerkleRoot = block.calc_merkle_root()
|
||||||
block.solve()
|
block.solve()
|
||||||
|
|
||||||
self.test_cltv_info(is_active=False)
|
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
|
||||||
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
||||||
self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
|
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
|
||||||
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
|
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
|
||||||
|
|
||||||
self.log.info("Test that blocks must now be at least version 4")
|
self.log.info("Test that blocks must now be at least version 4")
|
||||||
|
@ -155,7 +152,7 @@ class BIP65Test(BitcoinTestFramework):
|
||||||
block.hashMerkleRoot = block.calc_merkle_root()
|
block.hashMerkleRoot = block.calc_merkle_root()
|
||||||
block.solve()
|
block.solve()
|
||||||
|
|
||||||
self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
|
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
|
||||||
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
||||||
self.test_cltv_info(is_active=True) # Active as of current tip
|
self.test_cltv_info(is_active=True) # Active as of current tip
|
||||||
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
|
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
|
||||||
|
|
|
@ -52,14 +52,11 @@ class BIP66Test(BitcoinTestFramework):
|
||||||
self.skip_if_no_wallet()
|
self.skip_if_no_wallet()
|
||||||
|
|
||||||
def test_dersig_info(self, *, is_active):
|
def test_dersig_info(self, *, is_active):
|
||||||
assert_equal(
|
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
|
||||||
next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'),
|
|
||||||
{
|
{
|
||||||
"id": "bip66",
|
"active": is_active,
|
||||||
"version": 3,
|
"height": DERSIG_HEIGHT,
|
||||||
"reject": {
|
"type": "buried",
|
||||||
"status": is_active
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -88,9 +85,9 @@ class BIP66Test(BitcoinTestFramework):
|
||||||
block.rehash()
|
block.rehash()
|
||||||
block.solve()
|
block.solve()
|
||||||
|
|
||||||
self.test_dersig_info(is_active=False)
|
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
|
||||||
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
||||||
self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
|
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
|
||||||
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
|
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
|
||||||
|
|
||||||
self.log.info("Test that blocks must now be at least version 3")
|
self.log.info("Test that blocks must now be at least version 3")
|
||||||
|
@ -144,7 +141,7 @@ class BIP66Test(BitcoinTestFramework):
|
||||||
block.rehash()
|
block.rehash()
|
||||||
block.solve()
|
block.solve()
|
||||||
|
|
||||||
self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
|
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
|
||||||
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
self.nodes[0].p2p.send_and_ping(msg_block(block))
|
||||||
self.test_dersig_info(is_active=True) # Active as of current tip
|
self.test_dersig_info(is_active=True) # Active as of current tip
|
||||||
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
|
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)
|
||||||
|
|
|
@ -78,7 +78,6 @@ class BlockchainTest(BitcoinTestFramework):
|
||||||
|
|
||||||
keys = [
|
keys = [
|
||||||
'bestblockhash',
|
'bestblockhash',
|
||||||
'bip9_softforks',
|
|
||||||
'blocks',
|
'blocks',
|
||||||
'chain',
|
'chain',
|
||||||
'chainwork',
|
'chainwork',
|
||||||
|
|
|
@ -344,7 +344,7 @@ def delete_cookie_file(datadir):
|
||||||
|
|
||||||
def get_bip9_status(node, key):
|
def get_bip9_status(node, key):
|
||||||
info = node.getblockchaininfo()
|
info = node.getblockchaininfo()
|
||||||
return info['bip9_softforks'][key]
|
return info['softforks'][key]['bip9']
|
||||||
|
|
||||||
def set_node_times(nodes, t):
|
def set_node_times(nodes, t):
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
|
Loading…
Add table
Reference in a new issue