Update stake simulation
This commit is contained in:
parent
cbc9d7d1da
commit
af206bd0ed
|
@ -0,0 +1,76 @@
|
||||||
|
package simulation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AllInvariants runs all invariants of the stake module.
|
||||||
|
// Currently: total supply, positive power
|
||||||
|
func AllInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simulation.Invariant {
|
||||||
|
return func(t *testing.T, app *baseapp.BaseApp, log string) {
|
||||||
|
SupplyInvariants(ck, k, am)(t, app, log)
|
||||||
|
PositivePowerInvariant(k)(t, app, log)
|
||||||
|
ValidatorSetInvariant(k)(t, app, log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations
|
||||||
|
func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) simulation.Invariant {
|
||||||
|
return func(t *testing.T, app *baseapp.BaseApp, log string) {
|
||||||
|
ctx := app.NewContext(false, abci.Header{})
|
||||||
|
pool := k.GetPool(ctx)
|
||||||
|
|
||||||
|
// Loose tokens should equal coin supply
|
||||||
|
loose := sdk.ZeroInt()
|
||||||
|
am.IterateAccounts(ctx, func(acc auth.Account) bool {
|
||||||
|
loose = loose.Add(acc.GetCoins().AmountOf("steak"))
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
require.True(t, pool.LooseTokens.RoundInt64() == loose.Int64(), "expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v\nlog: %s",
|
||||||
|
pool.LooseTokens, loose, log)
|
||||||
|
// stats["stake/invariant/looseTokens"] += 1
|
||||||
|
|
||||||
|
// Bonded tokens should equal sum of tokens with bonded validators
|
||||||
|
bonded := sdk.ZeroRat()
|
||||||
|
k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool {
|
||||||
|
switch validator.GetStatus() {
|
||||||
|
case sdk.Bonded:
|
||||||
|
bonded = bonded.Add(validator.GetPower())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
require.True(t, pool.BondedTokens.Equal(bonded), "expected bonded tokens to equal total steak held by bonded validators\nlog: %s", log)
|
||||||
|
// stats["stake/invariant/bondedTokens"] += 1
|
||||||
|
|
||||||
|
// TODO Inflation check on total supply
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PositivePowerInvariant checks that all stored validators have > 0 power
|
||||||
|
func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
|
return func(t *testing.T, app *baseapp.BaseApp, log string) {
|
||||||
|
ctx := app.NewContext(false, abci.Header{})
|
||||||
|
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool {
|
||||||
|
require.True(t, validator.GetPower().GT(sdk.ZeroRat()), "validator with non-positive power stored")
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
// stats["stake/invariant/positivePower"] += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set
|
||||||
|
func ValidatorSetInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
|
return func(t *testing.T, app *baseapp.BaseApp, log string) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,19 @@
|
||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"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/mock/simulation"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
@ -20,73 +23,12 @@ var (
|
||||||
stats = make(map[string]int)
|
stats = make(map[string]int)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ModuleInvariants runs all invariants of the stake module.
|
|
||||||
// Currently: total supply, positive power
|
|
||||||
func ModuleInvariants(ck bank.Keeper, k stake.Keeper) mock.Invariant {
|
|
||||||
return func(t *testing.T, app *mock.App, log string) {
|
|
||||||
SupplyInvariants(ck, k)(t, app, log)
|
|
||||||
PositivePowerInvariant(k)(t, app, log)
|
|
||||||
ValidatorSetInvariant(k)(t, app, log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SupplyInvariants checks that the total supply reflects all held loose tokens, bonded tokens, and unbonding delegations
|
|
||||||
func SupplyInvariants(ck bank.Keeper, k stake.Keeper) mock.Invariant {
|
|
||||||
return func(t *testing.T, app *mock.App, log string) {
|
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
pool := k.GetPool(ctx)
|
|
||||||
|
|
||||||
// Loose tokens should equal coin supply
|
|
||||||
loose := sdk.ZeroInt()
|
|
||||||
app.AccountMapper.IterateAccounts(ctx, func(acc auth.Account) bool {
|
|
||||||
loose = loose.Add(acc.GetCoins().AmountOf("steak"))
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
require.True(t, pool.LooseTokens.RoundInt64() == loose.Int64(), "expected loose tokens to equal total steak held by accounts - pool.LooseTokens: %v, sum of account tokens: %v\nlog: %s",
|
|
||||||
pool.LooseTokens, loose, log)
|
|
||||||
stats["stake/invariant/looseTokens"] += 1
|
|
||||||
|
|
||||||
// Bonded tokens should equal sum of tokens with bonded validators
|
|
||||||
bonded := sdk.ZeroRat()
|
|
||||||
k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool {
|
|
||||||
switch validator.GetStatus() {
|
|
||||||
case sdk.Bonded:
|
|
||||||
bonded = bonded.Add(validator.GetPower())
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
require.True(t, pool.BondedTokens.Equal(bonded), "expected bonded tokens to equal total steak held by bonded validators\nlog: %s", log)
|
|
||||||
stats["stake/invariant/bondedTokens"] += 1
|
|
||||||
|
|
||||||
// TODO Inflation check on total supply
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PositivePowerInvariant checks that all stored validators have > 0 power
|
|
||||||
func PositivePowerInvariant(k stake.Keeper) mock.Invariant {
|
|
||||||
return func(t *testing.T, app *mock.App, log string) {
|
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool {
|
|
||||||
require.True(t, validator.GetPower().GT(sdk.ZeroRat()), "validator with non-positive power stored")
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
stats["stake/invariant/positivePower"] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set
|
|
||||||
func ValidatorSetInvariant(k stake.Keeper) mock.Invariant {
|
|
||||||
return func(t *testing.T, app *mock.App, log string) {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimulateMsgCreateValidator
|
// SimulateMsgCreateValidator
|
||||||
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
denom := k.GetParams(ctx).BondDenom
|
denom := k.GetParams(ctx).BondDenom
|
||||||
description := stake.Description{
|
description := stake.Description{
|
||||||
Moniker: mock.RandStringOfLength(r, 10),
|
Moniker: simulation.RandStringOfLength(r, 10),
|
||||||
}
|
}
|
||||||
key := keys[r.Intn(len(keys))]
|
key := keys[r.Intn(len(keys))]
|
||||||
pubkey := key.PubKey()
|
pubkey := key.PubKey()
|
||||||
|
@ -119,13 +61,13 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) mock.TestA
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgEditValidator
|
// SimulateMsgEditValidator
|
||||||
func SimulateMsgEditValidator(k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgEditValidator(k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
description := stake.Description{
|
description := stake.Description{
|
||||||
Moniker: mock.RandStringOfLength(r, 10),
|
Moniker: simulation.RandStringOfLength(r, 10),
|
||||||
Identity: mock.RandStringOfLength(r, 10),
|
Identity: simulation.RandStringOfLength(r, 10),
|
||||||
Website: mock.RandStringOfLength(r, 10),
|
Website: simulation.RandStringOfLength(r, 10),
|
||||||
Details: mock.RandStringOfLength(r, 10),
|
Details: simulation.RandStringOfLength(r, 10),
|
||||||
}
|
}
|
||||||
key := keys[r.Intn(len(keys))]
|
key := keys[r.Intn(len(keys))]
|
||||||
pubkey := key.PubKey()
|
pubkey := key.PubKey()
|
||||||
|
@ -147,8 +89,8 @@ func SimulateMsgEditValidator(k stake.Keeper) mock.TestAndRunMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgDelegate
|
// SimulateMsgDelegate
|
||||||
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
denom := k.GetParams(ctx).BondDenom
|
denom := k.GetParams(ctx).BondDenom
|
||||||
validatorKey := keys[r.Intn(len(keys))]
|
validatorKey := keys[r.Intn(len(keys))]
|
||||||
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
||||||
|
@ -179,8 +121,8 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) mock.TestAndRunMs
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgBeginUnbonding
|
// SimulateMsgBeginUnbonding
|
||||||
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
denom := k.GetParams(ctx).BondDenom
|
denom := k.GetParams(ctx).BondDenom
|
||||||
validatorKey := keys[r.Intn(len(keys))]
|
validatorKey := keys[r.Intn(len(keys))]
|
||||||
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
||||||
|
@ -211,8 +153,8 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) mock.TestAn
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgCompleteUnbonding
|
// SimulateMsgCompleteUnbonding
|
||||||
func SimulateMsgCompleteUnbonding(k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
validatorKey := keys[r.Intn(len(keys))]
|
validatorKey := keys[r.Intn(len(keys))]
|
||||||
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
validatorAddress := sdk.AccAddress(validatorKey.PubKey().Address())
|
||||||
delegatorKey := keys[r.Intn(len(keys))]
|
delegatorKey := keys[r.Intn(len(keys))]
|
||||||
|
@ -234,8 +176,8 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) mock.TestAndRunMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgBeginRedelegate
|
// SimulateMsgBeginRedelegate
|
||||||
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
denom := k.GetParams(ctx).BondDenom
|
denom := k.GetParams(ctx).BondDenom
|
||||||
sourceValidatorKey := keys[r.Intn(len(keys))]
|
sourceValidatorKey := keys[r.Intn(len(keys))]
|
||||||
sourceValidatorAddress := sdk.AccAddress(sourceValidatorKey.PubKey().Address())
|
sourceValidatorAddress := sdk.AccAddress(sourceValidatorKey.PubKey().Address())
|
||||||
|
@ -270,8 +212,8 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) mock.TestA
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulateMsgCompleteRedelegate
|
// SimulateMsgCompleteRedelegate
|
||||||
func SimulateMsgCompleteRedelegate(k stake.Keeper) mock.TestAndRunMsg {
|
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.TestAndRunTx {
|
||||||
return func(t *testing.T, r *rand.Rand, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string) (action string, err sdk.Error) {
|
||||||
validatorSrcKey := keys[r.Intn(len(keys))]
|
validatorSrcKey := keys[r.Intn(len(keys))]
|
||||||
validatorSrcAddress := sdk.AccAddress(validatorSrcKey.PubKey().Address())
|
validatorSrcAddress := sdk.AccAddress(validatorSrcKey.PubKey().Address())
|
||||||
validatorDstKey := keys[r.Intn(len(keys))]
|
validatorDstKey := keys[r.Intn(len(keys))]
|
||||||
|
@ -296,7 +238,7 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) mock.TestAndRunMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SimulationSetup
|
// SimulationSetup
|
||||||
func SimulationSetup(mapp *mock.App, k stake.Keeper) mock.RandSetup {
|
func SimulationSetup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
|
||||||
return func(r *rand.Rand, privKeys []crypto.PrivKey) {
|
return func(r *rand.Rand, privKeys []crypto.PrivKey) {
|
||||||
ctx := mapp.NewContext(false, abci.Header{})
|
ctx := mapp.NewContext(false, abci.Header{})
|
||||||
stake.InitGenesis(ctx, k, stake.DefaultGenesisState())
|
stake.InitGenesis(ctx, k, stake.DefaultGenesisState())
|
||||||
|
@ -338,8 +280,9 @@ func TestStakeWithRandomMessages(t *testing.T) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapp.SimpleRandomizedTestingFromSeed(
|
simulation.Simulate(
|
||||||
t, 20, []mock.TestAndRunMsg{
|
t, mapp.BaseApp, json.RawMessage("{}"),
|
||||||
|
[]simulation.TestAndRunTx{
|
||||||
SimulateMsgCreateValidator(mapper, stakeKeeper),
|
SimulateMsgCreateValidator(mapper, stakeKeeper),
|
||||||
SimulateMsgEditValidator(stakeKeeper),
|
SimulateMsgEditValidator(stakeKeeper),
|
||||||
SimulateMsgDelegate(mapper, stakeKeeper),
|
SimulateMsgDelegate(mapper, stakeKeeper),
|
||||||
|
@ -348,10 +291,10 @@ func TestStakeWithRandomMessages(t *testing.T) {
|
||||||
SimulateMsgCompleteUnbonding(stakeKeeper),
|
SimulateMsgCompleteUnbonding(stakeKeeper),
|
||||||
SimulateMsgBeginRedelegate(mapper, stakeKeeper),
|
SimulateMsgBeginRedelegate(mapper, stakeKeeper),
|
||||||
SimulateMsgCompleteRedelegate(stakeKeeper),
|
SimulateMsgCompleteRedelegate(stakeKeeper),
|
||||||
}, []mock.RandSetup{
|
}, []simulation.RandSetup{
|
||||||
SimulationSetup(mapp, stakeKeeper),
|
SimulationSetup(mapp, stakeKeeper),
|
||||||
}, []mock.Invariant{
|
}, []simulation.Invariant{
|
||||||
ModuleInvariants(coinKeeper, stakeKeeper),
|
AllInvariants(coinKeeper, stakeKeeper, mapp.AccountMapper),
|
||||||
}, 10, 100, 500,
|
}, 10, 100, 500,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue