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

epoching API: add epoch msg range queries and add timestamp to validator lifecycle #119

Merged
merged 10 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from 9 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
8 changes: 7 additions & 1 deletion proto/babylon/epoching/v1/epoching.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ message QueuedMessage {
}
}

message QueuedMessageList {
uint64 epoch_number = 1;
repeated QueuedMessage msgs = 2;
}

enum ValState {
CREATED = 0;
BONDED = 1;
Expand All @@ -43,7 +48,8 @@ enum ValState {

message ValStateUpdate {
ValState state = 1;
uint64 height = 2;
uint64 block_height = 2;
google.protobuf.Timestamp block_time = 3 [(gogoproto.stdtime) = true];
}

message ValidatorLifecycle {
Expand Down
27 changes: 26 additions & 1 deletion proto/babylon/epoching/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ service Query {

// EpochMsgs queries the messages of a given epoch
rpc EpochMsgs(QueryEpochMsgsRequest) returns (QueryEpochMsgsResponse) {
option (google.api.http).get = "/babylon/epoching/v1/epoch_msgs/{epoch_num}";
option (google.api.http).get = "/babylon/epoching/v1/epochs/{epoch_num=*}/messages";
}

// LatestEpochMsgs queries the messages within a given number of most recent epochs
rpc LatestEpochMsgs(QueryLatestEpochMsgsRequest) returns (QueryLatestEpochMsgsResponse) {
option (google.api.http).get = "/babylon/epoching/v1/epochs:latest/messages";
}

// ValidatorLifecycle queries the lifecycle of a given validator
Expand Down Expand Up @@ -69,6 +74,26 @@ message QueryEpochMsgsResponse {
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryLatestEpochMsgsRequest is request type for the Query/LatestEpochMsgs RPC method
// it returns epoch msgs within epoch [min(1, end_epoch-epoch_count+1), end_epoch]
message QueryLatestEpochMsgsRequest {
// end_epoch is the number of the last epoch to query
uint64 end_epoch = 1;
// epoch_count is the number of epochs to query
uint64 epoch_count = 2;

cosmos.base.query.v1beta1.PageRequest pagination = 3;
}

// QueryLatestEpochMsgsResponse is response type for the Query/LatestEpochMsgs RPC method
message QueryLatestEpochMsgsResponse {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add pagination to this? Even though the query is supposed to be used for the "latest" epochs, someone could provide a large number as an argument.

// epoch_msg_map is a list of QueuedMessageList
// each QueuedMessageList has a field identifying the epoch number
repeated babylon.epoching.v1.QueuedMessageList latest_epoch_msgs = 1;

cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

message QueryValidatorLifecycleRequest {
string val_addr = 1;
}
Expand Down
6 changes: 3 additions & 3 deletions x/epoching/keeper/epoch_msg_queue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func FuzzHandleQueuedMsg_MsgWrappedDelegate(f *testing.F) {
require.NotNil(t, lc)
require.Equal(t, 1, len(lc.ValLife))
require.Equal(t, types.ValState_CREATED, lc.ValLife[0].State)
require.Equal(t, uint64(0), lc.ValLife[0].Height)
require.Equal(t, uint64(0), lc.ValLife[0].BlockHeight)

// delegate a random amount of tokens to the validator
numNewDels := rand.Int63n(1000) + 1
Expand Down Expand Up @@ -161,7 +161,7 @@ func FuzzHandleQueuedMsg_MsgWrappedUndelegate(f *testing.F) {
require.NotNil(t, lc)
require.Equal(t, 1, len(lc.ValLife))
require.Equal(t, types.ValState_CREATED, lc.ValLife[0].State)
require.Equal(t, uint64(0), lc.ValLife[0].Height)
require.Equal(t, uint64(0), lc.ValLife[0].BlockHeight)

// unbond a random amount of tokens from the validator
// Note that for any pair of delegator and validator, there can be `<=DefaultMaxEntries=7` concurrent undelegations at any time slot
Expand Down Expand Up @@ -245,7 +245,7 @@ func FuzzHandleQueuedMsg_MsgWrappedBeginRedelegate(f *testing.F) {
require.NotNil(t, lc)
require.Equal(t, 1, len(lc.ValLife))
require.Equal(t, types.ValState_CREATED, lc.ValLife[0].State)
require.Equal(t, uint64(0), lc.ValLife[0].Height)
require.Equal(t, uint64(0), lc.ValLife[0].BlockHeight)
}

// redelegate a random amount of tokens from val1 to val2
Expand Down
59 changes: 58 additions & 1 deletion x/epoching/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/babylonchain/babylon/x/epoching/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/query"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -74,7 +75,64 @@ func (k Keeper) EpochMsgs(c context.Context, req *types.QueryEpochMsgsRequest) (
return resp, nil
}

// LatestEpochMsgs handles the QueryLatestEpochMsgsRequest query
// TODO: test this API
func (k Keeper) LatestEpochMsgs(c context.Context, req *types.QueryLatestEpochMsgsRequest) (*types.QueryLatestEpochMsgsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)

if req.EpochCount == 0 {
return nil, sdkerrors.Wrapf(
sdkerrors.ErrInvalidRequest, "epoch_count should be specified and be larger than zero",
)
}

// the API will return epoch msgs between [min(1, end_epoch-epoch_count+1), end_epoch].
// NOTE:
// - epoch 0 does not have any queued msg
// - if not specified, endEpoch will be the current epoch
endEpoch := req.EndEpoch
if endEpoch == 0 {
endEpoch = k.GetEpoch(ctx).EpochNumber
}
beginEpoch := endEpoch - req.EpochCount + 1
if beginEpoch <= 1 {
beginEpoch = 1
}

latestEpochMsgs := []*types.QueuedMessageList{}

// iterate over queueLenStore since we only need to iterate over the epoch number
queueLenStore := k.msgQueueLengthStore(ctx)
pageRes, err := query.FilteredPaginate(queueLenStore, req.Pagination, func(key []byte, _ []byte, accumulate bool) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the closure/anonymous function passing here ☝️

// unmarshal to epoch number
epochNumber := sdk.BigEndianToUint64(key)
// only return queued msgs within [beginEpoch, endEpoch]
if epochNumber < beginEpoch || endEpoch < epochNumber {
return false, nil
}

if accumulate {
msgList := &types.QueuedMessageList{
EpochNumber: epochNumber,
Msgs: k.GetEpochMsgs(ctx, epochNumber),
}
latestEpochMsgs = append(latestEpochMsgs, msgList)
}
return true, nil
})
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

resp := &types.QueryLatestEpochMsgsResponse{
LatestEpochMsgs: latestEpochMsgs,
Pagination: pageRes,
}
return resp, nil
}

// ValidatorLifecycle handles the QueryValidatorLifecycleRequest query
// TODO: test this API
func (k Keeper) ValidatorLifecycle(c context.Context, req *types.QueryValidatorLifecycleRequest) (*types.QueryValidatorLifecycleResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
valAddr, err := sdk.ValAddressFromBech32(req.ValAddr)
Expand All @@ -85,5 +143,4 @@ func (k Keeper) ValidatorLifecycle(c context.Context, req *types.QueryValidatorL
return &types.QueryValidatorLifecycleResponse{
ValLife: lc,
}, nil
// TODO: test this API
}
9 changes: 7 additions & 2 deletions x/epoching/keeper/val_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ func (k Keeper) RecordNewValState(ctx sdk.Context, valAddr sdk.ValAddress, state
ValLife: []*types.ValStateUpdate{},
}
}
height, time := ctx.BlockHeight(), ctx.BlockTime()
valStateUpdate := types.ValStateUpdate{
State: state,
Height: uint64(ctx.BlockHeight()),
State: state,
BlockHeight: uint64(height),
BlockTime: &time,
}
lc.ValLife = append(lc.ValLife, &valStateUpdate)
k.SetValLifecycle(ctx, valAddr, lc)
Expand All @@ -37,6 +39,9 @@ func (k Keeper) SetValLifecycle(ctx sdk.Context, valAddr sdk.ValAddress, lc *typ
func (k Keeper) GetValLifecycle(ctx sdk.Context, valAddr sdk.ValAddress) *types.ValidatorLifecycle {
store := k.valLifecycleStore(ctx)
lcBytes := store.Get([]byte(valAddr))
if len(lcBytes) == 0 {
return nil
}
var lc types.ValidatorLifecycle
k.cdc.MustUnmarshal(lcBytes, &lc)
return &lc
Expand Down
Loading