From 8e75823b4c87f669f4e8685f507cfc21bb087ab7 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 29 Jan 2019 23:53:42 +0100 Subject: [PATCH] Merge PR #3428: Simulate from a genesis file --- Makefile | 23 ++++++++++++--- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 53 +++++++++++++++++++++++++++++------ scripts/multisim.sh | 4 ++- x/mock/simulation/simulate.go | 19 +++++++++---- 5 files changed, 80 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 2f8086fd7ae4..222b824de1b1 100644 --- a/Makefile +++ b/Makefile @@ -150,9 +150,15 @@ test_sim_gaia_nondeterminism: @echo "Running nondeterminism test..." @go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m +test_sim_gaia_custom_genesis_fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationGenesis=${HOME}/.gaiad/config/genesis.json \ + -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h + test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h test_sim_gaia_import_export: @echo "Running Gaia import/export simulation. This may take several minutes..." @@ -162,6 +168,11 @@ test_sim_gaia_simulation_after_import: @echo "Running Gaia simulation-after-import. This may take several minutes..." @bash scripts/multisim.sh 50 5 TestGaiaSimulationAfterImport +test_sim_gaia_custom_genesis_multi_seed: + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation ${HOME}/.gaiad/config/genesis.json + test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation @@ -171,11 +182,13 @@ SIM_BLOCK_SIZE ?= 200 SIM_COMMIT ?= true test_sim_gaia_benchmark: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h + @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h test_sim_gaia_profile: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out + @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out test_cover: @export VERSION=$(VERSION); bash tests/test_cover.sh @@ -243,4 +256,6 @@ check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ -test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools +test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ +test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ +build-snap-edge diff --git a/PENDING.md b/PENDING.md index 4d87301f0d5b..6d3851f364ad 100644 --- a/PENDING.md +++ b/PENDING.md @@ -20,6 +20,7 @@ FEATURES * Gaia CLI (`gaiacli`) * Gaia + * \#3428 Run the simulation from a particular genesis state loaded from a file * SDK diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index b88e809eba6e..dd2796fdb79f 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -13,8 +13,10 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -35,16 +37,18 @@ import ( ) var ( - seed int64 - numBlocks int - blockSize int - enabled bool - verbose bool - commit bool - period int + genesisFile string + seed int64 + numBlocks int + blockSize int + enabled bool + verbose bool + commit bool + period int ) func init() { + flag.StringVar(&genesisFile, "SimulationGenesis", "", "Custom simulation genesis file") flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed") flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "Number of blocks") flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block") @@ -54,7 +58,31 @@ func init() { flag.IntVar(&period, "SimulationPeriod", 1, "Run slow invariants only once every period assertions") } -func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) json.RawMessage { +func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + var genesis tmtypes.GenesisDoc + cdc := MakeCodec() + bytes, err := ioutil.ReadFile(genesisFile) + if err != nil { + panic(err) + } + cdc.MustUnmarshalJSON(bytes, &genesis) + var appState GenesisState + cdc.MustUnmarshalJSON(genesis.AppState, &appState) + var newAccs []simulation.Account + for _, acc := range appState.Accounts { + // Pick a random private key, since we don't know the actual key + // This should be fine as it's only used for mock Tendermint validators + // and these keys are never actually used to sign by mock Tendermint. + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) + newAccs = append(newAccs, simulation.Account{privKey, privKey.PubKey(), acc.Address}) + } + return json.RawMessage(genesis.AppState), newAccs, genesis.ChainID +} + +func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + var genesisAccounts []GenesisAccount amount := int64(r.Intn(1e6)) @@ -216,7 +244,14 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T panic(err) } - return appState + return appState, accs, "simulation" +} + +func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + if genesisFile != "" { + return appStateFromGenesisFileFn(r, accs, genesisTimestamp) + } + return appStateRandomizedFn(r, accs, genesisTimestamp) } func randIntBetween(r *rand.Rand, min, max int) int { diff --git a/scripts/multisim.sh b/scripts/multisim.sh index e27035983dde..e8df463dcf8a 100755 --- a/scripts/multisim.sh +++ b/scripts/multisim.sh @@ -5,10 +5,12 @@ seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823 blocks=$1 period=$2 testname=$3 +genesis=$4 echo "Running multi-seed simulation with seeds ${seeds[@]}" echo "Running $blocks blocks per seed" echo "Running test $testname" +echo "Using genesis file $genesis" 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)." @@ -22,7 +24,7 @@ sim() { 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 $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks \ + go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks -SimulationGenesis=$genesis \ -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -SimulationPeriod=$period -v -timeout 24h > $file } diff --git a/x/mock/simulation/simulate.go b/x/mock/simulation/simulate.go index 81e360a1a827..723955f6b367 100644 --- a/x/mock/simulation/simulate.go +++ b/x/mock/simulation/simulate.go @@ -18,8 +18,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// AppStateFn returns the app state json bytes -type AppStateFn func(r *rand.Rand, accs []Account, genesisTimestamp time.Time) json.RawMessage +// AppStateFn returns the app state json bytes, the genesis accounts, and the chain identifier +type AppStateFn func(r *rand.Rand, accs []Account, genesisTimestamp time.Time) (appState json.RawMessage, accounts []Account, chainId string) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, @@ -35,15 +35,18 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, func initChain( r *rand.Rand, params Params, accounts []Account, app *baseapp.BaseApp, appStateFn AppStateFn, genesisTimestamp time.Time, -) mockValidators { +) (mockValidators, []Account) { + + appState, accounts, chainID := appStateFn(r, accounts, genesisTimestamp) req := abci.RequestInitChain{ - AppStateBytes: appStateFn(r, accounts, genesisTimestamp), + AppStateBytes: appState, + ChainId: chainID, } res := app.InitChain(req) validators := newMockValidators(r, res.Validators, params) - return validators + return validators, accounts } // SimulateFromSeed tests an application by running the provided @@ -73,7 +76,11 @@ 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, app, appStateFn, genesisTimestamp) + validators, accs := initChain(r, params, accs, app, appStateFn, genesisTimestamp) + if len(accs) == 0 { + return true, fmt.Errorf("must have greater than zero genesis accounts") + } + nextValidators := validators header := abci.Header{