Skip to content

Conversation

pithosian
Copy link

@pithosian pithosian commented May 24, 2025

As an alternative to #124.

  • Add a warning on startup within 4 weeks of expiry to bitcoind and bitcoin-qt.
  • Add a note on how to override in the existing bitcoind startup failure message for expired software.
  • Replace the existing bitcoin-qt startup error message for expired software with an input box requiring the user to type I accept this software may be unsafe. to continue (overriding expiry this way updates configuration).
  • Shut down when bitcoind or bitcoin-qt expire at runtime, rather than silently rejecting blocks.

The rationale for these changes is largely explained by my comments on the above-linked PR. I considered using the same prompt at runtime as at startup for bitcoin-qt, but the churn required was much higher with minimal benefit (as it stands, the software pops up a dialog telling the user the software is expired, on closing, bitcoin-qt exits, and if the user tries to start it again, they get the override prompt).

As a brief summary:

  • It's good to make a user who hasn't explicitly indicated that they want to run EOLed software (see Core's end of life policy and disclosure policy) think about what they're running.
  • In bitcoind, shutting down with an error message telling the user how to override is effectively identical to pushing a prompt for continued operation when running attached to a terminal and, when not running attached to a terminal (eg disowned, or under a service manager), a prompt cannot be displayed anyway.
  • bitcoin-qt now gives the user the opportunity to override without going and manually editing the configuration files, without simply showing them a dialog they can hit yes on to continue without thinking about anything.
  • Shutting down outright is far more detectable and safe than leaving the node running, but silently rejecting blocks.

@1440000bytes
Copy link

Approach ACK

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;
if (IsThisSoftwareExpiringSoon(GetTime())) {
QMessageBox::warning(nullptr, QObject::tr("Software expires soon"), QObject::tr("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)."));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably shouldn't pause startup waiting for a response

return EXIT_FAILURE;
}

gArgs.ModifyRWConfigFile("softwareexpiry", "0");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rwconf is kind of deprecated at this point (superceded by settings.json)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ModifyRWConfigFile function has a default-true arg for also_settings_json. I've confirmed that this actually updates the settings.json file (I've been launching with -softwareexpiry=X for testing purposes).

void ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change, bool also_settings_json = true);

void ArgsManager::ModifyRWConfigFile(const std::string& setting_to_change, const std::string& new_value, const bool also_settings_json)

m_settings.rw_settings[setting_change.first] = setting_change.second;

void ArgsManager::ModifyRWConfigFile(const std::map<std::string, std::string>& settings_to_change, const bool also_settings_json)

Is there some other approach we want to take, or all good?

return EXIT_FAILURE;
}

gArgs.ModifyRWConfigFile("softwareexpiry", "0");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably shouldn't persist forever, and should reset to default if the user upgrades?

Copy link
Author

@pithosian pithosian Jun 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would we want to approach that?

I think if we don't introduce an update detection mechanism, we have to go with 0. If we set eg 2 years in the future, then in a year the user updates to a brand new version, that version would end up erroneously expiring in 1 year.

The approach which springs to mind is adding an additional config value, like uiexpiryoverrideversion, and setting it to the current version when overriding software expiry. Then on launch we could check whether the uiexpiryoverrideversion is the currently running version (and not an empty string) and, if not, clear uiexpiryoverrideversion and softwareexpiry in the rwconf/settings.json. If the user manually configured softwareexpiry in their bitcoin.conf, it would be untouched.

We could also generalize this with just a lastexecutedversion value instead, always set to the current version, and have some handler for when we detect a change.

@@ -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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it should be

Suggested change
for (int i = 0; i < std::min(144, pindexPrev->nHeight); ++i) {
for (int i = 0; i < std::min(144, pindexPrev->nHeight - 1); ++i) {

?

But to avoid re-calculating it every iteration, better to offset the starting index.

assert(blockindex_old);
blockindex_old = blockindex_old->pprev;
}
assert(blockindex_old);
if (IsThisSoftwareExpired(blockindex_old->GetMedianTimePast())) {
chainman.GetNotifications().fatalError(_("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)."));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use warningSet so it shows up in RPC, -alertnotify, etc?

@pithosian
Copy link
Author

Been away from the computer for a few days, but ack the comments; will take a look at cleanup this coming week.

@Retropex
Copy link

Retropex commented Jun 8, 2025

Approach ACK

1 similar comment
@chrisguida
Copy link

Approach ACK

@luke-jr
Copy link
Collaborator

luke-jr commented Aug 23, 2025

I believe I've addressed the concepts presented herein in the upcoming Knots 29

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants