New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Network upgrade activation mechanism #2898
Changes from 9 commits
780f526
b174b7e
f52da91
89f2045
9e85145
5486837
149d69e
828940b
5009136
cad27eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -90,6 +90,13 @@ class CMainParams : public CChainParams { | |
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down | ||
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up | ||
consensus.nPowTargetSpacing = 2.5 * 60; | ||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = | ||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE; | ||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
|
||
/** | ||
* The message start string should be awesome! ⓩ❤ | ||
*/ | ||
|
@@ -241,6 +248,13 @@ class CTestNetParams : public CChainParams { | |
consensus.nPowMaxAdjustDown = 32; // 32% adjustment down | ||
consensus.nPowMaxAdjustUp = 16; // 16% adjustment up | ||
consensus.nPowTargetSpacing = 2.5 * 60; | ||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = | ||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE; | ||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
|
||
pchMessageStart[0] = 0xfa; | ||
pchMessageStart[1] = 0x1a; | ||
pchMessageStart[2] = 0xf9; | ||
|
@@ -341,6 +355,12 @@ class CRegTestParams : public CChainParams { | |
consensus.nPowMaxAdjustDown = 0; // Turn off adjustment down | ||
consensus.nPowMaxAdjustUp = 0; // Turn off adjustment up | ||
consensus.nPowTargetSpacing = 2.5 * 60; | ||
consensus.vUpgrades[Consensus::BASE_SPROUT].nActivationHeight = | ||
Consensus::NetworkUpgrade::ALWAYS_ACTIVE; | ||
consensus.vUpgrades[Consensus::UPGRADE_TESTDUMMY].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
consensus.vUpgrades[Consensus::UPGRADE_OVERWINTER].nActivationHeight = | ||
Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT; | ||
|
||
pchMessageStart[0] = 0xaa; | ||
pchMessageStart[1] = 0xe8; | ||
|
@@ -394,6 +414,12 @@ class CRegTestParams : public CChainParams { | |
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" }; | ||
assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight()); | ||
} | ||
|
||
void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight) | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
assert(idx > Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); | ||
consensus.vUpgrades[idx].nActivationHeight = nActivationHeight; | ||
} | ||
}; | ||
static CRegTestParams regTestParams; | ||
|
||
|
@@ -467,3 +493,8 @@ std::string CChainParams::GetFoundersRewardAddressAtIndex(int i) const { | |
assert(i >= 0 && i < vFoundersRewardAddress.size()); | ||
return vFoundersRewardAddress[i]; | ||
} | ||
|
||
void UpdateNetworkUpgradeParameters(Consensus::UpgradeIndex idx, int nActivationHeight) | ||
{ | ||
regTestParams.UpdateNetworkUpgradeParameters(idx, nActivationHeight); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Upstream has a similar function for their BIP9 tests, which they later changed to be usable on any params for testing flexibility (while still only allowing regtest-level configuration from outside the binary). I will end up doing the same thing when I pull in bitcoin/bitcoin#8855 (which as a reminder to myself requires bitcoin/bitcoin#6235). |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,49 @@ | |
#include "uint256.h" | ||
|
||
namespace Consensus { | ||
|
||
/** | ||
* Index into Params.vUpgrades and NetworkUpgradeInfo | ||
* | ||
* Being array indices, these MUST be numbered consecutively. | ||
* | ||
* The order of these indices MUST match the order of the upgrades on-chain, as | ||
* several functions depends on the enum being sorted. | ||
*/ | ||
enum UpgradeIndex { | ||
// Sprout must be first | ||
BASE_SPROUT, | ||
UPGRADE_TESTDUMMY, | ||
UPGRADE_OVERWINTER, | ||
// NOTE: Also add new upgrades to NetworkUpgradeInfo in upgrades.cpp | ||
MAX_NETWORK_UPGRADES | ||
}; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add static asserts to ensure UPGRADE_SPROUT==1 and UPGRADE_SPROUT < UPGRADE_OVERWINTER etc? Just in case a developer changes thing around? Although a stronger comment to the effect of "Do not reorder these enum constants!" could also work. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Happy to add the latter. Not adding the former, per above comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decided to strongly-word the comment instead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add a comment that they must be consecutively numbered. |
||
struct NetworkUpgrade { | ||
/** | ||
* Height of the first block for which the new consensus rules will be active | ||
*/ | ||
int nActivationHeight; | ||
|
||
/** | ||
* Special value for nActivationHeight indicating that the upgrade is always active. | ||
* This is useful for testing, as it means tests don't need to deal with the activation | ||
* process (namely, faking a chain of somewhat-arbitrary length). | ||
* | ||
* New blockchains that want to enable upgrade rules from the beginning can also use | ||
* this value. However, additional care must be taken to ensure the genesis block | ||
* satisfies the enabled rules. | ||
*/ | ||
static constexpr int ALWAYS_ACTIVE = 0; | ||
|
||
/** | ||
* Special value for nActivationHeight indicating that the upgrade will never activate. | ||
* This is useful when adding upgrade code that has a testnet activation height, but | ||
* should remain disabled on mainnet. | ||
*/ | ||
static constexpr int NO_ACTIVATION_HEIGHT = -1; | ||
}; | ||
|
||
/** | ||
* Parameters that influence chain consensus. | ||
*/ | ||
|
@@ -39,6 +82,7 @@ struct Params { | |
int nMajorityEnforceBlockUpgrade; | ||
int nMajorityRejectBlockOutdated; | ||
int nMajorityWindow; | ||
NetworkUpgrade vUpgrades[MAX_NETWORK_UPGRADES]; | ||
/** Proof of work parameters */ | ||
uint256 powLimit; | ||
int64_t nPowAveragingWindow; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Copyright (c) 2018 The Zcash developers | ||
// Distributed under the MIT software license, see the accompanying | ||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
||
#include "consensus/upgrades.h" | ||
|
||
/** | ||
* General information about each network upgrade. | ||
* Ordered by Consensus::UpgradeIndex. | ||
*/ | ||
const struct NUInfo NetworkUpgradeInfo[Consensus::MAX_NETWORK_UPGRADES] = { | ||
{ | ||
/*.nBranchId =*/ 0, | ||
/*.strName =*/ "Sprout", | ||
/*.strInfo =*/ "The Zcash network at launch", | ||
}, | ||
{ | ||
/*.nBranchId =*/ 0x74736554, | ||
/*.strName =*/ "Test dummy", | ||
/*.strInfo =*/ "Test dummy info", | ||
}, | ||
{ | ||
/*.nBranchId =*/ 0x5ba81b19, | ||
/*.strName =*/ "Overwinter", | ||
/*.strInfo =*/ "TBD", | ||
} | ||
}; | ||
|
||
UpgradeState NetworkUpgradeState( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to see I understand: there is no way to make an Upgrade for a limited range of blocks. Once it's active, it is active for ever - unless you change it's activation height to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's a confusion between upgrades and epochs here, which maybe needs clarification in the ZIP:
So if you want some rule to be only applied to a specific epoch, you could use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ZIP now defines a "branch" vs a "hard fork", which captures the above distinction (epochs are sub-regions of branches, and upgrades are subsets of hard forks). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, so if you want a change to be for a limited range of blocks, you define in advance two network upgrades that are the endpoints of this range. |
||
int nHeight, | ||
const Consensus::Params& params, | ||
Consensus::UpgradeIndex idx) | ||
{ | ||
assert(nHeight >= 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); | ||
auto nActivationHeight = params.vUpgrades[idx].nActivationHeight; | ||
|
||
if (nActivationHeight == Consensus::NetworkUpgrade::NO_ACTIVATION_HEIGHT) { | ||
return UPGRADE_DISABLED; | ||
} else if (nHeight >= nActivationHeight) { | ||
// From ZIP ???: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a ZIP for this? This comment needs to be updated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I will update this comment once @daira assigns a ZIP number to the corresponding draft. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's possible that the caller passes in -1 for nHeight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No it isn't, because of the assertion at the start of the function. |
||
// | ||
// ACTIVATION_HEIGHT | ||
// The block height at which the network upgrade rules will come into effect. | ||
// | ||
// For removal of ambiguity, the block at height ACTIVATION_HEIGHT - 1 is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So there will be no tx embargo period? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not at the consensus layer, no. @zookozcash was only willing to consider it for Overwinter itself if necessary, not for the general mechanism (which is what this PR implements). |
||
// subject to the pre-upgrade consensus rules. | ||
return UPGRADE_ACTIVE; | ||
} else { | ||
return UPGRADE_PENDING; | ||
} | ||
} | ||
|
||
bool NetworkUpgradeActive( | ||
int nHeight, | ||
const Consensus::Params& params, | ||
Consensus::UpgradeIndex idx) | ||
{ | ||
return NetworkUpgradeState(nHeight, params, idx) == UPGRADE_ACTIVE; | ||
} | ||
|
||
int CurrentEpoch(int nHeight, const Consensus::Params& params) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assumes the latest upgrade is the last one in the vector. I guess that's fine but should be clearly documented. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct; it assumes that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't bother here because There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's possible that the caller passes in -1 for nHeight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is possible, but per above comment, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really wish we were writing in a language with integer range types. (cf #627) |
||
for (auto idxInt = Consensus::MAX_NETWORK_UPGRADES - 1; idxInt >= Consensus::BASE_SPROUT; idxInt--) { | ||
if (NetworkUpgradeActive(nHeight, params, Consensus::UpgradeIndex(idxInt))) { | ||
return idxInt; | ||
} | ||
} | ||
} | ||
|
||
uint32_t CurrentEpochBranchId(int nHeight, const Consensus::Params& params) { | ||
return NetworkUpgradeInfo[CurrentEpoch(nHeight, params)].nBranchId; | ||
} | ||
|
||
bool IsActivationHeight( | ||
int nHeight, | ||
const Consensus::Params& params, | ||
Consensus::UpgradeIndex idx) | ||
{ | ||
assert(idx >= Consensus::BASE_SPROUT && idx < Consensus::MAX_NETWORK_UPGRADES); | ||
|
||
// Don't count Sprout as an activation height | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That requires the caller to handle that case, and we know better here how that case should be handled (specifically, it's because Sprout wasn't actually an upgrade but is being included as one for code niceness, and this case is needed to prevent the entire chain being reindexed on first start due to missing, and unnecessary, index data). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ACK, makes sense. |
||
if (idx == Consensus::BASE_SPROUT) { | ||
return false; | ||
} | ||
|
||
return nHeight >= 0 && nHeight == params.vUpgrades[idx].nActivationHeight; | ||
} | ||
|
||
bool IsActivationHeightForAnyUpgrade( | ||
int nHeight, | ||
const Consensus::Params& params) | ||
{ | ||
if (nHeight < 0) { | ||
return false; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Oh, yes it is because |
||
|
||
// Don't count Sprout as an activation height | ||
for (int idx = Consensus::BASE_SPROUT + 1; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) { | ||
if (nHeight == params.vUpgrades[idx].nActivationHeight) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
boost::optional<int> NextActivationHeight( | ||
int nHeight, | ||
const Consensus::Params& params) | ||
{ | ||
if (nHeight < 0) { | ||
return boost::none; | ||
} | ||
|
||
// Don't count Sprout as an activation height | ||
for (auto idx = Consensus::BASE_SPROUT + 1; idx < Consensus::MAX_NETWORK_UPGRADES; idx++) { | ||
if (NetworkUpgradeState(nHeight, params, Consensus::UpgradeIndex(idx)) == UPGRADE_PENDING) { | ||
return params.vUpgrades[idx].nActivationHeight; | ||
} | ||
} | ||
|
||
return boost::none; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the moment, this means that all the RPC tests will default to Sprout mode. I think we should at some point change this to
ALWAYS_ACTIVE
(updatingsrc/gtest/test_upgrades.cpp
accordingly), and have a few specific RPC tests that disable Overwinter.