-
Notifications
You must be signed in to change notification settings - Fork 170
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 bls pubkey set api #232
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,49 @@ package keeper | |
import ( | ||
"context" | ||
"github.com/babylonchain/babylon/x/checkpointing/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/jinzhu/copier" | ||
"google.golang.org/grpc/codes" | ||
"google.golang.org/grpc/status" | ||
) | ||
|
||
func (k Keeper) BlsPublicKeyList(c context.Context, req *types.QueryBlsPublicKeyListRequest) (*types.QueryBlsPublicKeyListResponse, error) { | ||
panic("TODO: implement this") | ||
if req == nil { | ||
return nil, status.Error(codes.InvalidArgument, "invalid request") | ||
} | ||
sdkCtx := sdk.UnwrapSDKContext(c) | ||
valBLSKeys, err := k.GetBLSPubKeySet(sdkCtx, req.EpochNum) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if req.Pagination == nil { | ||
return &types.QueryBlsPublicKeyListResponse{ | ||
ValidatorWithBlsKeys: valBLSKeys, | ||
}, nil | ||
} | ||
|
||
total := uint64(len(valBLSKeys)) | ||
start := req.Pagination.Offset | ||
if start > total-1 { | ||
return nil, status.Error(codes.InvalidArgument, "pagination offset out of range") | ||
} | ||
var end uint64 | ||
if req.Pagination.Limit == 0 { | ||
end = total | ||
} else { | ||
end = req.Pagination.Limit + start | ||
} | ||
if end > total { | ||
end = total | ||
} | ||
var copiedValBLSKeys []*types.ValidatorWithBlsKey | ||
err = copier.Copy(&copiedValBLSKeys, valBLSKeys[start:end]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we want to copy it rather than using the slice directly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We want a deep copy since we don't want the passed slice to be affected by the original one. But, in this specific case, we do not need to copy it |
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &types.QueryBlsPublicKeyListResponse{ | ||
ValidatorWithBlsKeys: copiedValBLSKeys, | ||
}, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package keeper_test | ||
|
||
import ( | ||
"github.com/babylonchain/babylon/app" | ||
"github.com/babylonchain/babylon/crypto/bls12381" | ||
"github.com/babylonchain/babylon/testutil/datagen" | ||
checkpointingkeeper "github.com/babylonchain/babylon/x/checkpointing/keeper" | ||
"github.com/babylonchain/babylon/x/checkpointing/types" | ||
"github.com/babylonchain/babylon/x/epoching/testepoching" | ||
"github.com/cosmos/cosmos-sdk/baseapp" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/types/query" | ||
"github.com/stretchr/testify/require" | ||
"math/rand" | ||
"testing" | ||
) | ||
|
||
// FuzzQueryBLSKeySet does the following checks | ||
// 1. check the query when there's only a genesis validator | ||
// 2. check the query when there are n+1 validators without pagination | ||
// 3. check the query when there are n+1 validators with pagination | ||
func FuzzQueryBLSKeySet(f *testing.F) { | ||
datagen.AddRandomSeedsToFuzzer(f, 10) | ||
f.Fuzz(func(t *testing.T, seed int64) { | ||
rand.Seed(seed) | ||
// a genesis validator is generated for setup | ||
helper := testepoching.NewHelper(t) | ||
ek := helper.EpochingKeeper | ||
ck := helper.App.CheckpointingKeeper | ||
querier := checkpointingkeeper.Querier{Keeper: ck} | ||
queryHelper := baseapp.NewQueryServerTestHelper(helper.Ctx, helper.App.InterfaceRegistry()) | ||
types.RegisterQueryServer(queryHelper, querier) | ||
queryClient := types.NewQueryClient(queryHelper) | ||
msgServer := checkpointingkeeper.NewMsgServerImpl(ck) | ||
Comment on lines
+28
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems that L28-34 will be used quite frequently. Maybe they can be a part of the response of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would that be strange that we have checkpointing stuff inside an epoching test helper? I have thought about having a helper specifically for checkpointing, but I think that would be a little bit heavy for now |
||
// add BLS pubkey to the genesis validator | ||
valSet := ek.GetValidatorSet(helper.Ctx, 0) | ||
require.Len(t, valSet, 1) | ||
genesisVal := valSet[0] | ||
genesisBLSPubkey := bls12381.GenPrivKey().PubKey() | ||
err := ck.CreateRegistration(helper.Ctx, genesisBLSPubkey, genesisVal.Addr) | ||
require.NoError(t, err) | ||
|
||
// BeginBlock of block 1, and thus entering epoch 1 | ||
ctx := helper.BeginBlock() | ||
epoch := ek.GetEpoch(ctx) | ||
require.Equal(t, uint64(1), epoch.EpochNumber) | ||
|
||
// 1. query public key list when there's only a genesis validator | ||
queryRequest := &types.QueryBlsPublicKeyListRequest{ | ||
EpochNum: 1, | ||
} | ||
res, err := queryClient.BlsPublicKeyList(ctx, queryRequest) | ||
require.NoError(t, err) | ||
require.Len(t, res.ValidatorWithBlsKeys, 1) | ||
require.Equal(t, res.ValidatorWithBlsKeys[0].BlsPubKey, genesisBLSPubkey.Bytes()) | ||
require.Equal(t, res.ValidatorWithBlsKeys[0].ValidatorAddress, genesisVal.Addr.String()) | ||
|
||
// add n new validators via MsgWrappedCreateValidator | ||
n := rand.Intn(3) + 1 | ||
addrs := app.AddTestAddrs(helper.App, helper.Ctx, n, sdk.NewInt(100000000)) | ||
|
||
wcvMsgs := make([]*types.MsgWrappedCreateValidator, n) | ||
for i := 0; i < n; i++ { | ||
msg, err := buildMsgWrappedCreateValidator(addrs[i]) | ||
require.NoError(t, err) | ||
wcvMsgs[i] = msg | ||
_, err = msgServer.WrappedCreateValidator(ctx, msg) | ||
require.NoError(t, err) | ||
} | ||
|
||
// EndBlock of block 1 | ||
ctx = helper.EndBlock() | ||
|
||
// go to BeginBlock of block 11, and thus entering epoch 2 | ||
for i := uint64(0); i < ek.GetParams(ctx).EpochInterval; i++ { | ||
ctx = helper.GenAndApplyEmptyBlock() | ||
} | ||
epoch = ek.GetEpoch(ctx) | ||
require.Equal(t, uint64(2), epoch.EpochNumber) | ||
|
||
// 2. query BLS public keys when there are n+1 validators without pagination | ||
req := types.QueryBlsPublicKeyListRequest{ | ||
EpochNum: 2, | ||
} | ||
resp, err := queryClient.BlsPublicKeyList(ctx, &req) | ||
require.NoError(t, err) | ||
require.Len(t, resp.ValidatorWithBlsKeys, n+1) | ||
expectedValSet := ek.GetValidatorSet(ctx, 2) | ||
require.Len(t, expectedValSet, n+1) | ||
for i, expectedVal := range expectedValSet { | ||
require.Equal(t, expectedVal.Addr.String(), resp.ValidatorWithBlsKeys[i].ValidatorAddress) | ||
} | ||
|
||
// 3.1 query BLS public keys when there are n+1 validators with limit pagination | ||
req = types.QueryBlsPublicKeyListRequest{ | ||
EpochNum: 2, | ||
Pagination: &query.PageRequest{ | ||
Limit: 1, | ||
}, | ||
} | ||
resp, err = queryClient.BlsPublicKeyList(ctx, &req) | ||
require.NoError(t, err) | ||
require.Len(t, resp.ValidatorWithBlsKeys, 1) | ||
|
||
// 3.2 query BLS public keys when there are n+1 validators with offset pagination | ||
req = types.QueryBlsPublicKeyListRequest{ | ||
EpochNum: 2, | ||
Pagination: &query.PageRequest{ | ||
Offset: 1, | ||
}, | ||
Comment on lines
+108
to
+110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great, you even have a test for the pagination 👍 |
||
} | ||
resp, err = queryClient.BlsPublicKeyList(ctx, &req) | ||
require.NoError(t, err) | ||
require.Len(t, resp.ValidatorWithBlsKeys, n) | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember Cosmos SDK provides functionalities for getting the paginated response given a pagination request, e.g., something like https://github.com/babylonchain/babylon/blob/dev/x/epoching/keeper/grpc_query.go#L67-L92
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, thanks for the pointer. Thing is that the set is obtained from epoching store and sorted. I'm not sure how to use this functionality in our case