Merge PR #2930: Simulation spring cleaning
* Update PENDING.md * Add simple period for expensive invariants * Remove individual module simulations * Simulate a few more blocks * Add README explaining reason for shell scripts * Deduplicate scripts, log exact replication command on failure * Refactor invariants to take sdk.Context instead of baseapp.BaseApp * Reference all issues in PENDING.md entry * Remove no longer used simulation.RandSetup * Bug fixes * Address @rigelrozanski comments * Fix typo
This commit is contained in:
parent
5d700a597c
commit
b2b026b5e0
|
@ -90,24 +90,6 @@ jobs:
|
|||
make test_cli
|
||||
make test_examples
|
||||
|
||||
test_sim_modules:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- checkout
|
||||
- run:
|
||||
name: dependencies
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make get_vendor_deps
|
||||
- run:
|
||||
name: Test individual module simulations
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make test_sim_modules
|
||||
|
||||
test_sim_gaia_nondeterminism:
|
||||
<<: *defaults
|
||||
parallelism: 1
|
||||
|
@ -307,9 +289,6 @@ workflows:
|
|||
- integration_tests:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_sim_modules:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_sim_gaia_nondeterminism:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
|
|
12
Makefile
12
Makefile
|
@ -164,29 +164,25 @@ test_unit:
|
|||
test_race:
|
||||
@VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION)
|
||||
|
||||
test_sim_modules:
|
||||
@echo "Running individual module simulations..."
|
||||
@go test $(PACKAGES_SIMTEST)
|
||||
|
||||
test_sim_gaia_nondeterminism:
|
||||
@echo "Running nondeterminism test..."
|
||||
@go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m
|
||||
|
||||
test_sim_gaia_fast:
|
||||
@echo "Running quick Gaia simulation. This may take several minutes..."
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=500 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -v -timeout 24h
|
||||
@go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -v -timeout 24h
|
||||
|
||||
test_sim_gaia_import_export:
|
||||
@echo "Running Gaia import/export simulation. This may take several minutes..."
|
||||
@bash scripts/import-export-sim.sh 50
|
||||
@bash scripts/multisim.sh 50 TestGaiaImportExport
|
||||
|
||||
test_sim_gaia_simulation_after_import:
|
||||
@echo "Running Gaia simulation-after-import. This may take several minutes..."
|
||||
@bash scripts/simulation-after-import.sh 50
|
||||
@bash scripts/multisim.sh 50 TestGaiaSimulationAfterImport
|
||||
|
||||
test_sim_gaia_multi_seed:
|
||||
@echo "Running multi-seed Gaia simulation. This may take awhile!"
|
||||
@bash scripts/multisim.sh 25
|
||||
@bash scripts/multisim.sh 50 TestFullGaiaSimulation
|
||||
|
||||
SIM_NUM_BLOCKS ?= 500
|
||||
SIM_BLOCK_SIZE ?= 200
|
||||
|
|
|
@ -74,6 +74,7 @@ IMPROVEMENTS
|
|||
- [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated.
|
||||
|
||||
* SDK
|
||||
- [x/mock/simulation] \#2832, \#2885, \#2873, \#2902 Simulation cleanup
|
||||
- [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization
|
||||
- \#2821 Codespaces are now strings
|
||||
- [types] #2776 Improve safety of `Coin` and `Coins` types. Various functions
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
func (app *GaiaApp) runtimeInvariants() []simulation.Invariant {
|
||||
|
@ -23,8 +24,9 @@ func (app *GaiaApp) runtimeInvariants() []simulation.Invariant {
|
|||
func (app *GaiaApp) assertRuntimeInvariants() {
|
||||
invariants := app.runtimeInvariants()
|
||||
start := time.Now()
|
||||
ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1})
|
||||
for _, inv := range invariants {
|
||||
if err := inv(app.BaseApp); err != nil {
|
||||
if err := inv(ctx); err != nil {
|
||||
panic(fmt.Errorf("invariant broken: %s", err))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ var (
|
|||
enabled bool
|
||||
verbose bool
|
||||
commit bool
|
||||
period int
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -49,6 +50,7 @@ func init() {
|
|||
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
|
||||
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
|
||||
flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit")
|
||||
flag.IntVar(&period, "SimulationPeriod", 100, "Run slow invariants only once every period assertions")
|
||||
}
|
||||
|
||||
func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
|
@ -185,12 +187,12 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
|
|||
|
||||
func invariants(app *GaiaApp) []simulation.Invariant {
|
||||
return []simulation.Invariant{
|
||||
banksim.NonnegativeBalanceInvariant(app.accountKeeper),
|
||||
govsim.AllInvariants(),
|
||||
distrsim.AllInvariants(app.distrKeeper, app.stakeKeeper),
|
||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper,
|
||||
app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
|
||||
slashingsim.AllInvariants(),
|
||||
simulation.PeriodicInvariant(banksim.NonnegativeBalanceInvariant(app.accountKeeper), period, 0),
|
||||
simulation.PeriodicInvariant(govsim.AllInvariants(), period, 0),
|
||||
simulation.PeriodicInvariant(distrsim.AllInvariants(app.distrKeeper, app.stakeKeeper), period, 0),
|
||||
simulation.PeriodicInvariant(stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper,
|
||||
app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper), period, 0),
|
||||
simulation.PeriodicInvariant(slashingsim.AllInvariants(), period, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +221,6 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
|
|||
_, err := simulation.SimulateFromSeed(
|
||||
b, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(app), // these shouldn't get ran
|
||||
numBlocks,
|
||||
blockSize,
|
||||
|
@ -262,7 +263,6 @@ func TestFullGaiaSimulation(t *testing.T) {
|
|||
_, err := simulation.SimulateFromSeed(
|
||||
t, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(app),
|
||||
numBlocks,
|
||||
blockSize,
|
||||
|
@ -304,7 +304,6 @@ func TestGaiaImportExport(t *testing.T) {
|
|||
_, err := simulation.SimulateFromSeed(
|
||||
t, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(app),
|
||||
numBlocks,
|
||||
blockSize,
|
||||
|
@ -401,7 +400,6 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
|
|||
stopEarly, err := simulation.SimulateFromSeed(
|
||||
t, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(app),
|
||||
numBlocks,
|
||||
blockSize,
|
||||
|
@ -447,7 +445,6 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
|
|||
_, err = simulation.SimulateFromSeed(
|
||||
t, newApp.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(newApp),
|
||||
[]simulation.RandSetup{},
|
||||
invariants(newApp),
|
||||
numBlocks,
|
||||
blockSize,
|
||||
|
@ -479,13 +476,11 @@ func TestAppStateDeterminism(t *testing.T) {
|
|||
simulation.SimulateFromSeed(
|
||||
t, app.BaseApp, appStateFn, seed,
|
||||
testAndRunTxs(app),
|
||||
[]simulation.RandSetup{},
|
||||
[]simulation.Invariant{},
|
||||
50,
|
||||
100,
|
||||
true,
|
||||
)
|
||||
//app.Commit()
|
||||
appHash := app.LastCommitID().Hash
|
||||
appHashList[j] = appHash
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Generally we should avoid shell scripting and write tests purely in Golang.
|
||||
However, some libraries are not Goroutine-safe (e.g. app simulations cannot be run safely in parallel),
|
||||
and OS-native threading may be more efficient for many parallel simulations, so we use shell scripts here.
|
|
@ -1,54 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \
|
||||
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
|
||||
blocks=$1
|
||||
|
||||
echo "Running multi-seed import-export simulation with seeds ${seeds[@]}"
|
||||
echo "Running $blocks blocks per seed"
|
||||
echo "Edit scripts/import-export-sim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce."
|
||||
echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)."
|
||||
|
||||
trap 'kill $(jobs -pr)' SIGINT SIGTERM
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
echo "Using temporary log directory: $tmpdir"
|
||||
|
||||
sim() {
|
||||
seed=$1
|
||||
echo "Running import/export Gaia simulation with seed $seed. This may take awhile!"
|
||||
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout"
|
||||
echo "Writing stdout to $file..."
|
||||
go test ./cmd/gaia/app -run TestGaiaImportExport -SimulationEnabled=true -SimulationNumBlocks=$blocks \
|
||||
-SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
||||
}
|
||||
|
||||
i=0
|
||||
pids=()
|
||||
for seed in ${seeds[@]}; do
|
||||
sim $seed &
|
||||
pids[${i}]=$!
|
||||
i=$(($i+1))
|
||||
sleep 10 # start in order, nicer logs
|
||||
done
|
||||
|
||||
echo "Simulation processes spawned, waiting for completion..."
|
||||
|
||||
code=0
|
||||
|
||||
i=0
|
||||
for pid in ${pids[*]}; do
|
||||
wait $pid
|
||||
last=$?
|
||||
seed=${seeds[${i}]}
|
||||
if [ $last -ne 0 ]
|
||||
then
|
||||
echo "Import/export simulation with seed $seed failed!"
|
||||
code=1
|
||||
else
|
||||
echo "Import/export simulation with seed $seed OK"
|
||||
fi
|
||||
i=$(($i+1))
|
||||
done
|
||||
|
||||
exit $code
|
|
@ -3,9 +3,11 @@
|
|||
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \
|
||||
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
|
||||
blocks=$1
|
||||
testname=$2
|
||||
|
||||
echo "Running multi-seed simulation with seeds ${seeds[@]}"
|
||||
echo "Running $blocks blocks per seed"
|
||||
echo "Running test $testname"
|
||||
echo "Edit scripts/multisim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce."
|
||||
echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)."
|
||||
|
||||
|
@ -16,10 +18,10 @@ echo "Using temporary log directory: $tmpdir"
|
|||
|
||||
sim() {
|
||||
seed=$1
|
||||
echo "Running full Gaia simulation with seed $seed. This may take awhile!"
|
||||
echo "Running Gaia simulation with seed $seed. This may take awhile!"
|
||||
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout"
|
||||
echo "Writing stdout to $file..."
|
||||
go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=$blocks \
|
||||
go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks \
|
||||
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
||||
}
|
||||
|
||||
|
@ -44,6 +46,7 @@ for pid in ${pids[*]}; do
|
|||
if [ $last -ne 0 ]
|
||||
then
|
||||
echo "Simulation with seed $seed failed!"
|
||||
echo "To replicate, run 'go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h'"
|
||||
code=1
|
||||
else
|
||||
echo "Simulation with seed $seed OK"
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823782 989182 89182391 \
|
||||
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
|
||||
blocks=$1
|
||||
|
||||
echo "Running multi-seed import-export simulation with seeds ${seeds[@]}"
|
||||
echo "Running $blocks blocks per seed"
|
||||
echo "Edit scripts/simulation-after-import.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce."
|
||||
echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)."
|
||||
|
||||
trap 'kill $(jobs -pr)' SIGINT SIGTERM
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
echo "Using temporary log directory: $tmpdir"
|
||||
|
||||
sim() {
|
||||
seed=$1
|
||||
echo "Running simulation after import with seed $seed. This may take awhile!"
|
||||
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -Iseconds -u).stdout"
|
||||
echo "Writing stdout to $file..."
|
||||
go test ./cmd/gaia/app -run TestGaiaSimulationAfterImport -SimulationEnabled=true -SimulationNumBlocks=$blocks \
|
||||
-SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
||||
}
|
||||
|
||||
i=0
|
||||
pids=()
|
||||
for seed in ${seeds[@]}; do
|
||||
sim $seed &
|
||||
pids[${i}]=$!
|
||||
i=$(($i+1))
|
||||
sleep 10 # start in order, nicer logs
|
||||
done
|
||||
|
||||
echo "Simulation processes spawned, waiting for completion..."
|
||||
|
||||
code=0
|
||||
|
||||
i=0
|
||||
for pid in ${pids[*]}; do
|
||||
wait $pid
|
||||
last=$?
|
||||
seed=${seeds[${i}]}
|
||||
if [ $last -ne 0 ]
|
||||
then
|
||||
echo "Import/export simulation with seed $seed failed!"
|
||||
code=1
|
||||
else
|
||||
echo "Import/export simulation with seed $seed OK"
|
||||
fi
|
||||
i=$(($i+1))
|
||||
done
|
||||
|
||||
exit $code
|
|
@ -4,18 +4,15 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
|
||||
func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
return func(ctx sdk.Context) error {
|
||||
accts := mock.GetAllAccounts(mapper, ctx)
|
||||
for _, acc := range accts {
|
||||
coins := acc.GetCoins()
|
||||
|
@ -32,8 +29,7 @@ func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant
|
|||
// TotalCoinsInvariant checks that the sum of the coins across all accounts
|
||||
// is what is expected
|
||||
func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
return func(ctx sdk.Context) error {
|
||||
totalCoins := sdk.Coins{}
|
||||
|
||||
chkAccount := func(acc auth.Account) bool {
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
)
|
||||
|
||||
func TestBankWithRandomMessages(t *testing.T) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
bank.RegisterCodec(mapp.Cdc)
|
||||
mapper := mapp.AccountKeeper
|
||||
bankKeeper := bank.NewBaseKeeper(mapper)
|
||||
mapp.Router().AddRoute("bank", bank.NewHandler(bankKeeper))
|
||||
|
||||
err := mapp.CompleteSetup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"})
|
||||
return json.RawMessage("{}")
|
||||
}
|
||||
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.WeightedOperation{
|
||||
{1, SingleInputSendTx(mapper)},
|
||||
{1, SingleInputSendMsg(mapper, bankKeeper)},
|
||||
},
|
||||
[]simulation.RandSetup{},
|
||||
[]simulation.Invariant{
|
||||
NonnegativeBalanceInvariant(mapper),
|
||||
TotalCoinsInvariant(mapper, func() sdk.Coins { return mapp.TotalCoinsSupply }),
|
||||
},
|
||||
30, 60,
|
||||
false,
|
||||
)
|
||||
}
|
|
@ -3,28 +3,26 @@ package simulation
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"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 distribution module
|
||||
// Currently: total supply, positive power
|
||||
func AllInvariants(d distr.Keeper, stk stake.Keeper) simulation.Invariant {
|
||||
sk := distr.StakeKeeper(stk)
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
err := ValAccumInvariants(d, sk)(app)
|
||||
return func(ctx sdk.Context) error {
|
||||
err := ValAccumInvariants(d, sk)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = DelAccumInvariants(d, sk)(app)
|
||||
err = DelAccumInvariants(d, sk)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = CanWithdrawInvariant(d, stk)(app)
|
||||
err = CanWithdrawInvariant(d, stk)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -35,9 +33,7 @@ func AllInvariants(d distr.Keeper, stk stake.Keeper) simulation.Invariant {
|
|||
// ValAccumInvariants checks that the fee pool accum == sum all validators' accum
|
||||
func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
||||
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
||||
ctx := app.NewContext(false, mockHeader)
|
||||
return func(ctx sdk.Context) error {
|
||||
height := ctx.BlockHeight()
|
||||
|
||||
valAccum := sdk.ZeroDec()
|
||||
|
@ -62,9 +58,7 @@ func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invaria
|
|||
// DelAccumInvariants checks that each validator del accum == sum all delegators' accum
|
||||
func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
||||
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
||||
ctx := app.NewContext(false, mockHeader)
|
||||
return func(ctx sdk.Context) error {
|
||||
height := ctx.BlockHeight()
|
||||
|
||||
totalDelAccumFromVal := make(map[string]sdk.Dec) // key is the valOpAddr string
|
||||
|
@ -139,10 +133,7 @@ func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invaria
|
|||
|
||||
// CanWithdrawInvariant checks that current rewards can be completely withdrawn
|
||||
func CanWithdrawInvariant(k distr.Keeper, sk stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
||||
ctx := app.NewContext(false, mockHeader)
|
||||
|
||||
return func(ctx sdk.Context) error {
|
||||
// we don't want to write the changes
|
||||
ctx, _ = ctx.CacheContext()
|
||||
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
)
|
||||
|
||||
// AllInvariants tests all governance invariants
|
||||
func AllInvariants() simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
return func(ctx sdk.Context) error {
|
||||
// TODO Add some invariants!
|
||||
// Checking proposal queues, no passed-but-unexecuted proposals, etc.
|
||||
return nil
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// TestGovWithRandomMessages
|
||||
func TestGovWithRandomMessages(t *testing.T) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
bank.RegisterCodec(mapp.Cdc)
|
||||
gov.RegisterCodec(mapp.Cdc)
|
||||
mapper := mapp.AccountKeeper
|
||||
|
||||
bankKeeper := bank.NewBaseKeeper(mapper)
|
||||
stakeKey := sdk.NewKVStoreKey("stake")
|
||||
stakeTKey := sdk.NewTransientStoreKey("transient_stake")
|
||||
paramKey := sdk.NewKVStoreKey("params")
|
||||
paramTKey := sdk.NewTransientStoreKey("transient_params")
|
||||
paramKeeper := params.NewKeeper(mapp.Cdc, paramKey, paramTKey)
|
||||
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, paramKeeper.Subspace(stake.DefaultParamspace), stake.DefaultCodespace)
|
||||
govKey := sdk.NewKVStoreKey("gov")
|
||||
govKeeper := gov.NewKeeper(mapp.Cdc, govKey, paramKeeper, paramKeeper.Subspace(gov.DefaultParamspace), bankKeeper, stakeKeeper, gov.DefaultCodespace)
|
||||
mapp.Router().AddRoute("gov", gov.NewHandler(govKeeper))
|
||||
mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
gov.EndBlocker(ctx, govKeeper)
|
||||
return abci.ResponseEndBlock{}
|
||||
})
|
||||
|
||||
err := mapp.CompleteSetup(stakeKey, stakeTKey, paramKey, paramTKey, govKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"})
|
||||
return json.RawMessage("{}")
|
||||
}
|
||||
|
||||
setup := func(r *rand.Rand, accs []simulation.Account) {
|
||||
ctx := mapp.NewContext(false, abci.Header{})
|
||||
stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState())
|
||||
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())
|
||||
}
|
||||
|
||||
// Test with unscheduled votes
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.WeightedOperation{
|
||||
{2, SimulateMsgSubmitProposal(govKeeper)},
|
||||
{3, SimulateMsgDeposit(govKeeper)},
|
||||
{20, SimulateMsgVote(govKeeper)},
|
||||
}, []simulation.RandSetup{
|
||||
setup,
|
||||
}, []simulation.Invariant{
|
||||
AllInvariants(),
|
||||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
|
||||
// Test with scheduled votes
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.WeightedOperation{
|
||||
{10, SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper)},
|
||||
{5, SimulateMsgDeposit(govKeeper)},
|
||||
}, []simulation.RandSetup{
|
||||
setup,
|
||||
}, []simulation.Invariant{
|
||||
AllInvariants(),
|
||||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
}
|
|
@ -5,13 +5,15 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// An Invariant is a function which tests a particular invariant.
|
||||
// If the invariant has been broken, it should return an error
|
||||
// containing a descriptive message about what happened.
|
||||
// The simulator will then halt and print the logs.
|
||||
type Invariant func(app *baseapp.BaseApp) error
|
||||
type Invariant func(ctx sdk.Context) error
|
||||
|
||||
// group of Invarient
|
||||
type Invariants []Invariant
|
||||
|
@ -20,8 +22,10 @@ type Invariants []Invariant
|
|||
func (invs Invariants) assertAll(t *testing.T, app *baseapp.BaseApp,
|
||||
event string, displayLogs func()) {
|
||||
|
||||
ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1})
|
||||
|
||||
for i := 0; i < len(invs); i++ {
|
||||
if err := invs[i](app); err != nil {
|
||||
if err := invs[i](ctx); err != nil {
|
||||
fmt.Printf("Invariants broken after %s\n%s\n", event, err.Error())
|
||||
displayLogs()
|
||||
t.Fatal()
|
||||
|
|
|
@ -18,25 +18,22 @@ import (
|
|||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// RandSetup performs the random setup the mock module needs.
|
||||
type RandSetup func(r *rand.Rand, accounts []Account)
|
||||
|
||||
// AppStateFn returns the app state json bytes
|
||||
type AppStateFn func(r *rand.Rand, accs []Account) json.RawMessage
|
||||
|
||||
// Simulate tests application by sending random messages.
|
||||
func Simulate(t *testing.T, app *baseapp.BaseApp,
|
||||
appStateFn AppStateFn, ops WeightedOperations, setups []RandSetup,
|
||||
appStateFn AppStateFn, ops WeightedOperations,
|
||||
invariants Invariants, numBlocks int, blockSize int, commit bool) (bool, error) {
|
||||
|
||||
time := time.Now().UnixNano()
|
||||
return SimulateFromSeed(t, app, appStateFn, time, ops,
|
||||
setups, invariants, numBlocks, blockSize, commit)
|
||||
invariants, numBlocks, blockSize, commit)
|
||||
}
|
||||
|
||||
// initialize the chain for the simulation
|
||||
func initChain(r *rand.Rand, params Params, accounts []Account,
|
||||
setups []RandSetup, app *baseapp.BaseApp,
|
||||
app *baseapp.BaseApp,
|
||||
appStateFn AppStateFn) mockValidators {
|
||||
|
||||
req := abci.RequestInitChain{
|
||||
|
@ -45,9 +42,6 @@ func initChain(r *rand.Rand, params Params, accounts []Account,
|
|||
res := app.InitChain(req)
|
||||
validators := newMockValidators(r, res.Validators, params)
|
||||
|
||||
for i := 0; i < len(setups); i++ {
|
||||
setups[i](r, accounts)
|
||||
}
|
||||
return validators
|
||||
}
|
||||
|
||||
|
@ -56,7 +50,7 @@ func initChain(r *rand.Rand, params Params, accounts []Account,
|
|||
// TODO split this monster function up
|
||||
func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
||||
appStateFn AppStateFn, seed int64, ops WeightedOperations,
|
||||
setups []RandSetup, invariants Invariants,
|
||||
invariants Invariants,
|
||||
numBlocks int, blockSize int, commit bool) (stopEarly bool, simError error) {
|
||||
|
||||
// in case we have to end early, don't os.Exit so that we can run cleanup code.
|
||||
|
@ -78,7 +72,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
|||
|
||||
// Second variable to keep pending validator set (delayed one block since
|
||||
// TM 0.24) Initially this is the same as the initial validator set
|
||||
validators := initChain(r, params, accs, setups, app, appStateFn)
|
||||
validators := initChain(r, params, accs, app, appStateFn)
|
||||
nextValidators := validators
|
||||
|
||||
header := abci.Header{
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func getTestingMode(tb testing.TB) (testingMode bool, t *testing.T, b *testing.B) {
|
||||
|
@ -113,9 +113,9 @@ func getBlockSize(r *rand.Rand, params Params,
|
|||
// computationally heavy simulations.
|
||||
// TODO reference this function in the codebase probably through use of a switch
|
||||
func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
if int(app.LastBlockHeight())%period == offset {
|
||||
return invariant(app)
|
||||
return func(ctx sdk.Context) error {
|
||||
if int(ctx.BlockHeight())%period == offset {
|
||||
return invariant(ctx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
)
|
||||
|
||||
// TODO Any invariants to check here?
|
||||
// AllInvariants tests all slashing invariants
|
||||
func AllInvariants() simulation.Invariant {
|
||||
return func(_ *baseapp.BaseApp) error {
|
||||
return func(_ sdk.Context) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"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"
|
||||
|
@ -13,7 +12,6 @@ import (
|
|||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// AllInvariants runs all invariants of the stake module.
|
||||
|
@ -22,29 +20,28 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper,
|
|||
f auth.FeeCollectionKeeper, d distribution.Keeper,
|
||||
am auth.AccountKeeper) simulation.Invariant {
|
||||
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
err := SupplyInvariants(ck, k, f, d, am)(app)
|
||||
return func(ctx sdk.Context) error {
|
||||
err := SupplyInvariants(ck, k, f, d, am)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = PositivePowerInvariant(k)(app)
|
||||
err = PositivePowerInvariant(k)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = PositiveDelegationInvariant(k)(app)
|
||||
err = PositiveDelegationInvariant(k)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = DelegatorSharesInvariant(k)(app)
|
||||
err = DelegatorSharesInvariant(k)(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ValidatorSetInvariant(k)(app)
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,8 +49,7 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper,
|
|||
// nolint: unparam
|
||||
func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
||||
f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
return func(ctx sdk.Context) error {
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
loose := sdk.ZeroDec()
|
||||
|
@ -117,9 +113,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
|||
|
||||
// PositivePowerInvariant checks that all stored validators have > 0 power.
|
||||
func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
|
||||
return func(ctx sdk.Context) error {
|
||||
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
|
@ -143,9 +137,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
|||
|
||||
// PositiveDelegationInvariant checks that all stored delegations have > 0 shares.
|
||||
func PositiveDelegationInvariant(k stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
|
||||
return func(ctx sdk.Context) error {
|
||||
delegations := k.GetAllDelegations(ctx)
|
||||
for _, delegation := range delegations {
|
||||
if delegation.Shares.IsNegative() {
|
||||
|
@ -164,9 +156,7 @@ func PositiveDelegationInvariant(k stake.Keeper) simulation.Invariant {
|
|||
// in the delegator object add up to the correct total delegator shares
|
||||
// amount stored in each validator
|
||||
func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
ctx := app.NewContext(false, abci.Header{})
|
||||
|
||||
return func(ctx sdk.Context) error {
|
||||
validators := k.GetAllValidators(ctx)
|
||||
for _, validator := range validators {
|
||||
|
||||
|
@ -187,11 +177,3 @@ func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ValidatorSetInvariant checks equivalence of Tendermint validator set and SDK validator set
|
||||
func ValidatorSetInvariant(k stake.Keeper) simulation.Invariant {
|
||||
return func(app *baseapp.BaseApp) error {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,9 @@ import (
|
|||
"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/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
)
|
||||
|
||||
// SimulateMsgCreateValidator
|
||||
|
@ -228,26 +226,3 @@ func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k stake.Keeper) simulation
|
|||
return action, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Setup
|
||||
// nolint: errcheck
|
||||
func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {
|
||||
return func(r *rand.Rand, accs []simulation.Account) {
|
||||
ctx := mapp.NewContext(false, abci.Header{})
|
||||
gen := stake.DefaultGenesisState()
|
||||
stake.InitGenesis(ctx, k, gen)
|
||||
params := k.GetParams(ctx)
|
||||
denom := params.BondDenom
|
||||
loose := sdk.ZeroInt()
|
||||
mapp.AccountKeeper.IterateAccounts(ctx, func(acc auth.Account) bool {
|
||||
balance := simulation.RandomAmount(r, sdk.NewInt(1000000))
|
||||
acc.SetCoins(acc.GetCoins().Plus(sdk.Coins{sdk.NewCoin(denom, balance)}))
|
||||
mapp.AccountKeeper.SetAccount(ctx, acc)
|
||||
loose = loose.Add(balance)
|
||||
return false
|
||||
})
|
||||
pool := k.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Add(sdk.NewDec(loose.Int64()))
|
||||
k.SetPool(ctx, pool)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
package simulation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
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/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||
)
|
||||
|
||||
// TestStakeWithRandomMessages
|
||||
func TestStakeWithRandomMessages(t *testing.T) {
|
||||
mapp := mock.NewApp()
|
||||
|
||||
bank.RegisterCodec(mapp.Cdc)
|
||||
mapper := mapp.AccountKeeper
|
||||
bankKeeper := bank.NewBaseKeeper(mapper)
|
||||
feeKey := sdk.NewKVStoreKey("fee")
|
||||
stakeKey := sdk.NewKVStoreKey("stake")
|
||||
stakeTKey := sdk.NewTransientStoreKey("transient_stake")
|
||||
paramsKey := sdk.NewKVStoreKey("params")
|
||||
paramsTKey := sdk.NewTransientStoreKey("transient_params")
|
||||
distrKey := sdk.NewKVStoreKey("distr")
|
||||
|
||||
feeCollectionKeeper := auth.NewFeeCollectionKeeper(mapp.Cdc, feeKey)
|
||||
paramstore := params.NewKeeper(mapp.Cdc, paramsKey, paramsTKey)
|
||||
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, paramstore.Subspace(stake.DefaultParamspace), stake.DefaultCodespace)
|
||||
distrKeeper := distribution.NewKeeper(mapp.Cdc, distrKey, paramstore.Subspace(distribution.DefaultParamspace), bankKeeper, stakeKeeper, feeCollectionKeeper, distribution.DefaultCodespace)
|
||||
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
|
||||
mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
validatorUpdates, tags := stake.EndBlocker(ctx, stakeKeeper)
|
||||
return abci.ResponseEndBlock{
|
||||
ValidatorUpdates: validatorUpdates,
|
||||
Tags: tags,
|
||||
}
|
||||
})
|
||||
|
||||
err := mapp.CompleteSetup(stakeKey, stakeTKey, paramsKey, paramsTKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage {
|
||||
simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"})
|
||||
return json.RawMessage("{}")
|
||||
}
|
||||
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.WeightedOperation{
|
||||
{10, SimulateMsgCreateValidator(mapper, stakeKeeper)},
|
||||
{5, SimulateMsgEditValidator(stakeKeeper)},
|
||||
{15, SimulateMsgDelegate(mapper, stakeKeeper)},
|
||||
{10, SimulateMsgBeginUnbonding(mapper, stakeKeeper)},
|
||||
{10, SimulateMsgBeginRedelegate(mapper, stakeKeeper)},
|
||||
}, []simulation.RandSetup{
|
||||
Setup(mapp, stakeKeeper),
|
||||
}, []simulation.Invariant{
|
||||
AllInvariants(bankKeeper, stakeKeeper, feeCollectionKeeper, distrKeeper, mapp.AccountKeeper),
|
||||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue