package testenv import ( "encoding/json" "fmt" "strings" "time" // helpers // tendermint abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/proto/tendermint/types" dbm "github.com/tendermint/tm-db" // cosmos-sdk "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/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" // wasmd "github.com/CosmWasm/wasmd/x/wasm" wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" // osmosis "github.com/osmosis-labs/osmosis/v13/app" ) type TestEnv struct { App *app.OsmosisApp Ctx sdk.Context } // DebugAppOptions is a stub implementing AppOptions type DebugAppOptions struct{} // Get implements AppOptions func (ao DebugAppOptions) Get(o string) interface{} { if o == server.FlagTrace { return true } return nil } func SetupOsmosisApp() *app.OsmosisApp { db := dbm.NewMemDB() appInstance := app.NewOsmosisApp( log.NewNopLogger(), db, nil, true, map[int64]bool{}, app.DefaultNodeHome, 5, DebugAppOptions{}, app.GetWasmEnabledProposals(), app.EmptyWasmOpts, ) genesisState := app.NewDefaultGenesisState() encCfg := app.MakeEncodingConfig() wasmGen := wasm.GenesisState{ Params: wasmtypes.Params{ // Allow store code without gov CodeUploadAccess: wasmtypes.AllowEverybody, InstantiateDefaultPermission: wasmtypes.AccessTypeEverybody, }, } genesisState[wasm.ModuleName] = encCfg.Marshaler.MustMarshalJSON(&wasmGen) stateBytes, err := json.MarshalIndent(genesisState, "", " ") requireNoErr(err) concensusParams := simapp.DefaultConsensusParams concensusParams.Block = &abci.BlockParams{ MaxBytes: 22020096, MaxGas: -1, } // replace sdk.DefaultDenom with "uosmo", a bit of a hack, needs improvement stateBytes = []byte(strings.Replace(string(stateBytes), "\"stake\"", "\"uosmo\"", -1)) appInstance.InitChain( abci.RequestInitChain{ Validators: []abci.ValidatorUpdate{}, ConsensusParams: concensusParams, AppStateBytes: stateBytes, }, ) return appInstance } func (env *TestEnv) BeginNewBlock(executeNextEpoch bool) { var valAddr []byte validators := env.App.StakingKeeper.GetAllValidators(env.Ctx) if len(validators) >= 1 { valAddrFancy, err := validators[0].GetConsAddr() requireNoErr(err) valAddr = valAddrFancy.Bytes() } else { valAddrFancy := env.setupValidator(stakingtypes.Bonded) validator, _ := env.App.StakingKeeper.GetValidator(env.Ctx, valAddrFancy) valAddr2, _ := validator.GetConsAddr() valAddr = valAddr2.Bytes() } env.beginNewBlockWithProposer(executeNextEpoch, valAddr) } // beginNewBlockWithProposer begins a new block with a proposer. func (env *TestEnv) beginNewBlockWithProposer(executeNextEpoch bool, proposer sdk.ValAddress) { validator, found := env.App.StakingKeeper.GetValidator(env.Ctx, proposer) if !found { panic("validator not found") } valConsAddr, err := validator.GetConsAddr() requireNoErr(err) valAddr := valConsAddr.Bytes() epochIdentifier := env.App.SuperfluidKeeper.GetEpochIdentifier(env.Ctx) epoch := env.App.EpochsKeeper.GetEpochInfo(env.Ctx, epochIdentifier) newBlockTime := env.Ctx.BlockTime().Add(5 * time.Second) if executeNextEpoch { newBlockTime = env.Ctx.BlockTime().Add(epoch.Duration).Add(time.Second) } header := tmtypes.Header{ChainID: "osmosis-1", Height: env.Ctx.BlockHeight() + 1, Time: newBlockTime} newCtx := env.Ctx.WithBlockTime(newBlockTime).WithBlockHeight(env.Ctx.BlockHeight() + 1) env.Ctx = newCtx lastCommitInfo := abci.LastCommitInfo{ Votes: []abci.VoteInfo{{ Validator: abci.Validator{Address: valAddr, Power: 1000}, SignedLastBlock: true, }}, } reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo} env.App.BeginBlock(reqBeginBlock) env.Ctx = env.App.NewContext(false, reqBeginBlock.Header) } func (env *TestEnv) setupValidator(bondStatus stakingtypes.BondStatus) sdk.ValAddress { valPub := secp256k1.GenPrivKey().PubKey() valAddr := sdk.ValAddress(valPub.Address()) bondDenom := env.App.StakingKeeper.GetParams(env.Ctx).BondDenom selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom}) err := simapp.FundAccount(env.App.BankKeeper, env.Ctx, sdk.AccAddress(valPub.Address()), selfBond) requireNoErr(err) stakingHandler := staking.NewHandler(*env.App.StakingKeeper) stakingCoin := sdk.NewCoin(bondDenom, selfBond[0].Amount) ZeroCommission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) msg, err := stakingtypes.NewMsgCreateValidator(valAddr, valPub, stakingCoin, stakingtypes.Description{}, ZeroCommission, sdk.OneInt()) requireNoErr(err) res, err := stakingHandler(env.Ctx, msg) requireNoErr(err) requireNoNil("staking handler", res) env.App.BankKeeper.SendCoinsFromModuleToModule(env.Ctx, stakingtypes.NotBondedPoolName, stakingtypes.BondedPoolName, sdk.NewCoins(stakingCoin)) val, found := env.App.StakingKeeper.GetValidator(env.Ctx, valAddr) requierTrue("validator found", found) val = val.UpdateStatus(bondStatus) env.App.StakingKeeper.SetValidator(env.Ctx, val) consAddr, err := val.GetConsAddr() requireNoErr(err) signingInfo := slashingtypes.NewValidatorSigningInfo( consAddr, env.Ctx.BlockHeight(), 0, time.Unix(0, 0), false, 0, ) env.App.SlashingKeeper.SetValidatorSigningInfo(env.Ctx, consAddr, signingInfo) return valAddr } func requireNoErr(err error) { if err != nil { panic(err) } } func requireNoNil(name string, nilable any) { if nilable == nil { panic(fmt.Sprintf("%s must not be nil", name)) } } func requierTrue(name string, b bool) { if !b { panic(fmt.Sprintf("%s must be true", name)) } }