From fa999b2e99ca0a2c46d5fb81b1aa00264bf02b3f Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Fri, 28 Feb 2020 14:03:28 +0100 Subject: [PATCH] End proposals early on impossible conditions --- incubator/group/integration_test.go | 4 ++-- incubator/group/keeper.go | 16 +++++++++------ incubator/group/types.go | 32 +++++++++++++++++++++-------- 3 files changed, 35 insertions(+), 17 deletions(-) diff --git a/incubator/group/integration_test.go b/incubator/group/integration_test.go index 66d1767..def4333 100644 --- a/incubator/group/integration_test.go +++ b/incubator/group/integration_test.go @@ -194,8 +194,8 @@ func TestFullProposalWorkflow(t *testing.T) { // verify second proposal proposal, err = app.GroupKeeper.GetProposal(ctx, 2) require.NoError(t, err) - assert.Equal(t, group.ProposalBase_Undefined, proposal.GetBase().Result, proposal.GetBase().Result.String()) - assert.Equal(t, group.ProposalBase_Submitted, proposal.GetBase().Status, proposal.GetBase().Status.String()) + assert.Equal(t, group.ProposalBase_Rejected, proposal.GetBase().Result, proposal.GetBase().Result.String()) + assert.Equal(t, group.ProposalBase_Closed, proposal.GetBase().Status, proposal.GetBase().Status.String()) expTally = group.Tally{YesCount: sdk.ZeroDec(), NoCount: sdk.ZeroDec(), AbstainCount: sdk.ZeroDec(), VetoCount: sdk.OneDec()} assert.Equal(t, expTally, proposal.GetBase().VoteState) } diff --git a/incubator/group/keeper.go b/incubator/group/keeper.go index 5039e84..3e1238d 100644 --- a/incubator/group/keeper.go +++ b/incubator/group/keeper.go @@ -363,13 +363,15 @@ func (k Keeper) Vote(ctx sdk.Context, id ProposalID, voters []sdk.AccAddress, ch if err != nil { return err } - switch accepted, err := policy.Allow(base.VoteState, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); { + switch result, err := policy.Allow(base.VoteState, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); { case err != nil: return errors.Wrap(err, "policy execution") - case accepted: - // with enough votes we can close the proposal early as votes can not be changed + case result == DecisionPolicyResult{Allow: true, Final: true}: base.Result = ProposalBase_Accepted base.Status = ProposalBase_Closed + case result == DecisionPolicyResult{Allow: false, Final: true}: + base.Result = ProposalBase_Rejected + base.Status = ProposalBase_Closed } proposal.SetBase(base) @@ -436,15 +438,17 @@ func (k Keeper) ExecProposal(ctx sdk.Context, id ProposalID) error { if err != nil { return errors.Wrap(err, "from proto time") } - switch accepted, err := policy.Allow(base.VoteState, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); { + switch result, err := policy.Allow(base.VoteState, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); { case err != nil: return errors.Wrap(err, "policy execution") - case accepted: + case result == DecisionPolicyResult{Allow: true, Final: true}: base.Result = ProposalBase_Accepted base.Status = ProposalBase_Closed + case result == DecisionPolicyResult{Allow: false, Final: true}: + base.Result = ProposalBase_Rejected + base.Status = ProposalBase_Closed default: // there might be votes coming so we can not close it - // todo: let decision policy decide on impossible cases to close early with ProposalBase_Rejected } } diff --git a/incubator/group/types.go b/incubator/group/types.go index 3e041f7..2833a6f 100644 --- a/incubator/group/types.go +++ b/incubator/group/types.go @@ -6,6 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/params/subspace" "github.com/cosmos/modules/incubator/orm" + "github.com/gogo/protobuf/types" "github.com/pkg/errors" "gopkg.in/yaml.v2" @@ -32,18 +33,31 @@ func (p ProposalID) Uint64() uint64 { return uint64(p) } +type DecisionPolicyResult struct { + Allow bool + Final bool +} + type DecisionPolicy interface { - // todo: @aaron: not sure if understood the concept of this policy correct but when the - // result is the decision if a proposal is accepted or rejected we need to check we need - // an error state as well. example: MsgExec before voting period end. - Allow(tally Tally, totalPower sdk.Dec, votingDuration time.Duration) (bool, error) + Allow(tally Tally, totalPower sdk.Dec, votingDuration time.Duration) (DecisionPolicyResult, error) } -func (p ThresholdDecisionPolicy) Allow(tally Tally, totalPower sdk.Dec, votingDuration time.Duration) (bool, error) { - // if p.MinVotingWindow > votingDuration { - // return false, errors.Wrap(ErrInvalid, "min voting period not") - // } - return tally.YesCount.GT(p.Threshold), nil +func (p ThresholdDecisionPolicy) Allow(tally Tally, totalPower sdk.Dec, votingDuration time.Duration) (DecisionPolicyResult, error) { + timeout, err := types.DurationFromProto(&p.Timout) + if err != nil { + return DecisionPolicyResult{}, err + } + if timeout < votingDuration { + return DecisionPolicyResult{Allow: false, Final: true}, nil + } + if tally.YesCount.GT(p.Threshold) { + return DecisionPolicyResult{Allow: true, Final: true}, nil + } + undecided := totalPower.Sub(tally.TotalCounts()) + if tally.YesCount.Add(undecided).LTE(p.Threshold) { + return DecisionPolicyResult{Allow: false, Final: true}, nil + } + return DecisionPolicyResult{Allow: false, Final: false}, nil } func (g GroupMember) NaturalKey() []byte {