Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into aditya/cleanup
This commit is contained in:
commit
5b98a3da48
|
@ -87,7 +87,7 @@ jobs:
|
||||||
|
|
||||||
test_cover:
|
test_cover:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
parallelism: 2
|
parallelism: 4
|
||||||
steps:
|
steps:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: /tmp/workspace
|
at: /tmp/workspace
|
||||||
|
@ -103,7 +103,6 @@ jobs:
|
||||||
make install
|
make install
|
||||||
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do
|
for pkg in $(go list github.com/cosmos/cosmos-sdk/... | grep -v /vendor/ | grep -v github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test | circleci tests split --split-by=timings); do
|
||||||
id=$(basename "$pkg")
|
id=$(basename "$pkg")
|
||||||
|
|
||||||
GOCACHE=off go test -v -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
GOCACHE=off go test -v -timeout 8m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||||
done
|
done
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
## 0.21.0
|
## 0.21.0
|
||||||
|
|
||||||
*TBD*
|
*July 13th, 2018*
|
||||||
|
|
||||||
BREAKING CHANGES
|
BREAKING CHANGES
|
||||||
* [x/stake] Specify DelegatorAddress in MsgCreateValidator
|
* [x/stake] Specify DelegatorAddress in MsgCreateValidator
|
||||||
|
* [x/stake] Remove the use of global shares in the pool
|
||||||
|
* Remove the use of `PoolShares` type in `x/stake/validator` type - replace with `Status` `Tokens` fields
|
||||||
* [x/auth] NewAccountMapper takes a constructor instead of a prototype
|
* [x/auth] NewAccountMapper takes a constructor instead of a prototype
|
||||||
* [keys] Keybase.Update function now takes in a function to get the newpass, rather than the password itself
|
* [keys] Keybase.Update function now takes in a function to get the newpass, rather than the password itself
|
||||||
|
|
||||||
|
|
|
@ -507,6 +507,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "37c54ed9bde68bad33be02f5e09b17a42397fb2fc9a10fa582e66b3852a99370"
|
inputs-digest = "5c3ab73a85af1b3110b5f7ddbb27e77bb9cf42848ee29efcad9d78c0ecc26519"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -146,7 +146,7 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
|
||||||
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
|
accAuth.Coins = sdk.Coins{sdk.NewCoin("steak", 100)}
|
||||||
acc := gapp.NewGenesisAccount(&accAuth)
|
acc := gapp.NewGenesisAccount(&accAuth)
|
||||||
genesisState.Accounts = append(genesisState.Accounts, acc)
|
genesisState.Accounts = append(genesisState.Accounts, acc)
|
||||||
genesisState.StakeData.Pool.LooseTokens += 100
|
genesisState.StakeData.Pool.LooseTokens = genesisState.StakeData.Pool.LooseTokens.Add(sdk.NewRat(100))
|
||||||
}
|
}
|
||||||
|
|
||||||
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
appState, err := wire.MarshalJSONIndent(cdc, genesisState)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
)
|
)
|
||||||
|
@ -45,6 +46,7 @@ type GaiaApp struct {
|
||||||
keySlashing *sdk.KVStoreKey
|
keySlashing *sdk.KVStoreKey
|
||||||
keyGov *sdk.KVStoreKey
|
keyGov *sdk.KVStoreKey
|
||||||
keyFeeCollection *sdk.KVStoreKey
|
keyFeeCollection *sdk.KVStoreKey
|
||||||
|
keyParams *sdk.KVStoreKey
|
||||||
|
|
||||||
// Manage getting and setting accounts
|
// Manage getting and setting accounts
|
||||||
accountMapper auth.AccountMapper
|
accountMapper auth.AccountMapper
|
||||||
|
@ -54,6 +56,7 @@ type GaiaApp struct {
|
||||||
stakeKeeper stake.Keeper
|
stakeKeeper stake.Keeper
|
||||||
slashingKeeper slashing.Keeper
|
slashingKeeper slashing.Keeper
|
||||||
govKeeper gov.Keeper
|
govKeeper gov.Keeper
|
||||||
|
paramsKeeper params.Keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGaiaApp returns a reference to an initialized GaiaApp.
|
// NewGaiaApp returns a reference to an initialized GaiaApp.
|
||||||
|
@ -73,6 +76,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
||||||
keySlashing: sdk.NewKVStoreKey("slashing"),
|
keySlashing: sdk.NewKVStoreKey("slashing"),
|
||||||
keyGov: sdk.NewKVStoreKey("gov"),
|
keyGov: sdk.NewKVStoreKey("gov"),
|
||||||
keyFeeCollection: sdk.NewKVStoreKey("fee"),
|
keyFeeCollection: sdk.NewKVStoreKey("fee"),
|
||||||
|
keyParams: sdk.NewKVStoreKey("params"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// define the accountMapper
|
// define the accountMapper
|
||||||
|
@ -85,10 +89,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
||||||
// add handlers
|
// add handlers
|
||||||
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
||||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||||
|
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
|
||||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
|
|
||||||
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
|
app.govKeeper = gov.NewKeeper(app.cdc, app.keyGov, app.coinKeeper, app.stakeKeeper, app.RegisterCodespace(gov.DefaultCodespace))
|
||||||
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
|
app.feeCollectionKeeper = auth.NewFeeCollectionKeeper(app.cdc, app.keyFeeCollection)
|
||||||
|
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||||
|
|
||||||
// register message routes
|
// register message routes
|
||||||
app.Router().
|
app.Router().
|
||||||
|
@ -103,7 +108,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio
|
||||||
app.SetBeginBlocker(app.BeginBlocker)
|
app.SetBeginBlocker(app.BeginBlocker)
|
||||||
app.SetEndBlocker(app.EndBlocker)
|
app.SetEndBlocker(app.EndBlocker)
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.feeCollectionKeeper))
|
||||||
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection)
|
app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake, app.keySlashing, app.keyGov, app.keyFeeCollection, app.keyParams)
|
||||||
err := app.LoadLatestVersion(app.keyMain)
|
err := app.LoadLatestVersion(app.keyMain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(err.Error())
|
cmn.Exit(err.Error())
|
||||||
|
|
|
@ -160,7 +160,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
|
||||||
}
|
}
|
||||||
acc := NewGenesisAccount(&accAuth)
|
acc := NewGenesisAccount(&accAuth)
|
||||||
genaccs[i] = acc
|
genaccs[i] = acc
|
||||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionsAcc // increase the supply
|
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionsAcc)) // increase the supply
|
||||||
|
|
||||||
// add the validator
|
// add the validator
|
||||||
if len(genTx.Name) > 0 {
|
if len(genTx.Name) > 0 {
|
||||||
|
@ -168,7 +168,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState
|
||||||
validator := stake.NewValidator(genTx.Address,
|
validator := stake.NewValidator(genTx.Address,
|
||||||
sdk.MustGetAccPubKeyBech32(genTx.PubKey), desc)
|
sdk.MustGetAccPubKeyBech32(genTx.PubKey), desc)
|
||||||
|
|
||||||
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens + freeFermionVal // increase the supply
|
stakeData.Pool.LooseTokens = stakeData.Pool.LooseTokens.Add(sdk.NewRat(freeFermionVal)) // increase the supply
|
||||||
|
|
||||||
// add some new shares to the validator
|
// add some new shares to the validator
|
||||||
var issuedDelShares sdk.Rat
|
var issuedDelShares sdk.Rat
|
||||||
|
|
|
@ -131,7 +131,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
||||||
|
|
||||||
validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags))
|
validator := executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags))
|
||||||
require.Equal(t, validator.Owner, barAddr)
|
require.Equal(t, validator.Owner, barAddr)
|
||||||
require.Equal(t, "2/1", validator.PoolShares.Amount.String())
|
require.True(sdk.RatEq(t, sdk.NewRat(2), validator.Tokens))
|
||||||
|
|
||||||
// unbond a single share
|
// unbond a single share
|
||||||
unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags)
|
unbondStr := fmt.Sprintf("gaiacli stake unbond begin %v", flags)
|
||||||
|
@ -149,7 +149,7 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
||||||
require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc)
|
require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak").Int64(), "%v", barAcc)
|
||||||
*/
|
*/
|
||||||
validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags))
|
validator = executeGetValidator(t, fmt.Sprintf("gaiacli stake validator %s --output=json %v", barAddr, flags))
|
||||||
require.Equal(t, "1/1", validator.PoolShares.Amount.String())
|
require.Equal(t, "1/1", validator.Tokens.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGaiaCLISubmitProposal(t *testing.T) {
|
func TestGaiaCLISubmitProposal(t *testing.T) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
"github.com/cosmos/cosmos-sdk/x/ibc"
|
"github.com/cosmos/cosmos-sdk/x/ibc"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/cosmos/cosmos-sdk/x/slashing"
|
"github.com/cosmos/cosmos-sdk/x/slashing"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
|
|
||||||
|
@ -133,6 +134,7 @@ type GaiaApp struct {
|
||||||
keyIBC *sdk.KVStoreKey
|
keyIBC *sdk.KVStoreKey
|
||||||
keyStake *sdk.KVStoreKey
|
keyStake *sdk.KVStoreKey
|
||||||
keySlashing *sdk.KVStoreKey
|
keySlashing *sdk.KVStoreKey
|
||||||
|
keyParams *sdk.KVStoreKey
|
||||||
|
|
||||||
// Manage getting and setting accounts
|
// Manage getting and setting accounts
|
||||||
accountMapper auth.AccountMapper
|
accountMapper auth.AccountMapper
|
||||||
|
@ -141,6 +143,7 @@ type GaiaApp struct {
|
||||||
ibcMapper ibc.Mapper
|
ibcMapper ibc.Mapper
|
||||||
stakeKeeper stake.Keeper
|
stakeKeeper stake.Keeper
|
||||||
slashingKeeper slashing.Keeper
|
slashingKeeper slashing.Keeper
|
||||||
|
paramsKeeper params.Keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseApp)) *GaiaApp {
|
||||||
|
@ -158,6 +161,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
||||||
keyIBC: sdk.NewKVStoreKey("ibc"),
|
keyIBC: sdk.NewKVStoreKey("ibc"),
|
||||||
keyStake: sdk.NewKVStoreKey("stake"),
|
keyStake: sdk.NewKVStoreKey("stake"),
|
||||||
keySlashing: sdk.NewKVStoreKey("slashing"),
|
keySlashing: sdk.NewKVStoreKey("slashing"),
|
||||||
|
keyParams: sdk.NewKVStoreKey("params"),
|
||||||
}
|
}
|
||||||
|
|
||||||
// define the accountMapper
|
// define the accountMapper
|
||||||
|
@ -170,8 +174,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp
|
||||||
// add handlers
|
// add handlers
|
||||||
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
app.coinKeeper = bank.NewKeeper(app.accountMapper)
|
||||||
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
app.ibcMapper = ibc.NewMapper(app.cdc, app.keyIBC, app.RegisterCodespace(ibc.DefaultCodespace))
|
||||||
|
app.paramsKeeper = params.NewKeeper(app.cdc, app.keyParams)
|
||||||
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
app.stakeKeeper = stake.NewKeeper(app.cdc, app.keyStake, app.coinKeeper, app.RegisterCodespace(stake.DefaultCodespace))
|
||||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.RegisterCodespace(slashing.DefaultCodespace))
|
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakeKeeper, app.paramsKeeper.Getter(), app.RegisterCodespace(slashing.DefaultCodespace))
|
||||||
|
|
||||||
// register message routes
|
// register message routes
|
||||||
app.Router().
|
app.Router().
|
||||||
|
|
|
@ -6,29 +6,18 @@
|
||||||
- value: `amino(pool)`
|
- value: `amino(pool)`
|
||||||
|
|
||||||
The pool is a space for all dynamic global state of the Cosmos Hub. It tracks
|
The pool is a space for all dynamic global state of the Cosmos Hub. It tracks
|
||||||
information about the total amounts of Atoms in all states, representative
|
information about the total amounts of Atoms in all states, moving Atom
|
||||||
validator shares for stake in the global pools, moving Atom inflation
|
inflation information, etc.
|
||||||
information, etc.
|
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
LooseTokens int64 // tokens not associated with any validator
|
LooseTokens int64 // tokens not associated with any bonded validator
|
||||||
UnbondedTokens int64 // reserve of unbonded tokens held with validators
|
|
||||||
UnbondingTokens int64 // tokens moving from bonded to unbonded pool
|
|
||||||
BondedTokens int64 // reserve of bonded tokens
|
BondedTokens int64 // reserve of bonded tokens
|
||||||
UnbondedShares sdk.Rat // sum of all shares distributed for the Unbonded Pool
|
|
||||||
UnbondingShares sdk.Rat // shares moving from Bonded to Unbonded Pool
|
|
||||||
BondedShares sdk.Rat // sum of all shares distributed for the Bonded Pool
|
|
||||||
InflationLastTime int64 // block which the last inflation was processed // TODO make time
|
InflationLastTime int64 // block which the last inflation was processed // TODO make time
|
||||||
Inflation sdk.Rat // current annual inflation rate
|
Inflation sdk.Rat // current annual inflation rate
|
||||||
|
|
||||||
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
|
DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PoolShares struct {
|
|
||||||
Status sdk.BondStatus // either: unbonded, unbonding, or bonded
|
|
||||||
Amount sdk.Rat // total shares of type ShareKind
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Params
|
### Params
|
||||||
|
@ -85,7 +74,8 @@ type Validator struct {
|
||||||
ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator
|
ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator
|
||||||
Revoked bool // has the validator been revoked?
|
Revoked bool // has the validator been revoked?
|
||||||
|
|
||||||
PoolShares PoolShares // total shares for tokens held in the pool
|
Status sdk.BondStatus // validator status (bonded/unbonding/unbonded)
|
||||||
|
Tokens sdk.Rat // delegated tokens (incl. self-delegation)
|
||||||
DelegatorShares sdk.Rat // total shares issued to a validator's delegators
|
DelegatorShares sdk.Rat // total shares issued to a validator's delegators
|
||||||
SlashRatio sdk.Rat // increases each time the validator is slashed
|
SlashRatio sdk.Rat // increases each time the validator is slashed
|
||||||
|
|
||||||
|
@ -100,7 +90,7 @@ type Validator struct {
|
||||||
ProposerRewardPool sdk.Coins // reward pool collected from being the proposer
|
ProposerRewardPool sdk.Coins // reward pool collected from being the proposer
|
||||||
|
|
||||||
// TODO: maybe this belongs in distribution module ?
|
// TODO: maybe this belongs in distribution module ?
|
||||||
PrevPoolShares PoolShares // total shares of a global hold pools
|
LastBondedTokens sdk.Rat // last bonded token amount
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommissionInfo struct {
|
type CommissionInfo struct {
|
||||||
|
|
|
@ -11,9 +11,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var kvPairs = []KVPair{
|
var kvPairs = []KVPair{
|
||||||
KVPair{Key: keyFmt(1), Value: valFmt(1)},
|
{Key: keyFmt(1), Value: valFmt(1)},
|
||||||
KVPair{Key: keyFmt(2), Value: valFmt(2)},
|
{Key: keyFmt(2), Value: valFmt(2)},
|
||||||
KVPair{Key: keyFmt(3), Value: valFmt(3)},
|
{Key: keyFmt(3), Value: valFmt(3)},
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTraceKVStore(w io.Writer) *TraceKVStore {
|
func newTraceKVStore(w io.Writer) *TraceKVStore {
|
||||||
|
|
|
@ -252,3 +252,11 @@ func RatsEqual(r1s, r2s []Rat) bool {
|
||||||
func RatEq(t *testing.T, exp, got Rat) (*testing.T, bool, string, Rat, Rat) {
|
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
|
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// minimum rational between two
|
||||||
|
func MinRat(r1, r2 Rat) Rat {
|
||||||
|
if r1.LT(r2) {
|
||||||
|
return r1
|
||||||
|
}
|
||||||
|
return r2
|
||||||
|
}
|
||||||
|
|
|
@ -26,10 +26,15 @@ func BondStatusToString(b BondStatus) string {
|
||||||
case 0x02:
|
case 0x02:
|
||||||
return "Bonded"
|
return "Bonded"
|
||||||
default:
|
default:
|
||||||
return ""
|
panic("improper use of BondStatusToString")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
func (b BondStatus) Equal(b2 BondStatus) bool {
|
||||||
|
return byte(b) == byte(b2)
|
||||||
|
}
|
||||||
|
|
||||||
// validator for a delegated proof of stake system
|
// validator for a delegated proof of stake system
|
||||||
type Validator interface {
|
type Validator interface {
|
||||||
GetRevoked() bool // whether the validator is revoked
|
GetRevoked() bool // whether the validator is revoked
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
package version
|
package version
|
||||||
|
|
||||||
const Maj = "0"
|
const Maj = "0"
|
||||||
const Min = "20"
|
const Min = "21"
|
||||||
const Fix = "0"
|
const Fix = "0"
|
||||||
|
|
||||||
const Version = "0.20.0"
|
const Version = "0.21.0"
|
||||||
|
|
||||||
// GitCommit set by build flags
|
// GitCommit set by build flags
|
||||||
var GitCommit = ""
|
var GitCommit = ""
|
||||||
|
|
|
@ -59,7 +59,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakeKeeper stake.Keeper) sdk
|
||||||
mapp.InitChainer(ctx, req)
|
mapp.InitChainer(ctx, req)
|
||||||
|
|
||||||
stakeGenesis := stake.DefaultGenesisState()
|
stakeGenesis := stake.DefaultGenesisState()
|
||||||
stakeGenesis.Pool.LooseTokens = 100000
|
stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000)
|
||||||
|
|
||||||
err := stake.InitGenesis(ctx, stakeKeeper, stakeGenesis)
|
err := stake.InitGenesis(ctx, stakeKeeper, stakeGenesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,405 @@
|
||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Keeper manages global parameter store
|
||||||
|
type Keeper struct {
|
||||||
|
cdc *wire.Codec
|
||||||
|
key sdk.StoreKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewKeeper constructs a new Keeper
|
||||||
|
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey) Keeper {
|
||||||
|
return Keeper{
|
||||||
|
cdc: cdc,
|
||||||
|
key: key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitKeeper constructs a new Keeper with initial parameters
|
||||||
|
func InitKeeper(ctx sdk.Context, cdc *wire.Codec, key sdk.StoreKey, params ...interface{}) Keeper {
|
||||||
|
if len(params)%2 != 0 {
|
||||||
|
panic("Odd params list length for InitKeeper")
|
||||||
|
}
|
||||||
|
|
||||||
|
k := NewKeeper(cdc, key)
|
||||||
|
|
||||||
|
for i := 0; i < len(params); i += 2 {
|
||||||
|
k.set(ctx, params[i].(string), params[i+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
// get automatically unmarshalls parameter to pointer
|
||||||
|
func (k Keeper) get(ctx sdk.Context, key string, ptr interface{}) error {
|
||||||
|
store := ctx.KVStore(k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
return k.cdc.UnmarshalBinary(bz, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRaw returns raw byte slice
|
||||||
|
func (k Keeper) getRaw(ctx sdk.Context, key string) []byte {
|
||||||
|
store := ctx.KVStore(k.key)
|
||||||
|
return store.Get([]byte(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
// set automatically marshalls and type check parameter
|
||||||
|
func (k Keeper) set(ctx sdk.Context, key string, param interface{}) error {
|
||||||
|
store := ctx.KVStore(k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz != nil {
|
||||||
|
ptrty := reflect.PtrTo(reflect.TypeOf(param))
|
||||||
|
ptr := reflect.New(ptrty).Interface()
|
||||||
|
|
||||||
|
if k.cdc.UnmarshalBinary(bz, ptr) != nil {
|
||||||
|
return fmt.Errorf("Type mismatch with stored param and provided param")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := k.cdc.MarshalBinary(param)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
store.Set([]byte(key), bz)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setRaw sets raw byte slice
|
||||||
|
func (k Keeper) setRaw(ctx sdk.Context, key string, param []byte) {
|
||||||
|
store := ctx.KVStore(k.key)
|
||||||
|
store.Set([]byte(key), param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter returns readonly struct
|
||||||
|
func (k Keeper) Getter() Getter {
|
||||||
|
return Getter{k}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setter returns read/write struct
|
||||||
|
func (k Keeper) Setter() Setter {
|
||||||
|
return Setter{Getter{k}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter exposes methods related with only getting params
|
||||||
|
type Getter struct {
|
||||||
|
k Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get exposes get
|
||||||
|
func (k Getter) Get(ctx sdk.Context, key string, ptr interface{}) error {
|
||||||
|
return k.k.get(ctx, key, ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRaw exposes getRaw
|
||||||
|
func (k Getter) GetRaw(ctx sdk.Context, key string) []byte {
|
||||||
|
return k.k.getRaw(ctx, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetString is helper function for string params
|
||||||
|
func (k Getter) GetString(ctx sdk.Context, key string) (res string, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBool is helper function for bool params
|
||||||
|
func (k Getter) GetBool(ctx sdk.Context, key string) (res bool, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt16 is helper function for int16 params
|
||||||
|
func (k Getter) GetInt16(ctx sdk.Context, key string) (res int16, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt32 is helper function for int32 params
|
||||||
|
func (k Getter) GetInt32(ctx sdk.Context, key string) (res int32, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64 is helper function for int64 params
|
||||||
|
func (k Getter) GetInt64(ctx sdk.Context, key string) (res int64, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint16 is helper function for uint16 params
|
||||||
|
func (k Getter) GetUint16(ctx sdk.Context, key string) (res uint16, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint32 is helper function for uint32 params
|
||||||
|
func (k Getter) GetUint32(ctx sdk.Context, key string) (res uint32, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint64 is helper function for uint64 params
|
||||||
|
func (k Getter) GetUint64(ctx sdk.Context, key string) (res uint64, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt is helper function for sdk.Int params
|
||||||
|
func (k Getter) GetInt(ctx sdk.Context, key string) (res sdk.Int, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint is helper function for sdk.Uint params
|
||||||
|
func (k Getter) GetUint(ctx sdk.Context, key string) (res sdk.Uint, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRat is helper function for rat params
|
||||||
|
func (k Getter) GetRat(ctx sdk.Context, key string) (res sdk.Rat, err error) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
err = k.k.cdc.UnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStringWithDefault is helper function for string params with default value
|
||||||
|
func (k Getter) GetStringWithDefault(ctx sdk.Context, key string, def string) (res string) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBoolWithDefault is helper function for bool params with default value
|
||||||
|
func (k Getter) GetBoolWithDefault(ctx sdk.Context, key string, def bool) (res bool) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt16WithDefault is helper function for int16 params with default value
|
||||||
|
func (k Getter) GetInt16WithDefault(ctx sdk.Context, key string, def int16) (res int16) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt32WithDefault is helper function for int32 params with default value
|
||||||
|
func (k Getter) GetInt32WithDefault(ctx sdk.Context, key string, def int32) (res int32) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInt64WithDefault is helper function for int64 params with default value
|
||||||
|
func (k Getter) GetInt64WithDefault(ctx sdk.Context, key string, def int64) (res int64) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint16WithDefault is helper function for uint16 params with default value
|
||||||
|
func (k Getter) GetUint16WithDefault(ctx sdk.Context, key string, def uint16) (res uint16) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint32WithDefault is helper function for uint32 params with default value
|
||||||
|
func (k Getter) GetUint32WithDefault(ctx sdk.Context, key string, def uint32) (res uint32) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUint64WithDefault is helper function for uint64 params with default value
|
||||||
|
func (k Getter) GetUint64WithDefault(ctx sdk.Context, key string, def uint64) (res uint64) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIntWithDefault is helper function for sdk.Int params with default value
|
||||||
|
func (k Getter) GetIntWithDefault(ctx sdk.Context, key string, def sdk.Int) (res sdk.Int) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUintWithDefault is helper function for sdk.Uint params with default value
|
||||||
|
func (k Getter) GetUintWithDefault(ctx sdk.Context, key string, def sdk.Uint) (res sdk.Uint) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRatWithDefault is helper function for sdk.Rat params with default value
|
||||||
|
func (k Getter) GetRatWithDefault(ctx sdk.Context, key string, def sdk.Rat) (res sdk.Rat) {
|
||||||
|
store := ctx.KVStore(k.k.key)
|
||||||
|
bz := store.Get([]byte(key))
|
||||||
|
if bz == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
k.k.cdc.MustUnmarshalBinary(bz, &res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setter exposes all methods including Set
|
||||||
|
type Setter struct {
|
||||||
|
Getter
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set exposes set
|
||||||
|
func (k Setter) Set(ctx sdk.Context, key string, param interface{}) error {
|
||||||
|
return k.k.set(ctx, key, param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRaw exposes setRaw
|
||||||
|
func (k Setter) SetRaw(ctx sdk.Context, key string, param []byte) {
|
||||||
|
k.k.setRaw(ctx, key, param)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetString is helper function for string params
|
||||||
|
func (k Setter) SetString(ctx sdk.Context, key string, param string) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBool is helper function for bool params
|
||||||
|
func (k Setter) SetBool(ctx sdk.Context, key string, param bool) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt16 is helper function for int16 params
|
||||||
|
func (k Setter) SetInt16(ctx sdk.Context, key string, param int16) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt32 is helper function for int32 params
|
||||||
|
func (k Setter) SetInt32(ctx sdk.Context, key string, param int32) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt64 is helper function for int64 params
|
||||||
|
func (k Setter) SetInt64(ctx sdk.Context, key string, param int64) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint16 is helper function for uint16 params
|
||||||
|
func (k Setter) SetUint16(ctx sdk.Context, key string, param uint16) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint32 is helper function for uint32 params
|
||||||
|
func (k Setter) SetUint32(ctx sdk.Context, key string, param uint32) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint64 is helper function for uint64 params
|
||||||
|
func (k Setter) SetUint64(ctx sdk.Context, key string, param uint64) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInt is helper function for sdk.Int params
|
||||||
|
func (k Setter) SetInt(ctx sdk.Context, key string, param sdk.Int) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUint is helper function for sdk.Uint params
|
||||||
|
func (k Setter) SetUint(ctx sdk.Context, key string, param sdk.Uint) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRat is helper function for rat params
|
||||||
|
func (k Setter) SetRat(ctx sdk.Context, key string, param sdk.Rat) {
|
||||||
|
if err := k.k.set(ctx, key, param); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,280 @@
|
||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/store"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
func defaultContext(key sdk.StoreKey) sdk.Context {
|
||||||
|
db := dbm.NewMemDB()
|
||||||
|
cms := store.NewCommitMultiStore(db)
|
||||||
|
cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
||||||
|
cms.LoadLatestVersion()
|
||||||
|
ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger())
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeeper(t *testing.T) {
|
||||||
|
kvs := []struct {
|
||||||
|
key string
|
||||||
|
param int64
|
||||||
|
}{
|
||||||
|
{"key1", 10},
|
||||||
|
{"key2", 55},
|
||||||
|
{"key3", 182},
|
||||||
|
{"key4", 17582},
|
||||||
|
{"key5", 2768554},
|
||||||
|
}
|
||||||
|
|
||||||
|
skey := sdk.NewKVStoreKey("test")
|
||||||
|
ctx := defaultContext(skey)
|
||||||
|
setter := NewKeeper(wire.NewCodec(), skey).Setter()
|
||||||
|
|
||||||
|
for _, kv := range kvs {
|
||||||
|
err := setter.Set(ctx, kv.key, kv.param)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range kvs {
|
||||||
|
var param int64
|
||||||
|
err := setter.Get(ctx, kv.key, ¶m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kv.param, param)
|
||||||
|
}
|
||||||
|
|
||||||
|
cdc := wire.NewCodec()
|
||||||
|
for _, kv := range kvs {
|
||||||
|
var param int64
|
||||||
|
bz := setter.GetRaw(ctx, kv.key)
|
||||||
|
err := cdc.UnmarshalBinary(bz, ¶m)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kv.param, param)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range kvs {
|
||||||
|
var param bool
|
||||||
|
err := setter.Get(ctx, kv.key, ¶m)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range kvs {
|
||||||
|
err := setter.Set(ctx, kv.key, true)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetter(t *testing.T) {
|
||||||
|
key := sdk.NewKVStoreKey("test")
|
||||||
|
ctx := defaultContext(key)
|
||||||
|
keeper := NewKeeper(wire.NewCodec(), key)
|
||||||
|
|
||||||
|
g := keeper.Getter()
|
||||||
|
s := keeper.Setter()
|
||||||
|
|
||||||
|
kvs := []struct {
|
||||||
|
key string
|
||||||
|
param interface{}
|
||||||
|
}{
|
||||||
|
{"string", "test"},
|
||||||
|
{"bool", true},
|
||||||
|
{"int16", int16(1)},
|
||||||
|
{"int32", int32(1)},
|
||||||
|
{"int64", int64(1)},
|
||||||
|
{"uint16", uint16(1)},
|
||||||
|
{"uint32", uint32(1)},
|
||||||
|
{"uint64", uint64(1)},
|
||||||
|
{"int", sdk.NewInt(1)},
|
||||||
|
{"uint", sdk.NewUint(1)},
|
||||||
|
{"rat", sdk.NewRat(1)},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotPanics(t, func() { s.SetString(ctx, kvs[0].key, "test") })
|
||||||
|
assert.NotPanics(t, func() { s.SetBool(ctx, kvs[1].key, true) })
|
||||||
|
assert.NotPanics(t, func() { s.SetInt16(ctx, kvs[2].key, int16(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetInt32(ctx, kvs[3].key, int32(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetInt64(ctx, kvs[4].key, int64(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetUint16(ctx, kvs[5].key, uint16(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetUint32(ctx, kvs[6].key, uint32(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetUint64(ctx, kvs[7].key, uint64(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetInt(ctx, kvs[8].key, sdk.NewInt(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetUint(ctx, kvs[9].key, sdk.NewUint(1)) })
|
||||||
|
assert.NotPanics(t, func() { s.SetRat(ctx, kvs[10].key, sdk.NewRat(1)) })
|
||||||
|
|
||||||
|
var res interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// String
|
||||||
|
def0 := "default"
|
||||||
|
res, err = g.GetString(ctx, kvs[0].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[0].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetString(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetStringWithDefault(ctx, kvs[0].key, def0)
|
||||||
|
assert.Equal(t, kvs[0].param, res)
|
||||||
|
|
||||||
|
res = g.GetStringWithDefault(ctx, "invalid", def0)
|
||||||
|
assert.Equal(t, def0, res)
|
||||||
|
|
||||||
|
// Bool
|
||||||
|
def1 := false
|
||||||
|
res, err = g.GetBool(ctx, kvs[1].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[1].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetBool(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetBoolWithDefault(ctx, kvs[1].key, def1)
|
||||||
|
assert.Equal(t, kvs[1].param, res)
|
||||||
|
|
||||||
|
res = g.GetBoolWithDefault(ctx, "invalid", def1)
|
||||||
|
assert.Equal(t, def1, res)
|
||||||
|
|
||||||
|
// Int16
|
||||||
|
def2 := int16(0)
|
||||||
|
res, err = g.GetInt16(ctx, kvs[2].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[2].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetInt16(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetInt16WithDefault(ctx, kvs[2].key, def2)
|
||||||
|
assert.Equal(t, kvs[2].param, res)
|
||||||
|
|
||||||
|
res = g.GetInt16WithDefault(ctx, "invalid", def2)
|
||||||
|
assert.Equal(t, def2, res)
|
||||||
|
|
||||||
|
// Int32
|
||||||
|
def3 := int32(0)
|
||||||
|
res, err = g.GetInt32(ctx, kvs[3].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[3].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetInt32(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetInt32WithDefault(ctx, kvs[3].key, def3)
|
||||||
|
assert.Equal(t, kvs[3].param, res)
|
||||||
|
|
||||||
|
res = g.GetInt32WithDefault(ctx, "invalid", def3)
|
||||||
|
assert.Equal(t, def3, res)
|
||||||
|
|
||||||
|
// Int64
|
||||||
|
def4 := int64(0)
|
||||||
|
res, err = g.GetInt64(ctx, kvs[4].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[4].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetInt64(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetInt64WithDefault(ctx, kvs[4].key, def4)
|
||||||
|
assert.Equal(t, kvs[4].param, res)
|
||||||
|
|
||||||
|
res = g.GetInt64WithDefault(ctx, "invalid", def4)
|
||||||
|
assert.Equal(t, def4, res)
|
||||||
|
|
||||||
|
// Uint16
|
||||||
|
def5 := uint16(0)
|
||||||
|
res, err = g.GetUint16(ctx, kvs[5].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[5].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetUint16(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetUint16WithDefault(ctx, kvs[5].key, def5)
|
||||||
|
assert.Equal(t, kvs[5].param, res)
|
||||||
|
|
||||||
|
res = g.GetUint16WithDefault(ctx, "invalid", def5)
|
||||||
|
assert.Equal(t, def5, res)
|
||||||
|
|
||||||
|
// Uint32
|
||||||
|
def6 := uint32(0)
|
||||||
|
res, err = g.GetUint32(ctx, kvs[6].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[6].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetUint32(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetUint32WithDefault(ctx, kvs[6].key, def6)
|
||||||
|
assert.Equal(t, kvs[6].param, res)
|
||||||
|
|
||||||
|
res = g.GetUint32WithDefault(ctx, "invalid", def6)
|
||||||
|
assert.Equal(t, def6, res)
|
||||||
|
|
||||||
|
// Uint64
|
||||||
|
def7 := uint64(0)
|
||||||
|
res, err = g.GetUint64(ctx, kvs[7].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[7].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetUint64(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetUint64WithDefault(ctx, kvs[7].key, def7)
|
||||||
|
assert.Equal(t, kvs[7].param, res)
|
||||||
|
|
||||||
|
res = g.GetUint64WithDefault(ctx, "invalid", def7)
|
||||||
|
assert.Equal(t, def7, res)
|
||||||
|
|
||||||
|
// Int
|
||||||
|
def8 := sdk.NewInt(0)
|
||||||
|
res, err = g.GetInt(ctx, kvs[8].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[8].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetInt(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetIntWithDefault(ctx, kvs[8].key, def8)
|
||||||
|
assert.Equal(t, kvs[8].param, res)
|
||||||
|
|
||||||
|
res = g.GetIntWithDefault(ctx, "invalid", def8)
|
||||||
|
assert.Equal(t, def8, res)
|
||||||
|
|
||||||
|
// Uint
|
||||||
|
def9 := sdk.NewUint(0)
|
||||||
|
res, err = g.GetUint(ctx, kvs[9].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[9].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetUint(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetUintWithDefault(ctx, kvs[9].key, def9)
|
||||||
|
assert.Equal(t, kvs[9].param, res)
|
||||||
|
|
||||||
|
res = g.GetUintWithDefault(ctx, "invalid", def9)
|
||||||
|
assert.Equal(t, def9, res)
|
||||||
|
|
||||||
|
// Rat
|
||||||
|
def10 := sdk.NewRat(0)
|
||||||
|
res, err = g.GetRat(ctx, kvs[10].key)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, kvs[10].param, res)
|
||||||
|
|
||||||
|
_, err = g.GetRat(ctx, "invalid")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
res = g.GetRatWithDefault(ctx, kvs[10].key, def10)
|
||||||
|
assert.Equal(t, kvs[10].param, res)
|
||||||
|
|
||||||
|
res = g.GetRatWithDefault(ctx, "invalid", def10)
|
||||||
|
assert.Equal(t, def10, res)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package params
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenesisState defines initial activated msg types
|
||||||
|
type GenesisState struct {
|
||||||
|
ActivatedTypes []string `json:"activated-types"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ActivatedParamKey - paramstore key for msg type activation
|
||||||
|
func ActivatedParamKey(ty string) string {
|
||||||
|
return "Activated/" + ty
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitGenesis stores activated type to param store
|
||||||
|
func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) {
|
||||||
|
for _, ty := range data.ActivatedTypes {
|
||||||
|
k.set(ctx, ActivatedParamKey(ty), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAnteHandler returns an AnteHandler that checks
|
||||||
|
// whether msg type is activate or not
|
||||||
|
func NewAnteHandler(k Keeper) sdk.AnteHandler {
|
||||||
|
return func(ctx sdk.Context, tx sdk.Tx) (sdk.Context, sdk.Result, bool) {
|
||||||
|
for _, msg := range tx.GetMsgs() {
|
||||||
|
ok := k.Getter().GetBoolWithDefault(ctx, ActivatedParamKey(msg.Type()), false)
|
||||||
|
if !ok {
|
||||||
|
return ctx, sdk.ErrUnauthorized("deactivated msg type").Result(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ctx, sdk.Result{}, false
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
@ -26,15 +27,18 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) {
|
||||||
RegisterWire(mapp.Cdc)
|
RegisterWire(mapp.Cdc)
|
||||||
keyStake := sdk.NewKVStoreKey("stake")
|
keyStake := sdk.NewKVStoreKey("stake")
|
||||||
keySlashing := sdk.NewKVStoreKey("slashing")
|
keySlashing := sdk.NewKVStoreKey("slashing")
|
||||||
|
keyParams := sdk.NewKVStoreKey("params")
|
||||||
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
coinKeeper := bank.NewKeeper(mapp.AccountMapper)
|
||||||
|
paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams)
|
||||||
stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(stake.DefaultCodespace))
|
stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, coinKeeper, mapp.RegisterCodespace(stake.DefaultCodespace))
|
||||||
keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, mapp.RegisterCodespace(DefaultCodespace))
|
|
||||||
|
keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Getter(), mapp.RegisterCodespace(DefaultCodespace))
|
||||||
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
|
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
|
||||||
mapp.Router().AddRoute("slashing", NewHandler(keeper))
|
mapp.Router().AddRoute("slashing", NewHandler(keeper))
|
||||||
|
|
||||||
mapp.SetEndBlocker(getEndBlocker(stakeKeeper))
|
mapp.SetEndBlocker(getEndBlocker(stakeKeeper))
|
||||||
mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper))
|
mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper))
|
||||||
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keySlashing}))
|
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keySlashing, keyParams}))
|
||||||
|
|
||||||
return mapp, stakeKeeper, keeper
|
return mapp, stakeKeeper, keeper
|
||||||
}
|
}
|
||||||
|
@ -54,7 +58,7 @@ func getInitChainer(mapp *mock.App, keeper stake.Keeper) sdk.InitChainer {
|
||||||
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
mapp.InitChainer(ctx, req)
|
mapp.InitChainer(ctx, req)
|
||||||
stakeGenesis := stake.DefaultGenesisState()
|
stakeGenesis := stake.DefaultGenesisState()
|
||||||
stakeGenesis.Pool.LooseTokens = 100000
|
stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000)
|
||||||
err := stake.InitGenesis(ctx, keeper, stakeGenesis)
|
err := stake.InitGenesis(ctx, keeper, stakeGenesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -101,8 +105,8 @@ func TestSlashingMsgs(t *testing.T) {
|
||||||
|
|
||||||
validator := checkValidator(t, mapp, stakeKeeper, addr1, true)
|
validator := checkValidator(t, mapp, stakeKeeper, addr1, true)
|
||||||
require.Equal(t, addr1, validator.Owner)
|
require.Equal(t, addr1, validator.Owner)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens()))
|
||||||
unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())}
|
unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())}
|
||||||
|
|
||||||
// no signing info yet
|
// no signing info yet
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
func TestCannotUnrevokeUnlessRevoked(t *testing.T) {
|
func TestCannotUnrevokeUnlessRevoked(t *testing.T) {
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
slh := NewHandler(keeper)
|
slh := NewHandler(keeper)
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,17 +14,19 @@ type Keeper struct {
|
||||||
storeKey sdk.StoreKey
|
storeKey sdk.StoreKey
|
||||||
cdc *wire.Codec
|
cdc *wire.Codec
|
||||||
validatorSet sdk.ValidatorSet
|
validatorSet sdk.ValidatorSet
|
||||||
|
params params.Getter
|
||||||
|
|
||||||
// codespace
|
// codespace
|
||||||
codespace sdk.CodespaceType
|
codespace sdk.CodespaceType
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper creates a slashing keeper
|
// NewKeeper creates a slashing keeper
|
||||||
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, codespace sdk.CodespaceType) Keeper {
|
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, params params.Getter, codespace sdk.CodespaceType) Keeper {
|
||||||
keeper := Keeper{
|
keeper := Keeper{
|
||||||
storeKey: key,
|
storeKey: key,
|
||||||
cdc: cdc,
|
cdc: cdc,
|
||||||
validatorSet: vs,
|
validatorSet: vs,
|
||||||
|
params: params,
|
||||||
codespace: codespace,
|
codespace: codespace,
|
||||||
}
|
}
|
||||||
return keeper
|
return keeper
|
||||||
|
@ -37,16 +40,17 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti
|
||||||
address := sdk.ValAddress(pubkey.Address())
|
address := sdk.ValAddress(pubkey.Address())
|
||||||
|
|
||||||
// Double sign too old
|
// Double sign too old
|
||||||
if age > MaxEvidenceAge {
|
maxEvidenceAge := k.MaxEvidenceAge(ctx)
|
||||||
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), infractionHeight, age, MaxEvidenceAge))
|
if age > maxEvidenceAge {
|
||||||
|
logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double sign confirmed
|
// Double sign confirmed
|
||||||
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, MaxEvidenceAge))
|
logger.Info(fmt.Sprintf("Confirmed double sign from %s at height %d, age of %d less than max age of %d", pubkey.Address(), infractionHeight, age, maxEvidenceAge))
|
||||||
|
|
||||||
// Slash validator
|
// Slash validator
|
||||||
k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, SlashFractionDoubleSign)
|
k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, k.SlashFractionDoubleSign(ctx))
|
||||||
|
|
||||||
// Revoke validator
|
// Revoke validator
|
||||||
k.validatorSet.Revoke(ctx, pubkey)
|
k.validatorSet.Revoke(ctx, pubkey)
|
||||||
|
@ -56,7 +60,7 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, pubkey crypto.PubKey, infracti
|
||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("Expected signing info for validator %s but not found", address))
|
panic(fmt.Sprintf("Expected signing info for validator %s but not found", address))
|
||||||
}
|
}
|
||||||
signInfo.JailedUntil = time + DoubleSignUnbondDuration
|
signInfo.JailedUntil = time + k.DoubleSignUnbondDuration(ctx)
|
||||||
k.setValidatorSigningInfo(ctx, address, signInfo)
|
k.setValidatorSigningInfo(ctx, address, signInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +77,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
|
||||||
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
|
// If this validator has never been seen before, construct a new SigningInfo with the correct start height
|
||||||
signInfo = NewValidatorSigningInfo(height, 0, 0, 0)
|
signInfo = NewValidatorSigningInfo(height, 0, 0, 0)
|
||||||
}
|
}
|
||||||
index := signInfo.IndexOffset % SignedBlocksWindow
|
index := signInfo.IndexOffset % k.SignedBlocksWindow(ctx)
|
||||||
signInfo.IndexOffset++
|
signInfo.IndexOffset++
|
||||||
|
|
||||||
// Update signed block bit array & counter
|
// Update signed block bit array & counter
|
||||||
|
@ -93,15 +97,15 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, pubkey crypto.PubKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !signed {
|
if !signed {
|
||||||
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, MinSignedPerWindow))
|
logger.Info(fmt.Sprintf("Absent validator %s at height %d, %d signed, threshold %d", pubkey.Address(), height, signInfo.SignedBlocksCounter, k.MinSignedPerWindow(ctx)))
|
||||||
}
|
}
|
||||||
minHeight := signInfo.StartHeight + SignedBlocksWindow
|
minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx)
|
||||||
if height > minHeight && signInfo.SignedBlocksCounter < MinSignedPerWindow {
|
if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) {
|
||||||
// Downtime confirmed, slash, revoke, and jail the validator
|
// Downtime confirmed, slash, revoke, and jail the validator
|
||||||
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, MinSignedPerWindow))
|
logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx)))
|
||||||
k.validatorSet.Slash(ctx, pubkey, height, power, SlashFractionDowntime)
|
k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx))
|
||||||
k.validatorSet.Revoke(ctx, pubkey)
|
k.validatorSet.Revoke(ctx, pubkey)
|
||||||
signInfo.JailedUntil = ctx.BlockHeader().Time + DowntimeUnbondDuration
|
signInfo.JailedUntil = ctx.BlockHeader().Time + k.DowntimeUnbondDuration(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the updated signing info
|
// Set the updated signing info
|
||||||
|
|
|
@ -14,10 +14,9 @@ import (
|
||||||
// Have to change these parameters for tests
|
// Have to change these parameters for tests
|
||||||
// lest the tests take forever
|
// lest the tests take forever
|
||||||
func init() {
|
func init() {
|
||||||
SignedBlocksWindow = 1000
|
defaultSignedBlocksWindow = 1000
|
||||||
MinSignedPerWindow = SignedBlocksWindow / 2
|
defaultDowntimeUnbondDuration = 60 * 60
|
||||||
DowntimeUnbondDuration = 60 * 60
|
defaultDoubleSignUnbondDuration = 60 * 60
|
||||||
DoubleSignUnbondDuration = 60 * 60
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that a validator is slashed correctly
|
// Test that a validator is slashed correctly
|
||||||
|
@ -25,7 +24,7 @@ func init() {
|
||||||
func TestHandleDoubleSign(t *testing.T) {
|
func TestHandleDoubleSign(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
|
got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt))
|
||||||
|
@ -46,7 +45,7 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||||
sk.Unrevoke(ctx, val)
|
sk.Unrevoke(ctx, val)
|
||||||
// power should be reduced
|
// power should be reduced
|
||||||
require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, sdk.NewRatFromInt(amt).Mul(sdk.NewRat(19).Quo(sdk.NewRat(20))), sk.Validator(ctx, addr).GetPower())
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + MaxEvidenceAge})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: 1 + keeper.MaxEvidenceAge(ctx)})
|
||||||
|
|
||||||
// double sign past max age
|
// double sign past max age
|
||||||
keeper.handleDoubleSign(ctx, val, 0, 0, amtInt)
|
keeper.handleDoubleSign(ctx, val, 0, 0, amtInt)
|
||||||
|
@ -58,7 +57,7 @@ func TestHandleDoubleSign(t *testing.T) {
|
||||||
func TestHandleAbsentValidator(t *testing.T) {
|
func TestHandleAbsentValidator(t *testing.T) {
|
||||||
|
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
amtInt := int64(100)
|
amtInt := int64(100)
|
||||||
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt)
|
||||||
sh := stake.NewHandler(sk)
|
sh := stake.NewHandler(sk)
|
||||||
|
@ -77,30 +76,30 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
height := int64(0)
|
height := int64(0)
|
||||||
|
|
||||||
// 1000 first blocks OK
|
// 1000 first blocks OK
|
||||||
for ; height < SignedBlocksWindow; height++ {
|
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val, amtInt, true)
|
keeper.handleValidatorSignature(ctx, val, amtInt, true)
|
||||||
}
|
}
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, SignedBlocksWindow, info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx), info.SignedBlocksCounter)
|
||||||
|
|
||||||
// 500 blocks missed
|
// 500 blocks missed
|
||||||
for ; height < SignedBlocksWindow+(SignedBlocksWindow-MinSignedPerWindow); height++ {
|
for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
||||||
}
|
}
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, SignedBlocksWindow-MinSignedPerWindow, info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter)
|
||||||
|
|
||||||
// validator should be bonded still
|
// validator should be bonded still
|
||||||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
pool := sk.GetPool(ctx)
|
pool := sk.GetPool(ctx)
|
||||||
require.Equal(t, int64(amtInt), pool.BondedTokens)
|
require.Equal(t, int64(amtInt), pool.BondedTokens.RoundInt64())
|
||||||
|
|
||||||
// 501st block missed
|
// 501st block missed
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
|
@ -108,7 +107,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(0), info.StartHeight)
|
require.Equal(t, int64(0), info.StartHeight)
|
||||||
require.Equal(t, SignedBlocksWindow-MinSignedPerWindow-1, info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
||||||
|
|
||||||
// validator should have been revoked
|
// validator should have been revoked
|
||||||
validator, _ = sk.GetValidatorByPubKey(ctx, val)
|
validator, _ = sk.GetValidatorByPubKey(ctx, val)
|
||||||
|
@ -119,7 +118,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
require.False(t, got.IsOK())
|
require.False(t, got.IsOK())
|
||||||
|
|
||||||
// unrevocation should succeed after jail expiration
|
// unrevocation should succeed after jail expiration
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: DowntimeUnbondDuration + 1})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: keeper.DowntimeUnbondDuration(ctx) + 1})
|
||||||
got = slh(ctx, NewMsgUnrevoke(addr))
|
got = slh(ctx, NewMsgUnrevoke(addr))
|
||||||
require.True(t, got.IsOK())
|
require.True(t, got.IsOK())
|
||||||
|
|
||||||
|
@ -129,13 +128,13 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
|
|
||||||
// validator should have been slashed
|
// validator should have been slashed
|
||||||
pool = sk.GetPool(ctx)
|
pool = sk.GetPool(ctx)
|
||||||
require.Equal(t, int64(amtInt-1), pool.BondedTokens)
|
require.Equal(t, int64(amtInt-1), pool.BondedTokens.RoundInt64())
|
||||||
|
|
||||||
// validator start height should have been changed
|
// validator start height should have been changed
|
||||||
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
info, found = keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, height, info.StartHeight)
|
require.Equal(t, height, info.StartHeight)
|
||||||
require.Equal(t, SignedBlocksWindow-MinSignedPerWindow-1, info.SignedBlocksCounter)
|
require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter)
|
||||||
|
|
||||||
// validator should not be immediately revoked again
|
// validator should not be immediately revoked again
|
||||||
height++
|
height++
|
||||||
|
@ -145,14 +144,14 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
|
|
||||||
// 500 signed blocks
|
// 500 signed blocks
|
||||||
nextHeight := height + MinSignedPerWindow + 1
|
nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1
|
||||||
for ; height < nextHeight; height++ {
|
for ; height < nextHeight; height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validator should be revoked again after 500 unsigned blocks
|
// validator should be revoked again after 500 unsigned blocks
|
||||||
nextHeight = height + MinSignedPerWindow + 1
|
nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1
|
||||||
for ; height <= nextHeight; height++ {
|
for ; height <= nextHeight; height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
keeper.handleValidatorSignature(ctx, val, amtInt, false)
|
||||||
|
@ -166,7 +165,7 @@ func TestHandleAbsentValidator(t *testing.T) {
|
||||||
// and that they are not immediately revoked
|
// and that they are not immediately revoked
|
||||||
func TestHandleNewValidator(t *testing.T) {
|
func TestHandleNewValidator(t *testing.T) {
|
||||||
// initial setup
|
// initial setup
|
||||||
ctx, ck, sk, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
addr, val, amt := addrs[0], pks[0], int64(100)
|
addr, val, amt := addrs[0], pks[0], int64(100)
|
||||||
sh := stake.NewHandler(sk)
|
sh := stake.NewHandler(sk)
|
||||||
got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
|
got := sh(ctx, newTestMsgCreateValidator(addr, val, sdk.NewInt(amt)))
|
||||||
|
@ -176,16 +175,16 @@ func TestHandleNewValidator(t *testing.T) {
|
||||||
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
|
require.Equal(t, sdk.NewRat(amt), sk.Validator(ctx, addr).GetPower())
|
||||||
|
|
||||||
// 1000 first blocks not a validator
|
// 1000 first blocks not a validator
|
||||||
ctx = ctx.WithBlockHeight(SignedBlocksWindow + 1)
|
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1)
|
||||||
|
|
||||||
// Now a validator, for two blocks
|
// Now a validator, for two blocks
|
||||||
keeper.handleValidatorSignature(ctx, val, 100, true)
|
keeper.handleValidatorSignature(ctx, val, 100, true)
|
||||||
ctx = ctx.WithBlockHeight(SignedBlocksWindow + 2)
|
ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 2)
|
||||||
keeper.handleValidatorSignature(ctx, val, 100, false)
|
keeper.handleValidatorSignature(ctx, val, 100, false)
|
||||||
|
|
||||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(val.Address()))
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(SignedBlocksWindow+1), info.StartHeight)
|
require.Equal(t, int64(keeper.SignedBlocksWindow(ctx)+1), info.StartHeight)
|
||||||
require.Equal(t, int64(2), info.IndexOffset)
|
require.Equal(t, int64(2), info.IndexOffset)
|
||||||
require.Equal(t, int64(1), info.SignedBlocksCounter)
|
require.Equal(t, int64(1), info.SignedBlocksCounter)
|
||||||
require.Equal(t, int64(0), info.JailedUntil)
|
require.Equal(t, int64(0), info.JailedUntil)
|
||||||
|
@ -194,5 +193,5 @@ func TestHandleNewValidator(t *testing.T) {
|
||||||
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
validator, _ := sk.GetValidatorByPubKey(ctx, val)
|
||||||
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
require.Equal(t, sdk.Bonded, validator.GetStatus())
|
||||||
pool := sk.GetPool(ctx)
|
pool := sk.GetPool(ctx)
|
||||||
require.Equal(t, int64(100), pool.BondedTokens)
|
require.Equal(t, int64(100), pool.BondedTokens.RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,39 +4,75 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
const (
|
||||||
|
MaxEvidenceAgeKey = "slashing/MaxEvidenceAge"
|
||||||
|
SignedBlocksWindowKey = "slashing/SignedBlocksWindow"
|
||||||
|
MinSignedPerWindowKey = "slashing/MinSignedPerWindow"
|
||||||
|
DoubleSignUnbondDurationKey = "slashing/DoubleSignUnbondDuration"
|
||||||
|
DowntimeUnbondDurationKey = "slashing/DowntimeUnbondDuration"
|
||||||
|
SlashFractionDoubleSignKey = "slashing/SlashFractionDoubleSign"
|
||||||
|
SlashFractionDowntimeKey = "slashing/SlashFractionDowntime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxEvidenceAge - Max age for evidence - 21 days (3 weeks)
|
||||||
|
// MaxEvidenceAge = 60 * 60 * 24 * 7 * 3
|
||||||
|
func (k Keeper) MaxEvidenceAge(ctx sdk.Context) int64 {
|
||||||
|
return k.params.GetInt64WithDefault(ctx, MaxEvidenceAgeKey, defaultMaxEvidenceAge)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignedBlocksWindow - sliding window for downtime slashing
|
||||||
|
func (k Keeper) SignedBlocksWindow(ctx sdk.Context) int64 {
|
||||||
|
return k.params.GetInt64WithDefault(ctx, SignedBlocksWindowKey, defaultSignedBlocksWindow)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downtime slashing thershold - default 50%
|
||||||
|
func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 {
|
||||||
|
minSignedPerWindow := k.params.GetRatWithDefault(ctx, MinSignedPerWindowKey, defaultMinSignedPerWindow)
|
||||||
|
signedBlocksWindow := k.SignedBlocksWindow(ctx)
|
||||||
|
return sdk.NewRat(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-sign unbond duration
|
||||||
|
func (k Keeper) DoubleSignUnbondDuration(ctx sdk.Context) int64 {
|
||||||
|
return k.params.GetInt64WithDefault(ctx, DoubleSignUnbondDurationKey, defaultDoubleSignUnbondDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Downtime unbond duration
|
||||||
|
func (k Keeper) DowntimeUnbondDuration(ctx sdk.Context) int64 {
|
||||||
|
return k.params.GetInt64WithDefault(ctx, DowntimeUnbondDurationKey, defaultDowntimeUnbondDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlashFractionDoubleSign - currently default 5%
|
||||||
|
func (k Keeper) SlashFractionDoubleSign(ctx sdk.Context) sdk.Rat {
|
||||||
|
return k.params.GetRatWithDefault(ctx, SlashFractionDoubleSignKey, defaultSlashFractionDoubleSign)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SlashFractionDowntime - currently default 1%
|
||||||
|
func (k Keeper) SlashFractionDowntime(ctx sdk.Context) sdk.Rat {
|
||||||
|
return k.params.GetRatWithDefault(ctx, SlashFractionDowntimeKey, defaultSlashFractionDowntime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// declared as var because of keeper_test.go
|
||||||
|
// TODO: make it const or parameter of NewKeeper
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// MaxEvidenceAge - Max age for evidence - 21 days (3 weeks)
|
// defaultMaxEvidenceAge = 60 * 60 * 24 * 7 * 3
|
||||||
// TODO Should this be a governance parameter or just modifiable with SoftwareUpgradeProposals?
|
|
||||||
// MaxEvidenceAge = 60 * 60 * 24 * 7 * 3
|
|
||||||
// TODO Temporarily set to 2 minutes for testnets.
|
// TODO Temporarily set to 2 minutes for testnets.
|
||||||
MaxEvidenceAge int64 = 60 * 2
|
defaultMaxEvidenceAge int64 = 60 * 2
|
||||||
|
|
||||||
// SignedBlocksWindow - sliding window for downtime slashing
|
|
||||||
// TODO Governance parameter?
|
|
||||||
// TODO Temporarily set to 40000 blocks for testnets
|
|
||||||
SignedBlocksWindow int64 = 40000
|
|
||||||
|
|
||||||
// Downtime slashing threshold - 50%
|
|
||||||
// TODO Governance parameter?
|
|
||||||
MinSignedPerWindow = SignedBlocksWindow / 2
|
|
||||||
|
|
||||||
// Downtime unbond duration
|
|
||||||
// TODO Governance parameter?
|
|
||||||
// TODO Temporarily set to five minutes for testnets
|
// TODO Temporarily set to five minutes for testnets
|
||||||
DowntimeUnbondDuration int64 = 60 * 5
|
defaultDoubleSignUnbondDuration int64 = 60 * 5
|
||||||
|
|
||||||
// Double-sign unbond duration
|
// TODO Temporarily set to 100 blocks for testnets
|
||||||
// TODO Governance parameter?
|
defaultSignedBlocksWindow int64 = 100
|
||||||
// TODO Temporarily set to five minutes for testnets
|
|
||||||
DoubleSignUnbondDuration int64 = 60 * 5
|
// TODO Temporarily set to 10 minutes for testnets
|
||||||
)
|
defaultDowntimeUnbondDuration int64 = 60 * 10
|
||||||
|
|
||||||
var (
|
defaultMinSignedPerWindow sdk.Rat = sdk.NewRat(1, 2)
|
||||||
// SlashFractionDoubleSign - currently 5%
|
|
||||||
// TODO Governance parameter?
|
defaultSlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20))
|
||||||
SlashFractionDoubleSign = sdk.NewRat(1).Quo(sdk.NewRat(20))
|
|
||||||
|
defaultSlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100))
|
||||||
// SlashFractionDowntime - currently 1%
|
|
||||||
// TODO Governance parameter?
|
|
||||||
SlashFractionDowntime = sdk.NewRat(1).Quo(sdk.NewRat(100))
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGetSetValidatorSigningInfo(t *testing.T) {
|
func TestGetSetValidatorSigningInfo(t *testing.T) {
|
||||||
ctx, _, _, keeper := createTestInput(t)
|
ctx, _, _, _, keeper := createTestInput(t)
|
||||||
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]))
|
info, found := keeper.getValidatorSigningInfo(ctx, sdk.ValAddress(addrs[0]))
|
||||||
require.False(t, found)
|
require.False(t, found)
|
||||||
newInfo := ValidatorSigningInfo{
|
newInfo := ValidatorSigningInfo{
|
||||||
|
@ -28,7 +28,7 @@ func TestGetSetValidatorSigningInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSetValidatorSigningBitArray(t *testing.T) {
|
func TestGetSetValidatorSigningBitArray(t *testing.T) {
|
||||||
ctx, _, _, keeper := createTestInput(t)
|
ctx, _, _, _, keeper := createTestInput(t)
|
||||||
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0)
|
signed := keeper.getValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0)
|
||||||
require.False(t, signed) // treat empty key as unsigned
|
require.False(t, signed) // treat empty key as unsigned
|
||||||
keeper.setValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0, true)
|
keeper.setValidatorSigningBitArray(ctx, sdk.ValAddress(addrs[0]), 0, true)
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/params"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -46,24 +47,29 @@ func createTestCodec() *wire.Codec {
|
||||||
return cdc
|
return cdc
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keeper) {
|
func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, params.Setter, Keeper) {
|
||||||
keyAcc := sdk.NewKVStoreKey("acc")
|
keyAcc := sdk.NewKVStoreKey("acc")
|
||||||
keyStake := sdk.NewKVStoreKey("stake")
|
keyStake := sdk.NewKVStoreKey("stake")
|
||||||
keySlashing := sdk.NewKVStoreKey("slashing")
|
keySlashing := sdk.NewKVStoreKey("slashing")
|
||||||
|
keyParams := sdk.NewKVStoreKey("params")
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
ms := store.NewCommitMultiStore(db)
|
ms := store.NewCommitMultiStore(db)
|
||||||
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
|
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
|
||||||
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
|
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
|
||||||
ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db)
|
ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db)
|
||||||
|
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
|
||||||
err := ms.LoadLatestVersion()
|
err := ms.LoadLatestVersion()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout))
|
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout))
|
||||||
cdc := createTestCodec()
|
cdc := createTestCodec()
|
||||||
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
|
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
|
||||||
ck := bank.NewKeeper(accountMapper)
|
ck := bank.NewKeeper(accountMapper)
|
||||||
|
params := params.NewKeeper(cdc, keyParams)
|
||||||
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
|
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
|
||||||
genesis := stake.DefaultGenesisState()
|
genesis := stake.DefaultGenesisState()
|
||||||
genesis.Pool.LooseTokens = initCoins.MulRaw(int64(len(addrs))).Int64()
|
|
||||||
|
genesis.Pool.LooseTokens = sdk.NewRat(initCoins.MulRaw(int64(len(addrs))).Int64())
|
||||||
|
|
||||||
err = stake.InitGenesis(ctx, sk, genesis)
|
err = stake.InitGenesis(ctx, sk, genesis)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
@ -73,8 +79,8 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, Keep
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
keeper := NewKeeper(cdc, keySlashing, sk, DefaultCodespace)
|
keeper := NewKeeper(cdc, keySlashing, sk, params.Getter(), DefaultCodespace)
|
||||||
return ctx, ck, sk, keeper
|
return ctx, ck, sk, params.Setter(), keeper
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPubKey(pk string) (res crypto.PubKey) {
|
func newPubKey(pk string) (res crypto.PubKey) {
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBeginBlocker(t *testing.T) {
|
func TestBeginBlocker(t *testing.T) {
|
||||||
ctx, ck, sk, keeper := createTestInput(t)
|
ctx, ck, sk, _, keeper := createTestInput(t)
|
||||||
addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100)
|
addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100)
|
||||||
|
|
||||||
// bond the validator
|
// bond the validator
|
||||||
|
@ -47,7 +47,7 @@ func TestBeginBlocker(t *testing.T) {
|
||||||
height := int64(0)
|
height := int64(0)
|
||||||
|
|
||||||
// for 1000 blocks, mark the validator as having signed
|
// for 1000 blocks, mark the validator as having signed
|
||||||
for ; height < SignedBlocksWindow; height++ {
|
for ; height < keeper.SignedBlocksWindow(ctx); height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
req = abci.RequestBeginBlock{
|
req = abci.RequestBeginBlock{
|
||||||
Validators: []abci.SigningValidator{{
|
Validators: []abci.SigningValidator{{
|
||||||
|
@ -59,7 +59,7 @@ func TestBeginBlocker(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// for 500 blocks, mark the validator as having not signed
|
// for 500 blocks, mark the validator as having not signed
|
||||||
for ; height < ((SignedBlocksWindow * 2) - MinSignedPerWindow + 1); height++ {
|
for ; height < ((keeper.SignedBlocksWindow(ctx) * 2) - keeper.MinSignedPerWindow(ctx) + 1); height++ {
|
||||||
ctx = ctx.WithBlockHeight(height)
|
ctx = ctx.WithBlockHeight(height)
|
||||||
req = abci.RequestBeginBlock{
|
req = abci.RequestBeginBlock{
|
||||||
Validators: []abci.SigningValidator{{
|
Validators: []abci.SigningValidator{{
|
||||||
|
|
|
@ -63,7 +63,7 @@ func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer {
|
||||||
mapp.InitChainer(ctx, req)
|
mapp.InitChainer(ctx, req)
|
||||||
|
|
||||||
stakeGenesis := DefaultGenesisState()
|
stakeGenesis := DefaultGenesisState()
|
||||||
stakeGenesis.Pool.LooseTokens = 100000
|
stakeGenesis.Pool.LooseTokens = sdk.NewRat(100000)
|
||||||
|
|
||||||
err := InitGenesis(ctx, keeper, stakeGenesis)
|
err := InitGenesis(ctx, keeper, stakeGenesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -135,8 +135,8 @@ func TestStakeMsgs(t *testing.T) {
|
||||||
|
|
||||||
validator := checkValidator(t, mApp, keeper, addr1, true)
|
validator := checkValidator(t, mApp, keeper, addr1, true)
|
||||||
require.Equal(t, addr1, validator.Owner)
|
require.Equal(t, addr1, validator.Owner)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens()))
|
||||||
|
|
||||||
// addr1 create validator on behalf of addr2
|
// addr1 create validator on behalf of addr2
|
||||||
createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(addr1, addr2, priv2.PubKey(), bondCoin, description)
|
createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf(addr1, addr2, priv2.PubKey(), bondCoin, description)
|
||||||
|
@ -147,8 +147,8 @@ func TestStakeMsgs(t *testing.T) {
|
||||||
|
|
||||||
validator = checkValidator(t, mApp, keeper, addr2, true)
|
validator = checkValidator(t, mApp, keeper, addr2, true)
|
||||||
require.Equal(t, addr2, validator.Owner)
|
require.Equal(t, addr2, validator.Owner)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
require.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
|
|
||||||
// check the bond that should have been created as well
|
// check the bond that should have been created as well
|
||||||
checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10))
|
checkDelegation(t, mApp, keeper, addr1, addr1, true, sdk.NewRat(10))
|
||||||
|
|
|
@ -215,57 +215,6 @@ func redHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move exist next to validator struct for maintainability
|
|
||||||
type StakeValidatorOutput struct {
|
|
||||||
Owner sdk.AccAddress `json:"owner"` // in bech32
|
|
||||||
PubKey string `json:"pub_key"` // in bech32
|
|
||||||
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
|
||||||
|
|
||||||
PoolShares stake.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 stake.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
|
|
||||||
}
|
|
||||||
|
|
||||||
func bech32StakeValidatorOutput(validator stake.Validator) (StakeValidatorOutput, error) {
|
|
||||||
bechValPubkey, err := sdk.Bech32ifyValPub(validator.PubKey)
|
|
||||||
if err != nil {
|
|
||||||
return StakeValidatorOutput{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return StakeValidatorOutput{
|
|
||||||
Owner: validator.Owner,
|
|
||||||
PubKey: bechValPubkey,
|
|
||||||
Revoked: validator.Revoked,
|
|
||||||
|
|
||||||
PoolShares: validator.PoolShares,
|
|
||||||
DelegatorShares: validator.DelegatorShares,
|
|
||||||
|
|
||||||
Description: validator.Description,
|
|
||||||
BondHeight: validator.BondHeight,
|
|
||||||
BondIntraTxCounter: validator.BondIntraTxCounter,
|
|
||||||
ProposerRewardPool: validator.ProposerRewardPool,
|
|
||||||
|
|
||||||
Commission: validator.Commission,
|
|
||||||
CommissionMax: validator.CommissionMax,
|
|
||||||
CommissionChangeRate: validator.CommissionChangeRate,
|
|
||||||
CommissionChangeToday: validator.CommissionChangeToday,
|
|
||||||
|
|
||||||
PrevBondedShares: validator.PrevBondedShares,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO bech32
|
// TODO bech32
|
||||||
// http request handler to query list of validators
|
// http request handler to query list of validators
|
||||||
func validatorsHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
func validatorsHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerFunc {
|
||||||
|
@ -284,7 +233,7 @@ func validatorsHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerF
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse out the validators
|
// parse out the validators
|
||||||
validators := make([]StakeValidatorOutput, len(kvs))
|
validators := make([]types.BechValidator, len(kvs))
|
||||||
for i, kv := range kvs {
|
for i, kv := range kvs {
|
||||||
|
|
||||||
addr := kv.Key[1:]
|
addr := kv.Key[1:]
|
||||||
|
@ -295,7 +244,7 @@ func validatorsHandlerFn(ctx context.CoreContext, cdc *wire.Codec) http.HandlerF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bech32Validator, err := bech32StakeValidatorOutput(validator)
|
bech32Validator, err := validator.Bech32Validator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(http.StatusBadRequest)
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
|
|
|
@ -20,7 +20,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
|
||||||
for _, validator := range data.Validators {
|
for _, validator := range data.Validators {
|
||||||
keeper.SetValidator(ctx, validator)
|
keeper.SetValidator(ctx, validator)
|
||||||
|
|
||||||
if validator.PoolShares.Amount.IsZero() {
|
if validator.Tokens.IsZero() {
|
||||||
return errors.Errorf("genesis validator cannot have zero pool shares, validator: %v", validator)
|
return errors.Errorf("genesis validator cannot have zero pool shares, validator: %v", validator)
|
||||||
}
|
}
|
||||||
if validator.DelegatorShares.IsZero() {
|
if validator.DelegatorShares.IsZero() {
|
||||||
|
@ -31,7 +31,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) error
|
||||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||||
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool)
|
||||||
|
|
||||||
if validator.Status() == sdk.Bonded {
|
if validator.Status == sdk.Bonded {
|
||||||
keeper.SetValidatorBondedIndex(ctx, validator)
|
keeper.SetValidatorBondedIndex(ctx, validator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,7 @@ func TestInitGenesis(t *testing.T) {
|
||||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||||
|
|
||||||
pool := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
pool.UnbondedTokens = 1
|
pool.LooseTokens = sdk.OneRat()
|
||||||
pool.UnbondedShares = sdk.OneRat()
|
|
||||||
|
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
var delegations []Delegation
|
var delegations []Delegation
|
||||||
|
@ -28,7 +27,7 @@ func TestInitGenesis(t *testing.T) {
|
||||||
err := InitGenesis(ctx, keeper, genesisState)
|
err := InitGenesis(ctx, keeper, genesisState)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
validators[0].PoolShares.Amount = sdk.OneRat()
|
validators[0].Tokens = sdk.OneRat()
|
||||||
validators[0].DelegatorShares = sdk.OneRat()
|
validators[0].DelegatorShares = sdk.OneRat()
|
||||||
|
|
||||||
genesisState = types.NewGenesisState(pool, params, validators, delegations)
|
genesisState = types.NewGenesisState(pool, params, validators, delegations)
|
||||||
|
|
|
@ -35,12 +35,13 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||||
// Called every block, process inflation, update validator set
|
// Called every block, process inflation, update validator set
|
||||||
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
|
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Validator) {
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
|
params := k.GetParams(ctx)
|
||||||
|
|
||||||
// Process types.Validator Provisions
|
// Process types.Validator Provisions
|
||||||
blockTime := ctx.BlockHeader().Time
|
blockTime := ctx.BlockHeader().Time
|
||||||
if pool.InflationLastTime+blockTime >= 3600 {
|
if pool.InflationLastTime+blockTime >= 3600 {
|
||||||
pool.InflationLastTime = blockTime
|
pool.InflationLastTime = blockTime
|
||||||
pool = k.ProcessProvisions(ctx)
|
pool = pool.ProcessProvisions(params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the params
|
// save the params
|
||||||
|
|
|
@ -84,8 +84,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||||
keeper.Revoke(ctx, keep.PKs[0])
|
keeper.Revoke(ctx, keep.PKs[0])
|
||||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded
|
require.Equal(t, sdk.Unbonded, validator.Status) // ensure is unbonded
|
||||||
require.Equal(t, int64(500000), validator.PoolShares.Amount.RoundInt64()) // ensure is unbonded
|
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure is unbonded
|
||||||
|
|
||||||
// the old power record should have been deleted as the power changed
|
// the old power record should have been deleted as the power changed
|
||||||
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))
|
||||||
|
@ -98,8 +98,9 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||||
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
|
require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power2))
|
||||||
|
|
||||||
// inflate a bunch
|
// inflate a bunch
|
||||||
for i := 0; i < 20000; i++ {
|
params := keeper.GetParams(ctx)
|
||||||
pool = keeper.ProcessProvisions(ctx)
|
for i := 0; i < 200; i++ {
|
||||||
|
pool = pool.ProcessProvisions(params)
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +134,10 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||||
validator, found := keeper.GetValidator(ctx, addr1)
|
validator, found := keeper.GetValidator(ctx, addr1)
|
||||||
|
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.Equal(t, sdk.Bonded, validator.Status())
|
assert.Equal(t, sdk.Bonded, validator.Status)
|
||||||
assert.Equal(t, addr1, validator.Owner)
|
assert.Equal(t, addr1, validator.Owner)
|
||||||
assert.Equal(t, pk1, validator.PubKey)
|
assert.Equal(t, pk1, validator.PubKey)
|
||||||
assert.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded())
|
assert.Equal(t, sdk.NewRat(10), validator.BondedTokens())
|
||||||
assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares)
|
assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares)
|
||||||
assert.Equal(t, Description{}, validator.Description)
|
assert.Equal(t, Description{}, validator.Description)
|
||||||
|
|
||||||
|
@ -157,11 +158,11 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) {
|
||||||
validator, found = keeper.GetValidator(ctx, addr2)
|
validator, found = keeper.GetValidator(ctx, addr2)
|
||||||
|
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
assert.Equal(t, sdk.Bonded, validator.Status())
|
assert.Equal(t, sdk.Bonded, validator.Status)
|
||||||
assert.Equal(t, addr2, validator.Owner)
|
assert.Equal(t, addr2, validator.Owner)
|
||||||
assert.Equal(t, pk2, validator.PubKey)
|
assert.Equal(t, pk2, validator.PubKey)
|
||||||
assert.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded())
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
assert.Equal(t, sdk.NewRat(10), validator.DelegatorShares)
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||||
assert.Equal(t, Description{}, validator.Description)
|
assert.Equal(t, Description{}, validator.Description)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,12 +178,12 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) {
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
|
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
assert.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.Equal(t, validatorAddr, validator.Owner)
|
assert.Equal(t, validatorAddr, validator.Owner)
|
||||||
require.Equal(t, pk, validator.PubKey)
|
assert.Equal(t, pk, validator.PubKey)
|
||||||
require.Equal(t, sdk.NewRat(10), validator.PoolShares.Bonded())
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
require.Equal(t, sdk.NewRat(10), validator.DelegatorShares)
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||||
require.Equal(t, Description{}, validator.Description)
|
assert.Equal(t, Description{}, validator.Description)
|
||||||
|
|
||||||
// one validator cannot be created twice even from different delegator
|
// one validator cannot be created twice even from different delegator
|
||||||
msgCreateValidatorOnBehalfOf.DelegatorAddr = keep.Addrs[2]
|
msgCreateValidatorOnBehalfOf.DelegatorAddr = keep.Addrs[2]
|
||||||
|
@ -206,9 +207,9 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
||||||
|
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64())
|
require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64())
|
||||||
require.Equal(t, bondAmount, validator.PoolShares.Bonded().RoundInt64(), "validator: %v", validator)
|
require.Equal(t, bondAmount, validator.BondedTokens().RoundInt64(), "validator: %v", validator)
|
||||||
|
|
||||||
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
_, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||||
require.False(t, found)
|
require.False(t, found)
|
||||||
|
@ -218,10 +219,9 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
||||||
require.Equal(t, bondAmount, bond.Shares.RoundInt64())
|
require.Equal(t, bondAmount, bond.Shares.RoundInt64())
|
||||||
|
|
||||||
pool := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
exRate := validator.DelegatorShareExRate(pool)
|
exRate := validator.DelegatorShareExRate()
|
||||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
|
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v", exRate)
|
||||||
require.Equal(t, bondAmount, pool.BondedShares.RoundInt64())
|
require.Equal(t, bondAmount, pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, bondAmount, pool.BondedTokens)
|
|
||||||
|
|
||||||
// just send the same msgbond multiple times
|
// just send the same msgbond multiple times
|
||||||
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
|
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount)
|
||||||
|
@ -238,8 +238,7 @@ func TestIncrementsMsgDelegate(t *testing.T) {
|
||||||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
||||||
pool := keeper.GetPool(ctx)
|
exRate := validator.DelegatorShareExRate()
|
||||||
exRate := validator.DelegatorShareExRate(pool)
|
|
||||||
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
|
require.True(t, exRate.Equal(sdk.OneRat()), "expected exRate 1 got %v, i = %v", exRate, i)
|
||||||
|
|
||||||
expBond := int64(i+1) * bondAmount
|
expBond := int64(i+1) * bondAmount
|
||||||
|
@ -291,7 +290,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
||||||
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
validator, found := keeper.GetValidator(ctx, validatorAddr)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
|
require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64())
|
||||||
require.Equal(t, initBond*2, validator.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, initBond*2, validator.BondedTokens().RoundInt64())
|
||||||
|
|
||||||
// just send the same msgUnbond multiple times
|
// just send the same msgUnbond multiple times
|
||||||
// TODO use decimals here
|
// TODO use decimals here
|
||||||
|
@ -674,7 +673,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) {
|
||||||
require.Equal(t, 2, len(vals), "vals %v", vals)
|
require.Equal(t, 2, len(vals), "vals %v", vals)
|
||||||
val1, found := keeper.GetValidator(ctx, validatorAddr1)
|
val1, found := keeper.GetValidator(ctx, validatorAddr1)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Bonded, val1.Status(), "%v", val1)
|
require.Equal(t, sdk.Bonded, val1.Status, "%v", val1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJoiningAsCliffValidator(t *testing.T) {
|
func TestJoiningAsCliffValidator(t *testing.T) {
|
||||||
|
|
|
@ -238,7 +238,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delegatorAddr sdk.AccAddress, bondAmt
|
||||||
|
|
||||||
// unbond the the delegation return
|
// unbond the the delegation return
|
||||||
func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress,
|
func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddress,
|
||||||
shares sdk.Rat) (amount int64, err sdk.Error) {
|
shares sdk.Rat) (amount sdk.Rat, err sdk.Error) {
|
||||||
|
|
||||||
// check if delegation has any shares in it unbond
|
// check if delegation has any shares in it unbond
|
||||||
delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
delegation, found := k.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||||
|
@ -306,7 +306,7 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context, delegatorAddr, validatorAddr sdk
|
||||||
// create the unbonding delegation
|
// create the unbonding delegation
|
||||||
params := k.GetParams(ctx)
|
params := k.GetParams(ctx)
|
||||||
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
minTime := ctx.BlockHeader().Time + params.UnbondingTime
|
||||||
balance := sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)}
|
balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
|
||||||
|
|
||||||
ubd := types.UnbondingDelegation{
|
ubd := types.UnbondingDelegation{
|
||||||
DelegatorAddr: delegatorAddr,
|
DelegatorAddr: delegatorAddr,
|
||||||
|
@ -356,7 +356,7 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delegatorAddr, validatorSrcAd
|
||||||
}
|
}
|
||||||
|
|
||||||
params := k.GetParams(ctx)
|
params := k.GetParams(ctx)
|
||||||
returnCoin := sdk.Coin{params.BondDenom, sdk.NewInt(returnAmount)}
|
returnCoin := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
|
||||||
dstValidator, found := k.GetValidator(ctx, validatorDstAddr)
|
dstValidator, found := k.GetValidator(ctx, validatorDstAddr)
|
||||||
if !found {
|
if !found {
|
||||||
return types.ErrBadRedelegationDst(k.Codespace())
|
return types.ErrBadRedelegationDst(k.Codespace())
|
||||||
|
|
|
@ -141,7 +141,7 @@ func TestUnbondingDelegation(t *testing.T) {
|
||||||
func TestUnbondDelegation(t *testing.T) {
|
func TestUnbondDelegation(t *testing.T) {
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||||
pool := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
pool.LooseTokens = 10
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
|
|
||||||
//create a validator and a delegator to that validator
|
//create a validator and a delegator to that validator
|
||||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||||
|
@ -151,8 +151,8 @@ func TestUnbondDelegation(t *testing.T) {
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = keeper.UpdateValidator(ctx, validator)
|
||||||
|
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
require.Equal(t, int64(10), pool.BondedTokens)
|
require.Equal(t, int64(10), pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, int64(10), validator.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, int64(10), validator.BondedTokens().RoundInt64())
|
||||||
|
|
||||||
delegation := types.Delegation{
|
delegation := types.Delegation{
|
||||||
DelegatorAddr: addrDels[0],
|
DelegatorAddr: addrDels[0],
|
||||||
|
@ -162,10 +162,10 @@ func TestUnbondDelegation(t *testing.T) {
|
||||||
keeper.SetDelegation(ctx, delegation)
|
keeper.SetDelegation(ctx, delegation)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var amount int64
|
var amount sdk.Rat
|
||||||
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6))
|
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewRat(6))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, int64(6), amount) // shares to be added to an unbonding delegation / redelegation
|
require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation
|
||||||
|
|
||||||
delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -174,9 +174,9 @@ func TestUnbondDelegation(t *testing.T) {
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
|
|
||||||
require.Equal(t, int64(4), delegation.Shares.RoundInt64())
|
require.Equal(t, int64(4), delegation.Shares.RoundInt64())
|
||||||
require.Equal(t, int64(4), validator.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, int64(4), validator.BondedTokens().RoundInt64())
|
||||||
require.Equal(t, int64(6), pool.LooseTokens, "%v", pool)
|
require.Equal(t, int64(6), pool.LooseTokens.RoundInt64(), "%v", pool)
|
||||||
require.Equal(t, int64(4), pool.BondedTokens)
|
require.Equal(t, int64(4), pool.BondedTokens.RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that that the retrieving the delegations doesn't affect the state
|
// Make sure that that the retrieving the delegations doesn't affect the state
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
package keeper
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
hrsPerYr = 8766 // as defined by a julian year of 365.25 days
|
|
||||||
precision = 100000000000 // increased to this precision for accuracy
|
|
||||||
)
|
|
||||||
|
|
||||||
var hrsPerYrRat = sdk.NewRat(hrsPerYr)
|
|
||||||
|
|
||||||
// process provisions for an hour period
|
|
||||||
func (k Keeper) ProcessProvisions(ctx sdk.Context) types.Pool {
|
|
||||||
|
|
||||||
pool := k.GetPool(ctx)
|
|
||||||
pool.Inflation = k.NextInflation(ctx)
|
|
||||||
|
|
||||||
provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).RoundInt64()
|
|
||||||
|
|
||||||
// TODO add to the fees provisions
|
|
||||||
pool.LooseTokens += provisions
|
|
||||||
return pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the next inflation rate for the hour
|
|
||||||
func (k Keeper) NextInflation(ctx sdk.Context) (inflation sdk.Rat) {
|
|
||||||
|
|
||||||
params := k.GetParams(ctx)
|
|
||||||
pool := k.GetPool(ctx)
|
|
||||||
// The target annual inflation rate is recalculated for each previsions cycle. The
|
|
||||||
// inflation is also subject to a rate change (positive or negative) depending on
|
|
||||||
// the distance from the desired ratio (67%). The maximum rate change possible is
|
|
||||||
// defined to be 13% per year, however the annual inflation is capped as between
|
|
||||||
// 7% and 20%.
|
|
||||||
|
|
||||||
// (1 - bondedRatio/GoalBonded) * InflationRateChange
|
|
||||||
inflationRateChangePerYear := sdk.OneRat().Sub(pool.BondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
|
||||||
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat)
|
|
||||||
|
|
||||||
// increase the new annual inflation for this next cycle
|
|
||||||
inflation = pool.Inflation.Add(inflationRateChange)
|
|
||||||
if inflation.GT(params.InflationMax) {
|
|
||||||
inflation = params.InflationMax
|
|
||||||
}
|
|
||||||
if inflation.LT(params.InflationMin) {
|
|
||||||
inflation = params.InflationMin
|
|
||||||
}
|
|
||||||
|
|
||||||
return inflation.Round(precision)
|
|
||||||
}
|
|
|
@ -1,378 +0,0 @@
|
||||||
package keeper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
|
|
||||||
var r = rand.New(rand.NewSource(6595))
|
|
||||||
|
|
||||||
func TestGetInflation(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
params := keeper.GetParams(ctx)
|
|
||||||
hrsPerYrRat := sdk.NewRat(hrsPerYr)
|
|
||||||
|
|
||||||
// Governing Mechanism:
|
|
||||||
// BondedRatio = BondedTokens / TotalSupply
|
|
||||||
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
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, 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, 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, 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, 33, sdk.NewRat(15, 100), sdk.ZeroRat()},
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
|
|
||||||
pool.Inflation = tc.setInflation
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
inflation := keeper.NextInflation(ctx)
|
|
||||||
diffInflation := inflation.Sub(tc.setInflation)
|
|
||||||
|
|
||||||
require.True(t, diffInflation.Equal(tc.expectedChange),
|
|
||||||
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that provisions are correctly added to the pool and validators each hour for 1 year
|
|
||||||
func TestProcessProvisions(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
var (
|
|
||||||
initialTotalTokens int64 = 550000000
|
|
||||||
initialBondedTokens int64 = 250000000
|
|
||||||
initialUnbondedTokens int64 = 300000000
|
|
||||||
cumulativeExpProvs int64
|
|
||||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
|
||||||
bondedValidators uint16 = 2
|
|
||||||
)
|
|
||||||
pool.LooseTokens = initialTotalTokens
|
|
||||||
|
|
||||||
// create some validators some bonded, some unbonded
|
|
||||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
|
||||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
|
||||||
|
|
||||||
// process the provisions for a year
|
|
||||||
for hr := 0; hr < 8766; hr++ {
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
_, expProvisions, _ := updateProvisions(t, keeper, pool, ctx, hr)
|
|
||||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the pool and do the final value checks from checkFinalPoolValues
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate
|
|
||||||
// Cycles through the whole gambit of inflation possibilities, starting at 7% inflation, up to 20%, back down to 7% (it takes ~11.4 years)
|
|
||||||
func TestHourlyInflationRateOfChange(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
var (
|
|
||||||
initialTotalTokens int64 = 550000000
|
|
||||||
initialBondedTokens int64 = 150000000
|
|
||||||
initialUnbondedTokens int64 = 400000000
|
|
||||||
cumulativeExpProvs int64
|
|
||||||
validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000}
|
|
||||||
bondedValidators uint16 = 1
|
|
||||||
)
|
|
||||||
pool.LooseTokens = initialTotalTokens
|
|
||||||
|
|
||||||
// create some validators some bonded, some unbonded
|
|
||||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
|
||||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
|
||||||
|
|
||||||
// ~11.4 years to go from 7%, up to 20%, back down to 7%
|
|
||||||
for hr := 0; hr < 100000; hr++ {
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
previousInflation := pool.Inflation
|
|
||||||
updatedInflation, expProvisions, pool := updateProvisions(t, keeper, pool, ctx, hr)
|
|
||||||
cumulativeExpProvs = cumulativeExpProvs + expProvisions
|
|
||||||
msg := strconv.Itoa(hr)
|
|
||||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final check that the pool equals initial values + cumulative provisions and adjustments we recorded
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test that a large unbonding will significantly lower the bonded ratio
|
|
||||||
func TestLargeUnbond(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
var (
|
|
||||||
initialTotalTokens int64 = 1200000000
|
|
||||||
initialBondedTokens int64 = 900000000
|
|
||||||
initialUnbondedTokens int64 = 300000000
|
|
||||||
val0UnbondedTokens int64
|
|
||||||
bondedShares = sdk.NewRat(900000000, 1)
|
|
||||||
unbondedShares = sdk.NewRat(300000000, 1)
|
|
||||||
bondSharesVal0 = sdk.NewRat(300000000, 1)
|
|
||||||
validatorTokens = []int64{300000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000}
|
|
||||||
bondedValidators uint16 = 7
|
|
||||||
)
|
|
||||||
pool.LooseTokens = initialTotalTokens
|
|
||||||
|
|
||||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
|
||||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
|
||||||
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
validator, found := keeper.GetValidator(ctx, Addrs[0])
|
|
||||||
require.True(t, found)
|
|
||||||
|
|
||||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
|
||||||
initialBondedRatio := pool.BondedRatio()
|
|
||||||
|
|
||||||
// validator[0] will be unbonded, bringing us from 75% bonded ratio to ~50% (unbonding 300,000,000)
|
|
||||||
pool, validator, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
|
||||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
|
||||||
|
|
||||||
bondedShares = bondedShares.Sub(bondSharesVal0)
|
|
||||||
val0UnbondedTokens = pool.UnbondedShareExRate().Mul(validator.PoolShares.Unbonded()).RoundInt64()
|
|
||||||
unbondedShares = unbondedShares.Add(sdk.NewRat(val0UnbondedTokens, 1).Mul(pool.UnbondedShareExRate()))
|
|
||||||
|
|
||||||
// unbonded shares should increase
|
|
||||||
require.True(t, unbondedShares.GT(sdk.NewRat(300000000, 1)))
|
|
||||||
// Ensure that new bonded ratio is less than old bonded ratio , because before they were increasing (i.e. 50% < 75)
|
|
||||||
require.True(t, (pool.BondedRatio().LT(initialBondedRatio)))
|
|
||||||
|
|
||||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test that a large bonding will significantly increase the bonded ratio
|
|
||||||
func TestLargeBond(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
var (
|
|
||||||
initialTotalTokens int64 = 1600000000
|
|
||||||
initialBondedTokens int64 = 400000000
|
|
||||||
initialUnbondedTokens int64 = 1200000000
|
|
||||||
unbondedShares = sdk.NewRat(1200000000, 1)
|
|
||||||
unbondedSharesVal9 = sdk.NewRat(400000000, 1)
|
|
||||||
validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000}
|
|
||||||
bondedValidators uint16 = 1
|
|
||||||
)
|
|
||||||
pool.LooseTokens = initialTotalTokens
|
|
||||||
|
|
||||||
_, keeper, pool = setupTestValidators(pool, keeper, ctx, validatorTokens, bondedValidators)
|
|
||||||
checkValidatorSetup(t, pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens)
|
|
||||||
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
validator, found := keeper.GetValidator(ctx, Addrs[9])
|
|
||||||
require.True(t, found)
|
|
||||||
|
|
||||||
// initialBondedRatio that we can use to compare to the new values after the unbond
|
|
||||||
initialBondedRatio := pool.BondedRatio()
|
|
||||||
|
|
||||||
params := types.DefaultParams()
|
|
||||||
params.MaxValidators = bondedValidators + 1 //must do this to allow for an extra validator to bond
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
|
|
||||||
// validator[9] will be bonded, bringing us from 25% to ~50% (bonding 400,000,000 tokens)
|
|
||||||
pool, _, _, _ = types.OpBondOrUnbond(r, pool, validator)
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
// process provisions after the bonding, to compare the difference in expProvisions and expInflation
|
|
||||||
_, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0)
|
|
||||||
unbondedShares = unbondedShares.Sub(unbondedSharesVal9)
|
|
||||||
|
|
||||||
// unbonded shares should decrease
|
|
||||||
require.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1)))
|
|
||||||
// Ensure that new bonded ratio is greater than old bonded ratio (i.e. 50% > 25%)
|
|
||||||
require.True(t, (pool.BondedRatio().GT(initialBondedRatio)))
|
|
||||||
// Final check that the pool equals initial values + provisions and adjustments we recorded
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators
|
|
||||||
func TestInflationWithRandomOperations(t *testing.T) {
|
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
|
||||||
params := types.DefaultParams()
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
numValidators := 20
|
|
||||||
|
|
||||||
// start off by randomly setting up 20 validators
|
|
||||||
pool, validators := types.RandomSetup(r, numValidators)
|
|
||||||
require.Equal(t, numValidators, len(validators))
|
|
||||||
|
|
||||||
for i := 0; i < len(validators); i++ {
|
|
||||||
keeper.SetValidator(ctx, validators[i])
|
|
||||||
}
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
// Used to rotate validators so each random operation is applied to a different validator
|
|
||||||
validatorCounter := 0
|
|
||||||
|
|
||||||
// Loop through 20 random operations, and check the inflation after each operation
|
|
||||||
for i := 0; i < numValidators; i++ {
|
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
|
|
||||||
// Get inflation before RandomOperation, for comparison later
|
|
||||||
previousInflation := pool.Inflation
|
|
||||||
|
|
||||||
// Perform the random operation, and record how validators are modified
|
|
||||||
poolMod, validatorMod, tokens, msg := types.RandomOperation(r)(r, pool, validators[validatorCounter])
|
|
||||||
validatorsMod := make([]types.Validator, len(validators))
|
|
||||||
copy(validatorsMod[:], validators[:])
|
|
||||||
require.Equal(t, numValidators, len(validators), "i %v", validatorCounter)
|
|
||||||
require.Equal(t, numValidators, len(validatorsMod), "i %v", validatorCounter)
|
|
||||||
validatorsMod[validatorCounter] = validatorMod
|
|
||||||
|
|
||||||
types.AssertInvariants(t, msg,
|
|
||||||
pool, validators,
|
|
||||||
poolMod, validatorsMod, tokens)
|
|
||||||
|
|
||||||
// set pool and validators after the random operation
|
|
||||||
pool = poolMod
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
validators = validatorsMod
|
|
||||||
|
|
||||||
// Must set inflation here manually, as opposed to most other tests in this suite, where we call keeper.processProvisions(), which updates pool.Inflation
|
|
||||||
updatedInflation := keeper.NextInflation(ctx)
|
|
||||||
pool.Inflation = updatedInflation
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
// Ensure inflation changes as expected when random operations are applied.
|
|
||||||
checkInflation(t, pool, previousInflation, updatedInflation, msg)
|
|
||||||
validatorCounter++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//_________________________________________________________________________________________
|
|
||||||
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
|
|
||||||
|
|
||||||
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
|
|
||||||
func checkFinalPoolValues(t *testing.T, pool types.Pool, initialTotalTokens, cumulativeExpProvs int64) {
|
|
||||||
calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs
|
|
||||||
require.Equal(t, calculatedTotalTokens, pool.TokenSupply())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processes provisions are added to the pool correctly every hour
|
|
||||||
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
|
|
||||||
func updateProvisions(t *testing.T, keeper Keeper, pool types.Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, types.Pool) {
|
|
||||||
expInflation := keeper.NextInflation(ctx)
|
|
||||||
expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).RoundInt64()
|
|
||||||
startTotalSupply := pool.TokenSupply()
|
|
||||||
pool = keeper.ProcessProvisions(ctx)
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
|
|
||||||
//check provisions were added to pool
|
|
||||||
require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply())
|
|
||||||
|
|
||||||
return expInflation, expProvisions, pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deterministic setup of validators and pool
|
|
||||||
// Allows you to decide how many validators to setup
|
|
||||||
// Allows you to pick which validators are bonded by adjusting the MaxValidators of params
|
|
||||||
func setupTestValidators(pool types.Pool, keeper Keeper, ctx sdk.Context, validatorTokens []int64,
|
|
||||||
maxValidators uint16) ([]types.Validator, Keeper, types.Pool) {
|
|
||||||
|
|
||||||
params := types.DefaultParams()
|
|
||||||
params.MaxValidators = maxValidators
|
|
||||||
keeper.SetParams(ctx, params)
|
|
||||||
numValidators := len(validatorTokens)
|
|
||||||
validators := make([]types.Validator, numValidators)
|
|
||||||
|
|
||||||
for i := 0; i < numValidators; i++ {
|
|
||||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
|
||||||
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, validatorTokens[i])
|
|
||||||
keeper.SetPool(ctx, pool)
|
|
||||||
validators[i] = keeper.UpdateValidator(ctx, validators[i]) //will kick out lower power validators. Keep this in mind when setting up the test validators order
|
|
||||||
pool = keeper.GetPool(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return validators, keeper, pool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that the deterministic validator setup you wanted matches the values in the pool
|
|
||||||
func checkValidatorSetup(t *testing.T, pool types.Pool, initialTotalTokens, initialBondedTokens, initialUnbondedTokens int64) {
|
|
||||||
require.Equal(t, initialTotalTokens, pool.TokenSupply(), "%v", pool)
|
|
||||||
require.Equal(t, initialBondedTokens, pool.BondedTokens, "%v", pool)
|
|
||||||
require.Equal(t, initialUnbondedTokens, pool.UnbondedTokens, "%v", pool)
|
|
||||||
|
|
||||||
// test initial bonded ratio
|
|
||||||
require.True(t, pool.BondedRatio().Equal(sdk.NewRat(initialBondedTokens, initialTotalTokens)), "%v", pool.BondedRatio())
|
|
||||||
// test the value of validator shares
|
|
||||||
require.True(t, pool.BondedShareExRate().Equal(sdk.OneRat()), "%v", pool.BondedShareExRate())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that The inflation will correctly increase or decrease after an update to the pool
|
|
||||||
// nolint: gocyclo
|
|
||||||
func checkInflation(t *testing.T, pool types.Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
|
||||||
inflationChange := updatedInflation.Sub(previousInflation)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
|
|
||||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
|
||||||
require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
|
||||||
|
|
||||||
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
|
|
||||||
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
|
||||||
if previousInflation.Equal(sdk.NewRat(20, 100)) {
|
|
||||||
require.Equal(t, true, inflationChange.IsZero(), msg)
|
|
||||||
|
|
||||||
//This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%)
|
|
||||||
} else {
|
|
||||||
require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
|
|
||||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
|
||||||
require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
|
||||||
|
|
||||||
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
|
|
||||||
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
|
||||||
if previousInflation.Equal(sdk.NewRat(7, 100)) {
|
|
||||||
require.Equal(t, true, inflationChange.IsZero(), msg)
|
|
||||||
|
|
||||||
//This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%)
|
|
||||||
} else {
|
|
||||||
require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
"github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ func TestPool(t *testing.T) {
|
||||||
require.True(t, expPool.Equal(resPool))
|
require.True(t, expPool.Equal(resPool))
|
||||||
|
|
||||||
//modify a params, save, and retrieve
|
//modify a params, save, and retrieve
|
||||||
expPool.BondedTokens = 777
|
expPool.BondedTokens = sdk.NewRat(777)
|
||||||
keeper.SetPool(ctx, expPool)
|
keeper.SetPool(ctx, expPool)
|
||||||
resPool = keeper.GetPool(ctx)
|
resPool = keeper.GetPool(ctx)
|
||||||
require.True(t, expPool.Equal(resPool))
|
require.True(t, expPool.Equal(resPool))
|
||||||
|
|
|
@ -69,8 +69,8 @@ func GetValidatorsByPowerIndexKey(validator types.Validator, pool types.Pool) []
|
||||||
// NOTE the larger values are of higher value
|
// NOTE the larger values are of higher value
|
||||||
func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
|
func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
|
||||||
|
|
||||||
power := validator.EquivalentBondedShares(pool)
|
potentialPower := validator.Tokens
|
||||||
powerBytes := []byte(power.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first)
|
||||||
|
|
||||||
revokedBytes := make([]byte, 1)
|
revokedBytes := make([]byte, 1)
|
||||||
if validator.Revoked {
|
if validator.Revoked {
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (k Keeper) Validator(ctx sdk.Context, address sdk.AccAddress) sdk.Validator
|
||||||
// total power from the bond
|
// total power from the bond
|
||||||
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
func (k Keeper) TotalPower(ctx sdk.Context) sdk.Rat {
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
return pool.BondedShares
|
return pool.BondedTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
//__________________________________________________________________________
|
//__________________________________________________________________________
|
||||||
|
|
|
@ -28,7 +28,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
||||||
}
|
}
|
||||||
|
|
||||||
// Amount of slashing = slash slashFactor * power at time of infraction
|
// Amount of slashing = slash slashFactor * power at time of infraction
|
||||||
slashAmount := sdk.NewRat(power).Mul(slashFactor).RoundInt()
|
slashAmount := sdk.NewRat(power).Mul(slashFactor)
|
||||||
// ref https://github.com/cosmos/cosmos-sdk/issues/1348
|
// ref https://github.com/cosmos/cosmos-sdk/issues/1348
|
||||||
// ref https://github.com/cosmos/cosmos-sdk/issues/1471
|
// ref https://github.com/cosmos/cosmos-sdk/issues/1471
|
||||||
|
|
||||||
|
@ -38,7 +38,9 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
||||||
// NOTE: Correctness dependent on invariant that unbonding delegations / redelegations must also have been completely
|
// NOTE: Correctness dependent on invariant that unbonding delegations / redelegations must also have been completely
|
||||||
// slashed in this case - which we don't explicitly check, but should be true.
|
// slashed in this case - which we don't explicitly check, but should be true.
|
||||||
// Log the slash attempt for future reference (maybe we should tag it too)
|
// Log the slash attempt for future reference (maybe we should tag it too)
|
||||||
logger.Error(fmt.Sprintf("WARNING: Ignored attempt to slash a nonexistent validator with address %s, we recommend you investigate immediately", pubkey.Address()))
|
logger.Error(fmt.Sprintf(
|
||||||
|
"WARNING: Ignored attempt to slash a nonexistent validator with address %s, we recommend you investigate immediately",
|
||||||
|
pubkey.Address()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ownerAddress := validator.GetOwner()
|
ownerAddress := validator.GetOwner()
|
||||||
|
@ -50,14 +52,21 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case infractionHeight > ctx.BlockHeight():
|
case infractionHeight > ctx.BlockHeight():
|
||||||
|
|
||||||
// Can't slash infractions in the future
|
// Can't slash infractions in the future
|
||||||
panic(fmt.Sprintf("impossible attempt to slash future infraction at height %d but we are at height %d", infractionHeight, ctx.BlockHeight()))
|
panic(fmt.Sprintf(
|
||||||
|
"impossible attempt to slash future infraction at height %d but we are at height %d",
|
||||||
|
infractionHeight, ctx.BlockHeight()))
|
||||||
|
|
||||||
case infractionHeight == ctx.BlockHeight():
|
case infractionHeight == ctx.BlockHeight():
|
||||||
|
|
||||||
// Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations
|
// Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations
|
||||||
logger.Info(fmt.Sprintf("Slashing at current height %d, not scanning unbonding delegations & redelegations", infractionHeight))
|
logger.Info(fmt.Sprintf(
|
||||||
|
"Slashing at current height %d, not scanning unbonding delegations & redelegations",
|
||||||
|
infractionHeight))
|
||||||
|
|
||||||
case infractionHeight < ctx.BlockHeight():
|
case infractionHeight < ctx.BlockHeight():
|
||||||
|
|
||||||
// Iterate through unbonding delegations from slashed validator
|
// Iterate through unbonding delegations from slashed validator
|
||||||
unbondingDelegations := k.GetUnbondingDelegationsFromValidator(ctx, ownerAddress)
|
unbondingDelegations := k.GetUnbondingDelegationsFromValidator(ctx, ownerAddress)
|
||||||
for _, unbondingDelegation := range unbondingDelegations {
|
for _, unbondingDelegation := range unbondingDelegations {
|
||||||
|
@ -77,29 +86,30 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
|
||||||
}
|
}
|
||||||
remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed)
|
remainingSlashAmount = remainingSlashAmount.Sub(amountSlashed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cannot decrease balance below zero
|
// Cannot decrease balance below zero
|
||||||
sharesToRemove := sdk.MinInt(remainingSlashAmount, validator.PoolShares.Amount.RoundInt())
|
tokensToBurn := sdk.MinRat(remainingSlashAmount, validator.Tokens)
|
||||||
|
|
||||||
// Get the current pool
|
// Get the current pool
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
// remove shares from the validator
|
// remove tokens from the validator
|
||||||
validator, pool, burned := validator.RemovePoolShares(pool, sdk.NewRatFromInt(sharesToRemove))
|
validator, pool = validator.RemoveTokens(pool, tokensToBurn)
|
||||||
// burn tokens
|
// burn tokens
|
||||||
pool.LooseTokens -= burned
|
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||||
// update the pool
|
// update the pool
|
||||||
k.SetPool(ctx, pool)
|
k.SetPool(ctx, pool)
|
||||||
// update the validator, possibly kicking it out
|
// update the validator, possibly kicking it out
|
||||||
validator = k.UpdateValidator(ctx, validator)
|
validator = k.UpdateValidator(ctx, validator)
|
||||||
// remove validator if it has been reduced to zero shares
|
// remove validator if it has been reduced to zero shares
|
||||||
if validator.PoolShares.Amount.IsZero() {
|
if validator.Tokens.IsZero() {
|
||||||
k.RemoveValidator(ctx, validator.Owner)
|
k.RemoveValidator(ctx, validator.Owner)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log that a slash occurred!
|
// Log that a slash occurred!
|
||||||
logger.Info(fmt.Sprintf("Validator %s slashed by slashFactor %v, removed %v shares and burned %d tokens", pubkey.Address(), slashFactor, sharesToRemove, burned))
|
logger.Info(fmt.Sprintf(
|
||||||
|
"Validator %s slashed by slashFactor %v, burned %v tokens",
|
||||||
|
pubkey.Address(), slashFactor, tokensToBurn))
|
||||||
|
|
||||||
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
|
||||||
return
|
return
|
||||||
|
@ -139,28 +149,30 @@ func (k Keeper) setRevoked(ctx sdk.Context, pubkey crypto.PubKey, revoked bool)
|
||||||
// the unbonding delegation had enough stake to slash
|
// the unbonding delegation had enough stake to slash
|
||||||
// (the amount actually slashed may be less if there's
|
// (the amount actually slashed may be less if there's
|
||||||
// insufficient stake remaining)
|
// insufficient stake remaining)
|
||||||
func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation, infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Int) {
|
func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation,
|
||||||
|
infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) {
|
||||||
|
|
||||||
now := ctx.BlockHeader().Time
|
now := ctx.BlockHeader().Time
|
||||||
|
|
||||||
// If unbonding started before this height, stake didn't contribute to infraction
|
// If unbonding started before this height, stake didn't contribute to infraction
|
||||||
if unbondingDelegation.CreationHeight < infractionHeight {
|
if unbondingDelegation.CreationHeight < infractionHeight {
|
||||||
return sdk.ZeroInt()
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if unbondingDelegation.MinTime < now {
|
if unbondingDelegation.MinTime < now {
|
||||||
// Unbonding delegation no longer eligible for slashing, skip it
|
// Unbonding delegation no longer eligible for slashing, skip it
|
||||||
// TODO Settle and delete it automatically?
|
// TODO Settle and delete it automatically?
|
||||||
return sdk.ZeroInt()
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate slash amount proportional to stake contributing to infraction
|
// Calculate slash amount proportional to stake contributing to infraction
|
||||||
slashAmount = sdk.NewRatFromInt(unbondingDelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor).RoundInt()
|
slashAmount = sdk.NewRatFromInt(unbondingDelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor)
|
||||||
|
|
||||||
// Don't slash more tokens than held
|
// Don't slash more tokens than held
|
||||||
// Possible since the unbonding delegation may already
|
// Possible since the unbonding delegation may already
|
||||||
// have been slashed, and slash amounts are calculated
|
// have been slashed, and slash amounts are calculated
|
||||||
// according to stake held at time of infraction
|
// according to stake held at time of infraction
|
||||||
unbondingSlashAmount := sdk.MinInt(slashAmount, unbondingDelegation.Balance.Amount)
|
unbondingSlashAmount := sdk.MinInt(slashAmount.RoundInt(), unbondingDelegation.Balance.Amount)
|
||||||
|
|
||||||
// Update unbonding delegation if necessary
|
// Update unbonding delegation if necessary
|
||||||
if !unbondingSlashAmount.IsZero() {
|
if !unbondingSlashAmount.IsZero() {
|
||||||
|
@ -169,7 +181,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
// Burn loose tokens
|
// Burn loose tokens
|
||||||
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
|
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
|
||||||
pool.LooseTokens -= slashAmount.Int64()
|
pool.LooseTokens = pool.LooseTokens.Sub(slashAmount)
|
||||||
k.SetPool(ctx, pool)
|
k.SetPool(ctx, pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,28 +193,30 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
||||||
// the unbonding delegation had enough stake to slash
|
// the unbonding delegation had enough stake to slash
|
||||||
// (the amount actually slashed may be less if there's
|
// (the amount actually slashed may be less if there's
|
||||||
// insufficient stake remaining)
|
// insufficient stake remaining)
|
||||||
func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, redelegation types.Redelegation, infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Int) {
|
func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, redelegation types.Redelegation,
|
||||||
|
infractionHeight int64, slashFactor sdk.Rat) (slashAmount sdk.Rat) {
|
||||||
|
|
||||||
now := ctx.BlockHeader().Time
|
now := ctx.BlockHeader().Time
|
||||||
|
|
||||||
// If redelegation started before this height, stake didn't contribute to infraction
|
// If redelegation started before this height, stake didn't contribute to infraction
|
||||||
if redelegation.CreationHeight < infractionHeight {
|
if redelegation.CreationHeight < infractionHeight {
|
||||||
return sdk.ZeroInt()
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
if redelegation.MinTime < now {
|
if redelegation.MinTime < now {
|
||||||
// Redelegation no longer eligible for slashing, skip it
|
// Redelegation no longer eligible for slashing, skip it
|
||||||
// TODO Delete it automatically?
|
// TODO Delete it automatically?
|
||||||
return sdk.ZeroInt()
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate slash amount proportional to stake contributing to infraction
|
// Calculate slash amount proportional to stake contributing to infraction
|
||||||
slashAmount = sdk.NewRatFromInt(redelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor).RoundInt()
|
slashAmount = sdk.NewRatFromInt(redelegation.InitialBalance.Amount, sdk.OneInt()).Mul(slashFactor)
|
||||||
|
|
||||||
// Don't slash more tokens than held
|
// Don't slash more tokens than held
|
||||||
// Possible since the redelegation may already
|
// Possible since the redelegation may already
|
||||||
// have been slashed, and slash amounts are calculated
|
// have been slashed, and slash amounts are calculated
|
||||||
// according to stake held at time of infraction
|
// according to stake held at time of infraction
|
||||||
redelegationSlashAmount := sdk.MinInt(slashAmount, redelegation.Balance.Amount)
|
redelegationSlashAmount := sdk.MinInt(slashAmount.RoundInt(), redelegation.Balance.Amount)
|
||||||
|
|
||||||
// Update redelegation if necessary
|
// Update redelegation if necessary
|
||||||
if !redelegationSlashAmount.IsZero() {
|
if !redelegationSlashAmount.IsZero() {
|
||||||
|
@ -227,7 +241,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
|
||||||
}
|
}
|
||||||
// Burn loose tokens
|
// Burn loose tokens
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
pool.LooseTokens -= tokensToBurn
|
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
|
||||||
k.SetPool(ctx, pool)
|
k.SetPool(ctx, pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,17 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
pool := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
numVals := 3
|
numVals := 3
|
||||||
pool.LooseTokens = amt * int64(numVals)
|
pool.LooseTokens = sdk.NewRat(amt * int64(numVals))
|
||||||
|
|
||||||
// add numVals validators
|
// add numVals validators
|
||||||
for i := 0; i < numVals; i++ {
|
for i := 0; i < numVals; i++ {
|
||||||
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
validator := types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||||
validator, pool, _ = validator.AddTokensFromDel(pool, amt)
|
validator, pool, _ = validator.AddTokensFromDel(pool, amt)
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
keeper.UpdateValidator(ctx, validator)
|
validator = keeper.UpdateValidator(ctx, validator)
|
||||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||||
}
|
}
|
||||||
|
pool = keeper.GetPool(ctx)
|
||||||
|
|
||||||
return ctx, keeper, params
|
return ctx, keeper, params
|
||||||
}
|
}
|
||||||
|
@ -77,20 +78,20 @@ func TestSlashUnbondingDelegation(t *testing.T) {
|
||||||
|
|
||||||
// unbonding started prior to the infraction height, stake didn't contribute
|
// unbonding started prior to the infraction height, stake didn't contribute
|
||||||
slashAmount := keeper.slashUnbondingDelegation(ctx, ubd, 1, fraction)
|
slashAmount := keeper.slashUnbondingDelegation(ctx, ubd, 1, fraction)
|
||||||
require.Equal(t, int64(0), slashAmount.Int64())
|
require.Equal(t, int64(0), slashAmount.RoundInt64())
|
||||||
|
|
||||||
// after the expiration time, no longer eligible for slashing
|
// after the expiration time, no longer eligible for slashing
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
|
||||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||||
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
|
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
|
||||||
require.Equal(t, int64(0), slashAmount.Int64())
|
require.Equal(t, int64(0), slashAmount.RoundInt64())
|
||||||
|
|
||||||
// test valid slash, before expiration timestamp and to which stake contributed
|
// test valid slash, before expiration timestamp and to which stake contributed
|
||||||
oldPool := keeper.GetPool(ctx)
|
oldPool := keeper.GetPool(ctx)
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(0)})
|
||||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||||
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
|
slashAmount = keeper.slashUnbondingDelegation(ctx, ubd, 0, fraction)
|
||||||
require.Equal(t, int64(5), slashAmount.Int64())
|
require.Equal(t, int64(5), slashAmount.RoundInt64())
|
||||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
// initialbalance unchanged
|
// initialbalance unchanged
|
||||||
|
@ -98,7 +99,7 @@ func TestSlashUnbondingDelegation(t *testing.T) {
|
||||||
// balance decreased
|
// balance decreased
|
||||||
require.Equal(t, sdk.NewCoin(params.BondDenom, 5), ubd.Balance)
|
require.Equal(t, sdk.NewCoin(params.BondDenom, 5), ubd.Balance)
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
require.Equal(t, int64(5), oldPool.LooseTokens-newPool.LooseTokens)
|
require.Equal(t, int64(5), oldPool.LooseTokens.Sub(newPool.LooseTokens).RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests slashRedelegation
|
// tests slashRedelegation
|
||||||
|
@ -133,7 +134,7 @@ func TestSlashRedelegation(t *testing.T) {
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[1])
|
validator, found := keeper.GetValidator(ctx, addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
slashAmount := keeper.slashRedelegation(ctx, validator, rd, 1, fraction)
|
slashAmount := keeper.slashRedelegation(ctx, validator, rd, 1, fraction)
|
||||||
require.Equal(t, int64(0), slashAmount.Int64())
|
require.Equal(t, int64(0), slashAmount.RoundInt64())
|
||||||
|
|
||||||
// after the expiration time, no longer eligible for slashing
|
// after the expiration time, no longer eligible for slashing
|
||||||
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
|
ctx = ctx.WithBlockHeader(abci.Header{Time: int64(10)})
|
||||||
|
@ -141,7 +142,7 @@ func TestSlashRedelegation(t *testing.T) {
|
||||||
validator, found = keeper.GetValidator(ctx, addrVals[1])
|
validator, found = keeper.GetValidator(ctx, addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction)
|
slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction)
|
||||||
require.Equal(t, int64(0), slashAmount.Int64())
|
require.Equal(t, int64(0), slashAmount.RoundInt64())
|
||||||
|
|
||||||
// test valid slash, before expiration timestamp and to which stake contributed
|
// test valid slash, before expiration timestamp and to which stake contributed
|
||||||
oldPool := keeper.GetPool(ctx)
|
oldPool := keeper.GetPool(ctx)
|
||||||
|
@ -150,7 +151,7 @@ func TestSlashRedelegation(t *testing.T) {
|
||||||
validator, found = keeper.GetValidator(ctx, addrVals[1])
|
validator, found = keeper.GetValidator(ctx, addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction)
|
slashAmount = keeper.slashRedelegation(ctx, validator, rd, 0, fraction)
|
||||||
require.Equal(t, int64(5), slashAmount.Int64())
|
require.Equal(t, int64(5), slashAmount.RoundInt64())
|
||||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
// initialbalance unchanged
|
// initialbalance unchanged
|
||||||
|
@ -163,7 +164,7 @@ func TestSlashRedelegation(t *testing.T) {
|
||||||
require.Equal(t, int64(5), del.Shares.RoundInt64())
|
require.Equal(t, int64(5), del.Shares.RoundInt64())
|
||||||
// pool bonded tokens decreased
|
// pool bonded tokens decreased
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
require.Equal(t, int64(5), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests Slash at a future height (must panic)
|
// tests Slash at a future height (must panic)
|
||||||
|
@ -193,7 +194,7 @@ func TestSlashAtCurrentHeight(t *testing.T) {
|
||||||
// power decreased
|
// power decreased
|
||||||
require.Equal(t, sdk.NewRat(5), validator.GetPower())
|
require.Equal(t, sdk.NewRat(5), validator.GetPower())
|
||||||
// pool bonded shares decreased
|
// pool bonded shares decreased
|
||||||
require.Equal(t, sdk.NewRat(5).RoundInt64(), oldPool.BondedShares.Sub(newPool.BondedShares).RoundInt64())
|
require.Equal(t, sdk.NewRat(5).RoundInt64(), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
// tests Slash at a previous height with an unbonding delegation
|
// tests Slash at a previous height with an unbonding delegation
|
||||||
|
@ -229,7 +230,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
// bonded tokens burned
|
// bonded tokens burned
|
||||||
require.Equal(t, int64(3), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -249,7 +250,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// bonded tokens burned again
|
// bonded tokens burned again
|
||||||
require.Equal(t, int64(6), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(6), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -269,7 +270,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// bonded tokens burned again
|
// bonded tokens burned again
|
||||||
require.Equal(t, int64(9), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(9), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -289,7 +290,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// just 1 bonded token burned again since that's all the validator now has
|
// just 1 bonded token burned again since that's all the validator now has
|
||||||
require.Equal(t, int64(10), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
// power decreased by 1 again, validator is out of stake
|
// power decreased by 1 again, validator is out of stake
|
||||||
// ergo validator should have been removed from the store
|
// ergo validator should have been removed from the store
|
||||||
|
@ -325,6 +326,11 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
}
|
}
|
||||||
keeper.SetDelegation(ctx, del)
|
keeper.SetDelegation(ctx, del)
|
||||||
|
|
||||||
|
// update bonded tokens
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.BondedTokens = pool.BondedTokens.Add(sdk.NewRat(6))
|
||||||
|
keeper.SetPool(ctx, pool)
|
||||||
|
|
||||||
// slash validator
|
// slash validator
|
||||||
ctx = ctx.WithBlockHeight(12)
|
ctx = ctx.WithBlockHeight(12)
|
||||||
oldPool := keeper.GetPool(ctx)
|
oldPool := keeper.GetPool(ctx)
|
||||||
|
@ -340,7 +346,7 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
// bonded tokens burned
|
// bonded tokens burned
|
||||||
require.Equal(t, int64(5), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -354,7 +360,7 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
ctx = ctx.WithBlockHeight(12)
|
ctx = ctx.WithBlockHeight(12)
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
keeper.Slash(ctx, pk, 10, 10, sdk.NewRat(3, 4))
|
require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneRat()) })
|
||||||
|
|
||||||
// read updating redelegation
|
// read updating redelegation
|
||||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||||
|
@ -363,8 +369,8 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
require.Equal(t, sdk.NewInt(0), rd.Balance.Amount)
|
require.Equal(t, sdk.NewInt(0), rd.Balance.Amount)
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// 7 bonded tokens burned
|
// seven bonded tokens burned
|
||||||
require.Equal(t, int64(12), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(12), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
validator, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
@ -385,7 +391,7 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// four more bonded tokens burned
|
// four more bonded tokens burned
|
||||||
require.Equal(t, int64(16), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
// validator decreased to zero power, should have been removed from the store
|
// validator decreased to zero power, should have been removed from the store
|
||||||
_, found = keeper.GetValidatorByPubKey(ctx, pk)
|
_, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
|
@ -407,7 +413,7 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool = keeper.GetPool(ctx)
|
newPool = keeper.GetPool(ctx)
|
||||||
// no more bonded tokens burned
|
// no more bonded tokens burned
|
||||||
require.Equal(t, int64(16), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
// power still zero, still not in the store
|
// power still zero, still not in the store
|
||||||
_, found = keeper.GetValidatorByPubKey(ctx, pk)
|
_, found = keeper.GetValidatorByPubKey(ctx, pk)
|
||||||
|
@ -469,9 +475,9 @@ func TestSlashBoth(t *testing.T) {
|
||||||
// read updated pool
|
// read updated pool
|
||||||
newPool := keeper.GetPool(ctx)
|
newPool := keeper.GetPool(ctx)
|
||||||
// loose tokens burned
|
// loose tokens burned
|
||||||
require.Equal(t, int64(2), oldPool.LooseTokens-newPool.LooseTokens)
|
require.Equal(t, int64(2), oldPool.LooseTokens.Sub(newPool.LooseTokens).RoundInt64())
|
||||||
// bonded tokens burned
|
// bonded tokens burned
|
||||||
require.Equal(t, int64(3), oldPool.BondedTokens-newPool.BondedTokens)
|
require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
|
||||||
// read updated validator
|
// read updated validator
|
||||||
validator, found = keeper.GetValidatorByPubKey(ctx, PKs[0])
|
validator, found = keeper.GetValidatorByPubKey(ctx, PKs[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
|
|
|
@ -118,7 +118,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
|
||||||
{keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
|
{keeper.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)},
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
pool.LooseTokens += initCoins
|
pool.LooseTokens = pool.LooseTokens.Add(sdk.NewRat(initCoins))
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
|
||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
|
||||||
}
|
}
|
||||||
if validator.Status() == sdk.Bonded {
|
if validator.Status == sdk.Bonded {
|
||||||
validators[i] = validator
|
validators[i] = validator
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
||||||
switch {
|
switch {
|
||||||
// if already bonded and power increasing only need to update tendermint
|
// if already bonded and power increasing only need to update tendermint
|
||||||
case powerIncreasing && !validator.Revoked &&
|
case powerIncreasing && !validator.Revoked &&
|
||||||
(oldFound && oldValidator.Status() == sdk.Bonded):
|
(oldFound && oldValidator.Status == sdk.Bonded):
|
||||||
|
|
||||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
||||||
|
@ -224,7 +224,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
||||||
|
|
||||||
// if was unbonded and the new power is less than the cliff validator
|
// if was unbonded and the new power is less than the cliff validator
|
||||||
case cliffPower != nil &&
|
case cliffPower != nil &&
|
||||||
(oldFound && oldValidator.Status() == sdk.Unbonded) &&
|
(oldFound && oldValidator.Status == sdk.Unbonded) &&
|
||||||
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
|
||||||
// skip to completion
|
// skip to completion
|
||||||
|
|
||||||
|
@ -234,19 +234,19 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
||||||
default:
|
default:
|
||||||
|
|
||||||
// update the validator set for this validator
|
// update the validator set for this validator
|
||||||
|
// if updated, the validator has changed bonding status
|
||||||
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
|
updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
|
||||||
if updated { // updates to validator occurred to be updated
|
if updated { // updates to validator occurred to be updated
|
||||||
validator = updatedVal
|
validator = updatedVal
|
||||||
} else {
|
break
|
||||||
|
|
||||||
// if decreased in power but still bonded, update Tendermint validator
|
|
||||||
// (if updatedVal is set, the validator has changed bonding status)
|
|
||||||
stillBonded := oldFound && oldValidator.Status() == sdk.Bonded
|
|
||||||
if stillBonded && oldValidator.PoolShares.Bonded().GT(validator.PoolShares.Bonded()) {
|
|
||||||
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
|
||||||
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if decreased in power but still bonded, update Tendermint validator
|
||||||
|
if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) {
|
||||||
|
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
|
||||||
|
store.Set(GetTendermintUpdatesKey(validator.Owner), bz)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
k.SetValidator(ctx, validator)
|
k.SetValidator(ctx, validator)
|
||||||
|
@ -254,7 +254,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
|
func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
|
||||||
if newValidator.Revoked && oldFound && oldValidator.Status() == sdk.Bonded {
|
if newValidator.Revoked && oldFound && oldValidator.Status == sdk.Bonded {
|
||||||
newValidator = k.unbondValidator(ctx, newValidator)
|
newValidator = k.unbondValidator(ctx, newValidator)
|
||||||
|
|
||||||
// need to also clear the cliff validator spot because the revoke has
|
// need to also clear the cliff validator spot because the revoke has
|
||||||
|
@ -266,7 +266,7 @@ func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool {
|
func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) bool {
|
||||||
if oldFound && oldValidator.PoolShares.Bonded().LT(newValidator.PoolShares.Bonded()) {
|
if oldFound && oldValidator.BondedTokens().LT(newValidator.BondedTokens()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -277,7 +277,7 @@ func (k Keeper) bondIncrement(ctx sdk.Context, oldFound bool, oldValidator,
|
||||||
newValidator types.Validator) (height int64, intraTxCounter int16) {
|
newValidator types.Validator) (height int64, intraTxCounter int16) {
|
||||||
|
|
||||||
// if already a validator, copy the old block height and counter, else set them
|
// if already a validator, copy the old block height and counter, else set them
|
||||||
if oldFound && oldValidator.Status() == sdk.Bonded {
|
if oldFound && oldValidator.Status == sdk.Bonded {
|
||||||
height = oldValidator.BondHeight
|
height = oldValidator.BondHeight
|
||||||
intraTxCounter = oldValidator.BondIntraTxCounter
|
intraTxCounter = oldValidator.BondIntraTxCounter
|
||||||
return
|
return
|
||||||
|
@ -350,14 +350,14 @@ func (k Keeper) UpdateBondedValidators(ctx sdk.Context,
|
||||||
|
|
||||||
// increment bondedValidatorsCount / get the validator to bond
|
// increment bondedValidatorsCount / get the validator to bond
|
||||||
if !validator.Revoked {
|
if !validator.Revoked {
|
||||||
if validator.Status() != sdk.Bonded {
|
if validator.Status != sdk.Bonded {
|
||||||
validatorToBond = validator
|
validatorToBond = validator
|
||||||
newValidatorBonded = true
|
newValidatorBonded = true
|
||||||
}
|
}
|
||||||
bondedValidatorsCount++
|
bondedValidatorsCount++
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
} else if validator.Status() == sdk.Bonded {
|
} else if validator.Status == sdk.Bonded {
|
||||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
|
||||||
if !validator.Revoked {
|
if !validator.Revoked {
|
||||||
bondedValidatorsCount++
|
bondedValidatorsCount++
|
||||||
} else {
|
} else {
|
||||||
if validator.Status() == sdk.Bonded {
|
if validator.Status == sdk.Bonded {
|
||||||
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
panic(fmt.Sprintf("revoked validator cannot be bonded, address: %v\n", ownerAddr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +483,7 @@ func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) type
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if validator.Status() == sdk.Unbonded {
|
if validator.Status == sdk.Unbonded {
|
||||||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,7 +510,7 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
|
|
||||||
// sanity check
|
// sanity check
|
||||||
if validator.Status() == sdk.Bonded {
|
if validator.Status == sdk.Bonded {
|
||||||
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
|
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ func TestSetValidator(t *testing.T) {
|
||||||
// test how the validator is set from a purely unbonbed pool
|
// test how the validator is set from a purely unbonbed pool
|
||||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||||
validator, pool, _ = validator.AddTokensFromDel(pool, 10)
|
validator, pool, _ = validator.AddTokensFromDel(pool, 10)
|
||||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
require.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Unbonded()))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
keeper.UpdateValidator(ctx, validator)
|
keeper.UpdateValidator(ctx, validator)
|
||||||
|
@ -27,8 +27,8 @@ func TestSetValidator(t *testing.T) {
|
||||||
// after the save the validator should be bonded
|
// after the save the validator should be bonded
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Bonded, validator.Status())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.PoolShares.Bonded()))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.DelegatorShares))
|
||||||
|
|
||||||
// Check each store for being saved
|
// Check each store for being saved
|
||||||
|
@ -55,25 +55,20 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||||
pool := keeper.GetPool(ctx)
|
pool := keeper.GetPool(ctx)
|
||||||
|
|
||||||
// create a random pool
|
// create a random pool
|
||||||
pool.LooseTokens = 10000
|
pool.LooseTokens = sdk.NewRat(10000)
|
||||||
pool.BondedTokens = 1234
|
pool.BondedTokens = sdk.NewRat(1234)
|
||||||
pool.BondedShares = sdk.NewRat(124)
|
|
||||||
pool.UnbondingTokens = 13934
|
|
||||||
pool.UnbondingShares = sdk.NewRat(145)
|
|
||||||
pool.UnbondedTokens = 154
|
|
||||||
pool.UnbondedShares = sdk.NewRat(1333)
|
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
|
|
||||||
// add a validator
|
// add a validator
|
||||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||||
validator, pool, delSharesCreated := validator.AddTokensFromDel(pool, 100)
|
validator, pool, delSharesCreated := validator.AddTokensFromDel(pool, 100)
|
||||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
require.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
require.Equal(t, int64(100), validator.PoolShares.Tokens(pool).RoundInt64())
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
keeper.UpdateValidator(ctx, validator)
|
keeper.UpdateValidator(ctx, validator)
|
||||||
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
validator, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, int64(100), validator.PoolShares.Tokens(pool).RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
|
||||||
|
|
||||||
pool = keeper.GetPool(ctx)
|
pool = keeper.GetPool(ctx)
|
||||||
power := GetValidatorsByPowerIndexKey(validator, pool)
|
power := GetValidatorsByPowerIndexKey(validator, pool)
|
||||||
|
@ -81,7 +76,7 @@ func TestUpdateValidatorByPowerIndex(t *testing.T) {
|
||||||
|
|
||||||
// burn half the delegator shares
|
// burn half the delegator shares
|
||||||
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
validator, pool, burned := validator.RemoveDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2)))
|
||||||
require.Equal(t, int64(50), burned)
|
require.Equal(t, int64(50), burned.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool) // update the pool
|
keeper.SetPool(ctx, pool) // update the pool
|
||||||
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
keeper.UpdateValidator(ctx, validator) // update the validator, possibly kicking it out
|
||||||
require.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
require.False(t, keeper.validatorByPowerIndexExists(ctx, power))
|
||||||
|
@ -101,12 +96,12 @@ func TestSlashToZeroPowerRemoved(t *testing.T) {
|
||||||
// add a validator
|
// add a validator
|
||||||
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
|
||||||
validator, pool, _ = validator.AddTokensFromDel(pool, 100)
|
validator, pool, _ = validator.AddTokensFromDel(pool, 100)
|
||||||
require.Equal(t, sdk.Unbonded, validator.Status())
|
require.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
require.Equal(t, int64(100), validator.PoolShares.Tokens(pool).RoundInt64())
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
keeper.SetValidatorByPubKeyIndex(ctx, validator)
|
||||||
validator = keeper.UpdateValidator(ctx, validator)
|
validator = keeper.UpdateValidator(ctx, validator)
|
||||||
require.Equal(t, int64(100), validator.PoolShares.Tokens(pool).RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool)
|
||||||
|
|
||||||
// slash the validator by 100%
|
// slash the validator by 100%
|
||||||
keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneRat())
|
keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneRat())
|
||||||
|
@ -125,9 +120,14 @@ func TestValidatorBasics(t *testing.T) {
|
||||||
amts := []int64{9, 8, 7}
|
amts := []int64{9, 8, 7}
|
||||||
for i, amt := range amts {
|
for i, amt := range amts {
|
||||||
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{})
|
||||||
validators[i].PoolShares = types.NewUnbondedShares(sdk.ZeroRat())
|
validators[i].Status = sdk.Unbonded
|
||||||
validators[i].AddTokensFromDel(pool, amt)
|
validators[i].Tokens = sdk.ZeroRat()
|
||||||
|
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, amt)
|
||||||
|
keeper.SetPool(ctx, pool)
|
||||||
}
|
}
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].Tokens))
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(8), validators[1].Tokens))
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(7), validators[2].Tokens))
|
||||||
|
|
||||||
// check the empty keeper first
|
// check the empty keeper first
|
||||||
_, found := keeper.GetValidator(ctx, addrVals[0])
|
_, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
|
@ -135,6 +135,9 @@ func TestValidatorBasics(t *testing.T) {
|
||||||
resVals := keeper.GetValidatorsBonded(ctx)
|
resVals := keeper.GetValidatorsBonded(ctx)
|
||||||
assert.Zero(t, len(resVals))
|
assert.Zero(t, len(resVals))
|
||||||
|
|
||||||
|
pool = keeper.GetPool(ctx)
|
||||||
|
assert.True(sdk.RatEq(t, sdk.ZeroRat(), pool.BondedTokens))
|
||||||
|
|
||||||
// set and retrieve a record
|
// set and retrieve a record
|
||||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||||
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
resVal, found := keeper.GetValidator(ctx, addrVals[0])
|
||||||
|
@ -144,9 +147,15 @@ func TestValidatorBasics(t *testing.T) {
|
||||||
resVals = keeper.GetValidatorsBonded(ctx)
|
resVals = keeper.GetValidatorsBonded(ctx)
|
||||||
require.Equal(t, 1, len(resVals))
|
require.Equal(t, 1, len(resVals))
|
||||||
assert.True(ValEq(t, validators[0], resVals[0]))
|
assert.True(ValEq(t, validators[0], resVals[0]))
|
||||||
|
assert.Equal(t, sdk.Bonded, validators[0].Status)
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(9), validators[0].BondedTokens()))
|
||||||
|
|
||||||
|
pool = keeper.GetPool(ctx)
|
||||||
|
assert.True(sdk.RatEq(t, pool.BondedTokens, validators[0].BondedTokens()))
|
||||||
|
|
||||||
// modify a records, save, and retrieve
|
// modify a records, save, and retrieve
|
||||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(10))
|
validators[0].Status = sdk.Bonded
|
||||||
|
validators[0].Tokens = sdk.NewRat(10)
|
||||||
validators[0].DelegatorShares = sdk.NewRat(10)
|
validators[0].DelegatorShares = sdk.NewRat(10)
|
||||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||||
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
resVal, found = keeper.GetValidator(ctx, addrVals[0])
|
||||||
|
@ -189,7 +198,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
|
||||||
var validators [5]types.Validator
|
var validators [5]types.Validator
|
||||||
for i, amt := range amts {
|
for i, amt := range amts {
|
||||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||||
validators[i].PoolShares = types.NewBondedShares(sdk.NewRat(amt))
|
validators[i].Status = sdk.Bonded
|
||||||
|
validators[i].Tokens = sdk.NewRat(amt)
|
||||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||||
keeper.UpdateValidator(ctx, validators[i])
|
keeper.UpdateValidator(ctx, validators[i])
|
||||||
}
|
}
|
||||||
|
@ -197,11 +207,11 @@ func GetValidatorSortingUnmixed(t *testing.T) {
|
||||||
// first make sure everything made it in to the gotValidator group
|
// first make sure everything made it in to the gotValidator group
|
||||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||||
assert.Equal(t, n, len(resValidators))
|
assert.Equal(t, n, len(resValidators))
|
||||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||||
|
@ -209,14 +219,14 @@ func GetValidatorSortingUnmixed(t *testing.T) {
|
||||||
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
assert.Equal(t, validators[0].Owner, resValidators[4].Owner, "%v", resValidators)
|
||||||
|
|
||||||
// test a basic increase in voting power
|
// test a basic increase in voting power
|
||||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(500))
|
validators[3].Tokens = sdk.NewRat(500)
|
||||||
keeper.UpdateValidator(ctx, validators[3])
|
keeper.UpdateValidator(ctx, validators[3])
|
||||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||||
require.Equal(t, len(resValidators), n)
|
require.Equal(t, len(resValidators), n)
|
||||||
assert.True(ValEq(t, validators[3], resValidators[0]))
|
assert.True(ValEq(t, validators[3], resValidators[0]))
|
||||||
|
|
||||||
// test a decrease in voting power
|
// test a decrease in voting power
|
||||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
validators[3].Tokens = sdk.NewRat(300)
|
||||||
keeper.UpdateValidator(ctx, validators[3])
|
keeper.UpdateValidator(ctx, validators[3])
|
||||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||||
require.Equal(t, len(resValidators), n)
|
require.Equal(t, len(resValidators), n)
|
||||||
|
@ -224,7 +234,7 @@ func GetValidatorSortingUnmixed(t *testing.T) {
|
||||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||||
|
|
||||||
// test equal voting power, different age
|
// test equal voting power, different age
|
||||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(200))
|
validators[3].Tokens = sdk.NewRat(200)
|
||||||
ctx = ctx.WithBlockHeight(10)
|
ctx = ctx.WithBlockHeight(10)
|
||||||
keeper.UpdateValidator(ctx, validators[3])
|
keeper.UpdateValidator(ctx, validators[3])
|
||||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||||
|
@ -243,8 +253,8 @@ func GetValidatorSortingUnmixed(t *testing.T) {
|
||||||
assert.True(ValEq(t, validators[4], resValidators[1]))
|
assert.True(ValEq(t, validators[4], resValidators[1]))
|
||||||
|
|
||||||
// change in voting power of both validators, both still in v-set, no age change
|
// change in voting power of both validators, both still in v-set, no age change
|
||||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
validators[3].Tokens = sdk.NewRat(300)
|
||||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(300))
|
validators[4].Tokens = sdk.NewRat(300)
|
||||||
keeper.UpdateValidator(ctx, validators[3])
|
keeper.UpdateValidator(ctx, validators[3])
|
||||||
resValidators = keeper.GetValidatorsByPower(ctx)
|
resValidators = keeper.GetValidatorsByPower(ctx)
|
||||||
require.Equal(t, len(resValidators), n)
|
require.Equal(t, len(resValidators), n)
|
||||||
|
@ -273,11 +283,19 @@ func GetValidatorSortingMixed(t *testing.T) {
|
||||||
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
validators[i] = types.NewValidator(Addrs[i], PKs[i], types.Description{})
|
||||||
validators[i].DelegatorShares = sdk.NewRat(amt)
|
validators[i].DelegatorShares = sdk.NewRat(amt)
|
||||||
}
|
}
|
||||||
validators[0].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[0]))
|
|
||||||
validators[1].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[1]))
|
validators[0].Status = sdk.Bonded
|
||||||
validators[2].PoolShares = types.NewUnbondedShares(sdk.NewRat(amts[2]))
|
validators[1].Status = sdk.Bonded
|
||||||
validators[3].PoolShares = types.NewBondedShares(sdk.NewRat(amts[3]))
|
validators[2].Status = sdk.Bonded
|
||||||
validators[4].PoolShares = types.NewBondedShares(sdk.NewRat(amts[4]))
|
validators[0].Tokens = sdk.NewRat(amts[0])
|
||||||
|
validators[1].Tokens = sdk.NewRat(amts[1])
|
||||||
|
validators[2].Tokens = sdk.NewRat(amts[2])
|
||||||
|
|
||||||
|
validators[3].Status = sdk.Bonded
|
||||||
|
validators[4].Status = sdk.Bonded
|
||||||
|
validators[3].Tokens = sdk.NewRat(amts[3])
|
||||||
|
validators[4].Tokens = sdk.NewRat(amts[4])
|
||||||
|
|
||||||
for i := range amts {
|
for i := range amts {
|
||||||
keeper.UpdateValidator(ctx, validators[i])
|
keeper.UpdateValidator(ctx, validators[i])
|
||||||
}
|
}
|
||||||
|
@ -291,20 +309,20 @@ func GetValidatorSortingMixed(t *testing.T) {
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
val4, found := keeper.GetValidator(ctx, Addrs[4])
|
val4, found := keeper.GetValidator(ctx, Addrs[4])
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
require.Equal(t, sdk.Unbonded, val0.Status())
|
require.Equal(t, sdk.Unbonded, val0.Status)
|
||||||
require.Equal(t, sdk.Unbonded, val1.Status())
|
require.Equal(t, sdk.Unbonded, val1.Status)
|
||||||
require.Equal(t, sdk.Unbonded, val2.Status())
|
require.Equal(t, sdk.Unbonded, val2.Status)
|
||||||
require.Equal(t, sdk.Bonded, val3.Status())
|
require.Equal(t, sdk.Bonded, val3.Status)
|
||||||
require.Equal(t, sdk.Bonded, val4.Status())
|
require.Equal(t, sdk.Bonded, val4.Status)
|
||||||
|
|
||||||
// first make sure everything made it in to the gotValidator group
|
// first make sure everything made it in to the gotValidator group
|
||||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||||
assert.Equal(t, n, len(resValidators))
|
assert.Equal(t, n, len(resValidators))
|
||||||
assert.Equal(t, sdk.NewRat(400), resValidators[0].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(400), resValidators[0].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(200), resValidators[1].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(200), resValidators[1].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(100), resValidators[2].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(100), resValidators[2].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(1), resValidators[3].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(1), resValidators[3].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, sdk.NewRat(0), resValidators[4].PoolShares.Bonded(), "%v", resValidators)
|
assert.Equal(t, sdk.NewRat(0), resValidators[4].BondedTokens(), "%v", resValidators)
|
||||||
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
assert.Equal(t, validators[3].Owner, resValidators[0].Owner, "%v", resValidators)
|
||||||
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
assert.Equal(t, validators[4].Owner, resValidators[1].Owner, "%v", resValidators)
|
||||||
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
assert.Equal(t, validators[1].Owner, resValidators[2].Owner, "%v", resValidators)
|
||||||
|
@ -392,6 +410,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) {
|
||||||
|
|
||||||
func TestValidatorBondHeight(t *testing.T) {
|
func TestValidatorBondHeight(t *testing.T) {
|
||||||
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
ctx, _, keeper := CreateTestInput(t, false, 1000)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
|
||||||
// now 2 max resValidators
|
// now 2 max resValidators
|
||||||
params := keeper.GetParams(ctx)
|
params := keeper.GetParams(ctx)
|
||||||
|
@ -399,7 +418,6 @@ func TestValidatorBondHeight(t *testing.T) {
|
||||||
keeper.SetParams(ctx, params)
|
keeper.SetParams(ctx, params)
|
||||||
|
|
||||||
// initialize some validators into the state
|
// initialize some validators into the state
|
||||||
pool := keeper.GetPool(ctx)
|
|
||||||
var validators [3]types.Validator
|
var validators [3]types.Validator
|
||||||
validators[0] = types.NewValidator(Addrs[0], PKs[0], types.Description{})
|
validators[0] = types.NewValidator(Addrs[0], PKs[0], types.Description{})
|
||||||
validators[1] = types.NewValidator(Addrs[1], PKs[1], types.Description{})
|
validators[1] = types.NewValidator(Addrs[1], PKs[1], types.Description{})
|
||||||
|
@ -408,14 +426,18 @@ func TestValidatorBondHeight(t *testing.T) {
|
||||||
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 200)
|
validators[0], pool, _ = validators[0].AddTokensFromDel(pool, 200)
|
||||||
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 100)
|
validators[1], pool, _ = validators[1].AddTokensFromDel(pool, 100)
|
||||||
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 100)
|
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, 100)
|
||||||
|
|
||||||
keeper.SetPool(ctx, pool)
|
keeper.SetPool(ctx, pool)
|
||||||
|
|
||||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||||
|
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
// If two validators both increase to the same voting power in the same block,
|
// If two validators both increase to the same voting power in the same block,
|
||||||
// the one with the first transaction should become bonded
|
// the one with the first transaction should become bonded
|
||||||
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
validators[1] = keeper.UpdateValidator(ctx, validators[1])
|
||||||
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
validators[2] = keeper.UpdateValidator(ctx, validators[2])
|
||||||
|
|
||||||
|
pool = keeper.GetPool(ctx)
|
||||||
|
|
||||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||||
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
|
require.Equal(t, uint16(len(resValidators)), params.MaxValidators)
|
||||||
|
|
||||||
|
@ -454,11 +476,11 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
|
||||||
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
validators[i], found = keeper.GetValidator(ctx, validators[i].Owner)
|
||||||
require.True(t, found)
|
require.True(t, found)
|
||||||
}
|
}
|
||||||
assert.Equal(t, sdk.Unbonded, validators[0].Status())
|
assert.Equal(t, sdk.Unbonded, validators[0].Status)
|
||||||
assert.Equal(t, sdk.Unbonded, validators[1].Status())
|
assert.Equal(t, sdk.Unbonded, validators[1].Status)
|
||||||
assert.Equal(t, sdk.Bonded, validators[2].Status())
|
assert.Equal(t, sdk.Bonded, validators[2].Status)
|
||||||
assert.Equal(t, sdk.Bonded, validators[3].Status())
|
assert.Equal(t, sdk.Bonded, validators[3].Status)
|
||||||
assert.Equal(t, sdk.Unbonded, validators[4].Status())
|
assert.Equal(t, sdk.Unbonded, validators[4].Status)
|
||||||
resValidators := keeper.GetValidatorsByPower(ctx)
|
resValidators := keeper.GetValidatorsByPower(ctx)
|
||||||
assert.Equal(t, max, len(resValidators))
|
assert.Equal(t, max, len(resValidators))
|
||||||
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs
|
||||||
|
@ -576,7 +598,8 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
|
||||||
|
|
||||||
// test single value change
|
// test single value change
|
||||||
// tendermintUpdate set: {} -> {c1'}
|
// tendermintUpdate set: {} -> {c1'}
|
||||||
validators[0].PoolShares = types.NewBondedShares(sdk.NewRat(600))
|
validators[0].Status = sdk.Bonded
|
||||||
|
validators[0].Tokens = sdk.NewRat(600)
|
||||||
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
validators[0] = keeper.UpdateValidator(ctx, validators[0])
|
||||||
|
|
||||||
updates := keeper.GetTendermintUpdates(ctx)
|
updates := keeper.GetTendermintUpdates(ctx)
|
||||||
|
|
|
@ -10,13 +10,13 @@ import (
|
||||||
type (
|
type (
|
||||||
Keeper = keeper.Keeper
|
Keeper = keeper.Keeper
|
||||||
Validator = types.Validator
|
Validator = types.Validator
|
||||||
|
BechValidator = types.BechValidator
|
||||||
Description = types.Description
|
Description = types.Description
|
||||||
Delegation = types.Delegation
|
Delegation = types.Delegation
|
||||||
UnbondingDelegation = types.UnbondingDelegation
|
UnbondingDelegation = types.UnbondingDelegation
|
||||||
Redelegation = types.Redelegation
|
Redelegation = types.Redelegation
|
||||||
Params = types.Params
|
Params = types.Params
|
||||||
Pool = types.Pool
|
Pool = types.Pool
|
||||||
PoolShares = types.PoolShares
|
|
||||||
MsgCreateValidator = types.MsgCreateValidator
|
MsgCreateValidator = types.MsgCreateValidator
|
||||||
MsgEditValidator = types.MsgEditValidator
|
MsgEditValidator = types.MsgEditValidator
|
||||||
MsgDelegate = types.MsgDelegate
|
MsgDelegate = types.MsgDelegate
|
||||||
|
@ -62,9 +62,6 @@ var (
|
||||||
|
|
||||||
DefaultParams = types.DefaultParams
|
DefaultParams = types.DefaultParams
|
||||||
InitialPool = types.InitialPool
|
InitialPool = types.InitialPool
|
||||||
NewUnbondedShares = types.NewUnbondedShares
|
|
||||||
NewUnbondingShares = types.NewUnbondingShares
|
|
||||||
NewBondedShares = types.NewBondedShares
|
|
||||||
NewValidator = types.NewValidator
|
NewValidator = types.NewValidator
|
||||||
NewDescription = types.NewDescription
|
NewDescription = types.NewDescription
|
||||||
NewGenesisState = types.NewGenesisState
|
NewGenesisState = types.NewGenesisState
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
//changing the int in NewSource will allow you to test different, deterministic, sets of operations
|
||||||
|
var r = rand.New(rand.NewSource(6595))
|
||||||
|
|
||||||
|
func TestGetInflation(t *testing.T) {
|
||||||
|
pool := InitialPool()
|
||||||
|
params := DefaultParams()
|
||||||
|
|
||||||
|
// Governing Mechanism:
|
||||||
|
// BondedRatio = BondedTokens / TotalSupply
|
||||||
|
// inflationRateChangePerYear = (1- BondedRatio/ GoalBonded) * MaxInflationRateChange
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
setBondedTokens, setLooseTokens,
|
||||||
|
setInflation, expectedChange sdk.Rat
|
||||||
|
}{
|
||||||
|
// with 0% bonded atom supply the inflation should increase by InflationRateChange
|
||||||
|
{"test 1", sdk.ZeroRat(), sdk.ZeroRat(), 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", sdk.OneRat(), sdk.ZeroRat(), 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", sdk.OneRat(), sdk.OneRat(), 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", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(7, 100), sdk.ZeroRat()},
|
||||||
|
{"test 5", sdk.OneRat(), sdk.ZeroRat(), sdk.NewRat(70001, 1000000), sdk.NewRat(-1, 1000000).Round(precision)},
|
||||||
|
|
||||||
|
// test 20% maximum stop (testing with 0% bonded)
|
||||||
|
{"test 6", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(20, 100), sdk.ZeroRat()},
|
||||||
|
{"test 7", sdk.ZeroRat(), sdk.ZeroRat(), sdk.NewRat(199999, 1000000), sdk.NewRat(1, 1000000).Round(precision)},
|
||||||
|
|
||||||
|
// perfect balance shouldn't change inflation
|
||||||
|
{"test 8", sdk.NewRat(67), sdk.NewRat(33), sdk.NewRat(15, 100), sdk.ZeroRat()},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
pool.BondedTokens, pool.LooseTokens = tc.setBondedTokens, tc.setLooseTokens
|
||||||
|
pool.Inflation = tc.setInflation
|
||||||
|
|
||||||
|
inflation := pool.NextInflation(params)
|
||||||
|
diffInflation := inflation.Sub(tc.setInflation)
|
||||||
|
|
||||||
|
require.True(t, diffInflation.Equal(tc.expectedChange),
|
||||||
|
"Name: %v\nDiff: %v\nExpected: %v\n", tc.name, diffInflation, tc.expectedChange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that provisions are correctly added to the pool and validators each hour for 1 year
|
||||||
|
func TestProcessProvisions(t *testing.T) {
|
||||||
|
pool := InitialPool()
|
||||||
|
params := DefaultParams()
|
||||||
|
|
||||||
|
var (
|
||||||
|
initialTotalTokens int64 = 550000000
|
||||||
|
cumulativeExpProvs = sdk.ZeroRat()
|
||||||
|
)
|
||||||
|
pool.LooseTokens = sdk.NewRat(initialTotalTokens)
|
||||||
|
|
||||||
|
// process the provisions for a year
|
||||||
|
for hr := 0; hr < 100; hr++ {
|
||||||
|
var expProvisions sdk.Rat
|
||||||
|
_, expProvisions, pool = updateProvisions(t, pool, params, hr)
|
||||||
|
cumulativeExpProvs = cumulativeExpProvs.Add(expProvisions)
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the pool and do the final value checks from checkFinalPoolValues
|
||||||
|
checkFinalPoolValues(t, pool, sdk.NewRat(initialTotalTokens), cumulativeExpProvs)
|
||||||
|
}
|
||||||
|
|
||||||
|
//_________________________________________________________________________________________
|
||||||
|
////////////////////////////////HELPER FUNCTIONS BELOW/////////////////////////////////////
|
||||||
|
|
||||||
|
// Final check on the global pool values for what the total tokens accumulated from each hour of provisions
|
||||||
|
func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs sdk.Rat) {
|
||||||
|
calculatedTotalTokens := initialTotalTokens.Add(cumulativeExpProvs)
|
||||||
|
require.True(sdk.RatEq(t, calculatedTotalTokens, pool.TokenSupply()))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processes provisions are added to the pool correctly every hour
|
||||||
|
// Returns expected Provisions, expected Inflation, and pool, to help with cumulative calculations back in main Tests
|
||||||
|
func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Rat, sdk.Rat, Pool) {
|
||||||
|
expInflation := pool.NextInflation(params)
|
||||||
|
expProvisions := expInflation.Mul(pool.TokenSupply()).Quo(hrsPerYrRat)
|
||||||
|
startTotalSupply := pool.TokenSupply()
|
||||||
|
pool = pool.ProcessProvisions(params)
|
||||||
|
|
||||||
|
//check provisions were added to pool
|
||||||
|
require.True(sdk.RatEq(t, startTotalSupply.Add(expProvisions), pool.TokenSupply()))
|
||||||
|
|
||||||
|
return expInflation, expProvisions, pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks that The inflation will correctly increase or decrease after an update to the pool
|
||||||
|
// nolint: gocyclo
|
||||||
|
func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Rat, msg string) {
|
||||||
|
inflationChange := updatedInflation.Sub(previousInflation)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
//BELOW 67% - Rate of change positive and increasing, while we are between 7% <= and < 20% inflation
|
||||||
|
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)):
|
||||||
|
require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||||
|
|
||||||
|
//BELOW 67% - Rate of change should be 0 while inflation continually stays at 20% until we reach 67% bonded ratio
|
||||||
|
case pool.BondedRatio().LT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(20, 100)):
|
||||||
|
if previousInflation.Equal(sdk.NewRat(20, 100)) {
|
||||||
|
require.Equal(t, true, inflationChange.IsZero(), msg)
|
||||||
|
|
||||||
|
//This else statement covers the one off case where we first hit 20%, but we still needed a positive ROC to get to 67% bonded ratio (i.e. we went from 19.99999% to 20%)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, true, inflationChange.GT(sdk.ZeroRat()), msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
//ABOVE 67% - Rate of change should be negative while the bond is above 67, and should stay negative until we reach inflation of 7%
|
||||||
|
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.LT(sdk.NewRat(20, 100)) && updatedInflation.GT(sdk.NewRat(7, 100)):
|
||||||
|
require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||||
|
|
||||||
|
//ABOVE 67% - Rate of change should be 0 while inflation continually stays at 7%.
|
||||||
|
case pool.BondedRatio().GT(sdk.NewRat(67, 100)) && updatedInflation.Equal(sdk.NewRat(7, 100)):
|
||||||
|
if previousInflation.Equal(sdk.NewRat(7, 100)) {
|
||||||
|
require.Equal(t, true, inflationChange.IsZero(), msg)
|
||||||
|
|
||||||
|
//This else statement covers the one off case where we first hit 7%, but we still needed a negative ROC to continue to get down to 67%. (i.e. we went from 7.00001% to 7%)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,13 +9,8 @@ import (
|
||||||
|
|
||||||
// Pool - dynamic parameters of the current state
|
// Pool - dynamic parameters of the current state
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
LooseTokens int64 `json:"loose_tokens"` // tokens not associated with any validator
|
LooseTokens sdk.Rat `json:"loose_tokens"` // tokens which are not bonded in a validator
|
||||||
UnbondedTokens int64 `json:"unbonded_tokens"` // reserve of unbonded tokens held with validators
|
BondedTokens sdk.Rat `json:"bonded_tokens"` // reserve of bonded tokens
|
||||||
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
|
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
|
Inflation sdk.Rat `json:"inflation"` // current annual inflation rate
|
||||||
|
|
||||||
|
@ -35,13 +30,8 @@ func (p Pool) Equal(p2 Pool) bool {
|
||||||
// initial pool for testing
|
// initial pool for testing
|
||||||
func InitialPool() Pool {
|
func InitialPool() Pool {
|
||||||
return Pool{
|
return Pool{
|
||||||
LooseTokens: 0,
|
LooseTokens: sdk.ZeroRat(),
|
||||||
BondedTokens: 0,
|
BondedTokens: sdk.ZeroRat(),
|
||||||
UnbondingTokens: 0,
|
|
||||||
UnbondedTokens: 0,
|
|
||||||
BondedShares: sdk.ZeroRat(),
|
|
||||||
UnbondingShares: sdk.ZeroRat(),
|
|
||||||
UnbondedShares: sdk.ZeroRat(),
|
|
||||||
InflationLastTime: 0,
|
InflationLastTime: 0,
|
||||||
Inflation: sdk.NewRat(7, 100),
|
Inflation: sdk.NewRat(7, 100),
|
||||||
DateLastCommissionReset: 0,
|
DateLastCommissionReset: 0,
|
||||||
|
@ -52,108 +42,78 @@ func InitialPool() Pool {
|
||||||
//____________________________________________________________________
|
//____________________________________________________________________
|
||||||
|
|
||||||
// Sum total of all staking tokens in the pool
|
// Sum total of all staking tokens in the pool
|
||||||
func (p Pool) TokenSupply() int64 {
|
func (p Pool) TokenSupply() sdk.Rat {
|
||||||
return p.LooseTokens + p.UnbondedTokens + p.UnbondingTokens + p.BondedTokens
|
return p.LooseTokens.Add(p.BondedTokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
//____________________________________________________________________
|
//____________________________________________________________________
|
||||||
|
|
||||||
// get the bond ratio of the global state
|
// get the bond ratio of the global state
|
||||||
func (p Pool) BondedRatio() sdk.Rat {
|
func (p Pool) BondedRatio() sdk.Rat {
|
||||||
if p.TokenSupply() > 0 {
|
supply := p.TokenSupply()
|
||||||
return sdk.NewRat(p.BondedTokens, p.TokenSupply())
|
if supply.GT(sdk.ZeroRat()) {
|
||||||
|
return p.BondedTokens.Quo(supply)
|
||||||
}
|
}
|
||||||
return sdk.ZeroRat()
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the exchange rate of bonded token per issued share
|
//_______________________________________________________________________
|
||||||
func (p Pool) BondedShareExRate() sdk.Rat {
|
|
||||||
if p.BondedShares.IsZero() {
|
func (p Pool) looseTokensToBonded(bondedTokens sdk.Rat) Pool {
|
||||||
return sdk.OneRat()
|
p.BondedTokens = p.BondedTokens.Add(bondedTokens)
|
||||||
|
p.LooseTokens = p.LooseTokens.Sub(bondedTokens)
|
||||||
|
if p.LooseTokens.LT(sdk.ZeroRat()) {
|
||||||
|
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
||||||
}
|
}
|
||||||
return sdk.NewRat(p.BondedTokens).Quo(p.BondedShares)
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the exchange rate of unbonding tokens held in validators per issued share
|
func (p Pool) bondedTokensToLoose(bondedTokens sdk.Rat) Pool {
|
||||||
func (p Pool) UnbondingShareExRate() sdk.Rat {
|
p.BondedTokens = p.BondedTokens.Sub(bondedTokens)
|
||||||
if p.UnbondingShares.IsZero() {
|
p.LooseTokens = p.LooseTokens.Add(bondedTokens)
|
||||||
return sdk.OneRat()
|
if p.BondedTokens.LT(sdk.ZeroRat()) {
|
||||||
|
panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p))
|
||||||
}
|
}
|
||||||
return sdk.NewRat(p.UnbondingTokens).Quo(p.UnbondingShares)
|
return p
|
||||||
}
|
|
||||||
|
|
||||||
// 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.UnbondedTokens).Quo(p.UnbondedShares)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//_______________________________________________________________________
|
//_______________________________________________________________________
|
||||||
|
// Inflation
|
||||||
|
|
||||||
func (p Pool) addTokensUnbonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
const precision = 100000000000 // increased to this precision for accuracy
|
||||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.UnbondedShareExRate()) // tokens * (shares/tokens)
|
var hrsPerYrRat = sdk.NewRat(8766) // as defined by a julian year of 365.25 days
|
||||||
p.UnbondedShares = p.UnbondedShares.Add(issuedSharesAmount)
|
|
||||||
p.UnbondedTokens += amount
|
// process provisions for an hour period
|
||||||
p.LooseTokens -= amount
|
func (p Pool) ProcessProvisions(params Params) Pool {
|
||||||
if p.LooseTokens < 0 {
|
p.Inflation = p.NextInflation(params)
|
||||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
provisions := p.Inflation.Mul(p.TokenSupply()).Quo(hrsPerYrRat)
|
||||||
}
|
|
||||||
return p, NewUnbondedShares(issuedSharesAmount)
|
// TODO add to the fees provisions
|
||||||
|
p.LooseTokens = p.LooseTokens.Add(provisions)
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Pool) removeSharesUnbonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
// get the next inflation rate for the hour
|
||||||
removedTokens = p.UnbondedShareExRate().Mul(shares).RoundInt64() // (tokens/shares) * shares
|
func (p Pool) NextInflation(params Params) (inflation sdk.Rat) {
|
||||||
p.UnbondedShares = p.UnbondedShares.Sub(shares)
|
|
||||||
p.UnbondedTokens -= removedTokens
|
|
||||||
p.LooseTokens += removedTokens
|
|
||||||
if p.UnbondedTokens < 0 {
|
|
||||||
panic(fmt.Sprintf("sanity check: unbonded tokens negative, pool: %v", p))
|
|
||||||
}
|
|
||||||
return p, removedTokens
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pool) addTokensUnbonding(amount int64) (p2 Pool, issuedShares PoolShares) {
|
// The target annual inflation rate is recalculated for each previsions cycle. The
|
||||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.UnbondingShareExRate()) // tokens * (shares/tokens)
|
// inflation is also subject to a rate change (positive or negative) depending on
|
||||||
p.UnbondingShares = p.UnbondingShares.Add(issuedSharesAmount)
|
// the distance from the desired ratio (67%). The maximum rate change possible is
|
||||||
p.UnbondingTokens += amount
|
// defined to be 13% per year, however the annual inflation is capped as between
|
||||||
p.LooseTokens -= amount
|
// 7% and 20%.
|
||||||
if p.LooseTokens < 0 {
|
|
||||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
|
||||||
}
|
|
||||||
return p, NewUnbondingShares(issuedSharesAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pool) removeSharesUnbonding(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
// (1 - bondedRatio/GoalBonded) * InflationRateChange
|
||||||
removedTokens = p.UnbondingShareExRate().Mul(shares).RoundInt64() // (tokens/shares) * shares
|
inflationRateChangePerYear := sdk.OneRat().Sub(p.BondedRatio().Quo(params.GoalBonded)).Mul(params.InflationRateChange)
|
||||||
p.UnbondingShares = p.UnbondingShares.Sub(shares)
|
inflationRateChange := inflationRateChangePerYear.Quo(hrsPerYrRat)
|
||||||
p.UnbondingTokens -= removedTokens
|
|
||||||
p.LooseTokens += removedTokens
|
|
||||||
if p.UnbondedTokens < 0 {
|
|
||||||
panic(fmt.Sprintf("sanity check: unbonding tokens negative, pool: %v", p))
|
|
||||||
}
|
|
||||||
return p, removedTokens
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares PoolShares) {
|
// increase the new annual inflation for this next cycle
|
||||||
issuedSharesAmount := sdk.NewRat(amount).Quo(p.BondedShareExRate()) // tokens * (shares/tokens)
|
inflation = p.Inflation.Add(inflationRateChange)
|
||||||
p.BondedShares = p.BondedShares.Add(issuedSharesAmount)
|
if inflation.GT(params.InflationMax) {
|
||||||
p.BondedTokens += amount
|
inflation = params.InflationMax
|
||||||
p.LooseTokens -= amount
|
}
|
||||||
if p.LooseTokens < 0 {
|
if inflation.LT(params.InflationMin) {
|
||||||
panic(fmt.Sprintf("sanity check: loose tokens negative, pool: %v", p))
|
inflation = params.InflationMin
|
||||||
}
|
}
|
||||||
return p, NewBondedShares(issuedSharesAmount)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
return inflation.Round(precision)
|
||||||
removedTokens = p.BondedShareExRate().Mul(shares).RoundInt64() // (tokens/shares) * shares
|
|
||||||
p.BondedShares = p.BondedShares.Sub(shares)
|
|
||||||
p.BondedTokens -= removedTokens
|
|
||||||
p.LooseTokens += removedTokens
|
|
||||||
if p.UnbondedTokens < 0 {
|
|
||||||
panic(fmt.Sprintf("sanity check: bonded tokens negative, pool: %v", p))
|
|
||||||
}
|
|
||||||
return p, removedTokens
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,131 +10,29 @@ import (
|
||||||
func TestPoolEqual(t *testing.T) {
|
func TestPoolEqual(t *testing.T) {
|
||||||
p1 := InitialPool()
|
p1 := InitialPool()
|
||||||
p2 := InitialPool()
|
p2 := InitialPool()
|
||||||
|
require.True(t, p1.Equal(p2))
|
||||||
ok := p1.Equal(p2)
|
p2.BondedTokens = sdk.NewRat(3)
|
||||||
require.True(t, ok)
|
require.False(t, p1.Equal(p2))
|
||||||
|
|
||||||
p2.BondedTokens = 3
|
|
||||||
p2.BondedShares = sdk.NewRat(10)
|
|
||||||
|
|
||||||
ok = p1.Equal(p2)
|
|
||||||
require.False(t, ok)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBondedRatio(t *testing.T) {
|
func TestAddBondedTokens(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 1
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
pool.BondedTokens = 2
|
pool.BondedTokens = sdk.NewRat(10)
|
||||||
|
|
||||||
// bonded pool / total supply
|
pool = pool.looseTokensToBonded(sdk.NewRat(10))
|
||||||
require.Equal(t, pool.BondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
|
||||||
|
|
||||||
// avoids divide-by-zero
|
require.True(sdk.RatEq(t, sdk.NewRat(20), pool.BondedTokens))
|
||||||
pool.LooseTokens = 0
|
require.True(sdk.RatEq(t, sdk.NewRat(0), pool.LooseTokens))
|
||||||
pool.BondedTokens = 0
|
|
||||||
require.Equal(t, pool.BondedRatio(), sdk.ZeroRat())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBondedShareExRate(t *testing.T) {
|
func TestRemoveBondedTokens(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.BondedTokens = 3
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
pool.BondedShares = sdk.NewRat(10)
|
pool.BondedTokens = sdk.NewRat(10)
|
||||||
|
|
||||||
// bonded pool / bonded shares
|
pool = pool.bondedTokensToLoose(sdk.NewRat(5))
|
||||||
require.Equal(t, pool.BondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
|
||||||
pool.BondedShares = sdk.ZeroRat()
|
|
||||||
|
|
||||||
// avoids divide-by-zero
|
require.True(sdk.RatEq(t, sdk.NewRat(5), pool.BondedTokens))
|
||||||
require.Equal(t, pool.BondedShareExRate(), sdk.OneRat())
|
require.True(sdk.RatEq(t, sdk.NewRat(15), pool.LooseTokens))
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnbondingShareExRate(t *testing.T) {
|
|
||||||
pool := InitialPool()
|
|
||||||
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) {
|
|
||||||
pool := InitialPool()
|
|
||||||
pool.UnbondedTokens = 3
|
|
||||||
pool.UnbondedShares = sdk.NewRat(10)
|
|
||||||
|
|
||||||
// unbonded pool / unbonded shares
|
|
||||||
require.Equal(t, pool.UnbondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
|
||||||
pool.UnbondedShares = sdk.ZeroRat()
|
|
||||||
|
|
||||||
// avoids divide-by-zero
|
|
||||||
require.Equal(t, pool.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddTokensBonded(t *testing.T) {
|
|
||||||
poolA := InitialPool()
|
|
||||||
poolA.LooseTokens = 10
|
|
||||||
require.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
poolB, sharesB := poolA.addTokensBonded(10)
|
|
||||||
require.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
// correct changes to bonded shares and bonded pool
|
|
||||||
require.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB.Amount))
|
|
||||||
require.Equal(t, poolB.BondedTokens, poolA.BondedTokens+10)
|
|
||||||
|
|
||||||
// same number of bonded shares / tokens when exchange rate is one
|
|
||||||
require.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveSharesBonded(t *testing.T) {
|
|
||||||
poolA := InitialPool()
|
|
||||||
poolA.LooseTokens = 10
|
|
||||||
require.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
|
|
||||||
require.Equal(t, poolB.BondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
// correct changes to bonded shares and bonded pool
|
|
||||||
require.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
|
|
||||||
require.Equal(t, poolB.BondedTokens, poolA.BondedTokens-tokensB)
|
|
||||||
|
|
||||||
// same number of bonded shares / tokens when exchange rate is one
|
|
||||||
require.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedTokens)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddTokensUnbonded(t *testing.T) {
|
|
||||||
poolA := InitialPool()
|
|
||||||
poolA.LooseTokens = 10
|
|
||||||
require.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
poolB, sharesB := poolA.addTokensUnbonded(10)
|
|
||||||
require.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
// correct changes to unbonded shares and unbonded pool
|
|
||||||
require.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB.Amount))
|
|
||||||
require.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens+10)
|
|
||||||
|
|
||||||
// same number of unbonded shares / tokens when exchange rate is one
|
|
||||||
require.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRemoveSharesUnbonded(t *testing.T) {
|
|
||||||
poolA := InitialPool()
|
|
||||||
poolA.UnbondedTokens = 10
|
|
||||||
poolA.UnbondedShares = sdk.NewRat(10)
|
|
||||||
require.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
|
|
||||||
require.Equal(t, poolB.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
|
|
||||||
// correct changes to unbonded shares and bonded pool
|
|
||||||
require.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
|
|
||||||
require.Equal(t, poolB.UnbondedTokens, poolA.UnbondedTokens-tokensB)
|
|
||||||
|
|
||||||
// same number of unbonded shares / tokens when exchange rate is one
|
|
||||||
require.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedTokens)))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,149 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PoolShares reflects the shares of a validator in a pool.
|
|
||||||
type PoolShares struct {
|
|
||||||
Status sdk.BondStatus `json:"status"`
|
|
||||||
Amount sdk.Rat `json:"amount"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal returns a boolean determining of two PoolShares are identical.
|
|
||||||
func (s PoolShares) Equal(s2 PoolShares) bool {
|
|
||||||
return s.Status == s2.Status &&
|
|
||||||
s.Amount.Equal(s2.Amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnbondedShares returns a new PoolShares with a specified unbonded amount.
|
|
||||||
func NewUnbondedShares(amount sdk.Rat) PoolShares {
|
|
||||||
return PoolShares{
|
|
||||||
Status: sdk.Unbonded,
|
|
||||||
Amount: amount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUnbondingShares returns a new PoolShares with a specified unbonding
|
|
||||||
// amount.
|
|
||||||
func NewUnbondingShares(amount sdk.Rat) PoolShares {
|
|
||||||
return PoolShares{
|
|
||||||
Status: sdk.Unbonding,
|
|
||||||
Amount: amount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBondedShares returns a new PoolSahres with a specified bonding amount.
|
|
||||||
func NewBondedShares(amount sdk.Rat) PoolShares {
|
|
||||||
return PoolShares{
|
|
||||||
Status: sdk.Bonded,
|
|
||||||
Amount: amount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unbonded returns the amount of unbonded shares.
|
|
||||||
func (s PoolShares) Unbonded() sdk.Rat {
|
|
||||||
if s.Status == sdk.Unbonded {
|
|
||||||
return s.Amount
|
|
||||||
}
|
|
||||||
return sdk.ZeroRat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unbonding returns the amount of unbonding shares.
|
|
||||||
func (s PoolShares) Unbonding() sdk.Rat {
|
|
||||||
if s.Status == sdk.Unbonding {
|
|
||||||
return s.Amount
|
|
||||||
}
|
|
||||||
return sdk.ZeroRat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bonded returns amount of bonded shares.
|
|
||||||
func (s PoolShares) Bonded() sdk.Rat {
|
|
||||||
if s.Status == sdk.Bonded {
|
|
||||||
return s.Amount
|
|
||||||
}
|
|
||||||
return sdk.ZeroRat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToUnbonded returns the equivalent amount of pool shares if the shares were
|
|
||||||
// unbonded.
|
|
||||||
func (s PoolShares) ToUnbonded(p Pool) PoolShares {
|
|
||||||
var amount sdk.Rat
|
|
||||||
|
|
||||||
switch s.Status {
|
|
||||||
case sdk.Bonded:
|
|
||||||
// (tok/bondedshr)/(tok/unbondedshr) = unbondedshr/bondedshr
|
|
||||||
exRate := p.BondedShareExRate().Quo(p.UnbondedShareExRate())
|
|
||||||
// bondedshr*unbondedshr/bondedshr = unbondedshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
case sdk.Unbonding:
|
|
||||||
// (tok/unbondingshr)/(tok/unbondedshr) = unbondedshr/unbondingshr
|
|
||||||
exRate := p.UnbondingShareExRate().Quo(p.UnbondedShareExRate())
|
|
||||||
// unbondingshr*unbondedshr/unbondingshr = unbondedshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
case sdk.Unbonded:
|
|
||||||
amount = s.Amount
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewUnbondedShares(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToUnbonding returns the equivalent amount of pool shares if the shares were
|
|
||||||
// unbonding.
|
|
||||||
func (s PoolShares) ToUnbonding(p Pool) PoolShares {
|
|
||||||
var amount sdk.Rat
|
|
||||||
|
|
||||||
switch s.Status {
|
|
||||||
case sdk.Bonded:
|
|
||||||
// (tok/bondedshr)/(tok/unbondingshr) = unbondingshr/bondedshr
|
|
||||||
exRate := p.BondedShareExRate().Quo(p.UnbondingShareExRate())
|
|
||||||
// bondedshr*unbondingshr/bondedshr = unbondingshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
case sdk.Unbonding:
|
|
||||||
amount = s.Amount
|
|
||||||
case sdk.Unbonded:
|
|
||||||
// (tok/unbondedshr)/(tok/unbondingshr) = unbondingshr/unbondedshr
|
|
||||||
exRate := p.UnbondedShareExRate().Quo(p.UnbondingShareExRate())
|
|
||||||
// unbondedshr*unbondingshr/unbondedshr = unbondingshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewUnbondingShares(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBonded the equivalent amount of pool 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:
|
|
||||||
// (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
|
||||||
exRate := p.UnbondingShareExRate().Quo(p.BondedShareExRate())
|
|
||||||
// ubshr*bshr/ubshr = bshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
case sdk.Unbonded:
|
|
||||||
// (tok/ubshr)/(tok/bshr) = bshr/ubshr
|
|
||||||
exRate := p.UnbondedShareExRate().Quo(p.BondedShareExRate())
|
|
||||||
// ubshr*bshr/ubshr = bshr
|
|
||||||
amount = s.Amount.Mul(exRate)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewUnbondedShares(amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tokens returns the equivalent amount of tokens contained by the pool shares
|
|
||||||
// for a given pool.
|
|
||||||
func (s PoolShares) Tokens(p Pool) sdk.Rat {
|
|
||||||
switch s.Status {
|
|
||||||
case sdk.Bonded:
|
|
||||||
return p.BondedShareExRate().Mul(s.Amount)
|
|
||||||
case sdk.Unbonding:
|
|
||||||
return p.UnbondingShareExRate().Mul(s.Amount)
|
|
||||||
case sdk.Unbonded:
|
|
||||||
return p.UnbondedShareExRate().Mul(s.Amount)
|
|
||||||
default:
|
|
||||||
panic("unknown share kind")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPoolSharesTokens(t *testing.T) {
|
|
||||||
pool := InitialPool()
|
|
||||||
pool.LooseTokens = 10
|
|
||||||
|
|
||||||
val := Validator{
|
|
||||||
Owner: addr1,
|
|
||||||
PubKey: pk1,
|
|
||||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
|
||||||
DelegatorShares: sdk.NewRat(100),
|
|
||||||
}
|
|
||||||
|
|
||||||
pool.BondedTokens = val.PoolShares.Bonded().RoundInt64()
|
|
||||||
pool.BondedShares = val.PoolShares.Bonded()
|
|
||||||
|
|
||||||
poolShares := NewBondedShares(sdk.NewRat(50))
|
|
||||||
tokens := poolShares.Tokens(pool)
|
|
||||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
|
||||||
|
|
||||||
poolShares = NewUnbondingShares(sdk.NewRat(50))
|
|
||||||
tokens = poolShares.Tokens(pool)
|
|
||||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
|
||||||
|
|
||||||
poolShares = NewUnbondedShares(sdk.NewRat(50))
|
|
||||||
tokens = poolShares.Tokens(pool)
|
|
||||||
require.Equal(t, int64(50), tokens.RoundInt64())
|
|
||||||
}
|
|
|
@ -25,62 +25,59 @@ var (
|
||||||
// Operation reflects any operation that transforms staking state. It takes in
|
// Operation reflects any operation that transforms staking state. It takes in
|
||||||
// a RNG instance, pool, validator and returns an updated pool, updated
|
// a RNG instance, pool, validator and returns an updated pool, updated
|
||||||
// validator, delta tokens, and descriptive message.
|
// validator, delta tokens, and descriptive message.
|
||||||
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, int64, string)
|
type Operation func(r *rand.Rand, pool Pool, c Validator) (Pool, Validator, sdk.Rat, string)
|
||||||
|
|
||||||
// OpBondOrUnbond implements an operation that bonds or unbonds a validator
|
// OpBondOrUnbond implements an operation that bonds or unbonds a validator
|
||||||
// depending on current status.
|
// depending on current status.
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func OpBondOrUnbond(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
// TODO split up into multiple operations
|
||||||
|
func OpBondOrUnbond(r *rand.Rand, pool Pool, validator Validator) (Pool, Validator, sdk.Rat, string) {
|
||||||
var (
|
var (
|
||||||
msg string
|
msg string
|
||||||
newStatus sdk.BondStatus
|
newStatus sdk.BondStatus
|
||||||
)
|
)
|
||||||
|
|
||||||
if val.Status() == sdk.Bonded {
|
if validator.Status == sdk.Bonded {
|
||||||
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %#v", validator)
|
||||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
|
||||||
newStatus = sdk.Unbonded
|
newStatus = sdk.Unbonded
|
||||||
|
|
||||||
} else if val.Status() == sdk.Unbonded {
|
} else if validator.Status == sdk.Unbonded {
|
||||||
msg = fmt.Sprintf("sdk.Bonded previously unbonded validator %s (poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
msg = fmt.Sprintf("sdk.Bonded previously bonded validator %#v", validator)
|
||||||
val.Owner, val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
|
||||||
newStatus = sdk.Bonded
|
newStatus = sdk.Bonded
|
||||||
}
|
}
|
||||||
|
|
||||||
val, pool = val.UpdateStatus(pool, newStatus)
|
validator, pool = validator.UpdateStatus(pool, newStatus)
|
||||||
return pool, val, 0, msg
|
return pool, validator, sdk.ZeroRat(), msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpAddTokens implements an operation that adds a random number of tokens to a
|
// OpAddTokens implements an operation that adds a random number of tokens to a
|
||||||
// validator.
|
// validator.
|
||||||
func OpAddTokens(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
func OpAddTokens(r *rand.Rand, pool Pool, validator Validator) (Pool, Validator, sdk.Rat, string) {
|
||||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
msg := fmt.Sprintf("validator %#v", validator)
|
||||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
|
||||||
|
|
||||||
tokens := int64(r.Int31n(1000))
|
tokens := int64(r.Int31n(1000))
|
||||||
val, pool, _ = val.AddTokensFromDel(pool, tokens)
|
validator, pool, _ = validator.AddTokensFromDel(pool, tokens)
|
||||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
|
|
||||||
// Tokens are removed so for accounting must be negative
|
// Tokens are removed so for accounting must be negative
|
||||||
return pool, val, -1 * tokens, msg
|
return pool, validator, sdk.NewRat(-1 * tokens), msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpRemoveShares implements an operation that removes a random number of
|
// OpRemoveShares implements an operation that removes a random number of
|
||||||
// shares from a validator.
|
// delegatorshares from a validator.
|
||||||
func OpRemoveShares(r *rand.Rand, pool Pool, val Validator) (Pool, Validator, int64, string) {
|
func OpRemoveShares(r *rand.Rand, pool Pool, validator Validator) (Pool, Validator, sdk.Rat, string) {
|
||||||
var shares sdk.Rat
|
var shares sdk.Rat
|
||||||
for {
|
for {
|
||||||
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||||
if shares.LT(val.DelegatorShares) {
|
if shares.LT(validator.DelegatorShares) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := fmt.Sprintf("Removed %v shares from validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
msg := fmt.Sprintf("Removed %v shares from validator %#v", shares, validator)
|
||||||
shares, val.Owner, val.Status(), val.PoolShares, val.DelegatorShares, val.DelegatorShareExRate(pool))
|
|
||||||
|
|
||||||
val, pool, tokens := val.RemoveDelShares(pool, shares)
|
validator, pool, tokens := validator.RemoveDelShares(pool, shares)
|
||||||
return pool, val, tokens, msg
|
return pool, validator, tokens, msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomOperation returns a random staking operation.
|
// RandomOperation returns a random staking operation.
|
||||||
|
@ -100,66 +97,48 @@ func RandomOperation(r *rand.Rand) Operation {
|
||||||
// AssertInvariants ensures invariants that should always be true are true.
|
// AssertInvariants ensures invariants that should always be true are true.
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func AssertInvariants(t *testing.T, msg string,
|
func AssertInvariants(t *testing.T, msg string,
|
||||||
pOrig Pool, cOrig []Validator, pMod Pool, vMods []Validator, tokens int64) {
|
pOrig Pool, cOrig []Validator, pMod Pool, vMods []Validator) {
|
||||||
|
|
||||||
// total tokens conserved
|
// total tokens conserved
|
||||||
require.Equal(t,
|
require.True(t,
|
||||||
pOrig.UnbondedTokens+pOrig.BondedTokens,
|
pOrig.LooseTokens.Add(pOrig.BondedTokens).Equal(
|
||||||
pMod.UnbondedTokens+pMod.BondedTokens+tokens,
|
pMod.LooseTokens.Add(pMod.BondedTokens)),
|
||||||
"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",
|
"Tokens not conserved - msg: %v\n, pOrig.BondedTokens: %v, pOrig.LooseTokens: %v, pMod.BondedTokens: %v, pMod.LooseTokens: %v",
|
||||||
msg,
|
msg,
|
||||||
pOrig.BondedShares, pOrig.UnbondedShares,
|
pOrig.BondedTokens, pOrig.LooseTokens,
|
||||||
pMod.BondedShares, pMod.UnbondedShares,
|
pMod.BondedTokens, pMod.LooseTokens)
|
||||||
pOrig.UnbondedTokens, pOrig.BondedTokens,
|
|
||||||
pMod.UnbondedTokens, pMod.BondedTokens, tokens)
|
|
||||||
|
|
||||||
// Nonnegative bonded shares
|
// Nonnegative bonded tokens
|
||||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
|
require.False(t, pMod.BondedTokens.LT(sdk.ZeroRat()),
|
||||||
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
"Negative bonded shares - msg: %v\npOrig: %v\npMod: %v\n",
|
||||||
msg, pOrig, pMod, tokens)
|
msg, pOrig, pMod)
|
||||||
|
|
||||||
// Nonnegative unbonded shares
|
// Nonnegative loose tokens
|
||||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
|
require.False(t, pMod.LooseTokens.LT(sdk.ZeroRat()),
|
||||||
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\ntokens: %v\n",
|
"Negative unbonded shares - msg: %v\npOrig: %v\npMod: %v\n",
|
||||||
msg, pOrig, pMod, tokens)
|
msg, pOrig, pMod)
|
||||||
|
|
||||||
// Nonnegative bonded ex rate
|
|
||||||
require.False(t, pMod.BondedShareExRate().LT(sdk.ZeroRat()),
|
|
||||||
"Applying operation \"%s\" resulted in negative BondedShareExRate: %d",
|
|
||||||
msg, pMod.BondedShareExRate().RoundInt64())
|
|
||||||
|
|
||||||
// Nonnegative unbonded ex rate
|
|
||||||
require.False(t, pMod.UnbondedShareExRate().LT(sdk.ZeroRat()),
|
|
||||||
"Applying operation \"%s\" resulted in negative UnbondedShareExRate: %d",
|
|
||||||
msg, pMod.UnbondedShareExRate().RoundInt64())
|
|
||||||
|
|
||||||
for _, vMod := range vMods {
|
for _, vMod := range vMods {
|
||||||
// Nonnegative ex rate
|
// Nonnegative ex rate
|
||||||
require.False(t, vMod.DelegatorShareExRate(pMod).LT(sdk.ZeroRat()),
|
require.False(t, vMod.DelegatorShareExRate().LT(sdk.ZeroRat()),
|
||||||
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Owner: %s)",
|
||||||
msg,
|
msg,
|
||||||
vMod.DelegatorShareExRate(pMod),
|
vMod.DelegatorShareExRate(),
|
||||||
vMod.Owner,
|
vMod.Owner,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Nonnegative poolShares
|
// Nonnegative poolShares
|
||||||
require.False(t, vMod.PoolShares.Bonded().LT(sdk.ZeroRat()),
|
require.False(t, vMod.BondedTokens().LT(sdk.ZeroRat()),
|
||||||
"Applying operation \"%s\" resulted in negative validator.PoolShares.Bonded(): %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Owner: %s)",
|
"Applying operation \"%s\" resulted in negative validator.BondedTokens(): %#v",
|
||||||
msg,
|
msg,
|
||||||
vMod.PoolShares.Bonded(),
|
vMod,
|
||||||
vMod.DelegatorShares,
|
|
||||||
vMod.DelegatorShareExRate(pMod),
|
|
||||||
vMod.Owner,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Nonnegative delShares
|
// Nonnegative delShares
|
||||||
require.False(t, vMod.DelegatorShares.LT(sdk.ZeroRat()),
|
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)",
|
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %#v",
|
||||||
msg,
|
msg,
|
||||||
vMod.DelegatorShares,
|
vMod,
|
||||||
vMod.PoolShares.Bonded(),
|
|
||||||
vMod.DelegatorShareExRate(pMod),
|
|
||||||
vMod.Owner,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,40 +148,40 @@ func AssertInvariants(t *testing.T, msg string,
|
||||||
// randomValidator generates a random validator.
|
// randomValidator generates a random validator.
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func randomValidator(r *rand.Rand, i int) Validator {
|
func randomValidator(r *rand.Rand, i int) Validator {
|
||||||
poolSharesAmt := sdk.NewRat(int64(r.Int31n(10000)))
|
|
||||||
|
tokens := sdk.NewRat(int64(r.Int31n(10000)))
|
||||||
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
delShares := sdk.NewRat(int64(r.Int31n(10000)))
|
||||||
|
|
||||||
var pShares PoolShares
|
// TODO add more options here
|
||||||
|
status := sdk.Bonded
|
||||||
if r.Float64() < float64(0.5) {
|
if r.Float64() > float64(0.5) {
|
||||||
pShares = NewBondedShares(poolSharesAmt)
|
status = sdk.Unbonded
|
||||||
} else {
|
|
||||||
pShares = NewUnbondedShares(poolSharesAmt)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Validator{
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
Owner: addr1,
|
validator.Status = status
|
||||||
PubKey: pk1,
|
validator.Tokens = tokens
|
||||||
PoolShares: pShares,
|
validator.DelegatorShares = delShares
|
||||||
DelegatorShares: delShares,
|
|
||||||
}
|
return validator
|
||||||
}
|
}
|
||||||
|
|
||||||
// RandomSetup generates a random staking state.
|
// RandomSetup generates a random staking state.
|
||||||
func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
func RandomSetup(r *rand.Rand, numValidators int) (Pool, []Validator) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 100000
|
pool.LooseTokens = sdk.NewRat(100000)
|
||||||
|
|
||||||
validators := make([]Validator, numValidators)
|
validators := make([]Validator, numValidators)
|
||||||
for i := 0; i < numValidators; i++ {
|
for i := 0; i < numValidators; i++ {
|
||||||
validator := randomValidator(r, i)
|
validator := randomValidator(r, i)
|
||||||
|
|
||||||
if validator.Status() == sdk.Bonded {
|
switch validator.Status {
|
||||||
pool.BondedShares = pool.BondedShares.Add(validator.PoolShares.Bonded())
|
case sdk.Bonded:
|
||||||
pool.BondedTokens += validator.PoolShares.Bonded().RoundInt64()
|
pool.BondedTokens = pool.BondedTokens.Add(validator.Tokens)
|
||||||
} else if validator.Status() == sdk.Unbonded {
|
case sdk.Unbonded, sdk.Unbonding:
|
||||||
pool.UnbondedShares = pool.UnbondedShares.Add(validator.PoolShares.Unbonded())
|
pool.LooseTokens = pool.LooseTokens.Add(validator.Tokens)
|
||||||
pool.UnbondedTokens += validator.PoolShares.Unbonded().RoundInt64()
|
default:
|
||||||
|
panic("improper use of RandomSetup")
|
||||||
}
|
}
|
||||||
|
|
||||||
validators[i] = validator
|
validators[i] = validator
|
||||||
|
|
|
@ -27,8 +27,9 @@ type Validator struct {
|
||||||
PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator
|
PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator
|
||||||
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
||||||
|
|
||||||
PoolShares PoolShares `json:"pool_shares"` // total shares for tokens held in the pool
|
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||||
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
|
Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||||
|
DelegatorShares sdk.Rat `json:"delegator_shares"` // total shares issued to a validator's delegators
|
||||||
|
|
||||||
Description Description `json:"description"` // description terms for the validator
|
Description Description `json:"description"` // description terms for the validator
|
||||||
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
|
||||||
|
@ -41,7 +42,7 @@ type Validator struct {
|
||||||
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
|
||||||
|
|
||||||
// fee related
|
// fee related
|
||||||
PrevBondedShares sdk.Rat `json:"prev_bonded_shares"` // total shares of a global hold pools
|
LastBondedTokens sdk.Rat `json:"prev_bonded_tokens"` // Previous bonded tokens held
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewValidator - initialize a new validator
|
// NewValidator - initialize a new validator
|
||||||
|
@ -50,7 +51,8 @@ func NewValidator(owner sdk.AccAddress, pubKey crypto.PubKey, description Descri
|
||||||
Owner: owner,
|
Owner: owner,
|
||||||
PubKey: pubKey,
|
PubKey: pubKey,
|
||||||
Revoked: false,
|
Revoked: false,
|
||||||
PoolShares: NewUnbondedShares(sdk.ZeroRat()),
|
Status: sdk.Unbonded,
|
||||||
|
Tokens: sdk.ZeroRat(),
|
||||||
DelegatorShares: sdk.ZeroRat(),
|
DelegatorShares: sdk.ZeroRat(),
|
||||||
Description: description,
|
Description: description,
|
||||||
BondHeight: int64(0),
|
BondHeight: int64(0),
|
||||||
|
@ -60,7 +62,7 @@ func NewValidator(owner sdk.AccAddress, pubKey crypto.PubKey, description Descri
|
||||||
CommissionMax: sdk.ZeroRat(),
|
CommissionMax: sdk.ZeroRat(),
|
||||||
CommissionChangeRate: sdk.ZeroRat(),
|
CommissionChangeRate: sdk.ZeroRat(),
|
||||||
CommissionChangeToday: sdk.ZeroRat(),
|
CommissionChangeToday: sdk.ZeroRat(),
|
||||||
PrevBondedShares: sdk.ZeroRat(),
|
LastBondedTokens: sdk.ZeroRat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +70,8 @@ func NewValidator(owner sdk.AccAddress, pubKey crypto.PubKey, description Descri
|
||||||
type validatorValue struct {
|
type validatorValue struct {
|
||||||
PubKey crypto.PubKey
|
PubKey crypto.PubKey
|
||||||
Revoked bool
|
Revoked bool
|
||||||
PoolShares PoolShares
|
Status sdk.BondStatus
|
||||||
|
Tokens sdk.Rat
|
||||||
DelegatorShares sdk.Rat
|
DelegatorShares sdk.Rat
|
||||||
Description Description
|
Description Description
|
||||||
BondHeight int64
|
BondHeight int64
|
||||||
|
@ -78,7 +81,7 @@ type validatorValue struct {
|
||||||
CommissionMax sdk.Rat
|
CommissionMax sdk.Rat
|
||||||
CommissionChangeRate sdk.Rat
|
CommissionChangeRate sdk.Rat
|
||||||
CommissionChangeToday sdk.Rat
|
CommissionChangeToday sdk.Rat
|
||||||
PrevBondedShares sdk.Rat
|
LastBondedTokens sdk.Rat
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the redelegation without fields contained within the key for the store
|
// return the redelegation without fields contained within the key for the store
|
||||||
|
@ -86,7 +89,8 @@ func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte {
|
||||||
val := validatorValue{
|
val := validatorValue{
|
||||||
PubKey: validator.PubKey,
|
PubKey: validator.PubKey,
|
||||||
Revoked: validator.Revoked,
|
Revoked: validator.Revoked,
|
||||||
PoolShares: validator.PoolShares,
|
Status: validator.Status,
|
||||||
|
Tokens: validator.Tokens,
|
||||||
DelegatorShares: validator.DelegatorShares,
|
DelegatorShares: validator.DelegatorShares,
|
||||||
Description: validator.Description,
|
Description: validator.Description,
|
||||||
BondHeight: validator.BondHeight,
|
BondHeight: validator.BondHeight,
|
||||||
|
@ -96,7 +100,7 @@ func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte {
|
||||||
CommissionMax: validator.CommissionMax,
|
CommissionMax: validator.CommissionMax,
|
||||||
CommissionChangeRate: validator.CommissionChangeRate,
|
CommissionChangeRate: validator.CommissionChangeRate,
|
||||||
CommissionChangeToday: validator.CommissionChangeToday,
|
CommissionChangeToday: validator.CommissionChangeToday,
|
||||||
PrevBondedShares: validator.PrevBondedShares,
|
LastBondedTokens: validator.LastBondedTokens,
|
||||||
}
|
}
|
||||||
return cdc.MustMarshalBinary(val)
|
return cdc.MustMarshalBinary(val)
|
||||||
}
|
}
|
||||||
|
@ -128,7 +132,8 @@ func UnmarshalValidator(cdc *wire.Codec, ownerAddr, value []byte) (validator Val
|
||||||
Owner: ownerAddr,
|
Owner: ownerAddr,
|
||||||
PubKey: storeValue.PubKey,
|
PubKey: storeValue.PubKey,
|
||||||
Revoked: storeValue.Revoked,
|
Revoked: storeValue.Revoked,
|
||||||
PoolShares: storeValue.PoolShares,
|
Tokens: storeValue.Tokens,
|
||||||
|
Status: storeValue.Status,
|
||||||
DelegatorShares: storeValue.DelegatorShares,
|
DelegatorShares: storeValue.DelegatorShares,
|
||||||
Description: storeValue.Description,
|
Description: storeValue.Description,
|
||||||
BondHeight: storeValue.BondHeight,
|
BondHeight: storeValue.BondHeight,
|
||||||
|
@ -138,15 +143,75 @@ func UnmarshalValidator(cdc *wire.Codec, ownerAddr, value []byte) (validator Val
|
||||||
CommissionMax: storeValue.CommissionMax,
|
CommissionMax: storeValue.CommissionMax,
|
||||||
CommissionChangeRate: storeValue.CommissionChangeRate,
|
CommissionChangeRate: storeValue.CommissionChangeRate,
|
||||||
CommissionChangeToday: storeValue.CommissionChangeToday,
|
CommissionChangeToday: storeValue.CommissionChangeToday,
|
||||||
PrevBondedShares: storeValue.PrevBondedShares,
|
LastBondedTokens: storeValue.LastBondedTokens,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________
|
||||||
|
|
||||||
|
// validator struct for bech output
|
||||||
|
type BechValidator struct {
|
||||||
|
Owner sdk.AccAddress `json:"owner"` // in bech32
|
||||||
|
PubKey string `json:"pub_key"` // in bech32
|
||||||
|
Revoked bool `json:"revoked"` // has the validator been revoked from bonded status?
|
||||||
|
|
||||||
|
Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded)
|
||||||
|
Tokens sdk.Rat `json:"tokens"` // delegated tokens (incl. self-delegation)
|
||||||
|
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
|
||||||
|
LastBondedTokens sdk.Rat `json:"prev_bonded_shares"` // last bonded token amount
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bech validator from the the regular validator
|
||||||
|
func (v Validator) Bech32Validator() (BechValidator, error) {
|
||||||
|
bechValPubkey, err := sdk.Bech32ifyValPub(v.PubKey)
|
||||||
|
if err != nil {
|
||||||
|
return BechValidator{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return BechValidator{
|
||||||
|
Owner: v.Owner,
|
||||||
|
PubKey: bechValPubkey,
|
||||||
|
Revoked: v.Revoked,
|
||||||
|
|
||||||
|
Status: v.Status,
|
||||||
|
Tokens: v.Tokens,
|
||||||
|
DelegatorShares: v.DelegatorShares,
|
||||||
|
|
||||||
|
Description: v.Description,
|
||||||
|
BondHeight: v.BondHeight,
|
||||||
|
BondIntraTxCounter: v.BondIntraTxCounter,
|
||||||
|
ProposerRewardPool: v.ProposerRewardPool,
|
||||||
|
|
||||||
|
Commission: v.Commission,
|
||||||
|
CommissionMax: v.CommissionMax,
|
||||||
|
CommissionChangeRate: v.CommissionChangeRate,
|
||||||
|
CommissionChangeToday: v.CommissionChangeToday,
|
||||||
|
|
||||||
|
LastBondedTokens: v.LastBondedTokens,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________
|
||||||
|
|
||||||
// only the vitals - does not check bond height of IntraTxCounter
|
// only the vitals - does not check bond height of IntraTxCounter
|
||||||
|
// nolint gocyclo - why dis fail?
|
||||||
func (v Validator) Equal(c2 Validator) bool {
|
func (v Validator) Equal(c2 Validator) bool {
|
||||||
return v.PubKey.Equals(c2.PubKey) &&
|
return v.PubKey.Equals(c2.PubKey) &&
|
||||||
bytes.Equal(v.Owner, c2.Owner) &&
|
bytes.Equal(v.Owner, c2.Owner) &&
|
||||||
v.PoolShares.Equal(c2.PoolShares) &&
|
v.Status.Equal(c2.Status) &&
|
||||||
|
v.Tokens.Equal(c2.Tokens) &&
|
||||||
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
v.DelegatorShares.Equal(c2.DelegatorShares) &&
|
||||||
v.Description == c2.Description &&
|
v.Description == c2.Description &&
|
||||||
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
|
||||||
|
@ -154,7 +219,7 @@ func (v Validator) Equal(c2 Validator) bool {
|
||||||
v.CommissionMax.Equal(c2.CommissionMax) &&
|
v.CommissionMax.Equal(c2.CommissionMax) &&
|
||||||
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
|
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
|
||||||
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
|
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) &&
|
||||||
v.PrevBondedShares.Equal(c2.PrevBondedShares)
|
v.LastBondedTokens.Equal(c2.LastBondedTokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Description - description fields for a validator
|
// Description - description fields for a validator
|
||||||
|
@ -221,7 +286,7 @@ func (d Description) EnsureLength() (Description, sdk.Error) {
|
||||||
func (v Validator) ABCIValidator() abci.Validator {
|
func (v Validator) ABCIValidator() abci.Validator {
|
||||||
return abci.Validator{
|
return abci.Validator{
|
||||||
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
PubKey: tmtypes.TM2PB.PubKey(v.PubKey),
|
||||||
Power: v.PoolShares.Bonded().RoundInt64(),
|
Power: v.BondedTokens().RoundInt64(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,145 +299,99 @@ func (v Validator) ABCIValidatorZero() abci.Validator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns the validator's bond status inferred from the pool shares.
|
// UpdateStatus updates the location of the shares within a validator
|
||||||
func (v Validator) Status() sdk.BondStatus {
|
// to reflect the new status
|
||||||
return v.PoolShares.Status
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateStatus updates the location of the shares within a validator if it's
|
|
||||||
// bond status has changed.
|
|
||||||
func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) {
|
func (v Validator) UpdateStatus(pool Pool, NewStatus sdk.BondStatus) (Validator, Pool) {
|
||||||
var tokens int64
|
|
||||||
|
|
||||||
switch v.Status() {
|
switch v.Status {
|
||||||
case sdk.Unbonded:
|
case sdk.Unbonded:
|
||||||
if NewStatus == sdk.Unbonded {
|
|
||||||
return v, pool
|
|
||||||
}
|
|
||||||
pool, tokens = pool.removeSharesUnbonded(v.PoolShares.Amount)
|
|
||||||
|
|
||||||
|
switch NewStatus {
|
||||||
|
case sdk.Unbonded:
|
||||||
|
return v, pool
|
||||||
|
case sdk.Bonded:
|
||||||
|
pool = pool.looseTokensToBonded(v.Tokens)
|
||||||
|
}
|
||||||
case sdk.Unbonding:
|
case sdk.Unbonding:
|
||||||
if NewStatus == sdk.Unbonding {
|
|
||||||
return v, pool
|
|
||||||
}
|
|
||||||
pool, tokens = pool.removeSharesUnbonding(v.PoolShares.Amount)
|
|
||||||
|
|
||||||
case sdk.Bonded:
|
switch NewStatus {
|
||||||
if NewStatus == sdk.Bonded {
|
case sdk.Unbonding:
|
||||||
// Return if nothing needs switching
|
|
||||||
return v, pool
|
return v, pool
|
||||||
|
case sdk.Bonded:
|
||||||
|
pool = pool.looseTokensToBonded(v.Tokens)
|
||||||
|
}
|
||||||
|
case sdk.Bonded:
|
||||||
|
|
||||||
|
switch NewStatus {
|
||||||
|
case sdk.Bonded:
|
||||||
|
return v, pool
|
||||||
|
default:
|
||||||
|
pool = pool.bondedTokensToLoose(v.Tokens)
|
||||||
}
|
}
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v.Status = NewStatus
|
||||||
return v, pool
|
return v, pool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePoolShares removes pool shares from a validator. It returns
|
// removes tokens from a validator
|
||||||
// corresponding tokens, which could be burned (e.g. when slashing a validator)
|
func (v Validator) RemoveTokens(pool Pool, tokens sdk.Rat) (Validator, Pool) {
|
||||||
// or redistributed elsewhere.
|
if v.Status == sdk.Bonded {
|
||||||
func (v Validator) RemovePoolShares(pool Pool, poolShares sdk.Rat) (Validator, Pool, int64) {
|
pool = pool.bondedTokensToLoose(tokens)
|
||||||
var tokens int64
|
|
||||||
|
|
||||||
switch v.Status() {
|
|
||||||
case sdk.Unbonded:
|
|
||||||
pool, tokens = pool.removeSharesUnbonded(poolShares)
|
|
||||||
case sdk.Unbonding:
|
|
||||||
pool, tokens = pool.removeSharesUnbonding(poolShares)
|
|
||||||
case sdk.Bonded:
|
|
||||||
pool, tokens = pool.removeSharesBonded(poolShares)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v.PoolShares.Amount = v.PoolShares.Amount.Sub(poolShares)
|
v.Tokens = v.Tokens.Sub(tokens)
|
||||||
return v, pool, tokens
|
return v, pool
|
||||||
}
|
|
||||||
|
|
||||||
// EquivalentBondedShares ...
|
|
||||||
//
|
|
||||||
// TODO: Remove should only be tokens 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//_________________________________________________________________________________________________________
|
//_________________________________________________________________________________________________________
|
||||||
|
|
||||||
// AddTokensFromDel adds tokens to a validator
|
// AddTokensFromDel adds tokens to a validator
|
||||||
func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Rat) {
|
func (v Validator) AddTokensFromDel(pool Pool, amount int64) (Validator, Pool, sdk.Rat) {
|
||||||
var (
|
|
||||||
poolShares PoolShares
|
|
||||||
equivalentBondedShares sdk.Rat
|
|
||||||
)
|
|
||||||
|
|
||||||
// bondedShare/delegatedShare
|
// bondedShare/delegatedShare
|
||||||
exRate := v.DelegatorShareExRate(pool)
|
exRate := v.DelegatorShareExRate()
|
||||||
|
amountRat := sdk.NewRat(amount)
|
||||||
|
|
||||||
switch v.Status() {
|
if v.Status == sdk.Bonded {
|
||||||
case sdk.Unbonded:
|
pool = pool.looseTokensToBonded(amountRat)
|
||||||
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
|
|
||||||
|
|
||||||
// bondedShare/(bondedShare/delegatedShare) = delegatedShare
|
v.Tokens = v.Tokens.Add(amountRat)
|
||||||
issuedDelegatorShares := equivalentBondedShares.Quo(exRate)
|
issuedShares := amountRat.Quo(exRate)
|
||||||
v.DelegatorShares = v.DelegatorShares.Add(issuedDelegatorShares)
|
v.DelegatorShares = v.DelegatorShares.Add(issuedShares)
|
||||||
|
|
||||||
return v, pool, issuedDelegatorShares
|
return v, pool, issuedShares
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveDelShares removes delegator shares from a validator.
|
// RemoveDelShares removes delegator shares from a validator.
|
||||||
//
|
func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Pool, sdk.Rat) {
|
||||||
// NOTE: This function assumes the shares have already been updated for the
|
issuedTokens := v.DelegatorShareExRate().Mul(delShares)
|
||||||
// validator status.
|
v.Tokens = v.Tokens.Sub(issuedTokens)
|
||||||
func (v Validator) RemoveDelShares(pool Pool, delShares sdk.Rat) (Validator, Pool, int64) {
|
|
||||||
amount := v.DelegatorShareExRate(pool).Mul(delShares)
|
|
||||||
eqBondedSharesToRemove := NewBondedShares(amount)
|
|
||||||
v.DelegatorShares = v.DelegatorShares.Sub(delShares)
|
v.DelegatorShares = v.DelegatorShares.Sub(delShares)
|
||||||
|
|
||||||
var createdCoins int64
|
if v.Status == sdk.Bonded {
|
||||||
|
pool = pool.bondedTokensToLoose(issuedTokens)
|
||||||
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
|
return v, pool, issuedTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
// DelegatorShareExRate gets the exchange rate of tokens over delegator shares.
|
// DelegatorShareExRate gets the exchange rate of tokens over delegator shares.
|
||||||
// UNITS: eq-val-bonded-shares/delegator-shares
|
// UNITS: tokens/delegator-shares
|
||||||
func (v Validator) DelegatorShareExRate(pool Pool) sdk.Rat {
|
func (v Validator) DelegatorShareExRate() sdk.Rat {
|
||||||
if v.DelegatorShares.IsZero() {
|
if v.DelegatorShares.IsZero() {
|
||||||
return sdk.OneRat()
|
return sdk.OneRat()
|
||||||
}
|
}
|
||||||
|
return v.Tokens.Quo(v.DelegatorShares)
|
||||||
|
}
|
||||||
|
|
||||||
eqBondedShares := v.PoolShares.ToBonded(pool).Amount
|
// Get the bonded tokens which the validator holds
|
||||||
return eqBondedShares.Quo(v.DelegatorShares)
|
func (v Validator) BondedTokens() sdk.Rat {
|
||||||
|
if v.Status == sdk.Bonded {
|
||||||
|
return v.Tokens
|
||||||
|
}
|
||||||
|
return sdk.ZeroRat()
|
||||||
}
|
}
|
||||||
|
|
||||||
//______________________________________________________________________
|
//______________________________________________________________________
|
||||||
|
@ -383,10 +402,10 @@ var _ sdk.Validator = Validator{}
|
||||||
// nolint - for sdk.Validator
|
// nolint - for sdk.Validator
|
||||||
func (v Validator) GetRevoked() bool { return v.Revoked }
|
func (v Validator) GetRevoked() bool { return v.Revoked }
|
||||||
func (v Validator) GetMoniker() string { return v.Description.Moniker }
|
func (v Validator) GetMoniker() string { return v.Description.Moniker }
|
||||||
func (v Validator) GetStatus() sdk.BondStatus { return v.Status() }
|
func (v Validator) GetStatus() sdk.BondStatus { return v.Status }
|
||||||
func (v Validator) GetOwner() sdk.AccAddress { return v.Owner }
|
func (v Validator) GetOwner() sdk.AccAddress { return v.Owner }
|
||||||
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
|
func (v Validator) GetPubKey() crypto.PubKey { return v.PubKey }
|
||||||
func (v Validator) GetPower() sdk.Rat { return v.PoolShares.Bonded() }
|
func (v Validator) GetPower() sdk.Rat { return v.BondedTokens() }
|
||||||
func (v Validator) GetDelegatorShares() sdk.Rat { return v.DelegatorShares }
|
func (v Validator) GetDelegatorShares() sdk.Rat { return v.DelegatorShares }
|
||||||
func (v Validator) GetBondHeight() int64 { return v.BondHeight }
|
func (v Validator) GetBondHeight() int64 { return v.BondHeight }
|
||||||
|
|
||||||
|
@ -402,7 +421,8 @@ func (v Validator) HumanReadableString() (string, error) {
|
||||||
resp := "Validator \n"
|
resp := "Validator \n"
|
||||||
resp += fmt.Sprintf("Owner: %s\n", v.Owner)
|
resp += fmt.Sprintf("Owner: %s\n", v.Owner)
|
||||||
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
resp += fmt.Sprintf("Validator: %s\n", bechVal)
|
||||||
resp += fmt.Sprintf("Shares: Status %s, Amount: %s\n", sdk.BondStatusToString(v.PoolShares.Status), v.PoolShares.Amount.FloatString())
|
resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status))
|
||||||
|
resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.FloatString())
|
||||||
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.FloatString())
|
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.FloatString())
|
||||||
resp += fmt.Sprintf("Description: %s\n", v.Description)
|
resp += fmt.Sprintf("Description: %s\n", v.Description)
|
||||||
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
|
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
|
||||||
|
@ -411,7 +431,7 @@ func (v Validator) HumanReadableString() (string, error) {
|
||||||
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
|
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
|
||||||
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
|
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
|
||||||
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
|
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
|
||||||
resp += fmt.Sprintf("Previously Bonded Stares: %s\n", v.PrevBondedShares.String())
|
resp += fmt.Sprintf("Previous Bonded Tokens: %s\n", v.LastBondedTokens.String())
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,209 +42,196 @@ func TestUpdateDescription(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABCIValidator(t *testing.T) {
|
func TestABCIValidator(t *testing.T) {
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
|
|
||||||
abciVal := val.ABCIValidator()
|
abciVal := validator.ABCIValidator()
|
||||||
require.Equal(t, tmtypes.TM2PB.PubKey(val.PubKey), abciVal.PubKey)
|
require.Equal(t, tmtypes.TM2PB.PubKey(validator.PubKey), abciVal.PubKey)
|
||||||
require.Equal(t, val.PoolShares.Bonded().RoundInt64(), abciVal.Power)
|
require.Equal(t, validator.BondedTokens().RoundInt64(), abciVal.Power)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestABCIValidatorZero(t *testing.T) {
|
func TestABCIValidatorZero(t *testing.T) {
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
|
|
||||||
abciVal := val.ABCIValidatorZero()
|
abciVal := validator.ABCIValidatorZero()
|
||||||
require.Equal(t, tmtypes.TM2PB.PubKey(val.PubKey), abciVal.PubKey)
|
require.Equal(t, tmtypes.TM2PB.PubKey(validator.PubKey), abciVal.PubKey)
|
||||||
require.Equal(t, int64(0), abciVal.Power)
|
require.Equal(t, int64(0), abciVal.Power)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRemovePoolShares(t *testing.T) {
|
func TestRemoveTokens(t *testing.T) {
|
||||||
pool := InitialPool()
|
|
||||||
pool.LooseTokens = 10
|
|
||||||
|
|
||||||
val := Validator{
|
validator := Validator{
|
||||||
Owner: addr1,
|
Owner: addr1,
|
||||||
PubKey: pk1,
|
PubKey: pk1,
|
||||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
Status: sdk.Bonded,
|
||||||
|
Tokens: sdk.NewRat(100),
|
||||||
DelegatorShares: sdk.NewRat(100),
|
DelegatorShares: sdk.NewRat(100),
|
||||||
}
|
}
|
||||||
|
|
||||||
pool.BondedTokens = val.PoolShares.Bonded().RoundInt64()
|
pool := InitialPool()
|
||||||
pool.BondedShares = val.PoolShares.Bonded()
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
|
pool.BondedTokens = validator.BondedTokens()
|
||||||
|
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||||
val, pool, tk := val.RemovePoolShares(pool, sdk.NewRat(10))
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.Equal(t, int64(90), val.PoolShares.Amount.RoundInt64())
|
|
||||||
require.Equal(t, int64(90), pool.BondedTokens)
|
|
||||||
require.Equal(t, int64(90), pool.BondedShares.RoundInt64())
|
|
||||||
require.Equal(t, int64(20), pool.LooseTokens)
|
|
||||||
require.Equal(t, int64(10), tk)
|
|
||||||
|
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
// remove tokens and test check everything
|
||||||
val, pool, tk = val.RemovePoolShares(pool, sdk.NewRat(10))
|
validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10))
|
||||||
require.Equal(t, int64(80), val.PoolShares.Amount.RoundInt64())
|
require.Equal(t, int64(90), validator.Tokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.BondedTokens)
|
require.Equal(t, int64(90), pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.BondedShares.RoundInt64())
|
require.Equal(t, int64(20), pool.LooseTokens.RoundInt64())
|
||||||
require.Equal(t, int64(30), pool.LooseTokens)
|
|
||||||
require.Equal(t, int64(10), tk)
|
// update validator to unbonded and remove some more tokens
|
||||||
|
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||||
|
require.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
|
require.Equal(t, int64(0), pool.BondedTokens.RoundInt64())
|
||||||
|
require.Equal(t, int64(110), pool.LooseTokens.RoundInt64())
|
||||||
|
|
||||||
|
validator, pool = validator.RemoveTokens(pool, sdk.NewRat(10))
|
||||||
|
require.Equal(t, int64(80), validator.Tokens.RoundInt64())
|
||||||
|
require.Equal(t, int64(0), pool.BondedTokens.RoundInt64())
|
||||||
|
require.Equal(t, int64(110), pool.LooseTokens.RoundInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensValidatorBonded(t *testing.T) {
|
func TestAddTokensValidatorBonded(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 10
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
validator, pool, delShares := validator.AddTokensFromDel(pool, 10)
|
||||||
|
|
||||||
require.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate())
|
||||||
require.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
|
||||||
|
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Bonded()))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.BondedTokens()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensValidatorUnbonding(t *testing.T) {
|
func TestAddTokensValidatorUnbonding(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 10
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
|
||||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
validator, pool, delShares := validator.AddTokensFromDel(pool, 10)
|
||||||
|
|
||||||
require.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate())
|
||||||
require.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
|
||||||
|
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonding()))
|
assert.Equal(t, sdk.Unbonding, validator.Status)
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensValidatorUnbonded(t *testing.T) {
|
func TestAddTokensValidatorUnbonded(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 10
|
pool.LooseTokens = sdk.NewRat(10)
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Unbonded)
|
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded)
|
||||||
val, pool, delShares := val.AddTokensFromDel(pool, 10)
|
validator, pool, delShares := validator.AddTokensFromDel(pool, 10)
|
||||||
|
|
||||||
require.Equal(t, sdk.OneRat(), val.DelegatorShareExRate(pool))
|
require.Equal(t, sdk.OneRat(), validator.DelegatorShareExRate())
|
||||||
require.Equal(t, sdk.OneRat(), pool.BondedShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondingShareExRate())
|
|
||||||
require.Equal(t, sdk.OneRat(), pool.UnbondedShareExRate())
|
|
||||||
|
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), delShares))
|
||||||
assert.True(sdk.RatEq(t, sdk.NewRat(10), val.PoolShares.Unbonded()))
|
assert.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
|
assert.True(sdk.RatEq(t, sdk.NewRat(10), validator.Tokens))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO refactor to make simpler like the AddToken tests above
|
// TODO refactor to make simpler like the AddToken tests above
|
||||||
func TestRemoveDelShares(t *testing.T) {
|
func TestRemoveDelShares(t *testing.T) {
|
||||||
poolA := InitialPool()
|
|
||||||
poolA.LooseTokens = 10
|
|
||||||
valA := Validator{
|
valA := Validator{
|
||||||
Owner: addr1,
|
Owner: addr1,
|
||||||
PubKey: pk1,
|
PubKey: pk1,
|
||||||
PoolShares: NewBondedShares(sdk.NewRat(100)),
|
Status: sdk.Bonded,
|
||||||
|
Tokens: sdk.NewRat(100),
|
||||||
DelegatorShares: sdk.NewRat(100),
|
DelegatorShares: sdk.NewRat(100),
|
||||||
}
|
}
|
||||||
poolA.BondedTokens = valA.PoolShares.Bonded().RoundInt64()
|
poolA := InitialPool()
|
||||||
poolA.BondedShares = valA.PoolShares.Bonded()
|
poolA.LooseTokens = sdk.NewRat(10)
|
||||||
require.Equal(t, valA.DelegatorShareExRate(poolA), sdk.OneRat())
|
poolA.BondedTokens = valA.BondedTokens()
|
||||||
require.Equal(t, poolA.BondedShareExRate(), sdk.OneRat())
|
require.Equal(t, valA.DelegatorShareExRate(), sdk.OneRat())
|
||||||
require.Equal(t, poolA.UnbondedShareExRate(), sdk.OneRat())
|
|
||||||
valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewRat(10))
|
// Remove delegator shares
|
||||||
|
valB, poolB, coinsB := valA.RemoveDelShares(poolA, sdk.NewRat(10))
|
||||||
|
assert.Equal(t, int64(10), coinsB.RoundInt64())
|
||||||
|
assert.Equal(t, int64(90), valB.DelegatorShares.RoundInt64())
|
||||||
|
assert.Equal(t, int64(90), valB.BondedTokens().RoundInt64())
|
||||||
|
assert.Equal(t, int64(90), poolB.BondedTokens.RoundInt64())
|
||||||
|
assert.Equal(t, int64(20), poolB.LooseTokens.RoundInt64())
|
||||||
|
|
||||||
// coins were created
|
|
||||||
require.Equal(t, coinsB, int64(10))
|
|
||||||
// pool shares were removed
|
|
||||||
require.Equal(t, valB.PoolShares.Bonded(), valA.PoolShares.Bonded().Sub(sdk.NewRat(10).Mul(valA.DelegatorShareExRate(poolA))))
|
|
||||||
// conservation of tokens
|
// conservation of tokens
|
||||||
require.Equal(t, poolB.UnbondedTokens+poolB.BondedTokens+coinsB, poolA.UnbondedTokens+poolA.BondedTokens)
|
require.True(sdk.RatEq(t,
|
||||||
|
poolB.LooseTokens.Add(poolB.BondedTokens),
|
||||||
|
poolA.LooseTokens.Add(poolA.BondedTokens)))
|
||||||
|
|
||||||
// specific case from random tests
|
// specific case from random tests
|
||||||
poolShares := sdk.NewRat(5102)
|
poolTokens := sdk.NewRat(5102)
|
||||||
delShares := sdk.NewRat(115)
|
delShares := sdk.NewRat(115)
|
||||||
val := Validator{
|
validator := Validator{
|
||||||
Owner: addr1,
|
Owner: addr1,
|
||||||
PubKey: pk1,
|
PubKey: pk1,
|
||||||
PoolShares: NewBondedShares(poolShares),
|
Status: sdk.Bonded,
|
||||||
|
Tokens: poolTokens,
|
||||||
DelegatorShares: delShares,
|
DelegatorShares: delShares,
|
||||||
}
|
}
|
||||||
pool := Pool{
|
pool := Pool{
|
||||||
BondedShares: sdk.NewRat(248305),
|
BondedTokens: sdk.NewRat(248305),
|
||||||
UnbondedShares: sdk.NewRat(232147),
|
LooseTokens: sdk.NewRat(232147),
|
||||||
BondedTokens: 248305,
|
|
||||||
UnbondedTokens: 232147,
|
|
||||||
InflationLastTime: 0,
|
InflationLastTime: 0,
|
||||||
Inflation: sdk.NewRat(7, 100),
|
Inflation: sdk.NewRat(7, 100),
|
||||||
}
|
}
|
||||||
shares := sdk.NewRat(29)
|
shares := sdk.NewRat(29)
|
||||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
_, newPool, tokens := validator.RemoveDelShares(pool, shares)
|
||||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
require.True(sdk.RatEq(t, sdk.NewRat(147958, 115), tokens))
|
||||||
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
require.True(sdk.RatEq(t,
|
||||||
_, newPool, tokens := val.RemoveDelShares(pool, shares)
|
newPool.LooseTokens.Add(newPool.BondedTokens),
|
||||||
require.Equal(t,
|
pool.LooseTokens.Add(pool.BondedTokens)))
|
||||||
tokens+newPool.UnbondedTokens+newPool.BondedTokens,
|
|
||||||
pool.BondedTokens+pool.UnbondedTokens,
|
|
||||||
"Tokens were not conserved: %s", msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateStatus(t *testing.T) {
|
func TestUpdateStatus(t *testing.T) {
|
||||||
pool := InitialPool()
|
pool := InitialPool()
|
||||||
pool.LooseTokens = 100
|
pool.LooseTokens = sdk.NewRat(100)
|
||||||
|
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
val, pool, _ = val.AddTokensFromDel(pool, 100)
|
validator, pool, _ = validator.AddTokensFromDel(pool, 100)
|
||||||
require.Equal(t, int64(0), val.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, sdk.Unbonded, validator.Status)
|
||||||
require.Equal(t, int64(0), val.PoolShares.Unbonding().RoundInt64())
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
|
||||||
require.Equal(t, int64(100), val.PoolShares.Unbonded().RoundInt64())
|
require.Equal(t, int64(0), pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.BondedTokens)
|
require.Equal(t, int64(100), pool.LooseTokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.UnbondingTokens)
|
|
||||||
require.Equal(t, int64(100), pool.UnbondedTokens)
|
|
||||||
|
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Unbonding)
|
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
|
||||||
require.Equal(t, int64(0), val.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, sdk.Bonded, validator.Status)
|
||||||
require.Equal(t, int64(100), val.PoolShares.Unbonding().RoundInt64())
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), val.PoolShares.Unbonded().RoundInt64())
|
require.Equal(t, int64(100), pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.BondedTokens)
|
require.Equal(t, int64(0), pool.LooseTokens.RoundInt64())
|
||||||
require.Equal(t, int64(100), pool.UnbondingTokens)
|
|
||||||
require.Equal(t, int64(0), pool.UnbondedTokens)
|
|
||||||
|
|
||||||
val, pool = val.UpdateStatus(pool, sdk.Bonded)
|
validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
|
||||||
require.Equal(t, int64(100), val.PoolShares.Bonded().RoundInt64())
|
require.Equal(t, sdk.Unbonding, validator.Status)
|
||||||
require.Equal(t, int64(0), val.PoolShares.Unbonding().RoundInt64())
|
require.Equal(t, int64(100), validator.Tokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), val.PoolShares.Unbonded().RoundInt64())
|
require.Equal(t, int64(0), pool.BondedTokens.RoundInt64())
|
||||||
require.Equal(t, int64(100), pool.BondedTokens)
|
require.Equal(t, int64(100), pool.LooseTokens.RoundInt64())
|
||||||
require.Equal(t, int64(0), pool.UnbondingTokens)
|
|
||||||
require.Equal(t, int64(0), pool.UnbondedTokens)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPossibleOverflow(t *testing.T) {
|
func TestPossibleOverflow(t *testing.T) {
|
||||||
poolShares := sdk.NewRat(2159)
|
poolTokens := sdk.NewRat(2159)
|
||||||
delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
delShares := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||||
val := Validator{
|
validator := Validator{
|
||||||
Owner: addr1,
|
Owner: addr1,
|
||||||
PubKey: pk1,
|
PubKey: pk1,
|
||||||
PoolShares: NewBondedShares(poolShares),
|
Status: sdk.Bonded,
|
||||||
|
Tokens: poolTokens,
|
||||||
DelegatorShares: delShares,
|
DelegatorShares: delShares,
|
||||||
}
|
}
|
||||||
pool := Pool{
|
pool := Pool{
|
||||||
LooseTokens: 100,
|
LooseTokens: sdk.NewRat(100),
|
||||||
BondedShares: poolShares,
|
BondedTokens: poolTokens,
|
||||||
UnbondedShares: sdk.ZeroRat(),
|
|
||||||
BondedTokens: poolShares.RoundInt64(),
|
|
||||||
UnbondedTokens: 0,
|
|
||||||
InflationLastTime: 0,
|
InflationLastTime: 0,
|
||||||
Inflation: sdk.NewRat(7, 100),
|
Inflation: sdk.NewRat(7, 100),
|
||||||
}
|
}
|
||||||
tokens := int64(71)
|
tokens := int64(71)
|
||||||
msg := fmt.Sprintf("validator %s (status: %d, poolShares: %v, delShares: %v, DelegatorShareExRate: %v)",
|
msg := fmt.Sprintf("validator %#v", validator)
|
||||||
val.Owner, val.Status(), val.PoolShares.Bonded(), val.DelegatorShares, val.DelegatorShareExRate(pool))
|
newValidator, _, _ := validator.AddTokensFromDel(pool, tokens)
|
||||||
newValidator, _, _ := val.AddTokensFromDel(pool, tokens)
|
|
||||||
|
|
||||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
require.False(t, newValidator.DelegatorShareExRate(pool).LT(sdk.ZeroRat()),
|
require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroRat()),
|
||||||
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
|
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
|
||||||
msg, newValidator.DelegatorShareExRate(pool))
|
msg, newValidator.DelegatorShareExRate())
|
||||||
}
|
}
|
||||||
|
|
||||||
// run random operations in a random order on a random single-validator state, assert invariants hold
|
// run random operations in a random order on a random single-validator state, assert invariants hold
|
||||||
|
@ -258,10 +245,10 @@ func TestSingleValidatorIntegrationInvariants(t *testing.T) {
|
||||||
// sanity check
|
// sanity check
|
||||||
AssertInvariants(t, "no operation",
|
AssertInvariants(t, "no operation",
|
||||||
poolOrig, validatorsOrig,
|
poolOrig, validatorsOrig,
|
||||||
poolOrig, validatorsOrig, 0)
|
poolOrig, validatorsOrig)
|
||||||
|
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
poolMod, validatorMod, tokens, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[0])
|
poolMod, validatorMod, _, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[0])
|
||||||
|
|
||||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||||
copy(validatorsMod[:], validatorsOrig[:])
|
copy(validatorsMod[:], validatorsOrig[:])
|
||||||
|
@ -271,7 +258,7 @@ func TestSingleValidatorIntegrationInvariants(t *testing.T) {
|
||||||
|
|
||||||
AssertInvariants(t, msg,
|
AssertInvariants(t, msg,
|
||||||
poolOrig, validatorsOrig,
|
poolOrig, validatorsOrig,
|
||||||
poolMod, validatorsMod, tokens)
|
poolMod, validatorsMod)
|
||||||
|
|
||||||
poolOrig = poolMod
|
poolOrig = poolMod
|
||||||
validatorsOrig = validatorsMod
|
validatorsOrig = validatorsMod
|
||||||
|
@ -288,18 +275,18 @@ func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
||||||
|
|
||||||
AssertInvariants(t, "no operation",
|
AssertInvariants(t, "no operation",
|
||||||
poolOrig, validatorsOrig,
|
poolOrig, validatorsOrig,
|
||||||
poolOrig, validatorsOrig, 0)
|
poolOrig, validatorsOrig)
|
||||||
|
|
||||||
for j := 0; j < 5; j++ {
|
for j := 0; j < 5; j++ {
|
||||||
index := int(r.Int31n(int32(len(validatorsOrig))))
|
index := int(r.Int31n(int32(len(validatorsOrig))))
|
||||||
poolMod, validatorMod, tokens, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[index])
|
poolMod, validatorMod, _, msg := RandomOperation(r)(r, poolOrig, validatorsOrig[index])
|
||||||
validatorsMod := make([]Validator, len(validatorsOrig))
|
validatorsMod := make([]Validator, len(validatorsOrig))
|
||||||
copy(validatorsMod[:], validatorsOrig[:])
|
copy(validatorsMod[:], validatorsOrig[:])
|
||||||
validatorsMod[index] = validatorMod
|
validatorsMod[index] = validatorMod
|
||||||
|
|
||||||
AssertInvariants(t, msg,
|
AssertInvariants(t, msg,
|
||||||
poolOrig, validatorsOrig,
|
poolOrig, validatorsOrig,
|
||||||
poolMod, validatorsMod, tokens)
|
poolMod, validatorsMod)
|
||||||
|
|
||||||
poolOrig = poolMod
|
poolOrig = poolMod
|
||||||
validatorsOrig = validatorsMod
|
validatorsOrig = validatorsMod
|
||||||
|
@ -309,11 +296,11 @@ func TestMultiValidatorIntegrationInvariants(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHumanReadableString(t *testing.T) {
|
func TestHumanReadableString(t *testing.T) {
|
||||||
val := NewValidator(addr1, pk1, Description{})
|
validator := NewValidator(addr1, pk1, Description{})
|
||||||
|
|
||||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||||
// actual contents of the string.
|
// actual contents of the string.
|
||||||
valStr, err := val.HumanReadableString()
|
valStr, err := validator.HumanReadableString()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.NotEmpty(t, valStr)
|
require.NotEmpty(t, valStr)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue