package apptesting import ( "encoding/json" "fmt" "testing" "time" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/simapp" "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/tx/signing" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" "github.com/cosmos/cosmos-sdk/x/authz" distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types" "github.com/cosmos/cosmos-sdk/x/staking" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" authzcodec "github.com/wormhole-foundation/wormchain/x/tokenfactory/types/authzcodec" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" "github.com/wormhole-foundation/wormchain/app" ) type KeeperTestHelper struct { suite.Suite App *app.App Ctx sdk.Context QueryHelper *baseapp.QueryServiceTestHelper TestAccs []sdk.AccAddress } var ( SecondaryDenom = "uion" SecondaryAmount = sdk.NewInt(100000000) ) // Setup sets up basic environment for suite (App, Ctx, and test accounts) func (s *KeeperTestHelper) Setup() { s.App = Setup(s.T(), true, 0) s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()}) s.QueryHelper = &baseapp.QueryServiceTestHelper{ GRPCQueryRouter: s.App.GRPCQueryRouter(), Ctx: s.Ctx, } s.TestAccs = CreateRandomAccounts(3) } func (s *KeeperTestHelper) SetupTestForInitGenesis() { // Setting to True, leads to init genesis not running s.App = Setup(s.T(), true, 0) s.Ctx = s.App.BaseApp.NewContext(true, tmtypes.Header{}) } // CreateTestContext creates a test context. func (s *KeeperTestHelper) CreateTestContext() sdk.Context { ctx, _ := s.CreateTestContextWithMultiStore() return ctx } // CreateTestContextWithMultiStore creates a test context and returns it together with multi store. func (s *KeeperTestHelper) CreateTestContextWithMultiStore() (sdk.Context, sdk.CommitMultiStore) { db := dbm.NewMemDB() logger := log.NewNopLogger() ms := rootmulti.NewStore(db, logger) return sdk.NewContext(ms, tmtypes.Header{}, false, logger), ms } // CreateTestContext creates a test context. func (s *KeeperTestHelper) Commit() { oldHeight := s.Ctx.BlockHeight() oldHeader := s.Ctx.BlockHeader() s.App.Commit() newHeader := tmtypes.Header{Height: oldHeight + 1, ChainID: oldHeader.ChainID, Time: oldHeader.Time.Add(time.Second)} s.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader}) s.Ctx = s.App.NewContext(false, newHeader) } // FundAcc funds target address with specified amount. func (s *KeeperTestHelper) FundAcc(acc sdk.AccAddress, amounts sdk.Coins) { err := simapp.FundAccount(s.App.BankKeeper, s.Ctx, acc, amounts) s.Require().NoError(err) } // FundModuleAcc funds target modules with specified amount. func (s *KeeperTestHelper) FundModuleAcc(moduleName string, amounts sdk.Coins) { err := simapp.FundModuleAccount(s.App.BankKeeper, s.Ctx, moduleName, amounts) s.Require().NoError(err) } func (s *KeeperTestHelper) MintCoins(coins sdk.Coins) { err := s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, coins) s.Require().NoError(err) } // SetupValidator sets up a validator and returns the ValAddress. func (s *KeeperTestHelper) SetupValidator(bondStatus stakingtypes.BondStatus) sdk.ValAddress { valPub := secp256k1.GenPrivKey().PubKey() valAddr := sdk.ValAddress(valPub.Address()) bondDenom := s.App.StakingKeeper.GetParams(s.Ctx).BondDenom selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom}) s.FundAcc(sdk.AccAddress(valAddr), selfBond) stakingHandler := staking.NewHandler(s.App.StakingKeeper) stakingCoin := sdk.NewCoin(sdk.DefaultBondDenom, selfBond[0].Amount) ZeroCommission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) msg, err := stakingtypes.NewMsgCreateValidator(valAddr, valPub, stakingCoin, stakingtypes.Description{}, ZeroCommission, sdk.OneInt()) s.Require().NoError(err) res, err := stakingHandler(s.Ctx, msg) s.Require().NoError(err) s.Require().NotNil(res) val, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) s.Require().True(found) val = val.UpdateStatus(bondStatus) s.App.StakingKeeper.SetValidator(s.Ctx, val) consAddr, err := val.GetConsAddr() s.Suite.Require().NoError(err) signingInfo := slashingtypes.NewValidatorSigningInfo( consAddr, s.Ctx.BlockHeight(), 0, time.Unix(0, 0), false, 0, ) s.App.SlashingKeeper.SetValidatorSigningInfo(s.Ctx, consAddr, signingInfo) return valAddr } // BeginNewBlock starts a new block. func (s *KeeperTestHelper) BeginNewBlock() { var valAddr []byte validators := s.App.StakingKeeper.GetAllValidators(s.Ctx) if len(validators) >= 1 { valAddrFancy, err := validators[0].GetConsAddr() s.Require().NoError(err) valAddr = valAddrFancy.Bytes() } else { valAddrFancy := s.SetupValidator(stakingtypes.Bonded) validator, _ := s.App.StakingKeeper.GetValidator(s.Ctx, valAddrFancy) valAddr2, _ := validator.GetConsAddr() valAddr = valAddr2.Bytes() } s.BeginNewBlockWithProposer(valAddr) } // BeginNewBlockWithProposer begins a new block with a proposer. func (s *KeeperTestHelper) BeginNewBlockWithProposer(proposer sdk.ValAddress) { validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, proposer) s.Assert().True(found) valConsAddr, err := validator.GetConsAddr() s.Require().NoError(err) valAddr := valConsAddr.Bytes() newBlockTime := s.Ctx.BlockTime().Add(5 * time.Second) header := tmtypes.Header{Height: s.Ctx.BlockHeight() + 1, Time: newBlockTime} newCtx := s.Ctx.WithBlockTime(newBlockTime).WithBlockHeight(s.Ctx.BlockHeight() + 1) s.Ctx = newCtx lastCommitInfo := abci.LastCommitInfo{ Votes: []abci.VoteInfo{{ Validator: abci.Validator{Address: valAddr, Power: 1000}, SignedLastBlock: true, }}, } reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo} fmt.Println("beginning block ", s.Ctx.BlockHeight()) s.App.BeginBlocker(s.Ctx, reqBeginBlock) } // EndBlock ends the block. func (s *KeeperTestHelper) EndBlock() { reqEndBlock := abci.RequestEndBlock{Height: s.Ctx.BlockHeight()} s.App.EndBlocker(s.Ctx, reqEndBlock) } // AllocateRewardsToValidator allocates reward tokens to a distribution module then allocates rewards to the validator address. func (s *KeeperTestHelper) AllocateRewardsToValidator(valAddr sdk.ValAddress, rewardAmt sdk.Int) { validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr) s.Require().True(found) // allocate reward tokens to distribution module coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, rewardAmt)} err := simapp.FundModuleAccount(s.App.BankKeeper, s.Ctx, distrtypes.ModuleName, coins) s.Require().NoError(err) // allocate rewards to validator s.Ctx = s.Ctx.WithBlockHeight(s.Ctx.BlockHeight() + 1) decTokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(20000)}} s.App.DistrKeeper.AllocateTokensToValidator(s.Ctx, validator, decTokens) } // BuildTx builds a transaction. func (s *KeeperTestHelper) BuildTx( txBuilder client.TxBuilder, msgs []sdk.Msg, sigV2 signing.SignatureV2, memo string, txFee sdk.Coins, gasLimit uint64, ) authsigning.Tx { err := txBuilder.SetMsgs(msgs[0]) s.Require().NoError(err) err = txBuilder.SetSignatures(sigV2) s.Require().NoError(err) txBuilder.SetMemo(memo) txBuilder.SetFeeAmount(txFee) txBuilder.SetGasLimit(gasLimit) return txBuilder.GetTx() } // CreateRandomAccounts is a function return a list of randomly generated AccAddresses func CreateRandomAccounts(numAccts int) []sdk.AccAddress { testAddrs := make([]sdk.AccAddress, numAccts) for i := 0; i < numAccts; i++ { pk := ed25519.GenPrivKey().PubKey() testAddrs[i] = sdk.AccAddress(pk.Address()) } return testAddrs } func TestMessageAuthzSerialization(t *testing.T, msg sdk.Msg) { someDate := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC) const ( mockGranter string = "cosmos1abc" mockGrantee string = "cosmos1xyz" ) var ( mockMsgGrant authz.MsgGrant mockMsgRevoke authz.MsgRevoke mockMsgExec authz.MsgExec ) // Authz: Grant Msg typeURL := sdk.MsgTypeURL(msg) grant, err := authz.NewGrant(authz.NewGenericAuthorization(typeURL), someDate.Add(time.Hour)) require.NoError(t, err) msgGrant := authz.MsgGrant{Granter: mockGranter, Grantee: mockGrantee, Grant: grant} msgGrantBytes := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgGrant))) err = authzcodec.ModuleCdc.UnmarshalJSON(msgGrantBytes, &mockMsgGrant) require.NoError(t, err) // Authz: Revoke Msg msgRevoke := authz.MsgRevoke{Granter: mockGranter, Grantee: mockGrantee, MsgTypeUrl: typeURL} msgRevokeByte := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgRevoke))) err = authzcodec.ModuleCdc.UnmarshalJSON(msgRevokeByte, &mockMsgRevoke) require.NoError(t, err) // Authz: Exec Msg msgAny, err := cdctypes.NewAnyWithValue(msg) require.NoError(t, err) msgExec := authz.MsgExec{Grantee: mockGrantee, Msgs: []*cdctypes.Any{msgAny}} execMsgByte := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgExec))) err = authzcodec.ModuleCdc.UnmarshalJSON(execMsgByte, &mockMsgExec) require.NoError(t, err) require.Equal(t, msgExec.Msgs[0].Value, mockMsgExec.Msgs[0].Value) } func GenerateTestAddrs() (string, string) { pk1 := ed25519.GenPrivKey().PubKey() validAddr := sdk.AccAddress(pk1.Address()).String() invalidAddr := sdk.AccAddress("invalid").String() return validAddr, invalidAddr }