wormhole/wormchain/app/apptesting/test_suite.go

296 lines
9.9 KiB
Go

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
}