Implement Version Upgrade Mechanism #3096
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What's in this pull request?
This PR implements the mechanism to carry out breaking changes (version upgrades) after launch.
It mostly follows the specification in the Albatross paper with one exception:
We will not only check for the proportion of stake supporting the upgrade, but also for the proportion of slots supporting the upgrade. This makes sure we don't propose an upgrade in an epoch where less than 2f+1 elected validators support the upgrade.
Mechanism Overview
The version upgrade mechanism introduces the following key changes, discussed in a bit more detail below:
Block validity
The version number is stored in every block. We allow version upgrades only in election blocks.
Instead of checking against a constant version from the
Policy
, block-level verification of the version is now split into two parts:Block::verify_header
: Making sure that the version is always below or equal to the maximum supported versionPolicy::max_supported_version(network_id)
. The maximum supported version depends on the network, so that we can roll out changes in the testnet first.Block:: verify_immediate_successor
andBlock::verify_macro_successor
: Making sure that the version number can only increase in election blocks and only in steps of one. For all other scenarios, the version needs to match the predecessor's version. We test this in both functions as these are called in different scenarios.Helper functions
We only perform version upgrades when at least
Policy:: UPGRADE_MIN_SUPPORT
% of the active stake signals support for the upgrade and when at leastPolicy::TWO_F_PLUS_ONE
slots of the currently elected validators signals support.We add helper functions to determine the supporting stake and slots.
The
get_supporting_stake
function is computationally expensive in that it iterates over all validators in the staking contract. This number is bounded by the validator deposit and we only call the function when proposing an election block and not being on the maximum version already.Currently, the support for a version is signalled by encoding the supported version in the first two bytes of the validator's signal data (in big-endian). This can still be changed later and be implemented version-specific in the
Policy::supports_upgrade
function.We also add a function to deactivate unsupportive stake, which is discussed in the section about the new inherent.
Producing an upgrade
Validators who updated their client and have a maximum supported version higher than the current blockchain's version (determined from the head block) will determine support for this upgrade whenever they are the ones to produce a macro proposal. Only when the conditions (
supporting_stake >= Policy:: UPGRADE_MIN_SUPPORT * active stake && supporting_slots > Policy::TWO_F_PLUS_ONE
) are met, the proposal will contain the increased version number. If the proposal is accepted, the upgrade is successfully active from this block on (including this election block).There is no explicit check for validators to only vote for a block if they support the upgrade. If they updated their client and have a new maximum supported version, the proposal will be deemed valid and voted for. Validators not supporting the upgrade won't update their clients and will reject the proposal due to an invalid version.
Version upgrade inherent
The mechanism adds a new inherent
VersionUpgrade
that also has the new version. It has the side effect of deactivating all validators that did not explicitly signal support for the new version with immediate effect. This also means that these unsupporting validators should not be selected for the next epoch (thus preventing possible stalls).Full nodes will apply this side-effect through trie-diffs. The inherent is not stored in the history and thus does not require any special handling.
Switching validator selection and accounts commit
In the current model, we first select the validators for the next epoch (based on the current staking contract) and then apply the accounts changes of an election block. The only accounts changes possible in an election block right now are: reward distribution and preparing the bitsets in the staking contract for the next epoch.
Since none of these have an effect on the validator selection, we switch the order of these two. This is required so that unsupporting validators are deactivated before the validator selection.
Tests
This PR includes unit tests in the following crates:
validator
: Testing that the validator will propose an election block with a new version if the conditions are met.block
: Testing the new validity rules.staking_contract
: Testing correct computation of stake/slot support and correct handling of inherent.blockchain
: Testing that the blockchain correctly emits the new inherent.Pull request checklist
clippy
andrustfmt
warnings.