github.com/status-im/status-go@v1.1.0/protocol/messenger_storenode_request_test.go (about)

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/libp2p/go-libp2p/core/peer"
    11  	"github.com/multiformats/go-multiaddr"
    12  	"github.com/stretchr/testify/suite"
    13  	"go.uber.org/zap"
    14  	"google.golang.org/protobuf/proto"
    15  
    16  	"github.com/waku-org/go-waku/waku/v2/protocol/legacy_store"
    17  
    18  	"github.com/ethereum/go-ethereum/common/hexutil"
    19  	"github.com/ethereum/go-ethereum/crypto"
    20  
    21  	"github.com/status-im/status-go/appdatabase"
    22  	gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
    23  	"github.com/status-im/status-go/eth-node/types"
    24  	"github.com/status-im/status-go/multiaccounts/accounts"
    25  	"github.com/status-im/status-go/params"
    26  	"github.com/status-im/status-go/protocol/common"
    27  	"github.com/status-im/status-go/protocol/common/shard"
    28  	"github.com/status-im/status-go/protocol/communities"
    29  	"github.com/status-im/status-go/protocol/communities/token"
    30  	"github.com/status-im/status-go/protocol/protobuf"
    31  	"github.com/status-im/status-go/protocol/requests"
    32  	"github.com/status-im/status-go/protocol/transport"
    33  	"github.com/status-im/status-go/protocol/tt"
    34  	mailserversDB "github.com/status-im/status-go/services/mailservers"
    35  	"github.com/status-im/status-go/services/wallet/bigint"
    36  	"github.com/status-im/status-go/t/helpers"
    37  	waku2 "github.com/status-im/status-go/wakuv2"
    38  	wakuV2common "github.com/status-im/status-go/wakuv2/common"
    39  )
    40  
    41  const (
    42  	localFleet              = "local-test-fleet-1"
    43  	localMailserverID       = "local-test-mailserver"
    44  	storeNodeConnectTimeout = 500 * time.Millisecond
    45  	runLocalTests           = false
    46  )
    47  
    48  func TestMessengerStoreNodeRequestSuite(t *testing.T) {
    49  	t.Skip("requires storev3 node")
    50  	suite.Run(t, new(MessengerStoreNodeRequestSuite))
    51  }
    52  
    53  type MessengerStoreNodeRequestSuite struct {
    54  	suite.Suite
    55  
    56  	cancel chan struct{}
    57  
    58  	owner *Messenger
    59  	bob   *Messenger
    60  
    61  	wakuStoreNode    *waku2.Waku
    62  	storeNodeAddress multiaddr.Multiaddr
    63  
    64  	ownerWaku types.Waku
    65  	bobWaku   types.Waku
    66  
    67  	collectiblesServiceMock *CollectiblesServiceMock
    68  
    69  	logger *zap.Logger
    70  }
    71  
    72  type singleResult struct {
    73  	EnvelopesCount   int
    74  	Envelopes        []*wakuV2common.ReceivedMessage
    75  	ShardEnvelopes   []*wakuV2common.ReceivedMessage
    76  	Error            error
    77  	FetchedCommunity *communities.Community
    78  }
    79  
    80  func (r *singleResult) ShardEnvelopesHashes() []string {
    81  	out := make([]string, 0, len(r.ShardEnvelopes))
    82  	for _, e := range r.ShardEnvelopes {
    83  		out = append(out, e.Hash().String())
    84  	}
    85  	return out
    86  }
    87  
    88  func (r *singleResult) EnvelopesHashes() []string {
    89  	out := make([]string, 0, len(r.Envelopes))
    90  	for _, e := range r.Envelopes {
    91  		out = append(out, e.Hash().String())
    92  	}
    93  	return out
    94  }
    95  
    96  func (r *singleResult) toString() string {
    97  	resultString := ""
    98  	communityString := ""
    99  
   100  	if r.FetchedCommunity != nil {
   101  		communityString = fmt.Sprintf("clock: %d, name: '%s', members: %d",
   102  			r.FetchedCommunity.Clock(),
   103  			r.FetchedCommunity.Name(),
   104  			len(r.FetchedCommunity.Members()),
   105  		)
   106  	}
   107  
   108  	if r.Error != nil {
   109  		resultString = fmt.Sprintf("error: %s", r.Error.Error())
   110  	} else {
   111  		resultString = fmt.Sprintf("envelopes fetched: %d, community - %s",
   112  			r.EnvelopesCount, communityString)
   113  	}
   114  
   115  	for i, envelope := range r.ShardEnvelopes {
   116  		resultString += fmt.Sprintf("\n\tshard envelope %3.0d: %s, timestamp: %d (%s), size: %d bytes, contentTopic: %s, pubsubTopic: %s",
   117  			i+1,
   118  			envelope.Hash().Hex(),
   119  			envelope.Envelope.Message().GetTimestamp(),
   120  			time.Unix(0, envelope.Envelope.Message().GetTimestamp()).UTC(),
   121  			len(envelope.Envelope.Message().Payload),
   122  			envelope.Envelope.Message().ContentTopic,
   123  			envelope.Envelope.PubsubTopic(),
   124  		)
   125  	}
   126  
   127  	for i, envelope := range r.Envelopes {
   128  		resultString += fmt.Sprintf("\n\tdescription envelope %3.0d: %s, timestamp: %d (%s), size: %d bytes, contentTopic: %s, pubsubTopic: %s",
   129  			i+1,
   130  			envelope.Hash().Hex(),
   131  			envelope.Envelope.Message().GetTimestamp(),
   132  			time.Unix(0, envelope.Envelope.Message().GetTimestamp()).UTC(),
   133  			len(envelope.Envelope.Message().Payload),
   134  			envelope.Envelope.Message().ContentTopic,
   135  			envelope.Envelope.PubsubTopic(),
   136  		)
   137  	}
   138  
   139  	return resultString
   140  }
   141  
   142  func (s *MessengerStoreNodeRequestSuite) SetupTest() {
   143  	s.logger = tt.MustCreateTestLogger()
   144  
   145  	s.cancel = make(chan struct{}, 10)
   146  
   147  	s.collectiblesServiceMock = &CollectiblesServiceMock{}
   148  
   149  	s.createStore()
   150  }
   151  
   152  func (s *MessengerStoreNodeRequestSuite) TearDown() {
   153  	close(s.cancel)
   154  	s.Require().NoError(s.wakuStoreNode.Stop())
   155  	TearDownMessenger(&s.Suite, s.owner)
   156  	TearDownMessenger(&s.Suite, s.bob)
   157  }
   158  
   159  func (s *MessengerStoreNodeRequestSuite) createStore() {
   160  	cfg := testWakuV2Config{
   161  		logger:      s.logger.Named("store-waku"),
   162  		enableStore: true,
   163  		clusterID:   shard.MainStatusShardCluster,
   164  	}
   165  
   166  	s.wakuStoreNode = NewTestWakuV2(&s.Suite, cfg)
   167  	s.storeNodeAddress = s.wakuListenAddress(s.wakuStoreNode)
   168  	s.logger.Info("store node ready", zap.Stringer("address", s.storeNodeAddress))
   169  }
   170  
   171  func (s *MessengerStoreNodeRequestSuite) tearDownOwner() {
   172  	_ = gethbridge.GetGethWakuV2From(s.ownerWaku).Stop()
   173  	TearDownMessenger(&s.Suite, s.owner)
   174  }
   175  
   176  func (s *MessengerStoreNodeRequestSuite) createOwner() {
   177  
   178  	cfg := testWakuV2Config{
   179  		logger:      s.logger.Named("owner-waku"),
   180  		enableStore: false,
   181  		clusterID:   shard.MainStatusShardCluster,
   182  	}
   183  
   184  	wakuV2 := NewTestWakuV2(&s.Suite, cfg)
   185  	s.ownerWaku = gethbridge.NewGethWakuV2Wrapper(wakuV2)
   186  
   187  	messengerLogger := s.logger.Named("owner-messenger")
   188  	s.owner = s.newMessenger(s.ownerWaku, messengerLogger, &s.storeNodeAddress)
   189  
   190  	// We force the owner to use the store node as relay peer
   191  	WaitForPeersConnected(&s.Suite, gethbridge.GetGethWakuV2From(s.ownerWaku), func() peer.IDSlice {
   192  		err := s.owner.DialPeer(s.storeNodeAddress)
   193  		s.Require().NoError(err)
   194  		return peer.IDSlice{s.wakuStoreNode.PeerID()}
   195  	})
   196  }
   197  
   198  func (s *MessengerStoreNodeRequestSuite) createBob() {
   199  	cfg := testWakuV2Config{
   200  		logger:      s.logger.Named("bob-waku"),
   201  		enableStore: false,
   202  		clusterID:   shard.MainStatusShardCluster,
   203  	}
   204  	wakuV2 := NewTestWakuV2(&s.Suite, cfg)
   205  	s.bobWaku = gethbridge.NewGethWakuV2Wrapper(wakuV2)
   206  
   207  	messengerLogger := s.logger.Named("bob-messenger")
   208  	s.bob = s.newMessenger(s.bobWaku, messengerLogger, &s.storeNodeAddress)
   209  }
   210  
   211  func (s *MessengerStoreNodeRequestSuite) tearDownBob() {
   212  	_ = gethbridge.GetGethWakuV2From(s.bobWaku).Stop()
   213  	TearDownMessenger(&s.Suite, s.bob)
   214  }
   215  
   216  func (s *MessengerStoreNodeRequestSuite) newMessenger(shh types.Waku, logger *zap.Logger, mailserverAddress *multiaddr.Multiaddr) *Messenger {
   217  	privateKey, err := crypto.GenerateKey()
   218  	s.Require().NoError(err)
   219  
   220  	options := []Option{
   221  		WithAutoRequestHistoricMessages(false),
   222  		WithCuratedCommunitiesUpdateLoop(false),
   223  	}
   224  
   225  	if mailserverAddress != nil {
   226  		options = append(options,
   227  			WithTestStoreNode(&s.Suite, localMailserverID, *mailserverAddress, localFleet, s.collectiblesServiceMock),
   228  		)
   229  	}
   230  
   231  	messenger, err := newMessengerWithKey(shh, privateKey, logger, options)
   232  
   233  	s.Require().NoError(err)
   234  	return messenger
   235  }
   236  
   237  func (s *MessengerStoreNodeRequestSuite) createCommunity(m *Messenger) *communities.Community {
   238  	s.waitForAvailableStoreNode(m)
   239  
   240  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(nil)
   241  
   242  	createCommunityRequest := &requests.CreateCommunity{
   243  		Name:        RandomLettersString(10),
   244  		Description: RandomLettersString(20),
   245  		Color:       RandomColor(),
   246  		Tags:        RandomCommunityTags(3),
   247  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   248  	}
   249  
   250  	response, err := m.CreateCommunity(createCommunityRequest, false)
   251  	s.Require().NoError(err)
   252  	s.Require().NotNil(response)
   253  	s.Require().Len(response.Communities(), 1)
   254  
   255  	s.waitForEnvelopes(storeNodeSubscription, 1)
   256  
   257  	return response.Communities()[0]
   258  }
   259  
   260  func (s *MessengerStoreNodeRequestSuite) requireCommunitiesEqual(c *communities.Community, expected *communities.Community) {
   261  	if expected == nil {
   262  		s.Require().Nil(c)
   263  		return
   264  	}
   265  	s.Require().NotNil(c)
   266  	s.Require().Equal(expected.IDString(), c.IDString())
   267  	s.Require().Equal(expected.Clock(), c.Clock())
   268  	s.Require().Equal(expected.Name(), c.Name())
   269  	s.Require().Equal(expected.Identity().Description, c.Identity().Description)
   270  	s.Require().Equal(expected.Color(), c.Color())
   271  	s.Require().Equal(expected.Tags(), c.Tags())
   272  	s.Require().Equal(expected.Shard(), c.Shard())
   273  	s.Require().Equal(len(expected.TokenPermissions()), len(c.TokenPermissions()))
   274  	for k, v := range expected.TokenPermissions() {
   275  		s.Require().True(proto.Equal(v, c.TokenPermissions()[k]))
   276  	}
   277  	s.Require().Equal(len(expected.CommunityTokensMetadata()), len(c.CommunityTokensMetadata()))
   278  	for i, v := range expected.CommunityTokensMetadata() {
   279  		s.Require().True(proto.Equal(v, c.CommunityTokensMetadata()[i]))
   280  	}
   281  }
   282  
   283  func (s *MessengerStoreNodeRequestSuite) requireContactsEqual(c *Contact, expected *Contact) {
   284  	s.Require().Equal(expected.DisplayName, c.DisplayName)
   285  	s.Require().Equal(expected.Bio, c.Bio)
   286  }
   287  
   288  func (s *MessengerStoreNodeRequestSuite) fetchCommunity(m *Messenger, communityShard communities.CommunityShard, expectedCommunity *communities.Community) StoreNodeRequestStats {
   289  	options := []StoreNodeRequestOption{
   290  		WithWaitForResponseOption(true),
   291  	}
   292  
   293  	fetchedCommunity, stats, err := m.storeNodeRequestsManager.FetchCommunity(communityShard, options)
   294  
   295  	s.Require().NoError(err)
   296  	s.requireCommunitiesEqual(fetchedCommunity, expectedCommunity)
   297  
   298  	return stats
   299  }
   300  
   301  func (s *MessengerStoreNodeRequestSuite) fetchProfile(m *Messenger, contactID string, expectedContact *Contact) {
   302  	fetchedContact, err := m.FetchContact(contactID, true)
   303  	s.Require().NoError(err)
   304  	s.Require().NotNil(fetchedContact)
   305  	s.Require().Equal(contactID, fetchedContact.ID)
   306  
   307  	if expectedContact != nil {
   308  		s.requireContactsEqual(fetchedContact, expectedContact)
   309  	}
   310  }
   311  
   312  func (s *MessengerStoreNodeRequestSuite) waitForAvailableStoreNode(messenger *Messenger) {
   313  	WaitForAvailableStoreNode(&s.Suite, messenger, storeNodeConnectTimeout)
   314  }
   315  
   316  func (s *MessengerStoreNodeRequestSuite) setupEnvelopesWatcher(wakuNode *waku2.Waku, topic *wakuV2common.TopicType, cb func(envelope *wakuV2common.ReceivedMessage)) {
   317  	envelopesWatcher := make(chan wakuV2common.EnvelopeEvent, 100)
   318  	envelopesSub := wakuNode.SubscribeEnvelopeEvents(envelopesWatcher)
   319  
   320  	go func() {
   321  		defer envelopesSub.Unsubscribe()
   322  		for {
   323  			select {
   324  			case <-s.cancel:
   325  				return
   326  
   327  			case envelopeEvent := <-envelopesWatcher:
   328  				if envelopeEvent.Event != wakuV2common.EventEnvelopeAvailable {
   329  					continue
   330  				}
   331  				if topic != nil && *topic != envelopeEvent.Topic {
   332  					continue
   333  				}
   334  				envelope := wakuNode.GetEnvelope(envelopeEvent.Hash)
   335  				cb(envelope)
   336  				s.logger.Debug("envelope available event for fetched content topic",
   337  					zap.Any("envelopeEvent", envelopeEvent),
   338  					zap.Any("envelope", envelope),
   339  				)
   340  			}
   341  
   342  		}
   343  	}()
   344  }
   345  
   346  func (s *MessengerStoreNodeRequestSuite) setupStoreNodeEnvelopesWatcher(topic *wakuV2common.TopicType) <-chan string {
   347  	storeNodeSubscription := make(chan string, 100)
   348  	s.setupEnvelopesWatcher(s.wakuStoreNode, topic, func(envelope *wakuV2common.ReceivedMessage) {
   349  		storeNodeSubscription <- envelope.Hash().String()
   350  	})
   351  	return storeNodeSubscription
   352  }
   353  
   354  func (s *MessengerStoreNodeRequestSuite) waitForEnvelopes(subscription <-chan string, expectedEnvelopesCount int) {
   355  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   356  	defer cancel()
   357  
   358  	for i := 0; i < expectedEnvelopesCount; i++ {
   359  		select {
   360  		case <-subscription:
   361  		case <-ctx.Done():
   362  			err := fmt.Sprintf("timeout waiting for store node to receive envelopes, received: %d, expected: %d", i, expectedEnvelopesCount)
   363  			s.Require().Fail(err)
   364  		}
   365  	}
   366  }
   367  
   368  func (s *MessengerStoreNodeRequestSuite) wakuListenAddress(waku *waku2.Waku) multiaddr.Multiaddr {
   369  	addresses := waku.ListenAddresses()
   370  	s.Require().LessOrEqual(1, len(addresses))
   371  	return addresses[0]
   372  }
   373  
   374  func (s *MessengerStoreNodeRequestSuite) ensureStoreNodeEnvelopes(contentTopic *wakuV2common.TopicType, minimumCount int) {
   375  	// Give some time for store node to put envelope into database. Otherwise, the test is flaky.
   376  	// Although we subscribed to EnvelopeEvents and waited, the actual saving to database happens asynchronously.
   377  	// It would be nice to implement a subscription for database storing event, but it isn't worth it right now.
   378  	time.Sleep(100 * time.Millisecond)
   379  
   380  	// Directly ensure profile is available on store node
   381  	queryOptions := []legacy_store.HistoryRequestOption{
   382  		legacy_store.WithLocalQuery(),
   383  	}
   384  	query := legacy_store.Query{
   385  		PubsubTopic:   "",
   386  		ContentTopics: []string{contentTopic.ContentTopic()},
   387  	}
   388  	result, err := s.wakuStoreNode.LegacyStoreNode().Query(context.Background(), query, queryOptions...)
   389  	s.Require().NoError(err)
   390  	s.Require().GreaterOrEqual(len(result.Messages), minimumCount)
   391  	s.logger.Debug("store node query result", zap.Int("messagesCount", len(result.Messages)))
   392  }
   393  
   394  func (s *MessengerStoreNodeRequestSuite) TestRequestCommunityInfo() {
   395  	s.createOwner()
   396  	s.createBob()
   397  
   398  	community := s.createCommunity(s.owner)
   399  
   400  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   401  }
   402  
   403  func (s *MessengerStoreNodeRequestSuite) TestConsecutiveRequests() {
   404  	s.createOwner()
   405  	s.createBob()
   406  
   407  	community := s.createCommunity(s.owner)
   408  
   409  	// Test consecutive requests to check that requests in manager are finalized
   410  	// At second request we expect to fetch nothing, because the community is already in the database
   411  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   412  	s.fetchCommunity(s.bob, community.CommunityShard(), nil)
   413  }
   414  
   415  func (s *MessengerStoreNodeRequestSuite) TestSimultaneousCommunityInfoRequests() {
   416  	s.createOwner()
   417  	s.createBob()
   418  
   419  	community := s.createCommunity(s.owner)
   420  
   421  	storeNodeRequestsCount := 0
   422  	s.bob.storeNodeRequestsManager.onPerformingBatch = func(batch MailserverBatch) {
   423  		storeNodeRequestsCount++
   424  	}
   425  
   426  	s.waitForAvailableStoreNode(s.bob)
   427  
   428  	wg := sync.WaitGroup{}
   429  
   430  	// Make 2 simultaneous fetch requests
   431  	// 1 fetch request = 2 requests to store node (fetch shard and fetch community)
   432  	// only 2 request to store node is expected
   433  	for i := 0; i < 2; i++ {
   434  		wg.Add(1)
   435  		go func() {
   436  			defer wg.Done()
   437  			s.fetchCommunity(s.bob, community.CommunityShard(), community)
   438  		}()
   439  	}
   440  
   441  	wg.Wait()
   442  	s.Require().Equal(2, storeNodeRequestsCount)
   443  }
   444  
   445  func (s *MessengerStoreNodeRequestSuite) TestRequestNonExistentCommunity() {
   446  	// On test start store node database is empty, so just request any valid community ID.
   447  	request := FetchCommunityRequest{
   448  		CommunityKey:    "0x036dc11a0663f88e15912f0adb68c3c5f68ca0ca7a233f1a88ff923a3d39b2cf07",
   449  		Shard:           nil,
   450  		TryDatabase:     false,
   451  		WaitForResponse: true,
   452  	}
   453  
   454  	s.createBob()
   455  
   456  	s.waitForAvailableStoreNode(s.bob)
   457  	fetchedCommunity, err := s.bob.FetchCommunity(&request)
   458  
   459  	s.Require().NoError(err)
   460  	s.Require().Nil(fetchedCommunity)
   461  }
   462  
   463  func (s *MessengerStoreNodeRequestSuite) TestRequestCommunityInfoWithStoreNodeDisconnected() {
   464  	s.createOwner()
   465  	s.createBob()
   466  
   467  	community := s.createCommunity(s.owner)
   468  
   469  	// WaitForAvailableStoreNode is done internally
   470  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   471  }
   472  
   473  func (s *MessengerStoreNodeRequestSuite) TestRequestCommunityPagingAlgorithm() {
   474  	const spamAmount = defaultStoreNodeRequestPageSize + initialStoreNodeRequestPageSize
   475  
   476  	s.createOwner()
   477  	s.createBob()
   478  
   479  	// Create a community
   480  	community := s.createCommunity(s.owner)
   481  	contentTopic := wakuV2common.BytesToTopic(transport.ToTopic(community.IDString()))
   482  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentTopic)
   483  
   484  	// Push spam to the same ContentTopic & PubsubTopic
   485  	// The first requested page size is 1. All subsequent pages are limited to 20.
   486  	// We want to test the algorithm, so we push 21 spam envelopes.
   487  	for i := 0; i < spamAmount; i++ {
   488  		spamMessage := common.RawMessage{
   489  			Payload:             RandomBytes(16),
   490  			Sender:              community.PrivateKey(),
   491  			SkipEncryptionLayer: true,
   492  			MessageType:         protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION,
   493  			PubsubTopic:         community.PubsubTopic(),
   494  		}
   495  		_, err := s.owner.sender.SendPublic(context.Background(), community.IDString(), spamMessage)
   496  		s.Require().NoError(err)
   497  	}
   498  
   499  	// Wait the store node to receive envelopes
   500  	s.waitForEnvelopes(storeNodeSubscription, spamAmount)
   501  
   502  	// Fetch the community
   503  	stats := s.fetchCommunity(s.bob, community.CommunityShard(), community)
   504  
   505  	// Expect 3 pages and 23 (24 spam + 1 community description + 1 general channel description) envelopes to be fetched.
   506  	// First we fetch a more up-to-date, but an invalid spam message, fail to decrypt it as community description,
   507  	// then we fetch another page of data and successfully decrypt a community description.
   508  	s.Require().Equal(spamAmount+1, stats.FetchedEnvelopesCount)
   509  	s.Require().Equal(3, stats.FetchedPagesCount)
   510  }
   511  
   512  func (s *MessengerStoreNodeRequestSuite) TestRequestCommunityWithSameContentTopic() {
   513  	s.createOwner()
   514  	s.createBob()
   515  
   516  	// Create 2 communities
   517  	community1 := s.createCommunity(s.owner)
   518  	community2 := s.createCommunity(s.owner)
   519  
   520  	description2, err := community2.MarshaledDescription()
   521  	s.Require().NoError(err)
   522  
   523  	// Push community2 description to the same ContentTopic & PubsubTopic as community1.
   524  	// This way we simulate 2 communities with same ContentTopic.
   525  	spamMessage := common.RawMessage{
   526  		Payload:             description2,
   527  		Sender:              community2.PrivateKey(),
   528  		SkipEncryptionLayer: true,
   529  		MessageType:         protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION,
   530  		PubsubTopic:         community1.PubsubTopic(),
   531  	}
   532  	_, err = s.owner.sender.SendPublic(context.Background(), community1.IDString(), spamMessage)
   533  	s.Require().NoError(err)
   534  
   535  	// Fetch the community
   536  	s.fetchCommunity(s.bob, community1.CommunityShard(), community1)
   537  }
   538  
   539  func (s *MessengerStoreNodeRequestSuite) TestRequestMultipleCommunities() {
   540  	s.createOwner()
   541  	s.createBob()
   542  
   543  	// Create 2 communities
   544  	community1 := s.createCommunity(s.owner)
   545  	community2 := s.createCommunity(s.owner)
   546  
   547  	fetchedCommunities := map[string]*communities.Community{}
   548  
   549  	err := WaitOnSignaledCommunityFound(s.bob,
   550  		func() {
   551  			err := s.bob.fetchCommunities([]communities.CommunityShard{
   552  				community1.CommunityShard(),
   553  				community2.CommunityShard(),
   554  			})
   555  			s.Require().NoError(err)
   556  		},
   557  		func(community *communities.Community) bool {
   558  			fetchedCommunities[community.IDString()] = community
   559  			return len(fetchedCommunities) == 2
   560  		},
   561  		1*time.Second,
   562  		"communities were not signalled in time",
   563  	)
   564  
   565  	s.Require().NoError(err)
   566  	s.Require().Contains(fetchedCommunities, community1.IDString())
   567  	s.Require().Contains(fetchedCommunities, community2.IDString())
   568  }
   569  
   570  func (s *MessengerStoreNodeRequestSuite) TestRequestWithoutWaitingResponse() {
   571  	s.createOwner()
   572  	s.createBob()
   573  
   574  	// Create a community
   575  	community := s.createCommunity(s.owner)
   576  
   577  	request := FetchCommunityRequest{
   578  		CommunityKey:    community.IDString(),
   579  		Shard:           nil,
   580  		TryDatabase:     false,
   581  		WaitForResponse: false,
   582  	}
   583  
   584  	fetchedCommunities := map[string]*communities.Community{}
   585  
   586  	err := WaitOnSignaledCommunityFound(s.bob,
   587  		func() {
   588  			fetchedCommunity, err := s.bob.FetchCommunity(&request)
   589  			s.Require().NoError(err)
   590  			s.Require().Nil(fetchedCommunity)
   591  		},
   592  		func(community *communities.Community) bool {
   593  			fetchedCommunities[community.IDString()] = community
   594  			return len(fetchedCommunities) == 1
   595  		},
   596  		1*time.Second,
   597  		"communities weren't signalled",
   598  	)
   599  
   600  	s.Require().NoError(err)
   601  	s.Require().Len(fetchedCommunities, 1)
   602  	s.Require().Contains(fetchedCommunities, community.IDString())
   603  
   604  	s.requireCommunitiesEqual(fetchedCommunities[community.IDString()], community)
   605  }
   606  
   607  func (s *MessengerStoreNodeRequestSuite) TestRequestProfileInfo() {
   608  	s.createOwner()
   609  	defer s.tearDownOwner()
   610  
   611  	// Set keypair (to be able to set displayName)
   612  	ownerProfileKp := accounts.GetProfileKeypairForTest(true, false, false)
   613  	ownerProfileKp.KeyUID = s.owner.account.KeyUID
   614  	ownerProfileKp.Accounts[0].KeyUID = s.owner.account.KeyUID
   615  
   616  	err := s.owner.settings.SaveOrUpdateKeypair(ownerProfileKp)
   617  	s.Require().NoError(err)
   618  
   619  	contentTopicString := transport.ContactCodeTopic(&s.owner.identity.PublicKey)
   620  	contentTopic := wakuV2common.BytesToTopic(transport.ToTopic(contentTopicString))
   621  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentTopic)
   622  
   623  	// Set display name, this will also publish contact code
   624  	err = s.owner.SetDisplayName("super-owner")
   625  	s.Require().NoError(err)
   626  
   627  	s.waitForEnvelopes(storeNodeSubscription, 1)
   628  	s.ensureStoreNodeEnvelopes(&contentTopic, 1)
   629  
   630  	// Fetch profile
   631  	s.createBob()
   632  	defer s.tearDownBob()
   633  	s.fetchProfile(s.bob, s.owner.selfContact.ID, s.owner.selfContact)
   634  }
   635  
   636  func (s *MessengerStoreNodeRequestSuite) TestRequestProfileInfoError() {
   637  	s.createOwner()
   638  	defer s.tearDownOwner()
   639  	// Non-existent contact used by QA team for e2e autotests: (https://github.com/status-im/status-desktop/issues/15297)
   640  	fetchedContact, err := s.owner.FetchContact("0x04e972b2a794c315e16411fc0930a65bffffe4f885341683f4532fbbd883a447d849ac0be63d6a4f721affa0d0408160974ff831408433972de2c4556ef06d1ae1", true)
   641  	s.Require().Nil(fetchedContact)
   642  	s.Require().Nil(err)
   643  
   644  	u := "https://status.app/u/iwWACgoKCHNxdWlzaGVyAw==#zQ3shvMPZSyaUbjBjaNpNP1bPGsGpQDp59dZ4Gmz7UEy5o791"
   645  	response, err := s.owner.UnfurlURLs(nil, []string{u})
   646  	s.Require().NoError(err)
   647  	s.Require().Len(response.LinkPreviews, 0)
   648  }
   649  
   650  // TestSequentialUpdates checks that making updates to the community
   651  // immediately results in new store node fetched information.
   652  // Before adding storeNodeSubscription we had problems with the test setup that we didn't have a mechanism to wait for store node to
   653  // receive and process new messages.
   654  func (s *MessengerStoreNodeRequestSuite) TestSequentialUpdates() {
   655  	s.createOwner()
   656  	s.createBob()
   657  
   658  	community := s.createCommunity(s.owner)
   659  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   660  
   661  	contentTopic := wakuV2common.BytesToTopic(transport.ToTopic(community.IDString()))
   662  	communityName := community.Name()
   663  
   664  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentTopic)
   665  
   666  	for i := 0; i < 3; i++ {
   667  		// Change community name, this will automatically publish a new community description
   668  		ownerEditRequest := &requests.EditCommunity{
   669  			CommunityID: community.ID(),
   670  			CreateCommunity: requests.CreateCommunity{
   671  				Name:        fmt.Sprintf("%s-%d", communityName, i),
   672  				Description: community.DescriptionText(),
   673  				Color:       community.Color(),
   674  				Membership:  community.Permissions().Access,
   675  			},
   676  		}
   677  		_, err := s.owner.EditCommunity(ownerEditRequest)
   678  		s.Require().NoError(err)
   679  
   680  		s.waitForEnvelopes(storeNodeSubscription, 1)
   681  
   682  		// Get updated community from the database
   683  		community, err = s.owner.communitiesManager.GetByID(community.ID())
   684  		s.Require().NoError(err)
   685  		s.Require().NotNil(community)
   686  
   687  		s.fetchCommunity(s.bob, community.CommunityShard(), community)
   688  	}
   689  }
   690  
   691  func (s *MessengerStoreNodeRequestSuite) TestRequestShardAndCommunityInfo() {
   692  	s.createOwner()
   693  	s.createBob()
   694  	community := s.createCommunity(s.owner)
   695  
   696  	topicPrivKey, err := crypto.GenerateKey()
   697  	s.Require().NoError(err)
   698  
   699  	expectedShard := &shard.Shard{
   700  		Cluster: shard.MainStatusShardCluster,
   701  		Index:   23,
   702  	}
   703  
   704  	err = s.wakuStoreNode.SubscribeToPubsubTopic(expectedShard.PubsubTopic(), &topicPrivKey.PublicKey)
   705  	s.Require().NoError(err)
   706  
   707  	topicPrivKeyBytes := crypto.FromECDSA(topicPrivKey)
   708  	h := types.HexBytes(topicPrivKeyBytes)
   709  
   710  	shardRequest := &requests.SetCommunityShard{
   711  		CommunityID: community.ID(),
   712  		Shard:       expectedShard,
   713  		PrivateKey:  &h,
   714  	}
   715  
   716  	shardTopic := transport.CommunityShardInfoTopic(community.IDString())
   717  	contentContentTopic := wakuV2common.BytesToTopic(transport.ToTopic(shardTopic))
   718  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentContentTopic)
   719  
   720  	_, err = s.owner.SetCommunityShard(shardRequest)
   721  	s.Require().NoError(err)
   722  
   723  	s.waitForEnvelopes(storeNodeSubscription, 1)
   724  
   725  	s.waitForAvailableStoreNode(s.bob)
   726  
   727  	communityShard := community.CommunityShard()
   728  
   729  	community, err = s.owner.communitiesManager.GetByID(community.ID())
   730  	s.Require().NoError(err)
   731  	s.Require().NotNil(community)
   732  	s.Require().NotNil(community.Shard())
   733  
   734  	s.fetchCommunity(s.bob, communityShard, community)
   735  }
   736  
   737  func (s *MessengerStoreNodeRequestSuite) TestFiltersNotRemoved() {
   738  	s.createOwner()
   739  	s.createBob()
   740  
   741  	community := s.createCommunity(s.owner)
   742  
   743  	// The owner is a member of the community, so he has a filter for community description content topic.
   744  	// We want to check that filter is not removed by `FetchCommunity` call.
   745  	filterBefore := s.owner.transport.FilterByChatID(community.IDString())
   746  	s.Require().NotNil(filterBefore)
   747  
   748  	s.fetchCommunity(s.owner, community.CommunityShard(), nil)
   749  
   750  	filterAfter := s.owner.transport.FilterByChatID(community.IDString())
   751  	s.Require().NotNil(filterAfter)
   752  
   753  	s.Require().Equal(filterBefore.FilterID, filterAfter.FilterID)
   754  }
   755  
   756  func (s *MessengerStoreNodeRequestSuite) TestFiltersRemoved() {
   757  	s.createOwner()
   758  	s.createBob()
   759  
   760  	community := s.createCommunity(s.owner)
   761  
   762  	// The bob is a member of the community, so he has no filters for community description content topic.
   763  	// We want to check that filter created by `FetchCommunity` is removed on request finish.
   764  	filterBefore := s.bob.transport.FilterByChatID(community.IDString())
   765  	s.Require().Nil(filterBefore)
   766  
   767  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   768  
   769  	filterAfter := s.bob.transport.FilterByChatID(community.IDString())
   770  	s.Require().Nil(filterAfter)
   771  }
   772  
   773  func (s *MessengerStoreNodeRequestSuite) TestRequestCommunityEnvelopesOrder() {
   774  	s.createOwner()
   775  	s.createBob()
   776  
   777  	const descriptionsCount = 4
   778  	community := s.createCommunity(s.owner)
   779  	contentTopic := wakuV2common.BytesToTopic(transport.ToTopic(community.IDString()))
   780  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentTopic)
   781  
   782  	// Push a few descriptions to the store node
   783  	for i := 0; i < descriptionsCount-1; i++ {
   784  		err := s.owner.publishOrg(community, false)
   785  		s.Require().NoError(err)
   786  	}
   787  
   788  	// Wait for store node to receive envelopes
   789  	s.waitForEnvelopes(storeNodeSubscription, descriptionsCount-1)
   790  
   791  	// Subscribe to received envelope
   792  	bobWakuV2 := gethbridge.GetGethWakuV2From(s.bobWaku)
   793  
   794  	var receivedEnvelopes []*wakuV2common.ReceivedMessage
   795  	s.setupEnvelopesWatcher(bobWakuV2, &contentTopic, func(envelope *wakuV2common.ReceivedMessage) {
   796  		receivedEnvelopes = append(receivedEnvelopes, envelope)
   797  	})
   798  
   799  	// Force a single-envelope page size to be able to check the order.
   800  	// Also force all envelopes to be fetched.
   801  	options := []StoreNodeRequestOption{
   802  		WithWaitForResponseOption(true),
   803  		WithStopWhenDataFound(false),
   804  		WithInitialPageSize(1),
   805  		WithFurtherPageSize(1),
   806  	}
   807  
   808  	// Fetch the community
   809  	fetchedCommunity, _, err := s.bob.storeNodeRequestsManager.FetchCommunity(community.CommunityShard(), options)
   810  	s.Require().NoError(err)
   811  	s.requireCommunitiesEqual(fetchedCommunity, community)
   812  
   813  	// Ensure all expected envelopes were received
   814  	s.Require().Equal(descriptionsCount, len(receivedEnvelopes))
   815  
   816  	// We check that each next envelope fetched is newer than the previous one
   817  	for i := 1; i < len(receivedEnvelopes); i++ {
   818  		s.Require().Less(
   819  			receivedEnvelopes[i].Envelope.Message().GetTimestamp(),
   820  			receivedEnvelopes[i-1].Envelope.Message().GetTimestamp())
   821  	}
   822  }
   823  
   824  /*
   825  	TestFetchRealCommunity is not actually a test, but an utility to check the community description in all of the store nodes.
   826  	It's intended to only run locally and shouldn't be executed in CI, because it relies on connection to the real network.
   827  
   828  	TODO: It would be nice to move this code to a real utility in /cmd.
   829  		  It should allow us to fairly verify the community owner and do other good things.
   830  
   831  	To run this test, first set `runLocalTests` to true.
   832  	Then carefully set all of communityID, communityShard, fleet and other const variables.
   833  
   834  	NOTE: I only tested it with the default parameters, but in theory it should work for any configuration.
   835  */
   836  
   837  type testFetchRealCommunityExampleTokenInfo struct {
   838  	ChainID         uint64
   839  	ContractAddress string
   840  }
   841  
   842  var testFetchRealCommunityExample = []struct {
   843  	CommunityID          string
   844  	CommunityURL         string       // If set, takes precedence over CommunityID
   845  	CommunityShard       *shard.Shard // WARNING: I didn't test a sharded community
   846  	Fleet                string
   847  	ClusterID            uint16
   848  	UserPrivateKeyString string // When empty a new user will be created
   849  	// Optional request parameters
   850  	CustomOptions []StoreNodeRequestOption
   851  	// Setup OwnerPublicKey and CommunityTokens if the community has owner token
   852  	// This is needed to mock the owner verification
   853  	OwnerPublicKey  string
   854  	CommunityTokens []testFetchRealCommunityExampleTokenInfo
   855  	// Fill these if you know what envelopes are expected.
   856  	// The test will fail if fetched array doesn't equal to the expected one.
   857  	CheckExpectedEnvelopes       bool
   858  	ExpectedShardEnvelopes       []string
   859  	ExpectedDescriptionEnvelopes []string
   860  }{
   861  	{
   862  		//Example 1,
   863  		CommunityID:    "0x03073514d4c14a7d10ae9fc9b0f05abc904d84166a6ac80add58bf6a3542a4e50a",
   864  		CommunityShard: nil,
   865  		Fleet:          params.FleetStatusProd,
   866  		ClusterID:      shard.MainStatusShardCluster,
   867  	},
   868  	{
   869  		// Example 3,
   870  		// https://status.app/c/CxiACi8KFGFwIHJlcSAxIHN0dCBiZWMgbWVtEgdkc2Fkc2FkGAMiByM0MzYwREYqAxkrHAM=#zQ3shwDYZHtrLE7NqoTGjTWzWUu6hom5D4qxfskLZfgfyGRyL
   871  		CommunityID: "0x03f64be95ed5c925022265f9250f538f65ed3dcf6e4ef6c139803dc02a3487ae7b",
   872  		Fleet:       params.FleetStatusProd,
   873  		ClusterID:   shard.MainStatusShardCluster,
   874  
   875  		CheckExpectedEnvelopes: true,
   876  		ExpectedShardEnvelopes: []string{
   877  			"0x8173eecd7ff9ebcaae3dde0e704daf9bdeb6d33b0d8505a67e7dc56d0d8fc07c",
   878  			"0x596bbafbe0e0b625d165378cd4c7641a4d23aa1145c705aad666ddeaf60c88cd",
   879  			"0x8a1ee798f3657da5a463e5f878ab2455d05b8f552359b58330ccd7fa4f5624b0",
   880  			"0x97bcde2103a01984bb45a8590a6cb6972411445a1b2d40e181d5f2b5366fa5f1",
   881  			"0x26e3c0c880d1a2c4e81bf4fffbdb8b7e1ecc91fce7c6a05ee87d200d62ffc11e",
   882  			"0x1a8820bd61ebcc9de75c25f31c9b05eb6e880a5a4902679bb6ce2f43f61bf159",
   883  			"0xce450cfb5f79d761f34dea5b2ccec63751886e43ae63477e12f517c31f800aeb",
   884  			"0x9607bd1cf08355c44bcce055da197ba177201882736fa8874910194ccdaa8760",
   885  			"0x0c4b989ca69f529e571e6ea8b3230a85e057d8b2ae6147d1fedc2a01f2816ed6",
   886  			"0xe40ea64c9007a064b6324b614976510f2a433c9f84d87139df8f66b536e37ee4",
   887  			"0x7a028466a095e40650bb0ef16e903309b0c38c5a7cb7e2e9debd0acf2151448d",
   888  			"0x96419c6be375b2b348778d4694e3a491de84eecde601d5d405a0e72e9cece4a1",
   889  			"0xbcaeb5e86128638fab7203428daddd741df44ceeabe7d9d25936a10cd0a8b808",
   890  			"0x2e0b5872cb5a7c9a3273048eb2dfcb1d6a28faad3fe307a7db6c2dbaca9ce462",
   891  			"0xfa96bbe4125514ce73c52ef3ccb1c4ad9c4ad4afe8803de8ab9309fe9483b1d0",
   892  			"0xdeddfc82f70cce77c26959d91851fbc33afe648428c3e6ea349b2a2456b92111",
   893  			"0x5b12f17d7b712071f57bb48b7dcd0d6568ff5e7c3f8b3811013aea8dde9c6243",
   894  			"0x18928fd044482c75518162104d487e6fe504f086eb8c5e9f21aa4bce2811d0fa",
   895  			"0x543c156ced76138d69229a39425a0a1cabd617770e023333c10501b979f52d61",
   896  			"0xf46ea6bf5ab6a70662bcf227cc5d2c8c7a70ce42a88e5bb7ebe9e598668a8ae2",
   897  			"0xedb9628dc1ce5b0ec899c3813dd4159a2e06fb3dc88ffaae047e927c804ad0b3",
   898  			"0x16248eccc3544af3fc4a73467d0925ca2f3741eb623516ee369f710e4aa8a3ef",
   899  			"0x6a85f784a9004b56bbb47d87f5541173f05bd61ff5b26e41c714adbb5516e9ff",
   900  			"0x91e320be2cb5c6178027390cbce165fe088a1a35e1442382064ddbc9aabea8a2",
   901  			"0x676496dd36ae40e184863725bfb7425e46d916f73f4b0dd5d10324f4e9325da6",
   902  		},
   903  		ExpectedDescriptionEnvelopes: []string{
   904  			"0xe2c38667ee160861b3dc5a00e4422f47de1303c8b61f0a33c4853ce0b71d0ae4",
   905  			"0x7d8392baa9dd134e43287e58d69b8c9f50aa5c144adda6a3c7d32f00c5dea309",
   906  			"0xf74918d445709ccf9c29e776d27e9b7dc31f25a28473e8fcd89ed9de8a2e6df4",
   907  			"0xafd7e9b6245d88ea2b4fe70265aa3c5ce5618827c1092f8e3058315ac27c5b98",
   908  			"0x4d16bd4fcfc2d8736dc29d8c7287671f7e1df62af74943dc40a39ae18f388a07",
   909  			"0xd101f14bcf6bb9a5e72b934b3e74ebe3f77774037cb5b193803b264d69bfb9bf",
   910  			"0xaa857389e8886401678690bb4dbc664486bd7039427ca53e826197b303696cff",
   911  			"0x4443448e575824330a96d5114a9a7d3fe0ee7168ab6c7a646057ca4502fb91d2",
   912  			"0xfbb79b8d0ee1a61109c543cbe580412fe6d23de33d163006f74fef4addbdad37",
   913  			"0x51bc55c732e0e9db40fd8865a8ae20cbd99bfd4f95c62cf8b591e793d9b642d2",
   914  			"0xc28ca742c0e159a60941fb68ee5832016b510afe54e8ee8bb2200495ac29f1e3",
   915  			"0xf44f1714743a55f0170ba3627390d84cb9307326028abf7236222c93104db833",
   916  			"0x69d067da262b124d2eb6a8ba9f08e0a1ad66afb8d7ff3641a256992fff53a6de",
   917  			"0xa86a08457854fbea347cdd92fd390a330a9971967c551bebc53b18ccfea876fe",
   918  			"0x592850f8bc3c6079826f168971746bd2a1b50a5011fe3b233ead6c72c92a3373",
   919  			"0x1b3151d7b9b37350e86c937dc2e7964d472815bbf9275714bcb16a0c4327fe3d",
   920  			"0x1f2beca64e52f996127647cf3f3abd2e4fe501646fc39e98713ae064333388b3",
   921  			"0xeafc8f9d3114426c08748e6171710874074fb1eb732d9364830ce9b58955c83c",
   922  			"0x6e014a5ec75465efaf036353ec5811b8f710f608a04002925eba3a0a37a30423",
   923  			"0xf7e12a5829cf90e4272132e7b62c5bf0dd09100c8d498c66c9740a798db559b3",
   924  			"0x62da7e828862c3c8692cbd077c5a62266d811764184378e55f9f1066510b4652",
   925  			"0x74201394d05b914bc7e6ce4d2fcf0b119491c80644f48ac9fd37b842e4a0275a",
   926  			"0x9d4c0b1be53810c45c2fa744baa8d16ef4ae3a319b09043f4b4e053127461bf0",
   927  			"0x1937979514ea1dba8ab3b621fc3d0a3f6246b4bf1f9b4073888b8dbd9b4a765a",
   928  			"0xf767f3f36fdecb5ef6650542232334df836bfca1e7f72f1215df50d3f9f9c9bc",
   929  			"0x3a06002325502bc39a77962241fe274d4e88f61762194d321f9cc95272ed4a74",
   930  			"0x13caae58261c181d4974d7a68e0b7c8580c3cc569840179d53ae76407548d8b8",
   931  			"0x36f3b4afbcc4177a7aef26ad567839ffce51896d4c40d0a08d222cebd1255e3b",
   932  			"0x159e685bbab26a5d54ab817c93c9c610055bcf2af75290abcc9a84f1b85a2de9",
   933  			"0x1fd5ff73d7ea9a19f282bd0716f04a5e86b7c515839f0c721f66b3fe99161054",
   934  			"0x95b1e9ada4913ca809c9c28fc225a21753f18a90253660750900c78f79ad2a00",
   935  			"0x4334826934a7cbfb7446ec9d581fa6433c5d1f7f51b97f24717f55cffa320c65",
   936  			"0x0c01d07108c448797ffd14a2152fd38d1764c8a9c5e2f3da12f70551588add7a",
   937  			"0xe7071c6587fc277c4f4c0d7e4575de1a0843d3cf6c2a4aac79be79edc1608038",
   938  			"0x5da4e482f3e6eacf080db685e00c199c8cbbad9a8f43b1d94944426444a7a84a",
   939  			"0x638f551acdd7ccffd6a40ad12bbba1da8fd8a58157fdf9625b12d4a95b4eef71",
   940  			"0xa1a52c28e0481f6004d98bbce906676fff67f04246454bd33fba02c640355af0",
   941  			"0xe0300eb9e0f215ace491b1104665b48b9f6bff039af40e0cfc52a3ce766e747f",
   942  			"0xd092c04d51ee963d59953324d84188a0c1636e8600cb0f5f6f3f4f826d70c8f3",
   943  			"0x8d94bbfee687d534361fc3069079cf4e4f7db2a179d24e6419f67e38b5f0bd34",
   944  			"0x1fd7a4d2c04fca3875126b7a951f619b4da0000ca47496df0c2fb1048a145108",
   945  			"0xbbefbd116cbb23de193318b328412addc500af965d31ba481d70fa1d9e99461d",
   946  			"0xaa4e0e8bd820438e22b93371bda24a29922d33c15fb312b343d2e81a22cbdd95",
   947  			"0x76aef29ea4dde107c22c520efb2a4516b69ae83bc237281d9990f68397d801f5",
   948  			"0x804789119513a065d892cba5d240cb4d89d7329aeee93fcd8e85379a4d362fc9",
   949  			"0x9029b4a13903a3369e3466f1bfabae3f26b6721628db138eaba25c1f55f6fc1e",
   950  			"0xee38c209cb95035a289034c737e5775877145efea31b2a01f7c9241ff02f3e92",
   951  			"0x3e76da87895ca821db3b7ed7dc6949557c620d9cbcaf97af39ed4955d37b734e",
   952  			"0xf5e77eb8f9a5c52e09a56dd5e461bfee6cf9a73e1253f1d41bcde81fe3646997",
   953  			"0x083e06375c366283e541b249ea8646c3f31feb970078e95861ea399f0a57d09a",
   954  			"0xcd7db07ba557ec1ba0104909fdb958661c60c82213a75e8d15e7b262ef4f58b7",
   955  			"0x57b49dc83e1d3ac7b56bb7d758a9bf448339593311103bce4f0a53028587d577",
   956  			"0xb08cd92a5ec6f44a6129a60107132ca17d5fef29fb2bc5ebba14028d57a8038e",
   957  			"0x76959f98c8c734307a985294c26f008f3f705912aab02a5b3a0602a8598c02e2",
   958  			"0xd8ad7df58ffeec20b16a140bdd91484a34fca3ad7fc602043530ca63c307809a",
   959  			"0x6872ef39653bb208ef51f5b11c4bda3eb494a13f5da14e33687d30aef99ef383",
   960  			"0x0fabebc2e0c02f4add94886216b01ecdbd34929ad303d1a20a4505bf729038f6",
   961  			"0xc18270cd532bb3d34f62704feb722c40be48aedb5ad38d4d09fd67f5843b686d",
   962  			"0xbc16217ece82998783d4209ed3bc3f2f33d92630e43933b0129eb8b792500a3f",
   963  			"0xbda651e3b9c82f4bcf5b16252407fc888952820c842c49c06b4f01c8127e359a",
   964  			"0xb4b1799950c6aca3b011ffb775d0f973437d7d46e40cf7b379ff736d08f24eb2",
   965  			"0x38f12fb09c71dd720cacbb2102ac78ad6fbf830558adc7af9fb773f39e728bdc",
   966  			"0x489eb6fa2f5ee5b2a071c7083bf36a0a6cb4ec96049707d25843d9a97b4ac7be",
   967  			"0x64ea5655c8caf89a53c94edd5a47ba750d9fbcf099ec0dcd4026656b044486f1",
   968  			"0x501aee1c5da6aaeaae14abffefbc377b59ebe3fcaa9981bc83bfeffb25344749",
   969  			"0x9a3d360ea866102a6268ffd2001617c442b74b221d131fb3c08ae29bfac18203",
   970  		},
   971  	},
   972  	{
   973  		//Example 1,
   974  		CommunityID:            "0x02471dd922756a3a50b623e59cf3b99355d6587e43d5c517eb55f9aea9d3fe9fe9",
   975  		Fleet:                  params.FleetStatusProd,
   976  		ClusterID:              shard.MainStatusShardCluster,
   977  		CheckExpectedEnvelopes: true,
   978  		ExpectedShardEnvelopes: []string{
   979  			"0xc3e68e838d09e0117b3f3fd27aabe5f5a509d13e9045263c78e6890953d43547",
   980  			"0x5ee13d052bedb855ce2b9ba6f43c78233fbd4e6539a3bdf156497053c6ddf76d",
   981  			"0xfb6638b7e050f9323a0fe7b84986b5c6f8827965e67e3b3bd0fea21cf24e43de",
   982  		},
   983  		ExpectedDescriptionEnvelopes: []string{
   984  			"0x5b4fa95d430c939c1cbbb26175eabfb4ee058d508c6b4c0e26624958ba02c3ce",
   985  			"0xbf44409ee40dea7816186b37a45dfebabcee59f76855ad5af663ccdf598861ab",
   986  			"0x98d98453f6017517d0114989da0938aad59a3ad9a10839c181f453283f64f5c9",
   987  		},
   988  	},
   989  	{
   990  		CommunityURL: "https://status.app/c/G4IAAMQn9ucHF-V3W5Ouuy0xf0BtTjlwCANJEmwB2CG5p2xKUYzK_l37kzXulUppltT1t6mBcCEJsljRoGrKCP7rWommQomrMA2gBN7RrvCMkFqQwnCNzkNYWrLG85E6GVoM_nolTtfIzl53J1N-tj8fz4_TnO4IIw==#zQ3shZeEJqTC1xhGUjxuS4rtHSrhJ8vUYp64v6qWkLpvdy9L9",
   991  		//CommunityID:            "0x02b5bdaf5a25fcfe2ee14c501fab1836b8de57f61621080c3d52073d16de0d98d6",
   992  		Fleet:          params.FleetStatusProd,
   993  		OwnerPublicKey: "0x04953f5f0d355b37c39d1d6460a31ed1114455f8263b3fd1b84406c5f12c9eb7dfb76ba7513b92186010928254984fe98aee069b4c7e20f9ea3da497c3ae769477",
   994  		CommunityTokens: []testFetchRealCommunityExampleTokenInfo{
   995  			{
   996  				ChainID:         10,
   997  				ContractAddress: "0x9eDc11E5932372387E76ff3dcF66DB5465893823",
   998  			},
   999  			{
  1000  				ChainID:         10,
  1001  				ContractAddress: "0xD91d2E898f996308D643E3b9C78f5FEb5c5F404F",
  1002  			},
  1003  			{
  1004  				ChainID:         10,
  1005  				ContractAddress: "0x852E13D2BDFC4C3a761DA7B450f631b555b39C5E",
  1006  			},
  1007  			{
  1008  				ChainID:         10,
  1009  				ContractAddress: "0x000CfAd029EE94d120e0c136278582F94Cdc532c",
  1010  			},
  1011  			{
  1012  				ChainID:         10,
  1013  				ContractAddress: "0x21F6F5Cb75E81e5104D890D750270eD6538C50cb",
  1014  			},
  1015  		},
  1016  		ClusterID:              shard.MainStatusShardCluster,
  1017  		CheckExpectedEnvelopes: false,
  1018  		CustomOptions: []StoreNodeRequestOption{
  1019  			WithInitialPageSize(1),
  1020  			WithStopWhenDataFound(true),
  1021  		},
  1022  	},
  1023  }
  1024  
  1025  func (s *MessengerStoreNodeRequestSuite) TestFetchRealCommunity() {
  1026  	if !runLocalTests {
  1027  		return
  1028  	}
  1029  
  1030  	exampleToRun := testFetchRealCommunityExample[3]
  1031  
  1032  	// Test configuration
  1033  	communityID := exampleToRun.CommunityID
  1034  	communityShard := exampleToRun.CommunityShard
  1035  	fleet := exampleToRun.Fleet
  1036  	clusterID := exampleToRun.ClusterID
  1037  	userPrivateKeyString := exampleToRun.UserPrivateKeyString
  1038  	ownerPublicKey := exampleToRun.OwnerPublicKey
  1039  	communityTokens := exampleToRun.CommunityTokens
  1040  
  1041  	if exampleToRun.CommunityURL != "" {
  1042  		urlResponse, err := ParseSharedURL(exampleToRun.CommunityURL)
  1043  		s.Require().NoError(err)
  1044  		s.Require().NotNil(urlResponse.Community)
  1045  		communityID = urlResponse.Community.CommunityID
  1046  	}
  1047  
  1048  	// Prepare things depending on the configuration
  1049  	nodesList := mailserversDB.DefaultMailserversByFleet(fleet)
  1050  	descriptionContentTopic := wakuV2common.BytesToTopic(transport.ToTopic(communityID))
  1051  	shardContentTopic := wakuV2common.BytesToTopic(transport.ToTopic(transport.CommunityShardInfoTopic(communityID)))
  1052  
  1053  	communityIDBytes, err := types.DecodeHex(communityID)
  1054  	s.Require().NoError(err)
  1055  
  1056  	// update mock - the signer for the community returned by the contracts should be owner
  1057  	for _, communityToken := range communityTokens {
  1058  		s.collectiblesServiceMock.SetSignerPubkeyForCommunity(communityIDBytes, ownerPublicKey)
  1059  		s.collectiblesServiceMock.SetMockCollectibleContractData(communityToken.ChainID, communityToken.ContractAddress,
  1060  			&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
  1061  	}
  1062  
  1063  	results := map[string]singleResult{}
  1064  	wg := sync.WaitGroup{}
  1065  
  1066  	// We run a separate request for each node in the fleet.
  1067  	for i, mailserver := range nodesList {
  1068  		wg.Add(1)
  1069  		go func(i int, mailserver mailserversDB.Mailserver) {
  1070  			defer wg.Done()
  1071  
  1072  			fmt.Printf("--- starting request [%d] from %s\n", i, mailserver.ID)
  1073  
  1074  			result := singleResult{}
  1075  
  1076  			//
  1077  			// Create WakuV2 node
  1078  			// NOTE: Another option was to create a bare waku node and fetch envelopes directly with it
  1079  			// 		 and after that push all of the envelopes to a new messenger and check the result.
  1080  			// 		 But this turned out to be harder to implement.
  1081  			//
  1082  
  1083  			wakuLogger := s.logger.Named(fmt.Sprintf("user-waku-%d", i))
  1084  			messengerLogger := s.logger.Named(fmt.Sprintf("user-messenger-%d", i))
  1085  
  1086  			cfg := testWakuV2Config{
  1087  				logger:      wakuLogger,
  1088  				enableStore: false,
  1089  				clusterID:   clusterID,
  1090  			}
  1091  			wakuV2 := NewTestWakuV2(&s.Suite, cfg)
  1092  			userWaku := gethbridge.NewGethWakuV2Wrapper(wakuV2)
  1093  
  1094  			//
  1095  			// Create a messenger to process envelopes
  1096  			//
  1097  
  1098  			var privateKeyString = userPrivateKeyString
  1099  
  1100  			if privateKeyString == "" {
  1101  				privateKey, err := crypto.GenerateKey()
  1102  				s.Require().NoError(err)
  1103  				privateKeyString = hexutil.Encode(crypto.FromECDSA(privateKey))
  1104  			}
  1105  
  1106  			privateKeyBytes, err := hexutil.Decode(privateKeyString)
  1107  			s.Require().NoError(err)
  1108  			privateKey, err := crypto.ToECDSA(privateKeyBytes)
  1109  			s.Require().NoError(err)
  1110  
  1111  			// Mock a local fleet with single store node
  1112  			// This is done by settings custom store nodes in the database
  1113  
  1114  			mailserversSQLDb, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
  1115  			s.Require().NoError(err)
  1116  			mailserversDatabase := mailserversDB.NewDB(mailserversSQLDb)
  1117  
  1118  			mailserver.Fleet = localFleet
  1119  			err = mailserversDatabase.Add(mailserver)
  1120  			s.Require().NoError(err)
  1121  
  1122  			options := []Option{
  1123  				WithMailserversDatabase(mailserversDatabase),
  1124  				WithClusterConfig(params.ClusterConfig{
  1125  					Fleet:     localFleet,
  1126  					ClusterID: clusterID,
  1127  				}),
  1128  				WithCommunityTokensService(s.collectiblesServiceMock),
  1129  			}
  1130  
  1131  			// Create user without `createBob` func to force desired fleet
  1132  			user, err := newMessengerWithKey(userWaku, privateKey, messengerLogger, options)
  1133  			s.Require().NoError(err)
  1134  			defer TearDownMessenger(&s.Suite, user)
  1135  
  1136  			communityAddress := communities.CommunityShard{
  1137  				CommunityID: communityID,
  1138  				Shard:       communityShard,
  1139  			}
  1140  
  1141  			// Setup envelopes watcher to gather fetched envelopes
  1142  
  1143  			s.setupEnvelopesWatcher(wakuV2, &shardContentTopic, func(envelope *wakuV2common.ReceivedMessage) {
  1144  				result.ShardEnvelopes = append(result.ShardEnvelopes, envelope)
  1145  			})
  1146  
  1147  			s.setupEnvelopesWatcher(wakuV2, &descriptionContentTopic, func(envelope *wakuV2common.ReceivedMessage) {
  1148  				result.Envelopes = append(result.Envelopes, envelope)
  1149  			})
  1150  
  1151  			// Start fetching
  1152  
  1153  			storeNodeRequestOptions := []StoreNodeRequestOption{
  1154  				WithWaitForResponseOption(true),
  1155  				WithStopWhenDataFound(false),                         // In this test we want all envelopes to be fetched
  1156  				WithInitialPageSize(defaultStoreNodeRequestPageSize), // Because we're fetching all envelopes anyway
  1157  			}
  1158  			storeNodeRequestOptions = append(storeNodeRequestOptions, exampleToRun.CustomOptions...)
  1159  
  1160  			fetchedCommunity, stats, err := user.storeNodeRequestsManager.FetchCommunity(communityAddress, storeNodeRequestOptions)
  1161  
  1162  			result.EnvelopesCount = stats.FetchedEnvelopesCount
  1163  			result.FetchedCommunity = fetchedCommunity
  1164  			result.Error = err
  1165  
  1166  			results[mailserver.ID] = result
  1167  		}(i, mailserver)
  1168  	}
  1169  
  1170  	// Wait for all requests to finish
  1171  
  1172  	wg.Wait()
  1173  
  1174  	// Print the results
  1175  	for storeNodeName, result := range results {
  1176  		fmt.Printf("%s --- %s\n", storeNodeName, result.toString())
  1177  	}
  1178  
  1179  	// Check that results has no errors and contain correct envelopes
  1180  	for storeNodeName, result := range results {
  1181  		s.Require().NoError(result.Error)
  1182  		if exampleToRun.CheckExpectedEnvelopes {
  1183  			s.Require().Equal(exampleToRun.ExpectedShardEnvelopes, result.ShardEnvelopesHashes(),
  1184  				fmt.Sprintf("wrong shard envelopes for store node %s", storeNodeName))
  1185  			s.Require().Equal(exampleToRun.ExpectedDescriptionEnvelopes, result.EnvelopesHashes(),
  1186  				fmt.Sprintf("wrong envelopes for store node %s", storeNodeName))
  1187  		}
  1188  	}
  1189  }
  1190  
  1191  func (s *MessengerStoreNodeRequestSuite) TestFetchingCommunityWithOwnerToken() {
  1192  	s.createOwner()
  1193  	s.createBob()
  1194  
  1195  	s.waitForAvailableStoreNode(s.owner)
  1196  	community := s.createCommunity(s.owner)
  1197  
  1198  	// owner mints owner token
  1199  	var chainID uint64 = 1
  1200  	tokenAddress := "token-address"
  1201  	tokenName := "tokenName"
  1202  	tokenSymbol := "TSM"
  1203  	_, err := s.owner.SaveCommunityToken(&token.CommunityToken{
  1204  		TokenType:       protobuf.CommunityTokenType_ERC721,
  1205  		CommunityID:     community.IDString(),
  1206  		Address:         tokenAddress,
  1207  		ChainID:         int(chainID),
  1208  		Name:            tokenName,
  1209  		Supply:          &bigint.BigInt{},
  1210  		Symbol:          tokenSymbol,
  1211  		PrivilegesLevel: token.OwnerLevel,
  1212  	}, nil)
  1213  	s.Require().NoError(err)
  1214  
  1215  	// owner adds minted owner token to community
  1216  	err = s.owner.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
  1217  	s.Require().NoError(err)
  1218  
  1219  	// update mock - the signer for the community returned by the contracts should be owner
  1220  	s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.owner.identity.PublicKey))
  1221  	s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
  1222  		&communities.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
  1223  
  1224  	community, err = s.owner.communitiesManager.GetByID(community.ID())
  1225  	s.Require().NoError(err)
  1226  	s.Require().Len(community.TokenPermissions(), 1)
  1227  
  1228  	s.waitForAvailableStoreNode(s.bob)
  1229  
  1230  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
  1231  }
  1232  
  1233  func (s *MessengerStoreNodeRequestSuite) TestFetchingHistoryWhenOnline() {
  1234  	storeAddress := s.storeNodeAddress
  1235  	storePeerID := s.wakuStoreNode.PeerID()
  1236  
  1237  	// Create messengers
  1238  	s.createOwner()
  1239  	s.createBob()
  1240  
  1241  	s.logger.Debug("store node info", zap.String("peerID", s.wakuStoreNode.PeerID().String()))
  1242  	s.logger.Debug("owner node info", zap.String("peerID", gethbridge.GetGethWakuV2From(s.ownerWaku).PeerID().String()))
  1243  	s.logger.Debug("bob node info", zap.String("peerID", gethbridge.GetGethWakuV2From(s.bobWaku).PeerID().String()))
  1244  
  1245  	// Connect to store node to force "online" status
  1246  	{
  1247  		WaitForPeersConnected(&s.Suite, gethbridge.GetGethWakuV2From(s.bobWaku), func() peer.IDSlice {
  1248  			err := s.bob.DialPeer(storeAddress)
  1249  			s.Require().NoError(err)
  1250  			return peer.IDSlice{storePeerID}
  1251  		})
  1252  		s.Require().True(s.bob.Online())
  1253  
  1254  		// Wait for bob to fetch backup and historic messages
  1255  		time.Sleep(2 * time.Second)
  1256  	}
  1257  
  1258  	// bob goes offline
  1259  	{
  1260  		WaitForConnectionStatus(&s.Suite, gethbridge.GetGethWakuV2From(s.bobWaku), func() bool {
  1261  			err := s.bob.DropPeer(storePeerID)
  1262  			s.Require().NoError(err)
  1263  			return false
  1264  		})
  1265  		s.Require().False(s.bob.Online())
  1266  	}
  1267  
  1268  	// Owner sends a contact request while bob is offline
  1269  	{
  1270  		// Setup store nodes envelopes watcher
  1271  		partitionedTopic := transport.PartitionedTopic(s.bob.IdentityPublicKey())
  1272  		topic := transport.ToTopic(partitionedTopic)
  1273  		contentTopic := wakuV2common.BytesToTopic(topic)
  1274  		storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(&contentTopic)
  1275  
  1276  		// Send contact request
  1277  		response, err := s.owner.SendContactRequest(context.Background(), &requests.SendContactRequest{
  1278  			ID:      s.bob.IdentityPublicKeyString(),
  1279  			Message: "1",
  1280  		})
  1281  		s.Require().NoError(err)
  1282  		s.Require().NotNil(response)
  1283  		s.Require().Len(response.Messages(), 2)
  1284  
  1285  		// Ensure contact request is stored
  1286  		s.waitForEnvelopes(storeNodeSubscription, 1)
  1287  	}
  1288  
  1289  	// owner goes offline to prevent message resend and any other side effects
  1290  	// to go offline we disconnect from both relay and store peers
  1291  	WaitForConnectionStatus(&s.Suite, gethbridge.GetGethWakuV2From(s.ownerWaku), func() bool {
  1292  		err := s.owner.DropPeer(storePeerID)
  1293  		s.Require().NoError(err)
  1294  		return false
  1295  	})
  1296  	s.Require().False(s.owner.Online())
  1297  
  1298  	// bob goes back online, this should trigger fetching historic messages
  1299  	{
  1300  		// Enable auto request historic messages, so that when bob goes online it will fetch historic messages
  1301  		// We don't enable it earlier to control when we connect to the store node.
  1302  		s.bob.config.codeControlFlags.AutoRequestHistoricMessages = true
  1303  
  1304  		WaitForPeersConnected(&s.Suite, gethbridge.GetGethWakuV2From(s.bobWaku), func() peer.IDSlice {
  1305  			err := s.bob.DialPeer(storeAddress)
  1306  			s.Require().NoError(err)
  1307  			return peer.IDSlice{storePeerID}
  1308  		})
  1309  		s.Require().True(s.bob.Online())
  1310  
  1311  		// Don't  dial the peer, message should be fetched from store node
  1312  		response, err := WaitOnMessengerResponse(
  1313  			s.bob,
  1314  			func(r *MessengerResponse) bool {
  1315  				return len(r.Contacts) == 1
  1316  			},
  1317  			"no contact request received",
  1318  		)
  1319  		s.Require().NoError(err)
  1320  		s.Require().NotNil(response)
  1321  		s.Require().Len(response.Contacts, 1)
  1322  	}
  1323  }