Integrate in types and rational staking

This commit is contained in:
rigelrozanski 2018-01-31 21:56:46 -05:00
parent 73eb98ca7f
commit a1c7722a80
6 changed files with 568 additions and 174 deletions

55
glide.yaml Normal file
View File

@ -0,0 +1,55 @@
package: github.com/cosmos/cosmos-sdk
import:
- package: github.com/golang/protobuf
version: ^1.0.0
subpackages:
- proto
- package: github.com/bgentry/speakeasy
version: ^0.1.0
- package: github.com/mattn/go-isatty
version: ~0.0.3
- package: github.com/pkg/errors
version: ^0.8.0
- package: github.com/rigelrozanski/common
- package: github.com/tendermint/abci
version: develop
subpackages:
- server
- types
- package: github.com/tendermint/go-crypto
version: develop
- package: github.com/tendermint/go-wire
version: develop
- package: github.com/tendermint/iavl
version: develop
- package: github.com/tendermint/tmlibs
version: develop
subpackages:
- common
- db
- log
- logger
- merkle
- package: github.com/tendermint/tendermint
version: breaking/wire-sdk2
subpackages:
- cmd/tendermint/commands
- config
- lite
- rpc/client
- types
- package: golang.org/x/crypto
subpackages:
- ripemd160
- package: github.com/spf13/pflag
version: v1.0.0
- package: github.com/spf13/cobra
version: v0.0.1
- package: github.com/spf13/viper
version: ^1.0.0
testImport:
- package: github.com/stretchr/testify
version: ^1.2.1
subpackages:
- assert
- require

View File

@ -1,39 +1,54 @@
package stake
import (
"encoding/binary"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/rational"
"github.com/cosmos/cosmos-sdk/types"
)
// nolint
var (
// internal wire codec
cdc *wire.Codec
// Keys for store prefixes
CandidatesPubKeysKey = []byte{0x01} // key for all candidates' pubkeys
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
DelegatorBondKeyPrefix = []byte{0x06} // prefix for each key to a delegator's bond
DelegatorBondsKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
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
)
func init() {
cdc = wire.NewCodec()
cdc.RegisterInterface((*rational.Rational)(nil), nil)
cdc.RegisterConcrete(rational.Rat{}, "rat", nil)
}
// GetCandidateKey - get the key for the candidate with pubKey
func GetCandidateKey(pubKey crypto.PubKey) []byte {
return append(CandidateKeyPrefix, pubKey.Bytes()...)
}
// GetValidatorKey - get the key for the validator used in the power-store
func GetValidatorKey(pubKey crypto.PubKey, power int64) []byte {
b := make([]byte, 8)
binary.LittleEndian.PutUint64(b, uint64(power))
func GetValidatorKey(pubKey crypto.PubKey, power rational.Rational) []byte {
b, _ := cdc.MarshalJSON(power) // TODO need to handle error here?
return append(ValidatorKeyPrefix, append(b, pubKey.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(pubKey crypto.PubKey) []byte {
return append(ValidatorUpdatesKeyPrefix, pubKey.Bytes()...) // TODO does this need prefix if its in its own store
}
// GetDelegatorBondKey - get the key for delegator bond with candidate
func GetDelegatorBondKey(delegator crypto.Address, candidate crypto.PubKey) []byte {
return append(GetDelegatorBondKeyPrefix(delegator), candidate.Bytes()...)
@ -41,7 +56,7 @@ func GetDelegatorBondKey(delegator crypto.Address, candidate crypto.PubKey) []by
// GetDelegatorBondKeyPrefix - get the prefix for a delegator for all candidates
func GetDelegatorBondKeyPrefix(delegator crypto.Address) []byte {
res, err := cdc.MarshalBinary(&delegator)
res, err := cdc.MarshalJSON(&delegator)
if err != nil {
panic(err)
}
@ -50,7 +65,7 @@ func GetDelegatorBondKeyPrefix(delegator crypto.Address) []byte {
// GetDelegatorBondsKey - get the key for list of all the delegator's bonds
func GetDelegatorBondsKey(delegator crypto.Address) []byte {
res, err := cdc.MarshalBinary(&delegator)
res, err := cdc.MarshalJSON(&delegator)
if err != nil {
panic(err)
}
@ -60,15 +75,12 @@ func GetDelegatorBondsKey(delegator crypto.Address) []byte {
//---------------------------------------------------------------------
func loadCandidate(store types.KVStore, pubKey crypto.PubKey) *Candidate {
//if pubKey.Empty() {
//return nil
//}
b := store.Get(GetCandidateKey(pubKey))
if b == nil {
return nil
}
candidate := new(Candidate)
err := cdc.UnmarshalBinary(b, candidate)
err := cdc.UnmarshalJSON(b, candidate)
if err != nil {
panic(err) // This error should never occur big problem if does
}
@ -77,11 +89,12 @@ func loadCandidate(store types.KVStore, pubKey crypto.PubKey) *Candidate {
func saveCandidate(store types.KVStore, candidate *Candidate) {
removeValidatorFromKey(store, candidate.PubKey)
// XXX should only remove validator if we know candidate is a validator
removeValidator(store, candidate.PubKey)
validator := &Validator{candidate.PubKey, candidate.VotingPower}
saveValidator(store, validator)
updateValidator(store, validator)
b, err := cdc.MarshalBinary(*candidate)
b, err := cdc.MarshalJSON(*candidate)
if err != nil {
panic(err)
}
@ -89,54 +102,67 @@ func saveCandidate(store types.KVStore, candidate *Candidate) {
}
func removeCandidate(store types.KVStore, pubKey crypto.PubKey) {
removeValidatorFromKey(store, pubKey)
// XXX should only remove validator if we know candidate is a validator
removeValidator(store, pubKey)
store.Delete(GetCandidateKey(pubKey))
}
//---------------------------------------------------------------------
func loadValidator(store types.KVStore, pubKey crypto.PubKey, votingPower int64) *Validator {
b := store.Get(GetValidatorKey(pubKey, 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
}
//func loadValidator(store types.KVStore, pubKey crypto.PubKey, votingPower rational.Rational) *Validator {
//b := store.Get(GetValidatorKey(pubKey, votingPower))
//if b == nil {
//return nil
//}
//validator := new(Validator)
//err := cdc.UnmarshalJSON(b, validator)
//if err != nil {
//panic(err) // This error should never occur big problem if does
//}
//return validator
//}
func saveValidator(store types.KVStore, validator *Validator) {
b, err := cdc.MarshalBinary(*validator)
// updateValidator - update a validator and create accumulate any changes
// in the changed validator substore
func updateValidator(store types.KVStore, validator *Validator) {
b, err := cdc.MarshalJSON(*validator)
if err != nil {
panic(err)
}
// add to the validators to update list if necessary
store.Set(GetValidatorUpdatesKey(validator.PubKey), b)
// update the list ordered by voting power
store.Set(GetValidatorKey(validator.PubKey, validator.VotingPower), b)
}
func removeValidator(store types.KVStore, pubKey crypto.PubKey, votingPower int64) {
store.Delete(GetValidatorKey(pubKey, votingPower))
}
func removeValidator(store types.KVStore, pubKey crypto.PubKey) {
func removeValidatorFromKey(store types.KVStore, pubKey crypto.PubKey) {
// remove validator if already there, then add new validator
//add validator with zero power to the validator updates
b, err := cdc.MarshalJSON(Validator{pubKey, rational.Zero})
if err != nil {
panic(err)
}
store.Set(GetValidatorUpdatesKey(pubKey), b)
// now actually delete from the validator set
candidate := loadCandidate(store, pubKey)
if candidate != nil {
removeValidator(store, pubKey, candidate.VotingPower)
store.Delete(GetValidatorKey(pubKey, candidate.VotingPower))
}
}
// Validators - get the most recent updated validator set from the
// Candidates. These bonds are already sorted by VotingPower from
// the UpdateVotingPower function which is the only function which
// is to modify the VotingPower
func getValidators(store types.KVStore, maxVal int) Validators {
// get the most recent updated validator set from the Candidates. These bonds
// are already sorted by VotingPower from the UpdateVotingPower function which
// is the only function which is to modify the VotingPower
func getValidators(store types.KVStore, maxVal int) (validators []Validator) {
iterator := store.Iterator(subspace(ValidatorKeyPrefix)) //smallest to largest
validators := make(Validators, maxVal)
validators = make([]Validator, maxVal)
for i := 0; ; i++ {
if !iterator.Valid() || i > maxVal {
iterator.Close()
@ -144,7 +170,7 @@ func getValidators(store types.KVStore, maxVal int) Validators {
}
valBytes := iterator.Value()
var val Validator
err := cdc.UnmarshalBinary(valBytes, &val)
err := cdc.UnmarshalJSON(valBytes, &val)
if err != nil {
panic(err)
}
@ -152,7 +178,37 @@ func getValidators(store types.KVStore, maxVal int) Validators {
iterator.Next()
}
return validators
return
}
//---------------------------------------------------------------------
// get the most updated validators
func getValidatorUpdates(store types.KVStore) (updates []Validator) {
iterator := store.Iterator(subspace(ValidatorUpdatesKeyPrefix)) //smallest to largest
for ; iterator.Valid(); iterator.Next() {
valBytes := iterator.Value()
var val Validator
err := cdc.UnmarshalJSON(valBytes, &val)
if err != nil {
panic(err)
}
updates = append(updates, val)
}
iterator.Close()
return
}
// remove all validator update entries
func clearValidatorUpdates(store types.KVStore, maxVal int) {
iterator := store.Iterator(subspace(ValidatorUpdatesKeyPrefix))
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key()) // XXX write test for this, may need to be in a second loop
}
iterator.Close()
}
//---------------------------------------------------------------------
@ -160,26 +216,20 @@ func getValidators(store types.KVStore, maxVal int) Validators {
// loadCandidates - get the active list of all candidates TODO replace with multistore
func loadCandidates(store types.KVStore) (candidates Candidates) {
//iterator := store.Iterator(subspace(CandidateKeyPrefix)) //smallest to largest
//iterator := store.Iterator(CandidateKeyPrefix, []byte(nil)) //smallest to largest
iterator := store.Iterator([]byte{}, []byte(nil)) //smallest to largest
iterator := store.Iterator(subspace(CandidateKeyPrefix))
//iterator := store.Iterator(CandidateKeyPrefix, []byte(nil))
//iterator := store.Iterator([]byte{}, []byte(nil))
for i := 0; ; i++ {
if !iterator.Valid() {
//panic(fmt.Sprintf("debug i: %v\n", i))
iterator.Close()
break
}
for ; iterator.Valid(); iterator.Next() {
candidateBytes := iterator.Value()
var candidate Candidate
err := cdc.UnmarshalBinary(candidateBytes, &candidate)
err := cdc.UnmarshalJSON(candidateBytes, &candidate)
if err != nil {
panic(err)
}
candidates[i] = &candidate
iterator.Next()
candidates = append(candidates, &candidate)
}
iterator.Close()
return candidates
}
@ -194,7 +244,7 @@ func loadDelegatorCandidates(store types.KVStore,
return nil
}
err := cdc.UnmarshalBinary(candidateBytes, &candidates)
err := cdc.UnmarshalJSON(candidateBytes, &candidates)
if err != nil {
panic(err)
}
@ -212,7 +262,7 @@ func loadDelegatorBond(store types.KVStore,
}
bond := new(DelegatorBond)
err := cdc.UnmarshalBinary(delegatorBytes, bond)
err := cdc.UnmarshalJSON(delegatorBytes, bond)
if err != nil {
panic(err)
}
@ -225,7 +275,7 @@ func saveDelegatorBond(store types.KVStore, delegator crypto.Address, bond *Dele
if loadDelegatorBond(store, delegator, bond.PubKey) == nil {
pks := loadDelegatorCandidates(store, delegator)
pks = append(pks, (*bond).PubKey)
b, err := cdc.MarshalBinary(pks)
b, err := cdc.MarshalJSON(pks)
if err != nil {
panic(err)
}
@ -233,7 +283,7 @@ func saveDelegatorBond(store types.KVStore, delegator crypto.Address, bond *Dele
}
// now actually save the bond
b, err := cdc.MarshalBinary(*bond)
b, err := cdc.MarshalJSON(*bond)
if err != nil {
panic(err)
}
@ -250,7 +300,7 @@ func removeDelegatorBond(store types.KVStore, delegator crypto.Address, candidat
pks = append(pks[:i], pks[i+1:]...)
}
}
b, err := cdc.MarshalBinary(pks)
b, err := cdc.MarshalJSON(pks)
if err != nil {
panic(err)
}
@ -261,39 +311,6 @@ func removeDelegatorBond(store types.KVStore, delegator crypto.Address, candidat
//updateDelegatorBonds(store, delegator)
}
//func updateDelegatorBonds(store types.KVStore,
//delegator crypto.Address) {
//var bonds []*DelegatorBond
//prefix := GetDelegatorBondKeyPrefix(delegator)
//l := len(prefix)
//delegatorsBytes := store.List(prefix,
//append(prefix[:l-1], (prefix[l-1]+1)), loadParams(store).MaxVals)
//for _, delegatorBytesModel := range delegatorsBytes {
//delegatorBytes := delegatorBytesModel.Value
//if delegatorBytes == nil {
//continue
//}
//bond := new(DelegatorBond)
//err := wire.UnmarshalBinary(delegatorBytes, bond)
//if err != nil {
//panic(err)
//}
//bonds = append(bonds, bond)
//}
//if len(bonds) == 0 {
//store.Remove(GetDelegatorBondsKey(delegator))
//return
//}
//b := wire.MarshalBinary(bonds)
//store.Set(GetDelegatorBondsKey(delegator), b)
//}
//_______________________________________________________________________
// load/save the global staking params
@ -303,7 +320,7 @@ func loadParams(store types.KVStore) (params Params) {
return defaultParams()
}
err := cdc.UnmarshalBinary(b, &params)
err := cdc.UnmarshalJSON(b, &params)
if err != nil {
panic(err) // This error should never occur big problem if does
}
@ -311,7 +328,7 @@ func loadParams(store types.KVStore) (params Params) {
return
}
func saveParams(store types.KVStore, params Params) {
b, err := cdc.MarshalBinary(params)
b, err := cdc.MarshalJSON(params)
if err != nil {
panic(err)
}
@ -327,14 +344,14 @@ func loadGlobalState(store types.KVStore) (gs *GlobalState) {
return initialGlobalState()
}
gs = new(GlobalState)
err := cdc.UnmarshalBinary(b, gs)
err := cdc.UnmarshalJSON(b, gs)
if err != nil {
panic(err) // This error should never occur big problem if does
}
return
}
func saveGlobalState(store types.KVStore, gs *GlobalState) {
b, err := cdc.MarshalBinary(*gs)
b, err := cdc.MarshalJSON(*gs)
if err != nil {
panic(err)
}

View File

@ -4,25 +4,201 @@ import (
"bytes"
"testing"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/rational"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
)
func initTestStore(t *testing.T) sdk.KVStore {
// Capabilities key to access the main KVStore.
db, err := dbm.NewGoLevelDB("stake", "data")
require.Nil(t, err)
stakeStoreKey := sdk.NewKVStoreKey("stake")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(stakeStoreKey, sdk.StoreTypeIAVL, db)
ms.LoadLatestVersion()
return ms.GetKVStore(stakeStoreKey)
}
// XXX XXX XXX
// XXX revive these tests but for the store update proceedure
// XXX XXX XXX
//func TestUpdateVotingPower(t *testing.T) {
//assert := assert.New(t)
//store := initTestStore(t)
//params := loadParams(store)
//gs := loadGlobalState(store)
//N := 5
//actors := newAddrs(N)
//candidates := candidatesFromActors(actors, []int64{400, 200, 100, 10, 1})
//// test a basic change in voting power
//candidates[0].Assets = rational.New(500)
//candidates.updateVotingPower(store, gs, params)
//assert.Equal(int64(500), candidates[0].VotingPower.Evaluate(), "%v", candidates[0])
//// test a swap in voting power
//candidates[1].Assets = rational.New(600)
//candidates.updateVotingPower(store, gs, 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.MaxVals = 4
//saveParams(store, params)
//candidates.updateVotingPower(store, gs, params)
//assert.Equal(int64(0), candidates[4].VotingPower.Evaluate(), "%v", candidates[4])
//}
//func TestValidatorsChanged(t *testing.T) {
//require := require.New(t)
//v1 := (&Candidate{PubKey: pks[0], VotingPower: rational.New(10)}).validator()
//v2 := (&Candidate{PubKey: pks[1], VotingPower: rational.New(10)}).validator()
//v3 := (&Candidate{PubKey: pks[2], VotingPower: rational.New(10)}).validator()
//v4 := (&Candidate{PubKey: pks[3], VotingPower: rational.New(10)}).validator()
//v5 := (&Candidate{PubKey: pks[4], VotingPower: rational.New(10)}).validator()
//// test from nothing to something
//vs1 := []Validator{}
//vs2 := []Validator{v1, v2}
//changed := vs1.validatorsUpdated(vs2)
//require.Equal(2, len(changed))
//testChange(t, vs2[0], changed[0])
//testChange(t, vs2[1], changed[1])
//// test from something to nothing
//vs1 = []Validator{v1, v2}
//vs2 = []Validator{}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(2, len(changed))
//testRemove(t, vs1[0], changed[0])
//testRemove(t, vs1[1], changed[1])
//// test identical
//vs1 = []Validator{v1, v2, v4}
//vs2 = []Validator{v1, v2, v4}
//changed = vs1.validatorsUpdated(vs2)
//require.Zero(len(changed))
//// test single value change
//vs2[2].VotingPower = rational.One
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testChange(t, vs2[2], changed[0])
//// test multiple value change
//vs2[0].VotingPower = rational.New(11)
//vs2[2].VotingPower = rational.New(5)
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(2, len(changed))
//testChange(t, vs2[0], changed[0])
//testChange(t, vs2[2], changed[1])
//// test validator added at the beginning
//vs1 = []Validator{v2, v4}
//vs2 = []Validator{v2, v4, v1}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testChange(t, vs2[0], changed[0])
//// test validator added in the middle
//vs1 = []Validator{v1, v2, v4}
//vs2 = []Validator{v3, v1, v4, v2}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testChange(t, vs2[2], changed[0])
//// test validator added at the end
//vs2 = []Validator{v1, v2, v4, v5}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testChange(t, vs2[3], changed[0])
//// test multiple validators added
//vs2 = []Validator{v1, v2, v3, v4, v5}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(2, len(changed))
//testChange(t, vs2[2], changed[0])
//testChange(t, vs2[4], changed[1])
//// test validator removed at the beginning
//vs2 = []Validator{v2, v4}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testRemove(t, vs1[0], changed[0])
//// test validator removed in the middle
//vs2 = []Validator{v1, v4}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testRemove(t, vs1[1], changed[0])
//// test validator removed at the end
//vs2 = []Validator{v1, v2}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(1, len(changed))
//testRemove(t, vs1[2], changed[0])
//// test multiple validators removed
//vs2 = []Validator{v1}
//changed = vs1.validatorsUpdated(vs2)
//require.Equal(2, len(changed))
//testRemove(t, vs1[1], changed[0])
//testRemove(t, vs1[2], changed[1])
//// test many types of changes
//vs2 = []Validator{v1, v3, v4, v5}
//vs2[2].VotingPower = rational.New(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 := loadParams(store)
//gs := loadGlobalState(store)
//N := 5
//actors := newAddrs(N)
//candidates := candidatesFromActors(actors, []int64{400, 200, 100, 10, 1})
//for _, c := range candidates {
//saveCandidate(store, c)
//}
//// they should all already be validators
//change, err := UpdateValidatorSet(store, gs, 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.MaxVals = 4
//saveParams(store, params)
//change, err = UpdateValidatorSet(store, gs, params)
//require.Nil(err)
//require.Equal(1, len(change), "%v", change)
//testRemove(t, candidates[4].validator(), change[0])
//candidates = loadCandidates(store)
//assert.Equal(int64(0), candidates[4].VotingPower.Evaluate())
//// mess with the power's of the candidates and test
//candidates[0].Assets = rational.New(10)
//candidates[1].Assets = rational.New(600)
//candidates[2].Assets = rational.New(1000)
//candidates[3].Assets = rational.One
//candidates[4].Assets = rational.New(10)
//for _, c := range candidates {
//saveCandidate(store, c)
//}
//change, err = UpdateValidatorSet(store, gs, params)
//require.Nil(err)
//require.Equal(5, len(change), "%v", change) // 3 changed, 1 added, 1 removed
//candidates = loadCandidates(store)
//testChange(t, candidates[0].validator(), change[0])
//testChange(t, candidates[1].validator(), change[1])
//testChange(t, candidates[2].validator(), change[2])
//testRemove(t, candidates[3].validator(), change[3])
//testChange(t, candidates[4].validator(), change[4])
//}
func TestState(t *testing.T) {
assert, require := assert.New(t), require.New(t)
@ -45,9 +221,9 @@ func TestState(t *testing.T) {
candidate := &Candidate{
Owner: validator,
PubKey: pk,
Assets: 9, //rational.New(9),
Liabilities: 9, // rational.New(9),
VotingPower: 0, //rational.Zero,
Assets: rational.New(9),
Liabilities: rational.New(9),
VotingPower: rational.Zero,
}
candidatesEqual := func(c1, c2 *Candidate) bool {
@ -72,12 +248,11 @@ func TestState(t *testing.T) {
assert.True(candidatesEqual(candidate, resCand))
// modify a records, save, and retrieve
candidate.Liabilities = 99 //rational.New(99)
candidate.Liabilities = rational.New(99)
saveCandidate(store, candidate)
resCand = loadCandidate(store, pk)
assert.True(candidatesEqual(candidate, resCand))
store.Write()
// also test that the pubkey has been added to pubkey list
resPks = loadCandidates(store)
require.Equal(1, len(resPks))
@ -88,7 +263,7 @@ func TestState(t *testing.T) {
bond := &DelegatorBond{
PubKey: pk,
Shares: 9, // rational.New(9),
Shares: rational.New(9),
}
bondsEqual := func(b1, b2 *DelegatorBond) bool {
@ -106,7 +281,7 @@ func TestState(t *testing.T) {
assert.True(bondsEqual(bond, resBond))
//modify a records, save, and retrieve
bond.Shares = 99 //rational.New(99)
bond.Shares = rational.New(99)
saveDelegatorBond(store, delegator, bond)
resBond = loadDelegatorBond(store, delegator, pk)
assert.True(bondsEqual(bond, resBond))
@ -133,7 +308,7 @@ func TestGetValidators(t *testing.T) {
store := initTestStore(t)
N := 5
addrs := newAddrs(N)
candidatesFromActors(store, addrs, []int{400, 200, 0, 0, 0})
candidatesFromActors(store, addrs, []int64{400, 200, 0, 0, 0})
validators := getValidators(store, 5)
require.Equal(2, len(validators))

View File

@ -3,9 +3,15 @@ package stake
import (
"encoding/hex"
"fmt"
"testing"
"github.com/stretchr/testify/require"
crypto "github.com/tendermint/go-crypto"
dbm "github.com/tendermint/tmlibs/db"
"github.com/tendermint/tmlibs/rational"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -15,6 +21,17 @@ func subspace(prefix []byte) (start, end []byte) {
return
}
func initTestStore(t *testing.T) sdk.KVStore {
// Capabilities key to access the main KVStore.
db, err := dbm.NewGoLevelDB("stake", "data")
require.Nil(t, err)
stakeStoreKey := sdk.NewKVStoreKey("stake")
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(stakeStoreKey, sdk.StoreTypeIAVL, db)
ms.LoadLatestVersion()
return ms.GetKVStore(stakeStoreKey)
}
func newAddrs(n int) (addrs []crypto.Address) {
for i := 0; i < n; i++ {
addrs = append(addrs, []byte(fmt.Sprintf("addr%d", i)))
@ -49,15 +66,15 @@ var pks = []crypto.PubKey{
// NOTE: PubKey is supposed to be the binaryBytes of the crypto.PubKey
// instead this is just being set the address here for testing purposes
func candidatesFromActors(store sdk.KVStore, addrs []crypto.Address, amts []int) {
func candidatesFromActors(store sdk.KVStore, addrs []crypto.Address, amts []int64) {
for i := 0; i < len(addrs); i++ {
c := &Candidate{
Status: Unbonded,
PubKey: pks[i],
Owner: addrs[i],
Assets: int64(amts[i]), //rational.New(amts[i]),
Liabilities: int64(amts[i]), //rational.New(amts[i]),
VotingPower: int64(amts[i]), //rational.New(amts[i]),
Assets: rational.New(amts[i]),
Liabilities: rational.New(amts[i]),
VotingPower: rational.New(amts[i]),
}
saveCandidate(store, c)
}
@ -69,9 +86,9 @@ func candidatesFromActorsEmpty(addrs []crypto.Address) (candidates Candidates) {
Status: Unbonded,
PubKey: pks[i],
Owner: addrs[i],
Assets: 0, //rational.Zero,
Liabilities: 0, //rational.Zero,
VotingPower: 0, //rational.Zero,
Assets: rational.Zero,
Liabilities: rational.Zero,
VotingPower: rational.Zero,
}
candidates = append(candidates, c)
}

View File

@ -1,21 +1,20 @@
package stake
import (
abci "github.com/tendermint/abci/types"
crypto "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
"github.com/tendermint/tmlibs/rational"
)
var cdc = wire.NewCodec()
//nolint
// Params defines the high level settings for staking
type Params struct {
HoldBonded crypto.Address `json:"hold_bonded"` // account where all bonded coins are held
HoldUnbonded crypto.Address `json:"hold_unbonded"` // account where all delegated but unbonded coins are held
InflationRateChange int64 `json:"inflation_rate_change"` // XXX maximum annual change in inflation rate
InflationMax int64 `json:"inflation_max"` // XXX maximum inflation rate
InflationMin int64 `json:"inflation_min"` // XXX minimum inflation rate
GoalBonded int64 `json:"goal_bonded"` // XXX Goal of percent bonded atoms
InflationRateChange rational.Rational `json:"inflation_rate_change"` // maximum annual change in inflation rate
InflationMax rational.Rational `json:"inflation_max"` // maximum inflation rate
InflationMin rational.Rational `json:"inflation_min"` // minimum inflation rate
GoalBonded rational.Rational `json:"goal_bonded"` // Goal of percent bonded atoms
MaxVals uint16 `json:"max_vals"` // maximum number of validators
AllowedBondDenom string `json:"allowed_bond_denom"` // bondable coin denomination
@ -31,10 +30,10 @@ func defaultParams() Params {
return Params{
HoldBonded: []byte("77777777777777777777777777777777"),
HoldUnbonded: []byte("88888888888888888888888888888888"),
InflationRateChange: 13, //rational.New(13, 100),
InflationMax: 20, //rational.New(20, 100),
InflationMin: 7, //rational.New(7, 100),
GoalBonded: 67, //rational.New(67, 100),
InflationRateChange: rational.New(13, 100),
InflationMax: rational.New(20, 100),
InflationMin: rational.New(7, 100),
GoalBonded: rational.New(67, 100),
MaxVals: 100,
AllowedBondDenom: "fermion",
GasDeclareCandidacy: 20,
@ -44,15 +43,17 @@ func defaultParams() Params {
}
}
//_________________________________________________________________________
// GlobalState - dynamic parameters of the current state
type GlobalState struct {
TotalSupply int64 `json:"total_supply"` // total supply of all tokens
BondedShares int64 `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
UnbondedShares int64 `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
BondedPool int64 `json:"bonded_pool"` // reserve of bonded tokens
UnbondedPool int64 `json:"unbonded_pool"` // reserve of unbonded tokens held with candidates
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
Inflation int64 `json:"inflation"` // current annual inflation rate
TotalSupply int64 `json:"total_supply"` // total supply of all tokens
BondedShares rational.Rational `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
UnbondedShares rational.Rational `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
BondedPool int64 `json:"bonded_pool"` // reserve of bonded tokens
UnbondedPool int64 `json:"unbonded_pool"` // reserve of unbonded tokens held with candidates
InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time
Inflation rational.Rational `json:"inflation"` // current annual inflation rate
}
// XXX define globalstate interface?
@ -60,15 +61,69 @@ type GlobalState struct {
func initialGlobalState() *GlobalState {
return &GlobalState{
TotalSupply: 0,
BondedShares: 0, //rational.Zero,
UnbondedShares: 0, //rational.Zero,
BondedShares: rational.Zero,
UnbondedShares: rational.Zero,
BondedPool: 0,
UnbondedPool: 0,
InflationLastTime: 0,
Inflation: 0, //rational.New(7, 100),
Inflation: rational.New(7, 100),
}
}
// get the bond ratio of the global state
func (gs *GlobalState) bondedRatio() rational.Rational {
if gs.TotalSupply > 0 {
return rational.New(gs.BondedPool, gs.TotalSupply)
}
return rational.Zero
}
// get the exchange rate of bonded token per issued share
func (gs *GlobalState) bondedShareExRate() rational.Rational {
if gs.BondedShares.IsZero() {
return rational.One
}
return gs.BondedShares.Inv().Mul(rational.New(gs.BondedPool))
}
// get the exchange rate of unbonded tokens held in candidates per issued share
func (gs *GlobalState) unbondedShareExRate() rational.Rational {
if gs.UnbondedShares.IsZero() {
return rational.One
}
return gs.UnbondedShares.Inv().Mul(rational.New(gs.UnbondedPool))
}
func (gs *GlobalState) addTokensBonded(amount int64) (issuedShares rational.Rational) {
issuedShares = gs.bondedShareExRate().Inv().Mul(rational.New(amount)) // (tokens/shares)^-1 * tokens
gs.BondedPool += amount
gs.BondedShares = gs.BondedShares.Add(issuedShares)
return
}
func (gs *GlobalState) removeSharesBonded(shares rational.Rational) (removedTokens int64) {
removedTokens = gs.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.BondedShares = gs.BondedShares.Sub(shares)
gs.BondedPool -= removedTokens
return
}
func (gs *GlobalState) addTokensUnbonded(amount int64) (issuedShares rational.Rational) {
issuedShares = gs.unbondedShareExRate().Inv().Mul(rational.New(amount)) // (tokens/shares)^-1 * tokens
gs.UnbondedShares = gs.UnbondedShares.Add(issuedShares)
gs.UnbondedPool += amount
return
}
func (gs *GlobalState) removeSharesUnbonded(shares rational.Rational) (removedTokens int64) {
removedTokens = gs.unbondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
gs.UnbondedShares = gs.UnbondedShares.Sub(shares)
gs.UnbondedPool -= removedTokens
return
}
//_______________________________________________________________________________________________________
// CandidateStatus - status of a validator-candidate
type CandidateStatus byte
@ -87,23 +142,15 @@ const (
// exchange rate. Voting power can be calculated as total bonds multiplied by
// exchange rate.
type Candidate struct {
Status CandidateStatus `json:"status"` // Bonded status
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
Owner crypto.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here
Assets int64 `json:"assets"` // total shares of a global hold pools TODO custom type PoolShares
Liabilities int64 `json:"liabilities"` // total shares issued to a candidate's delegators TODO custom type DelegatorShares
VotingPower int64 `json:"voting_power"` // Voting power if considered a validator
Description Description `json:"description"` // Description terms for the candidate
Status CandidateStatus `json:"status"` // Bonded status
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
Owner crypto.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here
Assets rational.Rational `json:"assets"` // total shares of a global hold pools TODO custom type PoolShares
Liabilities rational.Rational `json:"liabilities"` // total shares issued to a candidate's delegators TODO custom type DelegatorShares
VotingPower rational.Rational `json:"voting_power"` // Voting power if considered a validator
Description Description `json:"description"` // Description terms for the candidate
}
//nolint
type Candidates []*Candidate
type Validator struct {
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
VotingPower int64 `json:"voting_power"` // Voting power if considered a validator
}
type Validators []Validator
// Description - description fields for a candidate
type Description struct {
Moniker string `json:"moniker"`
@ -118,15 +165,95 @@ func NewCandidate(pubKey crypto.PubKey, owner crypto.Address, description Descri
Status: Unbonded,
PubKey: pubKey,
Owner: owner,
Assets: 0, // rational.Zero,
Liabilities: 0, // rational.Zero,
VotingPower: 0, //rational.Zero,
Assets: rational.Zero,
Liabilities: rational.Zero,
VotingPower: rational.Zero,
Description: description,
}
}
//nolint
type DelegatorBond struct {
PubKey crypto.PubKey `json:"pub_key"`
Shares int64 `json:"shares"`
// XXX define candidate interface?
// get the exchange rate of global pool shares over delegator shares
func (c *Candidate) delegatorShareExRate() rational.Rational {
if c.Liabilities.IsZero() {
return rational.One
}
return c.Assets.Quo(c.Liabilities)
}
// add tokens to a candidate
func (c *Candidate) addTokens(amount int64, gs *GlobalState) (issuedDelegatorShares rational.Rational) {
exRate := c.delegatorShareExRate()
var receivedGlobalShares rational.Rational
if c.Status == Bonded {
receivedGlobalShares = gs.addTokensBonded(amount)
} else {
receivedGlobalShares = gs.addTokensUnbonded(amount)
}
c.Assets = c.Assets.Add(receivedGlobalShares)
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
c.Liabilities = c.Liabilities.Add(issuedDelegatorShares)
return
}
// remove shares from a candidate
func (c *Candidate) removeShares(shares rational.Rational, gs *GlobalState) (removedTokens int64) {
globalPoolSharesToRemove := c.delegatorShareExRate().Mul(shares)
if c.Status == Bonded {
removedTokens = gs.removeSharesBonded(globalPoolSharesToRemove)
} else {
removedTokens = gs.removeSharesUnbonded(globalPoolSharesToRemove)
}
c.Assets = c.Assets.Sub(globalPoolSharesToRemove)
c.Liabilities = c.Liabilities.Sub(shares)
return
}
// Validator returns a copy of the Candidate as a Validator.
// Should only be called when the Candidate qualifies as a validator.
func (c *Candidate) validator() Validator {
return Validator{
PubKey: c.PubKey,
VotingPower: c.VotingPower,
}
}
// Validator is one of the top Candidates
type Validator struct {
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
VotingPower rational.Rational `json:"voting_power"` // Voting power if considered a validator
}
// ABCIValidator - Get the validator from a bond value
func (v Validator) ABCIValidator() (*abci.Validator, error) {
pkBytes, err := cdc.MarshalBinary(v.PubKey)
if err != nil {
return nil, err
}
return &abci.Validator{
PubKey: pkBytes,
Power: v.VotingPower.Evaluate(),
}, nil
}
//_________________________________________________________________________
// Candidates - list of Candidates
type Candidates []*Candidate
//_________________________________________________________________________
// DelegatorBond 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.
type DelegatorBond struct {
PubKey crypto.PubKey `json:"pub_key"`
Shares rational.Rational `json:"shares"`
}

3
x/stake/types_test.go Normal file
View File

@ -0,0 +1,3 @@
package stake
// XXX test global state functions, candidate exchange rate functions etc.