package keeper
import (
"fmt"
"github.com/tendermint/tendermint/libs/log"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
"github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
commitmentexported "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/exported"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// Keeper defines the IBC connection keeper
type Keeper struct {
storeKey sdk.StoreKey
aminoCdc *codec.Codec // amino codec. TODO: remove after clients have been migrated to proto
cdc codec.Marshaler // hybrid codec
clientKeeper types.ClientKeeper
}
// NewKeeper creates a new IBC connection Keeper instance
func NewKeeper(aminoCdc *codec.Codec, cdc codec.Marshaler, key sdk.StoreKey, ck types.ClientKeeper) Keeper {
return Keeper{
storeKey: key,
aminoCdc: aminoCdc,
cdc: cdc,
clientKeeper: ck,
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", fmt.Sprintf("x/%s/%s", host.ModuleName, types.SubModuleName))
// GetCommitmentPrefix returns the IBC connection store prefix as a commitment
// Prefix
func (k Keeper) GetCommitmentPrefix() commitmentexported.Prefix {
return commitmenttypes.NewMerklePrefix([]byte(k.storeKey.Name()))
// GetConnection returns a connection with a particular identifier
func (k Keeper) GetConnection(ctx sdk.Context, connectionID string) (types.ConnectionEnd, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(host.KeyConnection(connectionID))
if bz == nil {
return types.ConnectionEnd{}, false
var connection types.ConnectionEnd
k.cdc.MustUnmarshalBinaryBare(bz, &connection)
return connection, true
// SetConnection sets a connection to the store
func (k Keeper) SetConnection(ctx sdk.Context, connectionID string, connection types.ConnectionEnd) {
bz := k.cdc.MustMarshalBinaryBare(&connection)
store.Set(host.KeyConnection(connectionID), bz)
// GetTimestampAtHeight returns the timestamp in nanoseconds of the consensus state at the
// given height.
func (k Keeper) GetTimestampAtHeight(ctx sdk.Context, connection types.ConnectionEnd, height uint64) (uint64, error) {
consensusState, found := k.clientKeeper.GetClientConsensusState(
ctx, connection.GetClientID(), height,
if !found {
return 0, sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"clientID (%s), height (%d)", connection.GetClientID(), height,
return consensusState.GetTimestamp(), nil
// GetClientConnectionPaths returns all the connection paths stored under a
// particular client
func (k Keeper) GetClientConnectionPaths(ctx sdk.Context, clientID string) ([]string, bool) {
bz := store.Get(host.KeyClientConnections(clientID))
return nil, false
var clientPaths types.ClientPaths
k.cdc.MustUnmarshalBinaryBare(bz, &clientPaths)
return clientPaths.Paths, true
// SetClientConnectionPaths sets the connections paths for client
func (k Keeper) SetClientConnectionPaths(ctx sdk.Context, clientID string, paths []string) {
clientPaths := types.ClientPaths{Paths: paths}
bz := k.cdc.MustMarshalBinaryBare(&clientPaths)
store.Set(host.KeyClientConnections(clientID), bz)
// GetAllClientConnectionPaths returns all stored clients connection id paths. It
// will ignore the clients that haven't initialized a connection handshake since
// no paths are stored.
func (k Keeper) GetAllClientConnectionPaths(ctx sdk.Context) []types.ConnectionPaths {
var allConnectionPaths []types.ConnectionPaths
k.clientKeeper.IterateClients(ctx, func(cs clientexported.ClientState) bool {
paths, found := k.GetClientConnectionPaths(ctx, cs.GetID())
// continue when connection handshake is not initialized
return false
connPaths := types.NewConnectionPaths(cs.GetID(), paths)
allConnectionPaths = append(allConnectionPaths, connPaths)
})
return allConnectionPaths
// IterateConnections provides an iterator over all ConnectionEnd objects.
// For each ConnectionEnd, cb will be called. If the cb returns true, the
// iterator will close and stop.
func (k Keeper) IterateConnections(ctx sdk.Context, cb func(types.ConnectionEnd) bool) {
iterator := sdk.KVStorePrefixIterator(store, host.KeyConnectionPrefix)
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &connection)
if cb(connection) {
break
// GetAllConnections returns all stored ConnectionEnd objects.
func (k Keeper) GetAllConnections(ctx sdk.Context) (connections []types.ConnectionEnd) {
k.IterateConnections(ctx, func(connection types.ConnectionEnd) bool {
connections = append(connections, connection)
return connections
// addConnectionToClient is used to add a connection identifier to the set of
// connections associated with a client.
func (k Keeper) addConnectionToClient(ctx sdk.Context, clientID, connectionID string) error {
_, found := k.clientKeeper.GetClientState(ctx, clientID)
return clienttypes.ErrClientNotFound
conns, found := k.GetClientConnectionPaths(ctx, clientID)
conns = []string{}
conns = append(conns, connectionID)
k.SetClientConnectionPaths(ctx, clientID, conns)
return nil
// removeConnectionFromClient is used to remove a connection identifier from the
// set of connections associated with a client.
//
// CONTRACT: client must already exist
// nolint: unused
func (k Keeper) removeConnectionFromClient(ctx sdk.Context, clientID, connectionID string) error {
return sdkerrors.Wrap(types.ErrClientConnectionPathsNotFound, clientID)
conns, ok := host.RemovePath(conns, connectionID)
if !ok {
return sdkerrors.Wrap(types.ErrConnectionPath, clientID)