Merge pull request #771 from cosmos/cwgoes/staking-tests-v2
Simplified multi-candidate staking pool tests
This commit is contained in:
commit
0c9ba2ba99
|
@ -60,7 +60,7 @@ func (p Pool) addTokensBonded(amount int64) (p2 Pool, issuedShares sdk.Rat) {
|
||||||
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
func (p Pool) removeSharesBonded(shares sdk.Rat) (p2 Pool, removedTokens int64) {
|
||||||
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
removedTokens = p.bondedShareExRate().Mul(shares).Evaluate() // (tokens/shares) * shares
|
||||||
p.BondedShares = p.BondedShares.Sub(shares)
|
p.BondedShares = p.BondedShares.Sub(shares)
|
||||||
p.BondedPool -= removedTokens
|
p.BondedPool = p.BondedPool - removedTokens
|
||||||
return p, removedTokens
|
return p, removedTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -12,6 +11,48 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBondedRatio(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.TotalSupply = 3
|
||||||
|
pool.BondedPool = 2
|
||||||
|
|
||||||
|
// bonded pool / total supply
|
||||||
|
require.Equal(t, pool.bondedRatio(), sdk.NewRat(2).Quo(sdk.NewRat(3)))
|
||||||
|
pool.TotalSupply = 0
|
||||||
|
|
||||||
|
// avoids divide-by-zero
|
||||||
|
require.Equal(t, pool.bondedRatio(), sdk.ZeroRat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBondedShareExRate(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.BondedPool = 3
|
||||||
|
pool.BondedShares = sdk.NewRat(10)
|
||||||
|
|
||||||
|
// bonded pool / bonded shares
|
||||||
|
require.Equal(t, pool.bondedShareExRate(), sdk.NewRat(3).Quo(sdk.NewRat(10)))
|
||||||
|
pool.BondedShares = sdk.ZeroRat
|
||||||
|
|
||||||
|
// avoids divide-by-zero
|
||||||
|
require.Equal(t, pool.bondedShareExRate(), sdk.OneRat)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnbondedShareExRate(t *testing.T) {
|
||||||
|
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||||
|
pool := keeper.GetPool(ctx)
|
||||||
|
pool.UnbondedPool = 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 TestBondedToUnbondedPool(t *testing.T) {
|
func TestBondedToUnbondedPool(t *testing.T) {
|
||||||
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
ctx, _, keeper := createTestInput(t, nil, false, 0)
|
||||||
|
|
||||||
|
@ -97,6 +138,7 @@ func TestRemoveSharesBonded(t *testing.T) {
|
||||||
|
|
||||||
// same number of bonded shares / tokens when exchange rate is one
|
// same number of bonded shares / tokens when exchange rate is one
|
||||||
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
assert.Equal(t, poolB.BondedShares, sdk.NewRat(poolB.BondedPool))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensUnbonded(t *testing.T) {
|
func TestAddTokensUnbonded(t *testing.T) {
|
||||||
|
@ -181,6 +223,35 @@ func TestCandidateRemoveShares(t *testing.T) {
|
||||||
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
|
assert.Equal(t, candB.Assets, candA.Assets.Sub(sdk.NewRat(10).Mul(candA.delegatorShareExRate())))
|
||||||
// conservation of tokens
|
// conservation of tokens
|
||||||
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
|
assert.Equal(t, poolB.UnbondedPool+poolB.BondedPool+coinsB, poolA.UnbondedPool+poolA.BondedPool)
|
||||||
|
|
||||||
|
// specific case from random tests
|
||||||
|
assets := sdk.NewRat(5102)
|
||||||
|
liabilities := sdk.NewRat(115)
|
||||||
|
cand := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: assets,
|
||||||
|
Liabilities: liabilities,
|
||||||
|
}
|
||||||
|
pool := Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: sdk.NewRat(248305),
|
||||||
|
UnbondedShares: sdk.NewRat(232147),
|
||||||
|
BondedPool: 248305,
|
||||||
|
UnbondedPool: 232147,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
shares := sdk.NewRat(29)
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
msg = fmt.Sprintf("Removed %v shares from %s", shares, msg)
|
||||||
|
newPool, _, tokens := pool.candidateRemoveShares(cand, shares)
|
||||||
|
require.Equal(t,
|
||||||
|
tokens+newPool.UnbondedPool+newPool.BondedPool,
|
||||||
|
pool.BondedPool+pool.UnbondedPool,
|
||||||
|
"Tokens were not conserved: %s", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////
|
/////////////////////////////////////
|
||||||
|
@ -206,7 +277,7 @@ func randomCandidate(r *rand.Rand) Candidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a random staking state
|
// generate a random staking state
|
||||||
func randomSetup(r *rand.Rand) (Pool, Candidate) {
|
func randomSetup(r *rand.Rand, numCandidates int) (Pool, Candidates) {
|
||||||
pool := Pool{
|
pool := Pool{
|
||||||
TotalSupply: 0,
|
TotalSupply: 0,
|
||||||
BondedShares: sdk.ZeroRat,
|
BondedShares: sdk.ZeroRat,
|
||||||
|
@ -217,75 +288,75 @@ func randomSetup(r *rand.Rand) (Pool, Candidate) {
|
||||||
Inflation: sdk.NewRat(7, 100),
|
Inflation: sdk.NewRat(7, 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate := randomCandidate(r)
|
candidates := make([]Candidate, numCandidates)
|
||||||
if candidate.Status == Bonded {
|
for i := 0; i < numCandidates; i++ {
|
||||||
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
|
candidate := randomCandidate(r)
|
||||||
pool.BondedPool += candidate.Assets.Evaluate()
|
if candidate.Status == Bonded {
|
||||||
} else {
|
pool.BondedShares = pool.BondedShares.Add(candidate.Assets)
|
||||||
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
|
pool.BondedPool += candidate.Assets.Evaluate()
|
||||||
pool.UnbondedPool += candidate.Assets.Evaluate()
|
} else if candidate.Status == Unbonded {
|
||||||
|
pool.UnbondedShares = pool.UnbondedShares.Add(candidate.Assets)
|
||||||
|
pool.UnbondedPool += candidate.Assets.Evaluate()
|
||||||
|
}
|
||||||
|
candidates[i] = candidate
|
||||||
}
|
}
|
||||||
return pool, candidate
|
return pool, candidates
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomTokens(r *rand.Rand) int64 {
|
// any operation that transforms staking state
|
||||||
return int64(r.Int31n(10000))
|
// takes in RNG instance, pool, candidate
|
||||||
|
// returns updated pool, updated candidate, delta tokens, descriptive message
|
||||||
|
type Operation func(r *rand.Rand, p Pool, c Candidate) (Pool, Candidate, int64, string)
|
||||||
|
|
||||||
|
// operation: bond or unbond a candidate depending on current status
|
||||||
|
func OpBondOrUnbond(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
var msg string
|
||||||
|
if cand.Status == Bonded {
|
||||||
|
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand = p.bondedToUnbondedPool(cand)
|
||||||
|
|
||||||
|
} else if cand.Status == Unbonded {
|
||||||
|
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand = p.unbondedToBondedPool(cand)
|
||||||
|
}
|
||||||
|
return p, cand, 0, msg
|
||||||
}
|
}
|
||||||
|
|
||||||
// operation that transforms staking state
|
// operation: add a random number of tokens to a candidate
|
||||||
type Operation func(p Pool, c Candidate) (Pool, Candidate, int64, string)
|
func OpAddTokens(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
tokens := int64(r.Int31n(1000))
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
p, cand, _ = p.candidateAddTokens(cand, tokens)
|
||||||
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
|
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
||||||
|
}
|
||||||
|
|
||||||
|
// operation: remove a random number of shares from a candidate
|
||||||
|
func OpRemoveShares(r *rand.Rand, p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
||||||
|
var shares sdk.Rat
|
||||||
|
for {
|
||||||
|
shares = sdk.NewRat(int64(r.Int31n(1000)))
|
||||||
|
if shares.LT(cand.Liabilities) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("Removed %v shares from candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
shares, cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
|
||||||
|
p, cand, tokens := p.candidateRemoveShares(cand, shares)
|
||||||
|
return p, cand, tokens, msg
|
||||||
|
}
|
||||||
|
|
||||||
// pick a random staking operation
|
// pick a random staking operation
|
||||||
func randomOperation(r *rand.Rand) Operation {
|
func randomOperation(r *rand.Rand) Operation {
|
||||||
operations := []Operation{
|
operations := []Operation{
|
||||||
|
OpBondOrUnbond,
|
||||||
// bond/unbond
|
OpAddTokens,
|
||||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
OpRemoveShares,
|
||||||
|
|
||||||
var msg string
|
|
||||||
if cand.Status == Bonded {
|
|
||||||
msg = fmt.Sprintf("Unbonded previously bonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
|
||||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
|
||||||
p, cand = p.bondedToUnbondedPool(cand)
|
|
||||||
} else {
|
|
||||||
msg = fmt.Sprintf("Bonded previously unbonded candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
|
||||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
|
||||||
p, cand = p.unbondedToBondedPool(cand)
|
|
||||||
}
|
|
||||||
return p, cand, 0, msg
|
|
||||||
},
|
|
||||||
|
|
||||||
// add some tokens to a candidate
|
|
||||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
|
||||||
|
|
||||||
tokens := int64(r.Int31n(1000))
|
|
||||||
|
|
||||||
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
|
||||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
|
||||||
|
|
||||||
p, cand, _ = p.candidateAddTokens(cand, tokens)
|
|
||||||
|
|
||||||
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
|
||||||
return p, cand, -1 * tokens, msg // tokens are removed so for accounting must be negative
|
|
||||||
},
|
|
||||||
|
|
||||||
// remove some shares from a candidate
|
|
||||||
func(p Pool, cand Candidate) (Pool, Candidate, int64, string) {
|
|
||||||
|
|
||||||
shares := sdk.NewRat(int64(r.Int31n(1000)))
|
|
||||||
|
|
||||||
if shares.GT(cand.Liabilities) {
|
|
||||||
shares = cand.Liabilities.Quo(sdk.NewRat(2))
|
|
||||||
}
|
|
||||||
|
|
||||||
msg := fmt.Sprintf("candidate %s (assets: %d, liabilities: %d, delegatorShareExRate: %v)",
|
|
||||||
cand.Address, cand.Assets.Evaluate(), cand.Liabilities.Evaluate(), cand.delegatorShareExRate())
|
|
||||||
p, cand, tokens := p.candidateRemoveShares(cand, shares)
|
|
||||||
|
|
||||||
msg = fmt.Sprintf("Removed %d shares from %s", shares.Evaluate(), msg)
|
|
||||||
|
|
||||||
return p, cand, tokens, msg
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
r.Shuffle(len(operations), func(i, j int) {
|
r.Shuffle(len(operations), func(i, j int) {
|
||||||
operations[i], operations[j] = operations[j], operations[i]
|
operations[i], operations[j] = operations[j], operations[i]
|
||||||
|
@ -295,80 +366,168 @@ func randomOperation(r *rand.Rand) Operation {
|
||||||
|
|
||||||
// ensure invariants that should always be true are true
|
// ensure invariants that should always be true are true
|
||||||
func assertInvariants(t *testing.T, msg string,
|
func assertInvariants(t *testing.T, msg string,
|
||||||
pOrig Pool, cOrig Candidate, pMod Pool, cMod Candidate, tokens int64) {
|
pOrig Pool, cOrig Candidates, pMod Pool, cMods Candidates, tokens int64) {
|
||||||
|
|
||||||
// total tokens conserved
|
// total tokens conserved
|
||||||
require.Equal(t,
|
require.Equal(t,
|
||||||
pOrig.UnbondedPool+pOrig.BondedPool,
|
pOrig.UnbondedPool+pOrig.BondedPool,
|
||||||
pMod.UnbondedPool+pMod.BondedPool+tokens,
|
pMod.UnbondedPool+pMod.BondedPool+tokens,
|
||||||
"msg: %v\n, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
|
"Tokens not conserved - msg: %v\n, pOrig.BondedShares: %v, pOrig.UnbondedShares: %v, pMod.BondedShares: %v, pMod.UnbondedShares: %v, pOrig.UnbondedPool: %v, pOrig.BondedPool: %v, pMod.UnbondedPool: %v, pMod.BondedPool: %v, tokens: %v\n",
|
||||||
msg,
|
msg,
|
||||||
|
pOrig.BondedShares, pOrig.UnbondedShares,
|
||||||
|
pMod.BondedShares, pMod.UnbondedShares,
|
||||||
pOrig.UnbondedPool, pOrig.BondedPool,
|
pOrig.UnbondedPool, pOrig.BondedPool,
|
||||||
pMod.UnbondedPool, pMod.BondedPool, tokens)
|
pMod.UnbondedPool, pMod.BondedPool, tokens)
|
||||||
|
|
||||||
// nonnegative shares
|
// nonnegative bonded shares
|
||||||
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat),
|
require.False(t, pMod.BondedShares.LT(sdk.ZeroRat),
|
||||||
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
|
"Negative bonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||||
msg, pOrig, pMod, cOrig, cMod, tokens)
|
msg, pOrig, pMod, tokens)
|
||||||
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat),
|
|
||||||
"msg: %v\n, pOrig: %v\n, pMod: %v\n, cOrig: %v\n, cMod %v, tokens: %v\n",
|
|
||||||
msg, pOrig, pMod, cOrig, cMod, tokens)
|
|
||||||
|
|
||||||
// nonnegative ex rates
|
// nonnegative unbonded shares
|
||||||
|
require.False(t, pMod.UnbondedShares.LT(sdk.ZeroRat),
|
||||||
|
"Negative unbonded shares - msg: %v\npOrig: %#v\npMod: %#v\ntokens: %v\n",
|
||||||
|
msg, pOrig, pMod, tokens)
|
||||||
|
|
||||||
|
// nonnegative bonded ex rate
|
||||||
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat),
|
require.False(t, pMod.bondedShareExRate().LT(sdk.ZeroRat),
|
||||||
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
"Applying operation \"%s\" resulted in negative bondedShareExRate: %d",
|
||||||
msg, pMod.bondedShareExRate().Evaluate())
|
msg, pMod.bondedShareExRate().Evaluate())
|
||||||
|
|
||||||
|
// nonnegative unbonded ex rate
|
||||||
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat),
|
require.False(t, pMod.unbondedShareExRate().LT(sdk.ZeroRat),
|
||||||
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
"Applying operation \"%s\" resulted in negative unbondedShareExRate: %d",
|
||||||
msg, pMod.unbondedShareExRate().Evaluate())
|
msg, pMod.unbondedShareExRate().Evaluate())
|
||||||
|
|
||||||
// nonnegative ex rate
|
for _, cMod := range cMods {
|
||||||
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat),
|
|
||||||
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.PubKey: %s)",
|
|
||||||
msg,
|
|
||||||
cMod.delegatorShareExRate(),
|
|
||||||
cMod.PubKey,
|
|
||||||
)
|
|
||||||
|
|
||||||
// nonnegative assets / liabilities
|
// nonnegative ex rate
|
||||||
require.False(t, cMod.Assets.LT(sdk.ZeroRat),
|
require.False(t, cMod.delegatorShareExRate().LT(sdk.ZeroRat),
|
||||||
"Applying operation \"%s\" resulted in negative candidate.Assets: %d (candidate.Liabilities: %d, candidate.PubKey: %s)",
|
"Applying operation \"%s\" resulted in negative candidate.delegatorShareExRate(): %v (candidate.Address: %s)",
|
||||||
msg,
|
msg,
|
||||||
cMod.Assets.Evaluate(),
|
cMod.delegatorShareExRate(),
|
||||||
cMod.Liabilities.Evaluate(),
|
cMod.Address,
|
||||||
cMod.PubKey,
|
)
|
||||||
)
|
|
||||||
|
// nonnegative assets
|
||||||
|
require.False(t, cMod.Assets.LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative candidate.Assets: %v (candidate.Liabilities: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||||
|
msg,
|
||||||
|
cMod.Assets,
|
||||||
|
cMod.Liabilities,
|
||||||
|
cMod.delegatorShareExRate(),
|
||||||
|
cMod.Address,
|
||||||
|
)
|
||||||
|
|
||||||
|
// nonnegative liabilities
|
||||||
|
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %v (candidate.Assets: %v, candidate.delegatorShareExRate: %v, candidate.Address: %s)",
|
||||||
|
msg,
|
||||||
|
cMod.Liabilities,
|
||||||
|
cMod.Assets,
|
||||||
|
cMod.delegatorShareExRate(),
|
||||||
|
cMod.Address,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
require.False(t, cMod.Liabilities.LT(sdk.ZeroRat),
|
|
||||||
"Applying operation \"%s\" resulted in negative candidate.Liabilities: %d (candidate.Assets: %d, candidate.PubKey: %s)",
|
|
||||||
msg,
|
|
||||||
cMod.Liabilities.Evaluate(),
|
|
||||||
cMod.Assets.Evaluate(),
|
|
||||||
cMod.PubKey,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// run random operations in a random order on a random state, assert invariants hold
|
// TODO Re-enable once the overflow bug is fixed!
|
||||||
func TestIntegrationInvariants(t *testing.T) {
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
/*
|
||||||
|
func TestPossibleOverflow(t *testing.T) {
|
||||||
|
assets := sdk.NewRat(2159)
|
||||||
|
liabilities := sdk.NewRat(391432570689183511).Quo(sdk.NewRat(40113011844664))
|
||||||
|
cand := Candidate{
|
||||||
|
Status: Bonded,
|
||||||
|
Address: addrs[0],
|
||||||
|
PubKey: pks[0],
|
||||||
|
Assets: assets,
|
||||||
|
Liabilities: liabilities,
|
||||||
|
}
|
||||||
|
pool := Pool{
|
||||||
|
TotalSupply: 0,
|
||||||
|
BondedShares: assets,
|
||||||
|
UnbondedShares: sdk.ZeroRat,
|
||||||
|
BondedPool: assets.Evaluate(),
|
||||||
|
UnbondedPool: 0,
|
||||||
|
InflationLastTime: 0,
|
||||||
|
Inflation: sdk.NewRat(7, 100),
|
||||||
|
}
|
||||||
|
tokens := int64(71)
|
||||||
|
msg := fmt.Sprintf("candidate %s (status: %d, assets: %v, liabilities: %v, delegatorShareExRate: %v)",
|
||||||
|
cand.Address, cand.Status, cand.Assets, cand.Liabilities, cand.delegatorShareExRate())
|
||||||
|
_, newCandidate, _ := pool.candidateAddTokens(cand, tokens)
|
||||||
|
|
||||||
|
msg = fmt.Sprintf("Added %d tokens to %s", tokens, msg)
|
||||||
|
require.False(t, newCandidate.delegatorShareExRate().LT(sdk.ZeroRat),
|
||||||
|
"Applying operation \"%s\" resulted in negative delegatorShareExRate(): %v",
|
||||||
|
msg, newCandidate.delegatorShareExRate())
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// run random operations in a random order on a random single-candidate state, assert invariants hold
|
||||||
|
func TestSingleCandidateIntegrationInvariants(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(41))
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
|
poolOrig, candidatesOrig := randomSetup(r, 1)
|
||||||
|
require.Equal(t, 1, len(candidatesOrig))
|
||||||
|
|
||||||
r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
// sanity check
|
||||||
pool, candidates := randomSetup(r1)
|
|
||||||
initialPool, initialCandidates := pool, candidates
|
|
||||||
|
|
||||||
assertInvariants(t, "no operation",
|
assertInvariants(t, "no operation",
|
||||||
initialPool, initialCandidates,
|
poolOrig, candidatesOrig,
|
||||||
pool, candidates, 0)
|
poolOrig, candidatesOrig, 0)
|
||||||
|
|
||||||
for j := 0; j < 100; j++ {
|
// TODO Increase iteration count once overflow bug is fixed
|
||||||
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
for j := 0; j < 4; j++ {
|
||||||
|
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[0])
|
||||||
|
|
||||||
r2 := rand.New(rand.NewSource(time.Now().UnixNano()))
|
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||||
pool, candidates, tokens, msg := randomOperation(r2)(pool, candidates)
|
copy(candidatesMod[:], candidatesOrig[:])
|
||||||
|
require.Equal(t, 1, len(candidatesOrig), "j %v", j)
|
||||||
|
require.Equal(t, 1, len(candidatesMod), "j %v", j)
|
||||||
|
candidatesMod[0] = candidateMod
|
||||||
|
|
||||||
assertInvariants(t, msg,
|
assertInvariants(t, msg,
|
||||||
initialPool, initialCandidates,
|
poolOrig, candidatesOrig,
|
||||||
pool, candidates, tokens)
|
poolMod, candidatesMod, tokens)
|
||||||
|
|
||||||
|
poolOrig = poolMod
|
||||||
|
candidatesOrig = candidatesMod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// run random operations in a random order on a random multi-candidate state, assert invariants hold
|
||||||
|
func TestMultiCandidateIntegrationInvariants(t *testing.T) {
|
||||||
|
r := rand.New(rand.NewSource(42))
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
poolOrig, candidatesOrig := randomSetup(r, 100)
|
||||||
|
|
||||||
|
assertInvariants(t, "no operation",
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolOrig, candidatesOrig, 0)
|
||||||
|
|
||||||
|
// TODO Increase iteration count once overflow bug is fixed
|
||||||
|
// ref https://github.com/cosmos/cosmos-sdk/issues/753
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
index := int(r.Int31n(int32(len(candidatesOrig))))
|
||||||
|
poolMod, candidateMod, tokens, msg := randomOperation(r)(r, poolOrig, candidatesOrig[index])
|
||||||
|
candidatesMod := make([]Candidate, len(candidatesOrig))
|
||||||
|
copy(candidatesMod[:], candidatesOrig[:])
|
||||||
|
candidatesMod[index] = candidateMod
|
||||||
|
|
||||||
|
assertInvariants(t, msg,
|
||||||
|
poolOrig, candidatesOrig,
|
||||||
|
poolMod, candidatesMod, tokens)
|
||||||
|
|
||||||
|
poolOrig = poolMod
|
||||||
|
candidatesOrig = candidatesMod
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ func (c Candidate) delegatorShareExRate() sdk.Rat {
|
||||||
// Should only be called when the Candidate qualifies as a validator.
|
// Should only be called when the Candidate qualifies as a validator.
|
||||||
func (c Candidate) validator() Validator {
|
func (c Candidate) validator() Validator {
|
||||||
return Validator{
|
return Validator{
|
||||||
Address: c.Address, // XXX !!!
|
Address: c.Address,
|
||||||
VotingPower: c.Assets,
|
VotingPower: c.Assets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue