cosmos-sdk/x/ibc/04-channel/keeper/grpc_query.go

447 lines
15 KiB
Go
Raw Normal View History

package keeper
import (
"context"
"strconv"
"strings"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
var _ types.QueryServer = (*Keeper)(nil)
// Channel implements the Query/Channel gRPC method
func (q Keeper) Channel(c context.Context, req *types.QueryChannelRequest) (*types.QueryChannelResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
channel, found := q.GetChannel(ctx, req.PortId, req.ChannelId)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryChannelResponse(req.PortId, req.ChannelId, channel, nil, selfHeight), nil
}
// Channels implements the Query/Channels gRPC method
func (q Keeper) Channels(c context.Context, req *types.QueryChannelsRequest) (*types.QueryChannelsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(c)
channels := []*types.IdentifiedChannel{}
store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyChannelPrefix))
pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error {
var result types.Channel
if err := q.cdc.UnmarshalBinaryBare(value, &result); err != nil {
return err
}
portID, channelID, err := host.ParseChannelPath(string(key))
if err != nil {
return err
}
identifiedChannel := types.NewIdentifiedChannel(portID, channelID, result)
channels = append(channels, &identifiedChannel)
return nil
})
if err != nil {
return nil, err
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryChannelsResponse{
Channels: channels,
Pagination: pageRes,
Height: selfHeight,
}, nil
}
// ConnectionChannels implements the Query/ConnectionChannels gRPC method
func (q Keeper) ConnectionChannels(c context.Context, req *types.QueryConnectionChannelsRequest) (*types.QueryConnectionChannelsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := host.ConnectionIdentifierValidator(req.Connection); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
ctx := sdk.UnwrapSDKContext(c)
channels := []*types.IdentifiedChannel{}
store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.KeyChannelPrefix))
pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error {
var result types.Channel
if err := q.cdc.UnmarshalBinaryBare(value, &result); err != nil {
return err
}
// ignore channel and continue to the next item if the connection is
// different than the requested one
if result.ConnectionHops[0] != req.Connection {
return nil
}
portID, channelID, err := host.ParseChannelPath(string(key))
if err != nil {
return err
}
identifiedChannel := types.NewIdentifiedChannel(portID, channelID, result)
channels = append(channels, &identifiedChannel)
return nil
})
if err != nil {
return nil, err
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryConnectionChannelsResponse{
Channels: channels,
Pagination: pageRes,
Height: selfHeight,
}, nil
}
// ChannelClientState implements the Query/ChannelClientState gRPC method
func (q Keeper) ChannelClientState(c context.Context, req *types.QueryChannelClientStateRequest) (*types.QueryChannelClientStateResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
channel, found := q.GetChannel(ctx, req.PortId, req.ChannelId)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}
connection, found := q.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]).Error(),
)
}
clientState, found := q.clientKeeper.GetClientState(ctx, connection.ClientId)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(clienttypes.ErrClientNotFound, "client-id: %s", connection.ClientId).Error(),
)
}
identifiedClientState := clienttypes.NewIdentifiedClientState(connection.ClientId, clientState)
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryChannelClientStateResponse(identifiedClientState, nil, selfHeight), nil
}
// ChannelConsensusState implements the Query/ChannelConsensusState gRPC method
func (q Keeper) ChannelConsensusState(c context.Context, req *types.QueryChannelConsensusStateRequest) (*types.QueryChannelConsensusStateResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
channel, found := q.GetChannel(ctx, req.PortId, req.ChannelId)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}
connection, found := q.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, "connection-id: %s", channel.ConnectionHops[0]).Error(),
)
}
consHeight := clienttypes.NewHeight(req.EpochNumber, req.EpochHeight)
consensusState, found := q.clientKeeper.GetClientConsensusState(ctx, connection.ClientId, consHeight)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", connection.ClientId).Error(),
)
}
anyConsensusState, err := clienttypes.PackConsensusState(consensusState)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryChannelConsensusStateResponse(connection.ClientId, anyConsensusState, consHeight, nil, selfHeight), nil
}
// PacketCommitment implements the Query/PacketCommitment gRPC method
func (q Keeper) PacketCommitment(c context.Context, req *types.QueryPacketCommitmentRequest) (*types.QueryPacketCommitmentResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
if req.Sequence == 0 {
return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0")
}
ctx := sdk.UnwrapSDKContext(c)
commitmentBz := q.GetPacketCommitment(ctx, req.PortId, req.ChannelId, req.Sequence)
if len(commitmentBz) == 0 {
return nil, status.Error(codes.NotFound, "packet commitment hash not found")
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryPacketCommitmentResponse(req.PortId, req.ChannelId, req.Sequence, commitmentBz, nil, selfHeight), nil
}
// PacketCommitments implements the Query/PacketCommitments gRPC method
func (q Keeper) PacketCommitments(c context.Context, req *types.QueryPacketCommitmentsRequest) (*types.QueryPacketCommitmentsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
commitments := []*types.PacketAckCommitment{}
store := prefix.NewStore(ctx.KVStore(q.storeKey), []byte(host.PacketCommitmentPrefixPath(req.PortId, req.ChannelId)))
pageRes, err := query.Paginate(store, req.Pagination, func(key, value []byte) error {
keySplit := strings.Split(string(key), "/")
sequence, err := strconv.ParseUint(keySplit[len(keySplit)-1], 10, 64)
if err != nil {
return err
}
commitment := types.NewPacketAckCommitment(req.PortId, req.ChannelId, sequence, value)
commitments = append(commitments, &commitment)
return nil
})
if err != nil {
return nil, err
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryPacketCommitmentsResponse{
Commitments: commitments,
Pagination: pageRes,
Height: selfHeight,
}, nil
}
// PacketAcknowledgement implements the Query/PacketAcknowledgement gRPC method
func (q Keeper) PacketAcknowledgement(c context.Context, req *types.QueryPacketAcknowledgementRequest) (*types.QueryPacketAcknowledgementResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
if req.Sequence == 0 {
return nil, status.Error(codes.InvalidArgument, "packet sequence cannot be 0")
}
ctx := sdk.UnwrapSDKContext(c)
acknowledgementBz, found := q.GetPacketAcknowledgement(ctx, req.PortId, req.ChannelId, req.Sequence)
if !found || len(acknowledgementBz) == 0 {
return nil, status.Error(codes.NotFound, "packet acknowledgement hash not found")
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryPacketAcknowledgementResponse(req.PortId, req.ChannelId, req.Sequence, acknowledgementBz, nil, selfHeight), nil
}
// UnreceivedPackets implements the Query/UnreceivedPackets gRPC method. Given
// a list of counterparty packet commitments, the querier checks if the packet
// has already been received by checking if an acknowledgement exists on this
// chain for the packet sequence. All packets that haven't been received yet
// are returned in the response
// Usage: To use this method correctly, first query all packet commitments on
// the sending chain using the Query/PacketCommitments gRPC method.
// Then input the returned sequences into the QueryUnreceivedPacketsRequest
// and send the request to this Query/UnreceivedPackets on the **receiving**
// chain. This gRPC method will then return the list of packet sequences that
// are yet to be received on the receiving chain.
//
// NOTE: The querier makes the assumption that the provided list of packet
// commitments is correct and will not function properly if the list
// is not up to date. Ideally the query height should equal the latest height
// on the counterparty's client which represents this chain.
// TODO: Replace GetPacketAcknowledgement with GetPacketReceipt once async
// acknowledgements issue is implemented.
// Issue #7254: https://github.com/cosmos/cosmos-sdk/issues/7254
func (q Keeper) UnreceivedPackets(c context.Context, req *types.QueryUnreceivedPacketsRequest) (*types.QueryUnreceivedPacketsResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
var unreceivedSequences = []uint64{}
for i, seq := range req.PacketCommitmentSequences {
if seq == 0 {
return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i)
}
// if acknowledgement exists on the receiving chain, then packet has already been received
if _, found := q.GetPacketAcknowledgement(ctx, req.PortId, req.ChannelId, seq); !found {
unreceivedSequences = append(unreceivedSequences, seq)
}
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryUnreceivedPacketsResponse{
Sequences: unreceivedSequences,
Height: selfHeight,
}, nil
}
// UnrelayedAcks implements the Query/UnrelayedAcks gRPC method. Given
// a list of counterparty packet acknowledgements, the querier checks if the packet
// has already been received by checking if the packet commitment still exists on this
// chain (original sender) for the packet sequence.
// All acknowledgmeents that haven't been received yet are returned in the response.
// Usage: To use this method correctly, first query all packet commitments on
// the sending chain using the Query/PacketCommitments gRPC method.
// Then input the returned sequences into the QueryUnrelayedPacketsRequest
// and send the request to this Query/UnrelayedPackets on the **receiving**
// chain. This gRPC method will then return the list of packet sequences whose
// acknowledgements are already written on the receiving chain but haven't yet
// been relayed back to the sending chain.
//
// NOTE: The querier makes the assumption that the provided list of packet
// acknowledgements is correct and will not function properly if the list
// is not up to date. Ideally the query height should equal the latest height
// on the counterparty's client which represents this chain.
func (q Keeper) UnrelayedAcks(c context.Context, req *types.QueryUnrelayedAcksRequest) (*types.QueryUnrelayedAcksResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
var unrelayedSequences = []uint64{}
for i, seq := range req.PacketCommitmentSequences {
if seq == 0 {
return nil, status.Errorf(codes.InvalidArgument, "packet sequence %d cannot be 0", i)
}
// if packet commitment still exists on the original sending chain, then packet ack has not been received
// since processing the ack will delete the packet commitment
if _, found := q.GetPacketAcknowledgement(ctx, req.PortId, req.ChannelId, seq); found {
unrelayedSequences = append(unrelayedSequences, seq)
}
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return &types.QueryUnrelayedAcksResponse{
Sequences: unrelayedSequences,
Height: selfHeight,
}, nil
}
// NextSequenceReceive implements the Query/NextSequenceReceive gRPC method
func (q Keeper) NextSequenceReceive(c context.Context, req *types.QueryNextSequenceReceiveRequest) (*types.QueryNextSequenceReceiveResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
if err := validategRPCRequest(req.PortId, req.ChannelId); err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(c)
sequence, found := q.GetNextSequenceRecv(ctx, req.PortId, req.ChannelId)
if !found {
return nil, status.Error(
codes.NotFound,
sdkerrors.Wrapf(types.ErrSequenceReceiveNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryNextSequenceReceiveResponse(req.PortId, req.ChannelId, sequence, nil, selfHeight), nil
}
func validategRPCRequest(portID, channelID string) error {
if err := host.PortIdentifierValidator(portID); err != nil {
return status.Error(codes.InvalidArgument, err.Error())
}
if err := host.ChannelIdentifierValidator(channelID); err != nil {
return status.Error(codes.InvalidArgument, err.Error())
}
return nil
}