Skip to content

Commit

Permalink
Merge pull request cosmos#982 from cosmos/rigel/stake-refactor
Browse files Browse the repository at this point in the history
Stake refactor // fee seperation
  • Loading branch information
rigelrozanski authored May 23, 2018
2 parents 6e92c8f + 5a46f26 commit 34f6475
Show file tree
Hide file tree
Showing 44 changed files with 3,151 additions and 2,111 deletions.
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@ BUG FIXES

*TBD*

BREAKING CHANGES

* [stake] candidate -> validator throughout (details in refactor comment)
* [stake] delegate-bond -> delegation throughout
* [stake] `gaiacli query validator` takes and argument instead of using the `--address-candidate` flag
* [stake] introduce `gaiacli query delegations`
* [stake] staking refactor
* ValidatorsBonded store now take sorted pubKey-address instead of validator owner-address,
is sorted like Tendermint by pk's address
* store names more understandable
* removed temporary ToKick store, just needs a local map!
* removed distinction between candidates and validators
* everything is now a validator
* only validators with a status == bonded are actively validating/receiving rewards
* Introduction of Unbonding fields, lowlevel logic throughout (not fully implemented with queue)
* Introduction of PoolShares type within validators,
replaces three rational fields (BondedShares, UnbondingShares, UnbondedShares

FEATURES

* [x/auth] Added ability to change pubkey to auth module
Expand All @@ -18,6 +36,17 @@ FEATURES
* Transactions which run out of gas stop execution and revert state changes
* A "simulate" query has been added to determine how much gas a transaction will need
* Modules can include their own gas costs for execution of particular message types
* [stake] Seperation of fee distribution to a new module
* [stake] Creation of a validator/delegation generics in `/types`
* [stake] Helper Description of the store in x/stake/store.md
* [stake] removed use of caches in the stake keeper

BUG FIXES

* Auto-sequencing now works correctly
* [stake] staking delegator shares exchange rate now relative to equivalent-bonded-tokens the validator has instead of bonded tokens
^ this is important for unbonded validators in the power store!


## 0.17.2

Expand All @@ -34,6 +63,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging)
BREAKING CHANGES

* [stake] MarshalJSON -> MarshalBinary
* Queries against the store must be prefixed with the path "/store"

FEATURES

Expand Down Expand Up @@ -71,7 +101,6 @@ BREAKING CHANGES
* gaiad init now requires use of `--name` flag
* Removed Get from Msg interface
* types/rational now extends big.Rat
* Queries against the store must be prefixed with the path "/store"

FEATURES:

Expand Down
3 changes: 2 additions & 1 deletion cmd/gaia/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
feed "github.com/cosmos/cosmos-sdk/x/fee_distribution"
"github.com/cosmos/cosmos-sdk/x/ibc"
"github.com/cosmos/cosmos-sdk/x/stake"
)
Expand Down Expand Up @@ -81,7 +82,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
app.SetInitChainer(app.initChainer)
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper))
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, stake.FeeHandler))
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, feed.BurnFeeHandler))
err := app.LoadLatestVersion(app.keyMain)
if err != nil {
cmn.Exit(err.Error())
Expand Down
28 changes: 17 additions & 11 deletions cmd/gaia/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {

genesisState := GenesisState{
Accounts: genaccs,
StakeData: stake.GetDefaultGenesisState(),
StakeData: stake.DefaultGenesisState(),
}

stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState)
Expand Down Expand Up @@ -147,7 +147,7 @@ func setGenesisAccounts(gapp *GaiaApp, accs ...*auth.BaseAccount) error {

genesisState := GenesisState{
Accounts: genaccs,
StakeData: stake.GetDefaultGenesisState(),
StakeData: stake.DefaultGenesisState(),
}

stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
Expand Down Expand Up @@ -405,9 +405,15 @@ func TestStakeMsgs(t *testing.T) {
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res1.GetCoins())
candidate, found := gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
validator, found := gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
require.True(t, found)
require.Equal(t, candidate.Address, addr1)
require.Equal(t, addr1, validator.Owner)
require.Equal(t, sdk.Bonded, validator.Status())
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))

// check the bond that should have been created as well
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1)
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))

// Edit Candidacy

Expand All @@ -417,9 +423,9 @@ func TestStakeMsgs(t *testing.T) {
)
SignDeliver(t, gapp, editCandidacyMsg, []int64{1}, true, priv1)

candidate, found = gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
validator, found = gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
require.True(t, found)
require.Equal(t, candidate.Description, description)
require.Equal(t, description, validator.Description)

// Delegate

Expand All @@ -428,12 +434,13 @@ func TestStakeMsgs(t *testing.T) {
)
SignDeliver(t, gapp, delegateMsg, []int64{0}, true, priv2)

ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins())
bond, found := gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
bond, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
require.True(t, found)
require.Equal(t, bond.DelegatorAddr, addr2)
require.Equal(t, addr2, bond.DelegatorAddr)
require.Equal(t, addr1, bond.ValidatorAddr)
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))

// Unbond

Expand All @@ -442,10 +449,9 @@ func TestStakeMsgs(t *testing.T) {
)
SignDeliver(t, gapp, unbondMsg, []int64{1}, true, priv2)

ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
require.Equal(t, genCoins, res2.GetCoins())
_, found = gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
_, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
require.False(t, found)
}

Expand Down
15 changes: 7 additions & 8 deletions cmd/gaia/app/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
}

// start with the default staking genesis state
stakeData := stake.GetDefaultGenesisState()
stakeData := stake.DefaultGenesisState()

// get genesis flag account information
genaccs := make([]GenesisAccount, len(appGenTxs))
Expand All @@ -155,19 +155,18 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
}
acc := NewGenesisAccount(&accAuth)
genaccs[i] = acc
stakeData.Pool.TotalSupply += freeFermionsAcc // increase the supply
stakeData.Pool.LooseUnbondedTokens += freeFermionsAcc // increase the supply

// add the validator
if len(genTx.Name) > 0 {
desc := stake.NewDescription(genTx.Name, "", "", "")
candidate := stake.NewCandidate(genTx.Address, genTx.PubKey, desc)
candidate.Assets = sdk.NewRat(freeFermionVal)
stakeData.Candidates = append(stakeData.Candidates, candidate)
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
validator.PoolShares = stake.NewBondedShares(sdk.NewRat(freeFermionVal))
stakeData.Validators = append(stakeData.Validators, validator)

// pool logic
stakeData.Pool.TotalSupply += freeFermionVal
stakeData.Pool.BondedPool += freeFermionVal
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedPool)
stakeData.Pool.BondedTokens += freeFermionVal
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedTokens)
}
}

Expand Down
4 changes: 2 additions & 2 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
assert.Equal(t, int64(7), barAcc.GetCoins().AmountOf("steak"))
candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
assert.Equal(t, candidate.Address.String(), barAddr)
assert.Equal(t, int64(3), candidate.Assets.Evaluate())
assert.Equal(t, int64(3), candidate.BondedShares.Evaluate())

// TODO timeout issues if not connected to the internet
// unbond a single share
Expand All @@ -117,7 +117,7 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
//barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
//assert.Equal(t, int64(99998), barAcc.GetCoins().AmountOf("steak"))
//candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
//assert.Equal(t, int64(2), candidate.Assets.Evaluate())
//assert.Equal(t, int64(2), candidate.BondedShares.Evaluate())
}

func executeWrite(t *testing.T, cmdStr string, writes ...string) {
Expand Down
8 changes: 4 additions & 4 deletions cmd/gaia/cmd/gaiacli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ func main() {
rootCmd.AddCommand(
client.GetCommands(
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
stakecmd.GetCmdQueryCandidate("stake", cdc),
stakecmd.GetCmdQueryCandidates("stake", cdc),
stakecmd.GetCmdQueryDelegatorBond("stake", cdc),
//stakecmd.GetCmdQueryDelegatorBonds("stake", cdc),
stakecmd.GetCmdQueryValidator("stake", cdc),
stakecmd.GetCmdQueryValidators("stake", cdc),
stakecmd.GetCmdQueryDelegation("stake", cdc),
stakecmd.GetCmdQueryDelegations("stake", cdc),
)...)
rootCmd.AddCommand(
client.PostCommands(
Expand Down
37 changes: 30 additions & 7 deletions types/rational.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"
"strconv"
"strings"
"testing"
)

// "that's one big rat!"
Expand Down Expand Up @@ -80,17 +81,17 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
}

//nolint
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 } // Equal - rationals are equal
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // Equal - rationals are equal
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // Equal - rationals are equal
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 }
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // greater than
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // less than
func (r Rat) Mul(r2 Rat) Rat { return Rat{*new(big.Rat).Mul(&(r.Rat), &(r2.Rat))} } // Mul - multiplication
func (r Rat) Quo(r2 Rat) Rat { return Rat{*new(big.Rat).Quo(&(r.Rat), &(r2.Rat))} } // Quo - quotient
func (r Rat) Add(r2 Rat) Rat { return Rat{*new(big.Rat).Add(&(r.Rat), &(r2.Rat))} } // Add - addition
func (r Rat) Sub(r2 Rat) Rat { return Rat{*new(big.Rat).Sub(&(r.Rat), &(r2.Rat))} } // Sub - subtraction
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) } // Sub - subtraction
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) }

var (
zero = big.NewInt(0)
Expand Down Expand Up @@ -168,3 +169,25 @@ func (r *Rat) UnmarshalAmino(text string) (err error) {
r.Rat = *tempRat
return nil
}

//___________________________________________________________________________________
// helpers

// test if two rat arrays are the equal
func RatsEqual(r1s, r2s []Rat) bool {
if len(r1s) != len(r2s) {
return false
}

for i, r1 := range r1s {
if !r1.Equal(r2s[i]) {
return false
}
}
return true
}

// intended to be used with require/assert: require.True(RatEq(...))
func RatEq(t *testing.T, exp, got Rat) (*testing.T, bool, string, Rat, Rat) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
}
21 changes: 21 additions & 0 deletions types/rational_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,24 @@ func TestEmbeddedStructSerializationGoWire(t *testing.T) {
assert.Equal(t, obj.Field2, obj2.Field2)
assert.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2)
}

func TestRatsEqual(t *testing.T) {
tests := []struct {
r1s, r2s []Rat
eq bool
}{
{[]Rat{NewRat(0)}, []Rat{NewRat(0)}, true},
{[]Rat{NewRat(0)}, []Rat{NewRat(1)}, false},
{[]Rat{NewRat(0)}, []Rat{}, false},
{[]Rat{NewRat(0), NewRat(1)}, []Rat{NewRat(0), NewRat(1)}, true},
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1), NewRat(0)}, true},
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(0), NewRat(1)}, false},
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1)}, false},
}

for _, tc := range tests {
assert.Equal(t, tc.eq, RatsEqual(tc.r1s, tc.r2s))
assert.Equal(t, tc.eq, RatsEqual(tc.r2s, tc.r1s))
}

}
65 changes: 65 additions & 0 deletions types/stake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package types

import (
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
)

// status of a validator
type BondStatus byte

// nolint
const (
Unbonded BondStatus = 0x00
Unbonding BondStatus = 0x01
Bonded BondStatus = 0x02
)

// validator for a delegated proof of stake system
type Validator interface {
GetStatus() BondStatus // status of the validator
GetOwner() Address // owner address to receive/return validators coins
GetPubKey() crypto.PubKey // validation pubkey
GetPower() Rat // validation power
GetBondHeight() int64 // height in which the validator became active
}

// validator which fulfills abci validator interface for use in Tendermint
func ABCIValidator(v Validator) abci.Validator {
return abci.Validator{
PubKey: v.GetPubKey().Bytes(),
Power: v.GetPower().Evaluate(),
}
}

// properties for the set of all validators
type ValidatorSet interface {
// iterate through validator by owner-address, execute func for each validator
IterateValidators(Context,
func(index int64, validator Validator) (stop bool))

// iterate through bonded validator by pubkey-address, execute func for each validator
IterateValidatorsBonded(Context,
func(index int64, validator Validator) (stop bool))

Validator(Context, Address) Validator // get a particular validator by owner address
TotalPower(Context) Rat // total power of the validator set
}

//_______________________________________________________________________________

// delegation bond for a delegated proof of stake system
type Delegation interface {
GetDelegator() Address // delegator address for the bond
GetValidator() Address // validator owner address for the bond
GetBondShares() Rat // amount of validator's shares
}

// properties for the set of all delegations for a particular
type DelegationSet interface {

// iterate through all delegations from one delegator by validator-address,
// execute func for each validator
IterateDelegators(Context, delegator Address,
fn func(index int64, delegation Delegation) (stop bool))
}
3 changes: 1 addition & 2 deletions x/auth/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,4 @@ func deductFees(acc sdk.Account, fee sdk.StdFee) (sdk.Account, sdk.Result) {
}

// BurnFeeHandler burns all fees (decreasing total supply)
func BurnFeeHandler(ctx sdk.Context, tx sdk.Tx, fee sdk.Coins) {
}
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}
Loading

0 comments on commit 34f6475

Please sign in to comment.