Skip to content

Commit

Permalink
staking: customization for bfs
Browse files Browse the repository at this point in the history
  • Loading branch information
Keefe Liu committed Nov 23, 2022
1 parent eb1e3eb commit 7ebbef6
Show file tree
Hide file tree
Showing 15 changed files with 1,863 additions and 912 deletions.
22 changes: 12 additions & 10 deletions proto/cosmos/staking/v1beta1/staking.proto
Original file line number Diff line number Diff line change
Expand Up @@ -90,36 +90,38 @@ message Validator {

// operator_address defines the address of the validator's operator; bech encoded in JSON.
string operator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// bls_pubkey defines the bls pubkey of the validator's authorized relayer/operator; bech encoded in JSON.
string bls_pubkey = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// consensus_pubkey is the consensus public key of the validator, as a Protobuf Any.
google.protobuf.Any consensus_pubkey = 2 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"];
google.protobuf.Any consensus_pubkey = 3 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"];
// jailed defined whether the validator has been jailed from bonded status or not.
bool jailed = 3;
bool jailed = 4;
// status is the validator status (bonded/unbonding/unbonded).
BondStatus status = 4;
BondStatus status = 5;
// tokens define the delegated tokens (incl. self-delegation).
string tokens = 5 [
string tokens = 6 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
// delegator_shares defines total shares issued to a validator's delegators.
string delegator_shares = 6 [
string delegator_shares = 7 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// description defines the description terms for the validator.
Description description = 7 [(gogoproto.nullable) = false];
Description description = 8 [(gogoproto.nullable) = false];
// unbonding_height defines, if unbonding, the height at which this validator has begun unbonding.
int64 unbonding_height = 8;
int64 unbonding_height = 9;
// unbonding_time defines, if unbonding, the min time for the validator to complete unbonding.
google.protobuf.Timestamp unbonding_time = 9 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
google.protobuf.Timestamp unbonding_time = 10 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
// commission defines the commission parameters.
Commission commission = 10 [(gogoproto.nullable) = false];
Commission commission = 11 [(gogoproto.nullable) = false];
// min_self_delegation is the validator's self declared minimum self delegation.
//
// Since: cosmos-sdk 0.46
string min_self_delegation = 11 [
string min_self_delegation = 12 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
Expand Down
54 changes: 46 additions & 8 deletions proto/cosmos/staking/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,30 @@ service Msg {
// EditValidator defines a method for editing an existing validator.
rpc EditValidator(MsgEditValidator) returns (MsgEditValidatorResponse);

// RemoveValidator defines a method for removing an existing validator.
rpc RemoveValidator(MsgRemoveValidator) returns (MsgRemoveValidatorResponse);

// SelfDelegate defines a method for performing a delegation of coins
// for a validator itself.
rpc SelfDelegate(MsgSelfDelegate) returns (MsgSelfDelegateResponse);

// Delegate defines a method for performing a delegation of coins
// from a delegator to a validator.
rpc Delegate(MsgDelegate) returns (MsgDelegateResponse);
// rpc Delegate(MsgDelegate) returns (MsgDelegateResponse);

// BeginRedelegate defines a method for performing a redelegation
// of coins from a delegator and source validator to a destination validator.
rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse);
// rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse);

// Undelegate defines a method for performing an undelegation from a
// delegate and a validator.
rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse);
// rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse);

// CancelUnbondingDelegation defines a method for performing canceling the unbonding delegation
// and delegate back to previous validator.
//
// Since: cosmos-sdk 0.46
rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
// rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
}

// MsgCreateValidator defines a SDK message for creating a new validator.
Expand All @@ -60,8 +67,10 @@ message MsgCreateValidator {
];
string delegator_address = 4 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 5 [(cosmos_proto.scalar) = "cosmos.AddressString"];
google.protobuf.Any pubkey = 6 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"];
cosmos.base.v1beta1.Coin value = 7 [(gogoproto.nullable) = false];
string bls_pubkey = 6 [(cosmos_proto.scalar) = "cosmos.AddressString"];
google.protobuf.Any pubkey = 7 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"];
cosmos.base.v1beta1.Coin value = 8 [(gogoproto.nullable) = false];
uint64 proposal_id = 9;
}

// MsgCreateValidatorResponse defines the Msg/CreateValidator response type.
Expand All @@ -76,20 +85,49 @@ message MsgEditValidator {

Description description = 1 [(gogoproto.nullable) = false];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string bls_pubkey = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];

// We pass a reference to the new commission rate and min self delegation as
// it's not mandatory to update. If not updated, the deserialized rate will be
// zero with no way to distinguish if an update was intended.
// REF: #2373
string commission_rate = 3
string commission_rate = 4
[(cosmos_proto.scalar) = "cosmos.Dec", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec"];
string min_self_delegation = 4
string min_self_delegation = 5
[(cosmos_proto.scalar) = "cosmos.Int", (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int"];
}

// MsgEditValidatorResponse defines the Msg/EditValidator response type.
message MsgEditValidatorResponse {}

// MsgRemoveValidator defines a SDK message for removing an existing validator.
message MsgRemoveValidator {
option (cosmos.msg.v1.signer) = "validator_address";

option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

string validator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
uint64 proposal_id = 9;
}

// MsgRemoveValidatorResponse defines the Msg/RemoveValidator response type.
message MsgRemoveValidatorResponse {}

// MsgSelfDelegate defines a SDK message for performing a delegation of coins
// for a validator itself.
message MsgSelfDelegate {
option (cosmos.msg.v1.signer) = "validator_address";

option (gogoproto.equal) = false;

string validator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false];
}

// MsgSelfDelegateResponse defines the Msg/SelfDelegate response type.
message MsgSelfDelegateResponse {}

// MsgDelegate defines a SDK message for performing a delegation of coins
// from a delegator to a validator.
message MsgDelegate {
Expand Down
32 changes: 32 additions & 0 deletions x/gov/keeper/validator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)

// CheckCreateProposal checks whether create validator proposal has passed and
// match the current create validator operation.
func (k Keeper) CheckCreateProposal(ctx sdk.Context, msg *types.MsgCreateValidator) error {
_, ok := k.GetProposal(ctx, msg.ProposalId)
if !ok {
return fmt.Errorf("proposal %d does not exist", msg.ProposalId)
}

// TODO: check the proposal according to the proposal contents.
return nil
}

// CheckRemoveProposal checks whether remove validator proposal has passed and
// match the current remove validator operation.
func (k Keeper) CheckRemoveProposal(ctx sdk.Context, msg *types.MsgRemoveValidator) error {
_, ok := k.GetProposal(ctx, msg.ProposalId)
if !ok {
return fmt.Errorf("proposal %d does not exist", msg.ProposalId)
}

// TODO: check the proposal according to the proposal contents.
return nil
}
20 changes: 20 additions & 0 deletions x/staking/keeper/alias_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,26 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress,
}
}

// iterate through all of the delegations to a validator
func (k Keeper) IterateDelegationsToValidator(ctx sdk.Context, valAddr sdk.AccAddress,
fn func(del types.DelegationI) (stop bool),
) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.DelegationKey)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
del := types.MustUnmarshalDelegation(k.cdc, iterator.Value())
if del.ValidatorAddress != valAddr.String() {
continue
}
stop := fn(del)
if stop {
break
}
}
}

// return all delegations used during genesis dump
// TODO: remove this func, change all usage for iterate functionality
func (k Keeper) GetAllSDKDelegations(ctx sdk.Context) (delegations []types.Delegation) {
Expand Down
1 change: 1 addition & 0 deletions x/staking/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (k Keeper) InitGenesis(ctx sdk.Context, data *types.GenesisState) (res []ab

// Manually set indices for the first time
k.SetValidatorByConsAddr(ctx, validator)
k.SetValidatorByBlsPubkey(ctx, validator)
k.SetValidatorByPowerIndex(ctx, validator)

// Call the creation hook if not exported
Expand Down
1 change: 1 addition & 0 deletions x/staking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Keeper struct {
cdc codec.BinaryCodec
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
govKeeper types.GovKeeper // TODO: how to init this keeper?
hooks types.StakingHooks
paramstore paramtypes.Subspace
}
Expand Down
75 changes: 75 additions & 0 deletions x/staking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ var _ types.MsgServer = msgServer{}
func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateValidator) (*types.MsgCreateValidatorResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

err := k.govKeeper.CheckCreateProposal(ctx, msg)
if err != nil {
return nil, err
}

valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, err
Expand All @@ -47,6 +52,10 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
return nil, types.ErrValidatorOwnerExists
}

if _, found := k.GetValidatorByBlsPubkey(ctx, msg.BlsPubkey); found {
return nil, types.ErrValidatorBlsPubkeyExists
}

pk, ok := msg.Pubkey.GetCachedValue().(cryptotypes.PubKey)
if !ok {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", pk)
Expand Down Expand Up @@ -106,8 +115,10 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
}

validator.MinSelfDelegation = msg.MinSelfDelegation
validator.BlsPubkey = msg.BlsPubkey

k.SetValidator(ctx, validator)
k.SetValidatorByBlsPubkey(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
k.SetNewValidatorByPowerIndex(ctx, validator)

Expand Down Expand Up @@ -153,6 +164,19 @@ func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValida
return nil, types.ErrNoValidatorFound
}

// replace bls pubkey
if len(msg.BlsPubkey) != 0 {
if tmpValidator, found := k.GetValidatorByBlsPubkey(ctx, msg.BlsPubkey); found {
if tmpValidator.OperatorAddress != validator.OperatorAddress {
return nil, types.ErrValidatorBlsPubkeyExists
}
} else {
k.DeleteValidatorByBlsPubkey(ctx, validator)
validator.BlsPubkey = msg.BlsPubkey
k.SetValidatorByBlsPubkey(ctx, validator)
}
}

// replace all editable fields (clients should autofill existing values)
description, err := validator.Description.UpdateDescription(msg.Description)
if err != nil {
Expand Down Expand Up @@ -205,6 +229,57 @@ func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValida
return &types.MsgEditValidatorResponse{}, nil
}

// RemoveValidator defines a method for removing an existing validator
func (k msgServer) RemoveValidator(goCtx context.Context, msg *types.MsgRemoveValidator) (*types.MsgRemoveValidatorResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

err := k.govKeeper.CheckRemoveProposal(ctx, msg)
if err != nil {
return nil, err
}

valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
if err != nil {
return nil, err
}
// validator must already be registered
_, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}

k.IterateDelegationsToValidator(ctx, sdk.MustAccAddressFromBech32(msg.ValidatorAddress), func(del types.DelegationI) (stop bool) {
_, err = k.Keeper.Undelegate(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr(), del.GetShares())
if err != nil {
return true
}

// TODO: emit events for undelegation?
return false
})

if err != nil {
return nil, err
}

return &types.MsgRemoveValidatorResponse{}, nil
}

// SelfDelegate defines a method for performing a delegation of coins for a validator itself
func (k msgServer) SelfDelegate(goCtx context.Context, msg *types.MsgSelfDelegate) (*types.MsgSelfDelegateResponse, error) {
msgDelegate := &types.MsgDelegate{
DelegatorAddress: msg.ValidatorAddress,
ValidatorAddress: msg.ValidatorAddress,
Amount: msg.Amount,
}
_, err := k.Delegate(goCtx, msgDelegate)
if err != nil {
return nil, err
}

return &types.MsgSelfDelegateResponse{}, nil
}

// Delegate defines a method for performing a delegation of coins from a delegator to a validator
func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
Expand Down
27 changes: 27 additions & 0 deletions x/staking/keeper/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ func (k Keeper) mustGetValidator(ctx sdk.Context, addr sdk.ValAddress) types.Val
return validator
}

// get a single validator by bls pubkey
func (k Keeper) GetValidatorByBlsPubkey(ctx sdk.Context, blsKey string) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)

opAddr := store.Get(types.GetValidatorByBlsPubkey(blsKey))
if opAddr == nil {
return validator, false
}

return k.GetValidator(ctx, opAddr)
}

// get a single validator by consensus address
func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
Expand Down Expand Up @@ -61,6 +73,21 @@ func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
store.Set(types.GetValidatorKey(validator.GetOperator()), bz)
}

// validator index
func (k Keeper) SetValidatorByBlsPubkey(ctx sdk.Context, validator types.Validator) error {
blsPk := validator.GetBlsPubkey()
store := ctx.KVStore(k.storeKey)
store.Set(types.GetValidatorByBlsPubkey(blsPk), validator.GetOperator())
return nil
}

// validator index
func (k Keeper) DeleteValidatorByBlsPubkey(ctx sdk.Context, validator types.Validator) {
blsPk := validator.GetBlsPubkey()
store := ctx.KVStore(k.storeKey)
store.Delete(types.GetValidatorByBlsPubkey(blsPk))
}

// validator index
func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) error {
consPk, err := validator.GetConsAddr()
Expand Down
2 changes: 2 additions & 0 deletions x/staking/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ var (
ErrNoHistoricalInfo = sdkerrors.Register(ModuleName, 38, "no historical info found")
ErrEmptyValidatorPubKey = sdkerrors.Register(ModuleName, 39, "empty validator public key")
ErrCommissionLTMinRate = sdkerrors.Register(ModuleName, 40, "commission cannot be less than min rate")

ErrValidatorBlsPubkeyExists = sdkerrors.Register(ModuleName, 41, "validator already exist for this bls pubkey; must use new validator bls pubkey")
)
6 changes: 6 additions & 0 deletions x/staking/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ type BankKeeper interface {
BurnCoins(ctx sdk.Context, name string, amt sdk.Coins) error
}

// GovKeeper defines the expected interface needed to check proposal.
type GovKeeper interface {
CheckCreateProposal(ctx sdk.Context, msg *MsgCreateValidator) error
CheckRemoveProposal(ctx sdk.Context, msg *MsgRemoveValidator) error
}

// ValidatorSet expected properties for the set of all validators (noalias)
type ValidatorSet interface {
// iterate through validators by operator address, execute func for each validator
Expand Down
Loading

0 comments on commit 7ebbef6

Please sign in to comment.