From 8c736bb72fe8713a478fa7fbb3b023ebb064096c Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Thu, 23 Apr 2020 10:51:03 -0400 Subject: [PATCH] x/evidence: StoreDecoder simulation (#6035) * x/evidence: simulations * update app * evidence store decoder * add evidence to sim import export * fix test * prevent zero values on randomized genesis * return empty evidences for genesis state * address comments from review --- CHANGELOG.md | 1 + simapp/app.go | 3 +- simapp/sim_test.go | 2 + x/evidence/module.go | 47 +++++++++++++++++--- x/evidence/simulation/decoder.go | 32 ++++++++++++++ x/evidence/simulation/decoder_test.go | 64 +++++++++++++++++++++++++++ x/evidence/simulation/genesis.go | 37 ++++++++++++++++ x/evidence/types/genesis.go | 7 ++- 8 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 x/evidence/simulation/decoder.go create mode 100644 x/evidence/simulation/decoder_test.go create mode 100644 x/evidence/simulation/genesis.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da0397be..e5a01b217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -219,6 +219,7 @@ functionality that requires an online connection. * (client) [\#5895](https://github.com/cosmos/cosmos-sdk/issues/5895) show config options in the config command's help screen. * (types/rest) [\#5900](https://github.com/cosmos/cosmos-sdk/pull/5900) Add Check*Error function family to spare developers from replicating tons of boilerplate code. * (x/evidence) [\#5952](https://github.com/cosmos/cosmos-sdk/pull/5952) Tendermint Consensus parameters can now be changed via parameter change proposals through `x/gov`. +* (x/evidence) [\#5961](https://github.com/cosmos/cosmos-sdk/issues/5961) Add `StoreDecoder` simulation for evidence module. * (x/auth/ante) [\#6040](https://github.com/cosmos/cosmos-sdk/pull/6040) `AccountKeeper` interface used for `NewAnteHandler` and handler's decorators to add support of using custom `AccountKeeper` implementations. * (simulation) [\#6002](https://github.com/cosmos/cosmos-sdk/pull/6002) Add randomized consensus params into simulation. diff --git a/simapp/app.go b/simapp/app.go index da06b9edb..9e977e994 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -271,7 +271,7 @@ func NewSimApp( distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper), upgrade.NewAppModule(app.UpgradeKeeper), - evidence.NewAppModule(app.EvidenceKeeper), + evidence.NewAppModule(appCodec, app.EvidenceKeeper), ibc.NewAppModule(app.IBCKeeper), params.NewAppModule(app.ParamsKeeper), transferModule, @@ -313,6 +313,7 @@ func NewSimApp( distr.NewAppModule(appCodec, app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper), params.NewAppModule(app.ParamsKeeper), + evidence.NewAppModule(appCodec, app.EvidenceKeeper), ) app.sm.RegisterStoreDecoders() diff --git a/simapp/sim_test.go b/simapp/sim_test.go index 81d5c1c1a..6e12ed7d3 100644 --- a/simapp/sim_test.go +++ b/simapp/sim_test.go @@ -20,6 +20,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/evidence" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -156,6 +157,7 @@ func TestAppImportExport(t *testing.T) { {app.keys[bank.StoreKey], newApp.keys[bank.StoreKey], [][]byte{bank.BalancesPrefix}}, {app.keys[paramtypes.StoreKey], newApp.keys[paramtypes.StoreKey], [][]byte{}}, {app.keys[gov.StoreKey], newApp.keys[gov.StoreKey], [][]byte{}}, + {app.keys[evidence.StoreKey], newApp.keys[evidence.StoreKey], [][]byte{}}, } for _, skp := range storeKeysPrefixes { diff --git a/x/evidence/module.go b/x/evidence/module.go index 87900aba3..e3c7cf353 100644 --- a/x/evidence/module.go +++ b/x/evidence/module.go @@ -3,14 +3,17 @@ package evidence import ( "encoding/json" "fmt" + "math/rand" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" "github.com/cosmos/cosmos-sdk/x/evidence/client" "github.com/cosmos/cosmos-sdk/x/evidence/client/cli" "github.com/cosmos/cosmos-sdk/x/evidence/client/rest" + "github.com/cosmos/cosmos-sdk/x/evidence/simulation" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -18,11 +21,9 @@ import ( ) var ( - _ module.AppModule = AppModule{} - _ module.AppModuleBasic = AppModuleBasic{} - - // TODO: Enable simulation once concrete types are defined. - // _ module.AppModuleSimulation = AppModuleSimulation{} + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} ) // ---------------------------------------------------------------------------- @@ -31,9 +32,11 @@ var ( // AppModuleBasic implements the AppModuleBasic interface for the evidence module. type AppModuleBasic struct { + cdc Codec evidenceHandlers []client.EvidenceHandler // client evidence submission handlers } +// NewAppModuleBasic crates a AppModuleBasic without the codec. func NewAppModuleBasic(evidenceHandlers ...client.EvidenceHandler) AppModuleBasic { return AppModuleBasic{ evidenceHandlers: evidenceHandlers, @@ -103,9 +106,9 @@ type AppModule struct { keeper Keeper } -func NewAppModule(keeper Keeper) AppModule { +func NewAppModule(cdc Codec, keeper Keeper) AppModule { return AppModule{ - AppModuleBasic: NewAppModuleBasic(), + AppModuleBasic: AppModuleBasic{cdc: cdc}, keeper: keeper, } } @@ -166,3 +169,33 @@ func (am AppModule) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { return []abci.ValidatorUpdate{} } + +//____________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the evidence module. +func (AppModule) GenerateGenesisState(simState *module.SimulationState) { + simulation.RandomizedGenState(simState) +} + +// ProposalContents returns all the evidence content functions used to +// simulate governance proposals. +func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { + return nil +} + +// RandomizedParams creates randomized evidence param changes for the simulator. +func (AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange { + return nil +} + +// RegisterStoreDecoder registers a decoder for evidence module's types +func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { + sdr[StoreKey] = simulation.NewDecodeStore(am.cdc) +} + +// WeightedOperations returns the all the gov module operations with their respective weights. +func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { + return nil +} diff --git a/x/evidence/simulation/decoder.go b/x/evidence/simulation/decoder.go new file mode 100644 index 000000000..232f69070 --- /dev/null +++ b/x/evidence/simulation/decoder.go @@ -0,0 +1,32 @@ +package simulation + +import ( + "bytes" + "fmt" + + tmkv "github.com/tendermint/tendermint/libs/kv" + + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// NewDecodeStore returns a decoder function closure that unmarshals the KVPair's +// Value to the corresponding evidence type. +func NewDecodeStore(cdc types.Codec) func(kvA, kvB tmkv.Pair) string { + return func(kvA, kvB tmkv.Pair) string { + switch { + case bytes.Equal(kvA.Key[:1], types.KeyPrefixEvidence): + evidenceA, err := cdc.UnmarshalEvidence(kvA.Value) + if err != nil { + panic(fmt.Sprintf("cannot unmarshal evidence: %s", err.Error())) + } + evidenceB, err := cdc.UnmarshalEvidence(kvB.Value) + if err != nil { + panic(fmt.Sprintf("cannot unmarshal evidence: %s", err.Error())) + } + return fmt.Sprintf("%v\n%v", evidenceA, evidenceB) + + default: + panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1])) + } + } +} diff --git a/x/evidence/simulation/decoder_test.go b/x/evidence/simulation/decoder_test.go new file mode 100644 index 000000000..c7f1ad30c --- /dev/null +++ b/x/evidence/simulation/decoder_test.go @@ -0,0 +1,64 @@ +package simulation_test + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + tmkv "github.com/tendermint/tendermint/libs/kv" + + codecstd "github.com/cosmos/cosmos-sdk/codec/std" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/evidence/simulation" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +func TestDecodeStore(t *testing.T) { + cdc := codecstd.NewAppCodec(codecstd.MakeCodec(simapp.ModuleBasics)) + dec := simulation.NewDecodeStore(cdc) + + delPk1 := ed25519.GenPrivKey().PubKey() + + ev := types.Equivocation{ + Height: 10, + Time: time.Now().UTC(), + Power: 1000, + ConsensusAddress: sdk.ConsAddress(delPk1.Address()), + } + + evBz, err := cdc.MarshalEvidence(&ev) + require.NoError(t, err) + + kvPairs := tmkv.Pairs{ + tmkv.Pair{ + Key: types.KeyPrefixEvidence, + Value: evBz, + }, + tmkv.Pair{ + Key: []byte{0x99}, + Value: []byte{0x99}, + }, + } + tests := []struct { + name string + expectedLog string + }{ + {"Evidence", fmt.Sprintf("%v\n%v", ev, ev)}, + {"other", ""}, + } + + for i, tt := range tests { + i, tt := i, tt + t.Run(tt.name, func(t *testing.T) { + switch i { + case len(tests) - 1: + require.Panics(t, func() { dec(kvPairs[i], kvPairs[i]) }, tt.name) + default: + require.Equal(t, tt.expectedLog, dec(kvPairs[i], kvPairs[i]), tt.name) + } + }) + } +} diff --git a/x/evidence/simulation/genesis.go b/x/evidence/simulation/genesis.go new file mode 100644 index 000000000..317468293 --- /dev/null +++ b/x/evidence/simulation/genesis.go @@ -0,0 +1,37 @@ +package simulation + +// DONTCOVER + +import ( + "fmt" + "math/rand" + + "github.com/cosmos/cosmos-sdk/codec" + + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" + "github.com/cosmos/cosmos-sdk/x/evidence/types" +) + +// Simulation parameter constants +const evidence = "evidence" + +// GenEvidences returns an empty slice of evidences. +func GenEvidences(_ *rand.Rand, _ []simtypes.Account) []exported.Evidence { + return []exported.Evidence{} +} + +// RandomizedGenState generates a random GenesisState for evidence +func RandomizedGenState(simState *module.SimulationState) { + var ev []exported.Evidence + simState.AppParams.GetOrGenerate( + simState.Cdc, evidence, &ev, simState.Rand, + func(r *rand.Rand) { ev = GenEvidences(r, simState.Accounts) }, + ) + + evidenceGenesis := types.GenesisState{Evidence: ev} + + fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, evidenceGenesis)) + simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(evidenceGenesis) +} diff --git a/x/evidence/types/genesis.go b/x/evidence/types/genesis.go index 1546ca2b7..2d2e3556f 100644 --- a/x/evidence/types/genesis.go +++ b/x/evidence/types/genesis.go @@ -1,6 +1,8 @@ package types import ( + "fmt" + "github.com/cosmos/cosmos-sdk/x/evidence/exported" ) @@ -27,7 +29,10 @@ func DefaultGenesisState() GenesisState { // Validate performs basic gensis state validation returning an error upon any // failure. func (gs GenesisState) Validate() error { - for _, e := range gs.Evidence { + for i, e := range gs.Evidence { + if e == nil { + return fmt.Errorf("evidence %d cannot be nil", i) + } if err := e.ValidateBasic(); err != nil { return err }