diff --git a/app/app.go b/app/app.go index df4a3f6..4ea6060 100644 --- a/app/app.go +++ b/app/app.go @@ -479,6 +479,8 @@ func New( panic(err) } + app.applyUpgrades() + return app } @@ -655,3 +657,7 @@ func initParamsKeeper( return paramsKeeper } + +func (app *App) applyUpgrades() { + app.applyUpgrade_v0_1_2() +} diff --git a/app/upgrade_v0_1_2.go b/app/upgrade_v0_1_2.go new file mode 100644 index 0000000..fb5a9f2 --- /dev/null +++ b/app/upgrade_v0_1_2.go @@ -0,0 +1,102 @@ +// Copyright 2024 Galactica Network +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + + "github.com/Galactica-corp/galactica/app/upgrades/v0_1_2" +) + +// applyUpgrade_v0_1_2 checks and applies the upgrade plan if necessary. +func (app *App) applyUpgrade_v0_1_2() { + latestBlock := app.LastBlockHeight() + logger := app.Logger().With("upgrade", v0_1_2.UpgradeName) + + ctx, err := app.CreateQueryContext(latestBlock, false) + if err != nil { + logger.Error("Failed to create query context with block", "error", err, "block", latestBlock) + return + } + + plan, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk() + if err != nil || plan.Height < v0_1_2.UpgradeBlockHeight { + logger.Info("Applying upgrade plan", "info", plan.Info) + app.UpgradeKeeper.SetUpgradeHandler(v0_1_2.UpgradeName, app.upgradeHandler_v0_1_2()) + app.UpgradeKeeper.ApplyUpgrade(ctx, v0_1_2.Plan) + } +} + +// upgradeHandler_v0_1_2 returns a handler function for processing the upgrade. +func (app *App) upgradeHandler_v0_1_2() func( + ctx sdk.Context, + _ upgradetypes.Plan, + fromVM module.VersionMap, +) (module.VersionMap, error) { + return func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + logger := ctx.Logger().With("upgrade", plan.Name) + + if plan.Name != v0_1_2.UpgradeName { + logger.Error("Invalid upgrade plan", "expected", v0_1_2.UpgradeName, "got", plan.Name) + return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + } + + validators := app.StakingKeeper.GetAllValidators(ctx) + + for _, validator := range validators { + if err := app.updateValidatorPowerIndex(ctx, validator); err != nil { + logger.Error("Failed to update validator power index", "error", err, "validator", validator.OperatorAddress) + return nil, err + } + logger.Info("Validator power index updated", "validator", validator.OperatorAddress) + } + + logger.Info("All validators updated successfully.") + + if err := app.UpgradeKeeper.DumpUpgradeInfoToDisk(plan.Height, plan); err != nil { + logger.Error("Failed to dump upgrade info to disk", "error", err) + return nil, err + } + + return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM) + } +} + +// updateValidatorPowerIndex updates the power index for a single validator. +func (app *App) updateValidatorPowerIndex(ctx sdk.Context, validator stakingtypes.Validator) error { + store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey)) + iterator := sdk.KVStorePrefixIterator(store, stakingtypes.ValidatorsByPowerIndexKey) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + valAddr := stakingtypes.ParseValidatorPowerRankKey(iterator.Key()) + if bytes.Equal(valAddr, validator.GetOperator()) { + store.Delete(iterator.Key()) + break // Assuming unique power index key per validator. + } + } + + app.StakingKeeper.SetValidatorByPowerIndex(ctx, validator) + if _, err := app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx); err != nil { + return err + } + + return nil +} diff --git a/app/upgrades/v0_1_2/README.md b/app/upgrades/v0_1_2/README.md new file mode 100644 index 0000000..177393c --- /dev/null +++ b/app/upgrades/v0_1_2/README.md @@ -0,0 +1,36 @@ +# Upgrade to v0.1.2 + +## Guide for Upgrading from v0.1.1 to v0.1.2 + +1. Halt the currently running `galacticad` process. +2. **!!! Backup the data directory.** !!! +3. Fetch the latest updates from the repository and switch to the `v0.1.2` tag. + ```bash + git fetch --all --tags + git checkout v0.1.2 + ``` +4. Build the updated source code. + ```bash + make install + ``` +5. Execute the upgrade script located at ./scripts/upgrade_v0_1_2.sh + ```bash + ./scripts/upgrade_v0_1_2.sh + ``` + Ensure the following environment variable is defined: + - `GALACTICA_HOME`: Specifies the home directory for the `galacticad` node. + + The script executes the following operations: + - Updates the node storage to version v0.1.2. + - Roll back the state of the most recent block. + - Backup of the current priv_validator_state.json and replaces it with a new version with default settings. + + Alternatively, these steps can be performed manually: + ```bash + galacticad --home $GALACTICA_HOME rollback --hard + echo '{"height":"0","round":0,"step":0}' > $GALACTICA_HOME/data/priv_validator_state.json + ``` + Executing the rollback command will also automatically execute the storage upgrade. + +6. Restart the `galacticad` node. + diff --git a/app/upgrades/v0_1_2/upgrade.go b/app/upgrades/v0_1_2/upgrade.go new file mode 100644 index 0000000..3afecbe --- /dev/null +++ b/app/upgrades/v0_1_2/upgrade.go @@ -0,0 +1,32 @@ +// Copyright 2024 Galactica Network +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v0_1_2 + +import ( + upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" +) + +const ( + UpgradeName = "0.1.2" + UpgradeBlockHeight = 16951 +) + +// Plan defines the upgrade plan for addressing the staking PowerReduction issue. +var Plan = upgradetypes.Plan{ + Name: UpgradeName, + Height: UpgradeBlockHeight, + Info: "Addresses a critical staking PowerReduction issue by mutating validators' " + + "power for accurate voting power recalibration and network integrity.", +} diff --git a/cmd/galacticad/cmd/config.go b/cmd/galacticad/cmd/config.go index 6cca750..0eb94f5 100644 --- a/cmd/galacticad/cmd/config.go +++ b/cmd/galacticad/cmd/config.go @@ -15,6 +15,8 @@ package cmd import ( + "math/big" + "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" @@ -73,6 +75,10 @@ func initSDKConfig() { RegisterDenoms() + sdk.DefaultPowerReduction = sdk.NewIntFromBigInt( + new(big.Int).Exp(big.NewInt(10), big.NewInt(BaseDenomUnit), nil), + ) + config.Seal() } diff --git a/scripts/upgrade_v0_1_2.sh b/scripts/upgrade_v0_1_2.sh new file mode 100755 index 0000000..30747c4 --- /dev/null +++ b/scripts/upgrade_v0_1_2.sh @@ -0,0 +1,65 @@ +# Copyright 2024 Galactica Network +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#!/bin/bash + +# This script is used to prepare the upgrade from v0.1.1 to v0.1.2 + +GALACTICAD_VERSION=$(galacticad version) +if [[ $GALACTICAD_VERSION != *"0.1.2"* ]]; then + echo "Galactica version must be 0.1.2" + exit 1 +fi + +# check env var GALACTICA_HOME and if not exists exit +if [ -z "$GALACTICA_HOME" ]; then + echo "GALACTICA_HOME is not set, using default path" + GALACTICA_HOME="$HOME/.galactica" +fi + +# ask user to confirm: +echo "GALACTICA_HOME: $GALACTICA_HOME" +echo "\nThis script will perform the following actions:\n\ +- Backup the existing priv_validator_state.json and replace it with a new one containing default values\n\ +- Upgrade the node storage to v0.1.2\n\ +- Rollback the latest block state\n" + +if [ "$1" != "-y" ]; then + read -p "Do you want to continue? (y/n): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "User cancelled the script" + exit 1 + fi +fi + +PRIV_VAL_STATE=$GALACTICA_HOME/data/priv_validator_state.json + +UPGRADE_INFO_FILE=$GALACTICA_HOME/data/upgrade-info.json +if [ -f "$UPGRADE_INFO_FILE" ]; then + echo "upgrade v0.1.2 already applied" + exit 1 +fi + +if [ ! -f "$PRIV_VAL_STATE" ]; then + echo "priv_validator_state.json not found at $PRIV_VAL_STATE" + exit 1 +fi + +cp $PRIV_VAL_STATE $PRIV_VAL_STATE.bkp + +galacticad --home $GALACTICA_HOME rollback --hard + +echo '{"height":"0","round":0,"step":0}' > $PRIV_VAL_STATE +