x/ibc: migrate rest of 04-channel to gRPC (#6964)

* migrate channel querier to grpc

* fix various issues in channel grpc migration

* remove querier and add tests

* fix lint

* update utils based on self review

* Update x/ibc/24-host/keys.go

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>

* apply @fedekunze review suggestions

* fix build

* fix build

* run make proto gen again

Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
colin axnér 2020-08-11 14:29:29 +02:00 committed by GitHub
parent 61a97ef347
commit 1744194e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2499 additions and 850 deletions

View File

@ -1,41 +1,68 @@
syntax = "proto3";
package ibc.channel;
import "ibc/client/client.proto";
import "gogoproto/gogo.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "ibc/channel/channel.proto";
import "google/protobuf/any.proto";
option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types";
// Query provides defines the gRPC querier service
service Query {
// Channel queries an IBC Channel.
rpc Channel(QueryChannelRequest) returns (QueryChannelResponse) {}
rpc Channel(QueryChannelRequest) returns (QueryChannelResponse) {
}
// Channels queries all the IBC channels of a chain.
rpc Channels(QueryChannelsRequest) returns (QueryChannelsResponse) {}
rpc Channels(QueryChannelsRequest) returns (QueryChannelsResponse) {
}
// ConnectionChannels queries all the channels associated with a connection end.
rpc ConnectionChannels(QueryConnectionChannelsRequest) returns (QueryConnectionChannelsResponse) {}
// ConnectionChannels queries all the channels associated with a connection
// end.
rpc ConnectionChannels(QueryConnectionChannelsRequest)
returns (QueryConnectionChannelsResponse) {
}
// ChannelClientState queries for the client state for the channel associated
// with the provided channel identifiers.
rpc ChannelClientState(QueryChannelClientStateRequest)
returns (QueryChannelClientStateResponse) {
}
// ChannelConsensusState queries for the consensus state for the channel
// associated with the provided channel identifiers.
rpc ChannelConsensusState(QueryChannelConsensusStateRequest)
returns (QueryChannelConsensusStateResponse) {
}
// PacketCommitment queries a stored packet commitment hash.
rpc PacketCommitment(QueryPacketCommitmentRequest) returns (QueryPacketCommitmentResponse) {}
rpc PacketCommitment(QueryPacketCommitmentRequest)
returns (QueryPacketCommitmentResponse) {
}
// PacketCommitments returns the all the packet commitments hashes associated with a channel.
rpc PacketCommitments(QueryPacketCommitmentsRequest) returns (QueryPacketCommitmentsResponse) {}
// PacketCommitments returns the all the packet commitments hashes associated
// with a channel.
rpc PacketCommitments(QueryPacketCommitmentsRequest)
returns (QueryPacketCommitmentsResponse) {
}
// PacketAcknowledgement queries a stored packet acknowledgement hash.
rpc PacketAcknowledgement(QueryPacketAcknowledgementRequest) returns (QueryPacketAcknowledgementResponse) {}
rpc PacketAcknowledgement(QueryPacketAcknowledgementRequest)
returns (QueryPacketAcknowledgementResponse) {
}
// UnrelayedPackets returns all the unrelayed IBC packets associated with a channel and sequences.
rpc UnrelayedPackets(QueryUnrelayedPacketsRequest) returns (QueryUnrelayedPacketsResponse) {}
// UnrelayedPackets returns all the unrelayed IBC packets associated with a
// channel and sequences.
rpc UnrelayedPackets(QueryUnrelayedPacketsRequest)
returns (QueryUnrelayedPacketsResponse) {
}
// NextSequenceReceive returns the next receive sequence for a given channel
rpc NextSequenceReceive(QueryNextSequenceReceiveRequest) returns (QueryNextSequenceReceiveResponse) {}
// TODO: blocked by client proto migration
// rpc ChannelClientState(QueryChannelClientStateRequest) returns (QueryChannelClientStateRequest) {}
// rpc ChannelConsensusState(QueryChannelConsensusStateRequest) returns (QueryChannelConsensusStateRequest) {}
rpc NextSequenceReceive(QueryNextSequenceReceiveRequest)
returns (QueryNextSequenceReceiveResponse) {
}
}
// QueryChannelRequest is the request type for the Query/Channel RPC method
@ -76,7 +103,8 @@ message QueryChannelsResponse {
int64 height = 3;
}
// QueryConnectionChannelsRequest is the request type for the Query/QueryConnectionChannels RPC method
// QueryConnectionChannelsRequest is the request type for the
// Query/QueryConnectionChannels RPC method
message QueryConnectionChannelsRequest {
// connection unique identifier
string connection = 1;
@ -84,7 +112,8 @@ message QueryConnectionChannelsRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}
// QueryConnectionChannelsResponse is the Response type for the Query/QueryConnectionChannels RPC method
// QueryConnectionChannelsResponse is the Response type for the
// Query/QueryConnectionChannels RPC method
message QueryConnectionChannelsResponse {
// list of channels associated with a connection.
repeated ibc.channel.IdentifiedChannel channels = 1;
@ -94,7 +123,56 @@ message QueryConnectionChannelsResponse {
int64 height = 3;
}
// QueryPacketCommitmentRequest is the request type for the Query/PacketCommitment RPC method
// QueryChannelClientStateRequest is the request type for the Query/ClientState
// RPC method
message QueryChannelClientStateRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
// channel unique identifier
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
}
// QueryChannelClientStateResponse is the Response type for the
// Query/QueryChannelClientState RPC method
message QueryChannelClientStateResponse {
// client state associated with the channel
ibc.client.IdentifiedClientState identified_client_state = 1;
// merkle proof of existence
bytes proof = 2;
// merkle proof path
string proof_path = 3;
// height at which the proof was retrieved
uint64 proof_height = 4;
}
// QueryChannelConsensusStateRequest is the request type for the
// Query/ConsensusState RPC method
message QueryChannelConsensusStateRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
// channel unique identifier
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
// height of the consensus state
uint64 height = 3;
}
// QueryChannelClientStateResponse is the Response type for the
// Query/QueryChannelClientState RPC method
message QueryChannelConsensusStateResponse {
// consensus state associated with the channel
google.protobuf.Any consensus_state = 1;
// client ID associated with the consensus state
string client_id = 2 [(gogoproto.customname) = "ClientID"];
// merkle proof of existence
bytes proof = 3;
// merkle proof path
string proof_path = 4;
// height at which the proof was retrieved
uint64 proof_height = 5;
}
// QueryPacketCommitmentRequest is the request type for the
// Query/PacketCommitment RPC method
message QueryPacketCommitmentRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
@ -104,8 +182,9 @@ message QueryPacketCommitmentRequest {
uint64 sequence = 3;
}
// QueryPacketCommitmentResponse defines the client query response for a packet which also
// includes a proof, its path and the height form which the proof was retrieved
// QueryPacketCommitmentResponse defines the client query response for a packet
// which also includes a proof, its path and the height form which the proof was
// retrieved
message QueryPacketCommitmentResponse {
// packet associated with the request fields
bytes commitment = 1;
@ -117,7 +196,8 @@ message QueryPacketCommitmentResponse {
uint64 proof_height = 4;
}
// QueryPacketCommitmentsRequest is the request type for the Query/QueryPacketCommitments RPC method
// QueryPacketCommitmentsRequest is the request type for the
// Query/QueryPacketCommitments RPC method
message QueryPacketCommitmentsRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
@ -127,7 +207,8 @@ message QueryPacketCommitmentsRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 3;
}
// QueryPacketCommitmentsResponse is the request type for the Query/QueryPacketCommitments RPC method
// QueryPacketCommitmentsResponse is the request type for the
// Query/QueryPacketCommitments RPC method
message QueryPacketCommitmentsResponse {
repeated ibc.channel.PacketAckCommitment commitments = 1;
// pagination response
@ -136,7 +217,8 @@ message QueryPacketCommitmentsResponse {
int64 height = 3;
}
// QueryPacketAcknowledgementRequest is the request type for the Query/PacketAcknowledgement RPC method
// QueryPacketAcknowledgementRequest is the request type for the
// Query/PacketAcknowledgement RPC method
message QueryPacketAcknowledgementRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
@ -146,8 +228,9 @@ message QueryPacketAcknowledgementRequest {
uint64 sequence = 3;
}
// QueryPacketAcknowledgementResponse defines the client query response for a packet which also
// includes a proof, its path and the height form which the proof was retrieved
// QueryPacketAcknowledgementResponse defines the client query response for a
// packet which also includes a proof, its path and the height form which the
// proof was retrieved
message QueryPacketAcknowledgementResponse {
// packet associated with the request fields
bytes acknowledgement = 1;
@ -159,19 +242,23 @@ message QueryPacketAcknowledgementResponse {
uint64 proof_height = 4;
}
// QueryUnrelayedPacketsRequest is the request type for the Query/UnrelayedPackets RPC method
// QueryUnrelayedPacketsRequest is the request type for the
// Query/UnrelayedPackets RPC method
message QueryUnrelayedPacketsRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
// channel unique identifier
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
// list of packet sequences
repeated uint64 packet_commitment_sequences = 3 [(gogoproto.customname) = "PacketCommitmentSequences"];
// flag indicating if the return value is packet commitments or acknowledgements
repeated uint64 packet_commitment_sequences = 3
[(gogoproto.customname) = "PacketCommitmentSequences"];
// flag indicating if the return value is packet commitments or
// acknowledgements
bool acknowledgements = 4;
}
// QueryUnrelayedPacketsResponse is the request type for the Query/UnrelayedPacketCommitments RPC method
// QueryUnrelayedPacketsResponse is the request type for the
// Query/UnrelayedPacketCommitments RPC method
message QueryUnrelayedPacketsResponse {
// list of unrelayed packet sequences
repeated uint64 sequences = 1;
@ -179,7 +266,8 @@ message QueryUnrelayedPacketsResponse {
int64 height = 2;
}
// QueryNextSequenceReceiveRequest is the request type for the Query/QueryNextSequenceReceiveRequest RPC method
// QueryNextSequenceReceiveRequest is the request type for the
// Query/QueryNextSequenceReceiveRequest RPC method
message QueryNextSequenceReceiveRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
@ -187,7 +275,8 @@ message QueryNextSequenceReceiveRequest {
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
}
// QuerySequenceResponse is the request type for the Query/QueryNextSequenceReceiveResponse RPC method
// QuerySequenceResponse is the request type for the
// Query/QueryNextSequenceReceiveResponse RPC method
message QueryNextSequenceReceiveResponse {
// next sequence receive number
uint64 next_sequence_receive = 1;
@ -199,18 +288,3 @@ message QueryNextSequenceReceiveResponse {
uint64 proof_height = 4;
}
// QueryChannelClientStateRequest is the request type for the Query/ClientState RPC method
message QueryChannelClientStateRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
// channel unique identifier
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
}
// QueryChannelConsensusStateRequest is the request type for the Query/ConsensusState RPC method
message QueryChannelConsensusStateRequest {
// port unique identifier
string port_id = 1 [(gogoproto.customname) = "PortID"];
// channel unique identifier
string channel_id = 2 [(gogoproto.customname) = "ChannelID"];
}

View File

@ -0,0 +1,15 @@
syntax = "proto3";
package ibc.client;
option go_package = "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types";
import "gogoproto/gogo.proto";
import "google/protobuf/any.proto";
// IdentifiedClientState defines a client state with additional client identifier field.
message IdentifiedClientState {
option (gogoproto.goproto_getters) = false;
// client identifier
string id = 1 [(gogoproto.customname) = "ID", (gogoproto.moretags) = "yaml:\"id\""];
google.protobuf.Any client_state = 2;
}

View File

@ -39,6 +39,7 @@ func QueryAllClientStates(clientCtx client.Context, page, limit int) ([]exported
// QueryClientState queries the store to get the light client state and a merkle
// proof.
// TODO: delete
func QueryClientState(
clientCtx client.Context, clientID string, prove bool,
) (types.StateResponse, error) {
@ -63,8 +64,40 @@ func QueryClientState(
return clientStateRes, nil
}
// QueryClientState queries the store to get the light client state and a merkle
// proof.
func QueryClientStateABCI(
clientCtx client.Context, clientID string,
) (exported.ClientState, []byte, uint64, error) {
req := abci.RequestQuery{
Path: "store/ibc/key",
Data: host.FullKeyClientPath(clientID, host.KeyClientState()),
Prove: true,
}
res, err := clientCtx.QueryABCI(req)
if err != nil {
return nil, nil, 0, err
}
proofBz, err := clientCtx.LegacyAmino.MarshalBinaryBare(res.Proof)
if err != nil {
return nil, nil, 0, err
}
var clientState exported.ClientState
if err := clientCtx.LegacyAmino.UnmarshalBinaryBare(res.Value, &clientState); err != nil {
return nil, nil, 0, err
}
// FIXME: height + 1 is returned as the proof height
// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567
return clientState, proofBz, uint64(res.Height + 1), nil
}
// QueryConsensusState queries the store to get the consensus state and a merkle
// proof.
// TODO: delete
func QueryConsensusState(
clientCtx client.Context, clientID string, height uint64, prove bool,
) (types.ConsensusStateResponse, error) {
@ -89,6 +122,38 @@ func QueryConsensusState(
return types.NewConsensusStateResponse(clientID, cs, res.Proof, res.Height), nil
}
// QueryConsensusState queries the store to get the consensus state of a light
// client and a merkle proof of its existence or non-existence.
func QueryConsensusStateABCI(
clientCtx client.Context, clientID string, height uint64,
) (exported.ConsensusState, []byte, uint64, error) {
req := abci.RequestQuery{
Path: "store/ibc/key",
Data: host.FullKeyClientPath(clientID, host.KeyConsensusState(height)),
Prove: true,
}
res, err := clientCtx.QueryABCI(req)
if err != nil {
return nil, nil, 0, err
}
proofBz, err := clientCtx.LegacyAmino.MarshalBinaryBare(res.Proof)
if err != nil {
return nil, nil, 0, err
}
var cs exported.ConsensusState
if err := clientCtx.LegacyAmino.UnmarshalBinaryBare(res.Value, &cs); err != nil {
return nil, nil, 0, err
}
// FIXME: height + 1 is returned as the proof height
// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567
return cs, proofBz, uint64(res.Height + 1), nil
}
// QueryTendermintHeader takes a client context and returns the appropriate
// tendermint header
func QueryTendermintHeader(clientCtx client.Context) (ibctmtypes.Header, int64, error) {

View File

@ -0,0 +1,40 @@
package types
import (
"fmt"
proto "github.com/gogo/protobuf/proto"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
)
var _ codectypes.UnpackInterfacesMessage = IdentifiedClientState{}
// NewIdentifiedClientState creates a new IdentifiedClientState instance
func NewIdentifiedClientState(clientID string, clientState exported.ClientState) IdentifiedClientState {
msg, ok := clientState.(proto.Message)
if !ok {
panic(fmt.Errorf("cannot proto marshal %T", clientState))
}
anyClientState, err := codectypes.NewAnyWithValue(msg)
if err != nil {
panic(err)
}
return IdentifiedClientState{
ID: clientID,
ClientState: anyClientState,
}
}
// UnpackInterfaces implements UnpackInterfacesMesssage.UnpackInterfaces
func (ics IdentifiedClientState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
var clientState exported.ClientState
err := unpacker.UnpackAny(ics.ClientState, &clientState)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,373 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: ibc/client/client.proto
package types
import (
fmt "fmt"
types "github.com/cosmos/cosmos-sdk/codec/types"
_ "github.com/gogo/protobuf/gogoproto"
proto "github.com/gogo/protobuf/proto"
io "io"
math "math"
math_bits "math/bits"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
// IdentifiedClientState defines a client state with additional client identifier field.
type IdentifiedClientState struct {
// client identifier
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty" yaml:"id"`
ClientState *types.Any `protobuf:"bytes,2,opt,name=client_state,json=clientState,proto3" json:"client_state,omitempty"`
}
func (m *IdentifiedClientState) Reset() { *m = IdentifiedClientState{} }
func (m *IdentifiedClientState) String() string { return proto.CompactTextString(m) }
func (*IdentifiedClientState) ProtoMessage() {}
func (*IdentifiedClientState) Descriptor() ([]byte, []int) {
return fileDescriptor_226f80e576f20abd, []int{0}
}
func (m *IdentifiedClientState) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *IdentifiedClientState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_IdentifiedClientState.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *IdentifiedClientState) XXX_Merge(src proto.Message) {
xxx_messageInfo_IdentifiedClientState.Merge(m, src)
}
func (m *IdentifiedClientState) XXX_Size() int {
return m.Size()
}
func (m *IdentifiedClientState) XXX_DiscardUnknown() {
xxx_messageInfo_IdentifiedClientState.DiscardUnknown(m)
}
var xxx_messageInfo_IdentifiedClientState proto.InternalMessageInfo
func init() {
proto.RegisterType((*IdentifiedClientState)(nil), "ibc.client.IdentifiedClientState")
}
func init() { proto.RegisterFile("ibc/client/client.proto", fileDescriptor_226f80e576f20abd) }
var fileDescriptor_226f80e576f20abd = []byte{
// 256 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0x4c, 0x4a, 0xd6,
0x4f, 0xce, 0xc9, 0x4c, 0xcd, 0x2b, 0x81, 0x52, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x5c,
0x99, 0x49, 0xc9, 0x7a, 0x10, 0x11, 0x29, 0x91, 0xf4, 0xfc, 0xf4, 0x7c, 0xb0, 0xb0, 0x3e, 0x88,
0x05, 0x51, 0x21, 0x25, 0x99, 0x9e, 0x9f, 0x9f, 0x9e, 0x93, 0xaa, 0x0f, 0xe6, 0x25, 0x95, 0xa6,
0xe9, 0x27, 0xe6, 0x55, 0x42, 0xa4, 0x94, 0xaa, 0xb9, 0x44, 0x3d, 0x53, 0x52, 0xf3, 0x4a, 0x32,
0xd3, 0x32, 0x53, 0x53, 0x9c, 0xc1, 0x86, 0x04, 0x97, 0x24, 0x96, 0xa4, 0x0a, 0x29, 0x73, 0x31,
0x65, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x3a, 0x09, 0x3f, 0xba, 0x27, 0xcf, 0xe4, 0xe9,
0xf2, 0xe9, 0x9e, 0x3c, 0x67, 0x65, 0x62, 0x6e, 0x8e, 0x95, 0x52, 0x66, 0x8a, 0x52, 0x10, 0x53,
0x66, 0x8a, 0x90, 0x39, 0x17, 0x0f, 0xc4, 0xe2, 0xf8, 0x62, 0x90, 0x26, 0x09, 0x26, 0x05, 0x46,
0x0d, 0x6e, 0x23, 0x11, 0x3d, 0x88, 0x7d, 0x7a, 0x30, 0xfb, 0xf4, 0x1c, 0xf3, 0x2a, 0x83, 0xb8,
0x93, 0x11, 0xa6, 0x5b, 0xb1, 0x74, 0x2c, 0x90, 0x67, 0x70, 0xf2, 0x39, 0xf1, 0x48, 0x8e, 0xf1,
0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e,
0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, 0xa3, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc,
0x5c, 0xfd, 0xe4, 0xfc, 0xe2, 0xdc, 0xfc, 0x62, 0x28, 0xa5, 0x5b, 0x9c, 0x92, 0xad, 0x5f, 0xa1,
0x0f, 0x0a, 0x0b, 0x03, 0x23, 0x5d, 0x68, 0x70, 0x94, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81,
0xad, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xa8, 0x0c, 0x55, 0xf9, 0x29, 0x01, 0x00, 0x00,
}
func (m *IdentifiedClientState) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *IdentifiedClientState) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *IdentifiedClientState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.ClientState != nil {
{
size, err := m.ClientState.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintClient(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
if len(m.ID) > 0 {
i -= len(m.ID)
copy(dAtA[i:], m.ID)
i = encodeVarintClient(dAtA, i, uint64(len(m.ID)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func encodeVarintClient(dAtA []byte, offset int, v uint64) int {
offset -= sovClient(v)
base := offset
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
v >>= 7
offset++
}
dAtA[offset] = uint8(v)
return base
}
func (m *IdentifiedClientState) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.ID)
if l > 0 {
n += 1 + l + sovClient(uint64(l))
}
if m.ClientState != nil {
l = m.ClientState.Size()
n += 1 + l + sovClient(uint64(l))
}
return n
}
func sovClient(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
func sozClient(x uint64) (n int) {
return sovClient(uint64((x << 1) ^ uint64((int64(x) >> 63))))
}
func (m *IdentifiedClientState) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowClient
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: IdentifiedClientState: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: IdentifiedClientState: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowClient
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthClient
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthClient
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ClientState", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowClient
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthClient
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthClient
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.ClientState == nil {
m.ClientState = &types.Any{}
}
if err := m.ClientState.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipClient(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthClient
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthClient
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipClient(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
depth := 0
for iNdEx < l {
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowClient
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
wireType := int(wire & 0x7)
switch wireType {
case 0:
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowClient
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
iNdEx++
if dAtA[iNdEx-1] < 0x80 {
break
}
}
case 1:
iNdEx += 8
case 2:
var length int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return 0, ErrIntOverflowClient
}
if iNdEx >= l {
return 0, io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
length |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if length < 0 {
return 0, ErrInvalidLengthClient
}
iNdEx += length
case 3:
depth++
case 4:
if depth == 0 {
return 0, ErrUnexpectedEndOfGroupClient
}
depth--
case 5:
iNdEx += 4
default:
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
}
if iNdEx < 0 {
return 0, ErrInvalidLengthClient
}
if depth == 0 {
return iNdEx, nil
}
}
return 0, io.ErrUnexpectedEOF
}
var (
ErrInvalidLengthClient = fmt.Errorf("proto: negative length found during unmarshaling")
ErrIntOverflowClient = fmt.Errorf("proto: integer overflow")
ErrUnexpectedEndOfGroupClient = fmt.Errorf("proto: unexpected end of group")
)

View File

@ -79,3 +79,14 @@ func MustPackConsensusState(consensusState exported.ConsensusState) *codectypes.
return anyConsensusState
}
// UnpackConsensusState unpacks an Any into a ConsensusState. It returns an error if the
// consensus state can't be unpacked into a ConsensusState.
func UnpackConsensusState(any *codectypes.Any) (exported.ConsensusState, error) {
consensusState, ok := any.GetCachedValue().(exported.ConsensusState)
if !ok {
return nil, fmt.Errorf("cannot unpack Any into ConsensusState %T", any)
}
return consensusState, nil
}

View File

@ -51,7 +51,6 @@ func GetCmdQueryChannels() *cobra.Command {
return err
}
clientCtx = clientCtx.WithHeight(res.Height)
return clientCtx.PrintOutput(res)
},
}
@ -88,7 +87,6 @@ func GetCmdQueryChannel() *cobra.Command {
return err
}
clientCtx = clientCtx.WithHeight(int64(channelRes.ProofHeight))
return clientCtx.PrintOutput(channelRes)
},
}
@ -130,7 +128,6 @@ func GetCmdQueryConnectionChannels() *cobra.Command {
return err
}
clientCtx = clientCtx.WithHeight(res.Height)
return clientCtx.PrintOutput(res)
},
}
@ -159,14 +156,12 @@ func GetCmdQueryChannelClientState() *cobra.Command {
portID := args[0]
channelID := args[1]
clientStateRes, height, err := utils.QueryChannelClientState(clientCtx, portID, channelID)
res, err := utils.QueryChannelClientState(clientCtx, portID, channelID, false)
if err != nil {
return err
}
clientCtx = clientCtx.WithHeight(height)
return clientCtx.PrintOutputLegacy(clientStateRes)
return clientCtx.PrintOutputLegacy(res.IdentifiedClientState)
},
}
@ -207,7 +202,6 @@ func GetCmdQueryPacketCommitments() *cobra.Command {
return err
}
clientCtx = clientCtx.WithHeight(res.Height)
return clientCtx.PrintOutput(res)
},
}
@ -249,7 +243,6 @@ func GetCmdQueryPacketCommitment() *cobra.Command {
return err
}
clientCtx = clientCtx.WithHeight(int64(res.ProofHeight))
return clientCtx.PrintOutput(res)
},
}

View File

@ -52,14 +52,14 @@ func queryChannelClientStateHandlerFn(clientCtx client.Context) http.HandlerFunc
return
}
clientState, height, err := utils.QueryChannelClientState(clientCtx, portID, channelID)
res, err := utils.QueryChannelClientState(clientCtx, portID, channelID, false)
if err != nil {
rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
clientCtx = clientCtx.WithHeight(height)
rest.PostProcessResponse(w, clientCtx, clientState)
clientCtx = clientCtx.WithHeight(int64(res.ProofHeight))
rest.PostProcessResponse(w, clientCtx, res.IdentifiedClientState)
}
}

View File

@ -3,12 +3,13 @@ package utils
import (
"context"
"encoding/binary"
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/client"
clientutils "github.com/cosmos/cosmos-sdk/x/ibc/02-client/client/utils"
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/04-channel/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
@ -53,7 +54,8 @@ func queryPacketCommitmentABCI(
return nil, err
}
// FIXME: res.Height+1 is hack, fix later
// FIXME: height + 1 is returned as the proof height
// Issue: https://github.com/cosmos/cosmos-sdk/issues/6567
return types.NewQueryPacketCommitmentResponse(portID, channelID, sequence, res.Value, proofBz, res.Height+1), nil
}
@ -101,67 +103,101 @@ func queryChannelABCI(clientCtx client.Context, portID, channelID string) (*type
return types.NewQueryChannelResponse(portID, channelID, channel, proofBz, res.Height), nil
}
// QueryChannelClientState uses the channel Querier to return the ClientState of
// a Channel.
func QueryChannelClientState(clientCtx client.Context, portID, channelID string) (clientexported.ClientState, int64, error) {
params := types.NewQueryChannelClientStateRequest(portID, channelID)
bz, err := clientCtx.JSONMarshaler.MarshalJSON(params)
if err != nil {
return nil, 0, fmt.Errorf("failed to marshal query params: %w", err)
// QueryChannelClientState returns the ClientState of a channel end. If
// prove is true, it performs an ABCI store query in order to retrieve the
// merkle proof. Otherwise, it uses the gRPC query client.
func QueryChannelClientState(
clientCtx client.Context, portID, channelID string, prove bool,
) (*types.QueryChannelClientStateResponse, error) {
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryChannelClientStateRequest{
PortID: portID,
ChannelID: channelID,
}
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryChannelClientState)
res, height, err := clientCtx.QueryWithData(route, bz)
res, err := queryClient.ChannelClientState(context.Background(), req)
if err != nil {
return nil, 0, err
return nil, err
}
var clientState clientexported.ClientState
err = clientCtx.JSONMarshaler.UnmarshalJSON(res, &clientState)
if err != nil {
return nil, 0, fmt.Errorf("failed to unmarshal client state: %w", err)
if prove {
clientState, proof, proofHeight, err := clientutils.QueryClientStateABCI(clientCtx, res.IdentifiedClientState.ID)
if err != nil {
return nil, err
}
// use client state returned from ABCI query in case query height differs
identifiedClientState := clienttypes.NewIdentifiedClientState(res.IdentifiedClientState.ID, clientState)
res = types.NewQueryChannelClientStateResponse(identifiedClientState, proof, int64(proofHeight))
}
return clientState, height, nil
return res, nil
}
// QueryChannelConsensusState uses the channel Querier to return the ConsensusState
// of a Channel.
func QueryChannelConsensusState(clientCtx client.Context, portID, channelID string) (clientexported.ConsensusState, int64, error) {
params := types.NewQueryChannelConsensusStateRequest(portID, channelID)
bz, err := clientCtx.JSONMarshaler.MarshalJSON(params)
if err != nil {
return nil, 0, fmt.Errorf("failed to marshal query params: %w", err)
// QueryChannelConsensusState returns the ConsensusState of a channel end. If
// prove is true, it performs an ABCI store query in order to retrieve the
// merkle proof. Otherwise, it uses the gRPC query client.
func QueryChannelConsensusState(
clientCtx client.Context, portID, channelID string, prove bool,
) (*types.QueryChannelConsensusStateResponse, error) {
queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryChannelConsensusStateRequest{
PortID: portID,
ChannelID: channelID,
}
route := fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryChannelConsensusState)
res, height, err := clientCtx.QueryWithData(route, bz)
res, err := queryClient.ChannelConsensusState(context.Background(), req)
if err != nil {
return nil, 0, err
return nil, err
}
var consensusState clientexported.ConsensusState
err = clientCtx.JSONMarshaler.UnmarshalJSON(res, &consensusState)
consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState)
if err != nil {
return nil, 0, fmt.Errorf("failed to unmarshal consensus state: %w", err)
return nil, err
}
return consensusState, height, nil
if prove {
consensusState, proof, proofHeight, err := clientutils.QueryConsensusStateABCI(clientCtx, res.ClientID, consensusState.GetHeight())
if err != nil {
return nil, err
}
// use consensus state returned from ABCI query in case query height differs
anyConsensusState, err := clienttypes.PackConsensusState(consensusState)
if err != nil {
return nil, err
}
res = types.NewQueryChannelConsensusStateResponse(res.ClientID, anyConsensusState, consensusState.GetHeight(), proof, int64(proofHeight))
}
return res, nil
}
// QueryCounterpartyConsensusState uses the channel Querier to return the
// counterparty ConsensusState given the source port ID and source channel ID.
func QueryCounterpartyConsensusState(clientCtx client.Context, portID, channelID string) (clientexported.ConsensusState, int64, error) {
func QueryCounterpartyConsensusState(
clientCtx client.Context, portID, channelID string,
) (clientexported.ConsensusState, uint64, error) {
channelRes, err := QueryChannel(clientCtx, portID, channelID, false)
if err != nil {
return nil, 0, err
}
counterparty := channelRes.Channel.Counterparty
clientState, height, err := QueryChannelConsensusState(clientCtx, counterparty.PortID, counterparty.ChannelID)
res, err := QueryChannelConsensusState(clientCtx, counterparty.PortID, counterparty.ChannelID, false)
if err != nil {
return nil, 0, err
}
return clientState, height, nil
consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState)
if err != nil {
return nil, 0, err
}
return consensusState, res.ProofHeight, nil
}
// QueryNextSequenceReceive returns the next sequence receive.

View File

@ -12,6 +12,8 @@ import (
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"
)
@ -126,6 +128,92 @@ func (q Keeper) ConnectionChannels(c context.Context, req *types.QueryConnection
}, 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)
return types.NewQueryChannelClientStateResponse(identifiedClientState, nil, ctx.BlockHeight()), 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(),
)
}
consensusState, found := q.clientKeeper.GetClientConsensusState(ctx, connection.ClientID, req.Height)
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())
}
return types.NewQueryChannelConsensusStateResponse(connection.ClientID, anyConsensusState, consensusState.GetHeight(), nil, ctx.BlockHeight()), nil
}
// PacketCommitment implements the Query/PacketCommitment gRPC method
func (q Keeper) PacketCommitment(c context.Context, req *types.QueryPacketCommitmentRequest) (*types.QueryPacketCommitmentResponse, error) {
if req == nil {

View File

@ -6,6 +6,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
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"
ibctesting "github.com/cosmos/cosmos-sdk/x/ibc/testing"
)
@ -292,6 +294,253 @@ func (suite *KeeperTestSuite) TestQueryConnectionChannels() {
}
}
func (suite *KeeperTestSuite) TestQueryChannelClientState() {
var (
req *types.QueryChannelClientStateRequest
expIdentifiedClientState clienttypes.IdentifiedClientState
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = nil
},
false,
},
{
"invalid port ID",
func() {
req = &types.QueryChannelClientStateRequest{
PortID: "",
ChannelID: "test-channel-id",
}
},
false,
},
{
"invalid channel ID",
func() {
req = &types.QueryChannelClientStateRequest{
PortID: "test-port-id",
ChannelID: "",
}
},
false,
},
{"channel not found",
func() {
req = &types.QueryChannelClientStateRequest{
PortID: "test-port-id",
ChannelID: "test-channel-id",
}
},
false,
},
{
"connection not found",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
channel := suite.chainA.GetChannel(channelA)
// update channel to reference a connection that does not exist
channel.ConnectionHops[0] = "doesnotexist"
// set connection hops to wrong connection ID
suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel)
req = &types.QueryChannelClientStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
}
}, false,
},
{
"client state for channel's connection not found",
func() {
_, _, connA, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// set connection to empty so clientID is empty
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connectiontypes.ConnectionEnd{})
req = &types.QueryChannelClientStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
}
}, false,
},
{
"success",
func() {
clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
// init channel
channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
suite.Require().NoError(err)
expClientState := suite.chainA.GetClientState(clientA)
expIdentifiedClientState = clienttypes.NewIdentifiedClientState(clientA, expClientState)
req = &types.QueryChannelClientStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
}
},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset
tc.malleate()
ctx := sdk.WrapSDKContext(suite.chainA.GetContext())
res, err := suite.chainA.QueryServer.ChannelClientState(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(&expIdentifiedClientState, res.IdentifiedClientState)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *KeeperTestSuite) TestQueryChannelConsensusState() {
var (
req *types.QueryChannelConsensusStateRequest
expConsensusState clientexported.ConsensusState
expClientID string
)
testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = nil
},
false,
},
{
"invalid port ID",
func() {
req = &types.QueryChannelConsensusStateRequest{
PortID: "",
ChannelID: "test-channel-id",
Height: 1,
}
},
false,
},
{
"invalid channel ID",
func() {
req = &types.QueryChannelConsensusStateRequest{
PortID: "test-port-id",
ChannelID: "",
Height: 1,
}
},
false,
},
{"channel not found",
func() {
req = &types.QueryChannelConsensusStateRequest{
PortID: "test-port-id",
ChannelID: "test-channel-id",
Height: 1,
}
},
false,
},
{
"connection not found",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
channel := suite.chainA.GetChannel(channelA)
// update channel to reference a connection that does not exist
channel.ConnectionHops[0] = "doesnotexist"
// set connection hops to wrong connection ID
suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel)
req = &types.QueryChannelConsensusStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
Height: 1,
}
}, false,
},
{
"consensus state for channel's connection not found",
func() {
_, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
req = &types.QueryChannelConsensusStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
Height: uint64(suite.chainA.GetContext().BlockHeight()), // use current height
}
}, false,
},
{
"success",
func() {
clientA, _, connA, connB := suite.coordinator.SetupClientConnections(suite.chainA, suite.chainB, clientexported.Tendermint)
// init channel
channelA, _, err := suite.coordinator.ChanOpenInit(suite.chainA, suite.chainB, connA, connB, types.ORDERED)
suite.Require().NoError(err)
clientState := suite.chainA.GetClientState(clientA)
expConsensusState, _ = suite.chainA.GetConsensusState(clientA, clientState.GetLatestHeight())
suite.Require().NotNil(expConsensusState)
expClientID = clientA
req = &types.QueryChannelConsensusStateRequest{
PortID: channelA.PortID,
ChannelID: channelA.ID,
Height: expConsensusState.GetHeight(),
}
},
true,
},
}
for _, tc := range testCases {
suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset
tc.malleate()
ctx := sdk.WrapSDKContext(suite.chainA.GetContext())
res, err := suite.chainA.QueryServer.ChannelConsensusState(ctx, req)
if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
consensusState, err := clienttypes.UnpackConsensusState(res.ConsensusState)
suite.Require().NoError(err)
suite.Require().Equal(expConsensusState, consensusState)
suite.Require().Equal(expClientID, res.ClientID)
} else {
suite.Require().Error(err)
}
})
}
}
func (suite *KeeperTestSuite) TestQueryPacketCommitment() {
var (
req *types.QueryPacketCommitmentRequest

View File

@ -1,44 +0,0 @@
package keeper
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
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"
)
// QuerierChannelClientState defines the sdk.Querier to query all the ClientState
// associated with a given Channel.
func QuerierChannelClientState(ctx sdk.Context, abciReq abci.RequestQuery, k Keeper, legacyQuerierCdc codec.JSONMarshaler) ([]byte, error) {
var req types.QueryChannelClientStateRequest
if err := legacyQuerierCdc.UnmarshalJSON(abciReq.Data, &req); err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
}
channel, found := k.GetChannel(ctx, req.PortID, req.ChannelID)
if !found {
return nil, sdkerrors.Wrapf(types.ErrChannelNotFound, req.PortID, req.ChannelID)
}
connection, found := k.connectionKeeper.GetConnection(ctx, channel.ConnectionHops[0])
if !found {
return nil, sdkerrors.Wrapf(connectiontypes.ErrConnectionNotFound, channel.ConnectionHops[0])
}
clientState, found := k.clientKeeper.GetClientState(ctx, connection.ClientID)
if !found {
return nil, sdkerrors.Wrapf(clienttypes.ErrClientNotFound, connection.ClientID)
}
res, err := codec.MarshalJSONIndent(legacyQuerierCdc, clientState)
if err != nil {
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
}
return res, nil
}

View File

@ -1,108 +0,0 @@
package keeper_test
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/cosmos/cosmos-sdk/codec"
clientexported "github.com/cosmos/cosmos-sdk/x/ibc/02-client/exported"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
"github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
)
// TestQuerierChannelClientState verifies correct querying of client state associated
// with a channel end.
func (suite *KeeperTestSuite) TestQuerierChannelClientState() {
path := []string{types.SubModuleName, types.QueryChannelClientState}
var (
clientID string
req *types.QueryChannelClientStateRequest
)
testCases := []struct {
name string
setup func()
expPass bool
}{
{
"channel not found",
func() {
clientA, err := suite.coordinator.CreateClient(suite.chainA, suite.chainB, clientexported.Tendermint)
suite.Require().NoError(err)
clientID = clientA
req = types.NewQueryChannelClientStateRequest("doesnotexist", "doesnotexist")
},
false,
},
{
"connection for channel not found",
func() {
// connection for channel is deleted from state
clientA, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
channel := suite.chainA.GetChannel(channelA)
channel.ConnectionHops[0] = "doesnotexist"
// set connection hops to wrong connection ID
suite.chainA.App.IBCKeeper.ChannelKeeper.SetChannel(suite.chainA.GetContext(), channelA.PortID, channelA.ID, channel)
clientID = clientA
req = types.NewQueryChannelClientStateRequest(channelA.PortID, channelA.ID)
},
false,
},
{
"client state for channel's connection not found",
func() {
clientA, _, connA, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
// setting connection to empty results in wrong clientID used
suite.chainA.App.IBCKeeper.ConnectionKeeper.SetConnection(suite.chainA.GetContext(), connA.ID, connectiontypes.ConnectionEnd{})
clientID = clientA
req = types.NewQueryChannelClientStateRequest(channelA.PortID, channelA.ID)
},
false,
},
{
"success",
func() {
clientA, _, _, _, channelA, _ := suite.coordinator.Setup(suite.chainA, suite.chainB)
clientID = clientA
req = types.NewQueryChannelClientStateRequest(channelA.PortID, channelA.ID)
},
true,
},
}
for i, tc := range testCases {
suite.SetupTest() // reset
tc.setup()
data, err := suite.chainA.App.AppCodec().MarshalJSON(req)
suite.Require().NoError(err)
query := abci.RequestQuery{
Path: "",
Data: data,
}
clientState, found := suite.chainA.App.IBCKeeper.ClientKeeper.GetClientState(suite.chainA.GetContext(), clientID)
bz, err := suite.chainA.Querier(suite.chainA.GetContext(), path, query)
if tc.expPass {
// set expected result
expRes, merr := codec.MarshalJSONIndent(suite.chainA.App.AppCodec(), clientState)
suite.Require().NoError(merr)
suite.Require().True(found, "test case %d failed: %s", i, tc.name)
suite.Require().NoError(err, "test case %d failed: %s", i, tc.name)
suite.Require().Equal(string(expRes), string(bz), "test case %d failed: %s", i, tc.name)
} else {
suite.Require().Error(err, "test case %d passed: %s", i, tc.name)
suite.Require().Nil(bz, "test case %d passed: %s", i, tc.name)
}
}
}

View File

@ -12,6 +12,7 @@ import (
// ClientKeeper expected account IBC client keeper
type ClientKeeper interface {
GetClientState(ctx sdk.Context, clientID string) (clientexported.ClientState, bool)
GetClientConsensusState(ctx sdk.Context, clientID string, height uint64) (clientexported.ConsensusState, bool)
}
// ConnectionKeeper expected account IBC connection keeper

View File

@ -3,16 +3,12 @@ package types
import (
"strings"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
)
// query routes supported by the IBC channel Querier
const (
QueryChannelClientState = "channel-client-state"
QueryChannelConsensusState = "channel-consensus-state"
)
// NewQueryChannelResponse creates a new QueryChannelResponse instance
func NewQueryChannelResponse(portID, channelID string, channel Channel, proof []byte, height int64) *QueryChannelResponse {
path := commitmenttypes.NewMerklePath(strings.Split(host.ChannelPath(portID, channelID), "/"))
@ -24,6 +20,29 @@ func NewQueryChannelResponse(portID, channelID string, channel Channel, proof []
}
}
// NewQueryChannelClientStateResponse creates a newQueryChannelClientStateResponse instance
func NewQueryChannelClientStateResponse(identifiedClientState clienttypes.IdentifiedClientState, proof []byte, height int64) *QueryChannelClientStateResponse {
path := commitmenttypes.NewMerklePath(strings.Split(host.FullClientPath(identifiedClientState.ID, host.ClientStatePath()), "/"))
return &QueryChannelClientStateResponse{
IdentifiedClientState: &identifiedClientState,
Proof: proof,
ProofPath: path.Pretty(),
ProofHeight: uint64(height),
}
}
// NewQueryChannelConsensusStateResponse creates a newQueryChannelConsensusStateResponse instance
func NewQueryChannelConsensusStateResponse(clientID string, anyConsensusState *codectypes.Any, consensusStateHeight uint64, proof []byte, height int64) *QueryChannelConsensusStateResponse {
path := commitmenttypes.NewMerklePath(strings.Split(host.FullClientPath(clientID, host.ConsensusStatePath(consensusStateHeight)), "/"))
return &QueryChannelConsensusStateResponse{
ConsensusState: anyConsensusState,
ClientID: clientID,
Proof: proof,
ProofPath: path.Pretty(),
ProofHeight: uint64(height),
}
}
// NewQueryPacketCommitmentResponse creates a new QueryPacketCommitmentResponse instance
func NewQueryPacketCommitmentResponse(
portID, channelID string, sequence uint64, commitment []byte, proof []byte, height int64,
@ -62,19 +81,3 @@ func NewQueryNextSequenceReceiveResponse(
ProofHeight: uint64(height),
}
}
// NewQueryChannelClientStateRequest creates a new QueryChannelClientStateRequest instance.
func NewQueryChannelClientStateRequest(portID, channelID string) *QueryChannelClientStateRequest {
return &QueryChannelClientStateRequest{
PortID: portID,
ChannelID: channelID,
}
}
// NewQueryChannelConsensusStateRequest creates a new QueryChannelConsensusStateRequest instance.
func NewQueryChannelConsensusStateRequest(portID, channelID string) *QueryChannelConsensusStateRequest {
return &QueryChannelConsensusStateRequest{
PortID: portID,
ChannelID: channelID,
}
}

File diff suppressed because it is too large Load Diff

View File

@ -40,8 +40,14 @@ func KeyPrefixBytes(prefix int) []byte {
return []byte(fmt.Sprintf("%d/", prefix))
}
// FullClientPath returns the full path of a specific client path in the format:
// "clients/{clientID}/{path}" as a string.
func FullClientPath(clientID string, path string) string {
return string(FullKeyClientPath(clientID, []byte(path)))
}
// FullKeyClientPath returns the full path of specific client path in the format:
// "clients/{clientID}/{path}".
// "clients/{clientID}/{path}" as a byte array.
func FullKeyClientPath(clientID string, path []byte) []byte {
return append(KeyClientStorePrefix, append([]byte("/"+clientID+"/"), path...)...)
}

View File

@ -37,6 +37,16 @@ func (q Keeper) ConnectionChannels(c context.Context, req *channeltypes.QueryCon
return q.ChannelKeeper.ConnectionChannels(c, req)
}
// ChannelClientState implements the IBC QueryServer interface
func (q Keeper) ChannelClientState(c context.Context, req *channeltypes.QueryChannelClientStateRequest) (*channeltypes.QueryChannelClientStateResponse, error) {
return q.ChannelKeeper.ChannelClientState(c, req)
}
// ChannelConsensusState implements the IBC QueryServer interface
func (q Keeper) ChannelConsensusState(c context.Context, req *channeltypes.QueryChannelConsensusStateRequest) (*channeltypes.QueryChannelConsensusStateResponse, error) {
return q.ChannelKeeper.ChannelConsensusState(c, req)
}
// PacketCommitment implements the IBC QueryServer interface
func (q Keeper) PacketCommitment(c context.Context, req *channeltypes.QueryPacketCommitmentRequest) (*channeltypes.QueryPacketCommitmentResponse, error) {
return q.ChannelKeeper.PacketCommitment(c, req)

View File

@ -10,7 +10,6 @@ import (
clientkeeper "github.com/cosmos/cosmos-sdk/x/ibc/02-client/keeper"
clienttypes "github.com/cosmos/cosmos-sdk/x/ibc/02-client/types"
connectiontypes "github.com/cosmos/cosmos-sdk/x/ibc/03-connection/types"
channelkeeper "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/keeper"
channeltypes "github.com/cosmos/cosmos-sdk/x/ibc/04-channel/types"
)
@ -33,12 +32,7 @@ func NewQuerier(k Keeper, legacyQuerierCdc codec.JSONMarshaler) sdk.Querier {
case connectiontypes.SubModuleName:
err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC %s query endpoint", connectiontypes.SubModuleName)
case channeltypes.SubModuleName:
switch path[1] {
case channeltypes.QueryChannelClientState:
res, err = channelkeeper.QuerierChannelClientState(ctx, req, k.ChannelKeeper, legacyQuerierCdc)
default:
err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC %s query endpoint", channeltypes.SubModuleName)
}
err = sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown IBC %s query endpoint", channeltypes.SubModuleName)
default:
err = sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown IBC query endpoint")
}

View File

@ -43,12 +43,6 @@ func (suite *KeeperTestSuite) TestNewQuerier() {
true,
fmt.Sprintf("unknown IBC %s query endpoint", connectiontypes.SubModuleName),
},
{
"channel - QuerierChannelClientState",
[]string{channeltypes.SubModuleName, channeltypes.QueryChannelClientState},
false,
"",
},
{
"channel - invalid query",
[]string{channeltypes.SubModuleName, "foo"},

View File

@ -6,8 +6,6 @@ import (
"testing"
"time"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
@ -31,7 +29,6 @@ import (
ibctmtypes "github.com/cosmos/cosmos-sdk/x/ibc/07-tendermint/types"
commitmenttypes "github.com/cosmos/cosmos-sdk/x/ibc/23-commitment/types"
host "github.com/cosmos/cosmos-sdk/x/ibc/24-host"
"github.com/cosmos/cosmos-sdk/x/ibc/keeper"
"github.com/cosmos/cosmos-sdk/x/ibc/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
@ -71,7 +68,6 @@ type TestChain struct {
ChainID string
LastHeader ibctmtypes.Header // header for last block height committed
CurrentHeader abci.Header // header for current block height
Querier sdk.Querier // TODO: deprecate once clients are migrated to gRPC
QueryServer types.QueryServer
TxConfig client.TxConfig
@ -114,7 +110,6 @@ func NewTestChain(t *testing.T, chainID string) *TestChain {
}
app := simapp.SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance)
legacyQuerierCdc := codec.NewAminoCodec(app.LegacyAmino())
// create current header and call begin block
header := abci.Header{
@ -130,7 +125,6 @@ func NewTestChain(t *testing.T, chainID string) *TestChain {
ChainID: chainID,
App: app,
CurrentHeader: header,
Querier: keeper.NewQuerier(*app.IBCKeeper, legacyQuerierCdc),
QueryServer: app.IBCKeeper,
TxConfig: txConfig,
Vals: valSet,