Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add channel consensus state rpc #7619

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions modules/core/04-channel/v2/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,38 @@ func (q *queryServer) ChannelClientState(ctx context.Context, req *types.QueryCh
return res, nil
}

// ChannelConsensusState implements the Query/ChannelConsensusState gRPC method
func (q *queryServer) ChannelConsensusState(ctx context.Context, req *types.QueryChannelConsensusStateRequest) (*types.QueryChannelConsensusStateResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}

if err := host.ChannelIdentifierValidator(req.ChannelId); err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

channel, found := q.GetChannel(ctx, req.ChannelId)
if !found {
return nil, status.Error(codes.NotFound, errorsmod.Wrapf(types.ErrChannelNotFound, "channel-id: %s", req.ChannelId).Error())
}

consHeight := clienttypes.NewHeight(req.RevisionNumber, req.RevisionHeight)
consensusState, found := q.ClientKeeper.GetClientConsensusState(ctx, channel.ClientId, consHeight)
if !found {
return nil, status.Error(
codes.NotFound,
errorsmod.Wrapf(clienttypes.ErrConsensusStateNotFound, "client-id: %s", channel.ClientId).Error(),
)
}

anyConsensusState, err := clienttypes.PackConsensusState(consensusState)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

return types.NewQueryChannelConsensusStateResponse(channel.ClientId, anyConsensusState, nil, clienttypes.GetSelfHeight(ctx)), nil
}

// NextSequenceSend implements the Query/NextSequenceSend gRPC method
func (q *queryServer) NextSequenceSend(ctx context.Context, req *types.QueryNextSequenceSendRequest) (*types.QueryNextSequenceSendResponse, error) {
if req == nil {
Expand Down
108 changes: 108 additions & 0 deletions modules/core/04-channel/v2/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/keeper"
"github.com/cosmos/ibc-go/v9/modules/core/04-channel/v2/types"
commitmenttypes "github.com/cosmos/ibc-go/v9/modules/core/23-commitment/types"
"github.com/cosmos/ibc-go/v9/modules/core/exported"
ibctesting "github.com/cosmos/ibc-go/v9/testing"
)

Expand Down Expand Up @@ -190,6 +191,113 @@ func (suite *KeeperTestSuite) TestQueryChannelClientState() {
}
}

func (suite *KeeperTestSuite) TestQueryChannelConsensusState() {
var (
req *types.QueryChannelConsensusStateRequest
expConsensusState exported.ConsensusState
expClientID string
)

testCases := []struct {
msg string
malleate func()
expError error
}{
{
"success",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

expConsensusState, _ = suite.chainA.GetConsensusState(path.EndpointA.ClientID, path.EndpointA.GetClientLatestHeight())
suite.Require().NotNil(expConsensusState)
expClientID = path.EndpointA.ClientID

req = &types.QueryChannelConsensusStateRequest{
ChannelId: path.EndpointA.ChannelID,
RevisionNumber: path.EndpointA.GetClientLatestHeight().GetRevisionNumber(),
RevisionHeight: path.EndpointA.GetClientLatestHeight().GetRevisionHeight(),
}
},
nil,
},
{
"empty request",
func() {
req = nil
},
status.Error(codes.InvalidArgument, "empty request"),
},
{
"invalid channel ID",
func() {
req = &types.QueryChannelConsensusStateRequest{
ChannelId: "",
RevisionNumber: 0,
RevisionHeight: 1,
}
},
status.Error(codes.InvalidArgument, "identifier cannot be blank: invalid identifier"),
},
{
"channel not found",
func() {
req = &types.QueryChannelConsensusStateRequest{
ChannelId: "test-channel-id",
RevisionNumber: 0,
RevisionHeight: 1,
}
},
status.Error(codes.NotFound, fmt.Sprintf("channel-id: %s: channel not found", "test-channel-id")),
},
{
"consensus state for channel's connection not found",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetupV2()

req = &types.QueryChannelConsensusStateRequest{
ChannelId: path.EndpointA.ChannelID,
RevisionNumber: 0,
RevisionHeight: uint64(suite.chainA.GetContext().BlockHeight()), // use current height
}
},
status.Error(codes.NotFound, fmt.Sprintf("client-id: %s: consensus state not found", "07-tendermint-0")),
},
}

for _, tc := range testCases {
tc := tc

suite.Run(fmt.Sprintf("Case %s", tc.msg), func() {
suite.SetupTest() // reset

tc.malleate()
ctx := suite.chainA.GetContext()

queryServer := keeper.NewQueryServer(suite.chainA.App.GetIBCKeeper().ChannelKeeperV2)
res, err := queryServer.ChannelConsensusState(ctx, req)

expPass := tc.expError == nil
if 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)

// ensure UnpackInterfaces is defined
cachedValue := res.ConsensusState.GetCachedValue()
suite.Require().NotNil(cachedValue)
} else {
suite.Require().ErrorIs(err, tc.expError)
suite.Require().Nil(res)
}
})
}
}

func (suite *KeeperTestSuite) TestQueryPacketCommitment() {
var (
expCommitment []byte
Expand Down
2 changes: 2 additions & 0 deletions modules/core/04-channel/v2/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ type ClientKeeper interface {
GetClientTimestampAtHeight(ctx context.Context, clientID string, height exported.Height) (uint64, error)
// GetClientState gets a particular client from the store
GetClientState(ctx context.Context, clientID string) (exported.ClientState, bool)
// GetClientConsensusState gets the stored consensus state from a client at a given height.
GetClientConsensusState(ctx context.Context, clientID string, height exported.Height) (exported.ConsensusState, bool)
}
16 changes: 15 additions & 1 deletion modules/core/04-channel/v2/types/query.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package types

import clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"

clienttypes "github.com/cosmos/ibc-go/v9/modules/core/02-client/types"
)

// NewQueryChannelRequest creates and returns a new channel query request.
func NewQueryChannelRequest(channelID string) *QueryChannelRequest {
Expand Down Expand Up @@ -32,6 +36,16 @@ func NewQueryChannelClientStateResponse(identifiedClientState clienttypes.Identi
}
}

// NewQueryChannelConsensusStateResponse creates and returns a new ChannelConsensusState query response.
func NewQueryChannelConsensusStateResponse(clientID string, anyConsensusState *codectypes.Any, proof []byte, height clienttypes.Height) *QueryChannelConsensusStateResponse {
return &QueryChannelConsensusStateResponse{
ConsensusState: anyConsensusState,
ClientId: clientID,
Proof: proof,
ProofHeight: height,
}
}

// NewQueryNextSequenceSendRequest creates a new next sequence send query.
func NewQueryNextSequenceSendRequest(channelID string) *QueryNextSequenceSendRequest {
return &QueryNextSequenceSendRequest{
Expand Down
Loading
Loading