Skip to content

Commit 4677151

Browse files
committed
Merge #9571: RPC: getblockchaininfo returns BIP signaling statistics
557c9a6 RPC: getblockchaininfo: BIP9 stats (Matthew Zipkin) Tree-SHA512: ecf0bf47f04f92becc77acc649fdfa270e768939acce42df39d30069398d40d9a30539862f7c307e08239f78d5c58c470ca5f6e717d2ab8e24db9be0dd7bec0c
2 parents 433c57a + 557c9a6 commit 4677151

File tree

6 files changed

+121
-7
lines changed

6 files changed

+121
-7
lines changed

src/rpc/blockchain.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
187187
latestblock.hash = pindex->GetBlockHash();
188188
latestblock.height = pindex->nHeight;
189189
}
190-
cond_blockchange.notify_all();
190+
cond_blockchange.notify_all();
191191
}
192192

193193
UniValue waitfornewblock(const JSONRPCRequest& request)
@@ -1074,6 +1074,17 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
10741074
rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime));
10751075
rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout));
10761076
rv.push_back(Pair("since", VersionBitsTipStateSinceHeight(consensusParams, id)));
1077+
if (THRESHOLD_STARTED == thresholdState)
1078+
{
1079+
UniValue statsUV(UniValue::VOBJ);
1080+
BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
1081+
statsUV.push_back(Pair("period", statsStruct.period));
1082+
statsUV.push_back(Pair("threshold", statsStruct.threshold));
1083+
statsUV.push_back(Pair("elapsed", statsStruct.elapsed));
1084+
statsUV.push_back(Pair("count", statsStruct.count));
1085+
statsUV.push_back(Pair("possible", statsStruct.possible));
1086+
rv.push_back(Pair("statistics", statsUV));
1087+
}
10771088
return rv;
10781089
}
10791090

@@ -1119,7 +1130,14 @@ UniValue getblockchaininfo(const JSONRPCRequest& request)
11191130
" \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n"
11201131
" \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n"
11211132
" \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n"
1122-
" \"since\": xx (numeric) height of the first block to which the status applies\n"
1133+
" \"since\": xx, (numeric) height of the first block to which the status applies\n"
1134+
" \"statistics\": { (object) numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)\n"
1135+
" \"period\": xx, (numeric) the length in blocks of the BIP9 signalling period \n"
1136+
" \"threshold\": xx, (numeric) the number of blocks with the version bit set required to activate the feature \n"
1137+
" \"elapsed\": xx, (numeric) the number of blocks elapsed since the beginning of the current period \n"
1138+
" \"count\": xx, (numeric) the number of blocks with the version bit set in the current period \n"
1139+
" \"possible\": xx (boolean) returns false if there are not enough blocks left in this period to pass activation threshold \n"
1140+
" }\n"
11231141
" }\n"
11241142
" }\n"
11251143
"}\n"

src/validation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3935,6 +3935,12 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
39353935
return VersionBitsState(chainActive.Tip(), params, pos, versionbitscache);
39363936
}
39373937

3938+
BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
3939+
{
3940+
LOCK(cs_main);
3941+
return VersionBitsStatistics(chainActive.Tip(), params, pos);
3942+
}
3943+
39383944
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)
39393945
{
39403946
LOCK(cs_main);

src/validation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,9 @@ std::string FormatStateMessage(const CValidationState &state);
339339
/** Get the BIP9 state for a given deployment at the current tip. */
340340
ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos);
341341

342+
/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */
343+
BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos);
344+
342345
/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
343346
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);
344347

src/versionbits.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include "versionbits.h"
6-
76
#include "consensus/params.h"
87

98
const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = {
@@ -105,6 +104,36 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
105104
return state;
106105
}
107106

107+
// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period
108+
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const
109+
{
110+
BIP9Stats stats;
111+
112+
stats.period = Period(params);
113+
stats.threshold = Threshold(params);
114+
115+
if (pindex == NULL)
116+
return stats;
117+
118+
// Find beginning of period
119+
const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period));
120+
stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight;
121+
122+
// Count from current block to beginning of period
123+
int count = 0;
124+
const CBlockIndex* currentIndex = pindex;
125+
while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){
126+
if (Condition(currentIndex, params))
127+
count++;
128+
currentIndex = currentIndex->pprev;
129+
}
130+
131+
stats.count = count;
132+
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count);
133+
134+
return stats;
135+
}
136+
108137
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
109138
{
110139
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
@@ -167,6 +196,11 @@ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::
167196
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
168197
}
169198

199+
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
200+
{
201+
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params);
202+
}
203+
170204
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
171205
{
172206
return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]);

src/versionbits.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ struct BIP9DeploymentInfo {
3737
bool gbt_force;
3838
};
3939

40+
struct BIP9Stats {
41+
int period;
42+
int threshold;
43+
int elapsed;
44+
int count;
45+
bool possible;
46+
};
47+
4048
extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[];
4149

4250
/**
@@ -51,6 +59,7 @@ class AbstractThresholdConditionChecker {
5159
virtual int Threshold(const Consensus::Params& params) const =0;
5260

5361
public:
62+
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
5463
// Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
5564
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
5665
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
@@ -64,6 +73,7 @@ struct VersionBitsCache
6473
};
6574

6675
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
76+
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
6777
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
6878
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);
6979

test/functional/bip9-softforks.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,43 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu
9999

100100
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
101101
assert_equal(self.get_bip9_status(bipName)['since'], 144)
102+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
103+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
102104
tmpl = self.nodes[0].getblocktemplate({})
103105
assert(bipName not in tmpl['rules'])
104106
assert_equal(tmpl['vbavailable'][bipName], bitno)
105107
assert_equal(tmpl['vbrequired'], 0)
106108
assert(tmpl['version'] & activated_version)
107109

110+
# Test 1-A
111+
# check stats after max number of "signalling not" blocks such that LOCKED_IN still possible this period
112+
test_blocks = self.generate_blocks(36, 4, test_blocks) # 0x00000004 (signalling not)
113+
test_blocks = self.generate_blocks(10, activated_version) # 0x20000001 (signalling ready)
114+
yield TestInstance(test_blocks, sync_every_block=False)
115+
116+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 46)
117+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
118+
assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
119+
120+
# Test 1-B
121+
# check stats after one additional "signalling not" block -- LOCKED_IN no longer possible this period
122+
test_blocks = self.generate_blocks(1, 4, test_blocks) # 0x00000004 (signalling not)
123+
yield TestInstance(test_blocks, sync_every_block=False)
124+
125+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 47)
126+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 10)
127+
assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], False)
128+
129+
# Test 1-C
130+
# finish period with "ready" blocks, but soft fork will still fail to advance to LOCKED_IN
131+
test_blocks = self.generate_blocks(97, activated_version) # 0x20000001 (signalling ready)
132+
yield TestInstance(test_blocks, sync_every_block=False)
133+
134+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
135+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
136+
assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
137+
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
138+
108139
# Test 2
109140
# Fail to achieve LOCKED_IN 100 out of 144 signal bit 1
110141
# using a variety of bits to simulate multiple parallel softforks
@@ -116,6 +147,8 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu
116147

117148
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
118149
assert_equal(self.get_bip9_status(bipName)['since'], 144)
150+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 0)
151+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 0)
119152
tmpl = self.nodes[0].getblocktemplate({})
120153
assert(bipName not in tmpl['rules'])
121154
assert_equal(tmpl['vbavailable'][bipName], bitno)
@@ -125,14 +158,24 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu
125158
# Test 3
126159
# 108 out of 144 signal bit 1 to achieve LOCKED_IN
127160
# using a variety of bits to simulate multiple parallel softforks
128-
test_blocks = self.generate_blocks(58, activated_version) # 0x20000001 (signalling ready)
161+
test_blocks = self.generate_blocks(57, activated_version) # 0x20000001 (signalling ready)
129162
test_blocks = self.generate_blocks(26, 4, test_blocks) # 0x00000004 (signalling not)
130163
test_blocks = self.generate_blocks(50, activated_version, test_blocks) # 0x20000101 (signalling ready)
131164
test_blocks = self.generate_blocks(10, 4, test_blocks) # 0x20010000 (signalling not)
132165
yield TestInstance(test_blocks, sync_every_block=False)
133166

167+
# check counting stats and "possible" flag before last block of this period achieves LOCKED_IN...
168+
assert_equal(self.get_bip9_status(bipName)['statistics']['elapsed'], 143)
169+
assert_equal(self.get_bip9_status(bipName)['statistics']['count'], 107)
170+
assert_equal(self.get_bip9_status(bipName)['statistics']['possible'], True)
171+
assert_equal(self.get_bip9_status(bipName)['status'], 'started')
172+
173+
# ...continue with Test 3
174+
test_blocks = self.generate_blocks(1, activated_version) # 0x20000001 (signalling ready)
175+
yield TestInstance(test_blocks, sync_every_block=False)
176+
134177
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
135-
assert_equal(self.get_bip9_status(bipName)['since'], 432)
178+
assert_equal(self.get_bip9_status(bipName)['since'], 576)
136179
tmpl = self.nodes[0].getblocktemplate({})
137180
assert(bipName not in tmpl['rules'])
138181

@@ -142,7 +185,7 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu
142185
yield TestInstance(test_blocks, sync_every_block=False)
143186

144187
assert_equal(self.get_bip9_status(bipName)['status'], 'locked_in')
145-
assert_equal(self.get_bip9_status(bipName)['since'], 432)
188+
assert_equal(self.get_bip9_status(bipName)['since'], 576)
146189
tmpl = self.nodes[0].getblocktemplate({})
147190
assert(bipName not in tmpl['rules'])
148191

@@ -168,7 +211,7 @@ def test_BIP(self, bipName, activated_version, invalidate, invalidatePostSignatu
168211
yield TestInstance([[block, True]])
169212

170213
assert_equal(self.get_bip9_status(bipName)['status'], 'active')
171-
assert_equal(self.get_bip9_status(bipName)['since'], 576)
214+
assert_equal(self.get_bip9_status(bipName)['since'], 720)
172215
tmpl = self.nodes[0].getblocktemplate({})
173216
assert(bipName in tmpl['rules'])
174217
assert(bipName not in tmpl['vbavailable'])

0 commit comments

Comments
 (0)