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: checkpointing/implement cli #78

Merged
merged 5 commits into from
Jul 28, 2022
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
22 changes: 22 additions & 0 deletions crypto/bls12381/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package bls12381

import (
"encoding/hex"
"errors"
blst "github.com/supranational/blst/bindings/go"
)
Expand Down Expand Up @@ -69,6 +70,27 @@ func (sig Signature) Equal(s Signature) bool {
return string(sig) == string(s)
}

func NewBLSSigFromHex(s string) (Signature, error) {
bz, err := hex.DecodeString(s)
if err != nil {
return nil, err
}

var sig Signature
err = sig.Unmarshal(bz)
if err != nil {
return nil, err
}

return sig, nil
}

func (sig Signature) String() string {
bz := sig.MustMarshal()

return hex.EncodeToString(bz)
}

func (pk PublicKey) Marshal() ([]byte, error) {
return pk, nil
}
Expand Down
76 changes: 73 additions & 3 deletions x/checkpointing/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package cli

import (
"context"
"errors"
"fmt"
// "strings"
"github.com/cosmos/cosmos-sdk/client/flags"
"strconv"

"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
// "github.com/cosmos/cosmos-sdk/client/flags"
// sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/babylonchain/babylon/x/checkpointing/types"
)
Expand All @@ -25,6 +26,75 @@ func GetQueryCmd(queryRoute string) *cobra.Command {
}

cmd.AddCommand(CmdQueryParams())
cmd.AddCommand(CmdRawCheckpoint())
cmd.AddCommand(CmdRawCheckpointList())

return cmd
}

// CmdRawCheckpointList defines the cobra command to query raw checkpoints by status
func CmdRawCheckpointList() *cobra.Command {
vitsalis marked this conversation as resolved.
Show resolved Hide resolved
cmd := &cobra.Command{
Use: "raw-checkpoint-list [status]",
Short: "retrieve the checkpoints by status",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)

queryClient := types.NewQueryClient(clientCtx)

pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}

status, exists := types.CheckpointStatus_value[args[0]]
if !exists {
return errors.New("invalid status")
}

params := types.NewQueryRawCheckpointListRequest(pageReq, types.CheckpointStatus(status))
res, err := queryClient.RawCheckpointList(context.Background(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}

// CmdRawCheckpoint defines the cobra command to query the raw checkpoint by epoch number
func CmdRawCheckpoint() *cobra.Command {
cmd := &cobra.Command{
Use: "raw-checkpoint [epoch_number]",
Short: "retrieve the checkpoint by epoch number",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)

queryClient := types.NewQueryClient(clientCtx)

epoch_num, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

params := types.NewQueryRawCheckpointRequest(epoch_num)
res, err := queryClient.RawCheckpoint(context.Background(), params)
if err != nil {
return err
}

return clientCtx.PrintProto(res)
Copy link
Contributor

Choose a reason for hiding this comment

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

So this will print the raw checkpoint in JSON, which has a field that you should sign, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you meaning signing LastCommitHash and the epoch number using the querier's BLS private key? If so, yes

},
}

flags.AddQueryFlagsToCmd(cmd)

return cmd
}
55 changes: 49 additions & 6 deletions x/checkpointing/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ package cli

import (
"fmt"
"time"

"github.com/babylonchain/babylon/crypto/bls12381"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"strconv"

"github.com/cosmos/cosmos-sdk/client"
// "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/babylonchain/babylon/x/checkpointing/types"
)

var (
DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds())
)

const (
flagPacketTimeoutTimestamp = "packet-timeout-timestamp"
listSeparator = ","
Expand All @@ -30,5 +29,49 @@ func GetTxCmd() *cobra.Command {
RunE: client.ValidateCmd,
}

cmd.AddCommand(CmdTxAddBlsSig())

return cmd
}

func CmdTxAddBlsSig() *cobra.Command {
cmd := &cobra.Command{
Use: "submit [epoch_number] [last_commit_hash] [bls_sig] [signer address]",
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't it rather ask for the BLS private key file for example, or the name of the BLS key in the keyring, so that it can sign for you? Or maybe have a CLI command to sign, before you can call submit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh this CLI is just for submission. Signing will be in a future CLI

Short: "submit a BLS signature",
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

epoch_num, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}

lch, err := types.NewLastCommitHashFromHex(args[1])
if err != nil {
return err
}

blsSig, err := bls12381.NewBLSSigFromHex(args[2])
if err != nil {
return err
}

addr, err := sdk.ValAddressFromBech32(args[3])
if err != nil {
return err
}

msg := types.NewMsgAddBlsSig(epoch_num, lch, blsSig, addr)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
2 changes: 1 addition & 1 deletion x/checkpointing/keeper/ckpt_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (cs CheckpointsState) GetRawCkptWithMeta(epoch uint64) (*types.RawCheckpoin
}

// GetRawCkptsWithMetaByStatus retrieves raw checkpoints with meta by their status by the descending order of epoch
func (cs CheckpointsState) GetRawCkptsWithMetaByStatus(status types.CheckpointStatus, f func(sig *types.RawCheckpointWithMeta) bool) error {
func (cs CheckpointsState) GetRawCkptsWithMetaByStatus(status types.CheckpointStatus, f func(*types.RawCheckpointWithMeta) bool) error {
store := prefix.NewStore(cs.checkpoints, types.CkptsObjectPrefix)
iter := store.ReverseIterator(nil, nil)
defer iter.Close()
Expand Down
52 changes: 47 additions & 5 deletions x/checkpointing/keeper/grpc_query_checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,61 @@ package keeper
import (
"context"
"github.com/babylonchain/babylon/x/checkpointing/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

var _ types.QueryServer = Keeper{}

func (k Keeper) RawCheckpointList(c context.Context, req *types.QueryRawCheckpointListRequest) (*types.QueryRawCheckpointListResponse, error) {
panic("TODO: implement this")
// RawCheckpointList returns a list of checkpoint by status in the ascending order of epoch
func (k Keeper) RawCheckpointList(ctx context.Context, req *types.QueryRawCheckpointListRequest) (*types.QueryRawCheckpointListResponse, error) {
vitsalis marked this conversation as resolved.
Show resolved Hide resolved
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}
var checkpointList []*types.RawCheckpoint

sdkCtx := sdk.UnwrapSDKContext(ctx)

store := k.CheckpointsState(sdkCtx).checkpoints
pageRes, err := query.FilteredPaginate(store, req.Pagination, func(_ []byte, value []byte, accumulate bool) (bool, error) {
if accumulate {
ckptWithMeta, err := types.BytesToCkptWithMeta(k.cdc, value)
if err != nil {
return false, err
}
if ckptWithMeta.Status == req.Status {
checkpointList = append(checkpointList, ckptWithMeta.Ckpt)
}
}
return true, nil
})

if err != nil {
return nil, err
}

return &types.QueryRawCheckpointListResponse{RawCheckpoints: checkpointList, Pagination: pageRes}, nil
}

func (k Keeper) RecentRawCheckpointList(c context.Context, req *types.QueryRecentRawCheckpointListRequest) (*types.QueryRecentRawCheckpointListResponse, error) {
panic("TODO: implement this")
// RawCheckpoint returns a checkpoint by epoch number
func (k Keeper) RawCheckpoint(ctx context.Context, req *types.QueryRawCheckpointRequest) (*types.QueryRawCheckpointResponse, error) {
vitsalis marked this conversation as resolved.
Show resolved Hide resolved
if req == nil {
return nil, status.Error(codes.InvalidArgument, "invalid request")
}

sdkCtx := sdk.UnwrapSDKContext(ctx)

ckptWithMeta, err := k.CheckpointsState(sdkCtx).GetRawCkptWithMeta(req.EpochNum)
if err != nil {
return nil, err
}

return &types.QueryRawCheckpointResponse{RawCheckpoint: ckptWithMeta.Ckpt}, nil
}

func (k Keeper) RawCheckpoint(c context.Context, req *types.QueryRawCheckpointRequest) (*types.QueryRawCheckpointResponse, error) {
func (k Keeper) RecentRawCheckpointList(c context.Context, req *types.QueryRecentRawCheckpointListRequest) (*types.QueryRecentRawCheckpointListResponse, error) {
panic("TODO: implement this")
}

Expand Down
14 changes: 13 additions & 1 deletion x/checkpointing/types/msgs.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
package types

import sdk "github.com/cosmos/cosmos-sdk/types"
import (
"github.com/babylonchain/babylon/crypto/bls12381"
sdk "github.com/cosmos/cosmos-sdk/types"
)

var (
// Ensure that MsgInsertHeader implements all functions of the Msg interface
_ sdk.Msg = (*MsgAddBlsSig)(nil)
)

func NewMsgAddBlsSig(epochNum uint64, lch LastCommitHash, sig bls12381.Signature, addr sdk.ValAddress) *MsgAddBlsSig {
return &MsgAddBlsSig{BlsSig: &BlsSig{
EpochNum: epochNum,
LastCommitHash: lch,
BlsSig: &sig,
SignerAddress: addr.String(),
}}
}

func (m *MsgAddBlsSig) ValidateBasic() error {
// This function validates stateless message elements
_, err := sdk.AccAddressFromBech32(m.BlsSig.SignerAddress)
Expand Down
16 changes: 16 additions & 0 deletions x/checkpointing/types/querier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package types

import "github.com/cosmos/cosmos-sdk/types/query"

// NewQueryRawCheckpointRequest creates a new instance of QueryRawCheckpointRequest.
func NewQueryRawCheckpointRequest(epoch_num uint64) *QueryRawCheckpointRequest {
return &QueryRawCheckpointRequest{EpochNum: epoch_num}
}

// NewQueryRawCheckpointListRequest creates a new instance of QueryRawCheckpointListRequest.
func NewQueryRawCheckpointListRequest(req *query.PageRequest, status CheckpointStatus) *QueryRawCheckpointListRequest {
return &QueryRawCheckpointListRequest{
Status: status,
Pagination: req,
}
}
22 changes: 22 additions & 0 deletions x/checkpointing/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
"bytes"
"encoding/hex"
"github.com/babylonchain/babylon/crypto/bls12381"
epochingtypes "github.com/babylonchain/babylon/x/epoching/types"
"github.com/boljen/go-bitmap"
Expand Down Expand Up @@ -86,6 +87,27 @@ func (cm *RawCheckpointWithMeta) Accumulate(
return true, nil
}

func NewLastCommitHashFromHex(s string) (LastCommitHash, error) {
bz, err := hex.DecodeString(s)
if err != nil {
return nil, err
}

var lch LastCommitHash

err = lch.Unmarshal(bz)
if err != nil {
return nil, err
}

return lch, nil
}

func (lch *LastCommitHash) Unmarshal(bz []byte) error {
*lch = bz
Copy link
Member

Choose a reason for hiding this comment

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

Do you need to perform any checks here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't need that because Cosmos is not aware of the content of the LastCommitHash, which comes from Tendermint. Thanks for pointing that out though.

Copy link
Contributor

Choose a reason for hiding this comment

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

So we are not checking something like the length, because the canonic hash to be signed is in the checkpoint. If they send anything else, it will just be rejected.

return nil
}

func BytesToRawCkpt(cdc codec.BinaryCodec, bz []byte) (*RawCheckpoint, error) {
ckpt := new(RawCheckpoint)
err := cdc.Unmarshal(bz, ckpt)
Expand Down