Skip to content

Commit

Permalink
fix_: mitigate permission stuck in pending state
Browse files Browse the repository at this point in the history
This PR mitigates permission stuck in pending state upon making device a
control node. It fixes
[#14023](status-im/status-desktop#14023)
  • Loading branch information
kounkou committed Apr 24, 2024
1 parent 9e5462e commit fab6ab3
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 4 deletions.
13 changes: 9 additions & 4 deletions protocol/communities/community_events_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,11 +311,16 @@ func (o *Community) addNewCommunityEvent(event *CommunityEvent) error {
}

func (o *Community) toCommunityEventsMessage() *CommunityEventsMessage {
return &CommunityEventsMessage{
CommunityID: o.ID(),
EventsBaseCommunityDescription: o.config.EventsData.EventsBaseCommunityDescription,
Events: o.config.EventsData.Events,
message := &CommunityEventsMessage{
CommunityID: o.ID(),
}

if o.config != nil && o.config.EventsData != nil {
message.EventsBaseCommunityDescription = o.config.EventsData.EventsBaseCommunityDescription
message.Events = o.config.EventsData.Events
}

return message
}

func validateAndGetEventsMessageCommunityDescription(signedDescription []byte, signerPubkey *ecdsa.PublicKey) (*protobuf.CommunityDescription, error) {
Expand Down
31 changes: 31 additions & 0 deletions protocol/communities/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5147,6 +5147,17 @@ func (m *Manager) PromoteSelfToControlNode(community *Community, clock uint64) (
return community.emptyCommunityChanges(), m.saveAndPublish(community)
}

func (m *Manager) applyCommunityEvents(community *Community) map[string]uint64 {
appliedEvents := map[string]uint64{}
if community.config.EventsData != nil {
for _, event := range community.config.EventsData.Events {
appliedEvents[event.EventTypeID()] = event.CommunityEventClock
}
}

return appliedEvents
}

func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) (bool, error) {
ownerChanged := false
community.setPrivateKey(m.identity)
Expand Down Expand Up @@ -5188,11 +5199,31 @@ func (m *Manager) promoteSelfToControlNode(community *Community, clock uint64) (
return false, err
}

if community.IsControlNode() {
err = m.handleCommunityEvents(community)
if err != nil {
return false, err
}
}

community.increaseClock()

return ownerChanged, nil
}

func (m *Manager) handleCommunityEvents(community *Community) error {
_, err := m.handleAdditionalAdminChanges(community)
if err != nil {
return err
}

if err = m.handleCommunityTokensMetadata(community); err != nil {
return err
}

return nil
}

func (m *Manager) shareRequestsToJoinWithNewPrivilegedMembers(community *Community, newPrivilegedMembers map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey) error {
requestsToJoin, err := m.GetCommunityRequestsToJoinWithRevealedAddresses(community.ID())
if err != nil {
Expand Down
115 changes: 115 additions & 0 deletions protocol/communities_messenger_signers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,3 +732,118 @@ func (s *MessengerCommunitiesSignersSuite) TestSyncTokenGatedCommunity() {
})
}
}

// func (s *MessengerCommunitiesSignersSuite) TestPendingCommunityEventsAppliedUponMakingDeviceControlNode() {
// community := s.createCommunity(s.john)

// s.advertiseCommunityTo(s.john, community, s.bob)
// s.advertiseCommunityTo(s.john, community, s.alice)

// s.joinCommunity(s.john, community, s.bob)
// s.joinCommunity(s.john, community, s.alice)

// // makes Alice to be admin
// grantPermission(&s.Suite, community, s.john, s.alice, protobuf.CommunityMember_ROLE_ADMIN)

// // john mints owner token
// var chainID uint64 = 1
// tokenAddress := "token-address"
// tokenName := "tokenName"
// tokenSymbol := "TSM"
// _, err := s.john.SaveCommunityToken(&token.CommunityToken{
// TokenType: protobuf.CommunityTokenType_ERC721,
// CommunityID: community.IDString(),
// Address: tokenAddress,
// ChainID: int(chainID),
// Name: tokenName,
// Supply: &bigint.BigInt{},
// Symbol: tokenSymbol,
// PrivilegesLevel: token.OwnerLevel,
// }, nil)
// s.Require().NoError(err)

// // john adds minted owner token to community
// err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
// s.Require().NoError(err)

// // update mock - the signer for the community returned by the contracts should be john
// s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
// s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
// &communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})

// // bob accepts community update
// _, err = WaitOnSignaledMessengerResponse(
// s.bob,
// func(r *MessengerResponse) bool {
// return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
// },
// "no communities",
// )
// s.Require().NoError(err)

// // alice accepts community update
// _, err = WaitOnSignaledMessengerResponse(
// s.alice,
// func(r *MessengerResponse) bool {
// return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
// },
// "no communities",
// )
// s.Require().NoError(err)

// // admin bob creates a permission that would require the owner to be present
// createTokenPermission := &requests.CreateCommunityTokenPermission{
// CommunityID: community.ID(),
// Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
// TokenCriteria: []*protobuf.TokenCriteria{
// &protobuf.TokenCriteria{
// Type: protobuf.CommunityTokenType_ERC20,
// ContractAddresses: map[uint64]string{uint64(testChainID1): "0x123"},
// Symbol: "TEST",
// AmountInWei: "100000000000000000000",
// Decimals: uint64(18),
// },
// },
// }

// response, err := s.alice.CreateCommunityTokenPermission(createTokenPermission)
// s.Require().NotNil(response)
// s.Require().Len(response.CommunityChanges, 1)
// s.Require().Len(response.CommunityChanges[0].TokenPermissionsAdded, 1)

// addedPermission := func() *communities.CommunityTokenPermission {
// for _, permission := range response.CommunityChanges[0].TokenPermissionsAdded {
// return permission
// }
// return nil
// }()
// s.Require().NotNil(addedPermission)

// community, err = s.alice.communitiesManager.GetByID(community.ID())
// s.Require().NoError(err)
// s.Require().False(community.IsControlNode())
// s.Require().False(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
// s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.john.identity.PublicKey))
// s.Require().False(community.IsOwner())
// s.Require().Equal(communities.TokenPermissionAdditionPending, addedPermission.State)

// response, err = s.alice.PromoteSelfToControlNode(community.ID())
// s.Require().NoError(err)
// s.Require().NotNil(response)

// addedPermission = func() *communities.CommunityTokenPermission {
// for _, permission := range response.CommunityChanges[0].TokenPermissionsAdded {
// return permission
// }
// return nil
// }()
// s.Require().NotNil(addedPermission)

// community, err = s.alice.communitiesManager.GetByID(community.ID())
// s.Require().NoError(err)
// s.Require().True(community.IsControlNode())
// s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
// s.Require().False(common.IsPubKeyEqual(community.ControlNode(), &s.john.identity.PublicKey))
// s.Require().True(community.IsOwner())
// s.Require().Equal(communities.TokenPermissionApproved, addedPermission.State)
// }
84 changes: 84 additions & 0 deletions protocol/communities_messenger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4225,3 +4225,87 @@ func (s *MessengerCommunitiesSuite) TestIsDisplayNameDupeOfCommunityMember() {
s.Require().NoError(err)
s.Require().False(result)
}

func (s *MessengerCommunitiesSuite) TestPendingCommunityEventsAppliedUponMakingDeviceControlNode() {
aim := &multidevice.InstallationMetadata{
Name: "alice's-device",
DeviceType: "alice's-device-type",
}
err := s.alice.SetInstallationMetadata(s.alice.installationID, aim)
s.Require().NoError(err)

alicesOtherDevice := s.createOtherDevice(s.alice)

PairDevices(&s.Suite, alicesOtherDevice, s.alice)
PairDevices(&s.Suite, s.alice, alicesOtherDevice)

bobCommunities, err := s.bob.communitiesManager.All()
s.Require().NoError(err, "admin.communitiesManager.All")
s.Len(bobCommunities, 1, "Must have 1 communities")

createCommunityReq := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
Name: "new community",
Color: "#000000",
Description: "new community description",
}
createCommunityRequest, err := s.alice.CreateCommunity(createCommunityReq, true)
s.Require().NoError(err, "CreateCommunity")
s.Require().NotNil(createCommunityRequest)
s.Len(createCommunityRequest.Communities(), 1)

community := createCommunityRequest.Communities()[0]

// Check that alice has 2 communities
aliceCommunities, err := s.alice.communitiesManager.All()
s.Require().NoError(err, "communitiesManager.All")
s.Len(aliceCommunities, 2, "Must have 2 communities")

s.advertiseCommunityTo(community, s.alice, s.bob)
s.joinCommunity(community, s.alice, s.bob)

grantPermission(&s.Suite, community, s.alice, s.bob, protobuf.CommunityMember_ROLE_ADMIN)

other := s.newMessenger()
s.advertiseCommunityTo(community, s.alice, other)

// user other request to join
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
response, err := other.RequestToJoinCommunity(requestToJoin)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity(), 1)

sentRequest := response.RequestsToJoinCommunity()[0]

community, err = s.alice.communitiesManager.GetByID(aliceCommunities[0].ID())
s.Require().NoError(err)
s.Require().False(community.IsControlNode())
s.Require().False(common.IsPubKeyEqual(community.ControlNode(), &s.alice.identity.PublicKey))
s.Require().False(community.IsOwner())

// s.alice dies
s.alice = nil

// aliceOtherDevice promotes herself to contorl node
alicesOtherDevice.PromoteSelfToControlNode(community.ID())

// aliceOtherDevice receives request to join
_, err = WaitOnMessengerResponse(
alicesOtherDevice,
func(r *MessengerResponse) bool { return len(r.RequestsToJoinCommunity()) > 0 },
"event sender did not receive community request to join",
)
s.Require().NoError(err)

rejectRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: sentRequest.ID}
response, err = s.bob.DeclineRequestToJoinCommunity(rejectRequestToJoin)
s.Require().NoError(err)
s.Require().NotNil(response)

community, err = s.bob.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.IsControlNode())
s.Require().True(common.IsPubKeyEqual(community.ControlNode(), &s.bob.identity.PublicKey))
s.Require().True(community.IsOwner())
}

0 comments on commit fab6ab3

Please sign in to comment.