mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-27 00:35:18 +00:00
55747ed0b8
* Generate pricefeed prices using a random walk at the beginning of sims Co-authored-by: rhuairahrighairigh <ruaridh.odonnell@gmail.com> Co-authored-by: John Maheswaran <john@kava.io> Co-authored-by: Ruaridh <rhuairahrighairidh@users.noreply.github.com> Co-authored-by: John Maheswaran <john@noreply> Co-authored-by: Kevin Davis <karzak@users.noreply.github.com> Co-authored-by: Kevin Davis <kjydavis3@gmail.com> Co-authored-by: Denali Marsh <denali@kava.io>
168 lines
5.2 KiB
Go
168 lines
5.2 KiB
Go
package operations
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/cosmos/cosmos-sdk/x/simulation"
|
|
|
|
"github.com/kava-labs/kava/x/pricefeed"
|
|
"github.com/kava-labs/kava/x/pricefeed/keeper"
|
|
"github.com/kava-labs/kava/x/pricefeed/types"
|
|
)
|
|
|
|
var (
|
|
noOpMsg = simulation.NoOpMsg(pricefeed.ModuleName)
|
|
btcPrices = []sdk.Dec{}
|
|
bnbPrices = []sdk.Dec{}
|
|
xrpPrices = []sdk.Dec{}
|
|
genPrices sync.Once
|
|
)
|
|
|
|
// SimulateMsgUpdatePrices updates the prices of various assets by randomly varying them based on current price
|
|
func SimulateMsgUpdatePrices(keeper keeper.Keeper, blocks int) simulation.Operation {
|
|
// get a pricefeed handler
|
|
handler := pricefeed.NewHandler(keeper)
|
|
|
|
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account) (
|
|
simulation.OperationMsg, []simulation.FutureOperation, error) {
|
|
genPrices.Do(func() {
|
|
// generate a random walk for each asset exactly once, with observations equal to the number of blocks in the sim
|
|
for _, m := range keeper.GetMarkets(ctx) {
|
|
startPrice := getStartPrice(m.MarketID)
|
|
// allow prices to fluctuate from 10x GAINZ to 100x REKT
|
|
maxPrice := sdk.MustNewDecFromStr("10.0").Mul(startPrice)
|
|
minPrice := sdk.MustNewDecFromStr("0.01").Mul(startPrice)
|
|
previousPrice := startPrice
|
|
for i := 0; i < blocks; i++ {
|
|
increment := getIncrement(m.MarketID)
|
|
// note calling r instead of rand here breaks determinism
|
|
upDown := rand.Intn(2)
|
|
if upDown == 0 {
|
|
if previousPrice.Add(increment).GT(maxPrice) {
|
|
previousPrice = maxPrice
|
|
} else {
|
|
previousPrice = previousPrice.Add(increment)
|
|
}
|
|
} else {
|
|
if previousPrice.Sub(increment).LT(minPrice) {
|
|
previousPrice = minPrice
|
|
} else {
|
|
previousPrice = previousPrice.Sub(increment)
|
|
}
|
|
}
|
|
setPrice(m.MarketID, previousPrice)
|
|
}
|
|
}
|
|
})
|
|
|
|
randomMarket := pickRandomAsset(ctx, keeper, r)
|
|
marketID := randomMarket.MarketID
|
|
address := getRandomOracle(r, randomMarket)
|
|
price := pickNewRandomPrice(marketID, int(ctx.BlockHeight()))
|
|
|
|
// get the expiry time based off the current time
|
|
expiry := getExpiryTime(ctx)
|
|
|
|
// now create the msg to post price
|
|
msg := types.NewMsgPostPrice(address, marketID, price, expiry)
|
|
|
|
// Perform basic validation of the msg - don't submit errors that fail ValidateBasic, use unit tests for testing ValidateBasic
|
|
if err := msg.ValidateBasic(); err != nil {
|
|
return noOpMsg, nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
|
}
|
|
|
|
// now we submit the pricefeed update message
|
|
if ok := submitMsg(ctx, handler, msg); !ok {
|
|
return noOpMsg, nil, fmt.Errorf("could not submit pricefeed msg")
|
|
}
|
|
return simulation.NewOperationMsg(msg, true, "pricefeed update submitted"), nil, nil
|
|
}
|
|
}
|
|
|
|
func getStartPrice(marketID string) (startPrice sdk.Dec) {
|
|
switch marketID {
|
|
case "btc:usd":
|
|
return sdk.MustNewDecFromStr("7000")
|
|
case "bnb:usd":
|
|
return sdk.MustNewDecFromStr("15")
|
|
case "xrp:usd":
|
|
return sdk.MustNewDecFromStr("0.25")
|
|
}
|
|
return sdk.MustNewDecFromStr("100")
|
|
}
|
|
|
|
func getIncrement(marketID string) (increment sdk.Dec) {
|
|
startPrice := getStartPrice(marketID)
|
|
divisor := sdk.MustNewDecFromStr("20")
|
|
increment = startPrice.Quo(divisor)
|
|
return increment
|
|
}
|
|
|
|
func setPrice(marketID string, price sdk.Dec) {
|
|
switch marketID {
|
|
case "btc:usd":
|
|
btcPrices = append(btcPrices, price)
|
|
return
|
|
case "bnb:usd":
|
|
bnbPrices = append(bnbPrices, price)
|
|
return
|
|
case "xrp:usd":
|
|
xrpPrices = append(xrpPrices, price)
|
|
}
|
|
return
|
|
}
|
|
|
|
func pickNewRandomPrice(marketID string, blockHeight int) (newPrice sdk.Dec) {
|
|
switch marketID {
|
|
case "btc:usd":
|
|
return btcPrices[blockHeight-1]
|
|
case "bnb:usd":
|
|
return bnbPrices[blockHeight-1]
|
|
case "xrp:usd":
|
|
return xrpPrices[blockHeight-1]
|
|
}
|
|
panic("invalid price request")
|
|
}
|
|
|
|
// getRandomOracle picks a random oracle from the list of oracles
|
|
func getRandomOracle(r *rand.Rand, market pricefeed.Market) sdk.AccAddress {
|
|
randomIndex := simulation.RandIntBetween(r, 0, len(market.Oracles))
|
|
oracle := market.Oracles[randomIndex]
|
|
return oracle
|
|
}
|
|
|
|
// 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))
|
|
market = params.Markets[randomIndex]
|
|
return market
|
|
}
|
|
|
|
// 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
|
|
t = ctx.BlockTime().Add(time.Second * 1000000)
|
|
return t
|
|
}
|
|
|
|
// submitMsg submits a message to the current instance of the keeper and returns a boolean whether the operation completed successfully or not
|
|
func submitMsg(ctx sdk.Context, handler sdk.Handler, msg sdk.Msg) (ok bool) {
|
|
ctx, write := ctx.CacheContext()
|
|
got := handler(ctx, msg)
|
|
|
|
ok = got.IsOK()
|
|
if ok {
|
|
write()
|
|
}
|
|
return ok
|
|
}
|