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

feat: add oldest_unconfirmed_vsc query (backport #1740) #1745

Merged
merged 3 commits into from
Apr 2, 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
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
Loading