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 }