feat: sign community events

closes: status-im/status-desktop#11834
This commit is contained in:
Patryk Osmaczko 2023-08-08 20:33:29 +02:00 committed by osmaczko
parent 4b07960fba
commit dbd52edcd7
7 changed files with 368 additions and 135 deletions

View File

@ -2204,3 +2204,24 @@ func (o *Community) DeclineRequestToJoin(dbRequest *RequestToJoin) error {
return nil
}
func (o *Community) ValidateEvent(event *CommunityEvent, signer *ecdsa.PublicKey) error {
o.mutex.Lock()
defer o.mutex.Unlock()
err := validateCommunityEvent(event)
if err != nil {
return err
}
member := o.getMember(signer)
if member == nil {
return ErrMemberNotFound
}
if !RolesAuthorizedToPerformEvent(member.Roles, event) {
return ErrNotAuthorized
}
return nil
}

View File

@ -233,13 +233,8 @@ func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEven
}
func (o *Community) updateCommunityDescriptionByEvents() error {
for i := range o.config.EventsData.Events {
communityEvent := &o.config.EventsData.Events[i]
err := validateCommunityEvent(communityEvent)
if err != nil {
return err
}
err = o.updateCommunityDescriptionByCommunityEvent(*communityEvent)
for _, event := range o.config.EventsData.Events {
err := o.updateCommunityDescriptionByCommunityEvent(event)
if err != nil {
return err
}
@ -368,11 +363,6 @@ func (o *Community) addNewCommunityEvent(event *CommunityEvent) error {
return err
}
data, err := proto.Marshal(event.ToProtobuf())
if err != nil {
return errors.New("converting CommunityEvent to protobuf failed")
}
// All events must be built on top of the control node CommunityDescription
// If there were no events before, extract CommunityDescription from CommunityDescriptionProtocolMessage
// and check the signature
@ -388,7 +378,11 @@ func (o *Community) addNewCommunityEvent(event *CommunityEvent) error {
}
}
event.RawPayload = data
event.Payload, err = proto.Marshal(event.ToProtobuf())
if err != nil {
return err
}
o.config.EventsData.Events = append(o.config.EventsData.Events, *event)
return nil

View File

@ -23,7 +23,8 @@ type CommunityEvent struct {
RejectedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"rejectedRequestsToJoin,omitempty"`
AcceptedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"acceptedRequestsToJoin,omitempty"`
TokenMetadata *protobuf.CommunityTokenMetadata `json:"tokenMetadata,omitempty"`
RawPayload []byte `json:"rawPayload"`
Payload []byte `json:"payload"`
Signature []byte `json:"signature"`
}
func (e *CommunityEvent) ToProtobuf() *protobuf.CommunityEvent {
@ -41,15 +42,13 @@ func (e *CommunityEvent) ToProtobuf() *protobuf.CommunityEvent {
TokenMetadata: e.TokenMetadata,
}
}
func CommunityEventFromProtobuf(raw []byte) (*CommunityEvent, error) {
func communityEventFromProtobuf(msg *protobuf.SignedCommunityEvent) (*CommunityEvent, error) {
decodedEvent := protobuf.CommunityEvent{}
encodedEvent := raw
err := proto.Unmarshal(encodedEvent, &decodedEvent)
err := proto.Unmarshal(msg.Payload, &decodedEvent)
if err != nil {
return nil, err
}
return &CommunityEvent{
CommunityEventClock: decodedEvent.CommunityEventClock,
Type: decodedEvent.Type,
@ -62,7 +61,8 @@ func CommunityEventFromProtobuf(raw []byte) (*CommunityEvent, error) {
RejectedRequestsToJoin: decodedEvent.RejectedRequestsToJoin,
AcceptedRequestsToJoin: decodedEvent.AcceptedRequestsToJoin,
TokenMetadata: decodedEvent.TokenMetadata,
RawPayload: encodedEvent,
Payload: msg.Payload,
Signature: msg.Signature,
}, nil
}
@ -73,26 +73,39 @@ type CommunityEventsMessage struct {
}
func (m *CommunityEventsMessage) ToProtobuf() protobuf.CommunityEventsMessage {
rawEvents := communityEventsToBytes(m.Events)
return protobuf.CommunityEventsMessage{
result := protobuf.CommunityEventsMessage{
CommunityId: m.CommunityID,
EventsBaseCommunityDescription: m.EventsBaseCommunityDescription,
Events: rawEvents,
SignedEvents: []*protobuf.SignedCommunityEvent{},
}
for _, event := range m.Events {
signedEvent := &protobuf.SignedCommunityEvent{
Signature: event.Signature,
Payload: event.Payload,
}
result.SignedEvents = append(result.SignedEvents, signedEvent)
}
return result
}
func CommunityEventsMessageFromProtobuf(raw *protobuf.CommunityEventsMessage) (*CommunityEventsMessage, error) {
events, err := communityEventsFromBytes(raw.Events)
if err != nil {
return nil, err
func CommunityEventsMessageFromProtobuf(msg *protobuf.CommunityEventsMessage) (*CommunityEventsMessage, error) {
result := &CommunityEventsMessage{
CommunityID: msg.CommunityId,
EventsBaseCommunityDescription: msg.EventsBaseCommunityDescription,
Events: []CommunityEvent{},
}
return &CommunityEventsMessage{
CommunityID: raw.CommunityId,
EventsBaseCommunityDescription: raw.EventsBaseCommunityDescription,
Events: events,
}, nil
for _, signedEvent := range msg.SignedEvents {
event, err := communityEventFromProtobuf(signedEvent)
if err != nil {
return nil, err
}
result.Events = append(result.Events, *event)
}
return result, nil
}
func (m *CommunityEventsMessage) Marshal() ([]byte, error) {
@ -225,30 +238,7 @@ func validateCommunityEvent(communityEvent *CommunityEvent) error {
}
func isCommunityEventsEqual(left CommunityEvent, right CommunityEvent) bool {
return bytes.Equal(left.RawPayload, right.RawPayload)
}
func communityEventsToBytes(communityEvents []CommunityEvent) [][]byte {
var rawEvents [][]byte
for _, e := range communityEvents {
var encodedEvent []byte
encodedEvent = append(encodedEvent, e.RawPayload...)
rawEvents = append(rawEvents, encodedEvent)
}
return rawEvents
}
func communityEventsFromBytes(rawEvents [][]byte) ([]CommunityEvent, error) {
var events []CommunityEvent
for _, e := range rawEvents {
verifiedEvent, err := CommunityEventFromProtobuf(e)
if err != nil {
return nil, err
}
events = append(events, *verifiedEvent)
}
return events, nil
return bytes.Equal(left.Payload, right.Payload)
}
func communityEventsToJSONEncodedBytes(communityEvents []CommunityEvent) ([]byte, error) {

View File

@ -1314,17 +1314,66 @@ func (m *Manager) handleCommunityDescriptionMessageCommon(community *Community,
}, nil
}
func (m *Manager) signEvents(community *Community) error {
for i := range community.config.EventsData.Events {
communityEvent := &community.config.EventsData.Events[i]
if communityEvent.Signature == nil || len(communityEvent.Signature) == 0 {
var err error
communityEvent.Signature, err = crypto.Sign(crypto.Keccak256(communityEvent.Payload), m.identity)
if err != nil {
return err
}
}
}
return nil
}
func (m *Manager) validateAndFilterEvents(community *Community, events []CommunityEvent) []CommunityEvent {
validatedEvents := make([]CommunityEvent, 0, len(events))
validateEvent := func(event *CommunityEvent) error {
if event.Signature == nil || len(event.Signature) == 0 {
return errors.New("missing signature")
}
signer, err := crypto.SigToPub(
crypto.Keccak256(event.Payload),
event.Signature,
)
if err != nil {
return errors.New("failed to recover signer")
}
err = community.ValidateEvent(event, signer)
if err != nil {
return err
}
return nil
}
for i := range events {
if err := validateEvent(&events[i]); err == nil {
validatedEvents = append(validatedEvents, events[i])
} else {
m.logger.Warn("invalid community event", zap.Error(err))
}
}
return validatedEvents
}
func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message *protobuf.CommunityEventsMessage) (*CommunityResponse, error) {
if signer == nil {
return nil, errors.New("signer can't be nil")
}
adminMessage, err := CommunityEventsMessageFromProtobuf(message)
eventsMessage, err := CommunityEventsMessageFromProtobuf(message)
if err != nil {
return nil, err
}
community, err := m.persistence.GetByID(&m.identity.PublicKey, adminMessage.CommunityID)
community, err := m.persistence.GetByID(&m.identity.PublicKey, eventsMessage.CommunityID)
if err != nil {
return nil, err
}
@ -1337,7 +1386,9 @@ func (m *Manager) HandleCommunityEventsMessage(signer *ecdsa.PublicKey, message
return nil, errors.New("user has not permissions to send events")
}
changes, err := community.UpdateCommunityByEvents(adminMessage)
eventsMessage.Events = m.validateAndFilterEvents(community, eventsMessage.Events)
changes, err := community.UpdateCommunityByEvents(eventsMessage)
if err != nil {
return nil, err
}
@ -4235,7 +4286,11 @@ func (m *Manager) saveAndPublish(community *Community) error {
m.publish(&Subscription{Community: community})
return nil
} else if community.HasPermissionToSendCommunityEvents() {
err := m.persistence.SaveCommunityEvents(community)
err := m.signEvents(community)
if err != nil {
return err
}
err = m.persistence.SaveCommunityEvents(community)
if err != nil {
return err
}

View File

@ -0,0 +1,97 @@
package communities
import "github.com/status-im/status-go/protocol/protobuf"
var adminAuthorizedEventTypes = []protobuf.CommunityEvent_EventType{
protobuf.CommunityEvent_COMMUNITY_EDIT,
protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE,
protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE,
protobuf.CommunityEvent_COMMUNITY_CATEGORY_CREATE,
protobuf.CommunityEvent_COMMUNITY_CATEGORY_DELETE,
protobuf.CommunityEvent_COMMUNITY_CATEGORY_EDIT,
protobuf.CommunityEvent_COMMUNITY_CHANNEL_CREATE,
protobuf.CommunityEvent_COMMUNITY_CHANNEL_DELETE,
protobuf.CommunityEvent_COMMUNITY_CHANNEL_EDIT,
protobuf.CommunityEvent_COMMUNITY_CATEGORY_REORDER,
protobuf.CommunityEvent_COMMUNITY_CHANNEL_REORDER,
protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_ACCEPT,
protobuf.CommunityEvent_COMMUNITY_REQUEST_TO_JOIN_REJECT,
protobuf.CommunityEvent_COMMUNITY_MEMBER_KICK,
protobuf.CommunityEvent_COMMUNITY_MEMBER_BAN,
protobuf.CommunityEvent_COMMUNITY_MEMBER_UNBAN,
}
var tokenMasterAuthorizedEventTypes = append(adminAuthorizedEventTypes, []protobuf.CommunityEvent_EventType{
protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD,
}...)
var ownerAuthorizedEventTypes = tokenMasterAuthorizedEventTypes
var rolesToAuthorizedEventTypes = map[protobuf.CommunityMember_Roles][]protobuf.CommunityEvent_EventType{
protobuf.CommunityMember_ROLE_NONE: []protobuf.CommunityEvent_EventType{},
protobuf.CommunityMember_ROLE_OWNER: ownerAuthorizedEventTypes,
protobuf.CommunityMember_ROLE_MANAGE_USERS: []protobuf.CommunityEvent_EventType{},
protobuf.CommunityMember_ROLE_MODERATE_CONTENT: []protobuf.CommunityEvent_EventType{},
protobuf.CommunityMember_ROLE_ADMIN: adminAuthorizedEventTypes,
protobuf.CommunityMember_ROLE_TOKEN_MASTER: tokenMasterAuthorizedEventTypes,
}
var adminAuthorizedPermissionTypes = []protobuf.CommunityTokenPermission_Type{
protobuf.CommunityTokenPermission_BECOME_MEMBER,
protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL,
}
var tokenMasterAuthorizedPermissionTypes = append(adminAuthorizedPermissionTypes, []protobuf.CommunityTokenPermission_Type{
protobuf.CommunityTokenPermission_BECOME_ADMIN,
}...)
var ownerAuthorizedPermissionTypes = append(tokenMasterAuthorizedPermissionTypes, []protobuf.CommunityTokenPermission_Type{
protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
}...)
var rolesToAuthorizedPermissionTypes = map[protobuf.CommunityMember_Roles][]protobuf.CommunityTokenPermission_Type{
protobuf.CommunityMember_ROLE_NONE: []protobuf.CommunityTokenPermission_Type{},
protobuf.CommunityMember_ROLE_OWNER: ownerAuthorizedPermissionTypes,
protobuf.CommunityMember_ROLE_MANAGE_USERS: []protobuf.CommunityTokenPermission_Type{},
protobuf.CommunityMember_ROLE_MODERATE_CONTENT: []protobuf.CommunityTokenPermission_Type{},
protobuf.CommunityMember_ROLE_ADMIN: adminAuthorizedPermissionTypes,
protobuf.CommunityMember_ROLE_TOKEN_MASTER: tokenMasterAuthorizedPermissionTypes,
}
func canRolesPerformEvent(roles []protobuf.CommunityMember_Roles, eventType protobuf.CommunityEvent_EventType) bool {
for _, role := range roles {
authorizedEventTypes := rolesToAuthorizedEventTypes[role]
for _, authorizedEventType := range authorizedEventTypes {
if authorizedEventType == eventType {
return true
}
}
}
return false
}
func canRolesModifyPermission(roles []protobuf.CommunityMember_Roles, permissionType protobuf.CommunityTokenPermission_Type) bool {
for _, role := range roles {
authorizedPermissionTypes := rolesToAuthorizedPermissionTypes[role]
for _, authorizedPermissionType := range authorizedPermissionTypes {
if authorizedPermissionType == permissionType {
return true
}
}
}
return false
}
func RolesAuthorizedToPerformEvent(roles []protobuf.CommunityMember_Roles, event *CommunityEvent) bool {
if !canRolesPerformEvent(roles, event.Type) {
return false
}
if event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_CHANGE ||
event.Type == protobuf.CommunityEvent_COMMUNITY_MEMBER_TOKEN_PERMISSION_DELETE {
return canRolesModifyPermission(roles, event.TokenPermission.Type)
}
return true
}

View File

@ -417,6 +417,55 @@ func (m *ChannelData) GetChannel() *CommunityChat {
return nil
}
type SignedCommunityEvent struct {
// Signature of the payload field
Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"`
// Marshaled CommunityEvent
Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SignedCommunityEvent) Reset() { *m = SignedCommunityEvent{} }
func (m *SignedCommunityEvent) String() string { return proto.CompactTextString(m) }
func (*SignedCommunityEvent) ProtoMessage() {}
func (*SignedCommunityEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_52ed23dfc73918ab, []int{4}
}
func (m *SignedCommunityEvent) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignedCommunityEvent.Unmarshal(m, b)
}
func (m *SignedCommunityEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SignedCommunityEvent.Marshal(b, m, deterministic)
}
func (m *SignedCommunityEvent) XXX_Merge(src proto.Message) {
xxx_messageInfo_SignedCommunityEvent.Merge(m, src)
}
func (m *SignedCommunityEvent) XXX_Size() int {
return xxx_messageInfo_SignedCommunityEvent.Size(m)
}
func (m *SignedCommunityEvent) XXX_DiscardUnknown() {
xxx_messageInfo_SignedCommunityEvent.DiscardUnknown(m)
}
var xxx_messageInfo_SignedCommunityEvent proto.InternalMessageInfo
func (m *SignedCommunityEvent) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func (m *SignedCommunityEvent) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
// CommunityEventsMessage is a message used to propagate information
// about community changes.
type CommunityEventsMessage struct {
@ -424,17 +473,20 @@ type CommunityEventsMessage struct {
// Events base CommunityDescription with owner signature on top of which events were generated
EventsBaseCommunityDescription []byte `protobuf:"bytes,2,opt,name=events_base_community_description,json=eventsBaseCommunityDescription,proto3" json:"events_base_community_description,omitempty"`
// A list of admins events for the channel in bytes
Events [][]byte `protobuf:"bytes,3,rep,name=events,proto3" json:"events,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
// Deprecated: use signed_events instead.
Events [][]byte `protobuf:"bytes,3,rep,name=events,proto3" json:"events,omitempty"` // Deprecated: Do not use.
// A list of signed community events
SignedEvents []*SignedCommunityEvent `protobuf:"bytes,4,rep,name=signed_events,json=signedEvents,proto3" json:"signed_events,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommunityEventsMessage) Reset() { *m = CommunityEventsMessage{} }
func (m *CommunityEventsMessage) String() string { return proto.CompactTextString(m) }
func (*CommunityEventsMessage) ProtoMessage() {}
func (*CommunityEventsMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_52ed23dfc73918ab, []int{4}
return fileDescriptor_52ed23dfc73918ab, []int{5}
}
func (m *CommunityEventsMessage) XXX_Unmarshal(b []byte) error {
@ -469,6 +521,7 @@ func (m *CommunityEventsMessage) GetEventsBaseCommunityDescription() []byte {
return nil
}
// Deprecated: Do not use.
func (m *CommunityEventsMessage) GetEvents() [][]byte {
if m != nil {
return m.Events
@ -476,6 +529,13 @@ func (m *CommunityEventsMessage) GetEvents() [][]byte {
return nil
}
func (m *CommunityEventsMessage) GetSignedEvents() []*SignedCommunityEvent {
if m != nil {
return m.SignedEvents
}
return nil
}
func init() {
proto.RegisterEnum("protobuf.CommunityEvent_EventType", CommunityEvent_EventType_name, CommunityEvent_EventType_value)
proto.RegisterType((*CommunityEvent)(nil), "protobuf.CommunityEvent")
@ -485,6 +545,7 @@ func init() {
proto.RegisterType((*CommunityConfig)(nil), "protobuf.CommunityConfig")
proto.RegisterType((*CategoryData)(nil), "protobuf.CategoryData")
proto.RegisterType((*ChannelData)(nil), "protobuf.ChannelData")
proto.RegisterType((*SignedCommunityEvent)(nil), "protobuf.SignedCommunityEvent")
proto.RegisterType((*CommunityEventsMessage)(nil), "protobuf.CommunityEventsMessage")
}
@ -493,69 +554,73 @@ func init() {
}
var fileDescriptor_52ed23dfc73918ab = []byte{
// 1010 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xdb, 0x36,
0x14, 0x9e, 0x63, 0xe7, 0xc7, 0x47, 0xb6, 0xa3, 0x32, 0x8b, 0xe3, 0xb8, 0x6b, 0xe6, 0x7a, 0xbb,
0x30, 0x86, 0x21, 0xc5, 0xb2, 0xa1, 0x28, 0xd6, 0x9b, 0x29, 0x32, 0x91, 0x2a, 0xa9, 0xe5, 0x8c,
0x51, 0x30, 0xb4, 0x37, 0x04, 0x23, 0xb1, 0x89, 0x96, 0x58, 0xf2, 0x4c, 0xba, 0x80, 0x6f, 0xf7,
0x04, 0x7b, 0x80, 0x61, 0x8f, 0x30, 0xec, 0x11, 0x07, 0x51, 0x92, 0x25, 0x25, 0x72, 0xba, 0x8b,
0xdd, 0xd8, 0xe2, 0xf9, 0xbe, 0xf3, 0x7d, 0x87, 0x14, 0xc9, 0x23, 0x68, 0xbb, 0xe1, 0x64, 0x32,
0x0f, 0x7c, 0xb9, 0xa0, 0xf3, 0xa9, 0xc7, 0x24, 0x3f, 0x9c, 0xce, 0x42, 0x19, 0xa2, 0x2d, 0xf5,
0x77, 0x35, 0xff, 0xd0, 0xdd, 0x71, 0x6f, 0x98, 0xa4, 0xbe, 0xc7, 0x03, 0xe9, 0xcb, 0x45, 0x0c,
0x77, 0x9f, 0xa4, 0x69, 0x3e, 0x17, 0x71, 0xa8, 0xff, 0x47, 0x13, 0x5a, 0x66, 0x2a, 0x86, 0x3f,
0xf2, 0x40, 0xa2, 0x23, 0xd8, 0xcd, 0xe4, 0x79, 0x14, 0xa2, 0xee, 0x5d, 0xe8, 0xde, 0x76, 0x2a,
0xbd, 0xca, 0xa0, 0x46, 0x76, 0xdc, 0x02, 0xdd, 0x8c, 0x20, 0xf4, 0x12, 0x6a, 0x72, 0x31, 0xe5,
0x9d, 0xb5, 0x5e, 0x65, 0xd0, 0x3a, 0xea, 0x1f, 0xa6, 0x75, 0x1c, 0x16, 0xb5, 0x0f, 0xd5, 0xaf,
0xb3, 0x98, 0x72, 0xa2, 0xf8, 0x68, 0x08, 0x7a, 0xe6, 0xe5, 0x86, 0xc1, 0x07, 0xff, 0xba, 0x53,
0xed, 0x55, 0x06, 0xda, 0xd1, 0x7e, 0x89, 0x86, 0xa9, 0x08, 0x64, 0xdb, 0x2d, 0x06, 0xd0, 0x08,
0x74, 0x19, 0xde, 0xf2, 0x80, 0x4e, 0xf9, 0x6c, 0xe2, 0x0b, 0xe1, 0x87, 0x41, 0xa7, 0xa6, 0x54,
0xca, 0x2a, 0x71, 0x22, 0xea, 0xf9, 0x92, 0x49, 0xb6, 0x65, 0x31, 0x80, 0x5e, 0x43, 0xd3, 0x65,
0x92, 0x5f, 0x87, 0xb3, 0x05, 0xf5, 0x98, 0x64, 0x9d, 0x75, 0xa5, 0xd5, 0xce, 0x69, 0x25, 0xf0,
0x90, 0x49, 0x46, 0x1a, 0x6e, 0x6e, 0x84, 0x5e, 0x41, 0xc3, 0xbd, 0x61, 0x41, 0xc0, 0xef, 0xe2,
0xdc, 0x0d, 0x95, 0xbb, 0x9b, 0xcb, 0x8d, 0x51, 0x95, 0xaa, 0xb9, 0xd9, 0x00, 0x0d, 0x40, 0x9f,
0xf0, 0xc9, 0x15, 0x9f, 0x51, 0x19, 0x52, 0xe6, 0xca, 0x68, 0x16, 0x9b, 0xbd, 0xca, 0xa0, 0x4e,
0x5a, 0x71, 0xdc, 0x09, 0x0d, 0x15, 0x45, 0x36, 0x34, 0xe2, 0x88, 0x30, 0x3c, 0x8f, 0x7b, 0x9d,
0xad, 0x5e, 0x75, 0xa0, 0x1d, 0x7d, 0xb3, 0x72, 0xd5, 0x47, 0x39, 0x32, 0x0e, 0xe4, 0x6c, 0x41,
0x0a, 0xf9, 0xe8, 0x0e, 0xda, 0x33, 0xfe, 0x2b, 0x77, 0x25, 0xf7, 0x08, 0xff, 0x6d, 0xce, 0x85,
0x14, 0x4e, 0x78, 0x1a, 0xfa, 0x41, 0xa7, 0xae, 0x94, 0x7f, 0x58, 0xa9, 0x4c, 0x4a, 0xd3, 0x62,
0x8f, 0x15, 0x9a, 0x91, 0x1b, 0x73, 0x5d, 0x3e, 0x7d, 0xe8, 0x06, 0x9f, 0x70, 0x33, 0x4a, 0xd3,
0x12, 0xb7, 0x72, 0x4d, 0x74, 0x02, 0xad, 0x78, 0x6f, 0x4c, 0xb8, 0x64, 0xea, 0x8d, 0x68, 0xea,
0x8d, 0xf4, 0x56, 0xed, 0x8c, 0x51, 0xc2, 0x23, 0x4d, 0x99, 0x1f, 0x76, 0xdf, 0xc3, 0x93, 0x07,
0xeb, 0x88, 0x74, 0xa8, 0xde, 0xf2, 0x85, 0x3a, 0x19, 0x75, 0x12, 0x3d, 0xa2, 0x17, 0xb0, 0xfe,
0x91, 0xdd, 0xcd, 0xe3, 0xa3, 0x50, 0xbe, 0x8d, 0x63, 0x19, 0x12, 0xf3, 0x7e, 0x5c, 0x7b, 0x55,
0xe9, 0xde, 0xc2, 0xd3, 0x47, 0x56, 0xb2, 0xc4, 0xe5, 0x65, 0xd1, 0xa5, 0x6c, 0x32, 0x89, 0x50,
0xac, 0x73, 0xcf, 0xec, 0x91, 0x85, 0xfc, 0x7f, 0xcd, 0xfa, 0xff, 0xd4, 0xa0, 0xbe, 0x3c, 0xf4,
0x48, 0x83, 0xcd, 0x4b, 0xfb, 0xcc, 0x1e, 0xff, 0x62, 0xeb, 0x9f, 0x21, 0x04, 0x2d, 0x73, 0x3c,
0x1a, 0x5d, 0xda, 0x96, 0xf3, 0x8e, 0xe2, 0xa1, 0xe5, 0xe8, 0x15, 0xf4, 0x2d, 0x0c, 0xb2, 0xd8,
0x08, 0x8f, 0x8e, 0x31, 0xa1, 0xce, 0xf8, 0x0c, 0xdb, 0xf4, 0x1c, 0x93, 0x91, 0x75, 0x71, 0x61,
0x8d, 0x6d, 0x6a, 0xbe, 0x31, 0xec, 0x13, 0xac, 0xaf, 0xfd, 0x37, 0xf6, 0x10, 0xbf, 0xc5, 0x0e,
0xd6, 0xab, 0xe8, 0x19, 0xec, 0x67, 0x6c, 0xd3, 0x70, 0xf0, 0xc9, 0x98, 0xbc, 0xa3, 0x26, 0xc1,
0x86, 0x83, 0xf5, 0xda, 0x0a, 0x38, 0xc9, 0x5e, 0x47, 0x4f, 0x61, 0xaf, 0x04, 0x56, 0x65, 0x6f,
0xa0, 0x2f, 0xa0, 0x93, 0x03, 0xdf, 0x18, 0xb6, 0x8d, 0xdf, 0xa6, 0xca, 0x9b, 0xe5, 0x68, 0x22,
0xbc, 0x85, 0xba, 0xd0, 0x7e, 0x88, 0x2a, 0xdd, 0x3a, 0x3a, 0x80, 0x6e, 0x89, 0x29, 0xc1, 0x63,
0x32, 0xc4, 0x44, 0x87, 0x7b, 0x35, 0x27, 0xb9, 0x29, 0xac, 0xa1, 0xaf, 0xa1, 0x97, 0xc1, 0x04,
0xff, 0x7c, 0x89, 0x2f, 0x1c, 0xea, 0x8c, 0xe9, 0xe9, 0xd8, 0xb2, 0xa9, 0x61, 0x9a, 0xf8, 0xdc,
0xd1, 0x1b, 0x8f, 0xb3, 0x08, 0x3e, 0xc5, 0xa6, 0xa3, 0x37, 0xd1, 0x3e, 0xec, 0x3e, 0x58, 0xeb,
0x33, 0xcb, 0x3c, 0xd3, 0x5b, 0xa8, 0x03, 0x9f, 0x3f, 0x80, 0x8e, 0x0d, 0x5b, 0xdf, 0x2e, 0xce,
0x2d, 0x41, 0x2e, 0xed, 0x08, 0xd3, 0xd1, 0x1e, 0xec, 0x64, 0x58, 0xfc, 0xd6, 0x8c, 0xe1, 0x50,
0x7f, 0xd2, 0xff, 0x7b, 0x0d, 0xb6, 0xef, 0x5d, 0xf9, 0xe8, 0x08, 0xb6, 0xd2, 0x5e, 0xa6, 0x76,
0x66, 0xf1, 0x36, 0xbe, 0x61, 0xd2, 0x4a, 0x50, 0xb2, 0xe4, 0xa1, 0x9f, 0x40, 0xcb, 0xfa, 0x81,
0x48, 0x36, 0xef, 0x41, 0xc9, 0xe6, 0xcd, 0xae, 0x7e, 0x41, 0xf2, 0x29, 0xd1, 0xdd, 0xc1, 0xbc,
0x89, 0x1f, 0x50, 0xc1, 0xa5, 0xf4, 0x83, 0x6b, 0x91, 0xf4, 0xa6, 0xb2, 0x13, 0x60, 0x44, 0xc4,
0x8b, 0x84, 0x47, 0x9a, 0x2c, 0x3f, 0x44, 0x5f, 0x41, 0xd3, 0x0f, 0xe4, 0x2c, 0xa4, 0x13, 0x2e,
0x04, 0xbb, 0xe6, 0xaa, 0x3b, 0xd5, 0x49, 0x43, 0x05, 0x47, 0x71, 0x2c, 0x22, 0x85, 0xf3, 0x3c,
0x69, 0x3d, 0x26, 0xa9, 0x60, 0x4a, 0x42, 0x50, 0x93, 0xec, 0x5a, 0x74, 0x36, 0x7a, 0xd5, 0x41,
0x9d, 0xa8, 0xe7, 0xfe, 0xef, 0x15, 0x68, 0xe4, 0x3b, 0x12, 0xfa, 0x12, 0xb4, 0x65, 0x03, 0xf3,
0xbd, 0xe4, 0x28, 0x43, 0x1a, 0xb2, 0xbc, 0x48, 0x25, 0x60, 0x93, 0xf8, 0x40, 0xd7, 0x89, 0x7a,
0x46, 0xcf, 0x97, 0x8d, 0x4b, 0x50, 0xdf, 0x8b, 0xa6, 0x1a, 0x39, 0xa4, 0x1d, 0x4a, 0x58, 0x9e,
0x40, 0x5d, 0xd8, 0x9a, 0x86, 0xc2, 0x97, 0x69, 0x7f, 0x5d, 0x27, 0xcb, 0x71, 0xff, 0xcf, 0x0a,
0x68, 0xb9, 0xd6, 0xf6, 0xe9, 0x1a, 0x9e, 0x01, 0xa4, 0x8d, 0xd2, 0xf7, 0x92, 0x4a, 0xea, 0x49,
0xc4, 0xf2, 0x0a, 0x5e, 0xd5, 0xa2, 0x17, 0xfa, 0x0e, 0x36, 0x13, 0x62, 0xd2, 0xe6, 0xf7, 0xca,
0x3e, 0x16, 0x6e, 0x98, 0x24, 0x29, 0xaf, 0xff, 0x57, 0x05, 0xda, 0xc5, 0x6e, 0x22, 0xd2, 0x25,
0x8d, 0x26, 0xbe, 0xfc, 0x06, 0x49, 0x4a, 0x6d, 0x10, 0x6d, 0x19, 0xb3, 0x3c, 0x64, 0xc1, 0x73,
0xf5, 0x21, 0x24, 0xe8, 0x15, 0x13, 0x9c, 0x66, 0x74, 0x8f, 0x0b, 0x77, 0xe6, 0x4f, 0x55, 0x95,
0x6b, 0x2a, 0xef, 0x20, 0x26, 0x1e, 0x33, 0xc1, 0x97, 0x7e, 0xc3, 0x8c, 0x85, 0xda, 0xb0, 0x11,
0x33, 0xd4, 0x02, 0x37, 0x48, 0x32, 0x3a, 0x6e, 0xbe, 0xd7, 0x0e, 0x5f, 0xbc, 0x4e, 0xa7, 0x71,
0xb5, 0xa1, 0x9e, 0xbe, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x67, 0xf7, 0x49, 0xea, 0x09,
0x00, 0x00,
// 1073 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcb, 0x6e, 0xdb, 0x46,
0x14, 0xad, 0x2c, 0xf9, 0xa1, 0xab, 0x87, 0xe9, 0x71, 0x6c, 0xd3, 0x4a, 0xe2, 0x2a, 0x6a, 0x17,
0x42, 0x51, 0x38, 0xa8, 0x5b, 0x04, 0x41, 0xb3, 0x29, 0x4d, 0x0d, 0x1c, 0xda, 0x11, 0xe5, 0x8e,
0x69, 0x14, 0xc9, 0x86, 0x18, 0x93, 0x13, 0x99, 0xb5, 0x45, 0xaa, 0x9a, 0x51, 0x00, 0x6d, 0xfb,
0x05, 0xfd, 0x80, 0x7e, 0x43, 0xd1, 0xbf, 0xea, 0x6f, 0x14, 0x9c, 0x21, 0x45, 0xd2, 0xa6, 0x9d,
0x2e, 0xba, 0x91, 0x38, 0xf7, 0x9c, 0x7b, 0xce, 0xdc, 0x79, 0xf0, 0x12, 0x76, 0xbd, 0x68, 0x32,
0x99, 0x87, 0x81, 0x58, 0xb8, 0xf3, 0xa9, 0x4f, 0x05, 0x3b, 0x9c, 0xce, 0x22, 0x11, 0xa1, 0x0d,
0xf9, 0x77, 0x35, 0xff, 0xd8, 0xd9, 0xf6, 0xae, 0xa9, 0x70, 0x03, 0x9f, 0x85, 0x22, 0x10, 0x0b,
0x05, 0x77, 0xb6, 0xd2, 0xb4, 0x80, 0x71, 0x15, 0xea, 0xfd, 0xd1, 0x82, 0xb6, 0x99, 0x8a, 0xe1,
0x4f, 0x2c, 0x14, 0xe8, 0x08, 0x76, 0x32, 0x79, 0x16, 0x87, 0x5c, 0xef, 0x36, 0xf2, 0x6e, 0xf4,
0x4a, 0xb7, 0xd2, 0xaf, 0x91, 0x6d, 0xaf, 0x40, 0x37, 0x63, 0x08, 0xbd, 0x82, 0x9a, 0x58, 0x4c,
0x99, 0xbe, 0xd2, 0xad, 0xf4, 0xdb, 0x47, 0xbd, 0xc3, 0x74, 0x1e, 0x87, 0x45, 0xed, 0x43, 0xf9,
0xeb, 0x2c, 0xa6, 0x8c, 0x48, 0x3e, 0x1a, 0x80, 0x96, 0x79, 0x79, 0x51, 0xf8, 0x31, 0x18, 0xeb,
0xd5, 0x6e, 0xa5, 0xdf, 0x38, 0xda, 0x2f, 0xd1, 0x30, 0x25, 0x81, 0x6c, 0x7a, 0xc5, 0x00, 0x1a,
0x82, 0x26, 0xa2, 0x1b, 0x16, 0xba, 0x53, 0x36, 0x9b, 0x04, 0x9c, 0x07, 0x51, 0xa8, 0xd7, 0xa4,
0x4a, 0xd9, 0x4c, 0x9c, 0x98, 0x7a, 0xbe, 0x64, 0x92, 0x4d, 0x51, 0x0c, 0xa0, 0x37, 0xd0, 0xf2,
0xa8, 0x60, 0xe3, 0x68, 0xb6, 0x70, 0x7d, 0x2a, 0xa8, 0xbe, 0x2a, 0xb5, 0x76, 0x73, 0x5a, 0x09,
0x3c, 0xa0, 0x82, 0x92, 0xa6, 0x97, 0x1b, 0xa1, 0xd7, 0xd0, 0xf4, 0xae, 0x69, 0x18, 0xb2, 0x5b,
0x95, 0xbb, 0x26, 0x73, 0x77, 0x72, 0xb9, 0x0a, 0x95, 0xa9, 0x0d, 0x2f, 0x1b, 0xa0, 0x3e, 0x68,
0x13, 0x36, 0xb9, 0x62, 0x33, 0x57, 0x44, 0x2e, 0xf5, 0x44, 0x5c, 0xc5, 0x7a, 0xb7, 0xd2, 0xaf,
0x93, 0xb6, 0x8a, 0x3b, 0x91, 0x21, 0xa3, 0xc8, 0x86, 0xa6, 0x8a, 0x70, 0xc3, 0xf7, 0x99, 0xaf,
0x6f, 0x74, 0xab, 0xfd, 0xc6, 0xd1, 0x37, 0x0f, 0xae, 0xfa, 0x30, 0x47, 0xc6, 0xa1, 0x98, 0x2d,
0x48, 0x21, 0x1f, 0xdd, 0xc2, 0xee, 0x8c, 0xfd, 0xca, 0x3c, 0xc1, 0x7c, 0xc2, 0x7e, 0x9b, 0x33,
0x2e, 0xb8, 0x13, 0x9d, 0x46, 0x41, 0xa8, 0xd7, 0xa5, 0xf2, 0x0f, 0x0f, 0x2a, 0x93, 0xd2, 0x34,
0xe5, 0xf1, 0x80, 0x66, 0xec, 0x46, 0x3d, 0x8f, 0x4d, 0xef, 0xbb, 0xc1, 0x67, 0xdc, 0x8c, 0xd2,
0xb4, 0xc4, 0xad, 0x5c, 0x13, 0x9d, 0x40, 0x5b, 0x9d, 0x8d, 0x09, 0x13, 0x54, 0xee, 0x48, 0x43,
0xee, 0x48, 0xf7, 0xa1, 0x93, 0x31, 0x4c, 0x78, 0xa4, 0x25, 0xf2, 0xc3, 0xce, 0x07, 0xd8, 0xba,
0xb7, 0x8e, 0x48, 0x83, 0xea, 0x0d, 0x5b, 0xc8, 0x9b, 0x51, 0x27, 0xf1, 0x23, 0x7a, 0x09, 0xab,
0x9f, 0xe8, 0xed, 0x5c, 0x5d, 0x85, 0xf2, 0x63, 0xac, 0x64, 0x88, 0xe2, 0xfd, 0xb8, 0xf2, 0xba,
0xd2, 0xb9, 0x81, 0xa7, 0x8f, 0xac, 0x64, 0x89, 0xcb, 0xab, 0xa2, 0x4b, 0x59, 0x31, 0x89, 0x90,
0xd2, 0xb9, 0x63, 0xf6, 0xc8, 0x42, 0xfe, 0xbf, 0x66, 0xbd, 0xbf, 0x6b, 0x50, 0x5f, 0x5e, 0x7a,
0xd4, 0x80, 0xf5, 0x4b, 0xfb, 0xcc, 0x1e, 0xfd, 0x62, 0x6b, 0x5f, 0x20, 0x04, 0x6d, 0x73, 0x34,
0x1c, 0x5e, 0xda, 0x96, 0xf3, 0xde, 0xc5, 0x03, 0xcb, 0xd1, 0x2a, 0xe8, 0x5b, 0xe8, 0x67, 0xb1,
0x21, 0x1e, 0x1e, 0x63, 0xe2, 0x3a, 0xa3, 0x33, 0x6c, 0xbb, 0xe7, 0x98, 0x0c, 0xad, 0x8b, 0x0b,
0x6b, 0x64, 0xbb, 0xe6, 0x5b, 0xc3, 0x3e, 0xc1, 0xda, 0xca, 0x7f, 0x63, 0x0f, 0xf0, 0x3b, 0xec,
0x60, 0xad, 0x8a, 0x9e, 0xc3, 0x7e, 0xc6, 0x36, 0x0d, 0x07, 0x9f, 0x8c, 0xc8, 0x7b, 0xd7, 0x24,
0xd8, 0x70, 0xb0, 0x56, 0x7b, 0x00, 0x4e, 0xb2, 0x57, 0xd1, 0x53, 0xd8, 0x2b, 0x81, 0xe5, 0xb4,
0xd7, 0xd0, 0x33, 0xd0, 0x73, 0xe0, 0x5b, 0xc3, 0xb6, 0xf1, 0xbb, 0x54, 0x79, 0xbd, 0x1c, 0x4d,
0x84, 0x37, 0x50, 0x07, 0x76, 0xef, 0xa3, 0x52, 0xb7, 0x8e, 0x0e, 0xa0, 0x53, 0x62, 0x4a, 0xf0,
0x88, 0x0c, 0x30, 0xd1, 0xe0, 0xce, 0x9c, 0x93, 0xdc, 0x14, 0x6e, 0xa0, 0xaf, 0xa1, 0x9b, 0xc1,
0x04, 0xff, 0x7c, 0x89, 0x2f, 0x1c, 0xd7, 0x19, 0xb9, 0xa7, 0x23, 0xcb, 0x76, 0x0d, 0xd3, 0xc4,
0xe7, 0x8e, 0xd6, 0x7c, 0x9c, 0x45, 0xf0, 0x29, 0x36, 0x1d, 0xad, 0x85, 0xf6, 0x61, 0xe7, 0xde,
0x5a, 0x9f, 0x59, 0xe6, 0x99, 0xd6, 0x46, 0x3a, 0x3c, 0xb9, 0x07, 0x1d, 0x1b, 0xb6, 0xb6, 0x59,
0xac, 0x2d, 0x41, 0x2e, 0xed, 0x18, 0xd3, 0xd0, 0x1e, 0x6c, 0x67, 0x98, 0xda, 0x35, 0x63, 0x30,
0xd0, 0xb6, 0x7a, 0x7f, 0xad, 0xc0, 0xe6, 0x9d, 0x57, 0x3e, 0x3a, 0x82, 0x8d, 0xb4, 0x97, 0xc9,
0x93, 0x59, 0x7c, 0x1b, 0x5f, 0x53, 0x61, 0x25, 0x28, 0x59, 0xf2, 0xd0, 0x4f, 0xd0, 0xc8, 0xfa,
0x01, 0x4f, 0x0e, 0xef, 0x41, 0xc9, 0xe1, 0xcd, 0x5e, 0xfd, 0x9c, 0xe4, 0x53, 0xe2, 0x77, 0x07,
0xf5, 0x27, 0x41, 0xe8, 0x72, 0x26, 0x44, 0x10, 0x8e, 0x79, 0xd2, 0x9b, 0xca, 0x6e, 0x80, 0x11,
0x13, 0x2f, 0x12, 0x1e, 0x69, 0xd1, 0xfc, 0x10, 0x7d, 0x05, 0xad, 0x20, 0x14, 0xb3, 0xc8, 0x9d,
0x30, 0xce, 0xe9, 0x98, 0xc9, 0xee, 0x54, 0x27, 0x4d, 0x19, 0x1c, 0xaa, 0x58, 0x4c, 0x8a, 0xe6,
0x79, 0xd2, 0xaa, 0x22, 0xc9, 0x60, 0x4a, 0x42, 0x50, 0x13, 0x74, 0xcc, 0xf5, 0xb5, 0x6e, 0xb5,
0x5f, 0x27, 0xf2, 0xb9, 0xf7, 0x7b, 0x05, 0x9a, 0xf9, 0x8e, 0x84, 0xbe, 0x84, 0xc6, 0xb2, 0x81,
0x05, 0x7e, 0x72, 0x95, 0x21, 0x0d, 0x59, 0x7e, 0xac, 0x12, 0xd2, 0x89, 0xba, 0xd0, 0x75, 0x22,
0x9f, 0xd1, 0x8b, 0x65, 0xe3, 0xe2, 0x6e, 0xe0, 0xc7, 0xa5, 0xc6, 0x0e, 0x69, 0x87, 0xe2, 0x96,
0xcf, 0x51, 0x07, 0x36, 0xa6, 0x11, 0x0f, 0x44, 0xda, 0x5f, 0x57, 0xc9, 0x72, 0xdc, 0xfb, 0xb3,
0x02, 0x8d, 0x5c, 0x6b, 0xfb, 0xfc, 0x1c, 0x9e, 0x03, 0xa4, 0x8d, 0x32, 0xf0, 0x93, 0x99, 0xd4,
0x93, 0x88, 0xe5, 0x17, 0xbc, 0xaa, 0x45, 0x2f, 0xf4, 0x1d, 0xac, 0x27, 0xc4, 0xa4, 0xcd, 0xef,
0x95, 0x7d, 0x2c, 0x5c, 0x53, 0x41, 0x52, 0x5e, 0xcf, 0x86, 0x27, 0x17, 0xc1, 0x38, 0x64, 0xfe,
0x9d, 0x8f, 0x9d, 0x67, 0x50, 0xe7, 0xc1, 0x38, 0xa4, 0x62, 0x3e, 0x63, 0x72, 0x92, 0x4d, 0x92,
0x05, 0x90, 0x0e, 0xeb, 0x53, 0xba, 0xb8, 0x8d, 0xa8, 0x9a, 0x60, 0x93, 0xa4, 0xc3, 0xde, 0x3f,
0x15, 0xd8, 0x2d, 0x4a, 0xf1, 0x74, 0x8b, 0xe2, 0x85, 0x5c, 0x7e, 0xd3, 0x24, 0xa5, 0x37, 0x49,
0x63, 0x19, 0xb3, 0x7c, 0x64, 0xc1, 0x0b, 0xf9, 0x61, 0xc5, 0xdd, 0x2b, 0xca, 0x99, 0x9b, 0xd1,
0x7d, 0xc6, 0xbd, 0x59, 0x30, 0x95, 0x55, 0x2b, 0xc7, 0x03, 0x45, 0x3c, 0xa6, 0x9c, 0x2d, 0xfd,
0x06, 0x19, 0x0b, 0x75, 0x60, 0x4d, 0x31, 0xe4, 0x86, 0x35, 0x8f, 0x57, 0xf4, 0x0a, 0x49, 0x22,
0xc8, 0x84, 0x16, 0x97, 0x45, 0xbb, 0x09, 0xa5, 0x26, 0x1b, 0x6c, 0xee, 0x0e, 0x94, 0xad, 0x09,
0x69, 0xaa, 0x24, 0x55, 0xd5, 0x71, 0xeb, 0x43, 0xe3, 0xf0, 0xe5, 0x9b, 0x34, 0xe3, 0x6a, 0x4d,
0x3e, 0x7d, 0xff, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x39, 0xf7, 0x01, 0xf5, 0x83, 0x0a, 0x00,
0x00,
}

View File

@ -64,6 +64,13 @@ message ChannelData {
CommunityChat channel = 4;
}
message SignedCommunityEvent {
// Signature of the payload field
bytes signature = 1;
// Marshaled CommunityEvent
bytes payload = 2;
}
// CommunityEventsMessage is a message used to propagate information
// about community changes.
message CommunityEventsMessage {
@ -73,5 +80,9 @@ message CommunityEventsMessage {
bytes events_base_community_description = 2;
// A list of admins events for the channel in bytes
repeated bytes events = 3;
// Deprecated: use signed_events instead.
repeated bytes events = 3 [deprecated=true];
// A list of signed community events
repeated SignedCommunityEvent signed_events = 4;
}