Skip to content

Commit

Permalink
fix: add check for duplicate name value when creating private plans t…
Browse files Browse the repository at this point in the history
  • Loading branch information
jaybxyz committed Sep 8, 2021
1 parent 13e7406 commit a9567eb
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 29 deletions.
6 changes: 4 additions & 2 deletions x/farming/keeper/genesis_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package keeper_test

import (
_ "github.com/stretchr/testify/suite"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/tendermint/farming/x/farming/types"
Expand All @@ -11,7 +13,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() {
types.NewFixedAmountPlan(
types.NewBasePlan(
1,
"",
"name1",
types.PlanTypePrivate,
suite.addrs[0].String(),
suite.addrs[0].String(),
Expand All @@ -25,7 +27,7 @@ func (suite *KeeperTestSuite) TestInitGenesis() {
types.NewRatioPlan(
types.NewBasePlan(
2,
"",
"name2",
types.PlanTypePublic,
suite.addrs[0].String(),
suite.addrs[0].String(),
Expand Down
16 changes: 16 additions & 0 deletions x/farming/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ func (k msgServer) CreateFixedAmountPlan(goCtx context.Context, msg *types.MsgCr
if err != nil {
return nil, err
}

if _, err := k.Keeper.CreateFixedAmountPlan(ctx, msg, poolAcc, msg.GetCreator(), types.PlanTypePrivate); err != nil {
return nil, err
}

plans := k.GetAllPlans(ctx)
if err := types.ValidateName(plans); err != nil {
return nil, err
}

return &types.MsgCreateFixedAmountPlanResponse{}, nil
}

Expand All @@ -46,10 +52,20 @@ func (k msgServer) CreateRatioPlan(goCtx context.Context, msg *types.MsgCreateRa
if err != nil {
return nil, err
}

if _, err := k.Keeper.CreateRatioPlan(ctx, msg, poolAcc, msg.GetCreator(), types.PlanTypePrivate); err != nil {
return nil, err
}

plans := k.GetAllPlans(ctx)
if err := types.ValidateName(plans); err != nil {
return nil, err
}

if err := types.ValidateTotalEpochRatio(plans); err != nil {
return nil, err
}

return &types.MsgCreateRatioPlanResponse{}, nil
}

Expand Down
5 changes: 4 additions & 1 deletion x/farming/keeper/proposal_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ func HandlePublicPlanProposal(ctx sdk.Context, k Keeper, proposal *types.PublicP
}

plans := k.GetAllPlans(ctx)
if err := types.ValidateRatioPlans(plans); err != nil {
if err := types.ValidateName(plans); err != nil {
return err
}
if err := types.ValidateTotalEpochRatio(plans); err != nil {
return err
}

Expand Down
2 changes: 1 addition & 1 deletion x/farming/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
// ProposalContents returns all the farming content functions used to
// simulate governance proposals.
func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent {
return simulation.ProposalContents(am.keeper)
return simulation.ProposalContents(am.accountKeeper, am.bankKeeper, am.keeper)
}

// RandomizedParams creates randomized farming param changes for the simulator.
Expand Down
4 changes: 2 additions & 2 deletions x/farming/simulation/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func SimulateMsgCreateFixedAmountPlan(ak types.AccountKeeper, bk types.BankKeepe
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateFixedAmountPlan, "unable to mint pool coins"), nil, nil
}

name := "simulation"
name := "simulation-test-" + simtypes.RandStringOfLength(r, 5) // name must be unique
creatorAcc := account.GetAddress()
stakingCoinWeights := sdk.NewDecCoins(sdk.NewInt64DecCoin(sdk.DefaultBondDenom, 1))
startTime := ctx.BlockTime()
Expand Down Expand Up @@ -170,7 +170,7 @@ func SimulateMsgCreateRatioPlan(ak types.AccountKeeper, bk types.BankKeeper, k k
return simtypes.NoOpMsg(types.ModuleName, types.TypeMsgCreateFixedAmountPlan, "unable to mint pool coins"), nil, nil
}

name := "simulation"
name := "simulation-test-" + simtypes.RandStringOfLength(r, 5) // name must be unique
creatorAcc := account.GetAddress()
stakingCoinWeights := sdk.NewDecCoins(sdk.NewInt64DecCoin(sdk.DefaultBondDenom, 1))
startTime := ctx.BlockTime()
Expand Down
46 changes: 41 additions & 5 deletions x/farming/simulation/proposals.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,61 @@ import (
const OpWeightSimulatePublicPlanProposal = "op_weight_public_plan_proposal"

// ProposalContents defines the module weighted proposals' contents
func ProposalContents(k keeper.Keeper) []simtypes.WeightedProposalContent {
func ProposalContents(ak types.AccountKeeper, bk types.BankKeeper, k keeper.Keeper) []simtypes.WeightedProposalContent {
return []simtypes.WeightedProposalContent{
simulation.NewWeightedProposalContent(
OpWeightSimulatePublicPlanProposal,
params.DefaultWeightPublicPlanProposal,
SimulatePublicPlanProposal(k),
SimulatePublicPlanProposal(ak, bk, k),
),
}
}

// SimulatePublicPlanProposal generates random public plan proposal content
func SimulatePublicPlanProposal(k keeper.Keeper) simtypes.ContentSimulatorFn {
func SimulatePublicPlanProposal(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)
simAccount, _ := simtypes.RandomAcc(r, accs)

account := ak.GetAccount(ctx, simAccount.Address)
spendable := bk.SpendableCoins(ctx, account.GetAddress())

params := k.GetParams(ctx)
_, hasNeg := spendable.SafeSub(params.PrivatePlanCreationFee)
if hasNeg {
return nil
}

poolCoins, err := mintPoolCoins(ctx, r, bk, simAccount)
if err != nil {
return nil
}

// create add request proposal
// TODO: randomized values of the fields
req := &types.AddRequestProposal{
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)))),
EpochRatio: sdk.ZeroDec(),
}
addRequests := []*types.AddRequestProposal{req}

// TODO
// create update request proposal
// updating plan can only be allowed (owner)

// TODO
// create delete request proposal
// deleting plan can only be allowed

return types.NewPublicPlanProposal(
simtypes.RandStringOfLength(r, 10),
simtypes.RandStringOfLength(r, 100),
[]*types.AddRequestProposal{},
addRequests,
[]*types.UpdateRequestProposal{},
[]*types.DeleteRequestProposal{},
)
Expand Down
14 changes: 12 additions & 2 deletions x/farming/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func ValidateGenesis(data GenesisState) error {
if err := data.Params.Validate(); err != nil {
return err
}

id := uint64(0)

var plans []PlanI
for _, record := range data.PlanRecords {
if err := record.Validate(); err != nil {
Expand All @@ -54,8 +56,12 @@ func ValidateGenesis(data GenesisState) error {
}
plans = append(plans, plan)
}
err := ValidateRatioPlans(plans)
if err != nil {

if err := ValidateName(plans); err != nil {
return err
}

if err := ValidateTotalEpochRatio(plans); err != nil {
return err
}

Expand All @@ -64,17 +70,21 @@ func ValidateGenesis(data GenesisState) error {
return err
}
}

for _, reward := range data.Rewards {
if err := reward.Validate(); err != nil {
return err
}
}

if err := data.RewardPoolCoins.Validate(); err != nil {
return err
}

if err := data.StakingReserveCoins.Validate(); err != nil {
return err
}

return nil
}

Expand Down
30 changes: 21 additions & 9 deletions x/farming/types/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,17 +249,34 @@ type PlanI interface {
Validate() error
}

// ValidateRatioPlans validates a farmer's total epoch ratio and plan name.
// A total epoch ratio cannot be higher than 1 and plan name must not be duplicate.
func ValidateRatioPlans(i interface{}) error {
// ValidateName validates duplicate plan name value.
func ValidateName(i interface{}) error {
plans, ok := i.([]PlanI)
if !ok {
return sdkerrors.Wrapf(ErrInvalidPlanType, "invalid plan type %T", i)
}

totalEpochRatio := make(map[string]sdk.Dec)
names := make(map[string]bool)

for _, plan := range plans {
if _, ok := names[plan.GetName()]; ok {
return sdkerrors.Wrap(ErrDuplicatePlanName, plan.GetName())
}
names[plan.GetName()] = true
}

return nil
}

// ValidateTotalEpochRatio validates a farmer's total epoch ratio that must be equal to 1.
func ValidateTotalEpochRatio(i interface{}) error {
plans, ok := i.([]PlanI)
if !ok {
return sdkerrors.Wrapf(ErrInvalidPlanType, "invalid plan type %T", i)
}

totalEpochRatio := make(map[string]sdk.Dec)

for _, plan := range plans {
farmingPoolAddr := plan.GetFarmingPoolAddress().String()

Expand All @@ -273,11 +290,6 @@ func ValidateRatioPlans(i interface{}) error {
} else {
totalEpochRatio[farmingPoolAddr] = plan.EpochRatio
}

if _, ok := names[plan.Name]; ok {
return sdkerrors.Wrap(ErrDuplicatePlanName, plan.Name)
}
names[plan.Name] = true
}
}

Expand Down
50 changes: 43 additions & 7 deletions x/farming/types/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import (
"github.com/tendermint/farming/x/farming/types"
)

func TestRatioPlans(t *testing.T) {
name1 := "testPlan1"
name2 := "testPlan2"
func TestPlanName(t *testing.T) {
name := "testPlan1"
farmingPoolAddr1 := sdk.AccAddress("farmingPoolAddr1")
terminationAddr1 := sdk.AccAddress("terminationAddr1")
stakingCoinWeights := sdk.NewDecCoins(
Expand All @@ -31,7 +30,7 @@ func TestRatioPlans(t *testing.T) {
{
[]types.PlanI{
types.NewRatioPlan(
types.NewBasePlan(1, name1, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
types.NewBasePlan(1, name, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
sdk.NewDec(1),
),
},
Expand All @@ -40,15 +39,52 @@ func TestRatioPlans(t *testing.T) {
{
[]types.PlanI{
types.NewRatioPlan(
types.NewBasePlan(1, name1, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
types.NewBasePlan(1, name, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
sdk.NewDec(1),
),
types.NewRatioPlan(
types.NewBasePlan(1, name, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
sdk.NewDec(1),
),
},
sdkerrors.Wrap(types.ErrDuplicatePlanName, name),
},
}

for _, tc := range testCases {
err := types.ValidateName(tc.plans)
if tc.expectedErr == nil {
require.NoError(t, err)
} else {
require.Error(t, err)
require.Equal(t, tc.expectedErr.Error(), err.Error())
}
}
}

func TestTotalEpochRatio(t *testing.T) {
name1 := "testPlan1"
name2 := "testPlan2"
farmingPoolAddr1 := sdk.AccAddress("farmingPoolAddr1")
terminationAddr1 := sdk.AccAddress("terminationAddr1")
stakingCoinWeights := sdk.NewDecCoins(
sdk.DecCoin{Denom: "testFarmStakingCoinDenom", Amount: sdk.MustNewDecFromStr("1.0")},
)
startTime := time.Now().UTC()
endTime := startTime.AddDate(1, 0, 0)

testCases := []struct {
plans []types.PlanI
expectedErr error
}{
{
[]types.PlanI{
types.NewRatioPlan(
types.NewBasePlan(1, name1, 1, farmingPoolAddr1.String(), terminationAddr1.String(), stakingCoinWeights, startTime, endTime),
sdk.NewDec(1),
),
},
sdkerrors.Wrap(types.ErrDuplicatePlanName, name1),
nil,
},
{
[]types.PlanI{
Expand All @@ -66,7 +102,7 @@ func TestRatioPlans(t *testing.T) {
}

for _, tc := range testCases {
err := types.ValidateRatioPlans(tc.plans)
err := types.ValidateTotalEpochRatio(tc.plans)
if tc.expectedErr == nil {
require.NoError(t, err)
} else {
Expand Down

0 comments on commit a9567eb

Please sign in to comment.