Skip to content

Commit

Permalink
Merge PR cosmos#2365: Validator Commission Model
Browse files Browse the repository at this point in the history
* Update validator commission fields

* Remove CommissionChangeToday and update to use CommissionChangeTime

* Implement commission as a first class citizen type

* Implement stringer for Comission

* Move commission type and logic to new  file

* Add new commission errors

* Add commission to create validator message

* Implement and call UpdateValidatorCommission

* Update godoc for UpdateValidatorCommission

* Add Abs to the decimal type

* Implement new SetValidatorCommission

* Update decimal short godocs

* Move set initial commission logic

* Move initial commission validation to Commission type

* Update initial validator commission logic and unit tests

* Remove commission update time from struct and move to validator

* Update validator create handler tests

* Implement commission logic for CLI

* Fix make lint failure

* Fix make cover failure

* Update edit validator logic to handle new commission rate

* Fix lint and cover

* Update create/edit validator simulation to include commission params

* Update MsgEditValidator godoc

* Update pending log

* Update staking tx docs

* Fix CLI create validator test

* Update variables names for commission  strings

* Merge UpdateTime into Commission type

* Update create-validator usage in docs

* Update more docs with examples

* More doc updates
  • Loading branch information
alexanderbez authored and rigelrozanski committed Sep 24, 2018
1 parent 8774adc commit 9dafa32
Show file tree
Hide file tree
Showing 25 changed files with 731 additions and 285 deletions.
4 changes: 4 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,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`
Expand All @@ -88,6 +90,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

Expand Down
3 changes: 3 additions & 0 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
125 changes: 73 additions & 52 deletions docs/spec/staking/transactions.md
Original file line number Diff line number Diff line change
@@ -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 sdk.Dec
CommissionMax sdk.Dec
CommissionMaxChange sdk.Dec
}
Description Description
Commission Commission

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

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(operatorAddr, ConsensusPubKey, GovernancePubKey, Description)
init validator poolShares, delegatorShares set to 0
init validator commision fields from tx
validator.PoolShares = 0
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)

if tx.Commission > CommissionMax || tx.Commission < 0 then fail
if rateChange(tx.Commission) > CommissionMaxChange then fail
validator.Commission = tx.Commission

if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey
if tx.Description != nil validator.Description = tx.Description
validator, ok := getValidator(tx.ValidatorAddr)
if !ok return err // validator must exist

// Attempt to update the validator's description. The description provided
// must be valid.
description, err := updateDescription(validator, tx.Description)
if err != nil return err

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

// set the validator and public key
setValidator(validator)

setValidator(store, validator)
return
tags := createTags(tx)
return tags
```

### TxDelegate
Expand Down
19 changes: 17 additions & 2 deletions docs/validators/validator-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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:
Expand Down
6 changes: 4 additions & 2 deletions types/decimal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
18 changes: 11 additions & 7 deletions x/gov/tally_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down Expand Up @@ -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)

Expand Down
5 changes: 4 additions & 1 deletion x/slashing/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)})
Expand Down
6 changes: 4 additions & 2 deletions x/slashing/test_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,21 @@ 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),
}
}

func newTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, delAmount sdk.Int) stake.MsgDelegate {
return stake.MsgDelegate{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
Delegation: sdk.Coin{"steak", delAmount},
Delegation: sdk.NewCoin("steak", delAmount),
}
}
14 changes: 8 additions & 6 deletions x/stake/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
Loading

0 comments on commit 9dafa32

Please sign in to comment.