Skip to content

Commit

Permalink
feat: add oldest_unconfirmed_vsc query (backport #1740) (#1745)
Browse files Browse the repository at this point in the history
* feat: add oldest_unconfirmed_vsc query  (#1740)

* fix logging in ibc_module.go

* update proto files

* add query to keeper

* add CLI query

* add changelog entry

* change name to oldest_unconfirmed_vsc

* update changelog entry

* fix error messages

* fix UT

* adding comment for clarity

* QueryOldestUnconfirmVsc -> QueryOldestUnconfirmedVsc

* improve UT

(cherry picked from commit 60c4fd0)

# Conflicts:
#	.changelog/v3.2.0/features/consumer/1164-provider-info-query.md

* update changelog entries

---------

Co-authored-by: Marius Poke <[email protected]>
  • Loading branch information
mergify[bot] and mpoke authored Apr 2, 2024
1 parent 9c22abe commit f716502
Show file tree
Hide file tree
Showing 11 changed files with 705 additions and 94 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Introduce the gRPC query `/interchain_security/ccv/provider/oldest_unconfirmed_vsc/{chain_id}`
and CLI command `interchain-security-pd q provider oldest_unconfirmed_vsc`
to retrieve the send timestamp of the oldest unconfirmed VSCPacket by chain id.
([\#1740](https://github.com/cosmos/interchain-security/pull/1740))
13 changes: 13 additions & 0 deletions proto/interchain_security/ccv/provider/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ service Query {
option (google.api.http).get =
"/interchain_security/ccv/provider/params";
}

// QueryOldestUnconfirmedVsc returns the send timestamp of the oldest unconfirmed VSCPacket for a given chainID
rpc QueryOldestUnconfirmedVsc(QueryOldestUnconfirmedVscRequest)
returns (QueryOldestUnconfirmedVscResponse) {
option (google.api.http).get =
"/interchain_security/ccv/provider/oldest_unconfirmed_vsc/{chain_id}";
}
}

message QueryConsumerGenesisRequest { string chain_id = 1; }
Expand Down Expand Up @@ -212,3 +219,9 @@ message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}

message QueryOldestUnconfirmedVscRequest { string chain_id = 1; }

message QueryOldestUnconfirmedVscResponse {
interchain_security.ccv.provider.v1.VscSendTimestamp vsc_send_timestamp = 1
[ (gogoproto.nullable) = false ];
}
2 changes: 1 addition & 1 deletion x/ccv/consumer/ibc_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ func (am AppModule) OnRecvPacket(
ackErr = err
logger.Error(fmt.Sprintf("%s sequence %d", ackErr.Error(), packet.Sequence))
} else {
logger.Info("successfully handled VSCPacket sequence: %d", packet.Sequence)
logger.Info("successfully handled VSCPacket", "sequence", packet.Sequence)
}
}

Expand Down
28 changes: 28 additions & 0 deletions x/ccv/provider/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func NewQueryCmd() *cobra.Command {
cmd.AddCommand(CmdProposedConsumerChains())
cmd.AddCommand(CmdAllPairsValConAddrByConsumerChainID())
cmd.AddCommand(CmdProviderParameters())
cmd.AddCommand(CmdOldestUnconfirmedVsc())
return cmd
}

Expand Down Expand Up @@ -410,3 +411,30 @@ $ %s query provider params
return cmd

}

func CmdOldestUnconfirmedVsc() *cobra.Command {
cmd := &cobra.Command{
Use: "oldest_unconfirmed_vsc [chainid]",
Short: "Query the send timestamp of the oldest unconfirmed VSCPacket by chain id",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

req := types.QueryOldestUnconfirmedVscRequest{ChainId: args[0]}
res, err := queryClient.QueryOldestUnconfirmedVsc(cmd.Context(), &req)
if err != nil {
return err
}

return clientCtx.PrintProto(&res.VscSendTimestamp)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
4 changes: 2 additions & 2 deletions x/ccv/provider/ibc_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (am AppModule) OnRecvPacket(
data := *consumerPacket.GetVscMaturedPacketData()
err = am.keeper.OnRecvVSCMaturedPacket(ctx, packet, data)
if err == nil {
logger.Info("successfully handled VSCMaturedPacket sequence: %d", packet.Sequence)
logger.Info("successfully handled VSCMaturedPacket", "sequence", packet.Sequence)
eventAttributes = append(eventAttributes, sdk.NewAttribute(ccv.AttributeValSetUpdateID, strconv.Itoa(int(data.ValsetUpdateId))))
}
case ccv.SlashPacket:
Expand All @@ -211,7 +211,7 @@ func (am AppModule) OnRecvPacket(
ackResult, err = am.keeper.OnRecvSlashPacket(ctx, packet, data)
if err == nil {
ack = channeltypes.NewResultAcknowledgement(ackResult)
logger.Info("successfully handled SlashPacket sequence: %d", packet.Sequence)
logger.Info("successfully handled SlashPacket", "sequence", packet.Sequence)
eventAttributes = append(eventAttributes, sdk.NewAttribute(ccv.AttributeValSetUpdateID, strconv.Itoa(int(data.ValsetUpdateId))))
}
default:
Expand Down
36 changes: 35 additions & 1 deletion x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ func (k Keeper) QueryConsumerGenesis(c context.Context, req *types.QueryConsumer

gen, ok := k.GetConsumerGenesis(ctx, req.ChainId)
if !ok {
return nil, errorsmod.Wrap(types.ErrUnknownConsumerChainId, req.ChainId)
return nil, status.Error(
codes.NotFound,
errorsmod.Wrap(types.ErrUnknownConsumerChainId, req.ChainId).Error(),
)
}

return &types.QueryConsumerGenesisResponse{GenesisState: gen}, nil
Expand Down Expand Up @@ -221,3 +224,34 @@ func (k Keeper) QueryParams(c context.Context, _ *types.QueryParamsRequest) (*ty

return &types.QueryParamsResponse{Params: params}, nil
}

func (k Keeper) QueryOldestUnconfirmedVsc(goCtx context.Context, req *types.QueryOldestUnconfirmedVscRequest) (*types.QueryOldestUnconfirmedVscResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

if req == nil {
return nil, status.Errorf(codes.InvalidArgument, "empty request")
}

if req.ChainId == "" {
return nil, status.Errorf(codes.InvalidArgument, "invalid request: chain id cannot be empty")
}

if _, consumerRegistered := k.GetConsumerClientId(ctx, req.ChainId); !consumerRegistered {
return nil, status.Error(
codes.NotFound,
errorsmod.Wrap(types.ErrUnknownConsumerChainId, req.ChainId).Error(),
)
}

// Note that GetFirstVscSendTimestamp returns the send timestamp of the oldest
// unconfirmed VSCPacket as these timestamps are deleted when handling VSCMaturedPackets
ts, found := k.GetFirstVscSendTimestamp(ctx, req.ChainId)
if !found {
return nil, status.Error(
codes.NotFound,
errorsmod.Wrap(types.ErrNoUnconfirmedVSCPacket, req.ChainId).Error(),
)
}

return &types.QueryOldestUnconfirmedVscResponse{VscSendTimestamp: ts}, nil
}
44 changes: 44 additions & 0 deletions x/ccv/provider/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper_test

import (
"testing"
"time"

"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -54,3 +55,46 @@ func TestQueryAllPairsValConAddrByConsumerChainID(t *testing.T) {
require.Equal(t, &consumerKey, response.PairValConAddr[0].ConsumerKey)
require.Equal(t, &expectedResult, response.PairValConAddr[0])
}

func TestQueryOldestUnconfirmedVsc(t *testing.T) {
chainID := consumer

pk, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
defer ctrl.Finish()

now := time.Now().UTC()
pk.SetVscSendTimestamp(ctx, chainID, 2, now)
pk.SetVscSendTimestamp(ctx, chainID, 1, now)
pk.SetConsumerClientId(ctx, chainID, "client-1")

// Request is nil
_, err := pk.QueryOldestUnconfirmedVsc(ctx, nil)
require.Error(t, err)

// Request with chainId is empty
_, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{})
require.Error(t, err)

// Request with chainId is invalid
_, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: "invalidChainId"})
require.Error(t, err)

// Request is valid
response, err := pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: chainID})
require.NoError(t, err)
expectedResult := types.VscSendTimestamp{
VscId: 1,
Timestamp: now,
}
require.Equal(t, expectedResult, response.VscSendTimestamp)

// Make sure that the oldest is queried
pk.DeleteVscSendTimestamp(ctx, chainID, 1)
response, err = pk.QueryOldestUnconfirmedVsc(ctx, &types.QueryOldestUnconfirmedVscRequest{ChainId: chainID})
require.NoError(t, err)
expectedResult = types.VscSendTimestamp{
VscId: 2,
Timestamp: now,
}
require.Equal(t, expectedResult, response.VscSendTimestamp)
}
1 change: 1 addition & 0 deletions x/ccv/provider/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ var (
ErrInvalidConsumerClient = errorsmod.Register(ModuleName, 16, "ccv channel is not built on correct client")
ErrDuplicateConsumerChain = errorsmod.Register(ModuleName, 17, "consumer chain already exists")
ErrConsumerChainNotFound = errorsmod.Register(ModuleName, 18, "consumer chain not found")
ErrNoUnconfirmedVSCPacket = errorsmod.Register(ModuleName, 19, "no unconfirmed vsc packet for this chain id")
)
2 changes: 1 addition & 1 deletion x/ccv/provider/types/provider.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f716502

Please sign in to comment.