refactor(group): Distinguish Voting period and Execution period for group policies (#11198)
## Description Closes: #11092 ## TODOs I'm thinking to do the 2 todos in a separate PR, or else this PR is too big. WDYT? - [ ] #11246 This involves adding a new index ProposalsByVotingPeriodEnd, so might be better to do in another PR - [ ] #11245 Also should be done in a separate PR (as it needs the above index) ### Main change 1: Group policy proto defs have `voting_period` and `min_execution_period` For group policies: ```diff - // Within this times votes and exec messages can be submitted. - // timeout is the duration from submission of a proposal to the end of voting period - google.protobuf.Duration timeout = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false]; + // voting_period is the duration from submission of a proposal to the end of voting period + // Within this times votes can be submitted with MsgVote. + google.protobuf.Duration voting_period = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false]; + // min_execution_period is the minimum duration after the proposal submission + // where members can start sending MsgExec. This means that the window for + // sending a MsgExec transaction is: + // `[ submission + min_execution_period ; submission + voting_period + max_execution_period]` + // where max_execution_period is a app-specific config, defined in the keeper. + // If not set, min_execution_period will default to 0. + google.protobuf.Duration min_execution_period = 3 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false]; ``` ### Main Change 2: We don't update proposal's FinalTallyResult result on MsgVote/MsgSubmitProposal Unless the msg has TryExec set to true, in which case the FinalTallyResult is updated ONLY if the tally is final. ### Main Change 3: Add a keeper-level `MaxExecutionPeriod` MsgExecs will be rejected if they are sent after `voting_period_end + MaxExecutionPeriod` --- ### Author Checklist *All items are required. Please add a note to the item if the item is not applicable and please add links to any relevant follow up issues.* I have... - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] added `!` to the type prefix if API or client breaking change - [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] provided a link to the relevant issue or specification - [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules) - [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] added a changelog entry to `CHANGELOG.md` - [ ] included comments for [documenting Go code](https://blog.golang.org/godoc) - [ ] updated the relevant documentation or specification - [ ] reviewed "Files changed" and left comments if necessary - [ ] confirmed all CI checks have passed ### Reviewers Checklist *All items are required. Please add a note if the item is not applicable and please add your handle next to the items reviewed if you only reviewed selected items.* I have... - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title - [ ] confirmed `!` in the type prefix if API or client breaking change - [ ] confirmed all author checklist items have been addressed - [ ] reviewed state machine logic - [ ] reviewed API design and naming - [ ] reviewed documentation is accurate - [ ] reviewed tests and test coverage - [ ] manually tested (if applicable)
This commit is contained in:
parent
26c9a2d06d
commit
da36c46f3a
|
@ -3,15 +3,14 @@ package appv1alpha1
|
|||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
var _ protoreflect.List = (*_ModuleDescriptor_2_list)(nil)
|
||||
|
|
|
@ -4078,7 +4078,9 @@ type SnapshotIAVLItem struct {
|
|||
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// version is block height
|
||||
Version int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// height is depth of the tree.
|
||||
Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"`
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,16 +3,14 @@ package modulev1alpha1
|
|||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
_ "github.com/cosmos/cosmos-sdk/api/cosmos/app/v1alpha1"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
|
||||
_ "github.com/cosmos/cosmos-sdk/api/cosmos/app/v1alpha1"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -3,15 +3,14 @@ package ormv1alpha1
|
|||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
var _ protoreflect.List = (*_TableDescriptor_2_list)(nil)
|
||||
|
|
|
@ -3,15 +3,14 @@ package ormv1alpha1
|
|||
|
||||
import (
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
|
||||
runtime "github.com/cosmos/cosmos-proto/runtime"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoiface "google.golang.org/protobuf/runtime/protoiface"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
descriptorpb "google.golang.org/protobuf/types/descriptorpb"
|
||||
io "io"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
var _ protoreflect.List = (*_ModuleSchemaDescriptor_1_list)(nil)
|
||||
|
|
|
@ -41,9 +41,8 @@ message ThresholdDecisionPolicy {
|
|||
// threshold is the minimum weighted sum of yes votes that must be met or exceeded for a proposal to succeed.
|
||||
string threshold = 1;
|
||||
|
||||
// timeout is the duration from submission of a proposal to the end of voting period
|
||||
// Within this times votes and exec messages can be submitted.
|
||||
google.protobuf.Duration timeout = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];
|
||||
// windows defines the different windows for voting and execution.
|
||||
DecisionPolicyWindows windows = 2;
|
||||
}
|
||||
|
||||
// PercentageDecisionPolicy implements the DecisionPolicy interface
|
||||
|
@ -53,9 +52,28 @@ message PercentageDecisionPolicy {
|
|||
// percentage is the minimum percentage the weighted sum of yes votes must meet for a proposal to succeed.
|
||||
string percentage = 1;
|
||||
|
||||
// timeout is the duration from submission of a proposal to the end of voting period
|
||||
// Within these times votes and exec messages can be submitted.
|
||||
google.protobuf.Duration timeout = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];
|
||||
// windows defines the different windows for voting and execution.
|
||||
DecisionPolicyWindows windows = 2;
|
||||
}
|
||||
|
||||
// DecisionPolicyWindows defines the different windows for voting and execution.
|
||||
message DecisionPolicyWindows {
|
||||
// voting_period is the duration from submission of a proposal to the end of voting period
|
||||
// Within this times votes can be submitted with MsgVote.
|
||||
google.protobuf.Duration voting_period = 1 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];
|
||||
|
||||
// min_execution_period is the minimum duration after the proposal submission
|
||||
// where members can start sending MsgExec. This means that the window for
|
||||
// sending a MsgExec transaction is:
|
||||
// `[ submission + min_execution_period ; submission + voting_period + max_execution_period]`
|
||||
// where max_execution_period is a app-specific config, defined in the keeper.
|
||||
// If not set, min_execution_period will default to 0.
|
||||
//
|
||||
// Please make sure to set a `min_execution_period` that is smaller than
|
||||
// `voting_period + max_execution_period`, or else the above execution window
|
||||
// is empty, meaning that all proposals created with this decision policy
|
||||
// won't be able to be executed.
|
||||
google.protobuf.Duration min_execution_period = 2 [(gogoproto.stdduration) = true, (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// VoteOption enumerates the valid vote options for a given proposal.
|
||||
|
@ -184,11 +202,12 @@ message Proposal {
|
|||
// has ended.
|
||||
TallyResult final_tally_result = 10 [(gogoproto.nullable) = false];
|
||||
|
||||
// timeout is the timestamp before which both voting and execution must be
|
||||
// done. If this timestamp is passed, then the proposal cannot be executed
|
||||
// anymore and should be considered pending delete. This timestamp is checked
|
||||
// against the block header's timestamp.
|
||||
google.protobuf.Timestamp timeout = 11 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
// voting_period_end is the timestamp before which voting must be done.
|
||||
// Unless a successfull MsgExec is called before (to execute a proposal whose
|
||||
// tally is successful before the voting period ends), tallying will be done
|
||||
// at this point, and the `final_tally_result`, as well
|
||||
// as `status` and `result` fields will be accordingly updated.
|
||||
google.protobuf.Timestamp voting_period_end = 11 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
|
||||
// executor_result is the final result based on the votes and election rule. Initial value is NotRun.
|
||||
ProposalExecutorResult executor_result = 12;
|
||||
|
|
|
@ -295,7 +295,7 @@ func NewSimApp(
|
|||
|
||||
app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.msgSvcRouter, app.AccountKeeper)
|
||||
|
||||
groupConfig := groupkeeper.DefaultConfig()
|
||||
groupConfig := group.DefaultConfig()
|
||||
/*
|
||||
Example of setting group params:
|
||||
groupConfig.MaxMetadataLen = 1000
|
||||
|
|
|
@ -308,7 +308,9 @@ func (m *SnapshotStoreItem) GetName() string {
|
|||
type SnapshotIAVLItem struct {
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// version is block height
|
||||
Version int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"`
|
||||
// height is depth of the tree.
|
||||
Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"`
|
||||
}
|
||||
|
||||
|
|
|
@ -553,6 +553,8 @@ func (m *GetTxResponse) GetTxResponse() *types.TxResponse {
|
|||
|
||||
// GetBlockWithTxsRequest is the request type for the Service.GetBlockWithTxs
|
||||
// RPC method.
|
||||
//
|
||||
// Since: cosmos-sdk 0.45.2
|
||||
type GetBlockWithTxsRequest struct {
|
||||
// height is the height of the block to query.
|
||||
Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"`
|
||||
|
@ -608,6 +610,8 @@ func (m *GetBlockWithTxsRequest) GetPagination() *query.PageRequest {
|
|||
}
|
||||
|
||||
// GetBlockWithTxsResponse is the response type for the Service.GetBlockWithTxs method.
|
||||
//
|
||||
// Since: cosmos-sdk 0.45.2
|
||||
type GetBlockWithTxsResponse struct {
|
||||
// txs are the transactions in the block.
|
||||
Txs []*Tx `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
|
||||
|
|
|
@ -120,7 +120,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
|||
val.Address.String(),
|
||||
"1",
|
||||
validMetadata,
|
||||
fmt.Sprintf("{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"%d\", \"timeout\":\"30000s\"}", threshold),
|
||||
fmt.Sprintf("{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"%d\", \"windows\":{\"voting_period\":\"30000s\"}}", threshold),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -140,7 +140,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
|||
val.Address.String(),
|
||||
"1",
|
||||
validMetadata,
|
||||
fmt.Sprintf("{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"%f\", \"timeout\":\"30000s\"}", percentage),
|
||||
fmt.Sprintf("{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"%f\", \"windows\":{\"voting_period\":\"30000s\"}}", percentage),
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -746,7 +746,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
validMembersFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -764,7 +764,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
validMembersFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, true),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -782,7 +782,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
validMembersFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
|
||||
},
|
||||
|
@ -801,7 +801,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
strings.Repeat("a", 256),
|
||||
validMetadata,
|
||||
validMembersFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -819,7 +819,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
strings.Repeat("a", 256),
|
||||
validMembersFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -837,7 +837,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
invalidMembersAddressFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -855,7 +855,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
invalidMembersWeightFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -873,7 +873,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupWithPolicy() {
|
|||
validMetadata,
|
||||
validMetadata,
|
||||
invalidMembersMetadataFile.Name(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%v", client.FlagGroupPolicyAsAdmin, false),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -932,7 +932,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -948,7 +948,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"0.5\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"0.5\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -964,7 +964,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -981,7 +981,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
wrongAdmin.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -997,7 +997,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
strings.Repeat("a", 500),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1013,7 +1013,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
"10",
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1029,7 +1029,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"-0.5\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"-0.5\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1045,7 +1045,7 @@ func (s *IntegrationTestSuite) TestTxCreateGroupPolicy() {
|
|||
val.Address.String(),
|
||||
fmt.Sprintf("%v", groupID),
|
||||
validMetadata,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"2\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"2\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1205,7 +1205,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"40000s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"40000s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1220,7 +1220,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"0.5\", \"timeout\":\"40000s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"0.5\", \"windows\":{\"voting_period\":\"40000s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1235,7 +1235,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"50000s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"50000s\"}}",
|
||||
fmt.Sprintf("--%s=%s", flags.FlagSignMode, flags.SignModeLegacyAminoJSON),
|
||||
},
|
||||
commonFlags...,
|
||||
|
@ -1251,7 +1251,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
newAdmin.String(),
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1266,7 +1266,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
newAdmin.String(),
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.ThresholdDecisionPolicy\", \"threshold\":\"1\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1281,7 +1281,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"-0.5\", \"timeout\":\"1s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"-0.5\", \"windows\":{\"voting_period\":\"1s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
@ -1296,7 +1296,7 @@ func (s *IntegrationTestSuite) TestTxUpdateGroupPolicyDecisionPolicy() {
|
|||
[]string{
|
||||
groupPolicy.Admin,
|
||||
groupPolicy.Address,
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"2\", \"timeout\":\"40000s\"}",
|
||||
"{\"@type\":\"/cosmos.group.v1beta1.PercentageDecisionPolicy\", \"percentage\":\"2\", \"windows\":{\"voting_period\":\"40000s\"}}",
|
||||
},
|
||||
commonFlags...,
|
||||
),
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package keeper
|
||||
package group
|
||||
|
||||
import "time"
|
||||
|
||||
// Config is a config struct used for intialising the group module to avoid using globals.
|
||||
type Config struct {
|
||||
// MaxExecutionPeriod defines the max duration after a proposal's voting
|
||||
// period ends that members can send a MsgExec to execute the proposal.
|
||||
MaxExecutionPeriod time.Duration
|
||||
// MaxMetadataLen defines the max length of the metadata bytes field for various entities within the group module. Defaults to 255 if not explicitly set.
|
||||
MaxMetadataLen uint64
|
||||
}
|
||||
|
@ -9,6 +14,7 @@ type Config struct {
|
|||
// DefaultConfig returns the default config for group.
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
MaxExecutionPeriod: 2 * time.Hour * 24 * 7, // Two weeks.
|
||||
MaxMetadataLen: 255,
|
||||
}
|
||||
}
|
|
@ -31,7 +31,9 @@ func TestGenesisStateValidate(t *testing.T) {
|
|||
}
|
||||
err := groupPolicy.SetDecisionPolicy(&ThresholdDecisionPolicy{
|
||||
Threshold: "1",
|
||||
Timeout: time.Second,
|
||||
Windows: &DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -45,7 +47,9 @@ func TestGenesisStateValidate(t *testing.T) {
|
|||
}
|
||||
err = groupPolicy2.SetDecisionPolicy(&ThresholdDecisionPolicy{
|
||||
Threshold: "1",
|
||||
Timeout: 0,
|
||||
Windows: &DecisionPolicyWindows{
|
||||
VotingPeriod: 0,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
@ -67,7 +71,7 @@ func TestGenesisStateValidate(t *testing.T) {
|
|||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
Timeout: timeout,
|
||||
VotingPeriodEnd: timeout,
|
||||
ExecutorResult: PROPOSAL_EXECUTOR_RESULT_SUCCESS,
|
||||
}
|
||||
err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{
|
||||
|
|
|
@ -73,7 +73,9 @@ func (s *GenesisTestSuite) TestInitExportGenesis() {
|
|||
}
|
||||
err := groupPolicy.SetDecisionPolicy(&group.ThresholdDecisionPolicy{
|
||||
Threshold: "1",
|
||||
Timeout: time.Second,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
},
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -95,7 +97,7 @@ func (s *GenesisTestSuite) TestInitExportGenesis() {
|
|||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
Timeout: timeout,
|
||||
VotingPeriodEnd: timeout,
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS,
|
||||
}
|
||||
err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{
|
||||
|
@ -216,7 +218,7 @@ func (s *GenesisTestSuite) assertProposalsEqual(g *group.Proposal, other *group.
|
|||
require.Equal(g.Status, other.Status)
|
||||
require.Equal(g.Result, other.Result)
|
||||
require.Equal(g.FinalTallyResult, other.FinalTallyResult)
|
||||
require.Equal(g.Timeout, other.Timeout)
|
||||
require.Equal(g.VotingPeriodEnd, other.VotingPeriodEnd)
|
||||
require.Equal(g.ExecutorResult, other.ExecutorResult)
|
||||
require.Equal(g.GetMsgs(), other.GetMsgs())
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
"github.com/cosmos/cosmos-sdk/types/query"
|
||||
"github.com/cosmos/cosmos-sdk/x/group"
|
||||
"github.com/cosmos/cosmos-sdk/x/group/errors"
|
||||
"github.com/cosmos/cosmos-sdk/x/group/internal/orm"
|
||||
)
|
||||
|
||||
|
@ -303,3 +304,55 @@ func (q Keeper) getVotesByProposal(ctx sdk.Context, proposalID uint64, pageReque
|
|||
func (q Keeper) getVotesByVoter(ctx sdk.Context, voter sdk.AccAddress, pageRequest *query.PageRequest) (orm.Iterator, error) {
|
||||
return q.voteByVoterIndex.GetPaginated(ctx.KVStore(q.key), voter.Bytes(), pageRequest)
|
||||
}
|
||||
|
||||
// Tally is a function that tallies a proposal by iterating through its votes,
|
||||
// and returns the tally result without modifying the proposal or any state.
|
||||
// TODO Merge with https://github.com/cosmos/cosmos-sdk/issues/11151
|
||||
func (q Keeper) Tally(ctx sdk.Context, p group.Proposal, groupId uint64) (group.TallyResult, error) {
|
||||
// If proposal has already been tallied and updated, then its status is
|
||||
// closed, in which case we just return the previously stored result.
|
||||
if p.Status == group.PROPOSAL_STATUS_CLOSED {
|
||||
return p.FinalTallyResult, nil
|
||||
}
|
||||
|
||||
it, err := q.voteByProposalIndex.Get(ctx.KVStore(q.key), p.Id)
|
||||
if err != nil {
|
||||
return group.TallyResult{}, err
|
||||
}
|
||||
defer it.Close()
|
||||
|
||||
tallyResult := group.DefaultTallyResult()
|
||||
|
||||
var vote group.Vote
|
||||
for {
|
||||
_, err = it.LoadNext(&vote)
|
||||
if errors.ErrORMIteratorDone.Is(err) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return group.TallyResult{}, err
|
||||
}
|
||||
|
||||
var member group.GroupMember
|
||||
err := q.groupMemberTable.GetOne(ctx.KVStore(q.key), orm.PrimaryKey(&group.GroupMember{
|
||||
GroupId: groupId,
|
||||
Member: &group.Member{Address: vote.Voter},
|
||||
}), &member)
|
||||
|
||||
switch {
|
||||
case sdkerrors.ErrNotFound.Is(err):
|
||||
// If the member left the group after voting, then we simply skip the
|
||||
// vote.
|
||||
continue
|
||||
case err != nil:
|
||||
// For any other errors, we stop and return the error.
|
||||
return group.TallyResult{}, err
|
||||
}
|
||||
|
||||
if err := tallyResult.Add(vote, member.Member.Weight); err != nil {
|
||||
return group.TallyResult{}, sdkerrors.Wrap(err, "add new vote")
|
||||
}
|
||||
}
|
||||
|
||||
return tallyResult, nil
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ type invariantTestSuite struct {
|
|||
ctx sdk.Context
|
||||
cdc *codec.ProtoCodec
|
||||
key *storetypes.KVStoreKey
|
||||
blockTime time.Time
|
||||
}
|
||||
|
||||
func TestInvariantTestSuite(t *testing.T) {
|
||||
|
@ -81,7 +80,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "1", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
|
||||
|
@ -95,7 +94,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "2", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
},
|
||||
|
@ -110,7 +109,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "2", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
curProposal: &group.Proposal{
|
||||
|
@ -123,7 +122,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "1", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
expBroken: true,
|
||||
|
@ -139,7 +138,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "2", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
curProposal: &group.Proposal{
|
||||
|
@ -152,7 +151,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "1", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
expBroken: true,
|
||||
|
@ -168,7 +167,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "0", AbstainCount: "2", NoWithVetoCount: "0"},
|
||||
Timeout: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
curProposal: &group.Proposal{
|
||||
|
@ -181,7 +180,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "0", AbstainCount: "1", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
expBroken: true,
|
||||
|
@ -197,7 +196,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "2"},
|
||||
Timeout: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: prevCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
curProposal: &group.Proposal{
|
||||
|
@ -210,7 +209,7 @@ func (s *invariantTestSuite) TestTallyVotesInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "0", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "1"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
expBroken: true,
|
||||
|
@ -413,7 +412,7 @@ func (s *invariantTestSuite) TestTallyVotesSumInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "4", NoCount: "3", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
votes: []*group.Vote{
|
||||
|
@ -471,7 +470,7 @@ func (s *invariantTestSuite) TestTallyVotesSumInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "6", NoCount: "0", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
votes: []*group.Vote{
|
||||
|
@ -529,7 +528,7 @@ func (s *invariantTestSuite) TestTallyVotesSumInvariant() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{YesCount: "4", NoCount: "3", AbstainCount: "0", NoWithVetoCount: "0"},
|
||||
Timeout: curCtx.BlockTime().Add(time.Second * 600),
|
||||
VotingPeriodEnd: curCtx.BlockTime().Add(time.Second * 600),
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
},
|
||||
votes: []*group.Vote{
|
||||
|
@ -561,7 +560,7 @@ func (s *invariantTestSuite) TestTallyVotesSumInvariant() {
|
|||
_, err := groupTable.Create(cacheCurCtx.KVStore(key), groupsInfo)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = groupPolicy.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Second))
|
||||
err = groupPolicy.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Second, 0))
|
||||
s.Require().NoError(err)
|
||||
err = groupPolicyTable.Create(cacheCurCtx.KVStore(key), groupPolicy)
|
||||
s.Require().NoError(err)
|
||||
|
|
|
@ -74,10 +74,10 @@ type Keeper struct {
|
|||
|
||||
router *authmiddleware.MsgServiceRouter
|
||||
|
||||
config Config
|
||||
config group.Config
|
||||
}
|
||||
|
||||
func NewKeeper(storeKey storetypes.StoreKey, cdc codec.Codec, router *authmiddleware.MsgServiceRouter, accKeeper group.AccountKeeper, config Config) Keeper {
|
||||
func NewKeeper(storeKey storetypes.StoreKey, cdc codec.Codec, router *authmiddleware.MsgServiceRouter, accKeeper group.AccountKeeper, config group.Config) Keeper {
|
||||
k := Keeper{
|
||||
key: storeKey,
|
||||
router: router,
|
||||
|
@ -207,7 +207,10 @@ func NewKeeper(storeKey storetypes.StoreKey, cdc codec.Codec, router *authmiddle
|
|||
k.voteTable = *voteTable
|
||||
|
||||
if config.MaxMetadataLen == 0 {
|
||||
config.MaxMetadataLen = DefaultConfig().MaxMetadataLen
|
||||
config.MaxMetadataLen = group.DefaultConfig().MaxMetadataLen
|
||||
}
|
||||
if config.MaxExecutionPeriod == 0 {
|
||||
config.MaxExecutionPeriod = group.DefaultConfig().MaxExecutionPeriod
|
||||
}
|
||||
k.config = config
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ func (s *TestSuite) SetupTest() {
|
|||
policy := group.NewThresholdDecisionPolicy(
|
||||
"2",
|
||||
time.Second,
|
||||
0,
|
||||
)
|
||||
policyReq := &group.MsgCreateGroupPolicy{
|
||||
Admin: s.addrs[0].String(),
|
||||
|
@ -693,6 +694,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
},
|
||||
"group policy as admin is true": {
|
||||
|
@ -704,6 +706,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
},
|
||||
"group metadata too long": {
|
||||
|
@ -716,6 +719,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "limit exceeded",
|
||||
|
@ -730,6 +734,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "limit exceeded",
|
||||
|
@ -747,6 +752,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "limit exceeded",
|
||||
|
@ -763,6 +769,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "expected a positive decimal",
|
||||
|
@ -776,6 +783,7 @@ func (s *TestSuite) TestCreateGroupWithPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"10",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: false,
|
||||
},
|
||||
|
@ -876,6 +884,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
},
|
||||
"all good with percentage decision policy": {
|
||||
|
@ -886,6 +895,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewPercentageDecisionPolicy(
|
||||
"0.5",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
},
|
||||
"decision policy threshold > total group weight": {
|
||||
|
@ -896,6 +906,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"10",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
},
|
||||
"group id does not exists": {
|
||||
|
@ -906,6 +917,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "not found",
|
||||
|
@ -918,6 +930,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "not group admin",
|
||||
|
@ -931,6 +944,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "limit exceeded",
|
||||
|
@ -943,6 +957,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewPercentageDecisionPolicy(
|
||||
"-0.5",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "expected a positive decimal",
|
||||
|
@ -955,6 +970,7 @@ func (s *TestSuite) TestCreateGroupPolicy() {
|
|||
policy: group.NewPercentageDecisionPolicy(
|
||||
"2",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
expErr: true,
|
||||
expErrMsg: "percentage must be > 0 and <= 1",
|
||||
|
@ -1194,6 +1210,7 @@ func (s *TestSuite) TestUpdateGroupPolicyDecisionPolicy() {
|
|||
policy: group.NewThresholdDecisionPolicy(
|
||||
"2",
|
||||
time.Duration(2)*time.Second,
|
||||
0,
|
||||
),
|
||||
expGroupPolicy: &group.GroupPolicyInfo{
|
||||
Admin: admin.String(),
|
||||
|
@ -1216,6 +1233,7 @@ func (s *TestSuite) TestUpdateGroupPolicyDecisionPolicy() {
|
|||
policy: group.NewPercentageDecisionPolicy(
|
||||
"0.5",
|
||||
time.Duration(2)*time.Second,
|
||||
0,
|
||||
),
|
||||
expGroupPolicy: &group.GroupPolicyInfo{
|
||||
Admin: admin.String(),
|
||||
|
@ -1278,14 +1296,17 @@ func (s *TestSuite) TestGroupPoliciesByAdminOrGroup() {
|
|||
group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
group.NewThresholdDecisionPolicy(
|
||||
"10",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
group.NewPercentageDecisionPolicy(
|
||||
"0.5",
|
||||
time.Second,
|
||||
0,
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -1376,6 +1397,7 @@ func (s *TestSuite) TestSubmitProposal() {
|
|||
policy := group.NewThresholdDecisionPolicy(
|
||||
"100",
|
||||
time.Second,
|
||||
0,
|
||||
)
|
||||
err := policyReq.SetDecisionPolicy(policy)
|
||||
s.Require().NoError(err)
|
||||
|
@ -1523,7 +1545,7 @@ func (s *TestSuite) TestSubmitProposal() {
|
|||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
FinalTallyResult: group.TallyResult{
|
||||
YesCount: "1",
|
||||
YesCount: "0", // Since tally doesn't pass Allow(), we consider the proposal not final
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
|
@ -1562,7 +1584,7 @@ func (s *TestSuite) TestSubmitProposal() {
|
|||
s.Assert().Equal(spec.expProposal.Result, proposal.Result)
|
||||
s.Assert().Equal(spec.expProposal.FinalTallyResult, proposal.FinalTallyResult)
|
||||
s.Assert().Equal(spec.expProposal.ExecutorResult, proposal.ExecutorResult)
|
||||
s.Assert().Equal(s.blockTime.Add(time.Second), proposal.Timeout)
|
||||
s.Assert().Equal(s.blockTime.Add(time.Second), proposal.VotingPeriodEnd)
|
||||
|
||||
if spec.msgs == nil { // then empty list is ok
|
||||
s.Assert().Len(proposal.GetMsgs(), 0)
|
||||
|
@ -1684,6 +1706,7 @@ func (s *TestSuite) TestVote() {
|
|||
policy := group.NewThresholdDecisionPolicy(
|
||||
"2",
|
||||
time.Duration(2),
|
||||
0,
|
||||
)
|
||||
policyReq := &group.MsgCreateGroupPolicy{
|
||||
Admin: addr1.String(),
|
||||
|
@ -1732,22 +1755,18 @@ func (s *TestSuite) TestVote() {
|
|||
s.Assert().Equal(uint64(1), proposals[0].GroupPolicyVersion)
|
||||
s.Assert().Equal(group.PROPOSAL_STATUS_SUBMITTED, proposals[0].Status)
|
||||
s.Assert().Equal(group.PROPOSAL_RESULT_UNFINALIZED, proposals[0].Result)
|
||||
s.Assert().Equal(group.TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
}, proposals[0].FinalTallyResult)
|
||||
s.Assert().Equal(group.DefaultTallyResult(), proposals[0].FinalTallyResult)
|
||||
|
||||
specs := map[string]struct {
|
||||
srcCtx sdk.Context
|
||||
expFinalTallyResult group.TallyResult
|
||||
expTallyResult group.TallyResult // expected after tallying
|
||||
isFinal bool // is the tally result final?
|
||||
req *group.MsgVote
|
||||
doBefore func(ctx context.Context)
|
||||
postRun func(sdkCtx sdk.Context)
|
||||
expProposalStatus group.ProposalStatus
|
||||
expResult group.ProposalResult
|
||||
expExecutorResult group.ProposalExecutorResult
|
||||
expProposalStatus group.ProposalStatus // expected after tallying
|
||||
expResult group.ProposalResult // expected after tallying
|
||||
expExecutorResult group.ProposalExecutorResult // expected after tallying
|
||||
expErr bool
|
||||
}{
|
||||
"vote yes": {
|
||||
|
@ -1756,7 +1775,7 @@ func (s *TestSuite) TestVote() {
|
|||
Voter: addr4.String(),
|
||||
Option: group.VOTE_OPTION_YES,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "1",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
|
@ -1774,12 +1793,13 @@ func (s *TestSuite) TestVote() {
|
|||
Option: group.VOTE_OPTION_YES,
|
||||
Exec: group.Exec_EXEC_TRY,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "2",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
isFinal: true,
|
||||
expProposalStatus: group.PROPOSAL_STATUS_CLOSED,
|
||||
expResult: group.PROPOSAL_RESULT_ACCEPTED,
|
||||
expExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS,
|
||||
|
@ -1797,7 +1817,7 @@ func (s *TestSuite) TestVote() {
|
|||
Option: group.VOTE_OPTION_YES,
|
||||
Exec: group.Exec_EXEC_TRY,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "1",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
|
@ -1814,7 +1834,7 @@ func (s *TestSuite) TestVote() {
|
|||
Voter: addr4.String(),
|
||||
Option: group.VOTE_OPTION_NO,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "1",
|
||||
AbstainCount: "0",
|
||||
|
@ -1831,7 +1851,7 @@ func (s *TestSuite) TestVote() {
|
|||
Voter: addr4.String(),
|
||||
Option: group.VOTE_OPTION_ABSTAIN,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "0",
|
||||
AbstainCount: "1",
|
||||
|
@ -1848,7 +1868,7 @@ func (s *TestSuite) TestVote() {
|
|||
Voter: addr4.String(),
|
||||
Option: group.VOTE_OPTION_NO_WITH_VETO,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
|
@ -1865,7 +1885,7 @@ func (s *TestSuite) TestVote() {
|
|||
Voter: addr3.String(),
|
||||
Option: group.VOTE_OPTION_YES,
|
||||
},
|
||||
expFinalTallyResult: group.TallyResult{
|
||||
expTallyResult: group.TallyResult{
|
||||
YesCount: "2",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
|
@ -1887,6 +1907,7 @@ func (s *TestSuite) TestVote() {
|
|||
ProposalId: myProposalID,
|
||||
Voter: addr3.String(),
|
||||
Option: group.VOTE_OPTION_NO_WITH_VETO,
|
||||
Exec: 1, // Execute the proposal so that its status is final
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
},
|
||||
|
@ -1947,7 +1968,7 @@ func (s *TestSuite) TestVote() {
|
|||
expErr: true,
|
||||
postRun: func(sdkCtx sdk.Context) {},
|
||||
},
|
||||
"on timeout": {
|
||||
"on voting period end": {
|
||||
req: &group.MsgVote{
|
||||
ProposalId: myProposalID,
|
||||
Voter: addr4.String(),
|
||||
|
@ -1968,6 +1989,7 @@ func (s *TestSuite) TestVote() {
|
|||
ProposalId: myProposalID,
|
||||
Voter: addr3.String(),
|
||||
Option: group.VOTE_OPTION_YES,
|
||||
Exec: 1, // Execute to close the proposal.
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
},
|
||||
|
@ -2019,7 +2041,9 @@ func (s *TestSuite) TestVote() {
|
|||
groupPolicy,
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "1",
|
||||
Timeout: time.Second,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
},
|
||||
},
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
@ -2093,16 +2117,26 @@ func (s *TestSuite) TestVote() {
|
|||
s.Assert().Equal(spec.req.Metadata, votesByVoter[0].Metadata)
|
||||
s.Assert().Equal(s.blockTime, votesByVoter[0].SubmitTime)
|
||||
|
||||
// and proposal is updated
|
||||
proposalRes, err := s.keeper.Proposal(ctx, &group.QueryProposalRequest{
|
||||
ProposalId: spec.req.ProposalId,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
proposal := proposalRes.Proposal
|
||||
s.Assert().Equal(spec.expFinalTallyResult, proposal.FinalTallyResult)
|
||||
if spec.isFinal {
|
||||
s.Assert().Equal(spec.expTallyResult, proposal.FinalTallyResult)
|
||||
s.Assert().Equal(spec.expResult, proposal.Result)
|
||||
s.Assert().Equal(spec.expProposalStatus, proposal.Status)
|
||||
s.Assert().Equal(spec.expExecutorResult, proposal.ExecutorResult)
|
||||
} else {
|
||||
s.Assert().Equal(group.DefaultTallyResult(), proposal.FinalTallyResult) // Make sure proposal isn't mutated.
|
||||
|
||||
// do a round of tallying
|
||||
tallyResult, err := s.keeper.Tally(sdkCtx, *proposal, myGroupID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Assert().Equal(spec.expTallyResult, tallyResult)
|
||||
}
|
||||
|
||||
spec.postRun(sdkCtx)
|
||||
})
|
||||
|
@ -2370,6 +2404,7 @@ func createGroupAndGroupPolicy(
|
|||
policy := group.NewThresholdDecisionPolicy(
|
||||
"1",
|
||||
time.Second,
|
||||
0,
|
||||
)
|
||||
err = groupPolicy.SetDecisionPolicy(policy)
|
||||
s.Require().NoError(err)
|
||||
|
|
|
@ -467,16 +467,11 @@ func (k Keeper) SubmitProposal(goCtx context.Context, req *group.MsgSubmitPropos
|
|||
}
|
||||
|
||||
// Prevent proposal that can not succeed.
|
||||
err = policy.Validate(g)
|
||||
err = policy.Validate(g, k.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Define proposal timout.
|
||||
// The voting window begins as soon as the proposal is submitted.
|
||||
timeout := policy.GetTimeout()
|
||||
window := timeout
|
||||
|
||||
m := &group.Proposal{
|
||||
Id: k.proposalTable.Sequence().PeekNextVal(ctx.KVStore(k.key)),
|
||||
Address: req.Address,
|
||||
|
@ -488,14 +483,10 @@ func (k Keeper) SubmitProposal(goCtx context.Context, req *group.MsgSubmitPropos
|
|||
Result: group.PROPOSAL_RESULT_UNFINALIZED,
|
||||
Status: group.PROPOSAL_STATUS_SUBMITTED,
|
||||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
Timeout: ctx.BlockTime().Add(window),
|
||||
FinalTallyResult: group.TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "0",
|
||||
AbstainCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
},
|
||||
VotingPeriodEnd: ctx.BlockTime().Add(policy.GetVotingPeriod()), // The voting window begins as soon as the proposal is submitted.
|
||||
FinalTallyResult: group.DefaultTallyResult(),
|
||||
}
|
||||
|
||||
if err := m.SetMsgs(msgs); err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "create proposal")
|
||||
}
|
||||
|
@ -524,6 +515,7 @@ func (k Keeper) SubmitProposal(goCtx context.Context, req *group.MsgSubmitPropos
|
|||
return &group.MsgSubmitProposalResponse{ProposalId: id}, sdkerrors.Wrap(err, "The proposal was created but failed on vote")
|
||||
}
|
||||
}
|
||||
|
||||
// Then try to execute the proposal
|
||||
_, err = k.Exec(sdk.WrapSDKContext(ctx), &group.MsgExec{
|
||||
ProposalId: id,
|
||||
|
@ -619,15 +611,7 @@ func (k Keeper) Vote(goCtx context.Context, req *group.MsgVote) (*group.MsgVoteR
|
|||
if proposal.Status != group.PROPOSAL_STATUS_SUBMITTED {
|
||||
return nil, sdkerrors.Wrap(errors.ErrInvalid, "proposal not open for voting")
|
||||
}
|
||||
proposalTimeout, err := gogotypes.TimestampProto(proposal.Timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
votingPeriodEnd, err := gogotypes.TimestampFromProto(proposalTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if votingPeriodEnd.Before(ctx.BlockTime()) || votingPeriodEnd.Equal(ctx.BlockTime()) {
|
||||
if ctx.BlockTime().After(proposal.VotingPeriodEnd) {
|
||||
return nil, sdkerrors.Wrap(errors.ErrExpired, "voting period has ended already")
|
||||
}
|
||||
|
||||
|
@ -663,9 +647,6 @@ func (k Keeper) Vote(goCtx context.Context, req *group.MsgVote) (*group.MsgVoteR
|
|||
Metadata: metadata,
|
||||
SubmitTime: ctx.BlockTime(),
|
||||
}
|
||||
if err := proposal.FinalTallyResult.Add(newVote, voter.Member.Weight); err != nil {
|
||||
return nil, sdkerrors.Wrap(err, "add new vote")
|
||||
}
|
||||
|
||||
// The ORM will return an error if the vote already exists,
|
||||
// making sure than a voter hasn't already voted.
|
||||
|
@ -673,15 +654,6 @@ func (k Keeper) Vote(goCtx context.Context, req *group.MsgVote) (*group.MsgVoteR
|
|||
return nil, sdkerrors.Wrap(err, "store vote")
|
||||
}
|
||||
|
||||
// Run tally with new votes to close early.
|
||||
if err := doTally(ctx, &proposal, electorate, policyInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = k.proposalTable.Update(ctx.KVStore(k.key), id, &proposal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.EventManager().EmitTypedEvent(&group.EventVote{ProposalId: id})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -701,8 +673,9 @@ func (k Keeper) Vote(goCtx context.Context, req *group.MsgVote) (*group.MsgVoteR
|
|||
return &group.MsgVoteResponse{}, nil
|
||||
}
|
||||
|
||||
// doTally updates the proposal status and tally if necessary based on the group policy's decision policy.
|
||||
func doTally(ctx sdk.Context, p *group.Proposal, electorate group.GroupInfo, policyInfo group.GroupPolicyInfo) error {
|
||||
// doTallyAndUpdate performs a tally, and updates the proposal's
|
||||
// `FinalTallyResult` field only if the tally is final.
|
||||
func (k Keeper) doTallyAndUpdate(ctx sdk.Context, p *group.Proposal, electorate group.GroupInfo, policyInfo group.GroupPolicyInfo) error {
|
||||
policy := policyInfo.GetDecisionPolicy()
|
||||
pSubmittedAt, err := gogotypes.TimestampProto(p.SubmitTime)
|
||||
if err != nil {
|
||||
|
@ -712,16 +685,25 @@ func doTally(ctx sdk.Context, p *group.Proposal, electorate group.GroupInfo, pol
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch result, err := policy.Allow(p.FinalTallyResult, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); {
|
||||
|
||||
tallyResult, err := k.Tally(ctx, *p, policyInfo.GroupId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch result, err := policy.Allow(tallyResult, electorate.TotalWeight, ctx.BlockTime().Sub(submittedAt)); {
|
||||
case err != nil:
|
||||
return sdkerrors.Wrap(err, "policy execution")
|
||||
case result.Allow && result.Final:
|
||||
p.FinalTallyResult = tallyResult
|
||||
p.Result = group.PROPOSAL_RESULT_ACCEPTED
|
||||
p.Status = group.PROPOSAL_STATUS_CLOSED
|
||||
case !result.Allow && result.Final:
|
||||
p.FinalTallyResult = tallyResult
|
||||
p.Result = group.PROPOSAL_RESULT_REJECTED
|
||||
p.Status = group.PROPOSAL_STATUS_CLOSED
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -770,7 +752,8 @@ func (k Keeper) Exec(goCtx context.Context, req *group.MsgExec) (*group.MsgExecR
|
|||
proposal.Status = group.PROPOSAL_STATUS_ABORTED
|
||||
return storeUpdates()
|
||||
}
|
||||
if err := doTally(ctx, &proposal, electorate, policyInfo); err != nil {
|
||||
|
||||
if err := k.doTallyAndUpdate(ctx, &proposal, electorate, policyInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,16 @@ import (
|
|||
// doExecuteMsgs routes the messages to the registered handlers. Messages are limited to those that require no authZ or
|
||||
// by the account of group policy only. Otherwise this gives access to other peoples accounts as the sdk ant handler is bypassed
|
||||
func (s Keeper) doExecuteMsgs(ctx sdk.Context, router *authmiddleware.MsgServiceRouter, proposal group.Proposal, groupPolicyAcc sdk.AccAddress) ([]sdk.Result, error) {
|
||||
// Ensure it's not too late to execute the messages.
|
||||
// After https://github.com/cosmos/cosmos-sdk/issues/11245, proposals should
|
||||
// be pruned automatically, so this function should not even be called, as
|
||||
// the proposal doesn't exist in state. For sanity check, we can still keep
|
||||
// this simple and cheap check.
|
||||
expiryDate := proposal.VotingPeriodEnd.Add(s.config.MaxExecutionPeriod)
|
||||
if expiryDate.Before(ctx.BlockTime()) {
|
||||
return nil, grouperrors.ErrExpired.Wrapf("proposal expired on %s", expiryDate)
|
||||
}
|
||||
|
||||
msgs := proposal.GetMsgs()
|
||||
|
||||
results := make([]sdk.Result, len(msgs))
|
||||
|
|
|
@ -367,7 +367,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
"invalid admin address",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
admin := "admin"
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -385,7 +385,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid member address",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: "invalid_address",
|
||||
|
@ -403,7 +403,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"negative member's weight not allowed",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -421,7 +421,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"zero member's weight not allowed",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -439,7 +439,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"duplicate member not allowed",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -462,7 +462,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid threshold policy",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -480,7 +480,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"valid test case with single member",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -498,7 +498,7 @@ func TestMsgCreateGroupWithPolicy(t *testing.T) {
|
|||
{
|
||||
"valid test case with multiple members",
|
||||
func() *group.MsgCreateGroupWithPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
members := []group.Member{
|
||||
{
|
||||
Address: member1.String(),
|
||||
|
@ -566,7 +566,7 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid threshold policy",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -575,9 +575,53 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
"expected a positive decimal",
|
||||
},
|
||||
{
|
||||
"valid test case",
|
||||
"invalid voting period",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Duration(0), 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
},
|
||||
true,
|
||||
"expected a positive decimal",
|
||||
},
|
||||
{
|
||||
"invalid execution period",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("-1", time.Minute, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
},
|
||||
true,
|
||||
"expected a positive decimal",
|
||||
},
|
||||
{
|
||||
"valid test case, only voting period",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
},
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"valid test case, voting and execution, empty min exec period",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
},
|
||||
false,
|
||||
"",
|
||||
},
|
||||
{
|
||||
"valid test case, voting and execution, non-empty min exec period",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
policy := group.NewThresholdDecisionPolicy("1", time.Second, time.Minute)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", policy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -588,7 +632,7 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid percentage decision policy with zero value",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("0", time.Second)
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("0", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", percentagePolicy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -599,7 +643,7 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid percentage decision policy with negative value",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("-0.2", time.Second)
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("-0.2", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", percentagePolicy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -610,7 +654,7 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
{
|
||||
"invalid percentage decision policy with value greater than 1",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("2", time.Second)
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("2", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", percentagePolicy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -621,7 +665,7 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
{
|
||||
"valid test case with percentage decision policy",
|
||||
func() *group.MsgCreateGroupPolicy {
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("0.5", time.Second)
|
||||
percentagePolicy := group.NewPercentageDecisionPolicy("0.5", time.Second, 0)
|
||||
req, err := group.NewMsgCreateGroupPolicy(admin, 1, "metadata", percentagePolicy)
|
||||
require.NoError(t, err)
|
||||
return req
|
||||
|
@ -647,23 +691,23 @@ func TestMsgCreateGroupPolicy(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMsgUpdateGroupPolicyDecisionPolicy(t *testing.T) {
|
||||
validPolicy := group.NewThresholdDecisionPolicy("1", time.Second)
|
||||
validPolicy := group.NewThresholdDecisionPolicy("1", time.Second, 0)
|
||||
msg1, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(admin, member1, validPolicy)
|
||||
require.NoError(t, err)
|
||||
|
||||
invalidPolicy := group.NewThresholdDecisionPolicy("-1", time.Second)
|
||||
invalidPolicy := group.NewThresholdDecisionPolicy("-1", time.Second, 0)
|
||||
msg2, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(admin, member2, invalidPolicy)
|
||||
require.NoError(t, err)
|
||||
|
||||
validPercentagePolicy := group.NewPercentageDecisionPolicy("0.7", time.Second)
|
||||
validPercentagePolicy := group.NewPercentageDecisionPolicy("0.7", time.Second, 0)
|
||||
msg3, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(admin, member3, validPercentagePolicy)
|
||||
require.NoError(t, err)
|
||||
|
||||
invalidPercentagePolicy := group.NewPercentageDecisionPolicy("-0.1", time.Second)
|
||||
invalidPercentagePolicy := group.NewPercentageDecisionPolicy("-0.1", time.Second, 0)
|
||||
msg4, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(admin, member4, invalidPercentagePolicy)
|
||||
require.NoError(t, err)
|
||||
|
||||
invalidPercentagePolicy2 := group.NewPercentageDecisionPolicy("2", time.Second)
|
||||
invalidPercentagePolicy2 := group.NewPercentageDecisionPolicy("2", time.Second, 0)
|
||||
msg5, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(admin, member5, invalidPercentagePolicy2)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ func getGroupPolicies(r *rand.Rand, simState *module.SimulationState) []*group.G
|
|||
groupPolicies := make([]*group.GroupPolicyInfo, 3)
|
||||
for i := 0; i < 3; i++ {
|
||||
acc, _ := simtypes.RandomAcc(r, simState.Accounts)
|
||||
any, err := codectypes.NewAnyWithValue(group.NewThresholdDecisionPolicy("10", time.Second*time.Duration(1)))
|
||||
any, err := codectypes.NewAnyWithValue(group.NewThresholdDecisionPolicy("10", time.Second, 0))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ func getProposals(r *rand.Rand, simState *module.SimulationState) []*group.Propo
|
|||
ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN,
|
||||
Metadata: simtypes.RandStringOfLength(r, 50),
|
||||
SubmitTime: submittedAt,
|
||||
Timeout: timeout,
|
||||
VotingPeriodEnd: timeout,
|
||||
}
|
||||
err := proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{
|
||||
FromAddress: fromAddr,
|
||||
|
|
|
@ -285,7 +285,9 @@ func SimulateMsgCreateGroupWithPolicy(ak group.AccountKeeper, bk group.BankKeepe
|
|||
members := genGroupMembers(r, accounts)
|
||||
decisionPolicy := &group.ThresholdDecisionPolicy{
|
||||
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
|
||||
Timeout: time.Second * time.Duration(30*24*60*60),
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * time.Duration(30*24*60*60),
|
||||
},
|
||||
}
|
||||
|
||||
msg := &group.MsgCreateGroupWithPolicy{
|
||||
|
@ -349,7 +351,9 @@ func SimulateMsgCreateGroupPolicy(ak group.AccountKeeper, bk group.BankKeeper, k
|
|||
simtypes.RandStringOfLength(r, 10),
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
|
||||
Timeout: time.Second * time.Duration(30*24*60*60),
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * time.Duration(30*24*60*60),
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -400,7 +404,7 @@ func SimulateMsgSubmitProposal(ak group.AccountKeeper, bk group.BankKeeper, k ke
|
|||
|
||||
// Return a no-op if we know the proposal cannot be created
|
||||
policy := groupPolicy.GetDecisionPolicy()
|
||||
err = policy.Validate(*g)
|
||||
err = policy.Validate(*g, group.DefaultConfig())
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgSubmitProposal, ""), nil, nil
|
||||
}
|
||||
|
@ -721,7 +725,9 @@ func SimulateMsgUpdateGroupPolicyDecisionPolicy(ak group.AccountKeeper,
|
|||
|
||||
msg, err := group.NewMsgUpdateGroupPolicyDecisionPolicyRequest(acc.Address, groupPolicyBech32, &group.ThresholdDecisionPolicy{
|
||||
Threshold: fmt.Sprintf("%d", simtypes.RandIntBetween(r, 1, 10)),
|
||||
Timeout: time.Second * time.Duration(simtypes.RandIntBetween(r, 100, 1000)),
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * time.Duration(simtypes.RandIntBetween(r, 100, 1000)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgUpdateGroupPolicyDecisionPolicy, err.Error()), nil, err
|
||||
|
@ -820,7 +826,7 @@ func SimulateMsgWithdrawProposal(ak group.AccountKeeper,
|
|||
ctx := sdk.WrapSDKContext(sdkCtx)
|
||||
|
||||
policy := groupPolicy.GetDecisionPolicy()
|
||||
err = policy.Validate(*g)
|
||||
err = policy.Validate(*g, group.DefaultConfig())
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgWithdrawProposal, err.Error()), nil, nil
|
||||
}
|
||||
|
@ -840,7 +846,7 @@ func SimulateMsgWithdrawProposal(ak group.AccountKeeper,
|
|||
|
||||
for _, p := range proposals {
|
||||
if p.Status == group.PROPOSAL_STATUS_SUBMITTED {
|
||||
timeout := p.Timeout
|
||||
timeout := p.VotingPeriodEnd
|
||||
proposal = p
|
||||
proposalID = int(p.Id)
|
||||
if timeout.Before(sdkCtx.BlockTime()) || timeout.Equal(sdkCtx.BlockTime()) {
|
||||
|
@ -959,7 +965,7 @@ func SimulateMsgVote(ak group.AccountKeeper,
|
|||
|
||||
for _, p := range proposals {
|
||||
if p.Status == group.PROPOSAL_STATUS_SUBMITTED {
|
||||
timeout := p.Timeout
|
||||
timeout := p.VotingPeriodEnd
|
||||
proposal = p
|
||||
proposalID = int(p.Id)
|
||||
if timeout.Before(sdkCtx.BlockTime()) || timeout.Equal(sdkCtx.BlockTime()) {
|
||||
|
@ -1032,27 +1038,27 @@ func SimulateMsgExec(ak group.AccountKeeper,
|
|||
r *rand.Rand, app *baseapp.BaseApp, sdkCtx sdk.Context, accounts []simtypes.Account, chainID string) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
|
||||
_, groupPolicy, acc, account, err := randomGroupPolicy(r, k, ak, sdkCtx, accounts)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, ""), nil, err
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, ""), nil, err
|
||||
}
|
||||
if groupPolicy == nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no group policy found"), nil, nil
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, "no group policy found"), nil, nil
|
||||
}
|
||||
groupPolicyAddr := groupPolicy.Address
|
||||
|
||||
spendableCoins := bk.SpendableCoins(sdkCtx, account.GetAddress())
|
||||
fees, err := simtypes.RandomFees(r, sdkCtx, spendableCoins)
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "fee error"), nil, err
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, "fee error"), nil, err
|
||||
}
|
||||
|
||||
ctx := sdk.WrapSDKContext(sdkCtx)
|
||||
proposalsResult, err := k.ProposalsByGroupPolicy(ctx, &group.QueryProposalsByGroupPolicyRequest{Address: groupPolicyAddr})
|
||||
if err != nil {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "fail to query group info"), nil, err
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, "fail to query group info"), nil, err
|
||||
}
|
||||
proposals := proposalsResult.GetProposals()
|
||||
if len(proposals) == 0 {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no proposals found"), nil, nil
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, "no proposals found"), nil, nil
|
||||
}
|
||||
|
||||
proposalID := -1
|
||||
|
@ -1066,7 +1072,7 @@ func SimulateMsgExec(ak group.AccountKeeper,
|
|||
|
||||
// return no-op if no proposal found
|
||||
if proposalID == -1 {
|
||||
return simtypes.NoOpMsg(group.ModuleName, TypeMsgExec, "no proposals found"), nil, nil
|
||||
return simtypes.NoOpMsg(TypeMsgExec, TypeMsgExec, "no proposals found"), nil, nil
|
||||
}
|
||||
|
||||
msg := group.MsgExec{
|
||||
|
|
|
@ -221,7 +221,7 @@ func (suite *SimTestSuite) TestSimulateSubmitProposal() {
|
|||
Admin: acc.Address.String(),
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -275,7 +275,7 @@ func (suite *SimTestSuite) TestWithdrawProposal() {
|
|||
Admin: addr,
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -342,7 +342,7 @@ func (suite *SimTestSuite) TestSimulateVote() {
|
|||
GroupId: groupRes.GroupId,
|
||||
Metadata: "",
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -408,7 +408,7 @@ func (suite *SimTestSuite) TestSimulateExec() {
|
|||
Admin: addr,
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -430,6 +430,7 @@ func (suite *SimTestSuite) TestSimulateExec() {
|
|||
ProposalId: proposalRes.ProposalId,
|
||||
Voter: addr,
|
||||
Option: group.VOTE_OPTION_YES,
|
||||
Exec: 1,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
|
@ -607,7 +608,7 @@ func (suite *SimTestSuite) TestSimulateUpdateGroupPolicyAdmin() {
|
|||
Admin: acc.Address.String(),
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -660,7 +661,7 @@ func (suite *SimTestSuite) TestSimulateUpdateGroupPolicyDecisionPolicy() {
|
|||
Admin: acc.Address.String(),
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
@ -713,7 +714,7 @@ func (suite *SimTestSuite) TestSimulateUpdateGroupPolicyMetadata() {
|
|||
Admin: acc.Address.String(),
|
||||
GroupId: groupRes.GroupId,
|
||||
}
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour))
|
||||
err = accountReq.SetDecisionPolicy(group.NewThresholdDecisionPolicy("1", time.Hour, 0))
|
||||
suite.Require().NoError(err)
|
||||
groupPolicyRes, err := suite.app.GroupKeeper.CreateGroupPolicy(ctx, accountReq)
|
||||
suite.Require().NoError(err)
|
||||
|
|
|
@ -24,18 +24,28 @@ type DecisionPolicyResult struct {
|
|||
type DecisionPolicy interface {
|
||||
codec.ProtoMarshaler
|
||||
|
||||
// GetVotingPeriod returns the duration after proposal submission where
|
||||
// votes are accepted.
|
||||
GetVotingPeriod() time.Duration
|
||||
// Allow defines policy-specific logic to allow a proposal to pass or not,
|
||||
// based on its tally result, the group's total power and the time since
|
||||
// the proposal was submitted.
|
||||
Allow(tallyResult TallyResult, totalPower string, sinceSubmission time.Duration) (DecisionPolicyResult, error)
|
||||
|
||||
ValidateBasic() error
|
||||
GetTimeout() time.Duration
|
||||
Allow(tallyResult TallyResult, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error)
|
||||
Validate(g GroupInfo) error
|
||||
Validate(g GroupInfo, config Config) error
|
||||
}
|
||||
|
||||
// Implements DecisionPolicy Interface
|
||||
var _ DecisionPolicy = &ThresholdDecisionPolicy{}
|
||||
|
||||
// NewThresholdDecisionPolicy creates a threshold DecisionPolicy
|
||||
func NewThresholdDecisionPolicy(threshold string, timeout time.Duration) DecisionPolicy {
|
||||
return &ThresholdDecisionPolicy{threshold, timeout}
|
||||
func NewThresholdDecisionPolicy(threshold string, votingPeriod time.Duration, minExecutionPeriod time.Duration) DecisionPolicy {
|
||||
return &ThresholdDecisionPolicy{threshold, &DecisionPolicyWindows{votingPeriod, minExecutionPeriod}}
|
||||
}
|
||||
|
||||
func (p ThresholdDecisionPolicy) GetVotingPeriod() time.Duration {
|
||||
return p.Windows.VotingPeriod
|
||||
}
|
||||
|
||||
func (p ThresholdDecisionPolicy) ValidateBasic() error {
|
||||
|
@ -43,19 +53,17 @@ func (p ThresholdDecisionPolicy) ValidateBasic() error {
|
|||
return sdkerrors.Wrap(err, "threshold")
|
||||
}
|
||||
|
||||
timeout := p.Timeout
|
||||
|
||||
if timeout <= time.Nanosecond {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "timeout")
|
||||
if p.Windows == nil || p.Windows.VotingPeriod == 0 {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "voting period cannot be zero")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Allow allows a proposal to pass when the tally of yes votes equals or exceeds the threshold before the timeout.
|
||||
func (p ThresholdDecisionPolicy) Allow(tallyResult TallyResult, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) {
|
||||
timeout := p.Timeout
|
||||
if timeout <= votingDuration {
|
||||
return DecisionPolicyResult{Allow: false, Final: true}, nil
|
||||
func (p ThresholdDecisionPolicy) Allow(tallyResult TallyResult, totalPower string, sinceSubmission time.Duration) (DecisionPolicyResult, error) {
|
||||
if sinceSubmission < p.Windows.MinExecutionPeriod {
|
||||
return DecisionPolicyResult{}, errors.ErrUnauthorized.Wrapf("must wait %s after submission before execution, currently at %s", p.Windows.MinExecutionPeriod, sinceSubmission)
|
||||
}
|
||||
|
||||
threshold, err := math.NewPositiveDecFromString(p.Threshold)
|
||||
|
@ -93,7 +101,7 @@ func (p ThresholdDecisionPolicy) Allow(tallyResult TallyResult, totalPower strin
|
|||
}
|
||||
|
||||
// Validate returns an error if policy threshold is greater than the total group weight
|
||||
func (p *ThresholdDecisionPolicy) Validate(g GroupInfo) error {
|
||||
func (p *ThresholdDecisionPolicy) Validate(g GroupInfo, config Config) error {
|
||||
threshold, err := math.NewPositiveDecFromString(p.Threshold)
|
||||
if err != nil {
|
||||
return sdkerrors.Wrap(err, "threshold")
|
||||
|
@ -105,6 +113,9 @@ func (p *ThresholdDecisionPolicy) Validate(g GroupInfo) error {
|
|||
if threshold.Cmp(totalWeight) > 0 {
|
||||
return sdkerrors.Wrapf(errors.ErrInvalid, "policy threshold %s should not be greater than the total group weight %s", p.Threshold, g.TotalWeight)
|
||||
}
|
||||
if p.Windows.MinExecutionPeriod > p.Windows.VotingPeriod+config.MaxExecutionPeriod {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "min_execution_period should be smaller than voting_period + max_execution_period")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -112,8 +123,12 @@ func (p *ThresholdDecisionPolicy) Validate(g GroupInfo) error {
|
|||
var _ DecisionPolicy = &PercentageDecisionPolicy{}
|
||||
|
||||
// NewPercentageDecisionPolicy creates a new percentage DecisionPolicy
|
||||
func NewPercentageDecisionPolicy(percentage string, timeout time.Duration) DecisionPolicy {
|
||||
return &PercentageDecisionPolicy{percentage, timeout}
|
||||
func NewPercentageDecisionPolicy(percentage string, votingPeriod time.Duration, executionPeriod time.Duration) DecisionPolicy {
|
||||
return &PercentageDecisionPolicy{percentage, &DecisionPolicyWindows{votingPeriod, executionPeriod}}
|
||||
}
|
||||
|
||||
func (p PercentageDecisionPolicy) GetVotingPeriod() time.Duration {
|
||||
return p.Windows.VotingPeriod
|
||||
}
|
||||
|
||||
func (p PercentageDecisionPolicy) ValidateBasic() error {
|
||||
|
@ -125,22 +140,24 @@ func (p PercentageDecisionPolicy) ValidateBasic() error {
|
|||
return sdkerrors.Wrap(errors.ErrInvalid, "percentage must be > 0 and <= 1")
|
||||
}
|
||||
|
||||
timeout := p.Timeout
|
||||
if timeout <= time.Nanosecond {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "timeout")
|
||||
if p.Windows == nil || p.Windows.VotingPeriod == 0 {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "voting period cannot be 0")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PercentageDecisionPolicy) Validate(g GroupInfo, config Config) error {
|
||||
if p.Windows.MinExecutionPeriod > p.Windows.VotingPeriod+config.MaxExecutionPeriod {
|
||||
return sdkerrors.Wrap(errors.ErrInvalid, "min_execution_period should be smaller than voting_period + max_execution_period")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PercentageDecisionPolicy) Validate(g GroupInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Allow allows a proposal to pass when the tally of yes votes equals or exceeds the percentage threshold before the timeout.
|
||||
func (p PercentageDecisionPolicy) Allow(tally TallyResult, totalPower string, votingDuration time.Duration) (DecisionPolicyResult, error) {
|
||||
timeout := p.Timeout
|
||||
if timeout <= votingDuration {
|
||||
return DecisionPolicyResult{Allow: false, Final: true}, nil
|
||||
func (p PercentageDecisionPolicy) Allow(tally TallyResult, totalPower string, sinceSubmission time.Duration) (DecisionPolicyResult, error) {
|
||||
if sinceSubmission < p.Windows.MinExecutionPeriod {
|
||||
return DecisionPolicyResult{}, errors.ErrUnauthorized.Wrapf("must wait %s after submission before execution, currently at %s", p.Windows.MinExecutionPeriod, sinceSubmission)
|
||||
}
|
||||
|
||||
percentage, err := math.NewPositiveDecFromString(p.Percentage)
|
||||
|
@ -535,6 +552,16 @@ func (t TallyResult) TotalCounts() (math.Dec, error) {
|
|||
return totalCounts, nil
|
||||
}
|
||||
|
||||
// DefaultTallyResult returns a TallyResult with all counts set to 0.
|
||||
func DefaultTallyResult() TallyResult {
|
||||
return TallyResult{
|
||||
YesCount: "0",
|
||||
NoCount: "0",
|
||||
NoWithVetoCount: "0",
|
||||
AbstainCount: "0",
|
||||
}
|
||||
}
|
||||
|
||||
// VoteOptionFromString returns a VoteOption from a string. It returns an error
|
||||
// if the string is invalid.
|
||||
func VoteOptionFromString(str string) (VoteOption, error) {
|
||||
|
|
|
@ -307,9 +307,8 @@ func (m *Members) GetMembers() []Member {
|
|||
type ThresholdDecisionPolicy struct {
|
||||
// threshold is the minimum weighted sum of yes votes that must be met or exceeded for a proposal to succeed.
|
||||
Threshold string `protobuf:"bytes,1,opt,name=threshold,proto3" json:"threshold,omitempty"`
|
||||
// timeout is the duration from submission of a proposal to the end of voting period
|
||||
// Within this times votes and exec messages can be submitted.
|
||||
Timeout time.Duration `protobuf:"bytes,2,opt,name=timeout,proto3,stdduration" json:"timeout"`
|
||||
// windows defines the different windows for voting and execution.
|
||||
Windows *DecisionPolicyWindows `protobuf:"bytes,2,opt,name=windows,proto3" json:"windows,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ThresholdDecisionPolicy) Reset() { *m = ThresholdDecisionPolicy{} }
|
||||
|
@ -352,20 +351,19 @@ func (m *ThresholdDecisionPolicy) GetThreshold() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *ThresholdDecisionPolicy) GetTimeout() time.Duration {
|
||||
func (m *ThresholdDecisionPolicy) GetWindows() *DecisionPolicyWindows {
|
||||
if m != nil {
|
||||
return m.Timeout
|
||||
return m.Windows
|
||||
}
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// PercentageDecisionPolicy implements the DecisionPolicy interface
|
||||
type PercentageDecisionPolicy struct {
|
||||
// percentage is the minimum percentage the weighted sum of yes votes must meet for a proposal to succeed.
|
||||
Percentage string `protobuf:"bytes,1,opt,name=percentage,proto3" json:"percentage,omitempty"`
|
||||
// timeout is the duration from submission of a proposal to the end of voting period
|
||||
// Within these times votes and exec messages can be submitted.
|
||||
Timeout time.Duration `protobuf:"bytes,2,opt,name=timeout,proto3,stdduration" json:"timeout"`
|
||||
// windows defines the different windows for voting and execution.
|
||||
Windows *DecisionPolicyWindows `protobuf:"bytes,2,opt,name=windows,proto3" json:"windows,omitempty"`
|
||||
}
|
||||
|
||||
func (m *PercentageDecisionPolicy) Reset() { *m = PercentageDecisionPolicy{} }
|
||||
|
@ -408,9 +406,75 @@ func (m *PercentageDecisionPolicy) GetPercentage() string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (m *PercentageDecisionPolicy) GetTimeout() time.Duration {
|
||||
func (m *PercentageDecisionPolicy) GetWindows() *DecisionPolicyWindows {
|
||||
if m != nil {
|
||||
return m.Timeout
|
||||
return m.Windows
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecisionPolicyWindows defines the different windows for voting and execution.
|
||||
type DecisionPolicyWindows struct {
|
||||
// voting_period is the duration from submission of a proposal to the end of voting period
|
||||
// Within this times votes can be submitted with MsgVote.
|
||||
VotingPeriod time.Duration `protobuf:"bytes,1,opt,name=voting_period,json=votingPeriod,proto3,stdduration" json:"voting_period"`
|
||||
// min_execution_period is the minimum duration after the proposal submission
|
||||
// where members can start sending MsgExec. This means that the window for
|
||||
// sending a MsgExec transaction is:
|
||||
// `[ submission + min_execution_period ; submission + voting_period + max_execution_period]`
|
||||
// where max_execution_period is a app-specific config, defined in the keeper.
|
||||
// If not set, min_execution_period will default to 0.
|
||||
//
|
||||
// Please make sure to set a `min_execution_period` that is smaller than
|
||||
// `voting_period + max_execution_period`, or else the above execution window
|
||||
// is empty, meaning that all proposals created with this decision policy
|
||||
// won't be able to be executed.
|
||||
MinExecutionPeriod time.Duration `protobuf:"bytes,2,opt,name=min_execution_period,json=minExecutionPeriod,proto3,stdduration" json:"min_execution_period"`
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) Reset() { *m = DecisionPolicyWindows{} }
|
||||
func (m *DecisionPolicyWindows) String() string { return proto.CompactTextString(m) }
|
||||
func (*DecisionPolicyWindows) ProtoMessage() {}
|
||||
func (*DecisionPolicyWindows) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{4}
|
||||
}
|
||||
func (m *DecisionPolicyWindows) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *DecisionPolicyWindows) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_DecisionPolicyWindows.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *DecisionPolicyWindows) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DecisionPolicyWindows.Merge(m, src)
|
||||
}
|
||||
func (m *DecisionPolicyWindows) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *DecisionPolicyWindows) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DecisionPolicyWindows.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DecisionPolicyWindows proto.InternalMessageInfo
|
||||
|
||||
func (m *DecisionPolicyWindows) GetVotingPeriod() time.Duration {
|
||||
if m != nil {
|
||||
return m.VotingPeriod
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) GetMinExecutionPeriod() time.Duration {
|
||||
if m != nil {
|
||||
return m.MinExecutionPeriod
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
@ -438,7 +502,7 @@ func (m *GroupInfo) Reset() { *m = GroupInfo{} }
|
|||
func (m *GroupInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupInfo) ProtoMessage() {}
|
||||
func (*GroupInfo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{4}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{5}
|
||||
}
|
||||
func (m *GroupInfo) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -521,7 +585,7 @@ func (m *GroupMember) Reset() { *m = GroupMember{} }
|
|||
func (m *GroupMember) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupMember) ProtoMessage() {}
|
||||
func (*GroupMember) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{5}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{6}
|
||||
}
|
||||
func (m *GroupMember) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -587,7 +651,7 @@ func (m *GroupPolicyInfo) Reset() { *m = GroupPolicyInfo{} }
|
|||
func (m *GroupPolicyInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*GroupPolicyInfo) ProtoMessage() {}
|
||||
func (*GroupPolicyInfo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{6}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{7}
|
||||
}
|
||||
func (m *GroupPolicyInfo) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -647,11 +711,12 @@ type Proposal struct {
|
|||
// via gRPC, this field is not populated until the proposal's voting period
|
||||
// has ended.
|
||||
FinalTallyResult TallyResult `protobuf:"bytes,10,opt,name=final_tally_result,json=finalTallyResult,proto3" json:"final_tally_result"`
|
||||
// timeout is the timestamp before which both voting and execution must be
|
||||
// done. If this timestamp is passed, then the proposal cannot be executed
|
||||
// anymore and should be considered pending delete. This timestamp is checked
|
||||
// against the block header's timestamp.
|
||||
Timeout time.Time `protobuf:"bytes,11,opt,name=timeout,proto3,stdtime" json:"timeout"`
|
||||
// voting_period_end is the timestamp before which voting must be done.
|
||||
// Unless a successfull MsgExec is called before (to execute a proposal whose
|
||||
// tally is successful before the voting period ends), tallying will be done
|
||||
// at this point, and the `final_tally_result`, as well
|
||||
// as `status` and `result` fields will be accordingly updated.
|
||||
VotingPeriodEnd time.Time `protobuf:"bytes,11,opt,name=voting_period_end,json=votingPeriodEnd,proto3,stdtime" json:"voting_period_end"`
|
||||
// executor_result is the final result based on the votes and election rule. Initial value is NotRun.
|
||||
ExecutorResult ProposalExecutorResult `protobuf:"varint,12,opt,name=executor_result,json=executorResult,proto3,enum=cosmos.group.v1beta1.ProposalExecutorResult" json:"executor_result,omitempty"`
|
||||
// messages is a list of Msgs that will be executed if the proposal passes.
|
||||
|
@ -662,7 +727,7 @@ func (m *Proposal) Reset() { *m = Proposal{} }
|
|||
func (m *Proposal) String() string { return proto.CompactTextString(m) }
|
||||
func (*Proposal) ProtoMessage() {}
|
||||
func (*Proposal) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{7}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{8}
|
||||
}
|
||||
func (m *Proposal) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -707,7 +772,7 @@ func (m *TallyResult) Reset() { *m = TallyResult{} }
|
|||
func (m *TallyResult) String() string { return proto.CompactTextString(m) }
|
||||
func (*TallyResult) ProtoMessage() {}
|
||||
func (*TallyResult) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{8}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{9}
|
||||
}
|
||||
func (m *TallyResult) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -754,7 +819,7 @@ func (m *Vote) Reset() { *m = Vote{} }
|
|||
func (m *Vote) String() string { return proto.CompactTextString(m) }
|
||||
func (*Vote) ProtoMessage() {}
|
||||
func (*Vote) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{9}
|
||||
return fileDescriptor_e091dfce5c49c8b6, []int{10}
|
||||
}
|
||||
func (m *Vote) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
|
@ -827,6 +892,7 @@ func init() {
|
|||
proto.RegisterType((*Members)(nil), "cosmos.group.v1beta1.Members")
|
||||
proto.RegisterType((*ThresholdDecisionPolicy)(nil), "cosmos.group.v1beta1.ThresholdDecisionPolicy")
|
||||
proto.RegisterType((*PercentageDecisionPolicy)(nil), "cosmos.group.v1beta1.PercentageDecisionPolicy")
|
||||
proto.RegisterType((*DecisionPolicyWindows)(nil), "cosmos.group.v1beta1.DecisionPolicyWindows")
|
||||
proto.RegisterType((*GroupInfo)(nil), "cosmos.group.v1beta1.GroupInfo")
|
||||
proto.RegisterType((*GroupMember)(nil), "cosmos.group.v1beta1.GroupMember")
|
||||
proto.RegisterType((*GroupPolicyInfo)(nil), "cosmos.group.v1beta1.GroupPolicyInfo")
|
||||
|
@ -838,87 +904,91 @@ func init() {
|
|||
func init() { proto.RegisterFile("cosmos/group/v1beta1/types.proto", fileDescriptor_e091dfce5c49c8b6) }
|
||||
|
||||
var fileDescriptor_e091dfce5c49c8b6 = []byte{
|
||||
// 1274 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4f, 0x6f, 0x1b, 0x45,
|
||||
0x14, 0xf7, 0x3a, 0x8e, 0xff, 0x3c, 0xa7, 0x8e, 0x35, 0x8d, 0xda, 0x4d, 0x1a, 0x6c, 0xd7, 0xe4,
|
||||
0x10, 0x15, 0x6a, 0xb7, 0x01, 0x21, 0x54, 0x95, 0xa2, 0xb5, 0xb3, 0x2d, 0x46, 0xa9, 0x6d, 0x76,
|
||||
0xd7, 0x09, 0xf4, 0xc0, 0x6a, 0xed, 0x9d, 0x3a, 0x2b, 0xec, 0x1d, 0x6b, 0x77, 0x9c, 0xd6, 0xdf,
|
||||
0xa0, 0x42, 0x42, 0xf4, 0xc0, 0x81, 0x0b, 0x52, 0x25, 0xee, 0x48, 0x48, 0x3d, 0xf1, 0x09, 0x2a,
|
||||
0x4e, 0x15, 0x27, 0x4e, 0x80, 0xd2, 0x4b, 0xb9, 0xf3, 0x01, 0xd0, 0xce, 0xcc, 0xfa, 0x5f, 0x5d,
|
||||
0xab, 0xad, 0xe0, 0x64, 0xcf, 0xbc, 0xdf, 0x9b, 0xf7, 0x7b, 0xef, 0xfd, 0xe6, 0x8d, 0x16, 0x0a,
|
||||
0x1d, 0xe2, 0xf7, 0x89, 0x5f, 0xee, 0x7a, 0x64, 0x38, 0x28, 0x9f, 0x5c, 0x6d, 0x63, 0x6a, 0x5d,
|
||||
0x2d, 0xd3, 0xd1, 0x00, 0xfb, 0xa5, 0x81, 0x47, 0x28, 0x41, 0x1b, 0x1c, 0x51, 0x62, 0x88, 0x92,
|
||||
0x40, 0x6c, 0x6d, 0x74, 0x49, 0x97, 0x30, 0x40, 0x39, 0xf8, 0xc7, 0xb1, 0x5b, 0xb9, 0x2e, 0x21,
|
||||
0xdd, 0x1e, 0x2e, 0xb3, 0x55, 0x7b, 0x78, 0xb7, 0x6c, 0x0f, 0x3d, 0x8b, 0x3a, 0xc4, 0x15, 0xf6,
|
||||
0xfc, 0xbc, 0x9d, 0x3a, 0x7d, 0xec, 0x53, 0xab, 0x3f, 0x10, 0x80, 0x4d, 0x1e, 0xcc, 0xe4, 0x27,
|
||||
0x8b, 0xc8, 0xc2, 0x34, 0xef, 0x6b, 0xb9, 0x23, 0x6e, 0x2a, 0xfe, 0x2c, 0x41, 0xfc, 0x36, 0xee,
|
||||
0xb7, 0xb1, 0x87, 0xf6, 0x20, 0x61, 0xd9, 0xb6, 0x87, 0x7d, 0x5f, 0x96, 0x0a, 0xd2, 0x6e, 0xaa,
|
||||
0x22, 0xff, 0xf6, 0xf8, 0x72, 0x98, 0x82, 0xc2, 0x2d, 0x3a, 0xf5, 0x1c, 0xb7, 0xab, 0x85, 0x40,
|
||||
0x74, 0x0e, 0xe2, 0xf7, 0xb0, 0xd3, 0x3d, 0xa6, 0x72, 0x34, 0x70, 0xd1, 0xc4, 0x0a, 0x6d, 0x41,
|
||||
0xb2, 0x8f, 0xa9, 0x65, 0x5b, 0xd4, 0x92, 0x57, 0x98, 0x65, 0xbc, 0x46, 0x1f, 0x43, 0xd2, 0xb2,
|
||||
0x6d, 0x6c, 0x9b, 0x16, 0x95, 0x63, 0x05, 0x69, 0x37, 0xbd, 0xb7, 0x55, 0xe2, 0x04, 0x4b, 0x21,
|
||||
0xc1, 0x92, 0x11, 0x26, 0x57, 0x49, 0x3e, 0xf9, 0x23, 0x1f, 0x79, 0xf8, 0x67, 0x5e, 0x62, 0x41,
|
||||
0xb1, 0xad, 0xd0, 0xe2, 0x2d, 0x48, 0x70, 0xca, 0x3e, 0xba, 0x0e, 0x89, 0x3e, 0xff, 0x2b, 0x4b,
|
||||
0x85, 0x95, 0xdd, 0xf4, 0xde, 0x76, 0x69, 0x51, 0xcd, 0x4b, 0x1c, 0x5f, 0x89, 0x05, 0x87, 0x69,
|
||||
0xa1, 0x4b, 0xf1, 0x6b, 0x09, 0xce, 0x1b, 0xc7, 0x1e, 0xf6, 0x8f, 0x49, 0xcf, 0xde, 0xc7, 0x1d,
|
||||
0xc7, 0x77, 0x88, 0xdb, 0x24, 0x3d, 0xa7, 0x33, 0x42, 0xdb, 0x90, 0xa2, 0xa1, 0x89, 0xd7, 0x43,
|
||||
0x9b, 0x6c, 0xa0, 0x8f, 0x20, 0x11, 0xd4, 0x9f, 0x0c, 0x79, 0xe2, 0xe9, 0xbd, 0xcd, 0x17, 0x52,
|
||||
0xd8, 0x17, 0xfd, 0xe3, 0x19, 0x7c, 0xcf, 0x32, 0x10, 0x3e, 0xd7, 0xd0, 0xaf, 0x8f, 0x2f, 0x67,
|
||||
0x66, 0x03, 0x16, 0xbf, 0x91, 0x40, 0x6e, 0x62, 0xaf, 0x83, 0x5d, 0x6a, 0x75, 0xf1, 0x1c, 0x9b,
|
||||
0x1c, 0xc0, 0x60, 0x6c, 0x13, 0x74, 0xa6, 0x76, 0xfe, 0x0f, 0x3e, 0x7f, 0x4b, 0x90, 0xba, 0x15,
|
||||
0x14, 0xb1, 0xe6, 0xde, 0x25, 0x28, 0x03, 0x51, 0x87, 0xd7, 0x21, 0xa6, 0x45, 0x1d, 0x1b, 0x95,
|
||||
0x60, 0xd5, 0xb2, 0xfb, 0x8e, 0xcb, 0xfb, 0xbe, 0x44, 0x2a, 0x1c, 0xb6, 0x54, 0x10, 0x32, 0x24,
|
||||
0x4e, 0xb0, 0x17, 0x84, 0x66, 0x7a, 0x88, 0x69, 0xe1, 0x12, 0x5d, 0x84, 0x35, 0x4a, 0xa8, 0xd5,
|
||||
0x33, 0x85, 0xc8, 0x56, 0x99, 0x67, 0x9a, 0xed, 0x1d, 0x71, 0xa5, 0x55, 0x01, 0x3a, 0x1e, 0xb6,
|
||||
0x28, 0xd7, 0x53, 0xfc, 0x35, 0xf4, 0x94, 0x12, 0x7e, 0x0a, 0x2d, 0x7e, 0x09, 0x69, 0x96, 0xaa,
|
||||
0xb8, 0x09, 0x9b, 0x90, 0x64, 0xf2, 0x31, 0xc7, 0x29, 0x27, 0xd8, 0xba, 0x66, 0xa3, 0xf7, 0x21,
|
||||
0xce, 0xd5, 0x23, 0xea, 0xbc, 0x54, 0x6f, 0x9a, 0xc0, 0x16, 0x9f, 0x47, 0x61, 0x9d, 0x05, 0xe0,
|
||||
0xb5, 0x65, 0x15, 0x7d, 0x93, 0xeb, 0x36, 0x4d, 0x2c, 0x3a, 0x4b, 0x6c, 0xdc, 0x90, 0x95, 0xd7,
|
||||
0x6f, 0x48, 0xec, 0xe5, 0x0d, 0x59, 0x9d, 0x6d, 0xc8, 0x67, 0xb0, 0x6e, 0x0b, 0x99, 0x98, 0x03,
|
||||
0x96, 0x8b, 0x28, 0xf9, 0xc6, 0x0b, 0x25, 0x57, 0xdc, 0x51, 0x65, 0x81, 0xae, 0xb4, 0x8c, 0x3d,
|
||||
0x2b, 0xed, 0xd9, 0x06, 0x26, 0xde, 0xa8, 0x81, 0xd7, 0x92, 0x0f, 0x1e, 0xe5, 0x23, 0xcf, 0x1f,
|
||||
0xe5, 0xa5, 0xe2, 0xe9, 0x2a, 0x24, 0x9b, 0x1e, 0x19, 0x10, 0xdf, 0xea, 0xbd, 0xa0, 0xda, 0xa9,
|
||||
0x9a, 0x47, 0x5f, 0xb5, 0xe6, 0xcb, 0x94, 0xfb, 0x01, 0xa4, 0x06, 0x2c, 0x56, 0x30, 0x80, 0x62,
|
||||
0x85, 0x95, 0xa5, 0x27, 0x4e, 0xa0, 0x48, 0x85, 0xb4, 0x3f, 0x6c, 0xf7, 0x1d, 0x6a, 0x06, 0x37,
|
||||
0x90, 0x15, 0xf9, 0x55, 0x93, 0x06, 0xee, 0x18, 0x98, 0xd0, 0xdb, 0x70, 0x86, 0xcb, 0x21, 0xec,
|
||||
0x56, 0x9c, 0x65, 0xba, 0xc6, 0x36, 0x0f, 0x45, 0xcb, 0xae, 0xc0, 0x06, 0x07, 0xf1, 0x7e, 0x8d,
|
||||
0xb1, 0x09, 0x86, 0x45, 0xdd, 0x89, 0x2c, 0x43, 0x8f, 0xeb, 0x10, 0xf7, 0xa9, 0x45, 0x87, 0xbe,
|
||||
0x9c, 0x2c, 0x48, 0xbb, 0x99, 0xbd, 0x9d, 0xc5, 0x1a, 0x0f, 0xab, 0xac, 0x33, 0xac, 0x26, 0x7c,
|
||||
0x02, 0x6f, 0x0f, 0xfb, 0xc3, 0x1e, 0x95, 0x53, 0xaf, 0xe2, 0xad, 0x31, 0xac, 0x26, 0x7c, 0x50,
|
||||
0x0b, 0xd0, 0x5d, 0xc7, 0xb5, 0x7a, 0x26, 0xb5, 0x7a, 0xbd, 0x91, 0x29, 0x4e, 0x02, 0x56, 0xa0,
|
||||
0x8b, 0x8b, 0x4f, 0x32, 0x02, 0x24, 0x3f, 0x46, 0x0c, 0xf8, 0x2c, 0x3b, 0x62, 0x6a, 0x1f, 0xdd,
|
||||
0x98, 0xcc, 0xc7, 0xf4, 0xeb, 0x3c, 0x39, 0xc2, 0x09, 0xb5, 0x60, 0x1d, 0xdf, 0xc7, 0x9d, 0x21,
|
||||
0x25, 0x5e, 0xc8, 0x69, 0x8d, 0x65, 0xf7, 0xee, 0xf2, 0xec, 0x54, 0xe1, 0x24, 0xb2, 0xcc, 0xe0,
|
||||
0x99, 0x35, 0xba, 0x12, 0x68, 0xcb, 0xf7, 0xad, 0x2e, 0xf6, 0xe5, 0x33, 0xec, 0xfd, 0x5a, 0x78,
|
||||
0x8f, 0xb4, 0x31, 0xea, 0x5a, 0x2c, 0x10, 0x7a, 0xf1, 0x07, 0x09, 0xd2, 0xd3, 0xe9, 0x5d, 0x80,
|
||||
0xd4, 0x08, 0xfb, 0x66, 0x87, 0x0c, 0x5d, 0x2a, 0x5e, 0x87, 0xe4, 0x08, 0xfb, 0xd5, 0x60, 0x1d,
|
||||
0xa8, 0xc4, 0x6a, 0xfb, 0xd4, 0x72, 0x5c, 0x01, 0xe0, 0x4f, 0xf5, 0x9a, 0xd8, 0xe4, 0xa0, 0x4d,
|
||||
0x48, 0xba, 0x44, 0xd8, 0xb9, 0xca, 0x13, 0x2e, 0xe1, 0xa6, 0x77, 0x00, 0xb9, 0xc4, 0xbc, 0xe7,
|
||||
0xd0, 0x63, 0xf3, 0x04, 0xd3, 0x10, 0xc4, 0x67, 0xc6, 0xba, 0x4b, 0x8e, 0x1c, 0x7a, 0x7c, 0x88,
|
||||
0x29, 0x07, 0x0b, 0x7e, 0xff, 0x48, 0x10, 0x3b, 0x24, 0x14, 0xa3, 0x3c, 0xa4, 0x07, 0xa2, 0x14,
|
||||
0x93, 0x61, 0x0a, 0xe1, 0x16, 0x1f, 0x5b, 0x27, 0x84, 0x8a, 0x71, 0xba, 0x74, 0x6c, 0x31, 0x18,
|
||||
0xfa, 0x10, 0xe2, 0x64, 0x10, 0x3c, 0x63, 0x8c, 0x65, 0x66, 0xaf, 0xb0, 0xb8, 0xfe, 0x41, 0xf0,
|
||||
0x06, 0xc3, 0x69, 0x02, 0xbf, 0x74, 0xe0, 0xfd, 0x37, 0xf7, 0xf1, 0xd2, 0xb7, 0x12, 0xc0, 0x24,
|
||||
0x32, 0xba, 0x00, 0xe7, 0x0f, 0x1b, 0x86, 0x6a, 0x36, 0x9a, 0x46, 0xad, 0x51, 0x37, 0x5b, 0x75,
|
||||
0xbd, 0xa9, 0x56, 0x6b, 0x37, 0x6b, 0xea, 0x7e, 0x36, 0x82, 0xce, 0xc2, 0xfa, 0xb4, 0xf1, 0x0b,
|
||||
0x55, 0xcf, 0x4a, 0xe8, 0x3c, 0x9c, 0x9d, 0xde, 0x54, 0x2a, 0xba, 0xa1, 0xd4, 0xea, 0xd9, 0x28,
|
||||
0x42, 0x90, 0x99, 0x36, 0xd4, 0x1b, 0xd9, 0x15, 0xb4, 0x0d, 0xf2, 0xec, 0x9e, 0x79, 0x54, 0x33,
|
||||
0x3e, 0x31, 0x0f, 0x55, 0xa3, 0x91, 0x8d, 0x6d, 0xc5, 0x1e, 0xfc, 0x98, 0x8b, 0x5c, 0xfa, 0x49,
|
||||
0x82, 0xcc, 0xec, 0x3d, 0x45, 0x79, 0xb8, 0xd0, 0xd4, 0x1a, 0xcd, 0x86, 0xae, 0x1c, 0x98, 0xba,
|
||||
0xa1, 0x18, 0x2d, 0x7d, 0x8e, 0xd9, 0x5b, 0xb0, 0x39, 0x0f, 0xd0, 0x5b, 0x95, 0xdb, 0x35, 0xc3,
|
||||
0x50, 0xf7, 0xb3, 0x12, 0xda, 0x82, 0x73, 0xf3, 0xe6, 0xea, 0x41, 0x43, 0x57, 0xf7, 0xb3, 0xd1,
|
||||
0x20, 0xe3, 0x79, 0x9b, 0x52, 0x69, 0x68, 0x81, 0xe3, 0xca, 0xa2, 0x73, 0x03, 0xc2, 0xfb, 0x9a,
|
||||
0x72, 0x54, 0x1f, 0x13, 0xfe, 0x6e, 0x8a, 0xb0, 0x10, 0xf7, 0x34, 0x61, 0x4d, 0xd5, 0x5b, 0x07,
|
||||
0xc6, 0x1c, 0xe1, 0x85, 0x80, 0x9b, 0xb5, 0xba, 0x72, 0x50, 0xbb, 0xc3, 0x28, 0x6f, 0x83, 0x3c,
|
||||
0x0f, 0x50, 0xaa, 0x55, 0xb5, 0x69, 0x30, 0xd2, 0x0b, 0xac, 0x9a, 0xfa, 0xa9, 0x5a, 0x65, 0xac,
|
||||
0x05, 0xad, 0x5f, 0x24, 0x38, 0xb7, 0xf8, 0x4e, 0xa3, 0x5d, 0xd8, 0x19, 0xbb, 0xab, 0x9f, 0xab,
|
||||
0xd5, 0x96, 0xd1, 0xd0, 0x16, 0xf3, 0xdc, 0x81, 0xc2, 0x4b, 0x91, 0xf5, 0x86, 0x61, 0x6a, 0xad,
|
||||
0x7a, 0x56, 0x5a, 0x8a, 0xd2, 0x5b, 0xd5, 0xaa, 0xaa, 0xeb, 0xd9, 0xe8, 0x52, 0xd4, 0x4d, 0xa5,
|
||||
0x76, 0xd0, 0xd2, 0xd4, 0x90, 0x7c, 0xe5, 0xc6, 0x93, 0xd3, 0x9c, 0xf4, 0xf4, 0x34, 0x27, 0xfd,
|
||||
0x75, 0x9a, 0x93, 0x1e, 0x3e, 0xcb, 0x45, 0x9e, 0x3e, 0xcb, 0x45, 0x7e, 0x7f, 0x96, 0x8b, 0xdc,
|
||||
0xd9, 0xe9, 0x3a, 0xf4, 0x78, 0xd8, 0x2e, 0x75, 0x48, 0x5f, 0x7c, 0x31, 0x88, 0x9f, 0xcb, 0xbe,
|
||||
0xfd, 0x55, 0xf9, 0x3e, 0xff, 0xb4, 0x69, 0xc7, 0xd9, 0x05, 0x78, 0xef, 0xdf, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0x7b, 0x95, 0x5a, 0x6f, 0xf1, 0x0c, 0x00, 0x00,
|
||||
// 1344 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4d, 0x6f, 0x1b, 0x45,
|
||||
0x18, 0xf6, 0x3a, 0x8e, 0x3f, 0x5e, 0xa7, 0x8e, 0x99, 0x86, 0x76, 0x93, 0x06, 0xdb, 0x35, 0x39,
|
||||
0x44, 0x2d, 0xb5, 0x5b, 0x83, 0x10, 0xaa, 0x2a, 0x90, 0xed, 0x6c, 0x5b, 0xa3, 0xd4, 0x36, 0xbb,
|
||||
0xeb, 0x04, 0x7a, 0x60, 0xb5, 0xf6, 0x4e, 0x9d, 0x15, 0xf6, 0x8e, 0xb5, 0x3b, 0x4e, 0xea, 0x7f,
|
||||
0xd0, 0x1b, 0x15, 0x02, 0x89, 0x0b, 0x52, 0x25, 0xee, 0x48, 0x48, 0x3d, 0x20, 0x7e, 0x41, 0xc5,
|
||||
0xa9, 0xe2, 0xc4, 0x09, 0x50, 0x7b, 0x29, 0xf7, 0xfe, 0x00, 0xb4, 0x33, 0xb3, 0x8e, 0xed, 0xba,
|
||||
0x56, 0x52, 0xc1, 0xa9, 0x99, 0x79, 0x9f, 0xe7, 0x9d, 0xe7, 0xfd, 0xdc, 0x1a, 0x72, 0x1d, 0xe2,
|
||||
0xf5, 0x89, 0x57, 0xec, 0xba, 0x64, 0x38, 0x28, 0x1e, 0x5e, 0x6b, 0x63, 0x6a, 0x5e, 0x2b, 0xd2,
|
||||
0xd1, 0x00, 0x7b, 0x85, 0x81, 0x4b, 0x28, 0x41, 0x6b, 0x1c, 0x51, 0x60, 0x88, 0x82, 0x40, 0x6c,
|
||||
0xac, 0x75, 0x49, 0x97, 0x30, 0x40, 0xd1, 0xff, 0x8b, 0x63, 0x37, 0x32, 0x5d, 0x42, 0xba, 0x3d,
|
||||
0x5c, 0x64, 0xa7, 0xf6, 0xf0, 0x5e, 0xd1, 0x1a, 0xba, 0x26, 0xb5, 0x89, 0x23, 0xec, 0xd9, 0x59,
|
||||
0x3b, 0xb5, 0xfb, 0xd8, 0xa3, 0x66, 0x7f, 0x20, 0x00, 0xeb, 0xfc, 0x31, 0x83, 0x7b, 0x16, 0x2f,
|
||||
0x0b, 0xd3, 0x2c, 0xd7, 0x74, 0x46, 0xdc, 0x94, 0xff, 0x59, 0x82, 0xe8, 0x1d, 0xdc, 0x6f, 0x63,
|
||||
0x17, 0x95, 0x20, 0x66, 0x5a, 0x96, 0x8b, 0x3d, 0x4f, 0x96, 0x72, 0xd2, 0x76, 0xa2, 0x22, 0xff,
|
||||
0xfe, 0xf8, 0x4a, 0x10, 0x42, 0x99, 0x5b, 0x34, 0xea, 0xda, 0x4e, 0x57, 0x0d, 0x80, 0xe8, 0x1c,
|
||||
0x44, 0x8f, 0xb0, 0xdd, 0x3d, 0xa0, 0x72, 0xd8, 0xa7, 0xa8, 0xe2, 0x84, 0x36, 0x20, 0xde, 0xc7,
|
||||
0xd4, 0xb4, 0x4c, 0x6a, 0xca, 0x4b, 0xcc, 0x32, 0x3e, 0xa3, 0x4f, 0x20, 0x6e, 0x5a, 0x16, 0xb6,
|
||||
0x0c, 0x93, 0xca, 0x91, 0x9c, 0xb4, 0x9d, 0x2c, 0x6d, 0x14, 0xb8, 0xc0, 0x42, 0x20, 0xb0, 0xa0,
|
||||
0x07, 0xc1, 0x55, 0xe2, 0x4f, 0xfe, 0xcc, 0x86, 0x1e, 0xfe, 0x95, 0x95, 0xd8, 0xa3, 0xd8, 0x2a,
|
||||
0xd3, 0xfc, 0x2d, 0x88, 0x71, 0xc9, 0x1e, 0xba, 0x01, 0xb1, 0x3e, 0xff, 0x53, 0x96, 0x72, 0x4b,
|
||||
0xdb, 0xc9, 0xd2, 0x66, 0x61, 0x5e, 0xce, 0x0b, 0x1c, 0x5f, 0x89, 0xf8, 0xce, 0xd4, 0x80, 0x92,
|
||||
0xff, 0x46, 0x82, 0xf3, 0xfa, 0x81, 0x8b, 0xbd, 0x03, 0xd2, 0xb3, 0x76, 0x70, 0xc7, 0xf6, 0x6c,
|
||||
0xe2, 0x34, 0x49, 0xcf, 0xee, 0x8c, 0xd0, 0x26, 0x24, 0x68, 0x60, 0xe2, 0xf9, 0x50, 0x8f, 0x2f,
|
||||
0x90, 0x02, 0xb1, 0x23, 0xdb, 0xb1, 0xc8, 0x91, 0xc7, 0x02, 0x4f, 0x96, 0x2e, 0xcf, 0x7f, 0x77,
|
||||
0xda, 0xe9, 0x3e, 0xa7, 0xa8, 0x01, 0xf7, 0x3a, 0xfa, 0xed, 0xf1, 0x95, 0xd4, 0x34, 0x26, 0xff,
|
||||
0x9d, 0x04, 0x72, 0x13, 0xbb, 0x1d, 0xec, 0x50, 0xb3, 0x8b, 0x67, 0x54, 0x65, 0x00, 0x06, 0x63,
|
||||
0x9b, 0x90, 0x35, 0x71, 0xf3, 0x7f, 0xea, 0xfa, 0x45, 0x82, 0xb7, 0xe7, 0xd2, 0xd0, 0x6d, 0x38,
|
||||
0x73, 0x48, 0xa8, 0xed, 0x74, 0x8d, 0x01, 0x76, 0x6d, 0xc2, 0xd3, 0x95, 0x2c, 0xad, 0xbf, 0x52,
|
||||
0xd5, 0x1d, 0xd1, 0xd2, 0xbc, 0xa8, 0xdf, 0xfb, 0x45, 0x5d, 0xe1, 0xcc, 0x26, 0x23, 0xa2, 0x16,
|
||||
0xac, 0xf5, 0x6d, 0xc7, 0xc0, 0xf7, 0x71, 0x67, 0xe8, 0x03, 0x03, 0x87, 0xe1, 0x93, 0x3b, 0x44,
|
||||
0x7d, 0xdb, 0x51, 0x02, 0x3e, 0x77, 0x9b, 0xff, 0x47, 0x82, 0xc4, 0x2d, 0x3f, 0xfe, 0x9a, 0x73,
|
||||
0x8f, 0xa0, 0x14, 0x84, 0x6d, 0xae, 0x31, 0xa2, 0x86, 0x6d, 0x0b, 0x15, 0x60, 0xd9, 0xb4, 0xfa,
|
||||
0xb6, 0xc3, 0x5b, 0x78, 0x41, 0xd7, 0x73, 0xd8, 0xc2, 0xde, 0x96, 0x21, 0x76, 0x88, 0x5d, 0x3f,
|
||||
0x45, 0xac, 0xb5, 0x23, 0x6a, 0x70, 0x44, 0x17, 0x61, 0x85, 0x12, 0x6a, 0xf6, 0x0c, 0x31, 0x2f,
|
||||
0xcb, 0x8c, 0x99, 0x64, 0x77, 0xfb, 0x7c, 0x68, 0xaa, 0x00, 0x1d, 0x17, 0x9b, 0x94, 0x8f, 0x46,
|
||||
0xf4, 0x14, 0xa3, 0x91, 0x10, 0xbc, 0x32, 0xcd, 0x7f, 0x09, 0x49, 0x16, 0xaa, 0x18, 0xea, 0x75,
|
||||
0x88, 0xb3, 0xca, 0x1b, 0xe3, 0x90, 0x63, 0xec, 0x5c, 0xb3, 0xd0, 0x07, 0x10, 0xe5, 0x83, 0x20,
|
||||
0xd2, 0xbb, 0x70, 0x74, 0x54, 0x81, 0xcd, 0xbf, 0x08, 0xc3, 0x2a, 0x7b, 0x80, 0xf7, 0x00, 0xcb,
|
||||
0xe8, 0x9b, 0x6c, 0x8e, 0x49, 0x61, 0xe1, 0x69, 0x61, 0xe3, 0x82, 0x2c, 0x9d, 0xbe, 0x20, 0x91,
|
||||
0xd7, 0x17, 0x64, 0x79, 0xba, 0x20, 0x9f, 0xc1, 0xaa, 0x25, 0xda, 0xd9, 0x18, 0xb0, 0x58, 0x44,
|
||||
0xca, 0xd7, 0x5e, 0x49, 0x79, 0xd9, 0x19, 0x55, 0xe6, 0x8c, 0x84, 0x9a, 0xb2, 0xa6, 0xa7, 0x73,
|
||||
0xba, 0x80, 0xb1, 0x37, 0x2a, 0xe0, 0xf5, 0xf8, 0x83, 0x47, 0xd9, 0xd0, 0x8b, 0x47, 0x59, 0x29,
|
||||
0xff, 0x72, 0x19, 0xe2, 0x4d, 0x97, 0x0c, 0x88, 0x67, 0xf6, 0x5e, 0xe9, 0xda, 0x89, 0x9c, 0x87,
|
||||
0x4f, 0x9a, 0xf3, 0x45, 0x9d, 0xfb, 0x21, 0x24, 0x06, 0xec, 0x2d, 0x7f, 0x97, 0x46, 0x72, 0x4b,
|
||||
0x0b, 0x3d, 0x1e, 0x43, 0x91, 0x02, 0x49, 0x6f, 0xd8, 0xee, 0xdb, 0xd4, 0xf0, 0x3f, 0x48, 0x2c,
|
||||
0xc9, 0x27, 0x0d, 0x1a, 0x38, 0xd1, 0x37, 0xa1, 0x77, 0xe1, 0x0c, 0x6f, 0x87, 0xa0, 0x5a, 0x51,
|
||||
0x16, 0xe9, 0x0a, 0xbb, 0xdc, 0x13, 0x25, 0xbb, 0x0a, 0x6b, 0x1c, 0xc4, 0xeb, 0x35, 0xc6, 0xc6,
|
||||
0x18, 0x16, 0x75, 0x8f, 0xdb, 0x32, 0x60, 0xdc, 0x80, 0xa8, 0x47, 0x4d, 0x3a, 0xf4, 0xe4, 0x78,
|
||||
0x4e, 0xda, 0x4e, 0x95, 0xb6, 0xe6, 0xf7, 0x78, 0x90, 0x65, 0x8d, 0x61, 0x55, 0xc1, 0xf1, 0xd9,
|
||||
0x2e, 0xf6, 0x86, 0x3d, 0x2a, 0x27, 0x4e, 0xc2, 0x56, 0x19, 0x56, 0x15, 0x1c, 0xd4, 0x02, 0x74,
|
||||
0xcf, 0x76, 0xcc, 0x9e, 0x41, 0xcd, 0x5e, 0x6f, 0x64, 0x08, 0x4f, 0xc0, 0x12, 0x74, 0x71, 0xbe,
|
||||
0x27, 0xdd, 0x47, 0x72, 0x37, 0xe2, 0x5b, 0x95, 0x66, 0x2e, 0x26, 0xee, 0x51, 0x13, 0xde, 0x9a,
|
||||
0xda, 0xb6, 0x06, 0x76, 0x2c, 0x39, 0x79, 0x8a, 0xb4, 0xaf, 0x4e, 0xae, 0x5c, 0xc5, 0xf1, 0xb7,
|
||||
0xee, 0x2a, 0xdf, 0xb8, 0xc4, 0x0d, 0x54, 0xae, 0xb0, 0x78, 0xdf, 0x5b, 0x1c, 0xaf, 0x22, 0x48,
|
||||
0x22, 0xee, 0x14, 0x9e, 0x3a, 0xa3, 0xab, 0x7e, 0xb7, 0x79, 0x9e, 0xd9, 0xc5, 0x9e, 0x7c, 0x86,
|
||||
0x7d, 0x9c, 0xe7, 0x4e, 0x96, 0x3a, 0x46, 0x5d, 0x8f, 0xf8, 0xad, 0x9f, 0xff, 0x41, 0x82, 0xe4,
|
||||
0x64, 0xc0, 0x17, 0x20, 0x31, 0xc2, 0x9e, 0xd1, 0x21, 0x43, 0x87, 0x8a, 0x4f, 0x5e, 0x7c, 0x84,
|
||||
0xbd, 0xaa, 0x7f, 0xf6, 0xfb, 0xc6, 0x6c, 0x7b, 0xd4, 0xb4, 0x1d, 0x01, 0xe0, 0xff, 0x0f, 0x59,
|
||||
0x11, 0x97, 0x1c, 0xb4, 0x0e, 0x71, 0x87, 0x08, 0x3b, 0xef, 0xfb, 0x98, 0x43, 0xb8, 0xe9, 0x32,
|
||||
0x20, 0x87, 0x18, 0x47, 0x36, 0x3d, 0x30, 0x0e, 0x31, 0x0d, 0x40, 0x7c, 0x8b, 0xac, 0x3a, 0x64,
|
||||
0xdf, 0xa6, 0x07, 0x7b, 0x98, 0x72, 0xb0, 0xd0, 0xf7, 0x52, 0x82, 0xc8, 0x1e, 0xa1, 0x18, 0x65,
|
||||
0x21, 0x39, 0x10, 0xa9, 0x38, 0x5e, 0xaf, 0x10, 0x5c, 0xf1, 0x45, 0x76, 0x48, 0xa8, 0x58, 0xb0,
|
||||
0x0b, 0x17, 0x19, 0x83, 0xa1, 0x8f, 0x20, 0x4a, 0x06, 0xfe, 0x77, 0x8b, 0xa9, 0x4c, 0x95, 0x72,
|
||||
0xf3, 0xf3, 0xef, 0x3f, 0xde, 0x60, 0x38, 0x55, 0xe0, 0x17, 0xae, 0xc0, 0xff, 0x66, 0x42, 0x2f,
|
||||
0x7d, 0x2d, 0x01, 0x1c, 0xbf, 0x8c, 0x2e, 0xc0, 0xf9, 0xbd, 0x86, 0xae, 0x18, 0x8d, 0xa6, 0x5e,
|
||||
0x6b, 0xd4, 0x8d, 0x56, 0x5d, 0x6b, 0x2a, 0xd5, 0xda, 0xcd, 0x9a, 0xb2, 0x93, 0x0e, 0xa1, 0xb3,
|
||||
0xb0, 0x3a, 0x69, 0xfc, 0x42, 0xd1, 0xd2, 0x12, 0x3a, 0x0f, 0x67, 0x27, 0x2f, 0xcb, 0x15, 0x4d,
|
||||
0x2f, 0xd7, 0xea, 0xe9, 0x30, 0x42, 0x90, 0x9a, 0x34, 0xd4, 0x1b, 0xe9, 0x25, 0xb4, 0x09, 0xf2,
|
||||
0xf4, 0x9d, 0xb1, 0x5f, 0xd3, 0x6f, 0x1b, 0x7b, 0x8a, 0xde, 0x48, 0x47, 0x36, 0x22, 0x0f, 0x7e,
|
||||
0xcc, 0x84, 0x2e, 0xfd, 0x24, 0x41, 0x6a, 0x7a, 0x72, 0x51, 0x16, 0x2e, 0x34, 0xd5, 0x46, 0xb3,
|
||||
0xa1, 0x95, 0x77, 0x0d, 0x4d, 0x2f, 0xeb, 0x2d, 0x6d, 0x46, 0xd9, 0x3b, 0xb0, 0x3e, 0x0b, 0xd0,
|
||||
0x5a, 0x95, 0x3b, 0x35, 0x5d, 0x57, 0x76, 0xd2, 0x12, 0xda, 0x80, 0x73, 0xb3, 0xe6, 0xea, 0x6e,
|
||||
0x43, 0x53, 0x76, 0xd2, 0x61, 0x3f, 0xe2, 0x59, 0x5b, 0xb9, 0xd2, 0x50, 0x7d, 0xe2, 0xd2, 0x3c,
|
||||
0xbf, 0xbe, 0xe0, 0x1d, 0xb5, 0xbc, 0x5f, 0x1f, 0x0b, 0xfe, 0x76, 0x42, 0xb0, 0x68, 0xee, 0x49,
|
||||
0xc1, 0xaa, 0xa2, 0xb5, 0x76, 0xf5, 0x19, 0xc1, 0x73, 0x01, 0x37, 0x6b, 0xf5, 0xf2, 0x6e, 0xed,
|
||||
0x2e, 0x93, 0xbc, 0x09, 0xf2, 0x2c, 0xa0, 0x5c, 0xad, 0x2a, 0x4d, 0x9d, 0x89, 0x9e, 0x63, 0x55,
|
||||
0x95, 0x4f, 0x95, 0x2a, 0x53, 0x2d, 0x64, 0xfd, 0x2a, 0xc1, 0xb9, 0xf9, 0x33, 0x8d, 0xb6, 0x61,
|
||||
0x6b, 0x4c, 0x57, 0x3e, 0x57, 0xaa, 0x2d, 0xbd, 0xa1, 0xce, 0xd7, 0xb9, 0x05, 0xb9, 0xd7, 0x22,
|
||||
0xeb, 0x0d, 0xdd, 0x50, 0x5b, 0xf5, 0xb4, 0xb4, 0x10, 0xa5, 0xb5, 0xaa, 0x55, 0x45, 0xd3, 0xd2,
|
||||
0xe1, 0x85, 0xa8, 0x9b, 0xe5, 0xda, 0x6e, 0x4b, 0x55, 0x02, 0xf1, 0x95, 0x8f, 0x9f, 0x3c, 0xcb,
|
||||
0x48, 0x4f, 0x9f, 0x65, 0xa4, 0xbf, 0x9f, 0x65, 0xa4, 0x87, 0xcf, 0x33, 0xa1, 0xa7, 0xcf, 0x33,
|
||||
0xa1, 0x3f, 0x9e, 0x67, 0x42, 0x77, 0xb7, 0xba, 0x36, 0x3d, 0x18, 0xb6, 0x0b, 0x1d, 0xd2, 0x17,
|
||||
0x3f, 0x87, 0xc4, 0x3f, 0x57, 0x3c, 0xeb, 0xab, 0xe2, 0x7d, 0xfe, 0xbb, 0xad, 0x1d, 0x65, 0x03,
|
||||
0xf0, 0xfe, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0xf7, 0x5a, 0xf3, 0xf9, 0xce, 0x0d, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (this *GroupPolicyInfo) Equal(that interface{}) bool {
|
||||
|
@ -1072,14 +1142,18 @@ func (m *ThresholdDecisionPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error)
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n2, err2 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Timeout, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Timeout):])
|
||||
if err2 != nil {
|
||||
return 0, err2
|
||||
if m.Windows != nil {
|
||||
{
|
||||
size, err := m.Windows.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i -= n2
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n2))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if len(m.Threshold) > 0 {
|
||||
i -= len(m.Threshold)
|
||||
copy(dAtA[i:], m.Threshold)
|
||||
|
@ -1110,14 +1184,18 @@ func (m *PercentageDecisionPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n3, err3 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.Timeout, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.Timeout):])
|
||||
if err3 != nil {
|
||||
return 0, err3
|
||||
if m.Windows != nil {
|
||||
{
|
||||
size, err := m.Windows.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintTypes(dAtA, i, uint64(size))
|
||||
}
|
||||
i -= n3
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n3))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if len(m.Percentage) > 0 {
|
||||
i -= len(m.Percentage)
|
||||
copy(dAtA[i:], m.Percentage)
|
||||
|
@ -1128,6 +1206,45 @@ func (m *PercentageDecisionPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error
|
|||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n4, err4 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.MinExecutionPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.MinExecutionPeriod):])
|
||||
if err4 != nil {
|
||||
return 0, err4
|
||||
}
|
||||
i -= n4
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n4))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.VotingPeriod, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.VotingPeriod):])
|
||||
if err5 != nil {
|
||||
return 0, err5
|
||||
}
|
||||
i -= n5
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n5))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *GroupInfo) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
|
@ -1148,12 +1265,12 @@ func (m *GroupInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):])
|
||||
if err4 != nil {
|
||||
return 0, err4
|
||||
n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):])
|
||||
if err6 != nil {
|
||||
return 0, err6
|
||||
}
|
||||
i -= n4
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n4))
|
||||
i -= n6
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n6))
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
if len(m.TotalWeight) > 0 {
|
||||
|
@ -1250,12 +1367,12 @@ func (m *GroupPolicyInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):])
|
||||
if err6 != nil {
|
||||
return 0, err6
|
||||
n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):])
|
||||
if err8 != nil {
|
||||
return 0, err8
|
||||
}
|
||||
i -= n6
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n6))
|
||||
i -= n8
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n8))
|
||||
i--
|
||||
dAtA[i] = 0x3a
|
||||
if m.DecisionPolicy != nil {
|
||||
|
@ -1343,12 +1460,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
i--
|
||||
dAtA[i] = 0x60
|
||||
}
|
||||
n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timeout, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timeout):])
|
||||
if err8 != nil {
|
||||
return 0, err8
|
||||
n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.VotingPeriodEnd, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingPeriodEnd):])
|
||||
if err10 != nil {
|
||||
return 0, err10
|
||||
}
|
||||
i -= n8
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n8))
|
||||
i -= n10
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n10))
|
||||
i--
|
||||
dAtA[i] = 0x5a
|
||||
{
|
||||
|
@ -1381,12 +1498,12 @@ func (m *Proposal) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
i--
|
||||
dAtA[i] = 0x30
|
||||
}
|
||||
n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SubmitTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime):])
|
||||
if err10 != nil {
|
||||
return 0, err10
|
||||
n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SubmitTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime):])
|
||||
if err12 != nil {
|
||||
return 0, err12
|
||||
}
|
||||
i -= n10
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n10))
|
||||
i -= n12
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n12))
|
||||
i--
|
||||
dAtA[i] = 0x2a
|
||||
if len(m.Proposers) > 0 {
|
||||
|
@ -1491,12 +1608,12 @@ func (m *Vote) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
|||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SubmitTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime):])
|
||||
if err11 != nil {
|
||||
return 0, err11
|
||||
n13, err13 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.SubmitTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.SubmitTime):])
|
||||
if err13 != nil {
|
||||
return 0, err13
|
||||
}
|
||||
i -= n11
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n11))
|
||||
i -= n13
|
||||
i = encodeVarintTypes(dAtA, i, uint64(n13))
|
||||
i--
|
||||
dAtA[i] = 0x2a
|
||||
if len(m.Metadata) > 0 {
|
||||
|
@ -1585,8 +1702,10 @@ func (m *ThresholdDecisionPolicy) Size() (n int) {
|
|||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Timeout)
|
||||
if m.Windows != nil {
|
||||
l = m.Windows.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
|
@ -1600,7 +1719,22 @@ func (m *PercentageDecisionPolicy) Size() (n int) {
|
|||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.Timeout)
|
||||
if m.Windows != nil {
|
||||
l = m.Windows.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *DecisionPolicyWindows) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.VotingPeriod)
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdDuration(m.MinExecutionPeriod)
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
@ -1722,7 +1856,7 @@ func (m *Proposal) Size() (n int) {
|
|||
}
|
||||
l = m.FinalTallyResult.Size()
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Timeout)
|
||||
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.VotingPeriodEnd)
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
if m.ExecutorResult != 0 {
|
||||
n += 1 + sovTypes(uint64(m.ExecutorResult))
|
||||
|
@ -2118,7 +2252,7 @@ func (m *ThresholdDecisionPolicy) Unmarshal(dAtA []byte) error {
|
|||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Windows", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
|
@ -2145,7 +2279,10 @@ func (m *ThresholdDecisionPolicy) Unmarshal(dAtA []byte) error {
|
|||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Timeout, dAtA[iNdEx:postIndex]); err != nil {
|
||||
if m.Windows == nil {
|
||||
m.Windows = &DecisionPolicyWindows{}
|
||||
}
|
||||
if err := m.Windows.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
|
@ -2233,7 +2370,7 @@ func (m *PercentageDecisionPolicy) Unmarshal(dAtA []byte) error {
|
|||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Windows", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
|
@ -2260,7 +2397,126 @@ func (m *PercentageDecisionPolicy) Unmarshal(dAtA []byte) error {
|
|||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.Timeout, dAtA[iNdEx:postIndex]); err != nil {
|
||||
if m.Windows == nil {
|
||||
m.Windows = &DecisionPolicyWindows{}
|
||||
}
|
||||
if err := m.Windows.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *DecisionPolicyWindows) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: DecisionPolicyWindows: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: DecisionPolicyWindows: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field VotingPeriod", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.VotingPeriod, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field MinExecutionPeriod", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&m.MinExecutionPeriod, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
|
@ -3148,7 +3404,7 @@ func (m *Proposal) Unmarshal(dAtA []byte) error {
|
|||
iNdEx = postIndex
|
||||
case 11:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType)
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field VotingPeriodEnd", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
|
@ -3175,7 +3431,7 @@ func (m *Proposal) Unmarshal(dAtA []byte) error {
|
|||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.Timeout, dAtA[iNdEx:postIndex]); err != nil {
|
||||
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.VotingPeriodEnd, dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
|
|
|
@ -9,6 +9,105 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestThresholdDecisionPolicyValidate(t *testing.T) {
|
||||
g := group.GroupInfo{
|
||||
TotalWeight: "10",
|
||||
}
|
||||
config := group.DefaultConfig()
|
||||
testCases := []struct {
|
||||
name string
|
||||
policy group.ThresholdDecisionPolicy
|
||||
expErr bool
|
||||
}{
|
||||
|
||||
{
|
||||
"threshold bigger than total weight",
|
||||
group.ThresholdDecisionPolicy{
|
||||
Threshold: "12",
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"min exec period too big",
|
||||
group.ThresholdDecisionPolicy{
|
||||
Threshold: "5",
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
MinExecutionPeriod: time.Hour * 24 * 30,
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"all good",
|
||||
group.ThresholdDecisionPolicy{
|
||||
Threshold: "5",
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Hour,
|
||||
MinExecutionPeriod: time.Hour * 24,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.policy.Validate(g, config)
|
||||
if tc.expErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentageDecisionPolicyValidate(t *testing.T) {
|
||||
g := group.GroupInfo{}
|
||||
config := group.DefaultConfig()
|
||||
testCases := []struct {
|
||||
name string
|
||||
policy group.PercentageDecisionPolicy
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"min exec period too big",
|
||||
group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second,
|
||||
MinExecutionPeriod: time.Hour * 24 * 30,
|
||||
},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"all good",
|
||||
group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Hour,
|
||||
MinExecutionPeriod: time.Hour * 24,
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.policy.Validate(g, config)
|
||||
if tc.expErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
@ -17,12 +116,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
totalPower string
|
||||
votingDuration time.Duration
|
||||
result group.DecisionPolicyResult
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"YesCount percentage > decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "2",
|
||||
|
@ -36,12 +138,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: true,
|
||||
Final: true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"YesCount percentage == decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "2",
|
||||
|
@ -55,12 +160,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: true,
|
||||
Final: true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"YesCount percentage < decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -74,12 +182,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sum percentage (YesCount + undecided votes percentage) < decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -93,12 +204,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sum percentage = decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -112,12 +226,15 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sum percentage > decision policy percentage",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -131,12 +248,16 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"decision policy timeout <= voting duration",
|
||||
"time since submission < min execution period",
|
||||
&group.PercentageDecisionPolicy{
|
||||
Percentage: "0.5",
|
||||
Timeout: time.Second * 10,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 10,
|
||||
MinExecutionPeriod: time.Minute,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "2",
|
||||
|
@ -146,17 +267,19 @@ func TestPercentageDecisionPolicyAllow(t *testing.T) {
|
|||
},
|
||||
"3",
|
||||
time.Duration(time.Second * 50),
|
||||
group.DecisionPolicyResult{
|
||||
Allow: false,
|
||||
Final: true,
|
||||
},
|
||||
group.DecisionPolicyResult{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
policyResult, err := tc.policy.Allow(*tc.tally, tc.totalPower, tc.votingDuration)
|
||||
if tc.expErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.result, policyResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -169,12 +292,15 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
totalPower string
|
||||
votingDuration time.Duration
|
||||
result group.DecisionPolicyResult
|
||||
expErr bool
|
||||
}{
|
||||
{
|
||||
"YesCount >= threshold decision policy",
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "3",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "3",
|
||||
|
@ -188,12 +314,15 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: true,
|
||||
Final: true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"YesCount < threshold decision policy",
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "3",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -207,12 +336,15 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sum votes < threshold decision policy",
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "3",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -226,12 +358,15 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: true,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"sum votes >= threshold decision policy",
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "3",
|
||||
Timeout: time.Second * 100,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 100,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "1",
|
||||
|
@ -245,12 +380,16 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: false,
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"decision policy timeout <= voting duration",
|
||||
"time since submission < min execution period",
|
||||
&group.ThresholdDecisionPolicy{
|
||||
Threshold: "3",
|
||||
Timeout: time.Second * 10,
|
||||
Windows: &group.DecisionPolicyWindows{
|
||||
VotingPeriod: time.Second * 10,
|
||||
MinExecutionPeriod: time.Minute,
|
||||
},
|
||||
},
|
||||
&group.TallyResult{
|
||||
YesCount: "3",
|
||||
|
@ -264,13 +403,18 @@ func TestThresholdDecisionPolicyAllow(t *testing.T) {
|
|||
Allow: false,
|
||||
Final: true,
|
||||
},
|
||||
true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
policyResult, err := tc.policy.Allow(*tc.tally, tc.totalPower, tc.votingDuration)
|
||||
if tc.expErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.result, policyResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue