Restore warning for individual unknown version bits, as well as unknown version schemas

This commit is contained in:
Luke Dashjr 2023-07-29 23:40:09 +00:00
parent ee43549507
commit 9b32a6f5d5
3 changed files with 77 additions and 0 deletions

View File

@ -2753,6 +2753,40 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
} }
} }
} }
// Check the version of the last 100 blocks to see if we need to upgrade:
int unexpected_bit_count[VERSIONBITS_NUM_BITS], nonversionbit_count = 0;
for (size_t i = 0; i < VERSIONBITS_NUM_BITS; ++i) unexpected_bit_count[i] = 0;
static constexpr int WARNING_THRESHOLD = 100/2;
bool warning_threshold_hit = false;
for (int i = 0; i < 100 && pindex != nullptr; i++)
{
int32_t nExpectedVersion = m_chainman.m_versionbitscache.ComputeBlockVersion(pindex->pprev, params.GetConsensus());
if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0)
{
if ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) {
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; ++bit) {
const int32_t mask = 1 << bit;
if ((nExpectedVersion & mask) != (pindex->nVersion & mask)) {
if (++unexpected_bit_count[bit] > WARNING_THRESHOLD) {
warning_threshold_hit = true;
}
}
}
} else {
// Non-versionbits upgrade
if (++nonversionbit_count > WARNING_THRESHOLD) {
warning_threshold_hit = true;
}
}
}
pindex = pindex->pprev;
}
if (warning_threshold_hit) {
auto strWarning = _("Warning: Unrecognised block version being mined! Unknown rules may or may not be in effect");
// notify GetWarnings(), called by Qt and the JSON-RPC code to warn the user:
m_chainman.GetNotifications().warning(strWarning);
}
} }
UpdateTipLog(coins_tip, pindexNew, params, __func__, "", warning_messages.original); UpdateTipLog(coins_tip, pindexNew, params, __func__, "", warning_messages.original);
} }

View File

@ -49,6 +49,7 @@ class NotificationsTest(BitcoinTestFramework):
f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}", f"-blocknotify=echo > {os.path.join(self.blocknotify_dir, '%s')}",
f"-shutdownnotify=echo > {self.shutdownnotify_file}", f"-shutdownnotify=echo > {self.shutdownnotify_file}",
], [ ], [
"-blockversion=211",
f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}", f"-walletnotify=echo %h_%b > {os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))}",
]] ]]
self.wallet_names = [self.default_wallet_name, self.wallet] self.wallet_names = [self.default_wallet_name, self.wallet]
@ -163,6 +164,22 @@ class NotificationsTest(BitcoinTestFramework):
# TODO: add test for `-alertnotify` large fork notifications # TODO: add test for `-alertnotify` large fork notifications
# Mine 51 unknown-version blocks. -alertnotify should trigger on the 51st.
self.log.info("test -alertnotify")
self.generatetoaddress(self.nodes[1], 51, ADDRESS_BCRT1_UNSPENDABLE)
# Give bitcoind 10 seconds to write the alert notification
self.wait_until(lambda: len(os.listdir(self.alertnotify_dir)), timeout=10)
for notify_file in os.listdir(self.alertnotify_dir):
os.remove(os.path.join(self.alertnotify_dir, notify_file))
# Mine more up-version blocks, should not get more alerts:
self.generatetoaddress(self.nodes[1], 2, ADDRESS_BCRT1_UNSPENDABLE)
self.log.info("-alertnotify should not continue notifying for more unknown version blocks")
assert_equal(len(os.listdir(self.alertnotify_dir)), 0)
self.log.info("test -shutdownnotify") self.log.info("test -shutdownnotify")
self.stop_nodes() self.stop_nodes()
self.wait_until(lambda: os.path.isfile(self.shutdownnotify_file), timeout=10) self.wait_until(lambda: os.path.isfile(self.shutdownnotify_file), timeout=10)

View File

@ -20,7 +20,10 @@ VB_THRESHOLD = 108 # versionbits activation threshold for regtest
VB_TOP_BITS = 0x20000000 VB_TOP_BITS = 0x20000000
VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT) VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT)
UNKNOWN_VERSION_SCHEMA = 0x60000000
UNKNOWN_VERSION_SCHEMA_THRESHOLD = 51
WARN_UNKNOWN_RULES_MINED = "Warning: Unrecognised block version being mined! Unknown rules may or may not be in effect"
WARN_UNKNOWN_RULES_ACTIVE = f"Unknown new rules activated (versionbit {VB_UNKNOWN_BIT})" WARN_UNKNOWN_RULES_ACTIVE = f"Unknown new rules activated (versionbit {VB_UNKNOWN_BIT})"
VB_PATTERN = re.compile("Unknown new rules activated.*versionbit") VB_PATTERN = re.compile("Unknown new rules activated.*versionbit")
@ -76,10 +79,33 @@ class VersionBitsWarningTest(BitcoinTestFramework):
assert not VB_PATTERN.match(node.getmininginfo()["warnings"]) assert not VB_PATTERN.match(node.getmininginfo()["warnings"])
assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"]) assert not VB_PATTERN.match(node.getnetworkinfo()["warnings"])
self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version schema")
# Build UNKNOWN_VERSION_SCHEMA_THRESHOLD blocks signaling some unknown schema
self.send_blocks_with_version(peer, UNKNOWN_VERSION_SCHEMA_THRESHOLD, UNKNOWN_VERSION_SCHEMA)
# Check that get*info() shows the 51/100 unknown block version error.
assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"])
# Close the period normally
self.generatetoaddress(node, VB_PERIOD - UNKNOWN_VERSION_SCHEMA_THRESHOLD, node_deterministic_address)
# Make sure the warning remains
assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"])
# Stop-start the node, and make sure the warning is gone
self.restart_node(0)
assert(WARN_UNKNOWN_RULES_MINED not in node.getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED not in node.getnetworkinfo()["warnings"])
peer = node.add_p2p_connection(P2PInterface())
self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version")
# Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit
self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION) self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION)
self.generatetoaddress(node, VB_PERIOD - VB_THRESHOLD, node_deterministic_address) self.generatetoaddress(node, VB_PERIOD - VB_THRESHOLD, node_deterministic_address)
# Check that get*info() shows the 51/100 unknown block version error.
assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"])
assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"])
self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.") self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.")
# Mine a period worth of expected blocks so the generic block-version warning # Mine a period worth of expected blocks so the generic block-version warning
# is cleared. This will move the versionbit state to ACTIVE. # is cleared. This will move the versionbit state to ACTIVE.