diff --git a/app/params/weights.go b/app/params/weights.go index 9890600d..7da8bd3b 100644 --- a/app/params/weights.go +++ b/app/params/weights.go @@ -8,7 +8,7 @@ const ( DefaultWeightMsgUnstake int = 30 DefaultWeightMsgHarvest int = 30 - DefaultWeightAddPublicPlanProposal int = 10 + DefaultWeightAddPublicPlanProposal int = 5 DefaultWeightUpdatePublicPlanProposal int = 5 DefaultWeightDeletePublicPlanProposal int = 5 ) diff --git a/x/farming/simulation/decoder.go b/x/farming/simulation/decoder.go index 813fd93f..ff7c3b31 100644 --- a/x/farming/simulation/decoder.go +++ b/x/farming/simulation/decoder.go @@ -18,22 +18,33 @@ func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { case bytes.Equal(kvA.Key[:1], types.PlanKeyPrefix): var pA, pB types.BasePlan cdc.MustUnmarshal(kvA.Value, &pA) - cdc.MustUnmarshal(kvA.Value, &pB) + cdc.MustUnmarshal(kvB.Value, &pB) return fmt.Sprintf("%v\n%v", pA, pB) case bytes.Equal(kvA.Key[:1], types.StakingKeyPrefix): var sA, sB types.Staking cdc.MustUnmarshal(kvA.Value, &sA) - cdc.MustUnmarshal(kvA.Value, &sB) + cdc.MustUnmarshal(kvB.Value, &sB) return fmt.Sprintf("%v\n%v", sA, sB) case bytes.Equal(kvA.Key[:1], types.QueuedStakingKeyPrefix): var sA, sB types.QueuedStaking cdc.MustUnmarshal(kvA.Value, &sA) - cdc.MustUnmarshal(kvA.Value, &sB) + cdc.MustUnmarshal(kvB.Value, &sB) return fmt.Sprintf("%v\n%v", sA, sB) - //TODO: add f1 struct + case bytes.Equal(kvA.Key[:1], types.HistoricalRewardsKeyPrefix): + var rA, rB types.HistoricalRewards + cdc.MustUnmarshal(kvA.Value, &rA) + cdc.MustUnmarshal(kvB.Value, &rB) + return fmt.Sprintf("%v\n%v", rA, rB) + + case bytes.Equal(kvA.Key[:1], types.OutstandingRewardsKeyPrefix): + var rA, rB types.OutstandingRewards + cdc.MustUnmarshal(kvA.Value, &rA) + cdc.MustUnmarshal(kvB.Value, &rB) + return fmt.Sprintf("%v\n%v", rA, rB) + default: panic(fmt.Sprintf("invalid farming key prefix %X", kvA.Key[:1])) } diff --git a/x/farming/simulation/decoder_test.go b/x/farming/simulation/decoder_test.go index c58ae880..1e371718 100644 --- a/x/farming/simulation/decoder_test.go +++ b/x/farming/simulation/decoder_test.go @@ -20,13 +20,16 @@ func TestDecodeFarmingStore(t *testing.T) { basePlan := types.BasePlan{} staking := types.Staking{} queuedStaking := types.QueuedStaking{} + historicalRewards := types.HistoricalRewards{} + outstandingRewards := types.OutstandingRewards{} kvPairs := kv.Pairs{ Pairs: []kv.Pair{ {Key: types.PlanKeyPrefix, Value: cdc.MustMarshal(&basePlan)}, {Key: types.StakingKeyPrefix, Value: cdc.MustMarshal(&staking)}, {Key: types.QueuedStakingKeyPrefix, Value: cdc.MustMarshal(&queuedStaking)}, - // TODO: f1 structs, indexes + {Key: types.HistoricalRewardsKeyPrefix, Value: cdc.MustMarshal(&historicalRewards)}, + {Key: types.OutstandingRewardsKeyPrefix, Value: cdc.MustMarshal(&outstandingRewards)}, {Key: []byte{0x99}, Value: []byte{0x99}}, }, } @@ -38,6 +41,8 @@ func TestDecodeFarmingStore(t *testing.T) { {"Plan", fmt.Sprintf("%v\n%v", basePlan, basePlan)}, {"Staking", fmt.Sprintf("%v\n%v", staking, staking)}, {"QueuedStaking", fmt.Sprintf("%v\n%v", queuedStaking, queuedStaking)}, + {"HistoricalRewardsKeyPrefix", fmt.Sprintf("%v\n%v", historicalRewards, historicalRewards)}, + {"OutstandingRewardsKeyPrefix", fmt.Sprintf("%v\n%v", outstandingRewards, outstandingRewards)}, {"other", ""}, } for i, tt := range tests { diff --git a/x/farming/simulation/operations.go b/x/farming/simulation/operations.go index 33536116..fb88dc95 100644 --- a/x/farming/simulation/operations.go +++ b/x/farming/simulation/operations.go @@ -315,11 +315,6 @@ func SimulateMsgHarvest(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Ke return func( r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simtypes.Account, chainID string, ) (simtypes.OperationMsg, []simtypes.FutureOperation, error) { - // - // TODO: not fully implemented yet. It needs debugging why - // there are no rewards although it processes queued coins and distribute rewards - // - var simAccount simtypes.Account // find staking from the simulated accounts diff --git a/x/farming/simulation/operations_test.go b/x/farming/simulation/operations_test.go index 40184c19..b9b4eccd 100644 --- a/x/farming/simulation/operations_test.go +++ b/x/farming/simulation/operations_test.go @@ -187,7 +187,7 @@ func TestSimulateMsgUnstake(t *testing.T) { err := app.FarmingKeeper.Stake(ctx, accounts[0].Address, stakingCoins) require.NoError(t, err) - // begin a new block + // begin a new block and advance epoch app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) err = app.FarmingKeeper.AdvanceEpoch(ctx) require.NoError(t, err) @@ -217,7 +217,7 @@ func TestSimulateMsgHarvest(t *testing.T) { s := rand.NewSource(1) r := rand.New(s) - accounts := getTestingAccounts(t, r, app, ctx, 1) + accounts := getTestingAccounts(t, r, app, ctx, 2) // setup epoch days to 1 to ease the test params := app.FarmingKeeper.GetParams(ctx) @@ -225,52 +225,42 @@ func TestSimulateMsgHarvest(t *testing.T) { app.FarmingKeeper.SetParams(ctx, params) // setup a fixed amount plan - msgPlan := &types.MsgCreateFixedAmountPlan{ - Name: "simulation", - Creator: accounts[0].Address.String(), - StakingCoinWeights: sdk.NewDecCoins( - sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10, 1)), // 100% - ), - StartTime: types.ParseTime("0001-01-01T00:00:00Z"), - EndTime: types.ParseTime("9999-01-01T00:00:00Z"), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 200_000_000)), - } - _, err := app.FarmingKeeper.CreateFixedAmountPlan( ctx, - msgPlan, + &types.MsgCreateFixedAmountPlan{ + Name: "simulation-test", + Creator: accounts[0].Address.String(), + StakingCoinWeights: sdk.NewDecCoins( + sdk.NewDecCoinFromDec(sdk.DefaultBondDenom, sdk.NewDecWithPrec(10, 1)), // 100% + ), + StartTime: types.ParseTime("0001-01-01T00:00:00Z"), + EndTime: types.ParseTime("9999-01-01T00:00:00Z"), + EpochAmount: sdk.NewCoins( + sdk.NewInt64Coin("pool93E069B333B5ECEBFE24C6E1437E814003248E0DD7FF8B9F82119F4587449BA5", 300_000_000), + ), + }, accounts[0].Address, accounts[0].Address, types.PlanTypePrivate, ) require.NoError(t, err) - // begin a new block - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) - - // set staking - stakingCoins := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100_000_000)) - err = app.FarmingKeeper.Stake(ctx, accounts[0].Address, stakingCoins) + // stake + err = app.FarmingKeeper.Stake(ctx, accounts[1].Address, sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 100_000_000))) require.NoError(t, err) - queuedStaking, found := app.FarmingKeeper.GetQueuedStaking(ctx, sdk.DefaultBondDenom, accounts[0].Address) - require.Equal(t, true, found) - require.Equal(t, true, queuedStaking.Amount.IsPositive()) + _, qsf := app.FarmingKeeper.GetQueuedStaking(ctx, sdk.DefaultBondDenom, accounts[1].Address) + require.True(t, qsf) - // begin a new block + // begin a new block and advance epoch app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) err = app.FarmingKeeper.AdvanceEpoch(ctx) require.NoError(t, err) // check that queue coins are moved to staked coins - staking, found := app.FarmingKeeper.GetStaking(ctx, sdk.DefaultBondDenom, accounts[0].Address) - require.Equal(t, true, found) - require.Equal(t, true, staking.Amount.IsPositive()) - queuedStaking, found = app.FarmingKeeper.GetQueuedStaking(ctx, sdk.DefaultBondDenom, accounts[0].Address) - require.Equal(t, false, found) + _, sf := app.FarmingKeeper.GetStaking(ctx, sdk.DefaultBondDenom, accounts[1].Address) + require.True(t, sf) - // begin a new block - app.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{Height: app.LastBlockHeight() + 1, AppHash: app.LastCommitID().Hash}}) err = app.FarmingKeeper.AdvanceEpoch(ctx) require.NoError(t, err) @@ -285,9 +275,12 @@ func TestSimulateMsgHarvest(t *testing.T) { require.True(t, operationMsg.OK) require.Equal(t, types.TypeMsgHarvest, msg.Type()) - require.Equal(t, "cosmos1tnh2q55v8wyygtt9srz5safamzdengsnqeycj3", msg.Farmer) + require.Equal(t, "cosmos1p8wcgrjr4pjju90xg6u9cgq55dxwq8j7u4x9a0", msg.Farmer) require.Equal(t, []string{"stake"}, msg.StakingCoinDenoms) require.Len(t, futureOperations, 0) + + balances := app.BankKeeper.GetBalance(ctx, accounts[1].Address, "pool93E069B333B5ECEBFE24C6E1437E814003248E0DD7FF8B9F82119F4587449BA5") + require.Equal(t, sdk.NewInt64Coin("pool93E069B333B5ECEBFE24C6E1437E814003248E0DD7FF8B9F82119F4587449BA5", 100300000000), balances) } func createTestApp(isCheckTx bool) (*farmingapp.FarmingApp, sdk.Context) { @@ -304,7 +297,10 @@ func getTestingAccounts(t *testing.T, r *rand.Rand, app *farmingapp.FarmingApp, accounts := simtypes.RandomAccounts(r, n) initAmt := app.StakingKeeper.TokensFromConsensusPower(ctx, 100_000_000_000) - initCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, initAmt)) + initCoins := sdk.NewCoins( + sdk.NewCoin(sdk.DefaultBondDenom, initAmt), + sdk.NewInt64Coin("pool93E069B333B5ECEBFE24C6E1437E814003248E0DD7FF8B9F82119F4587449BA5", 100_000_000_000), + ) // add coins to the accounts for _, account := range accounts { diff --git a/x/farming/simulation/proposals.go b/x/farming/simulation/proposals.go index 6b326e99..c243adee 100644 --- a/x/farming/simulation/proposals.go +++ b/x/farming/simulation/proposals.go @@ -12,16 +12,6 @@ import ( "github.com/tendermint/farming/x/farming/types" ) -/* -[TODO]: - We need to come up with better ways to simulate public plan proposals. - Currently, the details are ignored and only basic logics are written to simulate. - - These are some of the following considerations that i think need to be discussed and addressed: - 1. Randomize staking coin weights (single or multiple denoms) - 2. Simulate multiple proposals (add new weighted proposal content for multiple plans?) -*/ - // Simulation operation weights constants. const ( OpWeightSimulateAddPublicPlanProposal = "op_weight_add_public_plan_proposal" @@ -50,7 +40,7 @@ func ProposalContents(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keep } } -// SimulateAddPublicPlanProposal generates random public plan proposal content +// SimulateAddPublicPlanProposal generates random public add plan proposal content. func SimulateAddPublicPlanProposal(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.ContentSimulatorFn { return func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { simAccount, _ := simtypes.RandomAcc(r, accs) @@ -69,29 +59,19 @@ func SimulateAddPublicPlanProposal(ak types.AccountKeeper, bk types.BankKeeper, return nil } - // add request proposal - req := &types.AddPlanRequest{ - Name: "simulation-test-" + simtypes.RandStringOfLength(r, 5), - FarmingPoolAddress: simAccount.Address.String(), - TerminationAddress: simAccount.Address.String(), - StakingCoinWeights: sdk.NewDecCoins(sdk.NewInt64DecCoin(sdk.DefaultBondDenom, 1)), - StartTime: ctx.BlockTime(), - EndTime: ctx.BlockTime().AddDate(0, 1, 0), - EpochAmount: sdk.NewCoins(sdk.NewInt64Coin(poolCoins[r.Intn(3)].Denom, int64(simtypes.RandIntBetween(r, 10_000_000, 1_000_000_000)))), - } - addRequests := []*types.AddPlanRequest{req} + addPlanReqs := ranAddPlanRequests(r, ctx, simAccount, poolCoins) return types.NewPublicPlanProposal( simtypes.RandStringOfLength(r, 10), simtypes.RandStringOfLength(r, 100), - addRequests, + addPlanReqs, []*types.ModifyPlanRequest{}, []*types.DeletePlanRequest{}, ) } } -// SimulateModifyPublicPlanProposal generates random public plan proposal content +// SimulateModifyPublicPlanProposal generates random public modify plan proposal content. func SimulateModifyPublicPlanProposal(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.ContentSimulatorFn { return func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { simAccount, _ := simtypes.RandomAcc(r, accs) @@ -112,32 +92,33 @@ func SimulateModifyPublicPlanProposal(ak types.AccountKeeper, bk types.BankKeepe req := &types.ModifyPlanRequest{} - // TODO: decide which values of fields to randomize plans := k.GetPlans(ctx) for _, p := range plans { if p.GetType() == types.PlanTypePublic { startTime := ctx.BlockTime() - endTime := startTime.AddDate(0, 1, 0) + endTime := startTime.AddDate(0, simtypes.RandIntBetween(r, 1, 28), 0) switch plan := p.(type) { case *types.FixedAmountPlan: req.PlanId = plan.GetId() - req.Name = plan.GetName() + req.Name = "simulation-test-" + simtypes.RandStringOfLength(r, 5) req.FarmingPoolAddress = plan.GetFarmingPoolAddress().String() req.TerminationAddress = plan.GetTerminationAddress().String() req.StakingCoinWeights = plan.GetStakingCoinWeights() req.StartTime = &startTime req.EndTime = &endTime - req.EpochAmount = sdk.NewCoins(sdk.NewInt64Coin(poolCoins[r.Intn(3)].Denom, int64(simtypes.RandIntBetween(r, 10_000_000, 1_000_000_000)))) + req.EpochAmount = sdk.NewCoins( + sdk.NewInt64Coin(poolCoins[r.Intn(3)].Denom, int64(simtypes.RandIntBetween(r, 10_000_000, 1_000_000_000))), + ) case *types.RatioPlan: req.PlanId = plan.GetId() - req.Name = plan.GetName() + req.Name = "simulation-test-" + simtypes.RandStringOfLength(r, 5) req.FarmingPoolAddress = plan.GetFarmingPoolAddress().String() req.TerminationAddress = plan.GetTerminationAddress().String() req.StakingCoinWeights = plan.GetStakingCoinWeights() req.StartTime = &startTime req.EndTime = &endTime - req.EpochRatio = sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 1, 10)), 1) + req.EpochRatio = sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 1, 5)), 1) } break } @@ -147,19 +128,19 @@ func SimulateModifyPublicPlanProposal(ak types.AccountKeeper, bk types.BankKeepe return nil } - updateRequests := []*types.ModifyPlanRequest{req} + modifyPlanReqs := []*types.ModifyPlanRequest{req} return types.NewPublicPlanProposal( simtypes.RandStringOfLength(r, 10), simtypes.RandStringOfLength(r, 100), []*types.AddPlanRequest{}, - updateRequests, + modifyPlanReqs, []*types.DeletePlanRequest{}, ) } } -// SimulateDeletePublicPlanProposal generates random public plan proposal content +// SimulateDeletePublicPlanProposal generates random public delete plan proposal content. func SimulateDeletePublicPlanProposal(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) simtypes.ContentSimulatorFn { return func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { simAccount, _ := simtypes.RandomAcc(r, accs) @@ -187,14 +168,42 @@ func SimulateDeletePublicPlanProposal(ak types.AccountKeeper, bk types.BankKeepe return nil } - deleteRequest := []*types.DeletePlanRequest{req} + deletePlanReqs := []*types.DeletePlanRequest{req} return types.NewPublicPlanProposal( simtypes.RandStringOfLength(r, 10), simtypes.RandStringOfLength(r, 100), []*types.AddPlanRequest{}, []*types.ModifyPlanRequest{}, - deleteRequest, + deletePlanReqs, ) } } + +// ranAddPlanRequests returns randomized add request proposals. +func ranAddPlanRequests(r *rand.Rand, ctx sdk.Context, simAccount simtypes.Account, poolCoins sdk.Coins) []*types.AddPlanRequest { + ranProposals := make([]*types.AddPlanRequest, 0) + + // Generate a random number of proposals with random values of each parameter + for i := 0; i < simtypes.RandIntBetween(r, 1, 3); i++ { + req := &types.AddPlanRequest{} + req.Name = "simulation-test-" + simtypes.RandStringOfLength(r, 5) + req.FarmingPoolAddress = simAccount.Address.String() + req.TerminationAddress = simAccount.Address.String() + req.StakingCoinWeights = sdk.NewDecCoins(sdk.NewInt64DecCoin(sdk.DefaultBondDenom, 1)) + req.StartTime = ctx.BlockTime() + req.EndTime = ctx.BlockTime().AddDate(0, simtypes.RandIntBetween(r, 1, 28), 0) + + // Generate a fixed amount plan if pseudo-random integer is an even number and + // generate a ratio plan if it is an odd number + if r.Int()%2 == 0 { + req.EpochAmount = sdk.NewCoins( + sdk.NewInt64Coin(poolCoins[r.Intn(3)].Denom, int64(simtypes.RandIntBetween(r, 10_000_000, 100_000_000))), + ) + } else { + req.EpochRatio = sdk.NewDecWithPrec(int64(simtypes.RandIntBetween(r, 1, 10)), 2) // 1% ~ 10% + } + ranProposals = append(ranProposals, req) + } + return ranProposals +} diff --git a/x/farming/simulation/proposals_test.go b/x/farming/simulation/proposals_test.go index 059eb55c..8573d521 100644 --- a/x/farming/simulation/proposals_test.go +++ b/x/farming/simulation/proposals_test.go @@ -42,9 +42,8 @@ func TestProposalContents(t *testing.T) { require.Equal(t, params.DefaultWeightDeletePublicPlanProposal, w2.DefaultWeight()) content0 := w0.ContentSimulatorFn()(r, ctx, accounts) - - require.Equal(t, "yNhYFmBZHe", content0.GetTitle()) - require.Equal(t, "weXhSUkMhPjMaxKlMIJMOXcnQfyzeOcbWwNbeHVIkPZBSpYuLyYggwexjxusrBqDOTtGTOWeLrQKjLxzIivHSlcxgdXhhuTSkuxK", content0.GetDescription()) + require.Equal(t, "eOcbWwNbeH", content0.GetTitle()) + require.Equal(t, "AjEdlEWDODFRregDTqGNoFBIHxvimmIZwLfFyKUfEWAnNBdtdzDmTPXtpHRGdIbuucfTjOygZsTxPjfweXhSUkMhPjMaxKlMIJMO", content0.GetDescription()) require.Equal(t, "farming", content0.ProposalRoute()) require.Equal(t, "PublicPlan", content0.ProposalType()) @@ -70,14 +69,14 @@ func TestProposalContents(t *testing.T) { require.NoError(t, err) content1 := w1.ContentSimulatorFn()(r, ctx, accounts) - require.Equal(t, "GNoFBIHxvi", content1.GetTitle()) - require.Equal(t, "TVpQoottZyPFfNOoMioXHRuFwMRYUiKvcWPkrayyTLOCFJlAyslDameIuqVAuxErqFPEWIScKpBORIuZqoXlZuTvAjEdlEWDODFR", content1.GetDescription()) + require.Equal(t, "OoMioXHRuF", content1.GetTitle()) + require.Equal(t, "REqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRXpPfKwMhptXaxIxgqBoUqzrWbaoLTVpQoottZ", content1.GetDescription()) require.Equal(t, "farming", content1.ProposalRoute()) require.Equal(t, "PublicPlan", content1.ProposalType()) content2 := w2.ContentSimulatorFn()(r, ctx, accounts) - require.Equal(t, "MhptXaxIxg", content2.GetTitle()) - require.Equal(t, "MBcObErwgTDNGWnwQMUgFFSKtPDMEoEQCTKVREqrXZSGLqwTMcxHfWotDllNkIJPMbXzjDVjPOOjCFuIvTyhXKLyhUScOXvYthRX", content2.GetDescription()) + require.Equal(t, "wQMUgFFSKt", content2.GetTitle()) + require.Equal(t, "MwMANGoQwFnCqFrUGMCRZUGJKTZIGPyldsifauoMnJPLTcDHmilcmahlqOELaAUYDBuzsVywnDQfwRLGIWozYaOAilMBcObErwgT", content2.GetDescription()) require.Equal(t, "farming", content2.ProposalRoute()) require.Equal(t, "PublicPlan", content2.ProposalType()) }