status-im/status-go

View on GitHub
protocol/communities_messenger_shared_member_address_test.go

Summary

Maintainability
B
5 hrs
Test Coverage
package protocol

import (
    "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
    mockedCollectibles      communities.CollectiblesByChain
    collectiblesManagerMock CollectiblesManagerMock
}

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.mockedCollectibles = communities.CollectiblesByChain{}
    s.collectiblesManagerMock = CollectiblesManagerMock{
        Collectibles: &s.mockedCollectibles,
    }

    s.resetMockedBalances()

    s.logger = tt.MustCreateTestLogger()

    wakuNodes := CreateWakuV2Network(&s.Suite, s.logger, []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,
        collectiblesManager: &s.collectiblesManagerMock,
    })
}

func (s *MessengerCommunitiesSharedMemberAddressSuite) joinCommunity(community *communities.Community, user *Messenger, password string, addresses []string) {
    joinCommunity(&s.Suite, community.ID(), s.owner, user, password, addresses)
}

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) {
    makeAddressSatisfyTheCriteria(&s.Suite, s.mockedBalances, s.mockedCollectibles, chainID, address, criteria)
}

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) joinOnRequestCommunityAsTokenMaster(community *communities.Community) {
    bobRequest := createRequestToJoinCommunity(&s.Suite, community.ID(), s.bob, bobPassword, []string{bobAddress})
    requestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.bob, bobRequest)
    // accept join request
    acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoinID}
    response, err := s.owner.AcceptRequestToJoinCommunity(acceptRequestToJoin)
    s.Require().NoError(err)
    s.Require().NotNil(response)

    updatedCommunity := response.Communities()[0]
    s.Require().NotNil(updatedCommunity)
    s.Require().True(updatedCommunity.HasMember(&s.bob.identity.PublicKey))
    s.Require().True(updatedCommunity.IsMemberTokenMaster(&s.bob.identity.PublicKey))

    // receive request to join response
    _, err = WaitOnMessengerResponse(
        s.bob,
        func(r *MessengerResponse) bool {
            return len(r.Communities()) > 0 &&
                r.Communities()[0].HasMember(&s.bob.identity.PublicKey) &&
                r.Communities()[0].IsMemberTokenMaster(&s.bob.identity.PublicKey)
        },
        "user did not receive request to join response",
    )
    s.Require().NoError(err)

    userCommunity, err := s.bob.GetCommunityByID(community.ID())
    s.Require().NoError(err)
    s.Require().True(userCommunity.HasMember(&s.bob.identity.PublicKey))
    s.Require().True(userCommunity.IsTokenMaster())
}

func (s *MessengerCommunitiesSharedMemberAddressSuite) waitForRevealedAddresses(receiver *Messenger, communityID types.HexBytes, expectedAccounts []*protobuf.RevealedAccount) {
    _, err := WaitOnMessengerResponse(receiver, func(r *MessengerResponse) bool {
        revealedAccounts, err := receiver.communitiesManager.GetRevealedAddresses(communityID, s.alice.IdentityPublicKeyString())
        if err != nil {
            return false
        }
        if len(expectedAccounts) != len(revealedAccounts) {
            return false
        }

        for index := range revealedAccounts {
            if revealedAccounts[index].Address != expectedAccounts[index].Address {
                return false
            }
        }

        return true
    }, "client did not receive alice shared address")
    s.Require().NoError(err)
}

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.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1, 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[0].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
    s.waitForRevealedAddresses(s.owner, community.ID(), 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)

    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.waitForRevealedAddresses(s.bob, community.ID(), expectedAliceRevealedAccounts)

    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)

    s.waitForRevealedAddresses(s.owner, community.ID(), expectedAliceRevealedAccounts)

    s.Require().NoError(err)

    s.waitForRevealedAddresses(s.bob, community.ID(), 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})
    s.Require().NoError(err)

    // 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.waitForRevealedAddresses(s.bob, community.ID(), expectedAliceRevealedAccounts)

    // remove alice revealed addresses
    requestToDelete, err := s.bob.communitiesManager.GetRequestToJoinByPkAndCommunityID(s.alice.IdentityPublicKey(), community.ID())
    s.Require().NoError(err)
    err = s.bob.communitiesManager.RemoveRequestToJoinRevealedAddresses(requestToDelete.ID)
    s.Require().NoError(err)
    err = s.bob.communitiesManager.DeletePendingRequestToJoin(requestToDelete)
    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()
            aliceAccounts, err := s.bob.communitiesManager.GetRevealedAddresses(community.ID(), s.alice.IdentityPublicKeyString())
            s.Require().NoError(err)
            return len(aliceAccounts) > 0 && aliceAccounts[0].Address == aliceAddress1
        },
        "alice request to join with revealed addresses not received",
    )
    s.Require().NoError(err)
}

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})

    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)

    // check that bob received revealed address
    _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool {
        return checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, len(expectedAliceRevealedAccounts))
    }, "user not accepted")
    s.Require().NoError(err)

    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.waitForRevealedAddresses(s.bob, community.ID(), 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)

    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)

    _, 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.waitForRevealedAddresses(s.bob, community.ID(), 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)

    aliceRequest := createRequestToJoinCommunity(&s.Suite, community.ID(), s.alice, alicePassword, []string{aliceAddress1})
    aliceRequestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.alice, aliceRequest)

    s.joinOnRequestCommunityAsTokenMaster(community)

    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 {
        return checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, 1)
    }, "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)

    aliceRequest := createRequestToJoinCommunity(&s.Suite, community.ID(), s.alice, alicePassword, []string{aliceAddress1})
    aliceRequestToJoinID := requestToJoinCommunity(&s.Suite, s.owner, s.alice, aliceRequest)

    joinOnRequestCommunity(&s.Suite, community.ID(), s.owner, s.bob, bobPassword, []string{bobAddress})

    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)

    _, 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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, 1)
    }, "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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, 1)
    }, "bob did not receive alice revealed addresses")
    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
    s.waitForRevealedAddresses(s.owner, community.ID(), 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)

    _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{
        CommunityID:   community.ID(),
        Type:          protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER,
        TokenCriteria: []*protobuf.TokenCriteria{tokenCriteria},
    })
    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.waitForRevealedAddresses(s.bob, community.ID(), 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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, 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
    s.waitForRevealedAddresses(s.bob, community.ID(), 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)
    s.joinOnRequestCommunityAsTokenMaster(community)

    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)

    aliceRequest := createRequestToJoinCommunity(&s.Suite, community.ID(), s.alice, alicePassword, []string{aliceAddress1})
    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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStatePending, 0)
    }, "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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateDeclined, 0)
    }, "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 checkRequestToJoinInResponse(r, s.alice, communities.RequestToJoinStateAccepted, len(aliceRequest.AddressesToReveal))
    }, "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)
}