split types into multiple files, fix delegation share exrate
This commit is contained in:
parent
0c98cc6895
commit
b64363fcbe
@ -25,6 +25,12 @@ FEATURES
|
||||
* Seperation of fee distribution to a new module
|
||||
* Creation of a validator/delegation generics in `/types`
|
||||
|
||||
BUG FIXES
|
||||
|
||||
* Auto-sequencing now works correctly
|
||||
* staking delegator shares exchange rate now relative to equivalent-bonded-tokens the validator has instead of bonded tokens
|
||||
|
||||
|
||||
## 0.17.0 (May 15, 2018)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
@ -166,8 +166,8 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
x/stake/delegation.go
Normal file
33
x/stake/delegation.go
Normal file
@ -0,0 +1,33 @@
|
||||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Delegation represents the bond with tokens held by an account. It is
|
||||
// owned by one delegator, and is associated with the voting power of one
|
||||
// pubKey.
|
||||
// TODO better way of managing space
|
||||
type Delegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
func (b Delegation) equal(b2 Delegation) bool {
|
||||
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b.ValidatorAddr, b2.ValidatorAddr) &&
|
||||
b.Height == b2.Height &&
|
||||
b.Shares.Equal(b2.Shares)
|
||||
}
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Delegation = Delegation{}
|
||||
|
||||
// nolint - for sdk.Delegation
|
||||
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
|
||||
func (b Delegation) GetValidator() sdk.Address { return b.ValidatorAddr }
|
||||
func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
|
||||
54
x/stake/genesis.go
Normal file
54
x/stake/genesis.go
Normal file
@ -0,0 +1,54 @@
|
||||
package stake
|
||||
|
||||
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
Validators []Validator `json:"validators"`
|
||||
Bonds []Delegation `json:"bonds"`
|
||||
}
|
||||
|
||||
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
|
||||
return GenesisState{
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: initialPool(),
|
||||
Params: defaultParams(),
|
||||
}
|
||||
}
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
k.setPool(ctx, data.Pool)
|
||||
k.setParams(ctx, data.Params)
|
||||
for _, validator := range data.Validators {
|
||||
k.setValidator(ctx, validator)
|
||||
}
|
||||
for _, bond := range data.Bonds {
|
||||
k.setDelegation(ctx, bond)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
validators := k.GetValidators(ctx, 32767)
|
||||
bonds := k.getBonds(ctx, 32767)
|
||||
return GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
bonds,
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,6 @@ const (
|
||||
GasUnbond int64 = 20
|
||||
)
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
|
||||
// NOTE msg already has validate basic run
|
||||
@ -35,8 +33,6 @@ func NewHandler(k Keeper) sdk.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// NewEndBlocker generates sdk.EndBlocker
|
||||
// Performs tick functionality
|
||||
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||
@ -48,34 +44,6 @@ func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// InitGenesis - store genesis parameters
|
||||
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||
k.setPool(ctx, data.Pool)
|
||||
k.setParams(ctx, data.Params)
|
||||
for _, validator := range data.Validators {
|
||||
k.setValidator(ctx, validator)
|
||||
}
|
||||
for _, bond := range data.Bonds {
|
||||
k.setDelegation(ctx, bond)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
validators := k.GetValidators(ctx, 32767)
|
||||
bonds := k.getBonds(ctx, 32767)
|
||||
return GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
bonds,
|
||||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
|
||||
powerIncreasing = true
|
||||
}
|
||||
// delete the old record in the power ordered list
|
||||
store.Delete(GetValidatorsBondedByPowerKey(oldValidator))
|
||||
store.Delete(GetValidatorsBondedByPowerKey(oldValidator, pool))
|
||||
}
|
||||
|
||||
// if already a validator, copy the old block height and counter, else set them
|
||||
@ -107,7 +107,7 @@ func (k Keeper) setValidator(ctx sdk.Context, validator Validator) {
|
||||
|
||||
// update the list ordered by voting power
|
||||
bz := k.cdc.MustMarshalBinary(validator)
|
||||
store.Set(GetValidatorsBondedByPowerKey(validator), bz)
|
||||
store.Set(GetValidatorsBondedByPowerKey(validator, pool), bz)
|
||||
|
||||
// add to the validators and return to update list if is already a validator and power is increasing
|
||||
if powerIncreasing && oldValidator.Status == sdk.Bonded {
|
||||
@ -143,8 +143,9 @@ func (k Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
|
||||
|
||||
// delete the old validator record
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
pool := k.getPool(store)
|
||||
store.Delete(GetValidatorKey(address))
|
||||
store.Delete(GetValidatorsBondedByPowerKey(validator))
|
||||
store.Delete(GetValidatorsBondedByPowerKey(validator, pool))
|
||||
|
||||
// delete from current and power weighted validator groups if the validator
|
||||
// exists and add validator with zero power to the validator updates
|
||||
|
||||
@ -13,14 +13,14 @@ import (
|
||||
//nolint
|
||||
var (
|
||||
// Keys for store prefixes
|
||||
ParamKey = []byte{0x00} // key for global parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for global parameters relating to staking
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPowerKey = []byte{0x03} // prefix for each key to a validator
|
||||
ParamKey = []byte{0x00} // key for global parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for global parameters relating to staking
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsByPowerKey = []byte{0x03} // prefix for each key to a validator
|
||||
TendermintUpdatesKey = []byte{0x04} // prefix for each key to a validator which is being updated
|
||||
ValidatorsBondedKey = []byte{0x05} // prefix for each key to bonded/actively validating validators
|
||||
DelegationKey = []byte{0x06} // prefix for each key to a delegator's bond
|
||||
IntraTxCounterKey = []byte{0x07} // key for block-local tx index
|
||||
ValidatorsBondedKey = []byte{0x05} // prefix for each key to bonded/actively validating validators
|
||||
DelegationKey = []byte{0x06} // prefix for each key to a delegator's bond
|
||||
IntraTxCounterKey = []byte{0x07} // key for block-local tx index
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
@ -31,8 +31,10 @@ func GetValidatorKey(addr sdk.Address) []byte {
|
||||
}
|
||||
|
||||
// get the key for the validator used in the power-store
|
||||
func GetValidatorsBondedByPowerKey(validator Validator) []byte {
|
||||
powerBytes := []byte(validator.BondedShares.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
func GetValidatorsBondedByPowerKey(validator Validator, pool Pool) []byte {
|
||||
|
||||
power := pool.EquivalentBondedShares(validator)
|
||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||
|
||||
// TODO ensure that the key will be a readable string.. probably should add seperators and have
|
||||
// heightBytes and counterBytes represent strings like powerBytes does
|
||||
|
||||
@ -39,7 +39,7 @@ func TestValidatorBasics(t *testing.T) {
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||
assert.False(t, found)
|
||||
UnbondedSharesassert.False(t, found)
|
||||
resVals := keeper.GetValidatorsBonded(ctx)
|
||||
assert.Zero(t, len(resVals))
|
||||
|
||||
@ -88,7 +88,7 @@ func TestValidatorBasics(t *testing.T) {
|
||||
}
|
||||
|
||||
// test how the validators are sorted, tests GetValidatorsBondedByPower
|
||||
func GetValidatorSorting(t *testing.T) {
|
||||
func GetValidatorSortingUnmixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// initialize some validators into the state
|
||||
@ -121,16 +121,15 @@ func GetValidatorSorting(t *testing.T) {
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(ValEq(t, validators[3], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], gotValidators[0]))
|
||||
|
||||
// test a decrease in voting power
|
||||
validators[3].BondedShares = sdk.NewRat(300)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(t, sdk.NewRat(300), gotValidators[0].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[3], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], gotValidators[1]))
|
||||
|
||||
// test equal voting power, different age
|
||||
validators[3].BondedShares = sdk.NewRat(200)
|
||||
@ -138,10 +137,8 @@ func GetValidatorSorting(t *testing.T) {
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(t, sdk.NewRat(200), gotValidators[0].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), gotValidators[1].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[3], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], gotValidators[1]))
|
||||
assert.Equal(t, int64(0), gotValidators[0].BondHeight, "%v", gotValidators)
|
||||
assert.Equal(t, int64(0), gotValidators[1].BondHeight, "%v", gotValidators)
|
||||
|
||||
@ -150,8 +147,8 @@ func GetValidatorSorting(t *testing.T) {
|
||||
keeper.setValidator(ctx, validators[4])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[3], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], gotValidators[1]))
|
||||
|
||||
// change in voting power of both validators, both still in v-set, no age change
|
||||
validators[3].BondedShares = sdk.NewRat(300)
|
||||
@ -163,14 +160,169 @@ func GetValidatorSorting(t *testing.T) {
|
||||
keeper.setValidator(ctx, validators[4])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[3], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[4], gotValidators[1]))
|
||||
}
|
||||
|
||||
func GetValidatorSortingMixed(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// now 2 max gotValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 1, 400, 200}
|
||||
|
||||
n := len(amts)
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].UnbondedShares = sdk.NewRat(amts[0])
|
||||
validators[1].UnbondedShares = sdk.NewRat(amts[1])
|
||||
validators[2].UnbondedShares = sdk.NewRat(amts[2])
|
||||
validators[3].BondedShares = sdk.NewRat(amts[3])
|
||||
validators[4].BondedShares = sdk.NewRat(amts[4])
|
||||
for i := range amts {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
}
|
||||
assert.Equal(t, sdk.Unbonded, keeper.GetValidator(ctx, addr[0]).Status)
|
||||
assert.Equal(t, sdk.Unbonded, keeper.GetValidator(ctx, addr[1]).Status)
|
||||
assert.Equal(t, sdk.Unbonded, keeper.GetValidator(ctx, addr[2]).Status)
|
||||
assert.Equal(t, sdk.Bonded, keeper.GetValidator(ctx, addr[3]).Status)
|
||||
assert.Equal(t, sdk.Bonded, keeper.GetValidator(ctx, addr[4]).Status)
|
||||
|
||||
// first make sure everything made it in to the gotValidator group
|
||||
gotValidators := keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, n, len(gotValidators))
|
||||
assert.Equal(t, sdk.NewRat(400), gotValidators[0].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(200), gotValidators[1].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(100), gotValidators[2].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(1), gotValidators[3].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(0), gotValidators[4].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[1].Address, gotValidators[2].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[2].Address, gotValidators[3].Address, "%v", gotValidators)
|
||||
assert.Equal(t, validators[0].Address, gotValidators[4].Address, "%v", gotValidators)
|
||||
}
|
||||
|
||||
// TODO seperate out into multiple tests
|
||||
func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// now 2 max gotValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
nMax := uint16(2)
|
||||
params.MaxValidators = nMax
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
amts := []int64{0, 100, 400, 400}
|
||||
var validators [5]Validator
|
||||
for i, amt := range amts {
|
||||
validators[i] = NewValidator(addrs[i], pks[i], Description{})
|
||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||
}
|
||||
validators[0].UnbondedShares = sdk.NewRat(amts[0])
|
||||
validators[1].UnbondedShares = sdk.NewRat(amts[1])
|
||||
validators[2].BondedShares = sdk.NewRat(amts[2])
|
||||
validators[3].BondedShares = sdk.NewRat(amts[3])
|
||||
for i := range amts {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
validators[0].UnbondedShares = sdk.NewRat(500)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
gotValidators := keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(gotValidators)))
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
|
||||
// validator 3 was set before validator 4
|
||||
assert.True(ValEq(t, validators[2], gotValidators[1]))
|
||||
|
||||
// A validator which leaves the gotValidator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
// ref https://github.com/cosmos/cosmos-sdk/issues/582#issuecomment-380757108
|
||||
validators[3].BondedShares = sdk.NewRat(401)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(gotValidators)))
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], gotValidators[1]))
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
|
||||
// validator 3 kicked out temporarily
|
||||
validators[3].BondedShares = sdk.NewRat(200)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(gotValidators)))
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], gotValidators[1]))
|
||||
|
||||
// validator 4 does not get spot back
|
||||
validators[3].BondedShares = sdk.NewRat(400)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, nMax, uint16(len(gotValidators)))
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], gotValidators[1]))
|
||||
validator, exists := keeper.GetValidator(ctx, validators[3].Address)
|
||||
require.Equal(t, exists, true)
|
||||
require.Equal(t, validator.BondHeight, int64(40))
|
||||
}
|
||||
|
||||
// TODO seperate out into multiple tests
|
||||
func TestValidatorBondHeight(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// now 2 max gotValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
|
||||
// initialize some validators into the state
|
||||
var validators [3]Validator
|
||||
validators[0] = NewValidator(addrs[0], pks[0], Description{})
|
||||
validators[0].BondedShares = sdk.NewRat(200)
|
||||
validators[0].DelegatorShares = sdk.NewRat(200)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
validators[1] = NewValidator(addrs[1], pks[1], Description{})
|
||||
validators[1].BondedShares = sdk.NewRat(100)
|
||||
validators[1].DelegatorShares = sdk.NewRat(100)
|
||||
validators[2] = NewValidator(addrs[2], pks[2], Description{})
|
||||
validators[2].UnbondedShares = sdk.NewRat(100)
|
||||
validators[2].DelegatorShares = sdk.NewRat(100)
|
||||
|
||||
////////////////////////////////////////
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should become bonded
|
||||
keeper.setValidator(ctx, validators[1])
|
||||
keeper.setValidator(ctx, validators[2])
|
||||
gotValidators := keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[1], gotValidators[1]))
|
||||
validators[1].BondedShares = sdk.NewRat(1100)
|
||||
validators[2].BondedShares = sdk.NewRat(1100)
|
||||
keeper.setValidator(ctx, validators[2])
|
||||
keeper.setValidator(ctx, validators[1])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, params.MaxValidators, uint16(len(gotValidators)))
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[2], gotValidators[1]))
|
||||
}
|
||||
|
||||
// XXX rename test
|
||||
func TestGetValidatorsEdgeCases2(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
// now 2 max gotValidators
|
||||
params := keeper.GetParams(ctx)
|
||||
params.MaxValidators = 2
|
||||
@ -187,89 +339,13 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
keeper.setValidator(ctx, validators[i])
|
||||
}
|
||||
|
||||
validators[0].BondedShares = sdk.NewRat(500)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
gotValidators := keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
// validator 3 was set before validator 4
|
||||
require.Equal(t, validators[2].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
|
||||
// A validator which leaves the gotValidator set due to a decrease in voting power,
|
||||
// then increases to the original voting power, does not get its spot back in the
|
||||
// case of a tie.
|
||||
// ref https://github.com/cosmos/cosmos-sdk/issues/582#issuecomment-380757108
|
||||
validators[3].BondedShares = sdk.NewRat(401)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
require.Equal(t, validators[3].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
ctx = ctx.WithBlockHeight(40)
|
||||
// validator 3 kicked out temporarily
|
||||
validators[3].BondedShares = sdk.NewRat(200)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
require.Equal(t, validators[2].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
// validator 4 does not get spot back
|
||||
validators[3].BondedShares = sdk.NewRat(400)
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
require.Equal(t, validators[2].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
validator, exists := keeper.GetValidator(ctx, validators[3].Address)
|
||||
require.Equal(t, exists, true)
|
||||
require.Equal(t, validator.BondHeight, int64(40))
|
||||
|
||||
// If two validators both increase to the same voting power in the same block,
|
||||
// the one with the first transaction should take precedence (become a gotValidator).
|
||||
// ref https://github.com/cosmos/cosmos-sdk/issues/582#issuecomment-381250392
|
||||
validators[0].BondedShares = sdk.NewRat(2000)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
validators[1].BondedShares = sdk.NewRat(1000)
|
||||
validators[2].BondedShares = sdk.NewRat(1000)
|
||||
keeper.setValidator(ctx, validators[1])
|
||||
keeper.setValidator(ctx, validators[2])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
require.Equal(t, validators[1].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
validators[1].BondedShares = sdk.NewRat(1100)
|
||||
validators[2].BondedShares = sdk.NewRat(1100)
|
||||
keeper.setValidator(ctx, validators[2])
|
||||
keeper.setValidator(ctx, validators[1])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, uint16(len(gotValidators)), params.MaxValidators)
|
||||
require.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
require.Equal(t, validators[2].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
|
||||
// reset assets / heights
|
||||
params.MaxValidators = 100
|
||||
keeper.setParams(ctx, params)
|
||||
validators[0].BondedShares = sdk.NewRat(0)
|
||||
validators[1].BondedShares = sdk.NewRat(100)
|
||||
validators[2].BondedShares = sdk.NewRat(1)
|
||||
validators[3].BondedShares = sdk.NewRat(300)
|
||||
validators[4].BondedShares = sdk.NewRat(200)
|
||||
ctx = ctx.WithBlockHeight(0)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
keeper.setValidator(ctx, validators[1])
|
||||
keeper.setValidator(ctx, validators[2])
|
||||
keeper.setValidator(ctx, validators[3])
|
||||
keeper.setValidator(ctx, validators[4])
|
||||
|
||||
// test a swap in voting power
|
||||
validators[0].BondedShares = sdk.NewRat(600)
|
||||
keeper.setValidator(ctx, validators[0])
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
gotValidators := keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(t, sdk.NewRat(600), gotValidators[0].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(300), gotValidators[1].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], gotValidators[1]))
|
||||
|
||||
// test the max gotValidators term
|
||||
params = keeper.GetParams(ctx)
|
||||
@ -278,10 +354,8 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||
keeper.setParams(ctx, params)
|
||||
gotValidators = keeper.GetValidatorsBondedByPower(ctx)
|
||||
require.Equal(t, len(gotValidators), n)
|
||||
assert.Equal(t, sdk.NewRat(600), gotValidators[0].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[0].Address, gotValidators[0].Address, "%v", gotValidators)
|
||||
assert.Equal(t, sdk.NewRat(300), gotValidators[1].BondedShares, "%v", gotValidators)
|
||||
assert.Equal(t, validators[3].Address, gotValidators[1].Address, "%v", gotValidators)
|
||||
assert.True(ValEq(t, validators[0], gotValidators[0]))
|
||||
assert.True(ValEq(t, validators[3], gotValidators[1]))
|
||||
}
|
||||
|
||||
// clear the tracked changes to the gotValidator set
|
||||
@ -343,8 +417,8 @@ func TestGetTendermintUpdates(t *testing.T) {
|
||||
require.Equal(t, 2, len(validators))
|
||||
assert.Equal(t, validators[0].abciValidator(keeper.cdc), updates[0])
|
||||
assert.Equal(t, validators[1].abciValidator(keeper.cdc), updates[1])
|
||||
assert.True(t, validators[0].equal(vals[1]))
|
||||
assert.True(t, validators[1].equal(vals[0]))
|
||||
assert.True(ValEq(t, validators[0], vals[1]))
|
||||
assert.True(ValEq(t, validators[1], vals[0]))
|
||||
|
||||
// test identical,
|
||||
// validator set: {c1, c3} -> {c1, c3}
|
||||
|
||||
36
x/stake/params.go
Normal file
36
x/stake/params.go
Normal file
@ -0,0 +1,36 @@
|
||||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Params defines the high level settings for staking
|
||||
type Params struct {
|
||||
InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate
|
||||
InflationMax sdk.Rat `json:"inflation_max"` // maximum inflation rate
|
||||
InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate
|
||||
GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms
|
||||
|
||||
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
|
||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
}
|
||||
|
||||
func (p Params) equal(p2 Params) bool {
|
||||
return p.InflationRateChange.Equal(p2.InflationRateChange) &&
|
||||
p.InflationMax.Equal(p2.InflationMax) &&
|
||||
p.InflationMin.Equal(p2.InflationMin) &&
|
||||
p.GoalBonded.Equal(p2.GoalBonded) &&
|
||||
p.MaxValidators == p2.MaxValidators &&
|
||||
p.BondDenom == p2.BondDenom
|
||||
}
|
||||
|
||||
func defaultParams() Params {
|
||||
return Params{
|
||||
InflationRateChange: sdk.NewRat(13, 100),
|
||||
InflationMax: sdk.NewRat(20, 100),
|
||||
InflationMin: sdk.NewRat(7, 100),
|
||||
GoalBonded: sdk.NewRat(67, 100),
|
||||
MaxValidators: 100,
|
||||
BondDenom: "steak",
|
||||
}
|
||||
}
|
||||
202
x/stake/pool.go
202
x/stake/pool.go
@ -4,10 +4,59 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Pool - dynamic parameters of the current state
|
||||
type Pool struct {
|
||||
TotalSupply int64 `json:"total_supply"` // total supply of all tokens
|
||||
BondedShares sdk.Rat `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
|
||||
UnbondingShares sdk.Rat `json:"unbonding_shares"` // shares moving from Bonded to Unbonded Pool
|
||||
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
|
||||
BondedTokens int64 `json:"bonded_pool"` // reserve of bonded tokens
|
||||
UnbondingTokens int64 `json:"unbonding_pool"` // tokens moving from bonded to unbonded pool
|
||||
UnbondedTokens int64 `json:"unbonded_pool"` // reserve of unbonded tokens held with validators
|
||||
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
|
||||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
|
||||
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
|
||||
|
||||
// Fee Related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calcualtions
|
||||
}
|
||||
|
||||
func (p Pool) equal(p2 Pool) bool {
|
||||
return p.TotalSupply == p2.TotalSupply &&
|
||||
p.BondedShares.Equal(p2.BondedShares) &&
|
||||
p.UnbondedShares.Equal(p2.UnbondedShares) &&
|
||||
p.BondedTokens == p2.BondedTokens &&
|
||||
p.UnbondedTokens == p2.UnbondedTokens &&
|
||||
p.InflationLastTime == p2.InflationLastTime &&
|
||||
p.Inflation.Equal(p2.Inflation) &&
|
||||
p.DateLastCommissionReset == p2.DateLastCommissionReset &&
|
||||
p.PrevBondedShares.Equal(p2.PrevBondedShares)
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedTokens: 0,
|
||||
UnbondingTokens: 0,
|
||||
UnbondedTokens: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
DateLastCommissionReset: 0,
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// get the bond ratio of the global state
|
||||
func (p Pool) bondedRatio() sdk.Rat {
|
||||
if p.TotalSupply > 0 {
|
||||
return sdk.NewRat(p.BondedPool, p.TotalSupply)
|
||||
return sdk.NewRat(p.BondedTokens, p.TotalSupply)
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
@ -17,7 +66,7 @@ func (p Pool) bondedShareExRate() sdk.Rat {
|
||||
if p.BondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
|
||||
return sdk.NewRat(p.BondedTokens).Quo(p.BondedShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonding tokens held in validators per issued share
|
||||
@ -25,7 +74,7 @@ func (p Pool) unbondingShareExRate() sdk.Rat {
|
||||
if p.UnbondingShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondingPool).Quo(p.UnbondingShares)
|
||||
return sdk.NewRat(p.UnbondingTokens).Quo(p.UnbondingShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonded tokens held in validators per issued share
|
||||
@ -33,9 +82,56 @@ func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
||||
return sdk.NewRat(p.UnbondedTokens).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.BondedTokens += amount
|
||||
p.BondedShares = p.BondedShares.Add(issuedShares)
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.BondedShares = p.BondedShares.Sub(shares)
|
||||
p.BondedTokens = p.BondedTokens - removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.unbondingShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondingShares = p.UnbondingShares.Add(issuedShares)
|
||||
p.UnbondingTokens += amount
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.unbondingShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondingShares = p.UnbondingShares.Sub(shares)
|
||||
p.UnbondingTokens -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.unbondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
||||
p.UnbondedTokens += amount
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||
p.UnbondedTokens -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
// Validator Properties TODO move back to types.go under validator (maybe split types.go)
|
||||
|
||||
// XXX write test
|
||||
// update the location of the shares within a validator if its bond status has changed
|
||||
func (p Pool) UpdateSharesLocation(validator Validator) (Pool, Validator) {
|
||||
@ -71,67 +167,61 @@ func (p Pool) UpdateSharesLocation(validator Validator) (Pool, Validator) {
|
||||
return p, validator
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.bondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.BondedPool += amount
|
||||
p.BondedShares = p.BondedShares.Add(issuedShares)
|
||||
return p, issuedShares
|
||||
// XXX TEST
|
||||
// get the power or potential power for a validator
|
||||
// if bonded, the power is the BondedShares
|
||||
// if not bonded, the power is the amount of bonded shares which the
|
||||
// the validator would have it was bonded
|
||||
func (p Pool) EquivalentBondedShares(validator Validator) (power sdk.Rat) {
|
||||
switch validator.Status {
|
||||
case sdk.Bonded:
|
||||
power = validator.BondedShares
|
||||
case sdk.Unbonding:
|
||||
shares := validator.UnbondingShares // ubShr
|
||||
exRate := p.unbondingShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
power = shares.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
case sdk.Unbonded, sdk.Revoked:
|
||||
shares := validator.UnbondedShares // ubShr
|
||||
exRate := p.unbondedShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
power = shares.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.BondedShares = p.BondedShares.Sub(shares)
|
||||
p.BondedPool = p.BondedPool - removedTokens
|
||||
return p, removedTokens
|
||||
// get the equivalent amount of tokens contained by a validator
|
||||
func (p Pool) validatorTokens(v Validator) sdk.Rat {
|
||||
switch v.Status {
|
||||
case sdk.Bonded:
|
||||
return p.unbondedShareExRate().Mul(v.BondedShares) // (tokens/shares) * shares
|
||||
case sdk.Unbonding:
|
||||
return p.unbondedShareExRate().Mul(v.UnbondingShares)
|
||||
case sdk.Unbonded, sdk.Revoked:
|
||||
return p.unbondedShareExRate().Mul(v.UnbondedShares)
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.unbondingShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondingShares = p.UnbondingShares.Add(issuedShares)
|
||||
p.UnbondingPool += amount
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.unbondingShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondingShares = p.UnbondingShares.Sub(shares)
|
||||
p.UnbondingPool -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||
issuedShares = sdk.NewRat(amount).Quo(p.unbondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
|
||||
p.UnbondedPool += amount
|
||||
return p, issuedShares
|
||||
}
|
||||
|
||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
||||
p.UnbondedPool -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// XXX Audit this function further to make sure it's correct
|
||||
// add tokens to a validator
|
||||
func (p Pool) validatorAddTokens(validator Validator,
|
||||
amount int64) (p2 Pool, validator2 Validator, issuedDelegatorShares sdk.Rat) {
|
||||
|
||||
exRate := validator.DelegatorShareExRate()
|
||||
|
||||
var receivedGlobalShares sdk.Rat
|
||||
if validator.Status == sdk.Bonded {
|
||||
p, receivedGlobalShares = p.addTokensBonded(amount)
|
||||
} else {
|
||||
p, receivedGlobalShares = p.addTokensUnbonded(amount)
|
||||
var poolShares sdk.Rat
|
||||
switch validator.Status {
|
||||
case sdk.Bonded:
|
||||
p, poolShares = p.addTokensBonded(amount)
|
||||
validator.BondedShares = validator.BondedShares.Add(poolShares)
|
||||
case sdk.Unbonding:
|
||||
p, poolShares = p.addTokensUnbonding(amount)
|
||||
validator.UnbondingShares = validator.UnbondingShares.Add(poolShares)
|
||||
case sdk.Unbonded, sdk.Revoked:
|
||||
p, poolShares = p.addTokensUnbonded(amount)
|
||||
validator.UnbondedShares = validator.UnbondedShares.Add(poolShares)
|
||||
}
|
||||
validator.BondedShares = validator.BondedShares.Add(receivedGlobalShares)
|
||||
|
||||
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
||||
equivalentBondedShares := p.EquivalentBondedShares(validator)
|
||||
exRate := validator.DelegatorShareExRate(p) // eq-val-bonded-shares/delegator-shares
|
||||
issuedDelegatorShares = equivalentBondedShares.Quo(exRate)
|
||||
validator.DelegatorShares = validator.DelegatorShares.Add(issuedDelegatorShares)
|
||||
|
||||
return p, validator, issuedDelegatorShares
|
||||
@ -143,7 +233,7 @@ func (p Pool) validatorRemoveShares(validator Validator,
|
||||
|
||||
//exRate := validator.DelegatorShareExRate() //XXX make sure not used
|
||||
|
||||
globalPoolSharesToRemove := validator.DelegatorShareExRate().Mul(shares)
|
||||
globalPoolSharesToRemove := validator.DelegatorShareExRate(p).Mul(shares)
|
||||
if validator.Status == sdk.Bonded {
|
||||
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
|
||||
} else {
|
||||
|
||||
@ -52,7 +52,7 @@ func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
||||
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
|
||||
|
||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
|
||||
pool.BondedPool += provisions
|
||||
pool.BondedTokens += provisions
|
||||
pool.TotalSupply += provisions
|
||||
return pool
|
||||
}
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
package stake
|
||||
|
||||
// XXX test global state functions, validator exchange rate functions etc.
|
||||
@ -10,115 +10,6 @@ import (
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// GenesisState - all staking state that must be provided at genesis
|
||||
type GenesisState struct {
|
||||
Pool Pool `json:"pool"`
|
||||
Params Params `json:"params"`
|
||||
Validators []Validator `json:"validators"`
|
||||
Bonds []Delegation `json:"bonds"`
|
||||
}
|
||||
|
||||
func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState {
|
||||
return GenesisState{
|
||||
Pool: pool,
|
||||
Params: params,
|
||||
Validators: validators,
|
||||
Bonds: bonds,
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: initialPool(),
|
||||
Params: defaultParams(),
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// Params defines the high level settings for staking
|
||||
type Params struct {
|
||||
InflationRateChange sdk.Rat `json:"inflation_rate_change"` // maximum annual change in inflation rate
|
||||
InflationMax sdk.Rat `json:"inflation_max"` // maximum inflation rate
|
||||
InflationMin sdk.Rat `json:"inflation_min"` // minimum inflation rate
|
||||
GoalBonded sdk.Rat `json:"goal_bonded"` // Goal of percent bonded atoms
|
||||
|
||||
MaxValidators uint16 `json:"max_validators"` // maximum number of validators
|
||||
BondDenom string `json:"bond_denom"` // bondable coin denomination
|
||||
}
|
||||
|
||||
func (p Params) equal(p2 Params) bool {
|
||||
return p.InflationRateChange.Equal(p2.InflationRateChange) &&
|
||||
p.InflationMax.Equal(p2.InflationMax) &&
|
||||
p.InflationMin.Equal(p2.InflationMin) &&
|
||||
p.GoalBonded.Equal(p2.GoalBonded) &&
|
||||
p.MaxValidators == p2.MaxValidators &&
|
||||
p.BondDenom == p2.BondDenom
|
||||
}
|
||||
|
||||
func defaultParams() Params {
|
||||
return Params{
|
||||
InflationRateChange: sdk.NewRat(13, 100),
|
||||
InflationMax: sdk.NewRat(20, 100),
|
||||
InflationMin: sdk.NewRat(7, 100),
|
||||
GoalBonded: sdk.NewRat(67, 100),
|
||||
MaxValidators: 100,
|
||||
BondDenom: "steak",
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// Pool - dynamic parameters of the current state
|
||||
type Pool struct {
|
||||
TotalSupply int64 `json:"total_supply"` // total supply of all tokens
|
||||
BondedShares sdk.Rat `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
|
||||
UnbondingShares sdk.Rat `json:"unbonding_shares"` // shares moving from Bonded to Unbonded Pool
|
||||
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
|
||||
BondedPool int64 `json:"bonded_pool"` // reserve of bonded tokens
|
||||
UnbondingPool int64 `json:"unbonding_pool"` // tokens moving from bonded to unbonded pool
|
||||
UnbondedPool int64 `json:"unbonded_pool"` // reserve of unbonded tokens held with validators
|
||||
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
|
||||
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
|
||||
DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily)
|
||||
|
||||
// Fee Related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // last recorded bonded shares - for fee calcualtions
|
||||
}
|
||||
|
||||
func (p Pool) equal(p2 Pool) bool {
|
||||
return p.TotalSupply == p2.TotalSupply &&
|
||||
p.BondedShares.Equal(p2.BondedShares) &&
|
||||
p.UnbondedShares.Equal(p2.UnbondedShares) &&
|
||||
p.BondedPool == p2.BondedPool &&
|
||||
p.UnbondedPool == p2.UnbondedPool &&
|
||||
p.InflationLastTime == p2.InflationLastTime &&
|
||||
p.Inflation.Equal(p2.Inflation) &&
|
||||
p.DateLastCommissionReset == p2.DateLastCommissionReset &&
|
||||
p.PrevBondedShares.Equal(p2.PrevBondedShares)
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedPool: 0,
|
||||
UnbondingPool: 0,
|
||||
UnbondedPool: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
DateLastCommissionReset: 0,
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// Validator defines the total amount of bond shares and their exchange rate to
|
||||
// coins. Accumulation of interest is modelled as an in increase in the
|
||||
// exchange rate, and slashing as a decrease. When coins are delegated to this
|
||||
@ -163,6 +54,8 @@ func NewValidator(address sdk.Address, pubKey crypto.PubKey, description Descrip
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
DelegatorShares: sdk.ZeroRat(),
|
||||
Description: description,
|
||||
BondHeight: int64(0),
|
||||
@ -176,6 +69,7 @@ func NewValidator(address sdk.Address, pubKey crypto.PubKey, description Descrip
|
||||
}
|
||||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
func (v Validator) equal(c2 Validator) bool {
|
||||
return v.Status == c2.Status &&
|
||||
v.PubKey.Equals(c2.PubKey) &&
|
||||
@ -183,7 +77,7 @@ func (v Validator) equal(c2 Validator) bool {
|
||||
v.BondedShares.Equal(c2.BondedShares) &&
|
||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||
v.Description == c2.Description &&
|
||||
v.BondHeight == c2.BondHeight &&
|
||||
//v.BondHeight == c2.BondHeight &&
|
||||
//v.BondIntraTxCounter == c2.BondIntraTxCounter && // counter is always changing
|
||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||
v.Commission.Equal(c2.Commission) &&
|
||||
@ -215,19 +109,14 @@ func NewDescription(moniker, identity, website, details string) Description {
|
||||
}
|
||||
}
|
||||
|
||||
// get the exchange rate of global pool shares over delegator shares
|
||||
func (v Validator) DelegatorShareExRate() sdk.Rat {
|
||||
// get the exchange rate of tokens over delegator shares
|
||||
// UNITS: eq-val-bonded-shares/delegator-shares
|
||||
func (v Validator) DelegatorShareExRate(p Pool) sdk.Rat {
|
||||
if v.DelegatorShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
switch v.Status {
|
||||
case sdk.Bonded:
|
||||
return v.BondedShares.Quo(v.DelegatorShares)
|
||||
case sdk.Unbonding:
|
||||
return v.UnbondingShares.Quo(v.DelegatorShares)
|
||||
default: //sdk.Unbonded, sdk.Revoked:
|
||||
return v.UnbondedShares.Quo(v.DelegatorShares)
|
||||
}
|
||||
tokens := p.EquivalentBondedShares(v)
|
||||
return tokens.Quo(v.DelegatorShares)
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
@ -261,31 +150,3 @@ func (v Validator) GetAddress() sdk.Address { return v.Address }
|
||||
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
|
||||
func (v Validator) GetPower() sdk.Rat { return v.BondedShares }
|
||||
func (v Validator) GetBondHeight() int64 { return v.BondHeight }
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// Delegation represents the bond with tokens held by an account. It is
|
||||
// owned by one delegator, and is associated with the voting power of one
|
||||
// pubKey.
|
||||
// TODO better way of managing space
|
||||
type Delegation struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
ValidatorAddr sdk.Address `json:"validator_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
||||
|
||||
func (b Delegation) equal(b2 Delegation) bool {
|
||||
return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b.ValidatorAddr, b2.ValidatorAddr) &&
|
||||
b.Height == b2.Height &&
|
||||
b.Shares.Equal(b2.Shares)
|
||||
}
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Delegation = Delegation{}
|
||||
|
||||
// nolint - for sdk.Delegation
|
||||
func (b Delegation) GetDelegator() sdk.Address { return b.DelegatorAddr }
|
||||
func (b Delegation) GetValidator() sdk.Address { return b.ValidatorAddr }
|
||||
func (b Delegation) GetBondShares() sdk.Rat { return b.Shares }
|
||||
Loading…
Reference in New Issue
Block a user