mirror of
https://github.com/status-im/status-go.git
synced 2025-02-17 09:16:53 +00:00
feat: Add an expiration and periodical publishing for community grants (#5024)
* feat_: add periodical publishing for community grants feat_: Validate grant when receiving it feat_: add expiration for grants feat_: add test for grants expiration fix_: move grants test to profile showcase, fix a few bugs * feat_: use one group mesage to update grants * chore_: review fixes
This commit is contained in:
parent
3de2756660
commit
6da423fc71
@ -31,6 +31,9 @@ import (
|
|||||||
|
|
||||||
const signatureLength = 65
|
const signatureLength = 65
|
||||||
|
|
||||||
|
// GrantExpirationTime interval of 7 days
|
||||||
|
var GrantExpirationTime = 168 * time.Hour
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
PrivateKey *ecdsa.PrivateKey
|
PrivateKey *ecdsa.PrivateKey
|
||||||
ControlNode *ecdsa.PublicKey
|
ControlNode *ecdsa.PublicKey
|
||||||
@ -1881,6 +1884,9 @@ func (o *Community) VerifyGrantSignature(data []byte) (*protobuf.Grant, error) {
|
|||||||
if !bytes.Equal(grant.CommunityId, o.ID()) {
|
if !bytes.Equal(grant.CommunityId, o.ID()) {
|
||||||
return nil, ErrInvalidGrant
|
return nil, ErrInvalidGrant
|
||||||
}
|
}
|
||||||
|
if grant.Expires < uint64(time.Now().UnixMilli()) {
|
||||||
|
return nil, ErrGrantExpired
|
||||||
|
}
|
||||||
|
|
||||||
extractedPublicKey, err := crypto.SigToPub(crypto.Keccak256(payload), signature)
|
extractedPublicKey, err := crypto.SigToPub(crypto.Keccak256(payload), signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1973,6 +1979,7 @@ func (o *Community) buildGrant(key *ecdsa.PublicKey, chatID string) ([]byte, err
|
|||||||
MemberId: crypto.CompressPubkey(key),
|
MemberId: crypto.CompressPubkey(key),
|
||||||
ChatId: chatID,
|
ChatId: chatID,
|
||||||
Clock: o.config.CommunityDescription.Clock,
|
Clock: o.config.CommunityDescription.Clock,
|
||||||
|
Expires: uint64(time.Now().Add(GrantExpirationTime).UnixMilli()),
|
||||||
}
|
}
|
||||||
marshaledGrant, err := proto.Marshal(grant)
|
marshaledGrant, err := proto.Marshal(grant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,6 +27,8 @@ var ErrNotAdmin = errors.New("no admin privileges for this community")
|
|||||||
var ErrNotOwner = errors.New("no owner privileges for this community")
|
var ErrNotOwner = errors.New("no owner privileges for this community")
|
||||||
var ErrNotControlNode = errors.New("not a control node")
|
var ErrNotControlNode = errors.New("not a control node")
|
||||||
var ErrInvalidGrant = errors.New("invalid grant")
|
var ErrInvalidGrant = errors.New("invalid grant")
|
||||||
|
var ErrGrantExpired = errors.New("expired grant")
|
||||||
|
var ErrGrantOlder = errors.New("received grant older than the current one")
|
||||||
var ErrNotAuthorized = errors.New("not authorized")
|
var ErrNotAuthorized = errors.New("not authorized")
|
||||||
var ErrAlreadyMember = errors.New("already a member")
|
var ErrAlreadyMember = errors.New("already a member")
|
||||||
var ErrAlreadyJoined = errors.New("already joined")
|
var ErrAlreadyJoined = errors.New("already joined")
|
||||||
@ -46,3 +48,4 @@ var ErrNoRevealedAccountsSignature = errors.New("revealed accounts without the s
|
|||||||
var ErrNoFreeSpaceForHistoryArchives = errors.New("history archive: No free space for downloading history archives")
|
var ErrNoFreeSpaceForHistoryArchives = errors.New("history archive: No free space for downloading history archives")
|
||||||
var ErrPermissionToJoinNotSatisfied = errors.New("permission to join not satisfied")
|
var ErrPermissionToJoinNotSatisfied = errors.New("permission to join not satisfied")
|
||||||
var ErrBannedMemberNotFound = errors.New("banned member not found")
|
var ErrBannedMemberNotFound = errors.New("banned member not found")
|
||||||
|
var ErrGrantMemberPublicKeyIsDifferent = errors.New("grant member public key is different")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package communities
|
package communities
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
@ -862,7 +863,7 @@ func (m *Manager) CreateCommunity(request *requests.CreateCommunity, publish boo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = m.persistence.SaveCommunityGrant(community.IDString(), grant, description.Clock)
|
err = m.persistence.SaveCommunityGrant(community.IDString(), grant, uint64(time.Now().UnixMilli()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1415,7 +1416,7 @@ func (m *Manager) ImportCommunity(key *ecdsa.PrivateKey, clock uint64) (*Communi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = m.persistence.SaveCommunityGrant(community.IDString(), grant, community.Description().Clock)
|
err = m.persistence.SaveCommunityGrant(community.IDString(), grant, uint64(time.Now().UnixMilli()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2979,8 +2980,11 @@ func (m *Manager) HandleCommunityRequestToJoinResponse(signer *ecdsa.PublicKey,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = m.handleCommunityGrant(community.ID(), request.Grant, request.Clock); err != nil {
|
if community.Encrypted() && len(request.Grant) > 0 {
|
||||||
return nil, err
|
_, err = m.HandleCommunityGrant(community, request.Grant, request.Clock)
|
||||||
|
if err != nil && err != ErrGrantOlder && err != ErrGrantExpired {
|
||||||
|
m.logger.Error("Error handling a community grant", zap.Error(err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.persistence.SaveCommunity(community)
|
err = m.persistence.SaveCommunity(community)
|
||||||
@ -4870,17 +4874,26 @@ func (m *Manager) handleCommunityTokensMetadata(community *Community) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) handleCommunityGrant(communityID types.HexBytes, grant []byte, clock uint64) error {
|
func (m *Manager) HandleCommunityGrant(community *Community, grant []byte, clock uint64) (uint64, error) {
|
||||||
_, oldClock, err := m.persistence.GetCommunityGrant(communityID.String())
|
_, oldClock, err := m.GetCommunityGrant(community.IDString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldClock >= clock {
|
if oldClock >= clock {
|
||||||
return nil
|
return 0, ErrGrantOlder
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.persistence.SaveCommunityGrant(communityID.String(), grant, clock)
|
verifiedGrant, err := community.VerifyGrantSignature(grant)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(verifiedGrant.MemberId, crypto.CompressPubkey(&m.identity.PublicKey)) {
|
||||||
|
return 0, ErrGrantMemberPublicKeyIsDifferent
|
||||||
|
}
|
||||||
|
|
||||||
|
return clock - oldClock, m.persistence.SaveCommunityGrant(community.IDString(), grant, clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) FetchCommunityToken(community *Community, tokenMetadata *protobuf.CommunityTokenMetadata, chainID uint64, contractAddress string) (*community_token.CommunityToken, error) {
|
func (m *Manager) FetchCommunityToken(community *Community, tokenMetadata *protobuf.CommunityTokenMetadata, chainID uint64, contractAddress string) (*community_token.CommunityToken, error) {
|
||||||
|
@ -3,6 +3,7 @@ package encryption
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@ -422,6 +423,43 @@ func (p *Protocol) BuildHashRatchetMessage(groupID []byte, payload []byte) (*Pro
|
|||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) EncryptCommunityGrants(privateKey *ecdsa.PrivateKey, recipientGrants map[*ecdsa.PublicKey][]byte) (map[uint32][]byte, error) {
|
||||||
|
grants := make(map[uint32][]byte)
|
||||||
|
|
||||||
|
for recipientKey, grant := range recipientGrants {
|
||||||
|
sharedKey, err := generateSharedKey(privateKey, recipientKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedGrant, err := encrypt(grant, sharedKey, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
kBytes := publicKeyMostRelevantBytes(recipientKey)
|
||||||
|
grants[kBytes] = encryptedGrant
|
||||||
|
}
|
||||||
|
|
||||||
|
return grants, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Protocol) DecryptCommunityGrant(myIdentityKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, grants map[uint32][]byte) ([]byte, error) {
|
||||||
|
kBytes := publicKeyMostRelevantBytes(&myIdentityKey.PublicKey)
|
||||||
|
|
||||||
|
ecryptedGrant, ok := grants[kBytes]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("can't find related grant in the map")
|
||||||
|
}
|
||||||
|
|
||||||
|
sharedKey, err := generateSharedKey(myIdentityKey, senderKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypt(ecryptedGrant, sharedKey)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Protocol) GetKeyExMessageSpecs(groupID []byte, identity *ecdsa.PrivateKey, recipients []*ecdsa.PublicKey, forceRekey bool) ([]*ProtocolMessageSpec, error) {
|
func (p *Protocol) GetKeyExMessageSpecs(groupID []byte, identity *ecdsa.PrivateKey, recipients []*ecdsa.PublicKey, forceRekey bool) ([]*ProtocolMessageSpec, error) {
|
||||||
var ratchets []*HashRatchetKeyCompatibility
|
var ratchets []*HashRatchetKeyCompatibility
|
||||||
var err error
|
var err error
|
||||||
|
@ -813,6 +813,7 @@ func (m *Messenger) Start() (*MessengerResponse, error) {
|
|||||||
m.handleCommunitiesSubscription(m.communitiesManager.Subscribe())
|
m.handleCommunitiesSubscription(m.communitiesManager.Subscribe())
|
||||||
m.handleCommunitiesHistoryArchivesSubscription(m.communitiesManager.Subscribe())
|
m.handleCommunitiesHistoryArchivesSubscription(m.communitiesManager.Subscribe())
|
||||||
m.updateCommunitiesActiveMembersPeriodically()
|
m.updateCommunitiesActiveMembersPeriodically()
|
||||||
|
m.schedulePublishGrantsForControlledCommunities()
|
||||||
m.handleENSVerificationSubscription(ensSubscription)
|
m.handleENSVerificationSubscription(ensSubscription)
|
||||||
m.watchConnectionChange()
|
m.watchConnectionChange()
|
||||||
m.watchChatsAndCommunitiesToUnmute()
|
m.watchChatsAndCommunitiesToUnmute()
|
||||||
|
@ -51,6 +51,12 @@ var messageArchiveInterval = 7 * 24 * time.Hour
|
|||||||
// 1 day interval
|
// 1 day interval
|
||||||
var updateActiveMembersInterval = 24 * time.Hour
|
var updateActiveMembersInterval = 24 * time.Hour
|
||||||
|
|
||||||
|
// 1 day interval
|
||||||
|
var grantUpdateInterval = 24 * time.Hour
|
||||||
|
|
||||||
|
// 4 hours interval
|
||||||
|
var grantInvokesProfileDispatchInterval = 4 * time.Hour
|
||||||
|
|
||||||
const discordTimestampLayout = time.RFC3339
|
const discordTimestampLayout = time.RFC3339
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -481,8 +487,8 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti
|
|||||||
lastPublished = time.Now().Unix()
|
lastPublished = time.Now().Unix()
|
||||||
|
|
||||||
case <-m.quit:
|
case <-m.quit:
|
||||||
|
ticker.Stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -526,6 +532,134 @@ func (m *Messenger) updateCommunitiesActiveMembersPeriodically() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case <-m.quit:
|
case <-m.quit:
|
||||||
|
ticker.Stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) HandleCommunityUpdateGrant(state *ReceivedMessageState, message *protobuf.CommunityUpdateGrant, statusMessage *v1protocol.StatusMessage) error {
|
||||||
|
community, err := m.communitiesManager.GetByID(message.CommunityId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
grant, err := m.encryptor.DecryptCommunityGrant(m.identity, state.CurrentMessageState.PublicKey, message.Grants)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.handleCommunityGrant(community, grant, message.Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) handleCommunityGrant(community *communities.Community, grant []byte, clock uint64) error {
|
||||||
|
difference, err := m.communitiesManager.HandleCommunityGrant(community, grant, clock)
|
||||||
|
if err == communities.ErrGrantOlder || err == communities.ErrGrantExpired {
|
||||||
|
// Don't log an error for these cases
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if grant is significantly newer than the one we have, we should check the profile showcase
|
||||||
|
if time.Duration(difference)*time.Millisecond > grantInvokesProfileDispatchInterval {
|
||||||
|
err = m.UpdateProfileShowcaseCommunity(community)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) publishGroupGrantMessage(community *communities.Community, timestamp uint64, recipientGrants map[*ecdsa.PublicKey][]byte) error {
|
||||||
|
grants, err := m.encryptor.EncryptCommunityGrants(community.PrivateKey(), recipientGrants)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
message := &protobuf.CommunityUpdateGrant{
|
||||||
|
Timestamp: timestamp,
|
||||||
|
CommunityId: community.ID(),
|
||||||
|
Grants: grants,
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := proto.Marshal(message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rawMessage := common.RawMessage{
|
||||||
|
Payload: payload,
|
||||||
|
Sender: community.PrivateKey(),
|
||||||
|
SkipEncryptionLayer: true,
|
||||||
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_UPDATE_GRANT,
|
||||||
|
PubsubTopic: community.PubsubTopic(),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = m.sender.SendPublic(context.Background(), community.IDString(), rawMessage)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) updateGrantsForControlledCommunities() {
|
||||||
|
controlledCommunities, err := m.communitiesManager.Controlled()
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("failed fetch controlled communities for grants update", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, community := range controlledCommunities {
|
||||||
|
// Skip unencrypted communities
|
||||||
|
if !community.Encrypted() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
memberGrants := map[*ecdsa.PublicKey][]byte{}
|
||||||
|
for memberKey := range community.Members() {
|
||||||
|
if memberKey == m.IdentityPublicKeyString() {
|
||||||
|
grant, err := community.BuildGrant(m.IdentityPublicKey(), "")
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("can't build own grant for controlled community", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.handleCommunityGrant(community, grant, uint64(time.Now().UnixMilli()))
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("error handling grant for controlled community", zap.Error(err))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memberPubKey, err := common.HexToPubkey(memberKey)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("Pubkey decode ", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
grant, err := community.BuildGrant(memberPubKey, "")
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("can't build member's grant for controlled community", zap.Error(err))
|
||||||
|
}
|
||||||
|
|
||||||
|
memberGrants[memberPubKey] = grant
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = m.publishGroupGrantMessage(community, uint64(time.Now().UnixMilli()), memberGrants)
|
||||||
|
if err != nil {
|
||||||
|
m.logger.Error("failed to update grant for community members", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) schedulePublishGrantsForControlledCommunities() {
|
||||||
|
// Send once immediately
|
||||||
|
m.updateGrantsForControlledCommunities()
|
||||||
|
|
||||||
|
ticker := time.NewTicker(grantUpdateInterval)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
m.updateGrantsForControlledCommunities()
|
||||||
|
case <-m.quit:
|
||||||
|
ticker.Stop()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,6 +253,9 @@ func (m *Messenger) dispatchToHandler(messageState *ReceivedMessageState, protoB
|
|||||||
case protobuf.ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES:
|
case protobuf.ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES:
|
||||||
return m.handleDeleteCommunityMemberMessagesProtobuf(messageState, protoBytes, msg, filter)
|
return m.handleDeleteCommunityMemberMessagesProtobuf(messageState, protoBytes, msg, filter)
|
||||||
|
|
||||||
|
case protobuf.ApplicationMetadataMessage_COMMUNITY_UPDATE_GRANT:
|
||||||
|
return m.handleCommunityUpdateGrantProtobuf(messageState, protoBytes, msg, filter)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m.logger.Info("protobuf type not found", zap.String("type", string(msg.ApplicationLayer.Type)))
|
m.logger.Info("protobuf type not found", zap.String("type", string(msg.ApplicationLayer.Type)))
|
||||||
return errors.New("protobuf type not found")
|
return errors.New("protobuf type not found")
|
||||||
@ -1817,3 +1820,21 @@ func (m *Messenger) handleDeleteCommunityMemberMessagesProtobuf(messageState *Re
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (m *Messenger) handleCommunityUpdateGrantProtobuf(messageState *ReceivedMessageState, protoBytes []byte, msg *v1protocol.StatusMessage, filter transport.Filter) error {
|
||||||
|
m.logger.Info("handling CommunityUpdateGrant")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
p := &protobuf.CommunityUpdateGrant{}
|
||||||
|
err := proto.Unmarshal(protoBytes, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.outputToCSV(msg.TransportLayer.Message.Timestamp, msg.ApplicationLayer.ID, messageState.CurrentMessageState.Contact.ID, filter.ContentTopic, filter.ChatID, msg.ApplicationLayer.Type, p)
|
||||||
|
|
||||||
|
return m.HandleCommunityUpdateGrant(messageState, p, msg)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -118,13 +118,13 @@ func (m *Messenger) validateCommunityMembershipEntry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if community.Encrypted() {
|
if community.Encrypted() {
|
||||||
grant, err := community.VerifyGrantSignature(entry.Grant)
|
verifiedGrant, err := community.VerifyGrantSignature(entry.Grant)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Warn("failed to verify grant signature ", zap.Error(err))
|
m.logger.Warn("failed to verify grant signature ", zap.Error(err))
|
||||||
return identity.ProfileShowcaseMembershipStatusNotAMember, nil
|
return identity.ProfileShowcaseMembershipStatusUnproven, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if grant != nil && bytes.Equal(grant.MemberId, crypto.CompressPubkey(contactPubKey)) {
|
if bytes.Equal(verifiedGrant.MemberId, crypto.CompressPubkey(contactPubKey)) {
|
||||||
return identity.ProfileShowcaseMembershipStatusProvenMember, nil
|
return identity.ProfileShowcaseMembershipStatusProvenMember, nil
|
||||||
}
|
}
|
||||||
// Show as not a member if membership can't be proven
|
// Show as not a member if membership can't be proven
|
||||||
@ -432,8 +432,6 @@ func (m *Messenger) GetProfileShowcaseForContact(contactID string, validate bool
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: validate collectibles & assets ownership, https://github.com/status-im/status-desktop/issues/14129
|
|
||||||
|
|
||||||
return profileShowcase, nil
|
return profileShowcase, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -713,6 +711,19 @@ func (m *Messenger) DeleteProfileShowcaseWalletAccount(account *accounts.Account
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Messenger) UpdateProfileShowcaseCommunity(community *communities.Community) error {
|
||||||
|
profileCommunity, err := m.persistence.GetProfileShowcaseCommunityPreference(community.IDString())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if profileCommunity == nil {
|
||||||
|
// No corresponding profile entry, exit
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return m.DispatchProfileShowcase()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Messenger) DeleteProfileShowcaseCommunity(community *communities.Community) error {
|
func (m *Messenger) DeleteProfileShowcaseCommunity(community *communities.Community) error {
|
||||||
deleted, err := m.persistence.DeleteProfileShowcaseCommunityPreference(community.IDString())
|
deleted, err := m.persistence.DeleteProfileShowcaseCommunityPreference(community.IDString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
"github.com/status-im/status-go/protocol/common"
|
"github.com/status-im/status-go/protocol/common"
|
||||||
|
"github.com/status-im/status-go/protocol/communities"
|
||||||
"github.com/status-im/status-go/protocol/identity"
|
"github.com/status-im/status-go/protocol/identity"
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
"github.com/status-im/status-go/protocol/requests"
|
"github.com/status-im/status-go/protocol/requests"
|
||||||
@ -702,5 +704,139 @@ func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipEncry
|
|||||||
s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString())
|
s.Require().Equal(profileShowcase.Communities[0].CommunityID, aliceCommunity.IDString())
|
||||||
s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)
|
s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)
|
||||||
s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString())
|
s.Require().Equal(profileShowcase.Communities[1].CommunityID, bobCommunity.IDString())
|
||||||
s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusNotAMember)
|
s.Require().Equal(profileShowcase.Communities[1].MembershipStatus, identity.ProfileShowcaseMembershipStatusUnproven)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The scenario tested is as follow:
|
||||||
|
// 1) Owner creates an encrypted community
|
||||||
|
// 2) Bob add Alice becommes a mutual contacts
|
||||||
|
// 3) Alice and bob join the community
|
||||||
|
// 4) Alice presents the community in her profile showcase
|
||||||
|
// 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant
|
||||||
|
// 6) Wait until the grant expires, Bob should not be able to validate the membership anymore (commented step)
|
||||||
|
// 7) Owner updates the grant
|
||||||
|
// 8) Bob should be able to validate the membership again
|
||||||
|
func (s *TestMessengerProfileShowcase) TestProfileShowcaseCommuniesDispatchOnGrantUpdate() {
|
||||||
|
// NOTE: smaller timeouts can lead test to be flaky
|
||||||
|
communities.GrantExpirationTime = 500 * time.Millisecond
|
||||||
|
grantInvokesProfileDispatchInterval = 1 * time.Millisecond
|
||||||
|
alice := s.m
|
||||||
|
|
||||||
|
// Set Display name to pass shouldPublishChatIdentity check
|
||||||
|
profileKp := accounts.GetProfileKeypairForTest(true, false, false)
|
||||||
|
profileKp.KeyUID = alice.account.KeyUID
|
||||||
|
profileKp.Accounts[0].KeyUID = alice.account.KeyUID
|
||||||
|
|
||||||
|
err := alice.settings.SaveOrUpdateKeypair(profileKp)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = alice.SetDisplayName("Alice")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// 1) Owner creates an encrypted community
|
||||||
|
owner := s.newMessengerForProfileShowcase()
|
||||||
|
defer TearDownMessenger(&s.Suite, owner)
|
||||||
|
|
||||||
|
owner.communitiesManager.PermissionChecker = &testPermissionChecker{}
|
||||||
|
|
||||||
|
community, _ := createEncryptedCommunity(&s.Suite, owner)
|
||||||
|
s.Require().True(community.Encrypted())
|
||||||
|
|
||||||
|
// 2) Bob add Alice becommes a mutual contacts
|
||||||
|
bob := s.newMessengerForProfileShowcase()
|
||||||
|
defer TearDownMessenger(&s.Suite, bob)
|
||||||
|
|
||||||
|
s.mutualContact(bob)
|
||||||
|
|
||||||
|
// 3) Alice and bob join the community
|
||||||
|
advertiseCommunityTo(&s.Suite, community, owner, alice)
|
||||||
|
advertiseCommunityTo(&s.Suite, community, owner, bob)
|
||||||
|
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
|
||||||
|
joinCommunity(&s.Suite, community, owner, alice, request, "")
|
||||||
|
|
||||||
|
joinedCommunities, err := alice.communitiesManager.Joined()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(joinedCommunities, 1)
|
||||||
|
s.Require().Equal(joinedCommunities[0].IDString(), community.IDString())
|
||||||
|
s.Require().True(joinedCommunities[0].Encrypted())
|
||||||
|
|
||||||
|
grant, clock, err := alice.communitiesManager.GetCommunityGrant(community.IDString())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().NotEqual(grant, []byte{})
|
||||||
|
s.Require().True(clock > 0)
|
||||||
|
|
||||||
|
// 4) Alice presents the community in her profile showcase
|
||||||
|
err = alice.SetProfileShowcasePreferences(&identity.ProfileShowcasePreferences{
|
||||||
|
Communities: []*identity.ProfileShowcaseCommunityPreference{
|
||||||
|
&identity.ProfileShowcaseCommunityPreference{
|
||||||
|
CommunityID: community.IDString(),
|
||||||
|
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
|
||||||
|
Order: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, false)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// 5) Bob gets the community from Alice's profile showcase and validates community's membership with grant
|
||||||
|
contactID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
|
||||||
|
_, err = WaitOnMessengerResponse(
|
||||||
|
bob,
|
||||||
|
func(r *MessengerResponse) bool {
|
||||||
|
return r.updatedProfileShowcaseContactIDs[contactID] == true
|
||||||
|
},
|
||||||
|
"no messages",
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
profileShowcase, err := bob.GetProfileShowcaseForContact(contactID, true)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(profileShowcase.Communities, 1)
|
||||||
|
s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID)
|
||||||
|
s.Require().Equal(identity.ProfileShowcaseMembershipStatusProvenMember, profileShowcase.Communities[0].MembershipStatus)
|
||||||
|
|
||||||
|
// 6) Wait until the grant expires, Bob should not be able to validate the membership anymore
|
||||||
|
time.Sleep(communities.GrantExpirationTime)
|
||||||
|
|
||||||
|
profileShowcase, err = bob.GetProfileShowcaseForContact(contactID, true)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(profileShowcase.Communities, 1)
|
||||||
|
s.Require().Equal(community.IDString(), profileShowcase.Communities[0].CommunityID)
|
||||||
|
s.Require().Equal(identity.ProfileShowcaseMembershipStatusUnproven, profileShowcase.Communities[0].MembershipStatus)
|
||||||
|
|
||||||
|
// 7) Owner updates the grant
|
||||||
|
owner.updateGrantsForControlledCommunities()
|
||||||
|
|
||||||
|
// Retrieve for grant clock update
|
||||||
|
err = tt.RetryWithBackOff(func() error {
|
||||||
|
_, err = alice.RetrieveAll()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, updatedClock, err := alice.communitiesManager.GetCommunityGrant(community.IDString())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if clock == updatedClock {
|
||||||
|
return errors.New("can't recive an updated grant")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// 8) Bob should be able to validate the membership again
|
||||||
|
_, err = WaitOnMessengerResponse(
|
||||||
|
bob,
|
||||||
|
func(r *MessengerResponse) bool {
|
||||||
|
return r.updatedProfileShowcaseContactIDs[contactID] == true
|
||||||
|
},
|
||||||
|
"no messages",
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
profileShowcase, err = bob.GetProfileShowcaseForContact(contactID, true)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(profileShowcase.Communities, 1)
|
||||||
|
s.Require().Equal(profileShowcase.Communities[0].CommunityID, community.IDString())
|
||||||
|
s.Require().Equal(profileShowcase.Communities[0].MembershipStatus, identity.ProfileShowcaseMembershipStatusProvenMember)
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ const selectProfileShowcasePreferencesQuery = "SELECT clock FROM profile_showcas
|
|||||||
|
|
||||||
const upsertProfileShowcaseCommunityPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_communities_preferences(community_id, visibility, sort_order) VALUES (?, ?, ?)" // #nosec G101
|
const upsertProfileShowcaseCommunityPreferenceQuery = "INSERT OR REPLACE INTO profile_showcase_communities_preferences(community_id, visibility, sort_order) VALUES (?, ?, ?)" // #nosec G101
|
||||||
const selectProfileShowcaseCommunityPreferenceQuery = "SELECT community_id, visibility, sort_order FROM profile_showcase_communities_preferences" // #nosec G101
|
const selectProfileShowcaseCommunityPreferenceQuery = "SELECT community_id, visibility, sort_order FROM profile_showcase_communities_preferences" // #nosec G101
|
||||||
|
const selectSpecifiedShowcaseCommunityPreferenceQuery = "SELECT community_id, visibility, sort_order FROM profile_showcase_communities_preferences WHERE community_id = ?" // #nosec G101
|
||||||
const deleteProfileShowcaseCommunityPreferenceQuery = "DELETE FROM profile_showcase_communities_preferences WHERE community_id = ?" // #nosec G101
|
const deleteProfileShowcaseCommunityPreferenceQuery = "DELETE FROM profile_showcase_communities_preferences WHERE community_id = ?" // #nosec G101
|
||||||
const clearProfileShowcaseCommunitiyPreferencesQuery = "DELETE FROM profile_showcase_communities_preferences" // #nosec G101
|
const clearProfileShowcaseCommunitiyPreferencesQuery = "DELETE FROM profile_showcase_communities_preferences" // #nosec G101
|
||||||
|
|
||||||
@ -99,14 +100,11 @@ func (db sqlitePersistence) saveProfileShowcaseCommunityPreference(tx *sql.Tx, c
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db sqlitePersistence) getProfileShowcaseCommunitiesPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseCommunityPreference, error) {
|
func (db sqlitePersistence) processProfileShowcaseCommunityPreferences(rows *sql.Rows) (result []*identity.ProfileShowcaseCommunityPreference, err error) {
|
||||||
rows, err := tx.Query(selectProfileShowcaseCommunityPreferenceQuery)
|
if rows == nil {
|
||||||
if err != nil {
|
return nil, errors.New("rows is nil")
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
communities := []*identity.ProfileShowcaseCommunityPreference{}
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
community := &identity.ProfileShowcaseCommunityPreference{}
|
community := &identity.ProfileShowcaseCommunityPreference{}
|
||||||
|
|
||||||
@ -120,9 +118,33 @@ func (db sqlitePersistence) getProfileShowcaseCommunitiesPreferences(tx *sql.Tx)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
communities = append(communities, community)
|
result = append(result, community)
|
||||||
}
|
}
|
||||||
return communities, nil
|
|
||||||
|
err = rows.Err()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db sqlitePersistence) getProfileShowcaseCommunitiesPreferences(tx *sql.Tx) ([]*identity.ProfileShowcaseCommunityPreference, error) {
|
||||||
|
rows, err := tx.Query(selectProfileShowcaseCommunityPreferenceQuery)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.processProfileShowcaseCommunityPreferences(rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db sqlitePersistence) GetProfileShowcaseCommunityPreference(communityID string) (*identity.ProfileShowcaseCommunityPreference, error) {
|
||||||
|
rows, err := db.db.Query(selectSpecifiedShowcaseCommunityPreferenceQuery, communityID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
communities, err := db.processProfileShowcaseCommunityPreferences(rows)
|
||||||
|
if len(communities) > 0 {
|
||||||
|
return communities[0], err
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db sqlitePersistence) DeleteProfileShowcaseCommunityPreference(communityID string) (bool, error) {
|
func (db sqlitePersistence) DeleteProfileShowcaseCommunityPreference(communityID string) (bool, error) {
|
||||||
|
@ -106,6 +106,7 @@ const (
|
|||||||
ApplicationMetadataMessage_COMMUNITY_PUBLIC_STORENODES_INFO ApplicationMetadataMessage_Type = 83
|
ApplicationMetadataMessage_COMMUNITY_PUBLIC_STORENODES_INFO ApplicationMetadataMessage_Type = 83
|
||||||
ApplicationMetadataMessage_COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST ApplicationMetadataMessage_Type = 84
|
ApplicationMetadataMessage_COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST ApplicationMetadataMessage_Type = 84
|
||||||
ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES ApplicationMetadataMessage_Type = 85
|
ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES ApplicationMetadataMessage_Type = 85
|
||||||
|
ApplicationMetadataMessage_COMMUNITY_UPDATE_GRANT ApplicationMetadataMessage_Type = 86
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for ApplicationMetadataMessage_Type.
|
// Enum value maps for ApplicationMetadataMessage_Type.
|
||||||
@ -192,6 +193,7 @@ var (
|
|||||||
83: "COMMUNITY_PUBLIC_STORENODES_INFO",
|
83: "COMMUNITY_PUBLIC_STORENODES_INFO",
|
||||||
84: "COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST",
|
84: "COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST",
|
||||||
85: "DELETE_COMMUNITY_MEMBER_MESSAGES",
|
85: "DELETE_COMMUNITY_MEMBER_MESSAGES",
|
||||||
|
86: "COMMUNITY_UPDATE_GRANT",
|
||||||
}
|
}
|
||||||
ApplicationMetadataMessage_Type_value = map[string]int32{
|
ApplicationMetadataMessage_Type_value = map[string]int32{
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
@ -275,6 +277,7 @@ var (
|
|||||||
"COMMUNITY_PUBLIC_STORENODES_INFO": 83,
|
"COMMUNITY_PUBLIC_STORENODES_INFO": 83,
|
||||||
"COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST": 84,
|
"COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST": 84,
|
||||||
"DELETE_COMMUNITY_MEMBER_MESSAGES": 85,
|
"DELETE_COMMUNITY_MEMBER_MESSAGES": 85,
|
||||||
|
"COMMUNITY_UPDATE_GRANT": 86,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -376,7 +379,7 @@ var File_application_metadata_message_proto protoreflect.FileDescriptor
|
|||||||
var file_application_metadata_message_proto_rawDesc = []byte{
|
var file_application_metadata_message_proto_rawDesc = []byte{
|
||||||
0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65,
|
0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65,
|
||||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70,
|
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70,
|
||||||
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0xc3,
|
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0xdf,
|
||||||
0x15, 0x0a, 0x1a, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65,
|
0x15, 0x0a, 0x1a, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65,
|
||||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a,
|
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a,
|
||||||
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||||
@ -386,7 +389,7 @@ var file_application_metadata_message_proto_rawDesc = []byte{
|
|||||||
0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
|
0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
|
||||||
0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
||||||
0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
|
0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
|
||||||
0x74, 0x79, 0x70, 0x65, 0x22, 0xad, 0x14, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
|
0x74, 0x79, 0x70, 0x65, 0x22, 0xc9, 0x14, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
|
||||||
0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x48,
|
0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x48,
|
||||||
0x41, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e,
|
0x41, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e,
|
||||||
0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02,
|
0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02,
|
||||||
@ -538,19 +541,21 @@ var file_application_metadata_message_proto_rawDesc = []byte{
|
|||||||
0x45, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x52, 0x45,
|
0x45, 0x5f, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x52, 0x45,
|
||||||
0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x54, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x45, 0x4c, 0x45, 0x54,
|
0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x54, 0x12, 0x24, 0x0a, 0x20, 0x44, 0x45, 0x4c, 0x45, 0x54,
|
||||||
0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x4d, 0x45, 0x4d, 0x42,
|
0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x4d, 0x45, 0x4d, 0x42,
|
||||||
0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x53, 0x10, 0x55, 0x22, 0x04, 0x08,
|
0x45, 0x52, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x53, 0x10, 0x55, 0x12, 0x1a, 0x0a,
|
||||||
0x0e, 0x10, 0x0e, 0x22, 0x04, 0x08, 0x41, 0x10, 0x41, 0x22, 0x04, 0x08, 0x42, 0x10, 0x42, 0x22,
|
0x16, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54,
|
||||||
0x04, 0x08, 0x47, 0x10, 0x47, 0x2a, 0x1d, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x53, 0x54,
|
0x45, 0x5f, 0x47, 0x52, 0x41, 0x4e, 0x54, 0x10, 0x56, 0x22, 0x04, 0x08, 0x0e, 0x10, 0x0e, 0x22,
|
||||||
0x41, 0x4c, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f,
|
0x04, 0x08, 0x41, 0x10, 0x41, 0x22, 0x04, 0x08, 0x42, 0x10, 0x42, 0x22, 0x04, 0x08, 0x47, 0x10,
|
||||||
0x43, 0x48, 0x41, 0x54, 0x2a, 0x22, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
|
0x47, 0x2a, 0x1d, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x41,
|
||||||
|
0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x43, 0x48, 0x41, 0x54,
|
||||||
|
0x2a, 0x22, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f,
|
||||||
|
0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54,
|
||||||
|
0x49, 0x4f, 0x4e, 0x53, 0x2a, 0x27, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
|
||||||
0x49, 0x54, 0x59, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46,
|
0x49, 0x54, 0x59, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46,
|
||||||
0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x2a, 0x27, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41,
|
0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x2a, 0x21, 0x43,
|
||||||
0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e,
|
0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x53, 0x5f,
|
||||||
0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54,
|
0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44,
|
||||||
0x45, 0x2a, 0x21, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x45, 0x56, 0x45,
|
0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62,
|
||||||
0x4e, 0x54, 0x53, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x45, 0x4a, 0x45,
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
0x43, 0x54, 0x45, 0x44, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
|
||||||
0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -104,5 +104,6 @@ message ApplicationMetadataMessage {
|
|||||||
COMMUNITY_PUBLIC_STORENODES_INFO = 83;
|
COMMUNITY_PUBLIC_STORENODES_INFO = 83;
|
||||||
COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST = 84;
|
COMMUNITY_REEVALUATE_PERMISSIONS_REQUEST = 84;
|
||||||
DELETE_COMMUNITY_MEMBER_MESSAGES = 85;
|
DELETE_COMMUNITY_MEMBER_MESSAGES = 85;
|
||||||
|
COMMUNITY_UPDATE_GRANT = 86;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ message Grant {
|
|||||||
bytes member_id = 2;
|
bytes member_id = 2;
|
||||||
string chat_id = 3;
|
string chat_id = 3;
|
||||||
uint64 clock = 4;
|
uint64 clock = 4;
|
||||||
|
uint64 expires = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CommunityMember {
|
message CommunityMember {
|
||||||
@ -271,3 +272,9 @@ message DeleteCommunityMemberMessages {
|
|||||||
string member_id = 3;
|
string member_id = 3;
|
||||||
repeated DeleteCommunityMemberMessage messages = 4;
|
repeated DeleteCommunityMemberMessage messages = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message CommunityUpdateGrant {
|
||||||
|
uint64 timestamp = 1;
|
||||||
|
bytes community_id = 2;
|
||||||
|
map<uint32, bytes> grants = 3;
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user