Merge PR #3546: Min Self Delegation

This commit is contained in:
Sunny Aggarwal 2019-02-08 12:44:19 -08:00 committed by Jack Zampolin
parent ba63eb1801
commit cff985ffc5
31 changed files with 337 additions and 125 deletions

View File

@ -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

View File

@ -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,

View File

@ -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, "")
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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
}

View File

@ -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{

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)})

View File

@ -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")
}

View File

@ -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()

View File

@ -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())

View File

@ -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

View File

@ -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(),
)
}

View File

@ -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)

View File

@ -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")

View File

@ -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,
)
}

View File

@ -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(

View File

@ -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)

View File

@ -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)

View File

@ -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) {

View File

@ -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() {

View File

@ -164,6 +164,10 @@ var (
ErrBothShareMsgsGiven = types.ErrBothShareMsgsGiven
ErrNeitherShareMsgsGiven = types.ErrNeitherShareMsgsGiven
ErrMissingSignature = types.ErrMissingSignature
ErrMinSelfDelegationInvalid = types.ErrMinSelfDelegationInvalid
ErrMinSelfDelegationDecreased = types.ErrMinSelfDelegationDecreased
ErrSelfDelegationBelowMinimum = types.ErrSelfDelegationBelowMinimum
)
var (

View File

@ -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())
}

View File

@ -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")
}

View File

@ -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")

View File

@ -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")
}

View File

@ -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() }