2020-04-23 16:35:58 +00:00
|
|
|
package simulation
|
2020-04-12 16:34:01 +00:00
|
|
|
|
|
|
|
import (
|
2020-09-01 15:33:12 +00:00
|
|
|
"fmt"
|
2020-04-12 16:34:01 +00:00
|
|
|
"math/rand"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
2020-04-23 16:35:58 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
|
|
"github.com/cosmos/cosmos-sdk/simapp/helpers"
|
2020-04-12 16:34:01 +00:00
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
2020-04-23 16:35:58 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
2020-04-12 16:34:01 +00:00
|
|
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
appparams "github.com/kava-labs/kava/app/params"
|
2020-04-12 16:34:01 +00:00
|
|
|
"github.com/kava-labs/kava/x/pricefeed/keeper"
|
|
|
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
|
|
|
)
|
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
// Simulation operation weights constants
|
|
|
|
const (
|
|
|
|
OpWeightMsgUpdatePrices = "op_weight_msg_update_prices"
|
2020-09-01 15:33:12 +00:00
|
|
|
|
|
|
|
// Block time params are un-exported constants in cosmos-sdk/x/simulation.
|
|
|
|
// Copy them here in lieu of importing them.
|
|
|
|
minTimePerBlock time.Duration = (10000 / 2) * time.Second
|
|
|
|
maxTimePerBlock time.Duration = 10000 * time.Second
|
|
|
|
|
|
|
|
// Calculate the average block time
|
|
|
|
AverageBlockTime time.Duration = (maxTimePerBlock - minTimePerBlock) / 2
|
2020-04-23 16:35:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// WeightedOperations returns all the operations from the module with their respective weights
|
|
|
|
func WeightedOperations(
|
|
|
|
appParams simulation.AppParams, cdc *codec.Codec, ak auth.AccountKeeper, k keeper.Keeper,
|
|
|
|
) simulation.WeightedOperations {
|
|
|
|
var weightMsgUpdatePrices int
|
|
|
|
// var numBlocks int
|
|
|
|
|
|
|
|
appParams.GetOrGenerate(cdc, OpWeightMsgUpdatePrices, &weightMsgUpdatePrices, nil,
|
|
|
|
func(_ *rand.Rand) {
|
|
|
|
weightMsgUpdatePrices = appparams.DefaultWeightMsgUpdatePrices
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
return simulation.WeightedOperations{
|
|
|
|
simulation.NewWeightedOperation(
|
|
|
|
weightMsgUpdatePrices,
|
|
|
|
SimulateMsgUpdatePrices(ak, k, 10000),
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-12 16:34:01 +00:00
|
|
|
// SimulateMsgUpdatePrices updates the prices of various assets by randomly varying them based on current price
|
2020-04-23 16:35:58 +00:00
|
|
|
func SimulateMsgUpdatePrices(ak auth.AccountKeeper, keeper keeper.Keeper, blocks int) simulation.Operation {
|
2020-06-18 02:03:47 +00:00
|
|
|
// runs one at the start of each simulation
|
|
|
|
startingPrices := map[string]sdk.Dec{
|
|
|
|
"btc:usd": sdk.MustNewDecFromStr("7000"),
|
|
|
|
"bnb:usd": sdk.MustNewDecFromStr("15"),
|
|
|
|
"xrp:usd": sdk.MustNewDecFromStr("0.25"),
|
|
|
|
}
|
|
|
|
|
|
|
|
// creates the new price generator from starting prices - resets for each sim
|
|
|
|
priceGenerator := NewPriceGenerator(startingPrices)
|
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
return func(
|
|
|
|
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
|
|
|
|
) (simulation.OperationMsg, []simulation.FutureOperation, error) {
|
2020-06-18 02:03:47 +00:00
|
|
|
// walk prices to current block height, noop if already called for current height
|
|
|
|
priceGenerator.Step(r, ctx.BlockHeight())
|
2020-04-12 16:34:01 +00:00
|
|
|
|
|
|
|
randomMarket := pickRandomAsset(ctx, keeper, r)
|
|
|
|
marketID := randomMarket.MarketID
|
|
|
|
address := getRandomOracle(r, randomMarket)
|
2020-04-23 16:35:58 +00:00
|
|
|
|
|
|
|
oracle, found := simulation.FindAccount(accs, address)
|
|
|
|
if !found {
|
|
|
|
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
oracleAcc := ak.GetAccount(ctx, oracle.Address)
|
|
|
|
if oracleAcc == nil {
|
|
|
|
return simulation.NoOpMsg(types.ModuleName), nil, nil
|
|
|
|
}
|
|
|
|
|
2020-06-18 02:03:47 +00:00
|
|
|
// get price for marketID and current block height set in Step
|
|
|
|
price := priceGenerator.GetCurrentPrice(marketID)
|
2020-04-12 16:34:01 +00:00
|
|
|
|
|
|
|
// get the expiry time based off the current time
|
|
|
|
expiry := getExpiryTime(ctx)
|
|
|
|
|
|
|
|
// now create the msg to post price
|
2020-04-23 16:35:58 +00:00
|
|
|
msg := types.NewMsgPostPrice(oracle.Address, marketID, price, expiry)
|
2020-04-12 16:34:01 +00:00
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
spendable := oracleAcc.SpendableCoins(ctx.BlockTime())
|
|
|
|
fees, err := simulation.RandomFees(r, ctx, spendable)
|
|
|
|
if err != nil {
|
|
|
|
return simulation.NoOpMsg(types.ModuleName), nil, err
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|
|
|
|
|
2020-04-23 16:35:58 +00:00
|
|
|
tx := helpers.GenTx(
|
|
|
|
[]sdk.Msg{msg},
|
|
|
|
fees,
|
|
|
|
helpers.DefaultGenTxGas,
|
|
|
|
chainID,
|
|
|
|
[]uint64{oracleAcc.GetAccountNumber()},
|
|
|
|
[]uint64{oracleAcc.GetSequence()},
|
|
|
|
oracle.PrivKey,
|
|
|
|
)
|
|
|
|
|
|
|
|
_, result, err := app.Deliver(tx)
|
|
|
|
if err != nil {
|
2020-09-01 15:33:12 +00:00
|
|
|
// to aid debugging, add the stack trace to the comment field of the returned opMsg
|
|
|
|
return simulation.NewOperationMsg(msg, false, fmt.Sprintf("%+v", err)), nil, err
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|
2020-04-23 16:35:58 +00:00
|
|
|
return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// getRandomOracle picks a random oracle from the list of oracles
|
2020-04-23 16:35:58 +00:00
|
|
|
func getRandomOracle(r *rand.Rand, market types.Market) sdk.AccAddress {
|
2020-04-12 16:34:01 +00:00
|
|
|
randomIndex := simulation.RandIntBetween(r, 0, len(market.Oracles))
|
2020-04-23 16:35:58 +00:00
|
|
|
return market.Oracles[randomIndex]
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// pickRandomAsset picks a random asset out of the assets with equal probability
|
|
|
|
// it returns the Market which includes the base asset as one of its fields
|
|
|
|
func pickRandomAsset(ctx sdk.Context, keeper keeper.Keeper, r *rand.Rand) (market types.Market) {
|
|
|
|
// get the params
|
|
|
|
params := keeper.GetParams(ctx)
|
|
|
|
// now pick a random asset
|
|
|
|
randomIndex := simulation.RandIntBetween(r, 0, len(params.Markets))
|
2020-04-23 16:35:58 +00:00
|
|
|
return params.Markets[randomIndex]
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// getExpiryTime gets a price expiry time by taking the current time and adding a delta to it
|
|
|
|
func getExpiryTime(ctx sdk.Context) (t time.Time) {
|
|
|
|
// need to use the blocktime from the context as the context generates random start time when running simulations
|
2020-09-01 15:33:12 +00:00
|
|
|
return ctx.BlockTime().Add(AverageBlockTime * 5000) // if blocks were 6 seconds, the expiry would be 8 hrs
|
2020-04-12 16:34:01 +00:00
|
|
|
}
|