Merge branch 'rigel/truncate-decimal' into rigel/new-hooks
This commit is contained in:
commit
f5116b9d57
|
@ -489,14 +489,6 @@
|
|||
revision = "81df19e68ab1519399fccf0cab81cb75bf9d782e"
|
||||
version = "v0.23.1-rc0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e"
|
||||
name = "github.com/tendermint/tmlibs"
|
||||
packages = ["cli"]
|
||||
pruneopts = "UT"
|
||||
revision = "49596e0a1f48866603813df843c9409fc19805c6"
|
||||
version = "v0.9.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666"
|
||||
name = "github.com/zondax/ledger-goclient"
|
||||
|
@ -677,7 +669,6 @@
|
|||
"github.com/tendermint/tendermint/rpc/lib/server",
|
||||
"github.com/tendermint/tendermint/types",
|
||||
"github.com/tendermint/tendermint/version",
|
||||
"github.com/tendermint/tmlibs/cli",
|
||||
"github.com/zondax/ledger-goclient",
|
||||
"golang.org/x/crypto/blowfish",
|
||||
]
|
||||
|
|
|
@ -78,6 +78,8 @@ FEATURES
|
|||
* [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT.
|
||||
* [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag.
|
||||
* [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command.
|
||||
* [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced
|
||||
new commission flags for validator commands `create-validator` and `edit-validator`.
|
||||
|
||||
* Gaia
|
||||
* [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address`
|
||||
|
@ -90,6 +92,8 @@ FEATURES
|
|||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) allow operations to specify future operations
|
||||
* [simulation] [\#1924](https://github.com/cosmos/cosmos-sdk/issues/1924) Add benchmarking capabilities, with makefile commands "test_sim_gaia_benchmark, test_sim_gaia_profile"
|
||||
* [simulation] [\#2349](https://github.com/cosmos/cosmos-sdk/issues/2349) Add time-based future scheduled operations to simulator
|
||||
* [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement
|
||||
basis for the validator commission model.
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
@ -149,5 +153,6 @@ BUG FIXES
|
|||
loading a Ledger device at runtime.
|
||||
* [\#2158](https://github.com/cosmos/cosmos-sdk/issues/2158) Fix non-deterministic ordering of validator iteration when slashing in `gov EndBlocker`
|
||||
* [simulation] \#1924 Make simulation stop on SIGTERM
|
||||
* [\#2388](https://github.com/cosmos/cosmos-sdk/issues/2388) Remove dependency on deprecated tendermint/tmlibs repository.
|
||||
|
||||
* Tendermint
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
"github.com/tendermint/tendermint/libs/cli"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// This will fail half the time with the second output being 173
|
||||
// This is due to secp256k1 signatures not being constant size.
|
||||
// This will be resolved when updating to tendermint v0.24.0
|
||||
// nolint: vet
|
||||
func ExampleTxSendSize() {
|
||||
cdc := app.MakeCodec()
|
||||
priv1 := secp256k1.GenPrivKeySecp256k1([]byte{0})
|
||||
addr1 := sdk.AccAddress(priv1.PubKey().Address())
|
||||
priv2 := secp256k1.GenPrivKeySecp256k1([]byte{1})
|
||||
addr2 := sdk.AccAddress(priv2.PubKey().Address())
|
||||
coins := []sdk.Coin{sdk.NewCoin("denom", sdk.NewInt(10))}
|
||||
msg1 := bank.MsgSend{
|
||||
Inputs: []bank.Input{bank.NewInput(addr1, coins)},
|
||||
Outputs: []bank.Output{bank.NewOutput(addr2, coins)},
|
||||
}
|
||||
sig, _ := priv1.Sign(msg1.GetSignBytes())
|
||||
sigs := []auth.StdSignature{auth.StdSignature{nil, sig, 0, 0}}
|
||||
tx := auth.NewStdTx([]sdk.Msg{msg1}, auth.NewStdFee(0, coins...), sigs, "")
|
||||
fmt.Println(len(cdc.MustMarshalBinaryBare([]sdk.Msg{msg1})))
|
||||
fmt.Println(len(cdc.MustMarshalBinaryBare(tx)))
|
||||
// output: 80
|
||||
// 173
|
||||
}
|
|
@ -238,6 +238,9 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
|||
cvStr += fmt.Sprintf(" --pubkey=%s", barCeshPubKey)
|
||||
cvStr += fmt.Sprintf(" --amount=%v", "2steak")
|
||||
cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally")
|
||||
cvStr += fmt.Sprintf(" --commission-rate=%v", "0.05")
|
||||
cvStr += fmt.Sprintf(" --commission-max-rate=%v", "0.20")
|
||||
cvStr += fmt.Sprintf(" --commission-max-change-rate=%v", "0.10")
|
||||
|
||||
initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewDec(1))
|
||||
|
||||
|
|
|
@ -1,86 +1,107 @@
|
|||
## Transaction Overview
|
||||
# Transaction Overview
|
||||
|
||||
In this section we describe the processing of the transactions and the
|
||||
corresponding updates to the state. Transactions:
|
||||
- TxCreateValidator
|
||||
- TxEditValidator
|
||||
- TxDelegation
|
||||
- TxStartUnbonding
|
||||
- TxCompleteUnbonding
|
||||
- TxRedelegate
|
||||
- TxCompleteRedelegation
|
||||
|
||||
* TxCreateValidator
|
||||
* TxEditValidator
|
||||
* TxDelegation
|
||||
* TxStartUnbonding
|
||||
* TxCompleteUnbonding
|
||||
* TxRedelegate
|
||||
* TxCompleteRedelegation
|
||||
|
||||
Other important state changes:
|
||||
- Update Validators
|
||||
|
||||
* Update Validators
|
||||
|
||||
Other notes:
|
||||
- `tx` denotes a reference to the transaction being processed
|
||||
- `sender` denotes the address of the sender of the transaction
|
||||
- `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and
|
||||
modify objects from the store
|
||||
- `sdk.Dec` refers to a decimal type specified by the SDK.
|
||||
|
||||
### TxCreateValidator
|
||||
* `tx` denotes a reference to the transaction being processed
|
||||
* `sender` denotes the address of the sender of the transaction
|
||||
* `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and
|
||||
modify objects from the store
|
||||
* `sdk.Dec` refers to a decimal type specified by the SDK.
|
||||
|
||||
- triggers: `distribution.CreateValidatorDistribution`
|
||||
## TxCreateValidator
|
||||
|
||||
* triggers: `distribution.CreateValidatorDistribution`
|
||||
|
||||
A validator is created using the `TxCreateValidator` transaction.
|
||||
|
||||
```golang
|
||||
type TxCreateValidator struct {
|
||||
Operator sdk.Address
|
||||
ConsensusPubKey crypto.PubKey
|
||||
GovernancePubKey crypto.PubKey
|
||||
SelfDelegation coin.Coin
|
||||
Description Description
|
||||
Commission Commission
|
||||
|
||||
Description Description
|
||||
Commission sdk.Dec
|
||||
CommissionMax sdk.Dec
|
||||
CommissionMaxChange sdk.Dec
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
PubKey crypto.PubKey
|
||||
Delegation sdk.Coin
|
||||
}
|
||||
|
||||
|
||||
createValidator(tx TxCreateValidator):
|
||||
validator = getValidator(tx.Operator)
|
||||
if validator != nil return // only one validator per address
|
||||
ok := validatorExists(tx.ValidatorAddr)
|
||||
if ok return err // only one validator per address
|
||||
|
||||
validator = NewValidator(operatorAddr, ConsensusPubKey, GovernancePubKey, Description)
|
||||
init validator poolShares, delegatorShares set to 0
|
||||
init validator commision fields from tx
|
||||
validator.PoolShares = 0
|
||||
ok := validatorByPubKeyExists(tx.PubKey)
|
||||
if ok return err // only one validator per public key
|
||||
|
||||
err := validateDenom(tx.Delegation.Denom)
|
||||
if err != nil return err // denomination must be valid
|
||||
|
||||
validator := NewValidator(tx.ValidatorAddr, tx.PubKey, tx.Description)
|
||||
|
||||
err := setInitialCommission(validator, tx.Commission, blockTime)
|
||||
if err != nil return err // must be able to set initial commission correctly
|
||||
|
||||
// set the validator and public key
|
||||
setValidator(validator)
|
||||
setValidatorByPubKeyIndex(validator)
|
||||
|
||||
txDelegate = TxDelegate(tx.Operator, tx.Operator, tx.SelfDelegation)
|
||||
delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate)
|
||||
return
|
||||
// delegate coins from tx.DelegatorAddr to the validator
|
||||
err := delegate(tx.DelegatorAddr, tx.Delegation, validator)
|
||||
if err != nil return err // must be able to set delegation correctly
|
||||
|
||||
tags := createTags(tx)
|
||||
return tags
|
||||
```
|
||||
|
||||
### TxEditValidator
|
||||
## TxEditValidator
|
||||
|
||||
If either the `Description` (excluding `DateBonded` which is constant),
|
||||
`Commission`, or the `GovernancePubKey` need to be updated, the
|
||||
`TxEditCandidacy` transaction should be sent from the operator account:
|
||||
If either the `Description`, `Commission`, or the `ValidatorAddr` need to be
|
||||
updated, the `TxEditCandidacy` transaction should be sent from the operator
|
||||
account:
|
||||
|
||||
```golang
|
||||
type TxEditCandidacy struct {
|
||||
GovernancePubKey crypto.PubKey
|
||||
Commission sdk.Dec
|
||||
Description Description
|
||||
Description Description
|
||||
ValidatorAddr sdk.ValAddress
|
||||
CommissionRate sdk.Dec
|
||||
}
|
||||
|
||||
editCandidacy(tx TxEditCandidacy):
|
||||
validator = getValidator(tx.ValidatorAddr)
|
||||
validator, ok := getValidator(tx.ValidatorAddr)
|
||||
if !ok return err // validator must exist
|
||||
|
||||
if tx.Commission > CommissionMax || tx.Commission < 0 then fail
|
||||
if rateChange(tx.Commission) > CommissionMaxChange then fail
|
||||
validator.Commission = tx.Commission
|
||||
// Attempt to update the validator's description. The description provided
|
||||
// must be valid.
|
||||
description, err := updateDescription(validator, tx.Description)
|
||||
if err != nil return err
|
||||
|
||||
if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey
|
||||
if tx.Description != nil validator.Description = tx.Description
|
||||
// a validator is not required to update it's commission rate
|
||||
if tx.CommissionRate != nil {
|
||||
// Attempt to update a validator's commission rate. The rate provided
|
||||
// must be valid. It's rate can only be updated once a day.
|
||||
err := updateValidatorCommission(validator, tx.CommissionRate)
|
||||
if err != nil return err
|
||||
}
|
||||
|
||||
setValidator(store, validator)
|
||||
return
|
||||
// set the validator and public key
|
||||
setValidator(validator)
|
||||
|
||||
tags := createTags(tx)
|
||||
return tags
|
||||
```
|
||||
|
||||
### TxDelegate
|
||||
|
|
|
@ -35,9 +35,16 @@ gaiacli stake create-validator \
|
|||
--address-validator=<account_cosmosval>
|
||||
--moniker="choose a moniker" \
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name>
|
||||
--name=<key_name> \
|
||||
--commission-rate="0.10" \
|
||||
--commission-max-rate="0.20" \
|
||||
--commission-max-change-rate="0.01"
|
||||
```
|
||||
|
||||
__Note__: When specifying commission parameters, the `commission-max-change-rate`
|
||||
is used to measure % _point_ change over the `commission-rate`. E.g. 1% to 2% is
|
||||
a 100% rate increase, but only 1 percentage point.
|
||||
|
||||
### Edit Validator Description
|
||||
|
||||
You can edit your validator's public description. This info is to identify your validator, and will be relied on by delegators to decide which validators to stake to. Make sure to provide input for every flag below, otherwise the field will default to empty (`--moniker` defaults to the machine name).
|
||||
|
@ -52,9 +59,17 @@ gaiacli stake edit-validator
|
|||
--identity=6A0D65E29A4CBC8E
|
||||
--details="To infinity and beyond!"
|
||||
--chain-id=<chain_id> \
|
||||
--name=<key_name>
|
||||
--name=<key_name> \
|
||||
--commission-rate="0.10"
|
||||
```
|
||||
|
||||
__Note__: The `commission-rate` value must adhere to the following invariants:
|
||||
|
||||
- Must be between 0 and the validator's `commission-max-rate`
|
||||
- Must not exceed the validator's `commission-max-change-rate` which is maximum
|
||||
% point change rate **per day**. In other words, a validator can only change
|
||||
its commission once per day and within `commission-max-change-rate` bounds.
|
||||
|
||||
### View Validator Description
|
||||
|
||||
View the validator's information with this command:
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -68,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 address
|
||||
ValidatorByConsPubKey(Context, crypto.PubKey) Validator // get a particular validator by consensus PubKey
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -192,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))))
|
||||
}
|
||||
}
|
||||
|
@ -295,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))))
|
||||
}
|
||||
}
|
||||
|
@ -322,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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
@ -378,20 +384,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)
|
||||
|
||||
|
|
|
@ -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)})
|
||||
|
|
|
@ -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())
|
||||
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -62,27 +62,38 @@ 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()
|
||||
}
|
||||
|
@ -97,13 +108,13 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k
|
|||
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 {
|
||||
|
@ -115,17 +126,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,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -473,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)
|
||||
|
@ -490,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())
|
||||
|
|
|
@ -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)
|
||||
|
@ -697,6 +685,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
|
||||
|
|
|
@ -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"
|
||||
|
@ -314,7 +316,7 @@ func TestValidatorBasics(t *testing.T) {
|
|||
resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address()))
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
resVal, found = keeper.GetValidatorByConsPubKey(ctx, PKs[0])
|
||||
resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0]))
|
||||
require.True(t, found)
|
||||
assert.True(ValEq(t, validators[0], resVal))
|
||||
|
||||
|
@ -1053,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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,32 +23,48 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
|
|||
description := stake.Description{
|
||||
Moniker: simulation.RandStringOfLength(r, 10),
|
||||
}
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
key := simulation.RandomKey(r, keys)
|
||||
pubkey := key.PubKey()
|
||||
address := sdk.ValAddress(pubkey.Address())
|
||||
amount := m.GetAccount(ctx, sdk.AccAddress(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,
|
||||
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
|
||||
|
@ -66,16 +82,24 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
|
|||
Website: simulation.RandStringOfLength(r, 10),
|
||||
Details: simulation.RandStringOfLength(r, 10),
|
||||
}
|
||||
|
||||
maxCommission := sdk.NewInt(10)
|
||||
newCommissionRate := sdk.NewDecWithPrec(simulation.RandomAmount(r, maxCommission).Int64(), 1)
|
||||
|
||||
key := simulation.RandomKey(r, keys)
|
||||
pubkey := key.PubKey()
|
||||
address := sdk.ValAddress(pubkey.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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -65,6 +65,26 @@ func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
|||
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than 100%")
|
||||
}
|
||||
|
||||
func ErrCommissionGTMaxRate(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidValidator, "commission cannot be more than the max rate")
|
||||
}
|
||||
|
||||
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 {
|
||||
return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -478,6 +458,6 @@ 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 }
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue