feat: Min commission upgrade 0.44 (#10529)

<!--
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
-->

## Description

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

replaces https://github.com/cosmos/cosmos-sdk/pull/10422

---

### Author Checklist

*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

### Reviewers Checklist

*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)
This commit is contained in:
Marko 2021-11-12 12:55:55 +01:00 committed by GitHub
parent 878e3e8a4a
commit c455e5e0bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 864 additions and 689 deletions

View File

@ -168,6 +168,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (x/bank) [\#9832] (https://github.com/cosmos/cosmos-sdk/pull/9832) Account balance is stored as `sdk.Int` rather than `sdk.Coin`.
* (x/bank) [\#9890] (https://github.com/cosmos/cosmos-sdk/pull/9890) Remove duplicate denom from denom metadata key.
* (x/upgrade) [\#10189](https://github.com/cosmos/cosmos-sdk/issues/10189) Removed potential sources of non-determinism in upgrades
* [\#10393](https://github.com/cosmos/cosmos-sdk/pull/10422) Add `MinCommissionRate` param to `x/staking` module.
### Deprecated

View File

@ -8405,6 +8405,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 |

View File

@ -282,6 +282,12 @@ message Params {
uint32 historical_entries = 4;
// bond_denom defines the bondable coin denomination.
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

View File

@ -885,12 +885,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 {

View File

@ -25,5 +25,5 @@ func (m Migrator) Migrate1to2(ctx sdk.Context) error {
// 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)
return v045.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper.paramstore)
}

View File

@ -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
@ -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,

View File

@ -47,7 +47,13 @@ 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),
@ -55,6 +61,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params {
k.MaxEntries(ctx),
k.HistoricalEntries(ctx),
k.BondDenom(ctx),
k.MinCommissionRate(ctx),
)
}

View File

@ -138,6 +138,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

View File

@ -1052,6 +1052,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),
@ -1076,6 +1081,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},
}

View File

@ -5,19 +5,29 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
v040staking "github.com/cosmos/cosmos-sdk/x/staking/migrations/v040"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
// MigrateStore performs in-place store migrations from v0.43/v0.44 to v0.45.
// The migration includes:
//
// - Removing delegations that have a zero share or token amount.
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error {
// - Setting the MinCommissionRate param in the paramstore
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec, paramstore paramtypes.Subspace) error {
store := ctx.KVStore(storeKey)
migrateParamsStore(ctx, paramstore)
return purgeDelegations(store, cdc)
}
func migrateParamsStore(ctx sdk.Context, paramstore paramtypes.Subspace) {
paramstore.WithKeyTable(types.ParamKeyTable())
paramstore.Set(ctx, types.KeyMinCommissionRate, types.DefaultMinCommissionRate)
}
func purgeDelegations(store sdk.KVStore, cdc codec.BinaryCodec) error {
prefixDelStore := prefix.NewStore(store, v040staking.DelegationKey)

View File

@ -0,0 +1,32 @@
package v045_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
v045staking "github.com/cosmos/cosmos-sdk/x/staking/migrations/v045"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)
func TestStoreMigration(t *testing.T) {
encCfg := simapp.MakeTestEncodingConfig()
stakingKey := sdk.NewKVStoreKey("staking")
tStakingKey := sdk.NewTransientStoreKey("transient_test")
ctx := testutil.DefaultContext(stakingKey, tStakingKey)
paramstore := paramtypes.NewSubspace(encCfg.Codec, encCfg.Amino, stakingKey, tStakingKey, "staking")
// Check no params
require.False(t, paramstore.Has(ctx, types.KeyMinCommissionRate))
// Run migrations.
err := v045staking.MigrateStore(ctx, stakingKey, encCfg.Codec, paramstore)
require.NoError(t, err)
// Make sure the new params are set.
require.True(t, paramstore.Has(ctx, types.KeyMinCommissionRate))
}

View File

@ -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(
@ -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 (

View File

@ -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" |

View File

@ -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")
)

View File

@ -32,12 +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")
KeyMinCommissionRate = []byte("MinCommissionRate")
)
var _ paramtypes.ParamSet = (*Params)(nil)
@ -48,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,
}
}
@ -66,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),
}
}
@ -77,6 +85,7 @@ func DefaultParams() Params {
DefaultMaxEntries,
DefaultHistoricalEntries,
sdk.DefaultBondDenom,
DefaultMinCommissionRate,
)
}
@ -124,6 +133,10 @@ func (p Params) Validate() error {
return err
}
if err := validateMinCommissionRate(p.MinCommissionRate); err != nil {
return err
}
return nil
}
@ -204,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
}

View File

@ -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"
)
@ -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())
}

File diff suppressed because it is too large Load Diff