From 6d0f4f086fe6c3ada132b3a50d097fa1d6acd31e Mon Sep 17 00:00:00 2001 From: MD Aleem <72057206+aleem1314@users.noreply.github.com> Date: Tue, 5 Oct 2021 17:32:51 +0530 Subject: [PATCH] feat!: Ensure InitGenesis returns with non-empty validator set (#9697) ## Description Closes: #8961 SDK allows InitGenesis to return with an empty validator set. In practice, the error for an empty validator set gets thrown in tendermint. To fix this, * Add non-empty validator set check to the `mm.InitGenesis` function. This will break `simapp.Setup` because it relies on an empty validator set [#comment](https://github.com/cosmos/cosmos-sdk/pull/8909#issuecomment-804850834). * Update `simapp.Setup` to use a single validator. * Fix failing tests (Most of them are keeper tests). --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [x] added `!` to the type prefix if API or client breaking change - [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [x] provided a link to the relevant issue or specification - [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [x] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [x] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable) --- sim_test.go | 13 ++++++++ test_helpers.go | 86 ++++++++++++++++++++++++++++++------------------- 2 files changed, 65 insertions(+), 34 deletions(-) diff --git a/sim_test.go b/sim_test.go index 377c93790140..26b1252086aa 100644 --- a/sim_test.go +++ b/sim_test.go @@ -5,6 +5,8 @@ import ( "fmt" "math/rand" "os" + "runtime/debug" + "strings" "testing" storetypes "github.com/cosmos/cosmos-sdk/store/types" @@ -153,6 +155,17 @@ func TestAppImportExport(t *testing.T) { err = json.Unmarshal(exported.AppState, &genesisState) require.NoError(t, err) + defer func() { + if r := recover(); r != nil { + err := fmt.Sprintf("%v", r) + if !strings.Contains(err, "validator set is empty after InitGenesis") { + panic(r) + } + logger.Info("Skipping simulation as all validators have been unbonded") + logger.Info("err", err, "stacktrace", string(debug.Stack())) + } + }() + ctxA := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) ctxB := newApp.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()}) newApp.mm.InitGenesis(ctxB, app.AppCodec(), genesisState) diff --git a/test_helpers.go b/test_helpers.go index 58e365aed655..bf6476676d32 100644 --- a/test_helpers.go +++ b/test_helpers.go @@ -121,22 +121,24 @@ func NewSimappWithCustomOptions(t *testing.T, isCheckTx bool, options SetupOptio func Setup(t *testing.T, isCheckTx bool) *SimApp { t.Helper() - app, genesisState := setup(!isCheckTx, 5) - if !isCheckTx { - // init chain must be called to stop deliverState from being nil - stateBytes, err := json.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) - // Initialize the chain - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) + // 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) + balance := banktypes.Balance{ + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), } + app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance) + return app } @@ -181,8 +183,13 @@ func genesisStateWithValSet(t *testing.T, totalSupply := sdk.NewCoins() for _, b := range balances { - // add genesis acc tokens and delegated tokens to total supply - totalSupply = totalSupply.Add(b.Coins.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt))...) + // add genesis acc tokens to total supply + totalSupply = totalSupply.Add(b.Coins...) + } + + for range delegations { + // add delegated tokens to total supply + totalSupply = totalSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, bondAmt)) } // add bonded amount to bonded pool module account @@ -237,33 +244,44 @@ func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs func SetupWithGenesisAccounts(t *testing.T, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *SimApp { t.Helper() - app, genesisState := setup(true, 0) - authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) - genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) - totalSupply := sdk.NewCoins() - for _, b := range balances { - totalSupply = totalSupply.Add(b.Coins...) - } + // create validator set with single validator + validator := tmtypes.NewValidator(pubKey, 1) + valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) - bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, totalSupply, []banktypes.Metadata{}) - genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) + return SetupWithGenesisValSet(t, valSet, genAccs, balances...) +} - stateBytes, err := json.MarshalIndent(genesisState, "", " ") +// SetupWithGenesisValSet initializes GenesisState with a single validator and genesis accounts +// that also act as delegators. +func GenesisStateWithSingleValidator(t *testing.T, app *SimApp) GenesisState { + t.Helper() + + privVal := mock.NewPV() + pubKey, err := privVal.GetPubKey() require.NoError(t, err) - app.InitChain( - abci.RequestInitChain{ - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: DefaultConsensusParams, - AppStateBytes: stateBytes, + // 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) + balances := []banktypes.Balance{ + { + Address: acc.GetAddress().String(), + Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100000000000000))), }, - ) + } - app.Commit() - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1}}) + genesisState := NewDefaultGenesisState(app.appCodec) + genesisState = genesisStateWithValSet(t, app, genesisState, valSet, []authtypes.GenesisAccount{acc}, balances...) - return app + return genesisState } type GenerateAccountStrategy func(int) []sdk.AccAddress