diff --git a/app/app.go b/app/app.go index 2e06ecf03..2fd1ed416 100644 --- a/app/app.go +++ b/app/app.go @@ -121,6 +121,7 @@ import ( v4_5 "github.com/notional-labs/centauri/v5/app/upgrades/v4_5" v4_5_1 "github.com/notional-labs/centauri/v5/app/upgrades/v4_5_1" v5_1_0 "github.com/notional-labs/centauri/v5/app/upgrades/v5_1_0" + v5_2_0 "github.com/notional-labs/centauri/v5/app/upgrades/v5_2_0" upgrades "github.com/notional-labs/centauri/v5/app/upgrades" ) @@ -141,7 +142,7 @@ var ( EnableSpecificProposals = "" Upgrades = []upgrades.Upgrade{v4.Upgrade, v5.Upgrade} - Forks = []upgrades.Fork{v4_5.Fork, v4_5_1.Fork, v5_1_0.Fork} + Forks = []upgrades.Fork{v4_5.Fork, v4_5_1.Fork, v5_1_0.Fork, v5_2_0.Fork} ) // GetEnabledProposals parses the ProposalsEnabled / EnableSpecificProposals values to diff --git a/app/upgrades/v5_2_0/constants.go b/app/upgrades/v5_2_0/constants.go new file mode 100644 index 000000000..8198d0e74 --- /dev/null +++ b/app/upgrades/v5_2_0/constants.go @@ -0,0 +1,18 @@ +package v5_2_0 + +import "github.com/notional-labs/centauri/v5/app/upgrades" + +const ( + // UpgradeName defines the on-chain upgrade name for the Composable v5 upgrade. + UpgradeName = "v5_2_0" + + // UpgradeHeight defines the block height at which the Composable v6 upgrade is + // triggered. + UpgradeHeight = 1769900 +) + +var Fork = upgrades.Fork{ + UpgradeName: UpgradeName, + UpgradeHeight: UpgradeHeight, + BeginForkLogic: RunForkLogic, +} diff --git a/app/upgrades/v5_2_0/fork.go b/app/upgrades/v5_2_0/fork.go new file mode 100644 index 000000000..4bed3bb71 --- /dev/null +++ b/app/upgrades/v5_2_0/fork.go @@ -0,0 +1,87 @@ +package v5_2_0 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + transfertypes "github.com/cosmos/ibc-go/v7/modules/apps/transfer/types" + clienttypes "github.com/cosmos/ibc-go/v7/modules/core/02-client/types" + "github.com/cosmos/ibc-go/v7/modules/core/exported" + ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" + wasm08types "github.com/cosmos/ibc-go/v7/modules/light-clients/08-wasm/types" + + "github.com/notional-labs/centauri/v5/app/keepers" +) + +const ( + newWasmCodeID = "ad84ee3292e28b4e46da16974c118d40093e1a6e28a083f2f045f68fde7fb575" + clientId = "08-wasm-05" + substituteClientId = "08-wasm-132" +) + +func RunForkLogic(ctx sdk.Context, keepers *keepers.AppKeepers) { + ctx.Logger().Info("Applying v5_2_0 upgrade" + + "Upgrade 08-wasm contract", + ) + + UpdateWasmContract(ctx, keepers.IBCKeeper) + + err := ClientUpdate(ctx, keepers.IBCKeeper.Codec(), keepers.IBCKeeper, clientId, substituteClientId) + if err != nil { + panic(err) + } +} + +func UpdateWasmContract(ctx sdk.Context, ibckeeper *ibckeeper.Keeper) { + unknownClientState, found := ibckeeper.ClientKeeper.GetClientState(ctx, clientId) + if !found { + panic("cannot update client with ID") + } + + clientState, ok := unknownClientState.(*wasm08types.ClientState) + if !ok { + panic("cannot update client with ID") + } + + code, err := transfertypes.ParseHexHash(newWasmCodeID) + if err != nil { + panic(err) + } + + clientState.CodeId = code + + ibckeeper.ClientKeeper.SetClientState(ctx, clientId, clientState) +} + +func ClientUpdate(ctx sdk.Context, codec codec.BinaryCodec, ibckeeper *ibckeeper.Keeper, subjectClientId string, substituteClientId string) error { + subjectClientState, found := ibckeeper.ClientKeeper.GetClientState(ctx, subjectClientId) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "subject client with ID %s", subjectClientId) + } + + subjectClientStore := ibckeeper.ClientKeeper.ClientStore(ctx, subjectClientId) + + substituteClientState, found := ibckeeper.ClientKeeper.GetClientState(ctx, substituteClientId) + if !found { + return sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "substitute client with ID %s", substituteClientId) + } + + if subjectClientState.GetLatestHeight().GTE(substituteClientState.GetLatestHeight()) { + return sdkerrors.Wrapf(clienttypes.ErrInvalidHeight, "subject client state latest height is greater or equal to substitute client state latest height (%s >= %s)", subjectClientState.GetLatestHeight(), substituteClientState.GetLatestHeight()) + } + + substituteClientStore := ibckeeper.ClientKeeper.ClientStore(ctx, substituteClientId) + + if status := ibckeeper.ClientKeeper.GetClientStatus(ctx, substituteClientState, substituteClientId); status != exported.Active { + return sdkerrors.Wrapf(clienttypes.ErrClientNotActive, "substitute client is not Active, status is %s", status) + } + + if err := subjectClientState.CheckSubstituteAndUpdateState(ctx, codec, subjectClientStore, substituteClientStore, substituteClientState); err != nil { + return err + } + + ctx.Logger().Info("client updated after hark fork passed", "client-id", subjectClientId) + + return nil +}