114 lines
4.0 KiB
Go
114 lines
4.0 KiB
Go
package keeper
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"cosmossdk.io/x/upgrade/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/telemetry"
|
|
)
|
|
|
|
// PreBlocker will check if there is a scheduled plan and if it is ready to be executed.
|
|
// If the current height is in the provided set of heights to skip, it will skip and clear the upgrade plan.
|
|
// 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 purpose is to ensure the binary is switched EXACTLY at the desired block, and to allow
|
|
// a migration to be executed if needed upon this switch (migration defined in the new binary)
|
|
// skipUpgradeHeightArray is a set of block heights for which the upgrade must be skipped
|
|
func (k Keeper) PreBlocker(ctx context.Context) error {
|
|
start := telemetry.Now()
|
|
defer telemetry.ModuleMeasureSince(types.ModuleName, start, telemetry.MetricKeyBeginBlocker)
|
|
|
|
blockHeight := k.HeaderService.HeaderInfo(ctx).Height
|
|
plan, err := k.GetUpgradePlan(ctx)
|
|
if err != nil && !errors.Is(err, types.ErrNoUpgradePlanFound) {
|
|
return err
|
|
}
|
|
found := err == nil
|
|
|
|
if !k.DowngradeVerified() {
|
|
k.SetDowngradeVerified(true)
|
|
// This check will make sure that we are using a valid binary.
|
|
// It'll panic in these cases if there is no upgrade handler registered for the last applied upgrade.
|
|
// 1. If there is no scheduled upgrade.
|
|
// 2. If the plan is not ready.
|
|
// 3. If the plan is ready and skip upgrade height is set for current height.
|
|
if !found || !plan.ShouldExecute(blockHeight) || (plan.ShouldExecute(blockHeight) && k.IsSkipHeight(blockHeight)) {
|
|
lastAppliedPlan, _, err := k.GetLastCompletedUpgrade(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if lastAppliedPlan != "" && !k.HasHandler(lastAppliedPlan) {
|
|
appVersion, err := k.consensusKeeper.AppVersion(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return fmt.Errorf("wrong app version %d, upgrade handler is missing for %s upgrade plan", appVersion, lastAppliedPlan)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return nil
|
|
}
|
|
|
|
// To make sure clear upgrade is executed at the same block
|
|
if plan.ShouldExecute(blockHeight) {
|
|
// If skip upgrade has been set for current height, we clear the upgrade plan
|
|
if k.IsSkipHeight(blockHeight) {
|
|
skipUpgradeMsg := fmt.Sprintf("UPGRADE \"%s\" SKIPPED at %d: %s", plan.Name, plan.Height, plan.Info)
|
|
k.Logger.Info(skipUpgradeMsg)
|
|
|
|
// Clear the upgrade plan at current height
|
|
if err := k.ClearUpgradePlan(ctx); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Prepare shutdown if we don't have an upgrade handler for this upgrade name (meaning this software is out of date)
|
|
if !k.HasHandler(plan.Name) {
|
|
// Write the upgrade info to disk. The UpgradeStoreLoader uses this info to perform or skip
|
|
// store migrations.
|
|
err := k.DumpUpgradeInfoToDisk(blockHeight, plan)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to write upgrade info to filesystem: %w", err)
|
|
}
|
|
|
|
upgradeMsg := BuildUpgradeNeededMsg(plan)
|
|
k.Logger.Error(upgradeMsg)
|
|
|
|
// Returning an error will end up in a panic
|
|
return errors.New(upgradeMsg)
|
|
}
|
|
|
|
// We have an upgrade handler for this upgrade name, so apply the upgrade
|
|
k.Logger.Info(fmt.Sprintf("applying upgrade \"%s\" at %s", plan.Name, plan.DueAt()))
|
|
if err := k.ApplyUpgrade(ctx, plan); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// if we have a pending upgrade, but it is not yet time, make sure we did not
|
|
// set the handler already
|
|
if k.HasHandler(plan.Name) {
|
|
downgradeMsg := fmt.Sprintf("BINARY UPDATED BEFORE TRIGGER! UPGRADE \"%s\" - in binary but not executed on chain. Downgrade your binary", plan.Name)
|
|
k.Logger.Error(downgradeMsg)
|
|
|
|
// Returning an error will end up in a panic
|
|
return errors.New(downgradeMsg)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// BuildUpgradeNeededMsg prints the message that notifies that an upgrade is needed.
|
|
func BuildUpgradeNeededMsg(plan types.Plan) string {
|
|
return fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info)
|
|
}
|