Merge PR #3546: Min Self Delegation
This commit is contained in:
parent
ba63eb1801
commit
cff985ffc5
@ -21,6 +21,7 @@ BREAKING CHANGES
|
||||
|
||||
* Gaia
|
||||
* [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec
|
||||
* [\#3495](https://github.com/cosmos/cosmos-sdk/issues/3495) Added Validator Minimum Self Delegation
|
||||
* Reintroduce OR semantics for tx fees
|
||||
|
||||
* SDK
|
||||
|
||||
@ -238,6 +238,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
||||
sdk.NewCoin(staking.DefaultBondDenom, startTokens),
|
||||
staking.NewDescription(fmt.Sprintf("validator-%d", i+1), "", "", ""),
|
||||
staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||
sdk.OneInt(),
|
||||
)
|
||||
stdSignMsg := txbuilder.StdSignMsg{
|
||||
ChainID: genDoc.ChainID,
|
||||
|
||||
@ -105,7 +105,7 @@ func makeMsg(name string, pk crypto.PubKey) auth.StdTx {
|
||||
desc := staking.NewDescription(name, "", "", "")
|
||||
comm := staking.CommissionMsg{}
|
||||
msg := staking.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(bondDenom,
|
||||
50), desc, comm)
|
||||
50), desc, comm, sdk.OneInt())
|
||||
return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "")
|
||||
}
|
||||
|
||||
|
||||
@ -303,6 +303,7 @@ func (f *Fixtures) TxStakingCreateValidator(from, consPubKey string, amount sdk.
|
||||
cmd := fmt.Sprintf("gaiacli tx staking create-validator %v --from=%s --pubkey=%s", f.Flags(), from, consPubKey)
|
||||
cmd += fmt.Sprintf(" --amount=%v --moniker=%v --commission-rate=%v", amount, from, "0.05")
|
||||
cmd += fmt.Sprintf(" --commission-max-rate=%v --commission-max-change-rate=%v", "0.20", "0.10")
|
||||
cmd += fmt.Sprintf(" --min-self-delegation=%v", "1")
|
||||
return executeWriteRetStdStreams(f.T, addFlags(cmd, flags), app.DefaultKeyPass)
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ var (
|
||||
defaultCommissionRate = "0.1"
|
||||
defaultCommissionMaxRate = "0.2"
|
||||
defaultCommissionMaxChangeRate = "0.01"
|
||||
defaultMinSelfDelegation = "1"
|
||||
)
|
||||
|
||||
// GenTxCmd builds the gaiad gentx command.
|
||||
@ -53,7 +54,8 @@ following delegation and commission default parameters:
|
||||
commission rate: %s
|
||||
commission max rate: %s
|
||||
commission max change rate: %s
|
||||
`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate),
|
||||
minimum self delegation: %s
|
||||
`, defaultAmount, defaultCommissionRate, defaultCommissionMaxRate, defaultCommissionMaxChangeRate, defaultMinSelfDelegation),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
config := ctx.Config
|
||||
@ -173,6 +175,7 @@ following delegation and commission default parameters:
|
||||
cmd.Flags().String(cli.FlagIP, ip, "The node's public IP")
|
||||
cmd.Flags().String(cli.FlagNodeID, "", "The node's NodeID")
|
||||
cmd.Flags().AddFlagSet(cli.FsCommissionCreate)
|
||||
cmd.Flags().AddFlagSet(cli.FsMinSelfDelegation)
|
||||
cmd.Flags().AddFlagSet(cli.FsAmount)
|
||||
cmd.Flags().AddFlagSet(cli.FsPk)
|
||||
cmd.MarkFlagRequired(client.FlagName)
|
||||
@ -232,6 +235,9 @@ func prepareFlagsForTxCreateValidator(config *cfg.Config, nodeID, ip, chainID st
|
||||
if viper.GetString(cli.FlagCommissionMaxChangeRate) == "" {
|
||||
viper.Set(cli.FlagCommissionMaxChangeRate, defaultCommissionMaxChangeRate)
|
||||
}
|
||||
if viper.GetString(cli.FlagMinSelfDelegation) == "" {
|
||||
viper.Set(cli.FlagMinSelfDelegation, defaultMinSelfDelegation)
|
||||
}
|
||||
}
|
||||
|
||||
func makeOutputFilepath(rootDir, nodeID string) (string, error) {
|
||||
|
||||
@ -207,6 +207,7 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error {
|
||||
sdk.NewCoin(stakingtypes.DefaultBondDenom, valTokens),
|
||||
staking.NewDescription(nodeDirName, "", "", ""),
|
||||
staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||
sdk.OneInt(),
|
||||
)
|
||||
kb, err := keys.NewKeyBaseFromDir(clientDir)
|
||||
if err != nil {
|
||||
|
||||
@ -46,6 +46,7 @@ type Validator interface {
|
||||
GetBondedTokens() Int // validator bonded tokens
|
||||
GetTendermintPower() int64 // validation power in tendermint
|
||||
GetCommission() Dec // validator commission rate
|
||||
GetMinSelfDelegation() Int // validator minimum self delegation
|
||||
GetDelegatorShares() Dec // total outstanding delegator shares
|
||||
GetDelegatorShareExRate() Dec // tokens per delegator share exchange rate
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ func TestAllocateTokensToValidatorWithCommission(t *testing.T) {
|
||||
// create validator with 50% commission
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
val := sk.Validator(ctx, valOpAddr1)
|
||||
|
||||
@ -50,13 +50,13 @@ func TestAllocateTokensToManyValidators(t *testing.T) {
|
||||
// create validator with 50% commission
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// create second validator with 0% commission
|
||||
commission = staking.NewCommissionMsg(sdk.NewDec(0), sdk.NewDec(0), sdk.NewDec(0))
|
||||
msg = staking.NewMsgCreateValidator(valOpAddr2, valConsPk2,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
abciValA := abci.Validator{
|
||||
|
||||
@ -19,7 +19,7 @@ func TestCalculateRewardsBasic(t *testing.T) {
|
||||
// create validator with 50% commission
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
@ -74,7 +74,7 @@ func TestCalculateRewardsAfterSlash(t *testing.T) {
|
||||
valPower := int64(100)
|
||||
valTokens := staking.TokensFromTendermintPower(valPower)
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt())
|
||||
got := sh(ctx, msg)
|
||||
require.True(t, got.IsOK(), "%v", got)
|
||||
|
||||
@ -137,7 +137,7 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) {
|
||||
valTokens := staking.TokensFromTendermintPower(power)
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
@ -209,7 +209,7 @@ func TestCalculateRewardsMultiDelegator(t *testing.T) {
|
||||
// create validator with 50% commission
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
@ -271,7 +271,7 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
|
||||
valTokens := staking.TokensFromTendermintPower(power)
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// assert correct initial balance
|
||||
@ -327,7 +327,7 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) {
|
||||
valTokens := staking.TokensFromTendermintPower(power)
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
@ -394,7 +394,7 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) {
|
||||
power := int64(100)
|
||||
valTokens := staking.TokensFromTendermintPower(power)
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
@ -464,7 +464,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
|
||||
// create validator with 50% commission
|
||||
commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
|
||||
// end block to bond validator
|
||||
|
||||
@ -158,7 +158,7 @@ func TestQueries(t *testing.T) {
|
||||
keeper.SetOutstandingRewards(ctx, sdk.DecCoins{})
|
||||
comm := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0))
|
||||
msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1,
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, comm)
|
||||
sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, comm, sdk.OneInt())
|
||||
require.True(t, sh(ctx, msg).IsOK())
|
||||
staking.EndBlocker(ctx, sk)
|
||||
val := sk.Validator(ctx, valOpAddr1)
|
||||
|
||||
@ -28,7 +28,7 @@ func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context,
|
||||
valTokens := staking.TokensFromTendermintPower(powerAmt[i])
|
||||
valCreateMsg := staking.NewMsgCreateValidator(
|
||||
addrs[i], pubkeys[i], sdk.NewCoin(staking.DefaultBondDenom, valTokens),
|
||||
testDescription, testCommissionMsg,
|
||||
testDescription, testCommissionMsg, sdk.OneInt(),
|
||||
)
|
||||
|
||||
res := stakingHandler(ctx, valCreateMsg)
|
||||
@ -426,19 +426,19 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
|
||||
|
||||
valTokens1 := staking.TokensFromTendermintPower(25)
|
||||
val1CreateMsg := staking.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens1), testDescription, testCommissionMsg,
|
||||
sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens1), testDescription, testCommissionMsg, sdk.OneInt(),
|
||||
)
|
||||
stakingHandler(ctx, val1CreateMsg)
|
||||
|
||||
valTokens2 := staking.TokensFromTendermintPower(6)
|
||||
val2CreateMsg := staking.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens2), testDescription, testCommissionMsg,
|
||||
sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens2), testDescription, testCommissionMsg, sdk.OneInt(),
|
||||
)
|
||||
stakingHandler(ctx, val2CreateMsg)
|
||||
|
||||
valTokens3 := staking.TokensFromTendermintPower(7)
|
||||
val3CreateMsg := staking.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens3), testDescription, testCommissionMsg,
|
||||
sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens3), testDescription, testCommissionMsg, sdk.OneInt(),
|
||||
)
|
||||
stakingHandler(ctx, val3CreateMsg)
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@ func TestSlashingMsgs(t *testing.T) {
|
||||
commission := staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
|
||||
|
||||
createValidatorMsg := staking.NewMsgCreateValidator(
|
||||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission,
|
||||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission, sdk.OneInt(),
|
||||
)
|
||||
mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1)
|
||||
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})
|
||||
|
||||
@ -16,6 +16,7 @@ const (
|
||||
CodeValidatorJailed CodeType = 102
|
||||
CodeValidatorNotJailed CodeType = 103
|
||||
CodeMissingSelfDelegation CodeType = 104
|
||||
CodeSelfDelegationTooLow CodeType = 105
|
||||
)
|
||||
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
@ -37,3 +38,7 @@ func ErrValidatorNotJailed(codespace sdk.CodespaceType) sdk.Error {
|
||||
func ErrMissingSelfDelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeMissingSelfDelegation, "validator has no self-delegation; cannot be unjailed")
|
||||
}
|
||||
|
||||
func ErrSelfDelegationTooLowToUnjail(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeValidatorNotJailed, "validator's self delegation less than MinSelfDelegation, cannot be unjailed")
|
||||
}
|
||||
|
||||
@ -31,6 +31,10 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
|
||||
return ErrMissingSelfDelegation(k.codespace).Result()
|
||||
}
|
||||
|
||||
if validator.GetDelegatorShareExRate().Mul(selfDel.GetShares()).TruncateInt().LT(validator.GetMinSelfDelegation()) {
|
||||
return ErrSelfDelegationTooLowToUnjail(k.codespace).Result()
|
||||
}
|
||||
|
||||
// cannot be unjailed if not jailed
|
||||
if !validator.GetJailed() {
|
||||
return ErrValidatorNotJailed(k.codespace).Result()
|
||||
|
||||
@ -35,6 +35,35 @@ func TestCannotUnjailUnlessJailed(t *testing.T) {
|
||||
require.EqualValues(t, DefaultCodespace, got.Codespace)
|
||||
}
|
||||
|
||||
func TestCannotUnjailUnlessMeetMinSelfDelegation(t *testing.T) {
|
||||
// initial setup
|
||||
ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams())
|
||||
slh := NewHandler(keeper)
|
||||
amtInt := int64(100)
|
||||
addr, val, amt := addrs[0], pks[0], types.TokensFromTendermintPower(amtInt)
|
||||
msg := NewTestMsgCreateValidator(addr, val, amt)
|
||||
msg.MinSelfDelegation = amt
|
||||
got := staking.NewHandler(sk)(ctx, msg)
|
||||
require.True(t, got.IsOK())
|
||||
staking.EndBlocker(ctx, sk)
|
||||
|
||||
require.Equal(
|
||||
t, ck.GetCoins(ctx, sdk.AccAddress(addr)),
|
||||
sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))},
|
||||
)
|
||||
|
||||
undelegateMsg := staking.NewMsgUndelegate(sdk.AccAddress(addr), addr, sdk.OneDec())
|
||||
got = staking.NewHandler(sk)(ctx, undelegateMsg)
|
||||
|
||||
require.True(t, sk.Validator(ctx, addr).GetJailed())
|
||||
|
||||
// assert non-jailed validator can't be unjailed
|
||||
got = slh(ctx, NewMsgUnjail(addr))
|
||||
require.False(t, got.IsOK(), "allowed unjail of validator with less than MinSelfDelegation")
|
||||
require.EqualValues(t, CodeValidatorNotJailed, got.Code)
|
||||
require.EqualValues(t, DefaultCodespace, got.Codespace)
|
||||
}
|
||||
|
||||
func TestJailedValidatorDelegations(t *testing.T) {
|
||||
ctx, _, stakingKeeper, _, slashingKeeper := createTestInput(t, DefaultParams())
|
||||
|
||||
|
||||
@ -18,10 +18,10 @@ const (
|
||||
|
||||
// The Double Sign Jail period ends at Max Time supported by Amino (Dec 31, 9999 - 23:59:59 GMT)
|
||||
var (
|
||||
DoubleSignJailEndTime = time.Unix(253402300799, 0)
|
||||
DefaultMinSignedPerWindow sdk.Dec = sdk.NewDecWithPrec(5, 1)
|
||||
DefaultSlashFractionDoubleSign sdk.Dec = sdk.NewDec(1).Quo(sdk.NewDec(20))
|
||||
DefaultSlashFractionDowntime sdk.Dec = sdk.NewDec(1).Quo(sdk.NewDec(100))
|
||||
DoubleSignJailEndTime = time.Unix(253402300799, 0)
|
||||
DefaultMinSignedPerWindow = sdk.NewDecWithPrec(5, 1)
|
||||
DefaultSlashFractionDoubleSign = sdk.NewDec(1).Quo(sdk.NewDec(20))
|
||||
DefaultSlashFractionDowntime = sdk.NewDec(1).Quo(sdk.NewDec(100))
|
||||
)
|
||||
|
||||
// Parameter store keys
|
||||
|
||||
@ -116,7 +116,7 @@ func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt
|
||||
commission := staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
|
||||
return staking.NewMsgCreateValidator(
|
||||
address, pubKey, sdk.NewCoin(staking.DefaultBondDenom, amt),
|
||||
staking.Description{}, commission,
|
||||
staking.Description{}, commission, sdk.OneInt(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -119,7 +119,7 @@ func TestStakingMsgs(t *testing.T) {
|
||||
// create validator
|
||||
description := NewDescription("foo_moniker", "", "", "")
|
||||
createValidatorMsg := NewMsgCreateValidator(
|
||||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionMsg,
|
||||
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionMsg, sdk.OneInt(),
|
||||
)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1)
|
||||
@ -133,7 +133,7 @@ func TestStakingMsgs(t *testing.T) {
|
||||
|
||||
// addr1 create validator on behalf of addr2
|
||||
createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(
|
||||
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg,
|
||||
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg, sdk.OneInt(),
|
||||
)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []uint64{0, 0}, []uint64{1, 0}, true, true, priv1, priv2)
|
||||
@ -150,7 +150,7 @@ func TestStakingMsgs(t *testing.T) {
|
||||
|
||||
// edit the validator
|
||||
description = NewDescription("bar_moniker", "", "", "")
|
||||
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil)
|
||||
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil, nil)
|
||||
|
||||
mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{2}, true, true, priv1)
|
||||
validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true)
|
||||
|
||||
@ -26,8 +26,11 @@ const (
|
||||
FlagCommissionMaxRate = "commission-max-rate"
|
||||
FlagCommissionMaxChangeRate = "commission-max-change-rate"
|
||||
|
||||
FlagNodeID = "node-id"
|
||||
FlagIP = "ip"
|
||||
FlagMinSelfDelegation = "min-self-delegation"
|
||||
|
||||
FlagGenesisFormat = "genesis-format"
|
||||
FlagNodeID = "node-id"
|
||||
FlagIP = "ip"
|
||||
)
|
||||
|
||||
// common flagsets to add to various functions
|
||||
@ -38,6 +41,7 @@ var (
|
||||
fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
FsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
FsMinSelfDelegation = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
@ -57,6 +61,7 @@ func init() {
|
||||
FsCommissionCreate.String(FlagCommissionRate, "", "The initial commission rate percentage")
|
||||
FsCommissionCreate.String(FlagCommissionMaxRate, "", "The maximum commission rate percentage")
|
||||
FsCommissionCreate.String(FlagCommissionMaxChangeRate, "", "The maximum commission change rate percentage (per day)")
|
||||
FsMinSelfDelegation.String(FlagMinSelfDelegation, "", "The minimum self delegation required on the validator")
|
||||
fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "The validator's name")
|
||||
fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "The (optional) identity signature (ex. UPort or Keybase)")
|
||||
fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "The validator's (optional) website")
|
||||
|
||||
@ -43,6 +43,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
|
||||
cmd.Flags().AddFlagSet(FsAmount)
|
||||
cmd.Flags().AddFlagSet(fsDescriptionCreate)
|
||||
cmd.Flags().AddFlagSet(FsCommissionCreate)
|
||||
cmd.Flags().AddFlagSet(FsMinSelfDelegation)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().String(FlagIP, "", fmt.Sprintf("The node's public IP. It takes effect only when used in combination with --%s", client.FlagGenerateOnly))
|
||||
cmd.Flags().String(FlagNodeID, "", "The node's ID")
|
||||
@ -87,7 +88,20 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command {
|
||||
newRate = &rate
|
||||
}
|
||||
|
||||
msg := staking.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate)
|
||||
var newMinSelfDelegation *sdk.Int
|
||||
|
||||
minSelfDelegationString := viper.GetString(FlagMinSelfDelegation)
|
||||
if minSelfDelegationString != "" {
|
||||
msb, ok := sdk.NewIntFromString(minSelfDelegationString)
|
||||
if !ok {
|
||||
return fmt.Errorf(staking.ErrMinSelfDelegationInvalid(staking.DefaultCodespace).Error())
|
||||
}
|
||||
newMinSelfDelegation = &msb
|
||||
}
|
||||
|
||||
msg := staking.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate, newMinSelfDelegation)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}, false)
|
||||
},
|
||||
}
|
||||
@ -238,6 +252,13 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
|
||||
return txBldr, nil, err
|
||||
}
|
||||
|
||||
// get the initial validator min self delegation
|
||||
msbStr := viper.GetString(FlagMinSelfDelegation)
|
||||
minSelfDelegation, ok := sdk.NewIntFromString(msbStr)
|
||||
if !ok {
|
||||
return txBldr, nil, fmt.Errorf(staking.ErrMinSelfDelegationInvalid(staking.DefaultCodespace).Error())
|
||||
}
|
||||
|
||||
delAddr := viper.GetString(FlagAddressDelegator)
|
||||
|
||||
var msg sdk.Msg
|
||||
@ -248,11 +269,11 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder
|
||||
}
|
||||
|
||||
msg = staking.NewMsgCreateValidatorOnBehalfOf(
|
||||
delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg,
|
||||
delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation,
|
||||
)
|
||||
} else {
|
||||
msg = staking.NewMsgCreateValidator(
|
||||
sdk.ValAddress(valAddr), pk, amount, description, commissionMsg,
|
||||
sdk.ValAddress(valAddr), pk, amount, description, commissionMsg, minSelfDelegation,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -126,6 +126,8 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
validator.MinSelfDelegation = msg.MinSelfDelegation
|
||||
|
||||
k.SetValidator(ctx, validator)
|
||||
k.SetValidatorByConsAddr(ctx, validator)
|
||||
k.SetNewValidatorByPowerIndex(ctx, validator)
|
||||
@ -178,6 +180,16 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
|
||||
validator.Commission = commission
|
||||
}
|
||||
|
||||
if msg.MinSelfDelegation != nil {
|
||||
if !(*msg.MinSelfDelegation).GT(validator.MinSelfDelegation) {
|
||||
return ErrMinSelfDelegationDecreased(k.Codespace()).Result()
|
||||
}
|
||||
if (*msg.MinSelfDelegation).GT(validator.Tokens) {
|
||||
return ErrSelfDelegationBelowMinimum(k.Codespace()).Result()
|
||||
}
|
||||
validator.MinSelfDelegation = (*msg.MinSelfDelegation)
|
||||
}
|
||||
|
||||
k.SetValidator(ctx, validator)
|
||||
|
||||
tags := sdk.NewTags(
|
||||
|
||||
@ -375,6 +375,70 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditValidatorDecreaseMinSelfDelegation(t *testing.T) {
|
||||
validatorAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
|
||||
initPower := int64(100)
|
||||
initBond := types.TokensFromTendermintPower(100)
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, initPower)
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator
|
||||
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
msgCreateValidator.MinSelfDelegation = sdk.NewInt(2)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// must end-block
|
||||
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
|
||||
// verify the self-delegation exists
|
||||
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
|
||||
require.True(t, found)
|
||||
gotBond := bond.Shares.RoundInt()
|
||||
require.Equal(t, initBond, gotBond,
|
||||
"initBond: %v\ngotBond: %v\nbond: %v\n",
|
||||
initBond, gotBond, bond)
|
||||
|
||||
newMinSelfDelegation := sdk.OneInt()
|
||||
msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation)
|
||||
got = handleMsgEditValidator(ctx, msgEditValidator, keeper)
|
||||
require.False(t, got.IsOK(), "should not be able to decrease minSelfDelegation")
|
||||
}
|
||||
|
||||
func TestEditValidatorIncreaseMinSelfDelegationBeyondCurrentBond(t *testing.T) {
|
||||
validatorAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
|
||||
initPower := int64(100)
|
||||
initBond := types.TokensFromTendermintPower(100)
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, initPower)
|
||||
_ = setInstantUnbondPeriod(keeper, ctx)
|
||||
|
||||
// create validator
|
||||
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], initBond)
|
||||
msgCreateValidator.MinSelfDelegation = sdk.NewInt(2)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got)
|
||||
|
||||
// must end-block
|
||||
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
|
||||
// verify the self-delegation exists
|
||||
bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
|
||||
require.True(t, found)
|
||||
gotBond := bond.Shares.RoundInt()
|
||||
require.Equal(t, initBond, gotBond,
|
||||
"initBond: %v\ngotBond: %v\nbond: %v\n",
|
||||
initBond, gotBond, bond)
|
||||
|
||||
newMinSelfDelegation := initBond.Add(sdk.OneInt())
|
||||
msgEditValidator := NewMsgEditValidator(validatorAddr, Description{}, nil, &newMinSelfDelegation)
|
||||
got = handleMsgEditValidator(ctx, msgEditValidator, keeper)
|
||||
require.False(t, got.IsOK(), "should not be able to increase minSelfDelegation above current self delegation")
|
||||
}
|
||||
|
||||
func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
initPower := int64(1000)
|
||||
initBond := TokensFromTendermintPower(initPower)
|
||||
|
||||
@ -516,13 +516,17 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
||||
// subtract shares from delegation
|
||||
delegation.Shares = delegation.Shares.Sub(shares)
|
||||
|
||||
// update or remove the delegation
|
||||
isValidatorOperator := bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr)
|
||||
|
||||
// if the delegation is the operator of the validator and undelegating will decrease the validator's self delegation below their minimum
|
||||
// trigger a jail validator
|
||||
if isValidatorOperator && !validator.Jailed && validator.DelegatorShareExRate().Mul(delegation.Shares).TruncateInt().LT(validator.MinSelfDelegation) {
|
||||
k.jailValidator(ctx, validator)
|
||||
validator = k.mustGetValidator(ctx, validator.OperatorAddr)
|
||||
}
|
||||
|
||||
// remove the delegation
|
||||
if delegation.Shares.IsZero() {
|
||||
// if the delegator is the operator of the validator then jail the validator
|
||||
if bytes.Equal(delegation.DelegatorAddr, validator.OperatorAddr) && !validator.Jailed {
|
||||
k.jailValidator(ctx, validator)
|
||||
validator = k.mustGetValidator(ctx, validator.OperatorAddr)
|
||||
}
|
||||
k.RemoveDelegation(ctx, delegation)
|
||||
} else {
|
||||
k.SetDelegation(ctx, delegation)
|
||||
|
||||
@ -250,9 +250,10 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// test removing all self delegation from a validator which should
|
||||
// shift it from the bonded to unbonded state
|
||||
func TestUndelegateSelfDelegation(t *testing.T) {
|
||||
// test undelegating self delegation from a validator pushing it below MinSelfDelegation
|
||||
// shift it from the bonded to unbonding state and jailed
|
||||
func TestUndelegateSelfDelegationBelowMinSelfDelegation(t *testing.T) {
|
||||
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
startTokens := types.TokensFromTendermintPower(20)
|
||||
@ -260,9 +261,12 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||
|
||||
//create a validator with a self-delegation
|
||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||
|
||||
valTokens := types.TokensFromTendermintPower(10)
|
||||
validator.MinSelfDelegation = valTokens
|
||||
validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens)
|
||||
require.Equal(t, valTokens, issuedShares.RoundInt())
|
||||
|
||||
keeper.SetPool(ctx, pool)
|
||||
validator = TestingUpdateValidator(keeper, ctx, validator, true)
|
||||
pool = keeper.GetPool(ctx)
|
||||
@ -281,7 +285,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
|
||||
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
|
||||
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens))
|
||||
_, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(types.TokensFromTendermintPower(6)))
|
||||
require.NoError(t, err)
|
||||
|
||||
// end block
|
||||
@ -290,8 +294,9 @@ func TestUndelegateSelfDelegation(t *testing.T) {
|
||||
|
||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Equal(t, delTokens, validator.Tokens)
|
||||
require.Equal(t, types.TokensFromTendermintPower(14), validator.Tokens)
|
||||
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||
require.True(t, validator.Jailed)
|
||||
}
|
||||
|
||||
func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||
|
||||
@ -44,7 +44,7 @@ func SimulateMsgCreateValidator(m auth.AccountKeeper, k staking.Keeper) simulati
|
||||
|
||||
selfDelegation := sdk.NewCoin(denom, amount)
|
||||
msg := staking.NewMsgCreateValidator(address, acc.PubKey,
|
||||
selfDelegation, description, commission)
|
||||
selfDelegation, description, commission, sdk.OneInt())
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
@ -82,16 +82,11 @@ func SimulateMsgEditValidator(k staking.Keeper) simulation.Operation {
|
||||
address := val.GetOperator()
|
||||
newCommissionRate := simulation.RandomDecAmount(r, val.Commission.MaxRate)
|
||||
|
||||
msg := staking.MsgEditValidator{
|
||||
Description: description,
|
||||
ValidatorAddr: address,
|
||||
CommissionRate: &newCommissionRate,
|
||||
}
|
||||
msg := staking.NewMsgEditValidator(address, description, &newCommissionRate, nil)
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
|
||||
@ -164,6 +164,10 @@ var (
|
||||
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
|
||||
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
|
||||
ErrMissingSignature = types.ErrMissingSignature
|
||||
|
||||
ErrMinSelfDelegationInvalid = types.ErrMinSelfDelegationInvalid
|
||||
ErrMinSelfDelegationDecreased = types.ErrMinSelfDelegationDecreased
|
||||
ErrSelfDelegationBelowMinimum = types.ErrSelfDelegationBelowMinimum
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -28,7 +28,7 @@ var (
|
||||
|
||||
func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {
|
||||
return types.NewMsgCreateValidator(
|
||||
address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commissionMsg,
|
||||
address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commissionMsg, sdk.OneInt(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -38,7 +38,15 @@ func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey cryp
|
||||
commission := NewCommissionMsg(commissionRate, sdk.OneDec(), sdk.ZeroDec())
|
||||
|
||||
return types.NewMsgCreateValidator(
|
||||
address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commission,
|
||||
address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commission, sdk.OneInt(),
|
||||
)
|
||||
}
|
||||
|
||||
func NewTestMsgCreateValidatorWithMinSelfDelegation(address sdk.ValAddress, pubKey crypto.PubKey,
|
||||
amt sdk.Int, minSelfDelegation sdk.Int) MsgCreateValidator {
|
||||
|
||||
return types.NewMsgCreateValidator(
|
||||
address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commissionMsg, minSelfDelegation,
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,5 +59,5 @@ func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.Val
|
||||
valPubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator {
|
||||
|
||||
amount := sdk.NewCoin(types.DefaultBondDenom, amt)
|
||||
return NewMsgCreateValidatorOnBehalfOf(delAddr, valAddr, valPubKey, amount, Description{}, commissionMsg)
|
||||
return NewMsgCreateValidatorOnBehalfOf(delAddr, valAddr, valPubKey, amount, Description{}, commissionMsg, sdk.OneInt())
|
||||
}
|
||||
|
||||
@ -91,6 +91,18 @@ func ErrCommissionGTMaxChangeRate(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be changed more than max change rate")
|
||||
}
|
||||
|
||||
func ErrSelfDelegationBelowMinimum(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "validator's self delegation must be greater than their minimum self delegation")
|
||||
}
|
||||
|
||||
func ErrMinSelfDelegationInvalid(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "minimum self delegation must be a positive integer")
|
||||
}
|
||||
|
||||
func ErrMinSelfDelegationDecreased(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "minimum self delegation cannot be decrease")
|
||||
}
|
||||
|
||||
func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil")
|
||||
}
|
||||
|
||||
@ -23,42 +23,45 @@ var (
|
||||
// MsgCreateValidator - struct for bonding transactions
|
||||
// TODO: Why does this need to contain a denomination in `Value`
|
||||
type MsgCreateValidator struct {
|
||||
Description Description `json:"description"`
|
||||
Commission CommissionMsg `json:"commission"`
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Value sdk.Coin `json:"value"`
|
||||
Description Description `json:"description"`
|
||||
Commission CommissionMsg `json:"commission"`
|
||||
MinSelfDelegation sdk.Int `json:"min_self_delegation"`
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Value sdk.Coin `json:"value"`
|
||||
}
|
||||
|
||||
type msgCreateValidatorJSON struct {
|
||||
Description Description `json:"description"`
|
||||
Commission CommissionMsg `json:"commission"`
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Value sdk.Coin `json:"value"`
|
||||
Description Description `json:"description"`
|
||||
Commission CommissionMsg `json:"commission"`
|
||||
MinSelfDelegation sdk.Int `json:"min_self_delegation"`
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_address"`
|
||||
PubKey string `json:"pubkey"`
|
||||
Value sdk.Coin `json:"value"`
|
||||
}
|
||||
|
||||
// Default way to create validator. Delegator address and validator address are the same
|
||||
func NewMsgCreateValidator(valAddr sdk.ValAddress, pubkey crypto.PubKey,
|
||||
selfDelegation sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator {
|
||||
selfDelegation sdk.Coin, description Description, commission CommissionMsg, minSelfDelegation sdk.Int) MsgCreateValidator {
|
||||
|
||||
return NewMsgCreateValidatorOnBehalfOf(
|
||||
sdk.AccAddress(valAddr), valAddr, pubkey, selfDelegation, description, commission,
|
||||
sdk.AccAddress(valAddr), valAddr, pubkey, selfDelegation, description, commission, minSelfDelegation,
|
||||
)
|
||||
}
|
||||
|
||||
// Creates validator msg by delegator address on behalf of validator address
|
||||
func NewMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress,
|
||||
pubkey crypto.PubKey, value sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator {
|
||||
pubkey crypto.PubKey, value sdk.Coin, description Description, commission CommissionMsg, minSelfDelegation sdk.Int) MsgCreateValidator {
|
||||
return MsgCreateValidator{
|
||||
Description: description,
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorAddr: valAddr,
|
||||
PubKey: pubkey,
|
||||
Value: value,
|
||||
Commission: commission,
|
||||
Description: description,
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorAddr: valAddr,
|
||||
PubKey: pubkey,
|
||||
Value: value,
|
||||
Commission: commission,
|
||||
MinSelfDelegation: minSelfDelegation,
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,12 +86,13 @@ func (msg MsgCreateValidator) GetSigners() []sdk.AccAddress {
|
||||
// serialization of the MsgCreateValidator type.
|
||||
func (msg MsgCreateValidator) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(msgCreateValidatorJSON{
|
||||
Description: msg.Description,
|
||||
Commission: msg.Commission,
|
||||
DelegatorAddr: msg.DelegatorAddr,
|
||||
ValidatorAddr: msg.ValidatorAddr,
|
||||
PubKey: sdk.MustBech32ifyConsPub(msg.PubKey),
|
||||
Value: msg.Value,
|
||||
Description: msg.Description,
|
||||
Commission: msg.Commission,
|
||||
DelegatorAddr: msg.DelegatorAddr,
|
||||
ValidatorAddr: msg.ValidatorAddr,
|
||||
PubKey: sdk.MustBech32ifyConsPub(msg.PubKey),
|
||||
Value: msg.Value,
|
||||
MinSelfDelegation: msg.MinSelfDelegation,
|
||||
})
|
||||
}
|
||||
|
||||
@ -106,6 +110,7 @@ func (msg *MsgCreateValidator) UnmarshalJSON(bz []byte) error {
|
||||
msg.ValidatorAddr = msgCreateValJSON.ValidatorAddr
|
||||
msg.PubKey = sdk.MustGetConsPubKeyBech32(msgCreateValJSON.PubKey)
|
||||
msg.Value = msgCreateValJSON.Value
|
||||
msg.MinSelfDelegation = msgCreateValJSON.MinSelfDelegation
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -134,6 +139,12 @@ func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
|
||||
if msg.Commission == (CommissionMsg{}) {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission must be included")
|
||||
}
|
||||
if !msg.MinSelfDelegation.GT(sdk.ZeroInt()) {
|
||||
return ErrMinSelfDelegationInvalid(DefaultCodespace)
|
||||
}
|
||||
if msg.Value.Amount.LT(msg.MinSelfDelegation) {
|
||||
return ErrSelfDelegationBelowMinimum(DefaultCodespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -143,19 +154,21 @@ type MsgEditValidator struct {
|
||||
Description
|
||||
ValidatorAddr sdk.ValAddress `json:"address"`
|
||||
|
||||
// We pass a reference to the new commission rate as it's not mandatory to
|
||||
// We pass a reference to the new commission rate and min self delegation as it's not mandatory to
|
||||
// update. If not updated, the deserialized rate will be zero with no way to
|
||||
// distinguish if an update was intended.
|
||||
//
|
||||
// REF: #2373
|
||||
CommissionRate *sdk.Dec `json:"commission_rate"`
|
||||
CommissionRate *sdk.Dec `json:"commission_rate"`
|
||||
MinSelfDelegation *sdk.Int `json:"min_self_delegation"`
|
||||
}
|
||||
|
||||
func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec) MsgEditValidator {
|
||||
func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec, newMinSelfDelegation *sdk.Int) MsgEditValidator {
|
||||
return MsgEditValidator{
|
||||
Description: description,
|
||||
CommissionRate: newRate,
|
||||
ValidatorAddr: valAddr,
|
||||
Description: description,
|
||||
CommissionRate: newRate,
|
||||
ValidatorAddr: valAddr,
|
||||
MinSelfDelegation: newMinSelfDelegation,
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,6 +195,10 @@ func (msg MsgEditValidator) ValidateBasic() sdk.Error {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
|
||||
}
|
||||
|
||||
if msg.MinSelfDelegation != nil && !(*msg.MinSelfDelegation).GT(sdk.ZeroInt()) {
|
||||
return ErrMinSelfDelegationInvalid(DefaultCodespace)
|
||||
}
|
||||
|
||||
if msg.CommissionRate != nil {
|
||||
if msg.CommissionRate.GT(sdk.OneDec()) || msg.CommissionRate.LT(sdk.ZeroDec()) {
|
||||
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission rate must be between 0 and 1, inclusive")
|
||||
|
||||
@ -23,22 +23,26 @@ func TestMsgCreateValidator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
commissionMsg CommissionMsg
|
||||
minSelfDelegation sdk.Int
|
||||
validatorAddr sdk.ValAddress
|
||||
pubkey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", commission1, addr1, pk1, coinPos, true},
|
||||
{"partial description", "", "", "c", "", commission1, addr1, pk1, coinPos, true},
|
||||
{"empty description", "", "", "", "", commission2, addr1, pk1, coinPos, false},
|
||||
{"empty address", "a", "b", "c", "d", commission2, emptyAddr, pk1, coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", commission1, addr1, emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", commission2, addr1, pk1, coinZero, false},
|
||||
{"basic good", "a", "b", "c", "d", commission1, sdk.OneInt(), addr1, pk1, coinPos, true},
|
||||
{"partial description", "", "", "c", "", commission1, sdk.OneInt(), addr1, pk1, coinPos, true},
|
||||
{"empty description", "", "", "", "", commission2, sdk.OneInt(), addr1, pk1, coinPos, false},
|
||||
{"empty address", "a", "b", "c", "d", commission2, sdk.OneInt(), emptyAddr, pk1, coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", commission1, sdk.OneInt(), addr1, emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", commission2, sdk.OneInt(), addr1, pk1, coinZero, false},
|
||||
{"zero min self delegation", "a", "b", "c", "d", commission1, sdk.ZeroInt(), addr1, pk1, coinPos, false},
|
||||
{"negative min self delegation", "a", "b", "c", "d", commission1, sdk.NewInt(-1), addr1, pk1, coinPos, false},
|
||||
{"delegation less than min self delegation", "a", "b", "c", "d", commission1, coinPos.Amount.Add(sdk.OneInt()), addr1, pk1, coinPos, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.commissionMsg)
|
||||
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.commissionMsg, tc.minSelfDelegation)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
@ -63,8 +67,9 @@ func TestMsgEditValidator(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
newRate := sdk.ZeroDec()
|
||||
newMinSelfDelegation := sdk.OneInt()
|
||||
|
||||
msg := NewMsgEditValidator(tc.validatorAddr, description, &newRate)
|
||||
msg := NewMsgEditValidator(tc.validatorAddr, description, &newRate, &newMinSelfDelegation)
|
||||
if tc.expectPass {
|
||||
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
@ -81,25 +86,29 @@ func TestMsgCreateValidatorOnBehalfOf(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
commissionMsg CommissionMsg
|
||||
minSelfDelegation sdk.Int
|
||||
delegatorAddr sdk.AccAddress
|
||||
validatorAddr sdk.ValAddress
|
||||
validatorPubKey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
|
||||
{"partial description", "", "", "c", "", commission2, sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
|
||||
{"empty description", "", "", "", "", commission1, sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
|
||||
{"empty delegator address", "a", "b", "c", "d", commission1, sdk.AccAddress(emptyAddr), addr2, pk2, coinPos, false},
|
||||
{"empty validator address", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), emptyAddr, pk2, coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", commission1, sdk.AccAddress(addr1), addr2, emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), addr2, pk2, coinZero, false},
|
||||
{"basic good", "a", "b", "c", "d", commission2, sdk.OneInt(), sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
|
||||
{"partial description", "", "", "c", "", commission2, sdk.OneInt(), sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
|
||||
{"empty description", "", "", "", "", commission1, sdk.OneInt(), sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
|
||||
{"empty delegator address", "a", "b", "c", "d", commission1, sdk.OneInt(), sdk.AccAddress(emptyAddr), addr2, pk2, coinPos, false},
|
||||
{"empty validator address", "a", "b", "c", "d", commission2, sdk.OneInt(), sdk.AccAddress(addr1), emptyAddr, pk2, coinPos, false},
|
||||
{"empty pubkey", "a", "b", "c", "d", commission1, sdk.OneInt(), sdk.AccAddress(addr1), addr2, emptyPubkey, coinPos, true},
|
||||
{"empty bond", "a", "b", "c", "d", commission2, sdk.OneInt(), sdk.AccAddress(addr1), addr2, pk2, coinZero, false},
|
||||
{"zero min self delegation", "a", "b", "c", "d", commission2, sdk.ZeroInt(), sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
|
||||
{"negative min self delegation", "", "", "c", "", commission2, sdk.NewInt(-1), sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
|
||||
{"delegation less than min self delegation", "a", "b", "c", "d", commission2, coinPos.Amount.Add(sdk.OneInt()), sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgCreateValidatorOnBehalfOf(
|
||||
tc.delegatorAddr, tc.validatorAddr, tc.validatorPubKey, tc.bond, description, tc.commissionMsg,
|
||||
tc.delegatorAddr, tc.validatorAddr, tc.validatorPubKey, tc.bond, description, tc.commissionMsg, tc.minSelfDelegation,
|
||||
)
|
||||
|
||||
if tc.expectPass {
|
||||
@ -109,11 +118,11 @@ func TestMsgCreateValidatorOnBehalfOf(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
msg := NewMsgCreateValidator(addr1, pk1, coinPos, Description{}, CommissionMsg{})
|
||||
msg := NewMsgCreateValidator(addr1, pk1, coinPos, Description{}, CommissionMsg{}, sdk.OneInt())
|
||||
addrs := msg.GetSigners()
|
||||
require.Equal(t, []sdk.AccAddress{sdk.AccAddress(addr1)}, addrs, "Signers on default msg is wrong")
|
||||
|
||||
msg = NewMsgCreateValidatorOnBehalfOf(sdk.AccAddress(addr2), addr1, pk1, coinPos, Description{}, CommissionMsg{})
|
||||
msg = NewMsgCreateValidatorOnBehalfOf(sdk.AccAddress(addr2), addr1, pk1, coinPos, Description{}, CommissionMsg{}, sdk.OneInt())
|
||||
addrs = msg.GetSigners()
|
||||
require.Equal(t, []sdk.AccAddress{sdk.AccAddress(addr2), sdk.AccAddress(addr1)}, addrs, "Signers for onbehalfof msg is wrong")
|
||||
}
|
||||
|
||||
@ -32,16 +32,17 @@ const (
|
||||
// divided by the current exchange rate. Voting power can be calculated as total
|
||||
// bonded shares multiplied by exchange rate.
|
||||
type Validator struct {
|
||||
OperatorAddr sdk.ValAddress `json:"operator_address"` // address of the validator's operator; bech encoded in JSON
|
||||
ConsPubKey crypto.PubKey `json:"consensus_pubkey"` // the consensus public key of the validator; bech encoded in JSON
|
||||
Jailed bool `json:"jailed"` // has the validator been jailed from bonded status?
|
||||
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||
Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||
DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
Description Description `json:"description"` // description terms for the validator
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
Commission Commission `json:"commission"` // commission parameters
|
||||
OperatorAddr sdk.ValAddress `json:"operator_address"` // address of the validator's operator; bech encoded in JSON
|
||||
ConsPubKey crypto.PubKey `json:"consensus_pubkey"` // the consensus public key of the validator; bech encoded in JSON
|
||||
Jailed bool `json:"jailed"` // has the validator been jailed from bonded status?
|
||||
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||
Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||
DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
Description Description `json:"description"` // description terms for the validator
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
Commission Commission `json:"commission"` // commission parameters
|
||||
MinSelfDelegation sdk.Int `json:"min_self_delegation"` // validator's self declared minimum self delegation
|
||||
}
|
||||
|
||||
// Validators is a collection of Validator
|
||||
@ -67,6 +68,7 @@ func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Des
|
||||
UnbondingHeight: int64(0),
|
||||
UnbondingCompletionTime: time.Unix(0, 0).UTC(),
|
||||
Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||
MinSelfDelegation: sdk.OneInt(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,24 +108,26 @@ func (v Validator) String() string {
|
||||
Description: %s
|
||||
Unbonding Height: %d
|
||||
Unbonding Completion Time: %v
|
||||
Minimum Self Delegation: %v
|
||||
Commission: %s`, v.OperatorAddr, bechConsPubKey,
|
||||
v.Jailed, sdk.BondStatusToString(v.Status), v.Tokens,
|
||||
v.DelegatorShares, v.Description,
|
||||
v.UnbondingHeight, v.UnbondingCompletionTime, v.Commission)
|
||||
v.UnbondingHeight, v.UnbondingCompletionTime, v.MinSelfDelegation, v.Commission)
|
||||
}
|
||||
|
||||
// this is a helper struct used for JSON de- and encoding only
|
||||
type bechValidator struct {
|
||||
OperatorAddr sdk.ValAddress `json:"operator_address"` // the bech32 address of the validator's operator
|
||||
ConsPubKey string `json:"consensus_pubkey"` // the bech32 consensus public key of the validator
|
||||
Jailed bool `json:"jailed"` // has the validator been jailed from bonded status?
|
||||
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||
Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||
DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
Description Description `json:"description"` // description terms for the validator
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
Commission Commission `json:"commission"` // commission parameters
|
||||
OperatorAddr sdk.ValAddress `json:"operator_address"` // the bech32 address of the validator's operator
|
||||
ConsPubKey string `json:"consensus_pubkey"` // the bech32 consensus public key of the validator
|
||||
Jailed bool `json:"jailed"` // has the validator been jailed from bonded status?
|
||||
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||
Tokens sdk.Int `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||
DelegatorShares sdk.Dec `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
Description Description `json:"description"` // description terms for the validator
|
||||
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
|
||||
UnbondingCompletionTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
|
||||
Commission Commission `json:"commission"` // commission parameters
|
||||
MinSelfDelegation sdk.Int `json:"min_self_delegation"` // minimum self delegation
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the validator to JSON using Bech32
|
||||
@ -143,6 +147,7 @@ func (v Validator) MarshalJSON() ([]byte, error) {
|
||||
Description: v.Description,
|
||||
UnbondingHeight: v.UnbondingHeight,
|
||||
UnbondingCompletionTime: v.UnbondingCompletionTime,
|
||||
MinSelfDelegation: v.MinSelfDelegation,
|
||||
Commission: v.Commission,
|
||||
})
|
||||
}
|
||||
@ -168,6 +173,7 @@ func (v *Validator) UnmarshalJSON(data []byte) error {
|
||||
UnbondingHeight: bv.UnbondingHeight,
|
||||
UnbondingCompletionTime: bv.UnbondingCompletionTime,
|
||||
Commission: bv.Commission,
|
||||
MinSelfDelegation: bv.MinSelfDelegation,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -444,5 +450,6 @@ func (v Validator) GetTokens() sdk.Int { return v.Tokens }
|
||||
func (v Validator) GetBondedTokens() sdk.Int { return v.BondedTokens() }
|
||||
func (v Validator) GetTendermintPower() int64 { return v.TendermintPower() }
|
||||
func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate }
|
||||
func (v Validator) GetMinSelfDelegation() sdk.Int { return v.MinSelfDelegation }
|
||||
func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares }
|
||||
func (v Validator) GetDelegatorShareExRate() sdk.Dec { return v.DelegatorShareExRate() }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user