Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/bitcoind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,6 @@ static bool ParseArgs(ArgsManager& args, int argc, char* argv[])
}
}

g_software_expiry = args.GetIntArg("-softwareexpiry", DEFAULT_SOFTWARE_EXPIRY);
if (IsThisSoftwareExpired(GetTime())) {
tfm::format(std::cerr, "This software is expired, and may be out of consensus. You must choose to upgrade or override this expiration.\n");
exit(EXIT_FAILURE);
}

return true;
}

Expand Down
9 changes: 9 additions & 0 deletions src/clientversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,12 @@ bool IsThisSoftwareExpired(int64_t nTime)
}
return (nTime > g_software_expiry);
}

bool IsThisSoftwareExpiringSoon(int64_t ntime)
{
if (g_software_expiry <= 0) {
return false;
}

return (ntime <= g_software_expiry && ntime > g_software_expiry - SOFTWARE_EXPIRY_WARN_PERIOD);
}
5 changes: 5 additions & 0 deletions src/clientversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,14 @@ static constexpr int64_t SECONDS_PER_YEAR = 31558060;
static constexpr int POSIX_EPOCH_YEAR = 1970;
static constexpr int64_t DEFAULT_SOFTWARE_EXPIRY_OFFSET = 26784000; // Around Nov 7
static constexpr int64_t DEFAULT_SOFTWARE_EXPIRY = ((COPYRIGHT_YEAR - POSIX_EPOCH_YEAR) * SECONDS_PER_YEAR) + (SECONDS_PER_YEAR * 2) + DEFAULT_SOFTWARE_EXPIRY_OFFSET;

static constexpr int64_t SECONDS_PER_WEEK = 604800;
static constexpr int64_t SOFTWARE_EXPIRY_WARN_PERIOD = SECONDS_PER_WEEK * 4;

extern int64_t g_software_expiry;

bool IsThisSoftwareExpired(int64_t nTime);
bool IsThisSoftwareExpiringSoon(int64_t nTime);

#endif // WINDRES_PREPROC

Expand Down
23 changes: 23 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,29 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}

// ********************************************************* Step 4a: application initialization

// Test for software expiry, prompt for continue confirmation if necessary.
g_software_expiry = args.GetIntArg("-softwareexpiry", DEFAULT_SOFTWARE_EXPIRY);
uint64_t ntime = GetTime();
if (IsThisSoftwareExpiringSoon(ntime)) {
uiInterface.ThreadSafeMessageBox(
_("This software expires soon, and may fall out of consensus. You must choose to upgrade, or override this expiration with the 'softwareexpiry' option (Unix datetime, or 0 for no expiry)."),
"Software expires soon",
CClientUIInterface::MSG_WARNING);
} else if (IsThisSoftwareExpired(ntime)) {
bool override_expired = uiInterface.ThreadSafeConfirmation(
_("This software is expired, and may be out of consensus.\nYou must choose to upgrade, or override this expiration by typing:"),
_("I accept this software may be unsafe."),
"This software is expired, and may be out of consensus. You must choose to upgrade, or override this expiration with the 'softwareexpiry' option (Unix datetime, or 0 for no expiry).",
"Software expired");
if (!override_expired) {
return false;
}

node.args->ModifyRWConfigFile("softwareexpiry", "0");
g_software_expiry = 0;
}

if (!CreatePidFile(args)) {
// Detailed error printed inside CreatePidFile().
return false;
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ class Node
unsigned int style)>;
virtual std::unique_ptr<Handler> handleQuestion(QuestionFn fn) = 0;

//! Register handler for confirmation messages.
using ConfirmationFn = std::function<bool(const bilingual_str& message,
const bilingual_str& confirmation_string,
const std::string& non_interactive_message,
const std::string& caption)>;
virtual std::unique_ptr<Handler> handleConfirmation(ConfirmationFn fn) = 0;

//! Register handler for progress messages.
using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>;
virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0;
Expand Down
2 changes: 2 additions & 0 deletions src/kernel/notifications_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class Notifications
//! the user, or triggering an early shutdown as a precaution against
//! causing more errors.
virtual void fatalError(const bilingual_str& message) {}

virtual void softwareExpired() {}
};
} // namespace kernel

Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ CClientUIInterface uiInterface;
struct UISignals {
boost::signals2::signal<CClientUIInterface::ThreadSafeMessageBoxSig, boost::signals2::optional_last_value<bool>> ThreadSafeMessageBox;
boost::signals2::signal<CClientUIInterface::ThreadSafeQuestionSig, boost::signals2::optional_last_value<bool>> ThreadSafeQuestion;
boost::signals2::signal<CClientUIInterface::ThreadSafeConfirmationSig, boost::signals2::optional_last_value<bool>> ThreadSafeConfirmation;
boost::signals2::signal<CClientUIInterface::InitMessageSig> InitMessage;
boost::signals2::signal<CClientUIInterface::InitWalletSig> InitWallet;
boost::signals2::signal<CClientUIInterface::NotifyNumConnectionsChangedSig> NotifyNumConnectionsChanged;
Expand All @@ -38,6 +39,7 @@ static UISignals g_ui_signals;

ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeMessageBox);
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeQuestion);
ADD_SIGNALS_IMPL_WRAPPER(ThreadSafeConfirmation);
ADD_SIGNALS_IMPL_WRAPPER(InitMessage);
ADD_SIGNALS_IMPL_WRAPPER(InitWallet);
ADD_SIGNALS_IMPL_WRAPPER(NotifyNumConnectionsChanged);
Expand All @@ -51,6 +53,7 @@ ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged);

bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style).value_or(false);}
bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style).value_or(false);}
bool CClientUIInterface::ThreadSafeConfirmation(const bilingual_str& message, const bilingual_str& confirmation_string, const std::string& non_interactive_message, const std::string& caption) { return g_ui_signals.ThreadSafeConfirmation(message, confirmation_string, non_interactive_message, caption).value_or(false);}
void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_signals.InitMessage(message); }
void CClientUIInterface::InitWallet() { return g_ui_signals.InitWallet(); }
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
Expand Down
3 changes: 3 additions & 0 deletions src/node/interface_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class CClientUIInterface
/** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
ADD_SIGNALS_DECL_WRAPPER(ThreadSafeQuestion, bool, const bilingual_str& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style);

/** If possible, ask the user to confirm by typing a string. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */
ADD_SIGNALS_DECL_WRAPPER(ThreadSafeConfirmation, bool, const bilingual_str& message, const bilingual_str& confirmation_string, const std::string& noninteractive_message, const std::string& caption);

/** Progress message during initialization. */
ADD_SIGNALS_DECL_WRAPPER(InitMessage, void, const std::string& message);

Expand Down
4 changes: 4 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ class NodeImpl : public Node
{
return MakeSignalHandler(::uiInterface.ThreadSafeQuestion_connect(fn));
}
std::unique_ptr<Handler> handleConfirmation(ConfirmationFn fn) override
{
return MakeSignalHandler(::uiInterface.ThreadSafeConfirmation_connect(fn));
}
std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) override
{
return MakeSignalHandler(::uiInterface.ShowProgress_connect(fn));
Expand Down
16 changes: 16 additions & 0 deletions src/node/kernel_notifications.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "kernel_notifications.h"
#include <node/kernel_notifications.h>

#include <config/bitcoin-config.h> // IWYU pragma: keep

#include <chain.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/system.h>
#include <kernel/context.h>
Expand Down Expand Up @@ -95,6 +97,20 @@ void KernelNotifications::fatalError(const bilingual_str& message)
m_exit_status, message, &m_warnings);
}

void KernelNotifications::softwareExpired()
{
if (uiInterface.ThreadSafeConfirmation(
_("This software is expired, and may be out of consensus.\nYou must choose to upgrade, or override this expiration by typing:"),
_("I accept this software may be unsafe."),
"This software is expired, and may be out of consensus. You must choose to upgrade, or override this expiration with the 'softwareexpiry' option (Unix datetime, or 0 for no expiry).",
"Software expired")) {
gArgs.ModifyRWConfigFile("softwareexpiry", "0");
g_software_expiry = 0;
} else {
AbortNode(&m_shutdown, m_exit_status, _("Shut down due to software expiry."), &m_warnings);
}
}

void ReadNotificationArgs(const ArgsManager& args, KernelNotifications& notifications)
{
if (auto value{args.GetIntArg("-stopatheight")}) notifications.m_stop_at_height = *value;
Expand Down
2 changes: 2 additions & 0 deletions src/node/kernel_notifications.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class KernelNotifications : public kernel::Notifications

void fatalError(const bilingual_str& message) override;

void softwareExpired() override;

//! Block height after which blockTip notification will return Interrupted{}, if >0.
int m_stop_at_height{DEFAULT_STOPATHEIGHT};
//! Useful for tests, can be set to false to avoid shutdown on fatal error.
Expand Down
19 changes: 19 additions & 0 deletions src/noui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/** Store connections so we can disconnect them when suppressing output */
boost::signals2::connection noui_ThreadSafeMessageBoxConn;
boost::signals2::connection noui_ThreadSafeQuestionConn;
boost::signals2::connection noui_ThreadSafeConfirmationConn;
boost::signals2::connection noui_InitMessageConn;

bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style)
Expand Down Expand Up @@ -52,6 +53,11 @@ bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message
return noui_ThreadSafeMessageBox(Untranslated(message), caption, style);
}

bool noui_ThreadSafeConfirmation(/* ignored interactive */const bilingual_str&, const bilingual_str&, const std::string& message, const std::string& caption)
{
return noui_ThreadSafeMessageBox(Untranslated(message), caption, CClientUIInterface::MSG_ERROR);
}

void noui_InitMessage(const std::string& message)
{
LogPrintf("init message: %s\n", message);
Expand All @@ -61,6 +67,7 @@ void noui_connect()
{
noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
noui_ThreadSafeConfirmationConn = uiInterface.ThreadSafeConfirmation_connect(noui_ThreadSafeConfirmation);
noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage);
}

Expand All @@ -76,6 +83,15 @@ bool noui_ThreadSafeQuestionRedirect(const bilingual_str& /* ignored interactive
return false;
}

bool noui_ThreadSafeConfirmationRedirect(
const bilingual_str& /* ignored interactive message */,
const bilingual_str& /* ignored interactive confirmation string */,
const std::string& message, const std::string& caption)
{
LogPrintf("%s: %s\n", caption, message);
return false;
}

void noui_InitMessageRedirect(const std::string& message)
{
LogPrintf("init message: %s\n", message);
Expand All @@ -85,16 +101,19 @@ void noui_test_redirect()
{
noui_ThreadSafeMessageBoxConn.disconnect();
noui_ThreadSafeQuestionConn.disconnect();
noui_ThreadSafeConfirmationConn.disconnect();
noui_InitMessageConn.disconnect();
noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBoxRedirect);
noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionRedirect);
noui_ThreadSafeConfirmationConn = uiInterface.ThreadSafeConfirmation_connect(noui_ThreadSafeConfirmationRedirect);
noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessageRedirect);
}

void noui_reconnect()
{
noui_ThreadSafeMessageBoxConn.disconnect();
noui_ThreadSafeQuestionConn.disconnect();
noui_ThreadSafeConfirmationConn.disconnect();
noui_InitMessageConn.disconnect();
noui_connect();
}
5 changes: 5 additions & 0 deletions src/noui.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ struct bilingual_str;
bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style);
/** Non-GUI handler, which logs and prints questions. */
bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style);
/** Non-GUI handler, which logs and prints confirmations. */
bool noui_ThreadSafeConfirmation(
const bilingual_str& /* ignored interactive message */,
const bilingual_str& confirmationString /* ignored interactive confirmation string */,
const std::string& message, const std::string& caption);
/** Non-GUI handler, which only logs a message. */
void noui_InitMessage(const std::string& message);

Expand Down
8 changes: 1 addition & 7 deletions src/qt/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <qt/bitcoin.h>

#include <chainparams.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/init.h>
#include <common/system.h>
Expand Down Expand Up @@ -520,6 +519,7 @@ int GuiMain(int argc, char* argv[])
// Subscribe to global signals from core
boost::signals2::scoped_connection handler_message_box = ::uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox);
boost::signals2::scoped_connection handler_question = ::uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion);
boost::signals2::scoped_connection handler_confirmation = ::uiInterface.ThreadSafeConfirmation_connect(noui_ThreadSafeConfirmation);
boost::signals2::scoped_connection handler_init_message = ::uiInterface.InitMessage_connect(noui_InitMessage);

// Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory
Expand Down Expand Up @@ -653,12 +653,6 @@ int GuiMain(int argc, char* argv[])
// Re-initialize translations after changing application name (language in network-specific settings can be different)
initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);

g_software_expiry = gArgs.GetIntArg("-softwareexpiry", DEFAULT_SOFTWARE_EXPIRY);
if (IsThisSoftwareExpired(GetTime())) {
QMessageBox::critical(nullptr, QObject::tr("Software expired"), QObject::tr("This software is expired, and may be out of consensus. You must choose to upgrade or override this expiration."));
return EXIT_FAILURE;
}

#ifdef ENABLE_WALLET
/// 8. URI IPC sending
// - Do this early as we don't want to bother initializing if we are just calling IPC
Expand Down
28 changes: 28 additions & 0 deletions src/qt/bitcoingui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,17 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
}
}

void BitcoinGUI::input_text(const QString& title, QString message, bool* ret, QString* input) {
// Default title. On macOS, the window title is ignored (as required by the macOS Guidelines).
QString strTitle{PACKAGE_NAME};
showNormalIfMinimized();

bool ok;
QString text = QInputDialog::getText(this, strTitle, message, QLineEdit::Normal, "", &ok);
if (input != nullptr) *input = text;
if (ret != nullptr) *ret = ok;
}

void BitcoinGUI::changeEvent(QEvent *e)
{
if (e->type() == QEvent::PaletteChange) {
Expand Down Expand Up @@ -1676,18 +1687,35 @@ static bool ThreadSafeMessageBox(BitcoinGUI* gui, const bilingual_str& message,
return ret;
}

static bool ThreadSafeConfirmation(BitcoinGUI* gui, const bilingual_str& message, const bilingual_str& confirmation_string, const std::string& caption)
{
bool ok = false;
QString confirm_input = QString::fromStdString("");
// Use blocking connection to wait for user input.
bool invoked = QMetaObject::invokeMethod(gui, "input_text",
GUIUtil::blockingGUIThreadConnection(),
Q_ARG(QString, QString::fromStdString(caption)),
Q_ARG(QString, QString::fromStdString(message.translated + "\n" + confirmation_string.translated)),
Q_ARG(bool*, &ok),
Q_ARG(QString*, &confirm_input));
assert(invoked);
return ok && confirm_input == QString::fromStdString(confirmation_string.translated);
}

void BitcoinGUI::subscribeToCoreSignals()
{
// Connect signals to client
m_handler_message_box = m_node.handleMessageBox(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_handler_question = m_node.handleQuestion(std::bind(ThreadSafeMessageBox, this, std::placeholders::_1, std::placeholders::_3, std::placeholders::_4));
m_handler_confirmation = m_node.handleConfirmation(std::bind(ThreadSafeConfirmation, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_4));
}

void BitcoinGUI::unsubscribeFromCoreSignals()
{
// Disconnect signals from client
m_handler_message_box->disconnect();
m_handler_question->disconnect();
m_handler_confirmation->disconnect();
}

bool BitcoinGUI::isPrivacyModeActivated() const
Expand Down
2 changes: 2 additions & 0 deletions src/qt/bitcoingui.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class BitcoinGUI : public QMainWindow
WalletController* m_wallet_controller{nullptr};
std::unique_ptr<interfaces::Handler> m_handler_message_box;
std::unique_ptr<interfaces::Handler> m_handler_question;
std::unique_ptr<interfaces::Handler> m_handler_confirmation;
ClientModel* clientModel = nullptr;
WalletFrame* walletFrame = nullptr;

Expand Down Expand Up @@ -251,6 +252,7 @@ public Q_SLOTS:
@param[in] detailed_message the text to be displayed in the details area
*/
void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr, const QString& detailed_message = QString());
void input_text(const QString& title, QString message, bool* ret = nullptr, QString* input = nullptr);

#ifdef ENABLE_WALLET
void setCurrentWallet(WalletModel* wallet_model);
Expand Down
3 changes: 2 additions & 1 deletion src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4458,12 +4458,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
if (IsThisSoftwareExpired(block.nTime)) {
// Wait an extra day before we start rejecting blocks
CBlockIndex const *blockindex_old = pindexPrev;
for (int i = 0; i < 144; ++i) {
for (int i = 0; i < std::min(144, pindexPrev->nHeight); ++i) {
assert(blockindex_old);
blockindex_old = blockindex_old->pprev;
}
assert(blockindex_old);
if (IsThisSoftwareExpired(blockindex_old->GetMedianTimePast())) {
chainman.GetNotifications().softwareExpired();
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "node-expired", "node software has expired");
}
}
Expand Down