Skip to content

Commit

Permalink
fix: add missing query for next sequence send (#3417)
Browse files Browse the repository at this point in the history
Co-authored-by: Jim Fasarakis-Hilliard <[email protected]>
  • Loading branch information
expertdicer and DimitrisJim authored Apr 12, 2023
1 parent a18f96a commit 3a7106c
Show file tree
Hide file tree
Showing 10 changed files with 1,000 additions and 96 deletions.
2 changes: 1 addition & 1 deletion modules/core/04-channel/client/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func GetQueryCmd() *cobra.Command {
GetCmdQueryUnreceivedPackets(),
GetCmdQueryUnreceivedAcks(),
GetCmdQueryNextSequenceReceive(),
// TODO: next sequence Send ?
GetCmdQueryNextSequenceSend(),
)

return queryCmd
Expand Down
35 changes: 35 additions & 0 deletions modules/core/04-channel/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,38 @@ func GetCmdQueryNextSequenceReceive() *cobra.Command {

return cmd
}

// GetCmdQueryNextSequenceSend defines the command to query a next send sequence for a given channel
func GetCmdQueryNextSequenceSend() *cobra.Command {
cmd := &cobra.Command{
Use: "next-sequence-send [port-id] [channel-id]",
Short: "Query a next send sequence",
Long: "Query the next sequence send for a given channel",
Example: fmt.Sprintf(
"%s query %s %s next-sequence-send [port-id] [channel-id]", version.AppName, ibcexported.ModuleName, types.SubModuleName,
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
portID := args[0]
channelID := args[1]
prove, _ := cmd.Flags().GetBool(flags.FlagProve)

sequenceRes, err := utils.QueryNextSequenceSend(clientCtx, portID, channelID, prove)
if err != nil {
return err
}

clientCtx = clientCtx.WithHeight(int64(sequenceRes.ProofHeight.RevisionHeight))
return clientCtx.PrintProto(sequenceRes)
},
}

cmd.Flags().Bool(flags.FlagProve, true, "show proofs for the query results")
flags.AddQueryFlagsToCmd(cmd)

return cmd
}
36 changes: 36 additions & 0 deletions modules/core/04-channel/client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,42 @@ func queryNextSequenceRecvABCI(clientCtx client.Context, portID, channelID strin
return types.NewQueryNextSequenceReceiveResponse(sequence, proofBz, proofHeight), nil
}

// QueryNextSequenceSend returns the next sequence send.
// 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 QueryNextSequenceSend(
clientCtx client.Context, portID, channelID string, prove bool,
) (*types.QueryNextSequenceSendResponse, error) {
if prove {
return queryNextSequenceSendABCI(clientCtx, portID, channelID)
}

queryClient := types.NewQueryClient(clientCtx)
req := &types.QueryNextSequenceSendRequest{
PortId: portID,
ChannelId: channelID,
}
return queryClient.NextSequenceSend(context.Background(), req)
}

func queryNextSequenceSendABCI(clientCtx client.Context, portID, channelID string) (*types.QueryNextSequenceSendResponse, error) {
key := host.NextSequenceSendKey(portID, channelID)

value, proofBz, proofHeight, err := ibcclient.QueryTendermintProof(clientCtx, key)
if err != nil {
return nil, err
}

// check if next sequence send exists
if len(value) == 0 {
return nil, errorsmod.Wrapf(types.ErrChannelNotFound, "portID (%s), channelID (%s)", portID, channelID)
}

sequence := binary.BigEndian.Uint64(value)

return types.NewQueryNextSequenceSendResponse(sequence, proofBz, proofHeight), nil
}

// QueryPacketCommitment returns a packet commitment.
// If prove is true, it performs an ABCI store query in order to retrieve the merkle proof. Otherwise,
// it uses the gRPC query client.
Expand Down
36 changes: 36 additions & 0 deletions modules/core/04-channel/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,42 @@ func (q Keeper) NextSequenceReceive(c context.Context, req *types.QueryNextSeque
return types.NewQueryNextSequenceReceiveResponse(sequence, nil, selfHeight), nil
}

// NextSequenceSend implements the Query/NextSequenceSend gRPC method
func (q Keeper) NextSequenceSend(c context.Context, req *types.QueryNextSequenceSendRequest) (*types.QueryNextSequenceSendResponse, 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,
errorsmod.Wrapf(types.ErrChannelNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}

// Return the next sequence send for ordered channels. Unordered channels
// do not make use of the next sequence send.
var sequence uint64
if channel.Ordering != types.UNORDERED {
sequence, found = q.GetNextSequenceSend(ctx, req.PortId, req.ChannelId)
if !found {
return nil, status.Error(
codes.NotFound,
errorsmod.Wrapf(types.ErrSequenceSendNotFound, "port-id: %s, channel-id %s", req.PortId, req.ChannelId).Error(),
)
}
}
selfHeight := clienttypes.GetSelfHeight(ctx)
return types.NewQueryNextSequenceSendResponse(sequence, nil, selfHeight), nil
}

func validategRPCRequest(portID, channelID string) error {
if err := host.PortIdentifierValidator(portID); err != nil {
return status.Error(codes.InvalidArgument, err.Error())
Expand Down
101 changes: 101 additions & 0 deletions modules/core/04-channel/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1565,3 +1565,104 @@ func (suite *KeeperTestSuite) TestQueryNextSequenceReceive() {
})
}
}

func (suite *KeeperTestSuite) TestQueryNextSequenceSend() {
var (
req *types.QueryNextSequenceSendRequest
expSeq uint64
)

testCases := []struct {
msg string
malleate func()
expPass bool
}{
{
"empty request",
func() {
req = nil
},
false,
},
{
"invalid port ID",
func() {
req = &types.QueryNextSequenceSendRequest{
PortId: "",
ChannelId: "test-channel-id",
}
},
false,
},
{
"invalid channel ID",
func() {
req = &types.QueryNextSequenceSendRequest{
PortId: "test-port-id",
ChannelId: "",
}
},
false,
},
{
"channel not found",
func() {
req = &types.QueryNextSequenceSendRequest{
PortId: "test-port-id",
ChannelId: "test-channel-id",
}
},
false,
},
{
"basic success on unordered channel returns zero",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
suite.coordinator.Setup(path)

expSeq = 0
req = &types.QueryNextSequenceSendRequest{
PortId: path.EndpointA.ChannelConfig.PortID,
ChannelId: path.EndpointA.ChannelID,
}
},
true,
},
{
"basic success on ordered channel returns the set send sequence",
func() {
path := ibctesting.NewPath(suite.chainA, suite.chainB)
path.SetChannelOrdered()
suite.coordinator.Setup(path)

expSeq = 3
seq := uint64(3)
suite.chainA.App.GetIBCKeeper().ChannelKeeper.SetNextSequenceSend(suite.chainA.GetContext(), path.EndpointA.ChannelConfig.PortID, path.EndpointA.ChannelID, seq)

req = &types.QueryNextSequenceSendRequest{
PortId: path.EndpointA.ChannelConfig.PortID,
ChannelId: path.EndpointA.ChannelID,
}
},
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.NextSequenceSend(ctx, req)

if tc.expPass {
suite.Require().NoError(err)
suite.Require().NotNil(res)
suite.Require().Equal(expSeq, res.NextSequenceSend)
} else {
suite.Require().Error(err)
}
})
}
}
11 changes: 11 additions & 0 deletions modules/core/04-channel/types/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,14 @@ func NewQueryNextSequenceReceiveResponse(
ProofHeight: height,
}
}

// NewQueryNextSequenceSendResponse creates a new QueryNextSequenceSendResponse instance
func NewQueryNextSequenceSendResponse(
sequence uint64, proof []byte, height clienttypes.Height,
) *QueryNextSequenceSendResponse {
return &QueryNextSequenceSendResponse{
NextSequenceSend: sequence,
Proof: proof,
ProofHeight: height,
}
}
Loading

0 comments on commit 3a7106c

Please sign in to comment.