keeper tests/revisions

This commit is contained in:
rigelrozanski
2018-03-28 19:01:50 +02:00
parent a4ab2fcf2f
commit 3a011678e7
15 changed files with 754 additions and 939 deletions
+54 -179
View File
@@ -6,63 +6,6 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
)
//nolint
var (
// Keys for store prefixes
CandidatesAddrKey = []byte{0x01} // key for all candidates' addresses
ParamKey = []byte{0x02} // key for global parameters relating to staking
GlobalStateKey = []byte{0x03} // key for global parameters relating to staking
// Key prefixes
CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate
ValidatorKeyPrefix = []byte{0x05} // prefix for each key to a candidate
ValidatorUpdatesKeyPrefix = []byte{0x06} // prefix for each key to a candidate
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
DelegatorBondsKeyPrefix = []byte{0x08} // prefix for each key to a delegator's bond
)
// XXX remove beggining word get from all these keys
// GetCandidateKey - get the key for the candidate with address
func GetCandidateKey(addr sdk.Address) []byte {
return append(CandidateKeyPrefix, addr.Bytes()...)
}
// GetValidatorKey - get the key for the validator used in the power-store
func GetValidatorKey(addr sdk.Address, power sdk.Rat, cdc *wire.Codec) []byte {
b, _ := cdc.MarshalBinary(power) // TODO need to handle error here?
return append(ValidatorKeyPrefix, append(b, addr.Bytes()...)...) // TODO does this need prefix if its in its own store
}
// GetValidatorUpdatesKey - get the key for the validator used in the power-store
func GetValidatorUpdatesKey(addr sdk.Address) []byte {
return append(ValidatorUpdatesKeyPrefix, addr.Bytes()...) // TODO does this need prefix if its in its own store
}
// GetDelegatorBondKey - get the key for delegator bond with candidate
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegatorBondKeyPrefix(delegatorAddr, cdc), candidateAddr.Bytes()...)
}
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
func GetDelegatorBondKeyPrefix(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res, err := cdc.MarshalBinary(&delegatorAddr)
if err != nil {
panic(err)
}
return append(DelegatorBondKeyPrefix, res...)
}
// GetDelegatorBondsKey - get the key for list of all the delegator's bonds
func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res, err := cdc.MarshalBinary(&delegatorAddr)
if err != nil {
panic(err)
}
return append(DelegatorBondsKeyPrefix, res...)
}
//___________________________________________________________________________
// keeper of the staking store
type Keeper struct {
storeKey sdk.StoreKey
@@ -70,7 +13,7 @@ type Keeper struct {
coinKeeper bank.CoinKeeper
//just caches
gs GlobalState
gs Pool
params Params
}
@@ -83,7 +26,8 @@ func NewKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, ck bank.CoinK
return keeper
}
//XXX load/save -> get/set
//_________________________________________________________________________
func (k Keeper) getCandidate(ctx sdk.Context, addr sdk.Address) (candidate Candidate, found bool) {
store := ctx.KVStore(k.storeKey)
b := store.Get(GetCandidateKey(addr))
@@ -100,7 +44,6 @@ func (k Keeper) getCandidate(ctx sdk.Context, addr sdk.Address) (candidate Candi
func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
store := ctx.KVStore(k.storeKey)
// XXX should only remove validator if we know candidate is a validator
k.removeValidator(ctx, candidate.Address)
validator := Validator{candidate.Address, candidate.VotingPower}
k.updateValidator(ctx, validator)
@@ -114,26 +57,34 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) {
func (k Keeper) removeCandidate(ctx sdk.Context, candidateAddr sdk.Address) {
store := ctx.KVStore(k.storeKey)
// XXX should only remove validator if we know candidate is a validator
k.removeValidator(ctx, candidateAddr)
store.Delete(GetCandidateKey(candidateAddr))
}
//___________________________________________________________________________
func (k Keeper) getCandidates(ctx sdk.Context, maxRetrieve int16) (candidates Candidates) {
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(subspace(CandidateKeyPrefix))
//func loadValidator(store sdk.KVStore, address sdk.Address, votingPower sdk.Rat) *Validator {
//b := store.Get(GetValidatorKey(address, votingPower))
//if b == nil {
//return nil
//}
//validator := new(Validator)
//err := cdc.UnmarshalBinary(b, validator)
//if err != nil {
//panic(err) // This error should never occur big problem if does
//}
//return validator
//}
candidates = make([]Candidate, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bz := iterator.Value()
var candidate Candidate
err := k.cdc.UnmarshalBinary(bz, &candidate)
if err != nil {
panic(err)
}
candidates[i] = candidate
iterator.Next()
}
return candidates[:i] // trim
}
//___________________________________________________________________________
// updateValidator - update a validator and create accumulate any changes
// in the changed validator substore
@@ -155,6 +106,8 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) {
func (k Keeper) removeValidator(ctx sdk.Context, address sdk.Address) {
store := ctx.KVStore(k.storeKey)
// XXX ensure that this record is a validator even?
//add validator with zero power to the validator updates
b, err := k.cdc.MarshalBinary(Validator{address, sdk.ZeroRat})
if err != nil {
@@ -193,7 +146,6 @@ func (k Keeper) getValidators(ctx sdk.Context, maxVal uint16) (validators []Vali
validators[i] = val
iterator.Next()
}
return
}
@@ -229,45 +181,6 @@ func (k Keeper) clearValidatorUpdates(ctx sdk.Context, maxVal int) {
iterator.Close()
}
//---------------------------------------------------------------------
// getCandidates - get the active list of all candidates
func (k Keeper) getCandidates(ctx sdk.Context) (candidates Candidates) {
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(subspace(CandidateKeyPrefix))
for ; iterator.Valid(); iterator.Next() {
candidateBytes := iterator.Value()
var candidate Candidate
err := k.cdc.UnmarshalBinary(candidateBytes, &candidate)
if err != nil {
panic(err)
}
candidates = append(candidates, candidate)
}
iterator.Close()
return candidates
}
//_____________________________________________________________________
// XXX use a store iterator here instead
//// load the pubkeys of all candidates a delegator is delegated too
//func (k Keeper) getDelegatorCandidates(ctx sdk.Context, delegator sdk.Address) (candidateAddrs []sdk.Address) {
//store := ctx.KVStore(k.storeKey)
//candidateBytes := store.Get(GetDelegatorBondsKey(delegator, k.cdc))
//if candidateBytes == nil {
//return nil
//}
//err := k.cdc.UnmarshalBinary(candidateBytes, &candidateAddrs)
//if err != nil {
//panic(err)
//}
//return
//}
//_____________________________________________________________________
func (k Keeper) getDelegatorBond(ctx sdk.Context,
@@ -288,20 +201,6 @@ func (k Keeper) getDelegatorBond(ctx sdk.Context,
func (k Keeper) setDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
store := ctx.KVStore(k.storeKey)
// XXX use store iterator
// if a new bond add to the list of bonds
//if k.getDelegatorBond(delegator, bond.Address) == nil {
//pks := k.getDelegatorCandidates(delegator)
//pks = append(pks, bond.Address)
//b, err := k.cdc.MarshalBinary(pks)
//if err != nil {
//panic(err)
//}
//store.Set(GetDelegatorBondsKey(delegator, k.cdc), b)
//}
// now actually save the bond
b, err := k.cdc.MarshalBinary(bond)
if err != nil {
panic(err)
@@ -311,25 +210,32 @@ func (k Keeper) setDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
func (k Keeper) removeDelegatorBond(ctx sdk.Context, bond DelegatorBond) {
store := ctx.KVStore(k.storeKey)
// XXX use store iterator
// TODO use list queries on multistore to remove iterations here!
// first remove from the list of bonds
//addrs := k.getDelegatorCandidates(delegator)
//for i, addr := range addrs {
//if bytes.Equal(candidateAddr, addr) {
//addrs = append(addrs[:i], addrs[i+1:]...)
//}
//}
//b, err := k.cdc.MarshalBinary(addrs)
//if err != nil {
//panic(err)
//}
//store.Set(GetDelegatorBondsKey(delegator, k.cdc), b)
// now remove the actual bond
store.Delete(GetDelegatorBondKey(bond.DelegatorAddr, bond.CandidateAddr, k.cdc))
//updateDelegatorBonds(store, delegator) //XXX remove?
}
// load all bonds of a delegator
func (k Keeper) getDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
store := ctx.KVStore(k.storeKey)
delegatorPrefixKey := GetDelegatorBondsKey(delegator, k.cdc)
iterator := store.Iterator(subspace(delegatorPrefixKey)) //smallest to largest
bonds = make([]DelegatorBond, maxRetrieve)
i := 0
for ; ; i++ {
if !iterator.Valid() || i > int(maxRetrieve-1) {
iterator.Close()
break
}
bondBytes := iterator.Value()
var bond DelegatorBond
err := k.cdc.UnmarshalBinary(bondBytes, &bond)
if err != nil {
panic(err)
}
bonds[i] = bond
iterator.Next()
}
return bonds[:i] // trim
}
//_______________________________________________________________________
@@ -349,7 +255,7 @@ func (k Keeper) getParams(ctx sdk.Context) (params Params) {
err := k.cdc.UnmarshalBinary(b, &params)
if err != nil {
panic(err) // This error should never occur big problem if does
panic(err)
}
return
}
@@ -362,34 +268,3 @@ func (k Keeper) setParams(ctx sdk.Context, params Params) {
store.Set(ParamKey, b)
k.params = Params{} // clear the cache
}
//_______________________________________________________________________
// XXX nothing is this Keeper should return a pointer...!!!!!!
// load/save the global staking state
func (k Keeper) getGlobalState(ctx sdk.Context) (gs GlobalState) {
// check if cached before anything
if k.gs != (GlobalState{}) {
return k.gs
}
store := ctx.KVStore(k.storeKey)
b := store.Get(GlobalStateKey)
if b == nil {
return initialGlobalState()
}
err := k.cdc.UnmarshalBinary(b, &gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
}
func (k Keeper) setGlobalState(ctx sdk.Context, gs GlobalState) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(gs)
if err != nil {
panic(err)
}
store.Set(GlobalStateKey, b)
k.gs = GlobalState{} // clear the cache
}
+51
View File
@@ -0,0 +1,51 @@
package stake
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
)
//nolint
var (
// Keys for store prefixes
CandidatesAddrKey = []byte{0x01} // key for all candidates' addresses
ParamKey = []byte{0x02} // key for global parameters relating to staking
PoolKey = []byte{0x03} // key for global parameters relating to staking
// Key prefixes
CandidateKeyPrefix = []byte{0x04} // prefix for each key to a candidate
ValidatorKeyPrefix = []byte{0x05} // prefix for each key to a candidate
ValidatorUpdatesKeyPrefix = []byte{0x06} // prefix for each key to a candidate
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
)
// XXX remove beggining word get from all these keys
// GetCandidateKey - get the key for the candidate with address
func GetCandidateKey(addr sdk.Address) []byte {
return append(CandidateKeyPrefix, addr.Bytes()...)
}
// GetValidatorKey - get the key for the validator used in the power-store
func GetValidatorKey(addr sdk.Address, power sdk.Rat, cdc *wire.Codec) []byte {
b, _ := cdc.MarshalBinary(power) // TODO need to handle error here?
return append(ValidatorKeyPrefix, append(b, addr.Bytes()...)...) // TODO does this need prefix if its in its own store
}
// GetValidatorUpdatesKey - get the key for the validator used in the power-store
func GetValidatorUpdatesKey(addr sdk.Address) []byte {
return append(ValidatorUpdatesKeyPrefix, addr.Bytes()...) // TODO does this need prefix if its in its own store
}
// GetDelegatorBondKey - get the key for delegator bond with candidate
func GetDelegatorBondKey(delegatorAddr, candidateAddr sdk.Address, cdc *wire.Codec) []byte {
return append(GetDelegatorBondsKey(delegatorAddr, cdc), candidateAddr.Bytes()...)
}
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
res, err := cdc.MarshalBinary(&delegatorAddr)
if err != nil {
panic(err)
}
return append(DelegatorBondKeyPrefix, res...)
}
+108 -57
View File
@@ -19,7 +19,7 @@ import (
//assert := assert.New(t)
//store := initTestStore(t)
//params := getParams(store)
//gs := getGlobalState(store)
//gs := getPool(store)
//N := 5
//actors := newAddrs(N)
@@ -27,19 +27,19 @@ import (
//// test a basic change in voting power
//candidates[0].Assets = sdk.NewRat(500)
//candidates.updateVotingPower(store, gs, params)
//candidates.updateVotingPower(store, p, params)
//assert.Equal(int64(500), candidates[0].VotingPower.Evaluate(), "%v", candidates[0])
//// test a swap in voting power
//candidates[1].Assets = sdk.NewRat(600)
//candidates.updateVotingPower(store, gs, params)
//candidates.updateVotingPower(store, p, params)
//assert.Equal(int64(600), candidates[0].VotingPower.Evaluate(), "%v", candidates[0])
//assert.Equal(int64(500), candidates[1].VotingPower.Evaluate(), "%v", candidates[1])
//// test the max validators term
//params.MaxValidators = 4
//setParams(store, params)
//candidates.updateVotingPower(store, gs, params)
//candidates.updateVotingPower(store, p, params)
//assert.Equal(int64(0), candidates[4].VotingPower.Evaluate(), "%v", candidates[4])
//}
@@ -118,7 +118,7 @@ import (
//testRemove(t, vs1[1], changed[0])
//testRemove(t, vs1[2], changed[1])
//// test many sdk of changes //vs2 = []Validator{v1, v3, v4, v5} //vs2[2].VotingPower = sdk.NewRat(11) //changed = vs1.validatorsUpdated(vs2) //require.Equal(4, len(changed), "%v", changed) // change 1, remove 1, add 2 //testRemove(t, vs1[1], changed[0]) //testChange(t, vs2[1], changed[1]) //testChange(t, vs2[2], changed[2]) //testChange(t, vs2[3], changed[3]) //} //func TestUpdateValidatorSet(t *testing.T) { //assert, require := assert.New(t), require.New(t) //store := initTestStore(t) //params := getParams(store) //gs := getGlobalState(store) //N := 5
//// test many sdk of changes //vs2 = []Validator{v1, v3, v4, v5} //vs2[2].VotingPower = sdk.NewRat(11) //changed = vs1.validatorsUpdated(vs2) //require.Equal(4, len(changed), "%v", changed) // change 1, remove 1, add 2 //testRemove(t, vs1[1], changed[0]) //testChange(t, vs2[1], changed[1]) //testChange(t, vs2[2], changed[2]) //testChange(t, vs2[3], changed[3]) //} //func TestUpdateValidatorSet(t *testing.T) { //assert, require := assert.New(t), require.New(t) //store := initTestStore(t) //params := getParams(store) //gs := getPool(store) //N := 5
//actors := newAddrs(N)
//candidates := candidatesFromActors(actors, []int64{400, 200, 100, 10, 1})
//for _, c := range candidates {
@@ -126,14 +126,14 @@ import (
//}
//// they should all already be validators
//change, err := UpdateValidatorSet(store, gs, params)
//change, err := UpdateValidatorSet(store, p, params)
//require.Nil(err)
//require.Equal(0, len(change), "%v", change) // change 1, remove 1, add 2
//// test the max value and test again
//params.MaxValidators = 4
//setParams(store, params)
//change, err = UpdateValidatorSet(store, gs, params)
//change, err = UpdateValidatorSet(store, p, params)
//require.Nil(err)
//require.Equal(1, len(change), "%v", change)
//testRemove(t, candidates[4].validator(), change[0])
@@ -149,7 +149,7 @@ import (
//for _, c := range candidates {
//setCandidate(store, c)
//}
//change, err = UpdateValidatorSet(store, gs, params)
//change, err = UpdateValidatorSet(store, p, params)
//require.Nil(err)
//require.Equal(5, len(change), "%v", change) // 3 changed, 1 added, 1 removed
//candidates = getCandidates(store)
@@ -170,25 +170,42 @@ import (
//assert.Equal(t, addrs[1], validators[1].Address)
//}
func TestState(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
var (
addrDel1 = addrs[0]
addrDel2 = addrs[1]
addrVal1 = addrs[2]
addrVal2 = addrs[3]
addrVal3 = addrs[4]
pk1 = crypto.GenPrivKeyEd25519().PubKey()
pk2 = crypto.GenPrivKeyEd25519().PubKey()
pk3 = crypto.GenPrivKeyEd25519().PubKey()
addrDel := sdk.Address([]byte("addressdelegator"))
addrVal := sdk.Address([]byte("addressvalidator"))
//pk := newPubKey("0B485CFC0EECC619440448436F8FC9DF40566F2369E72400281454CB552AFB57")
pk := crypto.GenPrivKeyEd25519().PubKey()
//----------------------------------------------------------------------
// Candidate checks
// XXX expand to include both liabilities and assets use/test all candidate fields
candidate := Candidate{
Address: addrVal,
PubKey: pk,
candidate1 = Candidate{
Address: addrVal1,
PubKey: pk1,
Assets: sdk.NewRat(9),
Liabilities: sdk.NewRat(9),
VotingPower: sdk.ZeroRat,
}
candidate2 = Candidate{
Address: addrVal2,
PubKey: pk2,
Assets: sdk.NewRat(9),
Liabilities: sdk.NewRat(9),
VotingPower: sdk.ZeroRat,
}
candidate3 = Candidate{
Address: addrVal3,
PubKey: pk3,
Assets: sdk.NewRat(9),
Liabilities: sdk.NewRat(9),
VotingPower: sdk.ZeroRat,
}
)
// XXX expand to include both liabilities and assets use/test all candidate1 fields
func TestCandidate(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
candidatesEqual := func(c1, c2 Candidate) bool {
return c1.Status == c2.Status &&
@@ -201,35 +218,40 @@ func TestState(t *testing.T) {
}
// check the empty keeper first
_, found := keeper.getCandidate(ctx, addrVal)
_, found := keeper.getCandidate(ctx, addrVal1)
assert.False(t, found)
resAddrs := keeper.getCandidates(ctx)
resAddrs := keeper.getCandidates(ctx, 100)
assert.Zero(t, len(resAddrs))
// set and retrieve a record
keeper.setCandidate(ctx, candidate)
resCand, found := keeper.getCandidate(ctx, addrVal)
keeper.setCandidate(ctx, candidate1)
resCand, found := keeper.getCandidate(ctx, addrVal1)
assert.True(t, found)
assert.True(t, candidatesEqual(candidate, resCand), "%v \n %v", resCand, candidate)
assert.True(t, candidatesEqual(candidate1, resCand), "%v \n %v", resCand, candidate1)
// modify a records, save, and retrieve
candidate.Liabilities = sdk.NewRat(99)
keeper.setCandidate(ctx, candidate)
resCand, found = keeper.getCandidate(ctx, addrVal)
candidate1.Liabilities = sdk.NewRat(99)
keeper.setCandidate(ctx, candidate1)
resCand, found = keeper.getCandidate(ctx, addrVal1)
assert.True(t, found)
assert.True(t, candidatesEqual(candidate, resCand))
assert.True(t, candidatesEqual(candidate1, resCand))
// also test that the address has been added to address list
resAddrs = keeper.getCandidates(ctx)
resAddrs = keeper.getCandidates(ctx, 100)
require.Equal(t, 1, len(resAddrs))
assert.Equal(t, addrVal, resAddrs[0].Address)
assert.Equal(t, addrVal1, resAddrs[0].Address)
//----------------------------------------------------------------------
// Bond checks
}
bond := DelegatorBond{
DelegatorAddr: addrDel,
CandidateAddr: addrVal,
func TestBond(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
// first add a candidate1 to delegate too
keeper.setCandidate(ctx, candidate1)
bond1to1 := DelegatorBond{
DelegatorAddr: addrDel1,
CandidateAddr: addrVal1,
Shares: sdk.NewRat(9),
}
@@ -239,36 +261,65 @@ func TestState(t *testing.T) {
b1.Shares == b2.Shares
}
//check the empty keeper first
_, found = keeper.getDelegatorBond(ctx, addrDel, addrVal)
// check the empty keeper first
_, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
assert.False(t, found)
//Set and retrieve a record
keeper.setDelegatorBond(ctx, bond)
resBond, found := keeper.getDelegatorBond(ctx, addrDel, addrVal)
// set and retrieve a record
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found := keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
assert.True(t, found)
assert.True(t, bondsEqual(bond, resBond))
assert.True(t, bondsEqual(bond1to1, resBond))
//modify a records, save, and retrieve
bond.Shares = sdk.NewRat(99)
keeper.setDelegatorBond(ctx, bond)
resBond, found = keeper.getDelegatorBond(ctx, addrDel, addrVal)
// modify a records, save, and retrieve
bond1to1.Shares = sdk.NewRat(99)
keeper.setDelegatorBond(ctx, bond1to1)
resBond, found = keeper.getDelegatorBond(ctx, addrDel1, addrVal1)
assert.True(t, found)
assert.True(t, bondsEqual(bond, resBond))
assert.True(t, bondsEqual(bond1to1, resBond))
//----------------------------------------------------------------------
// Param checks
// add some more records
keeper.setCandidate(ctx, candidate2)
keeper.setCandidate(ctx, candidate3)
bond1to2 := DelegatorBond{addrDel1, addrVal2, sdk.NewRat(9)}
bond1to3 := DelegatorBond{addrDel1, addrVal3, sdk.NewRat(9)}
bond2to1 := DelegatorBond{addrDel2, addrVal1, sdk.NewRat(9)}
bond2to2 := DelegatorBond{addrDel2, addrVal2, sdk.NewRat(9)}
bond2to3 := DelegatorBond{addrDel2, addrVal3, sdk.NewRat(9)}
keeper.setDelegatorBond(ctx, bond1to2)
keeper.setDelegatorBond(ctx, bond1to3)
keeper.setDelegatorBond(ctx, bond2to1)
keeper.setDelegatorBond(ctx, bond2to2)
keeper.setDelegatorBond(ctx, bond2to3)
keeper.setParams(ctx, defaultParams())
params := defaultParams()
// test all bond retrieve capabilities
resBonds := keeper.getDelegatorBonds(ctx, addrDel1, 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bondsEqual(bond1to1, resBonds[0]))
assert.True(t, bondsEqual(bond1to2, resBonds[1]))
assert.True(t, bondsEqual(bond1to3, resBonds[2]))
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 3)
require.Equal(t, 3, len(resBonds))
resBonds = keeper.getDelegatorBonds(ctx, addrDel1, 2)
require.Equal(t, 2, len(resBonds))
resBonds = keeper.getDelegatorBonds(ctx, addrDel2, 5)
require.Equal(t, 3, len(resBonds))
assert.True(t, bondsEqual(bond2to1, resBonds[0]))
assert.True(t, bondsEqual(bond2to2, resBonds[1]))
assert.True(t, bondsEqual(bond2to3, resBonds[2]))
}
func TestParams(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
expParams := defaultParams()
//check that the empty keeper loads the default
resParams := keeper.getParams(ctx)
assert.Equal(t, params, resParams)
assert.Equal(t, expParams, resParams)
//modify a params, save, and retrieve
params.MaxValidators = 777
keeper.setParams(ctx, params)
expParams.MaxValidators = 777
keeper.setParams(ctx, expParams)
resParams = keeper.getParams(ctx)
assert.Equal(t, params, resParams)
assert.Equal(t, expParams, resParams)
}
+54 -24
View File
@@ -4,6 +4,36 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// load/save the global staking state
func (k Keeper) getPool(ctx sdk.Context) (gs Pool) {
// check if cached before anything
if k.gs != (Pool{}) {
return k.gs
}
store := ctx.KVStore(k.storeKey)
b := store.Get(PoolKey)
if b == nil {
return initialPool()
}
err := k.cdc.UnmarshalBinary(b, &gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
}
func (k Keeper) setPool(ctx sdk.Context, p Pool) {
store := ctx.KVStore(k.storeKey)
b, err := k.cdc.MarshalBinary(p)
if err != nil {
panic(err)
}
store.Set(PoolKey, b)
k.gs = Pool{} // clear the cache
}
//_______________________________________________________________________
//TODO make these next two functions more efficient should be reading and writting to state ye know
// move a candidates asset pool from bonded to unbonded pool
@@ -29,38 +59,38 @@ func (k Keeper) unbondedToBondedPool(ctx sdk.Context, candidate Candidate) {
//_______________________________________________________________________
func (k Keeper) addTokensBonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
gs := k.getGlobalState(ctx)
issuedShares = gs.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
gs.BondedPool += amount
gs.BondedShares = gs.BondedShares.Add(issuedShares)
k.setGlobalState(ctx, gs)
p := k.getPool(ctx)
issuedShares = p.bondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
p.BondedPool += amount
p.BondedShares = p.BondedShares.Add(issuedShares)
k.setPool(ctx, p)
return
}
func (k Keeper) removeSharesBonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
gs := k.getGlobalState(ctx)
removedTokens = gs.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.BondedShares = gs.BondedShares.Sub(shares)
gs.BondedPool -= removedTokens
k.setGlobalState(ctx, gs)
p := k.getPool(ctx)
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
p.BondedShares = p.BondedShares.Sub(shares)
p.BondedPool -= removedTokens
k.setPool(ctx, p)
return
}
func (k Keeper) addTokensUnbonded(ctx sdk.Context, amount int64) (issuedShares sdk.Rat) {
gs := k.getGlobalState(ctx)
issuedShares = gs.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
gs.UnbondedShares = gs.UnbondedShares.Add(issuedShares)
gs.UnbondedPool += amount
k.setGlobalState(ctx, gs)
p := k.getPool(ctx)
issuedShares = p.unbondedShareExRate().Inv().Mul(sdk.NewRat(amount)) // (tokens/shares)^-1 * tokens
p.UnbondedShares = p.UnbondedShares.Add(issuedShares)
p.UnbondedPool += amount
k.setPool(ctx, p)
return
}
func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTokens int64) {
gs := k.getGlobalState(ctx)
removedTokens = gs.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.UnbondedShares = gs.UnbondedShares.Sub(shares)
gs.UnbondedPool -= removedTokens
k.setGlobalState(ctx, gs)
p := k.getPool(ctx)
removedTokens = p.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
p.UnbondedShares = p.UnbondedShares.Sub(shares)
p.UnbondedPool -= removedTokens
k.setPool(ctx, p)
return
}
@@ -69,7 +99,7 @@ func (k Keeper) removeSharesUnbonded(ctx sdk.Context, shares sdk.Rat) (removedTo
// add tokens to a candidate
func (k Keeper) candidateAddTokens(ctx sdk.Context, candidate Candidate, amount int64) (issuedDelegatorShares sdk.Rat) {
gs := k.getGlobalState(ctx)
p := k.getPool(ctx)
exRate := candidate.delegatorShareExRate()
var receivedGlobalShares sdk.Rat
@@ -82,14 +112,14 @@ func (k Keeper) candidateAddTokens(ctx sdk.Context, candidate Candidate, amount
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
k.setGlobalState(ctx, gs) // TODO cache GlobalState?
k.setPool(ctx, p) // TODO cache Pool?
return
}
// remove shares from a candidate
func (k Keeper) candidateRemoveShares(ctx sdk.Context, candidate Candidate, shares sdk.Rat) (createdCoins int64) {
gs := k.getGlobalState(ctx)
p := k.getPool(ctx)
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
@@ -100,6 +130,6 @@ func (k Keeper) candidateRemoveShares(ctx sdk.Context, candidate Candidate, shar
}
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
candidate.Liabilities = candidate.Liabilities.Sub(shares)
k.setGlobalState(ctx, gs) // TODO cache GlobalState?
k.setPool(ctx, p) // TODO cache Pool?
return
}
+22
View File
@@ -0,0 +1,22 @@
package stake
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestPool(t *testing.T) {
ctx, _, keeper := createTestInput(t, nil, false, 0)
expPool := initialPool()
//check that the empty keeper loads the default
resPool := keeper.getPool(ctx)
assert.Equal(t, expPool, resPool)
//modify a params, save, and retrieve
expPool.TotalSupply = 777
keeper.setPool(ctx, expPool)
resPool = keeper.getPool(ctx)
assert.Equal(t, expPool, resPool)
}
+2 -2
View File
@@ -90,8 +90,8 @@ func createTestInput(t *testing.T, sender sdk.Address, isCheckTx bool, initCoins
ck := bank.NewCoinKeeper(accountMapper)
keeper := NewKeeper(ctx, cdc, keyStake, ck)
params := paramsNoInflation()
keeper.setParams(ctx, params)
//params := paramsNoInflation()
params := keeper.getParams(ctx)
// fill all the addresses with some coins
for _, addr := range addrs {
+13 -13
View File
@@ -10,14 +10,14 @@ func Tick(ctx sdk.Context, k Keeper) (change []*abci.Validator, err error) {
// retrieve params
params := k.getParams(ctx)
gs := k.getGlobalState(ctx)
p := k.getPool(ctx)
height := ctx.BlockHeight()
// Process Validator Provisions
// XXX right now just process every 5 blocks, in new SDK make hourly
if gs.InflationLastTime+5 <= height {
gs.InflationLastTime = height
processProvisions(ctx, k, gs, params)
if p.InflationLastTime+5 <= height {
p.InflationLastTime = height
processProvisions(ctx, k, p, params)
}
newVals := k.getValidators(ctx, params.MaxValidators)
@@ -29,28 +29,28 @@ func Tick(ctx sdk.Context, k Keeper) (change []*abci.Validator, err error) {
var hrsPerYr = sdk.NewRat(8766) // as defined by a julian year of 365.25 days
// process provisions for an hour period
func processProvisions(ctx sdk.Context, k Keeper, gs GlobalState, params Params) {
func processProvisions(ctx sdk.Context, k Keeper, p Pool, params Params) {
gs.Inflation = nextInflation(gs, params).Round(1000000000)
p.Inflation = nextInflation(p, params).Round(1000000000)
// Because the validators hold a relative bonded share (`GlobalStakeShare`), when
// more bonded tokens are added proportionally to all validators the only term
// which needs to be updated is the `BondedPool`. So for each previsions cycle:
provisions := gs.Inflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr).Evaluate()
gs.BondedPool += provisions
gs.TotalSupply += provisions
provisions := p.Inflation.Mul(sdk.NewRat(p.TotalSupply)).Quo(hrsPerYr).Evaluate()
p.BondedPool += provisions
p.TotalSupply += provisions
// XXX XXX XXX XXX XXX XXX XXX XXX XXX
// XXX Mint them to the hold account
// XXX XXX XXX XXX XXX XXX XXX XXX XXX
// save the params
k.setGlobalState(ctx, gs)
k.setPool(ctx, p)
}
// get the next inflation rate for the hour
func nextInflation(gs GlobalState, params Params) (inflation sdk.Rat) {
func nextInflation(p Pool, params Params) (inflation sdk.Rat) {
// The target annual inflation rate is recalculated for each previsions cycle. The
// inflation is also subject to a rate change (positive of negative) depending or
@@ -59,11 +59,11 @@ func nextInflation(gs GlobalState, params Params) (inflation sdk.Rat) {
// 7% and 20%.
// (1 - bondedRatio/GoalBonded) * InflationRateChange
inflationRateChangePerYear := sdk.OneRat.Sub(gs.bondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
inflationRateChangePerYear := sdk.OneRat.Sub(p.bondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYr)
// increase the new annual inflation for this next cycle
inflation = gs.Inflation.Add(inflationRateChange)
inflation = p.Inflation.Add(inflationRateChange)
if inflation.GT(params.InflationMax) {
inflation = params.InflationMax
}
+23 -23
View File
@@ -11,7 +11,7 @@ package stake
//ctx, _, keeper := createTestInput(t, nil, false, 0)
//params := defaultParams()
//keeper.setParams(ctx, params)
//gs := keeper.getGlobalState(ctx)
//gs := keeper.getPool(ctx)
//// Governing Mechanism:
//// bondedRatio = BondedPool / TotalSupply
@@ -42,7 +42,7 @@ package stake
//{67, 100, sdk.NewRat(15, 100), sdk.ZeroRat},
//}
//for _, tc := range tests {
//gs.BondedPool, gs.TotalSupply = tc.setBondedPool, tc.setTotalSupply
//gs.BondedPool, p.TotalSupply = tc.setBondedPool, tc.setTotalSupply
//gs.Inflation = tc.setInflation
//inflation := nextInflation(gs, params)
@@ -57,7 +57,7 @@ package stake
//ctx, _, keeper := createTestInput(t, nil, false, 0)
//params := defaultParams()
//keeper.setParams(ctx, params)
//gs := keeper.getGlobalState(ctx)
//gs := keeper.getPool(ctx)
//// create some candidates some bonded, some unbonded
//candidates := candidatesFromAddrsEmpty(addrs)
@@ -75,42 +75,42 @@ package stake
//var unbondedShares int64 = 400000000
//// initial bonded ratio ~ 27%
//assert.True(t, gs.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", gs.bondedRatio())
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", p.bondedRatio())
//// Supplies
//assert.Equal(t, totalSupply, gs.TotalSupply)
//assert.Equal(t, bondedShares, gs.BondedPool)
//assert.Equal(t, unbondedShares, gs.UnbondedPool)
//assert.Equal(t, totalSupply, p.TotalSupply)
//assert.Equal(t, bondedShares, p.BondedPool)
//assert.Equal(t, unbondedShares, p.UnbondedPool)
//// test the value of candidate shares
//assert.True(t, gs.bondedShareExRate().Equal(sdk.OneRat), "%v", gs.bondedShareExRate())
//assert.True(t, p.bondedShareExRate().Equal(sdk.OneRat), "%v", p.bondedShareExRate())
//initialSupply := gs.TotalSupply
//initialUnbonded := gs.TotalSupply - gs.BondedPool
//initialSupply := p.TotalSupply
//initialUnbonded := p.TotalSupply - p.BondedPool
//// process the provisions a year
//for hr := 0; hr < 8766; hr++ {
//expInflation := nextInflation(gs, params).Round(1000000000)
//expProvisions := (expInflation.Mul(sdk.NewRat(gs.TotalSupply)).Quo(hrsPerYr)).Evaluate()
//startBondedPool := gs.BondedPool
//startTotalSupply := gs.TotalSupply
//processProvisions(ctx, keeper, gs, params)
//assert.Equal(t, startBondedPool+expProvisions, gs.BondedPool)
//assert.Equal(t, startTotalSupply+expProvisions, gs.TotalSupply)
//startBondedPool := p.BondedPool
//startTotalSupply := p.TotalSupply
//processProvisions(ctx, keeper, p, params)
//assert.Equal(t, startBondedPool+expProvisions, p.BondedPool)
//assert.Equal(t, startTotalSupply+expProvisions, p.TotalSupply)
//}
//assert.NotEqual(t, initialSupply, gs.TotalSupply)
//assert.Equal(t, initialUnbonded, gs.UnbondedPool)
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", gs.TotalSupply, gs.BondedPool, gs.TotalSupply-gs.BondedPool))
//assert.NotEqual(t, initialSupply, p.TotalSupply)
//assert.Equal(t, initialUnbonded, p.UnbondedPool)
////panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, p.TotalSupply-gs.BondedPool))
//// initial bonded ratio ~ 35% ~ 30% increase for bonded holders
//assert.True(t, gs.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", gs.bondedRatio())
//assert.True(t, p.bondedRatio().Equal(sdk.NewRat(105906511, 305906511)), "%v", p.bondedRatio())
//// global supply
//assert.Equal(t, int64(611813022), gs.TotalSupply)
//assert.Equal(t, int64(211813022), gs.BondedPool)
//assert.Equal(t, unbondedShares, gs.UnbondedPool)
//assert.Equal(t, int64(611813022), p.TotalSupply)
//assert.Equal(t, int64(211813022), p.BondedPool)
//assert.Equal(t, unbondedShares, p.UnbondedPool)
//// test the value of candidate shares
//assert.True(t, gs.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", gs.bondedShareExRate())
//assert.True(t, p.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", p.bondedShareExRate())
//}
+13 -13
View File
@@ -29,8 +29,8 @@ func defaultParams() Params {
//_________________________________________________________________________
// GlobalState - dynamic parameters of the current state
type GlobalState struct {
// 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
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
@@ -42,8 +42,8 @@ type GlobalState struct {
// XXX define globalstate interface?
func initialGlobalState() GlobalState {
return GlobalState{
func initialPool() Pool {
return Pool{
TotalSupply: 0,
BondedShares: sdk.ZeroRat,
UnbondedShares: sdk.ZeroRat,
@@ -55,27 +55,27 @@ func initialGlobalState() GlobalState {
}
// get the bond ratio of the global state
func (gs GlobalState) bondedRatio() sdk.Rat {
if gs.TotalSupply > 0 {
return sdk.NewRat(gs.BondedPool, gs.TotalSupply)
func (p Pool) bondedRatio() sdk.Rat {
if p.TotalSupply > 0 {
return sdk.NewRat(p.BondedPool, p.TotalSupply)
}
return sdk.ZeroRat
}
// get the exchange rate of bonded token per issued share
func (gs GlobalState) bondedShareExRate() sdk.Rat {
if gs.BondedShares.IsZero() {
func (p Pool) bondedShareExRate() sdk.Rat {
if p.BondedShares.IsZero() {
return sdk.OneRat
}
return sdk.NewRat(gs.BondedPool).Quo(gs.BondedShares)
return sdk.NewRat(p.BondedPool).Quo(p.BondedShares)
}
// get the exchange rate of unbonded tokens held in candidates per issued share
func (gs GlobalState) unbondedShareExRate() sdk.Rat {
if gs.UnbondedShares.IsZero() {
func (p Pool) unbondedShareExRate() sdk.Rat {
if p.UnbondedShares.IsZero() {
return sdk.OneRat
}
return sdk.NewRat(gs.UnbondedPool).Quo(gs.UnbondedShares)
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
}
//_______________________________________________________________________________________________________