mukan-ibc/modules/light-clients/09-localhost/light_client_module.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

181 lines
7.2 KiB
Go

package localhost
import (
"bytes"
corestore "cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"git.cw.tr/mukan-network/mukan-sdk/codec"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
clienttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/02-client/types"
commitmenttypes "git.cw.tr/mukan-network/mukan-ibc/modules/core/23-commitment/types"
commitmenttypesv2 "git.cw.tr/mukan-network/mukan-ibc/modules/core/23-commitment/types/v2"
host "git.cw.tr/mukan-network/mukan-ibc/modules/core/24-host"
ibcerrors "git.cw.tr/mukan-network/mukan-ibc/modules/core/errors"
"git.cw.tr/mukan-network/mukan-ibc/modules/core/exported"
)
const (
// ModuleName defines the 09-localhost light client module name
ModuleName = "09-localhost"
)
// SentinelProof defines the 09-localhost sentinel proof.
// Submission of nil or empty proofs is disallowed in core IBC messaging.
// This serves as a placeholder value for relayers to leverage as the proof field in various message types.
// Localhost client state verification will fail if the sentintel proof value is not provided.
var SentinelProof = []byte{0x01}
var _ exported.LightClientModule = (*LightClientModule)(nil)
// LightClientModule implements the core IBC api.LightClientModule interface.
type LightClientModule struct {
cdc codec.BinaryCodec
storeService corestore.KVStoreService
}
// NewLightClientModule creates and returns a new 09-localhost LightClientModule.
func NewLightClientModule(cdc codec.BinaryCodec, storeService corestore.KVStoreService) *LightClientModule {
return &LightClientModule{
cdc: cdc,
storeService: storeService,
}
}
// Initialize returns an error because it is stateless.
func (LightClientModule) Initialize(_ sdk.Context, _ string, _, _ []byte) error {
return errorsmod.Wrap(clienttypes.ErrClientExists, "localhost is stateless and cannot be initialized")
}
// VerifyClientMessage is unsupported by the 09-localhost client type and returns an error.
func (LightClientModule) VerifyClientMessage(_ sdk.Context, _ string, _ exported.ClientMessage) error {
return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "client message verification is unsupported by the localhost client")
}
// CheckForMisbehaviour is unsupported by the 09-localhost client type and performs a no-op, returning false.
func (LightClientModule) CheckForMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) bool {
return false
}
// UpdateStateOnMisbehaviour is unsupported by the 09-localhost client type and performs a no-op.
func (LightClientModule) UpdateStateOnMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) {
// no-op
}
// UpdateState performs a no-op and returns the context height in the updated heights return value.
func (LightClientModule) UpdateState(ctx sdk.Context, _ string, _ exported.ClientMessage) []exported.Height {
return []exported.Height{clienttypes.GetSelfHeight(ctx)}
}
// VerifyMembership is a generic proof verification method which verifies the existence of a given key and value within the IBC store.
// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
// The caller must provide the full IBC store.
func (l LightClientModule) VerifyMembership(
ctx sdk.Context,
clientID string,
height exported.Height,
delayTimePeriod uint64,
delayBlockPeriod uint64,
proof []byte,
path exported.Path,
value []byte,
) error {
ibcStore := l.storeService.OpenKVStore(ctx)
// ensure the proof provided is the expected sentinel localhost client proof
if !bytes.Equal(proof, SentinelProof) {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof))
}
merklePath, ok := path.(commitmenttypesv2.MerklePath)
if !ok {
return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path)
}
if len(merklePath.GetKeyPath()) != 2 {
return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath())
}
// The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store
bz, err := ibcStore.Get(merklePath.KeyPath[1])
if err != nil {
panic(err)
}
if bz == nil {
return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value not found for path %s", path)
}
if !bytes.Equal(bz, value) {
return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value provided does not equal value stored at path: %s", path)
}
return nil
}
// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath within the IBC store.
// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
// The caller must provide the full IBC store.
func (l LightClientModule) VerifyNonMembership(
ctx sdk.Context,
clientID string,
height exported.Height,
delayTimePeriod uint64,
delayBlockPeriod uint64,
proof []byte,
path exported.Path,
) error {
ibcStore := l.storeService.OpenKVStore(ctx)
// ensure the proof provided is the expected sentinel localhost client proof
if !bytes.Equal(proof, SentinelProof) {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof))
}
merklePath, ok := path.(commitmenttypesv2.MerklePath)
if !ok {
return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path)
}
if len(merklePath.GetKeyPath()) != 2 {
return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath())
}
// The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store
has, err := ibcStore.Has(merklePath.KeyPath[1])
if err != nil {
return errorsmod.Wrapf(err, "error checking for value for path %s", path)
}
if has {
return errorsmod.Wrapf(clienttypes.ErrFailedNonMembershipVerification, "value found for path %s", path)
}
return nil
}
// Status always returns Active. The 09-localhost status cannot be changed.
func (LightClientModule) Status(_ sdk.Context, _ string) exported.Status {
return exported.Active
}
// LatestHeight returns the context height.
func (LightClientModule) LatestHeight(ctx sdk.Context, _ string) exported.Height {
return clienttypes.GetSelfHeight(ctx)
}
// TimestampAtHeight returns the current block time retrieved from the application context. The localhost client does not store consensus states and thus
// cannot provide a timestamp for the provided height.
func (LightClientModule) TimestampAtHeight(ctx sdk.Context, _ string, _ exported.Height) (uint64, error) {
return uint64(ctx.BlockTime().UnixNano()), nil
}
// RecoverClient returns an error. The localhost cannot be modified by proposals.
func (LightClientModule) RecoverClient(_ sdk.Context, _, _ string) error {
return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal")
}
// VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded.
func (LightClientModule) VerifyUpgradeAndUpdateState(_ sdk.Context, _ string, _, _, _, _ []byte) error {
return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client")
}