Skip to content

Commit

Permalink
Merge PR cosmos#5378: application interfaces for simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
fedekunze authored and alexanderbez committed Dec 17, 2019
1 parent 17d5f4f commit 2e61d4e
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 285 deletions.
11 changes: 11 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ func MakeCodec() *codec.Codec {
return cdc
}

// Verify app interface at compile time
var _ App = (*SimApp)(nil)

// SimApp extends an ABCI application, but with most of its parameters exported.
// They are exported for convenience in creating helper functions, as object
// capabilities aren't needed for testing.
Expand Down Expand Up @@ -295,6 +298,9 @@ func NewSimApp(
return app
}

// Name returns the name of the App
func (app *SimApp) Name() string { return app.BaseApp.Name() }

// BeginBlocker application updates every begin block
func (app *SimApp) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return app.mm.BeginBlock(ctx, req)
Expand Down Expand Up @@ -366,6 +372,11 @@ func (app *SimApp) GetSubspace(moduleName string) params.Subspace {
return app.subspaces[moduleName]
}

// SimulationManager implements the SimulationApp interface
func (app *SimApp) SimulationManager() *module.SimulationManager {
return app.sm
}

// GetMaccPerms returns a copy of the module account permissions
func GetMaccPerms() map[string][]string {
dupMaccPerms := make(map[string][]string)
Expand Down
2 changes: 1 addition & 1 deletion app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestSimAppExport(t *testing.T) {
app := NewSimApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, 0)

genesisState := NewDefaultGenesisState()
stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState)
stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState)
require.NoError(t, err)

// Initialize the chain
Expand Down
75 changes: 75 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package simapp

import (
"flag"

"github.com/cosmos/cosmos-sdk/x/simulation"
)

// List of available flags for the simulator
var (
FlagGenesisFileValue string
FlagParamsFileValue string
FlagExportParamsPathValue string
FlagExportParamsHeightValue int
FlagExportStatePathValue string
FlagExportStatsPathValue string
FlagSeedValue int64
FlagInitialBlockHeightValue int
FlagNumBlocksValue int
FlagBlockSizeValue int
FlagLeanValue bool
FlagCommitValue bool
FlagOnOperationValue bool // TODO: Remove in favor of binary search for invariant violation
FlagAllInvariantsValue bool

FlagEnabledValue bool
FlagVerboseValue bool
FlagPeriodValue uint
FlagGenesisTimeValue int64
)

// GetSimulatorFlags gets the values of all the available simulation flags
func GetSimulatorFlags() {
// config fields
flag.StringVar(&FlagGenesisFileValue, "Genesis", "", "custom simulation genesis file; cannot be used with params file")
flag.StringVar(&FlagParamsFileValue, "Params", "", "custom simulation params file which overrides any random params; cannot be used with genesis")
flag.StringVar(&FlagExportParamsPathValue, "ExportParamsPath", "", "custom file path to save the exported params JSON")
flag.IntVar(&FlagExportParamsHeightValue, "ExportParamsHeight", 0, "height to which export the randomly generated params")
flag.StringVar(&FlagExportStatePathValue, "ExportStatePath", "", "custom file path to save the exported app state JSON")
flag.StringVar(&FlagExportStatsPathValue, "ExportStatsPath", "", "custom file path to save the exported simulation statistics JSON")
flag.Int64Var(&FlagSeedValue, "Seed", 42, "simulation random seed")
flag.IntVar(&FlagInitialBlockHeightValue, "InitialBlockHeight", 1, "initial block to start the simulation")
flag.IntVar(&FlagNumBlocksValue, "NumBlocks", 500, "number of new blocks to simulate from the initial block height")
flag.IntVar(&FlagBlockSizeValue, "BlockSize", 200, "operations per block")
flag.BoolVar(&FlagLeanValue, "Lean", false, "lean simulation log output")
flag.BoolVar(&FlagCommitValue, "Commit", false, "have the simulation commit")
flag.BoolVar(&FlagOnOperationValue, "SimulateEveryOperation", false, "run slow invariants every operation")
flag.BoolVar(&FlagAllInvariantsValue, "PrintAllInvariants", false, "print all invariants if a broken invariant is found")

// simulation flags
flag.BoolVar(&FlagEnabledValue, "Enabled", false, "enable the simulation")
flag.BoolVar(&FlagVerboseValue, "Verbose", false, "verbose log output")
flag.UintVar(&FlagPeriodValue, "Period", 0, "run slow invariants only once every period assertions")
flag.Int64Var(&FlagGenesisTimeValue, "GenesisTime", 0, "override genesis UNIX time instead of using a random UNIX time")
}

// NewConfigFromFlags creates a simulation from the retrieved values of the flags.
func NewConfigFromFlags() simulation.Config {
return simulation.Config{
GenesisFile: FlagGenesisFileValue,
ParamsFile: FlagParamsFileValue,
ExportParamsPath: FlagExportParamsPathValue,
ExportParamsHeight: FlagExportParamsHeightValue,
ExportStatePath: FlagExportStatePathValue,
ExportStatsPath: FlagExportStatsPathValue,
Seed: FlagSeedValue,
InitialBlockHeight: FlagInitialBlockHeightValue,
NumBlocks: FlagNumBlocksValue,
BlockSize: FlagBlockSizeValue,
Lean: FlagLeanValue,
Commit: FlagCommitValue,
OnOperation: FlagOnOperationValue,
AllInvariants: FlagAllInvariantsValue,
}
}
2 changes: 1 addition & 1 deletion params.go → params/params.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package simapp
package params

// Simulation parameter constants
const (
Expand Down
97 changes: 37 additions & 60 deletions sim_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,113 +2,88 @@ package simapp

import (
"fmt"
"io/ioutil"
"os"
"testing"

abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/simapp/helpers"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/simulation"
abci "github.com/tendermint/tendermint/abci/types"
)

// Profile with:
// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out
func BenchmarkFullAppSimulation(b *testing.B) {
logger := log.NewNopLogger()
config := NewConfigFromFlags()
config.ChainID = helpers.SimAppChainID
config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation")
if err != nil {
b.Fatalf("simulation setup failed: %s", err.Error())
}

var db dbm.DB
dir, _ := ioutil.TempDir("", "goleveldb-app-sim")
db, _ = sdk.NewLevelDB("Simulation", dir)
defer func() {
db.Close()
os.RemoveAll(dir)
err = os.RemoveAll(dir)
if err != nil {
b.Fatal(err)
}
}()

app := NewSimApp(logger, db, nil, true, FlagPeriodValue, interBlockCacheOpt())

// Run randomized simulation
// TODO: parameterize numbers, save for a later PR
// run randomized simulation
_, simParams, simErr := simulation.SimulateFromSeed(
b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.sm),
b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()),
SimulationOperations(app, app.Codec(), config),
app.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
if err := ExportStateToJSON(app, config.ExportStatePath); err != nil {
fmt.Println(err)
b.Fail()
}
}

if config.ExportParamsPath != "" {
if err := ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil {
fmt.Println(err)
b.Fail()
}
// export state and simParams before the simulation error is checked
if err = CheckExportSimulation(app, config, simParams); err != nil {
b.Fatal(err)
}

if simErr != nil {
fmt.Println(simErr)
b.FailNow()
b.Fatal(simErr)
}

if config.Commit {
fmt.Println("\nGoLevelDB Stats")
fmt.Println(db.Stats()["leveldb.stats"])
fmt.Println("GoLevelDB cached block size", db.Stats()["leveldb.cachedblock"])
PrintStats(db)
}
}

func BenchmarkInvariants(b *testing.B) {
logger := log.NewNopLogger()
config, db, dir, logger, _, err := SetupSimulation("leveldb-app-invariant-bench", "Simulation")
if err != nil {
b.Fatalf("simulation setup failed: %s", err.Error())
}

config := NewConfigFromFlags()
config.AllInvariants = false
config.ChainID = helpers.SimAppChainID

dir, _ := ioutil.TempDir("", "goleveldb-app-invariant-bench")
db, _ := sdk.NewLevelDB("simulation", dir)

defer func() {
db.Close()
os.RemoveAll(dir)
err = os.RemoveAll(dir)
if err != nil {
b.Fatal(err)
}
}()

app := NewSimApp(logger, db, nil, true, FlagPeriodValue, interBlockCacheOpt())

// 2. Run parameterized simulation (w/o invariants)
// run randomized simulation
_, simParams, simErr := simulation.SimulateFromSeed(
b, ioutil.Discard, app.BaseApp, AppStateFn(app.Codec(), app.sm),
b, os.Stdout, app.BaseApp, AppStateFn(app.Codec(), app.SimulationManager()),
SimulationOperations(app, app.Codec(), config),
app.ModuleAccountAddrs(), config,
)

// export state and params before the simulation error is checked
if config.ExportStatePath != "" {
if err := ExportStateToJSON(app, config.ExportStatePath); err != nil {
fmt.Println(err)
b.Fail()
}
// export state and simParams before the simulation error is checked
if err = CheckExportSimulation(app, config, simParams); err != nil {
b.Fatal(err)
}

if config.ExportParamsPath != "" {
if err := ExportParamsToJSON(simParams, config.ExportParamsPath); err != nil {
fmt.Println(err)
b.Fail()
}
if simErr != nil {
b.Fatal(simErr)
}

if simErr != nil {
fmt.Println(simErr)
b.FailNow()
if config.Commit {
PrintStats(db)
}

ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight() + 1})
Expand All @@ -121,8 +96,10 @@ func BenchmarkInvariants(b *testing.B) {
cr := cr
b.Run(fmt.Sprintf("%s/%s", cr.ModuleName, cr.Route), func(b *testing.B) {
if res, stop := cr.Invar(ctx); stop {
fmt.Printf("broken invariant at block %d of %d\n%s", ctx.BlockHeight()-1, config.NumBlocks, res)
b.FailNow()
b.Fatalf(
"broken invariant at block %d of %d\n%s",
ctx.BlockHeight()-1, config.NumBlocks, res,
)
}
})
}
Expand Down
Loading

0 comments on commit 2e61d4e

Please sign in to comment.