package apptesting
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
dbm "github.com/cometbft/cometbft-db"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cometbft/cometbft/crypto/ed25519"
"github.com/cometbft/cometbft/libs/log"
tmtypes "github.com/cometbft/cometbft/proto/tendermint/types"
"cosmossdk.io/math"
"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/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"
authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec"
banktestutil "github.com/cosmos/cosmos-sdk/x/bank/testutil"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
stakinghelper "github.com/cosmos/cosmos-sdk/x/staking/testutil"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/cybercongress/go-cyber/v7/app"
appparams "github.com/cybercongress/go-cyber/v7/app/params"
)
type KeeperTestHelper struct {
suite.Suite
App *app.App
Ctx sdk.Context
QueryHelper *baseapp.QueryServiceTestHelper
TestAccs []sdk.AccAddress
StakingHelper *stakinghelper.Helper
}
var (
SecondaryDenom = "uion"
SecondaryAmount = sdk.NewInt(100000000)
)
func (s *KeeperTestHelper) Setup() {
t := s.T()
s.App = app.Setup(t)
s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "juno-1", Time: time.Now().UTC()})
s.QueryHelper = &baseapp.QueryServiceTestHelper{
GRPCQueryRouter: s.App.GRPCQueryRouter(),
Ctx: s.Ctx,
}
s.TestAccs = CreateRandomAccounts(3)
s.StakingHelper = stakinghelper.NewHelper(s.Suite.T(), s.Ctx, s.App.AppKeepers.StakingKeeper)
s.StakingHelper.Denom = "ujuno"
}
func (s *KeeperTestHelper) SetupTestForInitGenesis() {
t := s.T()
s.App = app.Setup(t)
s.Ctx = s.App.BaseApp.NewContext(true, tmtypes.Header{
ChainID: "testing",
})
}
func (s *KeeperTestHelper) CreateTestContext() sdk.Context {
ctx, _ := s.CreateTestContextWithMultiStore()
return ctx
}
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
}
func (s *KeeperTestHelper) Commit() {
oldHeight := s.Ctx.BlockHeight()
oldHeader := s.Ctx.BlockHeader()
s.App.Commit()
newHeader := tmtypes.Header{Height: oldHeight + 1, ChainID: "testing", Time: oldHeader.Time.Add(time.Second)}
s.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader})
s.Ctx = s.App.NewContext(false, newHeader)
}
func (s *KeeperTestHelper) FundAcc(acc sdk.AccAddress, amounts sdk.Coins) {
err := banktestutil.FundAccount(s.App.AppKeepers.BankKeeper, s.Ctx, acc, amounts)
s.Require().NoError(err)
}
func (s *KeeperTestHelper) FundModuleAcc(moduleName string, amounts sdk.Coins) {
err := banktestutil.FundModuleAccount(s.App.AppKeepers.BankKeeper, s.Ctx, moduleName, amounts)
s.Require().NoError(err)
}
func (s *KeeperTestHelper) MintCoins(coins sdk.Coins) {
err := s.App.AppKeepers.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, coins)
s.Require().NoError(err)
}
func (s *KeeperTestHelper) SetupValidator(bondStatus stakingtypes.BondStatus) sdk.ValAddress {
valPriv := secp256k1.GenPrivKey()
valPub := valPriv.PubKey()
valAddr := sdk.ValAddress(valPub.Address())
bondDenom := s.App.AppKeepers.StakingKeeper.GetParams(s.Ctx).BondDenom
selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom})
s.FundAcc(sdk.AccAddress(valAddr), selfBond)
msg := s.StakingHelper.CreateValidatorMsg(valAddr, valPub, selfBond[0].Amount)
res, err := s.StakingHelper.CreateValidatorWithMsg(s.Ctx, msg)
s.Require().NoError(err)
s.Require().NotNil(res)
val, found := s.App.AppKeepers.StakingKeeper.GetValidator(s.Ctx, valAddr)
s.Require().True(found)
val = val.UpdateStatus(bondStatus)
s.App.AppKeepers.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.AppKeepers.SlashingKeeper.SetValidatorSigningInfo(s.Ctx, consAddr, signingInfo)
return valAddr
}
func (s *KeeperTestHelper) BeginNewBlock() {
var valAddr []byte
validators := s.App.AppKeepers.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.AppKeepers.StakingKeeper.GetValidator(s.Ctx, valAddrFancy)
valAddr2, _ := validator.GetConsAddr()
valAddr = valAddr2.Bytes()
}
s.BeginNewBlockWithProposer(valAddr)
}
func (s *KeeperTestHelper) BeginNewBlockWithProposer(proposer sdk.ValAddress) {
validator, found := s.App.AppKeepers.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.CommitInfo{
Votes: []abci.VoteInfo{{
Validator: abci.Validator{Address: valAddr, Power: 1000},
SignedLastBlock: true,
}},
Round: 0,
}
reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo}
fmt.Println("beginning block ", s.Ctx.BlockHeight())
s.App.BeginBlocker(s.Ctx, reqBeginBlock)
}
func (s *KeeperTestHelper) EndBlock() {
reqEndBlock := abci.RequestEndBlock{Height: s.Ctx.BlockHeight()}
s.App.EndBlocker(s.Ctx, reqEndBlock)
}
func (s *KeeperTestHelper) AllocateRewardsToValidator(valAddr sdk.ValAddress, rewardAmt math.Int) {
validator, found := s.App.AppKeepers.StakingKeeper.GetValidator(s.Ctx, valAddr)
s.Require().True(found)
coins := sdk.Coins{sdk.NewCoin(appparams.BondDenom, rewardAmt)}
err := banktestutil.FundModuleAccount(s.App.AppKeepers.BankKeeper, s.Ctx, distrtypes.ModuleName, coins)
s.Require().NoError(err)
s.Ctx = s.Ctx.WithBlockHeight(s.Ctx.BlockHeight() + 1)
decTokens := sdk.DecCoins{{Denom: appparams.BondDenom, Amount: sdk.NewDec(20000)}}
s.App.AppKeepers.DistrKeeper.AllocateTokensToValidator(s.Ctx, validator, decTokens)
}
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()
}
func (s *KeeperTestHelper) ConfirmUpgradeSucceeded(upgradeName string, upgradeHeight int64) {
s.Ctx = s.Ctx.WithBlockHeight(upgradeHeight - 1)
plan := upgradetypes.Plan{Name: upgradeName, Height: upgradeHeight}
err := s.App.AppKeepers.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan)
s.Require().NoError(err)
_, exists := s.App.AppKeepers.UpgradeKeeper.GetUpgradePlan(s.Ctx)
s.Require().True(exists)
s.Ctx = s.Ctx.WithBlockHeight(upgradeHeight)
s.Require().NotPanics(func() {
beginBlockRequest := abci.RequestBeginBlock{}
s.App.BeginBlocker(s.Ctx, beginBlockRequest)
})
}
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
)
typeURL := sdk.MsgTypeURL(msg)
later := someDate.Add(time.Hour)
grant, err := authz.NewGrant(someDate, authz.NewGenericAuthorization(typeURL), &later)
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)
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)
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
}