From dd0ff04aeb9c9402ddc85ad8ff5f9679f60d0d7a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 16 May 2020 17:33:31 +0000 Subject: [PATCH] Restore warning for individual unknown version bits, as well as unknown version schemas --- src/validation.cpp | 27 ++++++++++++++++++- test/functional/feature_notifications.py | 18 +++++++++++++ .../functional/feature_versionbits_warning.py | 26 ++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/src/validation.cpp b/src/validation.cpp index cf2f9dde62311c..75b5ff7d966350 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2456,15 +2456,40 @@ void static UpdateTip(const CBlockIndex* pindexNew, const CChainParams& chainPar } } // 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 = ComputeBlockVersion(pindex->pprev, chainParams.GetConsensus()); - if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) + if (pindex->nVersion > VERSIONBITS_LAST_OLD_BLOCK_VERSION && (pindex->nVersion & ~nExpectedVersion) != 0) { ++nUpgraded; + 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 (nUpgraded > 0) AppendWarning(warning_messages, strprintf(_("%d of last 100 blocks have unexpected version"), nUpgraded)); + 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: + DoWarning(strWarning); + } } LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index dd4c318ceed47a..b387495c9cf3cd 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -139,6 +139,24 @@ def run_test(self): # 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.nodes[1].generatetoaddress(51, ADDRESS_BCRT1_UNSPENDABLE) + self.sync_all() + + # Give bitcoind 10 seconds to write the alert notification + 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.nodes[1].generatetoaddress(2, ADDRESS_BCRT1_UNSPENDABLE) + self.sync_all() + + self.log.info("-alertnotify should not continue notifying for more unknown version blocks") + assert_equal(len(os.listdir(self.alertnotify_dir)), 0) + def expect_wallet_notify(self, tx_ids): wait_until(lambda: len(os.listdir(self.walletnotify_dir)) >= len(tx_ids), timeout=10) assert_equal(sorted(notify_outputname(self.wallet, tx_id) for tx_id in tx_ids), sorted(os.listdir(self.walletnotify_dir))) diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 07139251411670..2f13d41e372eb4 100755 --- a/test/functional/feature_versionbits_warning.py +++ b/test/functional/feature_versionbits_warning.py @@ -21,7 +21,10 @@ VB_TOP_BITS = 0x20000000 VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment 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 = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT) VB_PATTERN = re.compile("Warning: unknown new rules activated.*versionbit") @@ -77,10 +80,33 @@ def run_test(self): assert not VB_PATTERN.match(node.getmininginfo()["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(node.p2p, 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 + node.generatetoaddress(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"]) + 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 self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION) node.generatetoaddress(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.") # Mine a period worth of expected blocks so the generic block-version warning # is cleared. This will move the versionbit state to ACTIVE.