Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
517 lines
19 KiB
Go
517 lines
19 KiB
Go
package keeper
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
corestore "cosmossdk.io/core/store"
|
|
errorsmod "cosmossdk.io/errors"
|
|
"cosmossdk.io/log"
|
|
"cosmossdk.io/store/prefix"
|
|
storetypes "cosmossdk.io/store/types"
|
|
upgradetypes "cosmossdk.io/x/upgrade/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-sdk/codec"
|
|
"git.cw.tr/mukan-network/mukan-sdk/runtime"
|
|
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
|
|
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
|
|
host "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host"
|
|
"git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
|
|
ibctm "git.cw.tr/mukan-network/mukan-ibc/modules/light-clients/07-tendermint"
|
|
localhost "git.cw.tr/mukan-network/mukan-ibc/modules/light-clients/09-localhost"
|
|
)
|
|
|
|
// Keeper represents a type that grants read and write permissions to any client
|
|
// state information
|
|
type Keeper struct {
|
|
storeService corestore.KVStoreService
|
|
cdc codec.BinaryCodec
|
|
router *types.Router
|
|
legacySubspace types.ParamSubspace
|
|
upgradeKeeper types.UpgradeKeeper
|
|
}
|
|
|
|
// NewKeeper creates a new NewKeeper instance
|
|
func NewKeeper(cdc codec.BinaryCodec, storeService corestore.KVStoreService, legacySubspace types.ParamSubspace, uk types.UpgradeKeeper) *Keeper {
|
|
router := types.NewRouter()
|
|
localhostModule := localhost.NewLightClientModule(cdc, storeService)
|
|
router.AddRoute(exported.Localhost, localhostModule)
|
|
|
|
return &Keeper{
|
|
storeService: storeService,
|
|
cdc: cdc,
|
|
router: router,
|
|
legacySubspace: legacySubspace,
|
|
upgradeKeeper: uk,
|
|
}
|
|
}
|
|
|
|
// Codec returns the IBC Client module codec.
|
|
func (k *Keeper) Codec() codec.BinaryCodec {
|
|
return k.cdc
|
|
}
|
|
|
|
// Logger returns a module-specific logger.
|
|
func (Keeper) Logger(ctx sdk.Context) log.Logger {
|
|
return ctx.Logger().With("module", "x/"+exported.ModuleName+"/"+types.SubModuleName)
|
|
}
|
|
|
|
// AddRoute adds a new route to the underlying router.
|
|
func (k *Keeper) AddRoute(clientType string, module exported.LightClientModule) {
|
|
k.router.AddRoute(clientType, module)
|
|
}
|
|
|
|
// GetStoreProvider returns the light client store provider.
|
|
func (k *Keeper) GetStoreProvider() types.StoreProvider {
|
|
return types.NewStoreProvider(k.storeService)
|
|
}
|
|
|
|
// Route returns the light client module for the given client identifier.
|
|
func (k *Keeper) Route(ctx sdk.Context, clientID string) (exported.LightClientModule, error) {
|
|
clientType, _, err := types.ParseClientIdentifier(clientID)
|
|
if err != nil {
|
|
return nil, errorsmod.Wrapf(err, "unable to parse client identifier %s", clientID)
|
|
}
|
|
|
|
if !k.GetParams(ctx).IsAllowedClient(clientType) {
|
|
return nil, errorsmod.Wrapf(
|
|
types.ErrInvalidClientType,
|
|
"client (%s) type %s is not in the allowed client list", clientID, clientType,
|
|
)
|
|
}
|
|
|
|
clientModule, found := k.router.GetRoute(clientType)
|
|
if !found {
|
|
return nil, errorsmod.Wrap(types.ErrRouteNotFound, clientID)
|
|
}
|
|
|
|
return clientModule, nil
|
|
}
|
|
|
|
// GenerateClientIdentifier returns the next client identifier.
|
|
func (k *Keeper) GenerateClientIdentifier(ctx sdk.Context, clientType string) string {
|
|
nextClientSeq := k.GetNextClientSequence(ctx)
|
|
clientID := types.FormatClientIdentifier(clientType, nextClientSeq)
|
|
|
|
nextClientSeq++
|
|
k.SetNextClientSequence(ctx, nextClientSeq)
|
|
return clientID
|
|
}
|
|
|
|
// GetClientState gets a particular client from the store
|
|
func (k *Keeper) GetClientState(ctx sdk.Context, clientID string) (exported.ClientState, bool) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
bz := store.Get(host.ClientStateKey())
|
|
if len(bz) == 0 {
|
|
return nil, false
|
|
}
|
|
|
|
clientState := types.MustUnmarshalClientState(k.cdc, bz)
|
|
return clientState, true
|
|
}
|
|
|
|
// SetClientState sets a particular Client to the store
|
|
func (k *Keeper) SetClientState(ctx sdk.Context, clientID string, clientState exported.ClientState) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
store.Set(host.ClientStateKey(), types.MustMarshalClientState(k.cdc, clientState))
|
|
}
|
|
|
|
// GetClientCreator returns the creator of a client
|
|
func (k *Keeper) GetClientCreator(ctx sdk.Context, clientID string) sdk.AccAddress {
|
|
store := k.ClientStore(ctx, clientID)
|
|
bz := store.Get(types.CreatorKey())
|
|
if len(bz) == 0 {
|
|
return nil
|
|
}
|
|
return sdk.AccAddress(bz)
|
|
}
|
|
|
|
// SetClientCreator sets the creator of a client
|
|
func (k *Keeper) SetClientCreator(ctx sdk.Context, clientID string, creator sdk.AccAddress) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
store.Set(types.CreatorKey(), creator.Bytes())
|
|
}
|
|
|
|
// DeleteClientCreator deletes the creator of a client
|
|
func (k *Keeper) DeleteClientCreator(ctx sdk.Context, clientID string) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
store.Delete(types.CreatorKey())
|
|
}
|
|
|
|
// GetClientConsensusState gets the stored consensus state from a client at a given height.
|
|
func (k *Keeper) GetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) (exported.ConsensusState, bool) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
bz := store.Get(host.ConsensusStateKey(height))
|
|
if len(bz) == 0 {
|
|
return nil, false
|
|
}
|
|
|
|
consensusState := types.MustUnmarshalConsensusState(k.cdc, bz)
|
|
return consensusState, true
|
|
}
|
|
|
|
// SetClientConsensusState sets a ConsensusState to a particular client at the given
|
|
// height
|
|
func (k *Keeper) SetClientConsensusState(ctx sdk.Context, clientID string, height exported.Height, consensusState exported.ConsensusState) {
|
|
store := k.ClientStore(ctx, clientID)
|
|
store.Set(host.ConsensusStateKey(height), types.MustMarshalConsensusState(k.cdc, consensusState))
|
|
}
|
|
|
|
// GetNextClientSequence gets the next client sequence from the store.
|
|
func (k *Keeper) GetNextClientSequence(ctx sdk.Context) uint64 {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz, err := store.Get([]byte(types.KeyNextClientSequence))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if len(bz) == 0 {
|
|
panic(errors.New("next client sequence is nil"))
|
|
}
|
|
|
|
return sdk.BigEndianToUint64(bz)
|
|
}
|
|
|
|
// SetNextClientSequence sets the next client sequence to the store.
|
|
func (k *Keeper) SetNextClientSequence(ctx sdk.Context, sequence uint64) {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz := sdk.Uint64ToBigEndian(sequence)
|
|
if err := store.Set([]byte(types.KeyNextClientSequence), bz); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// IterateConsensusStates provides an iterator over all stored consensus states.
|
|
// objects. For each State object, cb will be called. If the cb returns true,
|
|
// the iterator will close and stop.
|
|
func (k *Keeper) IterateConsensusStates(ctx sdk.Context, cb func(clientID string, cs types.ConsensusStateWithHeight) bool) {
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
iterator := storetypes.KVStorePrefixIterator(store, host.KeyClientStorePrefix)
|
|
|
|
defer sdk.LogDeferred(k.Logger(ctx), func() error { return iterator.Close() })
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
keySplit := strings.Split(string(iterator.Key()), "/")
|
|
// consensus key is in the format "clients/<clientID>/consensusStates/<height>"
|
|
if len(keySplit) != 4 || keySplit[2] != string(host.KeyConsensusStatePrefix) {
|
|
continue
|
|
}
|
|
clientID := keySplit[1]
|
|
height := types.MustParseHeight(keySplit[3])
|
|
consensusState := types.MustUnmarshalConsensusState(k.cdc, iterator.Value())
|
|
|
|
consensusStateWithHeight := types.NewConsensusStateWithHeight(height, consensusState)
|
|
|
|
if cb(clientID, consensusStateWithHeight) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// iterateMetadata provides an iterator over all stored metadata keys in the client store.
|
|
// For each metadata object, it will perform a callback.
|
|
func (k *Keeper) iterateMetadata(ctx sdk.Context, cb func(clientID string, key, value []byte) bool) {
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
iterator := storetypes.KVStorePrefixIterator(store, host.KeyClientStorePrefix)
|
|
|
|
defer sdk.LogDeferred(k.Logger(ctx), func() error { return iterator.Close() })
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
split := strings.Split(string(iterator.Key()), "/")
|
|
if len(split) == 3 && split[2] == string(host.KeyClientState) {
|
|
// skip client state keys
|
|
continue
|
|
}
|
|
|
|
if len(split) == 4 && split[2] == string(host.KeyConsensusStatePrefix) {
|
|
// skip consensus state keys
|
|
continue
|
|
}
|
|
|
|
if split[0] != string(host.KeyClientStorePrefix) {
|
|
panic(errorsmod.Wrapf(host.ErrInvalidPath, "path does not begin with client store prefix: expected %s, got %s", host.KeyClientStorePrefix, split[0]))
|
|
}
|
|
if strings.TrimSpace(split[1]) == "" {
|
|
panic(errorsmod.Wrap(host.ErrInvalidPath, "clientID is empty"))
|
|
}
|
|
|
|
clientID := split[1]
|
|
|
|
key := []byte(strings.Join(split[2:], "/"))
|
|
|
|
if cb(clientID, key, iterator.Value()) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetAllGenesisClients returns all the clients in state with their client ids returned as IdentifiedClientState
|
|
func (k *Keeper) GetAllGenesisClients(ctx sdk.Context) types.IdentifiedClientStates {
|
|
var genClients types.IdentifiedClientStates
|
|
k.IterateClientStates(ctx, nil, func(clientID string, cs exported.ClientState) bool {
|
|
genClients = append(genClients, types.NewIdentifiedClientState(clientID, cs))
|
|
return false
|
|
})
|
|
|
|
return genClients.Sort()
|
|
}
|
|
|
|
// GetAllClientMetadata will take a list of IdentifiedClientState and return a list
|
|
// of IdentifiedGenesisMetadata necessary for exporting and importing client metadata
|
|
// into the client store.
|
|
func (k *Keeper) GetAllClientMetadata(ctx sdk.Context, genClients []types.IdentifiedClientState) ([]types.IdentifiedGenesisMetadata, error) {
|
|
metadataMap := make(map[string][]types.GenesisMetadata)
|
|
k.iterateMetadata(ctx, func(clientID string, key, value []byte) bool {
|
|
metadataMap[clientID] = append(metadataMap[clientID], types.NewGenesisMetadata(key, value))
|
|
return false
|
|
})
|
|
|
|
genMetadata := make([]types.IdentifiedGenesisMetadata, 0)
|
|
for _, ic := range genClients {
|
|
metadata := metadataMap[ic.ClientId]
|
|
if len(metadata) != 0 {
|
|
genMetadata = append(genMetadata, types.NewIdentifiedGenesisMetadata(
|
|
ic.ClientId,
|
|
metadata,
|
|
))
|
|
}
|
|
}
|
|
|
|
return genMetadata, nil
|
|
}
|
|
|
|
// SetAllClientMetadata takes a list of IdentifiedGenesisMetadata and stores all of the metadata in the client store at the appropriate paths.
|
|
func (k *Keeper) SetAllClientMetadata(ctx sdk.Context, genMetadata []types.IdentifiedGenesisMetadata) {
|
|
for _, igm := range genMetadata {
|
|
// create client store
|
|
store := k.ClientStore(ctx, igm.ClientId)
|
|
// set all metadata kv pairs in client store
|
|
for _, md := range igm.ClientMetadata {
|
|
store.Set(md.GetKey(), md.GetValue())
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetAllConsensusStates returns all stored client consensus states.
|
|
func (k *Keeper) GetAllConsensusStates(ctx sdk.Context) types.ClientsConsensusStates {
|
|
clientConsStates := make(types.ClientsConsensusStates, 0)
|
|
mapClientIDToConsStateIdx := make(map[string]int)
|
|
|
|
k.IterateConsensusStates(ctx, func(clientID string, cs types.ConsensusStateWithHeight) bool {
|
|
idx, ok := mapClientIDToConsStateIdx[clientID]
|
|
if ok {
|
|
clientConsStates[idx].ConsensusStates = append(clientConsStates[idx].ConsensusStates, cs)
|
|
return false
|
|
}
|
|
|
|
clientConsState := types.ClientConsensusStates{
|
|
ClientId: clientID,
|
|
ConsensusStates: []types.ConsensusStateWithHeight{cs},
|
|
}
|
|
|
|
clientConsStates = append(clientConsStates, clientConsState)
|
|
mapClientIDToConsStateIdx[clientID] = len(clientConsStates) - 1
|
|
return false
|
|
})
|
|
|
|
return clientConsStates.Sort()
|
|
}
|
|
|
|
// HasClientConsensusState returns if keeper has a ConsensusState for a particular
|
|
// client at the given height
|
|
func (k *Keeper) HasClientConsensusState(ctx sdk.Context, clientID string, height exported.Height) bool {
|
|
store := k.ClientStore(ctx, clientID)
|
|
return store.Has(host.ConsensusStateKey(height))
|
|
}
|
|
|
|
// GetLatestClientConsensusState gets the latest ConsensusState stored for a given client
|
|
func (k *Keeper) GetLatestClientConsensusState(ctx sdk.Context, clientID string) (exported.ConsensusState, bool) {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
|
|
return k.GetClientConsensusState(ctx, clientID, clientModule.LatestHeight(ctx, clientID))
|
|
}
|
|
|
|
// VerifyMembership retrieves the light client module for the clientID and verifies the proof of the existence of a key-value pair at a specified height.
|
|
func (k *Keeper) VerifyMembership(ctx sdk.Context, clientID string, height exported.Height, delayTimePeriod uint64, delayBlockPeriod uint64, proof []byte, path exported.Path, value []byte) error {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if status := clientModule.Status(ctx, clientID); status != exported.Active {
|
|
return errorsmod.Wrapf(types.ErrClientNotActive, "cannot call verify membership on client (%s) with status %s", clientID, status)
|
|
}
|
|
|
|
return clientModule.VerifyMembership(ctx, clientID, height, delayTimePeriod, delayBlockPeriod, proof, path, value)
|
|
}
|
|
|
|
// VerifyNonMembership retrieves the light client module for the clientID and verifies the absence of a given key at a specified height.
|
|
func (k *Keeper) VerifyNonMembership(ctx sdk.Context, clientID string, height exported.Height, delayTimePeriod uint64, delayBlockPeriod uint64, proof []byte, path exported.Path) error {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if status := clientModule.Status(ctx, clientID); status != exported.Active {
|
|
return errorsmod.Wrapf(types.ErrClientNotActive, "cannot call verify non membership on client (%s) with status %s", clientID, status)
|
|
}
|
|
|
|
return clientModule.VerifyNonMembership(ctx, clientID, height, delayTimePeriod, delayBlockPeriod, proof, path)
|
|
}
|
|
|
|
// GetUpgradePlan executes the upgrade keeper GetUpgradePlan function.
|
|
func (k *Keeper) GetUpgradePlan(ctx sdk.Context) (upgradetypes.Plan, error) {
|
|
return k.upgradeKeeper.GetUpgradePlan(ctx)
|
|
}
|
|
|
|
// GetUpgradedClient executes the upgrade keeper GetUpgradeClient function.
|
|
func (k *Keeper) GetUpgradedClient(ctx sdk.Context, planHeight int64) ([]byte, error) {
|
|
return k.upgradeKeeper.GetUpgradedClient(ctx, planHeight)
|
|
}
|
|
|
|
// GetUpgradedConsensusState returns the upgraded consensus state
|
|
func (k *Keeper) GetUpgradedConsensusState(ctx sdk.Context, planHeight int64) ([]byte, error) {
|
|
return k.upgradeKeeper.GetUpgradedConsensusState(ctx, planHeight)
|
|
}
|
|
|
|
// SetUpgradedConsensusState executes the upgrade keeper SetUpgradedConsensusState function.
|
|
func (k *Keeper) SetUpgradedConsensusState(ctx sdk.Context, planHeight int64, bz []byte) error {
|
|
return k.upgradeKeeper.SetUpgradedConsensusState(ctx, planHeight, bz)
|
|
}
|
|
|
|
// IterateClientStates provides an iterator over all stored ibc ClientState
|
|
// objects using the provided store prefix. For each ClientState object, cb will be called. If the cb returns true,
|
|
// the iterator will close and stop.
|
|
func (k *Keeper) IterateClientStates(ctx sdk.Context, storePrefix []byte, cb func(clientID string, cs exported.ClientState) bool) {
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
iterator := storetypes.KVStorePrefixIterator(store, host.PrefixedClientStoreKey(storePrefix))
|
|
|
|
defer sdk.LogDeferred(k.Logger(ctx), func() error { return iterator.Close() })
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
path := string(iterator.Key())
|
|
if !strings.Contains(path, host.KeyClientState) {
|
|
// skip non client state keys
|
|
continue
|
|
}
|
|
|
|
clientID := host.MustParseClientStatePath(path)
|
|
clientState := types.MustUnmarshalClientState(k.cdc, iterator.Value())
|
|
|
|
if cb(clientID, clientState) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetAllClients returns all stored light client State objects.
|
|
func (k *Keeper) GetAllClients(ctx sdk.Context) []exported.ClientState {
|
|
var states []exported.ClientState
|
|
k.IterateClientStates(ctx, nil, func(_ string, state exported.ClientState) bool {
|
|
states = append(states, state)
|
|
return false
|
|
})
|
|
|
|
return states
|
|
}
|
|
|
|
// ClientStore returns isolated prefix store for each client so they can read/write in separate
|
|
// namespace without being able to read/write other client's data
|
|
func (k *Keeper) ClientStore(ctx sdk.Context, clientID string) storetypes.KVStore {
|
|
clientPrefix := fmt.Appendf(nil, "%s/%s/", host.KeyClientStorePrefix, clientID)
|
|
store := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
|
|
return prefix.NewStore(store, clientPrefix)
|
|
}
|
|
|
|
// GetClientStatus returns the status for a client state given a client identifier. If the client type is not in the allowed
|
|
// clients param field, Unauthorized is returned, otherwise the client state status is returned.
|
|
func (k *Keeper) GetClientStatus(ctx sdk.Context, clientID string) exported.Status {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return exported.Unauthorized
|
|
}
|
|
|
|
return clientModule.Status(ctx, clientID)
|
|
}
|
|
|
|
// GetClientLatestHeight returns the latest height of a client state for a given client identifier. If the client type is not in the allowed
|
|
// clients param field, a zero value height is returned, otherwise the client state latest height is returned.
|
|
func (k *Keeper) GetClientLatestHeight(ctx sdk.Context, clientID string) types.Height {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return types.ZeroHeight()
|
|
}
|
|
|
|
var latestHeight types.Height
|
|
latestHeight, ok := clientModule.LatestHeight(ctx, clientID).(types.Height)
|
|
if !ok {
|
|
panic(fmt.Errorf("cannot convert %T to %T", clientModule.LatestHeight, latestHeight))
|
|
}
|
|
return latestHeight
|
|
}
|
|
|
|
// GetClientTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the given height.
|
|
func (k *Keeper) GetClientTimestampAtHeight(ctx sdk.Context, clientID string, height exported.Height) (uint64, error) {
|
|
clientModule, err := k.Route(ctx, clientID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return clientModule.TimestampAtHeight(ctx, clientID, height)
|
|
}
|
|
|
|
// GetParams returns the total set of ibc-client parameters.
|
|
func (k *Keeper) GetParams(ctx sdk.Context) types.Params {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz, err := store.Get([]byte(types.ParamsKey))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if bz == nil { // only panic on unset params and not on empty params
|
|
panic(errors.New("client params are not set in store"))
|
|
}
|
|
|
|
var params types.Params
|
|
k.cdc.MustUnmarshal(bz, ¶ms)
|
|
return params
|
|
}
|
|
|
|
// SetParams sets the total set of ibc-client parameters.
|
|
func (k *Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
|
store := k.storeService.OpenKVStore(ctx)
|
|
bz := k.cdc.MustMarshal(¶ms)
|
|
if err := store.Set([]byte(types.ParamsKey), bz); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// ScheduleIBCSoftwareUpgrade schedules an upgrade for the IBC client.
|
|
func (k *Keeper) ScheduleIBCSoftwareUpgrade(ctx sdk.Context, plan upgradetypes.Plan, upgradedClientState exported.ClientState) error {
|
|
// zero out any custom fields before setting
|
|
cs, ok := upgradedClientState.(*ibctm.ClientState)
|
|
if !ok {
|
|
return errorsmod.Wrapf(types.ErrInvalidClientType, "expected: %T, got: %T", &ibctm.ClientState{}, upgradedClientState)
|
|
}
|
|
|
|
cs = cs.ZeroCustomFields()
|
|
bz, err := types.MarshalClientState(k.cdc, cs)
|
|
if err != nil {
|
|
return errorsmod.Wrap(err, "could not marshal UpgradedClientState")
|
|
}
|
|
|
|
if err := k.upgradeKeeper.ScheduleUpgrade(ctx, plan); err != nil {
|
|
return err
|
|
}
|
|
|
|
// sets the new upgraded client last height committed on this chain at plan.Height,
|
|
// since the chain will panic at plan.Height and new chain will resume at plan.Height
|
|
if err = k.upgradeKeeper.SetUpgradedClient(ctx, plan.Height, bz); err != nil {
|
|
return err
|
|
}
|
|
|
|
// emitting an event for scheduling an upgrade plan
|
|
emitScheduleIBCSoftwareUpgradeEvent(ctx, plan.Name, plan.Height)
|
|
|
|
return nil
|
|
}
|