Skip to content

Commit

Permalink
feat!: Add BlocksUntilNextEpoch query (#2106)
Browse files Browse the repository at this point in the history
* Add BlocksUntilNextEpoch query

* Fix epoch end in relay

* Adjust test comment

* Add changelog entries

* Add command to CLI endpoint and document

* Fix typo in doc

* Update x/ccv/provider/keeper/relay.go

Co-authored-by: insumity <karolos@informal.systems>

* Delete .changelog/unreleased/state-breaking/provider/2106-query-blocks-until-next-epoch.md

* Update docs/docs/frequently-asked-questions.md

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Clarify relaying delay for VSCPackets

* Change BlocksUntilNextEpoch to return 0 on first block of epoch

---------

Co-authored-by: insumity <karolos@informal.systems>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 30, 2024
1 parent 33e016a commit e5c567e
Show file tree
Hide file tree
Showing 9 changed files with 628 additions and 129 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Add a query to get the blocks until the next epoch begins
([\#2106](https://github.com/cosmos/interchain-security/pull/2106))
13 changes: 12 additions & 1 deletion docs/docs/frequently-asked-questions.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,15 @@ By setting the commission rate ahead of time, they can make sure that they immed
Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal).

## Can a Top N consumer chain become Opt-In or vice versa?
Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal).
Yes, by issuing a [`ConsumerModificationProposal`](./features/proposals.md#consumermodificationproposal).

## How do I know when the next validator update will be sent to the consumer chain?
Validator updates are sent to the consumer chain every `BlocksPerEpoch` blocks.
Keep in mind that depending on the status of relayers between the Hub and the consumer chain,
it might take a while for the validator update to be processed and applied on the consumer chain.

To query how many blocks are left until the next epoch starts,
run the following command:
```bash
interchain-security-pd query provider blocks-until-next-epoch
```
15 changes: 15 additions & 0 deletions proto/interchain_security/ccv/provider/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ service Query {
option (google.api.http).get =
"/interchain_security/ccv/provider/consumer_validators/{chain_id}";
}

// QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch
// starts and validator updates are sent to the consumer chains
rpc QueryBlocksUntilNextEpoch(QueryBlocksUntilNextEpochRequest)
returns (QueryBlocksUntilNextEpochResponse) {
option (google.api.http).get =
"/interchain_security/ccv/provider/blocks_until_next_epoch";
}
}

message QueryConsumerGenesisRequest { string chain_id = 1; }
Expand Down Expand Up @@ -326,3 +334,10 @@ message QueryOldestUnconfirmedVscResponse {
interchain_security.ccv.provider.v1.VscSendTimestamp vsc_send_timestamp = 1
[ (gogoproto.nullable) = false ];
}

message QueryBlocksUntilNextEpochRequest { }

message QueryBlocksUntilNextEpochResponse {
// The number of blocks until the next epoch starts
uint64 blocks_until_next_epoch = 1;
}
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 @@ -40,6 +40,7 @@ func NewQueryCmd() *cobra.Command {
cmd.AddCommand(CmdConsumerChainsValidatorHasToValidate())
cmd.AddCommand(CmdValidatorConsumerCommissionRate())
cmd.AddCommand(CmdOldestUnconfirmedVsc())
cmd.AddCommand(CmdBlocksUntilNextEpoch())
return cmd
}

Expand Down Expand Up @@ -596,3 +597,30 @@ func CmdOldestUnconfirmedVsc() *cobra.Command {

return cmd
}

func CmdBlocksUntilNextEpoch() *cobra.Command {
cmd := &cobra.Command{
Use: "blocks-until-next-epoch",
Short: "Query the number of blocks until the next epoch begins and validator updates are sent to consumer chains",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)

req := &types.QueryBlocksUntilNextEpochRequest{}
res, err := queryClient.QueryBlocksUntilNextEpoch(cmd.Context(), req)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
10 changes: 10 additions & 0 deletions x/ccv/provider/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,13 @@ func (k Keeper) QueryOldestUnconfirmedVsc(goCtx context.Context, req *types.Quer

return &types.QueryOldestUnconfirmedVscResponse{VscSendTimestamp: ts}, nil
}

// QueryBlocksUntilNextEpoch returns the number of blocks until the next epoch
func (k Keeper) QueryBlocksUntilNextEpoch(goCtx context.Context, req *types.QueryBlocksUntilNextEpochRequest) (*types.QueryBlocksUntilNextEpochResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// Calculate the blocks until the next epoch
blocksUntilNextEpoch := k.BlocksUntilNextEpoch(ctx)

return &types.QueryBlocksUntilNextEpochResponse{BlocksUntilNextEpoch: uint64(blocksUntilNextEpoch)}, nil
}
15 changes: 14 additions & 1 deletion x/ccv/provider/keeper/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func (k Keeper) EndBlockVSU(ctx sdk.Context) {
// notify the staking module to complete all matured unbonding ops
k.completeMaturedUnbondingOps(ctx)

if ctx.BlockHeight()%k.GetBlocksPerEpoch(ctx) == 0 {
if k.BlocksUntilNextEpoch(ctx) == 0 {
// only queue and send VSCPackets at the boundaries of an epoch

// collect validator updates
Expand All @@ -162,6 +162,19 @@ func (k Keeper) EndBlockVSU(ctx sdk.Context) {
}
}

// BlocksUntilNextEpoch returns the number of blocks until the next epoch starts
// Returns 0 if VSCPackets are sent in the current block,
// which is done in the first block of each epoch.
func (k Keeper) BlocksUntilNextEpoch(ctx sdk.Context) int64 {
blocksSinceEpochStart := ctx.BlockHeight() % k.GetBlocksPerEpoch(ctx)

if blocksSinceEpochStart == 0 {
return 0
} else {
return int64(k.GetBlocksPerEpoch(ctx) - blocksSinceEpochStart)
}
}

// SendVSCPackets iterates over all registered consumers and sends pending
// VSC packets to the chains with established CCV channels.
// If the CCV channel is not established for a consumer chain,
Expand Down
36 changes: 36 additions & 0 deletions x/ccv/provider/keeper/relay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -924,3 +924,39 @@ func TestQueueVSCPacketsWithPowerCapping(t *testing.T) {

require.Equal(t, expectedQueuedVSCPackets, actualQueuedVSCPackets)
}

// TestBlocksUntilNextEpoch tests the `BlocksUntilNextEpoch` method
func TestBlocksUntilNextEpoch(t *testing.T) {
providerKeeper, ctx, ctrl, _ := testkeeper.GetProviderKeeperAndCtx(t, testkeeper.NewInMemKeeperParams(t))
defer ctrl.Finish()

// 10 blocks constitute an epoch
params := providertypes.DefaultParams()
params.BlocksPerEpoch = 10
providerKeeper.SetParams(ctx, params)

// with block height of 1 we expect 9 blocks until the next epoch
ctx = ctx.WithBlockHeight(1)
require.Equal(t, int64(9), providerKeeper.BlocksUntilNextEpoch(ctx))

// with block height of 5 we expect 5 blocks until the next epoch
ctx = ctx.WithBlockHeight(5)
require.Equal(t, int64(5), providerKeeper.BlocksUntilNextEpoch(ctx))

// with block height of 10 we expect 0 blocks until the next epoch, since
// this epoch is the one where we send the VSC packet
ctx = ctx.WithBlockHeight(10)
require.Equal(t, int64(0), providerKeeper.BlocksUntilNextEpoch(ctx))

// with block height of 11 we expect 9 blocks until the next epoch
ctx = ctx.WithBlockHeight(11)
require.Equal(t, int64(9), providerKeeper.BlocksUntilNextEpoch(ctx))

// with block height of 15 we expect 5 blocks until the next epoch
ctx = ctx.WithBlockHeight(15)
require.Equal(t, int64(5), providerKeeper.BlocksUntilNextEpoch(ctx))

// with block height of 19 we expect 1 block until the next epoch
ctx = ctx.WithBlockHeight(19)
require.Equal(t, int64(1), providerKeeper.BlocksUntilNextEpoch(ctx))
}
Loading

0 comments on commit e5c567e

Please sign in to comment.