Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix genesis and support abstain #545

Merged
merged 6 commits into from
Jun 2, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* (client) [\#476](https://github.com/line/lbm-sdk/pull/476) change the default value of the client output format in the config
* (server/grpc) [\#516](https://github.com/line/lbm-sdk/pull/516) restore build norace flag
* (genesis) [\#517](https://github.com/line/lbm-sdk/pull/517) fix genesis auth account format(cosmos-sdk style -> lbm-sdk style)
* (x/foundation) [\#545](https://github.com/line/lbm-sdk/pull/545) fix genesis and support abstain

### Breaking Changes

Expand Down
39 changes: 39 additions & 0 deletions x/foundation/authz_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package foundation_test

import (
"testing"

sdk "github.com/line/lbm-sdk/types"
"github.com/line/lbm-sdk/x/foundation"
"github.com/stretchr/testify/require"
)

func TestReceiveFromTreasuryAuthorization(t *testing.T) {
testCases := map[string]struct{
msg sdk.Msg
valid bool
accept bool
}{
"valid": {
msg: &foundation.MsgWithdrawFromTreasury{},
valid: true,
accept: true,
},
"msg mismatch": {
msg: &foundation.MsgVote{},
},
}

for name, tc := range testCases {
authorization := &foundation.ReceiveFromTreasuryAuthorization{}

resp, err := authorization.Accept(sdk.Context{}, tc.msg)
if !tc.valid {
require.Error(t, err, name)
continue
}
require.NoError(t, err, name)

require.Equal(t, tc.accept, resp.Accept)
}
}
173 changes: 95 additions & 78 deletions x/foundation/foundation.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,12 @@ import (
)

func DefaultDecisionPolicy(config Config) DecisionPolicy {
policy := &ThresholdDecisionPolicy{
return &ThresholdDecisionPolicy{
Threshold: config.MinThreshold,
Windows: &DecisionPolicyWindows{
VotingPeriod: 24 * time.Hour,
},
}

// check whether the default policy is valid
if err := policy.ValidateBasic(); err != nil {
panic(err)
}
if err := policy.Validate(config); err != nil {
panic(err)
}

return policy
}

func validateProposers(proposers []string) error {
Expand Down Expand Up @@ -119,6 +109,20 @@ type DecisionPolicy interface {
Validate(config Config) error
}

// DefaultTallyResult returns a TallyResult with all counts set to 0.
func DefaultTallyResult() TallyResult {
return NewTallyResult(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
}

func NewTallyResult(yes, abstain, no, veto sdk.Dec) TallyResult {
return TallyResult{
YesCount: yes,
AbstainCount: abstain,
NoCount: no,
NoWithVetoCount: veto,
}
}

func (t *TallyResult) Add(option VoteOption) error {
weight := sdk.OneDec()

Expand Down Expand Up @@ -221,49 +225,6 @@ func SetMsgs(msgs []sdk.Msg) ([]*codectypes.Any, error) {
return anys, nil
}

var _ codectypes.UnpackInterfacesMessage = (*FoundationInfo)(nil)

func (i FoundationInfo) GetDecisionPolicy() DecisionPolicy {
if i.DecisionPolicy == nil {
return nil
}

policy, ok := i.DecisionPolicy.GetCachedValue().(DecisionPolicy)
if !ok {
return nil
}
return policy
}

func (i *FoundationInfo) SetDecisionPolicy(policy DecisionPolicy) error {
msg, ok := policy.(proto.Message)
if !ok {
return sdkerrors.ErrInvalidType.Wrapf("can't proto marshal %T", msg)
}

any, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return err
}
i.DecisionPolicy = any

return nil
}

// for the tests
func (i FoundationInfo) WithDecisionPolicy(policy DecisionPolicy) *FoundationInfo {
info := i
if err := info.SetDecisionPolicy(policy); err != nil {
return nil
}
return &info
}

func (i *FoundationInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
var policy DecisionPolicy
return unpacker.UnpackAny(i.DecisionPolicy, &policy)
}

var _ DecisionPolicy = (*ThresholdDecisionPolicy)(nil)

func validateDecisionPolicyWindows(windows DecisionPolicyWindows, config Config) error {
Expand All @@ -273,13 +234,9 @@ func validateDecisionPolicyWindows(windows DecisionPolicyWindows, config Config)
return nil
}

func (p ThresholdDecisionPolicy) Validate(config Config) error {
if p.Threshold.LT(config.MinThreshold) {
return sdkerrors.ErrInvalidRequest.Wrap("threshold must be greater than or equal to min_threshold")
}

if err := validateDecisionPolicyWindows(*p.Windows, config); err != nil {
return err
func validateDecisionPolicyWindowsBasic(windows *DecisionPolicyWindows) error {
if windows == nil || windows.VotingPeriod == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("voting period cannot be zero")
}

return nil
Expand Down Expand Up @@ -307,10 +264,10 @@ func (p ThresholdDecisionPolicy) Allow(result TallyResult, totalWeight sdk.Dec,
// plus all undecided count (supposing they all vote yes).
maxYesCount := result.YesCount.Add(undecided)
if maxYesCount.LT(realThreshold) {
return &DecisionPolicyResult{Allow: false, Final: true}, nil
return &DecisionPolicyResult{Final: true}, nil
}

return &DecisionPolicyResult{Final: false}, nil
return &DecisionPolicyResult{}, nil
}

func (p ThresholdDecisionPolicy) GetVotingPeriod() time.Duration {
Expand All @@ -322,18 +279,15 @@ func (p ThresholdDecisionPolicy) ValidateBasic() error {
return sdkerrors.ErrInvalidRequest.Wrap("threshold must be a positive number")
}

if p.Windows == nil || p.Windows.VotingPeriod == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("voting period cannot be zero")
if err := validateDecisionPolicyWindowsBasic(p.Windows); err != nil {
return err
}

return nil
}

var _ DecisionPolicy = (*PercentageDecisionPolicy)(nil)

func (p PercentageDecisionPolicy) Validate(config Config) error {
if p.Percentage.LT(config.MinPercentage) {
return sdkerrors.ErrInvalidRequest.Wrap("percentage must be greater than or equal to min_percentage")
func (p ThresholdDecisionPolicy) Validate(config Config) error {
if p.Threshold.LT(config.MinThreshold) {
return sdkerrors.ErrInvalidRequest.Wrap("threshold must be greater than or equal to min_threshold")
}

if err := validateDecisionPolicyWindows(*p.Windows, config); err != nil {
Expand All @@ -343,34 +297,42 @@ func (p PercentageDecisionPolicy) Validate(config Config) error {
return nil
}

var _ DecisionPolicy = (*PercentageDecisionPolicy)(nil)

func (p PercentageDecisionPolicy) Allow(result TallyResult, totalWeight sdk.Dec, sinceSubmission time.Duration) (*DecisionPolicyResult, error) {
if sinceSubmission < p.Windows.MinExecutionPeriod {
return nil, sdkerrors.ErrUnauthorized.Wrapf("must wait %s after submission before execution, currently at %s", p.Windows.MinExecutionPeriod, sinceSubmission)
}

yesPercentage := result.YesCount.Quo(totalWeight)
notAbstaining := totalWeight.Sub(result.AbstainCount)
// If no one votes (everyone abstains), proposal fails
if notAbstaining.IsZero() {
return &DecisionPolicyResult{Final: true}, nil
}

yesPercentage := result.YesCount.Quo(notAbstaining)
if yesPercentage.GTE(p.Percentage) {
return &DecisionPolicyResult{Allow: true, Final: true}, nil
}

totalCounts := result.TotalCounts()
undecided := totalWeight.Sub(totalCounts)
undecided := notAbstaining.Sub(totalCounts)
maxYesCount := result.YesCount.Add(undecided)
maxYesPercentage := maxYesCount.Quo(totalWeight)
maxYesPercentage := maxYesCount.Quo(notAbstaining)
if maxYesPercentage.LT(p.Percentage) {
return &DecisionPolicyResult{Allow: false, Final: true}, nil
return &DecisionPolicyResult{Final: true}, nil
}

return &DecisionPolicyResult{Allow: false, Final: false}, nil
return &DecisionPolicyResult{}, nil
}

func (p PercentageDecisionPolicy) GetVotingPeriod() time.Duration {
return p.Windows.VotingPeriod
}

func (p PercentageDecisionPolicy) ValidateBasic() error {
if p.Windows == nil || p.Windows.VotingPeriod == 0 {
return sdkerrors.ErrInvalidRequest.Wrap("voting period cannot be zero")
if err := validateDecisionPolicyWindowsBasic(p.Windows); err != nil {
return err
}

if err := validateRatio(p.Percentage, "percentage"); err != nil {
Expand All @@ -380,6 +342,18 @@ func (p PercentageDecisionPolicy) ValidateBasic() error {
return nil
}

func (p PercentageDecisionPolicy) Validate(config Config) error {
if p.Percentage.LT(config.MinPercentage) {
return sdkerrors.ErrInvalidRequest.Wrap("percentage must be greater than or equal to min_percentage")
}

if err := validateDecisionPolicyWindows(*p.Windows, config); err != nil {
return err
}

return nil
}

func validateRatio(ratio sdk.Dec, name string) error {
if ratio.IsNil() {
return sdkerrors.ErrInvalidRequest.Wrapf("%s is nil", name)
Expand All @@ -391,6 +365,49 @@ func validateRatio(ratio sdk.Dec, name string) error {
return nil
}

var _ codectypes.UnpackInterfacesMessage = (*FoundationInfo)(nil)

func (i FoundationInfo) GetDecisionPolicy() DecisionPolicy {
if i.DecisionPolicy == nil {
return nil
}

policy, ok := i.DecisionPolicy.GetCachedValue().(DecisionPolicy)
if !ok {
return nil
}
return policy
}

func (i *FoundationInfo) SetDecisionPolicy(policy DecisionPolicy) error {
msg, ok := policy.(proto.Message)
if !ok {
return sdkerrors.ErrInvalidType.Wrapf("can't proto marshal %T", msg)
}

any, err := codectypes.NewAnyWithValue(msg)
if err != nil {
return err
}
i.DecisionPolicy = any

return nil
}

// for the tests
func (i FoundationInfo) WithDecisionPolicy(policy DecisionPolicy) *FoundationInfo {
info := i
if err := info.SetDecisionPolicy(policy); err != nil {
return nil
}
return &info
}

func (i *FoundationInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
var policy DecisionPolicy
return unpacker.UnpackAny(i.DecisionPolicy, &policy)
}

func GetAuthorization(any *codectypes.Any, name string) (Authorization, error) {
cached := any.GetCachedValue()
if cached == nil {
Expand Down
Loading