Skip to content

Commit

Permalink
test: add missing simulation operations
Browse files Browse the repository at this point in the history
having performance issue, need to solve it afterward
  • Loading branch information
kingcre committed Jul 27, 2023
1 parent 08fc9b8 commit c69d78c
Show file tree
Hide file tree
Showing 2 changed files with 315 additions and 23 deletions.
234 changes: 211 additions & 23 deletions x/exchange/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ import (

// Simulation operation weights constants.
const (
OpWeightMsgCreateMarket = "op_weight_msg_create_market"
OpWeightMsgPlaceLimitOrder = "op_weight_msg_place_limit_order"
OpWeightMsgPlaceMarketOrder = "op_weight_msg_place_market_order"
OpWeightMsgCreateMarket = "op_weight_msg_create_market"
OpWeightMsgPlaceLimitOrder = "op_weight_msg_place_limit_order"
OpWeightMsgPlaceMarketOrder = "op_weight_msg_place_market_order"
OpWeightMsgCancelOrder = "op_weight_msg_cancel_order"
OpWeightMsgCancelAllOrders = "op_weight_msg_cancel_all_orders"
OpWeightMsgSwapExactAmountIn = "op_weight_msg_swap_exact_amount_in"

DefaultWeightMsgCreateMarket = 10
DefaultWeightMsgPlaceLimitOrder = 90
DefaultWeightMsgPlaceMarketOrder = 90
DefaultWeightMsgCreateMarket = 10
DefaultWeightMsgPlaceLimitOrder = 90
DefaultWeightMsgPlaceMarketOrder = 90
DefaultWeightMsgCancelOrder = 20
DefaultWeightMsgCancelAllOrders = 10
DefaultWeightMsgSwapExactAmountIn = 80
)

var (
Expand All @@ -38,9 +44,12 @@ func WeightedOperations(
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
) simulation.WeightedOperations {
var (
weightMsgCreateMarket int
weightMsgPlaceLimitOrder int
weightMsgPlaceMarketOrder int
weightMsgCreateMarket int
weightMsgPlaceLimitOrder int
weightMsgPlaceMarketOrder int
weightMsgCancelOrder int
weightMsgCancelAllOrders int
weightMsgSwapExactAmountIn int
)
appParams.GetOrGenerate(cdc, OpWeightMsgCreateMarket, &weightMsgCreateMarket, nil, func(_ *rand.Rand) {
weightMsgCreateMarket = DefaultWeightMsgCreateMarket
Expand All @@ -51,6 +60,15 @@ func WeightedOperations(
appParams.GetOrGenerate(cdc, OpWeightMsgPlaceMarketOrder, &weightMsgPlaceMarketOrder, nil, func(_ *rand.Rand) {
weightMsgPlaceMarketOrder = DefaultWeightMsgPlaceMarketOrder
})
appParams.GetOrGenerate(cdc, OpWeightMsgCancelOrder, &weightMsgCancelOrder, nil, func(_ *rand.Rand) {
weightMsgCancelOrder = DefaultWeightMsgCancelOrder
})
appParams.GetOrGenerate(cdc, OpWeightMsgCancelAllOrders, &weightMsgCancelAllOrders, nil, func(_ *rand.Rand) {
weightMsgCancelAllOrders = DefaultWeightMsgCancelAllOrders
})
appParams.GetOrGenerate(cdc, OpWeightMsgSwapExactAmountIn, &weightMsgSwapExactAmountIn, nil, func(_ *rand.Rand) {
weightMsgSwapExactAmountIn = DefaultWeightMsgSwapExactAmountIn
})

return simulation.WeightedOperations{
simulation.NewWeightedOperation(
Expand All @@ -65,6 +83,18 @@ func WeightedOperations(
weightMsgPlaceMarketOrder,
SimulateMsgPlaceMarketOrder(ak, bk, k),
),
simulation.NewWeightedOperation(
weightMsgCancelOrder,
SimulateMsgCancelOrder(ak, bk, k),
),
simulation.NewWeightedOperation(
weightMsgCancelAllOrders,
SimulateMsgCancelAllOrders(ak, bk, k),
),
simulation.NewWeightedOperation(
weightMsgSwapExactAmountIn,
SimulateMsgSwapExactAmountIn(ak, bk, k),
),
}
}

Expand Down Expand Up @@ -156,6 +186,93 @@ func SimulateMsgPlaceMarketOrder(
}
}

func SimulateMsgCancelOrder(
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
simAccount, msg, found := findMsgCancelOrderParams(r, accs, k, ctx)
if !found {
return simtypes.NoOpMsg(
types.ModuleName, types.TypeMsgCancelOrder, "unable to cancel order"), nil, nil
}
txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: appparams.MakeTestEncodingConfig().TxConfig,
Msg: msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: bk.SpendableCoins(ctx, simAccount.Address),
}
return utils.GenAndDeliverTxWithFees(txCtx, gas, fees)
}
}

func SimulateMsgCancelAllOrders(
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
simAccount, msg, found := findMsgCancelAllOrdersParams(r, accs, k, ctx)
if !found {
return simtypes.NoOpMsg(
types.ModuleName, types.TypeMsgCancelAllOrders, "unable to cancel all orders"), nil, nil
}
txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: appparams.MakeTestEncodingConfig().TxConfig,
Msg: msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: bk.SpendableCoins(ctx, simAccount.Address),
}
return utils.GenAndDeliverTxWithFees(txCtx, gas, fees)
}
}

func SimulateMsgSwapExactAmountIn(
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
) simtypes.Operation {
return func(
r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
accs []simtypes.Account, chainID string,
) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
simAccount, msg, found := findMsgSwapExactAmountInParams(r, accs, bk, k, ctx)
if !found {
return simtypes.NoOpMsg(
types.ModuleName, types.TypeMsgSwapExactAmountIn, "unable to swap exact amount in"), nil, nil
}
txCtx := simulation.OperationInput{
R: r,
App: app,
TxGen: appparams.MakeTestEncodingConfig().TxConfig,
Msg: msg,
MsgType: msg.Type(),
Context: ctx,
SimAccount: simAccount,
AccountKeeper: ak,
Bankkeeper: bk,
ModuleName: types.ModuleName,
CoinsSpentInMsg: bk.SpendableCoins(ctx, simAccount.Address),
}
return utils.GenAndDeliverTxWithFees(txCtx, gas, fees)
}
}

func findMsgCreateMarketParams(r *rand.Rand, accs []simtypes.Account,
bk types.BankKeeper, k keeper.Keeper, ctx sdk.Context) (acc simtypes.Account, msg *types.MsgCreateMarket, found bool) {
var allDenoms []string
Expand Down Expand Up @@ -247,25 +364,96 @@ func findMsgPlaceMarketOrderParams(
return acc, msg, true
}
}
marketState := k.MustGetMarketState(ctx, market.Id)
if marketState.LastPrice == nil {
continue
// Temporarily comment out buy market order operation because
// of performance issue.
//marketState := k.MustGetMarketState(ctx, market.Id)
//if marketState.LastPrice == nil {
// continue
//}
//qty := utils.RandomDec(r, sdk.NewDec(100), sdk.NewDec(1_000000)).TruncateDec()
//cacheCtx, _ := ctx.CacheContext()
//if _, _, err := k.PlaceMarketOrder(cacheCtx, market.Id, acc.Address, true, qty); err != nil {
// continue
//}
//msg = types.NewMsgPlaceMarketOrder(acc.Address, market.Id, true, qty)
//return acc, msg, true
}
}
return acc, msg, false
}

func findMsgCancelOrderParams(
r *rand.Rand, accs []simtypes.Account, k keeper.Keeper, ctx sdk.Context) (acc simtypes.Account, msg *types.MsgCancelOrder, found bool) {
accs = utils.ShuffleSimAccounts(r, accs)
for _, acc = range accs {
var orders []types.Order
k.IterateOrdersByOrderer(ctx, acc.Address, func(order types.Order) (stop bool) {
if order.MsgHeight < ctx.BlockHeight() {
orders = append(orders, order)
}
qty := utils.RandomDec(r, sdk.NewDec(100), sdk.NewDec(1_000000)).TruncateDec()
cacheCtx, _ := ctx.CacheContext()
obs := k.ConstructMemOrderBookSide(cacheCtx, market, types.MemOrderBookSideOptions{
IsBuy: false,
QuantityLimit: &qty,
}, nil)
if len(obs.Levels()) == 0 {
return false
})
if len(orders) > 0 {
order := orders[r.Intn(len(orders))]
msg = types.NewMsgCancelOrder(acc.Address, order.Id)
return acc, msg, true
}
}
return acc, nil, false
}

func findMsgCancelAllOrdersParams(
r *rand.Rand, accs []simtypes.Account, k keeper.Keeper, ctx sdk.Context) (acc simtypes.Account, msg *types.MsgCancelAllOrders, found bool) {
acc, _ = simtypes.RandomAcc(r, accs)
var markets []types.Market
k.IterateAllMarkets(ctx, func(market types.Market) (stop bool) {
markets = append(markets, market)
return false
})
if len(markets) == 0 {
return acc, nil, false
}
// CancelAllOrders will succeed even if the orderer has no orders in the market.
// So we just choose random market.
market := markets[r.Intn(len(markets))]
msg = types.NewMsgCancelAllOrders(acc.Address, market.Id)
return acc, msg, true
}

func findMsgSwapExactAmountInParams(
r *rand.Rand, accs []simtypes.Account, bk types.BankKeeper, k keeper.Keeper, ctx sdk.Context) (acc simtypes.Account, msg *types.MsgSwapExactAmountIn, found bool) {
var allDenoms []string
bk.IterateTotalSupply(ctx, func(coin sdk.Coin) bool {
allDenoms = append(allDenoms, coin.Denom)
return false
})
accs = utils.ShuffleSimAccounts(r, accs)
for _, acc = range accs {
spendable := bk.SpendableCoins(ctx, acc.Address)
// We shuffle denoms every time for better randomization of candidate
// denom pair.
r.Shuffle(len(allDenoms), func(i, j int) {
allDenoms[i], allDenoms[j] = allDenoms[j], allDenoms[i]
})
for _, denomIn := range allDenoms {
if !spendable.AmountOf(denomIn).GTE(sdk.NewInt(1_000000)) {
continue
}
price := obs.Levels()[len(obs.Levels())-1].Price()
if balance := spendable.AmountOf(market.QuoteDenom).ToDec(); balance.GT(types.QuoteAmount(true, price, qty)) {
msg = types.NewMsgPlaceMarketOrder(acc.Address, market.Id, true, qty)
input := sdk.NewDecCoin(denomIn, utils.RandomInt(r, sdk.NewInt(10000), sdk.NewInt(1_000000)))
for _, denomOut := range allDenoms {
querier := keeper.Querier{Keeper: k}
resp, err := querier.BestSwapExactAmountInRoutes(sdk.WrapSDKContext(ctx), &types.QueryBestSwapExactAmountInRoutesRequest{
Input: input.String(),
OutputDenom: denomOut,
})
if err != nil {
continue
}
// If there's no error than the output amount always be positive.
msg = types.NewMsgSwapExactAmountIn(acc.Address, resp.Routes, input, resp.Output)
return acc, msg, true
}
}
}
return acc, msg, false
return acc, nil, false
}
104 changes: 104 additions & 0 deletions x/exchange/simulation/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,107 @@ func (s *SimTestSuite) TestSimulateMsgPlaceMarketOrder() {
s.Require().Equal(false, msg.IsBuy)
s.AssertEqual(utils.ParseDec("118906"), msg.Quantity)
}

func (s *SimTestSuite) TestSimulateMsgCancelOrder() {
r := rand.New(rand.NewSource(0))
accs := s.getTestingAccounts(r, 2)

market := s.CreateMarket("denom1", "denom2")
s.PlaceLimitOrder(
market.Id, accs[0].Address, true, utils.ParseDec("1.2"), sdk.NewDec(10_000000), time.Hour)
s.PlaceLimitOrder(
market.Id, accs[0].Address, true, utils.ParseDec("1.21"), sdk.NewDec(10_000000), time.Hour)
s.PlaceLimitOrder(
market.Id, accs[0].Address, true, utils.ParseDec("1.22"), sdk.NewDec(10_000000), time.Hour)
s.NextBlock()

op := simulation.SimulateMsgCancelOrder(
s.App.AccountKeeper, s.App.BankKeeper, s.keeper)
opMsg, futureOps, err := op(r, s.App.BaseApp, s.Ctx, accs, "")
s.Require().NoError(err)
s.Require().True(opMsg.OK)
s.Require().Len(futureOps, 0)

var msg types.MsgCancelOrder
types.ModuleCdc.MustUnmarshalJSON(opMsg.Msg, &msg)

s.Require().Equal(types.TypeMsgCancelOrder, msg.Type())
s.Require().Equal(types.ModuleName, msg.Route())
s.Require().Equal("cosmos1tp4es44j4vv8m59za3z0tm64dkmlnm8wg2frhc", msg.Sender)
s.Require().EqualValues(2, msg.OrderId)
}

func (s *SimTestSuite) TestSimulateMsgCancelAllOrders() {
r := rand.New(rand.NewSource(0))
accs := s.getTestingAccounts(r, 2)

// The order state doesn't matter but...
market1 := s.CreateMarket("denom1", "denom2")
s.PlaceLimitOrder(
market1.Id, accs[0].Address, true, utils.ParseDec("1.2"), sdk.NewDec(10_000000), time.Hour)
s.PlaceLimitOrder(
market1.Id, accs[0].Address, true, utils.ParseDec("1.21"), sdk.NewDec(10_000000), time.Hour)
market2 := s.CreateMarket("denom2", "denom3")
s.PlaceLimitOrder(
market2.Id, accs[0].Address, false, utils.ParseDec("5"), sdk.NewDec(10_000000), time.Hour)
s.PlaceLimitOrder(
market2.Id, accs[0].Address, false, utils.ParseDec("4.99"), sdk.NewDec(10_000000), time.Hour)
s.NextBlock()
s.PlaceLimitOrder(
market1.Id, accs[0].Address, true, utils.ParseDec("1.22"), sdk.NewDec(10_000000), time.Hour)
s.PlaceLimitOrder(
market2.Id, accs[0].Address, false, utils.ParseDec("4.98"), sdk.NewDec(10_000000), time.Hour)

op := simulation.SimulateMsgCancelAllOrders(
s.App.AccountKeeper, s.App.BankKeeper, s.keeper)
opMsg, futureOps, err := op(r, s.App.BaseApp, s.Ctx, accs, "")
s.Require().NoError(err)
s.Require().True(opMsg.OK)
s.Require().Len(futureOps, 0)

var msg types.MsgCancelAllOrders
types.ModuleCdc.MustUnmarshalJSON(opMsg.Msg, &msg)

s.Require().Equal(types.TypeMsgCancelAllOrders, msg.Type())
s.Require().Equal(types.ModuleName, msg.Route())
s.Require().Equal("cosmos1tp4es44j4vv8m59za3z0tm64dkmlnm8wg2frhc", msg.Sender)
s.Require().EqualValues(2, msg.MarketId)
}

func (s *SimTestSuite) TestSimulateMsgSwapExactAmountIn() {
r := rand.New(rand.NewSource(0))
accs := s.getTestingAccounts(r, 2)

market1 := s.CreateMarket("denom1", "denom2")
pool1 := s.CreatePool(market1.Id, utils.ParseDec("1"))
s.AddLiquidity(
accs[1].Address, pool1.Id, utils.ParseDec("0.5"), utils.ParseDec("2"),
utils.ParseCoins("10_000000denom1,10_0000000denom2"))
market2 := s.CreateMarket("denom2", "denom3")
pool2 := s.CreatePool(market2.Id, utils.ParseDec("5"))
s.AddLiquidity(
accs[1].Address, pool2.Id, utils.ParseDec("1"), utils.ParseDec("20"),
utils.ParseCoins("10_000000denom2,50_000000denom3"))
market3 := s.CreateMarket("denom3", "denom1")
pool3 := s.CreatePool(market3.Id, utils.ParseDec("0.05"))
s.AddLiquidity(
accs[1].Address, pool3.Id, utils.ParseDec("0.0001"), utils.ParseDec("1000"),
utils.ParseCoins("10_000000denom3,50_000000denom1"))

op := simulation.SimulateMsgSwapExactAmountIn(
s.App.AccountKeeper, s.App.BankKeeper, s.keeper)
opMsg, futureOps, err := op(r, s.App.BaseApp, s.Ctx, accs, "")
s.Require().NoError(err)
s.Require().True(opMsg.OK)
s.Require().Len(futureOps, 0)

var msg types.MsgSwapExactAmountIn
types.ModuleCdc.MustUnmarshalJSON(opMsg.Msg, &msg)

s.Require().Equal(types.TypeMsgSwapExactAmountIn, msg.Type())
s.Require().Equal(types.ModuleName, msg.Route())
s.Require().Equal("cosmos12jszjrc0qhjt0ugt2uh4ptwu0h55pq6qfp9ecl", msg.Sender)
s.Require().Equal([]uint64{2, 1, 3}, msg.Routes)
s.AssertEqual(utils.ParseDecCoin("82823denom3"), msg.Input)
s.AssertEqual(utils.ParseDecCoin("317178denom3"), msg.MinOutput) // Arbitrage!
}

0 comments on commit c69d78c

Please sign in to comment.