diff --git a/protocol/communities/community.go b/protocol/communities/community.go index 64a759e9c..70ea8ca4c 100644 --- a/protocol/communities/community.go +++ b/protocol/communities/community.go @@ -683,6 +683,15 @@ func (o *Community) Members() map[string]*protobuf.CommunityMember { return nil } +func (o *Community) UpdateMemberLastUpdateClock(publicKey string, clock uint64) { + o.mutex.Lock() + defer o.mutex.Unlock() + + if member, exists := o.config.CommunityDescription.Members[publicKey]; exists { + member.LastUpdateClock = clock + } +} + func (o *Community) MembersCount() int { if o != nil && o.config != nil && @@ -1434,26 +1443,21 @@ func (o *Community) ValidateRequestToJoin(signer *ecdsa.PublicKey, request *prot } // ValidateRequestToJoin validates a request, checks that the right permissions are applied -func (o *Community) ValidateEditSharedAddresses(signer *ecdsa.PublicKey, request *protobuf.CommunityEditSharedAddresses) error { +func (o *Community) ValidateEditSharedAddresses(signer string, request *protobuf.CommunityEditSharedAddresses) error { o.mutex.Lock() defer o.mutex.Unlock() - // If we are not owner, fuggetaboutit - if !o.IsControlNode() { - return ErrNotOwner - } - if len(request.RevealedAccounts) == 0 { return errors.New("no addresses were shared") } - member, exists := o.config.CommunityDescription.Members[common.PubkeyToHex(signer)] + member, exists := o.config.CommunityDescription.Members[signer] if !exists { return errors.New("signer is not a community member") } if request.Clock < member.LastUpdateClock { - return errors.New("edit request is older than the last one we have. Ignore") + return ErrEditSharedAddressesRequestOutdated } return nil @@ -1476,6 +1480,17 @@ func (o *Community) IsAdmin() bool { return o.IsMemberAdmin(o.MemberIdentity()) } +func (o *Community) GetTokenMasterMembers() []*ecdsa.PublicKey { + tokenMasterMembers := make([]*ecdsa.PublicKey, 0) + members := o.GetMemberPubkeys() + for _, member := range members { + if o.IsMemberTokenMaster(member) { + tokenMasterMembers = append(tokenMasterMembers, member) + } + } + return tokenMasterMembers +} + func (o *Community) GetPrivilegedMembers() []*ecdsa.PublicKey { privilegedMembers := make([]*ecdsa.PublicKey, 0) members := o.GetMemberPubkeys() diff --git a/protocol/communities/communnity_privileged_member_sync_msg.go b/protocol/communities/communnity_privileged_member_sync_msg.go index 5b4b38687..58e7cff82 100644 --- a/protocol/communities/communnity_privileged_member_sync_msg.go +++ b/protocol/communities/communnity_privileged_member_sync_msg.go @@ -2,6 +2,10 @@ package communities import ( "crypto/ecdsa" + "database/sql" + "errors" + + "go.uber.org/zap" multiaccountscommon "github.com/status-im/status-go/multiaccounts/common" @@ -10,13 +14,16 @@ import ( "github.com/status-im/status-go/protocol/protobuf" ) +var ErrOutdatedSharedRequestToJoinClock = errors.New("outdated clock in shared request to join") +var ErrOutdatedSharedRequestToJoinState = errors.New("outdated state in shared request to join") + type CommunityPrivilegedMemberSyncMessage struct { CommunityPrivateKey *ecdsa.PrivateKey Receivers []*ecdsa.PublicKey CommunityPrivilegedUserSyncMessage *protobuf.CommunityPrivilegedUserSyncMessage } -func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, communityID types.HexBytes) ([]*RequestToJoin, error) { +func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) ([]*RequestToJoin, error) { var state RequestToJoinState if message.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN { state = RequestToJoinStateAccepted @@ -24,8 +31,14 @@ func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf state = RequestToJoinStateDeclined } + myPk := common.PubkeyToHex(&m.identity.PublicKey) + requestsToJoin := make([]*RequestToJoin, 0) for signer, requestToJoinProto := range message.RequestToJoin { + if signer == myPk { + continue + } + requestToJoin := &RequestToJoin{ PublicKey: signer, Clock: requestToJoinProto.Clock, @@ -37,18 +50,14 @@ func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf } requestToJoin.CalculateID() - if _, err := m.saveOrUpdateRequestToJoin(communityID, requestToJoin); err != nil { - return nil, err - } - - if err := m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoin.ID); err != nil { - return nil, err - } - - if requestToJoin.RevealedAccounts != nil && len(requestToJoin.RevealedAccounts) > 0 { - if err := m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts); err != nil { - return nil, err - } + err := m.processPrivilegedUserSharedRequestToJoin(community, requestToJoin) + if err != nil { + m.logger.Warn("error to handle shared request to join", + zap.String("communityID", community.IDString()), + zap.String("requestToJoinID", types.Bytes2Hex(requestToJoin.ID)), + zap.String("publicKey", requestToJoin.PublicKey), + zap.String("error", err.Error())) + continue } requestsToJoin = append(requestsToJoin, requestToJoin) @@ -57,29 +66,26 @@ func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf return requestsToJoin, nil } -func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *protobuf.CommunityPrivilegedUserSyncMessage, communityID types.HexBytes) ([]*RequestToJoin, error) { +func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) ([]*RequestToJoin, error) { nonAcceptedRequestsToJoin := []*RequestToJoin{} - myPk := common.PubkeyToHex(&m.identity.PublicKey) - // We received all requests to join from the control node. Remove all requests to join except our own - err := m.persistence.RemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey(myPk, communityID) - if err != nil { - return nil, err - } - for _, syncRequestToJoin := range message.SyncRequestsToJoin { + if syncRequestToJoin.PublicKey == myPk { + continue + } + requestToJoin := new(RequestToJoin) requestToJoin.InitFromSyncProtobuf(syncRequestToJoin) - if _, err := m.saveOrUpdateRequestToJoin(communityID, requestToJoin); err != nil { - return nil, err - } - - if requestToJoin.RevealedAccounts != nil && len(requestToJoin.RevealedAccounts) > 0 { - if err := m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts); err != nil { - return nil, err - } + err := m.processPrivilegedUserSharedRequestToJoin(community, requestToJoin) + if err != nil { + m.logger.Warn("error to handle shared request to join from sync all requests to join msg", + zap.String("communityID", community.IDString()), + zap.String("requestToJoinID", types.Bytes2Hex(requestToJoin.ID)), + zap.String("publicKey", requestToJoin.PublicKey), + zap.String("error", err.Error())) + continue } if requestToJoin.State != RequestToJoinStateAccepted { @@ -88,3 +94,78 @@ func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *prot } return nonAcceptedRequestsToJoin, nil } + +func (m *Manager) HandleEditSharedAddressesPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) error { + if !(community.IsTokenMaster() || community.IsOwner()) { + return ErrNotEnoughPermissions + } + + publicKey := message.SyncEditSharedAddresses.PublicKey + editSharedAddress := message.SyncEditSharedAddresses.EditSharedAddress + if err := community.ValidateEditSharedAddresses(publicKey, editSharedAddress); err != nil { + return err + } + + return m.handleCommunityEditSharedAddresses(publicKey, community.ID(), editSharedAddress.RevealedAccounts, message.Clock) +} + +func (m *Manager) processPrivilegedUserSharedRequestToJoin(community *Community, requestToJoin *RequestToJoin) error { + existingRequestToJoin, err := m.persistence.GetCommunityRequestToJoinWithRevealedAddresses(requestToJoin.PublicKey, community.ID()) + if err != nil && err != sql.ErrNoRows { + return err + } + + statusUpdate := existingRequestToJoin != nil && existingRequestToJoin.Clock == requestToJoin.Clock + + if existingRequestToJoin != nil && existingRequestToJoin.Clock > requestToJoin.Clock { + return ErrOutdatedSharedRequestToJoinClock + } + + revealedAccountsExists := requestToJoin.RevealedAccounts != nil && len(requestToJoin.RevealedAccounts) > 0 + + if member, memberExists := community.Members()[requestToJoin.PublicKey]; memberExists && member.LastUpdateClock > requestToJoin.Clock { + return ErrOutdatedSharedRequestToJoinClock + } + + if statusUpdate { + isCurrentStateAccepted := existingRequestToJoin.State == RequestToJoinStateAccepted + isNewRequestAccepted := requestToJoin.State == RequestToJoinStateAccepted + isNewAcceptedRequestWithoutAccounts := isNewRequestAccepted && !revealedAccountsExists + isCurrentStateDeclined := existingRequestToJoin.State == RequestToJoinStateDeclined + + if (isCurrentStateAccepted && (!isNewRequestAccepted || isNewAcceptedRequestWithoutAccounts)) || + (isCurrentStateDeclined && !isNewRequestAccepted) { + return ErrOutdatedSharedRequestToJoinState + } + + err = m.persistence.SetRequestToJoinState(requestToJoin.PublicKey, community.ID(), requestToJoin.State) + if err != nil { + return err + } + } else { + err = m.persistence.SaveRequestToJoin(requestToJoin) + if err != nil { + return err + } + } + + // If we are a token master or owner without private key and we received request to join without + // revealed accounts - there is a chance, that we lost our role and did't get + // CommunityDescription update. But it also can indicate, that we received an outdated + // request to join, when we were admins + // Decision - is not to delete existing revealed accounts + if (community.IsTokenMaster() || community.IsOwner()) && !revealedAccountsExists { + return nil + } + + err = m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoin.ID) + if err != nil { + return err + } + + if revealedAccountsExists { + return m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts) + } + + return nil +} diff --git a/protocol/communities/errors.go b/protocol/communities/errors.go index 4d6f9022d..69de9c191 100644 --- a/protocol/communities/errors.go +++ b/protocol/communities/errors.go @@ -50,3 +50,4 @@ var ErrNoFreeSpaceForHistoryArchives = errors.New("history archive: No free spac var ErrPermissionToJoinNotSatisfied = errors.New("permission to join not satisfied") var ErrBannedMemberNotFound = errors.New("banned member not found") var ErrGrantMemberPublicKeyIsDifferent = errors.New("grant member public key is different") +var ErrEditSharedAddressesRequestOutdated = errors.New("outdated edit shares addresses request") diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index db91b6b10..6386cdaaa 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -1514,7 +1514,7 @@ func (m *Manager) reevaluateCommunityMembersPermissions(communityID types.HexByt return err } - return m.shareRequestsToJoinWithNewPrivilegedMembers(community, newPrivilegedMembers) + return m.ShareRequestsToJoinWithPrivilegedMembers(community, newPrivilegedMembers) } func (m *Manager) DeleteCommunity(id types.HexBytes) error { @@ -2649,7 +2649,6 @@ func (m *Manager) DeletePendingRequestToJoin(request *RequestToJoin) error { return nil } -// UpdateClockInRequestToJoin method is used for testing func (m *Manager) UpdateClockInRequestToJoin(id types.HexBytes, clock uint64) error { return m.persistence.UpdateClockInRequestToJoin(id, clock) } @@ -2853,7 +2852,7 @@ func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, err newPrivilegedMember := make(map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) newPrivilegedMember[memberRole] = []*ecdsa.PublicKey{pk} - if err = m.shareRequestsToJoinWithNewPrivilegedMembers(community, newPrivilegedMember); err != nil { + if err = m.ShareRequestsToJoinWithPrivilegedMembers(community, newPrivilegedMember); err != nil { return nil, err } } @@ -3124,10 +3123,17 @@ func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, re return err } - if err := community.ValidateEditSharedAddresses(signer, request); err != nil { + if !community.IsControlNode() { + return ErrNotOwner + } + + publicKey := common.PubkeyToHex(signer) + + if err := community.ValidateEditSharedAddresses(publicKey, request); err != nil { return err } + community.UpdateMemberLastUpdateClock(publicKey, request.Clock) // verify if revealed addresses indeed belong to requester for _, revealedAccount := range request.RevealedAccounts { recoverParams := account.RecoverParams{ @@ -3145,18 +3151,7 @@ func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, re } } - requestToJoin := &RequestToJoin{ - PublicKey: common.PubkeyToHex(signer), - CommunityID: community.ID(), - RevealedAccounts: request.RevealedAccounts, - } - requestToJoin.CalculateID() - - err = m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoin.ID) - if err != nil { - return err - } - err = m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts) + err = m.handleCommunityEditSharedAddresses(publicKey, request.CommunityId, request.RevealedAccounts, request.Clock) if err != nil { return err } @@ -3170,9 +3165,39 @@ func (m *Manager) HandleCommunityEditSharedAddresses(signer *ecdsa.PublicKey, re m.publish(&Subscription{Community: community}) } + subscriptionMsg := &CommunityPrivilegedMemberSyncMessage{ + CommunityPrivateKey: community.PrivateKey(), + Receivers: community.GetTokenMasterMembers(), + CommunityPrivilegedUserSyncMessage: &protobuf.CommunityPrivilegedUserSyncMessage{ + Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES, + CommunityId: community.ID(), + SyncEditSharedAddresses: &protobuf.SyncCommunityEditSharedAddresses{ + PublicKey: common.PubkeyToHex(signer), + EditSharedAddress: request, + }, + }, + } + + m.publish(&Subscription{CommunityPrivilegedMemberSyncMessage: subscriptionMsg}) + return nil } +func (m *Manager) handleCommunityEditSharedAddresses(publicKey string, communityID types.HexBytes, revealedAccounts []*protobuf.RevealedAccount, clock uint64) error { + requestToJoinID := CalculateRequestID(publicKey, communityID) + err := m.UpdateClockInRequestToJoin(requestToJoinID, clock) + if err != nil { + return err + } + + err = m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoinID) + if err != nil { + return err + } + + return m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoinID, revealedAccounts) +} + func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, requirementsChainIDs map[uint64]bool) []uint64 { revealedAccountsChainIDs := make([]uint64, 0) @@ -4588,6 +4613,11 @@ func (m *Manager) ValidateCommunityPrivilegedUserSyncMessage(message *protobuf.C if message.SyncRequestsToJoin == nil || len(message.SyncRequestsToJoin) == 0 { return errors.New("invalid sync requests to join in CommunityPrivilegedUserSyncMessage message") } + case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES: + if message.SyncEditSharedAddresses == nil || len(message.CommunityId) == 0 || + len(message.SyncEditSharedAddresses.PublicKey) == 0 || message.SyncEditSharedAddresses.EditSharedAddress == nil { + return errors.New("invalid edit shared adresses in CommunityPrivilegedUserSyncMessage message") + } } return nil @@ -4770,7 +4800,7 @@ func (m *Manager) handleCommunityEvents(community *Community) error { return nil } -func (m *Manager) shareRequestsToJoinWithNewPrivilegedMembers(community *Community, newPrivilegedMembers map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) error { +func (m *Manager) ShareRequestsToJoinWithPrivilegedMembers(community *Community, privilegedMembers map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) error { requestsToJoin, err := m.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) if err != nil { return err @@ -4801,7 +4831,7 @@ func (m *Manager) shareRequestsToJoinWithNewPrivilegedMembers(community *Communi CommunityPrivateKey: community.PrivateKey(), } - for role, members := range newPrivilegedMembers { + for role, members := range privilegedMembers { if len(members) == 0 { continue } diff --git a/protocol/communities/persistence.go b/protocol/communities/persistence.go index 576f419e8..2b0e0b86c 100644 --- a/protocol/communities/persistence.go +++ b/protocol/communities/persistence.go @@ -1764,15 +1764,6 @@ func (p *Persistence) AllNonApprovedCommunitiesRequestsToJoin() ([]*RequestToJoi return nonApprovedRequestsToJoin, nil } -func (p *Persistence) RemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey(pk string, communityID []byte) error { - _, err := p.db.Exec(` - DELETE FROM communities_requests_to_join_revealed_addresses - WHERE request_id IN (SELECT id FROM communities_requests_to_join WHERE community_id = ? AND public_key != ?); - DELETE FROM communities_requests_to_join - WHERE community_id = ? AND public_key != ?;`, communityID, pk, communityID, pk) - return err -} - func (p *Persistence) SaveCommunityShard(communityID types.HexBytes, shard *shard.Shard, clock uint64) error { var cluster, index *uint16 diff --git a/protocol/communities/persistence_test.go b/protocol/communities/persistence_test.go index edf8bf4c2..b24e50a52 100644 --- a/protocol/communities/persistence_test.go +++ b/protocol/communities/persistence_test.go @@ -766,111 +766,6 @@ func (s *PersistenceSuite) TestAllNonApprovedCommunitiesRequestsToJoin() { s.Require().Len(result, 6) // all except RequestToJoinStateAccepted } -func (s *PersistenceSuite) TestRemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey() { - myIdentity, err := crypto.GenerateKey() - s.Require().NoError(err, "crypto.GenerateKey shouldn't give any error") - - myPk := common.PubkeyToHex(&myIdentity.PublicKey) - - clock := uint64(time.Now().Unix()) - - // add a new community - community := s.makeNewCommunity(myIdentity) - err = s.db.SaveCommunity(community) - s.Require().NoError(err) - - // check on empty db - err = s.db.RemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey(myPk, community.ID()) - s.Require().NoError(err) - - // add requests to join to the community - allStates := []RequestToJoinState{ - RequestToJoinStatePending, - RequestToJoinStateDeclined, - RequestToJoinStateAccepted, - RequestToJoinStateCanceled, - RequestToJoinStateAcceptedPending, - RequestToJoinStateDeclinedPending, - RequestToJoinStateAwaitingAddresses, - } - - allRequestsToJoinIDs := [][]byte{} - - for i := range allStates { - identity, err := crypto.GenerateKey() - s.Require().NoError(err) - - revealedAccounts := []*protobuf.RevealedAccount{} - for j := 0; j < i; j++ { - acc := &protobuf.RevealedAccount{ - Address: "testAddr", - ChainIds: []uint64{123}, - IsAirdropAddress: true, - Signature: []byte{}, - } - revealedAccounts = append(revealedAccounts, acc) - } - - rtj := &RequestToJoin{ - ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, byte(i)}, - PublicKey: common.PubkeyToHex(&identity.PublicKey), - Clock: clock, - CommunityID: community.ID(), - State: allStates[i], - RevealedAccounts: revealedAccounts, - } - - allRequestsToJoinIDs = append(allRequestsToJoinIDs, rtj.ID) - - err = s.db.SaveRequestToJoin(rtj) - s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") - err = s.db.SaveRequestToJoinRevealedAddresses(rtj.ID, rtj.RevealedAccounts) - s.Require().NoError(err) - } - - err = s.db.RemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey(myPk, community.ID()) - s.Require().NoError(err) - - requests, err := s.db.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) - s.Require().NoError(err) - s.Require().Len(requests, 0) - - for _, rtjID := range allRequestsToJoinIDs { - accounts, err := s.db.GetRequestToJoinRevealedAddresses(rtjID) - s.Require().NoError(err) - s.Require().Len(accounts, 0) - } - - myRtj := &RequestToJoin{ - ID: types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}, - PublicKey: myPk, - Clock: clock, - CommunityID: community.ID(), - State: RequestToJoinStateAccepted, - RevealedAccounts: []*protobuf.RevealedAccount{ - { - Address: "testAddr", - ChainIds: []uint64{123}, - IsAirdropAddress: true, - Signature: []byte{}, - }, - }, - } - - err = s.db.SaveRequestToJoin(myRtj) - s.Require().NoError(err, "SaveRequestToJoin shouldn't give any error") - err = s.db.SaveRequestToJoinRevealedAddresses(myRtj.ID, myRtj.RevealedAccounts) - s.Require().NoError(err) - - err = s.db.RemoveAllCommunityRequestsToJoinWithRevealedAddressesExceptPublicKey(myPk, community.ID()) - s.Require().NoError(err) - - requests, err = s.db.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) - s.Require().NoError(err) - s.Require().Len(requests, 1) - s.Require().Len(requests[0].RevealedAccounts, 1) -} - func (s *PersistenceSuite) TestSaveShardInfo() { communityID := types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8} clock := uint64(1) diff --git a/protocol/communities_messenger_shared_member_address_test.go b/protocol/communities_messenger_shared_member_address_test.go new file mode 100644 index 000000000..c74376654 --- /dev/null +++ b/protocol/communities_messenger_shared_member_address_test.go @@ -0,0 +1,1243 @@ +package protocol + +import ( + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/suite" + "go.uber.org/zap" + + gethcommon "github.com/ethereum/go-ethereum/common" + hexutil "github.com/ethereum/go-ethereum/common/hexutil" + gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" + "github.com/status-im/status-go/eth-node/crypto" + "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/communities" + "github.com/status-im/status-go/protocol/protobuf" + "github.com/status-im/status-go/protocol/requests" + "github.com/status-im/status-go/protocol/tt" +) + +func TestMessengerCommunitiesSharedMemberAddressSuite(t *testing.T) { + suite.Run(t, new(MessengerCommunitiesSharedMemberAddressSuite)) +} + +type MessengerCommunitiesSharedMemberAddressSuite struct { + suite.Suite + owner *Messenger + bob *Messenger + alice *Messenger + + ownerWaku types.Waku + bobWaku types.Waku + aliceWaku types.Waku + + logger *zap.Logger + + mockedBalances map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big // chainID, account, token, balance + collectiblesServiceMock *CollectiblesServiceMock +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) SetupTest() { + // Initialize with nil to avoid panics in TearDownTest + s.owner = nil + s.bob = nil + s.alice = nil + s.ownerWaku = nil + s.bobWaku = nil + s.aliceWaku = nil + + communities.SetValidateInterval(300 * time.Millisecond) + s.collectiblesServiceMock = &CollectiblesServiceMock{} + + s.resetMockedBalances() + + s.logger = tt.MustCreateTestLogger() + + wakuNodes := CreateWakuV2Network(&s.Suite, s.logger, false, []string{"owner", "bob", "alice"}) + + s.ownerWaku = wakuNodes[0] + s.owner = s.newMessenger(ownerPassword, []string{ownerAddress}, s.ownerWaku, "owner", []Option{}) + + s.bobWaku = wakuNodes[1] + s.bob = s.newMessenger(bobPassword, []string{bobAddress}, s.bobWaku, "bob", []Option{}) + s.bob.EnableBackedupMessagesProcessing() + + s.aliceWaku = wakuNodes[2] + s.alice = s.newMessenger(alicePassword, []string{aliceAddress1, aliceAddress2}, s.aliceWaku, "alice", []Option{}) + + _, err := s.owner.Start() + s.Require().NoError(err) + _, err = s.bob.Start() + s.Require().NoError(err) + _, err = s.alice.Start() + s.Require().NoError(err) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TearDownTest() { + TearDownMessenger(&s.Suite, s.owner) + TearDownMessenger(&s.Suite, s.bob) + TearDownMessenger(&s.Suite, s.alice) + if s.ownerWaku != nil { + s.Require().NoError(gethbridge.GetGethWakuV2From(s.ownerWaku).Stop()) + } + if s.bobWaku != nil { + s.Require().NoError(gethbridge.GetGethWakuV2From(s.bobWaku).Stop()) + } + if s.aliceWaku != nil { + s.Require().NoError(gethbridge.GetGethWakuV2From(s.aliceWaku).Stop()) + } + _ = s.logger.Sync() +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) newMessenger(password string, walletAddresses []string, waku types.Waku, name string, extraOptions []Option) *Messenger { + communityManagerOptions := []communities.ManagerOption{ + communities.WithAllowForcingCommunityMembersReevaluation(true), + } + extraOptions = append(extraOptions, WithCommunityManagerOptions(communityManagerOptions)) + + return newTestCommunitiesMessenger(&s.Suite, waku, testCommunitiesMessengerConfig{ + testMessengerConfig: testMessengerConfig{ + logger: s.logger.Named(name), + extraOptions: extraOptions, + }, + password: password, + walletAddresses: walletAddresses, + mockedBalances: &s.mockedBalances, + collectiblesService: s.collectiblesServiceMock, + }) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) joinCommunity(community *communities.Community, user *Messenger, password string, addresses []string) { + s.joinCommunityWithAirdropAddress(community, user, password, addresses, "") +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) joinCommunityWithAirdropAddress(community *communities.Community, user *Messenger, password string, addresses []string, airdropAddress string) { + passwdHash := types.EncodeHex(crypto.Keccak256([]byte(password))) + if airdropAddress == "" && len(addresses) > 0 { + airdropAddress = addresses[0] + } + + request := &requests.RequestToJoinCommunity{CommunityID: community.ID(), AddressesToReveal: addresses, AirdropAddress: airdropAddress} + joinCommunity(&s.Suite, community, s.owner, user, request, passwdHash) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) checkRevealedAccounts(communityID types.HexBytes, user *Messenger, expectedAccounts []*protobuf.RevealedAccount) { + revealedAccounts, err := user.communitiesManager.GetRevealedAddresses(communityID, s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Equal(revealedAccounts, expectedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) makeAddressSatisfyTheCriteria(chainID uint64, address string, criteria *protobuf.TokenCriteria) { + walletAddress := gethcommon.HexToAddress(address) + contractAddress := gethcommon.HexToAddress(criteria.ContractAddresses[chainID]) + balance, ok := new(big.Int).SetString(criteria.AmountInWei, 10) + s.Require().True(ok) + + s.mockedBalances[chainID][walletAddress][contractAddress] = (*hexutil.Big)(balance) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) resetMockedBalances() { + s.mockedBalances = make(map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big) + s.mockedBalances[testChainID1] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big) + s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress1)] = make(map[gethcommon.Address]*hexutil.Big) + s.mockedBalances[testChainID1][gethcommon.HexToAddress(aliceAddress2)] = make(map[gethcommon.Address]*hexutil.Big) + s.mockedBalances[testChainID1][gethcommon.HexToAddress(bobAddress)] = make(map[gethcommon.Address]*hexutil.Big) +} + +func createTokenMasterTokenCriteria() *protobuf.TokenCriteria { + return &protobuf.TokenCriteria{ + ContractAddresses: map[uint64]string{testChainID1: "0x123"}, + Type: protobuf.CommunityTokenType_ERC20, + Symbol: "STT", + Name: "Status Test Token", + AmountInWei: "10000000000000000000", + Decimals: 18, + } +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) createEditSharedAddressesRequest(communityID types.HexBytes) *requests.EditSharedAddresses { + request := &requests.EditSharedAddresses{CommunityID: communityID, AddressesToReveal: []string{aliceAddress2}, AirdropAddress: aliceAddress2} + + signingParams, err := s.alice.GenerateJoiningCommunityRequestsForSigning(common.PubkeyToHex(&s.alice.identity.PublicKey), communityID, request.AddressesToReveal) + s.Require().NoError(err) + + passwdHash := types.EncodeHex(crypto.Keccak256([]byte(alicePassword))) + for i := range signingParams { + signingParams[i].Password = passwdHash + } + signatures, err := s.alice.SignData(signingParams) + s.Require().NoError(err) + + updateAddresses := len(request.AddressesToReveal) == 0 + if updateAddresses { + request.AddressesToReveal = make([]string, len(signingParams)) + } + for i := range signingParams { + request.AddressesToReveal[i] = signingParams[i].Address + request.Signatures = append(request.Signatures, types.FromHex(signatures[i])) + } + if updateAddresses { + request.AirdropAddress = request.AddressesToReveal[0] + } + + return request +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestJoinedCommunityMembersSharedAddress() { + community, _ := createCommunity(&s.Suite, s.owner) + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + + s.joinCommunity(community, s.alice, alicePassword, []string{}) + s.joinCommunity(community, s.bob, bobPassword, []string{}) + + community, err := s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + + s.Require().Equal(3, community.MembersCount()) + + // Check owner's DB for revealed accounts + for pubKey := range community.Members() { + if pubKey != common.PubkeyToHex(&s.owner.identity.PublicKey) { + revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), pubKey) + s.Require().NoError(err) + switch pubKey { + case common.PubkeyToHex(&s.alice.identity.PublicKey): + s.Require().Len(revealedAccounts, 2) + s.Require().Equal(revealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(revealedAccounts[1].Address, aliceAddress2) + s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) + case common.PubkeyToHex(&s.bob.identity.PublicKey): + s.Require().Len(revealedAccounts, 1) + s.Require().Equal(revealedAccounts[0].Address, bobAddress) + s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) + default: + s.Require().Fail("pubKey does not match expected keys") + } + } + } + + // Check Bob's DB for revealed accounts + revealedAccountsInBobsDB, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), common.PubkeyToHex(&s.bob.identity.PublicKey)) + s.Require().NoError(err) + s.Require().Len(revealedAccountsInBobsDB, 1) + s.Require().Equal(revealedAccountsInBobsDB[0].Address, bobAddress) + s.Require().Equal(true, revealedAccountsInBobsDB[0].IsAirdropAddress) + + // Check Alices's DB for revealed accounts + revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) + s.Require().NoError(err) + s.Require().Len(revealedAccountsInAlicesDB, 2) + s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress1) + s.Require().Equal(revealedAccountsInAlicesDB[1].Address, aliceAddress2) + s.Require().Equal(true, revealedAccountsInAlicesDB[0].IsAirdropAddress) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestJoinedCommunityMembersSelectedSharedAddress() { + community, _ := createCommunity(&s.Suite, s.owner) + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress2}) + + community, err := s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + + s.Require().Equal(2, community.MembersCount()) + + alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) + + // Check Alice's DB for revealed accounts + revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(revealedAccountsInAlicesDB, 1) + s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress2) + s.Require().Equal(true, revealedAccountsInAlicesDB[0].IsAirdropAddress) + + // Check owner's DB for revealed accounts + s.checkRevealedAccounts(community.ID(), s.owner, revealedAccountsInAlicesDB) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestJoinedCommunityMembersMultipleSelectedSharedAddresses() { + community, _ := createCommunity(&s.Suite, s.owner) + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + + s.joinCommunityWithAirdropAddress(community, s.alice, alicePassword, []string{aliceAddress1, aliceAddress2}, aliceAddress2) + + community, err := s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + + s.Require().Equal(2, community.MembersCount()) + + alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) + + // Check Alice's DB for revealed accounts + revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(revealedAccountsInAlicesDB, 2) + s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress1) + s.Require().Equal(revealedAccountsInAlicesDB[1].Address, aliceAddress2) + s.Require().Equal(true, revealedAccountsInAlicesDB[1].IsAirdropAddress) + + // Check owner's DB for revealed accounts + s.checkRevealedAccounts(community.ID(), s.owner, revealedAccountsInAlicesDB) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestEditSharedAddresses() { + community, _ := createCommunity(&s.Suite, s.owner) + alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + community, err := s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + s.Require().Equal(2, community.MembersCount()) + + aliceExpectedRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(aliceExpectedRevealedAccounts, 1) + s.Require().Equal(aliceExpectedRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, aliceExpectedRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.owner, aliceExpectedRevealedAccounts) + + request := s.createEditSharedAddressesRequest(community.ID()) + + response, err := s.alice.EditSharedAddressesForCommunity(request) + s.Require().NoError(err) + s.Require().NotNil(response) + + aliceExpectedRevealedAccounts, err = s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(aliceExpectedRevealedAccounts, 1) + s.Require().Equal(aliceExpectedRevealedAccounts[0].Address, aliceAddress2) + s.Require().Equal(true, aliceExpectedRevealedAccounts[0].IsAirdropAddress) + + // check that owner received revealed address + _, err = WaitOnMessengerResponse(s.owner, func(r *MessengerResponse) bool { + revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(revealedAccounts, 1) + return revealedAccounts[0].Address == aliceAddress2 + + }, "owned did not receive alice shared address") + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.owner, aliceExpectedRevealedAccounts) + + // check that we filter out outdated edit shared addresses events + community, err = s.owner.GetCommunityByID(community.ID()) + s.Require().NoError(err) + + aliceClock := community.Description().Members[s.alice.IdentityPublicKeyString()].LastUpdateClock + s.Require().Greater(aliceClock, uint64(1)) + + editMsg := &protobuf.CommunityEditSharedAddresses{ + Clock: aliceClock - 1, + CommunityId: community.ID(), + RevealedAccounts: aliceExpectedRevealedAccounts, + } + + state := &ReceivedMessageState{ + CurrentMessageState: &CurrentMessageState{ + PublicKey: s.alice.IdentityPublicKey(), + }, + } + + err = s.owner.HandleCommunityEditSharedAddresses(state, editMsg, nil) + s.Require().Error(err, communities.ErrEditSharedAddressesRequestOutdated) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestTokenMasterReceivesEditedSharedAddresses() { + community, _ := createCommunity(&s.Suite, s.owner) + + alicePubkey := s.alice.IdentityPublicKeyString() + + tokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + // check bob has TM role + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community) + + aliceRevealedAccounts, err := s.bob.GetRevealedAccounts(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(aliceRevealedAccounts, 1) + + request := s.createEditSharedAddressesRequest(community.ID()) + + response, err := s.alice.EditSharedAddressesForCommunity(request) + s.Require().NoError(err) + s.Require().NotNil(response) + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress2) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + // check that owner received revealed address + _, err = WaitOnMessengerResponse(s.owner, func(r *MessengerResponse) bool { + revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(revealedAccounts, 1) + return revealedAccounts[0].Address == aliceAddress2 + + }, "owned did not receive alice shared address") + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.owner, expectedAliceRevealedAccounts) + + // check that bob as a token master received revealed address + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + revealedAccounts, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(revealedAccounts, 1) + return revealedAccounts[0].Address == aliceAddress2 + }, "user not accepted") + s.Require().NoError(err) + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestSharedAddressesReturnsRevealedAccount() { + community, _ := createCommunity(&s.Suite, s.owner) + + permissionRequest := requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL, + TokenCriteria: []*protobuf.TokenCriteria{ + &protobuf.TokenCriteria{ + Type: protobuf.CommunityTokenType_ERC20, + ContractAddresses: map[uint64]string{testChainID1: "0x123"}, + Symbol: "TEST", + AmountInWei: "100000000000000000000", + Decimals: uint64(18), + }, + }, + } + + response, err := s.owner.CreateCommunityTokenPermission(&permissionRequest) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + + s.joinCommunity(community, s.alice, alicePassword, []string{}) + + revealedAccounts, err := s.alice.GetRevealedAccounts(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) + s.Require().NoError(err) + + revealedAddressesMap := make(map[string]struct{}, len(revealedAccounts)) + for _, acc := range revealedAccounts { + revealedAddressesMap[acc.Address] = struct{}{} + } + + s.Require().Len(revealedAddressesMap, 2) + s.Require().Contains(revealedAddressesMap, aliceAddress1) + s.Require().Contains(revealedAddressesMap, aliceAddress2) + + sharedAddresses, err := s.alice.getSharedAddresses(community.ID(), []string{}) + s.Require().NoError(err) + s.Require().Len(sharedAddresses, 2) + + sharedAddressesMap := make(map[string]struct{}, len(sharedAddresses)) + for _, acc := range sharedAddresses { + sharedAddressesMap[acc.String()] = struct{}{} + } + + s.Require().Len(sharedAddressesMap, 2) + s.Require().Contains(sharedAddressesMap, aliceAddress1) + s.Require().Contains(sharedAddressesMap, aliceAddress2) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestResendSharedAddressesOnBackupRestore() { + community, _ := createCommunity(&s.Suite, s.owner) + + // bob joins the community + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + currentBobSharedAddresses, err := s.bob.GetRevealedAccounts(community.ID(), s.bob.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(currentBobSharedAddresses, 1) + + requestID := communities.CalculateRequestID(s.bob.IdentityPublicKeyString(), community.ID()) + err = s.bob.communitiesManager.RemoveRequestToJoinRevealedAddresses(requestID) + s.Require().NoError(err) + + emptySharedAddresses, err := s.bob.GetRevealedAccounts(community.ID(), s.bob.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(emptySharedAddresses, 0) + + // Simulate backup creation and handling backup message + // As a result, bob sends request to resend encryption keys to the owner + clock, _ := s.bob.getLastClockWithRelatedChat() + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + backupMessage, err := s.bob.backupCommunity(community, clock) + s.Require().NoError(err) + + err = s.bob.HandleBackup(s.bob.buildMessageState(), backupMessage, nil) + s.Require().NoError(err) + + // Owner will receive the request for addresses and send them back to Bob + response, err := WaitOnMessengerResponse( + s.bob, + func(r *MessengerResponse) bool { + _, _ = s.owner.RetrieveAll() + return len(r.requestsToJoinCommunity) > 0 + }, + "request to join not received", + ) + s.Require().NoError(err) + + requestToJoin, ok := response.requestsToJoinCommunity[requestID.String()] + s.Require().Equal(true, ok) + s.Require().Equal(currentBobSharedAddresses, requestToJoin.RevealedAccounts) + + currentBobSharedAddresses, err = s.bob.GetRevealedAccounts(community.ID(), s.bob.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(currentBobSharedAddresses, 1) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestTokenMasterReceivesMembersSharedAddressesOnBackupRestore() { + community, _ := createCommunity(&s.Suite, s.owner) + + tokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + expectedAliceRevealedAccounts, err := s.alice.GetRevealedAccounts(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + // check bob has TM role + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) + + // remove alice revealed addresses + aliceRequestID := communities.CalculateRequestID(s.alice.IdentityPublicKeyString(), community.ID()) + err = s.bob.communitiesManager.RemoveRequestToJoinRevealedAddresses(aliceRequestID) + s.Require().NoError(err) + + emptySharedAddresses, err := s.bob.GetRevealedAccounts(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(emptySharedAddresses, 0) + s.Require().NotEqual(emptySharedAddresses, expectedAliceRevealedAccounts) + + // Simulate backup creation and handling backup message + // As a result, bob sends request to resend encryption keys to the owner + clock, _ := s.bob.getLastClockWithRelatedChat() + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + backupMessage, err := s.bob.backupCommunity(community, clock) + s.Require().NoError(err) + + err = s.bob.HandleBackup(s.bob.buildMessageState(), backupMessage, nil) + s.Require().NoError(err) + + // Owner will receive the request for addresses and send requests to join with revealed + // addresses to token master + _, err = WaitOnMessengerResponse( + s.bob, + func(r *MessengerResponse) bool { + _, _ = s.owner.RetrieveAll() + if len(r.requestsToJoinCommunity) == 0 { + return false + } + + for _, requestToJoin := range r.requestsToJoinCommunity { + if requestToJoin.PublicKey == s.alice.IdentityPublicKeyString() { + return true + } + } + + return false + }, + "alice request to join with revealed addresses not received", + ) + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestTokenMasterReceivedRevealedAddressesFromJoinedMember() { + community, _ := createCommunity(&s.Suite, s.owner) + + tokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 1) + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + // check bob has TM role + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + // check that bob received revealed address + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() + }, "user not accepted") + s.Require().NoError(err) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestTokenMasterJoinedToCommunityAndReceivedRevealedAddresses() { + community, _ := createCommunity(&s.Suite, s.owner) + + tokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 1) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestMemberReceivedSharedAddressOnGettingTokenMasterRole() { + community, _ := createCommunity(&s.Suite, s.owner) + + community, err := s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 0) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + tokenCriteria := createTokenMasterTokenCriteria() + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + // wait for owner to send sync message for bob, who got a TM role + waitOnOwnerSendSyncMessage := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool { + return sub.CommunityPrivilegedMemberSyncMessage != nil && + sub.CommunityPrivilegedMemberSyncMessage.CommunityPrivilegedUserSyncMessage.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN && + len(sub.CommunityPrivilegedMemberSyncMessage.Receivers) == 1 && + sub.CommunityPrivilegedMemberSyncMessage.Receivers[0].Equal(&s.bob.identity.PublicKey) + }) + + _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + err = <-waitOnOwnerSendSyncMessage + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.Communities()) == 1 && r.Communities()[0].IsTokenMaster() + }, "bob didn't receive token master role") + s.Require().NoError(err) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestTokenMasterReceivesAccountsAfterPendingRequestToJoinApproval() { + community, _ := createOnRequestCommunity(&s.Suite, s.owner) + + tokenCriteria := createTokenMasterTokenCriteria() + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 1) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + + aliceArray64Bytes := common.HashPublicKey(&s.alice.identity.PublicKey) + aliceSignature := append([]byte{0}, aliceArray64Bytes...) + aliceRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{aliceAddress1}, + AirdropAddress: aliceAddress1, + Signatures: []types.HexBytes{aliceSignature}, + } + + aliceRequestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.alice, aliceRequest) + + bobArray64Bytes := common.HashPublicKey(&s.bob.identity.PublicKey) + bobSignature := append([]byte{0}, bobArray64Bytes...) + bobRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{bobAddress}, + AirdropAddress: bobAddress, + Signatures: []types.HexBytes{bobSignature}, + } + + joinOnRequestCommunity(&s.Suite, community, s.owner, s.bob, bobRequest) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().True(community.IsMemberTokenMaster(&s.bob.identity.PublicKey)) + + _, err = s.owner.AcceptRequestToJoinCommunity(&requests.AcceptRequestToJoinCommunity{ID: aliceRequestToJoinID}) + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + if r.RequestsToJoinCommunity() != nil { + for _, request := range r.RequestsToJoinCommunity() { + if request.PublicKey == s.alice.IdentityPublicKeyString() && request.State == communities.RequestToJoinStateAccepted { + return true + } + } + } + return false + }, "bob didn't receive accepted Alice request to join") + s.Require().NoError(err) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestMemberReceivesPendingRequestToJoinAfterAfterGettingTokenMasterRole() { + community, _ := createOnRequestCommunity(&s.Suite, s.owner) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + + aliceArray64Bytes := common.HashPublicKey(&s.alice.identity.PublicKey) + aliceSignature := append([]byte{0}, aliceArray64Bytes...) + aliceRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{aliceAddress1}, + AirdropAddress: aliceAddress1, + Signatures: []types.HexBytes{aliceSignature}, + } + + aliceRequestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.alice, aliceRequest) + + bobArray64Bytes := common.HashPublicKey(&s.bob.identity.PublicKey) + bobSignature := append([]byte{0}, bobArray64Bytes...) + bobRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{bobAddress}, + AirdropAddress: bobAddress, + Signatures: []types.HexBytes{bobSignature}, + } + + joinOnRequestCommunity(&s.Suite, community, s.owner, s.bob, bobRequest) + + tokenCriteria := createTokenMasterTokenCriteria() + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + // wait for owner to send sync message for bob, who got a TM role + waitOnOwnerSendSyncMessage := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool { + return sub.CommunityPrivilegedMemberSyncMessage != nil && + sub.CommunityPrivilegedMemberSyncMessage.CommunityPrivilegedUserSyncMessage.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN && + len(sub.CommunityPrivilegedMemberSyncMessage.Receivers) == 1 && + sub.CommunityPrivilegedMemberSyncMessage.Receivers[0].Equal(&s.bob.identity.PublicKey) + }) + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + err = <-waitOnOwnerSendSyncMessage + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.Communities()) == 1 && r.Communities()[0].IsTokenMaster() + }, "bob didn't receive token master role") + s.Require().NoError(err) + + _, err = s.owner.AcceptRequestToJoinCommunity(&requests.AcceptRequestToJoinCommunity{ID: aliceRequestToJoinID}) + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) > 0 && + r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() && + r.RequestsToJoinCommunity()[0].State == communities.RequestToJoinStateAccepted + + }, "bob didn't receive accepted Alice request to join") + s.Require().NoError(err) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestHandlingOutdatedPrivilegedUserSyncMessages() { + community, _ := createCommunity(&s.Suite, s.owner) + + tokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 1) + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + // check bob has TM role + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, &s.bob.identity.PublicKey, community) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + // check that bob received revealed address + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() + }, "user not accepted") + s.Require().NoError(err) + + // handle outdated CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN msg + expectedAliceRequestToJoin, err := s.bob.communitiesManager.GetRequestToJoinByPkAndCommunityID(s.alice.IdentityPublicKey(), community.ID()) + s.Require().NoError(err) + s.Require().NotNil(expectedAliceRequestToJoin) + + bobRequestToJoin, err := s.bob.communitiesManager.GetRequestToJoinByPkAndCommunityID(s.bob.IdentityPublicKey(), community.ID()) + s.Require().NoError(err) + s.Require().NotNil(bobRequestToJoin) + + invalidAliceSyncRtj := expectedAliceRequestToJoin.ToSyncProtobuf() + invalidAliceSyncRtj.RevealedAccounts = bobRequestToJoin.RevealedAccounts + invalidAliceSyncRtj.EnsName = "corrupted" + invalidAliceSyncRtj.State = uint64(communities.RequestToJoinStatePending) + + syncMsg := &protobuf.CommunityPrivilegedUserSyncMessage{ + Type: protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN, + CommunityId: community.ID(), + SyncRequestsToJoin: []*protobuf.SyncCommunityRequestsToJoin{invalidAliceSyncRtj}, + } + + state := &ReceivedMessageState{ + CurrentMessageState: &CurrentMessageState{ + PublicKey: community.PublicKey(), + }, + } + + err = s.bob.HandleCommunityPrivilegedUserSyncMessage(state, syncMsg, nil) + s.Require().NoError(err) + + aliceRtj, err := s.bob.communitiesManager.GetRequestToJoinByPkAndCommunityID(s.alice.IdentityPublicKey(), community.ID()) + s.Require().NoError(err) + s.Require().Equal(aliceRtj, expectedAliceRequestToJoin) + + // handle outdated CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN msg + + invalidAliceCommunityRtj := aliceRtj.ToCommunityRequestToJoinProtobuf() + invalidAliceCommunityRtj.RevealedAccounts = bobRequestToJoin.RevealedAccounts + invalidAliceCommunityRtj.EnsName = "corrupted" + + syncMsg.Type = protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN + syncMsg.RequestToJoin = map[string]*protobuf.CommunityRequestToJoin{ + s.alice.IdentityPublicKeyString(): invalidAliceCommunityRtj, + } + + err = s.bob.HandleCommunityPrivilegedUserSyncMessage(state, syncMsg, nil) + s.Require().NoError(err) + + aliceRtj, err = s.bob.communitiesManager.GetRequestToJoinByPkAndCommunityID(s.alice.IdentityPublicKey(), community.ID()) + s.Require().NoError(err) + s.Require().Equal(aliceRtj, expectedAliceRequestToJoin) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestMemberReceivedEditedSharedAddressOnGettingTokenMasterRole() { + community, _ := createCommunity(&s.Suite, s.owner) + + alicePubkey := s.alice.IdentityPublicKeyString() + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + request := s.createEditSharedAddressesRequest(community.ID()) + + response, err := s.alice.EditSharedAddressesForCommunity(request) + s.Require().NoError(err) + s.Require().NotNil(response) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress2) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + // check that owner received edited shared adresses + _, err = WaitOnMessengerResponse(s.owner, func(r *MessengerResponse) bool { + revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) + s.Require().NoError(err) + s.Require().Len(revealedAccounts, 1) + return revealedAccounts[0].Address == aliceAddress2 + + }, "owned did not receive alice shared address") + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.owner, expectedAliceRevealedAccounts) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + tokenCriteria := createTokenMasterTokenCriteria() + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + // wait for owner to send sync message for bob, who got a TM role + waitOnOwnerSendSyncMessage := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool { + return sub.CommunityPrivilegedMemberSyncMessage != nil && + sub.CommunityPrivilegedMemberSyncMessage.CommunityPrivilegedUserSyncMessage.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN && + len(sub.CommunityPrivilegedMemberSyncMessage.Receivers) == 1 && + sub.CommunityPrivilegedMemberSyncMessage.Receivers[0].Equal(&s.bob.identity.PublicKey) + }) + + _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + err = <-waitOnOwnerSendSyncMessage + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.Communities()) == 1 && r.Communities()[0].IsTokenMaster() + }, "bob didn't receive token master role") + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestMemberReceivesAccountsOnRoleChangeFromAdminToTokenMaster() { + community, _ := createCommunity(&s.Suite, s.owner) + + alicePublicKey := s.alice.IdentityPublicKeyString() + + adminTokenCriteria := &protobuf.TokenCriteria{ + ContractAddresses: map[uint64]string{testChainID1: "0x125"}, + Type: protobuf.CommunityTokenType_ERC20, + Symbol: "STT", + Name: "Status Test Token", + AmountInWei: "10000000000000000000", + Decimals: 18, + } + tokenMasterTokenCriteria := createTokenMasterTokenCriteria() + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenMasterTokenCriteria}, + }) + s.Require().NoError(err) + + _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_ADMIN, + TokenCriteria: []*protobuf.TokenCriteria{adminTokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 2) + + // make bob satisfy the admin criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, adminTokenCriteria) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + s.joinCommunity(community, s.bob, bobPassword, []string{bobAddress}) + + // check bob has admin role + community, err = s.bob.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + checkRoleBasedOnThePermissionType(protobuf.CommunityTokenPermission_BECOME_ADMIN, &s.bob.identity.PublicKey, community) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1}) + + expectedAliceRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePublicKey) + s.Require().NoError(err) + s.Require().Len(expectedAliceRevealedAccounts, 1) + s.Require().Equal(expectedAliceRevealedAccounts[0].Address, aliceAddress1) + s.Require().Equal(true, expectedAliceRevealedAccounts[0].IsAirdropAddress) + + // check that bob received alice request to join without revealed accounts + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && + r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() && + len(r.RequestsToJoinCommunity()[0].RevealedAccounts) == 0 + }, "alice request to join was not delivered to admin bob") + s.Require().NoError(err) + + emptyAliceAccounts, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(emptyAliceAccounts, 0) + + // make bob satisfy TokenMaster criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenMasterTokenCriteria) + + // wait for owner to send sync message for bob, who got a TM role + waitOnOwnerSendSyncMessage := waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool { + return sub.CommunityPrivilegedMemberSyncMessage != nil && + sub.CommunityPrivilegedMemberSyncMessage.CommunityPrivilegedUserSyncMessage.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN && + len(sub.CommunityPrivilegedMemberSyncMessage.Receivers) == 1 && + sub.CommunityPrivilegedMemberSyncMessage.Receivers[0].Equal(&s.bob.identity.PublicKey) + }) + + err = s.owner.communitiesManager.ForceMembersReevaluation(community.ID()) + s.Require().NoError(err) + + err = <-waitOnOwnerSendSyncMessage + s.Require().NoError(err) + + // check that bob received alice request to join with revealed accounts + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + revealedAccounts, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), alicePublicKey) + s.Require().NoError(err) + return len(revealedAccounts) > 0 + }, "alice request to join was not delivered to token master bob") + s.Require().NoError(err) + + s.checkRevealedAccounts(community.ID(), s.bob, expectedAliceRevealedAccounts) +} + +func (s *MessengerCommunitiesSharedMemberAddressSuite) TestOwnerRejectAndAcceptAliceRequestToJoin() { + community, _ := createOnRequestCommunity(&s.Suite, s.owner) + s.Require().False(community.AutoAccept()) + + tokenCriteria := createTokenMasterTokenCriteria() + + // make bob satisfy the Token Master criteria + s.makeAddressSatisfyTheCriteria(testChainID1, bobAddress, tokenCriteria) + + _, err := s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ + CommunityID: community.ID(), + Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER, + TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria}, + }) + s.Require().NoError(err) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.TokenPermissions(), 1) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) + + aliceArray64Bytes := common.HashPublicKey(&s.alice.identity.PublicKey) + aliceSignature := append([]byte{0}, aliceArray64Bytes...) + aliceRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{aliceAddress1}, + AirdropAddress: aliceAddress1, + Signatures: []types.HexBytes{aliceSignature}, + } + + bobArray64Bytes := common.HashPublicKey(&s.bob.identity.PublicKey) + bobSignature := append([]byte{0}, bobArray64Bytes...) + bobRequest := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{bobAddress}, + AirdropAddress: bobAddress, + Signatures: []types.HexBytes{bobSignature}, + } + + joinOnRequestCommunity(&s.Suite, community, s.owner, s.bob, bobRequest) + + community, err = s.owner.communitiesManager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().True(community.IsMemberTokenMaster(&s.bob.identity.PublicKey)) + + advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) + + aliceRequestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.alice, aliceRequest) + + // check that bob received alice request to join without revealed accounts due to pending state + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && + r.RequestsToJoinCommunity()[0].State == communities.RequestToJoinStatePending && + r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() + }, "alice pending request to join was not delivered to token master bob") + s.Require().NoError(err) + + // request to join was not approved, bob should not have alice revealed addresses + aliceRevealedAccounts, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(aliceRevealedAccounts, 0) + + _, err = s.owner.DeclineRequestToJoinCommunity(&requests.DeclineRequestToJoinCommunity{ID: aliceRequestToJoinID}) + s.Require().NoError(err) + + // check that bob received owner decline sync msg + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && + r.RequestsToJoinCommunity()[0].State == communities.RequestToJoinStateDeclined && + r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() + }, "alice declined request to join was not delivered to token master bob") + s.Require().NoError(err) + + // request to join was declined, bob should not have alice revealed addresses + aliceRevealedAccounts, err = s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(aliceRevealedAccounts, 0) + + _, err = s.owner.AcceptRequestToJoinCommunity(&requests.AcceptRequestToJoinCommunity{ID: aliceRequestToJoinID}) + s.Require().NoError(err) + + _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { + return len(r.RequestsToJoinCommunity()) == 1 && + r.RequestsToJoinCommunity()[0].State == communities.RequestToJoinStateAccepted && + r.RequestsToJoinCommunity()[0].PublicKey == s.alice.IdentityPublicKeyString() + }, "bob didn't receive accepted Alice request to join") + s.Require().NoError(err) + + // request to join was accepted, bob should have alice revealed addresses + aliceRevealedAccounts, err = s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString()) + s.Require().NoError(err) + s.Require().Len(aliceRevealedAccounts, 1) +} diff --git a/protocol/communities_messenger_token_permissions_test.go b/protocol/communities_messenger_token_permissions_test.go index 4b491f5aa..9915eec6f 100644 --- a/protocol/communities_messenger_token_permissions_test.go +++ b/protocol/communities_messenger_token_permissions_test.go @@ -456,193 +456,6 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestRequestAccessWithENSToke } } -func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinedCommunityMembersSharedAddress() { - community, _ := s.createCommunity() - s.advertiseCommunityTo(community, s.alice) - s.advertiseCommunityTo(community, s.bob) - - s.joinCommunity(community, s.alice, alicePassword, []string{}) - s.joinCommunity(community, s.bob, bobPassword, []string{}) - - community, err := s.owner.GetCommunityByID(community.ID()) - s.Require().NoError(err) - - s.Require().Equal(3, community.MembersCount()) - - // Check owner's DB for revealed accounts - for pubKey := range community.Members() { - if pubKey != common.PubkeyToHex(&s.owner.identity.PublicKey) { - revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), pubKey) - s.Require().NoError(err) - switch pubKey { - case common.PubkeyToHex(&s.alice.identity.PublicKey): - s.Require().Len(revealedAccounts, 2) - s.Require().Equal(revealedAccounts[0].Address, aliceAddress1) - s.Require().Equal(revealedAccounts[1].Address, aliceAddress2) - s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) - case common.PubkeyToHex(&s.bob.identity.PublicKey): - s.Require().Len(revealedAccounts, 1) - s.Require().Equal(revealedAccounts[0].Address, bobAddress) - s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) - default: - s.Require().Fail("pubKey does not match expected keys") - } - } - } - - // Check Bob's DB for revealed accounts - revealedAccountsInBobsDB, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), common.PubkeyToHex(&s.bob.identity.PublicKey)) - s.Require().NoError(err) - s.Require().Len(revealedAccountsInBobsDB, 1) - s.Require().Equal(revealedAccountsInBobsDB[0].Address, bobAddress) - s.Require().Equal(true, revealedAccountsInBobsDB[0].IsAirdropAddress) - - // Check Alices's DB for revealed accounts - revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) - s.Require().NoError(err) - s.Require().Len(revealedAccountsInAlicesDB, 2) - s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress1) - s.Require().Equal(revealedAccountsInAlicesDB[1].Address, aliceAddress2) - s.Require().Equal(true, revealedAccountsInAlicesDB[0].IsAirdropAddress) -} - -func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinedCommunityMembersSelectedSharedAddress() { - community, _ := s.createCommunity() - s.advertiseCommunityTo(community, s.alice) - - s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress2}) - - community, err := s.owner.GetCommunityByID(community.ID()) - s.Require().NoError(err) - - s.Require().Equal(2, community.MembersCount()) - - alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) - - // Check owner's DB for revealed accounts - revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(revealedAccounts, 1) - s.Require().Equal(revealedAccounts[0].Address, aliceAddress2) - s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) - - // Check Alice's DB for revealed accounts - revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(revealedAccountsInAlicesDB, 1) - s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress2) - s.Require().Equal(true, revealedAccountsInAlicesDB[0].IsAirdropAddress) -} - -func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinedCommunityMembersMultipleSelectedSharedAddresses() { - community, _ := s.createCommunity() - s.advertiseCommunityTo(community, s.alice) - - s.joinCommunityWithAirdropAddress(community, s.alice, alicePassword, []string{aliceAddress1, aliceAddress2}, aliceAddress2) - - community, err := s.owner.GetCommunityByID(community.ID()) - s.Require().NoError(err) - - s.Require().Equal(2, community.MembersCount()) - - alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) - - // Check owner's DB for revealed accounts - revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(revealedAccounts, 2) - s.Require().Equal(revealedAccounts[0].Address, aliceAddress1) - s.Require().Equal(revealedAccounts[1].Address, aliceAddress2) - s.Require().Equal(true, revealedAccounts[1].IsAirdropAddress) - - // Check Alice's DB for revealed accounts - revealedAccountsInAlicesDB, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(revealedAccountsInAlicesDB, 2) - s.Require().Equal(revealedAccountsInAlicesDB[0].Address, aliceAddress1) - s.Require().Equal(revealedAccountsInAlicesDB[1].Address, aliceAddress2) - s.Require().Equal(true, revealedAccountsInAlicesDB[1].IsAirdropAddress) -} - -func (s *MessengerCommunitiesTokenPermissionsSuite) TestEditSharedAddresses() { - community, _ := s.createCommunity() - s.advertiseCommunityTo(community, s.alice) - - s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress2}) - - community, err := s.owner.GetCommunityByID(community.ID()) - s.Require().NoError(err) - s.Require().Equal(2, community.MembersCount()) - - alicePubkey := common.PubkeyToHex(&s.alice.identity.PublicKey) - - revealedAccounts, err := s.owner.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - - s.Require().Len(revealedAccounts, 1) - s.Require().Equal(revealedAccounts[0].Address, aliceAddress2) - s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) - - alicesRevealedAccounts, err := s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(alicesRevealedAccounts, 1) - s.Require().Equal(alicesRevealedAccounts[0].Address, aliceAddress2) - s.Require().Equal(true, alicesRevealedAccounts[0].IsAirdropAddress) - - request := &requests.EditSharedAddresses{CommunityID: community.ID(), AddressesToReveal: []string{aliceAddress1}, AirdropAddress: aliceAddress1} - - signingParams, err := s.alice.GenerateJoiningCommunityRequestsForSigning(common.PubkeyToHex(&s.alice.identity.PublicKey), community.ID(), request.AddressesToReveal) - s.Require().NoError(err) - - passwdHash := types.EncodeHex(crypto.Keccak256([]byte(alicePassword))) - for i := range signingParams { - signingParams[i].Password = passwdHash - } - signatures, err := s.alice.SignData(signingParams) - s.Require().NoError(err) - - updateAddresses := len(request.AddressesToReveal) == 0 - if updateAddresses { - request.AddressesToReveal = make([]string, len(signingParams)) - } - for i := range signingParams { - request.AddressesToReveal[i] = signingParams[i].Address - request.Signatures = append(request.Signatures, types.FromHex(signatures[i])) - } - if updateAddresses { - request.AirdropAddress = request.AddressesToReveal[0] - } - - response, err := s.alice.EditSharedAddressesForCommunity(request) - s.Require().NoError(err) - s.Require().NotNil(response) - - // Retrieve address change - err = tt.RetryWithBackOff(func() error { - response, err := s.owner.RetrieveAll() - if err != nil { - return err - } - if len(response.Communities()) == 0 { - return errors.New("no communities in response (address change reception)") - } - return nil - }) - s.Require().NoError(err) - revealedAccounts, err = s.owner.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - - s.Require().Len(revealedAccounts, 1) - s.Require().Equal(revealedAccounts[0].Address, aliceAddress1) - s.Require().Equal(true, revealedAccounts[0].IsAirdropAddress) - - alicesRevealedAccounts, err = s.alice.communitiesManager.GetRevealedAddresses(community.ID(), alicePubkey) - s.Require().NoError(err) - s.Require().Len(alicesRevealedAccounts, 1) - s.Require().Equal(alicesRevealedAccounts[0].Address, aliceAddress1) - s.Require().Equal(true, alicesRevealedAccounts[0].IsAirdropAddress) -} - // NOTE(cammellos): Disabling for now as flaky, for some reason does not pass on CI, but passes locally func (s *MessengerCommunitiesTokenPermissionsSuite) TestBecomeMemberPermissions() { s.T().Skip("flaky test") @@ -883,57 +696,6 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinCommunityWithAdminPe s.Require().Equal(bobAddress, revealedAccounts[0].Address) } -func (s *MessengerCommunitiesTokenPermissionsSuite) TestSharedAddressesReturnsRevealedAccount() { - community, _ := s.createCommunity() - - permissionRequest := requests.CreateCommunityTokenPermission{ - CommunityID: community.ID(), - Type: protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL, - TokenCriteria: []*protobuf.TokenCriteria{ - &protobuf.TokenCriteria{ - Type: protobuf.CommunityTokenType_ERC20, - ContractAddresses: map[uint64]string{testChainID1: "0x123"}, - Symbol: "TEST", - AmountInWei: "100000000000000000000", - Decimals: uint64(18), - }, - }, - } - - response, err := s.owner.CreateCommunityTokenPermission(&permissionRequest) - s.Require().NoError(err) - s.Require().Len(response.Communities(), 1) - - s.advertiseCommunityTo(community, s.alice) - - s.joinCommunity(community, s.alice, alicePassword, []string{}) - - revealedAccounts, err := s.alice.GetRevealedAccounts(community.ID(), common.PubkeyToHex(&s.alice.identity.PublicKey)) - s.Require().NoError(err) - - revealedAddressesMap := make(map[string]struct{}, len(revealedAccounts)) - for _, acc := range revealedAccounts { - revealedAddressesMap[acc.Address] = struct{}{} - } - - s.Require().Len(revealedAddressesMap, 2) - s.Require().Contains(revealedAddressesMap, aliceAddress1) - s.Require().Contains(revealedAddressesMap, aliceAddress2) - - sharedAddresses, err := s.alice.getSharedAddresses(community.ID(), []string{}) - s.Require().NoError(err) - s.Require().Len(sharedAddresses, 2) - - sharedAddressesMap := make(map[string]struct{}, len(sharedAddresses)) - for _, acc := range sharedAddresses { - sharedAddressesMap[acc.String()] = struct{}{} - } - - s.Require().Len(sharedAddressesMap, 2) - s.Require().Contains(sharedAddressesMap, aliceAddress1) - s.Require().Contains(sharedAddressesMap, aliceAddress2) -} - func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinCommunityAsMemberWithMemberAndAdminPermission() { community, _ := s.createCommunity() @@ -2098,46 +1860,6 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestResendEncryptionKeyOnBac s.Require().Len(response.Messages(), 1) } -func (s *MessengerCommunitiesTokenPermissionsSuite) TestResendSharedAddressesOnBackupRestore() { - community, _ := s.createCommunity() - - // bob joins the community - s.advertiseCommunityTo(community, s.bob) - s.joinCommunity(community, s.bob, bobPassword, []string{}) - - currentBobSharedAddresses, err := s.bob.GetRevealedAccounts(community.ID(), s.bob.IdentityPublicKeyString()) - s.Require().NoError(err) - - // Simulate backup creation and handling backup message - // As a result, bob sends request to resend encryption keys to the owner - clock, _ := s.bob.getLastClockWithRelatedChat() - - community, err = s.owner.communitiesManager.GetByID(community.ID()) - s.Require().NoError(err) - - backupMessage, err := s.bob.backupCommunity(community, clock) - s.Require().NoError(err) - - err = s.bob.HandleBackup(s.bob.buildMessageState(), backupMessage, nil) - s.Require().NoError(err) - - // Owner will receive the request for addresses and send them back to Bob - response, err := WaitOnMessengerResponse( - s.bob, - func(r *MessengerResponse) bool { - _, _ = s.owner.RetrieveAll() - return len(r.requestsToJoinCommunity) > 0 - }, - "request to join not received", - ) - s.Require().NoError(err) - - requestID := communities.CalculateRequestID(common.PubkeyToHex(&s.bob.identity.PublicKey), community.ID()) - requestToJoin, ok := response.requestsToJoinCommunity[requestID.String()] - s.Require().Equal(true, ok) - s.Require().Equal(currentBobSharedAddresses, requestToJoin.RevealedAccounts) -} - func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberPermissionsPerformance() { // This test is created for a performance degradation tracking for reevaluateMember permissions // current scenario mostly track channels permissions reevaluating, but feel free to expand it to diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index a5957a855..dd41d41d0 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -682,6 +682,15 @@ func (m *Messenger) handleCommunitySharedAddressesRequest(state *ReceivedMessage return err } + if community.IsPrivilegedMember(signer) { + memberRole := community.MemberRole(signer) + newPrivilegedMember := make(map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) + newPrivilegedMember[memberRole] = []*ecdsa.PublicKey{signer} + if err = m.communitiesManager.ShareRequestsToJoinWithPrivilegedMembers(community, newPrivilegedMember); err != nil { + return err + } + } + return nil } @@ -1656,23 +1665,6 @@ func (m *Messenger) EditSharedAddressesForCommunity(request *requests.EditShared return nil, err } - // send edit message also to TokenMasters and Owners - skipMembers := make(map[string]struct{}) - skipMembers[common.PubkeyToHex(&m.identity.PublicKey)] = struct{}{} - - privilegedMembers := community.GetFilteredPrivilegedMembers(skipMembers) - for role, members := range privilegedMembers { - if len(members) == 0 || (role != protobuf.CommunityMember_ROLE_TOKEN_MASTER && role != protobuf.CommunityMember_ROLE_OWNER) { - continue - } - for _, member := range members { - rawMessage.Recipients = append(rawMessage.Recipients, member) - _, err := m.sender.SendPrivate(context.Background(), member, &rawMessage) - if err != nil { - return nil, err - } - } - } if _, err = m.UpsertRawMessageToWatch(&rawMessage); err != nil { return nil, err } @@ -3573,6 +3565,10 @@ func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMess return err } + if community.IsControlNode() { + return nil + } + // Currently this type of msg coming from the control node. // If it will change in the future, check that events types starting from // CONTROL_NODE were sent by a control node @@ -3590,18 +3586,23 @@ func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMess case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN: fallthrough case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN: - requestsToJoin, err := m.communitiesManager.HandleRequestToJoinPrivilegedUserSyncMessage(message, community.ID()) + requestsToJoin, err := m.communitiesManager.HandleRequestToJoinPrivilegedUserSyncMessage(message, community) if err != nil { - return nil + return err } state.Response.AddRequestsToJoinCommunity(requestsToJoin) case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN: - nonAcceptedRequestsToJoin, err := m.communitiesManager.HandleSyncAllRequestToJoinForNewPrivilegedMember(message, community.ID()) + nonAcceptedRequestsToJoin, err := m.communitiesManager.HandleSyncAllRequestToJoinForNewPrivilegedMember(message, community) if err != nil { - return nil + return err } state.Response.AddRequestsToJoinCommunity(nonAcceptedRequestsToJoin) + case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES: + err = m.communitiesManager.HandleEditSharedAddressesPrivilegedUserSyncMessage(message, community) + if err != nil { + return err + } } return nil diff --git a/protocol/protobuf/community_privileged_user_sync_message.pb.go b/protocol/protobuf/community_privileged_user_sync_message.pb.go index a3b239c90..4725ca2a8 100644 --- a/protocol/protobuf/community_privileged_user_sync_message.pb.go +++ b/protocol/protobuf/community_privileged_user_sync_message.pb.go @@ -23,10 +23,11 @@ const ( type CommunityPrivilegedUserSyncMessage_EventType int32 const ( - CommunityPrivilegedUserSyncMessage_UNKNOWN CommunityPrivilegedUserSyncMessage_EventType = 0 - CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 1 - CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 2 - CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 3 + CommunityPrivilegedUserSyncMessage_UNKNOWN CommunityPrivilegedUserSyncMessage_EventType = 0 + CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 1 + CommunityPrivilegedUserSyncMessage_CONTROL_NODE_REJECT_REQUEST_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 2 + CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN CommunityPrivilegedUserSyncMessage_EventType = 3 + CommunityPrivilegedUserSyncMessage_CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES CommunityPrivilegedUserSyncMessage_EventType = 4 ) // Enum value maps for CommunityPrivilegedUserSyncMessage_EventType. @@ -36,12 +37,14 @@ var ( 1: "CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN", 2: "CONTROL_NODE_REJECT_REQUEST_TO_JOIN", 3: "CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN", + 4: "CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES", } CommunityPrivilegedUserSyncMessage_EventType_value = map[string]int32{ - "UNKNOWN": 0, - "CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN": 1, - "CONTROL_NODE_REJECT_REQUEST_TO_JOIN": 2, - "CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN": 3, + "UNKNOWN": 0, + "CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN": 1, + "CONTROL_NODE_REJECT_REQUEST_TO_JOIN": 2, + "CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN": 3, + "CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES": 4, } ) @@ -69,7 +72,62 @@ func (x CommunityPrivilegedUserSyncMessage_EventType) Number() protoreflect.Enum // Deprecated: Use CommunityPrivilegedUserSyncMessage_EventType.Descriptor instead. func (CommunityPrivilegedUserSyncMessage_EventType) EnumDescriptor() ([]byte, []int) { - return file_community_privileged_user_sync_message_proto_rawDescGZIP(), []int{0, 0} + return file_community_privileged_user_sync_message_proto_rawDescGZIP(), []int{1, 0} +} + +type SyncCommunityEditSharedAddresses struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + EditSharedAddress *CommunityEditSharedAddresses `protobuf:"bytes,2,opt,name=edit_shared_address,json=editSharedAddress,proto3" json:"edit_shared_address,omitempty"` +} + +func (x *SyncCommunityEditSharedAddresses) Reset() { + *x = SyncCommunityEditSharedAddresses{} + if protoimpl.UnsafeEnabled { + mi := &file_community_privileged_user_sync_message_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncCommunityEditSharedAddresses) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncCommunityEditSharedAddresses) ProtoMessage() {} + +func (x *SyncCommunityEditSharedAddresses) ProtoReflect() protoreflect.Message { + mi := &file_community_privileged_user_sync_message_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncCommunityEditSharedAddresses.ProtoReflect.Descriptor instead. +func (*SyncCommunityEditSharedAddresses) Descriptor() ([]byte, []int) { + return file_community_privileged_user_sync_message_proto_rawDescGZIP(), []int{0} +} + +func (x *SyncCommunityEditSharedAddresses) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +func (x *SyncCommunityEditSharedAddresses) GetEditSharedAddress() *CommunityEditSharedAddresses { + if x != nil { + return x.EditSharedAddress + } + return nil } type CommunityPrivilegedUserSyncMessage struct { @@ -77,17 +135,18 @@ type CommunityPrivilegedUserSyncMessage struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` - Type CommunityPrivilegedUserSyncMessage_EventType `protobuf:"varint,2,opt,name=type,proto3,enum=protobuf.CommunityPrivilegedUserSyncMessage_EventType" json:"type,omitempty"` - CommunityId []byte `protobuf:"bytes,3,opt,name=community_id,json=communityId,proto3" json:"community_id,omitempty"` - RequestToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,4,rep,name=request_to_join,json=requestToJoin,proto3" json:"request_to_join,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - SyncRequestsToJoin []*SyncCommunityRequestsToJoin `protobuf:"bytes,5,rep,name=sync_requests_to_join,json=syncRequestsToJoin,proto3" json:"sync_requests_to_join,omitempty"` + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + Type CommunityPrivilegedUserSyncMessage_EventType `protobuf:"varint,2,opt,name=type,proto3,enum=protobuf.CommunityPrivilegedUserSyncMessage_EventType" json:"type,omitempty"` + CommunityId []byte `protobuf:"bytes,3,opt,name=community_id,json=communityId,proto3" json:"community_id,omitempty"` + RequestToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,4,rep,name=request_to_join,json=requestToJoin,proto3" json:"request_to_join,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + SyncRequestsToJoin []*SyncCommunityRequestsToJoin `protobuf:"bytes,5,rep,name=sync_requests_to_join,json=syncRequestsToJoin,proto3" json:"sync_requests_to_join,omitempty"` + SyncEditSharedAddresses *SyncCommunityEditSharedAddresses `protobuf:"bytes,6,opt,name=sync_edit_shared_addresses,json=syncEditSharedAddresses,proto3" json:"sync_edit_shared_addresses,omitempty"` } func (x *CommunityPrivilegedUserSyncMessage) Reset() { *x = CommunityPrivilegedUserSyncMessage{} if protoimpl.UnsafeEnabled { - mi := &file_community_privileged_user_sync_message_proto_msgTypes[0] + mi := &file_community_privileged_user_sync_message_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -100,7 +159,7 @@ func (x *CommunityPrivilegedUserSyncMessage) String() string { func (*CommunityPrivilegedUserSyncMessage) ProtoMessage() {} func (x *CommunityPrivilegedUserSyncMessage) ProtoReflect() protoreflect.Message { - mi := &file_community_privileged_user_sync_message_proto_msgTypes[0] + mi := &file_community_privileged_user_sync_message_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -113,7 +172,7 @@ func (x *CommunityPrivilegedUserSyncMessage) ProtoReflect() protoreflect.Message // Deprecated: Use CommunityPrivilegedUserSyncMessage.ProtoReflect.Descriptor instead. func (*CommunityPrivilegedUserSyncMessage) Descriptor() ([]byte, []int) { - return file_community_privileged_user_sync_message_proto_rawDescGZIP(), []int{0} + return file_community_privileged_user_sync_message_proto_rawDescGZIP(), []int{1} } func (x *CommunityPrivilegedUserSyncMessage) GetClock() uint64 { @@ -151,6 +210,13 @@ func (x *CommunityPrivilegedUserSyncMessage) GetSyncRequestsToJoin() []*SyncComm return nil } +func (x *CommunityPrivilegedUserSyncMessage) GetSyncEditSharedAddresses() *SyncCommunityEditSharedAddresses { + if x != nil { + return x.SyncEditSharedAddresses + } + return nil +} + var File_community_privileged_user_sync_message_proto protoreflect.FileDescriptor var file_community_privileged_user_sync_message_proto_rawDesc = []byte{ @@ -159,47 +225,67 @@ var file_community_privileged_user_sync_message_proto_rawDesc = []byte{ 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x11, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x70, 0x61, 0x69, - 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe9, 0x04, 0x0a, 0x22, 0x43, - 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, - 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, - 0x65, 0x67, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, - 0x6e, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x67, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x3f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, + 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x01, 0x0a, 0x20, 0x53, + 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x45, 0x64, 0x69, 0x74, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x56, + 0x0a, 0x13, 0x65, 0x64, 0x69, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, + 0x45, 0x64, 0x69, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x52, 0x11, 0x65, 0x64, 0x69, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x81, 0x06, 0x0a, 0x22, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x55, 0x73, - 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x12, - 0x58, 0x0a, 0x15, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, - 0x5f, 0x74, 0x6f, 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, - 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x54, - 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x12, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x73, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x1a, 0x62, 0x0a, 0x12, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, - 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x96, 0x01, - 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, - 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x27, 0x0a, 0x23, 0x43, 0x4f, 0x4e, 0x54, - 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x50, 0x54, 0x5f, - 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x10, - 0x01, 0x12, 0x27, 0x0a, 0x23, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, - 0x45, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, - 0x5f, 0x54, 0x4f, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x02, 0x12, 0x2a, 0x0a, 0x26, 0x43, 0x4f, - 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x4c, 0x4c, 0x5f, 0x53, - 0x59, 0x4e, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x53, 0x5f, 0x54, 0x4f, 0x5f, - 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x03, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, + 0x05, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x63, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x4a, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x36, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, + 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, + 0x55, 0x73, 0x65, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, + 0x49, 0x64, 0x12, 0x67, 0x0a, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x6f, + 0x5f, 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, + 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x53, 0x79, + 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x58, 0x0a, 0x15, 0x73, + 0x79, 0x6e, 0x63, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, + 0x6a, 0x6f, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x54, 0x6f, 0x4a, 0x6f, 0x69, + 0x6e, 0x52, 0x12, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x54, + 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x12, 0x67, 0x0a, 0x1a, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x65, 0x64, + 0x69, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x74, 0x79, 0x45, 0x64, 0x69, 0x74, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x17, 0x73, 0x79, 0x6e, 0x63, 0x45, 0x64, 0x69, 0x74, 0x53, + 0x68, 0x61, 0x72, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x62, + 0x0a, 0x12, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x54, 0x6f, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, + 0x38, 0x01, 0x22, 0xc5, 0x01, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x27, 0x0a, + 0x23, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x43, + 0x43, 0x45, 0x50, 0x54, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x54, 0x4f, 0x5f, + 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x27, 0x0a, 0x23, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, + 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x52, 0x45, + 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x54, 0x4f, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x02, 0x12, + 0x2a, 0x0a, 0x26, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, + 0x41, 0x4c, 0x4c, 0x5f, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, + 0x53, 0x5f, 0x54, 0x4f, 0x5f, 0x4a, 0x4f, 0x49, 0x4e, 0x10, 0x03, 0x12, 0x2d, 0x0a, 0x29, 0x43, + 0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x5f, 0x4e, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x45, 0x4d, 0x42, + 0x45, 0x52, 0x5f, 0x45, 0x44, 0x49, 0x54, 0x5f, 0x53, 0x48, 0x41, 0x52, 0x45, 0x44, 0x5f, 0x41, + 0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x45, 0x53, 0x10, 0x04, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, + 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -215,24 +301,28 @@ func file_community_privileged_user_sync_message_proto_rawDescGZIP() []byte { } var file_community_privileged_user_sync_message_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_community_privileged_user_sync_message_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_community_privileged_user_sync_message_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_community_privileged_user_sync_message_proto_goTypes = []interface{}{ (CommunityPrivilegedUserSyncMessage_EventType)(0), // 0: protobuf.CommunityPrivilegedUserSyncMessage.EventType - (*CommunityPrivilegedUserSyncMessage)(nil), // 1: protobuf.CommunityPrivilegedUserSyncMessage - nil, // 2: protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry - (*SyncCommunityRequestsToJoin)(nil), // 3: protobuf.SyncCommunityRequestsToJoin - (*CommunityRequestToJoin)(nil), // 4: protobuf.CommunityRequestToJoin + (*SyncCommunityEditSharedAddresses)(nil), // 1: protobuf.SyncCommunityEditSharedAddresses + (*CommunityPrivilegedUserSyncMessage)(nil), // 2: protobuf.CommunityPrivilegedUserSyncMessage + nil, // 3: protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry + (*CommunityEditSharedAddresses)(nil), // 4: protobuf.CommunityEditSharedAddresses + (*SyncCommunityRequestsToJoin)(nil), // 5: protobuf.SyncCommunityRequestsToJoin + (*CommunityRequestToJoin)(nil), // 6: protobuf.CommunityRequestToJoin } var file_community_privileged_user_sync_message_proto_depIdxs = []int32{ - 0, // 0: protobuf.CommunityPrivilegedUserSyncMessage.type:type_name -> protobuf.CommunityPrivilegedUserSyncMessage.EventType - 2, // 1: protobuf.CommunityPrivilegedUserSyncMessage.request_to_join:type_name -> protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry - 3, // 2: protobuf.CommunityPrivilegedUserSyncMessage.sync_requests_to_join:type_name -> protobuf.SyncCommunityRequestsToJoin - 4, // 3: protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry.value:type_name -> protobuf.CommunityRequestToJoin - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 4, // 0: protobuf.SyncCommunityEditSharedAddresses.edit_shared_address:type_name -> protobuf.CommunityEditSharedAddresses + 0, // 1: protobuf.CommunityPrivilegedUserSyncMessage.type:type_name -> protobuf.CommunityPrivilegedUserSyncMessage.EventType + 3, // 2: protobuf.CommunityPrivilegedUserSyncMessage.request_to_join:type_name -> protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry + 5, // 3: protobuf.CommunityPrivilegedUserSyncMessage.sync_requests_to_join:type_name -> protobuf.SyncCommunityRequestsToJoin + 1, // 4: protobuf.CommunityPrivilegedUserSyncMessage.sync_edit_shared_addresses:type_name -> protobuf.SyncCommunityEditSharedAddresses + 6, // 5: protobuf.CommunityPrivilegedUserSyncMessage.RequestToJoinEntry.value:type_name -> protobuf.CommunityRequestToJoin + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_community_privileged_user_sync_message_proto_init() } @@ -244,6 +334,18 @@ func file_community_privileged_user_sync_message_proto_init() { file_pairing_proto_init() if !protoimpl.UnsafeEnabled { file_community_privileged_user_sync_message_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncCommunityEditSharedAddresses); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_community_privileged_user_sync_message_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CommunityPrivilegedUserSyncMessage); i { case 0: return &v.state @@ -262,7 +364,7 @@ func file_community_privileged_user_sync_message_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_community_privileged_user_sync_message_proto_rawDesc, NumEnums: 1, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/protocol/protobuf/community_privileged_user_sync_message.proto b/protocol/protobuf/community_privileged_user_sync_message.proto index ad90da53e..4b78059b7 100644 --- a/protocol/protobuf/community_privileged_user_sync_message.proto +++ b/protocol/protobuf/community_privileged_user_sync_message.proto @@ -6,17 +6,24 @@ package protobuf; import "communities.proto"; import "pairing.proto"; +message SyncCommunityEditSharedAddresses { + string public_key = 1; + CommunityEditSharedAddresses edit_shared_address = 2; +} + message CommunityPrivilegedUserSyncMessage { uint64 clock = 1; EventType type = 2; bytes community_id = 3; map request_to_join = 4; repeated SyncCommunityRequestsToJoin sync_requests_to_join = 5; + SyncCommunityEditSharedAddresses sync_edit_shared_addresses = 6; enum EventType { UNKNOWN = 0; CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN = 1; CONTROL_NODE_REJECT_REQUEST_TO_JOIN = 2; CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN = 3; + CONTROL_NODE_MEMBER_EDIT_SHARED_ADDRESSES = 4; } } \ No newline at end of file