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

fix: Global min Self Delegation #134

Merged
merged 21 commits into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6505,6 +6505,7 @@ Params defines the parameters for the staking module.
| `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 |
| `min_self_delegation` | [string](#string) | | min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators |
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved



Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -615,8 +615,6 @@ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/osmosis-labs/iavl v0.17.3-osmo-v3 h1:q2Qv3+DK52w5b68I96VApHI05jBu7HeQK/YDttKh/jY=
github.com/osmosis-labs/iavl v0.17.3-osmo-v3/go.mod h1:lJEOIlsd3sVO0JDyXWIXa9/Ur5FBscP26zJx0KxHjto=
github.com/osmosis-labs/iavl v0.17.3-osmo-v4 h1:U1HA2WEMAYVPjeJlfClH10ajv6O0C1C1jk/SQxDPwyM=
github.com/osmosis-labs/iavl v0.17.3-osmo-v4/go.mod h1:lJEOIlsd3sVO0JDyXWIXa9/Ur5FBscP26zJx0KxHjto=
github.com/osmosis-labs/iavl v0.17.3-osmo-v4.rc2 h1:Sq5909z6y/tfN4lrOrqgSwZWjqQzmyeTtVfIlRK9tz0=
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 @@ -288,6 +288,12 @@ message Params {
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// min_self_delegation is the chain-wide minimum amount that a validator has to self delegate
string min_self_delegation = 7 [
(gogoproto.moretags) = "yaml:\"min_self_delegation\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(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 @@ -877,12 +877,13 @@ historical_entries: 10000
max_entries: 7
max_validators: 100
min_commission_rate: "0.000000000000000000"
min_self_delegation: "0"
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","min_commission_rate":"0.000000000000000000"}`,
`{"unbonding_time":"1814400s","max_validators":100,"max_entries":7,"historical_entries":10000,"bond_denom":"stake","min_commission_rate":"0.000000000000000000","min_self_delegation":"0"}`,
},
}
for _, tc := range testCases {
Expand Down
6 changes: 6 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"
v046 "github.com/cosmos/cosmos-sdk/x/staking/legacy/v046"
)

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

// Migrate1to2 migrates from version 1 to 2.
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
func (m Migrator) Migrate2to3(ctx sdk.Context) error {
return v046.MigrateStore(ctx, m.keeper.paramstore)
}
19 changes: 19 additions & 0 deletions x/staking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa
)
}

//Uses GT instead of GTE for consistency with other MinSelfDelegation checks
if msg.MinSelfDelegation.GT(msg.Value.Amount) {
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
return nil, types.ErrSelfDelegationBelowMinimum
}

if _, err := msg.Description.EnsureLength(); err != nil {
return nil, err
}
Expand Down Expand Up @@ -99,6 +104,13 @@ func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateVa

validator.MinSelfDelegation = msg.MinSelfDelegation

globalMinSelfDelegation := k.MinSelfDelegation(ctx)

//Minimum validator self delegation must be greater than or equal to global minimum
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
if globalMinSelfDelegation.GT(validator.MinSelfDelegation) {
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
return nil, types.ErrMinSelfDelegationBelowMinimum
}

k.SetValidator(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
k.SetNewValidatorByPowerIndex(ctx, validator)
Expand Down Expand Up @@ -163,6 +175,13 @@ func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValida
validator.Commission = commission
}

globalMinSelfDelegation := k.MinSelfDelegation(ctx)

//Minimum validator self delegation must be greater than or equal to global minimum
alexanderbez marked this conversation as resolved.
Show resolved Hide resolved
if globalMinSelfDelegation.GT(validator.MinSelfDelegation) {
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
return nil, types.ErrMinSelfDelegationBelowMinimum
}

if msg.MinSelfDelegation != nil {
if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) {
return nil, types.ErrMinSelfDelegationDecreased
Expand Down
83 changes: 83 additions & 0 deletions x/staking/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func TestCreateValidatorWithLessThanMinCommission(t *testing.T) {
// set min commission rate to non-zero
params := app.StakingKeeper.GetParams(ctx)
params.MinCommissionRate = sdk.NewDecWithPrec(1, 2)
params.MinSelfDelegation = sdk.NewInt(0)
app.StakingKeeper.SetParams(ctx, params)

// create validator with 0% commission
Expand All @@ -51,3 +52,85 @@ func TestCreateValidatorWithLessThanMinCommission(t *testing.T) {
_, err = sh(ctx.WithBlockHeight(712001), msg2)
require.Error(t, err)
}

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

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 two validators with 0% commission and self delegations that are above and below the minimum, respectively
msg1, 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)
msg2, err := stakingtypes.NewMsgCreateValidator(
sdk.ValAddress(addrs[1]),
valConsPk2,
sdk.NewInt64Coin(sdk.DefaultBondDenom, 0),
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, msg1)
require.NoError(t, err)
_, err = sh(ctx, msg2)
require.Error(t, err)
}

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

app := simapp.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

addrs := simapp.AddTestAddrs(app, ctx, 3, sdk.NewInt(1234))

// set min commission rate and min self delegation to non-zero
params := app.StakingKeeper.GetParams(ctx)
params.MinCommissionRate = sdk.NewDecWithPrec(1, 2)
params.MinSelfDelegation = sdk.NewInt(5)
app.StakingKeeper.SetParams(ctx, params)

// create two validators with 0% commission and self delegations that are above and below the minimum, respectively
msg1, 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)),
params.MinSelfDelegation)
require.NoError(t, err)
msg2, err := stakingtypes.NewMsgCreateValidator(
sdk.ValAddress(addrs[1]),
valConsPk2,
sdk.NewInt64Coin(sdk.DefaultBondDenom, 10),
stakingtypes.Description{},
stakingtypes.NewCommissionRates(sdk.NewDec(0), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)),
// input minimum self delegation that is less than the allowed global minimum
sdk.OneInt())
require.NoError(t, err)

sh := staking.NewHandler(app.StakingKeeper)
_, err = sh(ctx, msg1)
require.NoError(t, err)
_, err = sh(ctx, msg2)
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 @@ -53,6 +53,12 @@ func (k Keeper) MinCommissionRate(ctx sdk.Context) (res sdk.Dec) {
return
}

// MinSelfDelegation - Minimum validator self-delegation
func (k Keeper) MinSelfDelegation(ctx sdk.Context) (res sdk.Int) {
k.paramstore.Get(ctx, types.KeyMinSelfDelegation, &res)
return
}

// Get all parameters as types.Params
func (k Keeper) GetParams(ctx sdk.Context) types.Params {
return types.NewParams(
Expand All @@ -62,6 +68,7 @@ func (k Keeper) GetParams(ctx sdk.Context) types.Params {
k.HistoricalEntries(ctx),
k.BondDenom(ctx),
k.MinCommissionRate(ctx),
k.MinSelfDelegation(ctx),
)
}

Expand Down
24 changes: 24 additions & 0 deletions x/staking/legacy/v046/store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package v046

//package v046
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved

import (
sdk "github.com/cosmos/cosmos-sdk/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"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:
//
// - Setting the MinCommissionRate param in the paramstore
func MigrateStore(ctx sdk.Context, paramstore paramtypes.Subspace) error {
migrateParamsStore(ctx, paramstore)

return nil
}

func migrateParamsStore(ctx sdk.Context, paramstore paramtypes.Subspace) {
paramstore.WithKeyTable(types.ParamKeyTable())
paramstore.Set(ctx, types.KeyMinSelfDelegation, types.DefaultMinSelfDelegation)
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved
}
34 changes: 34 additions & 0 deletions x/staking/legacy/v046/store_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package v046_test

//package v046_test
AlpinYukseloglu marked this conversation as resolved.
Show resolved Hide resolved

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"
v046staking "github.com/cosmos/cosmos-sdk/x/staking/legacy/v046"
"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.Marshaler, encCfg.Amino, stakingKey, tStakingKey, "staking")

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

// Run migrations.
err := v046staking.MigrateStore(ctx, paramstore)
require.NoError(t, err)

// Make sure the new params are set.
require.True(t, paramstore.Has(ctx, types.KeyMinSelfDelegation))
}
1 change: 1 addition & 0 deletions x/staking/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {

m := keeper.NewMigrator(am.keeper)
cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2)
cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3)
}

// InitGenesis performs genesis initialization for the staking module. It returns
Expand Down
3 changes: 2 additions & 1 deletion x/staking/simulation/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func RandomizedGenState(simState *module.SimulationState) {
maxVals uint32
histEntries uint32
minComRate sdk.Dec
minSelfDel sdk.Int
)

simState.AppParams.GetOrGenerate(
Expand All @@ -70,7 +71,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, minComRate)
params := types.NewParams(simState.UnbondTime, maxVals, 7, histEntries, sdk.DefaultBondDenom, minComRate, minSelfDel)

// validators & delegations
var (
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 @@ -50,4 +50,5 @@ var (
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")
ErrMinSelfDelegationBelowMinimum = sdkerrors.Register(ModuleName, 41, "validator's minimum self delegation must be greater than the global minimum self delegation")
)
24 changes: 23 additions & 1 deletion x/staking/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ var (
DefaultMinCommissionRate = sdk.ZeroDec()
)

var (
// DefaultMinSelfDelegation is set to 0
DefaultMinSelfDelegation = sdk.ZeroInt()
)

var (
KeyUnbondingTime = []byte("UnbondingTime")
KeyMaxValidators = []byte("MaxValidators")
Expand All @@ -45,6 +50,7 @@ var (
KeyHistoricalEntries = []byte("HistoricalEntries")
KeyPowerReduction = []byte("PowerReduction")
KeyMinCommissionRate = []byte("MinCommissionRate")
KeyMinSelfDelegation = []byte("MinSelfDelegation")
)

var _ paramtypes.ParamSet = (*Params)(nil)
Expand All @@ -55,14 +61,15 @@ func ParamKeyTable() paramtypes.KeyTable {
}

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

Expand All @@ -75,6 +82,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
paramtypes.NewParamSetPair(KeyHistoricalEntries, &p.HistoricalEntries, validateHistoricalEntries),
paramtypes.NewParamSetPair(KeyBondDenom, &p.BondDenom, validateBondDenom),
paramtypes.NewParamSetPair(KeyMinCommissionRate, &p.MinCommissionRate, validateMinCommissionRate),
paramtypes.NewParamSetPair(KeyMinSelfDelegation, &p.MinSelfDelegation, validateMinSelfDelegation),
}
}

Expand All @@ -87,6 +95,7 @@ func DefaultParams() Params {
DefaultHistoricalEntries,
sdk.DefaultBondDenom,
DefaultMinCommissionRate,
DefaultMinSelfDelegation,
)
}

Expand Down Expand Up @@ -235,3 +244,16 @@ func validateMinCommissionRate(i interface{}) error {

return nil
}

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

if v.IsNegative() {
return fmt.Errorf("minimum self delegation cannot be negative: %s", v)
}

return nil
}
Loading