diff --git a/x/bep3/simulation/operations.go b/x/bep3/simulation/operations.go index 814845e2..8a76659c 100644 --- a/x/bep3/simulation/operations.go +++ b/x/bep3/simulation/operations.go @@ -51,14 +51,49 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string, ) (simulation.OperationMsg, []simulation.FutureOperation, error) { - senderAddr := k.GetBnbDeputyAddress(ctx) - - sender, found := simulation.FindAccount(accs, senderAddr) - if !found { + // Set up deputy address as it's required for all atomic swaps + deputyAddr := k.GetBnbDeputyAddress(ctx) + deputyAcc, foundDeputy := simulation.FindAccount(accs, deputyAddr) + if !foundDeputy { return noOpMsg, nil, nil } - recipient, _ := simulation.RandomAcc(r, accs) + // Get asset supplies and shuffle them + supplies := k.GetAllAssetSupplies(ctx) + r.Shuffle(len(supplies), func(i, j int) { + supplies[i], supplies[j] = supplies[j], supplies[i] + }) + + // Search for an account that holds coins received by an atomic swap + senderOut, asset, found := findValidAccountAssetSupplyPair(accs, supplies, func(acc simulation.Account, asset types.AssetSupply) bool { + if asset.CurrentSupply.Amount.IsPositive() { + authAcc := ak.GetAccount(ctx, acc.Address) + if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).IsPositive() { + return true + } + } + return false + }) + + // Set sender, recipient, and denom depending on swap direction + var sender simulation.Account + var recipient simulation.Account + var denom string + // If an outgoing swap can be created, it's chosen 50% of the time. + if found && r.Intn(100) < 50 { + sender = senderOut + recipient = deputyAcc + denom = asset.Denom + } else { + sender = deputyAcc + recipient, _ = simulation.RandomAcc(r, accs) + // Randomly select an asset from supported assets + assets, foundAsset := k.GetAssets(ctx) + if !foundAsset { + return noOpMsg, nil, fmt.Errorf("no supported assets found") + } + denom = assets[r.Intn(len(assets))].Denom + } recipientOtherChain := simulation.RandStringOfLength(r, 43) senderOtherChain := simulation.RandStringOfLength(r, 43) @@ -72,36 +107,29 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula timestamp := ctx.BlockTime().Unix() randomNumberHash := types.CalculateRandomHash(randomNumber.BigInt().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 - senderAcc := ak.GetAccount(ctx, senderAddr) + // Check that the sender has coins for fee + senderAcc := ak.GetAccount(ctx, sender.Address) fees, err := simulation.RandomFees(r, ctx, senderAcc.SpendableCoins(ctx.BlockTime())) if err != nil { return simulation.NoOpMsg(types.ModuleName), nil, err } - availableAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(asset.Denom) + // Get an amount of coins between 0.1 and 2% of total coins + availableAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(denom) amount := availableAmount.Quo(sdk.NewInt(int64(simulation.RandIntBetween(r, 50, 1000)))) if amount.IsZero() { - return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil + return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (all funds exhausted for asset %s)", denom), "", false, nil), nil, nil } - coin := sdk.NewCoin(asset.Denom, amount) - coins := sdk.NewCoins(coin) - expectedIncome := coin.String() + coins := sdk.NewCoins(sdk.NewCoin(denom, amount)) + expectedIncome := coins.String() // We're assuming that sims are run with -NumBlocks=100 heightSpan := int64(55) crossChain := true msg := types.NewMsgCreateAtomicSwap( - senderAddr, recipient.Address, recipientOtherChain, senderOtherChain, randomNumberHash, - timestamp, coins, expectedIncome, heightSpan, crossChain, + sender.Address, recipient.Address, recipientOtherChain, senderOtherChain, + randomNumberHash, timestamp, coins, expectedIncome, heightSpan, crossChain, ) tx := helpers.GenTx( @@ -119,7 +147,7 @@ func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simula return simulation.NoOpMsg(types.ModuleName), nil, err } - // If created, construct a MsgClaimAtomicSwap or MsgRefundAtomicSwap future operation + // Construct a MsgClaimAtomicSwap or MsgRefundAtomicSwap future operation var futureOp simulation.FutureOperation swapID := types.CalculateSwapID(msg.RandomNumberHash, msg.From, msg.SenderOtherChain) if r.Intn(100) < 50 { @@ -207,3 +235,16 @@ func operationRefundAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID [ return simulation.NewOperationMsg(msg, true, result.Log), nil, nil } } + +// findValidAccountAssetSupplyPair finds an account for which the callback func returns true +func findValidAccountAssetSupplyPair(accounts []simulation.Account, supplies types.AssetSupplies, + cb func(simulation.Account, types.AssetSupply) bool) (simulation.Account, types.AssetSupply, bool) { + for _, supply := range supplies { + for _, acc := range accounts { + if isValid := cb(acc, supply); isValid { + return acc, supply, true + } + } + } + return simulation.Account{}, types.AssetSupply{}, false +}