diff --git a/scripts/mockgen.sh b/scripts/mockgen.sh index f0b02b5d4de9..ed1aced410ca 100755 --- a/scripts/mockgen.sh +++ b/scripts/mockgen.sh @@ -22,3 +22,4 @@ $mockgen_cmd -source=x/bank/types/expected_keepers.go -package testutil -destina $mockgen_cmd -source=x/group/testutil/expected_keepers.go -package testutil -destination x/group/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/evidence/types/expected_keepers.go -package testutil -destination x/evidence/testutil/expected_keepers_mocks.go $mockgen_cmd -source=x/slashing/types/expected_keepers.go -package testutil -destination x/slashing/testutil/expected_keepers_mocks.go +$mockgen_cmd -source=x/genutil/types/expected_keepers.go -package testutil -destination x/genutil/testutil/expected_keepers_mocks.go \ No newline at end of file diff --git a/x/genutil/gentx_test.go b/x/genutil/gentx_test.go new file mode 100644 index 000000000000..89047d034fc3 --- /dev/null +++ b/x/genutil/gentx_test.go @@ -0,0 +1,337 @@ +package genutil_test + +import ( + "cosmossdk.io/math" + "encoding/json" + "fmt" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/testutil" + simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/genutil" + genutiltestutil "github.com/cosmos/cosmos-sdk/x/genutil/testutil" + "github.com/cosmos/cosmos-sdk/x/genutil/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/suite" + abci "github.com/tendermint/tendermint/abci/types" + "math/rand" + "testing" + "time" +) + +var ( + priv1 = secp256k1.GenPrivKey() + priv2 = secp256k1.GenPrivKey() + pk1 = priv1.PubKey() + pk2 = priv2.PubKey() + addr1 = sdk.AccAddress(pk1.Address()) + addr2 = sdk.AccAddress(pk2.Address()) + desc = stakingtypes.NewDescription("testname", "", "", "", "") + comm = stakingtypes.CommissionRates{} +) + +// GenTxTestSuite is a test suite to be used with gentx tests. +type GenTxTestSuite struct { + suite.Suite + + ctx sdk.Context + + stakingKeeper *genutiltestutil.MockStakingKeeper + encodingConfig moduletestutil.TestEncodingConfig + msg1, msg2 *stakingtypes.MsgCreateValidator +} + +func (suite *GenTxTestSuite) SetupTest() { + suite.encodingConfig = moduletestutil.MakeTestEncodingConfig(genutil.AppModuleBasic{}) + key := sdk.NewKVStoreKey("a_Store_Key") + tkey := sdk.NewTransientStoreKey("a_transient_store") + suite.ctx = testutil.DefaultContext(key, tkey) + + ctrl := gomock.NewController(suite.T()) + suite.stakingKeeper = genutiltestutil.NewMockStakingKeeper(ctrl) + + stakingtypes.RegisterInterfaces(suite.encodingConfig.InterfaceRegistry) + banktypes.RegisterInterfaces(suite.encodingConfig.InterfaceRegistry) + + var err error + amount := sdk.NewInt64Coin(sdk.DefaultBondDenom, 50) + one := math.OneInt() + suite.msg1, err = stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(pk1.Address()), pk1, amount, desc, comm, one) + suite.NoError(err) + suite.msg2, err = stakingtypes.NewMsgCreateValidator( + sdk.ValAddress(pk2.Address()), pk1, amount, desc, comm, one) + suite.NoError(err) +} + +func (suite *GenTxTestSuite) setAccountBalance(balances []banktypes.Balance) json.RawMessage { + bankGenesisState := banktypes.GenesisState{ + Params: banktypes.Params{DefaultSendEnabled: true}, + Balances: []banktypes.Balance{ + { + Address: "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh", + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000000)}, + }, + { + Address: "cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 2059726)}, + }, + { + Address: "cosmos1k5lndq46x9xpejdxq52q3ql3ycrphg4qxlfqn7", + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 100000000000000)}, + }, + }, + Supply: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)}, + } + for _, balance := range balances { + bankGenesisState.Balances = append(bankGenesisState.Balances, balance) + + } + for _, balance := range bankGenesisState.Balances { + bankGenesisState.Supply.Add(balance.Coins...) + } + bankGenesis, err := suite.encodingConfig.Amino.MarshalJSON(bankGenesisState) // TODO switch this to use Marshaler + suite.Require().NoError(err) + + return bankGenesis +} + +func (suite *GenTxTestSuite) TestSetGenTxsInAppGenesisState() { + var ( + txBuilder = suite.encodingConfig.TxConfig.NewTxBuilder() + genTxs []sdk.Tx + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "one genesis transaction", + func() { + err := txBuilder.SetMsgs(suite.msg1) + suite.Require().NoError(err) + tx := txBuilder.GetTx() + genTxs = []sdk.Tx{tx} + }, + true, + }, + { + "two genesis transactions", + func() { + err := txBuilder.SetMsgs(suite.msg1, suite.msg2) + suite.Require().NoError(err) + tx := txBuilder.GetTx() + genTxs = []sdk.Tx{tx} + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + cdc := suite.encodingConfig.Codec + txJSONEncoder := suite.encodingConfig.TxConfig.TxJSONEncoder() + + tc.malleate() + appGenesisState, err := genutil.SetGenTxsInAppGenesisState(cdc, txJSONEncoder, make(map[string]json.RawMessage), genTxs) + + if tc.expPass { + suite.Require().NoError(err) + suite.Require().NotNil(appGenesisState[types.ModuleName]) + + var genesisState types.GenesisState + err := cdc.UnmarshalJSON(appGenesisState[types.ModuleName], &genesisState) + suite.Require().NoError(err) + suite.Require().NotNil(genesisState.GenTxs) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *GenTxTestSuite) TestValidateAccountInGenesis() { + var ( + appGenesisState = make(map[string]json.RawMessage) + coins sdk.Coins + ) + + testCases := []struct { + msg string + malleate func() + expPass bool + }{ + { + "no accounts", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)} + }, + false, + }, + { + "account without balance in the genesis state", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 0)} + balances := banktypes.Balance{ + Address: addr2.String(), + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)}, + } + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance([]banktypes.Balance{balances}) + }, + false, + }, + { + "account without enough funds of default bond denom", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 50)} + balances := banktypes.Balance{ + Address: addr1.String(), + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}, + } + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance([]banktypes.Balance{balances}) + }, + false, + }, + { + "account with enough funds of default bond denom", + func() { + coins = sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)} + balances := banktypes.Balance{ + Address: addr1.String(), + Coins: sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 25)}, + } + appGenesisState[banktypes.ModuleName] = suite.setAccountBalance([]banktypes.Balance{balances}) + }, + true, + }, + } + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + cdc := suite.encodingConfig.Codec + + stakingGenesis, err := cdc.MarshalJSON(&stakingtypes.GenesisState{Params: stakingtypes.DefaultParams()}) // TODO switch this to use Marshaler + suite.Require().NoError(err) + appGenesisState[stakingtypes.ModuleName] = stakingGenesis + + tc.malleate() + err = genutil.ValidateAccountInGenesis( + appGenesisState, banktypes.GenesisBalancesIterator{}, + addr1, coins, cdc, + ) + + if tc.expPass { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + } + }) + } +} + +func (suite *GenTxTestSuite) TestDeliverGenTxs() { + var ( + genTxs []json.RawMessage + txBuilder = suite.encodingConfig.TxConfig.NewTxBuilder() + ) + + testCases := []struct { + msg string + malleate func() + deliverTxFn func(abci.RequestDeliverTx) abci.ResponseDeliverTx + expPass bool + }{ + { + "no signature supplied", + func() { + err := txBuilder.SetMsgs(suite.msg1) + suite.Require().NoError(err) + + genTxs = make([]json.RawMessage, 1) + tx, err := suite.encodingConfig.TxConfig.TxJSONEncoder()(txBuilder.GetTx()) + suite.Require().NoError(err) + genTxs[0] = tx + }, + func(_ abci.RequestDeliverTx) abci.ResponseDeliverTx { + + return abci.ResponseDeliverTx{ + Code: sdkerrors.ErrNoSignatures.ABCICode(), + GasWanted: int64(10000000), + GasUsed: int64(41913), + Log: "no signatures supplied", + } + }, + false, + }, + { + "success", + func() { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + msg := banktypes.NewMsgSend(addr1, addr2, sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 1)}) + tx, err := simtestutil.GenSignedMockTx( + r, + suite.encodingConfig.TxConfig, + []sdk.Msg{msg}, + sdk.Coins{sdk.NewInt64Coin(sdk.DefaultBondDenom, 10)}, + simtestutil.DefaultGenTxGas, + suite.ctx.ChainID(), + []uint64{7}, + []uint64{0}, + priv1, + ) + suite.Require().NoError(err) + + genTxs = make([]json.RawMessage, 1) + genTx, err := suite.encodingConfig.TxConfig.TxJSONEncoder()(tx) + suite.Require().NoError(err) + genTxs[0] = genTx + }, + func(tx abci.RequestDeliverTx) abci.ResponseDeliverTx { + return abci.ResponseDeliverTx{ + Code: sdkerrors.ErrUnauthorized.ABCICode(), + GasWanted: int64(10000000), + GasUsed: int64(41353), + Log: "signature verification failed; please verify account number (4) and chain-id (): unauthorized", + Codespace: "sdk", + } + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { + suite.SetupTest() + + tc.malleate() + + if tc.expPass { + suite.Require().NotPanics(func() { + genutil.DeliverGenTxs( + suite.ctx, genTxs, suite.stakingKeeper, tc.deliverTxFn, + suite.encodingConfig.TxConfig, + ) + }) + } else { + _, err := genutil.DeliverGenTxs( + suite.ctx, genTxs, suite.stakingKeeper, tc.deliverTxFn, + suite.encodingConfig.TxConfig, + ) + + suite.Require().Error(err) + } + }) + } +} + +func TestGenTxTestSuite(t *testing.T) { + suite.Run(t, new(GenTxTestSuite)) +} diff --git a/x/genutil/testutil/expected_keepers_mocks.go b/x/genutil/testutil/expected_keepers_mocks.go new file mode 100644 index 000000000000..f51fa2eb2733 --- /dev/null +++ b/x/genutil/testutil/expected_keepers_mocks.go @@ -0,0 +1,186 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: x/genutil/types/expected_keepers.go + +// Package testutil is a generated GoMock package. +package testutil + +import ( + json "encoding/json" + reflect "reflect" + + codec "github.com/cosmos/cosmos-sdk/codec" + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/cosmos/cosmos-sdk/x/auth/types" + exported "github.com/cosmos/cosmos-sdk/x/bank/exported" + gomock "github.com/golang/mock/gomock" + types1 "github.com/tendermint/tendermint/abci/types" +) + +// MockStakingKeeper is a mock of StakingKeeper interface. +type MockStakingKeeper struct { + ctrl *gomock.Controller + recorder *MockStakingKeeperMockRecorder +} + +// MockStakingKeeperMockRecorder is the mock recorder for MockStakingKeeper. +type MockStakingKeeperMockRecorder struct { + mock *MockStakingKeeper +} + +// NewMockStakingKeeper creates a new mock instance. +func NewMockStakingKeeper(ctrl *gomock.Controller) *MockStakingKeeper { + mock := &MockStakingKeeper{ctrl: ctrl} + mock.recorder = &MockStakingKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockStakingKeeper) EXPECT() *MockStakingKeeperMockRecorder { + return m.recorder +} + +// ApplyAndReturnValidatorSetUpdates mocks base method. +func (m *MockStakingKeeper) ApplyAndReturnValidatorSetUpdates(arg0 types.Context) ([]types1.ValidatorUpdate, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ApplyAndReturnValidatorSetUpdates", arg0) + ret0, _ := ret[0].([]types1.ValidatorUpdate) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ApplyAndReturnValidatorSetUpdates indicates an expected call of ApplyAndReturnValidatorSetUpdates. +func (mr *MockStakingKeeperMockRecorder) ApplyAndReturnValidatorSetUpdates(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ApplyAndReturnValidatorSetUpdates", reflect.TypeOf((*MockStakingKeeper)(nil).ApplyAndReturnValidatorSetUpdates), arg0) +} + +// MockAccountKeeper is a mock of AccountKeeper interface. +type MockAccountKeeper struct { + ctrl *gomock.Controller + recorder *MockAccountKeeperMockRecorder +} + +// MockAccountKeeperMockRecorder is the mock recorder for MockAccountKeeper. +type MockAccountKeeperMockRecorder struct { + mock *MockAccountKeeper +} + +// NewMockAccountKeeper creates a new mock instance. +func NewMockAccountKeeper(ctrl *gomock.Controller) *MockAccountKeeper { + mock := &MockAccountKeeper{ctrl: ctrl} + mock.recorder = &MockAccountKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAccountKeeper) EXPECT() *MockAccountKeeperMockRecorder { + return m.recorder +} + +// IterateAccounts mocks base method. +func (m *MockAccountKeeper) IterateAccounts(ctx types.Context, process func(types0.AccountI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateAccounts", ctx, process) +} + +// IterateAccounts indicates an expected call of IterateAccounts. +func (mr *MockAccountKeeperMockRecorder) IterateAccounts(ctx, process interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateAccounts", reflect.TypeOf((*MockAccountKeeper)(nil).IterateAccounts), ctx, process) +} + +// NewAccount mocks base method. +func (m *MockAccountKeeper) NewAccount(arg0 types.Context, arg1 types0.AccountI) types0.AccountI { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewAccount", arg0, arg1) + ret0, _ := ret[0].(types0.AccountI) + return ret0 +} + +// NewAccount indicates an expected call of NewAccount. +func (mr *MockAccountKeeperMockRecorder) NewAccount(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewAccount", reflect.TypeOf((*MockAccountKeeper)(nil).NewAccount), arg0, arg1) +} + +// SetAccount mocks base method. +func (m *MockAccountKeeper) SetAccount(arg0 types.Context, arg1 types0.AccountI) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetAccount", arg0, arg1) +} + +// SetAccount indicates an expected call of SetAccount. +func (mr *MockAccountKeeperMockRecorder) SetAccount(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetAccount), arg0, arg1) +} + +// MockGenesisAccountsIterator is a mock of GenesisAccountsIterator interface. +type MockGenesisAccountsIterator struct { + ctrl *gomock.Controller + recorder *MockGenesisAccountsIteratorMockRecorder +} + +// MockGenesisAccountsIteratorMockRecorder is the mock recorder for MockGenesisAccountsIterator. +type MockGenesisAccountsIteratorMockRecorder struct { + mock *MockGenesisAccountsIterator +} + +// NewMockGenesisAccountsIterator creates a new mock instance. +func NewMockGenesisAccountsIterator(ctrl *gomock.Controller) *MockGenesisAccountsIterator { + mock := &MockGenesisAccountsIterator{ctrl: ctrl} + mock.recorder = &MockGenesisAccountsIteratorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGenesisAccountsIterator) EXPECT() *MockGenesisAccountsIteratorMockRecorder { + return m.recorder +} + +// IterateGenesisAccounts mocks base method. +func (m *MockGenesisAccountsIterator) IterateGenesisAccounts(cdc *codec.LegacyAmino, appGenesis map[string]json.RawMessage, cb func(types0.AccountI) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateGenesisAccounts", cdc, appGenesis, cb) +} + +// IterateGenesisAccounts indicates an expected call of IterateGenesisAccounts. +func (mr *MockGenesisAccountsIteratorMockRecorder) IterateGenesisAccounts(cdc, appGenesis, cb interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateGenesisAccounts", reflect.TypeOf((*MockGenesisAccountsIterator)(nil).IterateGenesisAccounts), cdc, appGenesis, cb) +} + +// MockGenesisBalancesIterator is a mock of GenesisBalancesIterator interface. +type MockGenesisBalancesIterator struct { + ctrl *gomock.Controller + recorder *MockGenesisBalancesIteratorMockRecorder +} + +// MockGenesisBalancesIteratorMockRecorder is the mock recorder for MockGenesisBalancesIterator. +type MockGenesisBalancesIteratorMockRecorder struct { + mock *MockGenesisBalancesIterator +} + +// NewMockGenesisBalancesIterator creates a new mock instance. +func NewMockGenesisBalancesIterator(ctrl *gomock.Controller) *MockGenesisBalancesIterator { + mock := &MockGenesisBalancesIterator{ctrl: ctrl} + mock.recorder = &MockGenesisBalancesIteratorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockGenesisBalancesIterator) EXPECT() *MockGenesisBalancesIteratorMockRecorder { + return m.recorder +} + +// IterateGenesisBalances mocks base method. +func (m *MockGenesisBalancesIterator) IterateGenesisBalances(cdc codec.JSONCodec, appGenesis map[string]json.RawMessage, cb func(exported.GenesisBalance) bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "IterateGenesisBalances", cdc, appGenesis, cb) +} + +// IterateGenesisBalances indicates an expected call of IterateGenesisBalances. +func (mr *MockGenesisBalancesIteratorMockRecorder) IterateGenesisBalances(cdc, appGenesis, cb interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IterateGenesisBalances", reflect.TypeOf((*MockGenesisBalancesIterator)(nil).IterateGenesisBalances), cdc, appGenesis, cb) +}