pull a bunch from develop

This commit is contained in:
rigelrozanski 2018-09-25 17:43:26 -04:00
parent 0867ce8656
commit 380d3e1c5a
59 changed files with 917 additions and 655 deletions

View File

@ -24,13 +24,13 @@ func (v Validator) GetOperator() sdk.ValAddress {
}
// Implements sdk.Validator
func (v Validator) GetPubKey() crypto.PubKey {
func (v Validator) GetConsPubKey() crypto.PubKey {
return nil
}
// Implements sdk.Validator
func (v Validator) GetConsAddr() sdk.ConsAddress {
return sdk.ConsAddress{}
return nil
}
// Implements sdk.Validator
@ -48,11 +48,6 @@ func (v Validator) GetDelegatorShares() sdk.Dec {
return sdk.ZeroDec()
}
// Implements sdk.Validator
func (v Validator) GetCommission() sdk.Dec {
return sdk.ZeroDec()
}
// Implements sdk.Validator
func (v Validator) GetJailed() bool {
return false
@ -98,12 +93,12 @@ func (vs *ValidatorSet) Validator(ctx sdk.Context, addr sdk.ValAddress) sdk.Vali
}
// ValidatorByPubKey implements sdk.ValidatorSet
func (vs *ValidatorSet) ValidatorByConsPubKey(ctx sdk.Context, _ crypto.PubKey) sdk.Validator {
func (vs *ValidatorSet) ValidatorByConsPubKey(_ sdk.Context, _ crypto.PubKey) sdk.Validator {
panic("not implemented")
}
// ValidatorByPubKey implements sdk.ValidatorSet
func (vs *ValidatorSet) ValidatorByConsAddr(ctx sdk.Context, _ sdk.ConsAddress) sdk.Validator {
func (vs *ValidatorSet) ValidatorByConsAddr(_ sdk.Context, _ sdk.ConsAddress) sdk.Validator {
panic("not implemented")
}
@ -137,7 +132,7 @@ func (vs *ValidatorSet) RemoveValidator(addr sdk.AccAddress) {
}
// Implements sdk.ValidatorSet
func (vs *ValidatorSet) Slash(ctx sdk.Context, _ sdk.ConsAddress, height int64, power int64, amt sdk.Dec) {
func (vs *ValidatorSet) Slash(_ sdk.Context, _ sdk.ConsAddress, _ int64, _ int64, _ sdk.Dec) {
panic("not implemented")
}
@ -152,6 +147,6 @@ func (vs *ValidatorSet) Unjail(_ sdk.Context, _ sdk.ConsAddress) {
}
// Implements sdk.ValidatorSet
func (vs *ValidatorSet) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) sdk.Delegation {
func (vs *ValidatorSet) Delegation(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) sdk.Delegation {
panic("not implemented")
}

View File

@ -292,6 +292,11 @@ func ConsAddressFromBech32(address string) (addr ConsAddress, err error) {
return ConsAddress(bz), nil
}
// get ConsAddress from pubkey
func GetConsAddress(pubkey crypto.PubKey) ConsAddress {
return ConsAddress(pubkey.Address())
}
// Returns boolean for whether two ConsAddress are Equal
func (ca ConsAddress) Equals(ca2 ConsAddress) bool {
if ca.Empty() && ca2.Empty() {

View File

@ -10,7 +10,7 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/cosmos/cosmos-sdk/types"
)
)
var invalidStrs = []string{
"",

View File

@ -174,13 +174,15 @@ func NewDecFromStr(str string) (d Dec, err Error) {
//______________________________________________________________________________________________
//nolint
func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // Is equal to zero
func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 }
func (d Dec) IsNil() bool { return d.Int == nil } // is decimal nil
func (d Dec) IsZero() bool { return (d.Int).Sign() == 0 } // is equal to zero
func (d Dec) Equal(d2 Dec) bool { return (d.Int).Cmp(d2.Int) == 0 } // equal decimals
func (d Dec) GT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) > 0 } // greater than
func (d Dec) GTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) >= 0 } // greater than or equal
func (d Dec) LT(d2 Dec) bool { return (d.Int).Cmp(d2.Int) < 0 } // less than
func (d Dec) LTE(d2 Dec) bool { return (d.Int).Cmp(d2.Int) <= 0 } // less than or equal
func (d Dec) Neg() Dec { return Dec{new(big.Int).Neg(d.Int)} } // reverse the decimal sign
func (d Dec) Abs() Dec { return Dec{new(big.Int).Abs(d.Int)} } // absolute value
// addition
func (d Dec) Add(d2 Dec) Dec {
@ -333,7 +335,7 @@ func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int {
return chopPrecisionAndTruncate(tmp)
}
// RoundInt64 rounds the decimal using bankers rounding
// TruncateInt64 truncates the decimals from the number and returns an int64
func (d Dec) TruncateInt64() int64 {
chopped := chopPrecisionAndTruncateNonMutative(d.Int)
if !chopped.IsInt64() {
@ -342,7 +344,7 @@ func (d Dec) TruncateInt64() int64 {
return chopped.Int64()
}
// RoundInt round the decimal using bankers rounding
// TruncateInt truncates the decimals from the number and returns an Int
func (d Dec) TruncateInt() Int {
return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int))
}

View File

@ -73,7 +73,6 @@ func unknownCodeMsg(code CodeType) string {
}
// NOTE: Don't stringer this, we'll put better messages in later.
// nolint: gocyclo
func CodeToDefaultMsg(code CodeType) string {
switch code {
case CodeInternal:

View File

@ -37,24 +37,23 @@ func (b BondStatus) Equal(b2 BondStatus) bool {
// validator for a delegated proof of stake system
type Validator interface {
GetJailed() bool // whether the validator is jailed
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator
GetOperator() ValAddress // operator address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetConsAddr() ConsAddress // validation consensus address
GetPower() Dec // validation power
GetTokens() Dec // validation tokens
GetCommission() Dec // validator commission rate
GetDelegatorShares() Dec // Total delegator shares
GetBondHeight() int64 // height in which the validator became active
GetJailed() bool // whether the validator is jailed
GetMoniker() string // moniker of the validator
GetStatus() BondStatus // status of the validator
GetOperator() ValAddress // operator address to receive/return validators coins
GetConsPubKey() crypto.PubKey // validation consensus pubkey
GetConsAddr() ConsAddress // validation consensus address
GetPower() Dec // validation power
GetTokens() Dec // validation tokens
GetDelegatorShares() Dec // Total out standing delegator shares
GetBondHeight() int64 // height in which the validator became active
}
// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()),
Address: v.GetPubKey().Address(),
PubKey: tmtypes.TM2PB.PubKey(v.GetConsPubKey()),
Address: v.GetConsPubKey().Address(),
Power: v.GetPower().RoundInt64(),
}
}
@ -69,10 +68,9 @@ type ValidatorSet interface {
IterateValidatorsBonded(Context,
func(index int64, validator Validator) (stop bool))
Validator(Context, ValAddress) Validator // get a particular validator by operator
ValidatorByConsPubKey(Context, crypto.PubKey) Validator // get a particular validator by consensus address
ValidatorByConsAddr(Context, ConsAddress) Validator // get a particular validator by consensus address
TotalPower(Context) Dec // total power of the validator set
Validator(Context, ValAddress) Validator // get a particular validator by operator address
ValidatorByConsAddr(Context, ConsAddress) Validator // get a particular validator by consensus address
TotalPower(Context) Dec // total power of the validator set
// slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction
Slash(Context, ConsAddress, int64, int64, Dec)
@ -89,8 +87,8 @@ type ValidatorSet interface {
// delegation bond for a delegated proof of stake system
type Delegation interface {
GetDelegator() AccAddress // delegator AccAddress for the bond
GetValidator() ValAddress // validator operator address TODO change to GetValAddr
GetShares() Dec // amount of validator's shares
GetValidator() ValAddress // validator operator address
GetShares() Dec // amount of validator's shares held in this delegation
}
// properties for the set of all delegations for a particular
@ -103,25 +101,12 @@ type DelegationSet interface {
fn func(index int64, delegation Delegation) (stop bool))
}
//_______________________________________________________________________________
// Event Hooks
// These can be utilized to communicate between a staking keeper and another
// keeper which must take particular actions when validators/delegators change
// state. The second keeper must implement this interface, which then the
// staking keeper can call.
// TODO refactor event hooks out to the receiver modules
// event hooks for staking validator object
type StakingHooks interface {
OnValidatorCreated(ctx Context, address ValAddress) // Must be called when a validator is created
OnValidatorCommissionChange(ctx Context, address ValAddress) // Must be called when a validator's commission is modified
OnValidatorRemoved(ctx Context, address ValAddress) // Must be called when a validator is deleted
// validator event hooks
// These can be utilized to communicate between a staking keeper
// and another keeper which must take particular actions when
// validators are bonded and unbonded. The second keeper must implement
// this interface, which then the staking keeper can call.
type ValidatorHooks interface {
OnValidatorBonded(ctx Context, address ConsAddress) // Must be called when a validator is bonded
OnValidatorBeginUnbonding(ctx Context, address ConsAddress) // Must be called when a validator begins unbonding
OnDelegationCreated(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is created
OnDelegationSharesModified(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation's shares are modified
OnDelegationRemoved(ctx Context, delAddr AccAddress, valAddr ValAddress) // Must be called when a delegation is removed
}

View File

@ -285,7 +285,6 @@ func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command {
return cmd
}
// nolint: gocyclo
// GetCmdQueryProposals implements a query proposals command.
func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{

View File

@ -334,7 +334,6 @@ func queryVoteHandlerFn(cdc *codec.Codec) http.HandlerFunc {
}
}
// nolint: gocyclo
// todo: Split this functionality into helper functions to remove the above
func queryVotesOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@ -373,7 +372,6 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc {
}
}
// nolint: gocyclo
// todo: Split this functionality into helper functions to remove the above
func queryProposalsWithParameterFn(cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@ -439,7 +437,6 @@ func queryProposalsWithParameterFn(cdc *codec.Codec) http.HandlerFunc {
}
}
// nolint: gocyclo
// todo: Split this functionality into helper functions to remove the above
func queryTallyOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

View File

@ -2,11 +2,11 @@ package gov
import (
"testing"
"time"
"github.com/stretchr/testify/require"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/tendermint/abci/types"
)
@ -28,12 +28,18 @@ func TestTickExpiredDepositPeriod(t *testing.T) {
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
ctx = ctx.WithBlockHeight(10)
newHeader := ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
EndBlocker(ctx, keeper)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
ctx = ctx.WithBlockHeight(250)
newHeader = ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod)
ctx = ctx.WithBlockHeader(newHeader)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.True(t, shouldPopInactiveProposalQueue(ctx, keeper))
EndBlocker(ctx, keeper)
@ -59,7 +65,10 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
ctx = ctx.WithBlockHeight(10)
newHeader := ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(2) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
EndBlocker(ctx, keeper)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
@ -68,14 +77,20 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) {
res = govHandler(ctx, newProposalMsg2)
require.True(t, res.IsOK())
ctx = ctx.WithBlockHeight(205)
newHeader = ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.True(t, shouldPopInactiveProposalQueue(ctx, keeper))
EndBlocker(ctx, keeper)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
ctx = ctx.WithBlockHeight(215)
newHeader = ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(5) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.True(t, shouldPopInactiveProposalQueue(ctx, keeper))
EndBlocker(ctx, keeper)
@ -105,7 +120,10 @@ func TestTickPassedDepositPeriod(t *testing.T) {
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
ctx = ctx.WithBlockHeight(10)
newHeader := ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
EndBlocker(ctx, keeper)
require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx))
require.False(t, shouldPopInactiveProposalQueue(ctx, keeper))
@ -146,14 +164,20 @@ func TestTickPassedVotingPeriod(t *testing.T) {
var proposalID int64
keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID)
ctx = ctx.WithBlockHeight(10)
newHeader := ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second)
ctx = ctx.WithBlockHeader(newHeader)
newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)})
res = govHandler(ctx, newDepositMsg)
require.True(t, res.IsOK())
EndBlocker(ctx, keeper)
ctx = ctx.WithBlockHeight(215)
newHeader = ctx.BlockHeader()
newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod)
ctx = ctx.WithBlockHeader(newHeader)
require.True(t, shouldPopActiveProposalQueue(ctx, keeper))
depositsIterator := keeper.GetDeposits(ctx, proposalID)
require.True(t, depositsIterator.Valid())
@ -169,56 +193,3 @@ func TestTickPassedVotingPeriod(t *testing.T) {
require.Equal(t, StatusRejected, keeper.GetProposal(ctx, proposalID).GetStatus())
require.True(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult()))
}
func TestSlashing(t *testing.T) {
mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10)
SortAddresses(addrs)
mapp.BeginBlock(abci.RequestBeginBlock{})
ctx := mapp.BaseApp.NewContext(false, abci.Header{})
govHandler := NewHandler(keeper)
stakeHandler := stake.NewHandler(sk)
valAddrs := make([]sdk.ValAddress, len(addrs[:3]))
for i, addr := range addrs[:3] {
valAddrs[i] = sdk.ValAddress(addr)
}
createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7})
initTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx)
val0Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(initTotalPower)
val1Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(initTotalPower)
val2Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(initTotalPower)
newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 15)})
res := govHandler(ctx, newProposalMsg)
require.True(t, res.IsOK())
var proposalID int64
keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID)
ctx = ctx.WithBlockHeight(10)
require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus())
newVoteMsg := NewMsgVote(addrs[0], proposalID, OptionYes)
res = govHandler(ctx, newVoteMsg)
require.True(t, res.IsOK())
EndBlocker(ctx, keeper)
ctx = ctx.WithBlockHeight(215)
require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus())
EndBlocker(ctx, keeper)
require.False(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult()))
endTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx)
val0End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(endTotalPower)
val1End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(endTotalPower)
val2End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(endTotalPower)
require.True(t, val0End.GTE(val0Initial))
require.True(t, val1End.LT(val1Initial))
require.True(t, val2End.LT(val2Initial))
}

View File

@ -1,6 +1,8 @@
package gov
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -27,10 +29,10 @@ func DefaultGenesisState() GenesisState {
StartingProposalID: 1,
DepositProcedure: DepositProcedure{
MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", 10)},
MaxDepositPeriod: 200,
MaxDepositPeriod: time.Duration(172800) * time.Second,
},
VotingProcedure: VotingProcedure{
VotingPeriod: 200,
VotingPeriod: time.Duration(172800) * time.Second,
},
TallyingProcedure: TallyingProcedure{
Threshold: sdk.NewDecWithPrec(5, 1),

View File

@ -128,13 +128,13 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
for shouldPopActiveProposalQueue(ctx, keeper) {
activeProposal := keeper.ActiveProposalQueuePop(ctx)
proposalStartBlock := activeProposal.GetVotingStartBlock()
proposalStartTime := activeProposal.GetVotingStartTime()
votingPeriod := keeper.GetVotingProcedure(ctx).VotingPeriod
if ctx.BlockHeight() < proposalStartBlock+votingPeriod {
if ctx.BlockHeader().Time.Before(proposalStartTime.Add(votingPeriod)) {
continue
}
passes, tallyResults, nonVotingVals := tally(ctx, keeper, activeProposal)
passes, tallyResults := tally(ctx, keeper, activeProposal)
proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(activeProposal.GetProposalID())
var action []byte
if passes {
@ -152,18 +152,6 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v",
activeProposal.GetProposalID(), activeProposal.GetTitle(), passes))
for _, valAddr := range nonVotingVals {
val := keeper.ds.GetValidatorSet().Validator(ctx, valAddr)
keeper.ds.GetValidatorSet().Slash(ctx,
val.GetConsAddr(),
ctx.BlockHeight(),
val.GetPower().RoundInt64(),
keeper.GetTallyingProcedure(ctx).GovernancePenalty)
logger.Info(fmt.Sprintf("validator %s failed to vote on proposal %d; slashing",
val.GetOperator(), activeProposal.GetProposalID()))
}
resTags.AppendTag(tags.Action, action)
resTags.AppendTag(tags.ProposalID, proposalIDBytes)
}
@ -178,7 +166,7 @@ func shouldPopInactiveProposalQueue(ctx sdk.Context, keeper Keeper) bool {
return false
} else if peekProposal.GetStatus() != StatusDepositPeriod {
return true
} else if ctx.BlockHeight() >= peekProposal.GetSubmitBlock()+depositProcedure.MaxDepositPeriod {
} else if !ctx.BlockHeader().Time.Before(peekProposal.GetSubmitTime().Add(depositProcedure.MaxDepositPeriod)) {
return true
}
return false
@ -190,7 +178,7 @@ func shouldPopActiveProposalQueue(ctx sdk.Context, keeper Keeper) bool {
if peekProposal == nil {
return false
} else if ctx.BlockHeight() >= peekProposal.GetVotingStartBlock()+votingProcedure.VotingPeriod {
} else if !ctx.BlockHeader().Time.Before(peekProposal.GetVotingStartTime().Add(votingProcedure.VotingPeriod)) {
return true
}
return false

View File

@ -38,7 +38,11 @@ type Keeper struct {
codespace sdk.CodespaceType
}
// NewGovernanceMapper returns a mapper that uses go-codec to (binary) encode and decode gov types.
// NewKeeper returns a governance keeper. It handles:
// - submitting governance proposals
// - depositing funds into proposals, and activating upon sufficient funds being deposited
// - users voting on proposals, with weight proportional to stake in the system
// - and tallying the result of the vote.
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ps params.Setter, ck bank.Keeper, ds sdk.DelegationSet, codespace sdk.CodespaceType) Keeper {
return Keeper{
storeKey: key,
@ -51,11 +55,6 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ps params.Setter, ck bank.Kee
}
}
// Returns the go-codec codec.
func (keeper Keeper) WireCodec() *codec.Codec {
return keeper.cdc
}
// =====================================================
// Proposals
@ -66,15 +65,14 @@ func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description
return nil
}
var proposal Proposal = &TextProposal{
ProposalID: proposalID,
Title: title,
Description: description,
ProposalType: proposalType,
Status: StatusDepositPeriod,
TallyResult: EmptyTallyResult(),
TotalDeposit: sdk.Coins{},
SubmitBlock: ctx.BlockHeight(),
VotingStartBlock: -1, // TODO: Make Time
ProposalID: proposalID,
Title: title,
Description: description,
ProposalType: proposalType,
Status: StatusDepositPeriod,
TallyResult: EmptyTallyResult(),
TotalDeposit: sdk.Coins{},
SubmitTime: ctx.BlockHeader().Time,
}
keeper.SetProposal(ctx, proposal)
keeper.InactiveProposalQueuePush(ctx, proposal)
@ -108,7 +106,6 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) {
store.Delete(KeyProposal(proposal.GetProposalID()))
}
// nolint: gocyclo
// Get Proposal from store by ProposalID
func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal {
@ -200,7 +197,7 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e
}
func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) {
proposal.SetVotingStartBlock(ctx.BlockHeight())
proposal.SetVotingStartTime(ctx.BlockHeader().Time)
proposal.SetStatus(StatusVotingPeriod)
keeper.SetProposal(ctx, proposal)
keeper.ActiveProposalQueuePush(ctx, proposal)

View File

@ -2,6 +2,7 @@ package gov
import (
"testing"
"time"
"github.com/stretchr/testify/require"
@ -45,12 +46,12 @@ func TestActivateVotingPeriod(t *testing.T) {
proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText)
require.Equal(t, int64(-1), proposal.GetVotingStartBlock())
require.True(t, proposal.GetVotingStartTime().Equal(time.Time{}))
require.Nil(t, keeper.ActiveProposalQueuePeek(ctx))
keeper.activateVotingPeriod(ctx, proposal)
require.Equal(t, proposal.GetVotingStartBlock(), ctx.BlockHeight())
require.True(t, proposal.GetVotingStartTime().Equal(ctx.BlockHeader().Time))
require.Equal(t, proposal.GetProposalID(), keeper.ActiveProposalQueuePeek(ctx).GetProposalID())
}
@ -77,7 +78,7 @@ func TestDeposits(t *testing.T) {
// Check no deposits at beginning
deposit, found := keeper.GetDeposit(ctx, proposalID, addrs[1])
require.False(t, found)
require.Equal(t, keeper.GetProposal(ctx, proposalID).GetVotingStartBlock(), int64(-1))
require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(time.Time{}))
require.Nil(t, keeper.ActiveProposalQueuePeek(ctx))
// Check first deposit
@ -114,7 +115,7 @@ func TestDeposits(t *testing.T) {
require.Equal(t, addr1Initial.Minus(fourSteak), keeper.ck.GetCoins(ctx, addrs[1]))
// Check that proposal moved to voting period
require.Equal(t, ctx.BlockHeight(), keeper.GetProposal(ctx, proposalID).GetVotingStartBlock())
require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(ctx.BlockHeader().Time))
require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx))
require.Equal(t, proposalID, keeper.ActiveProposalQueuePeek(ctx).GetProposalID())

View File

@ -1,13 +1,15 @@
package gov
import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Procedure around Deposits for governance
type DepositProcedure struct {
MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period.
MaxDepositPeriod int64 `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period.
MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months
}
// Procedure around Tallying votes in governance
@ -19,5 +21,5 @@ type TallyingProcedure struct {
// Procedure around Voting in governance
type VotingProcedure struct {
VotingPeriod int64 `json:"voting_period"` // Length of the voting period.
VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period.
}

View File

@ -3,6 +3,7 @@ package gov
import (
"encoding/json"
"fmt"
"time"
"github.com/pkg/errors"
@ -30,14 +31,14 @@ type Proposal interface {
GetTallyResult() TallyResult
SetTallyResult(TallyResult)
GetSubmitBlock() int64
SetSubmitBlock(int64)
GetSubmitTime() time.Time
SetSubmitTime(time.Time)
GetTotalDeposit() sdk.Coins
SetTotalDeposit(sdk.Coins)
GetVotingStartBlock() int64
SetVotingStartBlock(int64)
GetVotingStartTime() time.Time
SetVotingStartTime(time.Time)
}
// checks if two proposals are equal
@ -48,9 +49,9 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool {
proposalA.GetProposalType() == proposalB.GetProposalType() &&
proposalA.GetStatus() == proposalB.GetStatus() &&
proposalA.GetTallyResult().Equals(proposalB.GetTallyResult()) &&
proposalA.GetSubmitBlock() == proposalB.GetSubmitBlock() &&
proposalA.GetSubmitTime().Equal(proposalB.GetSubmitTime()) &&
proposalA.GetTotalDeposit().IsEqual(proposalB.GetTotalDeposit()) &&
proposalA.GetVotingStartBlock() == proposalB.GetVotingStartBlock() {
proposalA.GetVotingStartTime().Equal(proposalB.GetVotingStartTime()) {
return true
}
return false
@ -67,10 +68,10 @@ type TextProposal struct {
Status ProposalStatus `json:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected}
TallyResult TallyResult `json:"tally_result"` // Result of Tallys
SubmitBlock int64 `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included
SubmitTime time.Time `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included
TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit
VotingStartBlock int64 `json:"voting_start_block"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
VotingStartTime time.Time `json:"voting_start_block"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached
}
// Implements Proposal Interface
@ -89,13 +90,13 @@ func (tp TextProposal) GetStatus() ProposalStatus { return tp.S
func (tp *TextProposal) SetStatus(status ProposalStatus) { tp.Status = status }
func (tp TextProposal) GetTallyResult() TallyResult { return tp.TallyResult }
func (tp *TextProposal) SetTallyResult(tallyResult TallyResult) { tp.TallyResult = tallyResult }
func (tp TextProposal) GetSubmitBlock() int64 { return tp.SubmitBlock }
func (tp *TextProposal) SetSubmitBlock(submitBlock int64) { tp.SubmitBlock = submitBlock }
func (tp TextProposal) GetSubmitTime() time.Time { return tp.SubmitTime }
func (tp *TextProposal) SetSubmitTime(submitTime time.Time) { tp.SubmitTime = submitTime }
func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit }
func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit }
func (tp TextProposal) GetVotingStartBlock() int64 { return tp.VotingStartBlock }
func (tp *TextProposal) SetVotingStartBlock(votingStartBlock int64) {
tp.VotingStartBlock = votingStartBlock
func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime }
func (tp *TextProposal) SetVotingStartTime(votingStartTime time.Time) {
tp.VotingStartTime = votingStartTime
}
//-----------------------------------------------------------
@ -191,8 +192,9 @@ func (pt ProposalKind) String() string {
func (pt ProposalKind) Format(s fmt.State, verb rune) {
switch verb {
case 's':
s.Write([]byte(fmt.Sprintf("%s", pt.String())))
s.Write([]byte(pt.String()))
default:
// TODO: Do this conversion more directly
s.Write([]byte(fmt.Sprintf("%v", byte(pt))))
}
}
@ -294,8 +296,9 @@ func (status ProposalStatus) String() string {
func (status ProposalStatus) Format(s fmt.State, verb rune) {
switch verb {
case 's':
s.Write([]byte(fmt.Sprintf("%s", status.String())))
s.Write([]byte(status.String()))
default:
// TODO: Do this conversion more directly
s.Write([]byte(fmt.Sprintf("%v", byte(status))))
}
}
@ -321,11 +324,8 @@ func EmptyTallyResult() TallyResult {
// checks if two proposals are equal
func (resultA TallyResult) Equals(resultB TallyResult) bool {
if resultA.Yes.Equal(resultB.Yes) &&
return (resultA.Yes.Equal(resultB.Yes) &&
resultA.Abstain.Equal(resultB.Abstain) &&
resultA.No.Equal(resultB.No) &&
resultA.NoWithVeto.Equal(resultB.NoWithVeto) {
return true
}
return false
resultA.NoWithVeto.Equal(resultB.NoWithVeto))
}

40
x/gov/proposals_test.go Normal file
View File

@ -0,0 +1,40 @@
package gov
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
)
func TestProposalKind_Format(t *testing.T) {
typeText, _ := ProposalTypeFromString("Text")
tests := []struct {
pt ProposalKind
sprintFArgs string
expectedStringOutput string
}{
{typeText, "%s", "Text"},
{typeText, "%v", "1"},
}
for _, tt := range tests {
got := fmt.Sprintf(tt.sprintFArgs, tt.pt)
require.Equal(t, tt.expectedStringOutput, got)
}
}
func TestProposalStatus_Format(t *testing.T) {
statusDepositPeriod, _ := ProposalStatusFromString("DepositPeriod")
tests := []struct {
pt ProposalStatus
sprintFArgs string
expectedStringOutput string
}{
{statusDepositPeriod, "%s", "DepositPeriod"},
{statusDepositPeriod, "%v", "1"},
}
for _, tt := range tests {
got := fmt.Sprintf(tt.sprintFArgs, tt.pt)
require.Equal(t, tt.expectedStringOutput, got)
}
}

View File

@ -205,12 +205,12 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
var proposalID int64
err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID)
if err2 != nil {
return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
return res, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error()))
}
proposal := keeper.GetProposal(ctx, proposalID)
if proposal == nil {
return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID)
return res, ErrUnknownProposal(DefaultCodespace, proposalID)
}
var tallyResult TallyResult
@ -220,7 +220,7 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke
} else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected {
tallyResult = proposal.GetTallyResult()
} else {
_, tallyResult, _ = tally(ctx, keeper, proposal)
_, tallyResult = tally(ctx, keeper, proposal)
}
bz, err2 := codec.MarshalJSONIndent(keeper.cdc, tallyResult)

View File

@ -4,8 +4,7 @@ import (
"fmt"
"math"
"math/rand"
"github.com/tendermint/tendermint/crypto"
"time"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -44,9 +43,9 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
})
statePercentageArray := []float64{1, .9, .75, .4, .15, 0}
curNumVotesState := 1
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
// 1) submit proposal now
sender := simulation.RandomKey(r, keys)
sender := simulation.RandomAcc(r, accs)
msg, err := simulationCreateMsgSubmitProposal(r, sender)
if err != nil {
return "", nil, err
@ -60,16 +59,16 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
// 2) Schedule operations for votes
// 2.1) first pick a number of people to vote.
curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState)
numVotes := int(math.Ceil(float64(len(keys)) * statePercentageArray[curNumVotesState]))
numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState]))
// 2.2) select who votes and when
whoVotes := r.Perm(len(keys))
whoVotes := r.Perm(len(accs))
// didntVote := whoVotes[numVotes:]
whoVotes = whoVotes[:numVotes]
votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod
fops := make([]simulation.FutureOperation, numVotes+1)
for i := 0; i < numVotes; i++ {
whenVote := ctx.BlockHeight() + r.Int63n(votingPeriod)
fops[i] = simulation.FutureOperation{BlockHeight: int(whenVote), Op: operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)}
whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second)
fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, accs[whoVotes[i]], proposalID)}
}
// 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant)
// TODO: Find a way to check if a validator was slashed other than just checking their balance a block
@ -83,8 +82,8 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe
// Note: Currently doesn't ensure that the proposal txt is in JSON form
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
handler := gov.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
sender := simulation.RandomKey(r, keys)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) {
sender := simulation.RandomAcc(r, accs)
msg, err := simulationCreateMsgSubmitProposal(r, sender)
if err != nil {
return "", nil, err
@ -110,14 +109,13 @@ func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper,
return
}
func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg gov.MsgSubmitProposal, err error) {
addr := sdk.AccAddress(sender.PubKey().Address())
func simulationCreateMsgSubmitProposal(r *rand.Rand, sender simulation.Account) (msg gov.MsgSubmitProposal, err error) {
deposit := randomDeposit(r)
msg = gov.NewMsgSubmitProposal(
simulation.RandStringOfLength(r, 5),
simulation.RandStringOfLength(r, 5),
gov.ProposalTypeText,
addr,
sender.Address,
deposit,
)
if msg.ValidateBasic() != nil {
@ -128,15 +126,14 @@ func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg
// SimulateMsgDeposit
func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
key := simulation.RandomKey(r, keys)
addr := sdk.AccAddress(key.PubKey().Address())
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
acc := simulation.RandomAcc(r, accs)
proposalID, ok := randomProposalID(r, k, ctx)
if !ok {
return "no-operation", nil, nil
}
deposit := randomDeposit(r)
msg := gov.NewMsgDeposit(addr, proposalID, deposit)
msg := gov.NewMsgDeposit(acc.Address, proposalID, deposit)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
@ -158,14 +155,14 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
// SimulateMsgVote
// nolint: unparam
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
return operationSimulateMsgVote(k, sk, nil, -1)
return operationSimulateMsgVote(k, sk, simulation.Account{}, -1)
}
// nolint: unparam
func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
if key == nil {
key = simulation.RandomKey(r, keys)
func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, acc simulation.Account, proposalID int64) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
if acc.Equals(simulation.Account{}) {
acc = simulation.RandomAcc(r, accs)
}
var ok bool
@ -176,10 +173,9 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey,
return "no-operation", nil, nil
}
}
addr := sdk.AccAddress(key.PubKey().Address())
option := randomVotingOption(r)
msg := gov.NewMsgVote(addr, proposalID, option)
msg := gov.NewMsgVote(acc.Address, proposalID, option)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}

View File

@ -6,7 +6,6 @@ import (
"testing"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -43,12 +42,12 @@ func TestGovWithRandomMessages(t *testing.T) {
panic(err)
}
appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
mock.RandomSetGenesis(r, mapp, accs, []string{"stake"})
appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage {
simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"})
return json.RawMessage("{}")
}
setup := func(r *rand.Rand, privKeys []crypto.PrivKey) {
setup := func(r *rand.Rand, accs []simulation.Account) {
ctx := mapp.NewContext(false, abci.Header{})
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())

View File

@ -13,7 +13,7 @@ type validatorGovInfo struct {
Vote VoteOption // Vote of the validator
}
func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult, nonVoting []sdk.ValAddress) {
func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult) {
results := make(map[VoteOption]sdk.Dec)
results[OptionYes] = sdk.ZeroDec()
results[OptionAbstain] = sdk.ZeroDec()
@ -70,12 +70,9 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall
keeper.deleteVote(ctx, vote.ProposalID, vote.Voter)
}
// iterate over the validators again to tally their voting power and see
// who didn't vote
nonVoting = []sdk.ValAddress{}
// iterate over the validators again to tally their voting power
for _, val := range currValidators {
if val.Vote == OptionEmpty {
nonVoting = append(nonVoting, val.Address)
continue
}
@ -98,19 +95,17 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall
// If no one votes, proposal fails
if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) {
return false, tallyResults, nonVoting
return false, tallyResults
}
// If more than 1/3 of voters veto, proposal fails
if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyingProcedure.Veto) {
return false, tallyResults, nonVoting
return false, tallyResults
}
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyingProcedure.Threshold) {
return true, tallyResults, nonVoting
return true, tallyResults
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
SortValAddresses(nonVoting)
return false, tallyResults, nonVoting
return false, tallyResults
}

View File

@ -15,13 +15,19 @@ import (
var (
pubkeys = []crypto.PubKey{ed25519.GenPrivKey().PubKey(), ed25519.GenPrivKey().PubKey(), ed25519.GenPrivKey().PubKey()}
testDescription = stake.NewDescription("T", "E", "S", "T")
testCommissionMsg = stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
func createValidators(t *testing.T, stakeHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, coinAmt []int64) {
require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.")
dummyDescription := stake.NewDescription("T", "E", "S", "T")
for i := 0; i < len(addrs); i++ {
valCreateMsg := stake.NewMsgCreateValidator(addrs[i], pubkeys[i], sdk.NewInt64Coin("steak", coinAmt[i]), dummyDescription)
valCreateMsg := stake.NewMsgCreateValidator(
addrs[i], pubkeys[i], sdk.NewInt64Coin("steak", coinAmt[i]), testDescription, testCommissionMsg,
)
res := stakeHandler(ctx, valCreateMsg)
require.True(t, res.IsOK())
}
@ -45,7 +51,7 @@ func TestTallyNoOneVotes(t *testing.T) {
proposal.SetStatus(StatusVotingPeriod)
keeper.SetProposal(ctx, proposal)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.True(t, tallyResults.Equals(EmptyTallyResult()))
@ -74,7 +80,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[1], OptionYes)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.True(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -103,7 +109,7 @@ func TestTallyOnlyValidators51No(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[1], OptionNo)
require.Nil(t, err)
passes, _, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
}
@ -133,7 +139,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.True(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -164,7 +170,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNoWithVeto)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -195,7 +201,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.True(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -226,7 +232,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -255,11 +261,9 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo)
require.Nil(t, err)
passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.Equal(t, 1, len(nonVoting))
require.Equal(t, sdk.ValAddress(addrs[0]), nonVoting[0])
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
@ -293,7 +297,7 @@ func TestTallyDelgatorOverride(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -327,10 +331,9 @@ func TestTallyDelgatorInherit(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes)
require.Nil(t, err)
passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.True(t, passes)
require.Equal(t, 0, len(nonVoting))
require.False(t, tallyResults.Equals(EmptyTallyResult()))
}
@ -366,7 +369,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -378,20 +381,18 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
ctx := mapp.BaseApp.NewContext(false, abci.Header{})
stakeHandler := stake.NewHandler(sk)
dummyDescription := stake.NewDescription("T", "E", "S", "T")
val1CreateMsg := stake.NewMsgCreateValidator(
sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 25), dummyDescription,
sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 25), testDescription, testCommissionMsg,
)
stakeHandler(ctx, val1CreateMsg)
val2CreateMsg := stake.NewMsgCreateValidator(
sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 6), dummyDescription,
sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 6), testDescription, testCommissionMsg,
)
stakeHandler(ctx, val2CreateMsg)
val3CreateMsg := stake.NewMsgCreateValidator(
sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 7), dummyDescription,
sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin("steak", 7), testDescription, testCommissionMsg,
)
stakeHandler(ctx, val3CreateMsg)
@ -413,7 +414,7 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.False(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))
@ -454,7 +455,7 @@ func TestTallyJailedValidator(t *testing.T) {
err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo)
require.Nil(t, err)
passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID))
require.True(t, passes)
require.False(t, tallyResults.Equals(EmptyTallyResult()))

View File

@ -99,9 +99,12 @@ func TestSlashingMsgs(t *testing.T) {
}
accs := []auth.Account{acc1}
mock.SetGenesis(mapp, accs)
description := stake.NewDescription("foo_moniker", "", "", "")
commission := stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
createValidatorMsg := stake.NewMsgCreateValidator(
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission,
)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)})

View File

@ -38,7 +38,6 @@ type UnjailBody struct {
ValidatorAddr string `json:"validator_addr"`
}
// nolint: gocyclo
func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var m UnjailBody

View File

@ -8,7 +8,7 @@ import (
// InitGenesis initializes the keeper's address to pubkey map.
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) {
for _, validator := range data.Validators {
keeper.addPubkey(ctx, validator.GetPubKey())
keeper.addPubkey(ctx, validator.GetConsPubKey())
}
return
}

View File

@ -34,7 +34,7 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result {
return ErrValidatorNotJailed(k.codespace).Result()
}
consAddr := sdk.ConsAddress(validator.GetPubKey().Address())
consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address())
info, found := k.getValidatorSigningInfo(ctx, consAddr)
if !found {

View File

@ -29,10 +29,10 @@ type Hooks struct {
k Keeper
}
var _ sdk.StakingHooks = Hooks{}
var _ sdk.ValidatorHooks = Hooks{}
// Return the wrapper struct
func (k Keeper) Hooks() Hooks {
func (k Keeper) ValidatorHooks() Hooks {
return Hooks{k}
}
@ -45,11 +45,3 @@ func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) {
func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) {
h.k.onValidatorBeginUnbonding(ctx, address)
}
// nolint - unused hooks
func (h Hooks) OnValidatorCreated(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorCommissionChange(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnValidatorRemoved(_ sdk.Context, _ sdk.ValAddress) {}
func (h Hooks) OnDelegationCreated(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {}
func (h Hooks) OnDelegationSharesModified(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {}
func (h Hooks) OnDelegationRemoved(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) {}

View File

@ -79,7 +79,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio
// handle a validator signature, must be called once per validator per block
// TODO refactor to take in a consensus address, additionally should maybe just take in the pubkey too
// nolint gocyclo
func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) {
logger := ctx.Logger().With("module", "x/slashing")
height := ctx.BlockHeight()

View File

@ -27,7 +27,7 @@ func TestHandleDoubleSign(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t)
sk = sk.WithHooks(keeper.Hooks())
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
@ -69,7 +69,7 @@ func TestSlashingPeriodCap(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t)
sk = sk.WithHooks(keeper.Hooks())
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
amtInt := int64(100)
addr, amt := addrs[0], sdk.NewInt(amtInt)
valConsPubKey, valConsAddr := pks[0], sdk.ConsAddress(pks[0].Address())
@ -125,7 +125,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// initial setup
ctx, ck, sk, _, keeper := createTestInput(t)
sk = sk.WithHooks(keeper.Hooks())
sk = sk.WithValidatorHooks(keeper.ValidatorHooks())
amtInt := int64(100)
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
sh := stake.NewHandler(sk)
@ -167,7 +167,7 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter)
// validator should be bonded still
validator, _ := sk.GetValidatorByConsPubKey(ctx, val)
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.Equal(t, amtInt, pool.BondedTokens.RoundInt64())
@ -181,7 +181,7 @@ func TestHandleAbsentValidator(t *testing.T) {
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
// validator should have been jailed
validator, _ = sk.GetValidatorByConsPubKey(ctx, val)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
// unrevocation should fail prior to jail expiration
@ -194,7 +194,7 @@ func TestHandleAbsentValidator(t *testing.T) {
require.True(t, got.IsOK())
// validator should be rebonded now
validator, _ = sk.GetValidatorByConsPubKey(ctx, val)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// validator should have been slashed
@ -212,7 +212,7 @@ func TestHandleAbsentValidator(t *testing.T) {
height++
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
validator, _ = sk.GetValidatorByConsPubKey(ctx, val)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
// 500 signed blocks
@ -228,7 +228,7 @@ func TestHandleAbsentValidator(t *testing.T) {
ctx = ctx.WithBlockHeight(height)
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
}
validator, _ = sk.GetValidatorByConsPubKey(ctx, val)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}
@ -263,7 +263,7 @@ func TestHandleNewValidator(t *testing.T) {
require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil)
// validator should be bonded still, should not have been jailed or slashed
validator, _ := sk.GetValidatorByConsPubKey(ctx, val)
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Bonded, validator.GetStatus())
pool := sk.GetPool(ctx)
require.Equal(t, int64(100), pool.BondedTokens.RoundInt64())
@ -297,7 +297,7 @@ func TestHandleAlreadyJailed(t *testing.T) {
}
// validator should have been jailed and slashed
validator, _ := sk.GetValidatorByConsPubKey(ctx, val)
validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, sdk.Unbonding, validator.GetStatus())
// validator should have been slashed
@ -308,7 +308,7 @@ func TestHandleAlreadyJailed(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
// validator should not have been slashed twice
validator, _ = sk.GetValidatorByConsPubKey(ctx, val)
validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val))
require.Equal(t, amtInt-1, validator.GetTokens().RoundInt64())
}

View File

@ -4,8 +4,6 @@ import (
"fmt"
"math/rand"
"github.com/tendermint/tendermint/crypto"
"github.com/cosmos/cosmos-sdk/baseapp"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
@ -14,9 +12,9 @@ import (
// SimulateMsgUnjail
func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
key := simulation.RandomKey(r, keys)
address := sdk.ValAddress(key.PubKey().Address())
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
acc := simulation.RandomAcc(r, accs)
address := sdk.ValAddress(acc.Address)
msg := slashing.NewMsgUnjail(address)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())

View File

@ -103,12 +103,14 @@ func testAddr(addr string) sdk.AccAddress {
}
func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) stake.MsgCreateValidator {
commission := stake.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
return stake.MsgCreateValidator{
Description: stake.Description{},
Commission: commission,
DelegatorAddr: sdk.AccAddress(address),
ValidatorAddr: address,
PubKey: pubKey,
Delegation: sdk.Coin{"steak", amt},
Delegation: sdk.NewCoin("steak", amt),
}
}
@ -116,6 +118,6 @@ func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmoun
return stake.MsgDelegate{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.Coin{"steak", delAmount},
Delegation: sdk.NewCoin("steak", delAmount),
}
}

View File

@ -78,7 +78,7 @@ func TestBeginBlocker(t *testing.T) {
}
// validator should be jailed
validator, found := sk.GetValidatorByConsPubKey(ctx, pk)
validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk))
require.True(t, found)
require.Equal(t, sdk.Unbonding, validator.GetStatus())
}

View File

@ -21,10 +21,12 @@ var (
priv4 = ed25519.GenPrivKey()
addr4 = sdk.AccAddress(priv4.PubKey().Address())
coins = sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(10))}
fee = auth.StdFee{
sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))},
fee = auth.NewStdFee(
100000,
}
sdk.Coins{sdk.NewCoin("foocoin", sdk.NewInt(0))}...,
)
commissionMsg = NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
)
// getMockApp returns an initialized mock application for this module.
@ -129,7 +131,7 @@ func TestStakeMsgs(t *testing.T) {
// create validator
description := NewDescription("foo_moniker", "", "", "")
createValidatorMsg := NewMsgCreateValidator(
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description,
sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionMsg,
)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []int64{0}, []int64{0}, true, true, priv1)
@ -143,7 +145,7 @@ func TestStakeMsgs(t *testing.T) {
// addr1 create validator on behalf of addr2
createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description,
addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg,
)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []int64{0, 1}, []int64{1, 0}, true, true, priv1, priv2)
@ -160,7 +162,7 @@ func TestStakeMsgs(t *testing.T) {
// edit the validator
description = NewDescription("bar_moniker", "", "", "")
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description)
editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil)
mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []int64{0}, []int64{2}, true, true, priv1)
validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true)

View File

@ -21,6 +21,10 @@ const (
FlagIdentity = "identity"
FlagWebsite = "website"
FlagDetails = "details"
FlagCommissionRate = "commission-rate"
FlagCommissionMaxRate = "commission-max-rate"
FlagCommissionMaxChangeRate = "commission-max-change-rate"
)
// common flagsets to add to various functions
@ -29,6 +33,8 @@ var (
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
fsDescriptionCreate = flag.NewFlagSet("", flag.ContinueOnError)
fsCommissionCreate = flag.NewFlagSet("", flag.ContinueOnError)
fsCommissionUpdate = flag.NewFlagSet("", flag.ContinueOnError)
fsDescriptionEdit = flag.NewFlagSet("", flag.ContinueOnError)
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
@ -44,6 +50,10 @@ func init() {
fsDescriptionCreate.String(FlagIdentity, "", "optional identity signature (ex. UPort or Keybase)")
fsDescriptionCreate.String(FlagWebsite, "", "optional website")
fsDescriptionCreate.String(FlagDetails, "", "optional details")
fsCommissionUpdate.String(FlagCommissionRate, "", "The new commission rate percentage")
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)")
fsDescriptionEdit.String(FlagMoniker, types.DoNotModifyDesc, "validator name")
fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "optional identity signature (ex. UPort or Keybase)")
fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "optional website")

View File

@ -12,9 +12,7 @@ import (
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
@ -66,6 +64,15 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
Details: viper.GetString(FlagDetails),
}
// get the initial validator commission parameters
rateStr := viper.GetString(FlagCommissionRate)
maxRateStr := viper.GetString(FlagCommissionMaxRate)
maxChangeRateStr := viper.GetString(FlagCommissionMaxChangeRate)
commissionMsg, err := buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr)
if err != nil {
return err
}
var msg sdk.Msg
if viper.GetString(FlagAddressDelegator) != "" {
delAddr, err := sdk.AccAddressFromBech32(viper.GetString(FlagAddressDelegator))
@ -73,13 +80,19 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
return err
}
msg = stake.NewMsgCreateValidatorOnBehalfOf(delAddr, sdk.ValAddress(valAddr), pk, amount, description)
msg = stake.NewMsgCreateValidatorOnBehalfOf(
delAddr, sdk.ValAddress(valAddr), pk, amount, description, commissionMsg,
)
} else {
msg = stake.NewMsgCreateValidator(sdk.ValAddress(valAddr), pk, amount, description)
msg = stake.NewMsgCreateValidator(
sdk.ValAddress(valAddr), pk, amount, description, commissionMsg,
)
}
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.SendTx(txBldr, cliCtx, []sdk.Msg{msg})
},
@ -88,6 +101,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command {
cmd.Flags().AddFlagSet(fsPk)
cmd.Flags().AddFlagSet(fsAmount)
cmd.Flags().AddFlagSet(fsDescriptionCreate)
cmd.Flags().AddFlagSet(fsCommissionCreate)
cmd.Flags().AddFlagSet(fsDelegator)
return cmd
@ -117,17 +131,31 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command {
Details: viper.GetString(FlagDetails),
}
msg := stake.NewMsgEditValidator(sdk.ValAddress(valAddr), description)
var newRate *sdk.Dec
commissionRate := viper.GetString(FlagCommissionRate)
if commissionRate != "" {
rate, err := sdk.NewDecFromStr(commissionRate)
if err != nil {
return fmt.Errorf("invalid new commission rate: %v", err)
}
newRate = &rate
}
msg := stake.NewMsgEditValidator(sdk.ValAddress(valAddr), description, newRate)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.SendTx(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().AddFlagSet(fsDescriptionEdit)
cmd.Flags().AddFlagSet(fsCommissionUpdate)
return cmd
}
@ -247,54 +275,6 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
return cmd
}
// nolint: gocyclo
// TODO: Make this pass gocyclo linting
func getShares(
storeName string, cdc *codec.Codec, sharesAmountStr,
sharesPercentStr string, delAddr sdk.AccAddress, valAddr sdk.ValAddress,
) (sharesAmount sdk.Dec, err error) {
switch {
case sharesAmountStr != "" && sharesPercentStr != "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr == "" && sharesPercentStr == "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr != "":
sharesAmount, err = sdk.NewDecFromStr(sharesAmountStr)
if err != nil {
return sharesAmount, err
}
if !sharesAmount.GT(sdk.ZeroDec()) {
return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
}
case sharesPercentStr != "":
var sharesPercent sdk.Dec
sharesPercent, err = sdk.NewDecFromStr(sharesPercentStr)
if err != nil {
return sharesAmount, err
}
if !sharesPercent.GT(sdk.ZeroDec()) || !sharesPercent.LTE(sdk.OneDec()) {
return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
}
// make a query to get the existing delegation shares
key := stake.GetDelegationKey(delAddr, valAddr)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
resQuery, err := cliCtx.QueryStore(key, storeName)
if err != nil {
return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err)
}
delegation, err := types.UnmarshalDelegation(cdc, key, resQuery)
if err != nil {
return sdk.ZeroDec(), err
}
sharesAmount = sharesPercent.Mul(delegation.Shares)
}
return
}
// GetCmdCompleteRedelegate implements the complete redelegation command.
func GetCmdCompleteRedelegate(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{

View File

@ -0,0 +1,88 @@
package cli
import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/stake"
"github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/pkg/errors"
)
func getShares(
storeName string, cdc *codec.Codec, sharesAmountStr,
sharesPercentStr string, delAddr sdk.AccAddress, valAddr sdk.ValAddress,
) (sharesAmount sdk.Dec, err error) {
switch {
case sharesAmountStr != "" && sharesPercentStr != "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr == "" && sharesPercentStr == "":
return sharesAmount, errors.Errorf("can either specify the amount OR the percent of the shares, not both")
case sharesAmountStr != "":
sharesAmount, err = sdk.NewDecFromStr(sharesAmountStr)
if err != nil {
return sharesAmount, err
}
if !sharesAmount.GT(sdk.ZeroDec()) {
return sharesAmount, errors.Errorf("shares amount must be positive number (ex. 123, 1.23456789)")
}
case sharesPercentStr != "":
var sharesPercent sdk.Dec
sharesPercent, err = sdk.NewDecFromStr(sharesPercentStr)
if err != nil {
return sharesAmount, err
}
if !sharesPercent.GT(sdk.ZeroDec()) || !sharesPercent.LTE(sdk.OneDec()) {
return sharesAmount, errors.Errorf("shares percent must be >0 and <=1 (ex. 0.01, 0.75, 1)")
}
// make a query to get the existing delegation shares
key := stake.GetDelegationKey(delAddr, valAddr)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
resQuery, err := cliCtx.QueryStore(key, storeName)
if err != nil {
return sharesAmount, errors.Errorf("cannot find delegation to determine percent Error: %v", err)
}
delegation, err := types.UnmarshalDelegation(cdc, key, resQuery)
if err != nil {
return sdk.ZeroDec(), err
}
sharesAmount = sharesPercent.Mul(delegation.Shares)
}
return
}
func buildCommissionMsg(rateStr, maxRateStr, maxChangeRateStr string) (commission types.CommissionMsg, err error) {
if rateStr == "" || maxRateStr == "" || maxChangeRateStr == "" {
return commission, errors.Errorf("must specify all validator commission parameters")
}
rate, err := sdk.NewDecFromStr(rateStr)
if err != nil {
return commission, err
}
maxRate, err := sdk.NewDecFromStr(maxRateStr)
if err != nil {
return commission, err
}
maxChangeRate, err := sdk.NewDecFromStr(maxChangeRateStr)
if err != nil {
return commission, err
}
commission = types.NewCommissionMsg(rate, maxRate, maxChangeRate)
return commission, nil
}

View File

@ -120,7 +120,6 @@ func delegatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle
}
}
// nolint gocyclo
// HTTP request handler to query all staking txs (msgs) from a delegator
func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {

View File

@ -69,7 +69,6 @@ type EditDelegationsBody struct {
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
}
// nolint: gocyclo
// TODO: Split this up into several smaller functions, and remove the above nolint
// TODO: use sdk.ValAddress instead of sdk.AccAddress for validators in messages
func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc {

View File

@ -3,11 +3,12 @@ package rest
import (
"fmt"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/stake/tags"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"github.com/cosmos/cosmos-sdk/client/context"
)
// contains checks if the a given query contains one of the tx types

View File

@ -75,7 +75,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState {
func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) {
keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) {
vals = append(vals, tmtypes.GenesisValidator{
PubKey: validator.GetPubKey(),
PubKey: validator.GetConsPubKey(),
Power: validator.GetPower().RoundInt64(),
Name: validator.GetMoniker(),
})

View File

@ -62,49 +62,55 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
// now we just perform action and save
func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k keeper.Keeper) sdk.Result {
// check to see if the pubkey or sender has been registered before
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
if found {
return ErrValidatorOwnerExists(k.Codespace()).Result()
}
_, found = k.GetValidatorByConsPubKey(ctx, msg.PubKey)
_, found = k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey))
if found {
return ErrValidatorPubKeyExists(k.Codespace()).Result()
}
if msg.Delegation.Denom != k.GetParams(ctx).BondDenom {
return ErrBadDenom(k.Codespace()).Result()
}
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
commission := NewCommissionWithTime(
msg.Commission.Rate, msg.Commission.MaxChangeRate,
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
)
validator, err := validator.SetInitialCommission(commission)
if err != nil {
return err.Result()
}
k.SetValidator(ctx, validator)
k.SetValidatorByConsAddr(ctx, validator)
// move coins from the msg.Address account to a (self-delegation) delegator account
// the validator account and global shares are updated within here
_, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
_, err = k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true)
if err != nil {
return err.Result()
}
// call the hook if present
k.OnValidatorCreated(ctx, validator.OperatorAddr)
accAddr := sdk.AccAddress(validator.OperatorAddr)
k.OnDelegationCreated(ctx, accAddr, validator.OperatorAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionCreateValidator,
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
tags.Moniker, []byte(msg.Description.Moniker),
tags.Identity, []byte(msg.Description.Identity),
)
return sdk.Result{
Tags: tags,
}
}
func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keeper.Keeper) sdk.Result {
// validator must already be registered
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
if !found {
@ -116,17 +122,26 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe
if err != nil {
return err.Result()
}
validator.Description = description
if msg.CommissionRate != nil {
if err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate); err != nil {
return err.Result()
}
}
// We don't need to run through all the power update logic within k.UpdateValidator
// We just need to override the entry in state, since only the description has changed.
k.SetValidator(ctx, validator)
tags := sdk.NewTags(
tags.Action, tags.ActionEditValidator,
tags.DstValidator, []byte(msg.ValidatorAddr.String()),
tags.Moniker, []byte(description.Moniker),
tags.Identity, []byte(description.Identity),
)
return sdk.Result{
Tags: tags,
}
@ -151,9 +166,6 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
return err.Result()
}
// call the hook if present
k.OnDelegationCreated(ctx, msg.DelegatorAddr, validator.OperatorAddr)
tags := sdk.NewTags(
tags.Action, tags.ActionDelegate,
tags.Delegator, []byte(msg.DelegatorAddr.String()),

View File

@ -17,7 +17,9 @@ import (
//______________________________________________________________________
func newTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator {
return types.NewMsgCreateValidator(address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{})
return types.NewMsgCreateValidator(
address, pubKey, sdk.NewCoin("steak", sdk.NewInt(amt)), Description{}, commissionMsg,
)
}
func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate {
@ -31,6 +33,7 @@ func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int6
func newTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress, valPubKey crypto.PubKey, amt int64) MsgCreateValidator {
return MsgCreateValidator{
Description: Description{},
Commission: commissionMsg,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
PubKey: valPubKey,

View File

@ -67,11 +67,6 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
// remove a delegation from store
func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) {
// call the hook if present
if k.hooks != nil {
k.hooks.OnDelegationRemoved(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
store := ctx.KVStore(k.storeKey)
store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr))
}
@ -281,11 +276,6 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
k.SetDelegation(ctx, delegation)
k.UpdateValidator(ctx, validator)
// call the hook if present
if k.hooks != nil {
k.hooks.OnDelegationSharesModified(ctx, delegation.DelegatorAddr, validator.OperatorAddr)
}
return
}
@ -344,11 +334,6 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
k.RemoveValidator(ctx, validator.OperatorAddr)
}
// call the hook if present
if k.hooks != nil {
k.hooks.OnDelegationSharesModified(ctx, delegation.DelegatorAddr, validator.OperatorAddr)
}
return amount, nil
}

View File

@ -14,7 +14,7 @@ type Keeper struct {
storeTKey sdk.StoreKey
cdc *codec.Codec
bankKeeper bank.Keeper
hooks sdk.StakingHooks
hooks sdk.ValidatorHooks
// codespace
codespace sdk.CodespaceType
@ -33,7 +33,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespa
}
// Set the validator hooks
func (k Keeper) WithHooks(sh sdk.StakingHooks) Keeper {
func (k Keeper) WithValidatorHooks(sh sdk.ValidatorHooks) Keeper {
if k.hooks != nil {
panic("cannot set validator hooks twice")
}

View File

@ -5,7 +5,6 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/tendermint/tendermint/crypto"
)
// Implements ValidatorSet
@ -67,15 +66,6 @@ func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) sdk.V
return val
}
// get the sdk.validator for a particular pubkey
func (k Keeper) ValidatorByConsPubKey(ctx sdk.Context, consPubKey crypto.PubKey) sdk.Validator {
val, found := k.GetValidatorByConsPubKey(ctx, consPubKey)
if !found {
return nil
}
return val
}
// total power from the bond
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Dec {
pool := k.GetPool(ctx)

View File

@ -21,8 +21,6 @@ import (
// CONTRACT:
// Infraction committed at the current height or at a past height,
// not at a height in the future
//
// nolint: gocyclo
func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec) {
logger := ctx.Logger().With("module", "x/stake")
@ -143,7 +141,7 @@ func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
// set the jailed flag on a validator
func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) {
validator, found := k.GetValidatorByConsAddr(ctx, sdk.ConsAddress(consAddr))
validator, found := k.GetValidatorByConsAddr(ctx, consAddr)
if !found {
panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed))
}

View File

@ -14,6 +14,7 @@ import (
// TODO integrate with test_common.go helper (CreateTestInput)
// setup helper function - creates two validators
func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
// setup
ctx, _, keeper := CreateTestInput(t, false, amt)
params := keeper.GetParams(ctx)
@ -472,7 +473,7 @@ func TestSlashBoth(t *testing.T) {
// slash validator
ctx = ctx.WithBlockHeight(12)
oldPool := keeper.GetPool(ctx)
validator, found := keeper.GetValidatorByConsPubKey(ctx, PKs[0])
validator, found := keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
require.True(t, found)
consAddr0 := sdk.ConsAddress(PKs[0].Address())
keeper.Slash(ctx, consAddr0, 10, 10, fraction)
@ -489,7 +490,7 @@ func TestSlashBoth(t *testing.T) {
// bonded tokens burned
require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
// read updated validator
validator, found = keeper.GetValidatorByConsPubKey(ctx, PKs[0])
validator, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
require.True(t, found)
// power not decreased, all stake was bonded since
require.Equal(t, sdk.NewDec(10), validator.GetPower())

View File

@ -6,7 +6,6 @@ import (
"fmt"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
@ -68,17 +67,6 @@ func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress
return k.GetValidator(ctx, opAddr)
}
// get a single validator by pubkey
func (k Keeper) GetValidatorByConsPubKey(ctx sdk.Context, consPubKey crypto.PubKey) (validator types.Validator, found bool) {
store := ctx.KVStore(k.storeKey)
consAddr := sdk.ConsAddress(consPubKey.Address())
opAddr := store.Get(GetValidatorByConsAddrKey(consAddr))
if opAddr == nil {
return validator, false
}
return k.GetValidator(ctx, opAddr)
}
// set the main record holding validator details
func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
@ -87,10 +75,9 @@ func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) {
}
// validator index
// TODO change to SetValidatorByConsAddr? used for retrieving from ConsPubkey as well- kinda confusing
func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) {
store := ctx.KVStore(k.storeKey)
consAddr := sdk.ConsAddress(validator.OperatorAddr.Bytes())
consAddr := sdk.ConsAddress(validator.ConsPubKey.Address())
store.Set(GetValidatorByConsAddrKey(consAddr), validator.OperatorAddr)
}
@ -245,7 +232,6 @@ func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Valid
// It may kick out validators if a new validator is entering the bonded validator
// group.
//
// nolint: gocyclo
// TODO: Remove above nolint, function needs to be simplified!
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
tstore := ctx.TransientStore(k.storeTKey)
@ -435,9 +421,6 @@ func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidato
// updated in store with the ValidatorsBondedIndexKey. This store is used to
// determine if a validator is a validator without needing to iterate over all
// validators.
//
// nolint: gocyclo
// TODO: Remove the above golint
func (k Keeper) UpdateBondedValidators(
ctx sdk.Context, affectedValidator types.Validator) (
updatedVal types.Validator, updated bool) {
@ -681,11 +664,6 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
// remove the validator record and associated indexes
func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
// call the hook if present
if k.hooks != nil {
k.hooks.OnValidatorRemoved(ctx, address)
}
// first retrieve the old validator record
validator, found := k.GetValidator(ctx, address)
if !found {
@ -711,6 +689,23 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
tstore.Set(GetTendermintUpdatesTKey(address), bz)
}
// UpdateValidatorCommission attempts to update a validator's commission rate.
// An error is returned if the new commission rate is invalid.
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, validator types.Validator, newRate sdk.Dec) sdk.Error {
commission := validator.Commission
blockTime := ctx.BlockHeader().Time
if err := commission.ValidateNewRate(newRate, blockTime); err != nil {
return err
}
validator.Commission.Rate = newRate
validator.Commission.UpdateTime = blockTime
k.SetValidator(ctx, validator)
return nil
}
//__________________________________________________________________________
// get the current validator on the cliff
@ -745,36 +740,3 @@ func ensureValidatorFound(found bool, ownerAddr []byte) {
panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr))
}
}
//__________________________________________________________________________
// XXX remove this code - this is should be superceded by commission work that bez is doing
// get a single validator
func (k Keeper) UpdateValidatorCommission(ctx sdk.Context, addr sdk.ValAddress, newCommission sdk.Dec) sdk.Error {
// call the hook if present
if k.hooks != nil {
k.hooks.OnValidatorCommissionChange(ctx, addr)
}
validator, found := k.GetValidator(ctx, addr)
// check for errors
switch {
case !found:
return types.ErrNoValidatorFound(k.Codespace())
case newCommission.LT(sdk.ZeroDec()):
return types.ErrCommissionNegative(k.Codespace())
case newCommission.GT(validator.CommissionMax):
return types.ErrCommissionBeyondMax(k.Codespace())
//case rateChange(Commission) > CommissionMaxChange: // XXX XXX XXX TODO implementation
//return types.ErrCommissionPastRate(k.Codespace())
}
// TODO adjust all the commission terms appropriately
validator.Commission = newCommission
k.SetValidator(ctx, validator)
return nil
}

View File

@ -3,9 +3,11 @@ package keeper
import (
"fmt"
"testing"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -305,10 +307,19 @@ func TestValidatorBasics(t *testing.T) {
// set and retrieve a record
validators[0] = keeper.UpdateValidator(ctx, validators[0])
keeper.SetValidatorByConsAddr(ctx, validators[0])
resVal, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
// retrieve from consensus
resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address()))
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
require.True(t, found)
assert.True(ValEq(t, validators[0], resVal))
resVals = keeper.GetValidatorsBonded(ctx)
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validators[0], resVals[0]))
@ -1044,3 +1055,55 @@ func TestGetValidTendermintUpdatesBondTransition(t *testing.T) {
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
}
func TestUpdateValidatorCommission(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
ctx = ctx.WithBlockHeader(abci.Header{Time: time.Now().UTC()})
commission1 := types.NewCommissionWithTime(
sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1),
sdk.NewDecWithPrec(1, 1), time.Now().UTC().Add(time.Duration(-1)*time.Hour),
)
commission2 := types.NewCommission(sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(3, 1), sdk.NewDecWithPrec(1, 1))
val1 := types.NewValidator(addrVals[0], PKs[0], types.Description{})
val2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
val1, _ = val1.SetInitialCommission(commission1)
val2, _ = val2.SetInitialCommission(commission2)
testCases := []struct {
validator types.Validator
newRate sdk.Dec
expectedErr bool
}{
{val1, sdk.ZeroDec(), true},
{val2, sdk.NewDecWithPrec(-1, 1), true},
{val2, sdk.NewDecWithPrec(4, 1), true},
{val2, sdk.NewDecWithPrec(3, 1), true},
{val2, sdk.NewDecWithPrec(2, 1), false},
}
for i, tc := range testCases {
err := keeper.UpdateValidatorCommission(ctx, tc.validator, tc.newRate)
if tc.expectedErr {
require.Error(t, err, "expected error for test case #%d with rate: %s", i, tc.newRate)
} else {
val, found := keeper.GetValidator(ctx, tc.validator.OperatorAddr)
require.True(t, found,
"expected to find validator for test case #%d with rate: %s", i, tc.newRate,
)
require.NoError(t, err,
"unexpected error for test case #%d with rate: %s", i, tc.newRate,
)
require.Equal(t, tc.newRate, val.Commission.Rate,
"expected new validator commission rate for test case #%d with rate: %s", i, tc.newRate,
)
require.Equal(t, ctx.BlockHeader().Time, val.Commission.UpdateTime,
"expected new validator commission update time for test case #%d with rate: %s", i, tc.newRate,
)
}
}
}

View File

@ -80,7 +80,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
var err error
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool {
if !validator.GetPower().GT(sdk.ZeroDec()) {
err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetPubKey())
err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetConsPubKey())
return true
}
return false

View File

@ -11,44 +11,57 @@ import (
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
"github.com/cosmos/cosmos-sdk/x/stake"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
)
// SimulateMsgCreateValidator
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
description := stake.Description{
Moniker: simulation.RandStringOfLength(r, 10),
}
key := simulation.RandomKey(r, keys)
pubkey := key.PubKey()
address := sdk.ValAddress(pubkey.Address())
amount := m.GetAccount(ctx, sdk.AccAddress(address)).GetCoins().AmountOf(denom)
maxCommission := sdk.NewInt(10)
commission := stake.NewCommissionMsg(
sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1),
sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1),
sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1),
)
acc := simulation.RandomAcc(r, accs)
address := sdk.ValAddress(acc.Address)
amount := m.GetAccount(ctx, acc.Address).GetCoins().AmountOf(denom)
if amount.GT(sdk.ZeroInt()) {
amount = simulation.RandomAmount(r, amount)
}
if amount.Equal(sdk.ZeroInt()) {
return "no-operation", nil, nil
}
msg := stake.MsgCreateValidator{
Description: description,
Commission: commission,
ValidatorAddr: address,
DelegatorAddr: sdk.AccAddress(address),
PubKey: pubkey,
DelegatorAddr: acc.Address,
PubKey: acc.PubKey,
Delegation: sdk.NewCoin(denom, amount),
}
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() {
write()
}
event(fmt.Sprintf("stake/MsgCreateValidator/%v", result.IsOK()))
// require.True(t, result.IsOK(), "expected OK result but instead got %v", result)
action = fmt.Sprintf("TestMsgCreateValidator: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
@ -58,7 +71,7 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
// SimulateMsgEditValidator
func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
description := stake.Description{
Moniker: simulation.RandStringOfLength(r, 10),
@ -66,16 +79,22 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
Website: simulation.RandStringOfLength(r, 10),
Details: simulation.RandStringOfLength(r, 10),
}
key := simulation.RandomKey(r, keys)
pubkey := key.PubKey()
address := sdk.ValAddress(pubkey.Address())
maxCommission := sdk.NewInt(10)
newCommissionRate := sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1)
acc := simulation.RandomAcc(r, accs)
address := sdk.ValAddress(acc.Address)
msg := stake.MsgEditValidator{
Description: description,
ValidatorAddr: address,
Description: description,
ValidatorAddr: address,
CommissionRate: &newCommissionRate,
}
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() {
@ -90,13 +109,13 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
// SimulateMsgDelegate
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys)
delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
validatorAcc := simulation.RandomAcc(r, accs)
validatorAddress := sdk.ValAddress(validatorAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
if amount.GT(sdk.ZeroInt()) {
amount = simulation.RandomAmount(r, amount)
@ -126,13 +145,13 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat
// SimulateMsgBeginUnbonding
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys)
delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
validatorAcc := simulation.RandomAcc(r, accs)
validatorAddress := sdk.ValAddress(validatorAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
if amount.GT(sdk.ZeroInt()) {
amount = simulation.RandomAmount(r, amount)
@ -162,12 +181,12 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
// SimulateMsgCompleteUnbonding
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys)
delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
validatorAcc := simulation.RandomAcc(r, accs)
validatorAddress := sdk.ValAddress(validatorAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
msg := stake.MsgCompleteUnbonding{
DelegatorAddr: delegatorAddress,
ValidatorAddr: validatorAddress,
@ -189,15 +208,15 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
// SimulateMsgBeginRedelegate
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
denom := k.GetParams(ctx).BondDenom
sourceValidatorKey := simulation.RandomKey(r, keys)
sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address())
destValidatorKey := simulation.RandomKey(r, keys)
destValidatorAddress := sdk.ValAddress(destValidatorKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys)
delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
sourceValidatorAcc := simulation.RandomAcc(r, accs)
sourceValidatorAddress := sdk.ValAddress(sourceValidatorAcc.Address)
destValidatorAcc := simulation.RandomAcc(r, accs)
destValidatorAddress := sdk.ValAddress(destValidatorAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
// TODO
amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom)
if amount.GT(sdk.ZeroInt()) {
@ -229,14 +248,14 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation
// SimulateMsgCompleteRedelegate
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
validatorSrcKey := simulation.RandomKey(r, keys)
validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address())
validatorDstKey := simulation.RandomKey(r, keys)
validatorDstAddress := sdk.ValAddress(validatorDstKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys)
delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address())
validatorSrcAcc := simulation.RandomAcc(r, accs)
validatorSrcAddress := sdk.ValAddress(validatorSrcAcc.Address)
validatorDstAcc := simulation.RandomAcc(r, accs)
validatorDstAddress := sdk.ValAddress(validatorDstAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
msg := stake.MsgCompleteRedelegate{
DelegatorAddr: delegatorAddress,
ValidatorSrcAddr: validatorSrcAddress,
@ -259,7 +278,7 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
// Setup
// nolint: errcheck
func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
return func(r *rand.Rand, privKeys []crypto.PrivKey) {
return func(r *rand.Rand, accs []simulation.Account) {
ctx := mapp.NewContext(false, abci.Header{})
gen := stake.DefaultGenesisState()
gen.Params.InflationMax = sdk.NewDec(0)

View File

@ -6,7 +6,6 @@ import (
"testing"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -38,8 +37,8 @@ func TestStakeWithRandomMessages(t *testing.T) {
panic(err)
}
appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage {
mock.RandomSetGenesis(r, mapp, accs, []string{"stake"})
appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage {
simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"})
return json.RawMessage("{}")
}

View File

@ -12,6 +12,7 @@ type (
Keeper = keeper.Keeper
Validator = types.Validator
Description = types.Description
Commission = types.Commission
Delegation = types.Delegation
DelegationSummary = types.DelegationSummary
UnbondingDelegation = types.UnbondingDelegation
@ -64,13 +65,16 @@ var (
GetREDsToValDstIndexKey = keeper.GetREDsToValDstIndexKey
GetREDsByDelToValDstIndexKey = keeper.GetREDsByDelToValDstIndexKey
DefaultParams = types.DefaultParams
InitialPool = types.InitialPool
NewValidator = types.NewValidator
NewDescription = types.NewDescription
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
RegisterCodec = types.RegisterCodec
DefaultParams = types.DefaultParams
InitialPool = types.InitialPool
NewValidator = types.NewValidator
NewDescription = types.NewDescription
NewCommission = types.NewCommission
NewCommissionMsg = types.NewCommissionMsg
NewCommissionWithTime = types.NewCommissionWithTime
NewGenesisState = types.NewGenesisState
DefaultGenesisState = types.DefaultGenesisState
RegisterCodec = types.RegisterCodec
NewMsgCreateValidator = types.NewMsgCreateValidator
NewMsgCreateValidatorOnBehalfOf = types.NewMsgCreateValidatorOnBehalfOf

128
x/stake/types/commission.go Normal file
View File

@ -0,0 +1,128 @@
package types
import (
"fmt"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
)
type (
// Commission defines a commission parameters for a given validator.
Commission struct {
Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators
MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which validator can ever charge
MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission
UpdateTime time.Time `json:"update_time"` // the last time the commission rate was changed
}
// CommissionMsg defines a commission message to be used for creating a
// validator.
CommissionMsg struct {
Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators
MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which validator can ever charge
MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission
}
)
// NewCommissionMsg returns an initialized validator commission message.
func NewCommissionMsg(rate, maxRate, maxChangeRate sdk.Dec) CommissionMsg {
return CommissionMsg{
Rate: rate,
MaxRate: maxRate,
MaxChangeRate: maxChangeRate,
}
}
// NewCommission returns an initialized validator commission.
func NewCommission(rate, maxRate, maxChangeRate sdk.Dec) Commission {
return Commission{
Rate: rate,
MaxRate: maxRate,
MaxChangeRate: maxChangeRate,
UpdateTime: time.Unix(0, 0).UTC(),
}
}
// NewCommission returns an initialized validator commission with a specified
// update time which should be the current block BFT time.
func NewCommissionWithTime(rate, maxRate, maxChangeRate sdk.Dec, updatedAt time.Time) Commission {
return Commission{
Rate: rate,
MaxRate: maxRate,
MaxChangeRate: maxChangeRate,
UpdateTime: updatedAt,
}
}
// Equal checks if the given Commission object is equal to the receiving
// Commission object.
func (c Commission) Equal(c2 Commission) bool {
return c.Rate.Equal(c2.Rate) &&
c.MaxRate.Equal(c2.MaxRate) &&
c.MaxChangeRate.Equal(c2.MaxChangeRate) &&
c.UpdateTime.Equal(c2.UpdateTime)
}
// String implements the Stringer interface for a Commission.
func (c Commission) String() string {
return fmt.Sprintf("rate: %s, maxRate: %s, maxChangeRate: %s, updateTime: %s",
c.Rate, c.MaxRate, c.MaxChangeRate, c.UpdateTime,
)
}
// Validate performs basic sanity validation checks of initial commission
// parameters. If validation fails, an SDK error is returned.
func (c Commission) Validate() sdk.Error {
switch {
case c.MaxRate.LT(sdk.ZeroDec()):
// max rate cannot be negative
return ErrCommissionNegative(DefaultCodespace)
case c.MaxRate.GT(sdk.OneDec()):
// max rate cannot be greater than 100%
return ErrCommissionHuge(DefaultCodespace)
case c.Rate.LT(sdk.ZeroDec()):
// rate cannot be negative
return ErrCommissionNegative(DefaultCodespace)
case c.Rate.GT(c.MaxRate):
// rate cannot be greater than the max rate
return ErrCommissionGTMaxRate(DefaultCodespace)
case c.MaxChangeRate.LT(sdk.ZeroDec()):
// change rate cannot be negative
return ErrCommissionChangeRateNegative(DefaultCodespace)
case c.MaxChangeRate.GT(c.MaxRate):
// change rate cannot be greater than the max rate
return ErrCommissionChangeRateGTMaxRate(DefaultCodespace)
}
return nil
}
// ValidateNewRate performs basic sanity validation checks of a new commission
// rate. If validation fails, an SDK error is returned.
func (c Commission) ValidateNewRate(newRate sdk.Dec, blockTime time.Time) sdk.Error {
switch {
case blockTime.Sub(c.UpdateTime).Hours() < 24:
// new rate cannot be changed more than once within 24 hours
return ErrCommissionUpdateTime(DefaultCodespace)
case newRate.LT(sdk.ZeroDec()):
// new rate cannot be negative
return ErrCommissionNegative(DefaultCodespace)
case newRate.GT(c.MaxRate):
// new rate cannot be greater than the max rate
return ErrCommissionGTMaxRate(DefaultCodespace)
case newRate.Sub(c.Rate).Abs().GT(c.MaxChangeRate):
// new rate % points change cannot be greater than the max change rate
return ErrCommissionGTMaxChangeRate(DefaultCodespace)
}
return nil
}

View File

@ -65,12 +65,24 @@ func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
}
func ErrCommissionBeyondMax(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than preset commission maximum")
func ErrCommissionGTMaxRate(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than the max rate")
}
func ErrCommissionPastRate(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission change is greater than the commission rate, please wait before changing your commission more")
func ErrCommissionUpdateTime(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be changed more than once in 24h")
}
func ErrCommissionChangeRateNegative(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission change rate must be positive")
}
func ErrCommissionChangeRateGTMaxRate(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission change rate cannot be more than the max rate")
}
func ErrCommissionGTMaxChangeRate(codespace sdk.CodespaceType) sdk.Error {
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be changed more than max change rate")
}
func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {

View File

@ -107,7 +107,6 @@ func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Dec,
}
// Checks that The inflation will correctly increase or decrease after an update to the pool
// nolint: gocyclo
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Dec, msg string) {
inflationChange := updatedInflation.Sub(previousInflation)

View File

@ -20,6 +20,7 @@ var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{}
// MsgCreateValidator - struct for unbonding transactions
type MsgCreateValidator struct {
Description
Commission CommissionMsg
DelegatorAddr sdk.AccAddress `json:"delegator_address"`
ValidatorAddr sdk.ValAddress `json:"validator_address"`
PubKey crypto.PubKey `json:"pubkey"`
@ -28,22 +29,23 @@ type MsgCreateValidator struct {
// 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) MsgCreateValidator {
selfDelegation sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator {
return NewMsgCreateValidatorOnBehalfOf(
sdk.AccAddress(valAddr), valAddr, pubkey, selfDelegation, description,
sdk.AccAddress(valAddr), valAddr, pubkey, selfDelegation, description, commission,
)
}
// Creates validator msg by delegator address on behalf of validator address
func NewMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress,
pubkey crypto.PubKey, delegation sdk.Coin, description Description) MsgCreateValidator {
pubkey crypto.PubKey, delegation sdk.Coin, description Description, commission CommissionMsg) MsgCreateValidator {
return MsgCreateValidator{
Description: description,
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
PubKey: pubkey,
Delegation: delegation,
Commission: commission,
}
}
@ -95,10 +97,13 @@ func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
if !(msg.Delegation.Amount.GT(sdk.ZeroInt())) {
return ErrBadDelegationAmount(DefaultCodespace)
}
empty := Description{}
if msg.Description == empty {
if msg.Description == (Description{}) {
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "description must be included")
}
if msg.Commission == (CommissionMsg{}) {
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "commission must be included")
}
return nil
}
@ -108,12 +113,20 @@ func (msg MsgCreateValidator) ValidateBasic() sdk.Error {
type MsgEditValidator struct {
Description
ValidatorAddr sdk.ValAddress `json:"address"`
// We pass a reference to the new commission rate 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"`
}
func NewMsgEditValidator(valAddr sdk.ValAddress, description Description) MsgEditValidator {
func NewMsgEditValidator(valAddr sdk.ValAddress, description Description, newRate *sdk.Dec) MsgEditValidator {
return MsgEditValidator{
Description: description,
ValidatorAddr: valAddr,
Description: description,
CommissionRate: newRate,
ValidatorAddr: valAddr,
}
}
@ -144,10 +157,11 @@ func (msg MsgEditValidator) ValidateBasic() sdk.Error {
if msg.ValidatorAddr == nil {
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "nil validator address")
}
empty := Description{}
if msg.Description == empty {
if msg.Description == (Description{}) {
return sdk.NewError(DefaultCodespace, CodeInvalidInput, "transaction must include some information to modify")
}
return nil
}

View File

@ -17,26 +17,30 @@ var (
// test ValidateBasic for MsgCreateValidator
func TestMsgCreateValidator(t *testing.T) {
commission1 := NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
commission2 := NewCommissionMsg(sdk.NewDec(5), sdk.NewDec(5), sdk.NewDec(5))
tests := []struct {
name, moniker, identity, website, details string
commissionMsg CommissionMsg
validatorAddr sdk.ValAddress
pubkey crypto.PubKey
bond sdk.Coin
expectPass bool
}{
{"basic good", "a", "b", "c", "d", addr1, pk1, coinPos, true},
{"partial description", "", "", "c", "", addr1, pk1, coinPos, true},
{"empty description", "", "", "", "", addr1, pk1, coinPos, false},
{"empty address", "a", "b", "c", "d", emptyAddr, pk1, coinPos, false},
{"empty pubkey", "a", "b", "c", "d", addr1, emptyPubkey, coinPos, true},
{"empty bond", "a", "b", "c", "d", addr1, pk1, coinZero, false},
{"negative bond", "a", "b", "c", "d", addr1, pk1, coinNeg, false},
{"negative bond", "a", "b", "c", "d", addr1, pk1, coinNeg, false},
{"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},
{"negative bond", "a", "b", "c", "d", commission2, addr1, pk1, coinNeg, false},
{"negative bond", "a", "b", "c", "d", commission1, addr1, pk1, coinNeg, false},
}
for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description)
msg := NewMsgCreateValidator(tc.validatorAddr, tc.pubkey, tc.bond, description, tc.commissionMsg)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
@ -60,7 +64,9 @@ func TestMsgEditValidator(t *testing.T) {
for _, tc := range tests {
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
msg := NewMsgEditValidator(tc.validatorAddr, description)
newRate := sdk.ZeroDec()
msg := NewMsgEditValidator(tc.validatorAddr, description, &newRate)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
@ -71,28 +77,35 @@ func TestMsgEditValidator(t *testing.T) {
// test ValidateBasic and GetSigners for MsgCreateValidatorOnBehalfOf
func TestMsgCreateValidatorOnBehalfOf(t *testing.T) {
commission1 := NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
commission2 := NewCommissionMsg(sdk.NewDec(5), sdk.NewDec(5), sdk.NewDec(5))
tests := []struct {
name, moniker, identity, website, details string
commissionMsg CommissionMsg
delegatorAddr sdk.AccAddress
validatorAddr sdk.ValAddress
validatorPubKey crypto.PubKey
bond sdk.Coin
expectPass bool
}{
{"basic good", "a", "b", "c", "d", sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
{"partial description", "", "", "c", "", sdk.AccAddress(addr1), addr2, pk2, coinPos, true},
{"empty description", "", "", "", "", sdk.AccAddress(addr1), addr2, pk2, coinPos, false},
{"empty delegator address", "a", "b", "c", "d", sdk.AccAddress(emptyAddr), addr2, pk2, coinPos, false},
{"empty validator address", "a", "b", "c", "d", sdk.AccAddress(addr1), emptyAddr, pk2, coinPos, false},
{"empty pubkey", "a", "b", "c", "d", sdk.AccAddress(addr1), addr2, emptyPubkey, coinPos, true},
{"empty bond", "a", "b", "c", "d", sdk.AccAddress(addr1), addr2, pk2, coinZero, false},
{"negative bond", "a", "b", "c", "d", sdk.AccAddress(addr1), addr2, pk2, coinNeg, false},
{"negative bond", "a", "b", "c", "d", sdk.AccAddress(addr1), addr2, pk2, coinNeg, false},
{"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},
{"negative bond", "a", "b", "c", "d", commission1, sdk.AccAddress(addr1), addr2, pk2, coinNeg, false},
{"negative bond", "a", "b", "c", "d", commission2, sdk.AccAddress(addr1), addr2, pk2, coinNeg, 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)
msg := NewMsgCreateValidatorOnBehalfOf(
tc.delegatorAddr, tc.validatorAddr, tc.validatorPubKey, tc.bond, description, tc.commissionMsg,
)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
@ -100,11 +113,11 @@ func TestMsgCreateValidatorOnBehalfOf(t *testing.T) {
}
}
msg := NewMsgCreateValidator(addr1, pk1, coinPos, Description{})
msg := NewMsgCreateValidator(addr1, pk1, coinPos, Description{}, CommissionMsg{})
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{})
msg = NewMsgCreateValidatorOnBehalfOf(sdk.AccAddress(addr2), addr1, pk1, coinPos, Description{}, CommissionMsg{})
addrs = msg.GetSigners()
require.Equal(t, []sdk.AccAddress{sdk.AccAddress(addr2), sdk.AccAddress(addr1)}, addrs, "Signers for onbehalfof msg is wrong")
}

View File

@ -36,68 +36,56 @@ type Validator struct {
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
Commission Commission `json:"commission"` // commission parameters
}
// NewValidator - initialize a new validator
func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Description) Validator {
return Validator{
OperatorAddr: operator,
ConsPubKey: pubKey,
Jailed: false,
Status: sdk.Unbonded,
Tokens: sdk.ZeroDec(),
DelegatorShares: sdk.ZeroDec(),
Description: description,
BondHeight: int64(0),
BondIntraTxCounter: int16(0),
UnbondingHeight: int64(0),
UnbondingMinTime: time.Unix(0, 0).UTC(),
Commission: sdk.ZeroDec(),
CommissionMax: sdk.ZeroDec(),
CommissionChangeRate: sdk.ZeroDec(),
CommissionChangeToday: sdk.ZeroDec(),
OperatorAddr: operator,
ConsPubKey: pubKey,
Jailed: false,
Status: sdk.Unbonded,
Tokens: sdk.ZeroDec(),
DelegatorShares: sdk.ZeroDec(),
Description: description,
BondHeight: int64(0),
BondIntraTxCounter: int16(0),
UnbondingHeight: int64(0),
UnbondingMinTime: time.Unix(0, 0).UTC(),
Commission: NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
}
}
// what's kept in the store value
type validatorValue struct {
ConsPubKey crypto.PubKey
Jailed bool
Status sdk.BondStatus
Tokens sdk.Dec
DelegatorShares sdk.Dec
Description Description
BondHeight int64
BondIntraTxCounter int16
UnbondingHeight int64
UnbondingMinTime time.Time
Commission sdk.Dec
CommissionMax sdk.Dec
CommissionChangeRate sdk.Dec
CommissionChangeToday sdk.Dec
ConsPubKey crypto.PubKey
Jailed bool
Status sdk.BondStatus
Tokens sdk.Dec
DelegatorShares sdk.Dec
Description Description
BondHeight int64
BondIntraTxCounter int16
UnbondingHeight int64
UnbondingMinTime time.Time
Commission Commission
}
// return the redelegation without fields contained within the key for the store
func MustMarshalValidator(cdc *codec.Codec, validator Validator) []byte {
val := validatorValue{
ConsPubKey: validator.ConsPubKey,
Jailed: validator.Jailed,
Status: validator.Status,
Tokens: validator.Tokens,
DelegatorShares: validator.DelegatorShares,
Description: validator.Description,
BondHeight: validator.BondHeight,
BondIntraTxCounter: validator.BondIntraTxCounter,
UnbondingHeight: validator.UnbondingHeight,
UnbondingMinTime: validator.UnbondingMinTime,
Commission: validator.Commission,
CommissionMax: validator.CommissionMax,
CommissionChangeRate: validator.CommissionChangeRate,
CommissionChangeToday: validator.CommissionChangeToday,
ConsPubKey: validator.ConsPubKey,
Jailed: validator.Jailed,
Status: validator.Status,
Tokens: validator.Tokens,
DelegatorShares: validator.DelegatorShares,
Description: validator.Description,
BondHeight: validator.BondHeight,
BondIntraTxCounter: validator.BondIntraTxCounter,
UnbondingHeight: validator.UnbondingHeight,
UnbondingMinTime: validator.UnbondingMinTime,
Commission: validator.Commission,
}
return cdc.MustMarshalBinary(val)
}
@ -124,21 +112,18 @@ func UnmarshalValidator(cdc *codec.Codec, operatorAddr, value []byte) (validator
}
return Validator{
OperatorAddr: operatorAddr,
ConsPubKey: storeValue.ConsPubKey,
Jailed: storeValue.Jailed,
Tokens: storeValue.Tokens,
Status: storeValue.Status,
DelegatorShares: storeValue.DelegatorShares,
Description: storeValue.Description,
BondHeight: storeValue.BondHeight,
BondIntraTxCounter: storeValue.BondIntraTxCounter,
UnbondingHeight: storeValue.UnbondingHeight,
UnbondingMinTime: storeValue.UnbondingMinTime,
Commission: storeValue.Commission,
CommissionMax: storeValue.CommissionMax,
CommissionChangeRate: storeValue.CommissionChangeRate,
CommissionChangeToday: storeValue.CommissionChangeToday,
OperatorAddr: operatorAddr,
ConsPubKey: storeValue.ConsPubKey,
Jailed: storeValue.Jailed,
Tokens: storeValue.Tokens,
Status: storeValue.Status,
DelegatorShares: storeValue.DelegatorShares,
Description: storeValue.Description,
BondHeight: storeValue.BondHeight,
BondIntraTxCounter: storeValue.BondIntraTxCounter,
UnbondingHeight: storeValue.UnbondingHeight,
UnbondingMinTime: storeValue.UnbondingMinTime,
Commission: storeValue.Commission,
}, nil
}
@ -156,16 +141,13 @@ func (v Validator) HumanReadableString() (string, error) {
resp += fmt.Sprintf("Validator Consensus Pubkey: %s\n", bechConsPubKey)
resp += fmt.Sprintf("Jailed: %v\n", v.Jailed)
resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status))
resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.String())
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String())
resp += fmt.Sprintf("Tokens: %s\n", v.Tokens)
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares)
resp += fmt.Sprintf("Description: %s\n", v.Description)
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
resp += fmt.Sprintf("Unbonding Height: %d\n", v.UnbondingHeight)
resp += fmt.Sprintf("Minimum Unbonding Time: %v\n", v.UnbondingMinTime)
resp += fmt.Sprintf("Commission: %s\n", v.Commission.String())
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
resp += fmt.Sprintf("Commission: {%s}\n", v.Commission)
return resp, nil
}
@ -189,10 +171,7 @@ type bechValidator struct {
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
Commission Commission `json:"commission"` // commission parameters
}
// MarshalJSON marshals the validator to JSON using Bech32
@ -203,21 +182,18 @@ func (v Validator) MarshalJSON() ([]byte, error) {
}
return codec.Cdc.MarshalJSON(bechValidator{
OperatorAddr: v.OperatorAddr,
ConsPubKey: bechConsPubKey,
Jailed: v.Jailed,
Status: v.Status,
Tokens: v.Tokens,
DelegatorShares: v.DelegatorShares,
Description: v.Description,
BondHeight: v.BondHeight,
BondIntraTxCounter: v.BondIntraTxCounter,
UnbondingHeight: v.UnbondingHeight,
UnbondingMinTime: v.UnbondingMinTime,
Commission: v.Commission,
CommissionMax: v.CommissionMax,
CommissionChangeRate: v.CommissionChangeRate,
CommissionChangeToday: v.CommissionChangeToday,
OperatorAddr: v.OperatorAddr,
ConsPubKey: bechConsPubKey,
Jailed: v.Jailed,
Status: v.Status,
Tokens: v.Tokens,
DelegatorShares: v.DelegatorShares,
Description: v.Description,
BondHeight: v.BondHeight,
BondIntraTxCounter: v.BondIntraTxCounter,
UnbondingHeight: v.UnbondingHeight,
UnbondingMinTime: v.UnbondingMinTime,
Commission: v.Commission,
})
}
@ -232,21 +208,18 @@ func (v *Validator) UnmarshalJSON(data []byte) error {
return err
}
*v = Validator{
OperatorAddr: bv.OperatorAddr,
ConsPubKey: consPubKey,
Jailed: bv.Jailed,
Tokens: bv.Tokens,
Status: bv.Status,
DelegatorShares: bv.DelegatorShares,
Description: bv.Description,
BondHeight: bv.BondHeight,
BondIntraTxCounter: bv.BondIntraTxCounter,
UnbondingHeight: bv.UnbondingHeight,
UnbondingMinTime: bv.UnbondingMinTime,
Commission: bv.Commission,
CommissionMax: bv.CommissionMax,
CommissionChangeRate: bv.CommissionChangeRate,
CommissionChangeToday: bv.CommissionChangeToday,
OperatorAddr: bv.OperatorAddr,
ConsPubKey: consPubKey,
Jailed: bv.Jailed,
Tokens: bv.Tokens,
Status: bv.Status,
DelegatorShares: bv.DelegatorShares,
Description: bv.Description,
BondHeight: bv.BondHeight,
BondIntraTxCounter: bv.BondIntraTxCounter,
UnbondingHeight: bv.UnbondingHeight,
UnbondingMinTime: bv.UnbondingMinTime,
Commission: bv.Commission,
}
return nil
}
@ -254,18 +227,14 @@ func (v *Validator) UnmarshalJSON(data []byte) error {
//___________________________________________________________________
// only the vitals - does not check bond height of IntraTxCounter
// nolint gocyclo - why dis fail?
func (v Validator) Equal(c2 Validator) bool {
return v.ConsPubKey.Equals(c2.ConsPubKey) &&
bytes.Equal(v.OperatorAddr, c2.OperatorAddr) &&
v.Status.Equal(c2.Status) &&
v.Tokens.Equal(c2.Tokens) &&
v.DelegatorShares.Equal(c2.DelegatorShares) &&
v.Description == c2.Description &&
v.Commission.Equal(c2.Commission) &&
v.CommissionMax.Equal(c2.CommissionMax) &&
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
v.CommissionChangeToday.Equal(c2.CommissionChangeToday)
func (v Validator) Equal(v2 Validator) bool {
return v.ConsPubKey.Equals(v2.ConsPubKey) &&
bytes.Equal(v.OperatorAddr, v2.OperatorAddr) &&
v.Status.Equal(v2.Status) &&
v.Tokens.Equal(v2.Tokens) &&
v.DelegatorShares.Equal(v2.DelegatorShares) &&
v.Description == v2.Description &&
v.Commission.Equal(v2.Commission)
}
// return the TM validator address
@ -400,6 +369,17 @@ func (v Validator) RemoveTokens(pool Pool, tokens sdk.Dec) (Validator, Pool) {
return v, pool
}
// SetInitialCommission attempts to set a validator's initial commission. An
// error is returned if the commission is invalid.
func (v Validator) SetInitialCommission(commission Commission) (Validator, sdk.Error) {
if err := commission.Validate(); err != nil {
return v, err
}
v.Commission = commission
return v, nil
}
//_________________________________________________________________________________________________________
// AddTokensFromDel adds tokens to a validator
@ -474,10 +454,10 @@ func (v Validator) GetJailed() bool { return v.Jailed }
func (v Validator) GetMoniker() string { return v.Description.Moniker }
func (v Validator) GetStatus() sdk.BondStatus { return v.Status }
func (v Validator) GetOperator() sdk.ValAddress { return v.OperatorAddr }
func (v Validator) GetPubKey() crypto.PubKey { return v.ConsPubKey }
func (v Validator) GetConsPubKey() crypto.PubKey { return v.ConsPubKey }
func (v Validator) GetConsAddr() sdk.ConsAddress { return sdk.ConsAddress(v.ConsPubKey.Address()) }
func (v Validator) GetPower() sdk.Dec { return v.BondedTokens() }
func (v Validator) GetTokens() sdk.Dec { return v.Tokens }
func (v Validator) GetCommission() sdk.Dec { return v.Commission }
func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate }
func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares }
func (v Validator) GetBondHeight() int64 { return v.BondHeight }

View File

@ -274,3 +274,37 @@ func TestValidatorMarshalUnmarshalJSON(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, validator, *got)
}
func TestValidatorSetInitialCommission(t *testing.T) {
val := NewValidator(addr1, pk1, Description{})
testCases := []struct {
validator Validator
commission Commission
expectedErr bool
}{
{val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), false},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec()), true},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDec(15000000000), sdk.ZeroDec()), true},
{val, NewCommission(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.ZeroDec()), true},
{val, NewCommission(sdk.NewDecWithPrec(2, 1), sdk.NewDecWithPrec(1, 1), sdk.ZeroDec()), true},
{val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1)), true},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(2, 1)), true},
}
for i, tc := range testCases {
val, err := tc.validator.SetInitialCommission(tc.commission)
if tc.expectedErr {
require.Error(t, err,
"expected error for test case #%d with commission: %s", i, tc.commission,
)
} else {
require.NoError(t, err,
"unexpected error for test case #%d with commission: %s", i, tc.commission,
)
require.Equal(t, tc.commission, val.Commission,
"invalid validator commission for test case #%d with commission: %s", i, tc.commission,
)
}
}
}