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_cli
|
||||||
make test_examples
|
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:
|
test_sim_gaia_nondeterminism:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
parallelism: 1
|
parallelism: 1
|
||||||
|
@ -307,9 +289,6 @@ workflows:
|
||||||
- integration_tests:
|
- integration_tests:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
- test_sim_modules:
|
|
||||||
requires:
|
|
||||||
- setup_dependencies
|
|
||||||
- test_sim_gaia_nondeterminism:
|
- test_sim_gaia_nondeterminism:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -164,29 +164,25 @@ test_unit:
|
||||||
test_race:
|
test_race:
|
||||||
@VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION)
|
@VERSION=$(VERSION) go test -race $(PACKAGES_NOSIMULATION)
|
||||||
|
|
||||||
test_sim_modules:
|
|
||||||
@echo "Running individual module simulations..."
|
|
||||||
@go test $(PACKAGES_SIMTEST)
|
|
||||||
|
|
||||||
test_sim_gaia_nondeterminism:
|
test_sim_gaia_nondeterminism:
|
||||||
@echo "Running nondeterminism test..."
|
@echo "Running nondeterminism test..."
|
||||||
@go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m
|
@go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m
|
||||||
|
|
||||||
test_sim_gaia_fast:
|
test_sim_gaia_fast:
|
||||||
@echo "Running quick Gaia simulation. This may take several minutes..."
|
@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:
|
test_sim_gaia_import_export:
|
||||||
@echo "Running Gaia import/export simulation. This may take several minutes..."
|
@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:
|
test_sim_gaia_simulation_after_import:
|
||||||
@echo "Running Gaia simulation-after-import. This may take several minutes..."
|
@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:
|
test_sim_gaia_multi_seed:
|
||||||
@echo "Running multi-seed Gaia simulation. This may take awhile!"
|
@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_NUM_BLOCKS ?= 500
|
||||||
SIM_BLOCK_SIZE ?= 200
|
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.
|
- [#110](https://github.com/tendermint/devops/issues/110) Updated CircleCI job to trigger website build when cosmos docs are updated.
|
||||||
|
|
||||||
* SDK
|
* SDK
|
||||||
|
- [x/mock/simulation] \#2832, \#2885, \#2873, \#2902 Simulation cleanup
|
||||||
- [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization
|
- [x/mock/simulation] [\#2720] major cleanup, introduction of helper objects, reorganization
|
||||||
- \#2821 Codespaces are now strings
|
- \#2821 Codespaces are now strings
|
||||||
- [types] #2776 Improve safety of `Coin` and `Coins` types. Various functions
|
- [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"
|
distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||||
stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation"
|
stakesim "github.com/cosmos/cosmos-sdk/x/stake/simulation"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *GaiaApp) runtimeInvariants() []simulation.Invariant {
|
func (app *GaiaApp) runtimeInvariants() []simulation.Invariant {
|
||||||
|
@ -23,8 +24,9 @@ func (app *GaiaApp) runtimeInvariants() []simulation.Invariant {
|
||||||
func (app *GaiaApp) assertRuntimeInvariants() {
|
func (app *GaiaApp) assertRuntimeInvariants() {
|
||||||
invariants := app.runtimeInvariants()
|
invariants := app.runtimeInvariants()
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1})
|
||||||
for _, inv := range invariants {
|
for _, inv := range invariants {
|
||||||
if err := inv(app.BaseApp); err != nil {
|
if err := inv(ctx); err != nil {
|
||||||
panic(fmt.Errorf("invariant broken: %s", err))
|
panic(fmt.Errorf("invariant broken: %s", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ var (
|
||||||
enabled bool
|
enabled bool
|
||||||
verbose bool
|
verbose bool
|
||||||
commit bool
|
commit bool
|
||||||
|
period int
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -49,6 +50,7 @@ func init() {
|
||||||
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
|
flag.BoolVar(&enabled, "SimulationEnabled", false, "Enable the simulation")
|
||||||
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
|
flag.BoolVar(&verbose, "SimulationVerbose", false, "Verbose log output")
|
||||||
flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit")
|
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 {
|
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 {
|
func invariants(app *GaiaApp) []simulation.Invariant {
|
||||||
return []simulation.Invariant{
|
return []simulation.Invariant{
|
||||||
banksim.NonnegativeBalanceInvariant(app.accountKeeper),
|
simulation.PeriodicInvariant(banksim.NonnegativeBalanceInvariant(app.accountKeeper), period, 0),
|
||||||
govsim.AllInvariants(),
|
simulation.PeriodicInvariant(govsim.AllInvariants(), period, 0),
|
||||||
distrsim.AllInvariants(app.distrKeeper, app.stakeKeeper),
|
simulation.PeriodicInvariant(distrsim.AllInvariants(app.distrKeeper, app.stakeKeeper), period, 0),
|
||||||
stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper,
|
simulation.PeriodicInvariant(stakesim.AllInvariants(app.bankKeeper, app.stakeKeeper,
|
||||||
app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper),
|
app.feeCollectionKeeper, app.distrKeeper, app.accountKeeper), period, 0),
|
||||||
slashingsim.AllInvariants(),
|
simulation.PeriodicInvariant(slashingsim.AllInvariants(), period, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +221,6 @@ func BenchmarkFullGaiaSimulation(b *testing.B) {
|
||||||
_, err := simulation.SimulateFromSeed(
|
_, err := simulation.SimulateFromSeed(
|
||||||
b, app.BaseApp, appStateFn, seed,
|
b, app.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(app),
|
testAndRunTxs(app),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
invariants(app), // these shouldn't get ran
|
invariants(app), // these shouldn't get ran
|
||||||
numBlocks,
|
numBlocks,
|
||||||
blockSize,
|
blockSize,
|
||||||
|
@ -262,7 +263,6 @@ func TestFullGaiaSimulation(t *testing.T) {
|
||||||
_, err := simulation.SimulateFromSeed(
|
_, err := simulation.SimulateFromSeed(
|
||||||
t, app.BaseApp, appStateFn, seed,
|
t, app.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(app),
|
testAndRunTxs(app),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
invariants(app),
|
invariants(app),
|
||||||
numBlocks,
|
numBlocks,
|
||||||
blockSize,
|
blockSize,
|
||||||
|
@ -304,7 +304,6 @@ func TestGaiaImportExport(t *testing.T) {
|
||||||
_, err := simulation.SimulateFromSeed(
|
_, err := simulation.SimulateFromSeed(
|
||||||
t, app.BaseApp, appStateFn, seed,
|
t, app.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(app),
|
testAndRunTxs(app),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
invariants(app),
|
invariants(app),
|
||||||
numBlocks,
|
numBlocks,
|
||||||
blockSize,
|
blockSize,
|
||||||
|
@ -401,7 +400,6 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
|
||||||
stopEarly, err := simulation.SimulateFromSeed(
|
stopEarly, err := simulation.SimulateFromSeed(
|
||||||
t, app.BaseApp, appStateFn, seed,
|
t, app.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(app),
|
testAndRunTxs(app),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
invariants(app),
|
invariants(app),
|
||||||
numBlocks,
|
numBlocks,
|
||||||
blockSize,
|
blockSize,
|
||||||
|
@ -447,7 +445,6 @@ func TestGaiaSimulationAfterImport(t *testing.T) {
|
||||||
_, err = simulation.SimulateFromSeed(
|
_, err = simulation.SimulateFromSeed(
|
||||||
t, newApp.BaseApp, appStateFn, seed,
|
t, newApp.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(newApp),
|
testAndRunTxs(newApp),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
invariants(newApp),
|
invariants(newApp),
|
||||||
numBlocks,
|
numBlocks,
|
||||||
blockSize,
|
blockSize,
|
||||||
|
@ -479,13 +476,11 @@ func TestAppStateDeterminism(t *testing.T) {
|
||||||
simulation.SimulateFromSeed(
|
simulation.SimulateFromSeed(
|
||||||
t, app.BaseApp, appStateFn, seed,
|
t, app.BaseApp, appStateFn, seed,
|
||||||
testAndRunTxs(app),
|
testAndRunTxs(app),
|
||||||
[]simulation.RandSetup{},
|
|
||||||
[]simulation.Invariant{},
|
[]simulation.Invariant{},
|
||||||
50,
|
50,
|
||||||
100,
|
100,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
//app.Commit()
|
|
||||||
appHash := app.LastCommitID().Hash
|
appHash := app.LastCommitID().Hash
|
||||||
appHashList[j] = appHash
|
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 \
|
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)
|
11 22 44 77 99 2020 3232 123123 124124 582582 18931893 29892989 30123012 47284728 37827)
|
||||||
blocks=$1
|
blocks=$1
|
||||||
|
testname=$2
|
||||||
|
|
||||||
echo "Running multi-seed simulation with seeds ${seeds[@]}"
|
echo "Running multi-seed simulation with seeds ${seeds[@]}"
|
||||||
echo "Running $blocks blocks per seed"
|
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 "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)."
|
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() {
|
sim() {
|
||||||
seed=$1
|
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"
|
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout"
|
||||||
echo "Writing stdout to $file..."
|
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
|
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -v -timeout 24h > $file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +46,7 @@ for pid in ${pids[*]}; do
|
||||||
if [ $last -ne 0 ]
|
if [ $last -ne 0 ]
|
||||||
then
|
then
|
||||||
echo "Simulation with seed $seed failed!"
|
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
|
code=1
|
||||||
else
|
else
|
||||||
echo "Simulation with seed $seed OK"
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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/mock"
|
"github.com/cosmos/cosmos-sdk/x/mock"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"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
|
// NonnegativeBalanceInvariant checks that all accounts in the application have non-negative balances
|
||||||
func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant {
|
func NonnegativeBalanceInvariant(mapper auth.AccountKeeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
accts := mock.GetAllAccounts(mapper, ctx)
|
accts := mock.GetAllAccounts(mapper, ctx)
|
||||||
for _, acc := range accts {
|
for _, acc := range accts {
|
||||||
coins := acc.GetCoins()
|
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
|
// TotalCoinsInvariant checks that the sum of the coins across all accounts
|
||||||
// is what is expected
|
// is what is expected
|
||||||
func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant {
|
func TotalCoinsInvariant(mapper auth.AccountKeeper, totalSupplyFn func() sdk.Coins) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
totalCoins := sdk.Coins{}
|
totalCoins := sdk.Coins{}
|
||||||
|
|
||||||
chkAccount := func(acc auth.Account) bool {
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
distr "github.com/cosmos/cosmos-sdk/x/distribution"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllInvariants runs all invariants of the distribution module
|
// AllInvariants runs all invariants of the distribution module
|
||||||
// Currently: total supply, positive power
|
// Currently: total supply, positive power
|
||||||
func AllInvariants(d distr.Keeper, stk stake.Keeper) simulation.Invariant {
|
func AllInvariants(d distr.Keeper, stk stake.Keeper) simulation.Invariant {
|
||||||
sk := distr.StakeKeeper(stk)
|
sk := distr.StakeKeeper(stk)
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
err := ValAccumInvariants(d, sk)(app)
|
err := ValAccumInvariants(d, sk)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = DelAccumInvariants(d, sk)(app)
|
err = DelAccumInvariants(d, sk)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = CanWithdrawInvariant(d, stk)(app)
|
err = CanWithdrawInvariant(d, stk)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// ValAccumInvariants checks that the fee pool accum == sum all validators' accum
|
||||||
func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
func ValAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
||||||
|
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
|
||||||
ctx := app.NewContext(false, mockHeader)
|
|
||||||
height := ctx.BlockHeight()
|
height := ctx.BlockHeight()
|
||||||
|
|
||||||
valAccum := sdk.ZeroDec()
|
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
|
// DelAccumInvariants checks that each validator del accum == sum all delegators' accum
|
||||||
func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
func DelAccumInvariants(k distr.Keeper, sk distr.StakeKeeper) simulation.Invariant {
|
||||||
|
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
|
||||||
ctx := app.NewContext(false, mockHeader)
|
|
||||||
height := ctx.BlockHeight()
|
height := ctx.BlockHeight()
|
||||||
|
|
||||||
totalDelAccumFromVal := make(map[string]sdk.Dec) // key is the valOpAddr string
|
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
|
// CanWithdrawInvariant checks that current rewards can be completely withdrawn
|
||||||
func CanWithdrawInvariant(k distr.Keeper, sk stake.Keeper) simulation.Invariant {
|
func CanWithdrawInvariant(k distr.Keeper, sk stake.Keeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
mockHeader := abci.Header{Height: app.LastBlockHeight() + 1}
|
|
||||||
ctx := app.NewContext(false, mockHeader)
|
|
||||||
|
|
||||||
// we don't want to write the changes
|
// we don't want to write the changes
|
||||||
ctx, _ = ctx.CacheContext()
|
ctx, _ = ctx.CacheContext()
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllInvariants tests all governance invariants
|
// AllInvariants tests all governance invariants
|
||||||
func AllInvariants() simulation.Invariant {
|
func AllInvariants() simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
// TODO Add some invariants!
|
// TODO Add some invariants!
|
||||||
// Checking proposal queues, no passed-but-unexecuted proposals, etc.
|
// Checking proposal queues, no passed-but-unexecuted proposals, etc.
|
||||||
return nil
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
"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.
|
// An Invariant is a function which tests a particular invariant.
|
||||||
// If the invariant has been broken, it should return an error
|
// If the invariant has been broken, it should return an error
|
||||||
// containing a descriptive message about what happened.
|
// containing a descriptive message about what happened.
|
||||||
// The simulator will then halt and print the logs.
|
// 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
|
// group of Invarient
|
||||||
type Invariants []Invariant
|
type Invariants []Invariant
|
||||||
|
@ -20,8 +22,10 @@ type Invariants []Invariant
|
||||||
func (invs Invariants) assertAll(t *testing.T, app *baseapp.BaseApp,
|
func (invs Invariants) assertAll(t *testing.T, app *baseapp.BaseApp,
|
||||||
event string, displayLogs func()) {
|
event string, displayLogs func()) {
|
||||||
|
|
||||||
|
ctx := app.NewContext(false, abci.Header{Height: app.LastBlockHeight() + 1})
|
||||||
|
|
||||||
for i := 0; i < len(invs); i++ {
|
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())
|
fmt.Printf("Invariants broken after %s\n%s\n", event, err.Error())
|
||||||
displayLogs()
|
displayLogs()
|
||||||
t.Fatal()
|
t.Fatal()
|
||||||
|
|
|
@ -18,25 +18,22 @@ import (
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
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
|
// AppStateFn returns the app state json bytes
|
||||||
type AppStateFn func(r *rand.Rand, accs []Account) json.RawMessage
|
type AppStateFn func(r *rand.Rand, accs []Account) json.RawMessage
|
||||||
|
|
||||||
// Simulate tests application by sending random messages.
|
// Simulate tests application by sending random messages.
|
||||||
func Simulate(t *testing.T, app *baseapp.BaseApp,
|
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) {
|
invariants Invariants, numBlocks int, blockSize int, commit bool) (bool, error) {
|
||||||
|
|
||||||
time := time.Now().UnixNano()
|
time := time.Now().UnixNano()
|
||||||
return SimulateFromSeed(t, app, appStateFn, time, ops,
|
return SimulateFromSeed(t, app, appStateFn, time, ops,
|
||||||
setups, invariants, numBlocks, blockSize, commit)
|
invariants, numBlocks, blockSize, commit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize the chain for the simulation
|
// initialize the chain for the simulation
|
||||||
func initChain(r *rand.Rand, params Params, accounts []Account,
|
func initChain(r *rand.Rand, params Params, accounts []Account,
|
||||||
setups []RandSetup, app *baseapp.BaseApp,
|
app *baseapp.BaseApp,
|
||||||
appStateFn AppStateFn) mockValidators {
|
appStateFn AppStateFn) mockValidators {
|
||||||
|
|
||||||
req := abci.RequestInitChain{
|
req := abci.RequestInitChain{
|
||||||
|
@ -45,9 +42,6 @@ func initChain(r *rand.Rand, params Params, accounts []Account,
|
||||||
res := app.InitChain(req)
|
res := app.InitChain(req)
|
||||||
validators := newMockValidators(r, res.Validators, params)
|
validators := newMockValidators(r, res.Validators, params)
|
||||||
|
|
||||||
for i := 0; i < len(setups); i++ {
|
|
||||||
setups[i](r, accounts)
|
|
||||||
}
|
|
||||||
return validators
|
return validators
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +50,7 @@ func initChain(r *rand.Rand, params Params, accounts []Account,
|
||||||
// TODO split this monster function up
|
// TODO split this monster function up
|
||||||
func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
|
||||||
appStateFn AppStateFn, seed int64, ops WeightedOperations,
|
appStateFn AppStateFn, seed int64, ops WeightedOperations,
|
||||||
setups []RandSetup, invariants Invariants,
|
invariants Invariants,
|
||||||
numBlocks int, blockSize int, commit bool) (stopEarly bool, simError error) {
|
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.
|
// 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
|
// Second variable to keep pending validator set (delayed one block since
|
||||||
// TM 0.24) Initially this is the same as the initial validator set
|
// 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
|
nextValidators := validators
|
||||||
|
|
||||||
header := abci.Header{
|
header := abci.Header{
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
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.
|
// computationally heavy simulations.
|
||||||
// TODO reference this function in the codebase probably through use of a switch
|
// TODO reference this function in the codebase probably through use of a switch
|
||||||
func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant {
|
func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
if int(app.LastBlockHeight())%period == offset {
|
if int(ctx.BlockHeight())%period == offset {
|
||||||
return invariant(app)
|
return invariant(ctx)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO Any invariants to check here?
|
// TODO Any invariants to check here?
|
||||||
// AllInvariants tests all slashing invariants
|
// AllInvariants tests all slashing invariants
|
||||||
func AllInvariants() simulation.Invariant {
|
func AllInvariants() simulation.Invariant {
|
||||||
return func(_ *baseapp.BaseApp) error {
|
return func(_ sdk.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -13,7 +12,6 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||||
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
stakeTypes "github.com/cosmos/cosmos-sdk/x/stake/types"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AllInvariants runs all invariants of the stake module.
|
// 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,
|
f auth.FeeCollectionKeeper, d distribution.Keeper,
|
||||||
am auth.AccountKeeper) simulation.Invariant {
|
am auth.AccountKeeper) simulation.Invariant {
|
||||||
|
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
err := SupplyInvariants(ck, k, f, d, am)(app)
|
err := SupplyInvariants(ck, k, f, d, am)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = PositivePowerInvariant(k)(app)
|
err = PositivePowerInvariant(k)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = PositiveDelegationInvariant(k)(app)
|
err = PositiveDelegationInvariant(k)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = DelegatorSharesInvariant(k)(app)
|
err = DelegatorSharesInvariant(k)(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ValidatorSetInvariant(k)(app)
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +49,7 @@ func AllInvariants(ck bank.Keeper, k stake.Keeper,
|
||||||
// nolint: unparam
|
// nolint: unparam
|
||||||
func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
||||||
f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant {
|
f auth.FeeCollectionKeeper, d distribution.Keeper, am auth.AccountKeeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
pool := k.GetPool(ctx)
|
pool := k.GetPool(ctx)
|
||||||
|
|
||||||
loose := sdk.ZeroDec()
|
loose := sdk.ZeroDec()
|
||||||
|
@ -117,9 +113,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper,
|
||||||
|
|
||||||
// PositivePowerInvariant checks that all stored validators have > 0 power.
|
// PositivePowerInvariant checks that all stored validators have > 0 power.
|
||||||
func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
func PositivePowerInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
|
|
||||||
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
iterator := k.ValidatorsPowerStoreIterator(ctx)
|
||||||
pool := k.GetPool(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.
|
// PositiveDelegationInvariant checks that all stored delegations have > 0 shares.
|
||||||
func PositiveDelegationInvariant(k stake.Keeper) simulation.Invariant {
|
func PositiveDelegationInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
|
|
||||||
delegations := k.GetAllDelegations(ctx)
|
delegations := k.GetAllDelegations(ctx)
|
||||||
for _, delegation := range delegations {
|
for _, delegation := range delegations {
|
||||||
if delegation.Shares.IsNegative() {
|
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
|
// in the delegator object add up to the correct total delegator shares
|
||||||
// amount stored in each validator
|
// amount stored in each validator
|
||||||
func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant {
|
func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
return func(app *baseapp.BaseApp) error {
|
return func(ctx sdk.Context) error {
|
||||||
ctx := app.NewContext(false, abci.Header{})
|
|
||||||
|
|
||||||
validators := k.GetAllValidators(ctx)
|
validators := k.GetAllValidators(ctx)
|
||||||
for _, validator := range validators {
|
for _, validator := range validators {
|
||||||
|
|
||||||
|
@ -187,11 +177,3 @@ func DelegatorSharesInvariant(k stake.Keeper) simulation.Invariant {
|
||||||
return nil
|
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"
|
"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/mock"
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake"
|
"github.com/cosmos/cosmos-sdk/x/stake"
|
||||||
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
"github.com/cosmos/cosmos-sdk/x/stake/keeper"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SimulateMsgCreateValidator
|
// SimulateMsgCreateValidator
|
||||||
|
@ -228,26 +226,3 @@ func SimulateMsgBeginRedelegate(m auth.AccountKeeper, k stake.Keeper) simulation
|
||||||
return action, nil, nil
|
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