Skip to content

Commit 3862e47

Browse files
committed
[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.
1 parent 3a3d8b8 commit 3862e47

File tree

5 files changed

+81
-99
lines changed

5 files changed

+81
-99
lines changed

src/rpc/blockchain.cpp

Lines changed: 66 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,54 +1183,49 @@ static UniValue verifychain(const JSONRPCRequest& request)
11831183
return CVerifyDB().VerifyDB(Params(), pcoinsTip.get(), nCheckLevel, nCheckDepth);
11841184
}
11851185

1186-
/** Implementation of IsSuperMajority with better feedback */
1187-
static UniValue SoftForkMajorityDesc(int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
1186+
static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name, int height) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
11881187
{
1189-
UniValue rv(UniValue::VOBJ);
1190-
bool activated = false;
1191-
switch(version)
1192-
{
1193-
case 2:
1194-
activated = pindex->nHeight >= consensusParams.BIP34Height;
1195-
break;
1196-
case 3:
1197-
activated = pindex->nHeight >= consensusParams.BIP66Height;
1198-
break;
1199-
case 4:
1200-
activated = pindex->nHeight >= consensusParams.BIP65Height;
1201-
break;
1202-
}
1203-
rv.pushKV("status", activated);
1204-
return rv;
1205-
}
1188+
// For buried deployments.
1189+
// A buried deployment is one where the height of the activation has been hardcoded into
1190+
// the client implementation long after the consensus change has activated. See BIP 90.
1191+
// Buried deployments with activation height value of
1192+
// std::numeric_limits<int>::max() are disabled and thus hidden.
1193+
if (height == std::numeric_limits<int>::max()) return;
12061194

1207-
static UniValue SoftForkDesc(const std::string &name, int version, const CBlockIndex* pindex, const Consensus::Params& consensusParams)
1208-
{
12091195
UniValue rv(UniValue::VOBJ);
1210-
rv.pushKV("id", name);
1211-
rv.pushKV("version", version);
1212-
rv.pushKV("reject", SoftForkMajorityDesc(version, pindex, consensusParams));
1213-
return rv;
1196+
rv.pushKV("type", "buried");
1197+
// getblockchaininfo reports the softfork as active from when the chain height is
1198+
// one below the activation height
1199+
rv.pushKV("active", ::ChainActive().Tip()->nHeight + 1 >= height);
1200+
rv.pushKV("height", height);
1201+
softforks.pushKV(name, rv);
12141202
}
12151203

1216-
static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
1204+
static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
12171205
{
1218-
UniValue rv(UniValue::VOBJ);
1206+
// For BIP9 deployments.
1207+
// Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
1208+
// A timeout value of 0 guarantees a softfork will never be activated.
1209+
// This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
1210+
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
1211+
1212+
UniValue bip9(UniValue::VOBJ);
12191213
const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id);
12201214
switch (thresholdState) {
1221-
case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break;
1222-
case ThresholdState::STARTED: rv.pushKV("status", "started"); break;
1223-
case ThresholdState::LOCKED_IN: rv.pushKV("status", "locked_in"); break;
1224-
case ThresholdState::ACTIVE: rv.pushKV("status", "active"); break;
1225-
case ThresholdState::FAILED: rv.pushKV("status", "failed"); break;
1215+
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
1216+
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
1217+
case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
1218+
case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
1219+
case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
12261220
}
12271221
if (ThresholdState::STARTED == thresholdState)
12281222
{
1229-
rv.pushKV("bit", consensusParams.vDeployments[id].bit);
1223+
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
12301224
}
1231-
rv.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
1232-
rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
1233-
rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id));
1225+
bip9.pushKV("startTime", consensusParams.vDeployments[id].nStartTime);
1226+
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
1227+
int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
1228+
bip9.pushKV("since", since_height);
12341229
if (ThresholdState::STARTED == thresholdState)
12351230
{
12361231
UniValue statsUV(UniValue::VOBJ);
@@ -1240,18 +1235,18 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
12401235
statsUV.pushKV("elapsed", statsStruct.elapsed);
12411236
statsUV.pushKV("count", statsStruct.count);
12421237
statsUV.pushKV("possible", statsStruct.possible);
1243-
rv.pushKV("statistics", statsUV);
1238+
bip9.pushKV("statistics", statsUV);
12441239
}
1245-
return rv;
1246-
}
12471240

1248-
static void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
1249-
{
1250-
// Deployments with timeout value of 0 are hidden.
1251-
// A timeout value of 0 guarantees a softfork will never be activated.
1252-
// This is used when softfork codes are merged without specifying the deployment schedule.
1253-
if (consensusParams.vDeployments[id].nTimeout > 0)
1254-
bip9_softforks.pushKV(VersionBitsDeploymentInfo[id].name, BIP9SoftForkDesc(consensusParams, id));
1241+
UniValue rv(UniValue::VOBJ);
1242+
rv.pushKV("type", "bip9");
1243+
rv.pushKV("bip9", bip9);
1244+
if (ThresholdState::ACTIVE == thresholdState) {
1245+
rv.pushKV("height", since_height);
1246+
}
1247+
rv.pushKV("active", ThresholdState::ACTIVE == thresholdState);
1248+
1249+
softforks.pushKV(name, rv);
12551250
}
12561251

12571252
UniValue getblockchaininfo(const JSONRPCRequest& request)
@@ -1275,29 +1270,25 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
12751270
" \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n"
12761271
" \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n"
12771272
" \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n"
1278-
" \"softforks\": [ (array) status of softforks in progress\n"
1279-
" {\n"
1280-
" \"id\": \"xxxx\", (string) name of softfork\n"
1281-
" \"version\": xx, (numeric) block version\n"
1282-
" \"reject\": { (object) progress toward rejecting pre-softfork blocks\n"
1283-
" \"status\": xx, (boolean) true if threshold reached\n"
1284-
" },\n"
1285-
" }, ...\n"
1286-
" ],\n"
1287-
" \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n"
1273+
" \"softforks\": { (object) status of softforks\n"
12881274
" \"xxxx\" : { (string) name of the softfork\n"
1289-
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
1290-
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
1291-
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
1292-
" \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
1293-
" \"since\": xx, (numeric) height of the first block to which the status applies\n"
1294-
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
1295-
" \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
1296-
" \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
1297-
" \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
1298-
" \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
1299-
" \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
1300-
" }\n"
1275+
" \"type\": \"xxxx\", (string) one of \"buried\", \"bip9\"\n"
1276+
" \"bip9\": { (object) status of bip9 softforks (only for \"bip9\" type)\n"
1277+
" \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n"
1278+
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
1279+
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
1280+
" \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
1281+
" \"since\": xx, (numeric) height of the first block to which the status applies\n"
1282+
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork\n"
1283+
" \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
1284+
" \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
1285+
" \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
1286+
" \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
1287+
" \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
1288+
" }\n"
1289+
" },\n"
1290+
" \"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"
1291+
" \"active\": xx, (boolean) true if the rules are enforced for the mempool and the next block\n"
13011292
" }\n"
13021293
" }\n"
13031294
" \"warnings\" : \"...\", (string) any network and blockchain warnings.\n"
@@ -1342,16 +1333,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
13421333
}
13431334

13441335
const Consensus::Params& consensusParams = Params().GetConsensus();
1345-
UniValue softforks(UniValue::VARR);
1346-
UniValue bip9_softforks(UniValue::VOBJ);
1347-
softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams));
1348-
softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams));
1349-
softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams));
1350-
for (int pos = Consensus::DEPLOYMENT_CSV; pos != Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++pos) {
1351-
BIP9SoftForkDescPushBack(bip9_softforks, consensusParams, static_cast<Consensus::DeploymentPos>(pos));
1352-
}
1336+
UniValue softforks(UniValue::VOBJ);
1337+
BuriedForkDescPushBack(softforks, "bip34", consensusParams.BIP34Height);
1338+
BuriedForkDescPushBack(softforks, "bip66", consensusParams.BIP66Height);
1339+
BuriedForkDescPushBack(softforks, "bip65", consensusParams.BIP65Height);
1340+
BIP9SoftForkDescPushBack(softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV);
1341+
BIP9SoftForkDescPushBack(softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT);
1342+
BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY);
13531343
obj.pushKV("softforks", softforks);
1354-
obj.pushKV("bip9_softforks", bip9_softforks);
13551344

13561345
obj.pushKV("warnings", GetWarnings("statusbar"));
13571346
return obj;

test/functional/feature_cltv.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,11 @@ def skip_test_if_missing_module(self):
6969
self.skip_if_no_wallet()
7070

7171
def test_cltv_info(self, *, is_active):
72-
assert_equal(
73-
next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip65'),
72+
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip65'],
7473
{
75-
"id": "bip65",
76-
"version": 4,
77-
"reject": {
78-
"status": is_active
79-
}
74+
"active": is_active,
75+
"height": CLTV_HEIGHT,
76+
"type": "buried",
8077
},
8178
)
8279

@@ -104,9 +101,9 @@ def run_test(self):
104101
block.hashMerkleRoot = block.calc_merkle_root()
105102
block.solve()
106103

107-
self.test_cltv_info(is_active=False)
104+
self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
108105
self.nodes[0].p2p.send_and_ping(msg_block(block))
109-
self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
106+
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
110107
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
111108

112109
self.log.info("Test that blocks must now be at least version 4")
@@ -155,7 +152,7 @@ def run_test(self):
155152
block.hashMerkleRoot = block.calc_merkle_root()
156153
block.solve()
157154

158-
self.test_cltv_info(is_active=False) # Not active as of current tip, but next block must obey rules
155+
self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules
159156
self.nodes[0].p2p.send_and_ping(msg_block(block))
160157
self.test_cltv_info(is_active=True) # Active as of current tip
161158
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)

test/functional/feature_dersig.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,11 @@ def skip_test_if_missing_module(self):
5252
self.skip_if_no_wallet()
5353

5454
def test_dersig_info(self, *, is_active):
55-
assert_equal(
56-
next(s for s in self.nodes[0].getblockchaininfo()['softforks'] if s['id'] == 'bip66'),
55+
assert_equal(self.nodes[0].getblockchaininfo()['softforks']['bip66'],
5756
{
58-
"id": "bip66",
59-
"version": 3,
60-
"reject": {
61-
"status": is_active
62-
}
57+
"active": is_active,
58+
"height": DERSIG_HEIGHT,
59+
"type": "buried",
6360
},
6461
)
6562

@@ -88,9 +85,9 @@ def run_test(self):
8885
block.rehash()
8986
block.solve()
9087

91-
self.test_dersig_info(is_active=False)
88+
self.test_dersig_info(is_active=False) # Not active as of current tip and next block does not need to obey rules
9289
self.nodes[0].p2p.send_and_ping(msg_block(block))
93-
self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
90+
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
9491
assert_equal(self.nodes[0].getbestblockhash(), block.hash)
9592

9693
self.log.info("Test that blocks must now be at least version 3")
@@ -144,7 +141,7 @@ def run_test(self):
144141
block.rehash()
145142
block.solve()
146143

147-
self.test_dersig_info(is_active=False) # Not active as of current tip, but next block must obey rules
144+
self.test_dersig_info(is_active=True) # Not active as of current tip, but next block must obey rules
148145
self.nodes[0].p2p.send_and_ping(msg_block(block))
149146
self.test_dersig_info(is_active=True) # Active as of current tip
150147
assert_equal(int(self.nodes[0].getbestblockhash(), 16), block.sha256)

test/functional/rpc_blockchain.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ def _test_getblockchaininfo(self):
7878

7979
keys = [
8080
'bestblockhash',
81-
'bip9_softforks',
8281
'blocks',
8382
'chain',
8483
'chainwork',

test/functional/test_framework/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ def delete_cookie_file(datadir):
344344

345345
def get_bip9_status(node, key):
346346
info = node.getblockchaininfo()
347-
return info['bip9_softforks'][key]
347+
return info['softforks'][key]['bip9']
348348

349349
def set_node_times(nodes, t):
350350
for node in nodes:

0 commit comments

Comments
 (0)