From da36c46f3a3a8dec7eaa85b69e7fa154e9d64dce Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaurym@users.noreply.github.com> Date: Wed, 2 Mar 2022 13:00:59 +0100 Subject: [PATCH] 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) --- api/cosmos/app/v1alpha1/module.pulsar.go | 7 +- .../base/snapshots/v1beta1/snapshot.pulsar.go | 10 +- api/cosmos/group/v1beta1/types.pulsar.go | 1267 ++++++++++++----- .../orm/module/v1alpha1/module.pulsar.go | 10 +- api/cosmos/orm/v1alpha1/orm.pulsar.go | 7 +- api/cosmos/orm/v1alpha1/schema.pulsar.go | 7 +- proto/cosmos/group/v1beta1/types.proto | 41 +- simapp/app.go | 2 +- snapshots/types/snapshot.pb.go | 10 +- types/tx/service.pb.go | 4 + x/group/client/testutil/tx.go | 50 +- x/group/{keeper => }/config.go | 10 +- x/group/genesis_test.go | 12 +- x/group/keeper/genesis_test.go | 10 +- x/group/keeper/grpc_query.go | 53 + x/group/keeper/invariants_test.go | 35 +- x/group/keeper/keeper.go | 9 +- x/group/keeper/keeper_test.go | 97 +- x/group/keeper/msg_server.go | 59 +- x/group/keeper/proposal_executor.go | 10 + x/group/msgs_test.go | 84 +- x/group/simulation/genesis.go | 10 +- x/group/simulation/operations.go | 32 +- x/group/simulation/operations_test.go | 15 +- x/group/types.go | 75 +- x/group/types.pb.go | 558 ++++++-- x/group/types_test.go | 188 ++- 27 files changed, 1933 insertions(+), 739 deletions(-) rename x/group/{keeper => }/config.go (57%) diff --git a/api/cosmos/app/v1alpha1/module.pulsar.go b/api/cosmos/app/v1alpha1/module.pulsar.go index a5dfd51240db..a00331b63cce 100644 --- a/api/cosmos/app/v1alpha1/module.pulsar.go +++ b/api/cosmos/app/v1alpha1/module.pulsar.go @@ -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) diff --git a/api/cosmos/base/snapshots/v1beta1/snapshot.pulsar.go b/api/cosmos/base/snapshots/v1beta1/snapshot.pulsar.go index ce92b9e1ea1c..8bbe2e70da60 100644 --- a/api/cosmos/base/snapshots/v1beta1/snapshot.pulsar.go +++ b/api/cosmos/base/snapshots/v1beta1/snapshot.pulsar.go @@ -4076,10 +4076,12 @@ type SnapshotIAVLItem struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - 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 int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + 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"` } func (x *SnapshotIAVLItem) Reset() { diff --git a/api/cosmos/group/v1beta1/types.pulsar.go b/api/cosmos/group/v1beta1/types.pulsar.go index 5bf337ab945a..ee952520754f 100644 --- a/api/cosmos/group/v1beta1/types.pulsar.go +++ b/api/cosmos/group/v1beta1/types.pulsar.go @@ -1141,14 +1141,14 @@ func (x *fastReflection_Members) ProtoMethods() *protoiface.Methods { var ( md_ThresholdDecisionPolicy protoreflect.MessageDescriptor fd_ThresholdDecisionPolicy_threshold protoreflect.FieldDescriptor - fd_ThresholdDecisionPolicy_timeout protoreflect.FieldDescriptor + fd_ThresholdDecisionPolicy_windows protoreflect.FieldDescriptor ) func init() { file_cosmos_group_v1beta1_types_proto_init() md_ThresholdDecisionPolicy = File_cosmos_group_v1beta1_types_proto.Messages().ByName("ThresholdDecisionPolicy") fd_ThresholdDecisionPolicy_threshold = md_ThresholdDecisionPolicy.Fields().ByName("threshold") - fd_ThresholdDecisionPolicy_timeout = md_ThresholdDecisionPolicy.Fields().ByName("timeout") + fd_ThresholdDecisionPolicy_windows = md_ThresholdDecisionPolicy.Fields().ByName("windows") } var _ protoreflect.Message = (*fastReflection_ThresholdDecisionPolicy)(nil) @@ -1222,9 +1222,9 @@ func (x *fastReflection_ThresholdDecisionPolicy) Range(f func(protoreflect.Field return } } - if x.Timeout != nil { - value := protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) - if !f(fd_ThresholdDecisionPolicy_timeout, value) { + if x.Windows != nil { + value := protoreflect.ValueOfMessage(x.Windows.ProtoReflect()) + if !f(fd_ThresholdDecisionPolicy_windows, value) { return } } @@ -1245,8 +1245,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) Has(fd protoreflect.FieldDescri switch fd.FullName() { case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": return x.Threshold != "" - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - return x.Timeout != nil + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + return x.Windows != nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.ThresholdDecisionPolicy")) @@ -1265,8 +1265,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) Clear(fd protoreflect.FieldDesc switch fd.FullName() { case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": x.Threshold = "" - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - x.Timeout = nil + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + x.Windows = nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.ThresholdDecisionPolicy")) @@ -1286,8 +1286,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) Get(descriptor protoreflect.Fie case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": value := x.Threshold return protoreflect.ValueOfString(value) - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - value := x.Timeout + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + value := x.Windows return protoreflect.ValueOfMessage(value.ProtoReflect()) default: if descriptor.IsExtension() { @@ -1311,8 +1311,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) Set(fd protoreflect.FieldDescri switch fd.FullName() { case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": x.Threshold = value.Interface().(string) - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - x.Timeout = value.Message().Interface().(*durationpb.Duration) + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + x.Windows = value.Message().Interface().(*DecisionPolicyWindows) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.ThresholdDecisionPolicy")) @@ -1333,11 +1333,11 @@ func (x *fastReflection_ThresholdDecisionPolicy) Set(fd protoreflect.FieldDescri // Mutable is a mutating operation and unsafe for concurrent use. func (x *fastReflection_ThresholdDecisionPolicy) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - if x.Timeout == nil { - x.Timeout = new(durationpb.Duration) + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + if x.Windows == nil { + x.Windows = new(DecisionPolicyWindows) } - return protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) + return protoreflect.ValueOfMessage(x.Windows.ProtoReflect()) case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": panic(fmt.Errorf("field threshold of message cosmos.group.v1beta1.ThresholdDecisionPolicy is not mutable")) default: @@ -1355,8 +1355,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) NewField(fd protoreflect.FieldD switch fd.FullName() { case "cosmos.group.v1beta1.ThresholdDecisionPolicy.threshold": return protoreflect.ValueOfString("") - case "cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout": - m := new(durationpb.Duration) + case "cosmos.group.v1beta1.ThresholdDecisionPolicy.windows": + m := new(DecisionPolicyWindows) return protoreflect.ValueOfMessage(m.ProtoReflect()) default: if fd.IsExtension() { @@ -1431,8 +1431,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) ProtoMethods() *protoiface.Meth if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } - if x.Timeout != nil { - l = options.Size(x.Timeout) + if x.Windows != nil { + l = options.Size(x.Windows) n += 1 + l + runtime.Sov(uint64(l)) } if x.unknownFields != nil { @@ -1464,8 +1464,8 @@ func (x *fastReflection_ThresholdDecisionPolicy) ProtoMethods() *protoiface.Meth i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } - if x.Timeout != nil { - encoded, err := options.Marshal(x.Timeout) + if x.Windows != nil { + encoded, err := options.Marshal(x.Windows) if err != nil { return protoiface.MarshalOutput{ NoUnkeyedLiterals: input.NoUnkeyedLiterals, @@ -1568,7 +1568,7 @@ func (x *fastReflection_ThresholdDecisionPolicy) ProtoMethods() *protoiface.Meth iNdEx = postIndex case 2: if wireType != 2 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Windows", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -1595,10 +1595,10 @@ func (x *fastReflection_ThresholdDecisionPolicy) ProtoMethods() *protoiface.Meth if postIndex > l { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF } - if x.Timeout == nil { - x.Timeout = &durationpb.Duration{} + if x.Windows == nil { + x.Windows = &DecisionPolicyWindows{} } - if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Timeout); err != nil { + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Windows); err != nil { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex @@ -1640,14 +1640,14 @@ func (x *fastReflection_ThresholdDecisionPolicy) ProtoMethods() *protoiface.Meth var ( md_PercentageDecisionPolicy protoreflect.MessageDescriptor fd_PercentageDecisionPolicy_percentage protoreflect.FieldDescriptor - fd_PercentageDecisionPolicy_timeout protoreflect.FieldDescriptor + fd_PercentageDecisionPolicy_windows protoreflect.FieldDescriptor ) func init() { file_cosmos_group_v1beta1_types_proto_init() md_PercentageDecisionPolicy = File_cosmos_group_v1beta1_types_proto.Messages().ByName("PercentageDecisionPolicy") fd_PercentageDecisionPolicy_percentage = md_PercentageDecisionPolicy.Fields().ByName("percentage") - fd_PercentageDecisionPolicy_timeout = md_PercentageDecisionPolicy.Fields().ByName("timeout") + fd_PercentageDecisionPolicy_windows = md_PercentageDecisionPolicy.Fields().ByName("windows") } var _ protoreflect.Message = (*fastReflection_PercentageDecisionPolicy)(nil) @@ -1721,9 +1721,9 @@ func (x *fastReflection_PercentageDecisionPolicy) Range(f func(protoreflect.Fiel return } } - if x.Timeout != nil { - value := protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) - if !f(fd_PercentageDecisionPolicy_timeout, value) { + if x.Windows != nil { + value := protoreflect.ValueOfMessage(x.Windows.ProtoReflect()) + if !f(fd_PercentageDecisionPolicy_windows, value) { return } } @@ -1744,8 +1744,8 @@ func (x *fastReflection_PercentageDecisionPolicy) Has(fd protoreflect.FieldDescr switch fd.FullName() { case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": return x.Percentage != "" - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - return x.Timeout != nil + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + return x.Windows != nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.PercentageDecisionPolicy")) @@ -1764,8 +1764,8 @@ func (x *fastReflection_PercentageDecisionPolicy) Clear(fd protoreflect.FieldDes switch fd.FullName() { case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": x.Percentage = "" - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - x.Timeout = nil + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + x.Windows = nil default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.PercentageDecisionPolicy")) @@ -1785,8 +1785,8 @@ func (x *fastReflection_PercentageDecisionPolicy) Get(descriptor protoreflect.Fi case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": value := x.Percentage return protoreflect.ValueOfString(value) - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - value := x.Timeout + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + value := x.Windows return protoreflect.ValueOfMessage(value.ProtoReflect()) default: if descriptor.IsExtension() { @@ -1810,8 +1810,8 @@ func (x *fastReflection_PercentageDecisionPolicy) Set(fd protoreflect.FieldDescr switch fd.FullName() { case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": x.Percentage = value.Interface().(string) - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - x.Timeout = value.Message().Interface().(*durationpb.Duration) + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + x.Windows = value.Message().Interface().(*DecisionPolicyWindows) default: if fd.IsExtension() { panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.PercentageDecisionPolicy")) @@ -1832,11 +1832,11 @@ func (x *fastReflection_PercentageDecisionPolicy) Set(fd protoreflect.FieldDescr // Mutable is a mutating operation and unsafe for concurrent use. func (x *fastReflection_PercentageDecisionPolicy) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { switch fd.FullName() { - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - if x.Timeout == nil { - x.Timeout = new(durationpb.Duration) + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + if x.Windows == nil { + x.Windows = new(DecisionPolicyWindows) } - return protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) + return protoreflect.ValueOfMessage(x.Windows.ProtoReflect()) case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": panic(fmt.Errorf("field percentage of message cosmos.group.v1beta1.PercentageDecisionPolicy is not mutable")) default: @@ -1854,8 +1854,8 @@ func (x *fastReflection_PercentageDecisionPolicy) NewField(fd protoreflect.Field switch fd.FullName() { case "cosmos.group.v1beta1.PercentageDecisionPolicy.percentage": return protoreflect.ValueOfString("") - case "cosmos.group.v1beta1.PercentageDecisionPolicy.timeout": - m := new(durationpb.Duration) + case "cosmos.group.v1beta1.PercentageDecisionPolicy.windows": + m := new(DecisionPolicyWindows) return protoreflect.ValueOfMessage(m.ProtoReflect()) default: if fd.IsExtension() { @@ -1930,8 +1930,8 @@ func (x *fastReflection_PercentageDecisionPolicy) ProtoMethods() *protoiface.Met if l > 0 { n += 1 + l + runtime.Sov(uint64(l)) } - if x.Timeout != nil { - l = options.Size(x.Timeout) + if x.Windows != nil { + l = options.Size(x.Windows) n += 1 + l + runtime.Sov(uint64(l)) } if x.unknownFields != nil { @@ -1963,8 +1963,8 @@ func (x *fastReflection_PercentageDecisionPolicy) ProtoMethods() *protoiface.Met i -= len(x.unknownFields) copy(dAtA[i:], x.unknownFields) } - if x.Timeout != nil { - encoded, err := options.Marshal(x.Timeout) + if x.Windows != nil { + encoded, err := options.Marshal(x.Windows) if err != nil { return protoiface.MarshalOutput{ NoUnkeyedLiterals: input.NoUnkeyedLiterals, @@ -2067,7 +2067,521 @@ func (x *fastReflection_PercentageDecisionPolicy) ProtoMethods() *protoiface.Met iNdEx = postIndex case 2: if wireType != 2 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Windows", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.Windows == nil { + x.Windows = &DecisionPolicyWindows{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Windows); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := runtime.Skip(dAtA[iNdEx:]) + if err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if (iNdEx + skippy) > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if !options.DiscardUnknown { + x.unknownFields = append(x.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + } + iNdEx += skippy + } + } + + if iNdEx > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, nil + } + return &protoiface.Methods{ + NoUnkeyedLiterals: struct{}{}, + Flags: protoiface.SupportMarshalDeterministic | protoiface.SupportUnmarshalDiscardUnknown, + Size: size, + Marshal: marshal, + Unmarshal: unmarshal, + Merge: nil, + CheckInitialized: nil, + } +} + +var ( + md_DecisionPolicyWindows protoreflect.MessageDescriptor + fd_DecisionPolicyWindows_voting_period protoreflect.FieldDescriptor + fd_DecisionPolicyWindows_min_execution_period protoreflect.FieldDescriptor +) + +func init() { + file_cosmos_group_v1beta1_types_proto_init() + md_DecisionPolicyWindows = File_cosmos_group_v1beta1_types_proto.Messages().ByName("DecisionPolicyWindows") + fd_DecisionPolicyWindows_voting_period = md_DecisionPolicyWindows.Fields().ByName("voting_period") + fd_DecisionPolicyWindows_min_execution_period = md_DecisionPolicyWindows.Fields().ByName("min_execution_period") +} + +var _ protoreflect.Message = (*fastReflection_DecisionPolicyWindows)(nil) + +type fastReflection_DecisionPolicyWindows DecisionPolicyWindows + +func (x *DecisionPolicyWindows) ProtoReflect() protoreflect.Message { + return (*fastReflection_DecisionPolicyWindows)(x) +} + +func (x *DecisionPolicyWindows) slowProtoReflect() protoreflect.Message { + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +var _fastReflection_DecisionPolicyWindows_messageType fastReflection_DecisionPolicyWindows_messageType +var _ protoreflect.MessageType = fastReflection_DecisionPolicyWindows_messageType{} + +type fastReflection_DecisionPolicyWindows_messageType struct{} + +func (x fastReflection_DecisionPolicyWindows_messageType) Zero() protoreflect.Message { + return (*fastReflection_DecisionPolicyWindows)(nil) +} +func (x fastReflection_DecisionPolicyWindows_messageType) New() protoreflect.Message { + return new(fastReflection_DecisionPolicyWindows) +} +func (x fastReflection_DecisionPolicyWindows_messageType) Descriptor() protoreflect.MessageDescriptor { + return md_DecisionPolicyWindows +} + +// Descriptor returns message descriptor, which contains only the protobuf +// type information for the message. +func (x *fastReflection_DecisionPolicyWindows) Descriptor() protoreflect.MessageDescriptor { + return md_DecisionPolicyWindows +} + +// Type returns the message type, which encapsulates both Go and protobuf +// type information. If the Go type information is not needed, +// it is recommended that the message descriptor be used instead. +func (x *fastReflection_DecisionPolicyWindows) Type() protoreflect.MessageType { + return _fastReflection_DecisionPolicyWindows_messageType +} + +// New returns a newly allocated and mutable empty message. +func (x *fastReflection_DecisionPolicyWindows) New() protoreflect.Message { + return new(fastReflection_DecisionPolicyWindows) +} + +// Interface unwraps the message reflection interface and +// returns the underlying ProtoMessage interface. +func (x *fastReflection_DecisionPolicyWindows) Interface() protoreflect.ProtoMessage { + return (*DecisionPolicyWindows)(x) +} + +// Range iterates over every populated field in an undefined order, +// calling f for each field descriptor and value encountered. +// Range returns immediately if f returns false. +// While iterating, mutating operations may only be performed +// on the current field descriptor. +func (x *fastReflection_DecisionPolicyWindows) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { + if x.VotingPeriod != nil { + value := protoreflect.ValueOfMessage(x.VotingPeriod.ProtoReflect()) + if !f(fd_DecisionPolicyWindows_voting_period, value) { + return + } + } + if x.MinExecutionPeriod != nil { + value := protoreflect.ValueOfMessage(x.MinExecutionPeriod.ProtoReflect()) + if !f(fd_DecisionPolicyWindows_min_execution_period, value) { + return + } + } +} + +// Has reports whether a field is populated. +// +// Some fields have the property of nullability where it is possible to +// distinguish between the default value of a field and whether the field +// was explicitly populated with the default value. Singular message fields, +// member fields of a oneof, and proto2 scalar fields are nullable. Such +// fields are populated only if explicitly set. +// +// In other cases (aside from the nullable cases above), +// a proto3 scalar field is populated if it contains a non-zero value, and +// a repeated field is populated if it is non-empty. +func (x *fastReflection_DecisionPolicyWindows) Has(fd protoreflect.FieldDescriptor) bool { + switch fd.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + return x.VotingPeriod != nil + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + return x.MinExecutionPeriod != nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", fd.FullName())) + } +} + +// Clear clears the field such that a subsequent Has call reports false. +// +// Clearing an extension field clears both the extension type and value +// associated with the given field number. +// +// Clear is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_DecisionPolicyWindows) Clear(fd protoreflect.FieldDescriptor) { + switch fd.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + x.VotingPeriod = nil + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + x.MinExecutionPeriod = nil + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", fd.FullName())) + } +} + +// Get retrieves the value for a field. +// +// For unpopulated scalars, it returns the default value, where +// the default value of a bytes scalar is guaranteed to be a copy. +// For unpopulated composite types, it returns an empty, read-only view +// of the value; to obtain a mutable reference, use Mutable. +func (x *fastReflection_DecisionPolicyWindows) Get(descriptor protoreflect.FieldDescriptor) protoreflect.Value { + switch descriptor.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + value := x.VotingPeriod + return protoreflect.ValueOfMessage(value.ProtoReflect()) + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + value := x.MinExecutionPeriod + return protoreflect.ValueOfMessage(value.ProtoReflect()) + default: + if descriptor.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", descriptor.FullName())) + } +} + +// Set stores the value for a field. +// +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType. +// When setting a composite type, it is unspecified whether the stored value +// aliases the source's memory in any way. If the composite value is an +// empty, read-only value, then it panics. +// +// Set is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_DecisionPolicyWindows) Set(fd protoreflect.FieldDescriptor, value protoreflect.Value) { + switch fd.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + x.VotingPeriod = value.Message().Interface().(*durationpb.Duration) + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + x.MinExecutionPeriod = value.Message().Interface().(*durationpb.Duration) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", fd.FullName())) + } +} + +// Mutable returns a mutable reference to a composite type. +// +// If the field is unpopulated, it may allocate a composite value. +// For a field belonging to a oneof, it implicitly clears any other field +// that may be currently set within the same oneof. +// For extension fields, it implicitly stores the provided ExtensionType +// if not already stored. +// It panics if the field does not contain a composite type. +// +// Mutable is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_DecisionPolicyWindows) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + if x.VotingPeriod == nil { + x.VotingPeriod = new(durationpb.Duration) + } + return protoreflect.ValueOfMessage(x.VotingPeriod.ProtoReflect()) + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + if x.MinExecutionPeriod == nil { + x.MinExecutionPeriod = new(durationpb.Duration) + } + return protoreflect.ValueOfMessage(x.MinExecutionPeriod.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", fd.FullName())) + } +} + +// NewField returns a new value that is assignable to the field +// for the given descriptor. For scalars, this returns the default value. +// For lists, maps, and messages, this returns a new, empty, mutable value. +func (x *fastReflection_DecisionPolicyWindows) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { + switch fd.FullName() { + case "cosmos.group.v1beta1.DecisionPolicyWindows.voting_period": + m := new(durationpb.Duration) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + case "cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period": + m := new(durationpb.Duration) + return protoreflect.ValueOfMessage(m.ProtoReflect()) + default: + if fd.IsExtension() { + panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.group.v1beta1.DecisionPolicyWindows")) + } + panic(fmt.Errorf("message cosmos.group.v1beta1.DecisionPolicyWindows does not contain field %s", fd.FullName())) + } +} + +// WhichOneof reports which field within the oneof is populated, +// returning nil if none are populated. +// It panics if the oneof descriptor does not belong to this message. +func (x *fastReflection_DecisionPolicyWindows) WhichOneof(d protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { + switch d.FullName() { + default: + panic(fmt.Errorf("%s is not a oneof field in cosmos.group.v1beta1.DecisionPolicyWindows", d.FullName())) + } + panic("unreachable") +} + +// GetUnknown retrieves the entire list of unknown fields. +// The caller may only mutate the contents of the RawFields +// if the mutated bytes are stored back into the message with SetUnknown. +func (x *fastReflection_DecisionPolicyWindows) GetUnknown() protoreflect.RawFields { + return x.unknownFields +} + +// SetUnknown stores an entire list of unknown fields. +// The raw fields must be syntactically valid according to the wire format. +// An implementation may panic if this is not the case. +// Once stored, the caller must not mutate the content of the RawFields. +// An empty RawFields may be passed to clear the fields. +// +// SetUnknown is a mutating operation and unsafe for concurrent use. +func (x *fastReflection_DecisionPolicyWindows) SetUnknown(fields protoreflect.RawFields) { + x.unknownFields = fields +} + +// IsValid reports whether the message is valid. +// +// An invalid message is an empty, read-only value. +// +// An invalid message often corresponds to a nil pointer of the concrete +// message type, but the details are implementation dependent. +// Validity is not part of the protobuf data model, and may not +// be preserved in marshaling or other operations. +func (x *fastReflection_DecisionPolicyWindows) IsValid() bool { + return x != nil +} + +// ProtoMethods returns optional fastReflectionFeature-path implementations of various operations. +// This method may return nil. +// +// The returned methods type is identical to +// "google.golang.org/protobuf/runtime/protoiface".Methods. +// Consult the protoiface package documentation for details. +func (x *fastReflection_DecisionPolicyWindows) ProtoMethods() *protoiface.Methods { + size := func(input protoiface.SizeInput) protoiface.SizeOutput { + x := input.Message.Interface().(*DecisionPolicyWindows) + if x == nil { + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: 0, + } + } + options := runtime.SizeInputToOptions(input) + _ = options + var n int + var l int + _ = l + if x.VotingPeriod != nil { + l = options.Size(x.VotingPeriod) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.MinExecutionPeriod != nil { + l = options.Size(x.MinExecutionPeriod) + n += 1 + l + runtime.Sov(uint64(l)) + } + if x.unknownFields != nil { + n += len(x.unknownFields) + } + return protoiface.SizeOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Size: n, + } + } + + marshal := func(input protoiface.MarshalInput) (protoiface.MarshalOutput, error) { + x := input.Message.Interface().(*DecisionPolicyWindows) + if x == nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + options := runtime.MarshalInputToOptions(input) + _ = options + size := options.Size(x) + dAtA := make([]byte, size) + i := len(dAtA) + _ = i + var l int + _ = l + if x.unknownFields != nil { + i -= len(x.unknownFields) + copy(dAtA[i:], x.unknownFields) + } + if x.MinExecutionPeriod != nil { + encoded, err := options.Marshal(x.MinExecutionPeriod) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0x12 + } + if x.VotingPeriod != nil { + encoded, err := options.Marshal(x.VotingPeriod) + if err != nil { + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, err + } + i -= len(encoded) + copy(dAtA[i:], encoded) + i = runtime.EncodeVarint(dAtA, i, uint64(len(encoded))) + i-- + dAtA[i] = 0xa + } + if input.Buf != nil { + input.Buf = append(input.Buf, dAtA...) + } else { + input.Buf = dAtA + } + return protoiface.MarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Buf: input.Buf, + }, nil + } + unmarshal := func(input protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) { + x := input.Message.Interface().(*DecisionPolicyWindows) + if x == nil { + return protoiface.UnmarshalOutput{ + NoUnkeyedLiterals: input.NoUnkeyedLiterals, + Flags: input.Flags, + }, nil + } + options := runtime.UnmarshalInputToOptions(input) + _ = options + dAtA := input.Buf + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, 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 protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: DecisionPolicyWindows: wiretype end group for non-group") + } + if fieldNum <= 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: DecisionPolicyWindows: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field VotingPeriod", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrIntOverflow + } + if iNdEx >= l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength + } + if postIndex > l { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF + } + if x.VotingPeriod == nil { + x.VotingPeriod = &durationpb.Duration{} + } + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.VotingPeriod); err != nil { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field MinExecutionPeriod", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -2094,10 +2608,10 @@ func (x *fastReflection_PercentageDecisionPolicy) ProtoMethods() *protoiface.Met if postIndex > l { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF } - if x.Timeout == nil { - x.Timeout = &durationpb.Duration{} + if x.MinExecutionPeriod == nil { + x.MinExecutionPeriod = &durationpb.Duration{} } - if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Timeout); err != nil { + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.MinExecutionPeriod); err != nil { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex @@ -2166,7 +2680,7 @@ func (x *GroupInfo) ProtoReflect() protoreflect.Message { } func (x *GroupInfo) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[4] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2881,7 +3395,7 @@ func (x *GroupMember) ProtoReflect() protoreflect.Message { } func (x *GroupMember) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[5] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3374,7 +3888,7 @@ func (x *GroupPolicyInfo) ProtoReflect() protoreflect.Message { } func (x *GroupPolicyInfo) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[6] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4253,7 +4767,7 @@ var ( fd_Proposal_status protoreflect.FieldDescriptor fd_Proposal_result protoreflect.FieldDescriptor fd_Proposal_final_tally_result protoreflect.FieldDescriptor - fd_Proposal_timeout protoreflect.FieldDescriptor + fd_Proposal_voting_period_end protoreflect.FieldDescriptor fd_Proposal_executor_result protoreflect.FieldDescriptor fd_Proposal_messages protoreflect.FieldDescriptor ) @@ -4271,7 +4785,7 @@ func init() { fd_Proposal_status = md_Proposal.Fields().ByName("status") fd_Proposal_result = md_Proposal.Fields().ByName("result") fd_Proposal_final_tally_result = md_Proposal.Fields().ByName("final_tally_result") - fd_Proposal_timeout = md_Proposal.Fields().ByName("timeout") + fd_Proposal_voting_period_end = md_Proposal.Fields().ByName("voting_period_end") fd_Proposal_executor_result = md_Proposal.Fields().ByName("executor_result") fd_Proposal_messages = md_Proposal.Fields().ByName("messages") } @@ -4285,7 +4799,7 @@ func (x *Proposal) ProtoReflect() protoreflect.Message { } func (x *Proposal) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[7] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4401,9 +4915,9 @@ func (x *fastReflection_Proposal) Range(f func(protoreflect.FieldDescriptor, pro return } } - if x.Timeout != nil { - value := protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) - if !f(fd_Proposal_timeout, value) { + if x.VotingPeriodEnd != nil { + value := protoreflect.ValueOfMessage(x.VotingPeriodEnd.ProtoReflect()) + if !f(fd_Proposal_voting_period_end, value) { return } } @@ -4454,8 +4968,8 @@ func (x *fastReflection_Proposal) Has(fd protoreflect.FieldDescriptor) bool { return x.Result != 0 case "cosmos.group.v1beta1.Proposal.final_tally_result": return x.FinalTallyResult != nil - case "cosmos.group.v1beta1.Proposal.timeout": - return x.Timeout != nil + case "cosmos.group.v1beta1.Proposal.voting_period_end": + return x.VotingPeriodEnd != nil case "cosmos.group.v1beta1.Proposal.executor_result": return x.ExecutorResult != 0 case "cosmos.group.v1beta1.Proposal.messages": @@ -4496,8 +5010,8 @@ func (x *fastReflection_Proposal) Clear(fd protoreflect.FieldDescriptor) { x.Result = 0 case "cosmos.group.v1beta1.Proposal.final_tally_result": x.FinalTallyResult = nil - case "cosmos.group.v1beta1.Proposal.timeout": - x.Timeout = nil + case "cosmos.group.v1beta1.Proposal.voting_period_end": + x.VotingPeriodEnd = nil case "cosmos.group.v1beta1.Proposal.executor_result": x.ExecutorResult = 0 case "cosmos.group.v1beta1.Proposal.messages": @@ -4551,8 +5065,8 @@ func (x *fastReflection_Proposal) Get(descriptor protoreflect.FieldDescriptor) p case "cosmos.group.v1beta1.Proposal.final_tally_result": value := x.FinalTallyResult return protoreflect.ValueOfMessage(value.ProtoReflect()) - case "cosmos.group.v1beta1.Proposal.timeout": - value := x.Timeout + case "cosmos.group.v1beta1.Proposal.voting_period_end": + value := x.VotingPeriodEnd return protoreflect.ValueOfMessage(value.ProtoReflect()) case "cosmos.group.v1beta1.Proposal.executor_result": value := x.ExecutorResult @@ -4605,8 +5119,8 @@ func (x *fastReflection_Proposal) Set(fd protoreflect.FieldDescriptor, value pro x.Result = (ProposalResult)(value.Enum()) case "cosmos.group.v1beta1.Proposal.final_tally_result": x.FinalTallyResult = value.Message().Interface().(*TallyResult) - case "cosmos.group.v1beta1.Proposal.timeout": - x.Timeout = value.Message().Interface().(*timestamppb.Timestamp) + case "cosmos.group.v1beta1.Proposal.voting_period_end": + x.VotingPeriodEnd = value.Message().Interface().(*timestamppb.Timestamp) case "cosmos.group.v1beta1.Proposal.executor_result": x.ExecutorResult = (ProposalExecutorResult)(value.Enum()) case "cosmos.group.v1beta1.Proposal.messages": @@ -4649,11 +5163,11 @@ func (x *fastReflection_Proposal) Mutable(fd protoreflect.FieldDescriptor) proto x.FinalTallyResult = new(TallyResult) } return protoreflect.ValueOfMessage(x.FinalTallyResult.ProtoReflect()) - case "cosmos.group.v1beta1.Proposal.timeout": - if x.Timeout == nil { - x.Timeout = new(timestamppb.Timestamp) + case "cosmos.group.v1beta1.Proposal.voting_period_end": + if x.VotingPeriodEnd == nil { + x.VotingPeriodEnd = new(timestamppb.Timestamp) } - return protoreflect.ValueOfMessage(x.Timeout.ProtoReflect()) + return protoreflect.ValueOfMessage(x.VotingPeriodEnd.ProtoReflect()) case "cosmos.group.v1beta1.Proposal.messages": if x.Messages == nil { x.Messages = []*anypb.Any{} @@ -4712,7 +5226,7 @@ func (x *fastReflection_Proposal) NewField(fd protoreflect.FieldDescriptor) prot case "cosmos.group.v1beta1.Proposal.final_tally_result": m := new(TallyResult) return protoreflect.ValueOfMessage(m.ProtoReflect()) - case "cosmos.group.v1beta1.Proposal.timeout": + case "cosmos.group.v1beta1.Proposal.voting_period_end": m := new(timestamppb.Timestamp) return protoreflect.ValueOfMessage(m.ProtoReflect()) case "cosmos.group.v1beta1.Proposal.executor_result": @@ -4826,8 +5340,8 @@ func (x *fastReflection_Proposal) ProtoMethods() *protoiface.Methods { l = options.Size(x.FinalTallyResult) n += 1 + l + runtime.Sov(uint64(l)) } - if x.Timeout != nil { - l = options.Size(x.Timeout) + if x.VotingPeriodEnd != nil { + l = options.Size(x.VotingPeriodEnd) n += 1 + l + runtime.Sov(uint64(l)) } if x.ExecutorResult != 0 { @@ -4889,8 +5403,8 @@ func (x *fastReflection_Proposal) ProtoMethods() *protoiface.Methods { i-- dAtA[i] = 0x60 } - if x.Timeout != nil { - encoded, err := options.Marshal(x.Timeout) + if x.VotingPeriodEnd != nil { + encoded, err := options.Marshal(x.VotingPeriodEnd) if err != nil { return protoiface.MarshalOutput{ NoUnkeyedLiterals: input.NoUnkeyedLiterals, @@ -5293,7 +5807,7 @@ func (x *fastReflection_Proposal) ProtoMethods() *protoiface.Methods { iNdEx = postIndex case 11: if wireType != 2 { - return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) + return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field VotingPeriodEnd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -5320,10 +5834,10 @@ func (x *fastReflection_Proposal) ProtoMethods() *protoiface.Methods { if postIndex > l { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, io.ErrUnexpectedEOF } - if x.Timeout == nil { - x.Timeout = ×tamppb.Timestamp{} + if x.VotingPeriodEnd == nil { + x.VotingPeriodEnd = ×tamppb.Timestamp{} } - if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.Timeout); err != nil { + if err := options.Unmarshal(dAtA[iNdEx:postIndex], x.VotingPeriodEnd); err != nil { return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, err } iNdEx = postIndex @@ -5441,7 +5955,7 @@ func (x *TallyResult) ProtoReflect() protoreflect.Message { } func (x *TallyResult) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[8] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6055,7 +6569,7 @@ func (x *Vote) ProtoReflect() protoreflect.Message { } func (x *Vote) slowProtoReflect() protoreflect.Message { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[9] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7046,9 +7560,8 @@ 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 *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + // windows defines the different windows for voting and execution. + Windows *DecisionPolicyWindows `protobuf:"bytes,2,opt,name=windows,proto3" json:"windows,omitempty"` } func (x *ThresholdDecisionPolicy) Reset() { @@ -7078,9 +7591,9 @@ func (x *ThresholdDecisionPolicy) GetThreshold() string { return "" } -func (x *ThresholdDecisionPolicy) GetTimeout() *durationpb.Duration { +func (x *ThresholdDecisionPolicy) GetWindows() *DecisionPolicyWindows { if x != nil { - return x.Timeout + return x.Windows } return nil } @@ -7093,9 +7606,8 @@ 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 *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` + // windows defines the different windows for voting and execution. + Windows *DecisionPolicyWindows `protobuf:"bytes,2,opt,name=windows,proto3" json:"windows,omitempty"` } func (x *PercentageDecisionPolicy) Reset() { @@ -7125,9 +7637,66 @@ func (x *PercentageDecisionPolicy) GetPercentage() string { return "" } -func (x *PercentageDecisionPolicy) GetTimeout() *durationpb.Duration { +func (x *PercentageDecisionPolicy) GetWindows() *DecisionPolicyWindows { if x != nil { - return x.Timeout + return x.Windows + } + return nil +} + +// DecisionPolicyWindows defines the different windows for voting and execution. +type DecisionPolicyWindows struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 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 *durationpb.Duration `protobuf:"bytes,1,opt,name=voting_period,json=votingPeriod,proto3" json:"voting_period,omitempty"` + // 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 *durationpb.Duration `protobuf:"bytes,2,opt,name=min_execution_period,json=minExecutionPeriod,proto3" json:"min_execution_period,omitempty"` +} + +func (x *DecisionPolicyWindows) Reset() { + *x = DecisionPolicyWindows{} + if protoimpl.UnsafeEnabled { + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DecisionPolicyWindows) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DecisionPolicyWindows) ProtoMessage() {} + +// Deprecated: Use DecisionPolicyWindows.ProtoReflect.Descriptor instead. +func (*DecisionPolicyWindows) Descriptor() ([]byte, []int) { + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{4} +} + +func (x *DecisionPolicyWindows) GetVotingPeriod() *durationpb.Duration { + if x != nil { + return x.VotingPeriod + } + return nil +} + +func (x *DecisionPolicyWindows) GetMinExecutionPeriod() *durationpb.Duration { + if x != nil { + return x.MinExecutionPeriod } return nil } @@ -7158,7 +7727,7 @@ type GroupInfo struct { func (x *GroupInfo) Reset() { *x = GroupInfo{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[4] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7172,7 +7741,7 @@ func (*GroupInfo) ProtoMessage() {} // Deprecated: Use GroupInfo.ProtoReflect.Descriptor instead. func (*GroupInfo) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{4} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{5} } func (x *GroupInfo) GetId() uint64 { @@ -7232,7 +7801,7 @@ type GroupMember struct { func (x *GroupMember) Reset() { *x = GroupMember{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[5] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7246,7 +7815,7 @@ func (*GroupMember) ProtoMessage() {} // Deprecated: Use GroupMember.ProtoReflect.Descriptor instead. func (*GroupMember) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{5} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{6} } func (x *GroupMember) GetGroupId() uint64 { @@ -7289,7 +7858,7 @@ type GroupPolicyInfo struct { func (x *GroupPolicyInfo) Reset() { *x = GroupPolicyInfo{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[6] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7303,7 +7872,7 @@ func (*GroupPolicyInfo) ProtoMessage() {} // Deprecated: Use GroupPolicyInfo.ProtoReflect.Descriptor instead. func (*GroupPolicyInfo) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{6} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{7} } func (x *GroupPolicyInfo) GetAddress() string { @@ -7390,11 +7959,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,omitempty"` - // 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 *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=timeout,proto3" json:"timeout,omitempty"` + // 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 *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=voting_period_end,json=votingPeriodEnd,proto3" json:"voting_period_end,omitempty"` // 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. @@ -7404,7 +7974,7 @@ type Proposal struct { func (x *Proposal) Reset() { *x = Proposal{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[7] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7418,7 +7988,7 @@ func (*Proposal) ProtoMessage() {} // Deprecated: Use Proposal.ProtoReflect.Descriptor instead. func (*Proposal) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{7} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{8} } func (x *Proposal) GetId() uint64 { @@ -7491,9 +8061,9 @@ func (x *Proposal) GetFinalTallyResult() *TallyResult { return nil } -func (x *Proposal) GetTimeout() *timestamppb.Timestamp { +func (x *Proposal) GetVotingPeriodEnd() *timestamppb.Timestamp { if x != nil { - return x.Timeout + return x.VotingPeriodEnd } return nil } @@ -7531,7 +8101,7 @@ type TallyResult struct { func (x *TallyResult) Reset() { *x = TallyResult{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[8] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7545,7 +8115,7 @@ func (*TallyResult) ProtoMessage() {} // Deprecated: Use TallyResult.ProtoReflect.Descriptor instead. func (*TallyResult) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{8} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{9} } func (x *TallyResult) GetYesCount() string { @@ -7597,7 +8167,7 @@ type Vote struct { func (x *Vote) Reset() { *x = Vote{} if protoimpl.UnsafeEnabled { - mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[9] + mi := &file_cosmos_group_v1beta1_types_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7611,7 +8181,7 @@ func (*Vote) ProtoMessage() {} // Deprecated: Use Vote.ProtoReflect.Descriptor instead. func (*Vote) Descriptor() ([]byte, []int) { - return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{9} + return file_cosmos_group_v1beta1_types_proto_rawDescGZIP(), []int{10} } func (x *Vote) GetProposalId() uint64 { @@ -7680,195 +8250,209 @@ var file_cosmos_group_v1beta1_types_proto_rawDesc = []byte{ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x62, 0x65, - 0x72, 0x73, 0x22, 0x8a, 0x01, 0x0a, 0x17, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, + 0x72, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x17, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x3d, 0x0a, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x98, 0xdf, - 0x1f, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3a, 0x12, 0xca, 0xb4, 0x2d, - 0x0e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, - 0x8d, 0x01, 0x0a, 0x18, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x44, 0x65, - 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1e, 0x0a, 0x0a, - 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x12, 0x3d, 0x0a, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x98, 0xdf, - 0x1f, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x3a, 0x12, 0xca, 0xb4, 0x2d, + 0x09, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x12, 0x45, 0x0a, 0x07, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, + 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, + 0x6f, 0x77, 0x73, 0x3a, 0x12, 0xca, 0xb4, 0x2d, 0x0e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x95, 0x01, 0x0a, 0x18, 0x50, 0x65, 0x72, 0x63, + 0x65, 0x6e, 0x74, 0x61, 0x67, 0x65, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x61, + 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x61, 0x67, 0x65, 0x12, 0x45, 0x0a, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, + 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x63, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x77, 0x73, 0x52, 0x07, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x3a, 0x12, 0xca, 0xb4, 0x2d, 0x0e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, - 0xe9, 0x01, 0x0a, 0x09, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, - 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, - 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x1a, 0x0a, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x64, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, - 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x5e, 0x0a, 0x0b, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x6d, - 0x62, 0x65, 0x72, 0x52, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0xe8, 0x02, 0x0a, 0x0f, - 0x47, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x32, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x2e, - 0x0a, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, - 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x1a, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x0f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x41, 0x6e, 0x79, 0x42, 0x12, 0xca, 0xb4, 0x2d, 0x0e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0xb8, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x12, 0x48, 0x0a, 0x0d, 0x76, 0x6f, 0x74, + 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, 0x1f, + 0x00, 0x98, 0xdf, 0x1f, 0x01, 0x52, 0x0c, 0x76, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x65, 0x72, + 0x69, 0x6f, 0x64, 0x12, 0x55, 0x0a, 0x14, 0x6d, 0x69, 0x6e, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x08, 0xc8, 0xde, + 0x1f, 0x00, 0x98, 0xdf, 0x1f, 0x01, 0x52, 0x12, 0x6d, 0x69, 0x6e, 0x45, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x22, 0xe9, 0x01, 0x0a, 0x09, 0x47, + 0x72, 0x6f, 0x75, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x64, 0x6d, 0x69, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, + 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, + 0x67, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, + 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x57, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x09, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0x5e, 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4d, + 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, + 0x12, 0x34, 0x0a, 0x06, 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, + 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52, 0x06, + 0x6d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x22, 0xe8, 0x02, 0x0a, 0x0f, 0x47, 0x72, 0x6f, 0x75, 0x70, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, + 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x19, + 0x0a, 0x08, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x05, 0x61, 0x64, 0x6d, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x05, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x51, 0x0a, 0x0f, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x42, 0x12, + 0xca, 0xb4, 0x2d, 0x0e, 0x44, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x0e, 0x64, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x12, 0x43, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x09, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x08, 0x88, 0xa0, 0x1f, 0x00, 0xe8, 0xa0, 0x1f, + 0x01, 0x22, 0xf4, 0x05, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x32, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x36, + 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x72, 0x73, 0x12, 0x45, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, - 0x01, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x3a, 0x08, 0x88, 0xa0, - 0x1f, 0x00, 0xe8, 0xa0, 0x1f, 0x01, 0x22, 0xe2, 0x05, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x32, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, - 0x6f, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, - 0x52, 0x09, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x73, 0x12, 0x45, 0x0a, 0x0b, 0x73, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, - 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, - 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, - 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, - 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x55, 0x0a, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x74, - 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, - 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x42, 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x10, 0x66, 0x69, 0x6e, 0x61, - 0x6c, 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x3e, 0x0a, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x01, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, + 0x0d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x14, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x12, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x12, 0x3c, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x55, 0x0a, 0x12, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x74, 0x61, 0x6c, 0x6c, 0x79, 0x5f, + 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x2e, 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x42, + 0x04, 0xc8, 0xde, 0x1f, 0x00, 0x52, 0x10, 0x66, 0x69, 0x6e, 0x61, 0x6c, 0x54, 0x61, 0x6c, 0x6c, + 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x50, 0x0a, 0x11, 0x76, 0x6f, 0x74, 0x69, 0x6e, + 0x67, 0x5f, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, + 0xc8, 0xde, 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x0f, 0x76, 0x6f, 0x74, 0x69, 0x6e, 0x67, + 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x45, 0x6e, 0x64, 0x12, 0x55, 0x0a, 0x0f, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x0c, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x61, 0x6c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x3a, 0x04, 0x88, 0xa0, 0x1f, 0x00, 0x22, 0x9d, 0x01, 0x0a, 0x0b, 0x54, 0x61, 0x6c, + 0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x79, 0x65, 0x73, 0x5f, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x79, 0x65, 0x73, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x62, + 0x73, 0x74, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x6f, + 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x6f, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x6f, 0x5f, 0x77, 0x69, 0x74, 0x68, + 0x5f, 0x76, 0x65, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x6e, 0x6f, 0x57, 0x69, 0x74, 0x68, 0x56, 0x65, 0x74, 0x6f, 0x43, 0x6f, 0x75, + 0x6e, 0x74, 0x3a, 0x04, 0x88, 0xa0, 0x1f, 0x00, 0x22, 0xf4, 0x01, 0x0a, 0x04, 0x56, 0x6f, 0x74, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, + 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x6f, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, 0x76, 0x6f, 0x74, + 0x65, 0x72, 0x12, 0x38, 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x6f, 0x74, 0x65, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x45, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, 0x1f, 0x00, 0x90, - 0xdf, 0x1f, 0x01, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x55, 0x0a, 0x0f, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x52, 0x0e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, - 0x75, 0x6c, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, - 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x3a, 0x04, 0x88, 0xa0, 0x1f, 0x00, 0x22, 0x9d, 0x01, 0x0a, 0x0b, - 0x54, 0x61, 0x6c, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x79, - 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x79, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x62, 0x73, 0x74, - 0x61, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0c, 0x61, 0x62, 0x73, 0x74, 0x61, 0x69, 0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, - 0x08, 0x6e, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6e, 0x6f, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x6f, 0x5f, 0x77, - 0x69, 0x74, 0x68, 0x5f, 0x76, 0x65, 0x74, 0x6f, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x6f, 0x57, 0x69, 0x74, 0x68, 0x56, 0x65, 0x74, 0x6f, - 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3a, 0x04, 0x88, 0xa0, 0x1f, 0x00, 0x22, 0xf4, 0x01, 0x0a, 0x04, - 0x56, 0x6f, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x61, 0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x6f, 0x74, 0x65, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x18, 0xd2, 0xb4, 0x2d, 0x14, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, - 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x05, - 0x76, 0x6f, 0x74, 0x65, 0x72, 0x12, 0x38, 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, - 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x56, 0x6f, 0x74, - 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x45, 0x0a, 0x0b, 0x73, - 0x75, 0x62, 0x6d, 0x69, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xc8, 0xde, - 0x1f, 0x00, 0x90, 0xdf, 0x1f, 0x01, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x2a, 0x8f, 0x01, 0x0a, 0x0a, 0x56, 0x6f, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x59, 0x45, - 0x53, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x41, 0x42, 0x53, 0x54, 0x41, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, - 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x10, 0x03, - 0x12, 0x1c, 0x0a, 0x18, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x4e, 0x4f, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x5f, 0x56, 0x45, 0x54, 0x4f, 0x10, 0x04, 0x1a, 0x04, - 0x88, 0xa3, 0x1e, 0x00, 0x2a, 0xae, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, - 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x50, 0x4f, - 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, - 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x50, - 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x42, 0x4d, - 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50, 0x4f, - 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, - 0x44, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x03, - 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x55, 0x53, 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x4e, 0x10, 0x04, 0x1a, - 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x2a, 0x94, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x61, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x50, - 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, - 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x46, - 0x49, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, - 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x41, 0x43, - 0x43, 0x45, 0x50, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x50, - 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x52, 0x45, 0x4a, 0x45, - 0x43, 0x54, 0x45, 0x44, 0x10, 0x03, 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x2a, 0xba, 0x01, 0x0a, - 0x16, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, - 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x24, 0x50, 0x52, 0x4f, 0x50, 0x4f, - 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, - 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x4e, 0x4f, - 0x54, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x50, 0x4f, - 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, - 0x55, 0x4c, 0x54, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x24, 0x0a, - 0x20, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, - 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, - 0x45, 0x10, 0x03, 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x42, 0xdc, 0x01, 0x0a, 0x18, 0x63, 0x6f, - 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, - 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, - 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, - 0x64, 0x6b, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x67, 0x72, - 0x6f, 0x75, 0x70, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x47, 0x58, 0xaa, 0x02, - 0x14, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x56, 0x31, - 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x14, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, - 0x72, 0x6f, 0x75, 0x70, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x20, 0x43, - 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x5c, 0x56, 0x31, 0x62, 0x65, - 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, - 0x02, 0x16, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x3a, - 0x3a, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0xdf, 0x1f, 0x01, 0x52, 0x0a, 0x73, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x2a, + 0x8f, 0x01, 0x0a, 0x0a, 0x56, 0x6f, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, + 0x0a, 0x17, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x56, + 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x59, 0x45, 0x53, 0x10, 0x01, + 0x12, 0x17, 0x0a, 0x13, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, + 0x41, 0x42, 0x53, 0x54, 0x41, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4f, 0x54, + 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x10, 0x03, 0x12, 0x1c, 0x0a, + 0x18, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x5f, + 0x57, 0x49, 0x54, 0x48, 0x5f, 0x56, 0x45, 0x54, 0x4f, 0x10, 0x04, 0x1a, 0x04, 0x88, 0xa3, 0x1e, + 0x00, 0x2a, 0xae, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, + 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, + 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55, 0x42, 0x4d, 0x49, 0x54, 0x54, + 0x45, 0x44, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, + 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x02, + 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x55, 0x53, 0x5f, 0x41, 0x42, 0x4f, 0x52, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1d, 0x0a, + 0x19, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, + 0x5f, 0x57, 0x49, 0x54, 0x48, 0x44, 0x52, 0x41, 0x57, 0x4e, 0x10, 0x04, 0x1a, 0x04, 0x88, 0xa3, + 0x1e, 0x00, 0x2a, 0x94, 0x01, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, + 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1f, 0x0a, 0x1b, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, + 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x55, 0x4e, 0x46, 0x49, 0x4e, 0x41, + 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x50, 0x4f, + 0x53, 0x41, 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, + 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, + 0x4c, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, + 0x44, 0x10, 0x03, 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x2a, 0xba, 0x01, 0x0a, 0x16, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x12, 0x28, 0x0a, 0x24, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, + 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, + 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, + 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, + 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x52, + 0x55, 0x4e, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, + 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, + 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x24, 0x0a, 0x20, 0x50, 0x52, + 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x5f, + 0x52, 0x45, 0x53, 0x55, 0x4c, 0x54, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x55, 0x52, 0x45, 0x10, 0x03, + 0x1a, 0x04, 0x88, 0xa3, 0x1e, 0x00, 0x42, 0xdc, 0x01, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x63, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x76, 0x31, 0x62, 0x65, + 0x74, 0x61, 0x31, 0x42, 0x0a, 0x54, 0x79, 0x70, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x42, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2d, 0x73, 0x64, 0x6b, 0x2f, + 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x76, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x47, 0x58, 0xaa, 0x02, 0x14, 0x43, 0x6f, + 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x56, 0x31, 0x62, 0x65, 0x74, + 0x61, 0x31, 0xca, 0x02, 0x14, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x72, 0x6f, 0x75, + 0x70, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x20, 0x43, 0x6f, 0x73, 0x6d, + 0x6f, 0x73, 0x5c, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x5c, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x16, 0x43, + 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x3a, 0x3a, 0x56, 0x31, + 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -7884,7 +8468,7 @@ func file_cosmos_group_v1beta1_types_proto_rawDescGZIP() []byte { } var file_cosmos_group_v1beta1_types_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_cosmos_group_v1beta1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_cosmos_group_v1beta1_types_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_cosmos_group_v1beta1_types_proto_goTypes = []interface{}{ (VoteOption)(0), // 0: cosmos.group.v1beta1.VoteOption (ProposalStatus)(0), // 1: cosmos.group.v1beta1.ProposalStatus @@ -7894,39 +8478,42 @@ var file_cosmos_group_v1beta1_types_proto_goTypes = []interface{}{ (*Members)(nil), // 5: cosmos.group.v1beta1.Members (*ThresholdDecisionPolicy)(nil), // 6: cosmos.group.v1beta1.ThresholdDecisionPolicy (*PercentageDecisionPolicy)(nil), // 7: cosmos.group.v1beta1.PercentageDecisionPolicy - (*GroupInfo)(nil), // 8: cosmos.group.v1beta1.GroupInfo - (*GroupMember)(nil), // 9: cosmos.group.v1beta1.GroupMember - (*GroupPolicyInfo)(nil), // 10: cosmos.group.v1beta1.GroupPolicyInfo - (*Proposal)(nil), // 11: cosmos.group.v1beta1.Proposal - (*TallyResult)(nil), // 12: cosmos.group.v1beta1.TallyResult - (*Vote)(nil), // 13: cosmos.group.v1beta1.Vote - (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp - (*durationpb.Duration)(nil), // 15: google.protobuf.Duration - (*anypb.Any)(nil), // 16: google.protobuf.Any + (*DecisionPolicyWindows)(nil), // 8: cosmos.group.v1beta1.DecisionPolicyWindows + (*GroupInfo)(nil), // 9: cosmos.group.v1beta1.GroupInfo + (*GroupMember)(nil), // 10: cosmos.group.v1beta1.GroupMember + (*GroupPolicyInfo)(nil), // 11: cosmos.group.v1beta1.GroupPolicyInfo + (*Proposal)(nil), // 12: cosmos.group.v1beta1.Proposal + (*TallyResult)(nil), // 13: cosmos.group.v1beta1.TallyResult + (*Vote)(nil), // 14: cosmos.group.v1beta1.Vote + (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 16: google.protobuf.Duration + (*anypb.Any)(nil), // 17: google.protobuf.Any } var file_cosmos_group_v1beta1_types_proto_depIdxs = []int32{ - 14, // 0: cosmos.group.v1beta1.Member.added_at:type_name -> google.protobuf.Timestamp + 15, // 0: cosmos.group.v1beta1.Member.added_at:type_name -> google.protobuf.Timestamp 4, // 1: cosmos.group.v1beta1.Members.members:type_name -> cosmos.group.v1beta1.Member - 15, // 2: cosmos.group.v1beta1.ThresholdDecisionPolicy.timeout:type_name -> google.protobuf.Duration - 15, // 3: cosmos.group.v1beta1.PercentageDecisionPolicy.timeout:type_name -> google.protobuf.Duration - 14, // 4: cosmos.group.v1beta1.GroupInfo.created_at:type_name -> google.protobuf.Timestamp - 4, // 5: cosmos.group.v1beta1.GroupMember.member:type_name -> cosmos.group.v1beta1.Member - 16, // 6: cosmos.group.v1beta1.GroupPolicyInfo.decision_policy:type_name -> google.protobuf.Any - 14, // 7: cosmos.group.v1beta1.GroupPolicyInfo.created_at:type_name -> google.protobuf.Timestamp - 14, // 8: cosmos.group.v1beta1.Proposal.submit_time:type_name -> google.protobuf.Timestamp - 1, // 9: cosmos.group.v1beta1.Proposal.status:type_name -> cosmos.group.v1beta1.ProposalStatus - 2, // 10: cosmos.group.v1beta1.Proposal.result:type_name -> cosmos.group.v1beta1.ProposalResult - 12, // 11: cosmos.group.v1beta1.Proposal.final_tally_result:type_name -> cosmos.group.v1beta1.TallyResult - 14, // 12: cosmos.group.v1beta1.Proposal.timeout:type_name -> google.protobuf.Timestamp - 3, // 13: cosmos.group.v1beta1.Proposal.executor_result:type_name -> cosmos.group.v1beta1.ProposalExecutorResult - 16, // 14: cosmos.group.v1beta1.Proposal.messages:type_name -> google.protobuf.Any - 0, // 15: cosmos.group.v1beta1.Vote.option:type_name -> cosmos.group.v1beta1.VoteOption - 14, // 16: cosmos.group.v1beta1.Vote.submit_time:type_name -> google.protobuf.Timestamp - 17, // [17:17] is the sub-list for method output_type - 17, // [17:17] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 8, // 2: cosmos.group.v1beta1.ThresholdDecisionPolicy.windows:type_name -> cosmos.group.v1beta1.DecisionPolicyWindows + 8, // 3: cosmos.group.v1beta1.PercentageDecisionPolicy.windows:type_name -> cosmos.group.v1beta1.DecisionPolicyWindows + 16, // 4: cosmos.group.v1beta1.DecisionPolicyWindows.voting_period:type_name -> google.protobuf.Duration + 16, // 5: cosmos.group.v1beta1.DecisionPolicyWindows.min_execution_period:type_name -> google.protobuf.Duration + 15, // 6: cosmos.group.v1beta1.GroupInfo.created_at:type_name -> google.protobuf.Timestamp + 4, // 7: cosmos.group.v1beta1.GroupMember.member:type_name -> cosmos.group.v1beta1.Member + 17, // 8: cosmos.group.v1beta1.GroupPolicyInfo.decision_policy:type_name -> google.protobuf.Any + 15, // 9: cosmos.group.v1beta1.GroupPolicyInfo.created_at:type_name -> google.protobuf.Timestamp + 15, // 10: cosmos.group.v1beta1.Proposal.submit_time:type_name -> google.protobuf.Timestamp + 1, // 11: cosmos.group.v1beta1.Proposal.status:type_name -> cosmos.group.v1beta1.ProposalStatus + 2, // 12: cosmos.group.v1beta1.Proposal.result:type_name -> cosmos.group.v1beta1.ProposalResult + 13, // 13: cosmos.group.v1beta1.Proposal.final_tally_result:type_name -> cosmos.group.v1beta1.TallyResult + 15, // 14: cosmos.group.v1beta1.Proposal.voting_period_end:type_name -> google.protobuf.Timestamp + 3, // 15: cosmos.group.v1beta1.Proposal.executor_result:type_name -> cosmos.group.v1beta1.ProposalExecutorResult + 17, // 16: cosmos.group.v1beta1.Proposal.messages:type_name -> google.protobuf.Any + 0, // 17: cosmos.group.v1beta1.Vote.option:type_name -> cosmos.group.v1beta1.VoteOption + 15, // 18: cosmos.group.v1beta1.Vote.submit_time:type_name -> google.protobuf.Timestamp + 19, // [19:19] is the sub-list for method output_type + 19, // [19:19] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_cosmos_group_v1beta1_types_proto_init() } @@ -7984,7 +8571,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupInfo); i { + switch v := v.(*DecisionPolicyWindows); i { case 0: return &v.state case 1: @@ -7996,7 +8583,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupMember); i { + switch v := v.(*GroupInfo); i { case 0: return &v.state case 1: @@ -8008,7 +8595,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GroupPolicyInfo); i { + switch v := v.(*GroupMember); i { case 0: return &v.state case 1: @@ -8020,7 +8607,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Proposal); i { + switch v := v.(*GroupPolicyInfo); i { case 0: return &v.state case 1: @@ -8032,7 +8619,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TallyResult); i { + switch v := v.(*Proposal); i { case 0: return &v.state case 1: @@ -8044,6 +8631,18 @@ func file_cosmos_group_v1beta1_types_proto_init() { } } file_cosmos_group_v1beta1_types_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TallyResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_cosmos_group_v1beta1_types_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Vote); i { case 0: return &v.state @@ -8062,7 +8661,7 @@ func file_cosmos_group_v1beta1_types_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cosmos_group_v1beta1_types_proto_rawDesc, NumEnums: 4, - NumMessages: 10, + NumMessages: 11, NumExtensions: 0, NumServices: 0, }, diff --git a/api/cosmos/orm/module/v1alpha1/module.pulsar.go b/api/cosmos/orm/module/v1alpha1/module.pulsar.go index 90a776bfbcf9..ca381bb9cebe 100644 --- a/api/cosmos/orm/module/v1alpha1/module.pulsar.go +++ b/api/cosmos/orm/module/v1alpha1/module.pulsar.go @@ -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 ( diff --git a/api/cosmos/orm/v1alpha1/orm.pulsar.go b/api/cosmos/orm/v1alpha1/orm.pulsar.go index b9ba3148c313..9fd3dd4265ad 100644 --- a/api/cosmos/orm/v1alpha1/orm.pulsar.go +++ b/api/cosmos/orm/v1alpha1/orm.pulsar.go @@ -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) diff --git a/api/cosmos/orm/v1alpha1/schema.pulsar.go b/api/cosmos/orm/v1alpha1/schema.pulsar.go index beb6a817c5c7..f48193210ce3 100644 --- a/api/cosmos/orm/v1alpha1/schema.pulsar.go +++ b/api/cosmos/orm/v1alpha1/schema.pulsar.go @@ -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) diff --git a/proto/cosmos/group/v1beta1/types.proto b/proto/cosmos/group/v1beta1/types.proto index aab2b93ecfc3..d32768d6b023 100644 --- a/proto/cosmos/group/v1beta1/types.proto +++ b/proto/cosmos/group/v1beta1/types.proto @@ -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; diff --git a/simapp/app.go b/simapp/app.go index a5fcc3ea2bcc..452bfd7a8f97 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -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 diff --git a/snapshots/types/snapshot.pb.go b/snapshots/types/snapshot.pb.go index 5a70e5eb8d4d..c7e427343346 100644 --- a/snapshots/types/snapshot.pb.go +++ b/snapshots/types/snapshot.pb.go @@ -306,10 +306,12 @@ func (m *SnapshotStoreItem) GetName() string { // SnapshotIAVLItem is an exported IAVL node. 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 int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + 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"` } func (m *SnapshotIAVLItem) Reset() { *m = SnapshotIAVLItem{} } diff --git a/types/tx/service.pb.go b/types/tx/service.pb.go index b33e2a7a2f87..ee8ee8ac5608 100644 --- a/types/tx/service.pb.go +++ b/types/tx/service.pb.go @@ -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"` diff --git a/x/group/client/testutil/tx.go b/x/group/client/testutil/tx.go index e51b3210f48c..f0126d152b30 100644 --- a/x/group/client/testutil/tx.go +++ b/x/group/client/testutil/tx.go @@ -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..., ), diff --git a/x/group/keeper/config.go b/x/group/config.go similarity index 57% rename from x/group/keeper/config.go rename to x/group/config.go index f162b6aaf00d..715e1a4e4488 100644 --- a/x/group/keeper/config.go +++ b/x/group/config.go @@ -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{ - MaxMetadataLen: 255, + MaxExecutionPeriod: 2 * time.Hour * 24 * 7, // Two weeks. + MaxMetadataLen: 255, } } diff --git a/x/group/genesis_test.go b/x/group/genesis_test.go index f1ab9962b10f..2300836d6faf 100644 --- a/x/group/genesis_test.go +++ b/x/group/genesis_test.go @@ -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,8 +71,8 @@ func TestGenesisStateValidate(t *testing.T) { AbstainCount: "0", NoWithVetoCount: "0", }, - Timeout: timeout, - ExecutorResult: PROPOSAL_EXECUTOR_RESULT_SUCCESS, + VotingPeriodEnd: timeout, + ExecutorResult: PROPOSAL_EXECUTOR_RESULT_SUCCESS, } err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ FromAddress: accAddr.String(), diff --git a/x/group/keeper/genesis_test.go b/x/group/keeper/genesis_test.go index 3488d65e753c..fe824025df0e 100644 --- a/x/group/keeper/genesis_test.go +++ b/x/group/keeper/genesis_test.go @@ -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,8 +97,8 @@ func (s *GenesisTestSuite) TestInitExportGenesis() { AbstainCount: "0", NoWithVetoCount: "0", }, - Timeout: timeout, - ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS, + VotingPeriodEnd: timeout, + ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_SUCCESS, } err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ FromAddress: accAddr.String(), @@ -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()) } diff --git a/x/group/keeper/grpc_query.go b/x/group/keeper/grpc_query.go index 4c037f3f5261..c9151813b886 100644 --- a/x/group/keeper/grpc_query.go +++ b/x/group/keeper/grpc_query.go @@ -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 +} diff --git a/x/group/keeper/invariants_test.go b/x/group/keeper/invariants_test.go index 20b3a7f9567d..11c3a5f940f8 100644 --- a/x/group/keeper/invariants_test.go +++ b/x/group/keeper/invariants_test.go @@ -25,10 +25,9 @@ import ( type invariantTestSuite struct { suite.Suite - ctx sdk.Context - cdc *codec.ProtoCodec - key *storetypes.KVStoreKey - blockTime time.Time + ctx sdk.Context + cdc *codec.ProtoCodec + key *storetypes.KVStoreKey } 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) diff --git a/x/group/keeper/keeper.go b/x/group/keeper/keeper.go index 48640f1c1d92..96f05a53ee12 100644 --- a/x/group/keeper/keeper.go +++ b/x/group/keeper/keeper.go @@ -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 diff --git a/x/group/keeper/keeper_test.go b/x/group/keeper/keeper_test.go index db46581aa1ec..c1349271a5f0 100644 --- a/x/group/keeper/keeper_test.go +++ b/x/group/keeper/keeper_test.go @@ -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,23 +1755,19 @@ 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 - req *group.MsgVote - doBefore func(ctx context.Context) - postRun func(sdkCtx sdk.Context) - expProposalStatus group.ProposalStatus - expResult group.ProposalResult - expExecutorResult group.ProposalExecutorResult - expErr bool + srcCtx sdk.Context + 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 // expected after tallying + expResult group.ProposalResult // expected after tallying + expExecutorResult group.ProposalExecutorResult // expected after tallying + expErr bool }{ "vote yes": { req: &group.MsgVote{ @@ -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) - s.Assert().Equal(spec.expResult, proposal.Result) - s.Assert().Equal(spec.expProposalStatus, proposal.Status) - s.Assert().Equal(spec.expExecutorResult, proposal.ExecutorResult) + 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) diff --git a/x/group/keeper/msg_server.go b/x/group/keeper/msg_server.go index b2240260224b..5cfa45eee18d 100644 --- a/x/group/keeper/msg_server.go +++ b/x/group/keeper/msg_server.go @@ -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 } } diff --git a/x/group/keeper/proposal_executor.go b/x/group/keeper/proposal_executor.go index 865919aab277..917ac575d53a 100644 --- a/x/group/keeper/proposal_executor.go +++ b/x/group/keeper/proposal_executor.go @@ -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)) diff --git a/x/group/msgs_test.go b/x/group/msgs_test.go index c15b16011fe8..0a710778ae67 100644 --- a/x/group/msgs_test.go +++ b/x/group/msgs_test.go @@ -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.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) + 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) diff --git a/x/group/simulation/genesis.go b/x/group/simulation/genesis.go index d3958bfe7d06..1f5df3a2dc61 100644 --- a/x/group/simulation/genesis.go +++ b/x/group/simulation/genesis.go @@ -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) } @@ -97,10 +97,10 @@ func getProposals(r *rand.Rand, simState *module.SimulationState) []*group.Propo AbstainCount: "1", NoWithVetoCount: "0", }, - ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, - Metadata: simtypes.RandStringOfLength(r, 50), - SubmitTime: submittedAt, - Timeout: timeout, + ExecutorResult: group.PROPOSAL_EXECUTOR_RESULT_NOT_RUN, + Metadata: simtypes.RandStringOfLength(r, 50), + SubmitTime: submittedAt, + VotingPeriodEnd: timeout, } err := proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ FromAddress: fromAddr, diff --git a/x/group/simulation/operations.go b/x/group/simulation/operations.go index 58ffed18c837..23a00540a62c 100644 --- a/x/group/simulation/operations.go +++ b/x/group/simulation/operations.go @@ -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{ diff --git a/x/group/simulation/operations_test.go b/x/group/simulation/operations_test.go index 2dcd6e8ae4bd..38d37ad5dde8 100644 --- a/x/group/simulation/operations_test.go +++ b/x/group/simulation/operations_test.go @@ -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) diff --git a/x/group/types.go b/x/group/types.go index 5f8b16004707..fb9672a5a6bc 100644 --- a/x/group/types.go +++ b/x/group/types.go @@ -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) error { +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 } // 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) { diff --git a/x/group/types.pb.go b/x/group/types.pb.go index 3f2aff68c94a..21eda27b60b1 100644 --- a/x/group/types.pb.go +++ b/x/group/types.pb.go @@ -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.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.Timeout + 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-- + dAtA[i] = 0x12 } - 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-- + dAtA[i] = 0x12 } - 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,7 +1206,7 @@ func (m *PercentageDecisionPolicy) MarshalToSizedBuffer(dAtA []byte) (int, error return len(dAtA) - i, nil } -func (m *GroupInfo) Marshal() (dAtA []byte, err error) { +func (m *DecisionPolicyWindows) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -1138,23 +1216,62 @@ func (m *GroupInfo) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *GroupInfo) MarshalTo(dAtA []byte) (int, error) { +func (m *DecisionPolicyWindows) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *GroupInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *DecisionPolicyWindows) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = 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):]) + 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) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GroupInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GroupInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = 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 + } + i -= n6 + i = encodeVarintTypes(dAtA, i, uint64(n6)) + i-- dAtA[i] = 0x32 if len(m.TotalWeight) > 0 { i -= len(m.TotalWeight) @@ -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) - n += 1 + l + sovTypes(uint64(l)) + 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,126 @@ 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 { + 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 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 { @@ -2260,7 +2516,7 @@ 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 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 diff --git a/x/group/types_test.go b/x/group/types_test.go index 1fad739e4451..d1cebc4db4cc 100644 --- a/x/group/types_test.go +++ b/x/group/types_test.go @@ -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) - require.NoError(t, err) - require.Equal(t, tc.result, policyResult) + 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) - require.NoError(t, err) - require.Equal(t, tc.result, policyResult) + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.result, policyResult) + } }) } }