Skip to content

Commit d211df0

Browse files
committed
deploymentinfo: report signalling
1 parent e4dff2a commit d211df0

File tree

5 files changed

+106
-0
lines changed

5 files changed

+106
-0
lines changed

src/rpc/blockchain.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,24 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
14971497
case ThresholdState::LOCKED_IN:
14981498
case ThresholdState::ACTIVE:
14991499
bip9.pushKV("signal_abandon", strprintf("%08x", consensusParams.vDeployments[id].signal_abandon));
1500+
1501+
// Report observed signalling
1502+
{
1503+
UniValue signals(UniValue::VARR);
1504+
for (const auto& signal_info : g_versionbitscache.GetSignalInfo(blockindex, consensusParams, id)) {
1505+
UniValue s(UniValue::VOBJ);
1506+
if (signal_info.bip_version == -1) {
1507+
// don't report self-activation signals if already active
1508+
if (signal_info.activate && current_state == ThresholdState::ACTIVE) continue;
1509+
} else {
1510+
s.pushKV("bip_version", signal_info.bip_version);
1511+
}
1512+
s.pushKV("height", signal_info.height);
1513+
s.pushKV("action", (signal_info.activate ? "activate" : "abandon"));
1514+
signals.push_back(s);
1515+
}
1516+
bip9.pushKV("signals", signals);
1517+
}
15001518
break;
15011519
case ThresholdState::DEACTIVATING:
15021520
case ThresholdState::ABANDONED:

src/test/fuzz/versionbits.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
#include <test/fuzz/fuzz.h>
1414
#include <test/fuzz/util.h>
1515

16+
#include <algorithm>
1617
#include <cstdint>
1718
#include <limits>
1819
#include <memory>
1920
#include <vector>
2021

22+
bool operator==(const SignalInfo& a, const SignalInfo& b)
23+
{
24+
return a.height == b.height && a.bip_version == b.bip_version && a.activate == b.activate;
25+
}
26+
2127
namespace {
2228
class TestConditionChecker : public AbstractThresholdConditionChecker
2329
{
@@ -165,6 +171,18 @@ FUZZ_TARGET_INIT(versionbits, initialize)
165171
// Now that we have chosen time and versions, setup to mine blocks
166172
Blocks blocks(block_start_time, interval);
167173

174+
const auto siginfo_nosignal = [&]() -> std::optional<SignalInfo> {
175+
int bip, bip_ver;
176+
if (checker.BIP(bip, bip_ver)) {
177+
if ((ver_nosignal & 0xFFFFFF00l) == (ver_activate & 0xFFFFFF00l)) {
178+
return SignalInfo{.height = 0, .bip_version = static_cast<uint8_t>(ver_nosignal & 0xFF), .activate = true};
179+
} else if ((ver_nosignal & 0xFFFFFF00l) == (ver_abandon & 0xFFFFFF00l)) {
180+
return SignalInfo{.height = 0, .bip_version = static_cast<uint8_t>(ver_nosignal & 0xFF), .activate = false};
181+
}
182+
}
183+
return std::nullopt;
184+
}();
185+
168186
/* Strategy:
169187
* * we mine n*period blocks, with zero/one of
170188
* those blocks signalling activation, and zero/one of
@@ -207,8 +225,13 @@ FUZZ_TARGET_INIT(versionbits, initialize)
207225
bool sig_abandon = false;
208226
int next_active = 0;
209227
int next_abandon = 0;
228+
229+
std::vector<SignalInfo> exp_siginfo = checker.GetSignalInfo(nullptr); // dummy
230+
assert(exp_siginfo.empty());
231+
210232
for (int b = 1; b <= period; ++b) {
211233
CBlockIndex* current_block = blocks.tip();
234+
const int next_height = (current_block != nullptr ? current_block->nHeight + 1 : 0);
212235

213236
if (current_block != nullptr) {
214237
// state and since don't change within the period
@@ -221,14 +244,24 @@ FUZZ_TARGET_INIT(versionbits, initialize)
221244
while (b > next_abandon) next_abandon += 1 + fuzzed_data_provider.ConsumeIntegral<uint8_t>();
222245
while (b > next_active) next_active += 1 + fuzzed_data_provider.ConsumeIntegral<uint8_t>();
223246
if (b == next_abandon) {
247+
exp_siginfo.push_back({.height = next_height, .bip_version=-1, .activate=false});
224248
blocks.mine_block(ver_abandon);
225249
sig_abandon = true;
226250
} else if (b == next_active) {
251+
exp_siginfo.push_back({.height = next_height, .bip_version=-1, .activate=true});
227252
blocks.mine_block(ver_activate);
228253
sig_active = true;
229254
} else {
255+
if (siginfo_nosignal) {
256+
exp_siginfo.push_back(*siginfo_nosignal);
257+
exp_siginfo.back().height = next_height;
258+
}
230259
blocks.mine_block(ver_nosignal);
231260
}
261+
262+
const std::vector<SignalInfo> siginfo = checker.GetSignalInfo(blocks.tip());
263+
assert(siginfo.size() == exp_siginfo.size());
264+
assert(std::equal(siginfo.begin(), siginfo.end(), exp_siginfo.rbegin(), exp_siginfo.rend()));
232265
}
233266

234267
// grab the final block and the state it's left us in

src/versionbits.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,42 @@ int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex*
174174
return pindexPrev->nHeight + 1;
175175
}
176176

177+
std::vector<SignalInfo> AbstractThresholdConditionChecker::GetSignalInfo(const CBlockIndex* pindex) const
178+
{
179+
std::vector<SignalInfo> result;
180+
181+
int bip = 0, bip_version = 0;
182+
const bool check_other_versions = BIP(bip, bip_version);
183+
184+
const int32_t activate = ActivateVersion();
185+
const int32_t abandon = AbandonVersion();
186+
const int period = Period();
187+
188+
while (pindex != nullptr) {
189+
if (pindex->nVersion == activate) {
190+
result.push_back({ .height = pindex->nHeight, .bip_version = -1, .activate = true });
191+
} else if (pindex->nVersion == abandon) {
192+
result.push_back({ .height = pindex->nHeight, .bip_version = -1, .activate = false });
193+
} else if (check_other_versions) {
194+
if ((pindex->nVersion & 0x00FFFF00l) == (bip << 8)) {
195+
SignalInfo s;
196+
s.height = pindex->nHeight;
197+
s.bip_version = static_cast<uint8_t>(pindex->nVersion & 0xFF);
198+
if ((pindex->nVersion & 0xFF000000l) == VERSIONBITS_TOP_ACTIVE) {
199+
s.activate = true;
200+
result.push_back(s);
201+
} else if ((pindex->nVersion & 0xFF000000l) == VERSIONBITS_TOP_ABANDON) {
202+
s.activate = false;
203+
result.push_back(s);
204+
}
205+
}
206+
}
207+
if (pindex->nHeight % period == 0) break;
208+
pindex = pindex->pprev;
209+
}
210+
return result;
211+
}
212+
177213
namespace
178214
{
179215
/**
@@ -209,6 +245,11 @@ int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Cons
209245
return VersionBitsConditionChecker(params, pos).GetStateSinceHeightFor(pindexPrev, m_caches[pos]);
210246
}
211247

248+
std::vector<SignalInfo> VersionBitsCache::GetSignalInfo(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos) const
249+
{
250+
return VersionBitsConditionChecker(params, pos).GetSignalInfo(pindex);
251+
}
252+
212253
int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
213254
{
214255
int32_t nVersion = VERSIONBITS_TOP_BITS;

src/versionbits.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ enum class ThresholdState {
3636
// will either be nullptr or a block with (height + 1) % Period() == 0.
3737
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
3838

39+
struct SignalInfo
40+
{
41+
int height;
42+
int bip_version; // -1 = this version
43+
bool activate; // true = activate, false = abandon
44+
};
45+
3946
/**
4047
* Abstract class that implements BIP9-style threshold logic, and caches results.
4148
*/
@@ -56,6 +63,9 @@ class AbstractThresholdConditionChecker {
5663

5764
/** Report bip number and version, based on nVersion signalling standard */
5865
bool BIP(int& bip, int& version) const;
66+
67+
/** Returns signalling information */
68+
std::vector<SignalInfo> GetSignalInfo(const CBlockIndex* pindex) const;
5969
};
6070

6171
/** BIP 9 allows multiple softforks to be deployed in parallel. We cache
@@ -80,6 +90,9 @@ class VersionBitsCache
8090
/** Report bip number and version, based on nVersion signalling standard */
8191
bool BIP(int& bip, int& bip_version, const Consensus::Params& params, Consensus::DeploymentPos pos) const;
8292

93+
/** Returns signalling information */
94+
std::vector<SignalInfo> GetSignalInfo(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos) const;
95+
8396
void Clear();
8497
};
8598

test/functional/rpc_blockchain.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ def check_signalling_deploymentinfo_result(self, gdi_result, height, blockhash):
204204
'since': 144,
205205
'signal_activate': "30000000",
206206
'signal_abandon': "50000000",
207+
'signals': [],
207208
},
208209
'active': False
209210
},

0 commit comments

Comments
 (0)