Skip to content

Commit

Permalink
test: add simulation test
Browse files Browse the repository at this point in the history
  • Loading branch information
hacheigriega committed Feb 6, 2024
1 parent e46c733 commit a439282
Show file tree
Hide file tree
Showing 4 changed files with 345 additions and 28 deletions.
25 changes: 22 additions & 3 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"cosmossdk.io/client/v2/autocli"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/log"

sdkmath "cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/circuit"
Expand Down Expand Up @@ -105,6 +106,7 @@ import (
capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types"

// ibc
"github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8/packetforward"
packetforwardkeeper "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8/packetforward/keeper"
packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8/packetforward/types"
ibcfee "github.com/cosmos/ibc-go/v8/modules/apps/29-fee"
Expand All @@ -129,7 +131,6 @@ import (
icahosttypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/types"
icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types"

"github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8/packetforward"
"github.com/sedaprotocol/seda-chain/app/keepers"
appparams "github.com/sedaprotocol/seda-chain/app/params"
"github.com/sedaprotocol/seda-chain/docs"
Expand Down Expand Up @@ -566,6 +567,18 @@ func NewApp(
wasmOpts...,
)

// //
// //
// // Register the proposal types
// // Deprecated: Avoid adding new handlers, instead use the new proposal flow
// // by granting the governance module the right to execute the message.
// // See: https://docs.cosmos.network/main/modules/gov#proposal-messages
// govRouter := govv1beta1.NewRouter()
// govRouter.AddRoute(govtypes.RouterKey, govv1beta1.ProposalHandler).
// AddRoute(paramproposal.RouterKey, params.NewParamChangeProposalHandler(app.ParamsKeeper))
// //
// //

govConfig := govtypes.DefaultConfig()
govKeeper := govkeeper.NewKeeper(
appCodec,
Expand All @@ -574,12 +587,18 @@ func NewApp(
app.BankKeeper,
app.StakingKeeper,
app.DistrKeeper,

app.MsgServiceRouter(),
govConfig,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)

// //
// //
// // Set legacy router for backwards compatibility with gov v1beta1
// govKeeper.SetLegacyRouter(govRouter)
// //
// //

app.GovKeeper = *govKeeper.SetHooks(
govtypes.NewMultiGovHooks(
// register the governance hooks
Expand Down Expand Up @@ -846,7 +865,7 @@ func NewApp(

// create the simulation manager and define the order of the modules for deterministic simulations
overrideModules := map[string]module.AppModuleSimulation{
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts, nil),
authtypes.ModuleName: auth.NewAppModule(app.appCodec, app.AccountKeeper, randomGenesisAccounts, nil),
}
app.sm = module.NewSimulationManagerFromAppModules(app.mm.Modules, overrideModules)
app.sm.RegisterStoreDecoders()
Expand Down
53 changes: 53 additions & 0 deletions app/genesis.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package app

import (
"crypto/rand"
"encoding/json"

sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/auth/types"
vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
)

// The genesis state of the blockchain is represented here as a map of raw json
Expand All @@ -19,3 +26,49 @@ type GenesisState map[string]json.RawMessage
func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState {
return ModuleBasics.DefaultGenesis(cdc)
}

// randomGenesisAccounts defines the default RandomGenesisAccountsFn used on the SDK.
// It creates a slice of BaseAccount, ContinuousVestingAccount and DelayedVestingAccount.
// NOTE: This function is a modified version of
// https://github.com/cosmos/cosmos-sdk/blob/7e6948f50cd4838a0161838a099f74e0b5b0213c/x/auth/simulation/genesis.go#L26
func randomGenesisAccounts(simState *module.SimulationState) types.GenesisAccounts {
genesisAccs := make(types.GenesisAccounts, len(simState.Accounts))
for i, acc := range simState.Accounts {
bacc := types.NewBaseAccountWithAddress(acc.Address)

// Only consider making a vesting account once the initial bonded validator
// set is exhausted due to needing to track DelegatedVesting.
if !(int64(i) > simState.NumBonded && simState.Rand.Intn(100) < 50) {
genesisAccs[i] = bacc
continue
}

initialVestingAmount, err := rand.Int(rand.Reader, simState.InitialStake.BigInt())
if err != nil {
panic(err)
}
initialVesting := sdk.NewCoins(sdk.NewCoin(simState.BondDenom, sdkmath.NewIntFromBigInt(initialVestingAmount)))

var endTime int64
startTime := simState.GenTimestamp.Unix()
// Allow for some vesting accounts to vest very quickly while others very slowly.
if simState.Rand.Intn(100) < 50 {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*24*30))))
} else {
endTime = int64(simulation.RandIntBetween(simState.Rand, int(startTime)+1, int(startTime+(60*60*12))))
}

bva, err := vestingtypes.NewBaseVestingAccount(bacc, initialVesting, endTime)
if err != nil {
panic(err)
}

if simState.Rand.Intn(100) < 50 {
genesisAccs[i] = vestingtypes.NewContinuousVestingAccountRaw(bva, startTime)
} else {
genesisAccs[i] = vestingtypes.NewDelayedVestingAccountRaw(bva)
}
}

return genesisAccs
}
260 changes: 260 additions & 0 deletions simulation/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package app_test

import (
cryptorand "crypto/rand"
"encoding/json"
"fmt"
"math/big"
"math/rand"
"os"
"time"

"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/runtime"
simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
simcli "github.com/cosmos/cosmos-sdk/x/simulation/client/cli"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

type storeKeysPrefixes struct {
A storetypes.StoreKey
B storetypes.StoreKey
Prefixes [][]byte
}

// fauxMerkleModeOpt returns a BaseApp option to use a dbStoreAdapter instead of
// an IAVLStore for faster simulation speed.
func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
bapp.SetFauxMerkleMode()
}

// simulationOperations retrieves the simulation params from the provided file path
// and returns all the modules weighted operations
func simulationOperations(app runtime.AppI, cdc codec.JSONCodec, config simtypes.Config) []simtypes.WeightedOperation {
simState := module.SimulationState{
AppParams: make(simtypes.AppParams),
Cdc: cdc,
TxConfig: moduletestutil.MakeTestTxConfig(),
BondDenom: sdk.DefaultBondDenom,
}

if config.ParamsFile != "" {
bz, err := os.ReadFile(config.ParamsFile)
if err != nil {
panic(err)
}

err = json.Unmarshal(bz, &simState.AppParams)
if err != nil {
panic(err)
}
}

simState.LegacyProposalContents = nil
simState.ProposalMsgs = app.SimulationManager().GetProposalMsgs(simState)
return app.SimulationManager().WeightedOperations(simState)
}

// Simulation parameter constants
const (
StakePerAccount = "stake_per_account"
InitiallyBondedValidators = "initially_bonded_validators"
)

// appStateFn returns the initial application state using a genesis or the simulation parameters.
// NOTE: This function is a modified version of
// github.com/cosmos/cosmos-sdk/blob/7e6948f50cd4838a0161838a099f74e0b5b0213c/testutil/sims/state_helpers.go#L56
func appStateFn(cdc codec.JSONCodec, simManager *module.SimulationManager, genesisState map[string]json.RawMessage) simtypes.AppStateFn {
return func(
r *rand.Rand,
accs []simtypes.Account,
config simtypes.Config,
) (appState json.RawMessage, simAccs []simtypes.Account, chainID string, genesisTimestamp time.Time) {
if simcli.FlagGenesisTimeValue == 0 {
genesisTimestamp = simtypes.RandTimestamp(r)
} else {
genesisTimestamp = time.Unix(simcli.FlagGenesisTimeValue, 0)
}

chainID = config.ChainID
switch {
case config.ParamsFile != "" && config.GenesisFile != "":
panic("cannot provide both a genesis file and a params file")

case config.GenesisFile != "":
// override the default chain-id from simapp to set it later to the config
genesisDoc, accounts, err := simtestutil.AppStateFromGenesisFileFn(r, cdc, config.GenesisFile)
if err != nil {
panic(err)
}

if simcli.FlagGenesisTimeValue == 0 {
// use genesis timestamp if no custom timestamp is provided (i.e no random timestamp)
genesisTimestamp = genesisDoc.GenesisTime
}

appState = genesisDoc.AppState
chainID = genesisDoc.ChainID
simAccs = accounts

case config.ParamsFile != "":
appParams := make(simtypes.AppParams)
bz, err := os.ReadFile(config.ParamsFile)
if err != nil {
panic(err)
}

err = json.Unmarshal(bz, &appParams)
if err != nil {
panic(err)
}
appState, simAccs = appStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams, genesisState)

default:
appParams := make(simtypes.AppParams)
appState, simAccs = appStateRandomizedFn(simManager, r, cdc, accs, genesisTimestamp, appParams, genesisState)
}

rawState := make(map[string]json.RawMessage)
err := json.Unmarshal(appState, &rawState)
if err != nil {
panic(err)
}

stakingStateBz, ok := rawState[stakingtypes.ModuleName]
if !ok {
panic("staking genesis state is missing")
}

stakingState := new(stakingtypes.GenesisState)
if err = cdc.UnmarshalJSON(stakingStateBz, stakingState); err != nil {
panic(err)
}
// compute not bonded balance
notBondedTokens := math.ZeroInt()
for _, val := range stakingState.Validators {
if val.Status != stakingtypes.Unbonded {
continue
}
notBondedTokens = notBondedTokens.Add(val.GetTokens())
}
notBondedCoins := sdk.NewCoin(stakingState.Params.BondDenom, notBondedTokens)
// edit bank state to make it have the not bonded pool tokens
bankStateBz, ok := rawState[banktypes.ModuleName]
// TODO(fdymylja/jonathan): should we panic in this case
if !ok {
panic("bank genesis state is missing")
}
bankState := new(banktypes.GenesisState)
if err = cdc.UnmarshalJSON(bankStateBz, bankState); err != nil {
panic(err)
}

stakingAddr := authtypes.NewModuleAddress(stakingtypes.NotBondedPoolName).String()
var found bool
for _, balance := range bankState.Balances {
if balance.Address == stakingAddr {
found = true
break
}
}
if !found {
bankState.Balances = append(bankState.Balances, banktypes.Balance{
Address: stakingAddr,
Coins: sdk.NewCoins(notBondedCoins),
})
}

// change appState back
rawState[stakingtypes.ModuleName] = cdc.MustMarshalJSON(stakingState)
rawState[banktypes.ModuleName] = cdc.MustMarshalJSON(bankState)

// replace appstate
appState, err = json.Marshal(rawState)
if err != nil {
panic(err)
}
return appState, simAccs, chainID, genesisTimestamp
}
}

// appStateRandomizedFn creates calls each module's GenesisState generator function
// and creates the simulation params
func appStateRandomizedFn(
simManager *module.SimulationManager,
r *rand.Rand,
cdc codec.JSONCodec,
accs []simtypes.Account,
genesisTimestamp time.Time,
appParams simtypes.AppParams,
genesisState map[string]json.RawMessage,
) (json.RawMessage, []simtypes.Account) {
numAccs := int64(len(accs))
// generate a random amount of initial stake coins and a random initial
// number of bonded accounts
var (
numInitiallyBonded int64
initialStake math.Int
)

// max value 10^24 aseda
max := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(24), nil)
initialStakeAmount, err := cryptorand.Int(cryptorand.Reader, max)
if err != nil {
panic(err)
}

appParams.GetOrGenerate(
StakePerAccount, &initialStake, r,
func(r *rand.Rand) {
initialStake = math.NewIntFromBigInt(initialStakeAmount)
},
)
appParams.GetOrGenerate(
InitiallyBondedValidators, &numInitiallyBonded, r,
func(r *rand.Rand) { numInitiallyBonded = int64(r.Intn(300)) },
)

if numInitiallyBonded > numAccs {
numInitiallyBonded = numAccs
}

fmt.Printf(
`Selected randomly generated parameters for simulated genesis:
{
stake_per_account: "%s",
initially_bonded_validators: "%d"
}
`, initialStake.String(), numInitiallyBonded,
)

simState := &module.SimulationState{
AppParams: appParams,
Cdc: cdc,
Rand: r,
GenState: genesisState,
Accounts: accs,
InitialStake: initialStake,
NumBonded: numInitiallyBonded,
BondDenom: sdk.DefaultBondDenom,
GenTimestamp: genesisTimestamp,
}

simManager.GenerateGenesisStates(simState)

appState, err := json.Marshal(genesisState)
if err != nil {
panic(err)
}

return appState, accs
}
Loading

0 comments on commit a439282

Please sign in to comment.