Skip to content

Commit

Permalink
Merge pull request #28 from crescent-network/cherry-pick/292
Browse files Browse the repository at this point in the history
fix: fix simulation for the claim module #292 #304
  • Loading branch information
crypin authored May 3, 2022
2 parents 6d67882 + eb0e659 commit 9af5d4b
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- x/liquidstaking/**/*
"x/mint":
- x/mint/**/*
"documentation"
"documentation":
- docs/**/*
"build":
- Makefile
Expand Down
3 changes: 2 additions & 1 deletion x/claim/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) {
// WeightedOperations returns the all the module operations with their respective weights.
func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
return simulation.WeightedOperations(
simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper, am.keeper,
simState.AppParams, simState.Cdc, am.accountKeeper, am.bankKeeper,
am.liquidityKeeper, am.liquidStakingKeeper, am.govKeeper, am.keeper,
)
}
113 changes: 74 additions & 39 deletions x/claim/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package simulation

import (
"math/rand"
"sort"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/codec"
Expand All @@ -14,6 +13,7 @@ import (
utils "github.com/crescent-network/crescent/types"
"github.com/crescent-network/crescent/x/claim/keeper"
"github.com/crescent-network/crescent/x/claim/types"
minttypes "github.com/crescent-network/crescent/x/mint/types"
)

const (
Expand All @@ -25,9 +25,15 @@ var (
Fees = sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1000))
)

var (
airdropDenom = "airdrop"
)

func WeightedOperations(
appParams simtypes.AppParams, cdc codec.JSONCodec,
ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper,
ak types.AccountKeeper, bk types.BankKeeper,
lk types.LiquidityKeeper, lsk types.LiquidStakingKeeper,
gk types.GovKeeper, k keeper.Keeper,
) simulation.WeightedOperations {
var weightMsgClaim int
appParams.GetOrGenerate(cdc, OpWeightMsgClaim, &weightMsgClaim, nil, func(_ *rand.Rand) {
Expand All @@ -37,50 +43,68 @@ func WeightedOperations(
return simulation.WeightedOperations{
simulation.NewWeightedOperation(
weightMsgClaim,
SimulateMsgClaim(ak, bk, k),
SimulateMsgClaim(ak, bk, lk, lsk, gk, k),
),
}
}

func SimulateMsgClaim(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.Operation {
func SimulateMsgClaim(
ak types.AccountKeeper, bk types.BankKeeper,
lk types.LiquidityKeeper, lsk types.LiquidStakingKeeper,
gk types.GovKeeper, 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) {
accs = utils.ShuffleSimAccounts(r, accs)

airdrops := k.GetAllAirdrops(ctx)
rand.Shuffle(len(airdrops), func(i, j int) {
airdrops[i], airdrops[j] = airdrops[j], airdrops[i]
})
airdrop := setAirdrop(r, ctx, bk, k, accs)

// Look for an account that has executed any condition
var simAccount simtypes.Account
var airdrop types.Airdrop
var claimRecord types.ClaimRecord
var cond types.ConditionType
skip := true
loop:
for _, simAccount = range accs {
for _, airdrop = range airdrops {
var found bool
claimRecord, found = k.GetClaimRecordByRecipient(ctx, airdrop.Id, simAccount.Address)
if !found {
continue
}

conditions := unclaimedConditions(airdrop, claimRecord)
for _, cond = range conditions {
if err := k.ValidateCondition(ctx, simAccount.Address, cond); err == nil {
skip = false
break loop
}
for _, cond = range []types.ConditionType{
types.ConditionTypeDeposit,
types.ConditionTypeSwap,
types.ConditionTypeLiquidStake,
types.ConditionTypeVote,
} {
if err := k.ValidateCondition(ctx, simAccount.Address, cond); err == nil {
skip = false
break loop
}
}
}
if skip {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgClaim, "no account to claim"), nil, nil
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgClaim, "no recipient that has executed any condition"), nil, nil
}

recipient := simAccount.Address
spendable := bk.SpendableCoins(ctx, recipient)

// To reduce complexity, skip if the recipient has already claim record
_, found := k.GetClaimRecordByRecipient(ctx, airdrop.Id, recipient)
if found {
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgClaim, "recipient already has claim record"), nil, nil
}

spendable := bk.SpendableCoins(ctx, simAccount.Address)
initialClaimableCoins := sdk.NewCoins(
sdk.NewInt64Coin(airdropDenom, int64(simtypes.RandIntBetween(r, 100_000_000, 1_000_000_000))))

// Set new claim record for the recipient
record := types.ClaimRecord{
AirdropId: airdrop.Id,
Recipient: recipient.String(),
InitialClaimableCoins: initialClaimableCoins,
ClaimableCoins: initialClaimableCoins,
ClaimedConditions: []types.ConditionType{},
}
k.SetClaimRecord(ctx, record)

msg := types.NewMsgClaim(airdrop.Id, simAccount.Address, cond)

txCtx := simulation.OperationInput{
Expand All @@ -101,21 +125,32 @@ func SimulateMsgClaim(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keep
}
}

func unclaimedConditions(airdrop types.Airdrop, claimRecord types.ClaimRecord) []types.ConditionType {
conditionSet := map[types.ConditionType]struct{}{}
for _, cond := range airdrop.Conditions {
conditionSet[cond] = struct{}{}
func setAirdrop(r *rand.Rand, ctx sdk.Context, bk types.BankKeeper, k keeper.Keeper, accs []simtypes.Account) types.Airdrop {
sourceAddr := accs[r.Intn(len(accs))].Address
coins := sdk.NewCoins(sdk.NewInt64Coin(airdropDenom, 10_000_000_000_000))

if err := bk.MintCoins(ctx, minttypes.ModuleName, coins); err != nil {
panic(err)
}
for _, cond := range claimRecord.ClaimedConditions {
delete(conditionSet, cond)

if err := bk.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, sourceAddr, coins); err != nil {
panic(err)
}
var conditions []types.ConditionType
for cond := range conditionSet {
conditions = append(conditions, cond)

airdrop := types.Airdrop{
Id: 1,
SourceAddress: sourceAddr.String(),
Conditions: []types.ConditionType{
types.ConditionTypeDeposit,
types.ConditionTypeSwap,
types.ConditionTypeLiquidStake,
types.ConditionTypeVote,
},
StartTime: ctx.BlockTime(),
EndTime: ctx.BlockTime().AddDate(0, simtypes.RandIntBetween(r, 1, 24), 0),
}
// Sort conditions for deterministic simulation, since map keys are not sorted.
sort.Slice(conditions, func(i, j int) bool {
return conditions[i] < conditions[j]
})
return conditions

k.SetAirdrop(ctx, airdrop)

return airdrop
}
52 changes: 14 additions & 38 deletions x/claim/simulation/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,25 @@ import (
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

chain "github.com/crescent-network/crescent/app"
utils "github.com/crescent-network/crescent/types"
"github.com/crescent-network/crescent/x/claim/simulation"
"github.com/crescent-network/crescent/x/claim/types"
liquiditytypes "github.com/crescent-network/crescent/x/liquidity/types"
)

func TestSimulateMsgClaim(t *testing.T) {
app := chain.Setup(false)
ctx := app.BaseApp.NewContext(false, tmproto.Header{})

r := rand.New(rand.NewSource(0))
accs := getTestingAccounts(t, r, app, ctx, 1)

srcAddr := utils.TestAddress(0)
airdrop := types.Airdrop{
Id: 1,
SourceAddress: srcAddr.String(),
Conditions: []types.ConditionType{
types.ConditionTypeDeposit,
types.ConditionTypeSwap,
types.ConditionTypeLiquidStake,
types.ConditionTypeVote,
},
StartTime: utils.ParseTime("2022-01-01T00:00:00Z"),
EndTime: utils.ParseTime("2023-01-01T00:00:00Z"),
}
app.ClaimKeeper.SetAirdrop(ctx, airdrop)
err := chain.FundAccount(app.BankKeeper, ctx, srcAddr, utils.ParseCoins("1000000stake"))
require.NoError(t, err)
claimRecord := types.ClaimRecord{
AirdropId: airdrop.Id,
Recipient: accs[0].Address.String(),
InitialClaimableCoins: utils.ParseCoins("1000000stake"),
ClaimableCoins: utils.ParseCoins("1000000stake"),
ClaimedConditions: nil,
}
app.ClaimKeeper.SetClaimRecord(ctx, claimRecord)
s := rand.NewSource(0)
r := rand.New(s)

pair := liquiditytypes.NewPair(1, "stake", "denom1")
app.LiquidityKeeper.SetPair(ctx, pair)
_, err = app.LiquidityKeeper.LimitOrder(ctx, liquiditytypes.NewMsgLimitOrder(
accs[0].Address, pair.Id, liquiditytypes.OrderDirectionSell,
utils.ParseCoin("10000stake"), "denom1", utils.ParseDec("1.0"), sdk.NewInt(10000), 0))
require.NoError(t, err)
accs := getTestingAccounts(t, r, app, ctx, 1)

app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}})

op := simulation.SimulateMsgClaim(app.AccountKeeper, app.BankKeeper, app.ClaimKeeper)
op := simulation.SimulateMsgClaim(
app.AccountKeeper, app.BankKeeper,
app.LiquidityKeeper, app.LiquidStakingKeeper,
app.GovKeeper, app.ClaimKeeper)
opMsg, futureOps, err := op(r, app.BaseApp, ctx, accs, "")
require.NoError(t, err)
require.True(t, opMsg.OK)
Expand All @@ -72,14 +43,19 @@ func TestSimulateMsgClaim(t *testing.T) {
require.Equal(t, types.ModuleName, msg.Route())
require.Equal(t, "cosmos1tp4es44j4vv8m59za3z0tm64dkmlnm8wg2frhc", msg.Recipient)
require.Equal(t, uint64(1), msg.AirdropId)
require.Equal(t, types.ConditionTypeSwap, msg.ConditionType)
require.Equal(t, types.ConditionTypeLiquidStake, msg.ConditionType)
}

func getTestingAccounts(t *testing.T, r *rand.Rand, app *chain.App, ctx sdk.Context, n int) []simtypes.Account {
accs := simtypes.RandomAccounts(r, n)

params := app.LiquidStakingKeeper.GetParams(ctx)

initAmt := app.StakingKeeper.TokensFromConsensusPower(ctx, 200)
initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt))
initCoins := sdk.NewCoins(
sdk.NewCoin(sdk.DefaultBondDenom, initAmt),
sdk.NewCoin(params.LiquidBondDenom, initAmt),
)

// add coins to the accounts
for _, account := range accs {
Expand Down
3 changes: 3 additions & 0 deletions x/claim/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type AccountKeeper interface {
type BankKeeper interface {
SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
// MintCoins is used only for simulation test codes
MintCoins(ctx sdk.Context, name string, amt sdk.Coins) error
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
}

// DistrKeeper is the keeper of the distribution store
Expand Down

0 comments on commit 9af5d4b

Please sign in to comment.