From f10f5e5974d2ecbf9efc05bc0bfe1c99fdeed4b6 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Mon, 27 Jun 2022 16:05:10 +0100 Subject: [PATCH] test: fix `x/slashing` operations test (#12346) --- simapp/params/weights.go | 1 - testutil/sims/app_helpers.go | 58 +++++----- x/capability/capability_test.go | 1 - x/slashing/simulation/operations_test.go | 129 +++++++++++++---------- x/slashing/testutil/app.yaml | 18 +++- x/slashing/testutil/app_config.go | 1 + 6 files changed, 121 insertions(+), 87 deletions(-) diff --git a/simapp/params/weights.go b/simapp/params/weights.go index 0ae258cf570..26f9eb00cef 100644 --- a/simapp/params/weights.go +++ b/simapp/params/weights.go @@ -7,7 +7,6 @@ const ( DefaultWeightMsgDeposit int = 100 DefaultWeightMsgVote int = 67 DefaultWeightMsgVoteWeighted int = 33 - DefaultWeightMsgUnjail int = 100 DefaultWeightMsgCreateValidator int = 100 DefaultWeightMsgEditValidator int = 5 DefaultWeightMsgDelegate int = 100 diff --git a/testutil/sims/app_helpers.go b/testutil/sims/app_helpers.go index 61a5d521192..07362c01b6d 100644 --- a/testutil/sims/app_helpers.go +++ b/testutil/sims/app_helpers.go @@ -53,29 +53,46 @@ var DefaultConsensusParams = &tmproto.ConsensusParams{ }, } -// Setup initializes a new runtime.App. A Nop logger is set in runtime.App. -// appConfig usually load from a `app.yaml` with `appconfig.LoadYAML`, defines the application configuration. -// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). +// createDefaultRandomValidatorSet creates a validator set with one random validator +func createDefaultRandomValidatorSet() (*tmtypes.ValidatorSet, error) { + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey(context.TODO()) + if err != nil { + return nil, fmt.Errorf("failed to get pub key: %w", err) + } + + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + + return tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}), nil +} + +// Setup initializes a new runtime.App and can inject values into extraOutputs. +// It uses SetupWithConfiguration under the hood. func Setup(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithBaseAppOption(appConfig, nil, false, extraOutputs...) + return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, nil, false, extraOutputs...) } -// SetupAtGenesis initializes a new runtime.App at genesis. A Nop logger is set in runtime.App. -// appConfig usually load from a `app.yaml` with `appconfig.LoadYAML`, defines the application configuration. -// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). +// SetupAtGenesis initializes a new runtime.App at genesis and can inject values into extraOutputs. +// It uses SetupWithConfiguration under the hood. func SetupAtGenesis(appConfig depinject.Config, extraOutputs ...interface{}) (*runtime.App, error) { - return SetupWithBaseAppOption(appConfig, nil, true, extraOutputs...) + return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, nil, true, extraOutputs...) +} + +// SetupWithBaseAppOption initializes a new runtime.App and can inject values into extraOutputs. +// With specific baseApp options. It uses SetupWithConfiguration under the hood. +func SetupWithBaseAppOption(appConfig depinject.Config, baseAppOption runtime.BaseAppOption, extraOutputs ...interface{}) (*runtime.App, error) { + return SetupWithConfiguration(appConfig, createDefaultRandomValidatorSet, baseAppOption, false, extraOutputs...) } -// SetupWithBaseAppOption initializes a new runtime.App. A Nop logger is set in runtime.App. +// SetupWithConfiguration initializes a new runtime.App. A Nop logger is set in runtime.App. // appConfig usually load from a `app.yaml` with `appconfig.LoadYAML`, defines the application configuration. +// validatorSet defines a custom validator set to be validating the app. // baseAppOption defines the additional operations that must be run on baseapp before app start. -// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). // genesis defines if the app started should already have produced block or not. -func SetupWithBaseAppOption(appConfig depinject.Config, baseAppOption runtime.BaseAppOption, genesis bool, extraOutputs ...interface{}) (*runtime.App, error) { - // - // create app - // +// extraOutputs defines the extra outputs to be assigned by the dependency injector (depinject). +func SetupWithConfiguration(appConfig depinject.Config, validatorSet func() (*tmtypes.ValidatorSet, error), baseAppOption runtime.BaseAppOption, genesis bool, extraOutputs ...interface{}) (*runtime.App, error) { + // create the app with depinject var ( app *runtime.App appBuilder *runtime.AppBuilder @@ -98,19 +115,12 @@ func SetupWithBaseAppOption(appConfig depinject.Config, baseAppOption runtime.Ba return nil, fmt.Errorf("failed to load app: %w", err) } - // - // create genesis and validator - // - privVal := mock.NewPV() - pubKey, err := privVal.GetPubKey(context.TODO()) + // create validator set + valSet, err := validatorSet() if err != nil { - return nil, fmt.Errorf("failed to get pub key: %w", err) + return nil, fmt.Errorf("failed to create validator set") } - // create validator set with single validator - validator := tmtypes.NewValidator(pubKey, 1) - valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - // generate genesis account senderPrivKey := secp256k1.GenPrivKey() acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) diff --git a/x/capability/capability_test.go b/x/capability/capability_test.go index b00566b797e..ce14eb4d321 100644 --- a/x/capability/capability_test.go +++ b/x/capability/capability_test.go @@ -37,7 +37,6 @@ func (suite *CapabilityTestSuite) SetupTest() { func(ba *baseapp.BaseApp) { ba.MountStores(suite.memKey) }, - false, &suite.cdc, &suite.keeper, ) diff --git a/x/slashing/simulation/operations_test.go b/x/slashing/simulation/operations_test.go index 9b57557e1ad..1cd045a4b69 100644 --- a/x/slashing/simulation/operations_test.go +++ b/x/slashing/simulation/operations_test.go @@ -1,17 +1,19 @@ package simulation_test import ( + "fmt" "math/rand" "testing" "time" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" "github.com/cosmos/cosmos-sdk/runtime" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" @@ -34,7 +36,9 @@ import ( type SimTestSuite struct { suite.Suite - ctx sdk.Context + ctx sdk.Context + r *rand.Rand + accounts []simtypes.Account app *runtime.App legacyAmino *codec.LegacyAmino @@ -46,12 +50,31 @@ type SimTestSuite struct { slashingKeeper slashingkeeper.Keeper distrKeeper distributionkeeper.Keeper mintKeeper mintkeeper.Keeper - accs []simtypes.Account } func (suite *SimTestSuite) SetupTest() { - app, err := simtestutil.Setup( + s := rand.NewSource(1) + suite.r = rand.New(s) + accounts := simtypes.RandomAccounts(suite.r, 4) + + // create validator (non random as using a seed) + createValidator := func() (*tmtypes.ValidatorSet, error) { + account := accounts[0] + tmPk, err := cryptocodec.ToTmPubKeyInterface(account.PubKey) + if err != nil { + return nil, fmt.Errorf("failed to create pubkey: %w", err) + } + + validator := tmtypes.NewValidator(tmPk, 1) + + return tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}), nil + } + + app, err := simtestutil.SetupWithConfiguration( testutil.AppConfig, + createValidator, + nil, + false, &suite.legacyAmino, &suite.codec, &suite.interfaceRegistry, @@ -67,34 +90,30 @@ func (suite *SimTestSuite) SetupTest() { suite.app = app suite.ctx = app.BaseApp.NewContext(false, tmproto.Header{}) - s := rand.NewSource(1) - r := rand.New(s) - accounts := simtypes.RandomAccounts(r, 3) + // remove genesis validator account + suite.accounts = accounts[1:] - ctx := app.BaseApp.NewContext(false, tmproto.Header{}) - initAmt := suite.stakingKeeper.TokensFromConsensusPower(ctx, 200) + initAmt := suite.stakingKeeper.TokensFromConsensusPower(suite.ctx, 200) initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) // add coins to the accounts - for _, account := range accounts { - acc := suite.accountKeeper.NewAccountWithAddress(ctx, account.Address) - suite.accountKeeper.SetAccount(ctx, acc) - suite.Require().NoError(banktestutil.FundAccount(suite.bankKeeper, ctx, account.Address, initCoins)) + for _, account := range suite.accounts { + acc := suite.accountKeeper.NewAccountWithAddress(suite.ctx, account.Address) + suite.accountKeeper.SetAccount(suite.ctx, acc) + suite.Require().NoError(banktestutil.FundAccount(suite.bankKeeper, suite.ctx, account.Address, initCoins)) } - suite.mintKeeper.SetParams(ctx, minttypes.DefaultParams()) - suite.mintKeeper.SetMinter(ctx, minttypes.DefaultInitialMinter()) - suite.accs = accounts + suite.mintKeeper.SetParams(suite.ctx, minttypes.DefaultParams()) + suite.mintKeeper.SetMinter(suite.ctx, minttypes.DefaultInitialMinter()) } -// TestWeightedOperations tests the weights of the operations. -func (suite *SimTestSuite) TestWeightedOperations(t *testing.T) { - s := rand.NewSource(1) - r := rand.New(s) - app, ctx, accs := suite.app, suite.ctx, suite.accs - ctx.WithChainID("test-chain") +func TestSimTestSuite(t *testing.T) { + suite.Run(t, new(SimTestSuite)) +} - cdc := suite.codec +// TestWeightedOperations tests the weights of the operations. +func (suite *SimTestSuite) TestWeightedOperations() { + ctx := suite.ctx.WithChainID("test-chain") appParams := make(simtypes.AppParams) expected := []struct { @@ -103,40 +122,34 @@ func (suite *SimTestSuite) TestWeightedOperations(t *testing.T) { opMsgName string }{{simtestutil.DefaultWeightMsgUnjail, types.ModuleName, types.TypeMsgUnjail}} - weightesOps := simulation.WeightedOperations(appParams, cdc, suite.accountKeeper, suite.bankKeeper, suite.slashingKeeper, suite.stakingKeeper) + weightesOps := simulation.WeightedOperations(appParams, suite.codec, suite.accountKeeper, suite.bankKeeper, suite.slashingKeeper, suite.stakingKeeper) for i, w := range weightesOps { - operationMsg, _, err := w.Op()(r, app.BaseApp, ctx, accs, ctx.ChainID()) - require.NoError(t, err) + operationMsg, _, err := w.Op()(suite.r, suite.app.BaseApp, ctx, suite.accounts, ctx.ChainID()) + suite.Require().NoError(err) // the following checks are very much dependent from the ordering of the output given // by WeightedOperations. if the ordering in WeightedOperations changes some tests // will fail - suite.Require().Equal(t, expected[i].weight, w.Weight(), "weight should be the same") - suite.Require().Equal(t, expected[i].opMsgRoute, operationMsg.Route, "route should be the same") - suite.Require().Equal(t, expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") + suite.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") + suite.Require().Equal(expected[i].opMsgRoute, operationMsg.Route, "route should be the same") + suite.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") } } // TestSimulateMsgUnjail tests the normal scenario of a valid message of type types.MsgUnjail. // Abonormal scenarios, where the message is created by an errors, are not tested here. -func (suite *SimTestSuite) TestSimulateMsgUnjail(t *testing.T) { - // setup 3 accounts - s := rand.NewSource(5) - r := rand.New(s) - app, ctx, accounts := suite.app, suite.ctx, suite.accs +func (suite *SimTestSuite) TestSimulateMsgUnjail() { blockTime := time.Now().UTC() - ctx = ctx.WithBlockTime(blockTime) - - // remove genesis validator account - accounts = accounts[1:] + ctx := suite.ctx.WithBlockTime(blockTime) // setup accounts[0] as validator0 - validator0 := suite.getTestingValidator0(ctx, accounts) + validator0, err := getTestingValidator0(ctx, suite.stakingKeeper, suite.accounts) + suite.Require().NoError(err) // setup validator0 by consensus address suite.stakingKeeper.SetValidatorByConsAddr(ctx, validator0) val0ConsAddress, err := validator0.GetConsAddr() - require.NoError(t, err) + suite.Require().NoError(err) info := types.NewValidatorSigningInfo(val0ConsAddress, int64(4), int64(3), time.Unix(2, 0), false, int64(10)) suite.slashingKeeper.SetValidatorSigningInfo(ctx, val0ConsAddress, info) @@ -148,47 +161,51 @@ func (suite *SimTestSuite) TestSimulateMsgUnjail(t *testing.T) { delTokens := suite.stakingKeeper.TokensFromConsensusPower(ctx, 2) validator0, issuedShares := validator0.AddTokensFromDel(delTokens) val0AccAddress, err := sdk.ValAddressFromBech32(validator0.OperatorAddress) - require.NoError(t, err) + suite.Require().NoError(err) selfDelegation := stakingtypes.NewDelegation(val0AccAddress.Bytes(), validator0.GetOperator(), issuedShares) suite.stakingKeeper.SetDelegation(ctx, selfDelegation) suite.distrKeeper.SetDelegatorStartingInfo(ctx, validator0.GetOperator(), val0AccAddress.Bytes(), distrtypes.NewDelegatorStartingInfo(2, sdk.OneDec(), 200)) // begin a new block - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash, Time: blockTime}}) + suite.app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: suite.app.LastBlockHeight() + 1, AppHash: suite.app.LastCommitID().Hash, Time: blockTime}}) // execute operation op := simulation.SimulateMsgUnjail(codec.NewProtoCodec(suite.interfaceRegistry), suite.accountKeeper, suite.bankKeeper, suite.slashingKeeper, suite.stakingKeeper) - operationMsg, futureOperations, err := op(r, app.BaseApp, ctx, accounts, "") - require.NoError(t, err) + operationMsg, futureOperations, err := op(suite.r, suite.app.BaseApp, ctx, suite.accounts, "") + suite.Require().NoError(err) var msg types.MsgUnjail types.ModuleCdc.UnmarshalJSON(operationMsg.Msg, &msg) - require.True(t, operationMsg.OK) - require.Equal(t, types.TypeMsgUnjail, msg.Type()) - require.Equal(t, "cosmosvaloper17s94pzwhsn4ah25tec27w70n65h5t2scgxzkv2", msg.ValidatorAddr) - require.Len(t, futureOperations, 0) + suite.Require().True(operationMsg.OK) + suite.Require().Equal(types.TypeMsgUnjail, msg.Type()) + suite.Require().Equal("cosmosvaloper1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7epjs3u", msg.ValidatorAddr) + suite.Require().Len(futureOperations, 0) } -func (suite *SimTestSuite) getTestingValidator0(ctx sdk.Context, accounts []simtypes.Account) stakingtypes.Validator { +func getTestingValidator0(ctx sdk.Context, stakingKeeper *stakingkeeper.Keeper, accounts []simtypes.Account) (stakingtypes.Validator, error) { commission0 := stakingtypes.NewCommission(sdk.ZeroDec(), sdk.OneDec(), sdk.OneDec()) - return suite.getTestingValidator(commission0, 0) + return getTestingValidator(ctx, stakingKeeper, accounts, commission0, 0) } -func (suite *SimTestSuite) getTestingValidator(commission stakingtypes.Commission, n int) stakingtypes.Validator { - ctx, accounts := suite.ctx, suite.accs +func getTestingValidator(ctx sdk.Context, stakingKeeper *stakingkeeper.Keeper, accounts []simtypes.Account, commission stakingtypes.Commission, n int) (stakingtypes.Validator, error) { account := accounts[n] valPubKey := account.ConsKey.PubKey() valAddr := sdk.ValAddress(account.PubKey.Address().Bytes()) validator, err := stakingtypes.NewValidator(valAddr, valPubKey, stakingtypes.Description{}) - suite.Require().NoError(err) + if err != nil { + return stakingtypes.Validator{}, fmt.Errorf("failed to create validator: %w", err) + } + validator, err = validator.SetInitialCommission(commission) - suite.Require().NoError(err) + if err != nil { + return stakingtypes.Validator{}, fmt.Errorf("failed to set initial commission: %w", err) + } validator.DelegatorShares = sdk.NewDec(100) validator.Tokens = sdk.NewInt(1000000) - suite.stakingKeeper.SetValidator(ctx, validator) + stakingKeeper.SetValidator(ctx, validator) - return validator + return validator, nil } diff --git a/x/slashing/testutil/app.yaml b/x/slashing/testutil/app.yaml index 3e1c3830b4f..723977c8560 100644 --- a/x/slashing/testutil/app.yaml +++ b/x/slashing/testutil/app.yaml @@ -3,11 +3,14 @@ modules: config: "@type": cosmos.app.runtime.v1alpha1.Module - app_name: SlashApp + app_name: SlashingApp - begin_blockers: [mint, staking, auth, bank, genutil, slashing, params] - end_blockers: [mint, staking, auth, bank, genutil, slashing, params] - init_genesis: [auth, bank, staking, mint, slashing, genutil, params] + begin_blockers: + [mint, distribution, staking, auth, bank, genutil, slashing, params] + end_blockers: + [staking, auth, bank, genutil, distribution, mint, slashing, params] + init_genesis: + [auth, bank, distribution, staking, mint, slashing, genutil, params] - name: auth config: @@ -15,6 +18,7 @@ modules: bech32_prefix: cosmos module_account_permissions: - account: fee_collector + - account: distribution - account: mint permissions: [minter] - account: bonded_tokens_pool @@ -44,8 +48,12 @@ modules: - name: genutil config: - "@type": cosmos.genutil.module.v1.Module + "@type": cosmos.genutil.module.v1.Module - name: mint config: "@type": cosmos.mint.module.v1.Module + + - name: distribution + config: + "@type": cosmos.distribution.module.v1.Module diff --git a/x/slashing/testutil/app_config.go b/x/slashing/testutil/app_config.go index 39e7f475002..9a9069894d3 100644 --- a/x/slashing/testutil/app_config.go +++ b/x/slashing/testutil/app_config.go @@ -7,6 +7,7 @@ import ( _ "github.com/cosmos/cosmos-sdk/x/auth" _ "github.com/cosmos/cosmos-sdk/x/auth/tx/module" _ "github.com/cosmos/cosmos-sdk/x/bank" + _ "github.com/cosmos/cosmos-sdk/x/distribution" _ "github.com/cosmos/cosmos-sdk/x/genutil" _ "github.com/cosmos/cosmos-sdk/x/mint" _ "github.com/cosmos/cosmos-sdk/x/params"