Skip to content

Commit

Permalink
Merge pull request #169 from kogisin/kogisin/108-improve-sim-tests
Browse files Browse the repository at this point in the history
tests: add detailed and randomized cases for public plan proposals simulation
  • Loading branch information
jaybxyz authored Oct 29, 2021
2 parents 31fe858 + c574fb6 commit debb2e6
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 86 deletions.
2 changes: 1 addition & 1 deletion app/params/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const (
DefaultWeightMsgUnstake int = 30
DefaultWeightMsgHarvest int = 30

DefaultWeightAddPublicPlanProposal int = 10
DefaultWeightAddPublicPlanProposal int = 5
DefaultWeightUpdatePublicPlanProposal int = 5
DefaultWeightDeletePublicPlanProposal int = 5
)
19 changes: 15 additions & 4 deletions x/farming/simulation/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]))
}
Expand Down
7 changes: 6 additions & 1 deletion x/farming/simulation/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}},
},
}
Expand All @@ -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 {
Expand Down
5 changes: 0 additions & 5 deletions x/farming/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
62 changes: 29 additions & 33 deletions x/farming/simulation/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -217,60 +217,50 @@ 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)
params.NextEpochDays = 1
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)

Expand All @@ -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) {
Expand All @@ -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 {
Expand Down
79 changes: 44 additions & 35 deletions x/farming/simulation/proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
}
13 changes: 6 additions & 7 deletions x/farming/simulation/proposals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand All @@ -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())
}

0 comments on commit debb2e6

Please sign in to comment.