Merge PR #5294: Upgrade Module Spec
This commit is contained in:
parent
a13909a9ff
commit
3196b7c4ca
|
@ -12,13 +12,14 @@ import (
|
|||
// If it is ready, it will execute it if the handler is installed, and panic/abort otherwise.
|
||||
// If the plan is not ready, it will ensure the handler is not registered too early (and abort otherwise).
|
||||
//
|
||||
// The prupose is to ensure the binary is switch EXACTLY at the desired block, and to allow
|
||||
// The purpose is to ensure the binary is switch EXACTLY at the desired block, and to allow
|
||||
// a migration to be executed if needed upon this switch (migration defined in the new binary)
|
||||
func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) {
|
||||
plan, found := k.GetUpgradePlan(ctx)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
if plan.ShouldExecute(ctx) {
|
||||
if !k.HasHandler(plan.Name) {
|
||||
upgradeMsg := fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info)
|
||||
|
@ -26,10 +27,12 @@ func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) {
|
|||
ctx.Logger().Error(upgradeMsg)
|
||||
panic(upgradeMsg)
|
||||
}
|
||||
|
||||
// We have an upgrade handler for this upgrade name, so apply the upgrade
|
||||
ctx.Logger().Info(fmt.Sprintf("applying upgrade \"%s\" at %s", plan.Name, plan.DueAt()))
|
||||
ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter())
|
||||
k.ApplyUpgrade(ctx, plan)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ func NewSoftwareUpgradeProposalHandler(k Keeper) govtypes.Handler {
|
|||
switch c := content.(type) {
|
||||
case SoftwareUpgradeProposal:
|
||||
return handleSoftwareUpgradeProposal(ctx, k, c)
|
||||
|
||||
case CancelSoftwareUpgradeProposal:
|
||||
return handleCancelSoftwareUpgradeProposal(ctx, k, c)
|
||||
|
||||
|
|
|
@ -37,10 +37,10 @@ func (k Keeper) SetUpgradeHandler(name string, upgradeHandler types.UpgradeHandl
|
|||
// If there is another Plan already scheduled, it will overwrite it
|
||||
// (implicitly cancelling the current plan)
|
||||
func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) sdk.Error {
|
||||
err := plan.ValidateBasic()
|
||||
if err != nil {
|
||||
if err := plan.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !plan.Time.IsZero() {
|
||||
if !plan.Time.After(ctx.BlockHeader().Time) {
|
||||
return sdk.ErrUnknownRequest("upgrade cannot be scheduled in the past")
|
||||
|
@ -65,6 +65,7 @@ func (k Keeper) getDoneHeight(ctx sdk.Context, name string) int64 {
|
|||
if len(bz) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return int64(binary.BigEndian.Uint64(bz))
|
||||
}
|
||||
|
||||
|
@ -87,6 +88,7 @@ func (k Keeper) GetUpgradePlan(ctx sdk.Context) (plan types.Plan, havePlan bool)
|
|||
if bz == nil {
|
||||
return plan, false
|
||||
}
|
||||
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &plan)
|
||||
return plan, true
|
||||
}
|
||||
|
@ -111,7 +113,9 @@ func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) {
|
|||
if handler == nil {
|
||||
panic("ApplyUpgrade should never be called without first checking HasHandler")
|
||||
}
|
||||
|
||||
handler(ctx, plan)
|
||||
|
||||
k.ClearUpgradePlan(ctx)
|
||||
k.setDone(ctx, plan.Name)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
# Concepts
|
||||
|
||||
## Plan
|
||||
|
||||
The `x/upgrade` module defines a `Plan` type in which a live upgrade is scheduled
|
||||
to occur. A `Plan` can be scheduled at a specific block height or time, but not both.
|
||||
A `Plan` is created once a (frozen) release candidate along with an appropriate upgrade
|
||||
`Handler` (see below) is agreed upon, where the `Name` of a `Plan` corresponds to a
|
||||
specific `Handler`. Typically, a `Plan` is created through a governance proposal
|
||||
process, where if voted upon and passed, will be scheduled. The `Info` of a `Plan`
|
||||
may contain various metadata about the upgrade, typically application specific
|
||||
upgrade info to be included on-chain such as a git commit that validators could
|
||||
automatically upgrade to.
|
||||
|
||||
### Sidecar Process
|
||||
|
||||
If an operator running the application binary also runs a sidecar process to assist
|
||||
in the automatic download and upgrade of a binary, the `Info` allows this process to
|
||||
be seamless. Namely, the `x/upgrade` module fulfills the
|
||||
[cosmosd Upgradeable Binary Specification](https://github.com/regen-network/cosmosd#upgradeable-binary-specification)
|
||||
specification and `cosmosd` can optionally be used to fully automate the upgrade
|
||||
process for node operators. By populating the `Info` field with the necessary information,
|
||||
binaries can automatically be downloaded. See [here](https://github.com/regen-network/cosmosd#auto-download)
|
||||
for more info.
|
||||
|
||||
```go
|
||||
type Plan struct {
|
||||
Name string
|
||||
Time Time
|
||||
Height int64
|
||||
Info string
|
||||
}
|
||||
```
|
||||
|
||||
## Handler
|
||||
|
||||
The `x/upgrade` module facilitates upgrading from major version X to major version Y. To
|
||||
accomplish this, node operators must first upgrade their current binary to a new
|
||||
binary that has a corresponding `Handler` for the new version Y. It is assumed that
|
||||
this version has fully been tested and approved by the community at large. This
|
||||
`Handler` defines what state migrations need to occur before the new binary Y
|
||||
can successfully run the chain. Naturally, this `Handler` is application specific
|
||||
and not defined on a per-module basis. Registering a `Handler` is done via
|
||||
`Keeper#SetUpgradeHandler` in the application.
|
||||
|
||||
```go
|
||||
type UpgradeHandler func(Context, Plan)
|
||||
```
|
||||
|
||||
During each `EndBlock` execution, the `x/upgrade` module checks if there exists a
|
||||
`Plan` that should execute (is scheduled at that time or height). If so, the corresponding
|
||||
`Handler` is executed. If the `Plan` is expected to execute but no `Handler` is registered
|
||||
or if the binary was upgraded too early, the node will gracefully panic and exit.
|
||||
|
||||
## Proposal
|
||||
|
||||
Typically, a `Plan` is proposed and submitted through governance via a `SoftwareUpgradeProposal`.
|
||||
This proposal prescribes to the standard governance process. If the proposal passes,
|
||||
the `Plan`, which targets a specific `Handler`, is persisted and scheduled. The
|
||||
upgrade can be delayed or hastened by updating the `Plan.Time` in a new proposal.
|
||||
|
||||
```go
|
||||
type SoftwareUpgradeProposal struct {
|
||||
Title string
|
||||
Description string
|
||||
Plan Plan
|
||||
}
|
||||
```
|
||||
|
||||
### Cancelling Upgrade Proposals
|
||||
|
||||
Upgrade proposals can be cancelled. There exists a `CancelSoftwareUpgrade` proposal
|
||||
type, which can be voted on and passed and will remove the scheduled upgrade `Plan`.
|
||||
Of course this requires that the upgrade was known to be a bad idea well before the
|
||||
upgrade itself, to allow time for a vote.
|
||||
|
||||
If such a possibility is desired, the upgrade height is to be
|
||||
`2 * (VotingPeriod + DepositPeriod) + (SafetyDelta)` from the beginning of the
|
||||
upgrade proposal. The `SafetyDelta` is the time available from the success of an
|
||||
upgrade proposal and the realization it was a bad idea (due to external social consensus).
|
||||
|
||||
A `CancelSoftwareUpgrade` proposal can also be made while the original
|
||||
`SoftwareUpgradeProposal` is still being voted upon, as long as the `VotingPeriod`
|
||||
ends after the `SoftwareUpgradeProposal`.
|
|
@ -0,0 +1,7 @@
|
|||
# State
|
||||
|
||||
The internal state of the `x/upgrade` module is relatively minimal and simple. The
|
||||
state only contains the currently active upgrade `Plan` (if one exists) by key
|
||||
`0x0` and if a `Plan` is marked as "done" by key `0x1`.
|
||||
|
||||
The `x/upgrade` module contains no genesis state.
|
|
@ -0,0 +1,4 @@
|
|||
# Events
|
||||
|
||||
The `x/upgrade` does not emit any events by itself. Any and all proposal related
|
||||
events are emitted through the `x/gov` module.
|
|
@ -0,0 +1,20 @@
|
|||
# Upgrade Module Specification
|
||||
|
||||
## Abstract
|
||||
|
||||
`x/upgrade` is an implementation of a Cosmos SDK module that facilitates smoothly
|
||||
upgrading a live Cosmos chain to a new (breaking) software version. It accomplishes this by
|
||||
providing a `BeginBlocker` hook that prevents the blockchain state machine from
|
||||
proceeding once a pre-defined upgrade block time or height has been reached.
|
||||
|
||||
The module does not prescribe anything regarding how governance decides to do an
|
||||
upgrade, but just the mechanism for coordinating the upgrade safely. Without software
|
||||
support for upgrades, upgrading a live chain is risky because all of the validators
|
||||
need to pause their state machines at exactly the same point in the process. If
|
||||
this is not done correctly, there can be state inconsistencies which are hard to
|
||||
recover from.
|
||||
|
||||
<!-- TOC -->
|
||||
1. **[Concepts](01_concepts.md)**
|
||||
2. **[State](02_state.md)**
|
||||
3. **[Events](03_events.md)**
|
Loading…
Reference in New Issue