From fe604b2806dfa51ea748c5e61c2a87cc7ffd9c4a Mon Sep 17 00:00:00 2001 From: Mykhailo Prakhov Date: Thu, 7 Dec 2023 17:27:14 +0100 Subject: [PATCH] fix: tokenMaster does not have members revealed addresses (#4425) --- .../communnity_privileged_member_sync_msg.go | 51 ++++--- protocol/communities/manager.go | 25 ++-- protocol/communities/persistence.go | 9 ++ protocol/communities/persistence_test.go | 105 ++++++++++++++ protocol/communities_events_utils_test.go | 130 ++++++++++-------- protocol/messenger_communities.go | 14 +- 6 files changed, 242 insertions(+), 92 deletions(-) diff --git a/protocol/communities/communnity_privileged_member_sync_msg.go b/protocol/communities/communnity_privileged_member_sync_msg.go index 8234ca8cd..eb736a6d7 100644 --- a/protocol/communities/communnity_privileged_member_sync_msg.go +++ b/protocol/communities/communnity_privileged_member_sync_msg.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" "github.com/status-im/status-go/eth-node/types" + "github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/protobuf" ) @@ -33,22 +34,6 @@ func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf } requestToJoin.CalculateID() - _, err := m.saveOrUpdateRequestToJoin(communityID, requestToJoin) - if err != nil { - return nil, err - } - requestsToJoin = append(requestsToJoin, requestToJoin) - } - - return requestsToJoin, nil -} - -func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *protobuf.CommunityPrivilegedUserSyncMessage, communityID types.HexBytes) ([]*RequestToJoin, error) { - requestsToJoin := []*RequestToJoin{} - for _, syncRequestToJoin := range message.SyncRequestsToJoin { - requestToJoin := new(RequestToJoin) - requestToJoin.InitFromSyncProtobuf(syncRequestToJoin) - if _, err := m.saveOrUpdateRequestToJoin(communityID, requestToJoin); err != nil { return nil, err } @@ -62,7 +47,41 @@ func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *prot return nil, err } } + requestsToJoin = append(requestsToJoin, requestToJoin) } + return requestsToJoin, nil } + +func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *protobuf.CommunityPrivilegedUserSyncMessage, communityID types.HexBytes) ([]*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 { + 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 + } + } + + if requestToJoin.State != RequestToJoinStateAccepted { + nonAcceptedRequestsToJoin = append(nonAcceptedRequestsToJoin, requestToJoin) + } + } + return nonAcceptedRequestsToJoin, nil +} diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index 56e4c11e9..7b1d18a56 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -1580,24 +1580,20 @@ func (m *Manager) HandleCommunityDescriptionMessage(signer *ecdsa.PublicKey, des } } - if hasTokenOwnership { + if hasTokenOwnership && verifiedOwner != nil { // Override verified owner - if verifiedOwner != nil { - m.logger.Info("updating verified owner", zap.String("communityID", community.IDString()), zap.String("owner", common.PubkeyToHex(verifiedOwner))) + m.logger.Info("updating verified owner", zap.String("communityID", community.IDString()), zap.String("owner", common.PubkeyToHex(verifiedOwner))) - // If we are not the verified owner anymore, drop the private key - if !common.IsPubKeyEqual(verifiedOwner, &m.identity.PublicKey) { - community.config.PrivateKey = nil - } + // If we are not the verified owner anymore, drop the private key + if !common.IsPubKeyEqual(verifiedOwner, &m.identity.PublicKey) { + community.config.PrivateKey = nil + } - // new control node will be set in the 'UpdateCommunityDescription' - if !common.IsPubKeyEqual(verifiedOwner, signer) { - return nil, ErrNotAuthorized - } - } else if !common.IsPubKeyEqual(community.ControlNode(), signer) { + // new control node will be set in the 'UpdateCommunityDescription' + if !common.IsPubKeyEqual(verifiedOwner, signer) { return nil, ErrNotAuthorized } - } else if !common.IsPubKeyEqual(community.PublicKey(), signer) { + } else if !common.IsPubKeyEqual(community.ControlNode(), signer) { return nil, ErrNotAuthorized } @@ -2227,6 +2223,7 @@ func (m *Manager) AcceptRequestToJoin(dbRequest *RequestToJoin) (*Community, err return nil, err } + dbRequest.RevealedAccounts = revealedAccounts if err = m.shareAcceptedRequestToJoinWithPrivilegedMembers(community, dbRequest); err != nil { return nil, err } @@ -4925,7 +4922,7 @@ func (m *Manager) shareRequestsToJoinWithNewPrivilegedMembers(community *Communi case protobuf.CommunityMember_ROLE_ADMIN: subscriptionMsg.CommunityPrivilegedUserSyncMessage = syncMsgWithoutRevealedAccounts case protobuf.CommunityMember_ROLE_OWNER: - fallthrough + continue case protobuf.CommunityMember_ROLE_TOKEN_MASTER: subscriptionMsg.CommunityPrivilegedUserSyncMessage = syncMsgWitRevealedAccounts } diff --git a/protocol/communities/persistence.go b/protocol/communities/persistence.go index f8084c244..454ccb3f6 100644 --- a/protocol/communities/persistence.go +++ b/protocol/communities/persistence.go @@ -1669,3 +1669,12 @@ 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 +} diff --git a/protocol/communities/persistence_test.go b/protocol/communities/persistence_test.go index aabfd4b27..7ee2b74f4 100644 --- a/protocol/communities/persistence_test.go +++ b/protocol/communities/persistence_test.go @@ -757,3 +757,108 @@ func (s *PersistenceSuite) TestAllNonApprovedCommunitiesRequestsToJoin() { s.Require().NoError(err) 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) +} diff --git a/protocol/communities_events_utils_test.go b/protocol/communities_events_utils_test.go index 386662d81..e5f91a658 100644 --- a/protocol/communities_events_utils_test.go +++ b/protocol/communities_events_utils_test.go @@ -1459,7 +1459,7 @@ func testControlNodeHandlesMultipleEventSenderRequestToJoinDecisions(base Commun // control node receives event sender 1's and 2's decision _, err = WaitOnMessengerResponse( base.GetControlNode(), - func(r *MessengerResponse) bool { return len(r.Communities()) > 0 }, + func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity) > 0 }, "control node did not receive event senders decision", ) s.Require().NoError(err) @@ -2032,15 +2032,6 @@ func testJoinedPrivilegedMemberReceiveRequestsToJoin(base CommunityEventsTestsIn advertiseCommunityTo(s, community, base.GetControlNode(), bob) advertiseCommunityTo(s, community, base.GetControlNode(), newPrivilegedUser) - requestMember := &requests.RequestToJoinCommunity{ - CommunityID: community.ID(), - AddressesToReveal: []string{bobAccountAddress}, - ENSName: "bob", - AirdropAddress: bobAccountAddress, - } - - requestToJoinCommunity(s, base.GetControlNode(), bob, requestMember) - requestNewPrivilegedUser := &requests.RequestToJoinCommunity{ CommunityID: community.ID(), AddressesToReveal: []string{eventsSenderAccountAddress}, @@ -2060,38 +2051,39 @@ func testJoinedPrivilegedMemberReceiveRequestsToJoin(base CommunityEventsTestsIn s.Require().NotNil(updatedCommunity) s.Require().True(updatedCommunity.HasMember(&newPrivilegedUser.identity.PublicKey)) - // privileged user should receive request to join from user and its shared addresses from control node _, err = WaitOnMessengerResponse( - newPrivilegedUser, + base.GetEventSender(), func(r *MessengerResponse) bool { - requestsToJoin, err := newPrivilegedUser.communitiesManager.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) - if err != nil { - return false - } - if len(requestsToJoin) != 4 { - s.T().Log("invalid requests to join count:", len(requestsToJoin)) - return false - } - - for _, request := range requestsToJoin { - if tokenPermissionType == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER { - if len(request.RevealedAccounts) != 1 { - s.T().Log("no accounts revealed") - return false - } - } else { - if len(request.RevealedAccounts) != 0 { - s.T().Log("unexpected accounts revealed") - return false - } - } - } - - return true + return len(r.Communities()) > 0 && + len(r.Communities()[0].TokenPermissionsByType(tokenPermissionType)) > 0 && + r.Communities()[0].HasPermissionToSendCommunityEvents() }, - "newPrivilegedUser did not receive all requests to join from the control node", + "newPrivilegedUser did not receive privileged role", ) + s.Require().NoError(err) + + expectedLength := 3 + // newPrivilegedUser user should receive all requests to join with shared addresses from the control node + waitAndCheckRequestsToJoin(s, newPrivilegedUser, expectedLength, community.ID(), tokenPermissionType == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER) + + // bob joins the community + requestMember := &requests.RequestToJoinCommunity{ + CommunityID: community.ID(), + AddressesToReveal: []string{bobAccountAddress}, + ENSName: "bob", + AirdropAddress: bobAccountAddress, + } + + bobRequestToJoinID := requestToJoinCommunity(s, base.GetControlNode(), bob, requestMember) + + // accept join request + acceptRequestToJoin = &requests.AcceptRequestToJoinCommunity{ID: bobRequestToJoinID} + _, err = base.GetControlNode().AcceptRequestToJoinCommunity(acceptRequestToJoin) + s.Require().NoError(err) + + expectedLength = 4 + waitAndCheckRequestsToJoin(s, newPrivilegedUser, expectedLength, community.ID(), tokenPermissionType == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER) } func testMemberReceiveRequestsToJoinAfterGettingNewRole(base CommunityEventsTestsInterface, bob *Messenger, tokenPermissionType protobuf.CommunityTokenPermission_Type) { @@ -2102,7 +2094,6 @@ func testMemberReceiveRequestsToJoinAfterGettingNewRole(base CommunityEventsTest // control node creates a community and chat community := createTestCommunity(base, protobuf.CommunityPermissions_MANUAL_ACCEPT) - refreshMessengerResponses(base) advertiseCommunityTo(s, community, base.GetControlNode(), base.GetEventSender()) advertiseCommunityTo(s, community, base.GetControlNode(), base.GetMember()) @@ -2152,27 +2143,56 @@ func testMemberReceiveRequestsToJoinAfterGettingNewRole(base CommunityEventsTest s.Require().NoError(err) assertCheckTokenPermissionCreated(s, ownerCommunity, rolePermission.Type) - // receive request to join msg _, err = WaitOnMessengerResponse( base.GetEventSender(), func(r *MessengerResponse) bool { - return len(r.RequestsToJoinCommunity) > 2 + return len(r.Communities()) > 0 && + len(r.Communities()[0].TokenPermissionsByType(tokenPermissionType)) > 0 && + r.Communities()[0].HasPermissionToSendCommunityEvents() }, - "Event sender did not receive all requests to join from the control node", + "event sender did not receive privileged role", + ) + + s.Require().NoError(err) + + expectedLength := 3 + waitAndCheckRequestsToJoin(s, base.GetEventSender(), expectedLength, community.ID(), tokenPermissionType == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER) +} + +func waitAndCheckRequestsToJoin(s *suite.Suite, user *Messenger, expectedLength int, communityID types.HexBytes, checkRevealedAddresses bool) { + _, err := WaitOnMessengerResponse( + user, + func(r *MessengerResponse) bool { + requestsToJoin, err := user.communitiesManager.GetCommunityRequestsToJoinWithRevealedAddresses(communityID) + if err != nil { + return false + } + if len(requestsToJoin) != expectedLength { + s.T().Log("invalid requests to join count:", len(requestsToJoin)) + return false + } + + for _, request := range requestsToJoin { + if request.PublicKey == common.PubkeyToHex(&user.identity.PublicKey) { + if len(request.RevealedAccounts) != 1 { + s.T().Log("our own requests to join must always have accounts revealed") + return false + } + } else if checkRevealedAddresses { + if len(request.RevealedAccounts) != 1 { + s.T().Log("no accounts revealed") + return false + } + } else { + if len(request.RevealedAccounts) != 0 { + s.T().Log("unexpected accounts revealed") + return false + } + } + } + return true + }, + "user did not receive all requests to join from the control node", ) s.Require().NoError(err) - - requestsToJoin, err := base.GetEventSender().communitiesManager.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID()) - s.Require().NoError(err) - // event sender, bob and alice requests - s.Require().Len(requestsToJoin, 3) - - // check if revealed addresses are present based on the role - for _, request := range requestsToJoin { - if tokenPermissionType == protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER { - s.Require().Len(request.RevealedAccounts, 1) - } else { - s.Require().Len(request.RevealedAccounts, 0) - } - } } diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index 59f1d959e..2e76f3cff 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -3026,18 +3026,18 @@ func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMess return err } + if community == nil { + return communities.ErrOrgNotFound + } + // 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 - isControlNodeMsg := common.IsPubKeyEqual(community.PublicKey(), signer) + isControlNodeMsg := common.IsPubKeyEqual(community.ControlNode(), signer) if !isControlNodeMsg { return errors.New("accepted/requested to join sync messages can be send only by the control node") } - if community == nil { - return errors.New("community not found") - } - err = m.communitiesManager.ValidateCommunityPrivilegedUserSyncMessage(message) if err != nil { return err @@ -3054,11 +3054,11 @@ func (m *Messenger) handleCommunityPrivilegedUserSyncMessage(state *ReceivedMess state.Response.AddRequestsToJoinCommunity(requestsToJoin) case protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ALL_SYNC_REQUESTS_TO_JOIN: - requestsToJoin, err := m.communitiesManager.HandleSyncAllRequestToJoinForNewPrivilegedMember(message, community.ID()) + nonAcceptedRequestsToJoin, err := m.communitiesManager.HandleSyncAllRequestToJoinForNewPrivilegedMember(message, community.ID()) if err != nil { return nil } - state.Response.AddRequestsToJoinCommunity(requestsToJoin) + state.Response.AddRequestsToJoinCommunity(nonAcceptedRequestsToJoin) } return nil