diff --git a/proto/cosmos/group/v1beta1/genesis.proto b/proto/cosmos/group/v1beta1/genesis.proto new file mode 100644 index 000000000..0ad91e527 --- /dev/null +++ b/proto/cosmos/group/v1beta1/genesis.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +package cosmos.group.v1beta1; + +option go_package = "github.com/cosmos/cosmos-sdk/x/group"; + +import "cosmos/group/v1beta1/types.proto"; + +// GenesisState defines the group module's genesis state. +message GenesisState { + + // group_seq is the group table orm.Sequence, + // it is used to get the next group ID. + uint64 group_seq = 1; + + // groups is the list of groups info. + repeated GroupInfo groups = 2; + + // group_members is the list of groups members. + repeated GroupMember group_members = 3; + + // group_policy_account_seq is the group policy table orm.Sequence, + // it is used to generate the next group policy account address. + uint64 group_policy_account_seq = 4; + + // group_policies is the list of group policies info. + repeated GroupPolicyInfo group_policies = 5; + + // proposal_seq is the proposal table orm.Sequence, + // it is used to get the next proposal ID. + uint64 proposal_seq = 6; + + // proposals is the list of proposals. + repeated Proposal proposals = 7; + + // votes is the list of votes. + repeated Vote votes = 8; +} \ No newline at end of file diff --git a/simapp/app.go b/simapp/app.go index edc6a6333..6fa1e2f44 100644 --- a/simapp/app.go +++ b/simapp/app.go @@ -361,18 +361,16 @@ func NewSimApp( upgradetypes.ModuleName, capabilitytypes.ModuleName, minttypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, evidencetypes.ModuleName, stakingtypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, govtypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, - authz.ModuleName, feegrant.ModuleName, nft.ModuleName, + authz.ModuleName, feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, vestingtypes.ModuleName, - group.ModuleName, ) app.mm.SetOrderEndBlockers( crisistypes.ModuleName, govtypes.ModuleName, stakingtypes.ModuleName, capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, slashingtypes.ModuleName, minttypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, - feegrant.ModuleName, nft.ModuleName, + feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, - group.ModuleName, ) // NOTE: The genutils module must occur after staking so that pools are @@ -383,10 +381,9 @@ func NewSimApp( app.mm.SetOrderInitGenesis( capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName, - genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, group.ModuleName, - feegrant.ModuleName, nft.ModuleName, + genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName, + feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName, vestingtypes.ModuleName, - group.ModuleName, ) // Uncomment if you want to set a custom migration order here. diff --git a/simapp/app_test.go b/simapp/app_test.go index fa8881f93..7dd4f7391 100644 --- a/simapp/app_test.go +++ b/simapp/app_test.go @@ -28,6 +28,7 @@ import ( feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" "github.com/cosmos/cosmos-sdk/x/genutil" "github.com/cosmos/cosmos-sdk/x/gov" + group "github.com/cosmos/cosmos-sdk/x/group/module" "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/slashing" @@ -179,6 +180,7 @@ func TestRunMigrations(t *testing.T) { "distribution": distribution.AppModule{}.ConsensusVersion(), "slashing": slashing.AppModule{}.ConsensusVersion(), "gov": gov.AppModule{}.ConsensusVersion(), + "group": group.AppModule{}.ConsensusVersion(), "params": params.AppModule{}.ConsensusVersion(), "upgrade": upgrade.AppModule{}.ConsensusVersion(), "vesting": vesting.AppModule{}.ConsensusVersion(), diff --git a/x/group/client/cli/tx.go b/x/group/client/cli/tx.go index 5806cd249..af5e461d3 100644 --- a/x/group/client/cli/tx.go +++ b/x/group/client/cli/tx.go @@ -390,7 +390,7 @@ func MsgUpdateGroupPolicyAdminCmd() *cobra.Command { // MsgUpdateGroupPolicyDecisionPolicyCmd creates a CLI command for Msg/UpdateGroupPolicyDecisionPolicy. func MsgUpdateGroupPolicyDecisionPolicyCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "update-group-account-policy [admin] [group-policy-account] [decision-policy]", + Use: "update-group-policy-decision-policy [admin] [group-policy-account] [decision-policy]", Short: "Update a group policy's decision policy", Args: cobra.ExactArgs(3), RunE: func(cmd *cobra.Command, args []string) error { diff --git a/x/group/genesis.go b/x/group/genesis.go new file mode 100644 index 000000000..fead0cc97 --- /dev/null +++ b/x/group/genesis.go @@ -0,0 +1,96 @@ +package group + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/codec/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" +) + +// NewGenesisState creates a new genesis state with default values. +func NewGenesisState() *GenesisState { + return &GenesisState{} +} + +func (s GenesisState) Validate() error { + groups := make(map[uint64]GroupInfo) + groupPolicies := make(map[string]GroupPolicyInfo) + groupMembers := make(map[uint64]GroupMember) + proposals := make(map[uint64]Proposal) + + for _, g := range s.Groups { + if err := g.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "Group validation failed") + } + groups[g.GroupId] = *g + } + + for _, g := range s.GroupPolicies { + + // check that group with group policy's GroupId exists + if _, exists := groups[g.GroupId]; !exists { + return sdkerrors.Wrap(sdkerrors.ErrNotFound, fmt.Sprintf("group with GroupId %d doesn't exist", g.GroupId)) + } + + if err := g.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "GroupPolicy validation failed") + } + groupPolicies[g.Address] = *g + } + + for _, g := range s.GroupMembers { + + // check that group with group member's GroupId exists + if _, exists := groups[g.GroupId]; !exists { + return sdkerrors.Wrap(sdkerrors.ErrNotFound, fmt.Sprintf("group member with GroupId %d doesn't exist", g.GroupId)) + } + + if err := g.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "GroupMember validation failed") + } + groupMembers[g.GroupId] = *g + } + + for _, p := range s.Proposals { + + // check that group policy with proposal address exists + if _, exists := groupPolicies[p.Address]; !exists { + return sdkerrors.Wrap(sdkerrors.ErrNotFound, fmt.Sprintf("group policy account with address %s doesn't correspond to proposal address", p.Address)) + } + + if err := p.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "Proposal validation failed") + } + proposals[p.ProposalId] = *p + } + + for _, v := range s.Votes { + + if err := v.ValidateBasic(); err != nil { + return sdkerrors.Wrap(err, "Vote validation failed") + } + + // check that proposal exists + if _, exists := proposals[v.ProposalId]; !exists { + return sdkerrors.Wrap(sdkerrors.ErrNotFound, fmt.Sprintf("proposal with ProposalId %d doesn't exist", v.ProposalId)) + } + } + return nil +} + +// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces +func (s GenesisState) UnpackInterfaces(unpacker types.AnyUnpacker) error { + for _, g := range s.GroupPolicies { + err := g.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + for _, p := range s.Proposals { + err := p.UnpackInterfaces(unpacker) + if err != nil { + return err + } + } + return nil +} diff --git a/x/group/genesis.pb.go b/x/group/genesis.pb.go new file mode 100644 index 000000000..81c290b89 --- /dev/null +++ b/x/group/genesis.pb.go @@ -0,0 +1,705 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/group/v1beta1/genesis.proto + +package group + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the group module's genesis state. +type GenesisState struct { + // group_seq is the group table orm.Sequence, + // it is used to get the next group ID. + GroupSeq uint64 `protobuf:"varint,1,opt,name=group_seq,json=groupSeq,proto3" json:"group_seq,omitempty"` + // groups is the list of groups info. + Groups []*GroupInfo `protobuf:"bytes,2,rep,name=groups,proto3" json:"groups,omitempty"` + // group_members is the list of groups members. + GroupMembers []*GroupMember `protobuf:"bytes,3,rep,name=group_members,json=groupMembers,proto3" json:"group_members,omitempty"` + // group_policy_account_seq is the group policy table orm.Sequence, + // it is used to generate the next group policy account address. + GroupPolicyAccountSeq uint64 `protobuf:"varint,4,opt,name=group_policy_account_seq,json=groupPolicyAccountSeq,proto3" json:"group_policy_account_seq,omitempty"` + // group_policies is the list of group policies info. + GroupPolicies []*GroupPolicyInfo `protobuf:"bytes,5,rep,name=group_policies,json=groupPolicies,proto3" json:"group_policies,omitempty"` + // proposal_seq is the proposal table orm.Sequence, + // it is used to get the next proposal ID. + ProposalSeq uint64 `protobuf:"varint,6,opt,name=proposal_seq,json=proposalSeq,proto3" json:"proposal_seq,omitempty"` + // proposals is the list of proposals. + Proposals []*Proposal `protobuf:"bytes,7,rep,name=proposals,proto3" json:"proposals,omitempty"` + // votes is the list of votes. + Votes []*Vote `protobuf:"bytes,8,rep,name=votes,proto3" json:"votes,omitempty"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_7eedba45e0e08e2c, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetGroupSeq() uint64 { + if m != nil { + return m.GroupSeq + } + return 0 +} + +func (m *GenesisState) GetGroups() []*GroupInfo { + if m != nil { + return m.Groups + } + return nil +} + +func (m *GenesisState) GetGroupMembers() []*GroupMember { + if m != nil { + return m.GroupMembers + } + return nil +} + +func (m *GenesisState) GetGroupPolicyAccountSeq() uint64 { + if m != nil { + return m.GroupPolicyAccountSeq + } + return 0 +} + +func (m *GenesisState) GetGroupPolicies() []*GroupPolicyInfo { + if m != nil { + return m.GroupPolicies + } + return nil +} + +func (m *GenesisState) GetProposalSeq() uint64 { + if m != nil { + return m.ProposalSeq + } + return 0 +} + +func (m *GenesisState) GetProposals() []*Proposal { + if m != nil { + return m.Proposals + } + return nil +} + +func (m *GenesisState) GetVotes() []*Vote { + if m != nil { + return m.Votes + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "cosmos.group.v1beta1.GenesisState") +} + +func init() { + proto.RegisterFile("cosmos/group/v1beta1/genesis.proto", fileDescriptor_7eedba45e0e08e2c) +} + +var fileDescriptor_7eedba45e0e08e2c = []byte{ + // 355 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0xcf, 0x4e, 0xf2, 0x40, + 0x14, 0xc5, 0xe9, 0xc7, 0x9f, 0x0f, 0x06, 0x70, 0x31, 0xd1, 0x64, 0x82, 0xc9, 0x08, 0x44, 0x13, + 0x36, 0xb6, 0xa2, 0x0b, 0x36, 0xc6, 0x44, 0x17, 0x12, 0x13, 0x4d, 0x48, 0x49, 0x5c, 0xb8, 0x21, + 0x6d, 0x1d, 0x6b, 0x23, 0x65, 0x4a, 0xef, 0x40, 0xe4, 0x2d, 0x7c, 0x2a, 0xe3, 0x92, 0xa5, 0x4b, + 0x03, 0x2f, 0x62, 0xb8, 0x53, 0x52, 0x17, 0x0d, 0x2b, 0xb8, 0x73, 0xcf, 0xef, 0x9c, 0xd3, 0xe4, + 0x92, 0xb6, 0x27, 0x21, 0x94, 0x60, 0xf9, 0xb1, 0x9c, 0x45, 0xd6, 0xbc, 0xeb, 0x0a, 0xe5, 0x74, + 0x2d, 0x5f, 0x4c, 0x04, 0x04, 0x60, 0x46, 0xb1, 0x54, 0x92, 0xee, 0x6b, 0x8d, 0x89, 0x1a, 0x33, + 0xd1, 0x34, 0x9a, 0x99, 0xa4, 0x5a, 0x44, 0x22, 0xe1, 0xda, 0x9f, 0x79, 0x52, 0xeb, 0x6b, 0xa7, + 0xa1, 0x72, 0x94, 0xa0, 0x87, 0xa4, 0x82, 0xea, 0x11, 0x88, 0x29, 0x33, 0x9a, 0x46, 0xa7, 0x60, + 0x97, 0xf1, 0x61, 0x28, 0xa6, 0xb4, 0x47, 0x4a, 0xf8, 0x1f, 0xd8, 0xbf, 0x66, 0xbe, 0x53, 0x3d, + 0x3f, 0x32, 0xb3, 0x62, 0xcd, 0xfe, 0x66, 0xba, 0x9b, 0xbc, 0x48, 0x3b, 0x91, 0xd3, 0x5b, 0x52, + 0xd7, 0xae, 0xa1, 0x08, 0x5d, 0x11, 0x03, 0xcb, 0x23, 0xdf, 0xda, 0xc1, 0x3f, 0xa0, 0xd2, 0xae, + 0xf9, 0xe9, 0x00, 0xb4, 0x47, 0x98, 0xf6, 0x89, 0xe4, 0x38, 0xf0, 0x16, 0x23, 0xc7, 0xf3, 0xe4, + 0x6c, 0xa2, 0xb0, 0x6c, 0x01, 0xcb, 0x1e, 0xe0, 0x7e, 0x80, 0xeb, 0x6b, 0xbd, 0xdd, 0x34, 0xbf, + 0x27, 0x7b, 0x7f, 0xc0, 0x40, 0x00, 0x2b, 0x62, 0x83, 0x93, 0x1d, 0x0d, 0xb4, 0x09, 0x7e, 0x47, + 0x3d, 0x75, 0x0d, 0x04, 0xd0, 0x16, 0xa9, 0x45, 0xb1, 0x8c, 0x24, 0x38, 0x63, 0x8c, 0x2e, 0x61, + 0x74, 0x75, 0xfb, 0xb6, 0x09, 0xbc, 0x24, 0x95, 0xed, 0x08, 0xec, 0x3f, 0x66, 0xf1, 0xec, 0xac, + 0x41, 0x22, 0xb3, 0x53, 0x80, 0x9e, 0x91, 0xe2, 0x5c, 0x2a, 0x01, 0xac, 0x8c, 0x64, 0x23, 0x9b, + 0x7c, 0x94, 0x4a, 0xd8, 0x5a, 0x78, 0x73, 0xf5, 0xb5, 0xe2, 0xc6, 0x72, 0xc5, 0x8d, 0x9f, 0x15, + 0x37, 0x3e, 0xd6, 0x3c, 0xb7, 0x5c, 0xf3, 0xdc, 0xf7, 0x9a, 0xe7, 0x9e, 0x8e, 0xfd, 0x40, 0xbd, + 0xce, 0x5c, 0xd3, 0x93, 0xa1, 0x95, 0xdc, 0x83, 0xfe, 0x39, 0x85, 0xe7, 0x37, 0xeb, 0x5d, 0x1f, + 0x87, 0x5b, 0xc2, 0x7b, 0xb8, 0xf8, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf9, 0x7b, 0xee, 0x41, 0x6d, + 0x02, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Votes) > 0 { + for iNdEx := len(m.Votes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Votes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } + } + if len(m.Proposals) > 0 { + for iNdEx := len(m.Proposals) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Proposals[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a + } + } + if m.ProposalSeq != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.ProposalSeq)) + i-- + dAtA[i] = 0x30 + } + if len(m.GroupPolicies) > 0 { + for iNdEx := len(m.GroupPolicies) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GroupPolicies[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if m.GroupPolicyAccountSeq != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.GroupPolicyAccountSeq)) + i-- + dAtA[i] = 0x20 + } + if len(m.GroupMembers) > 0 { + for iNdEx := len(m.GroupMembers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.GroupMembers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Groups) > 0 { + for iNdEx := len(m.Groups) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Groups[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if m.GroupSeq != 0 { + i = encodeVarintGenesis(dAtA, i, uint64(m.GroupSeq)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.GroupSeq != 0 { + n += 1 + sovGenesis(uint64(m.GroupSeq)) + } + if len(m.Groups) > 0 { + for _, e := range m.Groups { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.GroupMembers) > 0 { + for _, e := range m.GroupMembers { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.GroupPolicyAccountSeq != 0 { + n += 1 + sovGenesis(uint64(m.GroupPolicyAccountSeq)) + } + if len(m.GroupPolicies) > 0 { + for _, e := range m.GroupPolicies { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if m.ProposalSeq != 0 { + n += 1 + sovGenesis(uint64(m.ProposalSeq)) + } + if len(m.Proposals) > 0 { + for _, e := range m.Proposals { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + if len(m.Votes) > 0 { + for _, e := range m.Votes { + l = e.Size() + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupSeq", wireType) + } + m.GroupSeq = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GroupSeq |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Groups", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Groups = append(m.Groups, &GroupInfo{}) + if err := m.Groups[len(m.Groups)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupMembers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GroupMembers = append(m.GroupMembers, &GroupMember{}) + if err := m.GroupMembers[len(m.GroupMembers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupPolicyAccountSeq", wireType) + } + m.GroupPolicyAccountSeq = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.GroupPolicyAccountSeq |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GroupPolicies", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.GroupPolicies = append(m.GroupPolicies, &GroupPolicyInfo{}) + if err := m.GroupPolicies[len(m.GroupPolicies)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ProposalSeq", wireType) + } + m.ProposalSeq = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ProposalSeq |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proposals", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proposals = append(m.Proposals, &Proposal{}) + if err := m.Proposals[len(m.Proposals)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Votes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Votes = append(m.Votes, &Vote{}) + if err := m.Votes[len(m.Votes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/group/genesis_test.go b/x/group/genesis_test.go new file mode 100644 index 000000000..536e4a6f1 --- /dev/null +++ b/x/group/genesis_test.go @@ -0,0 +1,750 @@ +package group + +import ( + "testing" + "time" + + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" +) + +var ( + memberPub = secp256k1.GenPrivKey().PubKey() + accPub = secp256k1.GenPrivKey().PubKey() + accAddr = sdk.AccAddress(accPub.Address()) + memberAddr = sdk.AccAddress(memberPub.Address()) +) + +func TestGenesisStateValidate(t *testing.T) { + + submittedAt := time.Now().UTC() + timeout := submittedAt.Add(time.Second * 1).UTC() + + groupPolicy := &GroupPolicyInfo{ + Address: accAddr.String(), + GroupId: 1, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + } + err := groupPolicy.SetDecisionPolicy(&ThresholdDecisionPolicy{ + Threshold: "1", + Timeout: time.Second, + }) + require.NoError(t, err) + + // create another group policy to set invalid decision policy for testing + groupPolicy2 := &GroupPolicyInfo{ + Address: accAddr.String(), + GroupId: 1, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + } + err = groupPolicy2.SetDecisionPolicy(&ThresholdDecisionPolicy{ + Threshold: "1", + Timeout: 0, + }) + require.NoError(t, err) + + proposal := &Proposal{ + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: ProposalStatusClosed, + Result: ProposalResultAccepted, + VoteState: Tally{ + YesCount: "1", + NoCount: "0", + AbstainCount: "0", + VetoCount: "0", + }, + Timeout: timeout, + ExecutorResult: ProposalExecutorResultSuccess, + } + err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ + FromAddress: accAddr.String(), + ToAddress: memberAddr.String(), + Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, + }}) + require.NoError(t, err) + + testCases := []struct { + name string + genesisState GenesisState + expErr bool + }{ + { + "valid genesisState", + GenesisState{ + GroupSeq: 2, + Groups: []*GroupInfo{{GroupId: 1, Admin: accAddr.String(), Metadata: []byte("1"), Version: 1, TotalWeight: "1"}, {GroupId: 2, Admin: accAddr.String(), Metadata: []byte("2"), Version: 2, TotalWeight: "2"}}, + GroupMembers: []*GroupMember{{GroupId: 1, Member: &Member{Address: memberAddr.String(), Weight: "1", Metadata: []byte("member metadata")}}, {GroupId: 2, Member: &Member{Address: memberAddr.String(), Weight: "2", Metadata: []byte("member metadata")}}}, + GroupPolicyAccountSeq: 1, + GroupPolicies: []*GroupPolicyInfo{groupPolicy}, + ProposalSeq: 1, + Proposals: []*Proposal{proposal}, + Votes: []*Vote{{ProposalId: proposal.ProposalId, Voter: memberAddr.String(), SubmittedAt: submittedAt, Choice: Choice_CHOICE_YES}}, + }, + false, + }, + { + "empty genesisState", + GenesisState{}, + false, + }, + { + "empty group id", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 0, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + }, + true, + }, + { + "invalid group admin", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: "invalid admin", + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + }, + true, + }, + { + "invalid group version", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 0, + TotalWeight: "1", + }, + }, + }, + true, + }, + { + "invalid group TotalWeight", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "-1", + }, + }, + }, + true, + }, + { + "invalid group policy address", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + { + Address: "invalid address", + GroupId: 1, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + }, + }, + }, + true, + }, + { + "invalid group policy admin", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + { + Address: accAddr.String(), + GroupId: 1, + Admin: "invalid admin", + Version: 1, + Metadata: []byte("policy metadata"), + }, + }, + }, + true, + }, + { + "invalid group policy's group id", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + { + Address: accAddr.String(), + GroupId: 0, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + }, + }, + }, + true, + }, + { + "invalid group policy version", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + { + Address: accAddr.String(), + GroupId: 1, + Admin: accAddr.String(), + Version: 0, + Metadata: []byte("policy metadata"), + }, + }, + }, + true, + }, + { + "invalid group policy's decision policy", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + { + Address: accAddr.String(), + GroupId: 1, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + DecisionPolicy: groupPolicy2.DecisionPolicy, + }, + }, + }, + true, + }, + { + "invalid group member's group id", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupMembers: []*GroupMember{ + { + GroupId: 0, + Member: &Member{ + Address: memberAddr.String(), + Weight: "1", Metadata: []byte("member metadata"), + }, + }, + }, + }, + true, + }, + { + "invalid group member address", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupMembers: []*GroupMember{ + { + GroupId: 1, + Member: &Member{ + Address: "invalid address", + Weight: "1", Metadata: []byte("member metadata"), + }, + }, + }, + }, + true, + }, + { + "invalid group member weight", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupMembers: []*GroupMember{ + { + GroupId: 1, + Member: &Member{ + Address: memberAddr.String(), + Weight: "-1", Metadata: []byte("member metadata"), + }, + }, + }, + }, + true, + }, + { + "invalid proposal id", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 0, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + }, + }, + }, + true, + }, + { + "invalid group policy address of proposal", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: "invalid address", + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + }, + }, + }, + true, + }, + { + "invalid group version of proposal", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 0, + GroupPolicyVersion: 1, + }, + }, + }, + true, + }, + { + "invalid group policy version", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 0, + }, + }, + }, + true, + }, + { + "invalid VoteState with negative YesCount", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: ProposalStatusClosed, + Result: ProposalResultAccepted, + VoteState: Tally{ + YesCount: "-1", + NoCount: "0", + AbstainCount: "0", + VetoCount: "0", + }, + }, + }, + }, + true, + }, + { + "invalid VoteState with negative NoCount", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: ProposalStatusClosed, + Result: ProposalResultAccepted, + VoteState: Tally{ + YesCount: "0", + NoCount: "-1", + AbstainCount: "0", + VetoCount: "0", + }, + }, + }, + }, + true, + }, + { + "invalid VoteState with negative AbstainCount", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: ProposalStatusClosed, + Result: ProposalResultAccepted, + VoteState: Tally{ + YesCount: "0", + NoCount: "0", + AbstainCount: "-1", + VetoCount: "0", + }, + }, + }, + }, + true, + }, + { + "invalid VoteState with negative VetoCount", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + { + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: ProposalStatusClosed, + Result: ProposalResultAccepted, + VoteState: Tally{ + YesCount: "0", + NoCount: "0", + AbstainCount: "0", + VetoCount: "-1", + }, + }, + }, + }, + true, + }, + { + "invalid voter", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + proposal, + }, + Votes: []*Vote{ + { + ProposalId: proposal.ProposalId, + Voter: "invalid voter", + SubmittedAt: submittedAt, + Choice: Choice_CHOICE_YES, + }, + }, + }, + true, + }, + { + "invalid proposal id", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + proposal, + }, + Votes: []*Vote{ + { + ProposalId: 0, + Voter: memberAddr.String(), + SubmittedAt: submittedAt, + Choice: Choice_CHOICE_YES, + }, + }, + }, + true, + }, + { + "vote on proposal that doesn't exist", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + proposal, + }, + Votes: []*Vote{ + { + ProposalId: 2, + Voter: memberAddr.String(), + SubmittedAt: submittedAt, + Choice: Choice_CHOICE_YES, + }, + }, + }, + true, + }, + { + "invalid choice", + GenesisState{ + Groups: []*GroupInfo{ + { + GroupId: 1, + Admin: accAddr.String(), + Metadata: []byte("1"), + Version: 1, + TotalWeight: "1", + }, + }, + GroupPolicies: []*GroupPolicyInfo{ + groupPolicy, + }, + Proposals: []*Proposal{ + proposal, + }, + Votes: []*Vote{ + { + ProposalId: proposal.ProposalId, + Voter: memberAddr.String(), + SubmittedAt: submittedAt, + Choice: Choice_CHOICE_UNSPECIFIED, + }, + }, + }, + true, + }, + } + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + + err := tc.genesisState.Validate() + if tc.expErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/x/group/keeper/genesis.go b/x/group/keeper/genesis.go new file mode 100644 index 000000000..3553c5c79 --- /dev/null +++ b/x/group/keeper/genesis.go @@ -0,0 +1,89 @@ +package keeper + +import ( + "encoding/json" + + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/group" +) + +func (k Keeper) InitGenesis(ctx types.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState group.GenesisState + cdc.MustUnmarshalJSON(data, &genesisState) + + if err := k.groupTable.Import(ctx.KVStore(k.key), genesisState.Groups, genesisState.GroupSeq); err != nil { + panic(errors.Wrap(err, "groups")) + } + + if err := k.groupMemberTable.Import(ctx.KVStore(k.key), genesisState.GroupMembers, 0); err != nil { + panic(errors.Wrap(err, "group members")) + } + + if err := k.groupPolicyTable.Import(ctx.KVStore(k.key), genesisState.GroupPolicies, 0); err != nil { + panic(errors.Wrap(err, "group policies")) + } + + if err := k.groupPolicySeq.InitVal(ctx.KVStore(k.key), genesisState.GroupPolicyAccountSeq); err != nil { + panic(errors.Wrap(err, "group policy account seq")) + } + + if err := k.proposalTable.Import(ctx.KVStore(k.key), genesisState.Proposals, genesisState.ProposalSeq); err != nil { + panic(errors.Wrap(err, "proposals")) + } + + if err := k.voteTable.Import(ctx.KVStore(k.key), genesisState.Votes, 0); err != nil { + panic(errors.Wrap(err, "votes")) + } + + return []abci.ValidatorUpdate{} + +} + +func (k Keeper) ExportGenesis(ctx types.Context, cdc codec.JSONCodec) *group.GenesisState { + genesisState := group.NewGenesisState() + + var groups []*group.GroupInfo + + groupSeq, err := k.groupTable.Export(ctx.KVStore(k.key), &groups) + if err != nil { + panic(errors.Wrap(err, "groups")) + } + genesisState.Groups = groups + genesisState.GroupSeq = groupSeq + + var groupMembers []*group.GroupMember + _, err = k.groupMemberTable.Export(ctx.KVStore(k.key), &groupMembers) + if err != nil { + panic(errors.Wrap(err, "group members")) + } + genesisState.GroupMembers = groupMembers + + var groupPolicies []*group.GroupPolicyInfo + _, err = k.groupPolicyTable.Export(ctx.KVStore(k.key), &groupPolicies) + if err != nil { + panic(errors.Wrap(err, "group policies")) + } + genesisState.GroupPolicies = groupPolicies + genesisState.GroupPolicyAccountSeq = k.groupPolicySeq.CurVal(ctx.KVStore(k.key)) + + var proposals []*group.Proposal + proposalSeq, err := k.proposalTable.Export(ctx.KVStore(k.key), &proposals) + if err != nil { + panic(errors.Wrap(err, "proposals")) + } + genesisState.Proposals = proposals + genesisState.ProposalSeq = proposalSeq + + var votes []*group.Vote + _, err = k.voteTable.Export(ctx.KVStore(k.key), &votes) + if err != nil { + panic(errors.Wrap(err, "votes")) + } + genesisState.Votes = votes + + return genesisState +} diff --git a/x/group/keeper/genesis_test.go b/x/group/keeper/genesis_test.go new file mode 100644 index 000000000..59644ea98 --- /dev/null +++ b/x/group/keeper/genesis_test.go @@ -0,0 +1,222 @@ +package keeper_test + +import ( + "context" + + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + dbm "github.com/tendermint/tm-db" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/simapp" + sdk "github.com/cosmos/cosmos-sdk/types" + + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/cosmos/cosmos-sdk/x/group" + "github.com/cosmos/cosmos-sdk/x/group/keeper" +) + +type GenesisTestSuite struct { + suite.Suite + + app *simapp.SimApp + ctx context.Context + sdkCtx sdk.Context + keeper keeper.Keeper + cdc *codec.ProtoCodec +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} + +var ( + memberPub = secp256k1.GenPrivKey().PubKey() + accPub = secp256k1.GenPrivKey().PubKey() + accAddr = sdk.AccAddress(accPub.Address()) + memberAddr = sdk.AccAddress(memberPub.Address()) +) + +func (s *GenesisTestSuite) SetupSuite() { + checkTx := false + db := dbm.NewMemDB() + encCdc := simapp.MakeTestEncodingConfig() + app := simapp.NewSimApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, simapp.DefaultNodeHome, 5, encCdc, simapp.EmptyAppOptions{}) + + s.app = app + s.sdkCtx = app.BaseApp.NewUncachedContext(checkTx, tmproto.Header{}) + s.keeper = app.GroupKeeper + s.cdc = codec.NewProtoCodec(app.InterfaceRegistry()) + s.ctx = sdk.WrapSDKContext(s.sdkCtx) +} + +func (s *GenesisTestSuite) TestInitExportGenesis() { + sdkCtx := s.sdkCtx + ctx := s.ctx + cdc := s.cdc + + submittedAt := time.Now().UTC() + timeout := submittedAt.Add(time.Second * 1).UTC() + + groupPolicy := &group.GroupPolicyInfo{ + Address: accAddr.String(), + GroupId: 1, + Admin: accAddr.String(), + Version: 1, + Metadata: []byte("policy metadata"), + } + err := groupPolicy.SetDecisionPolicy(&group.ThresholdDecisionPolicy{ + Threshold: "1", + Timeout: time.Second, + }) + s.Require().NoError(err) + + proposal := &group.Proposal{ + ProposalId: 1, + Address: accAddr.String(), + Metadata: []byte("proposal metadata"), + GroupVersion: 1, + GroupPolicyVersion: 1, + Proposers: []string{ + memberAddr.String(), + }, + SubmittedAt: submittedAt, + Status: group.ProposalStatusClosed, + Result: group.ProposalResultAccepted, + VoteState: group.Tally{ + YesCount: "1", + NoCount: "0", + AbstainCount: "0", + VetoCount: "0", + }, + Timeout: timeout, + ExecutorResult: group.ProposalExecutorResultSuccess, + } + err = proposal.SetMsgs([]sdk.Msg{&banktypes.MsgSend{ + FromAddress: accAddr.String(), + ToAddress: memberAddr.String(), + Amount: sdk.Coins{sdk.NewInt64Coin("test", 100)}, + }}) + s.Require().NoError(err) + + genesisState := &group.GenesisState{ + GroupSeq: 2, + Groups: []*group.GroupInfo{{GroupId: 1, Admin: accAddr.String(), Metadata: []byte("1"), Version: 1, TotalWeight: "1"}, {GroupId: 2, Admin: accAddr.String(), Metadata: []byte("2"), Version: 2, TotalWeight: "2"}}, + GroupMembers: []*group.GroupMember{{GroupId: 1, Member: &group.Member{Address: memberAddr.String(), Weight: "1", Metadata: []byte("member metadata")}}, {GroupId: 2, Member: &group.Member{Address: memberAddr.String(), Weight: "2", Metadata: []byte("member metadata")}}}, + GroupPolicyAccountSeq: 1, + GroupPolicies: []*group.GroupPolicyInfo{groupPolicy}, + ProposalSeq: 1, + Proposals: []*group.Proposal{proposal}, + Votes: []*group.Vote{{ProposalId: proposal.ProposalId, Voter: memberAddr.String(), SubmittedAt: submittedAt, Choice: group.Choice_CHOICE_YES}}, + } + genesisBytes, err := cdc.MarshalJSON(genesisState) + s.Require().NoError(err) + + genesisData := map[string]json.RawMessage{ + group.ModuleName: genesisBytes, + } + + s.keeper.InitGenesis(sdkCtx, cdc, genesisData[group.ModuleName]) + + for i, g := range genesisState.Groups { + res, err := s.keeper.GroupInfo(ctx, &group.QueryGroupInfoRequest{ + GroupId: g.GroupId, + }) + s.Require().NoError(err) + s.Require().Equal(g, res.Info) + + membersRes, err := s.keeper.GroupMembers(ctx, &group.QueryGroupMembersRequest{ + GroupId: g.GroupId, + }) + s.Require().NoError(err) + s.Require().Equal(len(membersRes.Members), 1) + s.Require().Equal(membersRes.Members[0], genesisState.GroupMembers[i]) + } + + for _, g := range genesisState.GroupPolicies { + res, err := s.keeper.GroupPolicyInfo(ctx, &group.QueryGroupPolicyInfoRequest{ + Address: g.Address, + }) + s.Require().NoError(err) + s.assertGroupPoliciesEqual(g, res.Info) + } + + for _, g := range genesisState.Proposals { + res, err := s.keeper.Proposal(ctx, &group.QueryProposalRequest{ + ProposalId: g.ProposalId, + }) + s.Require().NoError(err) + s.assertProposalsEqual(g, res.Proposal) + + votesRes, err := s.keeper.VotesByProposal(ctx, &group.QueryVotesByProposalRequest{ + ProposalId: g.ProposalId, + }) + s.Require().NoError(err) + s.Require().Equal(len(votesRes.Votes), 1) + s.Require().Equal(votesRes.Votes[0], genesisState.Votes[0]) + } + + exported := s.keeper.ExportGenesis(sdkCtx, cdc) + bz, err := cdc.MarshalJSON(exported) + s.Require().NoError(err) + + var exportedGenesisState group.GenesisState + err = cdc.UnmarshalJSON(bz, &exportedGenesisState) + s.Require().NoError(err) + + s.Require().Equal(genesisState.Groups, exportedGenesisState.Groups) + s.Require().Equal(genesisState.GroupMembers, exportedGenesisState.GroupMembers) + + s.Require().Equal(len(genesisState.GroupPolicies), len(exportedGenesisState.GroupPolicies)) + for i, g := range genesisState.GroupPolicies { + res := exportedGenesisState.GroupPolicies[i] + s.Require().NoError(err) + s.assertGroupPoliciesEqual(g, res) + } + + s.Require().Equal(len(genesisState.Proposals), len(exportedGenesisState.Proposals)) + for i, g := range genesisState.Proposals { + res := exportedGenesisState.Proposals[i] + s.Require().NoError(err) + s.assertProposalsEqual(g, res) + } + s.Require().Equal(genesisState.Votes, exportedGenesisState.Votes) + + s.Require().Equal(genesisState.GroupSeq, exportedGenesisState.GroupSeq) + s.Require().Equal(genesisState.GroupPolicyAccountSeq, exportedGenesisState.GroupPolicyAccountSeq) + s.Require().Equal(genesisState.ProposalSeq, exportedGenesisState.ProposalSeq) + +} + +func (s *GenesisTestSuite) assertGroupPoliciesEqual(g *group.GroupPolicyInfo, other *group.GroupPolicyInfo) { + require := s.Require() + require.Equal(g.Address, other.Address) + require.Equal(g.GroupId, other.GroupId) + require.Equal(g.Admin, other.Admin) + require.Equal(g.Metadata, other.Metadata) + require.Equal(g.Version, other.Version) + require.Equal(g.GetDecisionPolicy(), other.GetDecisionPolicy()) +} + +func (s *GenesisTestSuite) assertProposalsEqual(g *group.Proposal, other *group.Proposal) { + require := s.Require() + require.Equal(g.ProposalId, other.ProposalId) + require.Equal(g.Address, other.Address) + require.Equal(g.Metadata, other.Metadata) + require.Equal(g.Proposers, other.Proposers) + require.Equal(g.SubmittedAt, other.SubmittedAt) + require.Equal(g.GroupVersion, other.GroupVersion) + require.Equal(g.GroupPolicyVersion, other.GroupPolicyVersion) + require.Equal(g.Status, other.Status) + require.Equal(g.Result, other.Result) + require.Equal(g.VoteState, other.VoteState) + require.Equal(g.Timeout, other.Timeout) + require.Equal(g.ExecutorResult, other.ExecutorResult) + require.Equal(g.GetMsgs(), other.GetMsgs()) +} diff --git a/x/group/keeper/grpc_query.go b/x/group/keeper/grpc_query.go index 062dd1bff..5027c14f5 100644 --- a/x/group/keeper/grpc_query.go +++ b/x/group/keeper/grpc_query.go @@ -106,14 +106,14 @@ func (q Keeper) GroupPoliciesByGroup(goCtx context.Context, request *group.Query return nil, err } - var accounts []*group.GroupPolicyInfo - pageRes, err := orm.Paginate(it, request.Pagination, &accounts) + var policies []*group.GroupPolicyInfo + pageRes, err := orm.Paginate(it, request.Pagination, &policies) if err != nil { return nil, err } return &group.QueryGroupPoliciesByGroupResponse{ - GroupPolicies: accounts, + GroupPolicies: policies, Pagination: pageRes, }, nil } @@ -133,14 +133,14 @@ func (q Keeper) GroupPoliciesByAdmin(goCtx context.Context, request *group.Query return nil, err } - var accounts []*group.GroupPolicyInfo - pageRes, err := orm.Paginate(it, request.Pagination, &accounts) + var policies []*group.GroupPolicyInfo + pageRes, err := orm.Paginate(it, request.Pagination, &policies) if err != nil { return nil, err } return &group.QueryGroupPoliciesByAdminResponse{ - GroupPolicies: accounts, + GroupPolicies: policies, Pagination: pageRes, }, nil } diff --git a/x/group/module/module.go b/x/group/module/module.go index 58c9d893f..8601fb67f 100644 --- a/x/group/module/module.go +++ b/x/group/module/module.go @@ -3,6 +3,7 @@ package module import ( "context" "encoding/json" + "fmt" "math/rand" "github.com/gorilla/mux" @@ -58,14 +59,16 @@ func (AppModuleBasic) Name() string { // DefaultGenesis returns default genesis state as raw bytes for the group // module. func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { - // TODO: return default genesis state - return nil + return cdc.MustMarshalJSON(group.NewGenesisState()) } // ValidateGenesis performs genesis state validation for the group module. func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, config sdkclient.TxEncodingConfig, bz json.RawMessage) error { - // TODO: perform genesis validation - return nil + var data group.GenesisState + if err := cdc.UnmarshalJSON(bz, &data); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", group.ModuleName, err) + } + return data.Validate() } // GetQueryCmd returns the cli query commands for the group module @@ -127,15 +130,15 @@ func (am AppModule) LegacyQuerierHandler(legacyQuerierCdc *codec.LegacyAmino) sd // InitGenesis performs genesis initialization for the group module. It returns // no validator updates. func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { - // TODO: initialize genesis for group module + am.keeper.InitGenesis(ctx, cdc, data) return []abci.ValidatorUpdate{} } // ExportGenesis returns the exported genesis state as raw bytes for the group // module. func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { - // TODO: export genesis for group module - return nil + gs := am.keeper.ExportGenesis(ctx, cdc) + return cdc.MustMarshalJSON(gs) } // RegisterServices registers a gRPC query service to respond to the diff --git a/x/group/msgs.go b/x/group/msgs.go index 4e4a11352..ef1069dff 100644 --- a/x/group/msgs.go +++ b/x/group/msgs.go @@ -63,8 +63,7 @@ func (m MsgCreateGroup) ValidateBasic() error { } for i := range m.Members { member := m.Members[i] - if _, err := math.NewDecFromString(member.Weight); err != nil { - // if _, err := math.ParsePositiveDecimal(member.Weight); err != nil { + if _, err := math.NewPositiveDecFromString(member.Weight); err != nil { return sdkerrors.Wrap(err, "member weight") } } @@ -76,8 +75,7 @@ func (m Member) ValidateBasic() error { if err != nil { return sdkerrors.Wrap(err, "address") } - if _, err := math.NewDecFromString(m.Weight); err != nil { - // if _, err := math.ParseNonNegativeDecimal(m.Weight); err != nil { + if _, err := math.NewNonNegativeDecFromString(m.Weight); err != nil { return sdkerrors.Wrap(err, "weight") } diff --git a/x/group/spec/05_client.md b/x/group/spec/05_client.md index bdeb6b76b..0b8f02447 100644 --- a/x/group/spec/05_client.md +++ b/x/group/spec/05_client.md @@ -495,18 +495,18 @@ Example: simd tx group update-group-policy-metadata cosmos1.. cosmos1.. "AQ==" ``` -#### update-group-account-policy +#### update-group-policy-decision-policy -The `update-group-account-policy` command allows users to update a group policy's decision policy. +The `update-group-policy-decision-policy` command allows users to update a group policy's decision policy. ```bash -simd tx group update-group-account-policy [admin] [group-policy-account] [decision-policy] [flags] +simd tx group update-group-policy-decision-policy [admin] [group-policy-account] [decision-policy] [flags] ``` Example: ```bash -simd tx group update-group-account-policy cosmos1.. cosmos1.. '{"@type":"/cosmos.group.v1beta1.ThresholdDecisionPolicy", "threshold":"2", "timeout":"1000s"}' +simd tx group update-group-policy-decision-policy cosmos1.. cosmos1.. '{"@type":"/cosmos.group.v1beta1.ThresholdDecisionPolicy", "threshold":"2", "timeout":"1000s"}' ``` #### create-proposal diff --git a/x/group/spec/README.md b/x/group/spec/README.md index a27393775..d832f0b4b 100644 --- a/x/group/spec/README.md +++ b/x/group/spec/README.md @@ -17,7 +17,7 @@ This module allows the creation and management of on-chain multisig accounts and 1. **[Concepts](01_concepts.md)** - [Group](01_concepts.md#group) - - [Group Account](01_concepts.md#group-account) + - [Group Policy](01_concepts.md#group-policy) - [Decision Policy](01_concepts.md#decision-policy) - [Proposal](01_concepts.md#proposal) - [Voting](01_concepts.md#voting) @@ -25,7 +25,7 @@ This module allows the creation and management of on-chain multisig accounts and 2. **[State](02_state.md)** - [Group Table](02_state.md#group-table) - [Group Member Table](02_state.md#group-member-table) - - [Group Account Table](02_state.md#group-account-table) + - [Group Policy Table](02_state.md#group-policy-table) - [Proposal](02_state.md#proposal-table) - [Vote Table](02_state.md#vote-table) 3. **[Msg Service](03_messages.md)** @@ -33,18 +33,18 @@ This module allows the creation and management of on-chain multisig accounts and - [Msg/UpdateGroupMembers](03_messages.md#msgupdategroupmembers) - [Msg/UpdateGroupAdmin](03_messages.md#msgupdategroupadmin) - [Msg/UpdateGroupMetadata](03_messages.md#msgupdategroupmetadata) - - [Msg/CreateGroupAccount](03_messages.md#msgcreategroupaccount) - - [Msg/UpdateGroupAccountAdmin](03_messages.md#msgupdategroupaccountadmin) - - [Msg/UpdateGroupAccountDecisionPolicy](03_messages.md#msgupdategroupaccountdecisionpolicy) - - [Msg/UpdateGroupAccountMetadata](03_messages.md#msgupdategroupaccountmetadata) + - [Msg/CreateGroupPolicy](03_messages.md#msgcreategrouppolicy) + - [Msg/UpdateGroupPolicyAdmin](03_messages.md#msgupdategrouppolicyadmin) + - [Msg/UpdateGroupPolicyDecisionPolicy](03_messages.md#msgupdategrouppolicydecisionpolicy) + - [Msg/UpdateGroupPolicyMetadata](03_messages.md#msgupdategrouppolicymetadata) - [Msg/CreateProposal](03_messages.md#msgcreateproposal) - [Msg/Vote](03_messages.md#msgvote) - [Msg/Exec](03_messages.md#msgexec) 4. **[Events](04_events.md)** - [EventCreateGroup](04_events.md#eventcreategroup) - [EventUpdateGroup](04_events.md#eventupdategroup) - - [EventCreateGroupAccount](04_events.md#eventcreategroupaccount) - - [EventUpdateGroupAccount](04_events.md#eventupdategroupaccount) + - [EventCreateGroupPolicy](04_events.md#eventcreategrouppolicy) + - [EventUpdateGroupPolicy](04_events.md#eventupdategrouppolicy) - [EventCreateProposal](04_events.md#eventcreateproposal) - [EventVote](04_events.md#eventvote) - [EventExec](04_events.md#eventexec) diff --git a/x/group/types.go b/x/group/types.go index 7c28e3ec1..88fc9389b 100644 --- a/x/group/types.go +++ b/x/group/types.go @@ -167,6 +167,29 @@ func (g GroupPolicyInfo) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error return unpacker.UnpackAny(g.DecisionPolicy, &decisionPolicy) } +func (g GroupInfo) PrimaryKeyFields() []interface{} { + return []interface{}{g.GroupId} +} + +func (g GroupInfo) ValidateBasic() error { + if g.GroupId == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "group's GroupId") + } + + _, err := sdk.AccAddressFromBech32(g.Admin) + if err != nil { + return sdkerrors.Wrap(err, "admin") + } + + if _, err := math.NewNonNegativeDecFromString(g.TotalWeight); err != nil { + return sdkerrors.Wrap(err, "total weight") + } + if g.Version == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "version") + } + return nil +} + func (g GroupPolicyInfo) PrimaryKeyFields() []interface{} { addr, err := sdk.AccAddressFromBech32(g.Address) if err != nil { @@ -175,32 +198,34 @@ func (g GroupPolicyInfo) PrimaryKeyFields() []interface{} { return []interface{}{addr.Bytes()} } +func (g Proposal) PrimaryKeyFields() []interface{} { + return []interface{}{g.ProposalId} +} + func (g GroupPolicyInfo) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(g.Admin) if err != nil { - return sdkerrors.Wrap(err, "admin") + return sdkerrors.Wrap(err, "group policy admin") } - _, err = sdk.AccAddressFromBech32(g.Address) if err != nil { - return sdkerrors.Wrap(err, "group policy") + return sdkerrors.Wrap(err, "group policy account address") } if g.GroupId == 0 { - return sdkerrors.Wrap(errors.ErrEmpty, "group") + return sdkerrors.Wrap(errors.ErrEmpty, "group policy's group id") } if g.Version == 0 { - return sdkerrors.Wrap(errors.ErrEmpty, "version") + return sdkerrors.Wrap(errors.ErrEmpty, "group policy version") } policy := g.GetDecisionPolicy() if policy == nil { - return sdkerrors.Wrap(errors.ErrEmpty, "policy") + return sdkerrors.Wrap(errors.ErrEmpty, "group policy's decision policy") } if err := policy.ValidateBasic(); err != nil { - return sdkerrors.Wrap(err, "policy") + return sdkerrors.Wrap(err, "group policy's decision policy") } - return nil } @@ -214,12 +239,46 @@ func (g GroupMember) PrimaryKeyFields() []interface{} { func (g GroupMember) ValidateBasic() error { if g.GroupId == 0 { - return sdkerrors.Wrap(errors.ErrEmpty, "group") + return sdkerrors.Wrap(errors.ErrEmpty, "group member's group id") } err := g.Member.ValidateBasic() if err != nil { - return sdkerrors.Wrap(err, "member") + return sdkerrors.Wrap(err, "group member") + } + return nil +} + +func (p Proposal) ValidateBasic() error { + + if p.ProposalId == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "proposal id") + } + _, err := sdk.AccAddressFromBech32(p.Address) + if err != nil { + return sdkerrors.Wrap(err, "proposer group policy address") + } + if p.GroupVersion == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "proposal group version") + } + if p.GroupPolicyVersion == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "proposal group policy version") + } + _, err = p.VoteState.GetYesCount() + if err != nil { + return sdkerrors.Wrap(err, "proposal VoteState yes count") + } + _, err = p.VoteState.GetNoCount() + if err != nil { + return sdkerrors.Wrap(err, "proposal VoteState no count") + } + _, err = p.VoteState.GetAbstainCount() + if err != nil { + return sdkerrors.Wrap(err, "proposal VoteState abstain count") + } + _, err = p.VoteState.GetVetoCount() + if err != nil { + return sdkerrors.Wrap(err, "proposal VoteState veto count") } return nil } @@ -232,6 +291,26 @@ func (v Vote) PrimaryKeyFields() []interface{} { return []interface{}{v.ProposalId, addr.Bytes()} } +var _ orm.Validateable = Vote{} + +func (v Vote) ValidateBasic() error { + + _, err := sdk.AccAddressFromBech32(v.Voter) + if err != nil { + return sdkerrors.Wrap(err, "voter") + } + if v.ProposalId == 0 { + return sdkerrors.Wrap(errors.ErrEmpty, "voter ProposalId") + } + if v.Choice == Choice_CHOICE_UNSPECIFIED { + return sdkerrors.Wrap(errors.ErrEmpty, "voter choice") + } + if _, ok := Choice_name[int32(v.Choice)]; !ok { + return sdkerrors.Wrap(errors.ErrInvalid, "choice") + } + return nil +} + // UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces func (q QueryGroupPoliciesByGroupResponse) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error { return unpackGroupPolicies(unpacker, q.GroupPolicies)