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:
parent
878e3e8a4a
commit
c455e5e0bd
|
@ -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
|
||||
|
||||
|
|
|
@ -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 |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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},
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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 (
|
||||
|
|
|
@ -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" |
|
||||
|
|
|
@ -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,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
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue