Implement automatic shutdown of deprecated Zcash versions
Closes #2274.
This commit is contained in:
parent
20d61ac0ea
commit
5b3bc9716e
|
@ -36,7 +36,9 @@ previous release:
|
||||||
|
|
||||||
### B1. Check that you are up-to-date with current master, then create a release branch.
|
### B1. Check that you are up-to-date with current master, then create a release branch.
|
||||||
|
|
||||||
### B2. Update (commit) version in sources.
|
### B2. Update (commit) version and deprecation in sources.
|
||||||
|
|
||||||
|
Update the client version in these files:
|
||||||
|
|
||||||
README.md
|
README.md
|
||||||
src/clientversion.h
|
src/clientversion.h
|
||||||
|
@ -56,6 +58,11 @@ In `configure.ac` and `clientversion.h`:
|
||||||
|
|
||||||
- Change `CLIENT_VERSION_IS_RELEASE` to false while Zcash is in beta-test phase.
|
- Change `CLIENT_VERSION_IS_RELEASE` to false while Zcash is in beta-test phase.
|
||||||
|
|
||||||
|
Update `APPROX_RELEASE_HEIGHT` and `WEEKS_UNTIL_DEPRECATION` in `src/deprecation.h`
|
||||||
|
so that `APPROX_RELEASE_HEIGHT` will be reached shortly after release, and
|
||||||
|
`WEEKS_UNTIL_DEPRECATION` is the number of weeks from release day until the
|
||||||
|
deprecation target (as defined by the current deprecation policy).
|
||||||
|
|
||||||
If this release changes the behavior of the protocol or fixes a serious bug, we may
|
If this release changes the behavior of the protocol or fixes a serious bug, we may
|
||||||
also wish to change the `PROTOCOL_VERSION` in `version.h`.
|
also wish to change the `PROTOCOL_VERSION` in `version.h`.
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ BITCOIN_CORE_H = \
|
||||||
core_io.h \
|
core_io.h \
|
||||||
core_memusage.h \
|
core_memusage.h \
|
||||||
eccryptoverify.h \
|
eccryptoverify.h \
|
||||||
|
deprecation.h \
|
||||||
ecwrapper.h \
|
ecwrapper.h \
|
||||||
hash.h \
|
hash.h \
|
||||||
httprpc.h \
|
httprpc.h \
|
||||||
|
@ -215,6 +216,7 @@ libbitcoin_server_a_SOURCES = \
|
||||||
bloom.cpp \
|
bloom.cpp \
|
||||||
chain.cpp \
|
chain.cpp \
|
||||||
checkpoints.cpp \
|
checkpoints.cpp \
|
||||||
|
deprecation.cpp \
|
||||||
httprpc.cpp \
|
httprpc.cpp \
|
||||||
httpserver.cpp \
|
httpserver.cpp \
|
||||||
init.cpp \
|
init.cpp \
|
||||||
|
|
|
@ -19,6 +19,7 @@ zcash_gtest_SOURCES += \
|
||||||
endif
|
endif
|
||||||
zcash_gtest_SOURCES += \
|
zcash_gtest_SOURCES += \
|
||||||
gtest/test_tautology.cpp \
|
gtest/test_tautology.cpp \
|
||||||
|
gtest/test_deprecation.cpp \
|
||||||
gtest/test_equihash.cpp \
|
gtest/test_equihash.cpp \
|
||||||
gtest/test_joinsplit.cpp \
|
gtest/test_joinsplit.cpp \
|
||||||
gtest/test_keystore.cpp \
|
gtest/test_keystore.cpp \
|
||||||
|
|
|
@ -100,7 +100,7 @@ const std::string CLIENT_NAME("MagicBean");
|
||||||
const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
|
const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
|
||||||
const std::string CLIENT_DATE(BUILD_DATE);
|
const std::string CLIENT_DATE(BUILD_DATE);
|
||||||
|
|
||||||
static std::string FormatVersion(int nVersion)
|
std::string FormatVersion(int nVersion)
|
||||||
{
|
{
|
||||||
if (nVersion % 100 < 25)
|
if (nVersion % 100 < 25)
|
||||||
return strprintf("%d.%d.%d-beta%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)+1);
|
return strprintf("%d.%d.%d-beta%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)+1);
|
||||||
|
|
|
@ -63,6 +63,7 @@ extern const std::string CLIENT_BUILD;
|
||||||
extern const std::string CLIENT_DATE;
|
extern const std::string CLIENT_DATE;
|
||||||
|
|
||||||
|
|
||||||
|
std::string FormatVersion(int nVersion);
|
||||||
std::string FormatFullVersion();
|
std::string FormatFullVersion();
|
||||||
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);
|
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright (c) 2017 The Zcash developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "deprecation.h"
|
||||||
|
|
||||||
|
#include "clientversion.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION);
|
||||||
|
|
||||||
|
void EnforceNodeDeprecation(int nHeight, bool forceLogging) {
|
||||||
|
int blocksToDeprecation = DEPRECATION_HEIGHT - nHeight;
|
||||||
|
bool disableDeprecation = (GetArg("-disabledeprecation", "") == CLIENT_VERSION_STR);
|
||||||
|
if (blocksToDeprecation <= 0) {
|
||||||
|
// In order to ensure we only log once per process when deprecation is
|
||||||
|
// disabled (to avoid log spam), we only need to log in two cases:
|
||||||
|
// - The deprecating block just arrived
|
||||||
|
// - This can be triggered more than once if a block chain reorg
|
||||||
|
// occurs, but that's an irregular event that won't cause spam.
|
||||||
|
// - The node is starting
|
||||||
|
if (blocksToDeprecation == 0 || forceLogging) {
|
||||||
|
auto msg = strprintf(_("This version has been deprecated as of block height %d."),
|
||||||
|
DEPRECATION_HEIGHT) + " " +
|
||||||
|
_("You should upgrade to the latest version of Zcash.");
|
||||||
|
if (!disableDeprecation) {
|
||||||
|
msg += " " + strprintf(_("To disable deprecation for this version, set %s%s."),
|
||||||
|
"-disabledeprecation=", CLIENT_VERSION_STR);
|
||||||
|
}
|
||||||
|
LogPrintf("*** %s\n", msg);
|
||||||
|
uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR);
|
||||||
|
}
|
||||||
|
if (!disableDeprecation) {
|
||||||
|
StartShutdown();
|
||||||
|
}
|
||||||
|
} else if (blocksToDeprecation == DEPRECATION_WARN_LIMIT ||
|
||||||
|
(blocksToDeprecation < DEPRECATION_WARN_LIMIT && forceLogging)) {
|
||||||
|
std::string msg;
|
||||||
|
if (disableDeprecation) {
|
||||||
|
msg = strprintf(_("This version will be deprecated at block height %d."),
|
||||||
|
DEPRECATION_HEIGHT) + " " +
|
||||||
|
_("You should upgrade to the latest version of Zcash.");
|
||||||
|
} else {
|
||||||
|
msg = strprintf(_("This version will be deprecated at block height %d, and will automatically shut down."),
|
||||||
|
DEPRECATION_HEIGHT) + " " +
|
||||||
|
_("You should upgrade to the latest version of Zcash.") + " " +
|
||||||
|
strprintf(_("To disable deprecation for this version, set %s%s."),
|
||||||
|
"-disabledeprecation=", CLIENT_VERSION_STR);
|
||||||
|
}
|
||||||
|
LogPrintf("*** %s\n", msg);
|
||||||
|
uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_WARNING);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) 2017 The Zcash developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef ZCASH_DEPRECATION_H
|
||||||
|
#define ZCASH_DEPRECATION_H
|
||||||
|
|
||||||
|
// Deprecation policy is 4th third-Tuesday after a release
|
||||||
|
static const int APPROX_RELEASE_HEIGHT = 115000;
|
||||||
|
static const int WEEKS_UNTIL_DEPRECATION = 18;
|
||||||
|
static const int DEPRECATION_HEIGHT = APPROX_RELEASE_HEIGHT + (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 24);
|
||||||
|
|
||||||
|
// Number of blocks before deprecation to warn users
|
||||||
|
static const int DEPRECATION_WARN_LIMIT = 14 * 24 * 24; // 2 weeks
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the node is deprecated based on the current block height, and
|
||||||
|
* shuts down the node with an error if so (and deprecation is not disabled for
|
||||||
|
* the current client version).
|
||||||
|
*/
|
||||||
|
void EnforceNodeDeprecation(int nHeight, bool forceLogging=false);
|
||||||
|
|
||||||
|
#endif // ZCASH_DEPRECATION_H
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "clientversion.h"
|
||||||
|
#include "deprecation.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "ui_interface.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
using ::testing::StrictMock;
|
||||||
|
|
||||||
|
static const std::string CLIENT_VERSION_STR = FormatVersion(CLIENT_VERSION);
|
||||||
|
extern std::atomic<bool> fRequestShutdown;
|
||||||
|
|
||||||
|
class MockUIInterface {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD3(ThreadSafeMessageBox, bool(const std::string& message,
|
||||||
|
const std::string& caption,
|
||||||
|
unsigned int style));
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool ThreadSafeMessageBox(MockUIInterface *mock,
|
||||||
|
const std::string& message,
|
||||||
|
const std::string& caption,
|
||||||
|
unsigned int style)
|
||||||
|
{
|
||||||
|
mock->ThreadSafeMessageBox(message, caption, style);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DeprecationTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() {
|
||||||
|
uiInterface.ThreadSafeMessageBox.disconnect_all_slots();
|
||||||
|
uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, &mock_, _1, _2, _3));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TearDown() {
|
||||||
|
fRequestShutdown = false;
|
||||||
|
mapArgs["-disabledeprecation"] = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StrictMock<MockUIInterface> mock_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, NonDeprecatedNodeKeepsRunning) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT - 1);
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, NodeNearDeprecationIsWarned) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT);
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, NodeNearDeprecationWarningIsNotDuplicated) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1);
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, NodeNearDeprecationWarningIsRepeatedOnStartup) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_WARNING));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT - DEPRECATION_WARN_LIMIT + 1, true);
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, DeprecatedNodeShutsDown) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
|
||||||
|
EXPECT_TRUE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, DeprecatedNodeErrorIsNotDuplicated) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1);
|
||||||
|
EXPECT_TRUE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, DeprecatedNodeErrorIsRepeatedOnStartup) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT + 1, true);
|
||||||
|
EXPECT_TRUE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, DeprecatedNodeShutsDownIfOldVersionDisabled) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
mapArgs["-disabledeprecation"] = "1.0.0";
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
|
||||||
|
EXPECT_TRUE(ShutdownRequested());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(DeprecationTest, DeprecatedNodeKeepsRunningIfCurrentVersionDisabled) {
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
mapArgs["-disabledeprecation"] = CLIENT_VERSION_STR;
|
||||||
|
EXPECT_CALL(mock_, ThreadSafeMessageBox(::testing::_, "", CClientUIInterface::MSG_ERROR));
|
||||||
|
EnforceNodeDeprecation(DEPRECATION_HEIGHT);
|
||||||
|
EXPECT_FALSE(ShutdownRequested());
|
||||||
|
}
|
|
@ -344,6 +344,8 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
||||||
|
strUsage += HelpMessageOpt("-disabledeprecation=<version>", strprintf(_("Disable block-height node deprecation and automatic shutdown (example: -disabledeprecation=%s)"),
|
||||||
|
FormatVersion(CLIENT_VERSION)));
|
||||||
strUsage += HelpMessageOpt("-exportdir=<dir>", _("Specify directory to be used when exporting data"));
|
strUsage += HelpMessageOpt("-exportdir=<dir>", _("Specify directory to be used when exporting data"));
|
||||||
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
|
||||||
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
|
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file") + " " + _("on startup"));
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "checkqueue.h"
|
#include "checkqueue.h"
|
||||||
#include "consensus/validation.h"
|
#include "consensus/validation.h"
|
||||||
|
#include "deprecation.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "merkleblock.h"
|
#include "merkleblock.h"
|
||||||
#include "metrics.h"
|
#include "metrics.h"
|
||||||
|
@ -2520,6 +2521,8 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
|
||||||
// Update cached incremental witnesses
|
// Update cached incremental witnesses
|
||||||
GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true);
|
GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true);
|
||||||
|
|
||||||
|
EnforceNodeDeprecation(pindexNew->nHeight);
|
||||||
|
|
||||||
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
|
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
|
||||||
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
|
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);
|
||||||
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
|
LogPrint("bench", "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001);
|
||||||
|
@ -3613,6 +3616,8 @@ bool static LoadBlockIndexDB()
|
||||||
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
|
DateTimeStrFormat("%Y-%m-%d %H:%M:%S", chainActive.Tip()->GetBlockTime()),
|
||||||
Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip()));
|
Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip()));
|
||||||
|
|
||||||
|
EnforceNodeDeprecation(chainActive.Height(), true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue