mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-25 07:45:18 +00:00
[R4R] BEP3 simulations (#423)
* implement randomized genesis, params * implement operations: MsgCreateAtomicSwap * implement claim, refund future ops * remove dynamic block locks * refactor BondedAddresses * add consistent supported assets
This commit is contained in:
parent
1bdf580a61
commit
3da4657102
@ -34,6 +34,7 @@ import (
|
|||||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||||
stakingsimops "github.com/cosmos/cosmos-sdk/x/staking/simulation/operations"
|
stakingsimops "github.com/cosmos/cosmos-sdk/x/staking/simulation/operations"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply"
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
|
bep3simops "github.com/kava-labs/kava/x/bep3/simulation/operations"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Simulation parameter constants
|
// Simulation parameter constants
|
||||||
@ -56,6 +57,7 @@ const (
|
|||||||
OpWeightMsgUndelegate = "op_weight_msg_undelegate"
|
OpWeightMsgUndelegate = "op_weight_msg_undelegate"
|
||||||
OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate"
|
OpWeightMsgBeginRedelegate = "op_weight_msg_begin_redelegate"
|
||||||
OpWeightMsgUnjail = "op_weight_msg_unjail"
|
OpWeightMsgUnjail = "op_weight_msg_unjail"
|
||||||
|
OpWeightMsgCreateAtomicSwap = "op_weight_msg_create_atomic_Swap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestMain runs setup and teardown code before all tests.
|
// TestMain runs setup and teardown code before all tests.
|
||||||
@ -264,6 +266,17 @@ func testAndRunTxs(app *App, config simulation.Config) []simulation.WeightedOper
|
|||||||
}(nil),
|
}(nil),
|
||||||
slashingsimops.SimulateMsgUnjail(app.slashingKeeper),
|
slashingsimops.SimulateMsgUnjail(app.slashingKeeper),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
func(_ *rand.Rand) int {
|
||||||
|
var v int
|
||||||
|
ap.GetOrGenerate(app.cdc, OpWeightMsgCreateAtomicSwap, &v, nil,
|
||||||
|
func(_ *rand.Rand) {
|
||||||
|
v = 100
|
||||||
|
})
|
||||||
|
return v
|
||||||
|
}(nil),
|
||||||
|
bep3simops.SimulateMsgCreateAtomicSwap(app.accountKeeper, app.bep3Keeper),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,21 +2,167 @@ package simulation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"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/module"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
authexported "github.com/cosmos/cosmos-sdk/x/auth/exported"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/supply"
|
||||||
"github.com/kava-labs/kava/x/bep3/types"
|
"github.com/kava-labs/kava/x/bep3/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Simulation parameter constants
|
||||||
|
const (
|
||||||
|
BnbDeputyAddress = "bnb_deputy_address"
|
||||||
|
MinBlockLock = "min_block_lock"
|
||||||
|
MaxBlockLock = "max_block_lock"
|
||||||
|
SupportedAssets = "supported_assets"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MaxSupplyLimit = sdk.NewInt(10000000000000000)
|
||||||
|
Accs []simulation.Account
|
||||||
|
ConsistentDenoms = [3]string{"bnb", "xrp", "btc"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenBnbDeputyAddress randomized BnbDeputyAddress
|
||||||
|
func GenBnbDeputyAddress(r *rand.Rand) sdk.AccAddress {
|
||||||
|
return simulation.RandomAcc(r, Accs).Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenMinBlockLock randomized MinBlockLock
|
||||||
|
func GenMinBlockLock(r *rand.Rand) int64 {
|
||||||
|
min := int(types.AbsoluteMinimumBlockLock)
|
||||||
|
max := int(types.AbsoluteMaximumBlockLock)
|
||||||
|
return int64(r.Intn(max-min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenMaxBlockLock randomized MaxBlockLock
|
||||||
|
func GenMaxBlockLock(r *rand.Rand, minBlockLock int64) int64 {
|
||||||
|
min := int(minBlockLock)
|
||||||
|
max := int(types.AbsoluteMaximumBlockLock)
|
||||||
|
return int64(r.Intn(max-min) + min)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenSupportedAssets gets randomized SupportedAssets
|
||||||
|
func GenSupportedAssets(r *rand.Rand) types.AssetParams {
|
||||||
|
var assets types.AssetParams
|
||||||
|
for i := 0; i < (r.Intn(10) + 1); i++ {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
denom := strings.ToLower(simulation.RandStringOfLength(r, (r.Intn(3) + 3)))
|
||||||
|
asset := genSupportedAsset(r, denom)
|
||||||
|
assets = append(assets, asset)
|
||||||
|
}
|
||||||
|
// Add bnb, btc, or xrp as a supported asset for interactions with other modules
|
||||||
|
stableAsset := genSupportedAsset(r, ConsistentDenoms[r.Intn(3)])
|
||||||
|
assets = append(assets, stableAsset)
|
||||||
|
|
||||||
|
return assets
|
||||||
|
}
|
||||||
|
|
||||||
|
func genSupportedAsset(r *rand.Rand, denom string) types.AssetParam {
|
||||||
|
coinID, _ := simulation.RandPositiveInt(r, sdk.NewInt(100000))
|
||||||
|
limit, _ := simulation.RandPositiveInt(r, MaxSupplyLimit)
|
||||||
|
return types.AssetParam{
|
||||||
|
Denom: denom,
|
||||||
|
CoinID: int(coinID.Int64()),
|
||||||
|
Limit: limit,
|
||||||
|
Active: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RandomizedGenState generates a random GenesisState
|
// RandomizedGenState generates a random GenesisState
|
||||||
func RandomizedGenState(simState *module.SimulationState) {
|
func RandomizedGenState(simState *module.SimulationState) {
|
||||||
|
Accs = simState.Accounts
|
||||||
|
|
||||||
// TODO implement this fully
|
bep3Genesis := loadRandomBep3GenState(simState)
|
||||||
// - randomly generating the genesis params
|
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, bep3Genesis))
|
||||||
// - overwriting with genesis provided to simulation
|
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(bep3Genesis)
|
||||||
genesisState := types.DefaultGenesisState()
|
|
||||||
|
|
||||||
fmt.Printf("Selected randomly generated %s parameters:\n%s\n", types.ModuleName, codec.MustMarshalJSONIndent(simState.Cdc, genesisState))
|
authGenesis, totalCoins := loadAuthGenState(simState, bep3Genesis)
|
||||||
simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesisState)
|
simState.GenState[auth.ModuleName] = simState.Cdc.MustMarshalJSON(authGenesis)
|
||||||
|
|
||||||
|
// Update supply to match amount of coins in auth
|
||||||
|
var supplyGenesis supply.GenesisState
|
||||||
|
simState.Cdc.MustUnmarshalJSON(simState.GenState[supply.ModuleName], &supplyGenesis)
|
||||||
|
for _, deputyCoin := range totalCoins {
|
||||||
|
supplyGenesis.Supply = supplyGenesis.Supply.Add(deputyCoin)
|
||||||
|
}
|
||||||
|
simState.GenState[supply.ModuleName] = simState.Cdc.MustMarshalJSON(supplyGenesis)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRandomBep3GenState(simState *module.SimulationState) types.GenesisState {
|
||||||
|
bnbDeputyAddress := simulation.RandomAcc(simState.Rand, simState.Accounts).Address
|
||||||
|
|
||||||
|
// min/max block lock are hardcoded to 50/100 for expected -NumBlocks=100
|
||||||
|
minBlockLock := int64(types.AbsoluteMinimumBlockLock)
|
||||||
|
maxBlockLock := minBlockLock * 2
|
||||||
|
|
||||||
|
var supportedAssets types.AssetParams
|
||||||
|
simState.AppParams.GetOrGenerate(
|
||||||
|
simState.Cdc, SupportedAssets, &supportedAssets, simState.Rand,
|
||||||
|
func(r *rand.Rand) { supportedAssets = GenSupportedAssets(r) },
|
||||||
|
)
|
||||||
|
|
||||||
|
bep3Genesis := types.GenesisState{
|
||||||
|
Params: types.Params{
|
||||||
|
BnbDeputyAddress: bnbDeputyAddress,
|
||||||
|
MinBlockLock: minBlockLock,
|
||||||
|
MaxBlockLock: maxBlockLock,
|
||||||
|
SupportedAssets: supportedAssets,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return bep3Genesis
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadAuthGenState(simState *module.SimulationState, bep3Genesis types.GenesisState) (
|
||||||
|
auth.GenesisState, []sdk.Coins) {
|
||||||
|
var authGenesis auth.GenesisState
|
||||||
|
simState.Cdc.MustUnmarshalJSON(simState.GenState[auth.ModuleName], &authGenesis)
|
||||||
|
|
||||||
|
deputy, found := getAccount(authGenesis.Accounts, bep3Genesis.Params.BnbDeputyAddress)
|
||||||
|
if !found {
|
||||||
|
panic("deputy address not found in available accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load total limit of each supported asset to deputy's account
|
||||||
|
var totalCoins []sdk.Coins
|
||||||
|
for _, asset := range bep3Genesis.Params.SupportedAssets {
|
||||||
|
assetCoin := sdk.NewCoins(sdk.NewCoin(asset.Denom, asset.Limit))
|
||||||
|
if err := deputy.SetCoins(deputy.GetCoins().Add(assetCoin)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
totalCoins = append(totalCoins, assetCoin)
|
||||||
|
}
|
||||||
|
authGenesis.Accounts = replaceOrAppendAccount(authGenesis.Accounts, deputy)
|
||||||
|
|
||||||
|
return authGenesis, totalCoins
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an account from a list of accounts that matches an address.
|
||||||
|
func getAccount(accounts []authexported.GenesisAccount, addr sdk.AccAddress) (authexported.GenesisAccount, bool) {
|
||||||
|
for _, a := range accounts {
|
||||||
|
if a.GetAddress().Equals(addr) {
|
||||||
|
return a, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// In a list of accounts, replace the first account found with the same address. If not found, append the account.
|
||||||
|
func replaceOrAppendAccount(accounts []authexported.GenesisAccount, acc authexported.GenesisAccount) []authexported.GenesisAccount {
|
||||||
|
newAccounts := accounts
|
||||||
|
for i, a := range accounts {
|
||||||
|
if a.GetAddress().Equals(acc.GetAddress()) {
|
||||||
|
newAccounts[i] = acc
|
||||||
|
return newAccounts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(newAccounts, acc)
|
||||||
}
|
}
|
||||||
|
153
x/bep3/simulation/operations/msg.go
Normal file
153
x/bep3/simulation/operations/msg.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package operations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/bep3"
|
||||||
|
"github.com/kava-labs/kava/x/bep3/keeper"
|
||||||
|
"github.com/kava-labs/kava/x/bep3/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noOpMsg = simulation.NoOpMsg(bep3.ModuleName)
|
||||||
|
)
|
||||||
|
|
||||||
|
// SimulateMsgCreateAtomicSwap generates a MsgCreateAtomicSwap with random values
|
||||||
|
func SimulateMsgCreateAtomicSwap(ak auth.AccountKeeper, k keeper.Keeper) simulation.Operation {
|
||||||
|
handler := bep3.NewHandler(k)
|
||||||
|
|
||||||
|
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||||
|
simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||||
|
|
||||||
|
sender := k.GetBnbDeputyAddress(ctx)
|
||||||
|
recipient := simulation.RandomAcc(r, accs).Address
|
||||||
|
|
||||||
|
recipientOtherChain := simulation.RandStringOfLength(r, 43)
|
||||||
|
senderOtherChain := simulation.RandStringOfLength(r, 43)
|
||||||
|
|
||||||
|
// Generate cryptographically strong pseudo-random number
|
||||||
|
randomNumber, err := types.GenerateSecureRandomNumber()
|
||||||
|
if err != nil {
|
||||||
|
return noOpMsg, nil, err
|
||||||
|
}
|
||||||
|
// Must use current blocktime instead of 'now' since initial blocktime was randomly generated
|
||||||
|
timestamp := ctx.BlockTime().Unix()
|
||||||
|
randomNumberHash := types.CalculateRandomHash(randomNumber.Bytes(), timestamp)
|
||||||
|
|
||||||
|
// Randomly select an asset from supported assets
|
||||||
|
assets, found := k.GetAssets(ctx)
|
||||||
|
if !found {
|
||||||
|
return noOpMsg, nil, fmt.Errorf("no supported assets found")
|
||||||
|
}
|
||||||
|
asset := assets[r.Intn(len(assets))]
|
||||||
|
|
||||||
|
// Check that the sender has coins of this type
|
||||||
|
availableAmount := ak.GetAccount(ctx, sender).GetCoins().AmountOf(asset.Denom)
|
||||||
|
if !availableAmount.IsPositive() {
|
||||||
|
return noOpMsg, nil, fmt.Errorf("available amount must be positive")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a random amount of the available coins
|
||||||
|
amount, err := simulation.RandPositiveInt(r, availableAmount)
|
||||||
|
if err != nil {
|
||||||
|
return noOpMsg, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't adjust the conversion factor, we'll be out of funds soon
|
||||||
|
adjustedAmount := amount.Int64() / int64(math.Pow10(8))
|
||||||
|
coin := sdk.NewInt64Coin(asset.Denom, adjustedAmount)
|
||||||
|
coins := sdk.NewCoins(coin)
|
||||||
|
expectedIncome := coin.String()
|
||||||
|
|
||||||
|
// We're assuming that sims are run with -NumBlocks=100
|
||||||
|
heightSpan := int64(55)
|
||||||
|
crossChain := true
|
||||||
|
|
||||||
|
msg := types.NewMsgCreateAtomicSwap(
|
||||||
|
sender, recipient, recipientOtherChain, senderOtherChain, randomNumberHash,
|
||||||
|
timestamp, coins, expectedIncome, heightSpan, crossChain)
|
||||||
|
|
||||||
|
if err := msg.ValidateBasic(); err != nil {
|
||||||
|
return noOpMsg, nil, fmt.Errorf("expected MsgCreateAtomicSwap to pass ValidateBasic: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit msg
|
||||||
|
ok := submitMsg(ctx, handler, msg)
|
||||||
|
|
||||||
|
// If created, construct a MsgClaimAtomicSwap or MsgRefundAtomicSwap future operation
|
||||||
|
var futureOp simulation.FutureOperation
|
||||||
|
if ok {
|
||||||
|
swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain)
|
||||||
|
acc := simulation.RandomAcc(r, accs)
|
||||||
|
evenOdd := r.Intn(2) + 1
|
||||||
|
if evenOdd%2 == 0 {
|
||||||
|
// Claim future operation
|
||||||
|
executionBlock := ctx.BlockHeight() + (msg.HeightSpan / 2)
|
||||||
|
futureOp = loadClaimFutureOp(acc.Address, swapID, randomNumber.Bytes(), executionBlock, handler)
|
||||||
|
} else {
|
||||||
|
// Refund future operation
|
||||||
|
executionBlock := ctx.BlockHeight() + msg.HeightSpan
|
||||||
|
futureOp = loadRefundFutureOp(acc.Address, swapID, executionBlock, handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return simulation.NewOperationMsg(msg, ok, ""), []simulation.FutureOperation{futureOp}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadClaimFutureOp(sender sdk.AccAddress, swapID []byte, randomNumber []byte, height int64, handler sdk.Handler) simulation.FutureOperation {
|
||||||
|
claimOp := func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||||
|
simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||||
|
|
||||||
|
// Build the refund msg and validate basic
|
||||||
|
claimMsg := types.NewMsgClaimAtomicSwap(sender, swapID, randomNumber)
|
||||||
|
if err := claimMsg.ValidateBasic(); err != nil {
|
||||||
|
return noOpMsg, nil, fmt.Errorf("expected MsgClaimAtomicSwap to pass ValidateBasic: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test msg submission at target block height
|
||||||
|
ok := handler(ctx.WithBlockHeight(height), claimMsg).IsOK()
|
||||||
|
return simulation.NewOperationMsg(claimMsg, ok, ""), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return simulation.FutureOperation{
|
||||||
|
BlockHeight: int(height),
|
||||||
|
Op: claimOp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRefundFutureOp(sender sdk.AccAddress, swapID []byte, height int64, handler sdk.Handler) simulation.FutureOperation {
|
||||||
|
refundOp := func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
||||||
|
simulation.OperationMsg, []simulation.FutureOperation, error) {
|
||||||
|
// Build the refund msg and validate basic
|
||||||
|
refundMsg := types.NewMsgRefundAtomicSwap(sender, swapID)
|
||||||
|
if err := refundMsg.ValidateBasic(); err != nil {
|
||||||
|
return noOpMsg, nil, fmt.Errorf("expected MsgRefundAtomicSwap to pass ValidateBasic: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test msg submission at target block height
|
||||||
|
ok := handler(ctx.WithBlockHeight(height), refundMsg).IsOK()
|
||||||
|
return simulation.NewOperationMsg(refundMsg, ok, ""), nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return simulation.FutureOperation{
|
||||||
|
BlockHeight: int(height),
|
||||||
|
Op: refundOp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func submitMsg(ctx sdk.Context, handler sdk.Handler, msg sdk.Msg) (ok bool) {
|
||||||
|
ctx, write := ctx.CacheContext()
|
||||||
|
ok = handler(ctx, msg).IsOK()
|
||||||
|
if ok {
|
||||||
|
write()
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
@ -1,14 +1,46 @@
|
|||||||
package simulation
|
package simulation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/bep3/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyBnbDeputyAddress = "BnbDeputyAddress"
|
||||||
|
keyMinBlockLock = "MinBlockLock"
|
||||||
|
keyMaxBlockLock = "MaxBlockLock"
|
||||||
|
keySupportedAssets = "SupportedAssets"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParamChanges defines the parameters that can be modified by param change proposals
|
// ParamChanges defines the parameters that can be modified by param change proposals
|
||||||
// on the simulation
|
|
||||||
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
func ParamChanges(r *rand.Rand) []simulation.ParamChange {
|
||||||
// TODO implement this
|
// We generate MinBlockLock first because the result is required by GenMaxBlockLock()
|
||||||
return []simulation.ParamChange{}
|
minBlockLockVal := GenMinBlockLock(r)
|
||||||
|
|
||||||
|
return []simulation.ParamChange{
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, keyBnbDeputyAddress, "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%s\"", GenBnbDeputyAddress(r))
|
||||||
|
},
|
||||||
|
),
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, keyMinBlockLock, "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%d\"", minBlockLockVal)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, keyMaxBlockLock, "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%d\"", GenMaxBlockLock(r, minBlockLockVal))
|
||||||
|
},
|
||||||
|
),
|
||||||
|
simulation.NewSimParamChange(types.ModuleName, keySupportedAssets, "",
|
||||||
|
func(r *rand.Rand) string {
|
||||||
|
return fmt.Sprintf("\"%v\"", GenSupportedAssets(r))
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user