From 76750f1c5d966a72ae016f76bd3a37dba93fe73a Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sat, 26 Aug 2017 03:45:40 +0000 Subject: [PATCH 1/4] Expire bitcoind & bitcoin-qt 1-2 years after its last change COPYRIGHT_YEAR + 2 years is used as the basis for expiration, to achieve a constantly-moving-forward expiration date. The expiration can be disabled or extended with a debug-visibility "softwareexpiry" configuration option. --- src/bitcoind.cpp | 6 ++++++ src/clientversion.cpp | 11 +++++++++++ src/clientversion.h | 6 ++++++ src/init.cpp | 1 + src/qt/bitcoin.cpp | 6 ++++++ src/validation.cpp | 4 ++++ test/functional/test_framework/test_node.py | 5 ++++- 7 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index e2224befef..2b5602b482 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -130,6 +130,12 @@ static bool ParseArgs(ArgsManager& args, int argc, char* argv[]) return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.", argv[i]))); } } + + 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; } diff --git a/src/clientversion.cpp b/src/clientversion.cpp index e7d63e34c6..51fc239917 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include @@ -105,3 +107,12 @@ std::string LicenseInfo() strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "") + "\n"; } + +bool IsThisSoftwareExpired(int64_t nTime) +{ + int64_t nSoftwareExpiry = gArgs.GetIntArg("-softwareexpiry", DEFAULT_SOFTWARE_EXPIRY); + if (nSoftwareExpiry <= 0) { + nSoftwareExpiry = std::numeric_limits::max(); + } + return (nTime > nSoftwareExpiry); +} diff --git a/src/clientversion.h b/src/clientversion.h index 9da0cd0b39..3baefdefca 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -46,6 +46,12 @@ std::string CopyrightHolders(const std::string& strPrefix); /** Returns licensing information (for -version) */ std::string LicenseInfo(); +static const int64_t SECONDS_PER_YEAR = 31558060; +static const int POSIX_EPOCH_YEAR = 1970; +static const int64_t DEFAULT_SOFTWARE_EXPIRY = ((COPYRIGHT_YEAR - POSIX_EPOCH_YEAR) * SECONDS_PER_YEAR) + (SECONDS_PER_YEAR * 2); + +bool IsThisSoftwareExpired(int64_t nTime); + #endif // WINDRES_PREPROC #endif // BITCOIN_CLIENTVERSION_H diff --git a/src/init.cpp b/src/init.cpp index 8baa642d30..1808077d67 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -465,6 +465,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-reindex", "If enabled, wipe chain state and block index, and rebuild them from blk*.dat files on disk. Also wipe and rebuild other optional indexes that are active. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-reindex-chainstate", "If enabled, wipe chain state, and rebuild it from blk*.dat files on disk. If an assumeutxo snapshot was loaded, its chainstate will be wiped as well. The snapshot can then be reloaded via RPC.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-settings=", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-softwareexpiry", strprintf("Stop working after this POSIX timestamp (default: %s)", DEFAULT_SOFTWARE_EXPIRY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); #if HAVE_SYSTEM argsman.AddArg("-startupnotify=", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-shutdownnotify=", "Execute command immediately before beginning shutdown. The need for shutdown may be urgent, so be careful not to delay it long (if the command doesn't require interaction with the server, consider having it fork into the background).", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index bad4e72794..37b5ee9de6 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -611,6 +612,11 @@ 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); + 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 diff --git a/src/validation.cpp b/src/validation.cpp index f02d1e32a4..0c19773dfd 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3618,6 +3618,10 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "high-hash", "proof of work failed"); + if (IsThisSoftwareExpired(block.nTime)) { + return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "node-expired", "node software has expired"); + } + return true; } diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 478a7169ca..3ce3ce51d5 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -120,7 +120,10 @@ class TestNode(): "--error-exitcode=1", "--quiet"] + self.args if self.version is None: - self.args.append("-walletimplicitsegwit") + self.args += [ + "-softwareexpiry=0", + "-walletimplicitsegwit", + ] if self.version_is_at_least(190000): self.args.append("-logthreadnames") From 08b55f96cc242450b2103f88bf0f00a8f3dab655 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 25 May 2022 03:27:49 +0000 Subject: [PATCH 2/4] fuzz: tx_pool: Keep nTime < DEFAULT_SOFTWARE_EXPIRY --- src/test/fuzz/tx_pool.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index ee73f67f66..6c380c7dbb 100644 --- a/src/test/fuzz/tx_pool.cpp +++ b/src/test/fuzz/tx_pool.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -113,7 +114,7 @@ void MockTime(FuzzedDataProvider& fuzzed_data_provider, const Chainstate& chains { const auto time = ConsumeTime(fuzzed_data_provider, chainstate.m_chain.Tip()->GetMedianTimePast() + 1, - std::numeric_limitsnTime)>::max()); + DEFAULT_SOFTWARE_EXPIRY - 1); SetMockTime(time); } From 8ecc5db33c7a8ec66b821c056ce6c5bf6bf499af Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Mon, 26 Feb 2024 04:38:02 +0000 Subject: [PATCH 3/4] Bugfix: clientversion.h: Add missing include --- src/clientversion.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clientversion.h b/src/clientversion.h index 3baefdefca..bd0bd89d5a 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -27,6 +27,7 @@ #if !defined(WINDRES_PREPROC) +#include #include #include From 3f9f5d50da2cba6edbf3c0bba65355b0f08890e0 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Sun, 10 Mar 2024 18:19:12 +0000 Subject: [PATCH 4/4] Refactor softwareexpiry to use global variable (Necessary to avoid indirect univalue inclusion via common/args, in clientversion) --- src/bitcoind.cpp | 1 + src/clientversion.cpp | 11 +++++------ src/clientversion.h | 1 + src/qt/bitcoin.cpp | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 2b5602b482..b8dc7fe0a1 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -131,6 +131,7 @@ 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); diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 51fc239917..52be41b9cb 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -5,9 +5,7 @@ #include #include -#include #include -#include #include #include @@ -108,11 +106,12 @@ std::string LicenseInfo() "\n"; } +int64_t g_software_expiry{DEFAULT_SOFTWARE_EXPIRY}; + bool IsThisSoftwareExpired(int64_t nTime) { - int64_t nSoftwareExpiry = gArgs.GetIntArg("-softwareexpiry", DEFAULT_SOFTWARE_EXPIRY); - if (nSoftwareExpiry <= 0) { - nSoftwareExpiry = std::numeric_limits::max(); + if (g_software_expiry <= 0) { + return false; } - return (nTime > nSoftwareExpiry); + return (nTime > g_software_expiry); } diff --git a/src/clientversion.h b/src/clientversion.h index bd0bd89d5a..893505ae6b 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -50,6 +50,7 @@ std::string LicenseInfo(); static const int64_t SECONDS_PER_YEAR = 31558060; static const int POSIX_EPOCH_YEAR = 1970; static const int64_t DEFAULT_SOFTWARE_EXPIRY = ((COPYRIGHT_YEAR - POSIX_EPOCH_YEAR) * SECONDS_PER_YEAR) + (SECONDS_PER_YEAR * 2); +extern int64_t g_software_expiry; bool IsThisSoftwareExpired(int64_t nTime); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 37b5ee9de6..c7f6dc3220 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -612,6 +612,7 @@ 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;