Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [v0.45.9] add minimum commission rate to x/staking (cherry-picked from aca9d7cb5a593f10e964fad8fd47c20486634c7c) #124

Merged
merged 2 commits into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ BINDIR ?= $(GOPATH)/bin
BUILDDIR ?= $(CURDIR)/build
SIMAPP = ./simapp
MOCKS_DIR = $(CURDIR)/tests/mocks
HTTPS_GIT := https://github.com/cosmos/cosmos-sdk.git
HTTPS_GIT := https://github.com/medibloc/cosmos-sdk.git
DOCKER := $(shell which docker)
DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf
DOCKER_BUF := $(DOCKER) run --rm -v $(CURDIR):/workspace --workdir /workspace bufbuild/buf:0.56.0

export GO111MODULE = on

Expand Down Expand Up @@ -395,7 +395,7 @@ proto-lint:
@$(DOCKER_BUF) lint --error-format=json

proto-check-breaking:
@$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=master
@$(DOCKER_BUF) breaking --against $(HTTPS_GIT)#branch=v0.45.9-panacea


TM_URL = https://raw.githubusercontent.com/tendermint/tendermint/v0.34.0-rc6/proto/tendermint
Expand Down
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
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
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
5 changes: 5 additions & 0 deletions x/staking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
if err != nil {
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))
}

commission := types.NewCommissionWithTime(
msg.Commission.Rate, msg.Commission.MaxRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
Expand Down
40 changes: 40 additions & 0 deletions x/staking/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package keeper_test

import (
"testing"

"github.com/stretchr/testify/require"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

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

func TestCreateValidatorWithLessThanMinCommission(t *testing.T) {
PKS := simapp.CreateTestPubKeys(1)
valConsPk1 := PKS[0]

app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})
addrs := simapp.AddTestAddrs(app, ctx, 3, sdk.NewInt(1234))
// set min commission rate to non-zero
params := app.StakingKeeper.GetParams(ctx)
params.MinCommissionRate = sdk.NewDecWithPrec(1, 2)
app.StakingKeeper.SetParams(ctx, params)

// create validator with 0% commission
msg, err := stakingtypes.NewMsgCreateValidator(
sdk.ValAddress(addrs[0]),
valConsPk1,
sdk.NewInt64Coin(sdk.DefaultBondDenom, 100),
stakingtypes.Description{},
stakingtypes.NewCommissionRates(sdk.NewDec(0), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)),
sdk.OneInt())
require.NoError(t, err)

sh := staking.NewHandler(app.StakingKeeper)
_, err = sh(ctx, msg)
require.Error(t, err)
}
7 changes: 7 additions & 0 deletions x/staking/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func (k Keeper) BondDenom(ctx sdk.Context) (res string) {
return
}

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

// PowerReduction - is the amount of staking tokens required for 1 unit of consensus-engine power.
// Currently, this returns a global variable that the app developer can tweak.
// TODO: we might turn this into an on-chain param:
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
5 changes: 5 additions & 0 deletions x/staking/keeper/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,10 @@ func TestUpdateValidatorCommission(t *testing.T) {
app, ctx, _, addrVals := bootstrapValidatorTest(t, 1000, 20)
ctx = ctx.WithBlockHeader(tmproto.Header{Time: time.Now().UTC()})

params := app.StakingKeeper.GetParams(ctx)
params.MinCommissionRate = sdk.MustNewDecFromStr("0.05")
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 +1071,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}, // Commission rate below minimum
{val2, sdk.NewDecWithPrec(2, 1), false},
}

Expand Down
50 changes: 26 additions & 24 deletions x/staking/legacy/v040/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
v034staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v034"
v038staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v038"
v040staking "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func migrateBondStatus(oldStatus v034staking.BondStatus) BondStatus {
func migrateBondStatus(oldStatus v034staking.BondStatus) v040staking.BondStatus {
switch oldStatus {
case v034staking.Unbonded:
return Unbonded
return v040staking.Unbonded

case v034staking.Unbonding:
return Unbonding
return v040staking.Unbonding

case v034staking.Bonded:
return Bonded
return v040staking.Bonded

default:
panic(fmt.Errorf("invalid bond status %d", oldStatus))
Expand All @@ -30,29 +31,29 @@ func migrateBondStatus(oldStatus v034staking.BondStatus) BondStatus {
// - Convert addresses from bytes to bech32 strings.
// - Update BondStatus staking constants.
// - Re-encode in v0.40 GenesisState.
func Migrate(stakingState v038staking.GenesisState) *GenesisState {
newLastValidatorPowers := make([]LastValidatorPower, len(stakingState.LastValidatorPowers))
func Migrate(stakingState v038staking.GenesisState) *v040staking.GenesisState {
newLastValidatorPowers := make([]v040staking.LastValidatorPower, len(stakingState.LastValidatorPowers))
for i, oldLastValidatorPower := range stakingState.LastValidatorPowers {
newLastValidatorPowers[i] = LastValidatorPower{
newLastValidatorPowers[i] = v040staking.LastValidatorPower{
Address: oldLastValidatorPower.Address.String(),
Power: oldLastValidatorPower.Power,
}
}

newValidators := make([]Validator, len(stakingState.Validators))
newValidators := make([]v040staking.Validator, len(stakingState.Validators))
for i, oldValidator := range stakingState.Validators {
pkAny, err := codectypes.NewAnyWithValue(oldValidator.ConsPubKey)
if err != nil {
panic(fmt.Sprintf("Can't pack validator consensus PK as Any: %s", err))
}
newValidators[i] = Validator{
newValidators[i] = v040staking.Validator{
OperatorAddress: oldValidator.OperatorAddress.String(),
ConsensusPubkey: pkAny,
Jailed: oldValidator.Jailed,
Status: migrateBondStatus(oldValidator.Status),
Tokens: oldValidator.Tokens,
DelegatorShares: oldValidator.DelegatorShares,
Description: Description{
Description: v040staking.Description{
Moniker: oldValidator.Description.Moniker,
Identity: oldValidator.Description.Identity,
Website: oldValidator.Description.Website,
Expand All @@ -61,8 +62,8 @@ func Migrate(stakingState v038staking.GenesisState) *GenesisState {
},
UnbondingHeight: oldValidator.UnbondingHeight,
UnbondingTime: oldValidator.UnbondingCompletionTime,
Commission: Commission{
CommissionRates: CommissionRates{
Commission: v040staking.Commission{
CommissionRates: v040staking.CommissionRates{
Rate: oldValidator.Commission.Rate,
MaxRate: oldValidator.Commission.MaxRate,
MaxChangeRate: oldValidator.Commission.MaxChangeRate,
Expand All @@ -73,61 +74,62 @@ func Migrate(stakingState v038staking.GenesisState) *GenesisState {
}
}

newDelegations := make([]Delegation, len(stakingState.Delegations))
newDelegations := make([]v040staking.Delegation, len(stakingState.Delegations))
for i, oldDelegation := range stakingState.Delegations {
newDelegations[i] = Delegation{
newDelegations[i] = v040staking.Delegation{
DelegatorAddress: oldDelegation.DelegatorAddress.String(),
ValidatorAddress: oldDelegation.ValidatorAddress.String(),
Shares: oldDelegation.Shares,
}
}

newUnbondingDelegations := make([]UnbondingDelegation, len(stakingState.UnbondingDelegations))
newUnbondingDelegations := make([]v040staking.UnbondingDelegation, len(stakingState.UnbondingDelegations))
for i, oldUnbondingDelegation := range stakingState.UnbondingDelegations {
newEntries := make([]UnbondingDelegationEntry, len(oldUnbondingDelegation.Entries))
newEntries := make([]v040staking.UnbondingDelegationEntry, len(oldUnbondingDelegation.Entries))
for j, oldEntry := range oldUnbondingDelegation.Entries {
newEntries[j] = UnbondingDelegationEntry{
newEntries[j] = v040staking.UnbondingDelegationEntry{
CreationHeight: oldEntry.CreationHeight,
CompletionTime: oldEntry.CompletionTime,
InitialBalance: oldEntry.InitialBalance,
Balance: oldEntry.Balance,
}
}

newUnbondingDelegations[i] = UnbondingDelegation{
newUnbondingDelegations[i] = v040staking.UnbondingDelegation{
DelegatorAddress: oldUnbondingDelegation.DelegatorAddress.String(),
ValidatorAddress: oldUnbondingDelegation.ValidatorAddress.String(),
Entries: newEntries,
}
}

newRedelegations := make([]Redelegation, len(stakingState.Redelegations))
newRedelegations := make([]v040staking.Redelegation, len(stakingState.Redelegations))
for i, oldRedelegation := range stakingState.Redelegations {
newEntries := make([]RedelegationEntry, len(oldRedelegation.Entries))
newEntries := make([]v040staking.RedelegationEntry, len(oldRedelegation.Entries))
for j, oldEntry := range oldRedelegation.Entries {
newEntries[j] = RedelegationEntry{
newEntries[j] = v040staking.RedelegationEntry{
CreationHeight: oldEntry.CreationHeight,
CompletionTime: oldEntry.CompletionTime,
InitialBalance: oldEntry.InitialBalance,
SharesDst: oldEntry.SharesDst,
}
}

newRedelegations[i] = Redelegation{
newRedelegations[i] = v040staking.Redelegation{
DelegatorAddress: oldRedelegation.DelegatorAddress.String(),
ValidatorSrcAddress: oldRedelegation.ValidatorSrcAddress.String(),
ValidatorDstAddress: oldRedelegation.ValidatorDstAddress.String(),
Entries: newEntries,
}
}

return &GenesisState{
Params: Params{
return &v040staking.GenesisState{
Params: v040staking.Params{
UnbondingTime: stakingState.Params.UnbondingTime,
MaxValidators: uint32(stakingState.Params.MaxValidators),
MaxEntries: uint32(stakingState.Params.MaxEntries),
HistoricalEntries: uint32(stakingState.Params.HistoricalEntries),
BondDenom: stakingState.Params.BondDenom,
MinCommissionRate: v040staking.DefaultMinCommissionRate,
},
LastTotalPower: stakingState.LastTotalPower,
LastValidatorPowers: newLastValidatorPowers,
Expand Down
1 change: 1 addition & 0 deletions x/staking/legacy/v040/migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func TestMigrate(t *testing.T) {
"historical_entries": 0,
"max_entries": 0,
"max_validators": 0,
"min_commission_rate": "0.000000000000000000",
"unbonding_time": "0s"
},
"redelegations": [],
Expand Down
12 changes: 10 additions & 2 deletions x/staking/simulation/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
unbondingTime = "unbonding_time"
maxValidators = "max_validators"
historicalEntries = "historical_entries"
minCommissionRate = "min_commission_rate"
)

// genUnbondingTime returns randomized UnbondingTime
Expand All @@ -43,6 +44,7 @@ func RandomizedGenState(simState *module.SimulationState) {
unbondTime time.Duration
maxVals uint32
histEntries uint32
minComRate sdk.Dec
)

simState.AppParams.GetOrGenerate(
Expand All @@ -60,10 +62,15 @@ func RandomizedGenState(simState *module.SimulationState) {
func(r *rand.Rand) { histEntries = getHistEntries(r) },
)

simState.AppParams.GetOrGenerate(
simState.Cdc, minCommissionRate, &minComRate, simState.Rand,
func(r *rand.Rand) { minComRate = sdk.NewDec(0) },
)

// 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, minComRate)

// validators & delegations
var (
Expand All @@ -78,8 +85,9 @@ func RandomizedGenState(simState *module.SimulationState) {
valAddrs[i] = valAddr

maxCommission := sdk.NewDecWithPrec(int64(simulation.RandIntBetween(simState.Rand, 1, 100)), 2)
curCommissionRate := simulation.RandomDecAmount(simState.Rand, maxCommission.Sub(minComRate)).Add(minComRate)
commission := types.NewCommission(
simulation.RandomDecAmount(simState.Rand, maxCommission),
curCommissionRate,
maxCommission,
simulation.RandomDecAmount(simState.Rand, maxCommission),
)
Expand Down
1 change: 1 addition & 0 deletions x/staking/types/errors.go
Original file line number Diff line number Diff line change
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")
)
Loading