Skip to content

Commit

Permalink
feat: add MinCommissionRate param (#3)
Browse files Browse the repository at this point in the history
* add MinCommissionRate param

* migrate store

* expose migrate13to14

* fix migrate

* set default MinCommissionRate to zero

* update rosetta data
  • Loading branch information
alchemist-ti authored May 31, 2023
1 parent 3e328f6 commit 39362c4
Show file tree
Hide file tree
Showing 18 changed files with 1,032 additions and 723 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ Ref: https://keepachangelog.com/en/1.0.0/

## [Unreleased]

## v0.45.14 - 2023-05-31

### Improvements

* (x/staking) [\#3](https://github.com/classic-terra/cosmos-sdk/pull/3) Add `MinCommissionRate` param to `x/staking` module.

### Bug Fixes

* (x/feegrant) [#4](https://github.com/classic-terra/cosmos-sdk/pull/4) Fix infinite feegrant allowance bug.
Expand Down
Binary file modified contrib/rosetta/node/data.tar.gz
Binary file not shown.
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6728,6 +6728,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
6 changes: 6 additions & 0 deletions proto/cosmos/staking/v1beta1/staking.proto
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ message Params {
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\""];
// 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
Original file line number Diff line number Diff line change
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
7 changes: 7 additions & 0 deletions x/staking/keeper/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
sdk "github.com/cosmos/cosmos-sdk/types"
v043 "github.com/cosmos/cosmos-sdk/x/staking/legacy/v043"
v045 "github.com/cosmos/cosmos-sdk/x/staking/legacy/v045"
)

// Migrator is a struct for handling in-place store migrations.
Expand All @@ -19,3 +20,9 @@ func NewMigrator(keeper Keeper) Migrator {
func (m Migrator) Migrate1to2(ctx sdk.Context) error {
return v043.MigrateStore(ctx, m.keeper.storeKey)
}

// Migrate13to14 migrates from version v0.45.13 to v0.45.14.
// Only for this particular version, which do not use the version of module.
func (m Migrator) Migrate13to14(ctx sdk.Context) {
v045.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc, m.keeper.paramstore)
}
4 changes: 4 additions & 0 deletions x/staking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,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
7 changes: 7 additions & 0 deletions x/staking/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ func (k Keeper) PowerReduction(ctx sdk.Context) sdk.Int {
return sdk.DefaultPowerReduction
}

// 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 parameteras as types.Params
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
return types.NewParams(
Expand All @@ -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),
)
}

Expand Down
4 changes: 4 additions & 0 deletions x/staking/keeper/validator.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
49 changes: 49 additions & 0 deletions x/staking/legacy/v045/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package v045

import (
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)

// MinCommissionRate is set to 5%
var MinCommissionRate = sdk.NewDecWithPrec(5, 2)

// Migrate performs in-place store migrations from v0.45.13 to v0.45.14.
// The migration includes:
//
// - Adding MinCommissionRate param
// - Setting validaotr commission rate and max commission rate to MinCommissionRate if they are lower
func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec, paramstore paramtypes.Subspace) {
migrateParamsStore(ctx, paramstore)
migrateValidators(ctx, storeKey, cdc)
}

func migrateParamsStore(ctx sdk.Context, paramstore paramtypes.Subspace) {
if paramstore.HasKeyTable() {
paramstore.Set(ctx, types.KeyMinCommissionRate, MinCommissionRate)
} else {
paramstore.WithKeyTable(types.ParamKeyTable())
paramstore.Set(ctx, types.KeyMinCommissionRate, MinCommissionRate)
}
}

func migrateValidators(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) {
store := ctx.KVStore(storeKey)
iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey)
defer iterator.Close()

for ; iterator.Valid(); iterator.Next() {
validator := types.MustUnmarshalValidator(cdc, iterator.Value())
if validator.Commission.CommissionRates.Rate.LT(MinCommissionRate) {
validator.Commission.CommissionRates.Rate = MinCommissionRate
}

if validator.Commission.CommissionRates.MaxRate.LT(MinCommissionRate) {
validator.Commission.CommissionRates.MaxRate = MinCommissionRate
}
store.Set(iterator.Key(), types.MustMarshalValidator(cdc, &validator))
}
}
120 changes: 120 additions & 0 deletions x/staking/legacy/v045/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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/legacy/v045"
"github.com/cosmos/cosmos-sdk/x/staking/types"
)

func TestMigrate(t *testing.T) {
encCfg := simapp.MakeTestEncodingConfig()
stakingKey := sdk.NewKVStoreKey("staking")
tStakingKey := sdk.NewTransientStoreKey("transient_test")
ctx := testutil.DefaultContext(stakingKey, tStakingKey)
paramstore := paramtypes.NewSubspace(encCfg.Marshaler, encCfg.Amino, stakingKey, tStakingKey, "staking")

testCases := []struct {
OldValidator types.Validator
NewValidator types.Validator
}{
{
OldValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x00}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.01"),
MaxRate: sdk.MustNewDecFromStr("0.02"),
},
},
},
NewValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x00}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.05"),
MaxRate: sdk.MustNewDecFromStr("0.05"),
},
},
},
},
{
OldValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x01}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.05"),
MaxRate: sdk.MustNewDecFromStr("0.05"),
},
},
},
NewValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x01}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.05"),
MaxRate: sdk.MustNewDecFromStr("0.05"),
},
},
},
},
{
OldValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x02}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.1"),
MaxRate: sdk.MustNewDecFromStr("0.2"),
},
},
},
NewValidator: types.Validator{
OperatorAddress: sdk.ValAddress{0x02}.String(),
Commission: types.Commission{
CommissionRates: types.CommissionRates{
Rate: sdk.MustNewDecFromStr("0.1"),
MaxRate: sdk.MustNewDecFromStr("0.2"),
},
},
},
},
}

store := ctx.KVStore(stakingKey)
for _, vs := range testCases {
bz := types.MustMarshalValidator(encCfg.Marshaler, &vs.OldValidator)
store.Set(types.GetValidatorKey(vs.OldValidator.GetOperator()), bz)
}

// Check no params
require.False(t, paramstore.Has(ctx, types.KeyMinCommissionRate))

// Run migrations.
v045staking.MigrateStore(ctx, stakingKey, encCfg.Marshaler, paramstore)

// Make sure the new params are set.
require.True(t, paramstore.Has(ctx, types.KeyMinCommissionRate))

minCommissionRate := sdk.Dec{}
paramstore.Get(ctx, types.KeyMinCommissionRate, &minCommissionRate)
require.Equal(t, v045staking.MinCommissionRate, minCommissionRate)

// Make sure the validators commission is set correctly.
iterator := sdk.KVStorePrefixIterator(store, types.ValidatorsKey)
defer iterator.Close()

i := int64(0)
for ; iterator.Valid(); iterator.Next() {
validator := types.MustUnmarshalValidator(encCfg.Marshaler, iterator.Value())
require.Equal(t, testCases[i].NewValidator.OperatorAddress, validator.GetOperator().String())
require.Equal(t, testCases[i].NewValidator.Commission.Rate, validator.Commission.Rate)
require.Equal(t, testCases[i].NewValidator.Commission.MaxRate, validator.Commission.MaxRate)
i++
}
}
9 changes: 5 additions & 4 deletions x/staking/simulation/genesis.go
Original file line number Diff line number Diff line change
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
17 changes: 9 additions & 8 deletions x/staking/spec/08_params.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ 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" |
| PowerReduction | string | "1000000" |
| MinCommissionRate | string | "0.050000000000000000" |
1 change: 1 addition & 0 deletions x/staking/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ var (
ErrNoHistoricalInfo = sdkerrors.Register(ModuleName, 38, "no historical info found")
ErrEmptyValidatorPubKey = sdkerrors.Register(ModuleName, 39, "empty validator public key")
ErrMsgNotSupported = sdkerrors.Register(ModuleName, 40, "message not supported")
ErrCommissionLTMinRate = sdkerrors.Register(ModuleName, 41, "commission cannot be less than min rate")
)
Loading

0 comments on commit 39362c4

Please sign in to comment.