feat(x/gov): optimistic proposals (#18620)

Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
This commit is contained in:
Julien Robert 2023-12-20 22:49:03 +01:00 committed by GitHub
parent 4753d16ef4
commit eb3ea8db8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1594 additions and 589 deletions

View File

@ -5644,24 +5644,72 @@ func (x *_Params_12_list) IsValid() bool {
return x.list != nil
}
var _ protoreflect.List = (*_Params_17_list)(nil)
type _Params_17_list struct {
list *[]string
}
func (x *_Params_17_list) Len() int {
if x.list == nil {
return 0
}
return len(*x.list)
}
func (x *_Params_17_list) Get(i int) protoreflect.Value {
return protoreflect.ValueOfString((*x.list)[i])
}
func (x *_Params_17_list) Set(i int, value protoreflect.Value) {
valueUnwrapped := value.String()
concreteValue := valueUnwrapped
(*x.list)[i] = concreteValue
}
func (x *_Params_17_list) Append(value protoreflect.Value) {
valueUnwrapped := value.String()
concreteValue := valueUnwrapped
*x.list = append(*x.list, concreteValue)
}
func (x *_Params_17_list) AppendMutable() protoreflect.Value {
panic(fmt.Errorf("AppendMutable can not be called on message Params at list field OptimisticAuthorizedAddresses as it is not of Message kind"))
}
func (x *_Params_17_list) Truncate(n int) {
*x.list = (*x.list)[:n]
}
func (x *_Params_17_list) NewElement() protoreflect.Value {
v := ""
return protoreflect.ValueOfString(v)
}
func (x *_Params_17_list) IsValid() bool {
return x.list != nil
}
var (
md_Params protoreflect.MessageDescriptor
fd_Params_min_deposit protoreflect.FieldDescriptor
fd_Params_max_deposit_period protoreflect.FieldDescriptor
fd_Params_voting_period protoreflect.FieldDescriptor
fd_Params_quorum protoreflect.FieldDescriptor
fd_Params_threshold protoreflect.FieldDescriptor
fd_Params_veto_threshold protoreflect.FieldDescriptor
fd_Params_min_initial_deposit_ratio protoreflect.FieldDescriptor
fd_Params_proposal_cancel_ratio protoreflect.FieldDescriptor
fd_Params_proposal_cancel_dest protoreflect.FieldDescriptor
fd_Params_expedited_voting_period protoreflect.FieldDescriptor
fd_Params_expedited_threshold protoreflect.FieldDescriptor
fd_Params_expedited_min_deposit protoreflect.FieldDescriptor
fd_Params_burn_vote_quorum protoreflect.FieldDescriptor
fd_Params_burn_proposal_deposit_prevote protoreflect.FieldDescriptor
fd_Params_burn_vote_veto protoreflect.FieldDescriptor
fd_Params_min_deposit_ratio protoreflect.FieldDescriptor
md_Params protoreflect.MessageDescriptor
fd_Params_min_deposit protoreflect.FieldDescriptor
fd_Params_max_deposit_period protoreflect.FieldDescriptor
fd_Params_voting_period protoreflect.FieldDescriptor
fd_Params_quorum protoreflect.FieldDescriptor
fd_Params_threshold protoreflect.FieldDescriptor
fd_Params_veto_threshold protoreflect.FieldDescriptor
fd_Params_min_initial_deposit_ratio protoreflect.FieldDescriptor
fd_Params_proposal_cancel_ratio protoreflect.FieldDescriptor
fd_Params_proposal_cancel_dest protoreflect.FieldDescriptor
fd_Params_expedited_voting_period protoreflect.FieldDescriptor
fd_Params_expedited_threshold protoreflect.FieldDescriptor
fd_Params_expedited_min_deposit protoreflect.FieldDescriptor
fd_Params_burn_vote_quorum protoreflect.FieldDescriptor
fd_Params_burn_proposal_deposit_prevote protoreflect.FieldDescriptor
fd_Params_burn_vote_veto protoreflect.FieldDescriptor
fd_Params_min_deposit_ratio protoreflect.FieldDescriptor
fd_Params_optimistic_authorized_addresses protoreflect.FieldDescriptor
fd_Params_optimistic_rejected_threshold protoreflect.FieldDescriptor
)
func init() {
@ -5683,6 +5731,8 @@ func init() {
fd_Params_burn_proposal_deposit_prevote = md_Params.Fields().ByName("burn_proposal_deposit_prevote")
fd_Params_burn_vote_veto = md_Params.Fields().ByName("burn_vote_veto")
fd_Params_min_deposit_ratio = md_Params.Fields().ByName("min_deposit_ratio")
fd_Params_optimistic_authorized_addresses = md_Params.Fields().ByName("optimistic_authorized_addresses")
fd_Params_optimistic_rejected_threshold = md_Params.Fields().ByName("optimistic_rejected_threshold")
}
var _ protoreflect.Message = (*fastReflection_Params)(nil)
@ -5846,6 +5896,18 @@ func (x *fastReflection_Params) Range(f func(protoreflect.FieldDescriptor, proto
return
}
}
if len(x.OptimisticAuthorizedAddresses) != 0 {
value := protoreflect.ValueOfList(&_Params_17_list{list: &x.OptimisticAuthorizedAddresses})
if !f(fd_Params_optimistic_authorized_addresses, value) {
return
}
}
if x.OptimisticRejectedThreshold != "" {
value := protoreflect.ValueOfString(x.OptimisticRejectedThreshold)
if !f(fd_Params_optimistic_rejected_threshold, value) {
return
}
}
}
// Has reports whether a field is populated.
@ -5893,6 +5955,10 @@ func (x *fastReflection_Params) Has(fd protoreflect.FieldDescriptor) bool {
return x.BurnVoteVeto != false
case "cosmos.gov.v1.Params.min_deposit_ratio":
return x.MinDepositRatio != ""
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
return len(x.OptimisticAuthorizedAddresses) != 0
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
return x.OptimisticRejectedThreshold != ""
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -5941,6 +6007,10 @@ func (x *fastReflection_Params) Clear(fd protoreflect.FieldDescriptor) {
x.BurnVoteVeto = false
case "cosmos.gov.v1.Params.min_deposit_ratio":
x.MinDepositRatio = ""
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
x.OptimisticAuthorizedAddresses = nil
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
x.OptimisticRejectedThreshold = ""
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -6011,6 +6081,15 @@ func (x *fastReflection_Params) Get(descriptor protoreflect.FieldDescriptor) pro
case "cosmos.gov.v1.Params.min_deposit_ratio":
value := x.MinDepositRatio
return protoreflect.ValueOfString(value)
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
if len(x.OptimisticAuthorizedAddresses) == 0 {
return protoreflect.ValueOfList(&_Params_17_list{})
}
listValue := &_Params_17_list{list: &x.OptimisticAuthorizedAddresses}
return protoreflect.ValueOfList(listValue)
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
value := x.OptimisticRejectedThreshold
return protoreflect.ValueOfString(value)
default:
if descriptor.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -6067,6 +6146,12 @@ func (x *fastReflection_Params) Set(fd protoreflect.FieldDescriptor, value proto
x.BurnVoteVeto = value.Bool()
case "cosmos.gov.v1.Params.min_deposit_ratio":
x.MinDepositRatio = value.Interface().(string)
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
lv := value.List()
clv := lv.(*_Params_17_list)
x.OptimisticAuthorizedAddresses = *clv.list
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
x.OptimisticRejectedThreshold = value.Interface().(string)
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -6114,6 +6199,12 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore
}
value := &_Params_12_list{list: &x.ExpeditedMinDeposit}
return protoreflect.ValueOfList(value)
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
if x.OptimisticAuthorizedAddresses == nil {
x.OptimisticAuthorizedAddresses = []string{}
}
value := &_Params_17_list{list: &x.OptimisticAuthorizedAddresses}
return protoreflect.ValueOfList(value)
case "cosmos.gov.v1.Params.quorum":
panic(fmt.Errorf("field quorum of message cosmos.gov.v1.Params is not mutable"))
case "cosmos.gov.v1.Params.threshold":
@ -6136,6 +6227,8 @@ func (x *fastReflection_Params) Mutable(fd protoreflect.FieldDescriptor) protore
panic(fmt.Errorf("field burn_vote_veto of message cosmos.gov.v1.Params is not mutable"))
case "cosmos.gov.v1.Params.min_deposit_ratio":
panic(fmt.Errorf("field min_deposit_ratio of message cosmos.gov.v1.Params is not mutable"))
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
panic(fmt.Errorf("field optimistic_rejected_threshold of message cosmos.gov.v1.Params is not mutable"))
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -6186,6 +6279,11 @@ func (x *fastReflection_Params) NewField(fd protoreflect.FieldDescriptor) protor
return protoreflect.ValueOfBool(false)
case "cosmos.gov.v1.Params.min_deposit_ratio":
return protoreflect.ValueOfString("")
case "cosmos.gov.v1.Params.optimistic_authorized_addresses":
list := []string{}
return protoreflect.ValueOfList(&_Params_17_list{list: &list})
case "cosmos.gov.v1.Params.optimistic_rejected_threshold":
return protoreflect.ValueOfString("")
default:
if fd.IsExtension() {
panic(fmt.Errorf("proto3 declared messages do not support extensions: cosmos.gov.v1.Params"))
@ -6320,6 +6418,16 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods {
if l > 0 {
n += 2 + l + runtime.Sov(uint64(l))
}
if len(x.OptimisticAuthorizedAddresses) > 0 {
for _, s := range x.OptimisticAuthorizedAddresses {
l = len(s)
n += 2 + l + runtime.Sov(uint64(l))
}
}
l = len(x.OptimisticRejectedThreshold)
if l > 0 {
n += 2 + l + runtime.Sov(uint64(l))
}
if x.unknownFields != nil {
n += len(x.unknownFields)
}
@ -6349,6 +6457,26 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods {
i -= len(x.unknownFields)
copy(dAtA[i:], x.unknownFields)
}
if len(x.OptimisticRejectedThreshold) > 0 {
i -= len(x.OptimisticRejectedThreshold)
copy(dAtA[i:], x.OptimisticRejectedThreshold)
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.OptimisticRejectedThreshold)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x92
}
if len(x.OptimisticAuthorizedAddresses) > 0 {
for iNdEx := len(x.OptimisticAuthorizedAddresses) - 1; iNdEx >= 0; iNdEx-- {
i -= len(x.OptimisticAuthorizedAddresses[iNdEx])
copy(dAtA[i:], x.OptimisticAuthorizedAddresses[iNdEx])
i = runtime.EncodeVarint(dAtA, i, uint64(len(x.OptimisticAuthorizedAddresses[iNdEx])))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x8a
}
}
if len(x.MinDepositRatio) > 0 {
i -= len(x.MinDepositRatio)
copy(dAtA[i:], x.MinDepositRatio)
@ -7052,6 +7180,70 @@ func (x *fastReflection_Params) ProtoMethods() *protoiface.Methods {
}
x.MinDepositRatio = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 17:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field OptimisticAuthorizedAddresses", wireType)
}
var stringLen 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++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
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
}
x.OptimisticAuthorizedAddresses = append(x.OptimisticAuthorizedAddresses, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 18:
if wireType != 2 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, fmt.Errorf("proto: wrong wireType = %d for field OptimisticRejectedThreshold", wireType)
}
var stringLen 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++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protoiface.UnmarshalOutput{NoUnkeyedLiterals: input.NoUnkeyedLiterals, Flags: input.Flags}, runtime.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
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
}
x.OptimisticRejectedThreshold = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := runtime.Skip(dAtA[iNdEx:])
@ -7951,6 +8143,16 @@ type Params struct {
//
// Since: cosmos-sdk 0.50
MinDepositRatio string `protobuf:"bytes,16,opt,name=min_deposit_ratio,json=minDepositRatio,proto3" json:"min_deposit_ratio,omitempty"`
// optimistic_authorized_addresses is an optional governance parameter that limits the authorized accounts than can
// submit optimistic proposals
//
// Since: x/gov v1.0.0
OptimisticAuthorizedAddresses []string `protobuf:"bytes,17,rep,name=optimistic_authorized_addresses,json=optimisticAuthorizedAddresses,proto3" json:"optimistic_authorized_addresses,omitempty"`
// optimistic rejected threshold defines at which percentage of NO votes, the optimistic proposal should fail and be
// converted to a standard proposal. The threshold is expressed as a percentage of the total bonded tokens.
//
// Since: x/gov v1.0.0
OptimisticRejectedThreshold string `protobuf:"bytes,18,opt,name=optimistic_rejected_threshold,json=optimisticRejectedThreshold,proto3" json:"optimistic_rejected_threshold,omitempty"`
}
func (x *Params) Reset() {
@ -8085,6 +8287,20 @@ func (x *Params) GetMinDepositRatio() string {
return ""
}
func (x *Params) GetOptimisticAuthorizedAddresses() []string {
if x != nil {
return x.OptimisticAuthorizedAddresses
}
return nil
}
func (x *Params) GetOptimisticRejectedThreshold() string {
if x != nil {
return x.OptimisticRejectedThreshold
}
return ""
}
var File_cosmos_gov_v1_gov_proto protoreflect.FileDescriptor
var file_cosmos_gov_v1_gov_proto_rawDesc = []byte{
@ -8230,7 +8446,7 @@ var file_cosmos_gov_v1_gov_proto_rawDesc = []byte{
0x65, 0x74, 0x6f, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x42, 0x0e, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x44, 0x65, 0x63, 0x52, 0x0d, 0x76, 0x65, 0x74, 0x6f, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f,
0x6c, 0x64, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x8f, 0x08, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x6c, 0x64, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xc5, 0x09, 0x0a, 0x06, 0x50, 0x61, 0x72, 0x61, 0x6d,
0x73, 0x12, 0x45, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x69,
@ -8295,57 +8511,68 @@ var file_cosmos_gov_v1_gov_proto_rawDesc = []byte{
0x6d, 0x69, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x5f, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0e, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2e, 0x44, 0x65, 0x63, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x44, 0x65, 0x70, 0x6f,
0x73, 0x69, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x2a, 0xa7, 0x01, 0x0a, 0x0c, 0x50, 0x72, 0x6f,
0x70, 0x6f, 0x73, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f,
0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50,
0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x4e, 0x44, 0x41,
0x52, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c,
0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54, 0x49, 0x50, 0x4c, 0x45, 0x5f, 0x43,
0x48, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x50, 0x4f,
0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4d, 0x49, 0x53,
0x54, 0x49, 0x43, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41,
0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x45, 0x44, 0x49, 0x54, 0x45, 0x44,
0x10, 0x04, 0x2a, 0xfa, 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, 0x4f, 0x4e,
0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x59, 0x45, 0x53, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4f, 0x54, 0x45,
0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x57, 0x4f, 0x10, 0x02, 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, 0x15, 0x0a, 0x11, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f,
0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x48, 0x52, 0x45, 0x45, 0x10, 0x03, 0x12, 0x12, 0x0a,
0x0e, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x10,
0x03, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e,
0x5f, 0x46, 0x4f, 0x55, 0x52, 0x10, 0x04, 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, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50,
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x50, 0x41, 0x4d, 0x10, 0x05, 0x1a, 0x02, 0x10, 0x01, 0x2a,
0xce, 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, 0x22, 0x0a, 0x1e, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f,
0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x5f, 0x50,
0x45, 0x52, 0x49, 0x4f, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x50, 0x52, 0x4f, 0x50, 0x4f,
0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x56, 0x4f, 0x54, 0x49, 0x4e,
0x47, 0x5f, 0x50, 0x45, 0x52, 0x49, 0x4f, 0x44, 0x10, 0x02, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52,
0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x41,
0x53, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53,
0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54,
0x45, 0x44, 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c,
0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x05,
0x42, 0x99, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x67, 0x6f, 0x76, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x47, 0x6f, 0x76, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x50, 0x01, 0x5a, 0x24, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x67, 0x6f, 0x76, 0x2f,
0x76, 0x31, 0x3b, 0x67, 0x6f, 0x76, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x43, 0x47, 0x58, 0xaa, 0x02,
0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x47, 0x6f, 0x76, 0x2e, 0x56, 0x31, 0xca, 0x02,
0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x6f, 0x76, 0x5c, 0x56, 0x31, 0xe2, 0x02,
0x19, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x6f, 0x76, 0x5c, 0x56, 0x31, 0x5c, 0x47,
0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0f, 0x43, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x47, 0x6f, 0x76, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x73, 0x69, 0x74, 0x52, 0x61, 0x74, 0x69, 0x6f, 0x12, 0x60, 0x0a, 0x1f, 0x6f, 0x70, 0x74, 0x69,
0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x11, 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, 0x1d, 0x6f, 0x70, 0x74,
0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65,
0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x1d, 0x6f, 0x70,
0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65,
0x64, 0x5f, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x12, 0x20, 0x01, 0x28,
0x09, 0x42, 0x0e, 0xd2, 0xb4, 0x2d, 0x0a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x44, 0x65,
0x63, 0x52, 0x1b, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x52, 0x65, 0x6a,
0x65, 0x63, 0x74, 0x65, 0x64, 0x54, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x2a, 0xa7,
0x01, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12,
0x1d, 0x0a, 0x19, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1a,
0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
0x53, 0x54, 0x41, 0x4e, 0x44, 0x41, 0x52, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x50, 0x52,
0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x55, 0x4c, 0x54,
0x49, 0x50, 0x4c, 0x45, 0x5f, 0x43, 0x48, 0x4f, 0x49, 0x43, 0x45, 0x10, 0x02, 0x12, 0x1c, 0x0a,
0x18, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f,
0x50, 0x54, 0x49, 0x4d, 0x49, 0x53, 0x54, 0x49, 0x43, 0x10, 0x03, 0x12, 0x1b, 0x0a, 0x17, 0x50,
0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50,
0x45, 0x44, 0x49, 0x54, 0x45, 0x44, 0x10, 0x04, 0x2a, 0xfa, 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, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x56, 0x4f, 0x54,
0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x59, 0x45, 0x53, 0x10, 0x01, 0x12, 0x13,
0x0a, 0x0f, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x57,
0x4f, 0x10, 0x02, 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, 0x15, 0x0a, 0x11,
0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x54, 0x48, 0x52, 0x45,
0x45, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x56, 0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x4e, 0x4f, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x56, 0x4f, 0x54, 0x45, 0x5f,
0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x46, 0x4f, 0x55, 0x52, 0x10, 0x04, 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, 0x12, 0x14, 0x0a, 0x10, 0x56,
0x4f, 0x54, 0x45, 0x5f, 0x4f, 0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x50, 0x41, 0x4d, 0x10,
0x05, 0x1a, 0x02, 0x10, 0x01, 0x2a, 0xce, 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, 0x22, 0x0a, 0x1e, 0x50, 0x52, 0x4f,
0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x50,
0x4f, 0x53, 0x49, 0x54, 0x5f, 0x50, 0x45, 0x52, 0x49, 0x4f, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a,
0x1d, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53,
0x5f, 0x56, 0x4f, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x45, 0x52, 0x49, 0x4f, 0x44, 0x10, 0x02,
0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41,
0x54, 0x55, 0x53, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x45, 0x44, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18,
0x50, 0x52, 0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f,
0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x04, 0x12, 0x1a, 0x0a, 0x16, 0x50, 0x52,
0x4f, 0x50, 0x4f, 0x53, 0x41, 0x4c, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x46, 0x41,
0x49, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x42, 0x99, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x67, 0x6f, 0x76, 0x2e, 0x76, 0x31, 0x42, 0x08, 0x47, 0x6f,
0x76, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x24, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73,
0x73, 0x64, 0x6b, 0x2e, 0x69, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2f, 0x67, 0x6f, 0x76, 0x2f, 0x76, 0x31, 0x3b, 0x67, 0x6f, 0x76, 0x76, 0x31, 0xa2, 0x02,
0x03, 0x43, 0x47, 0x58, 0xaa, 0x02, 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x47, 0x6f,
0x76, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x0d, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x6f,
0x76, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x19, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x5c, 0x47, 0x6f,
0x76, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0xea, 0x02, 0x0f, 0x43, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x3a, 0x3a, 0x47, 0x6f, 0x76, 0x3a, 0x3a,
0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -6,7 +6,7 @@
## Status
PROPOSED
ACCEPTED
## Abstract

View File

@ -303,4 +303,16 @@ message Params {
//
// Since: cosmos-sdk 0.50
string min_deposit_ratio = 16 [(cosmos_proto.scalar) = "cosmos.Dec"];
// optimistic_authorized_addresses is an optional governance parameter that limits the authorized accounts than can
// submit optimistic proposals
//
// Since: x/gov v1.0.0
repeated string optimistic_authorized_addresses = 17 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// optimistic rejected threshold defines at which percentage of NO votes, the optimistic proposal should fail and be
// converted to a standard proposal. The threshold is expressed as a percentage of the total bonded tokens.
//
// Since: x/gov v1.0.0
string optimistic_rejected_threshold = 18 [(cosmos_proto.scalar) = "cosmos.Dec"];
}

View File

@ -22,9 +22,8 @@ func TestLegacyGRPCQueryTally(t *testing.T) {
addrs, _ := createValidators(t, f, []int64{5, 5, 5})
var (
req *v1beta1.QueryTallyResultRequest
expRes *v1beta1.QueryTallyResultResponse
proposal v1.Proposal
req *v1beta1.QueryTallyResultRequest
expRes *v1beta1.QueryTallyResultResponse
)
testCases := []struct {
@ -33,29 +32,13 @@ func TestLegacyGRPCQueryTally(t *testing.T) {
expPass bool
expErrMsg string
}{
{
"create a proposal and get tally",
func() {
var err error
proposal, err = f.govKeeper.SubmitProposal(ctx, TestProposal, "", "test", "description", addrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
assert.NilError(t, err)
assert.Assert(t, proposal.String() != "")
req = &v1beta1.QueryTallyResultRequest{ProposalId: proposal.Id}
tallyResult := v1beta1.EmptyTallyResult()
expRes = &v1beta1.QueryTallyResultResponse{
Tally: tallyResult,
}
},
true,
"",
},
{
"request tally after few votes",
func() {
proposal, err := f.govKeeper.SubmitProposal(ctx, TestProposal, "", "test", "description", addrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
assert.NilError(t, err)
proposal.Status = v1.StatusVotingPeriod
err := f.govKeeper.SetProposal(ctx, proposal)
err = f.govKeeper.SetProposal(ctx, proposal)
assert.NilError(t, err)
assert.NilError(t, f.govKeeper.AddVote(ctx, proposal.Id, addrs[0], v1.NewNonSplitVoteOption(v1.OptionYes), ""))
assert.NilError(t, f.govKeeper.AddVote(ctx, proposal.Id, addrs[1], v1.NewNonSplitVoteOption(v1.OptionYes), ""))

View File

@ -25,17 +25,15 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
## Improvements
* [#18445](https://github.com/cosmos/cosmos-sdk/pull/18445) Extend gov config
### Features
* [#18532](https://github.com/cosmos/cosmos-sdk/pull/18532) Add SPAM vote proposals.
* [#18532](https://github.com/cosmos/cosmos-sdk/pull/18532) Add SPAM vote to proposals.
* [#18532](https://github.com/cosmos/cosmos-sdk/pull/18532) Add proposal types to proposals.
* [#18620](https://github.com/cosmos/cosmos-sdk/pull/18620) Add optimistic proposals.
### Improvements
* [#18445](https://github.com/cosmos/cosmos-sdk/pull/18445) Extend gov config
* [#18532](https://github.com/cosmos/cosmos-sdk/pull/18532) Repurpose `govcliutils.NormalizeProposalType` to work for gov v1 proposal types.
### API Breaking Changes

View File

@ -38,7 +38,6 @@ staking token of the chain.
* [Proposal submission](#proposal-submission)
* [Deposit](#deposit)
* [Vote](#vote)
* [Software Upgrade](#software-upgrade)
* [State](#state)
* [Proposals](#proposals)
* [Parameters and base types](#parameters-and-base-types)
@ -187,10 +186,29 @@ For a weighted vote to be valid, the `options` field must not contain duplicate
Quorum is defined as the minimum percentage of voting power that needs to be
cast on a proposal for the result to be valid.
### Expedited Proposals
### Proposal Types
Proposal types have been introduced in ADR-069.
#### Standard proposal
A standard proposal is a proposal that can contain any messages. The proposal follows the standard governance flow and governance parameters.
#### Expedited Proposal
A proposal can be expedited, making the proposal use shorter voting duration and a higher tally threshold by its default. If an expedited proposal fails to meet the threshold within the scope of shorter voting duration, the expedited proposal is then converted to a regular proposal and restarts voting under regular voting conditions.
#### Optimistic Proposal
An optimistic proposal is a proposal that passes unless a threshold a NO votes is reached.
Voter can only vote NO on the proposal. If the NO threshold is reached, the optimistic proposal is converted to a standard proposal.
#### Multiple Choice Proposals
A multiple choice proposal is a proposal where the voting options can be defined by the proposer.
The number of voting options is limited to a maximum of 4.
Multiple choice proposals, contrary to any other proposal type, cannot have messages to execute. They are only text proposals.
#### Threshold
Threshold is defined as the minimum proportion of `Yes` votes (excluding
@ -427,67 +445,6 @@ For pseudocode purposes, here are the two function we will use to read or write
voted. If the proposal is accepted, deposits are refunded. Finally, the proposal
content `Handler` is executed.
And the pseudocode for the `ProposalProcessingQueue`:
```go
in EndBlock do
for finishedProposalID in GetAllFinishedProposalIDs(block.Time)
proposal = load(Governance, <proposalID|'proposal'>) // proposal is a const key
validators = Keeper.getAllValidators()
tmpValMap := map(sdk.AccAddress)ValidatorGovInfo
// Initiate mapping at 0. This is the amount of shares of the validator's vote that will be overridden by their delegator's votes
for each validator in validators
tmpValMap(validator.OperatorAddr).Minus = 0
// Tally
voterIterator = rangeQuery(Governance, <proposalID|'addresses'>) //return all the addresses that voted on the proposal
for each (voterAddress, vote) in voterIterator
delegations = stakingKeeper.getDelegations(voterAddress) // get all delegations for current voter
for each delegation in delegations
// make sure delegation.Shares does NOT include shares being unbonded
tmpValMap(delegation.ValidatorAddr).Minus += delegation.Shares
proposal.updateTally(vote, delegation.Shares)
_, isVal = stakingKeeper.getValidator(voterAddress)
if (isVal)
tmpValMap(voterAddress).Vote = vote
tallyingParam = load(GlobalParams, 'TallyingParam')
// Update tally if validator voted
for each validator in validators
if tmpValMap(validator).HasVoted
proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus))
// Check if proposal is accepted or rejected
totalNonAbstain := proposal.YesVotes + proposal.NoVotes + proposal.NoWithVetoVotes
if (proposal.Votes.YesVotes/totalNonAbstain > tallyingParam.Threshold AND proposal.Votes.NoWithVetoVotes/totalNonAbstain < tallyingParam.Veto)
// proposal was accepted at the end of the voting period
// refund deposits (non-voters already punished)
for each (amount, depositor) in proposal.Deposits
depositor.AtomBalance += amount
stateWriter, err := proposal.Handler()
if err != nil
// proposal passed but failed during state execution
proposal.CurrentStatus = ProposalStatusFailed
else
// proposal pass and state is persisted
proposal.CurrentStatus = ProposalStatusAccepted
stateWriter.save()
else
// proposal was rejected
proposal.CurrentStatus = ProposalStatusRejected
store(Governance, <proposalID|'proposal'>, proposal)
```
### Legacy Proposal
:::warning
@ -575,7 +532,7 @@ The governance module emits the following events:
### EndBlocker
| Type | Attribute Key | Attribute Value |
|-------------------|-----------------|------------------|
| ----------------- | --------------- | ---------------- |
| inactive_proposal | proposal_id | {proposalID} |
| inactive_proposal | proposal_result | {proposalResult} |
| active_proposal | proposal_id | {proposalID} |
@ -586,7 +543,7 @@ The governance module emits the following events:
#### MsgSubmitProposal
| Type | Attribute Key | Attribute Value |
|---------------------|---------------------|-----------------|
| ------------------- | ------------------- | --------------- |
| submit_proposal | proposal_id | {proposalID} |
| submit_proposal [0] | voting_period_start | {proposalID} |
| proposal_deposit | amount | {depositAmount} |
@ -600,7 +557,7 @@ The governance module emits the following events:
#### MsgVote
| Type | Attribute Key | Attribute Value |
|---------------|---------------|-----------------|
| ------------- | ------------- | --------------- |
| proposal_vote | option | {voteOption} |
| proposal_vote | proposal_id | {proposalID} |
| message | module | governance |
@ -610,7 +567,7 @@ The governance module emits the following events:
#### MsgVoteWeighted
| Type | Attribute Key | Attribute Value |
|---------------|---------------|-----------------------|
| ------------- | ------------- | --------------------- |
| proposal_vote | option | {weightedVoteOptions} |
| proposal_vote | proposal_id | {proposalID} |
| message | module | governance |
@ -620,7 +577,7 @@ The governance module emits the following events:
#### MsgDeposit
| Type | Attribute Key | Attribute Value |
|----------------------|---------------------|-----------------|
| -------------------- | ------------------- | --------------- |
| proposal_deposit | amount | {depositAmount} |
| proposal_deposit | proposal_id | {proposalID} |
| proposal_deposit [0] | voting_period_start | {proposalID} |
@ -634,21 +591,23 @@ The governance module emits the following events:
The governance module contains the following parameters:
| Key | Type | Example |
|-------------------------------|------------------|-----------------------------------------|
| min_deposit | array (coins) | [{"denom":"uatom","amount":"10000000"}] |
| max_deposit_period | string (time ns) | "172800000000000" (17280s) |
| voting_period | string (time ns) | "172800000000000" (17280s) |
| quorum | string (dec) | "0.334000000000000000" |
| threshold | string (dec) | "0.500000000000000000" |
| veto | string (dec) | "0.334000000000000000" |
| expedited_threshold | string (time ns) | "0.667000000000000000" |
| expedited_voting_period | string (time ns) | "86400000000000" (8600s) |
| expedited_min_deposit | array (coins) | [{"denom":"uatom","amount":"50000000"}] |
| burn_proposal_deposit_prevote | bool | false |
| burn_vote_quorum | bool | false |
| burn_vote_veto | bool | true |
| min_initial_deposit_ratio | string | "0.1" |
| Key | Type | Example |
| ------------------------------- | ---------------------- | --------------------------------------- |
| min_deposit | array (coins) | [{"denom":"uatom","amount":"10000000"}] |
| max_deposit_period | string (time ns) | "172800000000000" (17280s) |
| voting_period | string (time ns) | "172800000000000" (17280s) |
| quorum | string (dec) | "0.334000000000000000" |
| threshold | string (dec) | "0.500000000000000000" |
| veto | string (dec) | "0.334000000000000000" |
| expedited_threshold | string (time ns) | "0.667000000000000000" |
| expedited_voting_period | string (time ns) | "86400000000000" (8600s) |
| expedited_min_deposit | array (coins) | [{"denom":"uatom","amount":"50000000"}] |
| burn_proposal_deposit_prevote | bool | false |
| burn_vote_quorum | bool | false |
| burn_vote_veto | bool | true |
| min_initial_deposit_ratio | string | "0.1" |
| optimistic_rejected_threshold | string (dec) | "0.1" |
| optimistic_authorized_addresses | bytes array (addreses) | [][] |
**NOTE**: The governance module contains parameters that are objects unlike other
modules. If only a subset of parameters are desired to be changed, only they need

View File

@ -127,19 +127,17 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) error {
return false, err
}
// If an expedited proposal fails, we do not want to update
// the deposit at this point since the proposal is converted to regular.
// As a result, the deposits are either deleted or refunded in all cases
// EXCEPT when an expedited proposal fails.
if passes || !(proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_EXPEDITED) {
if burnDeposits {
err = keeper.DeleteAndBurnDeposits(ctx, proposal.Id)
} else {
err = keeper.RefundAndDeleteDeposits(ctx, proposal.Id)
}
if err != nil {
return false, err
}
// Deposits are always burned if tally said so, regardless of the proposal type.
// If a proposal passes, deposits are always refunded, regardless of the proposal type.
// If a proposal fails, and isn't spammy, deposits are refunded, unless the proposal is expedited or optimistic.
// An expedited or optimistic proposal that fails and isn't spammy is converted to a regular proposal.
if burnDeposits {
err = keeper.DeleteAndBurnDeposits(ctx, proposal.Id)
} else if passes || !(proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_EXPEDITED || proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC) {
err = keeper.RefundAndDeleteDeposits(ctx, proposal.Id)
}
if err != nil {
return false, err
}
if err = keeper.ActiveProposalsQueue.Remove(ctx, collections.Join(*proposal.VotingEndTime, proposal.Id)); err != nil {
@ -199,8 +197,9 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) error {
tagValue = types.AttributeValueProposalFailed
logMsg = fmt.Sprintf("passed, but msg %d (%s) failed on execution: %s", idx, sdk.MsgTypeURL(msg), err)
}
case proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_EXPEDITED:
// When expedited proposal fails, it is converted
case !burnDeposits && (proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_EXPEDITED ||
proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC):
// When a non spammy expedited/optimistic proposal fails, it is converted
// to a regular proposal. As a result, the voting period is extended, and,
// once the regular voting period expires again, the tally is repeated
// according to the regular proposal rules.
@ -218,8 +217,13 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) error {
return false, err
}
tagValue = types.AttributeValueExpeditedProposalRejected
logMsg = "expedited proposal converted to regular"
if proposal.ProposalType == v1.ProposalType_PROPOSAL_TYPE_EXPEDITED {
tagValue = types.AttributeValueExpeditedProposalRejected
logMsg = "expedited proposal converted to regular"
} else {
tagValue = types.AttributeValueOptimisticProposalRejected
logMsg = "optimistic proposal converted to regular"
}
default:
proposal.Status = v1.StatusRejected
proposal.FailedReason = "proposal did not get enough votes to pass"
@ -246,8 +250,8 @@ func EndBlocker(ctx sdk.Context, keeper *keeper.Keeper) error {
logger.Info(
"proposal tallied",
"proposal", proposal.Id,
"status", proposal.Status.String(),
"proposal_type", proposal.ProposalType,
"status", proposal.Status.String(),
"title", proposal.Title,
"results", logMsg,
)

View File

@ -2,6 +2,7 @@ package keeper
import (
"context"
"errors"
"fmt"
"time"
@ -208,6 +209,10 @@ func (k Keeper) validateProposalLengths(metadata, title, summary string) error {
// assertTitleLength returns an error if given title length
// is greater than a pre-defined MaxTitleLen.
func (k Keeper) assertTitleLength(title string) error {
if len(title) == 0 {
return errors.New("proposal title cannot be empty")
}
if uint64(len(title)) > k.config.MaxTitleLen {
return types.ErrTitleTooLong.Wrapf("got title with length %d", len(title))
}
@ -226,6 +231,10 @@ func (k Keeper) assertMetadataLength(metadata string) error {
// assertSummaryLength returns an error if given summary length
// is greater than a pre-defined MaxSummaryLen.
func (k Keeper) assertSummaryLength(summary string) error {
if len(summary) == 0 {
return errors.New("proposal summary cannot be empty")
}
if uint64(len(summary)) > k.config.MaxSummaryLen {
return types.ErrSummaryTooLong.Wrapf("got summary with length %d", len(summary))
}

View File

@ -73,7 +73,7 @@ func (suite *KeeperTestSuite) reset() {
suite.msgSrvr = keeper.NewMsgServerImpl(suite.govKeeper)
suite.legacyMsgSrvr = keeper.NewLegacyMsgServerImpl(govAcct.String(), suite.msgSrvr)
suite.addrs = simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 3, sdkmath.NewInt(30000000))
suite.addrs = simtestutil.AddTestAddrsIncremental(bankKeeper, stakingKeeper, ctx, 3, sdkmath.NewInt(300000000))
suite.acctKeeper.EXPECT().AddressCodec().Return(address.NewBech32Codec("cosmos")).AnyTimes()
}

View File

@ -41,5 +41,5 @@ func (m Migrator) Migrate4to5(ctx sdk.Context) error {
// Migrate4to5 migrates from version 5 to 6.
func (m Migrator) Migrate5to6(ctx sdk.Context) error {
return v6.MigrateStore(ctx, m.keeper.Proposals)
return v6.MigrateStore(ctx, m.keeper.Params, m.keeper.Proposals)
}

View File

@ -29,13 +29,6 @@ var _ v1.MsgServer = msgServer{}
// SubmitProposal implements the MsgServer.SubmitProposal method.
func (k msgServer) SubmitProposal(goCtx context.Context, msg *v1.MsgSubmitProposal) (*v1.MsgSubmitProposalResponse, error) {
if msg.Title == "" {
return nil, errors.Wrap(sdkerrors.ErrInvalidRequest, "proposal title cannot be empty")
}
if msg.Summary == "" {
return nil, errors.Wrap(sdkerrors.ErrInvalidRequest, "proposal summary cannot be empty")
}
proposer, err := k.authKeeper.AddressCodec().StringToBytes(msg.GetProposer())
if err != nil {
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid proposer address: %s", err)
@ -63,33 +56,35 @@ func (k msgServer) SubmitProposal(goCtx context.Context, msg *v1.MsgSubmitPropos
// nothing can be done here, and this is still a valid case, so we ignore the error
}
// This method checks that all message metadata, summary and title
// has te expected length defined in the module configuration.
if err := k.validateProposalLengths(msg.Metadata, msg.Title, msg.Summary); err != nil {
return nil, err
}
proposalMsgs, err := msg.GetMsgs()
if err != nil {
return nil, err
}
ctx := sdk.UnwrapSDKContext(goCtx)
initialDeposit := msg.GetInitialDeposit()
params, err := k.Params.Get(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get governance parameters: %w", err)
}
proposalType := msg.ProposalType
if msg.Expedited { // checking for backward compatibility
proposalType = v1.ProposalType_PROPOSAL_TYPE_EXPEDITED
msg.ProposalType = v1.ProposalType_PROPOSAL_TYPE_EXPEDITED
}
if err := k.validateInitialDeposit(ctx, params, initialDeposit, proposalType); err != nil {
if err := k.validateInitialDeposit(ctx, params, msg.GetInitialDeposit(), msg.ProposalType); err != nil {
return nil, err
}
if err := k.validateDepositDenom(ctx, params, initialDeposit); err != nil {
if err := k.validateDepositDenom(ctx, params, msg.GetInitialDeposit()); err != nil {
return nil, err
}
proposal, err := k.Keeper.SubmitProposal(ctx, proposalMsgs, msg.Metadata, msg.Title, msg.Summary, proposer, proposalType)
proposal, err := k.Keeper.SubmitProposal(ctx, proposalMsgs, msg.Metadata, msg.Title, msg.Summary, proposer, msg.ProposalType)
if err != nil {
return nil, err
}
@ -191,8 +186,7 @@ func (k msgServer) Vote(ctx context.Context, msg *v1.MsgVote) (*v1.MsgVoteRespon
return nil, errors.Wrap(govtypes.ErrInvalidVote, msg.Option.String())
}
err = k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, v1.NewNonSplitVoteOption(msg.Option), msg.Metadata)
if err != nil {
if err = k.Keeper.AddVote(ctx, msg.ProposalId, accAddr, v1.NewNonSplitVoteOption(msg.Option), msg.Metadata); err != nil {
return nil, err
}
@ -278,7 +272,7 @@ func (k msgServer) UpdateParams(ctx context.Context, msg *v1.MsgUpdateParams) (*
return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.authority, msg.Authority)
}
if err := msg.Params.ValidateBasic(); err != nil {
if err := msg.Params.ValidateBasic(k.authKeeper.AddressCodec()); err != nil {
return nil, err
}

View File

@ -153,7 +153,7 @@ func (suite *KeeperTestSuite) TestMsgSubmitProposal() {
[]sdk.Msg{bankMsg},
initialDeposit,
proposer.String(),
strings.Repeat("1", 256),
strings.Repeat("1", 257),
"Proposal",
"description of proposal",
v1.ProposalType_PROPOSAL_TYPE_STANDARD,
@ -452,6 +452,30 @@ func (suite *KeeperTestSuite) TestMsgVote() {
expErr: true,
expErrMsg: "invalid vote option",
},
"optimistic proposal: wrong vote option": {
preRun: func() uint64 {
msg, err := v1.NewMsgSubmitProposal(
[]sdk.Msg{bankMsg},
minDeposit,
proposer.String(),
"",
"Proposal",
"description of proposal",
v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC,
)
suite.Require().NoError(err)
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
suite.Require().NoError(err)
suite.Require().NotNil(res.ProposalId)
return res.ProposalId
},
option: v1.VoteOption_VOTE_OPTION_ONE,
voter: proposer,
metadata: "",
expErr: true,
expErrMsg: "optimistic proposals can only be rejected: invalid vote option",
},
"vote on inactive proposal": {
preRun: func() uint64 {
msg, err := v1.NewMsgSubmitProposal(
@ -658,6 +682,30 @@ func (suite *KeeperTestSuite) TestMsgVoteWeighted() {
expErr: true,
expErrMsg: "invalid vote option",
},
"optimistic proposal: wrong vote option": {
preRun: func() uint64 {
msg, err := v1.NewMsgSubmitProposal(
[]sdk.Msg{bankMsg},
minDeposit,
proposer.String(),
"",
"Proposal",
"description of proposal",
v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC,
)
suite.Require().NoError(err)
res, err := suite.msgSrvr.SubmitProposal(suite.ctx, msg)
suite.Require().NoError(err)
suite.Require().NotNil(res.ProposalId)
return res.ProposalId
},
option: v1.NewNonSplitVoteOption(v1.VoteOption_VOTE_OPTION_ONE), // vote yes
voter: proposer,
metadata: "",
expErr: true,
expErrMsg: "optimistic proposals can only be rejected: invalid vote option",
},
"weight sum < 1": {
preRun: func() uint64 {
return proposalID

View File

@ -5,6 +5,7 @@ import (
"context"
"errors"
"fmt"
"slices"
"strings"
"time"
@ -18,18 +19,26 @@ import (
// SubmitProposal creates a new proposal given an array of messages
func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, proposalType v1.ProposalType) (v1.Proposal, error) {
// This method checks that all message metadata, summary and title
// has te expected length defined in the module configuration.
if err := keeper.validateProposalLengths(metadata, title, summary); err != nil {
params, err := keeper.Params.Get(ctx)
if err != nil {
return v1.Proposal{}, err
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
// Will hold a string slice of all Msg type URLs.
msgs := []string{}
// additional checks per proposal types
if proposalType == v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC {
proposerStr, _ := keeper.authKeeper.AddressCodec().BytesToString(proposer)
// Loop through all messages and confirm that each has a handler and the gov module account
// as the only signer
if len(params.OptimisticAuthorizedAddresses) > 0 {
if !slices.Contains(params.OptimisticAuthorizedAddresses, proposerStr) {
return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposer, "proposer is not authorized to submit optimistic proposal")
}
}
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
msgs := []string{} // will hold a string slice of all Msg type URLs.
// Loop through all messages and confirm that each has a handler and the gov module account as the only signer
for _, msg := range messages {
msgs = append(msgs, sdk.MsgTypeURL(msg))
@ -81,15 +90,8 @@ func (keeper Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, met
return v1.Proposal{}, err
}
params, err := keeper.Params.Get(ctx)
if err != nil {
return v1.Proposal{}, err
}
submitTime := sdkCtx.HeaderInfo().Time
depositPeriod := params.MaxDepositPeriod
proposal, err := v1.NewProposal(messages, proposalID, submitTime, submitTime.Add(*depositPeriod), metadata, title, summary, proposer, proposalType)
proposal, err := v1.NewProposal(messages, proposalID, submitTime, submitTime.Add(*params.MaxDepositPeriod), metadata, title, summary, proposer, proposalType)
if err != nil {
return v1.Proposal{}, err
}

View File

@ -171,8 +171,6 @@ func (suite *KeeperTestSuite) TestSubmitProposal() {
{&v1beta1.TextProposal{Title: strings.Repeat("1234567890", 100), Description: "description"}, govAcct, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil},
{&v1beta1.TextProposal{Title: "title", Description: ""}, govAcct, "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, nil},
{&v1beta1.TextProposal{Title: "title", Description: strings.Repeat("1234567890", 1000)}, govAcct, "", v1.ProposalType_PROPOSAL_TYPE_EXPEDITED, nil},
// error when metadata is too long (>10000)
{&tp, govAcct, strings.Repeat("a", 100001), v1.ProposalType_PROPOSAL_TYPE_EXPEDITED, types.ErrMetadataTooLong},
// error when signer is not gov acct
{&tp, randomAddr.String(), "", v1.ProposalType_PROPOSAL_TYPE_STANDARD, types.ErrInvalidSigner},
// error only when invalid route

View File

@ -10,23 +10,155 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// TODO: Break into several smaller functions for clarity
// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the
// voters
// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the voters
func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
results := make(map[v1.VoteOption]math.LegacyDec)
results[v1.OptionYes] = math.LegacyZeroDec()
results[v1.OptionAbstain] = math.LegacyZeroDec()
results[v1.OptionNo] = math.LegacyZeroDec()
results[v1.OptionNoWithVeto] = math.LegacyZeroDec()
results[v1.OptionSpam] = math.LegacyZeroDec()
validators, err := keeper.getCurrentValidators(ctx)
if err != nil {
return false, false, v1.TallyResult{}, err
}
totalVotingPower := math.LegacyZeroDec()
totalVoterPower, results, err := keeper.calculateVoteResultsAndVotingPower(ctx, proposal.Id, validators)
if err != nil {
return false, false, v1.TallyResult{}, err
}
params, err := keeper.Params.Get(ctx)
if err != nil {
return false, false, v1.TallyResult{}, err
}
tallyResults = v1.NewTallyResultFromMap(results)
// If there is no staked coins, the proposal fails
totalBonded, err := keeper.sk.TotalBondedTokens(ctx)
if err != nil {
return false, false, v1.TallyResult{}, err
}
if totalBonded.IsZero() {
return false, false, tallyResults, nil
}
// If there are more spam votes than the sum of all other options, proposal fails
// A proposal with no votes should not be considered spam
if !totalVoterPower.Equal(math.LegacyZeroDec()) &&
results[v1.OptionSpam].GTE(results[v1.OptionOne].Add(results[v1.OptionTwo].Add(results[v1.OptionThree].Add(results[v1.OptionFour])))) {
return false, true, tallyResults, nil
}
switch proposal.ProposalType {
case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC:
return keeper.tallyOptimistic(totalVoterPower, totalBonded, results, params)
case v1.ProposalType_PROPOSAL_TYPE_EXPEDITED:
return keeper.tallyExpedited(totalVoterPower, totalBonded, results, params)
case v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE:
return keeper.tallyMultipleChoice(totalVoterPower, totalBonded, results, params) // TODO(@julienrbrt): implement in follow up
default:
return keeper.tallyStandard(totalVoterPower, totalBonded, results, params)
}
}
// tallyStandard tallies the votes of a standard proposal
func (keeper Keeper) tallyStandard(totalVoterPower math.LegacyDec, totalBonded math.Int, results map[v1.VoteOption]math.LegacyDec, params v1.Params) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
tallyResults = v1.NewTallyResultFromMap(results)
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVoterPower.Quo(math.LegacyNewDecFromInt(totalBonded))
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
if percentVoting.LT(quorum) {
return false, params.BurnVoteQuorum, tallyResults, nil
}
// If no one votes (everyone abstains), proposal fails
if totalVoterPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) {
return false, false, tallyResults, nil
}
// If more than 1/3 of voters veto, proposal fails
vetoThreshold, _ := math.LegacyNewDecFromStr(params.VetoThreshold)
if results[v1.OptionNoWithVeto].Quo(totalVoterPower).GT(vetoThreshold) {
return false, params.BurnVoteVeto, tallyResults, nil
}
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
threshold, _ := math.LegacyNewDecFromStr(params.GetThreshold())
if results[v1.OptionYes].Quo(totalVoterPower.Sub(results[v1.OptionAbstain])).GT(threshold) {
return true, false, tallyResults, nil
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
return false, false, tallyResults, nil
}
// tallyExpedited tallies the votes of an expedited proposal
func (keeper Keeper) tallyExpedited(totalVoterPower math.LegacyDec, totalBonded math.Int, results map[v1.VoteOption]math.LegacyDec, params v1.Params) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
tallyResults = v1.NewTallyResultFromMap(results)
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVoterPower.Quo(math.LegacyNewDecFromInt(totalBonded))
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
if percentVoting.LT(quorum) {
return false, params.BurnVoteQuorum, tallyResults, nil
}
// If no one votes (everyone abstains), proposal fails
if totalVoterPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) {
return false, false, tallyResults, nil
}
// If more than 1/3 of voters veto, proposal fails
vetoThreshold, _ := math.LegacyNewDecFromStr(params.VetoThreshold)
if results[v1.OptionNoWithVeto].Quo(totalVoterPower).GT(vetoThreshold) {
return false, params.BurnVoteVeto, tallyResults, nil
}
// If more than 2/3 of non-abstaining voters vote Yes, proposal passes
threshold, _ := math.LegacyNewDecFromStr(params.GetExpeditedThreshold())
if results[v1.OptionYes].Quo(totalVoterPower.Sub(results[v1.OptionAbstain])).GT(threshold) {
return true, false, tallyResults, nil
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
return false, false, tallyResults, nil
}
// tallyOptimistic tallies the votes of an optimistic proposal
func (keeper Keeper) tallyOptimistic(totalVoterPower math.LegacyDec, totalBonded math.Int, results map[v1.VoteOption]math.LegacyDec, params v1.Params) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
tallyResults = v1.NewTallyResultFromMap(results)
optimisticNoThreshold, _ := math.LegacyNewDecFromStr(params.OptimisticRejectedThreshold)
// If proposal has no votes, proposal passes
if totalVoterPower.Equal(math.LegacyZeroDec()) {
return true, false, tallyResults, nil
}
// If the threshold of no is reached, proposal fails
if results[v1.OptionNo].Quo(totalBonded.ToLegacyDec()).GT(optimisticNoThreshold) {
return false, false, tallyResults, nil
}
return true, false, tallyResults, nil
}
// tallyMultipleChoice tallies the votes of a multiple choice proposal
func (keeper Keeper) tallyMultipleChoice(totalVoterPower math.LegacyDec, totalBonded math.Int, results map[v1.VoteOption]math.LegacyDec, params v1.Params) (passes, burnDeposits bool, tallyResults v1.TallyResult, err error) {
tallyResults = v1.NewTallyResultFromMap(results)
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVoterPower.Quo(math.LegacyNewDecFromInt(totalBonded))
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
if percentVoting.LT(quorum) {
return false, params.BurnVoteQuorum, tallyResults, nil
}
return true, false, tallyResults, nil
}
// getCurrentValidators fetches all the bonded validators, insert them into currValidators
func (keeper Keeper) getCurrentValidators(ctx context.Context) (map[string]v1.ValidatorGovInfo, error) {
currValidators := make(map[string]v1.ValidatorGovInfo)
// fetch all the bonded validators, insert them into currValidators
err = keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.ValidatorI) (stop bool) {
if err := keeper.sk.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.ValidatorI) (stop bool) {
valBz, err := keeper.sk.ValidatorAddressCodec().StringToBytes(validator.GetOperator())
if err != nil {
return false
@ -40,13 +172,26 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
)
return false
})
if err != nil {
return false, false, tallyResults, err
}); err != nil {
return nil, err
}
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
err = keeper.Votes.Walk(ctx, rng, func(key collections.Pair[uint64, sdk.AccAddress], vote v1.Vote) (bool, error) {
return currValidators, nil
}
// calculateVoteResultsAndVotingPower iterate over all votes, tally up the voting power of each validator
// and returns the votes results from voters
func (keeper Keeper) calculateVoteResultsAndVotingPower(
ctx context.Context,
proposalID uint64,
validators map[string]v1.ValidatorGovInfo,
) (math.LegacyDec, map[v1.VoteOption]math.LegacyDec, error) {
totalVP := math.LegacyZeroDec()
results := createEmptyResults()
// iterate over all votes, tally up the voting power of each validator
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposalID)
if err := keeper.Votes.Walk(ctx, rng, func(key collections.Pair[uint64, sdk.AccAddress], vote v1.Vote) (bool, error) {
// if validator, just record it in the map
voter, err := keeper.authKeeper.AddressCodec().StringToBytes(vote.Voter)
if err != nil {
@ -57,20 +202,21 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
if err != nil {
return false, err
}
if val, ok := currValidators[valAddrStr]; ok {
if val, ok := validators[valAddrStr]; ok {
val.Vote = vote.Options
currValidators[valAddrStr] = val
validators[valAddrStr] = val
}
// iterate over all delegations from voter, deduct from any delegated-to validators
err = keeper.sk.IterateDelegations(ctx, voter, func(index int64, delegation sdk.DelegationI) (stop bool) {
valAddrStr := delegation.GetValidatorAddr()
if val, ok := currValidators[valAddrStr]; ok {
if val, ok := validators[valAddrStr]; ok {
// There is no need to handle the special case that validator address equal to voter address.
// Because voter's voting power will tally again even if there will be deduction of voter's voting power from validator.
val.DelegatorDeductions = val.DelegatorDeductions.Add(delegation.GetShares())
currValidators[valAddrStr] = val
validators[valAddrStr] = val
// delegation shares * bonded / total shares
votingPower := delegation.GetShares().MulInt(val.BondedTokens).Quo(val.DelegatorShares)
@ -80,7 +226,8 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
subPower := votingPower.Mul(weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower)
totalVP = totalVP.Add(votingPower)
}
return false
@ -90,14 +237,12 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
}
return false, keeper.Votes.Remove(ctx, collections.Join(vote.ProposalId, sdk.AccAddress(voter)))
})
if err != nil {
return false, false, tallyResults, err
}); err != nil {
return math.LegacyDec{}, nil, err
}
// iterate over the validators again to tally their voting power
for _, val := range currValidators {
for _, val := range validators {
if len(val.Vote) == 0 {
continue
}
@ -110,64 +255,19 @@ func (keeper Keeper) Tally(ctx context.Context, proposal v1.Proposal) (passes, b
subPower := votingPower.Mul(weight)
results[option.Option] = results[option.Option].Add(subPower)
}
totalVotingPower = totalVotingPower.Add(votingPower)
totalVP = totalVP.Add(votingPower)
}
params, err := keeper.Params.Get(ctx)
if err != nil {
return false, false, tallyResults, err
}
tallyResults = v1.NewTallyResultFromMap(results)
// TODO: Upgrade the spec to cover all of these cases & remove pseudocode.
// If there is no staked coins, the proposal fails
totalBonded, err := keeper.sk.TotalBondedTokens(ctx)
if err != nil {
return false, false, tallyResults, err
}
if totalBonded.IsZero() {
return false, false, tallyResults, nil
}
// If there is not enough quorum of votes, the proposal fails
percentVoting := totalVotingPower.Quo(math.LegacyNewDecFromInt(totalBonded))
quorum, _ := math.LegacyNewDecFromStr(params.Quorum)
if percentVoting.LT(quorum) {
return false, params.BurnVoteQuorum, tallyResults, nil
}
// If there are more spam votes than the sum of all other options, proposal fails
if results[v1.OptionSpam].GTE(results[v1.OptionOne].Add(results[v1.OptionTwo].Add(results[v1.OptionThree].Add(results[v1.OptionFour])))) {
return false, true, tallyResults, nil
}
// If no one votes (everyone abstains), proposal fails
if totalVotingPower.Sub(results[v1.OptionAbstain]).Equal(math.LegacyZeroDec()) {
return false, false, tallyResults, nil
}
// If more than 1/3 of voters veto, proposal fails
vetoThreshold, _ := math.LegacyNewDecFromStr(params.VetoThreshold)
if results[v1.OptionNoWithVeto].Quo(totalVotingPower).GT(vetoThreshold) {
return false, params.BurnVoteVeto, tallyResults, nil
}
// If more than 1/2 of non-abstaining voters vote Yes, proposal passes
// For expedited 2/3
var thresholdStr string
if proposal.Expedited {
thresholdStr = params.GetExpeditedThreshold()
} else {
thresholdStr = params.GetThreshold()
}
threshold, _ := math.LegacyNewDecFromStr(thresholdStr)
if results[v1.OptionYes].Quo(totalVotingPower.Sub(results[v1.OptionAbstain])).GT(threshold) {
return true, false, tallyResults, nil
}
// If more than 1/2 of non-abstaining voters vote No, proposal fails
return false, false, tallyResults, nil
return totalVP, results, nil
}
func createEmptyResults() map[v1.VoteOption]math.LegacyDec {
results := make(map[v1.VoteOption]math.LegacyDec)
results[v1.OptionYes] = math.LegacyZeroDec()
results[v1.OptionAbstain] = math.LegacyZeroDec()
results[v1.OptionNo] = math.LegacyZeroDec()
results[v1.OptionNoWithVeto] = math.LegacyZeroDec()
results[v1.OptionSpam] = math.LegacyZeroDec()
return results
}

View File

@ -19,45 +19,45 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestTally(t *testing.T) {
type suite struct {
t *testing.T
proposal v1.Proposal
valAddrs []sdk.ValAddress
delAddrs []sdk.AccAddress
keeper *keeper.Keeper
ctx sdk.Context
mocks mocks
}
type tallyFixture struct {
t *testing.T
proposal v1.Proposal
valAddrs []sdk.ValAddress
delAddrs []sdk.AccAddress
keeper *keeper.Keeper
ctx sdk.Context
mocks mocks
}
var (
// handy functions
setTotalBonded = func(s suite, n int64) {
s.mocks.stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec("cosmosvaloper")).AnyTimes()
s.mocks.stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(sdkmath.NewInt(n), nil)
}
delegatorVote = func(s suite, voter sdk.AccAddress, delegations []stakingtypes.Delegation, vote v1.VoteOption) {
err := s.keeper.AddVote(s.ctx, s.proposal.Id, voter, v1.NewNonSplitVoteOption(vote), "")
require.NoError(s.t, err)
s.mocks.stakingKeeper.EXPECT().
IterateDelegations(s.ctx, voter, gomock.Any()).
DoAndReturn(
func(ctx context.Context, voter sdk.AccAddress, fn func(index int64, d sdk.DelegationI) bool) error {
for i, d := range delegations {
fn(int64(i), d)
}
return nil
})
}
validatorVote = func(s suite, voter sdk.ValAddress, vote v1.VoteOption) {
// validatorVote is like delegatorVote but without delegations
delegatorVote(s, sdk.AccAddress(voter), nil, vote)
}
)
var (
// handy functions
setTotalBonded = func(s tallyFixture, n int64) {
s.mocks.stakingKeeper.EXPECT().ValidatorAddressCodec().Return(address.NewBech32Codec("cosmosvaloper")).AnyTimes()
s.mocks.stakingKeeper.EXPECT().TotalBondedTokens(gomock.Any()).Return(sdkmath.NewInt(n), nil)
}
delegatorVote = func(s tallyFixture, voter sdk.AccAddress, delegations []stakingtypes.Delegation, vote v1.VoteOption) {
err := s.keeper.AddVote(s.ctx, s.proposal.Id, voter, v1.NewNonSplitVoteOption(vote), "")
require.NoError(s.t, err)
s.mocks.stakingKeeper.EXPECT().
IterateDelegations(s.ctx, voter, gomock.Any()).
DoAndReturn(
func(ctx context.Context, voter sdk.AccAddress, fn func(index int64, d sdk.DelegationI) bool) error {
for i, d := range delegations {
fn(int64(i), d)
}
return nil
})
}
validatorVote = func(s tallyFixture, voter sdk.ValAddress, vote v1.VoteOption) {
// validatorVote is like delegatorVote but without delegations
delegatorVote(s, sdk.AccAddress(voter), nil, vote)
}
)
func TestTally_Standard(t *testing.T) {
tests := []struct {
name string
proposalType v1.ProposalType
setup func(suite)
setup func(tallyFixture)
expectedPass bool
expectedBurn bool
expectedTally v1.TallyResult
@ -65,7 +65,7 @@ func TestTally(t *testing.T) {
}{
{
name: "no votes, no bonded tokens: prop fails",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 0)
},
expectedPass: false,
@ -80,7 +80,7 @@ func TestTally(t *testing.T) {
},
{
name: "no votes: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
},
expectedPass: false,
@ -95,7 +95,7 @@ func TestTally(t *testing.T) {
},
{
name: "one validator votes: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_THREE)
},
@ -111,7 +111,7 @@ func TestTally(t *testing.T) {
},
{
name: "one account votes without delegation: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegatorVote(s, s.delAddrs[0], nil, v1.VoteOption_VOTE_OPTION_ONE)
},
@ -127,7 +127,7 @@ func TestTally(t *testing.T) {
},
{
name: "one delegator votes: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
@ -148,7 +148,7 @@ func TestTally(t *testing.T) {
},
{
name: "one delegator votes yes, validator votes also yes: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
@ -170,7 +170,7 @@ func TestTally(t *testing.T) {
},
{
name: "one delegator votes yes, validator votes no: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
@ -197,7 +197,7 @@ func TestTally(t *testing.T) {
// second validator votes no
// third validator (no delegation) votes abstain
name: "delegator with mixed delegations: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{
{
@ -228,7 +228,7 @@ func TestTally(t *testing.T) {
},
{
name: "quorum reached with only abstain: prop fails",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_TWO)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_TWO)
@ -247,7 +247,7 @@ func TestTally(t *testing.T) {
},
{
name: "quorum reached with veto>1/3: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
@ -269,7 +269,7 @@ func TestTally(t *testing.T) {
},
{
name: "quorum reached with yes<=.5: prop fails",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
@ -288,7 +288,7 @@ func TestTally(t *testing.T) {
},
{
name: "quorum reached with yes>.5: prop succeeds",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
@ -310,7 +310,7 @@ func TestTally(t *testing.T) {
},
{
name: "quorum reached thanks to abstain, yes>.5: prop succeeds",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
@ -329,55 +329,9 @@ func TestTally(t *testing.T) {
SpamCount: "0",
},
},
{
name: "quorum reached with yes<=.667: expedited prop fails",
proposalType: v1.ProposalType_PROPOSAL_TYPE_EXPEDITED,
setup: func(s suite) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_FOUR)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "4000000",
AbstainCount: "0",
NoCount: "2000000",
NoWithVetoCount: "1000000",
SpamCount: "0",
},
},
{
name: "quorum reached with yes>.667: expedited prop succeeds",
proposalType: v1.ProposalType_PROPOSAL_TYPE_EXPEDITED,
setup: func(s suite) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_FOUR)
},
expectedPass: true,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "5000000",
AbstainCount: "0",
NoCount: "1000000",
NoWithVetoCount: "1000000",
SpamCount: "0",
},
},
{
name: "quorum reached with spam > all other votes: prop fails/burn deposit",
setup: func(s suite) {
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
// spam votes
@ -432,11 +386,536 @@ func TestTally(t *testing.T) {
})
// Submit and activate a proposal
proposal, err := govKeeper.SubmitProposal(ctx, TestProposal, "", "title", "summary", delAddrs[0], tt.proposalType)
proposal, err := govKeeper.SubmitProposal(ctx, TestProposal, "", "title", "summary", delAddrs[0], v1.ProposalType_PROPOSAL_TYPE_STANDARD)
require.NoError(t, err)
err = govKeeper.ActivateVotingPeriod(ctx, proposal)
require.NoError(t, err)
suite := suite{
suite := tallyFixture{
t: t,
proposal: proposal,
valAddrs: valAddrs,
delAddrs: delAddrs,
ctx: ctx,
keeper: govKeeper,
mocks: mocks,
}
tt.setup(suite)
pass, burn, tally, err := govKeeper.Tally(ctx, proposal)
require.NoError(t, err)
assert.Equal(t, tt.expectedPass, pass, "wrong pass")
assert.Equal(t, tt.expectedBurn, burn, "wrong burn")
assert.Equal(t, tt.expectedTally, tally)
// Assert votes removal after tally
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
_, err = suite.keeper.Votes.Iterate(suite.ctx, rng)
assert.NoError(t, err)
})
}
}
func TestTally_Expedited(t *testing.T) {
tests := []struct {
name string
setup func(tallyFixture)
expectedPass bool
expectedBurn bool
expectedTally v1.TallyResult
expectedError string
}{
{
name: "no votes, no bonded tokens: prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 0)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "no votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "one validator votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_THREE)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "1000000",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "one account votes without delegation: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegatorVote(s, s.delAddrs[0], nil, v1.VoteOption_VOTE_OPTION_ONE)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "one delegator votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[0].String(),
Shares: sdkmath.LegacyNewDec(42),
}}
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_ONE)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "42",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "one delegator votes yes, validator votes also yes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[0].String(),
Shares: sdkmath.LegacyNewDec(42),
}}
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "1000000",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "one delegator votes yes, validator votes no: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[0].String(),
Shares: sdkmath.LegacyNewDec(42),
}}
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_THREE)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "42",
AbstainCount: "0",
NoCount: "999958",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
// one delegator delegates 42 shares to 2 different validators (21 each)
// delegator votes yes
// first validator votes yes
// second validator votes no
// third validator (no delegation) votes abstain
name: "delegator with mixed delegations: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{
{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[0].String(),
Shares: sdkmath.LegacyNewDec(21),
},
{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[1].String(),
Shares: sdkmath.LegacyNewDec(21),
},
}
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_TWO)
},
expectedPass: false,
expectedBurn: true, // burn because quorum not reached
expectedTally: v1.TallyResult{
YesCount: "1000021",
AbstainCount: "1000000",
NoCount: "999979",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "quorum reached with only abstain: prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_TWO)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_TWO)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_TWO)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_TWO)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "4000000",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "quorum reached with veto>1/3: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_FOUR)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_FOUR)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_FOUR)
},
expectedPass: false,
expectedBurn: true,
expectedTally: v1.TallyResult{
YesCount: "4000000",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "3000000",
SpamCount: "0",
},
},
{
name: "quorum reached with yes<=.5: prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_THREE)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "2000000",
AbstainCount: "0",
NoCount: "2000000",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "quorum reached with yes<=.667: expedited prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_FOUR)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "4000000",
AbstainCount: "0",
NoCount: "2000000",
NoWithVetoCount: "1000000",
SpamCount: "0",
},
},
{
name: "quorum reached with yes>.667: expedited prop succeeds",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_ONE)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_FOUR)
},
expectedPass: true,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "5000000",
AbstainCount: "0",
NoCount: "1000000",
NoWithVetoCount: "1000000",
SpamCount: "0",
},
},
{
name: "quorum reached with spam > all other votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_ONE)
// spam votes
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_SPAM)
},
expectedPass: false,
expectedBurn: true,
expectedTally: v1.TallyResult{
YesCount: "1000000",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "6000000",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
govKeeper, mocks, _, ctx := setupGovKeeper(t, mockAccountKeeperExpectations)
params := v1.DefaultParams()
// Ensure params value are different than false
params.BurnVoteQuorum = true
params.BurnVoteVeto = true
err := govKeeper.Params.Set(ctx, params)
require.NoError(t, err)
var (
numVals = 10
numDelegators = 5
addrs = simtestutil.CreateRandomAccounts(numVals + numDelegators)
valAddrs = simtestutil.ConvertAddrsToValAddrs(addrs[:numVals])
delAddrs = addrs[numVals:]
)
// Mocks a bunch of validators
mocks.stakingKeeper.EXPECT().
IterateBondedValidatorsByPower(ctx, gomock.Any()).
DoAndReturn(
func(ctx context.Context, fn func(index int64, validator sdk.ValidatorI) bool) error {
for i := int64(0); i < int64(numVals); i++ {
fn(i, stakingtypes.Validator{
OperatorAddress: valAddrs[i].String(),
Status: stakingtypes.Bonded,
Tokens: sdkmath.NewInt(1000000),
DelegatorShares: sdkmath.LegacyNewDec(1000000),
})
}
return nil
})
// Submit and activate a proposal
proposal, err := govKeeper.SubmitProposal(ctx, TestProposal, "", "title", "summary", delAddrs[0], v1.ProposalType_PROPOSAL_TYPE_EXPEDITED)
require.NoError(t, err)
err = govKeeper.ActivateVotingPeriod(ctx, proposal)
require.NoError(t, err)
suite := tallyFixture{
t: t,
proposal: proposal,
valAddrs: valAddrs,
delAddrs: delAddrs,
ctx: ctx,
keeper: govKeeper,
mocks: mocks,
}
tt.setup(suite)
pass, burn, tally, err := govKeeper.Tally(ctx, proposal)
require.NoError(t, err)
assert.Equal(t, tt.expectedPass, pass, "wrong pass")
assert.Equal(t, tt.expectedBurn, burn, "wrong burn")
assert.Equal(t, tt.expectedTally, tally)
// Assert votes removal after tally
rng := collections.NewPrefixedPairRange[uint64, sdk.AccAddress](proposal.Id)
_, err = suite.keeper.Votes.Iterate(suite.ctx, rng)
assert.NoError(t, err)
})
}
}
func TestTally_Optimistic(t *testing.T) {
tests := []struct {
name string
setup func(tallyFixture)
expectedPass bool
expectedBurn bool
expectedTally v1.TallyResult
expectedError string
}{
{
name: "no votes, no bonded tokens: prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 0)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "no votes: prop passes",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
},
expectedPass: true,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "spam votes: prop fails/burn deposit",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[4], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[5], v1.VoteOption_VOTE_OPTION_SPAM)
validatorVote(s, s.valAddrs[6], v1.VoteOption_VOTE_OPTION_SPAM)
},
expectedPass: false,
expectedBurn: true,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "0",
NoWithVetoCount: "0",
SpamCount: "6000000",
},
},
{
name: "one delegator votes: threshold no not reached, prop passes",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
delegations := []stakingtypes.Delegation{{
DelegatorAddress: s.delAddrs[0].String(),
ValidatorAddress: s.valAddrs[0].String(),
Shares: sdkmath.LegacyNewDec(42),
}}
delegatorVote(s, s.delAddrs[0], delegations, v1.VoteOption_VOTE_OPTION_THREE)
},
expectedPass: true,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "42",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
{
name: "no vote threshold reached: prop fails",
setup: func(s tallyFixture) {
setTotalBonded(s, 10000000)
validatorVote(s, s.valAddrs[0], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[1], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[2], v1.VoteOption_VOTE_OPTION_THREE)
validatorVote(s, s.valAddrs[3], v1.VoteOption_VOTE_OPTION_THREE)
},
expectedPass: false,
expectedBurn: false,
expectedTally: v1.TallyResult{
YesCount: "0",
AbstainCount: "0",
NoCount: "4000000",
NoWithVetoCount: "0",
SpamCount: "0",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
govKeeper, mocks, _, ctx := setupGovKeeper(t, mockAccountKeeperExpectations)
params := v1.DefaultParams()
// Ensure params value are different than false
params.BurnVoteQuorum = true
params.BurnVoteVeto = true
err := govKeeper.Params.Set(ctx, params)
require.NoError(t, err)
var (
numVals = 10
numDelegators = 5
addrs = simtestutil.CreateRandomAccounts(numVals + numDelegators)
valAddrs = simtestutil.ConvertAddrsToValAddrs(addrs[:numVals])
delAddrs = addrs[numVals:]
)
// Mocks a bunch of validators
mocks.stakingKeeper.EXPECT().
IterateBondedValidatorsByPower(ctx, gomock.Any()).
DoAndReturn(
func(ctx context.Context, fn func(index int64, validator sdk.ValidatorI) bool) error {
for i := int64(0); i < int64(numVals); i++ {
fn(i, stakingtypes.Validator{
OperatorAddress: valAddrs[i].String(),
Status: stakingtypes.Bonded,
Tokens: sdkmath.NewInt(1000000),
DelegatorShares: sdkmath.LegacyNewDec(1000000),
})
}
return nil
})
// Submit and activate a proposal
proposal, err := govKeeper.SubmitProposal(ctx, TestProposal, "", "title", "summary", delAddrs[0], v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC)
require.NoError(t, err)
err = govKeeper.ActivateVotingPeriod(ctx, proposal)
require.NoError(t, err)
suite := tallyFixture{
t: t,
proposal: proposal,
valAddrs: valAddrs,

View File

@ -24,14 +24,26 @@ func (keeper Keeper) AddVote(ctx context.Context, proposalID uint64, voterAddr s
return errors.Wrapf(types.ErrInactiveProposal, "%d", proposalID)
}
err = keeper.assertMetadataLength(metadata)
if err := keeper.assertMetadataLength(metadata); err != nil {
return err
}
// get proposal
proposal, err := keeper.Proposals.Get(ctx, proposalID)
if err != nil {
return err
}
for _, option := range options {
if !v1.ValidWeightedVoteOption(*option) {
return errors.Wrap(types.ErrInvalidVote, option.String())
switch proposal.ProposalType {
case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC:
if option.Option != v1.OptionNo && option.Option != v1.OptionSpam {
return errors.Wrap(types.ErrInvalidVote, "optimistic proposals can only be rejected")
}
default:
if !v1.ValidWeightedVoteOption(*option) {
return errors.Wrap(types.ErrInvalidVote, option.String())
}
}
}

View File

@ -1,6 +1,8 @@
package v6
import (
"fmt"
"cosmossdk.io/collections"
v1 "cosmossdk.io/x/gov/types/v1"
@ -11,9 +13,10 @@ import (
// migration includes:
//
// Addition of new field in params to store types of proposals that can be submitted.
func MigrateStore(ctx sdk.Context, proposalCollection collections.Map[uint64, v1.Proposal]) error {
// Addition of gov params for optimistic proposals.
func MigrateStore(ctx sdk.Context, paramsCollection collections.Item[v1.Params], proposalCollection collections.Map[uint64, v1.Proposal]) error {
// Migrate proposals
return proposalCollection.Walk(ctx, nil, func(key uint64, proposal v1.Proposal) (bool, error) {
err := proposalCollection.Walk(ctx, nil, func(key uint64, proposal v1.Proposal) (bool, error) {
if proposal.Expedited {
proposal.ProposalType = v1.ProposalType_PROPOSAL_TYPE_EXPEDITED
} else {
@ -26,4 +29,19 @@ func MigrateStore(ctx sdk.Context, proposalCollection collections.Map[uint64, v1
return false, nil
})
if err != nil {
return err
}
// Migrate params
govParams, err := paramsCollection.Get(ctx)
if err != nil {
return fmt.Errorf("failed to get gov params: %w", err)
}
defaultParams := v1.DefaultParams()
govParams.OptimisticAuthorizedAddresses = defaultParams.OptimisticAuthorizedAddresses
govParams.OptimisticRejectedThreshold = defaultParams.OptimisticRejectedThreshold
return paramsCollection.Set(ctx, govParams)
}

View File

@ -80,13 +80,13 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
}
// ValidateGenesis performs genesis state validation for the gov module.
func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error {
func (am AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config client.TxEncodingConfig, bz json.RawMessage) error {
var data v1.GenesisState
if err := cdc.UnmarshalJSON(bz, &data); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", govtypes.ModuleName, err)
}
return v1.ValidateGenesis(&data)
return v1.ValidateGenesis(am.ac, &data)
}
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the gov module.

View File

@ -17,18 +17,19 @@ import (
// Simulation parameter constants
const (
MinDeposit = "min_deposit"
ExpeditedMinDeposit = "expedited_min_deposit"
DepositPeriod = "deposit_period"
MinInitialRatio = "min_initial_ratio"
VotingPeriod = "voting_period"
ExpeditedVotingPeriod = "expedited_voting_period"
Quorum = "quorum"
Threshold = "threshold"
ExpeditedThreshold = "expedited_threshold"
Veto = "veto"
ProposalCancelRate = "proposal_cancel_rate"
MinDepositRatio = "min_deposit_ratio"
MinDeposit = "min_deposit"
ExpeditedMinDeposit = "expedited_min_deposit"
DepositPeriod = "deposit_period"
MinInitialRatio = "min_initial_ratio"
VotingPeriod = "voting_period"
ExpeditedVotingPeriod = "expedited_voting_period"
Quorum = "quorum"
Threshold = "threshold"
ExpeditedThreshold = "expedited_threshold"
Veto = "veto"
OptimisticRejectedThreshold = "optimistic_rejected_threshold"
ProposalCancelRate = "proposal_cancel_rate"
MinDepositRatio = "min_deposit_ratio"
// ExpeditedThreshold must be at least as large as the regular Threshold
// Therefore, we use this break out point in randomization.
@ -90,6 +91,11 @@ func GenExpeditedThreshold(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simulation.RandIntBetween(r, tallyNonExpeditedMax, 550)), 3)
}
// GenOptimisticRejectedThreshold randomized OptimisticRejectedThreshold
func GenOptimisticRejectedThreshold(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simulation.RandIntBetween(r, 0, 200)), 3)
}
// GenVeto returns randomized Veto
func GenVeto(r *rand.Rand) sdkmath.LegacyDec {
return sdkmath.LegacyNewDecWithPrec(int64(simulation.RandIntBetween(r, 250, 334)), 3)
@ -137,12 +143,15 @@ func RandomizedGenState(simState *module.SimulationState) {
var veto sdkmath.LegacyDec
simState.AppParams.GetOrGenerate(Veto, &veto, simState.Rand, func(r *rand.Rand) { veto = GenVeto(r) })
var optimisticRejectedThreshold sdkmath.LegacyDec
simState.AppParams.GetOrGenerate(OptimisticRejectedThreshold, &optimisticRejectedThreshold, simState.Rand, func(r *rand.Rand) { optimisticRejectedThreshold = GenOptimisticRejectedThreshold(r) })
var minDepositRatio sdkmath.LegacyDec
simState.AppParams.GetOrGenerate(MinDepositRatio, &minDepositRatio, simState.Rand, func(r *rand.Rand) { minDepositRatio = GenMinDepositRatio(r) })
govGenesis := v1.NewGenesisState(
startingProposalID,
v1.NewParams(minDeposit, expeditedMinDeposit, depositPeriod, votingPeriod, expeditedVotingPeriod, quorum.String(), threshold.String(), expitedVotingThreshold.String(), veto.String(), minInitialDepositRatio.String(), proposalCancelRate.String(), "", simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, minDepositRatio.String()),
v1.NewParams(minDeposit, expeditedMinDeposit, depositPeriod, votingPeriod, expeditedVotingPeriod, quorum.String(), threshold.String(), expitedVotingThreshold.String(), veto.String(), minInitialDepositRatio.String(), proposalCancelRate.String(), "", simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, simState.Rand.Intn(2) == 0, minDepositRatio.String(), optimisticRejectedThreshold.String(), []string{}),
)
bz, err := json.MarshalIndent(&govGenesis, "", " ")

View File

@ -9,19 +9,20 @@ const (
EventTypeActiveProposal = "active_proposal"
EventTypeCancelProposal = "cancel_proposal"
AttributeKeyProposalResult = "proposal_result"
AttributeKeyVoter = "voter"
AttributeKeyOption = "option"
AttributeKeyProposalID = "proposal_id"
AttributeKeyProposalMessages = "proposal_messages" // Msg type_urls in the proposal
AttributeKeyVotingPeriodStart = "voting_period_start"
AttributeKeyProposalLog = "proposal_log" // log of proposal execution
AttributeValueProposalDropped = "proposal_dropped" // didn't meet min deposit
AttributeValueProposalPassed = "proposal_passed" // met vote quorum
AttributeValueProposalRejected = "proposal_rejected" // didn't meet vote quorum
AttributeValueExpeditedProposalRejected = "expedited_proposal_rejected" // didn't meet expedited vote quorum
AttributeValueProposalFailed = "proposal_failed" // error on proposal handler
AttributeValueProposalCanceled = "proposal_canceled" // error on proposal handler
AttributeKeyProposalResult = "proposal_result"
AttributeKeyVoter = "voter"
AttributeKeyOption = "option"
AttributeKeyProposalID = "proposal_id"
AttributeKeyProposalMessages = "proposal_messages" // Msg type_urls in the proposal
AttributeKeyVotingPeriodStart = "voting_period_start"
AttributeKeyProposalLog = "proposal_log" // log of proposal execution
AttributeValueProposalDropped = "proposal_dropped" // didn't meet min deposit
AttributeValueProposalPassed = "proposal_passed" // met vote quorum
AttributeValueProposalRejected = "proposal_rejected" // didn't meet vote quorum
AttributeValueExpeditedProposalRejected = "expedited_proposal_rejected" // didn't meet expedited vote quorum
AttributeValueOptimisticProposalRejected = "optimistic_proposal_rejected" // didn't meet optimistic vote quorum
AttributeValueProposalFailed = "proposal_failed" // error on proposal handler
AttributeValueProposalCanceled = "proposal_canceled" // error on proposal handler
AttributeKeyProposalType = "proposal_type"
AttributeSignalTitle = "signal_title"

View File

@ -6,6 +6,8 @@ import (
"golang.org/x/sync/errgroup"
"cosmossdk.io/core/address"
"github.com/cosmos/cosmos-sdk/codec/types"
)
@ -34,7 +36,7 @@ func (data GenesisState) Empty() bool {
// It checks if params are in valid ranges
// It also makes sure that the provided proposal IDs are unique and
// that there are no duplicate deposit or vote records and no vote or deposits for non-existent proposals
func ValidateGenesis(data *GenesisState) error {
func ValidateGenesis(ac address.Codec, data *GenesisState) error {
if data.StartingProposalId == 0 {
return errors.New("starting proposal id must be greater than 0")
}
@ -99,7 +101,7 @@ func ValidateGenesis(data *GenesisState) error {
// verify params
errGroup.Go(func() error {
return data.Params.ValidateBasic()
return data.Params.ValidateBasic(ac)
})
return errGroup.Wait()

View File

@ -8,6 +8,7 @@ import (
sdkmath "cosmossdk.io/math"
v1 "cosmossdk.io/x/gov/types/v1"
"github.com/cosmos/cosmos-sdk/codec/address"
sdk "github.com/cosmos/cosmos-sdk/types"
)
@ -20,6 +21,7 @@ func TestEmptyGenesis(t *testing.T) {
}
func TestValidateGenesis(t *testing.T) {
codec := address.NewBech32Codec("cosmos")
params := v1.DefaultParams()
testCases := []struct {
@ -175,7 +177,7 @@ func TestValidateGenesis(t *testing.T) {
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
err := v1.ValidateGenesis(tc.genesisState())
err := v1.ValidateGenesis(codec, tc.genesisState())
if tc.expErrMsg != "" {
require.Error(t, err)
require.ErrorContains(t, err, tc.expErrMsg)

View File

@ -884,6 +884,16 @@ type Params struct {
//
// Since: cosmos-sdk 0.50
MinDepositRatio string `protobuf:"bytes,16,opt,name=min_deposit_ratio,json=minDepositRatio,proto3" json:"min_deposit_ratio,omitempty"`
// optimistic_authorized_addresses is an optional governance parameter that limits the authorized accounts than can
// submit optimistic proposals
//
// Since: x/gov v1.0.0
OptimisticAuthorizedAddresses []string `protobuf:"bytes,17,rep,name=optimistic_authorized_addresses,json=optimisticAuthorizedAddresses,proto3" json:"optimistic_authorized_addresses,omitempty"`
// optimistic rejected threshold defines at which percentage of NO votes, the optimistic proposal should fail and be
// converted to a standard proposal. The threshold is expressed as a percentage of the total bonded tokens.
//
// Since: x/gov v1.0.0
OptimisticRejectedThreshold string `protobuf:"bytes,18,opt,name=optimistic_rejected_threshold,json=optimisticRejectedThreshold,proto3" json:"optimistic_rejected_threshold,omitempty"`
}
func (m *Params) Reset() { *m = Params{} }
@ -1031,6 +1041,20 @@ func (m *Params) GetMinDepositRatio() string {
return ""
}
func (m *Params) GetOptimisticAuthorizedAddresses() []string {
if m != nil {
return m.OptimisticAuthorizedAddresses
}
return nil
}
func (m *Params) GetOptimisticRejectedThreshold() string {
if m != nil {
return m.OptimisticRejectedThreshold
}
return ""
}
func init() {
proto.RegisterEnum("cosmos.gov.v1.ProposalType", ProposalType_name, ProposalType_value)
proto.RegisterEnum("cosmos.gov.v1.VoteOption", VoteOption_name, VoteOption_value)
@ -1049,106 +1073,110 @@ func init() {
func init() { proto.RegisterFile("cosmos/gov/v1/gov.proto", fileDescriptor_e05cb1c0d030febb) }
var fileDescriptor_e05cb1c0d030febb = []byte{
// 1581 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4d, 0x53, 0xe3, 0xc8,
0x19, 0x46, 0xfe, 0xc2, 0x7e, 0xfd, 0x81, 0x68, 0x60, 0x11, 0xb0, 0x18, 0xd6, 0xd9, 0xda, 0x22,
0x64, 0xc7, 0x0e, 0xbb, 0x99, 0x1c, 0x76, 0x52, 0x95, 0xf8, 0x43, 0x13, 0x44, 0x01, 0x76, 0x64,
0x01, 0x33, 0xb9, 0xa8, 0x04, 0xea, 0x31, 0xaa, 0x58, 0x6a, 0x47, 0x6a, 0x33, 0xf8, 0x0f, 0xe4,
0x9a, 0x39, 0xe6, 0x94, 0xca, 0x2d, 0x39, 0xe6, 0x30, 0x95, 0xdf, 0x30, 0x95, 0x43, 0x6a, 0x6a,
0x4e, 0xb9, 0x64, 0x92, 0x9a, 0x39, 0xa4, 0x6a, 0x7e, 0x42, 0x4e, 0x29, 0xb5, 0x5a, 0x96, 0x6c,
0x3c, 0x0b, 0xcc, 0x05, 0xa4, 0xf7, 0x7d, 0x9e, 0xa7, 0xdf, 0x7e, 0x3f, 0xba, 0x2d, 0x58, 0xbd,
0x20, 0x9e, 0x4d, 0xbc, 0x5a, 0x8f, 0x5c, 0xd5, 0xae, 0xf6, 0xfc, 0x7f, 0xd5, 0x81, 0x4b, 0x28,
0x41, 0xc5, 0xc0, 0x51, 0xf5, 0x2d, 0x57, 0x7b, 0xeb, 0x65, 0x8e, 0x3b, 0x37, 0x3c, 0x5c, 0xbb,
0xda, 0x3b, 0xc7, 0xd4, 0xd8, 0xab, 0x5d, 0x10, 0xcb, 0x09, 0xe0, 0xeb, 0xcb, 0x3d, 0xd2, 0x23,
0xec, 0xb1, 0xe6, 0x3f, 0x71, 0xeb, 0x56, 0x8f, 0x90, 0x5e, 0x1f, 0xd7, 0xd8, 0xdb, 0xf9, 0xf0,
0x59, 0x8d, 0x5a, 0x36, 0xf6, 0xa8, 0x61, 0x0f, 0x38, 0x60, 0x6d, 0x1a, 0x60, 0x38, 0x23, 0xee,
0x2a, 0x4f, 0xbb, 0xcc, 0xa1, 0x6b, 0x50, 0x8b, 0x84, 0x2b, 0xae, 0x05, 0x11, 0xe9, 0xc1, 0xa2,
0x3c, 0xda, 0xc0, 0xb5, 0x68, 0xd8, 0x96, 0x43, 0x6a, 0xec, 0x6f, 0x60, 0xaa, 0x10, 0x40, 0x67,
0xd8, 0xea, 0x5d, 0x52, 0x6c, 0x9e, 0x12, 0x8a, 0xdb, 0x03, 0x5f, 0x09, 0xed, 0x41, 0x86, 0xb0,
0x27, 0x49, 0xd8, 0x16, 0x76, 0x4a, 0xdf, 0xac, 0x55, 0x27, 0x76, 0x5d, 0x8d, 0xa0, 0x2a, 0x07,
0xa2, 0xaf, 0x20, 0xf3, 0x9c, 0x09, 0x49, 0x89, 0x6d, 0x61, 0x27, 0xd7, 0x28, 0xbd, 0x79, 0xf9,
0x00, 0x38, 0xab, 0x85, 0x2f, 0x54, 0xee, 0xad, 0xfc, 0x49, 0x80, 0xf9, 0x16, 0x1e, 0x10, 0xcf,
0xa2, 0x68, 0x0b, 0xf2, 0x03, 0x97, 0x0c, 0x88, 0x67, 0xf4, 0x75, 0xcb, 0x64, 0x6b, 0xa5, 0x54,
0x08, 0x4d, 0x8a, 0x89, 0x7e, 0x0a, 0x39, 0x33, 0xc0, 0x12, 0x97, 0xeb, 0x4a, 0x6f, 0x5e, 0x3e,
0x58, 0xe6, 0xba, 0x75, 0xd3, 0x74, 0xb1, 0xe7, 0x75, 0xa9, 0x6b, 0x39, 0x3d, 0x35, 0x82, 0xa2,
0x9f, 0x41, 0xc6, 0xb0, 0xc9, 0xd0, 0xa1, 0x52, 0x72, 0x3b, 0xb9, 0x93, 0x8f, 0xe2, 0xf7, 0xcb,
0x54, 0xe5, 0x65, 0xaa, 0x36, 0x89, 0xe5, 0x34, 0x72, 0xaf, 0xde, 0x6e, 0xcd, 0xfd, 0xe5, 0xbf,
0x7f, 0xdd, 0x15, 0x54, 0xce, 0xa9, 0xfc, 0x3d, 0x03, 0xd9, 0x0e, 0x0f, 0x02, 0x95, 0x20, 0x31,
0x0e, 0x2d, 0x61, 0x99, 0xe8, 0xc7, 0x90, 0xb5, 0xb1, 0xe7, 0x19, 0x3d, 0xec, 0x49, 0x09, 0x26,
0xbe, 0x5c, 0x0d, 0x2a, 0x52, 0x0d, 0x2b, 0x52, 0xad, 0x3b, 0x23, 0x75, 0x8c, 0x42, 0x0f, 0x21,
0xe3, 0x51, 0x83, 0x0e, 0x3d, 0x29, 0xc9, 0x92, 0xb9, 0x39, 0x95, 0xcc, 0x70, 0xa9, 0x2e, 0x03,
0xa9, 0x1c, 0x8c, 0xf6, 0x01, 0x3d, 0xb3, 0x1c, 0xa3, 0xaf, 0x53, 0xa3, 0xdf, 0x1f, 0xe9, 0x2e,
0xf6, 0x86, 0x7d, 0x2a, 0xa5, 0xb6, 0x85, 0x9d, 0xfc, 0x37, 0xeb, 0x53, 0x12, 0x9a, 0x0f, 0x51,
0x19, 0x42, 0x15, 0x19, 0x2b, 0x66, 0x41, 0x75, 0xc8, 0x7b, 0xc3, 0x73, 0xdb, 0xa2, 0xba, 0xdf,
0x66, 0x52, 0x9a, 0x4b, 0x4c, 0x47, 0xad, 0x85, 0x3d, 0xd8, 0x48, 0xbd, 0xf8, 0xf7, 0x96, 0xa0,
0x42, 0x40, 0xf2, 0xcd, 0xe8, 0x00, 0x44, 0x9e, 0x5d, 0x1d, 0x3b, 0x66, 0xa0, 0x93, 0xb9, 0xa3,
0x4e, 0x89, 0x33, 0x65, 0xc7, 0x64, 0x5a, 0x0a, 0x14, 0x29, 0xa1, 0x46, 0x5f, 0xe7, 0x76, 0x69,
0xfe, 0x1e, 0x35, 0x2a, 0x30, 0x6a, 0xd8, 0x40, 0x87, 0xb0, 0x78, 0x45, 0xa8, 0xe5, 0xf4, 0x74,
0x8f, 0x1a, 0x2e, 0xdf, 0x5f, 0xf6, 0x8e, 0x71, 0x2d, 0x04, 0xd4, 0xae, 0xcf, 0x64, 0x81, 0xed,
0x03, 0x37, 0x45, 0x7b, 0xcc, 0xdd, 0x51, 0xab, 0x18, 0x10, 0xc3, 0x2d, 0xae, 0xfb, 0x4d, 0x42,
0x0d, 0xd3, 0xa0, 0x86, 0x04, 0x7e, 0xdb, 0xaa, 0xe3, 0x77, 0xb4, 0x0c, 0x69, 0x6a, 0xd1, 0x3e,
0x96, 0xf2, 0xcc, 0x11, 0xbc, 0x20, 0x09, 0xe6, 0xbd, 0xa1, 0x6d, 0x1b, 0xee, 0x48, 0x2a, 0x30,
0x7b, 0xf8, 0x8a, 0x7e, 0x02, 0xd9, 0x60, 0x22, 0xb0, 0x2b, 0x15, 0x6f, 0x19, 0x81, 0x31, 0x12,
0x6d, 0x43, 0x0e, 0x5f, 0x0f, 0xb0, 0x69, 0x51, 0x6c, 0x4a, 0xa5, 0x6d, 0x61, 0x27, 0xdb, 0x48,
0x48, 0x82, 0x1a, 0x19, 0xd1, 0x0f, 0xa0, 0xf8, 0xcc, 0xb0, 0xfa, 0xd8, 0xd4, 0x5d, 0x6c, 0x78,
0xc4, 0x91, 0x16, 0xd8, 0xba, 0x85, 0xc0, 0xa8, 0x32, 0x1b, 0xfa, 0x05, 0x14, 0xc7, 0x13, 0x4a,
0x47, 0x03, 0x2c, 0x89, 0xac, 0x85, 0x37, 0x3e, 0xd2, 0xc2, 0xda, 0x68, 0x80, 0xd5, 0xc2, 0x20,
0xf6, 0x56, 0xf9, 0x5d, 0x02, 0xf2, 0xf1, 0x66, 0xfc, 0x11, 0xe4, 0x46, 0xd8, 0xd3, 0x2f, 0xd8,
0x74, 0x0a, 0x37, 0x8e, 0x0a, 0xc5, 0xa1, 0x6a, 0x76, 0x84, 0xbd, 0xa6, 0xef, 0x47, 0xdf, 0x42,
0xd1, 0x38, 0xf7, 0xa8, 0x61, 0x39, 0x9c, 0x90, 0x98, 0x49, 0x28, 0x70, 0x50, 0x40, 0xfa, 0x21,
0x64, 0x1d, 0xc2, 0xf1, 0xc9, 0x99, 0xf8, 0x79, 0x87, 0x04, 0xd0, 0x47, 0x80, 0x1c, 0xa2, 0x3f,
0xb7, 0xe8, 0xa5, 0x7e, 0x85, 0x69, 0x48, 0x4a, 0xcd, 0x24, 0x2d, 0x38, 0xe4, 0xcc, 0xa2, 0x97,
0xa7, 0x98, 0x72, 0xf2, 0x03, 0x00, 0x6f, 0x60, 0xd8, 0x9c, 0x94, 0x9e, 0x49, 0xca, 0xf9, 0x08,
0x06, 0xaf, 0xfc, 0x4d, 0x80, 0x94, 0x7f, 0x6e, 0xde, 0x7e, 0xea, 0x55, 0x21, 0x7d, 0x45, 0x28,
0xbe, 0xfd, 0xc4, 0x0b, 0x60, 0xe8, 0x11, 0xcc, 0x07, 0x87, 0xb0, 0x27, 0xa5, 0xd8, 0x28, 0x7d,
0x31, 0x55, 0x9e, 0x9b, 0x27, 0xbc, 0x1a, 0x32, 0x26, 0x5a, 0x35, 0x3d, 0xd9, 0xaa, 0x07, 0xa9,
0x6c, 0x52, 0x4c, 0x55, 0xfe, 0x25, 0x40, 0x91, 0x0f, 0x5c, 0xc7, 0x70, 0x0d, 0xdb, 0x43, 0x4f,
0x21, 0x6f, 0x5b, 0xce, 0x78, 0x7e, 0x85, 0xdb, 0xe6, 0x77, 0xd3, 0x9f, 0xdf, 0x0f, 0x6f, 0xb7,
0x56, 0x62, 0xac, 0xaf, 0x89, 0x6d, 0x51, 0x6c, 0x0f, 0xe8, 0x48, 0x05, 0xdb, 0x72, 0xc2, 0x89,
0xb6, 0x01, 0xd9, 0xc6, 0x75, 0x08, 0xd2, 0x07, 0xd8, 0xb5, 0x88, 0xc9, 0x12, 0xe1, 0xaf, 0x30,
0x3d, 0x86, 0x2d, 0x7e, 0xf5, 0x35, 0xbe, 0xfc, 0xf0, 0x76, 0xeb, 0xf3, 0x9b, 0xc4, 0x68, 0x91,
0x3f, 0xf8, 0x53, 0x2a, 0xda, 0xc6, 0x75, 0xb8, 0x13, 0xe6, 0xff, 0x2e, 0x21, 0x09, 0x95, 0x27,
0x50, 0x38, 0x65, 0xd3, 0xcb, 0x77, 0xd7, 0x02, 0x3e, 0xcd, 0xe1, 0xea, 0xc2, 0x6d, 0xab, 0xa7,
0x98, 0x7a, 0x21, 0x60, 0xc5, 0x94, 0xff, 0x28, 0xf0, 0xde, 0xe7, 0xca, 0x5f, 0x41, 0xe6, 0xb7,
0x43, 0xe2, 0x0e, 0xed, 0x19, 0x8d, 0xcf, 0xee, 0xc8, 0xc0, 0x8b, 0xbe, 0x86, 0x1c, 0xbd, 0x74,
0xb1, 0x77, 0x49, 0xfa, 0xe6, 0x47, 0xae, 0xd3, 0x08, 0x80, 0x1e, 0x42, 0x89, 0x35, 0x6f, 0x44,
0x49, 0xce, 0xa4, 0x14, 0x7d, 0x94, 0x16, 0x82, 0x58, 0x80, 0xbf, 0xcf, 0x42, 0x86, 0xc7, 0x26,
0xdf, 0xb3, 0xa6, 0xb1, 0x33, 0x39, 0x5e, 0xbf, 0xa3, 0x4f, 0xab, 0x5f, 0x6a, 0x76, 0x7d, 0x6e,
0xd6, 0x22, 0xf9, 0x09, 0xb5, 0x88, 0xe5, 0x3d, 0x75, 0xf7, 0xbc, 0xa7, 0xef, 0x9f, 0xf7, 0xcc,
0x1d, 0xf2, 0x8e, 0x14, 0x58, 0xf3, 0x13, 0x6d, 0x39, 0x16, 0xb5, 0xa2, 0x4b, 0x50, 0x67, 0xe1,
0x4b, 0xf3, 0x33, 0x15, 0x3e, 0xb3, 0x2d, 0x47, 0x09, 0xf0, 0x3c, 0x3d, 0xaa, 0x8f, 0x46, 0x0d,
0x58, 0x19, 0x9f, 0x24, 0x17, 0x86, 0x73, 0x81, 0xfb, 0x5c, 0x26, 0x3b, 0x53, 0x66, 0x29, 0x04,
0x37, 0x19, 0x36, 0xd0, 0x38, 0x80, 0xe5, 0x69, 0x0d, 0x13, 0x7b, 0x94, 0xdd, 0x7c, 0xdf, 0x77,
0xf6, 0xa0, 0x49, 0xb1, 0x16, 0xf6, 0x28, 0x3a, 0x83, 0xd5, 0xf1, 0xfd, 0xa2, 0x4f, 0xd6, 0x0d,
0xee, 0x56, 0xb7, 0x95, 0x31, 0xff, 0x34, 0x5e, 0xc0, 0x9f, 0xc3, 0x52, 0x24, 0x1c, 0xe5, 0x3b,
0x3f, 0x73, 0x9b, 0x68, 0x0c, 0x8d, 0x92, 0xfe, 0x04, 0x22, 0x65, 0x3d, 0xde, 0xe7, 0x85, 0x7b,
0xf4, 0x79, 0x14, 0xc3, 0x51, 0xd4, 0xf0, 0x3b, 0x20, 0x9e, 0x0f, 0x5d, 0xc7, 0xdf, 0x2e, 0xd6,
0x79, 0x97, 0xf9, 0xd7, 0x74, 0x56, 0x2d, 0xf9, 0x76, 0xff, 0xc8, 0xfd, 0x55, 0xd0, 0x5d, 0x75,
0xd8, 0x64, 0xc8, 0x71, 0xba, 0xc7, 0x43, 0xe2, 0x62, 0x9f, 0x1d, 0x5c, 0xd3, 0xea, 0xba, 0x0f,
0x0a, 0x2f, 0xd4, 0x70, 0x1a, 0x02, 0x04, 0xfa, 0x12, 0x4a, 0xd1, 0x62, 0x7e, 0x5b, 0xb1, 0x4b,
0x3b, 0xab, 0x16, 0xc2, 0xa5, 0xfc, 0xdb, 0x09, 0x7d, 0x07, 0x8b, 0xb1, 0x2d, 0xf2, 0x96, 0x10,
0x67, 0xe6, 0x6a, 0x21, 0x1a, 0x5d, 0xd6, 0x0e, 0xbb, 0x7f, 0x16, 0xa0, 0x10, 0xbf, 0xcd, 0xd1,
0x26, 0xac, 0x75, 0xd4, 0x76, 0xa7, 0xdd, 0xad, 0x1f, 0xea, 0xda, 0xd3, 0x8e, 0xac, 0x9f, 0x1c,
0x77, 0x3b, 0x72, 0x53, 0x79, 0xac, 0xc8, 0x2d, 0x71, 0x0e, 0xad, 0xc3, 0x67, 0x93, 0xee, 0xae,
0x56, 0x3f, 0x6e, 0xd5, 0xd5, 0x96, 0x28, 0xa0, 0x2f, 0x60, 0x73, 0xd2, 0x77, 0x74, 0x72, 0xa8,
0x29, 0x9d, 0x43, 0x59, 0x6f, 0xee, 0xb7, 0x95, 0xa6, 0x2c, 0x26, 0xd0, 0xe7, 0x20, 0x4d, 0x42,
0xda, 0x1d, 0x4d, 0x39, 0x52, 0xba, 0x9a, 0xd2, 0x14, 0x93, 0x68, 0x03, 0x56, 0x27, 0xbd, 0xf2,
0x93, 0x8e, 0xdc, 0x52, 0x34, 0xb9, 0x25, 0xa6, 0x76, 0xff, 0x27, 0x00, 0xc4, 0x3e, 0x59, 0x36,
0x60, 0xf5, 0xb4, 0xad, 0x05, 0x02, 0xed, 0xe3, 0xa9, 0x28, 0x97, 0x60, 0x21, 0xee, 0x6c, 0x1f,
0xcb, 0xa2, 0x30, 0x6d, 0x7c, 0x2a, 0x77, 0x6f, 0x1a, 0xb5, 0xb3, 0xb6, 0x98, 0x40, 0xab, 0xb0,
0x14, 0x37, 0xd6, 0x1b, 0x5d, 0xad, 0xae, 0x1c, 0x8b, 0x09, 0xb4, 0x02, 0x8b, 0x13, 0xe8, 0x7d,
0x55, 0x96, 0xc5, 0x24, 0x42, 0x50, 0x8a, 0x9b, 0x8f, 0xdb, 0x62, 0x12, 0x2d, 0x83, 0x18, 0xb7,
0x3d, 0x6e, 0x9f, 0xa8, 0x62, 0xca, 0xdf, 0xff, 0x24, 0x52, 0x3f, 0x53, 0xb4, 0x7d, 0xfd, 0x54,
0xd6, 0xda, 0x62, 0x6a, 0x9a, 0xd3, 0xed, 0xd4, 0x8f, 0xc4, 0xf4, 0x7a, 0x42, 0x14, 0x76, 0xff,
0x21, 0x40, 0x69, 0xf2, 0xbb, 0x01, 0x6d, 0xc1, 0xc6, 0x38, 0x59, 0x5d, 0xad, 0xae, 0x9d, 0x74,
0xa7, 0x92, 0x50, 0x81, 0xf2, 0x34, 0xa0, 0x25, 0x77, 0xda, 0x5d, 0x45, 0xd3, 0x3b, 0xb2, 0xaa,
0xb4, 0xa7, 0x4b, 0xc6, 0x31, 0xa7, 0x6d, 0x4d, 0x39, 0xfe, 0x65, 0x08, 0x49, 0x4c, 0x54, 0x9c,
0x43, 0x3a, 0xf5, 0x6e, 0x57, 0x6e, 0x89, 0xc9, 0x89, 0x72, 0x72, 0x9f, 0x2a, 0x1f, 0xc8, 0x4d,
0x56, 0xb1, 0x59, 0xcc, 0xc7, 0x75, 0xe5, 0x50, 0x6e, 0x89, 0xe9, 0xc6, 0xc3, 0x57, 0xef, 0xca,
0xc2, 0xeb, 0x77, 0x65, 0xe1, 0x3f, 0xef, 0xca, 0xc2, 0x8b, 0xf7, 0xe5, 0xb9, 0xd7, 0xef, 0xcb,
0x73, 0xff, 0x7c, 0x5f, 0x9e, 0xfb, 0xf5, 0x46, 0xd0, 0xae, 0x9e, 0xf9, 0x9b, 0xaa, 0x45, 0x6a,
0xd7, 0xec, 0x8b, 0xdc, 0xff, 0x29, 0xea, 0xf9, 0x9f, 0xdb, 0x19, 0x76, 0x90, 0x7c, 0xfb, 0xff,
0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0xbf, 0x15, 0xa8, 0xaf, 0x0f, 0x00, 0x00,
// 1645 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x58, 0x4d, 0x6f, 0xe3, 0xc6,
0x19, 0x36, 0xf5, 0x65, 0xe9, 0xd5, 0x87, 0xe9, 0x59, 0x6f, 0x4c, 0xdb, 0xb1, 0xe4, 0xa8, 0x41,
0xe0, 0xba, 0x59, 0xa9, 0x4e, 0xba, 0x3d, 0x24, 0x05, 0x5a, 0x7d, 0x70, 0x6b, 0x1a, 0xb6, 0xa5,
0x52, 0xb4, 0xbd, 0xdb, 0x0b, 0x4b, 0x8b, 0xb3, 0x32, 0x5b, 0x91, 0xa3, 0x92, 0x23, 0xc5, 0xea,
0x0f, 0xe8, 0x39, 0xc7, 0x9e, 0x8a, 0xde, 0xda, 0x63, 0x0f, 0x41, 0xff, 0x41, 0x81, 0xa0, 0x87,
0x22, 0xc8, 0xa9, 0x97, 0x6e, 0x8b, 0xdd, 0x43, 0x81, 0xfc, 0x84, 0x9e, 0x02, 0x0e, 0x87, 0x22,
0x25, 0x2b, 0xb1, 0x9d, 0x8b, 0x4d, 0xbe, 0xef, 0xf3, 0x3c, 0xf3, 0xce, 0xfb, 0x31, 0x23, 0x09,
0x36, 0xfb, 0xc4, 0xb3, 0x89, 0x57, 0x1f, 0x90, 0x49, 0x7d, 0x72, 0xe8, 0xff, 0xab, 0x8d, 0x5c,
0x42, 0x09, 0x2a, 0x06, 0x8e, 0x9a, 0x6f, 0x99, 0x1c, 0x6e, 0x97, 0x39, 0xee, 0xca, 0xf0, 0x70,
0x7d, 0x72, 0x78, 0x85, 0xa9, 0x71, 0x58, 0xef, 0x13, 0xcb, 0x09, 0xe0, 0xdb, 0x1b, 0x03, 0x32,
0x20, 0xec, 0xb1, 0xee, 0x3f, 0x71, 0x6b, 0x65, 0x40, 0xc8, 0x60, 0x88, 0xeb, 0xec, 0xed, 0x6a,
0xfc, 0xb2, 0x4e, 0x2d, 0x1b, 0x7b, 0xd4, 0xb0, 0x47, 0x1c, 0xb0, 0xb5, 0x08, 0x30, 0x9c, 0x29,
0x77, 0x95, 0x17, 0x5d, 0xe6, 0xd8, 0x35, 0xa8, 0x45, 0xc2, 0x15, 0xb7, 0x82, 0x88, 0xf4, 0x60,
0x51, 0x1e, 0x6d, 0xe0, 0x5a, 0x37, 0x6c, 0xcb, 0x21, 0x75, 0xf6, 0x37, 0x30, 0x55, 0x09, 0xa0,
0x4b, 0x6c, 0x0d, 0xae, 0x29, 0x36, 0x2f, 0x08, 0xc5, 0x9d, 0x91, 0xaf, 0x84, 0x0e, 0x21, 0x43,
0xd8, 0x93, 0x24, 0xec, 0x09, 0xfb, 0xa5, 0x0f, 0xb6, 0x6a, 0x73, 0xbb, 0xae, 0x45, 0x50, 0x95,
0x03, 0xd1, 0x7b, 0x90, 0xf9, 0x84, 0x09, 0x49, 0x89, 0x3d, 0x61, 0x3f, 0xd7, 0x2c, 0x7d, 0xf9,
0xd9, 0x13, 0xe0, 0xac, 0x36, 0xee, 0xab, 0xdc, 0x5b, 0xfd, 0x93, 0x00, 0xab, 0x6d, 0x3c, 0x22,
0x9e, 0x45, 0x51, 0x05, 0xf2, 0x23, 0x97, 0x8c, 0x88, 0x67, 0x0c, 0x75, 0xcb, 0x64, 0x6b, 0xa5,
0x54, 0x08, 0x4d, 0x8a, 0x89, 0x7e, 0x0c, 0x39, 0x33, 0xc0, 0x12, 0x97, 0xeb, 0x4a, 0x5f, 0x7e,
0xf6, 0x64, 0x83, 0xeb, 0x36, 0x4c, 0xd3, 0xc5, 0x9e, 0xd7, 0xa3, 0xae, 0xe5, 0x0c, 0xd4, 0x08,
0x8a, 0x7e, 0x02, 0x19, 0xc3, 0x26, 0x63, 0x87, 0x4a, 0xc9, 0xbd, 0xe4, 0x7e, 0x3e, 0x8a, 0xdf,
0x2f, 0x53, 0x8d, 0x97, 0xa9, 0xd6, 0x22, 0x96, 0xd3, 0xcc, 0x7d, 0xfe, 0xaa, 0xb2, 0xf2, 0x97,
0xff, 0xfd, 0xf5, 0x40, 0x50, 0x39, 0xa7, 0xfa, 0x8f, 0x0c, 0x64, 0xbb, 0x3c, 0x08, 0x54, 0x82,
0xc4, 0x2c, 0xb4, 0x84, 0x65, 0xa2, 0x1f, 0x42, 0xd6, 0xc6, 0x9e, 0x67, 0x0c, 0xb0, 0x27, 0x25,
0x98, 0xf8, 0x46, 0x2d, 0xa8, 0x48, 0x2d, 0xac, 0x48, 0xad, 0xe1, 0x4c, 0xd5, 0x19, 0x0a, 0x3d,
0x85, 0x8c, 0x47, 0x0d, 0x3a, 0xf6, 0xa4, 0x24, 0x4b, 0xe6, 0xee, 0x42, 0x32, 0xc3, 0xa5, 0x7a,
0x0c, 0xa4, 0x72, 0x30, 0x3a, 0x02, 0xf4, 0xd2, 0x72, 0x8c, 0xa1, 0x4e, 0x8d, 0xe1, 0x70, 0xaa,
0xbb, 0xd8, 0x1b, 0x0f, 0xa9, 0x94, 0xda, 0x13, 0xf6, 0xf3, 0x1f, 0x6c, 0x2f, 0x48, 0x68, 0x3e,
0x44, 0x65, 0x08, 0x55, 0x64, 0xac, 0x98, 0x05, 0x35, 0x20, 0xef, 0x8d, 0xaf, 0x6c, 0x8b, 0xea,
0x7e, 0x9b, 0x49, 0x69, 0x2e, 0xb1, 0x18, 0xb5, 0x16, 0xf6, 0x60, 0x33, 0xf5, 0xe9, 0x7f, 0x2a,
0x82, 0x0a, 0x01, 0xc9, 0x37, 0xa3, 0x63, 0x10, 0x79, 0x76, 0x75, 0xec, 0x98, 0x81, 0x4e, 0xe6,
0x9e, 0x3a, 0x25, 0xce, 0x94, 0x1d, 0x93, 0x69, 0x29, 0x50, 0xa4, 0x84, 0x1a, 0x43, 0x9d, 0xdb,
0xa5, 0xd5, 0x07, 0xd4, 0xa8, 0xc0, 0xa8, 0x61, 0x03, 0x9d, 0xc0, 0xfa, 0x84, 0x50, 0xcb, 0x19,
0xe8, 0x1e, 0x35, 0x5c, 0xbe, 0xbf, 0xec, 0x3d, 0xe3, 0x5a, 0x0b, 0xa8, 0x3d, 0x9f, 0xc9, 0x02,
0x3b, 0x02, 0x6e, 0x8a, 0xf6, 0x98, 0xbb, 0xa7, 0x56, 0x31, 0x20, 0x86, 0x5b, 0xdc, 0xf6, 0x9b,
0x84, 0x1a, 0xa6, 0x41, 0x0d, 0x09, 0xfc, 0xb6, 0x55, 0x67, 0xef, 0x68, 0x03, 0xd2, 0xd4, 0xa2,
0x43, 0x2c, 0xe5, 0x99, 0x23, 0x78, 0x41, 0x12, 0xac, 0x7a, 0x63, 0xdb, 0x36, 0xdc, 0xa9, 0x54,
0x60, 0xf6, 0xf0, 0x15, 0xfd, 0x08, 0xb2, 0xc1, 0x44, 0x60, 0x57, 0x2a, 0xde, 0x31, 0x02, 0x33,
0x24, 0xda, 0x83, 0x1c, 0xbe, 0x19, 0x61, 0xd3, 0xa2, 0xd8, 0x94, 0x4a, 0x7b, 0xc2, 0x7e, 0xb6,
0x99, 0x90, 0x04, 0x35, 0x32, 0xa2, 0xef, 0x41, 0xf1, 0xa5, 0x61, 0x0d, 0xb1, 0xa9, 0xbb, 0xd8,
0xf0, 0x88, 0x23, 0xad, 0xb1, 0x75, 0x0b, 0x81, 0x51, 0x65, 0x36, 0xf4, 0x33, 0x28, 0xce, 0x26,
0x94, 0x4e, 0x47, 0x58, 0x12, 0x59, 0x0b, 0xef, 0x7c, 0x43, 0x0b, 0x6b, 0xd3, 0x11, 0x56, 0x0b,
0xa3, 0xd8, 0x5b, 0xf5, 0xf7, 0x09, 0xc8, 0xc7, 0x9b, 0xf1, 0x07, 0x90, 0x9b, 0x62, 0x4f, 0xef,
0xb3, 0xe9, 0x14, 0x6e, 0x1d, 0x15, 0x8a, 0x43, 0xd5, 0xec, 0x14, 0x7b, 0x2d, 0xdf, 0x8f, 0x3e,
0x84, 0xa2, 0x71, 0xe5, 0x51, 0xc3, 0x72, 0x38, 0x21, 0xb1, 0x94, 0x50, 0xe0, 0xa0, 0x80, 0xf4,
0x7d, 0xc8, 0x3a, 0x84, 0xe3, 0x93, 0x4b, 0xf1, 0xab, 0x0e, 0x09, 0xa0, 0x1f, 0x03, 0x72, 0x88,
0xfe, 0x89, 0x45, 0xaf, 0xf5, 0x09, 0xa6, 0x21, 0x29, 0xb5, 0x94, 0xb4, 0xe6, 0x90, 0x4b, 0x8b,
0x5e, 0x5f, 0x60, 0xca, 0xc9, 0x4f, 0x00, 0xbc, 0x91, 0x61, 0x73, 0x52, 0x7a, 0x29, 0x29, 0xe7,
0x23, 0x18, 0xbc, 0xfa, 0x37, 0x01, 0x52, 0xfe, 0xb9, 0x79, 0xf7, 0xa9, 0x57, 0x83, 0xf4, 0x84,
0x50, 0x7c, 0xf7, 0x89, 0x17, 0xc0, 0xd0, 0xc7, 0xb0, 0x1a, 0x1c, 0xc2, 0x9e, 0x94, 0x62, 0xa3,
0xf4, 0xce, 0x42, 0x79, 0x6e, 0x9f, 0xf0, 0x6a, 0xc8, 0x98, 0x6b, 0xd5, 0xf4, 0x7c, 0xab, 0x1e,
0xa7, 0xb2, 0x49, 0x31, 0x55, 0xfd, 0xb7, 0x00, 0x45, 0x3e, 0x70, 0x5d, 0xc3, 0x35, 0x6c, 0x0f,
0xbd, 0x80, 0xbc, 0x6d, 0x39, 0xb3, 0xf9, 0x15, 0xee, 0x9a, 0xdf, 0x5d, 0x7f, 0x7e, 0xbf, 0x7a,
0x55, 0x79, 0x1c, 0x63, 0xbd, 0x4f, 0x6c, 0x8b, 0x62, 0x7b, 0x44, 0xa7, 0x2a, 0xd8, 0x96, 0x13,
0x4e, 0xb4, 0x0d, 0xc8, 0x36, 0x6e, 0x42, 0x90, 0x3e, 0xc2, 0xae, 0x45, 0x4c, 0x96, 0x08, 0x7f,
0x85, 0xc5, 0x31, 0x6c, 0xf3, 0xab, 0xaf, 0xf9, 0xee, 0x57, 0xaf, 0x2a, 0x6f, 0xdf, 0x26, 0x46,
0x8b, 0xfc, 0xc1, 0x9f, 0x52, 0xd1, 0x36, 0x6e, 0xc2, 0x9d, 0x30, 0xff, 0x47, 0x09, 0x49, 0xa8,
0x3e, 0x87, 0xc2, 0x05, 0x9b, 0x5e, 0xbe, 0xbb, 0x36, 0xf0, 0x69, 0x0e, 0x57, 0x17, 0xee, 0x5a,
0x3d, 0xc5, 0xd4, 0x0b, 0x01, 0x2b, 0xa6, 0xfc, 0x47, 0x81, 0xf7, 0x3e, 0x57, 0x7e, 0x0f, 0x32,
0xbf, 0x1d, 0x13, 0x77, 0x6c, 0x2f, 0x69, 0x7c, 0x76, 0x47, 0x06, 0x5e, 0xf4, 0x3e, 0xe4, 0xe8,
0xb5, 0x8b, 0xbd, 0x6b, 0x32, 0x34, 0xbf, 0xe1, 0x3a, 0x8d, 0x00, 0xe8, 0x29, 0x94, 0x58, 0xf3,
0x46, 0x94, 0xe4, 0x52, 0x4a, 0xd1, 0x47, 0x69, 0x21, 0x88, 0x05, 0xf8, 0xf7, 0x1c, 0x64, 0x78,
0x6c, 0xf2, 0x03, 0x6b, 0x1a, 0x3b, 0x93, 0xe3, 0xf5, 0x3b, 0xfd, 0x6e, 0xf5, 0x4b, 0x2d, 0xaf,
0xcf, 0xed, 0x5a, 0x24, 0xbf, 0x43, 0x2d, 0x62, 0x79, 0x4f, 0xdd, 0x3f, 0xef, 0xe9, 0x87, 0xe7,
0x3d, 0x73, 0x8f, 0xbc, 0x23, 0x05, 0xb6, 0xfc, 0x44, 0x5b, 0x8e, 0x45, 0xad, 0xe8, 0x12, 0xd4,
0x59, 0xf8, 0xd2, 0xea, 0x52, 0x85, 0xb7, 0x6c, 0xcb, 0x51, 0x02, 0x3c, 0x4f, 0x8f, 0xea, 0xa3,
0x51, 0x13, 0x1e, 0xcf, 0x4e, 0x92, 0xbe, 0xe1, 0xf4, 0xf1, 0x90, 0xcb, 0x64, 0x97, 0xca, 0x3c,
0x0a, 0xc1, 0x2d, 0x86, 0x0d, 0x34, 0x8e, 0x61, 0x63, 0x51, 0xc3, 0xc4, 0x1e, 0x65, 0x37, 0xdf,
0xb7, 0x9d, 0x3d, 0x68, 0x5e, 0xac, 0x8d, 0x3d, 0x8a, 0x2e, 0x61, 0x73, 0x76, 0xbf, 0xe8, 0xf3,
0x75, 0x83, 0xfb, 0xd5, 0xed, 0xf1, 0x8c, 0x7f, 0x11, 0x2f, 0xe0, 0x4f, 0xe1, 0x51, 0x24, 0x1c,
0xe5, 0x3b, 0xbf, 0x74, 0x9b, 0x68, 0x06, 0x8d, 0x92, 0xfe, 0x1c, 0x22, 0x65, 0x3d, 0xde, 0xe7,
0x85, 0x07, 0xf4, 0x79, 0x14, 0xc3, 0x69, 0xd4, 0xf0, 0xfb, 0x20, 0x5e, 0x8d, 0x5d, 0xc7, 0xdf,
0x2e, 0xd6, 0x79, 0x97, 0xf9, 0xd7, 0x74, 0x56, 0x2d, 0xf9, 0x76, 0xff, 0xc8, 0xfd, 0x45, 0xd0,
0x5d, 0x0d, 0xd8, 0x65, 0xc8, 0x59, 0xba, 0x67, 0x43, 0xe2, 0x62, 0x9f, 0x1d, 0x5c, 0xd3, 0xea,
0xb6, 0x0f, 0x0a, 0x2f, 0xd4, 0x70, 0x1a, 0x02, 0x04, 0x7a, 0x17, 0x4a, 0xd1, 0x62, 0x7e, 0x5b,
0xb1, 0x4b, 0x3b, 0xab, 0x16, 0xc2, 0xa5, 0xfc, 0xdb, 0x09, 0x7d, 0x04, 0xeb, 0xb1, 0x2d, 0xf2,
0x96, 0x10, 0x97, 0xe6, 0x6a, 0x2d, 0x1a, 0xdd, 0xa0, 0x1d, 0x7e, 0x05, 0x15, 0xff, 0x66, 0xb0,
0x2d, 0x8f, 0x5a, 0x7d, 0xdd, 0x18, 0xd3, 0x6b, 0xe2, 0x5a, 0xbf, 0xc3, 0xa6, 0x6e, 0x04, 0xd5,
0xc7, 0x9e, 0xb4, 0xbe, 0x97, 0xfc, 0xd6, 0xce, 0xd8, 0x8d, 0x04, 0x1a, 0x33, 0x7e, 0x23, 0xa4,
0x23, 0x15, 0x62, 0x00, 0xdd, 0xc5, 0xbf, 0xc6, 0xfd, 0xf9, 0xaa, 0xa2, 0xa5, 0x91, 0xee, 0x44,
0x24, 0x95, 0x73, 0x66, 0xe5, 0x3d, 0xf8, 0xb3, 0x00, 0x85, 0xf8, 0x67, 0x10, 0xb4, 0x0b, 0x5b,
0x5d, 0xb5, 0xd3, 0xed, 0xf4, 0x1a, 0x27, 0xba, 0xf6, 0xa2, 0x2b, 0xeb, 0xe7, 0x67, 0xbd, 0xae,
0xdc, 0x52, 0x9e, 0x29, 0x72, 0x5b, 0x5c, 0x41, 0xdb, 0xf0, 0xd6, 0xbc, 0xbb, 0xa7, 0x35, 0xce,
0xda, 0x0d, 0xb5, 0x2d, 0x0a, 0xe8, 0x1d, 0xd8, 0x9d, 0xf7, 0x9d, 0x9e, 0x9f, 0x68, 0x4a, 0xf7,
0x44, 0xd6, 0x5b, 0x47, 0x1d, 0xa5, 0x25, 0x8b, 0x09, 0xf4, 0x36, 0x48, 0xf3, 0x90, 0x4e, 0x57,
0x53, 0x4e, 0x95, 0x9e, 0xa6, 0xb4, 0xc4, 0x24, 0xda, 0x81, 0xcd, 0x79, 0xaf, 0xfc, 0xbc, 0x2b,
0xb7, 0x15, 0x4d, 0x6e, 0x8b, 0xa9, 0x83, 0xff, 0x0b, 0x00, 0xb1, 0x2f, 0x5a, 0x3b, 0xb0, 0x79,
0xd1, 0xd1, 0x02, 0x81, 0xce, 0xd9, 0x42, 0x94, 0x8f, 0x60, 0x2d, 0xee, 0xec, 0x9c, 0xc9, 0xa2,
0xb0, 0x68, 0x7c, 0x21, 0xf7, 0x6e, 0x1b, 0xb5, 0xcb, 0x8e, 0x98, 0x40, 0x9b, 0xf0, 0x28, 0x6e,
0x6c, 0x34, 0x7b, 0x5a, 0x43, 0x39, 0x13, 0x13, 0xe8, 0x31, 0xac, 0xcf, 0xa1, 0x8f, 0x54, 0x59,
0x16, 0x93, 0x08, 0x41, 0x29, 0x6e, 0x3e, 0xeb, 0x88, 0x49, 0xb4, 0x01, 0x62, 0xdc, 0xf6, 0xac,
0x73, 0xae, 0x8a, 0x29, 0x7f, 0xff, 0xf3, 0x48, 0xfd, 0x52, 0xd1, 0x8e, 0xf4, 0x0b, 0x59, 0xeb,
0x88, 0xa9, 0x45, 0x4e, 0xaf, 0xdb, 0x38, 0x15, 0xd3, 0xdb, 0x09, 0x51, 0x38, 0xf8, 0xa7, 0x00,
0xa5, 0xf9, 0x6f, 0x3b, 0xa8, 0x02, 0x3b, 0xb3, 0x64, 0xf5, 0xb4, 0x86, 0x76, 0xde, 0x5b, 0x48,
0x42, 0x15, 0xca, 0x8b, 0x80, 0xb6, 0xdc, 0xed, 0xf4, 0x14, 0x4d, 0xef, 0xca, 0xaa, 0xd2, 0x59,
0x2c, 0x19, 0xc7, 0x5c, 0x74, 0x34, 0xe5, 0xec, 0xe7, 0x21, 0x24, 0x31, 0x57, 0x71, 0x0e, 0xe9,
0x36, 0x7a, 0x3d, 0xb9, 0x2d, 0x26, 0xe7, 0xca, 0xc9, 0x7d, 0xaa, 0x7c, 0x2c, 0xb7, 0x58, 0xc5,
0x96, 0x31, 0x9f, 0x35, 0x94, 0x13, 0xb9, 0x2d, 0xa6, 0x9b, 0x4f, 0x3f, 0x7f, 0x5d, 0x16, 0xbe,
0x78, 0x5d, 0x16, 0xfe, 0xfb, 0xba, 0x2c, 0x7c, 0xfa, 0xa6, 0xbc, 0xf2, 0xc5, 0x9b, 0xf2, 0xca,
0xbf, 0xde, 0x94, 0x57, 0x7e, 0xb9, 0x13, 0xb4, 0xae, 0x67, 0xfe, 0xa6, 0x66, 0x91, 0xfa, 0x0d,
0xfb, 0x1d, 0xc1, 0xff, 0x00, 0xed, 0xd5, 0x27, 0x87, 0x57, 0x19, 0x76, 0xfc, 0x7d, 0xf8, 0x75,
0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, 0x02, 0x8a, 0x3f, 0x65, 0x10, 0x00, 0x00,
}
func (m *WeightedVoteOption) Marshal() (dAtA []byte, err error) {
@ -1658,6 +1686,26 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.OptimisticRejectedThreshold) > 0 {
i -= len(m.OptimisticRejectedThreshold)
copy(dAtA[i:], m.OptimisticRejectedThreshold)
i = encodeVarintGov(dAtA, i, uint64(len(m.OptimisticRejectedThreshold)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x92
}
if len(m.OptimisticAuthorizedAddresses) > 0 {
for iNdEx := len(m.OptimisticAuthorizedAddresses) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.OptimisticAuthorizedAddresses[iNdEx])
copy(dAtA[i:], m.OptimisticAuthorizedAddresses[iNdEx])
i = encodeVarintGov(dAtA, i, uint64(len(m.OptimisticAuthorizedAddresses[iNdEx])))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x8a
}
}
if len(m.MinDepositRatio) > 0 {
i -= len(m.MinDepositRatio)
copy(dAtA[i:], m.MinDepositRatio)
@ -2108,6 +2156,16 @@ func (m *Params) Size() (n int) {
if l > 0 {
n += 2 + l + sovGov(uint64(l))
}
if len(m.OptimisticAuthorizedAddresses) > 0 {
for _, s := range m.OptimisticAuthorizedAddresses {
l = len(s)
n += 2 + l + sovGov(uint64(l))
}
}
l = len(m.OptimisticRejectedThreshold)
if l > 0 {
n += 2 + l + sovGov(uint64(l))
}
return n
}
@ -4138,6 +4196,70 @@ func (m *Params) Unmarshal(dAtA []byte) error {
}
m.MinDepositRatio = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 17:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field OptimisticAuthorizedAddresses", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGov
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGov
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGov
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.OptimisticAuthorizedAddresses = append(m.OptimisticAuthorizedAddresses, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
case 18:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field OptimisticRejectedThreshold", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGov
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGov
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGov
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.OptimisticRejectedThreshold = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGov(dAtA[iNdEx:])

View File

@ -4,6 +4,7 @@ import (
"fmt"
"time"
"cosmossdk.io/core/address"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -18,19 +19,21 @@ const (
// Default governance params
var (
DefaultMinDepositTokens = sdkmath.NewInt(10000000)
DefaultMinExpeditedDepositTokens = DefaultMinDepositTokens.Mul(sdkmath.NewInt(DefaultMinExpeditedDepositTokensRatio))
DefaultQuorum = sdkmath.LegacyNewDecWithPrec(334, 3)
DefaultThreshold = sdkmath.LegacyNewDecWithPrec(5, 1)
DefaultExpeditedThreshold = sdkmath.LegacyNewDecWithPrec(667, 3)
DefaultVetoThreshold = sdkmath.LegacyNewDecWithPrec(334, 3)
DefaultMinInitialDepositRatio = sdkmath.LegacyZeroDec()
DefaultProposalCancelRatio = sdkmath.LegacyMustNewDecFromStr("0.5")
DefaultProposalCancelDestAddress = ""
DefaultBurnProposalPrevote = false // set to false to replicate behavior of when this change was made (0.47)
DefaultBurnVoteQuorom = false // set to false to replicate behavior of when this change was made (0.47)
DefaultBurnVoteVeto = true // set to true to replicate behavior of when this change was made (0.47)
DefaultMinDepositRatio = sdkmath.LegacyMustNewDecFromStr("0.01")
DefaultMinDepositTokens = sdkmath.NewInt(10000000)
DefaultMinExpeditedDepositTokens = DefaultMinDepositTokens.Mul(sdkmath.NewInt(DefaultMinExpeditedDepositTokensRatio))
DefaultQuorum = sdkmath.LegacyNewDecWithPrec(334, 3)
DefaultThreshold = sdkmath.LegacyNewDecWithPrec(5, 1)
DefaultExpeditedThreshold = sdkmath.LegacyNewDecWithPrec(667, 3)
DefaultVetoThreshold = sdkmath.LegacyNewDecWithPrec(334, 3)
DefaultMinInitialDepositRatio = sdkmath.LegacyZeroDec()
DefaultProposalCancelRatio = sdkmath.LegacyMustNewDecFromStr("0.5")
DefaultProposalCancelDestAddress = ""
DefaultBurnProposalPrevote = false // set to false to replicate behavior of when this change was made (0.47)
DefaultBurnVoteQuorom = false // set to false to replicate behavior of when this change was made (0.47)
DefaultBurnVoteVeto = true // set to true to replicate behavior of when this change was made (0.47)
DefaultMinDepositRatio = sdkmath.LegacyMustNewDecFromStr("0.01")
DefaultOptimisticRejectedThreshold = sdkmath.LegacyMustNewDecFromStr("0.1")
DefaultOptimisticAuthorizedAddreses = []string(nil)
)
// Deprecated: NewDepositParams creates a new DepositParams object
@ -61,25 +64,27 @@ func NewVotingParams(votingPeriod *time.Duration) VotingParams {
func NewParams(
minDeposit, expeditedminDeposit sdk.Coins, maxDepositPeriod, votingPeriod, expeditedVotingPeriod time.Duration,
quorum, threshold, expeditedThreshold, vetoThreshold, minInitialDepositRatio, proposalCancelRatio, proposalCancelDest string,
burnProposalDeposit, burnVoteQuorum, burnVoteVeto bool, minDepositRatio string,
burnProposalDeposit, burnVoteQuorum, burnVoteVeto bool, minDepositRatio, optimisticRejectedThreshold string, optimisticAuthorizedAddresses []string,
) Params {
return Params{
MinDeposit: minDeposit,
ExpeditedMinDeposit: expeditedminDeposit,
MaxDepositPeriod: &maxDepositPeriod,
VotingPeriod: &votingPeriod,
ExpeditedVotingPeriod: &expeditedVotingPeriod,
Quorum: quorum,
Threshold: threshold,
ExpeditedThreshold: expeditedThreshold,
VetoThreshold: vetoThreshold,
MinInitialDepositRatio: minInitialDepositRatio,
ProposalCancelRatio: proposalCancelRatio,
ProposalCancelDest: proposalCancelDest,
BurnProposalDepositPrevote: burnProposalDeposit,
BurnVoteQuorum: burnVoteQuorum,
BurnVoteVeto: burnVoteVeto,
MinDepositRatio: minDepositRatio,
MinDeposit: minDeposit,
ExpeditedMinDeposit: expeditedminDeposit,
MaxDepositPeriod: &maxDepositPeriod,
VotingPeriod: &votingPeriod,
ExpeditedVotingPeriod: &expeditedVotingPeriod,
Quorum: quorum,
Threshold: threshold,
ExpeditedThreshold: expeditedThreshold,
VetoThreshold: vetoThreshold,
MinInitialDepositRatio: minInitialDepositRatio,
ProposalCancelRatio: proposalCancelRatio,
ProposalCancelDest: proposalCancelDest,
BurnProposalDepositPrevote: burnProposalDeposit,
BurnVoteQuorum: burnVoteQuorum,
BurnVoteVeto: burnVoteVeto,
MinDepositRatio: minDepositRatio,
OptimisticRejectedThreshold: optimisticRejectedThreshold,
OptimisticAuthorizedAddresses: optimisticAuthorizedAddresses,
}
}
@ -102,11 +107,13 @@ func DefaultParams() Params {
DefaultBurnVoteQuorom,
DefaultBurnVoteVeto,
DefaultMinDepositRatio.String(),
DefaultOptimisticRejectedThreshold.String(),
DefaultOptimisticAuthorizedAddreses,
)
}
// ValidateBasic performs basic validation on governance parameters.
func (p Params) ValidateBasic() error {
func (p Params) ValidateBasic(addressCodec address.Codec) error {
minDeposit := sdk.Coins(p.MinDeposit)
if minDeposit.Empty() || !minDeposit.IsValid() {
return fmt.Errorf("invalid minimum deposit: %s", minDeposit)
@ -173,6 +180,19 @@ func (p Params) ValidateBasic() error {
return fmt.Errorf("veto threshold too large: %s", vetoThreshold)
}
optimisticRejectedThreshold, err := sdkmath.LegacyNewDecFromStr(p.OptimisticRejectedThreshold)
if err != nil {
return fmt.Errorf("invalid optimistic rejected threshold string: %w", err)
}
if !optimisticRejectedThreshold.IsPositive() {
return fmt.Errorf("optimistic rejected threshold must be positive: %s", optimisticRejectedThreshold)
}
if optimisticRejectedThreshold.GT(sdkmath.LegacyOneDec()) {
return fmt.Errorf("optimistic rejected threshold too large: %s", optimisticRejectedThreshold)
}
if p.VotingPeriod == nil {
return fmt.Errorf("voting period must not be nil: %d", p.VotingPeriod)
}
@ -190,6 +210,12 @@ func (p Params) ValidateBasic() error {
return fmt.Errorf("expedited voting period %s must be strictly less that the regular voting period %s", p.ExpeditedVotingPeriod, p.VotingPeriod)
}
for _, addr := range p.OptimisticAuthorizedAddresses {
if _, err := addressCodec.StringToBytes(addr); err != nil {
return fmt.Errorf("invalid optimistic authorized address: %s", addr)
}
}
minInitialDepositRatio, err := sdkmath.LegacyNewDecFromStr(p.MinInitialDepositRatio)
if err != nil {
return fmt.Errorf("invalid minimum initial deposit ratio of proposal: %w", err)