package upgrade import ( "fmt" "time" abci "github.com/tendermint/tendermint/abci/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" "github.com/cosmos/cosmos-sdk/x/upgrade/types" ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/light-clients/07-tendermint/types" ) // BeginBlock 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 BeginBlocker(k keeper.Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker) plan, found := k.GetUpgradePlan(ctx) if !found { return } // Once we are at the last block this chain will commit, set the upgraded consensus state // so that IBC clients can use the last NextValidatorsHash as a trusted kernel for verifying // headers on the next version of the chain. // Set the time to the last block time of the current chain. // In order for a client to upgrade successfully, the first block of the new chain must be committed // within the trusting period of the last block time on this chain. if plan.IsIBCPlan() && ctx.BlockHeight() == plan.Height-1 { upgradedConsState := &ibctmtypes.ConsensusState{ Timestamp: ctx.BlockTime(), NextValidatorsHash: ctx.BlockHeader().NextValidatorsHash, } k.SetUpgradedConsensusState(ctx, plan.Height, upgradedConsState) } // To make sure clear upgrade is executed at the same block if plan.ShouldExecute(ctx) { // If skip upgrade has been set for current height, we clear the upgrade plan if k.IsSkipHeight(ctx.BlockHeight()) { skipUpgradeMsg := fmt.Sprintf("UPGRADE \"%s\" SKIPPED at %d: %s", plan.Name, plan.Height, plan.Info) ctx.Logger().Info(skipUpgradeMsg) // Clear the upgrade plan at current height k.ClearUpgradePlan(ctx) return } if !k.HasHandler(plan.Name) { upgradeMsg := BuildUpgradeNeededMsg(plan) // We don't have an upgrade handler for this upgrade name, meaning this software is out of date so shutdown ctx.Logger().Error(upgradeMsg) // Write the upgrade info to disk. The UpgradeStoreLoader uses this info to perform or skip // store migrations. err := k.DumpUpgradeInfoToDisk(ctx.BlockHeight(), plan.Name) if err != nil { panic(fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error())) } 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 } // 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", plan.Name) ctx.Logger().Error(downgradeMsg) panic(downgradeMsg) } } // 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) }