mukan-ibc/modules/core/02-client/keeper/client.go
Mukan Erkin Törük 88dd97a9f8
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
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

152 lines
6.3 KiB
Go

package keeper
import (
errorsmod "cosmossdk.io/errors"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
"git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
"git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
"git.cw.tr/mukan-network/mukan-ibc/modules/core/internal/telemetry"
)
// CreateClient generates a new client identifier and invokes the associated light client module in order to
// initialize a new client. An isolated prefixed store will be reserved for this client using the generated
// client identifier. The light client module is responsible for setting any client-specific data in the store
// via the Initialize method. This includes the client state, initial consensus state and any associated
// metadata. The generated client identifier will be returned if a client was successfully initialized.
func (k *Keeper) CreateClient(ctx sdk.Context, clientType string, clientState, consensusState []byte) (string, error) {
if clientType == exported.Localhost {
return "", errorsmod.Wrapf(types.ErrInvalidClientType, "cannot create client of type: %s", clientType)
}
clientID := k.GenerateClientIdentifier(ctx, clientType)
clientModule, err := k.Route(ctx, clientID)
if err != nil {
return "", err
}
if err := clientModule.Initialize(ctx, clientID, clientState, consensusState); err != nil {
return "", err
}
if status := clientModule.Status(ctx, clientID); status != exported.Active {
return "", errorsmod.Wrapf(types.ErrClientNotActive, "cannot create client (%s) with status %s", clientID, status)
}
initialHeight := clientModule.LatestHeight(ctx, clientID)
k.Logger(ctx).Info("client created at height", "client-id", clientID, "height", initialHeight.String())
defer telemetry.ReportCreateClient(clientType)
emitCreateClientEvent(ctx, clientID, clientType, initialHeight)
return clientID, nil
}
// UpdateClient updates the consensus state and the state root from a provided header.
func (k *Keeper) UpdateClient(ctx sdk.Context, clientID string, clientMsg exported.ClientMessage) 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 update client (%s) with status %s", clientID, status)
}
if err := clientModule.VerifyClientMessage(ctx, clientID, clientMsg); err != nil {
return err
}
foundMisbehaviour := clientModule.CheckForMisbehaviour(ctx, clientID, clientMsg)
if foundMisbehaviour {
clientModule.UpdateStateOnMisbehaviour(ctx, clientID, clientMsg)
k.Logger(ctx).Info("client frozen due to misbehaviour", "client-id", clientID)
clientType := types.MustParseClientIdentifier(clientID)
defer telemetry.ReportUpdateClient(foundMisbehaviour, clientType, clientID)
emitSubmitMisbehaviourEvent(ctx, clientID, clientType)
return nil
}
consensusHeights := clientModule.UpdateState(ctx, clientID, clientMsg)
k.Logger(ctx).Info("client state updated", "client-id", clientID, "heights", consensusHeights)
clientType := types.MustParseClientIdentifier(clientID)
defer telemetry.ReportUpdateClient(foundMisbehaviour, clientType, clientID)
emitUpdateClientEvent(ctx, clientID, clientType, consensusHeights, k.cdc, clientMsg)
return nil
}
// UpgradeClient upgrades the client to a new client state if this new client was committed to
// by the old client at the specified upgrade height
func (k *Keeper) UpgradeClient(
ctx sdk.Context,
clientID string,
upgradedClient, upgradedConsState, upgradeClientProof, upgradeConsensusStateProof []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 upgrade client (%s) with status %s", clientID, status)
}
if err := clientModule.VerifyUpgradeAndUpdateState(ctx, clientID, upgradedClient, upgradedConsState, upgradeClientProof, upgradeConsensusStateProof); err != nil {
return errorsmod.Wrapf(err, "cannot upgrade client with ID %s", clientID)
}
latestHeight := clientModule.LatestHeight(ctx, clientID)
k.Logger(ctx).Info("client state upgraded", "client-id", clientID, "height", latestHeight.String())
clientType := types.MustParseClientIdentifier(clientID)
defer telemetry.ReportUpgradeClient(clientType, clientID)
emitUpgradeClientEvent(ctx, clientID, clientType, latestHeight)
return nil
}
// RecoverClient will invoke the light client module associated with the subject clientID requesting it to
// recover the subject client given a substitute client identifier. The light client implementation
// is responsible for validating the parameters of the substitute (ensuring they match the subject's parameters)
// as well as copying the necessary consensus states from the substitute to the subject client store.
// The substitute must be Active and the subject must not be Active.
func (k *Keeper) RecoverClient(ctx sdk.Context, subjectClientID, substituteClientID string) error {
clientModule, err := k.Route(ctx, subjectClientID)
if err != nil {
return errorsmod.Wrap(types.ErrRouteNotFound, subjectClientID)
}
if status := clientModule.Status(ctx, subjectClientID); status == exported.Active {
return errorsmod.Wrapf(types.ErrInvalidRecoveryClient, "cannot recover subject client (%s) with status %s", subjectClientID, status)
}
if status := clientModule.Status(ctx, substituteClientID); status != exported.Active {
return errorsmod.Wrapf(types.ErrClientNotActive, "cannot recover client using substitute client (%s) with status %s", substituteClientID, status)
}
subjectLatestHeight := clientModule.LatestHeight(ctx, subjectClientID)
substituteLatestHeight := clientModule.LatestHeight(ctx, substituteClientID)
if subjectLatestHeight.GTE(substituteLatestHeight) {
return errorsmod.Wrapf(types.ErrInvalidHeight, "subject client state latest height is greater or equal to substitute client state latest height (%s >= %s)", subjectLatestHeight, substituteLatestHeight)
}
if err := clientModule.RecoverClient(ctx, subjectClientID, substituteClientID); err != nil {
return err
}
k.Logger(ctx).Info("client recovered", "client-id", subjectClientID)
clientType := types.MustParseClientIdentifier(subjectClientID)
defer telemetry.ReportRecoverClient(clientType, subjectClientID)
emitRecoverClientEvent(ctx, subjectClientID, clientType)
return nil
}