diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 3562c0d1b7..f04d88cee4 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -163,6 +163,11 @@ static bool AppInit(NodeContext& node, int argc, char* argv[]) } } + 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); + } + // -server defaults to true for bitcoind but not for the GUI so do this here args.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console diff --git a/src/clientversion.cpp b/src/clientversion.cpp index e7d63e34c6..229d5e5a88 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -6,6 +6,8 @@ #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 fb47c6de7b..12395315d2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -464,6 +465,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk. This will also rebuild active optional indexes. Setting this to auto automatically reindexes the block database if it is corrupted.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead. Deactivate all optional indexes before running this.", 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 1794190d1b..db20049ff2 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -617,6 +618,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/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp index a055705575..664329ed9a 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); } diff --git a/src/validation.cpp b/src/validation.cpp index 55b9a82af8..59568e1f9f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -3685,6 +3686,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 51e08676ba..606b8b4666 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -119,7 +119,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")