cosmos-sdk/x/stake/pool_test.go

526 lines
18 KiB
Go
Raw Normal View History

2018-03-22 09:00:45 -07:00
package stake
2018-03-28 11:35:25 -07:00
import (
"fmt"
2018-03-28 11:35:25 -07:00
"math/rand"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2018-03-28 11:35:25 -07:00
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestBondedRatio(t *testing.T) {
2018-04-04 20:25:04 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.TotalSupply = 3
pool.BondedPool = 2
2018-04-04 10:54:30 -07:00
// bonded pool / total supply
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
pool.TotalSupply = 0
2018-04-04 10:54:30 -07:00
// avoids divide-by-zero
2018-04-30 14:21:14 -07:00
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat())
}
func TestBondedShareExRate(t *testing.T) {
2018-04-04 20:25:04 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.BondedPool = 3
pool.BondedShares = sdk.NewRat(10)
2018-04-04 10:54:30 -07:00
// bonded pool / bonded shares
require.Equal(t, pool.bondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
2018-04-30 14:21:14 -07:00
pool.BondedShares = sdk.ZeroRat()
2018-04-04 10:54:30 -07:00
// avoids divide-by-zero
2018-04-30 14:21:14 -07:00
require.Equal(t, pool.bondedShareExRate(), sdk.OneRat())
}
func TestUnbondedShareExRate(t *testing.T) {
2018-04-04 20:25:04 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.UnbondedPool = 3
pool.UnbondedShares = sdk.NewRat(10)
2018-04-04 10:54:30 -07:00
// unbonded pool / unbonded shares
require.Equal(t, pool.unbondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
2018-04-30 14:21:14 -07:00
pool.UnbondedShares = sdk.ZeroRat()
2018-04-04 10:54:30 -07:00
// avoids divide-by-zero
2018-04-30 14:21:14 -07:00
require.Equal(t, pool.unbondedShareExRate(), sdk.OneRat())
}
2018-05-12 11:33:55 -07:00
// TODO convert these commend out tests to test UpdateSharesLocation
//func TestBondedToUnbondedPool(t *testing.T) {
//ctx, _, keeper := createTestInput(t, false, 0)
//poolA := keeper.GetPool(ctx)
//assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
//assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
//valA := Validator{
//Status: sdk.Bonded,
//Address: addrs[0],
//PubKey: pks[0],
//BondedShares: sdk.OneRat(),
//DelegatorShares: sdk.OneRat(),
//}
//poolB, valB := poolA.bondedToUnbondedPool(valA)
//// status unbonded
//assert.Equal(t, valB.Status, sdk.Unbonded)
//// same exchange rate, assets unchanged
//assert.Equal(t, valB.BondedShares, valA.BondedShares)
//// bonded pool decreased
//assert.Equal(t, poolB.BondedPool, poolA.BondedPool-valA.BondedShares.Evaluate())
//// unbonded pool increased
//assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+valA.BondedShares.Evaluate())
//// conservation of tokens
//assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
//}
//func TestUnbonbedtoBondedPool(t *testing.T) {
//ctx, _, keeper := createTestInput(t, false, 0)
//poolA := keeper.GetPool(ctx)
//assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
//assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
//valA := Validator{
//Status: sdk.Bonded,
//Address: addrs[0],
//PubKey: pks[0],
//BondedShares: sdk.OneRat(),
//DelegatorShares: sdk.OneRat(),
//}
//valA.Status = sdk.Unbonded
//poolB, valB := poolA.unbondedToBondedPool(valA)
//// status bonded
//assert.Equal(t, valB.Status, sdk.Bonded)
//// same exchange rate, assets unchanged
//assert.Equal(t, valB.BondedShares, valA.BondedShares)
//// bonded pool increased
//assert.Equal(t, poolB.BondedPool, poolA.BondedPool+valA.BondedShares.Evaluate())
//// unbonded pool decreased
//assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-valA.BondedShares.Evaluate())
//// conservation of tokens
//assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool, poolA.BondedPool+poolA.UnbondedPool)
//}
2018-03-28 11:35:25 -07:00
func TestAddTokensBonded(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
2018-03-28 11:35:25 -07:00
poolB, sharesB := poolA.addTokensBonded(10)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat())
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// correct changes to bonded shares and bonded pool
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Add(sharesB))
assert.Equal(t, poolB.BondedPool, poolA.BondedPool+10)
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// same number of bonded shares / tokens when exchange rate is one
2018-04-30 14:21:14 -07:00
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedPool)))
2018-03-28 11:35:25 -07:00
}
func TestRemoveSharesBonded(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
2018-03-28 11:35:25 -07:00
poolB, tokensB := poolA.removeSharesBonded(sdk.NewRat(10))
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolB.bondedShareExRate(), sdk.OneRat())
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// correct changes to bonded shares and bonded pool
assert.Equal(t, poolB.BondedShares, poolA.BondedShares.Sub(sdk.NewRat(10)))
assert.Equal(t, poolB.BondedPool, poolA.BondedPool-tokensB)
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// same number of bonded shares / tokens when exchange rate is one
2018-04-30 14:21:14 -07:00
assert.True(t, poolB.BondedShares.Equal(sdk.NewRat(poolB.BondedPool)))
2018-03-28 11:35:25 -07:00
}
func TestAddTokensUnbonded(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
2018-03-28 11:35:25 -07:00
poolB, sharesB := poolA.addTokensUnbonded(10)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat())
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// correct changes to unbonded shares and unbonded pool
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Add(sharesB))
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool+10)
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// same number of unbonded shares / tokens when exchange rate is one
2018-04-30 14:21:14 -07:00
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedPool)))
2018-03-28 11:35:25 -07:00
}
func TestRemoveSharesUnbonded(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
2018-03-28 11:35:25 -07:00
poolB, tokensB := poolA.removeSharesUnbonded(sdk.NewRat(10))
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolB.unbondedShareExRate(), sdk.OneRat())
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// correct changes to unbonded shares and bonded pool
assert.Equal(t, poolB.UnbondedShares, poolA.UnbondedShares.Sub(sdk.NewRat(10)))
assert.Equal(t, poolB.UnbondedPool, poolA.UnbondedPool-tokensB)
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// same number of unbonded shares / tokens when exchange rate is one
2018-04-30 14:21:14 -07:00
assert.True(t, poolB.UnbondedShares.Equal(sdk.NewRat(poolB.UnbondedPool)))
2018-03-28 11:35:25 -07:00
}
2018-05-09 21:01:58 -07:00
func TestValidatorAddTokens(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-30 13:51:40 -07:00
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-05-12 11:33:55 -07:00
valA := Validator{
2018-05-10 18:38:57 -07:00
Status: sdk.Bonded,
Address: addrs[0],
PubKey: pks[0],
BondedShares: sdk.NewRat(9),
2018-05-04 12:38:25 -07:00
DelegatorShares: sdk.NewRat(9),
2018-03-28 11:35:25 -07:00
}
2018-05-12 11:33:55 -07:00
poolA.BondedPool = valA.BondedShares.Evaluate()
poolA.BondedShares = valA.BondedShares
assert.Equal(t, valA.DelegatorShareExRate(), sdk.OneRat())
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
2018-05-12 11:33:55 -07:00
poolB, valB, sharesB := poolA.validatorAddTokens(valA, 10)
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// shares were issued
2018-05-12 11:33:55 -07:00
assert.Equal(t, sdk.NewRat(10).Mul(valA.DelegatorShareExRate()), sharesB)
2018-03-28 11:35:25 -07:00
// pool shares were added
2018-05-12 11:33:55 -07:00
assert.Equal(t, valB.BondedShares, valA.BondedShares.Add(sdk.NewRat(10)))
2018-03-28 11:35:25 -07:00
// conservation of tokens
2018-03-30 13:51:40 -07:00
assert.Equal(t, poolB.BondedPool, 10+poolA.BondedPool)
2018-03-28 11:35:25 -07:00
}
2018-05-09 21:01:58 -07:00
func TestValidatorRemoveShares(t *testing.T) {
2018-04-01 09:05:58 -07:00
ctx, _, keeper := createTestInput(t, false, 0)
2018-03-28 11:35:25 -07:00
poolA := keeper.GetPool(ctx)
2018-05-12 11:33:55 -07:00
valA := Validator{
2018-05-10 18:38:57 -07:00
Status: sdk.Bonded,
Address: addrs[0],
PubKey: pks[0],
BondedShares: sdk.NewRat(9),
2018-05-04 12:38:25 -07:00
DelegatorShares: sdk.NewRat(9),
2018-03-28 11:35:25 -07:00
}
2018-05-12 11:33:55 -07:00
poolA.BondedPool = valA.BondedShares.Evaluate()
poolA.BondedShares = valA.BondedShares
assert.Equal(t, valA.DelegatorShareExRate(), sdk.OneRat())
2018-04-30 14:21:14 -07:00
assert.Equal(t, poolA.bondedShareExRate(), sdk.OneRat())
assert.Equal(t, poolA.unbondedShareExRate(), sdk.OneRat())
2018-05-12 11:33:55 -07:00
poolB, valB, coinsB := poolA.validatorRemoveShares(valA, sdk.NewRat(10))
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// coins were created
assert.Equal(t, coinsB, int64(10))
// pool shares were removed
2018-05-12 11:33:55 -07:00
assert.Equal(t, valB.BondedShares, valA.BondedShares.Sub(sdk.NewRat(10).Mul(valA.DelegatorShareExRate())))
2018-03-28 11:35:25 -07:00
// conservation of tokens
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
2018-04-04 10:26:35 -07:00
// specific case from random tests
assets := sdk.NewRat(5102)
liabilities := sdk.NewRat(115)
2018-05-12 11:33:55 -07:00
val := Validator{
2018-05-10 18:38:57 -07:00
Status: sdk.Bonded,
Address: addrs[0],
PubKey: pks[0],
BondedShares: assets,
2018-05-04 12:38:25 -07:00
DelegatorShares: liabilities,
2018-04-04 10:26:35 -07:00
}
pool := Pool{
TotalSupply: 0,
BondedShares: sdk.NewRat(248305),
UnbondedShares: sdk.NewRat(232147),
BondedPool: 248305,
UnbondedPool: 232147,
InflationLastTime: 0,
Inflation: sdk.NewRat(7, 100),
}
shares := sdk.NewRat(29)
2018-05-12 11:33:55 -07:00
msg := fmt.Sprintf("validator %s (status: %d, assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
val.Address, val.Status, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
2018-04-04 10:26:35 -07:00
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
2018-05-12 11:33:55 -07:00
newPool, _, tokens := pool.validatorRemoveShares(val, shares)
2018-04-04 10:26:35 -07:00
require.Equal(t,
tokens+newPool.UnbondedPool+newPool.BondedPool,
pool.BondedPool+pool.UnbondedPool,
"Tokens were not conserved: %s", msg)
2018-03-28 11:35:25 -07:00
}
2018-05-12 11:33:55 -07:00
//________________________________________________________________________________
// TODO refactor this random setup
2018-03-30 15:47:33 -07:00
2018-05-09 21:01:58 -07:00
// generate a random validator
func randomValidator(r *rand.Rand) Validator {
var status sdk.BondStatus
2018-03-28 11:35:25 -07:00
if r.Float64() < float64(0.5) {
2018-05-10 18:38:57 -07:00
status = sdk.Bonded
2018-03-28 11:35:25 -07:00
} else {
2018-05-10 18:38:57 -07:00
status = sdk.Unbonded
2018-03-28 11:35:25 -07:00
}
assets := sdk.NewRat(int64(r.Int31n(10000)))
liabilities := sdk.NewRat(int64(r.Int31n(10000)))
2018-05-09 21:01:58 -07:00
return Validator{
2018-05-10 18:38:57 -07:00
Status: status,
Address: addrs[0],
PubKey: pks[0],
BondedShares: assets,
2018-05-04 12:38:25 -07:00
DelegatorShares: liabilities,
2018-03-28 11:35:25 -07:00
}
}
// generate a random staking state
2018-05-09 21:01:58 -07:00
func randomSetup(r *rand.Rand, numValidators int) (Pool, Validators) {
2018-03-28 11:35:25 -07:00
pool := Pool{
TotalSupply: 0,
2018-04-30 14:21:14 -07:00
BondedShares: sdk.ZeroRat(),
UnbondedShares: sdk.ZeroRat(),
2018-03-28 11:35:25 -07:00
BondedPool: 0,
UnbondedPool: 0,
InflationLastTime: 0,
Inflation: sdk.NewRat(7, 100),
}
2018-05-09 21:01:58 -07:00
validators := make([]Validator, numValidators)
for i := 0; i < numValidators; i++ {
validator := randomValidator(r)
2018-05-10 18:38:57 -07:00
if validator.Status == sdk.Bonded {
2018-05-09 21:01:58 -07:00
pool.BondedShares = pool.BondedShares.Add(validator.BondedShares)
pool.BondedPool += validator.BondedShares.Evaluate()
2018-05-10 18:38:57 -07:00
} else if validator.Status == sdk.Unbonded {
2018-05-09 21:01:58 -07:00
pool.UnbondedShares = pool.UnbondedShares.Add(validator.BondedShares)
pool.UnbondedPool += validator.BondedShares.Evaluate()
2018-04-02 03:13:14 -07:00
}
2018-05-09 21:01:58 -07:00
validators[i] = validator
2018-03-28 11:35:25 -07:00
}
2018-05-09 21:01:58 -07:00
return pool, validators
2018-03-30 15:47:33 -07:00
}
// any operation that transforms staking state
2018-05-09 21:01:58 -07:00
// takes in RNG instance, pool, validator
// returns updated pool, updated validator, delta tokens, descriptive message
type Operation func(r *rand.Rand, p Pool, c Validator) (Pool, Validator, int64, string)
2018-05-09 21:01:58 -07:00
// operation: bond or unbond a validator depending on current status
2018-05-12 11:33:55 -07:00
func OpBondOrUnbond(r *rand.Rand, p Pool, val Validator) (Pool, Validator, int64, string) {
var msg string
2018-05-12 11:33:55 -07:00
if val.Status == sdk.Bonded {
msg = fmt.Sprintf("sdk.Unbonded previously bonded validator %s (assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
val.Address, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
val.Status = sdk.Unbonded
} else if val.Status == sdk.Unbonded {
msg = fmt.Sprintf("sdk.Bonded previously unbonded validator %s (assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
val.Address, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
val.Status = sdk.Bonded
}
2018-05-12 11:33:55 -07:00
p, val = p.UpdateSharesLocation(val)
return p, val, 0, msg
}
2018-05-09 21:01:58 -07:00
// operation: add a random number of tokens to a validator
2018-05-12 11:33:55 -07:00
func OpAddTokens(r *rand.Rand, p Pool, val Validator) (Pool, Validator, int64, string) {
tokens := int64(r.Int31n(1000))
2018-05-12 11:33:55 -07:00
msg := fmt.Sprintf("validator %s (status: %d, assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
val.Address, val.Status, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
p, val, _ = p.validatorAddTokens(val, tokens)
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
2018-05-12 11:33:55 -07:00
return p, val, -1 * tokens, msg // tokens are removed so for accounting must be negative
}
2018-05-09 21:01:58 -07:00
// operation: remove a random number of shares from a validator
2018-05-12 11:33:55 -07:00
func OpRemoveShares(r *rand.Rand, p Pool, val Validator) (Pool, Validator, int64, string) {
2018-04-03 20:41:08 -07:00
var shares sdk.Rat
for {
shares = sdk.NewRat(int64(r.Int31n(1000)))
2018-05-12 11:33:55 -07:00
if shares.LT(val.DelegatorShares) {
2018-04-03 20:41:08 -07:00
break
}
}
2018-04-03 20:41:08 -07:00
2018-05-12 11:33:55 -07:00
msg := fmt.Sprintf("Removed %v shares from validator %s (status: %d, assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
shares, val.Address, val.Status, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
2018-04-03 20:41:08 -07:00
2018-05-12 11:33:55 -07:00
p, val, tokens := p.validatorRemoveShares(val, shares)
return p, val, tokens, msg
}
2018-03-28 11:35:25 -07:00
// pick a random staking operation
func randomOperation(r *rand.Rand) Operation {
operations := []Operation{
2018-04-04 10:54:30 -07:00
OpBondOrUnbond,
OpAddTokens,
OpRemoveShares,
2018-03-28 11:35:25 -07:00
}
r.Shuffle(len(operations), func(i, j int) {
operations[i], operations[j] = operations[j], operations[i]
})
return operations[0]
}
// ensure invariants that should always be true are true
2018-03-30 15:47:33 -07:00
func assertInvariants(t *testing.T, msg string,
2018-05-09 21:01:58 -07:00
pOrig Pool, cOrig Validators, pMod Pool, cMods Validators, tokens int64) {
2018-03-30 12:46:33 -07:00
2018-03-28 11:35:25 -07:00
// total tokens conserved
2018-03-30 15:47:33 -07:00
require.Equal(t,
2018-04-01 17:23:34 -07:00
pOrig.UnbondedPool+pOrig.BondedPool,
pMod.UnbondedPool+pMod.BondedPool+tokens,
2018-04-04 10:26:35 -07:00
"Tokens not conserved - msg: %v\n, pOrig.BondedShares: %v, pOrig.UnbondedShares: %v, pMod.BondedShares: %v, pMod.UnbondedShares: %v, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
2018-03-30 15:47:33 -07:00
msg,
2018-04-04 10:26:35 -07:00
pOrig.BondedShares, pOrig.UnbondedShares,
pMod.BondedShares, pMod.UnbondedShares,
2018-04-01 17:23:34 -07:00
pOrig.UnbondedPool, pOrig.BondedPool,
pMod.UnbondedPool, pMod.BondedPool, tokens)
2018-03-30 12:46:33 -07:00
2018-04-03 04:47:26 -07:00
// nonnegative bonded shares
2018-04-30 14:21:14 -07:00
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat()),
2018-04-03 20:41:08 -07:00
"Negative bonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
2018-04-02 05:38:50 -07:00
msg, pOrig, pMod, tokens)
2018-04-03 04:47:26 -07:00
// nonnegative unbonded shares
2018-04-30 14:21:14 -07:00
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat()),
2018-04-03 20:41:08 -07:00
"Negative unbonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
2018-04-02 05:38:50 -07:00
msg, pOrig, pMod, tokens)
2018-03-30 12:46:33 -07:00
2018-04-03 04:47:26 -07:00
// nonnegative bonded ex rate
2018-04-30 14:21:14 -07:00
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat()),
2018-03-30 12:46:33 -07:00
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
2018-04-01 17:23:34 -07:00
msg, pMod.bondedShareExRate().Evaluate())
2018-03-30 15:47:33 -07:00
2018-04-03 04:47:26 -07:00
// nonnegative unbonded ex rate
2018-04-30 14:21:14 -07:00
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat()),
2018-03-30 12:46:33 -07:00
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
2018-04-01 17:23:34 -07:00
msg, pMod.unbondedShareExRate().Evaluate())
2018-03-30 12:46:33 -07:00
2018-04-02 03:13:14 -07:00
for _, cMod := range cMods {
// nonnegative ex rate
2018-05-12 11:33:55 -07:00
require.False(t, cMod.DelegatorShareExRate().LT(sdk.ZeroRat()),
"Applying operation \"%s\" resulted in negative validator.DelegatorShareExRate(): %v (validator.Address: %s)",
2018-04-02 03:13:14 -07:00
msg,
2018-05-12 11:33:55 -07:00
cMod.DelegatorShareExRate(),
2018-04-02 05:38:50 -07:00
cMod.Address,
2018-04-02 03:13:14 -07:00
)
2018-04-03 04:47:26 -07:00
// nonnegative assets
2018-05-04 12:38:25 -07:00
require.False(t, cMod.BondedShares.LT(sdk.ZeroRat()),
2018-05-12 11:33:55 -07:00
"Applying operation \"%s\" resulted in negative validator.BondedShares: %v (validator.DelegatorShares: %v, validator.DelegatorShareExRate: %v, validator.Address: %s)",
2018-04-02 03:13:14 -07:00
msg,
2018-05-04 12:38:25 -07:00
cMod.BondedShares,
cMod.DelegatorShares,
2018-05-12 11:33:55 -07:00
cMod.DelegatorShareExRate(),
2018-04-02 05:38:50 -07:00
cMod.Address,
2018-04-02 03:13:14 -07:00
)
2018-04-03 04:47:26 -07:00
// nonnegative liabilities
2018-05-04 12:38:25 -07:00
require.False(t, cMod.DelegatorShares.LT(sdk.ZeroRat()),
2018-05-12 11:33:55 -07:00
"Applying operation \"%s\" resulted in negative validator.DelegatorShares: %v (validator.BondedShares: %v, validator.DelegatorShareExRate: %v, validator.Address: %s)",
2018-04-02 03:13:14 -07:00
msg,
2018-05-04 12:38:25 -07:00
cMod.DelegatorShares,
cMod.BondedShares,
2018-05-12 11:33:55 -07:00
cMod.DelegatorShareExRate(),
2018-04-02 05:38:50 -07:00
cMod.Address,
2018-04-02 03:13:14 -07:00
)
2018-04-03 04:47:26 -07:00
2018-04-02 03:13:14 -07:00
}
2018-04-03 03:50:50 -07:00
}
2018-04-03 04:25:29 -07:00
func TestPossibleOverflow(t *testing.T) {
assets := sdk.NewRat(2159)
liabilities := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
2018-05-12 11:33:55 -07:00
val := Validator{
2018-05-10 18:38:57 -07:00
Status: sdk.Bonded,
Address: addrs[0],
PubKey: pks[0],
BondedShares: assets,
2018-05-04 12:38:25 -07:00
DelegatorShares: liabilities,
2018-04-03 04:25:29 -07:00
}
pool := Pool{
TotalSupply: 0,
BondedShares: assets,
2018-04-30 14:21:14 -07:00
UnbondedShares: sdk.ZeroRat(),
2018-04-03 04:25:29 -07:00
BondedPool: assets.Evaluate(),
UnbondedPool: 0,
InflationLastTime: 0,
Inflation: sdk.NewRat(7, 100),
}
tokens := int64(71)
2018-05-12 11:33:55 -07:00
msg := fmt.Sprintf("validator %s (status: %d, assets: %v, liabilities: %v, DelegatorShareExRate: %v)",
val.Address, val.Status, val.BondedShares, val.DelegatorShares, val.DelegatorShareExRate())
_, newValidator, _ := pool.validatorAddTokens(val, tokens)
2018-04-03 20:41:08 -07:00
2018-04-03 04:25:29 -07:00
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
2018-05-12 11:33:55 -07:00
require.False(t, newValidator.DelegatorShareExRate().LT(sdk.ZeroRat()),
"Applying operation \"%s\" resulted in negative DelegatorShareExRate(): %v",
msg, newValidator.DelegatorShareExRate())
2018-04-03 04:25:29 -07:00
}
2018-05-09 21:01:58 -07:00
// run random operations in a random order on a random single-validator state, assert invariants hold
func TestSingleValidatorIntegrationInvariants(t *testing.T) {
2018-04-03 03:50:50 -07:00
r := rand.New(rand.NewSource(41))
for i := 0; i < 10; i++ {
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig := randomSetup(r, 1)
require.Equal(t, 1, len(validatorsOrig))
2018-04-03 03:50:50 -07:00
2018-04-03 20:41:08 -07:00
// sanity check
2018-04-03 03:50:50 -07:00
assertInvariants(t, "no operation",
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig,
poolOrig, validatorsOrig, 0)
2018-04-03 03:50:50 -07:00
2018-04-30 15:06:20 -07:00
for j := 0; j < 5; j++ {
2018-05-09 21:01:58 -07:00
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[0])
2018-04-03 03:50:50 -07:00
2018-05-09 21:01:58 -07:00
validatorsMod := make([]Validator, len(validatorsOrig))
copy(validatorsMod[:], validatorsOrig[:])
require.Equal(t, 1, len(validatorsOrig), "j %v", j)
require.Equal(t, 1, len(validatorsMod), "j %v", j)
validatorsMod[0] = validatorMod
2018-04-03 03:50:50 -07:00
assertInvariants(t, msg,
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig,
poolMod, validatorsMod, tokens)
2018-04-03 20:41:08 -07:00
poolOrig = poolMod
2018-05-09 21:01:58 -07:00
validatorsOrig = validatorsMod
2018-04-03 03:50:50 -07:00
}
}
2018-03-28 11:35:25 -07:00
}
2018-05-09 21:01:58 -07:00
// run random operations in a random order on a random multi-validator state, assert invariants hold
func TestMultiValidatorIntegrationInvariants(t *testing.T) {
2018-04-03 03:50:50 -07:00
r := rand.New(rand.NewSource(42))
2018-03-28 11:35:25 -07:00
for i := 0; i < 10; i++ {
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig := randomSetup(r, 100)
2018-03-30 15:47:33 -07:00
assertInvariants(t, "no operation",
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig,
poolOrig, validatorsOrig, 0)
2018-03-30 15:47:33 -07:00
2018-04-30 15:06:20 -07:00
for j := 0; j < 5; j++ {
2018-05-09 21:01:58 -07:00
index := int(r.Int31n(int32(len(validatorsOrig))))
poolMod, validatorMod, tokens, msg := randomOperation(r)(r, poolOrig, validatorsOrig[index])
validatorsMod := make([]Validator, len(validatorsOrig))
copy(validatorsMod[:], validatorsOrig[:])
validatorsMod[index] = validatorMod
2018-03-30 15:47:33 -07:00
assertInvariants(t, msg,
2018-05-09 21:01:58 -07:00
poolOrig, validatorsOrig,
poolMod, validatorsMod, tokens)
2018-04-02 05:38:50 -07:00
2018-04-04 10:26:35 -07:00
poolOrig = poolMod
2018-05-09 21:01:58 -07:00
validatorsOrig = validatorsMod
2018-03-28 11:35:25 -07:00
}
}
}