mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
introduce and use the generalized node::Warnings
interface
Instead of having separate warning functions (and globals) for each different warning that can be raised, encapsulate this logic into a single class and allow to (un)set any number of warnings. Introduces behaviour change: - the `-alertnotify` command is executed for all `KernelNotifications::warningSet` calls, which now also covers the `LARGE_WORK_INVALID_CHAIN` warning. - previously, warnings were returned based on a predetermined order, e.g. with the "pre-release test build" warning always first. This is no longer the case, and Warnings::GetMessages() will return messages sorted by the id of the warning. Removes warnings.cpp from kernel.
This commit is contained in:
parent
20e616f864
commit
b071ad9770
15 changed files with 215 additions and 83 deletions
7
doc/release-notes-30058.md
Normal file
7
doc/release-notes-30058.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
- When running with -alertnotify, an alert can now be raised multiple
|
||||
times instead of just once. Previously, it was only raised when unknown
|
||||
new consensus rules were activated, whereas the scope has now been
|
||||
increased to include all kernel warnings. Specifically, alerts will now
|
||||
also be raised when an invalid chain with a large amount of work has
|
||||
been detected. Additional warnings may be added in the future.
|
||||
(#30058)
|
|
@ -196,6 +196,7 @@ BITCOIN_CORE_H = \
|
|||
kernel/messagestartchars.h \
|
||||
kernel/notifications_interface.h \
|
||||
kernel/validation_cache_sizes.h \
|
||||
kernel/warning.h \
|
||||
key.h \
|
||||
key_io.h \
|
||||
logging.h \
|
||||
|
@ -996,8 +997,7 @@ libbitcoinkernel_la_SOURCES = \
|
|||
util/tokenpipe.cpp \
|
||||
validation.cpp \
|
||||
validationinterface.cpp \
|
||||
versionbits.cpp \
|
||||
warnings.cpp
|
||||
versionbits.cpp
|
||||
|
||||
# Required for obj/build.h to be generated first.
|
||||
# More details: https://www.gnu.org/software/automake/manual/html_node/Built-Sources-Example.html
|
||||
|
|
|
@ -118,6 +118,7 @@ BITCOIN_TESTS =\
|
|||
test/net_peer_eviction_tests.cpp \
|
||||
test/net_tests.cpp \
|
||||
test/netbase_tests.cpp \
|
||||
test/node_warnings_tests.cpp \
|
||||
test/orphanage_tests.cpp \
|
||||
test/peerman_tests.cpp \
|
||||
test/pmt_tests.cpp \
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <kernel/checks.h>
|
||||
#include <kernel/context.h>
|
||||
#include <kernel/validation_cache_sizes.h>
|
||||
#include <kernel/warning.h>
|
||||
|
||||
#include <consensus/validation.h>
|
||||
#include <core_io.h>
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include <util/fs.h>
|
||||
#include <util/signalinterrupt.h>
|
||||
#include <util/task_runner.h>
|
||||
#include <util/translation.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
|
@ -86,9 +88,13 @@ int main(int argc, char* argv[])
|
|||
{
|
||||
std::cout << "Progress: " << title.original << ", " << progress_percent << ", " << resume_possible << std::endl;
|
||||
}
|
||||
void warning(const bilingual_str& warning) override
|
||||
void warningSet(kernel::Warning id, const bilingual_str& message) override
|
||||
{
|
||||
std::cout << "Warning: " << warning.original << std::endl;
|
||||
std::cout << "Warning " << static_cast<int>(id) << " set: " << message.original << std::endl;
|
||||
}
|
||||
void warningUnset(kernel::Warning id) override
|
||||
{
|
||||
std::cout << "Warning " << static_cast<int>(id) << " unset" << std::endl;
|
||||
}
|
||||
void flushError(const bilingual_str& message) override
|
||||
{
|
||||
|
|
|
@ -16,6 +16,8 @@ namespace kernel {
|
|||
|
||||
//! Result type for use with std::variant to indicate that an operation should be interrupted.
|
||||
struct Interrupted{};
|
||||
enum class Warning;
|
||||
|
||||
|
||||
//! Simple result type for functions that need to propagate an interrupt status and don't have other return values.
|
||||
using InterruptResult = std::variant<std::monostate, Interrupted>;
|
||||
|
@ -38,7 +40,8 @@ public:
|
|||
[[nodiscard]] virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) { return {}; }
|
||||
virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {}
|
||||
virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {}
|
||||
virtual void warning(const bilingual_str& warning) {}
|
||||
virtual void warningSet(Warning id, const bilingual_str& message) {}
|
||||
virtual void warningUnset(Warning id) {}
|
||||
|
||||
//! The flush error notification is sent to notify the user that an error
|
||||
//! occurred while flushing block data to disk. Kernel code may ignore flush
|
||||
|
|
14
src/kernel/warning.h
Normal file
14
src/kernel/warning.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) 2024-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_KERNEL_WARNING_H
|
||||
#define BITCOIN_KERNEL_WARNING_H
|
||||
|
||||
namespace kernel {
|
||||
enum class Warning {
|
||||
UNKNOWN_NEW_RULES_ACTIVATED,
|
||||
LARGE_WORK_INVALID_CHAIN,
|
||||
};
|
||||
} // namespace kernel
|
||||
#endif // BITCOIN_KERNEL_WARNING_H
|
|
@ -12,13 +12,12 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
namespace node {
|
||||
|
||||
void AbortNode(util::SignalInterrupt* shutdown, std::atomic<int>& exit_status, const bilingual_str& message)
|
||||
{
|
||||
SetMiscWarning(message);
|
||||
g_warnings.Set(Warning::FATAL_INTERNAL_ERROR, message);
|
||||
InitError(_("A fatal internal error occurred, see debug.log for details: ") + message);
|
||||
exit_status.store(EXIT_FAILURE);
|
||||
if (shutdown && !(*shutdown)()) {
|
||||
|
|
|
@ -93,7 +93,7 @@ public:
|
|||
explicit NodeImpl(NodeContext& context) { setContext(&context); }
|
||||
void initLogging() override { InitLogging(args()); }
|
||||
void initParameterInteraction() override { InitParameterInteraction(args()); }
|
||||
bilingual_str getWarnings() override { return Join(GetWarnings(), Untranslated("<hr />")); }
|
||||
bilingual_str getWarnings() override { return Join(node::g_warnings.GetMessages(), Untranslated("<hr />")); }
|
||||
int getExitStatus() override { return Assert(m_context)->exit_status.load(); }
|
||||
uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
|
||||
bool baseInitialize() override
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <common/args.h>
|
||||
#include <common/system.h>
|
||||
#include <kernel/context.h>
|
||||
#include <kernel/warning.h>
|
||||
#include <logging.h>
|
||||
#include <node/abort.h>
|
||||
#include <node/interface_ui.h>
|
||||
|
@ -46,16 +47,6 @@ static void AlertNotify(const std::string& strMessage)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void DoWarning(const bilingual_str& warning)
|
||||
{
|
||||
static bool fWarned = false;
|
||||
node::SetMiscWarning(warning);
|
||||
if (!fWarned) {
|
||||
AlertNotify(warning.original);
|
||||
fWarned = true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace node {
|
||||
|
||||
kernel::InterruptResult KernelNotifications::blockTip(SynchronizationState state, CBlockIndex& index)
|
||||
|
@ -80,9 +71,16 @@ void KernelNotifications::progress(const bilingual_str& title, int progress_perc
|
|||
uiInterface.ShowProgress(title.translated, progress_percent, resume_possible);
|
||||
}
|
||||
|
||||
void KernelNotifications::warning(const bilingual_str& warning)
|
||||
void KernelNotifications::warningSet(kernel::Warning id, const bilingual_str& message)
|
||||
{
|
||||
DoWarning(warning);
|
||||
if (node::g_warnings.Set(id, message)) {
|
||||
AlertNotify(message.original);
|
||||
}
|
||||
}
|
||||
|
||||
void KernelNotifications::warningUnset(kernel::Warning id)
|
||||
{
|
||||
g_warnings.Unset(id);
|
||||
}
|
||||
|
||||
void KernelNotifications::flushError(const bilingual_str& message)
|
||||
|
|
|
@ -15,6 +15,10 @@ class CBlockIndex;
|
|||
enum class SynchronizationState;
|
||||
struct bilingual_str;
|
||||
|
||||
namespace kernel {
|
||||
enum class Warning;
|
||||
} // namespace kernel
|
||||
|
||||
namespace util {
|
||||
class SignalInterrupt;
|
||||
} // namespace util
|
||||
|
@ -34,7 +38,9 @@ public:
|
|||
|
||||
void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override;
|
||||
|
||||
void warning(const bilingual_str& warning) override;
|
||||
void warningSet(kernel::Warning id, const bilingual_str& message) override;
|
||||
|
||||
void warningUnset(kernel::Warning id) override;
|
||||
|
||||
void flushError(const bilingual_str& message) override;
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ bool TimeOffsets::WarnIfOutOfSync() const
|
|||
// when median == std::numeric_limits<int64_t>::min(), calling std::chrono::abs is UB
|
||||
auto median{std::max(Median(), std::chrono::seconds(std::numeric_limits<int64_t>::min() + 1))};
|
||||
if (std::chrono::abs(median) <= WARN_THRESHOLD) {
|
||||
node::SetMedianTimeOffsetWarning(std::nullopt);
|
||||
node::g_warnings.Unset(node::Warning::CLOCK_OUT_OF_SYNC);
|
||||
uiInterface.NotifyAlertChanged();
|
||||
return false;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ bool TimeOffsets::WarnIfOutOfSync() const
|
|||
"RPC methods to get more info."
|
||||
), Ticks<std::chrono::minutes>(WARN_THRESHOLD))};
|
||||
LogWarning("%s\n", msg.original);
|
||||
node::SetMedianTimeOffsetWarning(msg);
|
||||
node::g_warnings.Set(node::Warning::CLOCK_OUT_OF_SYNC, msg);
|
||||
uiInterface.NotifyAlertChanged();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -12,69 +12,53 @@
|
|||
#include <univalue.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
static GlobalMutex g_warnings_mutex;
|
||||
static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex);
|
||||
static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
|
||||
static std::optional<bilingual_str> g_timeoffset_warning GUARDED_BY(g_warnings_mutex){};
|
||||
|
||||
namespace node {
|
||||
void SetMiscWarning(const bilingual_str& warning)
|
||||
Warnings g_warnings;
|
||||
|
||||
Warnings::Warnings()
|
||||
{
|
||||
LOCK(g_warnings_mutex);
|
||||
g_misc_warnings = warning;
|
||||
}
|
||||
|
||||
void SetfLargeWorkInvalidChainFound(bool flag)
|
||||
{
|
||||
LOCK(g_warnings_mutex);
|
||||
fLargeWorkInvalidChainFound = flag;
|
||||
}
|
||||
|
||||
void SetMedianTimeOffsetWarning(std::optional<bilingual_str> warning)
|
||||
{
|
||||
LOCK(g_warnings_mutex);
|
||||
g_timeoffset_warning = warning;
|
||||
}
|
||||
|
||||
std::vector<bilingual_str> GetWarnings()
|
||||
{
|
||||
std::vector<bilingual_str> warnings;
|
||||
|
||||
LOCK(g_warnings_mutex);
|
||||
|
||||
// Pre-release build warning
|
||||
if (!CLIENT_VERSION_IS_RELEASE) {
|
||||
warnings.emplace_back(_("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"));
|
||||
m_warnings.insert(
|
||||
{Warning::PRE_RELEASE_TEST_BUILD,
|
||||
_("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications")});
|
||||
}
|
||||
}
|
||||
bool Warnings::Set(warning_type id, bilingual_str message)
|
||||
{
|
||||
LOCK(m_mutex);
|
||||
const auto& [_, inserted]{m_warnings.insert({id, std::move(message)})};
|
||||
return inserted;
|
||||
}
|
||||
|
||||
// Misc warnings like out of disk space and clock is wrong
|
||||
if (!g_misc_warnings.empty()) {
|
||||
warnings.emplace_back(g_misc_warnings);
|
||||
bool Warnings::Unset(warning_type id)
|
||||
{
|
||||
return WITH_LOCK(m_mutex, return m_warnings.erase(id));
|
||||
}
|
||||
|
||||
std::vector<bilingual_str> Warnings::GetMessages() const
|
||||
{
|
||||
LOCK(m_mutex);
|
||||
std::vector<bilingual_str> messages;
|
||||
messages.reserve(m_warnings.size());
|
||||
for (const auto& [id, msg] : m_warnings) {
|
||||
messages.push_back(msg);
|
||||
}
|
||||
|
||||
if (fLargeWorkInvalidChainFound) {
|
||||
warnings.emplace_back(_("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."));
|
||||
}
|
||||
|
||||
if (g_timeoffset_warning) {
|
||||
warnings.emplace_back(g_timeoffset_warning.value());
|
||||
}
|
||||
|
||||
return warnings;
|
||||
return messages;
|
||||
}
|
||||
|
||||
UniValue GetWarningsForRpc(bool use_deprecated)
|
||||
{
|
||||
if (use_deprecated) {
|
||||
const auto all_warnings{GetWarnings()};
|
||||
const auto all_warnings{g_warnings.GetMessages()};
|
||||
return all_warnings.empty() ? "" : all_warnings.back().original;
|
||||
}
|
||||
|
||||
UniValue warnings{UniValue::VARR};
|
||||
for (auto&& warning : GetWarnings()) {
|
||||
for (auto&& warning : g_warnings.GetMessages()) {
|
||||
warnings.push_back(std::move(warning.original));
|
||||
}
|
||||
return warnings;
|
||||
|
|
|
@ -6,26 +6,86 @@
|
|||
#ifndef BITCOIN_NODE_WARNINGS_H
|
||||
#define BITCOIN_NODE_WARNINGS_H
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <sync.h>
|
||||
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
class UniValue;
|
||||
struct bilingual_str;
|
||||
|
||||
namespace kernel {
|
||||
enum class Warning;
|
||||
} // namespace kernel
|
||||
|
||||
namespace node {
|
||||
void SetMiscWarning(const bilingual_str& warning);
|
||||
void SetfLargeWorkInvalidChainFound(bool flag);
|
||||
/** Pass std::nullopt to disable the warning */
|
||||
void SetMedianTimeOffsetWarning(std::optional<bilingual_str> warning);
|
||||
/** Return potential problems detected by the node. */
|
||||
std::vector<bilingual_str> GetWarnings();
|
||||
enum class Warning {
|
||||
CLOCK_OUT_OF_SYNC,
|
||||
PRE_RELEASE_TEST_BUILD,
|
||||
FATAL_INTERNAL_ERROR,
|
||||
};
|
||||
|
||||
/**
|
||||
* RPC helper function that wraps GetWarnings. Returns a UniValue::VSTR
|
||||
* with the latest warning if use_deprecated is set to true, or a
|
||||
* UniValue::VARR with all warnings otherwise.
|
||||
* @class Warnings
|
||||
* @brief Manages warning messages within a node.
|
||||
*
|
||||
* The Warnings class provides mechanisms to set, unset, and retrieve
|
||||
* warning messages.
|
||||
*
|
||||
* This class is designed to be non-copyable to ensure warnings
|
||||
* are managed centrally.
|
||||
*/
|
||||
class Warnings
|
||||
{
|
||||
typedef std::variant<kernel::Warning, node::Warning> warning_type;
|
||||
|
||||
mutable Mutex m_mutex;
|
||||
std::map<warning_type, bilingual_str> m_warnings GUARDED_BY(m_mutex);
|
||||
|
||||
public:
|
||||
Warnings();
|
||||
//! A warnings instance should always be passed by reference, never copied.
|
||||
Warnings(const Warnings&) = delete;
|
||||
Warnings& operator=(const Warnings&) = delete;
|
||||
/**
|
||||
* @brief Set a warning message. If a warning with the specified
|
||||
* `id` is already active, false is returned and the new
|
||||
* warning is ignored. If `id` does not yet exist, the
|
||||
* warning is set, and true is returned.
|
||||
*
|
||||
* @param[in] id Unique identifier of the warning.
|
||||
* @param[in] message Warning message to be shown.
|
||||
*
|
||||
* @returns true if the warning was indeed set (i.e. there is no
|
||||
* active warning with this `id`), otherwise false.
|
||||
*/
|
||||
bool Set(warning_type id, bilingual_str message) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
/**
|
||||
* @brief Unset a warning message. If a warning with the specified
|
||||
* `id` is active, it is unset, and true is returned.
|
||||
* Otherwise, no warning is unset and false is returned.
|
||||
*
|
||||
* @param[in] id Unique identifier of the warning.
|
||||
*
|
||||
* @returns true if the warning was indeed unset (i.e. there is an
|
||||
* active warning with this `id`), otherwise false.
|
||||
*/
|
||||
bool Unset(warning_type id) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
/** Return potential problems detected by the node, sorted by the
|
||||
* warning_type id */
|
||||
std::vector<bilingual_str> GetMessages() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
};
|
||||
|
||||
/**
|
||||
* RPC helper function that wraps g_warnings.GetMessages().
|
||||
*
|
||||
* Returns a UniValue::VSTR with the latest warning if use_deprecated is
|
||||
* set to true, or a UniValue::VARR with all warnings otherwise.
|
||||
*/
|
||||
UniValue GetWarningsForRpc(bool use_deprecated);
|
||||
|
||||
extern Warnings g_warnings;
|
||||
} // namespace node
|
||||
|
||||
#endif // BITCOIN_NODE_WARNINGS_H
|
||||
|
|
52
src/test/node_warnings_tests.cpp
Normal file
52
src/test/node_warnings_tests.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2024-present The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
//
|
||||
|
||||
#include <node/warnings.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(node_warnings_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(warnings)
|
||||
{
|
||||
node::Warnings warnings;
|
||||
// On pre-release builds, a warning is generated automatically
|
||||
warnings.Unset(node::Warning::PRE_RELEASE_TEST_BUILD);
|
||||
|
||||
// For these tests, we don't care what the exact warnings are, so
|
||||
// just refer to them as warning_1 and warning_2
|
||||
const auto warning_1{node::Warning::CLOCK_OUT_OF_SYNC};
|
||||
const auto warning_2{node::Warning::FATAL_INTERNAL_ERROR};
|
||||
|
||||
// Ensure we start without any warnings
|
||||
BOOST_CHECK(warnings.GetMessages().size() == 0);
|
||||
// Add two warnings
|
||||
BOOST_CHECK(warnings.Set(warning_1, _("warning 1")));
|
||||
BOOST_CHECK(warnings.Set(warning_2, _("warning 2")));
|
||||
// Unset the second one
|
||||
BOOST_CHECK(warnings.Unset(warning_2));
|
||||
// Since it's already been unset, this should return false
|
||||
BOOST_CHECK(!warnings.Unset(warning_2));
|
||||
// We should now be able to set w2 again
|
||||
BOOST_CHECK(warnings.Set(warning_2, _("warning 2 - revision 1")));
|
||||
// Setting w2 again should return false since it's already set
|
||||
BOOST_CHECK(!warnings.Set(warning_2, _("warning 2 - revision 2")));
|
||||
|
||||
// Verify messages are correct
|
||||
const auto messages{warnings.GetMessages()};
|
||||
BOOST_CHECK(messages.size() == 2);
|
||||
BOOST_CHECK(messages[0].original == "warning 1");
|
||||
BOOST_CHECK(messages[1].original == "warning 2 - revision 1");
|
||||
|
||||
// Clearing all warnings should also clear all messages
|
||||
BOOST_CHECK(warnings.Unset(warning_1));
|
||||
BOOST_CHECK(warnings.Unset(warning_2));
|
||||
BOOST_CHECK(warnings.GetMessages().size() == 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -27,11 +27,11 @@
|
|||
#include <kernel/mempool_entry.h>
|
||||
#include <kernel/messagestartchars.h>
|
||||
#include <kernel/notifications_interface.h>
|
||||
#include <kernel/warning.h>
|
||||
#include <logging.h>
|
||||
#include <logging/timer.h>
|
||||
#include <node/blockstorage.h>
|
||||
#include <node/utxo_snapshot.h>
|
||||
#include <node/warnings.h>
|
||||
#include <policy/policy.h>
|
||||
#include <policy/rbf.h>
|
||||
#include <policy/settings.h>
|
||||
|
@ -1922,9 +1922,11 @@ void Chainstate::CheckForkWarningConditions()
|
|||
|
||||
if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) {
|
||||
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
|
||||
node::SetfLargeWorkInvalidChainFound(true);
|
||||
m_chainman.GetNotifications().warningSet(
|
||||
kernel::Warning::LARGE_WORK_INVALID_CHAIN,
|
||||
_("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."));
|
||||
} else {
|
||||
node::SetfLargeWorkInvalidChainFound(false);
|
||||
m_chainman.GetNotifications().warningUnset(kernel::Warning::LARGE_WORK_INVALID_CHAIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2907,7 +2909,7 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew)
|
|||
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
|
||||
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
|
||||
if (state == ThresholdState::ACTIVE) {
|
||||
m_chainman.GetNotifications().warning(warning);
|
||||
m_chainman.GetNotifications().warningSet(kernel::Warning::UNKNOWN_NEW_RULES_ACTIVATED, warning);
|
||||
} else {
|
||||
warning_messages.push_back(warning);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue