Merge pull request #982 from cosmos/rigel/stake-refactor
Stake refactor // fee seperation
This commit is contained in:
commit
34f64752e4
31
CHANGELOG.md
31
CHANGELOG.md
|
@ -10,6 +10,24 @@ BUG FIXES
|
|||
|
||||
*TBD*
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
* [stake] candidate -> validator throughout (details in refactor comment)
|
||||
* [stake] delegate-bond -> delegation throughout
|
||||
* [stake] `gaiacli query validator` takes and argument instead of using the `--address-candidate` flag
|
||||
* [stake] introduce `gaiacli query delegations`
|
||||
* [stake] staking refactor
|
||||
* ValidatorsBonded store now take sorted pubKey-address instead of validator owner-address,
|
||||
is sorted like Tendermint by pk's address
|
||||
* store names more understandable
|
||||
* removed temporary ToKick store, just needs a local map!
|
||||
* removed distinction between candidates and validators
|
||||
* everything is now a validator
|
||||
* only validators with a status == bonded are actively validating/receiving rewards
|
||||
* Introduction of Unbonding fields, lowlevel logic throughout (not fully implemented with queue)
|
||||
* Introduction of PoolShares type within validators,
|
||||
replaces three rational fields (BondedShares, UnbondingShares, UnbondedShares
|
||||
|
||||
FEATURES
|
||||
|
||||
* [x/auth] Added ability to change pubkey to auth module
|
||||
|
@ -18,6 +36,17 @@ FEATURES
|
|||
* Transactions which run out of gas stop execution and revert state changes
|
||||
* A "simulate" query has been added to determine how much gas a transaction will need
|
||||
* Modules can include their own gas costs for execution of particular message types
|
||||
* [stake] Seperation of fee distribution to a new module
|
||||
* [stake] Creation of a validator/delegation generics in `/types`
|
||||
* [stake] Helper Description of the store in x/stake/store.md
|
||||
* [stake] removed use of caches in the stake keeper
|
||||
|
||||
BUG FIXES
|
||||
|
||||
* Auto-sequencing now works correctly
|
||||
* [stake] staking delegator shares exchange rate now relative to equivalent-bonded-tokens the validator has instead of bonded tokens
|
||||
^ this is important for unbonded validators in the power store!
|
||||
|
||||
|
||||
## 0.17.2
|
||||
|
||||
|
@ -34,6 +63,7 @@ Update to Tendermint v0.19.4 (fixes a consensus bug and improves logging)
|
|||
BREAKING CHANGES
|
||||
|
||||
* [stake] MarshalJSON -> MarshalBinary
|
||||
* Queries against the store must be prefixed with the path "/store"
|
||||
|
||||
FEATURES
|
||||
|
||||
|
@ -71,7 +101,6 @@ BREAKING CHANGES
|
|||
* gaiad init now requires use of `--name` flag
|
||||
* Removed Get from Msg interface
|
||||
* types/rational now extends big.Rat
|
||||
* Queries against the store must be prefixed with the path "/store"
|
||||
|
||||
FEATURES:
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
feed "github.com/cosmos/cosmos-sdk/x/fee_distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
@ -81,7 +82,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp {
|
|||
app.SetInitChainer(app.initChainer)
|
||||
app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper))
|
||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake)
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, stake.FeeHandler))
|
||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, feed.BurnFeeHandler))
|
||||
err := app.LoadLatestVersion(app.keyMain)
|
||||
if err != nil {
|
||||
cmn.Exit(err.Error())
|
||||
|
|
|
@ -105,7 +105,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
|||
|
||||
genesisState := GenesisState{
|
||||
Accounts: genaccs,
|
||||
StakeData: stake.GetDefaultGenesisState(),
|
||||
StakeData: stake.DefaultGenesisState(),
|
||||
}
|
||||
|
||||
stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState)
|
||||
|
@ -147,7 +147,7 @@ func setGenesisAccounts(gapp *GaiaApp, accs ...*auth.BaseAccount) error {
|
|||
|
||||
genesisState := GenesisState{
|
||||
Accounts: genaccs,
|
||||
StakeData: stake.GetDefaultGenesisState(),
|
||||
StakeData: stake.DefaultGenesisState(),
|
||||
}
|
||||
|
||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||
|
@ -405,9 +405,15 @@ func TestStakeMsgs(t *testing.T) {
|
|||
ctxDeliver := gapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res1 = gapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res1.GetCoins())
|
||||
candidate, found := gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
|
||||
validator, found := gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, candidate.Address, addr1)
|
||||
require.Equal(t, addr1, validator.Owner)
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
||||
|
||||
// check the bond that should have been created as well
|
||||
bond, found := gapp.stakeKeeper.GetDelegation(ctxDeliver, addr1, addr1)
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
|
||||
|
||||
// Edit Candidacy
|
||||
|
||||
|
@ -417,9 +423,9 @@ func TestStakeMsgs(t *testing.T) {
|
|||
)
|
||||
SignDeliver(t, gapp, editCandidacyMsg, []int64{1}, true, priv1)
|
||||
|
||||
candidate, found = gapp.stakeKeeper.GetCandidate(ctxDeliver, addr1)
|
||||
validator, found = gapp.stakeKeeper.GetValidator(ctxDeliver, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, candidate.Description, description)
|
||||
require.Equal(t, description, validator.Description)
|
||||
|
||||
// Delegate
|
||||
|
||||
|
@ -428,12 +434,13 @@ func TestStakeMsgs(t *testing.T) {
|
|||
)
|
||||
SignDeliver(t, gapp, delegateMsg, []int64{0}, true, priv2)
|
||||
|
||||
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||
require.Equal(t, genCoins.Minus(sdk.Coins{bondCoin}), res2.GetCoins())
|
||||
bond, found := gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
|
||||
bond, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
|
||||
require.True(t, found)
|
||||
require.Equal(t, bond.DelegatorAddr, addr2)
|
||||
require.Equal(t, addr2, bond.DelegatorAddr)
|
||||
require.Equal(t, addr1, bond.ValidatorAddr)
|
||||
require.True(sdk.RatEq(t, sdk.NewRat(10), bond.Shares))
|
||||
|
||||
// Unbond
|
||||
|
||||
|
@ -442,10 +449,9 @@ func TestStakeMsgs(t *testing.T) {
|
|||
)
|
||||
SignDeliver(t, gapp, unbondMsg, []int64{1}, true, priv2)
|
||||
|
||||
ctxDeliver = gapp.BaseApp.NewContext(false, abci.Header{})
|
||||
res2 = gapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||
require.Equal(t, genCoins, res2.GetCoins())
|
||||
_, found = gapp.stakeKeeper.GetDelegatorBond(ctxDeliver, addr2, addr1)
|
||||
_, found = gapp.stakeKeeper.GetDelegation(ctxDeliver, addr2, addr1)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
|
|||
}
|
||||
|
||||
// start with the default staking genesis state
|
||||
stakeData := stake.GetDefaultGenesisState()
|
||||
stakeData := stake.DefaultGenesisState()
|
||||
|
||||
// get genesis flag account information
|
||||
genaccs := make([]GenesisAccount, len(appGenTxs))
|
||||
|
@ -155,19 +155,18 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso
|
|||
}
|
||||
acc := NewGenesisAccount(&accAuth)
|
||||
genaccs[i] = acc
|
||||
stakeData.Pool.TotalSupply += freeFermionsAcc // increase the supply
|
||||
stakeData.Pool.LooseUnbondedTokens += freeFermionsAcc // increase the supply
|
||||
|
||||
// add the validator
|
||||
if len(genTx.Name) > 0 {
|
||||
desc := stake.NewDescription(genTx.Name, "", "", "")
|
||||
candidate := stake.NewCandidate(genTx.Address, genTx.PubKey, desc)
|
||||
candidate.Assets = sdk.NewRat(freeFermionVal)
|
||||
stakeData.Candidates = append(stakeData.Candidates, candidate)
|
||||
validator := stake.NewValidator(genTx.Address, genTx.PubKey, desc)
|
||||
validator.PoolShares = stake.NewBondedShares(sdk.NewRat(freeFermionVal))
|
||||
stakeData.Validators = append(stakeData.Validators, validator)
|
||||
|
||||
// pool logic
|
||||
stakeData.Pool.TotalSupply += freeFermionVal
|
||||
stakeData.Pool.BondedPool += freeFermionVal
|
||||
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedPool)
|
||||
stakeData.Pool.BondedTokens += freeFermionVal
|
||||
stakeData.Pool.BondedShares = sdk.NewRat(stakeData.Pool.BondedTokens)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
|
|||
assert.Equal(t, int64(7), barAcc.GetCoins().AmountOf("steak"))
|
||||
candidate := executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
|
||||
assert.Equal(t, candidate.Address.String(), barAddr)
|
||||
assert.Equal(t, int64(3), candidate.Assets.Evaluate())
|
||||
assert.Equal(t, int64(3), candidate.BondedShares.Evaluate())
|
||||
|
||||
// TODO timeout issues if not connected to the internet
|
||||
// unbond a single share
|
||||
|
@ -117,7 +117,7 @@ func TestGaiaCLIDeclareCandidacy(t *testing.T) {
|
|||
//barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barAddr, flags))
|
||||
//assert.Equal(t, int64(99998), barAcc.GetCoins().AmountOf("steak"))
|
||||
//candidate = executeGetCandidate(t, fmt.Sprintf("gaiacli candidate %v --address-candidate=%v", flags, barAddr))
|
||||
//assert.Equal(t, int64(2), candidate.Assets.Evaluate())
|
||||
//assert.Equal(t, int64(2), candidate.BondedShares.Evaluate())
|
||||
}
|
||||
|
||||
func executeWrite(t *testing.T, cmdStr string, writes ...string) {
|
||||
|
|
|
@ -45,10 +45,10 @@ func main() {
|
|||
rootCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)),
|
||||
stakecmd.GetCmdQueryCandidate("stake", cdc),
|
||||
stakecmd.GetCmdQueryCandidates("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegatorBond("stake", cdc),
|
||||
//stakecmd.GetCmdQueryDelegatorBonds("stake", cdc),
|
||||
stakecmd.GetCmdQueryValidator("stake", cdc),
|
||||
stakecmd.GetCmdQueryValidators("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegation("stake", cdc),
|
||||
stakecmd.GetCmdQueryDelegations("stake", cdc),
|
||||
)...)
|
||||
rootCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// "that's one big rat!"
|
||||
|
@ -80,17 +81,17 @@ func NewRatFromDecimal(decimalStr string) (f Rat, err Error) {
|
|||
}
|
||||
|
||||
//nolint
|
||||
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
|
||||
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
|
||||
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
|
||||
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 } // Equal - rationals are equal
|
||||
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // Equal - rationals are equal
|
||||
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // Equal - rationals are equal
|
||||
func (r Rat) Num() int64 { return r.Rat.Num().Int64() } // Num - return the numerator
|
||||
func (r Rat) Denom() int64 { return r.Rat.Denom().Int64() } // Denom - return the denominator
|
||||
func (r Rat) IsZero() bool { return r.Num() == 0 } // IsZero - Is the Rat equal to zero
|
||||
func (r Rat) Equal(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 0 }
|
||||
func (r Rat) GT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == 1 } // greater than
|
||||
func (r Rat) LT(r2 Rat) bool { return (&(r.Rat)).Cmp(&(r2.Rat)) == -1 } // less than
|
||||
func (r Rat) Mul(r2 Rat) Rat { return Rat{*new(big.Rat).Mul(&(r.Rat), &(r2.Rat))} } // Mul - multiplication
|
||||
func (r Rat) Quo(r2 Rat) Rat { return Rat{*new(big.Rat).Quo(&(r.Rat), &(r2.Rat))} } // Quo - quotient
|
||||
func (r Rat) Add(r2 Rat) Rat { return Rat{*new(big.Rat).Add(&(r.Rat), &(r2.Rat))} } // Add - addition
|
||||
func (r Rat) Sub(r2 Rat) Rat { return Rat{*new(big.Rat).Sub(&(r.Rat), &(r2.Rat))} } // Sub - subtraction
|
||||
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) } // Sub - subtraction
|
||||
func (r Rat) String() string { return fmt.Sprintf("%v/%v", r.Num(), r.Denom()) }
|
||||
|
||||
var (
|
||||
zero = big.NewInt(0)
|
||||
|
@ -168,3 +169,25 @@ func (r *Rat) UnmarshalAmino(text string) (err error) {
|
|||
r.Rat = *tempRat
|
||||
return nil
|
||||
}
|
||||
|
||||
//___________________________________________________________________________________
|
||||
// helpers
|
||||
|
||||
// test if two rat arrays are the equal
|
||||
func RatsEqual(r1s, r2s []Rat) bool {
|
||||
if len(r1s) != len(r2s) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, r1 := range r1s {
|
||||
if !r1.Equal(r2s[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// intended to be used with require/assert: require.True(RatEq(...))
|
||||
func RatEq(t *testing.T, exp, got Rat) (*testing.T, bool, string, Rat, Rat) {
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
}
|
||||
|
|
|
@ -276,3 +276,24 @@ func TestEmbeddedStructSerializationGoWire(t *testing.T) {
|
|||
assert.Equal(t, obj.Field2, obj2.Field2)
|
||||
assert.True(t, obj.Field3.Equal(obj2.Field3), "original: %v, unmarshalled: %v", obj, obj2)
|
||||
}
|
||||
|
||||
func TestRatsEqual(t *testing.T) {
|
||||
tests := []struct {
|
||||
r1s, r2s []Rat
|
||||
eq bool
|
||||
}{
|
||||
{[]Rat{NewRat(0)}, []Rat{NewRat(0)}, true},
|
||||
{[]Rat{NewRat(0)}, []Rat{NewRat(1)}, false},
|
||||
{[]Rat{NewRat(0)}, []Rat{}, false},
|
||||
{[]Rat{NewRat(0), NewRat(1)}, []Rat{NewRat(0), NewRat(1)}, true},
|
||||
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1), NewRat(0)}, true},
|
||||
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(0), NewRat(1)}, false},
|
||||
{[]Rat{NewRat(1), NewRat(0)}, []Rat{NewRat(1)}, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
assert.Equal(t, tc.eq, RatsEqual(tc.r1s, tc.r2s))
|
||||
assert.Equal(t, tc.eq, RatsEqual(tc.r2s, tc.r1s))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// status of a validator
|
||||
type BondStatus byte
|
||||
|
||||
// nolint
|
||||
const (
|
||||
Unbonded BondStatus = 0x00
|
||||
Unbonding BondStatus = 0x01
|
||||
Bonded BondStatus = 0x02
|
||||
)
|
||||
|
||||
// validator for a delegated proof of stake system
|
||||
type Validator interface {
|
||||
GetStatus() BondStatus // status of the validator
|
||||
GetOwner() Address // owner address to receive/return validators coins
|
||||
GetPubKey() crypto.PubKey // validation pubkey
|
||||
GetPower() Rat // validation power
|
||||
GetBondHeight() int64 // height in which the validator became active
|
||||
}
|
||||
|
||||
// validator which fulfills abci validator interface for use in Tendermint
|
||||
func ABCIValidator(v Validator) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: v.GetPubKey().Bytes(),
|
||||
Power: v.GetPower().Evaluate(),
|
||||
}
|
||||
}
|
||||
|
||||
// properties for the set of all validators
|
||||
type ValidatorSet interface {
|
||||
// iterate through validator by owner-address, execute func for each validator
|
||||
IterateValidators(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
// iterate through bonded validator by pubkey-address, execute func for each validator
|
||||
IterateValidatorsBonded(Context,
|
||||
func(index int64, validator Validator) (stop bool))
|
||||
|
||||
Validator(Context, Address) Validator // get a particular validator by owner address
|
||||
TotalPower(Context) Rat // total power of the validator set
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________
|
||||
|
||||
// delegation bond for a delegated proof of stake system
|
||||
type Delegation interface {
|
||||
GetDelegator() Address // delegator address for the bond
|
||||
GetValidator() Address // validator owner address for the bond
|
||||
GetBondShares() Rat // amount of validator's shares
|
||||
}
|
||||
|
||||
// properties for the set of all delegations for a particular
|
||||
type DelegationSet interface {
|
||||
|
||||
// iterate through all delegations from one delegator by validator-address,
|
||||
// execute func for each validator
|
||||
IterateDelegators(Context, delegator Address,
|
||||
fn func(index int64, delegation Delegation) (stop bool))
|
||||
}
|
|
@ -166,5 +166,4 @@ func deductFees(acc sdk.Account, fee sdk.StdFee) (sdk.Account, sdk.Result) {
|
|||
}
|
||||
|
||||
// BurnFeeHandler burns all fees (decreasing total supply)
|
||||
func BurnFeeHandler(ctx sdk.Context, tx sdk.Tx, fee sdk.Coins) {
|
||||
}
|
||||
func BurnFeeHandler(_ sdk.Context, _ sdk.Tx, _ sdk.Coins) {}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
package stake
|
||||
|
||||
//// keeper of the staking store
|
||||
//type Keeper struct {
|
||||
//storeKey sdk.StoreKey
|
||||
//cdc *wire.Codec
|
||||
//coinKeeper bank.Keeper
|
||||
|
||||
//// codespace
|
||||
//codespace sdk.CodespaceType
|
||||
//}
|
||||
|
||||
//func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
|
||||
//keeper := Keeper{
|
||||
//storeKey: key,
|
||||
//cdc: cdc,
|
||||
//coinKeeper: ck,
|
||||
//codespace: codespace,
|
||||
//}
|
||||
//return keeper
|
||||
//}
|
||||
|
||||
////_________________________________________________________________________
|
||||
|
||||
//// cummulative power of the non-absent prevotes
|
||||
//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Rat {
|
||||
//store := ctx.KVStore(k.storeKey)
|
||||
|
||||
//// get absent prevote indexes
|
||||
//absents := ctx.AbsentValidators()
|
||||
|
||||
//TotalPower := sdk.ZeroRat()
|
||||
//i := int32(0)
|
||||
//iterator := store.SubspaceIterator(ValidatorsBondedKey)
|
||||
//for ; iterator.Valid(); iterator.Next() {
|
||||
|
||||
//skip := false
|
||||
//for j, absentIndex := range absents {
|
||||
//if absentIndex > i {
|
||||
//break
|
||||
//}
|
||||
|
||||
//// if non-voting validator found, skip adding its power
|
||||
//if absentIndex == i {
|
||||
//absents = append(absents[:j], absents[j+1:]...) // won't need again
|
||||
//skip = true
|
||||
//break
|
||||
//}
|
||||
//}
|
||||
//if skip {
|
||||
//continue
|
||||
//}
|
||||
|
||||
//bz := iterator.Value()
|
||||
//var validator Validator
|
||||
//k.cdc.MustUnmarshalBinary(bz, &validator)
|
||||
//TotalPower = TotalPower.Add(validator.Power)
|
||||
//i++
|
||||
//}
|
||||
//iterator.Close()
|
||||
//return TotalPower
|
||||
//}
|
||||
|
||||
////_______________________________________________________________________
|
||||
|
||||
//// XXX TODO trim functionality
|
||||
|
||||
//// retrieve all the power changes which occur after a height
|
||||
//func (k Keeper) GetPowerChangesAfterHeight(ctx sdk.Context, earliestHeight int64) (pcs []PowerChange) {
|
||||
//store := ctx.KVStore(k.storeKey)
|
||||
|
||||
//iterator := store.SubspaceIterator(PowerChangeKey) //smallest to largest
|
||||
//for ; iterator.Valid(); iterator.Next() {
|
||||
//pcBytes := iterator.Value()
|
||||
//var pc PowerChange
|
||||
//k.cdc.MustUnmarshalBinary(pcBytes, &pc)
|
||||
//if pc.Height < earliestHeight {
|
||||
//break
|
||||
//}
|
||||
//pcs = append(pcs, pc)
|
||||
//}
|
||||
//iterator.Close()
|
||||
//return
|
||||
//}
|
||||
|
||||
//// set a power change
|
||||
//func (k Keeper) setPowerChange(ctx sdk.Context, pc PowerChange) {
|
||||
//store := ctx.KVStore(k.storeKey)
|
||||
//b := k.cdc.MustMarshalBinary(pc)
|
||||
//store.Set(GetPowerChangeKey(pc.Height), b)
|
||||
//}
|
|
@ -0,0 +1,31 @@
|
|||
package stake
|
||||
|
||||
//// test if is a gotValidator from the last update
|
||||
//func TestGetTotalPrecommitVotingPower(t *testing.T) {
|
||||
//ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
//amts := []int64{10000, 1000, 100, 10, 1}
|
||||
//var candidatesIn [5]Candidate
|
||||
//for i, amt := range amts {
|
||||
//candidatesIn[i] = NewCandidate(addrVals[i], pks[i], Description{})
|
||||
//candidatesIn[i].BondedShares = sdk.NewRat(amt)
|
||||
//candidatesIn[i].DelegatorShares = sdk.NewRat(amt)
|
||||
//keeper.setCandidate(ctx, candidatesIn[i])
|
||||
//}
|
||||
|
||||
//// test that an empty gotValidator set doesn't have any gotValidators
|
||||
//gotValidators := keeper.GetValidators(ctx)
|
||||
//assert.Equal(t, 5, len(gotValidators))
|
||||
|
||||
//totPow := keeper.GetTotalPrecommitVotingPower(ctx)
|
||||
//exp := sdk.NewRat(11111)
|
||||
//assert.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow)
|
||||
|
||||
//// set absent gotValidators to be the 1st and 3rd record sorted by pubKey address
|
||||
//ctx = ctx.WithAbsentValidators([]int32{1, 3})
|
||||
//totPow = keeper.GetTotalPrecommitVotingPower(ctx)
|
||||
|
||||
//// XXX verify that this order should infact exclude these two records
|
||||
//exp = sdk.NewRat(11100)
|
||||
//assert.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow)
|
||||
//}
|
|
@ -0,0 +1,72 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// burn burn burn
|
||||
func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {}
|
||||
|
||||
//// Handle fee distribution to the validators and delegators
|
||||
//func (k Keeper) FeeHandler(ctx sdk.Context, collectedFees sdk.Coins) {
|
||||
//pool := k.GetPool(ctx)
|
||||
//params := k.GetParams(ctx)
|
||||
|
||||
//// XXX determine
|
||||
//candidate := NewCandidate(addrs[0], pks[0], Description{})
|
||||
|
||||
//// calculate the proposer reward
|
||||
//precommitPower := k.GetTotalPrecommitVotingPower(ctx)
|
||||
//toProposer := coinsMulRat(collectedFees, (sdk.NewRat(1, 100).Add(sdk.NewRat(4, 100).Mul(precommitPower).Quo(pool.BondedShares))))
|
||||
//candidate.ProposerRewardPool = candidate.ProposerRewardPool.Plus(toProposer)
|
||||
|
||||
//toReservePool := coinsMulRat(collectedFees, params.ReservePoolFee)
|
||||
//pool.FeeReservePool = pool.FeeReservePool.Plus(toReservePool)
|
||||
|
||||
//distributedReward := (collectedFees.Minus(toProposer)).Minus(toReservePool)
|
||||
//pool.FeePool = pool.FeePool.Plus(distributedReward)
|
||||
//pool.FeeSumReceived = pool.FeeSumReceived.Plus(distributedReward)
|
||||
//pool.FeeRecent = distributedReward
|
||||
|
||||
//// lastly update the FeeRecent term
|
||||
//pool.FeeRecent = collectedFees
|
||||
|
||||
//k.setPool(ctx, pool)
|
||||
//}
|
||||
|
||||
//func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins {
|
||||
//var res sdk.Coins
|
||||
//for _, coin := range coins {
|
||||
//coinMulAmt := rat.Mul(sdk.NewRat(coin.Amount)).Evaluate()
|
||||
//coinMul := sdk.Coins{{coin.Denom, coinMulAmt}}
|
||||
//res = res.Plus(coinMul)
|
||||
//}
|
||||
//return res
|
||||
//}
|
||||
|
||||
////____________________________________________________________________________-
|
||||
|
||||
//// calculate adjustment changes for a candidate at a height
|
||||
//func CalculateAdjustmentChange(candidate Candidate, pool Pool, denoms []string, height int64) (Candidate, Pool) {
|
||||
|
||||
//heightRat := sdk.NewRat(height)
|
||||
//lastHeightRat := sdk.NewRat(height - 1)
|
||||
//candidateFeeCount := candidate.BondedShares.Mul(heightRat)
|
||||
//poolFeeCount := pool.BondedShares.Mul(heightRat)
|
||||
|
||||
//for i, denom := range denoms {
|
||||
//poolFeeSumReceived := sdk.NewRat(pool.FeeSumReceived.AmountOf(denom))
|
||||
//poolFeeRecent := sdk.NewRat(pool.FeeRecent.AmountOf(denom))
|
||||
//// calculate simple and projected pools
|
||||
//simplePool := candidateFeeCount.Quo(poolFeeCount).Mul(poolFeeSumReceived)
|
||||
//calc1 := candidate.PrevBondedShares.Mul(lastHeightRat).Quo(pool.PrevBondedShares.Mul(lastHeightRat)).Mul(poolFeeRecent)
|
||||
//calc2 := candidate.BondedShares.Quo(pool.BondedShares).Mul(poolFeeRecent)
|
||||
//projectedPool := calc1.Add(calc2)
|
||||
|
||||
//AdjustmentChange := simplePool.Sub(projectedPool)
|
||||
//candidate.FeeAdjustments[i] = candidate.FeeAdjustments[i].Add(AdjustmentChange)
|
||||
//pool.FeeAdjustments[i] = pool.FeeAdjustments[i].Add(AdjustmentChange)
|
||||
//}
|
||||
|
||||
//return candidate, pool
|
||||
//}
|
|
@ -0,0 +1,107 @@
|
|||
package stake
|
||||
|
||||
//// GenesisState - all staking state that must be provided at genesis
|
||||
//type GenesisState struct {
|
||||
//Pool Pool `json:"pool"`
|
||||
//Params Params `json:"params"`
|
||||
//}
|
||||
|
||||
//func NewGenesisState(pool Pool, params Params, candidates []Candidate, bonds []Delegation) GenesisState {
|
||||
//return GenesisState{
|
||||
//Pool: pool,
|
||||
//Params: params,
|
||||
//}
|
||||
//}
|
||||
|
||||
//// get raw genesis raw message for testing
|
||||
//func DefaultGenesisState() GenesisState {
|
||||
//return GenesisState{
|
||||
//Pool: initialPool(),
|
||||
//Params: defaultParams(),
|
||||
//}
|
||||
//}
|
||||
|
||||
//// fee information for a validator
|
||||
//type Validator struct {
|
||||
//Adjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
|
||||
//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
||||
//}
|
||||
|
||||
////_________________________________________________________________________
|
||||
|
||||
//// Params defines the high level settings for staking
|
||||
//type Params struct {
|
||||
//FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms
|
||||
//ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool
|
||||
//}
|
||||
|
||||
//func (p Params) equal(p2 Params) bool {
|
||||
//return p.BondDenom == p2.BondDenom &&
|
||||
//p.ReservePoolFee.Equal(p2.ReservePoolFee)
|
||||
//}
|
||||
|
||||
//func defaultParams() Params {
|
||||
//return Params{
|
||||
//FeeDenoms: []string{"steak"},
|
||||
//ReservePoolFee: sdk.NewRat(5, 100),
|
||||
//}
|
||||
//}
|
||||
|
||||
////_________________________________________________________________________
|
||||
|
||||
//// Pool - dynamic parameters of the current state
|
||||
//type Pool struct {
|
||||
//FeeReservePool sdk.Coins `json:"fee_reserve_pool"` // XXX reserve pool of collected fees for use by governance
|
||||
//FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed
|
||||
//FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"`
|
||||
//FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected
|
||||
//FeeAdjustments []sdk.Rat `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms
|
||||
//PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // XXX last recorded bonded shares
|
||||
//}
|
||||
|
||||
//func (p Pool) equal(p2 Pool) bool {
|
||||
//return p.FeeReservePool.IsEqual(p2.FeeReservePool) &&
|
||||
//p.FeePool.IsEqual(p2.FeePool) &&
|
||||
//p.FeeSumReceived.IsEqual(p2.FeeSumReceived) &&
|
||||
//p.FeeRecent.IsEqual(p2.FeeRecent) &&
|
||||
//sdk.RatsEqual(p.FeeAdjustments, p2.FeeAdjustments) &&
|
||||
//p.PrevBondedShares.Equal(p2.PrevBondedShares)
|
||||
//}
|
||||
|
||||
//// initial pool for testing
|
||||
//func initialPool() Pool {
|
||||
//return Pool{
|
||||
//FeeReservePool: sdk.Coins(nil),
|
||||
//FeePool: sdk.Coins(nil),
|
||||
//FeeSumReceived: sdk.Coins(nil),
|
||||
//FeeRecent: sdk.Coins(nil),
|
||||
//FeeAdjustments: []sdk.Rat{sdk.ZeroRat()},
|
||||
//PrevBondedShares: sdk.ZeroRat(),
|
||||
//}
|
||||
//}
|
||||
|
||||
////_________________________________________________________________________
|
||||
|
||||
//// Used in calculation of fee shares, added to a queue for each block where a power change occures
|
||||
//type PowerChange struct {
|
||||
//Height int64 `json:"height"` // block height at change
|
||||
//Power sdk.Rat `json:"power"` // total power at change
|
||||
//PrevPower sdk.Rat `json:"prev_power"` // total power at previous height-1
|
||||
//FeesIn sdk.Coins `json:"fees_in"` // fees in at block height
|
||||
//PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height
|
||||
//}
|
||||
|
||||
////_________________________________________________________________________
|
||||
//// KEY MANAGEMENT
|
||||
|
||||
//var (
|
||||
//// Keys for store prefixes
|
||||
//PowerChangeKey = []byte{0x09} // prefix for power change object
|
||||
//)
|
||||
|
||||
//// get the key for the accumulated update validators
|
||||
//func GetPowerChangeKey(height int64) []byte {
|
||||
//heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
//binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first)
|
||||
//return append(PowerChangeKey, heightBytes...)
|
||||
//}
|
|
@ -7,7 +7,7 @@ import (
|
|||
// nolint
|
||||
const (
|
||||
FlagAddressDelegator = "address-delegator"
|
||||
FlagAddressCandidate = "address-candidate"
|
||||
FlagAddressValidator = "address-validator"
|
||||
FlagPubKey = "pubkey"
|
||||
FlagAmount = "amount"
|
||||
FlagShares = "shares"
|
||||
|
@ -24,18 +24,18 @@ var (
|
|||
fsAmount = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsShares = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDescription = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsCandidate = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsValidator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fsDelegator = flag.NewFlagSet("", flag.ContinueOnError)
|
||||
)
|
||||
|
||||
func init() {
|
||||
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator-candidate. For Ed25519 the go-amino prepend hex is 1624de6220")
|
||||
fsPk.String(FlagPubKey, "", "Go-Amino encoded hex PubKey of the validator. For Ed25519 the go-amino prepend hex is 1624de6220")
|
||||
fsAmount.String(FlagAmount, "1steak", "Amount of coins to bond")
|
||||
fsShares.String(FlagShares, "", "Amount of shares to unbond, either in decimal or keyword MAX (ex. 1.23456789, 99, MAX)")
|
||||
fsDescription.String(FlagMoniker, "", "validator-candidate name")
|
||||
fsDescription.String(FlagMoniker, "", "validator name")
|
||||
fsDescription.String(FlagIdentity, "", "optional keybase signature")
|
||||
fsDescription.String(FlagWebsite, "", "optional website")
|
||||
fsDescription.String(FlagDetails, "", "optional details")
|
||||
fsCandidate.String(FlagAddressCandidate, "", "hex address of the validator/candidate")
|
||||
fsValidator.String(FlagAddressValidator, "", "hex address of the validator")
|
||||
fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator")
|
||||
}
|
||||
|
|
|
@ -15,51 +15,47 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// get the command to query a candidate
|
||||
func GetCmdQueryCandidate(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
// get the command to query a validator
|
||||
func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "candidate",
|
||||
Short: "Query a validator-candidate account",
|
||||
Use: "validator [owner-addr]",
|
||||
Short: "Query a validator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
addr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
addr, err := sdk.GetAddress(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key := stake.GetCandidateKey(addr)
|
||||
key := stake.GetValidatorKey(addr)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse out the candidate
|
||||
candidate := new(stake.Candidate)
|
||||
cdc.MustUnmarshalBinary(res, candidate)
|
||||
output, err := wire.MarshalJSONIndent(cdc, candidate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// parse out the validator
|
||||
validator := new(stake.Validator)
|
||||
cdc.MustUnmarshalBinary(res, validator)
|
||||
output, err := wire.MarshalJSONIndent(cdc, validator)
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
|
||||
// TODO output with proofs / machine parseable etc.
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a candidate
|
||||
func GetCmdQueryCandidates(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
// get the command to query a validator
|
||||
func GetCmdQueryValidators(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "candidates",
|
||||
Short: "Query for all validator-candidate accounts",
|
||||
Use: "validators",
|
||||
Short: "Query for all validators",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
key := stake.CandidatesKey
|
||||
key := stake.ValidatorsKey
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
|
@ -67,11 +63,11 @@ func GetCmdQueryCandidates(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
// parse out the candidates
|
||||
var candidates []stake.Candidate
|
||||
var candidates []stake.Validator
|
||||
for _, KV := range resKVs {
|
||||
var candidate stake.Candidate
|
||||
cdc.MustUnmarshalBinary(KV.Value, &candidate)
|
||||
candidates = append(candidates, candidate)
|
||||
var validator stake.Validator
|
||||
cdc.MustUnmarshalBinary(KV.Value, &validator)
|
||||
candidates = append(candidates, validator)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, candidates)
|
||||
|
@ -87,14 +83,14 @@ func GetCmdQueryCandidates(storeName string, cdc *wire.Codec) *cobra.Command {
|
|||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query a single delegator bond
|
||||
func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
// get the command to query a single delegation bond
|
||||
func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegator-bond",
|
||||
Short: "Query a delegators bond based on address and candidate pubkey",
|
||||
Use: "delegation",
|
||||
Short: "Query a delegations bond based on address and validator address",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
addr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
addr, err := sdk.GetAddress(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -103,9 +99,9 @@ func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delegator := crypto.Address(bz)
|
||||
delegation := crypto.Address(bz)
|
||||
|
||||
key := stake.GetDelegatorBondKey(delegator, addr, cdc)
|
||||
key := stake.GetDelegationKey(delegation, addr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
|
@ -113,7 +109,7 @@ func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command
|
|||
}
|
||||
|
||||
// parse out the bond
|
||||
bond := new(stake.DelegatorBond)
|
||||
bond := new(stake.Delegation)
|
||||
cdc.MustUnmarshalBinary(res, bond)
|
||||
output, err := wire.MarshalJSONIndent(cdc, bond)
|
||||
if err != nil {
|
||||
|
@ -126,23 +122,24 @@ func GetCmdQueryDelegatorBond(storeName string, cdc *wire.Codec) *cobra.Command
|
|||
},
|
||||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// get the command to query all the candidates bonded to a delegator
|
||||
func GetCmdQueryDelegatorBonds(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
// get the command to query all the candidates bonded to a delegation
|
||||
func GetCmdQueryDelegations(storeName string, cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegator-candidates",
|
||||
Short: "Query all delegators bonds based on delegator-address",
|
||||
Use: "delegations [delegator-addr]",
|
||||
Short: "Query all delegations made from one delegator",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
delegatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressDelegator))
|
||||
delegatorAddr, err := sdk.GetAddress(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := stake.GetDelegatorBondsKey(delegatorAddr, cdc)
|
||||
key := stake.GetDelegationsKey(delegatorAddr, cdc)
|
||||
ctx := context.NewCoreContextFromViper()
|
||||
resKVs, err := ctx.QuerySubspace(cdc, key, storeName)
|
||||
if err != nil {
|
||||
|
@ -150,14 +147,14 @@ func GetCmdQueryDelegatorBonds(storeName string, cdc *wire.Codec) *cobra.Command
|
|||
}
|
||||
|
||||
// parse out the candidates
|
||||
var delegators []stake.DelegatorBond
|
||||
var delegations []stake.Delegation
|
||||
for _, KV := range resKVs {
|
||||
var delegator stake.DelegatorBond
|
||||
cdc.MustUnmarshalBinary(KV.Value, &delegator)
|
||||
delegators = append(delegators, delegator)
|
||||
var delegation stake.Delegation
|
||||
cdc.MustUnmarshalBinary(KV.Value, &delegation)
|
||||
delegations = append(delegations, delegation)
|
||||
}
|
||||
|
||||
output, err := wire.MarshalJSONIndent(cdc, delegators)
|
||||
output, err := wire.MarshalJSONIndent(cdc, delegations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -167,6 +164,5 @@ func GetCmdQueryDelegatorBonds(storeName string, cdc *wire.Codec) *cobra.Command
|
|||
// TODO output with proofs / machine parseable etc.
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@ import (
|
|||
// create declare candidacy command
|
||||
func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "declare-candidacy",
|
||||
Short: "create new validator-candidate account and delegate some coins to it",
|
||||
Use: "create-validator",
|
||||
Short: "create new validator initialized with a self-delegation to it",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
||||
|
@ -28,7 +28,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
candidateAddr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
validatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
if viper.GetString(FlagMoniker) == "" {
|
||||
return fmt.Errorf("please enter a moniker for the validator-candidate using --moniker")
|
||||
return fmt.Errorf("please enter a moniker for the validator using --moniker")
|
||||
}
|
||||
description := stake.Description{
|
||||
Moniker: viper.GetString(FlagMoniker),
|
||||
|
@ -55,7 +55,7 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
Website: viper.GetString(FlagWebsite),
|
||||
Details: viper.GetString(FlagDetails),
|
||||
}
|
||||
msg := stake.NewMsgDeclareCandidacy(candidateAddr, pk, amount, description)
|
||||
msg := stake.NewMsgDeclareCandidacy(validatorAddr, pk, amount, description)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
res, err := ctx.EnsureSignBuildBroadcast(ctx.FromAddressName, msg, cdc)
|
||||
|
@ -71,18 +71,18 @@ func GetCmdDeclareCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
cmd.Flags().AddFlagSet(fsPk)
|
||||
cmd.Flags().AddFlagSet(fsAmount)
|
||||
cmd.Flags().AddFlagSet(fsDescription)
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
// create edit candidacy command
|
||||
func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "edit-candidacy",
|
||||
Short: "edit and existing validator-candidate account",
|
||||
Use: "edit-validator",
|
||||
Short: "edit and existing validator account",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
candidateAddr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
validatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
Website: viper.GetString(FlagWebsite),
|
||||
Details: viper.GetString(FlagDetails),
|
||||
}
|
||||
msg := stake.NewMsgEditCandidacy(candidateAddr, description)
|
||||
msg := stake.NewMsgEditCandidacy(validatorAddr, description)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -108,7 +108,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().AddFlagSet(fsDescription)
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ func GetCmdEditCandidacy(cdc *wire.Codec) *cobra.Command {
|
|||
func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delegate",
|
||||
Short: "delegate coins to an existing validator/candidate",
|
||||
Short: "delegate coins to an existing validator",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
amount, err := sdk.ParseCoin(viper.GetString(FlagAmount))
|
||||
if err != nil {
|
||||
|
@ -124,12 +124,12 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
delegatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressDelegator))
|
||||
candidateAddr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
validatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgDelegate(delegatorAddr, candidateAddr, amount)
|
||||
msg := stake.NewMsgDelegate(delegatorAddr, validatorAddr, amount)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -146,7 +146,7 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
cmd.Flags().AddFlagSet(fsAmount)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ func GetCmdDelegate(cdc *wire.Codec) *cobra.Command {
|
|||
func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "unbond",
|
||||
Short: "unbond coins from a validator/candidate",
|
||||
Short: "unbond shares from a validator",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// check the shares before broadcasting
|
||||
|
@ -172,12 +172,12 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
|||
}
|
||||
|
||||
delegatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressDelegator))
|
||||
candidateAddr, err := sdk.GetAddress(viper.GetString(FlagAddressCandidate))
|
||||
validatorAddr, err := sdk.GetAddress(viper.GetString(FlagAddressValidator))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := stake.NewMsgUnbond(delegatorAddr, candidateAddr, sharesStr)
|
||||
msg := stake.NewMsgUnbond(delegatorAddr, validatorAddr, sharesStr)
|
||||
|
||||
// build and sign the transaction, then broadcast to Tendermint
|
||||
ctx := context.NewCoreContextFromViper().WithDecoder(authcmd.GetAccountDecoder(cdc))
|
||||
|
@ -194,6 +194,6 @@ func GetCmdUnbond(cdc *wire.Codec) *cobra.Command {
|
|||
|
||||
cmd.Flags().AddFlagSet(fsShares)
|
||||
cmd.Flags().AddFlagSet(fsDelegator)
|
||||
cmd.Flags().AddFlagSet(fsCandidate)
|
||||
cmd.Flags().AddFlagSet(fsValidator)
|
||||
return cmd
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
|
||||
// RegisterRoutes - Central function to define routes that get registered by the main application
|
||||
func RegisterRoutes(ctx context.CoreContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) {
|
||||
r.HandleFunc("/stake/{delegator}/bonding_status/{candidate}", BondingStatusHandlerFn("stake", cdc, kb, ctx)).Methods("GET")
|
||||
r.HandleFunc("/stake/{delegator}/bonding_status/{validator}", BondingStatusHandlerFn("stake", cdc, kb, ctx)).Methods("GET")
|
||||
}
|
||||
|
||||
// BondingStatusHandlerFn - http request handler to query delegator bonding status
|
||||
|
@ -41,9 +41,9 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
|
|||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
candidateAddr := sdk.Address(bz)
|
||||
validatorAddr := sdk.Address(bz)
|
||||
|
||||
key := stake.GetDelegatorBondKey(delegatorAddr, candidateAddr, cdc)
|
||||
key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc)
|
||||
|
||||
res, err := ctx.Query(key, storeName)
|
||||
if err != nil {
|
||||
|
@ -58,7 +58,7 @@ func BondingStatusHandlerFn(storeName string, cdc *wire.Codec, kb keys.Keybase,
|
|||
return
|
||||
}
|
||||
|
||||
var bond stake.DelegatorBond
|
||||
var bond stake.Delegation
|
||||
err = cdc.UnmarshalBinary(res, &bond)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
|
|
@ -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 }
|
|
@ -14,9 +14,8 @@ const (
|
|||
|
||||
// Gaia errors reserve 200 ~ 299.
|
||||
CodeInvalidValidator CodeType = 201
|
||||
CodeInvalidCandidate CodeType = 202
|
||||
CodeInvalidBond CodeType = 203
|
||||
CodeInvalidInput CodeType = 204
|
||||
CodeInvalidBond CodeType = 202
|
||||
CodeInvalidInput CodeType = 203
|
||||
CodeUnauthorized CodeType = sdk.CodeUnauthorized
|
||||
CodeInternal CodeType = sdk.CodeInternal
|
||||
CodeUnknownRequest CodeType = sdk.CodeUnknownRequest
|
||||
|
@ -27,8 +26,6 @@ func codeToDefaultMsg(code CodeType) string {
|
|||
switch code {
|
||||
case CodeInvalidValidator:
|
||||
return "Invalid Validator"
|
||||
case CodeInvalidCandidate:
|
||||
return "Invalid Candidate"
|
||||
case CodeInvalidBond:
|
||||
return "Invalid Bond"
|
||||
case CodeInvalidInput:
|
||||
|
@ -50,8 +47,8 @@ func codeToDefaultMsg(code CodeType) string {
|
|||
func ErrNotEnoughBondShares(codespace sdk.CodespaceType, shares string) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, fmt.Sprintf("not enough shares only have %v", shares))
|
||||
}
|
||||
func ErrCandidateEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Cannot bond to an empty candidate")
|
||||
func ErrValidatorEmpty(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Cannot bond to an empty validator")
|
||||
}
|
||||
func ErrBadBondingDenom(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidBond, "Invalid coin denomination")
|
||||
|
@ -71,16 +68,13 @@ func ErrCommissionHuge(codespace sdk.CodespaceType) sdk.Error {
|
|||
func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Validator does not exist for that address")
|
||||
}
|
||||
func ErrBadCandidateAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Candidate does not exist for that address")
|
||||
}
|
||||
func ErrBadDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Delegator does not exist for that address")
|
||||
}
|
||||
func ErrCandidateExistsAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Candidate already exist, cannot re-declare candidacy")
|
||||
func ErrValidatorExistsAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Validator already exist, cannot re-declare candidacy")
|
||||
}
|
||||
func ErrCandidateRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Candidacy for this address is currently revoked")
|
||||
}
|
||||
func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
||||
|
@ -89,7 +83,7 @@ func ErrMissingSignature(codespace sdk.CodespaceType) sdk.Error {
|
|||
func ErrBondNotNominated(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Cannot bond to non-nominated account")
|
||||
}
|
||||
func ErrNoCandidateForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
return newError(codespace, CodeInvalidValidator, "Validator does not exist for that address")
|
||||
}
|
||||
func ErrNoDelegatorForAddress(codespace sdk.CodespaceType) sdk.Error {
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Handle fee distribution to the validators and delegators
|
||||
func FeeHandler(ctx sdk.Context, tx sdk.Tx, fee sdk.Coins) {
|
||||
|
||||
}
|
|
@ -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.setNewParams(ctx, data.Params)
|
||||
for _, validator := range data.Validators {
|
||||
k.updateValidator(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.getAllValidators(ctx)
|
||||
bonds := k.getAllDelegations(ctx)
|
||||
return GenesisState{
|
||||
pool,
|
||||
params,
|
||||
validators,
|
||||
bonds,
|
||||
}
|
||||
}
|
|
@ -7,16 +7,6 @@ import (
|
|||
abci "github.com/tendermint/abci/types"
|
||||
)
|
||||
|
||||
//nolint
|
||||
const (
|
||||
GasDeclareCandidacy int64 = 20
|
||||
GasEditCandidacy int64 = 20
|
||||
GasDelegate int64 = 20
|
||||
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 +25,6 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// NewEndBlocker generates sdk.EndBlocker
|
||||
// Performs tick functionality
|
||||
func NewEndBlocker(k Keeper) sdk.EndBlocker {
|
||||
|
@ -48,60 +36,35 @@ 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 _, candidate := range data.Candidates {
|
||||
k.setCandidate(ctx, candidate)
|
||||
}
|
||||
for _, bond := range data.Bonds {
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
}
|
||||
}
|
||||
|
||||
// WriteGenesis - output genesis parameters
|
||||
func WriteGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
pool := k.GetPool(ctx)
|
||||
params := k.GetParams(ctx)
|
||||
candidates := k.GetCandidates(ctx, 32767)
|
||||
bonds := k.getBonds(ctx, 32767)
|
||||
return GenesisState{
|
||||
pool,
|
||||
params,
|
||||
candidates,
|
||||
bonds,
|
||||
}
|
||||
}
|
||||
|
||||
//_____________________________________________________________________
|
||||
|
||||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keeper) sdk.Result {
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
_, found := k.GetCandidate(ctx, msg.CandidateAddr)
|
||||
_, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if found {
|
||||
return ErrCandidateExistsAddr(k.codespace).Result()
|
||||
return ErrValidatorExistsAddr(k.codespace).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasDeclareCandidacy,
|
||||
}
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
candidate := NewCandidate(msg.CandidateAddr, msg.PubKey, msg.Description)
|
||||
k.setCandidate(ctx, candidate)
|
||||
tags := sdk.NewTags("action", []byte("declareCandidacy"), "candidate", msg.CandidateAddr.Bytes(), "moniker", []byte(msg.Description.Moniker), "identity", []byte(msg.Description.Identity))
|
||||
validator := NewValidator(msg.ValidatorAddr, msg.PubKey, msg.Description)
|
||||
k.setValidator(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("declareCandidacy"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
)
|
||||
|
||||
// move coins from the msg.Address account to a (self-bond) delegator account
|
||||
// the candidate account and global shares are updated within here
|
||||
delegateTags, err := delegate(ctx, k, msg.CandidateAddr, msg.Bond, candidate)
|
||||
// the validator account and global shares are updated within here
|
||||
delegateTags, err := delegate(ctx, k, msg.ValidatorAddr, msg.Bond, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
@ -113,26 +76,29 @@ func handleMsgDeclareCandidacy(ctx sdk.Context, msg MsgDeclareCandidacy, k Keepe
|
|||
|
||||
func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk.Result {
|
||||
|
||||
// candidate must already be registered
|
||||
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
|
||||
// validator must already be registered
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadCandidateAddr(k.codespace).Result()
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasEditCandidacy,
|
||||
}
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// XXX move to types
|
||||
// replace all editable fields (clients should autofill existing values)
|
||||
candidate.Description.Moniker = msg.Description.Moniker
|
||||
candidate.Description.Identity = msg.Description.Identity
|
||||
candidate.Description.Website = msg.Description.Website
|
||||
candidate.Description.Details = msg.Description.Details
|
||||
validator.Description.Moniker = msg.Description.Moniker
|
||||
validator.Description.Identity = msg.Description.Identity
|
||||
validator.Description.Website = msg.Description.Website
|
||||
validator.Description.Details = msg.Description.Details
|
||||
|
||||
k.setCandidate(ctx, candidate)
|
||||
tags := sdk.NewTags("action", []byte("editCandidacy"), "candidate", msg.CandidateAddr.Bytes(), "moniker", []byte(msg.Description.Moniker), "identity", []byte(msg.Description.Identity))
|
||||
k.updateValidator(ctx, validator)
|
||||
tags := sdk.NewTags(
|
||||
"action", []byte("editCandidacy"),
|
||||
"validator", msg.ValidatorAddr.Bytes(),
|
||||
"moniker", []byte(msg.Description.Moniker),
|
||||
"identity", []byte(msg.Description.Identity),
|
||||
)
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
|
@ -140,22 +106,20 @@ func handleMsgEditCandidacy(ctx sdk.Context, msg MsgEditCandidacy, k Keeper) sdk
|
|||
|
||||
func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
||||
|
||||
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrBadCandidateAddr(k.codespace).Result()
|
||||
return ErrBadValidatorAddr(k.codespace).Result()
|
||||
}
|
||||
if msg.Bond.Denom != k.GetParams(ctx).BondDenom {
|
||||
return ErrBadBondingDenom(k.codespace).Result()
|
||||
}
|
||||
if candidate.Status == Revoked {
|
||||
return ErrCandidateRevoked(k.codespace).Result()
|
||||
if validator.Revoked == true {
|
||||
return ErrValidatorRevoked(k.codespace).Result()
|
||||
}
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasDelegate,
|
||||
}
|
||||
return sdk.Result{}
|
||||
}
|
||||
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, candidate)
|
||||
tags, err := delegate(ctx, k, msg.DelegatorAddr, msg.Bond, validator)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
@ -166,14 +130,14 @@ func handleMsgDelegate(ctx sdk.Context, msg MsgDelegate, k Keeper) sdk.Result {
|
|||
|
||||
// common functionality between handlers
|
||||
func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
||||
bondAmt sdk.Coin, candidate Candidate) (sdk.Tags, sdk.Error) {
|
||||
bondAmt sdk.Coin, validator Validator) (sdk.Tags, sdk.Error) {
|
||||
|
||||
// Get or create the delegator bond
|
||||
bond, found := k.GetDelegatorBond(ctx, delegatorAddr, candidate.Address)
|
||||
bond, found := k.GetDelegation(ctx, delegatorAddr, validator.Owner)
|
||||
if !found {
|
||||
bond = DelegatorBond{
|
||||
bond = Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidate.Address,
|
||||
ValidatorAddr: validator.Owner,
|
||||
Shares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
@ -184,31 +148,28 @@ func delegate(ctx sdk.Context, k Keeper, delegatorAddr sdk.Address,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pool, candidate, newShares := pool.candidateAddTokens(candidate, bondAmt.Amount)
|
||||
validator, pool, newShares := validator.addTokensFromDel(pool, bondAmt.Amount)
|
||||
bond.Shares = bond.Shares.Add(newShares)
|
||||
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
k.setCandidate(ctx, candidate)
|
||||
k.setPool(ctx, pool)
|
||||
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "candidate", candidate.Address.Bytes())
|
||||
k.setDelegation(ctx, bond)
|
||||
k.updateValidator(ctx, validator)
|
||||
tags := sdk.NewTags("action", []byte("delegate"), "delegator", delegatorAddr.Bytes(), "validator", validator.Owner.Bytes())
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
||||
|
||||
// check if bond has any shares in it unbond
|
||||
bond, found := k.GetDelegatorBond(ctx, msg.DelegatorAddr, msg.CandidateAddr)
|
||||
bond, found := k.GetDelegation(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoDelegatorForAddress(k.codespace).Result()
|
||||
}
|
||||
if !bond.Shares.GT(sdk.ZeroRat()) { // bond shares < msg shares
|
||||
return ErrInsufficientFunds(k.codespace).Result()
|
||||
}
|
||||
|
||||
var shares sdk.Rat
|
||||
var delShares sdk.Rat
|
||||
|
||||
// test that there are enough shares to unbond
|
||||
if msg.Shares == "MAX" {
|
||||
|
@ -217,81 +178,71 @@ func handleMsgUnbond(ctx sdk.Context, msg MsgUnbond, k Keeper) sdk.Result {
|
|||
}
|
||||
} else {
|
||||
var err sdk.Error
|
||||
shares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
delShares, err = sdk.NewRatFromDecimal(msg.Shares)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
if bond.Shares.LT(shares) {
|
||||
if bond.Shares.LT(delShares) {
|
||||
return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result()
|
||||
}
|
||||
}
|
||||
|
||||
// get candidate
|
||||
candidate, found := k.GetCandidate(ctx, msg.CandidateAddr)
|
||||
// get validator
|
||||
validator, found := k.GetValidator(ctx, msg.ValidatorAddr)
|
||||
if !found {
|
||||
return ErrNoCandidateForAddress(k.codespace).Result()
|
||||
return ErrNoValidatorForAddress(k.codespace).Result()
|
||||
}
|
||||
|
||||
if ctx.IsCheckTx() {
|
||||
return sdk.Result{
|
||||
GasUsed: GasUnbond,
|
||||
}
|
||||
return sdk.Result{}
|
||||
}
|
||||
|
||||
// retrieve the amount of bonds to remove (TODO remove redundancy already serialized)
|
||||
if msg.Shares == "MAX" {
|
||||
shares = bond.Shares
|
||||
delShares = bond.Shares
|
||||
}
|
||||
|
||||
// subtract bond tokens from delegator bond
|
||||
bond.Shares = bond.Shares.Sub(shares)
|
||||
bond.Shares = bond.Shares.Sub(delShares)
|
||||
|
||||
// remove the bond
|
||||
revokeCandidacy := false
|
||||
if bond.Shares.IsZero() {
|
||||
|
||||
// if the bond is the owner of the candidate then
|
||||
// if the bond is the owner of the validator then
|
||||
// trigger a revoke candidacy
|
||||
if bytes.Equal(bond.DelegatorAddr, candidate.Address) &&
|
||||
candidate.Status != Revoked {
|
||||
if bytes.Equal(bond.DelegatorAddr, validator.Owner) &&
|
||||
validator.Revoked == false {
|
||||
revokeCandidacy = true
|
||||
}
|
||||
|
||||
k.removeDelegatorBond(ctx, bond)
|
||||
k.removeDelegation(ctx, bond)
|
||||
} else {
|
||||
// Update bond height
|
||||
bond.Height = ctx.BlockHeight()
|
||||
k.setDelegatorBond(ctx, bond)
|
||||
k.setDelegation(ctx, bond)
|
||||
}
|
||||
|
||||
// Add the coins
|
||||
p := k.GetPool(ctx)
|
||||
p, candidate, returnAmount := p.candidateRemoveShares(candidate, shares)
|
||||
pool := k.GetPool(ctx)
|
||||
validator, pool, returnAmount := validator.removeDelShares(pool, delShares)
|
||||
k.setPool(ctx, pool)
|
||||
returnCoins := sdk.Coins{{k.GetParams(ctx).BondDenom, returnAmount}}
|
||||
k.coinKeeper.AddCoins(ctx, bond.DelegatorAddr, returnCoins)
|
||||
|
||||
/////////////////////////////////////
|
||||
|
||||
// revoke candidate if necessary
|
||||
// revoke validator if necessary
|
||||
if revokeCandidacy {
|
||||
|
||||
// change the share types to unbonded if they were not already
|
||||
if candidate.Status == Bonded {
|
||||
p, candidate = p.bondedToUnbondedPool(candidate)
|
||||
}
|
||||
|
||||
// lastly update the status
|
||||
candidate.Status = Revoked
|
||||
validator.Revoked = true
|
||||
}
|
||||
|
||||
// deduct shares from the candidate
|
||||
if candidate.Liabilities.IsZero() {
|
||||
k.removeCandidate(ctx, candidate.Address)
|
||||
} else {
|
||||
k.setCandidate(ctx, candidate)
|
||||
validator = k.updateValidator(ctx, validator)
|
||||
|
||||
if validator.DelegatorShares.IsZero() {
|
||||
k.removeValidator(ctx, validator.Owner)
|
||||
}
|
||||
k.setPool(ctx, p)
|
||||
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "candidate", msg.CandidateAddr.Bytes())
|
||||
|
||||
tags := sdk.NewTags("action", []byte("unbond"), "delegator", msg.DelegatorAddr.Bytes(), "validator", msg.ValidatorAddr.Bytes())
|
||||
return sdk.Result{
|
||||
Tags: tags,
|
||||
}
|
||||
|
|
|
@ -17,16 +17,16 @@ import (
|
|||
func newTestMsgDeclareCandidacy(address sdk.Address, pubKey crypto.PubKey, amt int64) MsgDeclareCandidacy {
|
||||
return MsgDeclareCandidacy{
|
||||
Description: Description{},
|
||||
CandidateAddr: address,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
ValidatorAddr: address,
|
||||
PubKey: pubKey,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
}
|
||||
}
|
||||
|
||||
func newTestMsgDelegate(delegatorAddr, candidateAddr sdk.Address, amt int64) MsgDelegate {
|
||||
func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: sdk.Coin{"steak", amt},
|
||||
}
|
||||
}
|
||||
|
@ -36,21 +36,21 @@ func newTestMsgDelegate(delegatorAddr, candidateAddr sdk.Address, amt int64) Msg
|
|||
func TestDuplicatesMsgDeclareCandidacy(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
|
||||
candidateAddr := addrs[0]
|
||||
validatorAddr := addrs[0]
|
||||
pk := pks[0]
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pk, 10)
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pk, 10)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "%v", got)
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, Unbonded, candidate.Status)
|
||||
assert.Equal(t, candidateAddr, candidate.Address)
|
||||
assert.Equal(t, pk, candidate.PubKey)
|
||||
assert.Equal(t, sdk.NewRat(10), candidate.Assets)
|
||||
assert.Equal(t, sdk.NewRat(10), candidate.Liabilities)
|
||||
assert.Equal(t, Description{}, candidate.Description)
|
||||
assert.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.Equal(t, validatorAddr, validator.Owner)
|
||||
assert.Equal(t, pk, validator.PubKey)
|
||||
assert.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded())
|
||||
assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares)
|
||||
assert.Equal(t, Description{}, validator.Description)
|
||||
|
||||
// one candidate cannot bond twice
|
||||
// one validator cannot bond twice
|
||||
msgDeclareCandidacy.PubKey = pks[1]
|
||||
got = handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.False(t, got.IsOK(), "%v", got)
|
||||
|
@ -62,20 +62,34 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
params := keeper.GetParams(ctx)
|
||||
|
||||
bondAmount := int64(10)
|
||||
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
|
||||
// first declare candidacy
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], bondAmount)
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], bondAmount)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "expected declare candidacy msg to be ok, got %v", got)
|
||||
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, bondAmount, candidate.Liabilities.Evaluate())
|
||||
assert.Equal(t, bondAmount, candidate.Assets.Evaluate())
|
||||
require.Equal(t, sdk.Bonded, validator.Status())
|
||||
assert.Equal(t, bondAmount, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, validator.PoolShares.Bonded().Evaluate(), "validator: %v", validator)
|
||||
|
||||
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, bondAmount, bond.Shares.Evaluate())
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
|
||||
assert.Equal(t, bondAmount, pool.BondedShares.Evaluate())
|
||||
assert.Equal(t, bondAmount, pool.BondedTokens)
|
||||
|
||||
// just send the same msgbond multiple times
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, bondAmount)
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
ctx = ctx.WithBlockHeight(int64(i))
|
||||
|
@ -84,30 +98,34 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
|||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
exRate := validator.DelegatorShareExRate(pool)
|
||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
|
||||
|
||||
expBond := int64(i+1) * bondAmount
|
||||
expLiabilities := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation)
|
||||
expDelegatorAcc := initBond - expBond
|
||||
|
||||
require.Equal(t, bond.Height, int64(i), "Incorrect bond height")
|
||||
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
"i: %v\nexpBond: %v\ngotBond: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expBond, gotBond, candidate, bond)
|
||||
require.Equal(t, expLiabilities, gotLiabilities,
|
||||
"i: %v\nexpLiabilities: %v\ngotLiabilities: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expLiabilities, gotLiabilities, candidate, bond)
|
||||
"i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expBond, gotBond, validator, bond)
|
||||
require.Equal(t, expDelegatorShares, gotDelegatorShares,
|
||||
"i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expDelegatorShares, gotDelegatorShares, validator, bond)
|
||||
require.Equal(t, expDelegatorAcc, gotDelegatorAcc,
|
||||
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expDelegatorAcc, gotDelegatorAcc, candidate, bond)
|
||||
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expDelegatorAcc, gotDelegatorAcc, validator, bond)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,53 +135,53 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
params := keeper.GetParams(ctx)
|
||||
|
||||
// declare candidacy, delegate
|
||||
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], initBond)
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], initBond)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
assert.True(t, got.IsOK(), "expected declare-candidacy to be ok, got %v", got)
|
||||
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, initBond)
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, initBond)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
assert.True(t, got.IsOK(), "expected delegation to be ok, got %v", got)
|
||||
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
assert.Equal(t, initBond*2, candidate.Liabilities.Evaluate())
|
||||
assert.Equal(t, initBond*2, candidate.Assets.Evaluate())
|
||||
assert.Equal(t, initBond*2, validator.DelegatorShares.Evaluate())
|
||||
assert.Equal(t, initBond*2, validator.PoolShares.Bonded().Evaluate())
|
||||
|
||||
// just send the same msgUnbond multiple times
|
||||
// TODO use decimals here
|
||||
unbondShares, unbondSharesStr := int64(10), "10"
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
numUnbonds := 5
|
||||
for i := 0; i < numUnbonds; i++ {
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
candidate, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
|
||||
expBond := initBond - int64(i+1)*unbondShares
|
||||
expLiabilities := 2*initBond - int64(i+1)*unbondShares
|
||||
expDelegatorShares := 2*initBond - int64(i+1)*unbondShares
|
||||
expDelegatorAcc := initBond - expBond
|
||||
|
||||
gotBond := bond.Shares.Evaluate()
|
||||
gotLiabilities := candidate.Liabilities.Evaluate()
|
||||
gotDelegatorShares := validator.DelegatorShares.Evaluate()
|
||||
gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom)
|
||||
|
||||
require.Equal(t, expBond, gotBond,
|
||||
"i: %v\nexpBond: %v\ngotBond: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expBond, gotBond, candidate, bond)
|
||||
require.Equal(t, expLiabilities, gotLiabilities,
|
||||
"i: %v\nexpLiabilities: %v\ngotLiabilities: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expLiabilities, gotLiabilities, candidate, bond)
|
||||
"i: %v\nexpBond: %v\ngotBond: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expBond, gotBond, validator, bond)
|
||||
require.Equal(t, expDelegatorShares, gotDelegatorShares,
|
||||
"i: %v\nexpDelegatorShares: %v\ngotDelegatorShares: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expDelegatorShares, gotDelegatorShares, validator, bond)
|
||||
require.Equal(t, expDelegatorAcc, gotDelegatorAcc,
|
||||
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\ncandidate: %v\nbond: %v\n",
|
||||
i, expDelegatorAcc, gotDelegatorAcc, candidate, bond)
|
||||
"i: %v\nexpDelegatorAcc: %v\ngotDelegatorAcc: %v\nvalidator: %v\nbond: %v\n",
|
||||
i, expDelegatorAcc, gotDelegatorAcc, validator, bond)
|
||||
}
|
||||
|
||||
// these are more than we have bonded now
|
||||
|
@ -176,7 +194,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
}
|
||||
for _, c := range errorCases {
|
||||
unbondShares := strconv.Itoa(int(c))
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, unbondShares)
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, unbondShares)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
require.False(t, got.IsOK(), "expected unbond msg to fail")
|
||||
}
|
||||
|
@ -185,14 +203,14 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
|||
|
||||
// should be unable to unbond one more than we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded) + 1)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.False(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
|
||||
// should be able to unbond just what we have
|
||||
unbondSharesStr = strconv.Itoa(int(leftBonded))
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, candidateAddr, unbondSharesStr)
|
||||
msgUnbond = NewMsgUnbond(delegatorAddr, validatorAddr, unbondSharesStr)
|
||||
got = handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
assert.True(t, got.IsOK(),
|
||||
"got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUnbond, unbondSharesStr, leftBonded)
|
||||
|
@ -202,108 +220,108 @@ func TestMultipleMsgDeclareCandidacy(t *testing.T) {
|
|||
initBond := int64(1000)
|
||||
ctx, accMapper, keeper := createTestInput(t, false, initBond)
|
||||
params := keeper.GetParams(ctx)
|
||||
candidateAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
validatorAddrs := []sdk.Address{addrs[0], addrs[1], addrs[2]}
|
||||
|
||||
// bond them all
|
||||
for i, candidateAddr := range candidateAddrs {
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[i], 10)
|
||||
for i, validatorAddr := range validatorAddrs {
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[i], 10)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is bonded
|
||||
candidates := keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, (i + 1), len(candidates))
|
||||
val := candidates[i]
|
||||
validators := keeper.GetValidators(ctx, 100)
|
||||
require.Equal(t, (i + 1), len(validators))
|
||||
val := validators[i]
|
||||
balanceExpd := initBond - 10
|
||||
balanceGot := accMapper.GetAccount(ctx, val.Address).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, i+1, len(candidates), "expected %d candidates got %d, candidates: %v", i+1, len(candidates), candidates)
|
||||
require.Equal(t, 10, int(val.Liabilities.Evaluate()), "expected %d shares, got %d", 10, val.Liabilities)
|
||||
balanceGot := accMapper.GetAccount(ctx, val.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators)
|
||||
require.Equal(t, 10, int(val.DelegatorShares.Evaluate()), "expected %d shares, got %d", 10, val.DelegatorShares)
|
||||
require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot)
|
||||
}
|
||||
|
||||
// unbond them all
|
||||
for i, candidateAddr := range candidateAddrs {
|
||||
candidatePre, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
for i, validatorAddr := range validatorAddrs {
|
||||
validatorPre, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
msgUnbond := NewMsgUnbond(candidateAddr, candidateAddr, "10") // self-delegation
|
||||
msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "10") // self-delegation
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
candidates := keeper.GetCandidates(ctx, 100)
|
||||
require.Equal(t, len(candidateAddrs)-(i+1), len(candidates),
|
||||
"expected %d candidates got %d", len(candidateAddrs)-(i+1), len(candidates))
|
||||
validators := keeper.GetValidators(ctx, 100)
|
||||
require.Equal(t, len(validatorAddrs)-(i+1), len(validators),
|
||||
"expected %d validators got %d", len(validatorAddrs)-(i+1), len(validators))
|
||||
|
||||
_, found = keeper.GetCandidate(ctx, candidateAddr)
|
||||
_, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.False(t, found)
|
||||
|
||||
expBalance := initBond
|
||||
gotBalance := accMapper.GetAccount(ctx, candidatePre.Address).GetCoins().AmountOf(params.BondDenom)
|
||||
gotBalance := accMapper.GetAccount(ctx, validatorPre.Owner).GetCoins().AmountOf(params.BondDenom)
|
||||
require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleMsgDelegate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
candidateAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||
validatorAddr, delegatorAddrs := addrs[0], addrs[1:]
|
||||
|
||||
//first make a candidate
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], 10)
|
||||
//first make a validator
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], 10)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
|
||||
|
||||
// delegate multiple parties
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, 10)
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got := handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is bonded
|
||||
bond, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.NotNil(t, bond, "expected delegatee bond %d to exist", bond)
|
||||
}
|
||||
|
||||
// unbond them all
|
||||
for i, delegatorAddr := range delegatorAddrs {
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, candidateAddr, "10")
|
||||
msgUnbond := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got := handleMsgUnbond(ctx, msgUnbond, keeper)
|
||||
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
|
||||
|
||||
//Check that the account is unbonded
|
||||
_, found := keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
require.False(t, found)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVoidCandidacy(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 1000)
|
||||
candidateAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
validatorAddr, delegatorAddr := addrs[0], addrs[1]
|
||||
|
||||
// create the candidate
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(candidateAddr, pks[0], 10)
|
||||
// create the validator
|
||||
msgDeclareCandidacy := newTestMsgDeclareCandidacy(validatorAddr, pks[0], 10)
|
||||
got := handleMsgDeclareCandidacy(ctx, msgDeclareCandidacy, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
// bond a delegator
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, candidateAddr, 10)
|
||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected ok, got %v", got)
|
||||
|
||||
// unbond the candidates bond portion
|
||||
msgUnbondCandidate := NewMsgUnbond(candidateAddr, candidateAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondCandidate, keeper)
|
||||
// unbond the validators bond portion
|
||||
msgUnbondValidator := NewMsgUnbond(validatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||
candidate, found := keeper.GetCandidate(ctx, candidateAddr)
|
||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
require.Equal(t, Revoked, candidate.Status)
|
||||
require.True(t, validator.Revoked)
|
||||
|
||||
// test that this address cannot yet be bonded too because is revoked
|
||||
got = handleMsgDelegate(ctx, msgDelegate, keeper)
|
||||
assert.False(t, got.IsOK(), "expected error, got %v", got)
|
||||
|
||||
// test that the delegator can still withdraw their bonds
|
||||
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, candidateAddr, "10")
|
||||
msgUnbondDelegator := NewMsgUnbond(delegatorAddr, validatorAddr, "10")
|
||||
got = handleMsgUnbond(ctx, msgUnbondDelegator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgDeclareCandidacy")
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// TODO remove some of these prefixes once have working multistore
|
||||
|
@ -12,67 +13,64 @@ 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
|
||||
CandidatesKey = []byte{0x02} // prefix for each key to a candidate
|
||||
ValidatorsKey = []byte{0x03} // prefix for each key to a validator
|
||||
AccUpdateValidatorsKey = []byte{0x04} // prefix for each key to a validator which is being updated
|
||||
RecentValidatorsKey = []byte{0x05} // prefix for each key to the last updated validator group
|
||||
|
||||
ToKickOutValidatorsKey = []byte{0x06} // prefix for each key to the last updated validator group
|
||||
|
||||
DelegatorBondKeyPrefix = []byte{0x07} // prefix for each key to a delegator's bond
|
||||
|
||||
CounterKey = []byte{0x08} // key for block-local tx index
|
||||
ParamKey = []byte{0x00} // key for parameters relating to staking
|
||||
PoolKey = []byte{0x01} // key for the staking pools
|
||||
ValidatorsKey = []byte{0x02} // prefix for each key to a validator
|
||||
ValidatorsBondedKey = []byte{0x03} // prefix for each key to bonded/actively validating validators
|
||||
ValidatorsByPowerKey = []byte{0x04} // prefix for each key to a validator sorted by power
|
||||
ValidatorCliffKey = []byte{0x05} // key for block-local tx index
|
||||
ValidatorPowerCliffKey = []byte{0x06} // key for block-local tx index
|
||||
TendermintUpdatesKey = []byte{0x07} // prefix for each key to a validator which is being updated
|
||||
DelegationKey = []byte{0x08} // prefix for each key to a delegator's bond
|
||||
IntraTxCounterKey = []byte{0x09} // key for block-local tx index
|
||||
)
|
||||
|
||||
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
|
||||
|
||||
// get the key for the candidate with address
|
||||
func GetCandidateKey(addr sdk.Address) []byte {
|
||||
return append(CandidatesKey, addr.Bytes()...)
|
||||
// get the key for the validator with address
|
||||
func GetValidatorKey(ownerAddr sdk.Address) []byte {
|
||||
return append(ValidatorsKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the current validator group, ordered like tendermint
|
||||
func GetValidatorsBondedKey(pk crypto.PubKey) []byte {
|
||||
addr := pk.Address()
|
||||
return append(ValidatorsBondedKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the validator used in the power-store
|
||||
func GetValidatorKey(addr sdk.Address, power sdk.Rat, height int64, counter int16, cdc *wire.Codec) []byte {
|
||||
func GetValidatorsByPowerKey(validator Validator, pool Pool) []byte {
|
||||
|
||||
power := validator.EquivalentBondedShares(pool)
|
||||
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
|
||||
heightBytes := make([]byte, binary.MaxVarintLen64)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first)
|
||||
binary.BigEndian.PutUint64(heightBytes, ^uint64(validator.BondHeight)) // invert height (older validators first)
|
||||
counterBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(counter)) // invert counter (first txns have priority)
|
||||
return append(ValidatorsKey, append(powerBytes, append(heightBytes, append(counterBytes, addr.Bytes()...)...)...)...)
|
||||
binary.BigEndian.PutUint16(counterBytes, ^uint16(validator.BondIntraTxCounter)) // invert counter (first txns have priority)
|
||||
return append(ValidatorsByPowerKey,
|
||||
append(powerBytes,
|
||||
append(heightBytes,
|
||||
append(counterBytes, validator.Owner.Bytes()...)...)...)...) // TODO don't technically need to store owner
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetAccUpdateValidatorKey(addr sdk.Address) []byte {
|
||||
return append(AccUpdateValidatorsKey, addr.Bytes()...)
|
||||
func GetTendermintUpdatesKey(ownerAddr sdk.Address) []byte {
|
||||
return append(TendermintUpdatesKey, ownerAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetRecentValidatorKey(addr sdk.Address) []byte {
|
||||
return append(RecentValidatorsKey, addr.Bytes()...)
|
||||
// get the key for delegator bond with validator
|
||||
func GetDelegationKey(delegatorAddr, validatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
return append(GetDelegationsKey(delegatorAddr, cdc), validatorAddr.Bytes()...)
|
||||
}
|
||||
|
||||
// reverse operation of GetRecentValidatorKey
|
||||
func AddrFromKey(key []byte) sdk.Address {
|
||||
return key[1:]
|
||||
}
|
||||
|
||||
// get the key for the accumulated update validators
|
||||
func GetToKickOutValidatorKey(addr sdk.Address) []byte {
|
||||
return append(ToKickOutValidatorsKey, addr.Bytes()...)
|
||||
}
|
||||
|
||||
// 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()...)
|
||||
}
|
||||
|
||||
// get the prefix for a delegator for all candidates
|
||||
func GetDelegatorBondsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
// get the prefix for a delegator for all validators
|
||||
func GetDelegationsKey(delegatorAddr sdk.Address, cdc *wire.Codec) []byte {
|
||||
res, err := cdc.MarshalBinary(&delegatorAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append(DelegatorBondKeyPrefix, res...)
|
||||
return append(DelegationKey, res...)
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,16 +31,16 @@ func init() {
|
|||
// MsgDeclareCandidacy - struct for unbonding transactions
|
||||
type MsgDeclareCandidacy struct {
|
||||
Description
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDeclareCandidacy(candidateAddr sdk.Address, pubkey crypto.PubKey,
|
||||
func NewMsgDeclareCandidacy(validatorAddr sdk.Address, pubkey crypto.PubKey,
|
||||
bond sdk.Coin, description Description) MsgDeclareCandidacy {
|
||||
return MsgDeclareCandidacy{
|
||||
Description: description,
|
||||
CandidateAddr: candidateAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
PubKey: pubkey,
|
||||
Bond: bond,
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func NewMsgDeclareCandidacy(candidateAddr sdk.Address, pubkey crypto.PubKey,
|
|||
|
||||
//nolint
|
||||
func (msg MsgDeclareCandidacy) Type() string { return MsgType } //TODO update "stake/declarecandidacy"
|
||||
func (msg MsgDeclareCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.CandidateAddr} }
|
||||
func (msg MsgDeclareCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.ValidatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgDeclareCandidacy) GetSignBytes() []byte {
|
||||
|
@ -57,8 +57,8 @@ func (msg MsgDeclareCandidacy) GetSignBytes() []byte {
|
|||
|
||||
// quick validity check
|
||||
func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error {
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrCandidateEmpty(DefaultCodespace)
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
|
@ -75,22 +75,22 @@ func (msg MsgDeclareCandidacy) ValidateBasic() sdk.Error {
|
|||
|
||||
//______________________________________________________________________
|
||||
|
||||
// MsgEditCandidacy - struct for editing a candidate
|
||||
// MsgEditCandidacy - struct for editing a validator
|
||||
type MsgEditCandidacy struct {
|
||||
Description
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
}
|
||||
|
||||
func NewMsgEditCandidacy(candidateAddr sdk.Address, description Description) MsgEditCandidacy {
|
||||
func NewMsgEditCandidacy(validatorAddr sdk.Address, description Description) MsgEditCandidacy {
|
||||
return MsgEditCandidacy{
|
||||
Description: description,
|
||||
CandidateAddr: candidateAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint
|
||||
func (msg MsgEditCandidacy) Type() string { return MsgType } //TODO update "stake/msgeditcandidacy"
|
||||
func (msg MsgEditCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.CandidateAddr} }
|
||||
func (msg MsgEditCandidacy) GetSigners() []sdk.Address { return []sdk.Address{msg.ValidatorAddr} }
|
||||
|
||||
// get the bytes for the message signer to sign on
|
||||
func (msg MsgEditCandidacy) GetSignBytes() []byte {
|
||||
|
@ -103,8 +103,8 @@ func (msg MsgEditCandidacy) GetSignBytes() []byte {
|
|||
|
||||
// quick validity check
|
||||
func (msg MsgEditCandidacy) ValidateBasic() sdk.Error {
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrCandidateEmpty(DefaultCodespace)
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrValidatorEmpty(DefaultCodespace)
|
||||
}
|
||||
empty := Description{}
|
||||
if msg.Description == empty {
|
||||
|
@ -118,14 +118,14 @@ func (msg MsgEditCandidacy) ValidateBasic() sdk.Error {
|
|||
// MsgDelegate - struct for bonding transactions
|
||||
type MsgDelegate struct {
|
||||
DelegatorAddr sdk.Address `json:"address"`
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
Bond sdk.Coin `json:"bond"`
|
||||
}
|
||||
|
||||
func NewMsgDelegate(delegatorAddr, candidateAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
func NewMsgDelegate(delegatorAddr, validatorAddr sdk.Address, bond sdk.Coin) MsgDelegate {
|
||||
return MsgDelegate{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Bond: bond,
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error {
|
|||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrBadCandidateAddr(DefaultCodespace)
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Bond.Denom != StakingToken {
|
||||
return ErrBadBondingDenom(DefaultCodespace)
|
||||
|
@ -165,14 +165,14 @@ func (msg MsgDelegate) ValidateBasic() sdk.Error {
|
|||
// MsgUnbond - struct for unbonding transactions
|
||||
type MsgUnbond struct {
|
||||
DelegatorAddr sdk.Address `json:"address"`
|
||||
CandidateAddr sdk.Address `json:"address"`
|
||||
ValidatorAddr sdk.Address `json:"address"`
|
||||
Shares string `json:"shares"`
|
||||
}
|
||||
|
||||
func NewMsgUnbond(delegatorAddr, candidateAddr sdk.Address, shares string) MsgUnbond {
|
||||
func NewMsgUnbond(delegatorAddr, validatorAddr sdk.Address, shares string) MsgUnbond {
|
||||
return MsgUnbond{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
CandidateAddr: candidateAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: shares,
|
||||
}
|
||||
}
|
||||
|
@ -195,8 +195,8 @@ func (msg MsgUnbond) ValidateBasic() sdk.Error {
|
|||
if msg.DelegatorAddr == nil {
|
||||
return ErrBadDelegatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.CandidateAddr == nil {
|
||||
return ErrBadCandidateAddr(DefaultCodespace)
|
||||
if msg.ValidatorAddr == nil {
|
||||
return ErrBadValidatorAddr(DefaultCodespace)
|
||||
}
|
||||
if msg.Shares != "MAX" {
|
||||
rat, err := sdk.NewRatFromDecimal(msg.Shares)
|
||||
|
|
|
@ -22,7 +22,7 @@ var (
|
|||
func TestMsgDeclareCandidacy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
candidateAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
pubkey crypto.PubKey
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
|
@ -40,7 +40,7 @@ func TestMsgDeclareCandidacy(t *testing.T) {
|
|||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgDeclareCandidacy(tc.candidateAddr, tc.pubkey, tc.bond, description)
|
||||
msg := NewMsgDeclareCandidacy(tc.validatorAddr, tc.pubkey, tc.bond, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
|
@ -53,7 +53,7 @@ func TestMsgDeclareCandidacy(t *testing.T) {
|
|||
func TestMsgEditCandidacy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name, moniker, identity, website, details string
|
||||
candidateAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", "a", "b", "c", "d", addrs[0], true},
|
||||
|
@ -64,7 +64,7 @@ func TestMsgEditCandidacy(t *testing.T) {
|
|||
|
||||
for _, tc := range tests {
|
||||
description := NewDescription(tc.moniker, tc.identity, tc.website, tc.details)
|
||||
msg := NewMsgEditCandidacy(tc.candidateAddr, description)
|
||||
msg := NewMsgEditCandidacy(tc.validatorAddr, description)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
|
@ -78,21 +78,21 @@ func TestMsgDelegate(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
candidateAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
bond sdk.Coin
|
||||
expectPass bool
|
||||
}{
|
||||
{"basic good", addrs[0], addrs[1], coinPos, true},
|
||||
{"self bond", addrs[0], addrs[0], coinPos, true},
|
||||
{"empty delegator", emptyAddr, addrs[0], coinPos, false},
|
||||
{"empty candidate", addrs[0], emptyAddr, coinPos, false},
|
||||
{"empty validator", addrs[0], emptyAddr, coinPos, false},
|
||||
{"empty bond", addrs[0], addrs[1], coinZero, false},
|
||||
{"negative bond", addrs[0], addrs[1], coinNeg, false},
|
||||
{"wrong staking token", addrs[0], addrs[1], coinPosNotAtoms, false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgDelegate(tc.delegatorAddr, tc.candidateAddr, tc.bond)
|
||||
msg := NewMsgDelegate(tc.delegatorAddr, tc.validatorAddr, tc.bond)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
|
@ -106,7 +106,7 @@ func TestMsgUnbond(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
delegatorAddr sdk.Address
|
||||
candidateAddr sdk.Address
|
||||
validatorAddr sdk.Address
|
||||
shares string
|
||||
expectPass bool
|
||||
}{
|
||||
|
@ -116,11 +116,11 @@ func TestMsgUnbond(t *testing.T) {
|
|||
{"zero unbond", addrs[0], addrs[1], "0.0", false},
|
||||
{"invalid decimal", addrs[0], addrs[0], "sunny", false},
|
||||
{"empty delegator", emptyAddr, addrs[0], "0.1", false},
|
||||
{"empty candidate", addrs[0], emptyAddr, "0.1", false},
|
||||
{"empty validator", addrs[0], emptyAddr, "0.1", false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
msg := NewMsgUnbond(tc.delegatorAddr, tc.candidateAddr, tc.shares)
|
||||
msg := NewMsgUnbond(tc.delegatorAddr, tc.validatorAddr, tc.shares)
|
||||
if tc.expectPass {
|
||||
assert.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
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 {
|
||||
bz1 := cdcEmpty.MustMarshalBinary(&p)
|
||||
bz2 := cdcEmpty.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
}
|
175
x/stake/pool.go
175
x/stake/pool.go
|
@ -1,13 +1,65 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Pool - dynamic parameters of the current state
|
||||
type Pool struct {
|
||||
LooseUnbondedTokens int64 `json:"loose_unbonded_tokens"` // tokens not associated with any validator
|
||||
UnbondedTokens int64 `json:"unbonded_tokens"` // reserve of unbonded tokens held with validators
|
||||
UnbondingTokens int64 `json:"unbonding_tokens"` // tokens moving from bonded to unbonded pool
|
||||
BondedTokens int64 `json:"bonded_tokens"` // reserve of bonded tokens
|
||||
UnbondedShares sdk.Rat `json:"unbonded_shares"` // sum of all shares distributed for the Unbonded Pool
|
||||
UnbondingShares sdk.Rat `json:"unbonding_shares"` // shares moving from Bonded to Unbonded Pool
|
||||
BondedShares sdk.Rat `json:"bonded_shares"` // sum of all shares distributed for the Bonded Pool
|
||||
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 {
|
||||
bz1 := cdcEmpty.MustMarshalBinary(&p)
|
||||
bz2 := cdcEmpty.MustMarshalBinary(&p2)
|
||||
return bytes.Equal(bz1, bz2)
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
LooseUnbondedTokens: 0,
|
||||
BondedTokens: 0,
|
||||
UnbondingTokens: 0,
|
||||
UnbondedTokens: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondingShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
DateLastCommissionReset: 0,
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// Sum total of all staking tokens in the pool
|
||||
func (p Pool) TokenSupply() int64 {
|
||||
return p.LooseUnbondedTokens + p.UnbondedTokens + p.UnbondingTokens + p.BondedTokens
|
||||
}
|
||||
|
||||
//____________________________________________________________________
|
||||
|
||||
// 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)
|
||||
if p.TokenSupply() > 0 {
|
||||
return sdk.NewRat(p.BondedTokens, p.TokenSupply())
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
@ -17,102 +69,65 @@ 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 unbonded tokens held in candidates per issued share
|
||||
// get the exchange rate of unbonding tokens held in validators per issued share
|
||||
func (p Pool) unbondingShareExRate() sdk.Rat {
|
||||
if p.UnbondingShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondingTokens).Quo(p.UnbondingShares)
|
||||
}
|
||||
|
||||
// get the exchange rate of unbonded tokens held in validators per issued share
|
||||
func (p Pool) unbondedShareExRate() sdk.Rat {
|
||||
if p.UnbondedShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return sdk.NewRat(p.UnbondedPool).Quo(p.UnbondedShares)
|
||||
}
|
||||
|
||||
// move a candidates asset pool from bonded to unbonded pool
|
||||
func (p Pool) bondedToUnbondedPool(candidate Candidate) (Pool, Candidate) {
|
||||
|
||||
// replace bonded shares with unbonded shares
|
||||
p, tokens := p.removeSharesBonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensUnbonded(tokens)
|
||||
candidate.Status = Unbonded
|
||||
return p, candidate
|
||||
}
|
||||
|
||||
// move a candidates asset pool from unbonded to bonded pool
|
||||
func (p Pool) unbondedToBondedPool(candidate Candidate) (Pool, Candidate) {
|
||||
|
||||
// replace unbonded shares with bonded shares
|
||||
p, tokens := p.removeSharesUnbonded(candidate.Assets)
|
||||
p, candidate.Assets = p.addTokensBonded(tokens)
|
||||
candidate.Status = Bonded
|
||||
return p, candidate
|
||||
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.BondedPool += 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.BondedPool = p.BondedPool - 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) addTokensUnbonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.unbondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondedShares = p.UnbondedShares.Add(issuedSharesAmount)
|
||||
p.UnbondedTokens += amount
|
||||
return p, NewUnbondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
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
|
||||
p.UnbondedTokens -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
||||
//_______________________________________________________________________
|
||||
|
||||
// add tokens to a candidate
|
||||
func (p Pool) candidateAddTokens(candidate Candidate,
|
||||
amount int64) (p2 Pool, candidate2 Candidate, issuedDelegatorShares sdk.Rat) {
|
||||
|
||||
exRate := candidate.delegatorShareExRate()
|
||||
|
||||
var receivedGlobalShares sdk.Rat
|
||||
if candidate.Status == Bonded {
|
||||
p, receivedGlobalShares = p.addTokensBonded(amount)
|
||||
} else {
|
||||
p, receivedGlobalShares = p.addTokensUnbonded(amount)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Add(receivedGlobalShares)
|
||||
|
||||
issuedDelegatorShares = exRate.Mul(receivedGlobalShares)
|
||||
candidate.Liabilities = candidate.Liabilities.Add(issuedDelegatorShares)
|
||||
|
||||
return p, candidate, issuedDelegatorShares
|
||||
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.unbondingShareExRate()) // tokens * (shares/tokens)
|
||||
p.UnbondingShares = p.UnbondingShares.Add(issuedSharesAmount)
|
||||
p.UnbondingTokens += amount
|
||||
return p, NewUnbondingShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
// remove shares from a candidate
|
||||
func (p Pool) candidateRemoveShares(candidate Candidate,
|
||||
shares sdk.Rat) (p2 Pool, candidate2 Candidate, createdCoins int64) {
|
||||
|
||||
//exRate := candidate.delegatorShareExRate() //XXX make sure not used
|
||||
|
||||
globalPoolSharesToRemove := candidate.delegatorShareExRate().Mul(shares)
|
||||
if candidate.Status == Bonded {
|
||||
p, createdCoins = p.removeSharesBonded(globalPoolSharesToRemove)
|
||||
} else {
|
||||
p, createdCoins = p.removeSharesUnbonded(globalPoolSharesToRemove)
|
||||
}
|
||||
candidate.Assets = candidate.Assets.Sub(globalPoolSharesToRemove)
|
||||
candidate.Liabilities = candidate.Liabilities.Sub(shares)
|
||||
return p, candidate, createdCoins
|
||||
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) addTokensBonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.bondedShareExRate()) // tokens * (shares/tokens)
|
||||
p.BondedShares = p.BondedShares.Add(issuedSharesAmount)
|
||||
p.BondedTokens += amount
|
||||
return p, NewBondedShares(issuedSharesAmount)
|
||||
}
|
||||
|
||||
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 -= removedTokens
|
||||
return p, removedTokens
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -14,21 +12,22 @@ import (
|
|||
func TestBondedRatio(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.TotalSupply = 3
|
||||
pool.BondedPool = 2
|
||||
pool.LooseUnbondedTokens = 1
|
||||
pool.BondedTokens = 2
|
||||
|
||||
// bonded pool / total supply
|
||||
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||
pool.TotalSupply = 0
|
||||
|
||||
// avoids divide-by-zero
|
||||
pool.LooseUnbondedTokens = 0
|
||||
pool.BondedTokens = 0
|
||||
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat())
|
||||
}
|
||||
|
||||
func TestBondedShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.BondedPool = 3
|
||||
pool.BondedTokens = 3
|
||||
pool.BondedShares = sdk.NewRat(10)
|
||||
|
||||
// bonded pool / bonded shares
|
||||
|
@ -39,10 +38,24 @@ func TestBondedShareExRate(t *testing.T) {
|
|||
require.Equal(t, pool.bondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondingShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.UnbondingTokens = 3
|
||||
pool.UnbondingShares = sdk.NewRat(10)
|
||||
|
||||
// unbonding pool / unbonding shares
|
||||
require.Equal(t, pool.unbondingShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||
pool.UnbondingShares = sdk.ZeroRat()
|
||||
|
||||
// avoids divide-by-zero
|
||||
require.Equal(t, pool.unbondingShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestUnbondedShareExRate(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
pool.UnbondedPool = 3
|
||||
pool.UnbondedTokens = 3
|
||||
pool.UnbondedShares = sdk.NewRat(10)
|
||||
|
||||
// unbonded pool / unbonded shares
|
||||
|
@ -53,61 +66,6 @@ func TestUnbondedShareExRate(t *testing.T) {
|
|||
require.Equal(t, pool.unbondedShareExRate(), sdk.OneRat())
|
||||
}
|
||||
|
||||
func TestBondedToUnbondedPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.OneRat(),
|
||||
Liabilities: sdk.OneRat(),
|
||||
}
|
||||
poolB, candB := poolA.bondedToUnbondedPool(candA)
|
||||
|
||||
// status unbonded
|
||||
assert.Equal(t, candB.Status, Unbonded)
|
||||
// same exchange rate, assets unchanged
|
||||
assert.Equal(t, candB.Assets, candA.Assets)
|
||||
// bonded pool decreased
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-candA.Assets.Evaluate())
|
||||
// unbonded pool increased
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+candA.Assets.Evaluate())
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||
}
|
||||
|
||||
func TestUnbonbedtoBondedPool(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.OneRat(),
|
||||
Liabilities: sdk.OneRat(),
|
||||
}
|
||||
candA.Status = Unbonded
|
||||
poolB, candB := poolA.unbondedToBondedPool(candA)
|
||||
|
||||
// status bonded
|
||||
assert.Equal(t, candB.Status, Bonded)
|
||||
// same exchange rate, assets unchanged
|
||||
assert.Equal(t, candB.Assets, candA.Assets)
|
||||
// bonded pool increased
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+candA.Assets.Evaluate())
|
||||
// unbonded pool decreased
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-candA.Assets.Evaluate())
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
|
||||
}
|
||||
|
||||
func TestAddTokensBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
|
@ -117,11 +75,11 @@ func TestAddTokensBonded(t *testing.T) {
|
|||
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB))
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+10)
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens+10)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedPool)))
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesBonded(t *testing.T) {
|
||||
|
@ -134,10 +92,10 @@ func TestRemoveSharesBonded(t *testing.T) {
|
|||
|
||||
// correct changes to bonded shares and bonded pool
|
||||
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-tokensB)
|
||||
assert.Equal(t, poolB.BondedTokens, poolA.BondedTokens-tokensB)
|
||||
|
||||
// same number of bonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedPool)))
|
||||
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
||||
}
|
||||
|
||||
func TestAddTokensUnbonded(t *testing.T) {
|
||||
|
@ -149,11 +107,11 @@ func TestAddTokensUnbonded(t *testing.T) {
|
|||
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat())
|
||||
|
||||
// correct changes to unbonded shares and unbonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB))
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+10)
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB.Amount))
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens+10)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedPool)))
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
||||
}
|
||||
|
||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
||||
|
@ -166,359 +124,8 @@ func TestRemoveSharesUnbonded(t *testing.T) {
|
|||
|
||||
// correct changes to unbonded shares and bonded pool
|
||||
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
|
||||
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-tokensB)
|
||||
assert.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens-tokensB)
|
||||
|
||||
// same number of unbonded shares / tokens when exchange rate is one
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedPool)))
|
||||
}
|
||||
|
||||
func TestCandidateAddTokens(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedPool = candA.Assets.Evaluate()
|
||||
poolA.BondedShares = candA.Assets
|
||||
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
poolB, candB, sharesB := poolA.candidateAddTokens(candA, 10)
|
||||
|
||||
// shares were issued
|
||||
assert.Equal(t, sdk.NewRat(10).Mul(candA.delegatorShareExRate()), sharesB)
|
||||
// pool shares were added
|
||||
assert.Equal(t, candB.Assets, candA.Assets.Add(sdk.NewRat(10)))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.BondedPool, 10+poolA.BondedPool)
|
||||
}
|
||||
|
||||
func TestCandidateRemoveShares(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
candA := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: sdk.NewRat(9),
|
||||
Liabilities: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedPool = candA.Assets.Evaluate()
|
||||
poolA.BondedShares = candA.Assets
|
||||
assert.Equal(t, candA.delegatorShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
poolB, candB, coinsB := poolA.candidateRemoveShares(candA, sdk.NewRat(10))
|
||||
|
||||
// coins were created
|
||||
assert.Equal(t, coinsB, int64(10))
|
||||
// pool shares were removed
|
||||
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
|
||||
|
||||
// specific case from random tests
|
||||
assets := sdk.NewRat(5102)
|
||||
liabilities := sdk.NewRat(115)
|
||||
cand := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: assets,
|
||||
Liabilities: liabilities,
|
||||
}
|
||||
pool := Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.NewRat(248305),
|
||||
UnbondedShares: sdk.NewRat(232147),
|
||||
BondedPool: 248305,
|
||||
UnbondedPool: 232147,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
shares := sdk.NewRat(29)
|
||||
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||
newPool, _, tokens := pool.candidateRemoveShares(cand, shares)
|
||||
require.Equal(t,
|
||||
tokens+newPool.UnbondedPool+newPool.BondedPool,
|
||||
pool.BondedPool+pool.UnbondedPool,
|
||||
"Tokens were not conserved: %s", msg)
|
||||
}
|
||||
|
||||
/////////////////////////////////////
|
||||
// TODO Make all random tests less obfuscated!
|
||||
|
||||
// generate a random candidate
|
||||
func randomCandidate(r *rand.Rand) Candidate {
|
||||
var status CandidateStatus
|
||||
if r.Float64() < float64(0.5) {
|
||||
status = Bonded
|
||||
} else {
|
||||
status = Unbonded
|
||||
}
|
||||
assets := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
liabilities := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
return Candidate{
|
||||
Status: status,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: assets,
|
||||
Liabilities: liabilities,
|
||||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
func randomSetup(r *rand.Rand, numCandidates int) (Pool, Candidates) {
|
||||
pool := Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedPool: 0,
|
||||
UnbondedPool: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
|
||||
candidates := make([]Candidate, numCandidates)
|
||||
for i := 0; i < numCandidates; i++ {
|
||||
candidate := randomCandidate(r)
|
||||
if candidate.Status == Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
|
||||
pool.BondedPool += candidate.Assets.Evaluate()
|
||||
} else if candidate.Status == Unbonded {
|
||||
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
|
||||
pool.UnbondedPool += candidate.Assets.Evaluate()
|
||||
}
|
||||
candidates[i] = candidate
|
||||
}
|
||||
return pool, candidates
|
||||
}
|
||||
|
||||
// any operation that transforms staking state
|
||||
// takes in RNG instance, pool, candidate
|
||||
// returns updated pool, updated candidate, delta tokens, descriptive message
|
||||
type Operation func(r *rand.Rand, p Pool, c Candidate) (Pool, Candidate, int64, string)
|
||||
|
||||
// operation: bond or unbond a candidate depending on current status
|
||||
func OpBondOrUnbond(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
var msg string
|
||||
if cand.Status == Bonded {
|
||||
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
p, cand = p.bondedToUnbondedPool(cand)
|
||||
|
||||
} else if cand.Status == Unbonded {
|
||||
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
p, cand = p.unbondedToBondedPool(cand)
|
||||
}
|
||||
return p, cand, 0, msg
|
||||
}
|
||||
|
||||
// operation: add a random number of tokens to a candidate
|
||||
func OpAddTokens(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
tokens := int64(r.Int31n(1000))
|
||||
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
p, cand, _ = p.candidateAddTokens(cand, tokens)
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||
}
|
||||
|
||||
// operation: remove a random number of shares from a candidate
|
||||
func OpRemoveShares(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||
var shares sdk.Rat
|
||||
for {
|
||||
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||
if shares.LT(cand.Liabilities) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Removed %v shares from candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
shares, cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
|
||||
p, cand, tokens := p.candidateRemoveShares(cand, shares)
|
||||
return p, cand, tokens, msg
|
||||
}
|
||||
|
||||
// pick a random staking operation
|
||||
func randomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
OpBondOrUnbond,
|
||||
OpAddTokens,
|
||||
OpRemoveShares,
|
||||
}
|
||||
r.Shuffle(len(operations), func(i, j int) {
|
||||
operations[i], operations[j] = operations[j], operations[i]
|
||||
})
|
||||
return operations[0]
|
||||
}
|
||||
|
||||
// ensure invariants that should always be true are true
|
||||
func assertInvariants(t *testing.T, msg string,
|
||||
pOrig Pool, cOrig Candidates, pMod Pool, cMods Candidates, tokens int64) {
|
||||
|
||||
// total tokens conserved
|
||||
require.Equal(t,
|
||||
pOrig.UnbondedPool+pOrig.BondedPool,
|
||||
pMod.UnbondedPool+pMod.BondedPool+tokens,
|
||||
"Tokens not conserved - msg: %v\n, pOrig.BondedShares: %v, pOrig.UnbondedShares: %v, pMod.BondedShares: %v, pMod.UnbondedShares: %v, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
|
||||
msg,
|
||||
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||
pMod.BondedShares, pMod.UnbondedShares,
|
||||
pOrig.UnbondedPool, pOrig.BondedPool,
|
||||
pMod.UnbondedPool, pMod.BondedPool, tokens)
|
||||
|
||||
// nonnegative bonded shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative bonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative unbonded shares
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative unbonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative bonded ex rate
|
||||
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||
msg, pMod.bondedShareExRate().Evaluate())
|
||||
|
||||
// nonnegative unbonded ex rate
|
||||
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||
msg, pMod.unbondedShareExRate().Evaluate())
|
||||
|
||||
for _, cMod := range cMods {
|
||||
|
||||
// nonnegative ex rate
|
||||
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.Address: %s)",
|
||||
msg,
|
||||
cMod.delegatorShareExRate(),
|
||||
cMod.Address,
|
||||
)
|
||||
|
||||
// nonnegative assets
|
||||
require.False(t, cMod.Assets.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative candidate.Assets: %v (candidate.Liabilities: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||
msg,
|
||||
cMod.Assets,
|
||||
cMod.Liabilities,
|
||||
cMod.delegatorShareExRate(),
|
||||
cMod.Address,
|
||||
)
|
||||
|
||||
// nonnegative liabilities
|
||||
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %v (candidate.Assets: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||
msg,
|
||||
cMod.Liabilities,
|
||||
cMod.Assets,
|
||||
cMod.delegatorShareExRate(),
|
||||
cMod.Address,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPossibleOverflow(t *testing.T) {
|
||||
assets := sdk.NewRat(2159)
|
||||
liabilities := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||
cand := Candidate{
|
||||
Status: Bonded,
|
||||
Address: addrs[0],
|
||||
PubKey: pks[0],
|
||||
Assets: assets,
|
||||
Liabilities: liabilities,
|
||||
}
|
||||
pool := Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: assets,
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedPool: assets.Evaluate(),
|
||||
UnbondedPool: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
tokens := int64(71)
|
||||
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||
_, newCandidate, _ := pool.candidateAddTokens(cand, tokens)
|
||||
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
require.False(t, newCandidate.delegatorShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative delegatorShareExRate(): %v",
|
||||
msg, newCandidate.delegatorShareExRate())
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random single-candidate state, assert invariants hold
|
||||
func TestSingleCandidateIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(41))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, candidatesOrig := randomSetup(r, 1)
|
||||
require.Equal(t, 1, len(candidatesOrig))
|
||||
|
||||
// sanity check
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, candidatesOrig,
|
||||
poolOrig, candidatesOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[0])
|
||||
|
||||
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||
copy(candidatesMod[:], candidatesOrig[:])
|
||||
require.Equal(t, 1, len(candidatesOrig), "j %v", j)
|
||||
require.Equal(t, 1, len(candidatesMod), "j %v", j)
|
||||
candidatesMod[0] = candidateMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, candidatesOrig,
|
||||
poolMod, candidatesMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
candidatesOrig = candidatesMod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random multi-candidate state, assert invariants hold
|
||||
func TestMultiCandidateIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(42))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, candidatesOrig := randomSetup(r, 100)
|
||||
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, candidatesOrig,
|
||||
poolOrig, candidatesOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
index := int(r.Int31n(int32(len(candidatesOrig))))
|
||||
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[index])
|
||||
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||
copy(candidatesMod[:], candidatesOrig[:])
|
||||
candidatesMod[index] = candidateMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, candidatesOrig,
|
||||
poolMod, candidatesMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
candidatesOrig = candidatesMod
|
||||
|
||||
}
|
||||
}
|
||||
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// kind of shares
|
||||
type PoolShareKind byte
|
||||
|
||||
// pool shares held by a validator
|
||||
type PoolShares struct {
|
||||
Status sdk.BondStatus `json:"status"`
|
||||
Amount sdk.Rat `json:"amount"` // total shares of type ShareKind
|
||||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
func (s PoolShares) Equal(s2 PoolShares) bool {
|
||||
return s.Status == s2.Status &&
|
||||
s.Amount.Equal(s2.Amount)
|
||||
}
|
||||
|
||||
func NewUnbondedShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Unbonded,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUnbondingShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Unbonding,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
func NewBondedShares(amount sdk.Rat) PoolShares {
|
||||
return PoolShares{
|
||||
Status: sdk.Bonded,
|
||||
Amount: amount,
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// amount of unbonded shares
|
||||
func (s PoolShares) Unbonded() sdk.Rat {
|
||||
if s.Status == sdk.Unbonded {
|
||||
return s.Amount
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// amount of unbonding shares
|
||||
func (s PoolShares) Unbonding() sdk.Rat {
|
||||
if s.Status == sdk.Unbonding {
|
||||
return s.Amount
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
// amount of bonded shares
|
||||
func (s PoolShares) Bonded() sdk.Rat {
|
||||
if s.Status == sdk.Bonded {
|
||||
return s.Amount
|
||||
}
|
||||
return sdk.ZeroRat()
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// equivalent amount of shares if the shares were unbonded
|
||||
func (s PoolShares) ToUnbonded(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.bondedShareExRate().Quo(p.unbondedShareExRate()) // (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondedshr/bondedshr = unbondedshr
|
||||
case sdk.Unbonding:
|
||||
exRate := p.unbondingShareExRate().Quo(p.unbondedShareExRate()) // (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
||||
amount = s.Amount.Mul(exRate) // unbondingshr*unbondedshr/unbondingshr = unbondedshr
|
||||
case sdk.Unbonded:
|
||||
amount = s.Amount
|
||||
}
|
||||
return NewUnbondedShares(amount)
|
||||
}
|
||||
|
||||
// equivalent amount of shares if the shares were unbonding
|
||||
func (s PoolShares) ToUnbonding(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
exRate := p.bondedShareExRate().Quo(p.unbondingShareExRate()) // (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
||||
amount = s.Amount.Mul(exRate) // bondedshr*unbondingshr/bondedshr = unbondingshr
|
||||
case sdk.Unbonding:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonded:
|
||||
exRate := p.unbondedShareExRate().Quo(p.unbondingShareExRate()) // (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
||||
amount = s.Amount.Mul(exRate) // unbondedshr*unbondingshr/unbondedshr = unbondingshr
|
||||
}
|
||||
return NewUnbondingShares(amount)
|
||||
}
|
||||
|
||||
// equivalent amount of shares if the shares were bonded
|
||||
func (s PoolShares) ToBonded(p Pool) PoolShares {
|
||||
var amount sdk.Rat
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
amount = s.Amount
|
||||
case sdk.Unbonding:
|
||||
exRate := p.unbondingShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
case sdk.Unbonded:
|
||||
exRate := p.unbondedShareExRate().Quo(p.bondedShareExRate()) // (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
||||
amount = s.Amount.Mul(exRate) // ubshr*bshr/ubshr = bshr
|
||||
}
|
||||
return NewUnbondedShares(amount)
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// get the equivalent amount of tokens contained by the shares
|
||||
func (s PoolShares) Tokens(p Pool) sdk.Rat {
|
||||
switch s.Status {
|
||||
case sdk.Bonded:
|
||||
return p.unbondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares
|
||||
case sdk.Unbonding:
|
||||
return p.unbondedShareExRate().Mul(s.Amount)
|
||||
case sdk.Unbonded:
|
||||
return p.unbondedShareExRate().Mul(s.Amount)
|
||||
default:
|
||||
panic("unknown share kind")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
# Stores
|
||||
|
||||
This document provided a bit more insight as to the purpose of several related
|
||||
prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
|
||||
|
||||
|
||||
## Validators
|
||||
- Prefix Key Space: ValidatorsKey
|
||||
- Key/Sort: Validator Owner Address
|
||||
- Value: Validator Object
|
||||
- Contains: All Validator records independent of being bonded or not
|
||||
- Used For: Retrieve validator from owner address, general validator retrieval
|
||||
|
||||
## Validators By Power
|
||||
- Prefix Key Space: ValidatorsByPowerKey
|
||||
- Key/Sort: Validator Power (equivalent bonded shares) then Block
|
||||
Height then Transaction Order
|
||||
- Value: Validator Owner Address
|
||||
- Contains: All Validator records independent of being bonded or not
|
||||
- Used For: Determining who the top validators are whom should be bonded
|
||||
|
||||
## Validators Cliff Power
|
||||
- Prefix Key Space: ValidatorCliffKey
|
||||
- Key/Sort: single-record
|
||||
- Value: Validator Power Key (as above store)
|
||||
- Contains: The cliff validator (ex. 100th validator) power
|
||||
- Used For: Efficient updates to validator status
|
||||
|
||||
## Validators Bonded
|
||||
- Prefix Key Space: ValidatorsBondedKey
|
||||
- Key/Sort: Validator PubKey Address (NOTE same as Tendermint)
|
||||
- Value: Validator Owner Address
|
||||
- Contains: Only currently bonded Validators
|
||||
- Used For: Retrieving the list of all currently bonded validators when updating
|
||||
for a new validator entering the validator set we may want to loop
|
||||
through this set to determine who we've kicked out.
|
||||
retrieving validator by tendermint index
|
||||
|
||||
## Tendermint Updates
|
||||
- Prefix Key Space: TendermintUpdatesKey
|
||||
- Key/Sort: Validator Owner Address
|
||||
- Value: Tendermint ABCI Validator
|
||||
- Contains: Validators are queued to affect the consensus validation set in Tendermint
|
||||
- Used For: Informing Tendermint of the validator set updates, is used only intra-block, as the
|
||||
updates are applied then cleared on endblock
|
|
@ -1,7 +1,6 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
|
@ -52,70 +51,14 @@ var (
|
|||
emptyPubkey crypto.PubKey
|
||||
)
|
||||
|
||||
func validatorsEqual(b1, b2 Validator) bool {
|
||||
return bytes.Equal(b1.Address, b2.Address) &&
|
||||
b1.PubKey.Equals(b2.PubKey) &&
|
||||
b1.Power.Equal(b2.Power) &&
|
||||
b1.Height == b2.Height &&
|
||||
b1.Counter == b2.Counter
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
// intended to be used with require/assert: require.True(ValEq(...))
|
||||
func ValEq(t *testing.T, exp, got Validator) (*testing.T, bool, string, Validator, Validator) {
|
||||
return t, exp.equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||
}
|
||||
|
||||
func candidatesEqual(c1, c2 Candidate) bool {
|
||||
return c1.Status == c2.Status &&
|
||||
c1.PubKey.Equals(c2.PubKey) &&
|
||||
bytes.Equal(c1.Address, c2.Address) &&
|
||||
c1.Assets.Equal(c2.Assets) &&
|
||||
c1.Liabilities.Equal(c2.Liabilities) &&
|
||||
c1.Description == c2.Description
|
||||
}
|
||||
|
||||
func bondsEqual(b1, b2 DelegatorBond) bool {
|
||||
return bytes.Equal(b1.DelegatorAddr, b2.DelegatorAddr) &&
|
||||
bytes.Equal(b1.CandidateAddr, b2.CandidateAddr) &&
|
||||
b1.Height == b2.Height &&
|
||||
b1.Shares.Equal(b2.Shares)
|
||||
}
|
||||
|
||||
// default params for testing
|
||||
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",
|
||||
}
|
||||
}
|
||||
|
||||
// initial pool for testing
|
||||
func initialPool() Pool {
|
||||
return Pool{
|
||||
TotalSupply: 0,
|
||||
BondedShares: sdk.ZeroRat(),
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedPool: 0,
|
||||
UnbondedPool: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
}
|
||||
|
||||
// get raw genesis raw message for testing
|
||||
func GetDefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Pool: initialPool(),
|
||||
Params: defaultParams(),
|
||||
}
|
||||
}
|
||||
|
||||
// XXX reference the common declaration of this function
|
||||
func subspace(prefix []byte) (start, end []byte) {
|
||||
end = make([]byte, len(prefix))
|
||||
copy(end, prefix)
|
||||
end[len(end)-1]++
|
||||
return prefix, end
|
||||
}
|
||||
//_______________________________________________________________________________________
|
||||
|
||||
func makeTestCodec() *wire.Codec {
|
||||
var cdc = wire.NewCodec()
|
||||
|
@ -149,12 +92,13 @@ func paramsNoInflation() Params {
|
|||
|
||||
// hogpodge of all sorts of input required for testing
|
||||
func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, sdk.AccountMapper, Keeper) {
|
||||
db := dbm.NewMemDB()
|
||||
keyStake := sdk.NewKVStoreKey("stake")
|
||||
keyMain := keyStake //sdk.NewKVStoreKey("main") //TODO fix multistore
|
||||
keyAcc := sdk.NewKVStoreKey("acc")
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
ms := store.NewCommitMultiStore(db)
|
||||
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
|
||||
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
|
||||
err := ms.LoadLatestVersion()
|
||||
require.Nil(t, err)
|
||||
|
||||
|
@ -162,13 +106,13 @@ func createTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
|
|||
cdc := makeTestCodec()
|
||||
accountMapper := auth.NewAccountMapper(
|
||||
cdc, // amino codec
|
||||
keyMain, // target store
|
||||
keyAcc, // target store
|
||||
&auth.BaseAccount{}, // prototype
|
||||
)
|
||||
ck := bank.NewKeeper(accountMapper)
|
||||
keeper := NewKeeper(cdc, keyStake, ck, DefaultCodespace)
|
||||
keeper.setPool(ctx, initialPool())
|
||||
keeper.setParams(ctx, defaultParams())
|
||||
keeper.setNewParams(ctx, defaultParams())
|
||||
|
||||
// fill all the addresses with some coins
|
||||
for _, addr := range addrs {
|
||||
|
|
|
@ -26,12 +26,14 @@ func (k Keeper) Tick(ctx sdk.Context) (change []abci.Validator) {
|
|||
// save the params
|
||||
k.setPool(ctx, p)
|
||||
|
||||
// reset the counter
|
||||
k.setCounter(ctx, 0)
|
||||
// reset the intra-transaction counter
|
||||
k.setIntraTxCounter(ctx, 0)
|
||||
|
||||
change = k.getAccUpdateValidators(ctx)
|
||||
// calculate validator set changes
|
||||
change = k.getTendermintUpdates(ctx)
|
||||
k.clearTendermintUpdates(ctx)
|
||||
|
||||
return
|
||||
return change
|
||||
}
|
||||
|
||||
// process provisions for an hour period
|
||||
|
@ -44,9 +46,8 @@ func (k Keeper) processProvisions(ctx sdk.Context) Pool {
|
|||
// 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 := pool.Inflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat).Evaluate()
|
||||
pool.BondedPool += provisions
|
||||
pool.TotalSupply += provisions
|
||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate()
|
||||
pool.BondedTokens += provisions
|
||||
return pool
|
||||
}
|
||||
|
||||
|
|
|
@ -15,39 +15,39 @@ func TestGetInflation(t *testing.T) {
|
|||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
||||
|
||||
// Governing Mechanism:
|
||||
// bondedRatio = BondedPool / TotalSupply
|
||||
// bondedRatio = BondedTokens / TotalSupply
|
||||
// inflationRateChangePerYear = (1- bondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
setBondedPool, setTotalSupply int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
name string
|
||||
setBondedTokens, setLooseTokens int64
|
||||
setInflation, expectedChange sdk.Rat
|
||||
}{
|
||||
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||
{"test 1", 0, 0, sdk.NewRat(7, 100), params.InflationRateChange.Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 100% bonded, starting at 20% inflation and being reduced
|
||||
// (1 - (1/0.67))*(0.13/8667)
|
||||
{"test 2", 1, 1, sdk.NewRat(20, 100),
|
||||
{"test 2", 1, 0, sdk.NewRat(20, 100),
|
||||
sdk.OneRat().Sub(sdk.OneRat().Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// 50% bonded, starting at 10% inflation and being increased
|
||||
{"test 3", 1, 2, sdk.NewRat(10, 100),
|
||||
{"test 3", 1, 1, sdk.NewRat(10, 100),
|
||||
sdk.OneRat().Sub(sdk.NewRat(1, 2).Quo(params.GoalBonded)).Mul(params.InflationRateChange).Quo(hrsPerYrRat).Round(precision)},
|
||||
|
||||
// test 7% minimum stop (testing with 100% bonded)
|
||||
{"test 4", 1, 1, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", 1, 1, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
{"test 4", 1, 0, sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||
{"test 5", 1, 0, sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||
|
||||
// test 20% maximum stop (testing with 0% bonded)
|
||||
{"test 6", 0, 0, sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||
{"test 7", 0, 0, sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||
|
||||
// perfect balance shouldn't change inflation
|
||||
{"test 8", 67, 100, sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
{"test 8", 67, 33, sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
pool.BondedPool, pool.TotalSupply = tc.setBondedPool, tc.setTotalSupply
|
||||
pool.BondedTokens, pool.LooseUnbondedTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||
pool.Inflation = tc.setInflation
|
||||
keeper.setPool(ctx, pool)
|
||||
|
||||
|
@ -62,72 +62,79 @@ func TestGetInflation(t *testing.T) {
|
|||
func TestProcessProvisions(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
params := defaultParams()
|
||||
params.MaxValidators = 2
|
||||
keeper.setParams(ctx, params)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
// create some candidates some bonded, some unbonded
|
||||
candidates := make([]Candidate, 10)
|
||||
for i := 0; i < 10; i++ {
|
||||
c := Candidate{
|
||||
Status: Unbonded,
|
||||
PubKey: pks[i],
|
||||
Address: addrs[i],
|
||||
Assets: sdk.NewRat(0),
|
||||
Liabilities: sdk.NewRat(0),
|
||||
}
|
||||
if i < 5 {
|
||||
c.Status = Bonded
|
||||
}
|
||||
mintedTokens := int64((i + 1) * 10000000)
|
||||
pool.TotalSupply += mintedTokens
|
||||
pool, c, _ = pool.candidateAddTokens(c, mintedTokens)
|
||||
|
||||
keeper.setCandidate(ctx, c)
|
||||
candidates[i] = c
|
||||
}
|
||||
keeper.setPool(ctx, pool)
|
||||
var totalSupply int64 = 550000000
|
||||
var tokenSupply int64 = 550000000
|
||||
var bondedShares int64 = 150000000
|
||||
var unbondedShares int64 = 400000000
|
||||
assert.Equal(t, totalSupply, pool.TotalSupply)
|
||||
assert.Equal(t, bondedShares, pool.BondedPool)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||
|
||||
// create some validators some bonded, some unbonded
|
||||
var validators [5]Validator
|
||||
validators[0] = NewValidator(addrs[0], pks[0], Description{})
|
||||
validators[0], pool, _ = validators[0].addTokensFromDel(pool, 150000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[0] = keeper.updateValidator(ctx, validators[0])
|
||||
pool = keeper.GetPool(ctx)
|
||||
require.Equal(t, bondedShares, pool.BondedTokens, "%v", pool)
|
||||
|
||||
validators[1] = NewValidator(addrs[1], pks[1], Description{})
|
||||
validators[1], pool, _ = validators[1].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[1] = keeper.updateValidator(ctx, validators[1])
|
||||
validators[2] = NewValidator(addrs[2], pks[2], Description{})
|
||||
validators[2], pool, _ = validators[2].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[2] = keeper.updateValidator(ctx, validators[2])
|
||||
validators[3] = NewValidator(addrs[3], pks[3], Description{})
|
||||
validators[3], pool, _ = validators[3].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[3] = keeper.updateValidator(ctx, validators[3])
|
||||
validators[4] = NewValidator(addrs[4], pks[4], Description{})
|
||||
validators[4], pool, _ = validators[4].addTokensFromDel(pool, 100000000)
|
||||
keeper.setPool(ctx, pool)
|
||||
validators[4] = keeper.updateValidator(ctx, validators[4])
|
||||
|
||||
assert.Equal(t, tokenSupply, pool.TokenSupply())
|
||||
assert.Equal(t, bondedShares, pool.BondedTokens)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedTokens)
|
||||
|
||||
// initial bonded ratio ~ 27%
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, totalSupply)), "%v", pool.bondedRatio())
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(bondedShares, tokenSupply)), "%v", pool.bondedRatio())
|
||||
|
||||
// test the value of candidate shares
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Equal(sdk.OneRat()), "%v", pool.bondedShareExRate())
|
||||
|
||||
initialSupply := pool.TotalSupply
|
||||
initialUnbonded := pool.TotalSupply - pool.BondedPool
|
||||
initialSupply := pool.TokenSupply()
|
||||
initialUnbonded := pool.TokenSupply() - pool.BondedTokens
|
||||
|
||||
// process the provisions a year
|
||||
for hr := 0; hr < 8766; hr++ {
|
||||
pool := keeper.GetPool(ctx)
|
||||
expInflation := keeper.nextInflation(ctx).Round(1000000000)
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TotalSupply)).Quo(hrsPerYrRat)).Evaluate()
|
||||
startBondedPool := pool.BondedPool
|
||||
startTotalSupply := pool.TotalSupply
|
||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate()
|
||||
startBondedTokens := pool.BondedTokens
|
||||
startTotalSupply := pool.TokenSupply()
|
||||
pool = keeper.processProvisions(ctx)
|
||||
keeper.setPool(ctx, pool)
|
||||
//fmt.Printf("hr %v, startBondedPool %v, expProvisions %v, pool.BondedPool %v\n", hr, startBondedPool, expProvisions, pool.BondedPool)
|
||||
require.Equal(t, startBondedPool+expProvisions, pool.BondedPool, "hr %v", hr)
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TotalSupply)
|
||||
//fmt.Printf("hr %v, startBondedTokens %v, expProvisions %v, pool.BondedTokens %v\n", hr, startBondedTokens, expProvisions, pool.BondedTokens)
|
||||
require.Equal(t, startBondedTokens+expProvisions, pool.BondedTokens, "hr %v", hr)
|
||||
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
|
||||
}
|
||||
pool = keeper.GetPool(ctx)
|
||||
assert.NotEqual(t, initialSupply, pool.TotalSupply)
|
||||
assert.Equal(t, initialUnbonded, pool.UnbondedPool)
|
||||
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedPool, pool.TotalSupply-pool.BondedPool))
|
||||
assert.NotEqual(t, initialSupply, pool.TokenSupply())
|
||||
assert.Equal(t, initialUnbonded, pool.UnbondedTokens)
|
||||
//panic(fmt.Sprintf("debug total %v, bonded %v, diff %v\n", p.TotalSupply, p.BondedTokens, pool.TokenSupply()-pool.BondedTokens))
|
||||
|
||||
// initial bonded ratio ~ from 27% to 40% increase for bonded holders ownership of total supply
|
||||
assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(211813022, 611813022)), "%v", pool.bondedRatio())
|
||||
|
||||
// global supply
|
||||
assert.Equal(t, int64(611813022), pool.TotalSupply)
|
||||
assert.Equal(t, int64(211813022), pool.BondedPool)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedPool)
|
||||
assert.Equal(t, int64(611813022), pool.TokenSupply())
|
||||
assert.Equal(t, int64(211813022), pool.BondedTokens)
|
||||
assert.Equal(t, unbondedShares, pool.UnbondedTokens)
|
||||
|
||||
// test the value of candidate shares
|
||||
// test the value of validator shares
|
||||
assert.True(t, pool.bondedShareExRate().Mul(sdk.NewRat(bondedShares)).Equal(sdk.NewRat(211813022)), "%v", pool.bondedShareExRate())
|
||||
}
|
||||
|
|
189
x/stake/types.go
189
x/stake/types.go
|
@ -1,189 +0,0 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
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"`
|
||||
Candidates []Candidate `json:"candidates"`
|
||||
Bonds []DelegatorBond `json:"bonds"`
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// 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
|
||||
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 sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||
}
|
||||
|
||||
func (p Pool) equal(p2 Pool) bool {
|
||||
return p.BondedShares.Equal(p2.BondedShares) &&
|
||||
p.UnbondedShares.Equal(p2.UnbondedShares) &&
|
||||
p.Inflation.Equal(p2.Inflation) &&
|
||||
p.TotalSupply == p2.TotalSupply &&
|
||||
p.BondedPool == p2.BondedPool &&
|
||||
p.UnbondedPool == p2.UnbondedPool &&
|
||||
p.InflationLastTime == p2.InflationLastTime
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// CandidateStatus - status of a validator-candidate
|
||||
type CandidateStatus byte
|
||||
|
||||
const (
|
||||
// nolint
|
||||
Bonded CandidateStatus = 0x00
|
||||
Unbonded CandidateStatus = 0x01
|
||||
Revoked CandidateStatus = 0x02
|
||||
)
|
||||
|
||||
// Candidate 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
|
||||
// candidate, the candidate is credited with a DelegatorBond whose number of
|
||||
// bond shares is based on the amount of coins delegated divided by the current
|
||||
// exchange rate. Voting power can be calculated as total bonds multiplied by
|
||||
// exchange rate.
|
||||
type Candidate struct {
|
||||
Status CandidateStatus `json:"status"` // Bonded status
|
||||
Address sdk.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here
|
||||
PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate
|
||||
Assets sdk.Rat `json:"assets"` // total shares of a global hold pools
|
||||
Liabilities sdk.Rat `json:"liabilities"` // total shares issued to a candidate's delegators
|
||||
Description Description `json:"description"` // Description terms for the candidate
|
||||
ValidatorBondHeight int64 `json:"validator_bond_height"` // Earliest height as a bonded validator
|
||||
ValidatorBondCounter int16 `json:"validator_bond_counter"` // Block-local tx index of validator change
|
||||
}
|
||||
|
||||
// Candidates - list of Candidates
|
||||
type Candidates []Candidate
|
||||
|
||||
// NewCandidate - initialize a new candidate
|
||||
func NewCandidate(address sdk.Address, pubKey crypto.PubKey, description Description) Candidate {
|
||||
return Candidate{
|
||||
Status: Unbonded,
|
||||
Address: address,
|
||||
PubKey: pubKey,
|
||||
Assets: sdk.ZeroRat(),
|
||||
Liabilities: sdk.ZeroRat(),
|
||||
Description: description,
|
||||
ValidatorBondHeight: int64(0),
|
||||
ValidatorBondCounter: int16(0),
|
||||
}
|
||||
}
|
||||
|
||||
// Description - description fields for a candidate
|
||||
type Description struct {
|
||||
Moniker string `json:"moniker"`
|
||||
Identity string `json:"identity"`
|
||||
Website string `json:"website"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
|
||||
func NewDescription(moniker, identity, website, details string) Description {
|
||||
return Description{
|
||||
Moniker: moniker,
|
||||
Identity: identity,
|
||||
Website: website,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
// get the exchange rate of global pool shares over delegator shares
|
||||
func (c Candidate) delegatorShareExRate() sdk.Rat {
|
||||
if c.Liabilities.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
return c.Assets.Quo(c.Liabilities)
|
||||
}
|
||||
|
||||
// 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{
|
||||
Address: c.Address,
|
||||
PubKey: c.PubKey,
|
||||
Power: c.Assets,
|
||||
Height: c.ValidatorBondHeight,
|
||||
Counter: c.ValidatorBondCounter,
|
||||
}
|
||||
}
|
||||
|
||||
//XXX updateDescription function
|
||||
//XXX enforce limit to number of description characters
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// Validator is one of the top Candidates
|
||||
type Validator struct {
|
||||
Address sdk.Address `json:"address"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
Power sdk.Rat `json:"voting_power"`
|
||||
Height int64 `json:"height"` // Earliest height as a validator
|
||||
Counter int16 `json:"counter"` // Block-local tx index for resolving equal voting power & height
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: v.PubKey.Bytes(),
|
||||
Power: v.Power.Evaluate(),
|
||||
}
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
// with zero power used for validator updates
|
||||
func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: v.PubKey.Bytes(),
|
||||
Power: 0,
|
||||
}
|
||||
}
|
||||
|
||||
//_________________________________________________________________________
|
||||
|
||||
// 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.
|
||||
// TODO better way of managing space
|
||||
type DelegatorBond struct {
|
||||
DelegatorAddr sdk.Address `json:"delegator_addr"`
|
||||
CandidateAddr sdk.Address `json:"candidate_addr"`
|
||||
Shares sdk.Rat `json:"shares"`
|
||||
Height int64 `json:"height"` // Last height bond updated
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package stake
|
||||
|
||||
// XXX test global state functions, candidate exchange rate functions etc.
|
|
@ -0,0 +1,238 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/wire"
|
||||
abci "github.com/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
)
|
||||
|
||||
// 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
|
||||
// validator, the validator is credited with a Delegation whose number of
|
||||
// bond shares is based on the amount of coins delegated divided by the current
|
||||
// exchange rate. Voting power can be calculated as total bonds multiplied by
|
||||
// exchange rate.
|
||||
type Validator struct {
|
||||
Owner sdk.Address `json:"owner"` // sender of BondTx - UnbondTx returns here
|
||||
PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator
|
||||
Revoked bool `json:"pub_key"` // has the validator been revoked from bonded status?
|
||||
|
||||
PoolShares PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
|
||||
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||
|
||||
Description Description `json:"description"` // description terms for the validator
|
||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
|
||||
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
|
||||
|
||||
Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators
|
||||
CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
|
||||
CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
|
||||
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||
|
||||
// fee related
|
||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
||||
}
|
||||
|
||||
// Validators - list of Validators
|
||||
type Validators []Validator
|
||||
|
||||
// NewValidator - initialize a new validator
|
||||
func NewValidator(owner sdk.Address, pubKey crypto.PubKey, description Description) Validator {
|
||||
return Validator{
|
||||
Owner: owner,
|
||||
PubKey: pubKey,
|
||||
PoolShares: NewUnbondedShares(sdk.ZeroRat()),
|
||||
DelegatorShares: sdk.ZeroRat(),
|
||||
Description: description,
|
||||
BondHeight: int64(0),
|
||||
BondIntraTxCounter: int16(0),
|
||||
ProposerRewardPool: sdk.Coins{},
|
||||
Commission: sdk.ZeroRat(),
|
||||
CommissionMax: sdk.ZeroRat(),
|
||||
CommissionChangeRate: sdk.ZeroRat(),
|
||||
CommissionChangeToday: sdk.ZeroRat(),
|
||||
PrevBondedShares: sdk.ZeroRat(),
|
||||
}
|
||||
}
|
||||
|
||||
// only the vitals - does not check bond height of IntraTxCounter
|
||||
func (v Validator) equal(c2 Validator) bool {
|
||||
return v.PubKey.Equals(c2.PubKey) &&
|
||||
bytes.Equal(v.Owner, c2.Owner) &&
|
||||
v.PoolShares.Equal(c2.PoolShares) &&
|
||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||
v.Description == c2.Description &&
|
||||
//v.BondHeight == c2.BondHeight &&
|
||||
//v.BondIntraTxCounter == c2.BondIntraTxCounter && // counter is always changing
|
||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||
v.Commission.Equal(c2.Commission) &&
|
||||
v.CommissionMax.Equal(c2.CommissionMax) &&
|
||||
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
|
||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
|
||||
v.PrevBondedShares.Equal(c2.PrevBondedShares)
|
||||
}
|
||||
|
||||
// Description - description fields for a validator
|
||||
type Description struct {
|
||||
Moniker string `json:"moniker"`
|
||||
Identity string `json:"identity"`
|
||||
Website string `json:"website"`
|
||||
Details string `json:"details"`
|
||||
}
|
||||
|
||||
func NewDescription(moniker, identity, website, details string) Description {
|
||||
return Description{
|
||||
Moniker: moniker,
|
||||
Identity: identity,
|
||||
Website: website,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
//XXX updateDescription function which enforce limit to number of description characters
|
||||
|
||||
// abci validator from stake validator type
|
||||
func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: v.PubKey.Bytes(),
|
||||
Power: v.PoolShares.Bonded().Evaluate(),
|
||||
}
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
// with zero power used for validator updates
|
||||
func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator {
|
||||
return abci.Validator{
|
||||
PubKey: v.PubKey.Bytes(),
|
||||
Power: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// abci validator from stake validator type
|
||||
func (v Validator) Status() sdk.BondStatus {
|
||||
return v.PoolShares.Status
|
||||
}
|
||||
|
||||
// update the location of the shares within a validator if its bond status has changed
|
||||
func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) {
|
||||
var tokens int64
|
||||
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
if NewStatus == sdk.Unbonded {
|
||||
return v, pool
|
||||
}
|
||||
pool, tokens = pool.removeSharesUnbonded(v.PoolShares.Amount)
|
||||
|
||||
case sdk.Unbonding:
|
||||
if NewStatus == sdk.Unbonding {
|
||||
return v, pool
|
||||
}
|
||||
pool, tokens = pool.removeSharesUnbonding(v.PoolShares.Amount)
|
||||
|
||||
case sdk.Bonded:
|
||||
if NewStatus == sdk.Bonded { // return if nothing needs switching
|
||||
return v, pool
|
||||
}
|
||||
pool, tokens = pool.removeSharesBonded(v.PoolShares.Amount)
|
||||
}
|
||||
|
||||
switch NewStatus {
|
||||
case sdk.Unbonded:
|
||||
pool, v.PoolShares = pool.addTokensUnbonded(tokens)
|
||||
case sdk.Unbonding:
|
||||
pool, v.PoolShares = pool.addTokensUnbonding(tokens)
|
||||
case sdk.Bonded:
|
||||
pool, v.PoolShares = pool.addTokensBonded(tokens)
|
||||
}
|
||||
return v, pool
|
||||
}
|
||||
|
||||
// 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 (v Validator) EquivalentBondedShares(pool Pool) (eqBondedShares sdk.Rat) {
|
||||
return v.PoolShares.ToBonded(pool).Amount
|
||||
}
|
||||
|
||||
//_________________________________________________________________________________________________________
|
||||
|
||||
// XXX Audit this function further to make sure it's correct
|
||||
// add tokens to a validator
|
||||
func (v Validator) addTokensFromDel(pool Pool,
|
||||
amount int64) (validator2 Validator, p2 Pool, issuedDelegatorShares sdk.Rat) {
|
||||
|
||||
exRate := v.DelegatorShareExRate(pool) // bshr/delshr
|
||||
|
||||
var poolShares PoolShares
|
||||
var equivalentBondedShares sdk.Rat
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
pool, poolShares = pool.addTokensUnbonded(amount)
|
||||
case sdk.Unbonding:
|
||||
pool, poolShares = pool.addTokensUnbonding(amount)
|
||||
case sdk.Bonded:
|
||||
pool, poolShares = pool.addTokensBonded(amount)
|
||||
}
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Add(poolShares.Amount)
|
||||
equivalentBondedShares = poolShares.ToBonded(pool).Amount
|
||||
|
||||
issuedDelegatorShares = equivalentBondedShares.Quo(exRate) // bshr/(bshr/delshr) = delshr
|
||||
v.DelegatorShares = v.DelegatorShares.Add(issuedDelegatorShares)
|
||||
|
||||
return v, pool, issuedDelegatorShares
|
||||
}
|
||||
|
||||
// remove delegator shares from a validator
|
||||
// NOTE this function assumes the shares have already been updated for the validator status
|
||||
func (v Validator) removeDelShares(pool Pool,
|
||||
delShares sdk.Rat) (validator2 Validator, p2 Pool, createdCoins int64) {
|
||||
|
||||
amount := v.DelegatorShareExRate(pool).Mul(delShares)
|
||||
eqBondedSharesToRemove := NewBondedShares(amount)
|
||||
v.DelegatorShares = v.DelegatorShares.Sub(delShares)
|
||||
|
||||
switch v.Status() {
|
||||
case sdk.Unbonded:
|
||||
unbondedShares := eqBondedSharesToRemove.ToUnbonded(pool).Amount
|
||||
pool, createdCoins = pool.removeSharesUnbonded(unbondedShares)
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(unbondedShares)
|
||||
case sdk.Unbonding:
|
||||
unbondingShares := eqBondedSharesToRemove.ToUnbonding(pool).Amount
|
||||
pool, createdCoins = pool.removeSharesUnbonding(unbondingShares)
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(unbondingShares)
|
||||
case sdk.Bonded:
|
||||
pool, createdCoins = pool.removeSharesBonded(eqBondedSharesToRemove.Amount)
|
||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(eqBondedSharesToRemove.Amount)
|
||||
}
|
||||
return v, pool, createdCoins
|
||||
}
|
||||
|
||||
// get the exchange rate of tokens over delegator shares
|
||||
// UNITS: eq-val-bonded-shares/delegator-shares
|
||||
func (v Validator) DelegatorShareExRate(pool Pool) sdk.Rat {
|
||||
if v.DelegatorShares.IsZero() {
|
||||
return sdk.OneRat()
|
||||
}
|
||||
eqBondedShares := v.PoolShares.ToBonded(pool).Amount
|
||||
return eqBondedShares.Quo(v.DelegatorShares)
|
||||
}
|
||||
|
||||
//______________________________________________________________________
|
||||
|
||||
// ensure fulfills the sdk validator types
|
||||
var _ sdk.Validator = Validator{}
|
||||
|
||||
// nolint - for sdk.Validator
|
||||
func (v Validator) GetStatus() sdk.BondStatus { return v.Status() }
|
||||
func (v Validator) GetOwner() sdk.Address { return v.Owner }
|
||||
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
|
||||
func (v Validator) GetPower() sdk.Rat { return v.PoolShares.Bonded() }
|
||||
func (v Validator) GetBondHeight() int64 { return v.BondHeight }
|
|
@ -0,0 +1,408 @@
|
|||
package stake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAddTokensValidatorBonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Bonded()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonding(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonding()))
|
||||
}
|
||||
|
||||
func TestAddTokensValidatorUnbonded(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
pool := keeper.GetPool(ctx)
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
||||
val, pool, delShares := val.addTokensFromDel(pool, 10)
|
||||
|
||||
assert.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
||||
assert.Equal(t, sdk.OneRat(), pool.bondedShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondingShareExRate())
|
||||
assert.Equal(t, sdk.OneRat(), pool.unbondedShareExRate())
|
||||
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonded()))
|
||||
}
|
||||
|
||||
// TODO refactor to make simpler like the AddToken tests above
|
||||
func TestRemoveShares(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
poolA := keeper.GetPool(ctx)
|
||||
valA := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(sdk.NewRat(9)),
|
||||
DelegatorShares: sdk.NewRat(9),
|
||||
}
|
||||
poolA.BondedTokens = valA.PoolShares.Bonded().Evaluate()
|
||||
poolA.BondedShares = valA.PoolShares.Bonded()
|
||||
assert.Equal(t, valA.DelegatorShareExRate(poolA), sdk.OneRat())
|
||||
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
|
||||
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
|
||||
valB, poolB, coinsB := valA.removeDelShares(poolA, sdk.NewRat(10))
|
||||
|
||||
// coins were created
|
||||
assert.Equal(t, coinsB, int64(10))
|
||||
// pool shares were removed
|
||||
assert.Equal(t, valB.PoolShares.Bonded(), valA.PoolShares.Bonded().Sub(sdk.NewRat(10).Mul(valA.DelegatorShareExRate(poolA))))
|
||||
// conservation of tokens
|
||||
assert.Equal(t, poolB.UnbondedTokens+poolB.BondedTokens+coinsB, poolA.UnbondedTokens+poolA.BondedTokens)
|
||||
|
||||
// specific case from random tests
|
||||
poolShares := sdk.NewRat(5102)
|
||||
delShares := sdk.NewRat(115)
|
||||
val := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
BondedShares: sdk.NewRat(248305),
|
||||
UnbondedShares: sdk.NewRat(232147),
|
||||
BondedTokens: 248305,
|
||||
UnbondedTokens: 232147,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
shares := sdk.NewRat(29)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||
_, newPool, tokens := val.removeDelShares(pool, shares)
|
||||
require.Equal(t,
|
||||
tokens+newPool.UnbondedTokens+newPool.BondedTokens,
|
||||
pool.BondedTokens+pool.UnbondedTokens,
|
||||
"Tokens were not conserved: %s", msg)
|
||||
}
|
||||
|
||||
func TestUpdateStatus(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
pool := keeper.GetPool(ctx)
|
||||
|
||||
val := NewValidator(addrs[0], pks[0], Description{})
|
||||
val, pool, _ = val.addTokensFromDel(pool, 100)
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(100), pool.UnbondedTokens)
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
||||
assert.Equal(t, int64(0), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(100), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(0), pool.BondedTokens)
|
||||
assert.Equal(t, int64(100), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens)
|
||||
|
||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
||||
assert.Equal(t, int64(100), val.PoolShares.Bonded().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonding().Evaluate())
|
||||
assert.Equal(t, int64(0), val.PoolShares.Unbonded().Evaluate())
|
||||
assert.Equal(t, int64(100), pool.BondedTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondingTokens)
|
||||
assert.Equal(t, int64(0), pool.UnbondedTokens)
|
||||
}
|
||||
|
||||
//________________________________________________________________________________
|
||||
// TODO refactor this random setup
|
||||
|
||||
// generate a random validator
|
||||
func randomValidator(r *rand.Rand) Validator {
|
||||
|
||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||
|
||||
var pShares PoolShares
|
||||
if r.Float64() < float64(0.5) {
|
||||
pShares = NewBondedShares(poolSharesAmt)
|
||||
} else {
|
||||
pShares = NewUnbondedShares(poolSharesAmt)
|
||||
}
|
||||
return Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: pShares,
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
}
|
||||
|
||||
// generate a random staking state
|
||||
func randomSetup(r *rand.Rand, numValidators int) (Pool, Validators) {
|
||||
pool := initialPool()
|
||||
|
||||
validators := make([]Validator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
validator := randomValidator(r)
|
||||
if validator.Status() == sdk.Bonded {
|
||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
||||
pool.BondedTokens += validator.PoolShares.Bonded().Evaluate()
|
||||
} else if validator.Status() == sdk.Unbonded {
|
||||
pool.UnbondedShares = pool.UnbondedShares.Add(validator.PoolShares.Unbonded())
|
||||
pool.UnbondedTokens += validator.PoolShares.Unbonded().Evaluate()
|
||||
}
|
||||
validators[i] = validator
|
||||
}
|
||||
return pool, validators
|
||||
}
|
||||
|
||||
// any operation that transforms staking state
|
||||
// takes in RNG instance, pool, validator
|
||||
// returns updated pool, updated validator, delta tokens, descriptive message
|
||||
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, int64, string)
|
||||
|
||||
// operation: bond or unbond a validator depending on current status
|
||||
func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var msg string
|
||||
var newStatus sdk.BondStatus
|
||||
if val.Status() == sdk.Bonded {
|
||||
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Unbonded
|
||||
|
||||
} else if val.Status() == sdk.Unbonded {
|
||||
msg = fmt.Sprintf("sdk.Bonded previously unbonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newStatus = sdk.Bonded
|
||||
}
|
||||
val, pool = val.UpdateStatus(pool, newStatus)
|
||||
return pool, val, 0, msg
|
||||
}
|
||||
|
||||
// operation: add a random number of tokens to a validator
|
||||
func OpAddTokens(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
tokens := int64(r.Int31n(1000))
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
val, pool, _ = val.addTokensFromDel(pool, tokens)
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
return pool, val, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||
}
|
||||
|
||||
// operation: remove a random number of shares from a validator
|
||||
func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
||||
var shares sdk.Rat
|
||||
for {
|
||||
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||
if shares.LT(val.DelegatorShares) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Removed %v shares from validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
shares, val.Owner, val.Status(), val.PoolShares, val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
|
||||
val, pool, tokens := val.removeDelShares(pool, shares)
|
||||
return pool, val, tokens, msg
|
||||
}
|
||||
|
||||
// pick a random staking operation
|
||||
func randomOperation(r *rand.Rand) Operation {
|
||||
operations := []Operation{
|
||||
OpBondOrUnbond,
|
||||
OpAddTokens,
|
||||
OpRemoveShares,
|
||||
}
|
||||
r.Shuffle(len(operations), func(i, j int) {
|
||||
operations[i], operations[j] = operations[j], operations[i]
|
||||
})
|
||||
return operations[0]
|
||||
}
|
||||
|
||||
// ensure invariants that should always be true are true
|
||||
func assertInvariants(t *testing.T, msg string,
|
||||
pOrig Pool, cOrig Validators, pMod Pool, vMods Validators, tokens int64) {
|
||||
|
||||
// total tokens conserved
|
||||
require.Equal(t,
|
||||
pOrig.UnbondedTokens+pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens+pMod.BondedTokens+tokens,
|
||||
"Tokens not conserved - msg: %v\n, pOrig.PoolShares.Bonded(): %v, pOrig.PoolShares.Unbonded(): %v, pMod.PoolShares.Bonded(): %v, pMod.PoolShares.Unbonded(): %v, pOrig.UnbondedTokens: %v, pOrig.BondedTokens: %v, pMod.UnbondedTokens: %v, pMod.BondedTokens: %v, tokens: %v\n",
|
||||
msg,
|
||||
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||
pMod.BondedShares, pMod.UnbondedShares,
|
||||
pOrig.UnbondedTokens, pOrig.BondedTokens,
|
||||
pMod.UnbondedTokens, pMod.BondedTokens, tokens)
|
||||
|
||||
// nonnegative bonded shares
|
||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative unbonded shares
|
||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
||||
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
||||
msg, pOrig, pMod, tokens)
|
||||
|
||||
// nonnegative bonded ex rate
|
||||
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||
msg, pMod.bondedShareExRate().Evaluate())
|
||||
|
||||
// nonnegative unbonded ex rate
|
||||
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||
msg, pMod.unbondedShareExRate().Evaluate())
|
||||
|
||||
for _, vMod := range vMods {
|
||||
|
||||
// nonnegative ex rate
|
||||
require.False(t, vMod.DelegatorShareExRate(pMod).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative poolShares
|
||||
require.False(t, vMod.PoolShares.Bonded().LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.PoolShares.Bonded(): %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShares,
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
// nonnegative delShares
|
||||
require.False(t, vMod.DelegatorShares.LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %v (validator.PoolShares.Bonded(): %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
||||
msg,
|
||||
vMod.DelegatorShares,
|
||||
vMod.PoolShares.Bonded(),
|
||||
vMod.DelegatorShareExRate(pMod),
|
||||
vMod.Owner,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPossibleOverflow(t *testing.T) {
|
||||
poolShares := sdk.NewRat(2159)
|
||||
delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||
val := Validator{
|
||||
Owner: addrs[0],
|
||||
PubKey: pks[0],
|
||||
PoolShares: NewBondedShares(poolShares),
|
||||
DelegatorShares: delShares,
|
||||
}
|
||||
pool := Pool{
|
||||
BondedShares: poolShares,
|
||||
UnbondedShares: sdk.ZeroRat(),
|
||||
BondedTokens: poolShares.Evaluate(),
|
||||
UnbondedTokens: 0,
|
||||
InflationLastTime: 0,
|
||||
Inflation: sdk.NewRat(7, 100),
|
||||
}
|
||||
tokens := int64(71)
|
||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
||||
newValidator, _, _ := val.addTokensFromDel(pool, tokens)
|
||||
|
||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||
require.False(t, newValidator.DelegatorShareExRate(pool).LT(sdk.ZeroRat()),
|
||||
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
|
||||
msg, newValidator.DelegatorShareExRate(pool))
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random single-validator state, assert invariants hold
|
||||
func TestSingleValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(41))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := randomSetup(r, 1)
|
||||
require.Equal(t, 1, len(validatorsOrig))
|
||||
|
||||
// sanity check
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[0])
|
||||
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
require.Equal(t, 1, len(validatorsOrig), "j %v", j)
|
||||
require.Equal(t, 1, len(validatorsMod), "j %v", j)
|
||||
validatorsMod[0] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run random operations in a random order on a random multi-validator state, assert invariants hold
|
||||
func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(42))
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
poolOrig, validatorsOrig := randomSetup(r, 100)
|
||||
|
||||
assertInvariants(t, "no operation",
|
||||
poolOrig, validatorsOrig,
|
||||
poolOrig, validatorsOrig, 0)
|
||||
|
||||
for j := 0; j < 5; j++ {
|
||||
index := int(r.Int31n(int32(len(validatorsOrig))))
|
||||
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[index])
|
||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||
copy(validatorsMod[:], validatorsOrig[:])
|
||||
validatorsMod[index] = validatorMod
|
||||
|
||||
assertInvariants(t, msg,
|
||||
poolOrig, validatorsOrig,
|
||||
poolMod, validatorsMod, tokens)
|
||||
|
||||
poolOrig = poolMod
|
||||
validatorsOrig = validatorsMod
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,13 +17,13 @@ func NewViewSlashKeeper(k Keeper) ViewSlashKeeper {
|
|||
}
|
||||
|
||||
// load a delegator bond
|
||||
func (v ViewSlashKeeper) GetDelegatorBond(ctx sdk.Context,
|
||||
delegatorAddr, candidateAddr sdk.Address) (bond DelegatorBond, found bool) {
|
||||
return v.keeper.GetDelegatorBond(ctx, delegatorAddr, candidateAddr)
|
||||
func (v ViewSlashKeeper) GetDelegation(ctx sdk.Context,
|
||||
delegatorAddr, validatorAddr sdk.Address) (bond Delegation, found bool) {
|
||||
return v.keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
}
|
||||
|
||||
// load n delegator bonds
|
||||
func (v ViewSlashKeeper) GetDelegatorBonds(ctx sdk.Context,
|
||||
delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) {
|
||||
return v.keeper.GetDelegatorBonds(ctx, delegator, maxRetrieve)
|
||||
func (v ViewSlashKeeper) GetDelegations(ctx sdk.Context,
|
||||
delegator sdk.Address, maxRetrieve int16) (bonds []Delegation) {
|
||||
return v.keeper.GetDelegations(ctx, delegator, maxRetrieve)
|
||||
}
|
||||
|
|
|
@ -9,78 +9,78 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// tests GetDelegatorBond, GetDelegatorBonds
|
||||
// tests GetDelegation, GetDelegations
|
||||
func TestViewSlashBond(t *testing.T) {
|
||||
ctx, _, keeper := createTestInput(t, false, 0)
|
||||
|
||||
//construct the candidates
|
||||
//construct the validators
|
||||
amts := []int64{9, 8, 7}
|
||||
var candidates [3]Candidate
|
||||
var validators [3]Validator
|
||||
for i, amt := range amts {
|
||||
candidates[i] = Candidate{
|
||||
Address: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
Assets: sdk.NewRat(amt),
|
||||
Liabilities: sdk.NewRat(amt),
|
||||
validators[i] = Validator{
|
||||
Owner: addrVals[i],
|
||||
PubKey: pks[i],
|
||||
PoolShares: NewUnbondedShares(sdk.NewRat(amt)),
|
||||
DelegatorShares: sdk.NewRat(amt),
|
||||
}
|
||||
}
|
||||
|
||||
// first add a candidates[0] to delegate too
|
||||
keeper.setCandidate(ctx, candidates[0])
|
||||
// first add a validators[0] to delegate too
|
||||
keeper.updateValidator(ctx, validators[0])
|
||||
|
||||
bond1to1 := DelegatorBond{
|
||||
bond1to1 := Delegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
CandidateAddr: addrVals[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
Shares: sdk.NewRat(9),
|
||||
}
|
||||
|
||||
viewSlashKeeper := NewViewSlashKeeper(keeper)
|
||||
|
||||
// check the empty keeper first
|
||||
_, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
_, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.False(t, found)
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found := viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
bond1to1.Shares = sdk.NewRat(99)
|
||||
keeper.setDelegatorBond(ctx, bond1to1)
|
||||
resBond, found = viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0])
|
||||
keeper.setDelegation(ctx, bond1to1)
|
||||
resBond, found = viewSlashKeeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||
assert.True(t, found)
|
||||
assert.True(t, bondsEqual(bond1to1, resBond))
|
||||
assert.True(t, bond1to1.equal(resBond))
|
||||
|
||||
// add some more records
|
||||
keeper.setCandidate(ctx, candidates[1])
|
||||
keeper.setCandidate(ctx, candidates[2])
|
||||
bond1to2 := DelegatorBond{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := DelegatorBond{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := DelegatorBond{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := DelegatorBond{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := DelegatorBond{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegatorBond(ctx, bond1to2)
|
||||
keeper.setDelegatorBond(ctx, bond1to3)
|
||||
keeper.setDelegatorBond(ctx, bond2to1)
|
||||
keeper.setDelegatorBond(ctx, bond2to2)
|
||||
keeper.setDelegatorBond(ctx, bond2to3)
|
||||
keeper.updateValidator(ctx, validators[1])
|
||||
keeper.updateValidator(ctx, validators[2])
|
||||
bond1to2 := Delegation{addrDels[0], addrVals[1], sdk.NewRat(9), 0}
|
||||
bond1to3 := Delegation{addrDels[0], addrVals[2], sdk.NewRat(9), 1}
|
||||
bond2to1 := Delegation{addrDels[1], addrVals[0], sdk.NewRat(9), 2}
|
||||
bond2to2 := Delegation{addrDels[1], addrVals[1], sdk.NewRat(9), 3}
|
||||
bond2to3 := Delegation{addrDels[1], addrVals[2], sdk.NewRat(9), 4}
|
||||
keeper.setDelegation(ctx, bond1to2)
|
||||
keeper.setDelegation(ctx, bond1to3)
|
||||
keeper.setDelegation(ctx, bond2to1)
|
||||
keeper.setDelegation(ctx, bond2to2)
|
||||
keeper.setDelegation(ctx, bond2to3)
|
||||
|
||||
// test all bond retrieve capabilities
|
||||
resBonds := viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 5)
|
||||
resBonds := viewSlashKeeper.GetDelegations(ctx, addrDels[0], 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 = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 3)
|
||||
assert.True(t, bond1to1.equal(resBonds[0]))
|
||||
assert.True(t, bond1to2.equal(resBonds[1]))
|
||||
assert.True(t, bond1to3.equal(resBonds[2]))
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 3)
|
||||
require.Equal(t, 3, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 2)
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[0], 2)
|
||||
require.Equal(t, 2, len(resBonds))
|
||||
resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[1], 5)
|
||||
resBonds = viewSlashKeeper.GetDelegations(ctx, addrDels[1], 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]))
|
||||
assert.True(t, bond2to1.equal(resBonds[0]))
|
||||
assert.True(t, bond2to2.equal(resBonds[1]))
|
||||
assert.True(t, bond2to3.equal(resBonds[2]))
|
||||
|
||||
}
|
||||
|
|
|
@ -11,3 +11,5 @@ func RegisterWire(cdc *wire.Codec) {
|
|||
cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil)
|
||||
cdc.RegisterConcrete(MsgUnbond{}, "cosmos-sdk/MsgUnbond", nil)
|
||||
}
|
||||
|
||||
var cdcEmpty = wire.NewCodec()
|
||||
|
|
Loading…
Reference in New Issue