Browse Source

Implement automatic shutdown of deprecated Zcash versions

Closes #2274.
pull/4/head
Jack Grigg 7 years ago
parent
commit
5b3bc9716e
  1. 9
      doc/release-process.md
  2. 2
      src/Makefile.am
  3. 1
      src/Makefile.gtest.include
  4. 2
      src/clientversion.cpp
  5. 1
      src/clientversion.h
  6. 55
      src/deprecation.cpp
  7. 23
      src/deprecation.h
  8. 105
      src/gtest/test_deprecation.cpp
  9. 2
      src/init.cpp
  10. 5
      src/main.cpp

9
doc/release-process.md

@ -36,7 +36,9 @@ previous release:
### 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
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.
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
also wish to change the `PROTOCOL_VERSION` in `version.h`.

2
src/Makefile.am

@ -125,6 +125,7 @@ BITCOIN_CORE_H = \
core_io.h \
core_memusage.h \
eccryptoverify.h \
deprecation.h \
ecwrapper.h \
hash.h \
httprpc.h \
@ -215,6 +216,7 @@ libbitcoin_server_a_SOURCES = \
bloom.cpp \
chain.cpp \
checkpoints.cpp \
deprecation.cpp \
httprpc.cpp \
httpserver.cpp \
init.cpp \

1
src/Makefile.gtest.include

@ -19,6 +19,7 @@ zcash_gtest_SOURCES += \
endif
zcash_gtest_SOURCES += \
gtest/test_tautology.cpp \
gtest/test_deprecation.cpp \
gtest/test_equihash.cpp \
gtest/test_joinsplit.cpp \
gtest/test_keystore.cpp \

2
src/clientversion.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_DATE(BUILD_DATE);
static std::string FormatVersion(int nVersion)
std::string FormatVersion(int nVersion)
{
if (nVersion % 100 < 25)
return strprintf("%d.%d.%d-beta%d", nVersion / 1000000, (nVersion / 10000) % 100, (nVersion / 100) % 100, (nVersion % 100)+1);

1
src/clientversion.h

@ -63,6 +63,7 @@ extern const std::string CLIENT_BUILD;
extern const std::string CLIENT_DATE;
std::string FormatVersion(int nVersion);
std::string FormatFullVersion();
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments);

55
src/deprecation.cpp

@ -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);
}
}

23
src/deprecation.h

@ -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

105
src/gtest/test_deprecation.cpp

@ -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());
}

2
src/init.cpp

@ -344,6 +344,8 @@ std::string HelpMessage(HelpMessageMode mode)
#endif
}
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("-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"));

5
src/main.cpp

@ -14,6 +14,7 @@
#include "checkpoints.h"
#include "checkqueue.h"
#include "consensus/validation.h"
#include "deprecation.h"
#include "init.h"
#include "merkleblock.h"
#include "metrics.h"
@ -2520,6 +2521,8 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true);
EnforceNodeDeprecation(pindexNew->nHeight);
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 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()),
Checkpoints::GuessVerificationProgress(chainparams.Checkpoints(), chainActive.Tip()));
EnforceNodeDeprecation(chainActive.Height(), true);
return true;
}

Loading…
Cancel
Save