Skip to content

Commit

Permalink
feat: Min commission upgrade 0.44 (cosmos#10529)
Browse files Browse the repository at this point in the history
<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

Added the ability to set a minimum commission rate that all validators cannot set their commission rate below.

replaces cosmos#10422

---

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
tac0turtle authored and lydia.pierce@principia.edu committed Aug 22, 2022
1 parent 35ae2c4 commit af33465
Show file tree
Hide file tree
Showing 14 changed files with 860 additions and 748 deletions.
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Expand Up @@ -6599,6 +6599,7 @@ Params defines the parameters for the staking module.
| `max_entries` | [uint32](#uint32) | | max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio). |
| `historical_entries` | [uint32](#uint32) | | historical_entries is the number of historical entries to persist. |
| `bond_denom` | [string](#string) | | bond_denom defines the bondable coin denomination. |
| `min_commission_rate` | [string](#string) | | min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators |



Expand Down
8 changes: 7 additions & 1 deletion proto/cosmos/staking/v1beta1/staking.proto
Expand Up @@ -281,7 +281,13 @@ message Params {
// historical_entries is the number of historical entries to persist.
uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""];
// bond_denom defines the bondable coin denomination.
string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""];
string bond_denom = 5;
// min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators
string min_commission_rate = 6 [
(gogoproto.moretags) = "yaml:\"min_commission_rate\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}

// DelegationResponse is equivalent to Delegation except that it contains a
Expand Down
3 changes: 2 additions & 1 deletion x/staking/client/testutil/suite.go
Expand Up @@ -876,12 +876,13 @@ func (s *IntegrationTestSuite) TestGetCmdQueryParams() {
historical_entries: 10000
max_entries: 7
max_validators: 100
min_commission_rate: "0.000000000000000000"
unbonding_time: 1814400s`,
},
{
"with json output",
[]string{fmt.Sprintf("--%s=json", tmcli.OutputFlag)},
`{"unbonding_time":"1814400s","max_validators":100,"max_entries":7,"historical_entries":10000,"bond_denom":"stake"}`,
`{"unbonding_time":"1814400s","max_validators":100,"max_entries":7,"historical_entries":10000,"bond_denom":"stake","min_commission_rate":"0.000000000000000000"}`,
},
}
for _, tc := range testCases {
Expand Down
5 changes: 5 additions & 0 deletions x/staking/keeper/migrations.go
Expand Up @@ -19,3 +19,8 @@ func NewMigrator(keeper Keeper) Migrator {
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v043.MigrateStore(ctx, m.keeper.storeKey)
}

// Migrate2to3 migrates x/staking state from consensus version 2 to 3.
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
return v045.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper.paramstore)
}
5 changes: 5 additions & 0 deletions x/staking/keeper/msg_server.go
Expand Up @@ -35,6 +35,10 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
return nil, err
}

if msg.Commission.Rate.LT(k.MinCommissionRate(ctx)) {
return nil, sdkerrors.Wrapf(types.ErrCommissionLTMinRate, "cannot set validator commission to less than minimum rate of %s", k.MinCommissionRate(ctx))
}

// check to see if the pubkey or sender has been registered before
if _, found := k.GetValidator(ctx, valAddr); found {
return nil, types.ErrValidatorOwnerExists
Expand Down Expand Up @@ -74,6 +78,7 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
if err != nil {
return nil, err
}

commission := types.NewCommissionWithTime(
msg.Commission.Rate, msg.Commission.MaxRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
Expand Down
9 changes: 8 additions & 1 deletion x/staking/keeper/params.go
Expand Up @@ -47,14 +47,21 @@ func (k Keeper) PowerReduction(ctx sdk.Context) sdk.Int {
return sdk.DefaultPowerReduction
}

// Get all parameteras as types.Params
// MinCommissionRate - Minimum validator commission rate
func (k Keeper) MinCommissionRate(ctx sdk.Context) (res sdk.Dec) {
k.paramstore.Get(ctx, types.KeyMinCommissionRate, &res)
return
}

// Get all parameters as types.Params
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
return types.NewParams(
k.UnbondingTime(ctx),
k.MaxValidators(ctx),
k.MaxEntries(ctx),
k.HistoricalEntries(ctx),
k.BondDenom(ctx),
k.MinCommissionRate(ctx),
)
}

Expand Down
4 changes: 4 additions & 0 deletions x/staking/keeper/validator.go
Expand Up @@ -142,6 +142,10 @@ func (k Keeper) UpdateValidatorCommission(ctx sdk.Context,
return commission, err
}

if newRate.LT(k.MinCommissionRate(ctx)) {
return commission, fmt.Errorf("cannot set validator commission to less than minimum rate of %s", k.MinCommissionRate(ctx))
}

commission.Rate = newRate
commission.UpdateTime = blockTime

Expand Down
6 changes: 6 additions & 0 deletions x/staking/keeper/validator_test.go
Expand Up @@ -1043,6 +1043,11 @@ func TestUpdateValidatorCommission(t *testing.T) {
app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20)
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Now().UTC()})

// Set MinCommissionRate to 0.05
params := app.StakingKeeper.GetParams(ctx)
params.MinCommissionRate = sdk.NewDecWithPrec(5, 2)
app.StakingKeeper.SetParams(ctx, params)

commission1 := types.NewCommissionWithTime(
sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1),
sdk.NewDecWithPrec(1, 1), time.Now().UTC().Add(time.Duration(-1)*time.Hour),
Expand All @@ -1067,6 +1072,7 @@ func TestUpdateValidatorCommission(t *testing.T) {
{val2, sdk.NewDecWithPrec(-1, 1), true},
{val2, sdk.NewDecWithPrec(4, 1), true},
{val2, sdk.NewDecWithPrec(3, 1), true},
{val2, sdk.NewDecWithPrec(1, 2), true},
{val2, sdk.NewDecWithPrec(2, 1), false},
}

Expand Down
9 changes: 5 additions & 4 deletions x/staking/simulation/genesis.go
Expand Up @@ -40,9 +40,10 @@ func getHistEntries(r *rand.Rand) uint32 {
func RandomizedGenState(simState *module.SimulationState) {
// params
var (
unbondTime time.Duration
maxVals uint32
histEntries uint32
unbondTime time.Duration
maxVals uint32
histEntries uint32
minCommissionRate sdk.Dec
)

simState.AppParams.GetOrGenerate(
Expand All @@ -63,7 +64,7 @@ func RandomizedGenState(simState *module.SimulationState) {
// NOTE: the slashing module need to be defined after the staking module on the
// NewSimulationManager constructor for this to work
simState.UnbondTime = unbondTime
params := types.NewParams(simState.UnbondTime, maxVals, 7, histEntries, sdk.DefaultBondDenom)
params := types.NewParams(simState.UnbondTime, maxVals, 7, histEntries, sdk.DefaultBondDenom, minCommissionRate)

// validators & delegations
var (
Expand Down
16 changes: 8 additions & 8 deletions x/staking/spec/08_params.md
Expand Up @@ -6,11 +6,11 @@ order: 8

The staking module contains the following parameters:

| Key | Type | Example |
|-------------------|------------------|-------------------|
| UnbondingTime | string (time ns) | "259200000000000" |
| MaxValidators | uint16 | 100 |
| KeyMaxEntries | uint16 | 7 |
| HistoricalEntries | uint16 | 3 |
| BondDenom | string | "stake" |
| PowerReduction | string | "1000000" |
| Key | Type | Example |
|-------------------|------------------|------------------------|
| UnbondingTime | string (time ns) | "259200000000000" |
| MaxValidators | uint16 | 100 |
| KeyMaxEntries | uint16 | 7 |
| HistoricalEntries | uint16 | 3 |
| BondDenom | string | "stake" |
| MinCommissionRate | string | "0.000000000000000000" |
1 change: 1 addition & 0 deletions x/staking/types/errors.go
Expand Up @@ -49,4 +49,5 @@ var (
ErrInvalidHistoricalInfo = sdkerrors.Register(ModuleName, 37, "invalid historical info")
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")
)
32 changes: 30 additions & 2 deletions x/staking/types/params.go
Expand Up @@ -32,13 +32,18 @@ const (
DefaultHistoricalEntries uint32 = 10000
)

var (
// DefaultMinCommissionRate is set to 0%
DefaultMinCommissionRate = sdk.ZeroDec()
)

var (
KeyUnbondingTime = []byte("UnbondingTime")
KeyMaxValidators = []byte("MaxValidators")
KeyMaxEntries = []byte("MaxEntries")
KeyBondDenom = []byte("BondDenom")
KeyHistoricalEntries = []byte("HistoricalEntries")
KeyPowerReduction = []byte("PowerReduction")
KeyMinCommissionRate = []byte("MinCommissionRate")
)

var _ paramtypes.ParamSet = (*Params)(nil)
Expand All @@ -49,13 +54,14 @@ func ParamKeyTable() paramtypes.KeyTable {
}

// NewParams creates a new Params instance
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint32, bondDenom string) Params {
func NewParams(unbondingTime time.Duration, maxValidators, maxEntries, historicalEntries uint32, bondDenom string, minCommissionRate sdk.Dec) Params {
return Params{
UnbondingTime: unbondingTime,
MaxValidators: maxValidators,
MaxEntries: maxEntries,
HistoricalEntries: historicalEntries,
BondDenom: bondDenom,
MinCommissionRate: minCommissionRate,
}
}

Expand All @@ -67,6 +73,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
paramtypes.NewParamSetPair(KeyMaxEntries, &p.MaxEntries, validateMaxEntries),
paramtypes.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries),
paramtypes.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom),
paramtypes.NewParamSetPair(KeyMinCommissionRate, &p.MinCommissionRate, validateMinCommissionRate),
}
}

Expand All @@ -78,6 +85,7 @@ func DefaultParams() Params {
DefaultMaxEntries,
DefaultHistoricalEntries,
sdk.DefaultBondDenom,
DefaultMinCommissionRate,
)
}

Expand Down Expand Up @@ -125,6 +133,10 @@ func (p Params) Validate() error {
return err
}

if err := validateMinCommissionRate(p.MinCommissionRate); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -205,3 +217,19 @@ func ValidatePowerReduction(i interface{}) error {

return nil
}

func validateMinCommissionRate(i interface{}) error {
v, ok := i.(sdk.Dec)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}

if v.IsNegative() {
return fmt.Errorf("minimum commission rate cannot be negative: %s", v)
}
if v.GT(sdk.OneDec()) {
return fmt.Errorf("minimum commission rate cannot be greater than 100%%: %s", v)
}

return nil
}
15 changes: 15 additions & 0 deletions x/staking/types/params_test.go
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/stretchr/testify/require"

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

Expand All @@ -21,3 +22,17 @@ func TestParamsEqual(t *testing.T) {
ok = p1.Equal(p2)
require.False(t, ok)
}

func Test_validateParams(t *testing.T) {
params := types.DefaultParams()

// default params have no error
require.NoError(t, params.Validate())

// validate mincommision
params.MinCommissionRate = sdk.NewDec(-1)
require.Error(t, params.Validate())

params.MinCommissionRate = sdk.NewDec(2)
require.Error(t, params.Validate())
}

0 comments on commit af33465

Please sign in to comment.