mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 11:37:27 +00:00 
			
		
		
		
	* Use cosmossdk.io/errors for deprecated error methods * Update error registration with cosmossdk.io/errors * Use cosmossdk.io/math for deprecated sdk.Int alias * Fix modified proto file * Update sdk.Int usage in swap hooks * Update e2e test deprecated method usage
		
			
				
	
	
		
			343 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			343 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package simulation
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"math/rand"
 | 
						|
 | 
						|
	"github.com/cosmos/cosmos-sdk/baseapp"
 | 
						|
	"github.com/cosmos/cosmos-sdk/codec"
 | 
						|
	"github.com/cosmos/cosmos-sdk/simapp/helpers"
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	"github.com/cosmos/cosmos-sdk/x/simulation"
 | 
						|
 | 
						|
	appparams "github.com/kava-labs/kava/app/params"
 | 
						|
	"github.com/kava-labs/kava/x/bep3/keeper"
 | 
						|
	"github.com/kava-labs/kava/x/bep3/types"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	noOpMsg      = simulation.NoOpMsg(types.ModuleName)
 | 
						|
	randomNumber = []byte{114, 21, 74, 180, 81, 92, 21, 91, 173, 164, 143, 111, 120, 58, 241, 58, 40, 22, 59, 133, 102, 233, 55, 149, 12, 199, 231, 63, 122, 23, 88, 9}
 | 
						|
)
 | 
						|
 | 
						|
// Simulation operation weights constants
 | 
						|
const (
 | 
						|
	OpWeightMsgCreateAtomicSwap = "op_weight_msg_create_atomic_swap"
 | 
						|
)
 | 
						|
 | 
						|
// WeightedOperations returns all the operations from the module with their respective weights
 | 
						|
func WeightedOperations(
 | 
						|
	appParams simulation.AppParams, cdc *codec.Codec, ak types.AccountKeeper, k keeper.Keeper,
 | 
						|
) simulation.WeightedOperations {
 | 
						|
	var weightCreateAtomicSwap int
 | 
						|
 | 
						|
	appParams.GetOrGenerate(cdc, OpWeightMsgCreateAtomicSwap, &weightCreateAtomicSwap, nil,
 | 
						|
		func(_ *rand.Rand) {
 | 
						|
			weightCreateAtomicSwap = appparams.DefaultWeightMsgCreateAtomicSwap
 | 
						|
		},
 | 
						|
	)
 | 
						|
 | 
						|
	return simulation.WeightedOperations{
 | 
						|
		simulation.NewWeightedOperation(
 | 
						|
			weightCreateAtomicSwap,
 | 
						|
			SimulateMsgCreateAtomicSwap(ak, k),
 | 
						|
		),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// SimulateMsgCreateAtomicSwap generates a MsgCreateAtomicSwap with random values
 | 
						|
func SimulateMsgCreateAtomicSwap(ak types.AccountKeeper, k keeper.Keeper) simulation.Operation {
 | 
						|
	return func(
 | 
						|
		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
 | 
						|
	) (simulation.OperationMsg, []simulation.FutureOperation, error) {
 | 
						|
		// Get asset supplies and shuffle them
 | 
						|
		assets, found := k.GetAssets(ctx)
 | 
						|
		if !found {
 | 
						|
			return noOpMsg, nil, nil
 | 
						|
		}
 | 
						|
		r.Shuffle(len(assets), func(i, j int) {
 | 
						|
			assets[i], assets[j] = assets[j], assets[i]
 | 
						|
		})
 | 
						|
		senderOutgoing, selectedAsset, found := findValidAccountAssetPair(accs, assets, func(simAcc simulation.Account, asset types.AssetParam) bool {
 | 
						|
			supply, found := k.GetAssetSupply(ctx, asset.Denom)
 | 
						|
			if !found {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
			if supply.CurrentSupply.Amount.IsPositive() {
 | 
						|
				authAcc := ak.GetAccount(ctx, simAcc.Address)
 | 
						|
				// deputy cannot be sender of outgoing swap
 | 
						|
				if authAcc.GetAddress().Equals(asset.DeputyAddress) {
 | 
						|
					return false
 | 
						|
				}
 | 
						|
				// Search for an account that holds coins received by an atomic swap
 | 
						|
				minAmountPlusFee := asset.MinSwapAmount.Add(asset.FixedFee)
 | 
						|
				if authAcc.SpendableCoins(ctx.BlockTime()).AmountOf(asset.Denom).GT(minAmountPlusFee) {
 | 
						|
					return true
 | 
						|
				}
 | 
						|
			}
 | 
						|
			return false
 | 
						|
		})
 | 
						|
		var sender simulation.Account
 | 
						|
		var recipient simulation.Account
 | 
						|
		var asset types.AssetParam
 | 
						|
 | 
						|
		// If an outgoing swap can be created, it's chosen 50% of the time.
 | 
						|
		if found && r.Intn(100) < 50 {
 | 
						|
			deputy, found := simulation.FindAccount(accs, selectedAsset.DeputyAddress)
 | 
						|
			if !found {
 | 
						|
				return noOpMsg, nil, nil
 | 
						|
			}
 | 
						|
			sender = senderOutgoing
 | 
						|
			recipient = deputy
 | 
						|
			asset = selectedAsset
 | 
						|
		} else {
 | 
						|
			// if an outgoing swap cannot be created or was not selected, simulate an incoming swap
 | 
						|
			assets, _ := k.GetAssets(ctx)
 | 
						|
			asset = assets[r.Intn(len(assets))]
 | 
						|
			var eligibleAccs []simulation.Account
 | 
						|
			for _, simAcc := range accs {
 | 
						|
				// don't allow recipient of incoming swap to be the deputy
 | 
						|
				if simAcc.Address.Equals(asset.DeputyAddress) {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				eligibleAccs = append(eligibleAccs, simAcc)
 | 
						|
			}
 | 
						|
			recipient, _ = simulation.RandomAcc(r, eligibleAccs)
 | 
						|
			deputy, found := simulation.FindAccount(accs, asset.DeputyAddress)
 | 
						|
			if !found {
 | 
						|
				return noOpMsg, nil, nil
 | 
						|
			}
 | 
						|
			sender = deputy
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		recipientOtherChain := simulation.RandStringOfLength(r, 43)
 | 
						|
		senderOtherChain := simulation.RandStringOfLength(r, 43)
 | 
						|
 | 
						|
		// Use same random number for determinism
 | 
						|
		timestamp := ctx.BlockTime().Unix()
 | 
						|
		randomNumberHash := types.CalculateRandomHash(randomNumber, timestamp)
 | 
						|
 | 
						|
		// 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
 | 
						|
		}
 | 
						|
 | 
						|
		// Get maximum valid amount
 | 
						|
		maximumAmount := senderAcc.SpendableCoins(ctx.BlockTime()).Sub(fees).AmountOf(asset.Denom)
 | 
						|
		assetSupply, foundAssetSupply := k.GetAssetSupply(ctx, asset.Denom)
 | 
						|
		if !foundAssetSupply {
 | 
						|
			return noOpMsg, nil, fmt.Errorf("no asset supply found for %s", asset.Denom)
 | 
						|
		}
 | 
						|
		// The maximum amount for outgoing swaps is limited by the asset's current supply
 | 
						|
		if recipient.Address.Equals(asset.DeputyAddress) {
 | 
						|
			if maximumAmount.GT(assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)) {
 | 
						|
				maximumAmount = assetSupply.CurrentSupply.Amount.Sub(assetSupply.OutgoingSupply.Amount)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			// the maximum amount for incoming swaps in limited by the asset's incoming supply + current supply (rate-limited if applicable)  + swap amount being less than the supply limit
 | 
						|
			var currentRemainingSupply sdkmath.Int
 | 
						|
			if asset.SupplyLimit.TimeLimited {
 | 
						|
				currentRemainingSupply = asset.SupplyLimit.Limit.Sub(assetSupply.IncomingSupply.Amount).Sub(assetSupply.TimeLimitedCurrentSupply.Amount)
 | 
						|
			} else {
 | 
						|
				currentRemainingSupply = asset.SupplyLimit.Limit.Sub(assetSupply.IncomingSupply.Amount).Sub(assetSupply.CurrentSupply.Amount)
 | 
						|
			}
 | 
						|
			if currentRemainingSupply.LT(maximumAmount) {
 | 
						|
				maximumAmount = currentRemainingSupply
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// The maximum amount for all swaps is limited by the total max limit
 | 
						|
		if maximumAmount.GT(asset.MaxSwapAmount) {
 | 
						|
			maximumAmount = asset.MaxSwapAmount
 | 
						|
		}
 | 
						|
 | 
						|
		// Get an amount of coins between 0.1 and 2% of total coins
 | 
						|
		amount := maximumAmount.Quo(sdkmath.NewInt(int64(simulation.RandIntBetween(r, 50, 1000))))
 | 
						|
		minAmountPlusFee := asset.MinSwapAmount.Add(asset.FixedFee)
 | 
						|
		if amount.LTE(minAmountPlusFee) {
 | 
						|
			return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (account funds exhausted for asset %s)", asset.Denom), "", false, nil), nil, nil
 | 
						|
		}
 | 
						|
		coins := sdk.NewCoins(sdk.NewCoin(asset.Denom, amount))
 | 
						|
 | 
						|
		// We're assuming that sims are run with -NumBlocks=100
 | 
						|
		heightSpan := uint64(simulation.RandIntBetween(r, int(asset.MinBlockLock), int(asset.MaxBlockLock)))
 | 
						|
 | 
						|
		msg := types.NewMsgCreateAtomicSwap(
 | 
						|
			sender.Address, recipient.Address, recipientOtherChain, senderOtherChain,
 | 
						|
			randomNumberHash, timestamp, coins, heightSpan,
 | 
						|
		)
 | 
						|
 | 
						|
		tx := helpers.GenTx(
 | 
						|
			[]sdk.Msg{msg},
 | 
						|
			fees,
 | 
						|
			helpers.DefaultGenTxGas,
 | 
						|
			chainID,
 | 
						|
			[]uint64{senderAcc.GetAccountNumber()},
 | 
						|
			[]uint64{senderAcc.GetSequence()},
 | 
						|
			sender.PrivKey,
 | 
						|
		)
 | 
						|
 | 
						|
		_, result, err := app.Deliver(tx)
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NewOperationMsg(msg, false, fmt.Sprintf("%+v", err)), nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		// 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 {
 | 
						|
			// Claim future operation - choose between next block and the block before height span
 | 
						|
			executionBlock := uint64(
 | 
						|
				int(ctx.BlockHeight()+1) + r.Intn(int(heightSpan-1)))
 | 
						|
 | 
						|
			futureOp = simulation.FutureOperation{
 | 
						|
				BlockHeight: int(executionBlock),
 | 
						|
				Op:          operationClaimAtomicSwap(ak, k, swapID, randomNumber),
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			// Refund future operation
 | 
						|
			executionBlock := uint64(ctx.BlockHeight()) + msg.HeightSpan
 | 
						|
			futureOp = simulation.FutureOperation{
 | 
						|
				BlockHeight: int(executionBlock),
 | 
						|
				Op:          operationRefundAtomicSwap(ak, k, swapID),
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return simulation.NewOperationMsg(msg, true, result.Log), []simulation.FutureOperation{futureOp}, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func operationClaimAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []byte, randomNumber []byte) simulation.Operation {
 | 
						|
	return func(
 | 
						|
		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
 | 
						|
	) (simulation.OperationMsg, []simulation.FutureOperation, error) {
 | 
						|
		simAccount, _ := simulation.RandomAcc(r, accs)
 | 
						|
		acc := ak.GetAccount(ctx, simAccount.Address)
 | 
						|
 | 
						|
		swap, found := k.GetAtomicSwap(ctx, swapID)
 | 
						|
		if !found {
 | 
						|
			return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("cannot claim: swap with ID %s not found", swapID)
 | 
						|
		}
 | 
						|
		// check that asset supply supports claiming (it could have changed due to a param change proposal)
 | 
						|
		// use CacheContext so changes don't take effect
 | 
						|
		cacheCtx, _ := ctx.CacheContext()
 | 
						|
		switch swap.Direction {
 | 
						|
		case types.Incoming:
 | 
						|
			err := k.DecrementIncomingAssetSupply(cacheCtx, swap.Amount[0])
 | 
						|
			if err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement incoming asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
			err = k.IncrementCurrentAssetSupply(cacheCtx, swap.Amount[0])
 | 
						|
			if err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to increment current asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
		case types.Outgoing:
 | 
						|
			err := k.DecrementOutgoingAssetSupply(cacheCtx, swap.Amount[0])
 | 
						|
			if err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement outgoing asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
			err = k.DecrementCurrentAssetSupply(cacheCtx, swap.Amount[0])
 | 
						|
			if err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - unable to decrement current asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		asset, err := k.GetAsset(ctx, swap.Amount[0].Denom)
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - asset not found %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
		}
 | 
						|
		supply, found := k.GetAssetSupply(ctx, asset.Denom)
 | 
						|
		if !found {
 | 
						|
			return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not claim - asset supply not found %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
		}
 | 
						|
		if asset.SupplyLimit.Limit.LT(supply.CurrentSupply.Amount.Add(swap.Amount[0].Amount)) {
 | 
						|
			return simulation.NoOpMsg(types.ModuleName), nil, nil
 | 
						|
		}
 | 
						|
 | 
						|
		msg := types.NewMsgClaimAtomicSwap(acc.GetAddress(), swapID, randomNumber)
 | 
						|
		fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime()))
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NoOpMsg(types.ModuleName), nil, err
 | 
						|
		}
 | 
						|
		tx := helpers.GenTx(
 | 
						|
			[]sdk.Msg{msg},
 | 
						|
			fees,
 | 
						|
			helpers.DefaultGenTxGas,
 | 
						|
			chainID,
 | 
						|
			[]uint64{acc.GetAccountNumber()},
 | 
						|
			[]uint64{acc.GetSequence()},
 | 
						|
			simAccount.PrivKey,
 | 
						|
		)
 | 
						|
		_, result, err := app.Deliver(tx)
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NewOperationMsg(msg, false, fmt.Sprintf("%+v", err)), nil, err
 | 
						|
		}
 | 
						|
		return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func operationRefundAtomicSwap(ak types.AccountKeeper, k keeper.Keeper, swapID []byte) simulation.Operation {
 | 
						|
	return func(
 | 
						|
		r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, chainID string,
 | 
						|
	) (simulation.OperationMsg, []simulation.FutureOperation, error) {
 | 
						|
		simAccount, _ := simulation.RandomAcc(r, accs)
 | 
						|
		acc := ak.GetAccount(ctx, simAccount.Address)
 | 
						|
 | 
						|
		swap, found := k.GetAtomicSwap(ctx, swapID)
 | 
						|
		if !found {
 | 
						|
			return simulation.NoOpMsg(types.ModuleName), nil, fmt.Errorf("cannot refund: swap with ID %s not found", swapID)
 | 
						|
		}
 | 
						|
		cacheCtx, _ := ctx.CacheContext()
 | 
						|
		switch swap.Direction {
 | 
						|
		case types.Incoming:
 | 
						|
			if err := k.DecrementIncomingAssetSupply(cacheCtx, swap.Amount[0]); err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not refund - unable to decrement incoming asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
		case types.Outgoing:
 | 
						|
			if err := k.DecrementOutgoingAssetSupply(cacheCtx, swap.Amount[0]); err != nil {
 | 
						|
				return simulation.NewOperationMsgBasic(types.ModuleName, fmt.Sprintf("no-operation (could not refund - unable to decrement outgoing asset supply %s)", swap.Amount[0].Denom), "", false, nil), nil, nil
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		msg := types.NewMsgRefundAtomicSwap(acc.GetAddress(), swapID)
 | 
						|
 | 
						|
		fees, err := simulation.RandomFees(r, ctx, acc.SpendableCoins(ctx.BlockTime()))
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NoOpMsg(types.ModuleName), nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		tx := helpers.GenTx(
 | 
						|
			[]sdk.Msg{msg},
 | 
						|
			fees,
 | 
						|
			helpers.DefaultGenTxGas,
 | 
						|
			chainID,
 | 
						|
			[]uint64{acc.GetAccountNumber()},
 | 
						|
			[]uint64{acc.GetSequence()},
 | 
						|
			simAccount.PrivKey,
 | 
						|
		)
 | 
						|
 | 
						|
		_, result, err := app.Deliver(tx)
 | 
						|
		if err != nil {
 | 
						|
			return simulation.NewOperationMsg(msg, false, fmt.Sprintf("%+v", err)), nil, err
 | 
						|
		}
 | 
						|
		return simulation.NewOperationMsg(msg, true, result.Log), nil, nil
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// findValidAccountAssetSupplyPair finds an account for which the callback func returns true
 | 
						|
func findValidAccountAssetPair(accounts []simulation.Account, assets types.AssetParams,
 | 
						|
	cb func(simulation.Account, types.AssetParam) bool,
 | 
						|
) (simulation.Account, types.AssetParam, bool) {
 | 
						|
	for _, asset := range assets {
 | 
						|
		for _, acc := range accounts {
 | 
						|
			if isValid := cb(acc, asset); isValid {
 | 
						|
				return acc, asset, true
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return simulation.Account{}, types.AssetParam{}, false
 | 
						|
}
 |