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

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/libp2p/go-libp2p/core/peer"
    10  	"github.com/multiformats/go-multiaddr"
    11  
    12  	"github.com/status-im/status-go/protocol/storenodes"
    13  
    14  	gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
    15  	"github.com/status-im/status-go/protocol/common/shard"
    16  	"github.com/status-im/status-go/protocol/communities"
    17  	"github.com/status-im/status-go/protocol/tt"
    18  
    19  	"github.com/stretchr/testify/suite"
    20  	"go.uber.org/zap"
    21  
    22  	"github.com/ethereum/go-ethereum/crypto"
    23  	"github.com/status-im/status-go/appdatabase"
    24  	"github.com/status-im/status-go/eth-node/types"
    25  	"github.com/status-im/status-go/protocol/protobuf"
    26  	"github.com/status-im/status-go/protocol/requests"
    27  	"github.com/status-im/status-go/protocol/sqlite"
    28  	"github.com/status-im/status-go/t/helpers"
    29  
    30  	mailserversDB "github.com/status-im/status-go/services/mailservers"
    31  	waku2 "github.com/status-im/status-go/wakuv2"
    32  	wakuV2common "github.com/status-im/status-go/wakuv2/common"
    33  )
    34  
    35  func TestMessengerStoreNodeCommunitySuite(t *testing.T) {
    36  	t.Skip("requires storev3 node")
    37  	suite.Run(t, new(MessengerStoreNodeCommunitySuite))
    38  }
    39  
    40  type MessengerStoreNodeCommunitySuite struct {
    41  	suite.Suite
    42  
    43  	cancel chan struct{}
    44  
    45  	owner     *Messenger
    46  	ownerWaku types.Waku
    47  
    48  	bob     *Messenger
    49  	bobWaku types.Waku
    50  
    51  	storeNode                 *waku2.Waku
    52  	storeNodeAddress          multiaddr.Multiaddr
    53  	communityStoreNode        *waku2.Waku
    54  	communityStoreNodeAddress multiaddr.Multiaddr
    55  
    56  	collectiblesServiceMock *CollectiblesServiceMock
    57  
    58  	logger *zap.Logger
    59  }
    60  
    61  func (s *MessengerStoreNodeCommunitySuite) SetupTest() {
    62  	s.logger = tt.MustCreateTestLogger()
    63  
    64  	s.cancel = make(chan struct{}, 10)
    65  
    66  	s.collectiblesServiceMock = &CollectiblesServiceMock{}
    67  
    68  	s.storeNode, s.storeNodeAddress = s.createStore("store-1")
    69  	s.communityStoreNode, s.communityStoreNodeAddress = s.createStore("store-community")
    70  
    71  	s.owner, s.ownerWaku = s.newMessenger("owner", &s.storeNodeAddress)
    72  	s.bob, s.bobWaku = s.newMessenger("bob", &s.storeNodeAddress)
    73  }
    74  
    75  func (s *MessengerStoreNodeCommunitySuite) TearDown() {
    76  	close(s.cancel)
    77  	if s.storeNode != nil {
    78  		s.Require().NoError(s.storeNode.Stop())
    79  	}
    80  	if s.communityStoreNode != nil {
    81  		s.Require().NoError(s.communityStoreNode.Stop())
    82  	}
    83  	if s.owner != nil {
    84  		TearDownMessenger(&s.Suite, s.owner)
    85  	}
    86  	if s.bob != nil {
    87  		TearDownMessenger(&s.Suite, s.bob)
    88  	}
    89  }
    90  
    91  func (s *MessengerStoreNodeCommunitySuite) createStore(name string) (*waku2.Waku, multiaddr.Multiaddr) {
    92  	cfg := testWakuV2Config{
    93  		logger:      s.logger.Named(name),
    94  		enableStore: true,
    95  		clusterID:   shard.MainStatusShardCluster,
    96  	}
    97  
    98  	storeNode := NewTestWakuV2(&s.Suite, cfg)
    99  	addresses := storeNode.ListenAddresses()
   100  	s.Require().GreaterOrEqual(len(addresses), 1, "no storenode listen address")
   101  	return storeNode, addresses[0]
   102  }
   103  
   104  func (s *MessengerStoreNodeCommunitySuite) newMessenger(name string, storenodeAddress *multiaddr.Multiaddr) (*Messenger, types.Waku) {
   105  	localMailserverID := "local-mailserver-007"
   106  	localFleet := "local-fleet-007"
   107  
   108  	logger := s.logger.Named(name)
   109  	cfg := testWakuV2Config{
   110  		logger:      logger,
   111  		enableStore: false,
   112  		clusterID:   shard.MainStatusShardCluster,
   113  	}
   114  	wakuV2 := NewTestWakuV2(&s.Suite, cfg)
   115  	wakuV2Wrapper := gethbridge.NewGethWakuV2Wrapper(wakuV2)
   116  
   117  	privateKey, err := crypto.GenerateKey()
   118  	s.Require().NoError(err)
   119  
   120  	mailserversSQLDb, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
   121  	s.Require().NoError(err)
   122  	err = sqlite.Migrate(mailserversSQLDb) // migrate default
   123  	s.Require().NoError(err)
   124  
   125  	mailserversDatabase := mailserversDB.NewDB(mailserversSQLDb)
   126  	err = mailserversDatabase.Add(mailserversDB.Mailserver{
   127  		ID:    localMailserverID,
   128  		Name:  localMailserverID,
   129  		Addr:  storenodeAddress,
   130  		Fleet: localFleet,
   131  	})
   132  	s.Require().NoError(err)
   133  
   134  	options := []Option{
   135  		WithAutoRequestHistoricMessages(false),
   136  		WithCuratedCommunitiesUpdateLoop(false),
   137  	}
   138  
   139  	if storenodeAddress != nil {
   140  		options = append(options,
   141  			WithTestStoreNode(&s.Suite, localMailserverID, *storenodeAddress, localFleet, s.collectiblesServiceMock),
   142  		)
   143  	}
   144  
   145  	messenger, err := newMessengerWithKey(wakuV2Wrapper, privateKey, logger, options)
   146  
   147  	s.Require().NoError(err)
   148  	return messenger, wakuV2Wrapper
   149  }
   150  
   151  func (s *MessengerStoreNodeCommunitySuite) createCommunityWithChat(m *Messenger) (*communities.Community, *Chat) {
   152  	WaitForAvailableStoreNode(&s.Suite, m, 500*time.Millisecond)
   153  
   154  	storeNodeSubscription := s.setupStoreNodeEnvelopesWatcher(nil)
   155  
   156  	createCommunityRequest := &requests.CreateCommunity{
   157  		Name:        RandomLettersString(10),
   158  		Description: RandomLettersString(20),
   159  		Color:       RandomColor(),
   160  		Tags:        RandomCommunityTags(3),
   161  		Membership:  protobuf.CommunityPermissions_AUTO_ACCEPT,
   162  	}
   163  
   164  	response, err := m.CreateCommunity(createCommunityRequest, true)
   165  	s.Require().NoError(err)
   166  	s.Require().NotNil(response)
   167  	s.Require().Len(response.Communities(), 1)
   168  	s.Require().Len(response.Chats(), 1)
   169  	s.Require().True(response.Communities()[0].Joined())
   170  	s.Require().True(response.Communities()[0].IsControlNode())
   171  	s.Require().True(response.Communities()[0].IsMemberOwner(&m.identity.PublicKey))
   172  
   173  	s.waitForEnvelopes(storeNodeSubscription, 1)
   174  
   175  	return response.Communities()[0], response.Chats()[0]
   176  }
   177  
   178  func (s *MessengerStoreNodeCommunitySuite) requireCommunitiesEqual(c *communities.Community, expected *communities.Community) {
   179  	if expected == nil {
   180  		s.Require().Nil(c)
   181  		return
   182  	}
   183  	s.Require().NotNil(c)
   184  	s.Require().Equal(expected.IDString(), c.IDString())
   185  	s.Require().Equal(expected.Clock(), c.Clock())
   186  	s.Require().Equal(expected.Name(), c.Name())
   187  	s.Require().Equal(expected.Identity().Description, c.Identity().Description)
   188  	s.Require().Equal(expected.Color(), c.Color())
   189  	s.Require().Equal(expected.Tags(), c.Tags())
   190  	s.Require().Equal(expected.Shard(), c.Shard())
   191  	s.Require().Equal(expected.TokenPermissions(), c.TokenPermissions())
   192  	s.Require().Equal(expected.CommunityTokensMetadata(), c.CommunityTokensMetadata())
   193  }
   194  
   195  func (s *MessengerStoreNodeCommunitySuite) fetchCommunity(m *Messenger, communityShard communities.CommunityShard, expectedCommunity *communities.Community) StoreNodeRequestStats {
   196  	options := []StoreNodeRequestOption{
   197  		WithWaitForResponseOption(true),
   198  	}
   199  
   200  	fetchedCommunity, stats, err := m.storeNodeRequestsManager.FetchCommunity(communityShard, options)
   201  
   202  	s.Require().NoError(err)
   203  	s.requireCommunitiesEqual(fetchedCommunity, expectedCommunity)
   204  
   205  	return stats
   206  }
   207  
   208  func (s *MessengerStoreNodeCommunitySuite) setupEnvelopesWatcher(wakuNode *waku2.Waku, topic *wakuV2common.TopicType, cb func(envelope *wakuV2common.ReceivedMessage)) {
   209  	envelopesWatcher := make(chan wakuV2common.EnvelopeEvent, 100)
   210  	envelopesSub := wakuNode.SubscribeEnvelopeEvents(envelopesWatcher)
   211  
   212  	go func() {
   213  		defer envelopesSub.Unsubscribe()
   214  		for {
   215  			select {
   216  			case <-s.cancel:
   217  				return
   218  
   219  			case envelopeEvent := <-envelopesWatcher:
   220  				if envelopeEvent.Event != wakuV2common.EventEnvelopeAvailable {
   221  					continue
   222  				}
   223  				if topic != nil && *topic != envelopeEvent.Topic {
   224  					continue
   225  				}
   226  				envelope := wakuNode.GetEnvelope(envelopeEvent.Hash)
   227  				cb(envelope)
   228  				s.logger.Debug("envelope available event for fetched content topic",
   229  					zap.Any("envelopeEvent", envelopeEvent),
   230  					zap.Any("envelope", envelope),
   231  				)
   232  			}
   233  
   234  		}
   235  	}()
   236  }
   237  
   238  func (s *MessengerStoreNodeCommunitySuite) setupStoreNodeEnvelopesWatcher(topic *wakuV2common.TopicType) <-chan string {
   239  	storeNodeSubscription := make(chan string, 100)
   240  	s.setupEnvelopesWatcher(s.storeNode, topic, func(envelope *wakuV2common.ReceivedMessage) {
   241  		storeNodeSubscription <- envelope.Hash().String()
   242  	})
   243  	return storeNodeSubscription
   244  }
   245  
   246  func (s *MessengerStoreNodeCommunitySuite) waitForEnvelopes(subscription <-chan string, expectedEnvelopesCount int) {
   247  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   248  	defer cancel()
   249  
   250  	for i := 0; i < expectedEnvelopesCount; i++ {
   251  		select {
   252  		case <-subscription:
   253  		case <-ctx.Done():
   254  			err := fmt.Sprintf("timeout waiting for store node to receive envelopes, received: %d, expected: %d", i, expectedEnvelopesCount)
   255  			s.Require().Fail(err)
   256  		}
   257  	}
   258  }
   259  
   260  func (s *MessengerStoreNodeCommunitySuite) TestSetCommunityStorenodesAndFetch() {
   261  	err := s.owner.DialPeer(s.storeNodeAddress)
   262  	s.Require().NoError(err)
   263  	err = s.bob.DialPeer(s.storeNodeAddress)
   264  	s.Require().NoError(err)
   265  
   266  	// Create a community
   267  	community, _ := s.createCommunityWithChat(s.owner)
   268  
   269  	// Set the storenode for the community
   270  	_, err = s.owner.SetCommunityStorenodes(&requests.SetCommunityStorenodes{
   271  		CommunityID: community.ID(),
   272  		Storenodes: []storenodes.Storenode{
   273  			{
   274  				StorenodeID: "community-store-node",
   275  				Name:        "community-store-node",
   276  				CommunityID: community.ID(),
   277  				Version:     2,
   278  				Address:     s.communityStoreNodeAddress,
   279  				Fleet:       "aaa",
   280  			},
   281  		},
   282  	})
   283  	s.Require().NoError(err)
   284  
   285  	// Bob tetches the community
   286  	s.fetchCommunity(s.bob, community.CommunityShard(), community)
   287  }
   288  
   289  func (s *MessengerStoreNodeCommunitySuite) TestSetStorenodeForCommunity_fetchMessagesFromNewStorenode() {
   290  	s.T().Skip("flaky")
   291  
   292  	err := s.owner.DialPeer(s.storeNodeAddress)
   293  	s.Require().NoError(err)
   294  	err = s.bob.DialPeer(s.storeNodeAddress)
   295  	s.Require().NoError(err)
   296  
   297  	ownerPeerID := gethbridge.GetGethWakuV2From(s.ownerWaku).PeerID()
   298  	bobPeerID := gethbridge.GetGethWakuV2From(s.bobWaku).PeerID()
   299  
   300  	// 1. Owner creates a community
   301  	community, chat := s.createCommunityWithChat(s.owner)
   302  
   303  	// waits for onwer and bob to connect to the store node
   304  	WaitForPeersConnected(&s.Suite, s.storeNode, func() peer.IDSlice {
   305  		return peer.IDSlice{ownerPeerID, bobPeerID}
   306  	})
   307  
   308  	// 2. Bob joins the community
   309  	advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
   310  	joinCommunity(&s.Suite, community.ID(), s.owner, s.bob, bobPassword, []string{bobAccountAddress})
   311  
   312  	// waits for onwer and bob to connect to the community store node
   313  	WaitForPeersConnected(&s.Suite, s.communityStoreNode, func() peer.IDSlice {
   314  		err := s.bob.DialPeer(s.communityStoreNodeAddress)
   315  		s.Require().NoError(err)
   316  		err = s.owner.DialPeer(s.communityStoreNodeAddress)
   317  		s.Require().NoError(err)
   318  
   319  		return peer.IDSlice{ownerPeerID, bobPeerID}
   320  	})
   321  
   322  	// 3. Owner sets the storenode for the community
   323  	_, err = s.owner.SetCommunityStorenodes(&requests.SetCommunityStorenodes{
   324  		CommunityID: community.ID(),
   325  		Storenodes: []storenodes.Storenode{
   326  			{
   327  				StorenodeID: "community-store-node",
   328  				Name:        "community-store-node",
   329  				CommunityID: community.ID(),
   330  				Version:     2,
   331  				Address:     s.communityStoreNodeAddress,
   332  				Fleet:       "aaa",
   333  			},
   334  		},
   335  	})
   336  	s.Require().NoError(err)
   337  
   338  	// 5. Bob sends a message to the community chat
   339  	inputMessage := buildTestMessage(*chat)
   340  	_, err = s.bob.SendChatMessage(context.Background(), inputMessage)
   341  	s.Require().NoError(err)
   342  
   343  	// 6. Owner fetches the message from the new storenode
   344  	err = s.owner.FetchMessages(&requests.FetchMessages{
   345  		ID: chat.ID,
   346  	})
   347  	s.Require().NoError(err)
   348  }
   349  
   350  func (s *MessengerStoreNodeCommunitySuite) TestToggleUseMailservers() {
   351  	// Enable use of mailservers
   352  	err := s.owner.ToggleUseMailservers(true)
   353  	s.Require().NoError(err)
   354  	s.Require().NotNil(s.owner.mailserverCycle.activeMailserver)
   355  
   356  	// Disable use of mailservers
   357  	err = s.owner.ToggleUseMailservers(false)
   358  	s.Require().NoError(err)
   359  	s.Require().Nil(s.owner.mailserverCycle.activeMailserver)
   360  }