github.com/status-im/status-go@v1.1.0/protocol/messenger_communities_sharding_test.go (about) 1 package protocol 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "testing" 7 8 "github.com/golang/protobuf/proto" 9 "github.com/stretchr/testify/suite" 10 "go.uber.org/zap" 11 12 gethbridge "github.com/status-im/status-go/eth-node/bridge/geth" 13 "github.com/status-im/status-go/eth-node/types" 14 "github.com/status-im/status-go/protocol/common" 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/protobuf" 18 "github.com/status-im/status-go/protocol/requests" 19 "github.com/status-im/status-go/protocol/tt" 20 ) 21 22 func TestMessengerCommunitiesShardingSuite(t *testing.T) { 23 suite.Run(t, new(MessengerCommunitiesShardingSuite)) 24 } 25 26 type MessengerCommunitiesShardingSuite struct { 27 suite.Suite 28 29 owner *Messenger 30 ownerWaku types.Waku 31 32 alice *Messenger 33 aliceWaku types.Waku 34 aliceUnhandledMessagesTracker *unhandledMessagesTracker 35 36 logger *zap.Logger 37 38 mockedBalances communities.BalancesByChain 39 mockedCollectibles communities.CollectiblesByChain 40 collectiblesServiceMock *CollectiblesServiceMock 41 collectiblesManagerMock *CollectiblesManagerMock 42 accountsTestData map[string][]string 43 accountsPasswords map[string]string 44 } 45 46 func (s *MessengerCommunitiesShardingSuite) SetupTest() { 47 s.logger = tt.MustCreateTestLogger() 48 s.collectiblesServiceMock = &CollectiblesServiceMock{} 49 s.mockedCollectibles = make(communities.CollectiblesByChain) 50 s.collectiblesManagerMock = &CollectiblesManagerMock{ 51 Collectibles: &s.mockedCollectibles, 52 } 53 s.accountsTestData = make(map[string][]string) 54 s.accountsPasswords = make(map[string]string) 55 56 wakuNodes := CreateWakuV2Network(&s.Suite, s.logger, []string{"owner", "alice"}) 57 58 nodeConfig := defaultTestCommunitiesMessengerNodeConfig() 59 60 s.ownerWaku = wakuNodes[0] 61 s.owner = newTestCommunitiesMessenger(&s.Suite, s.ownerWaku, testCommunitiesMessengerConfig{ 62 testMessengerConfig: testMessengerConfig{ 63 name: "owner", 64 logger: s.logger, 65 }, 66 walletAddresses: []string{}, 67 password: "", 68 nodeConfig: nodeConfig, 69 mockedBalances: &s.mockedBalances, 70 collectiblesManager: s.collectiblesManagerMock, 71 }) 72 73 s.aliceUnhandledMessagesTracker = &unhandledMessagesTracker{ 74 messages: map[protobuf.ApplicationMetadataMessage_Type][]*unhandedMessage{}, 75 } 76 s.aliceWaku = wakuNodes[1] 77 s.alice = newTestCommunitiesMessenger(&s.Suite, s.aliceWaku, testCommunitiesMessengerConfig{ 78 testMessengerConfig: testMessengerConfig{ 79 name: "alice", 80 logger: s.logger, 81 unhandledMessagesTracker: s.aliceUnhandledMessagesTracker, 82 }, 83 walletAddresses: []string{aliceAddress1}, 84 password: alicePassword, 85 nodeConfig: nodeConfig, 86 mockedBalances: &s.mockedBalances, 87 }) 88 89 _, err := s.owner.Start() 90 s.Require().NoError(err) 91 _, err = s.alice.Start() 92 s.Require().NoError(err) 93 } 94 95 func (s *MessengerCommunitiesShardingSuite) TearDownTest() { 96 if s.owner != nil { 97 TearDownMessenger(&s.Suite, s.owner) 98 } 99 if s.ownerWaku != nil { 100 s.Require().NoError(gethbridge.GetGethWakuV2From(s.ownerWaku).Stop()) 101 } 102 if s.alice != nil { 103 TearDownMessenger(&s.Suite, s.alice) 104 } 105 if s.aliceWaku != nil { 106 s.Require().NoError(gethbridge.GetGethWakuV2From(s.aliceWaku).Stop()) 107 } 108 _ = s.logger.Sync() 109 } 110 111 func (s *MessengerCommunitiesShardingSuite) testPostToCommunityChat(shard *shard.Shard, community *communities.Community, chat *Chat) { 112 _, err := s.owner.SetCommunityShard(&requests.SetCommunityShard{ 113 CommunityID: community.ID(), 114 Shard: shard, 115 }) 116 s.Require().NoError(err) 117 118 _, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool { 119 if len(mr.communities) == 0 { 120 return false 121 } 122 if shard == nil { 123 return mr.Communities()[0].Shard() == nil 124 } 125 return mr.Communities()[0].Shard() != nil && mr.Communities()[0].Shard().Index == shard.Index 126 }, "shard info not updated") 127 s.Require().NoError(err) 128 129 message := buildTestMessage(*chat) 130 _, err = s.owner.SendChatMessage(context.Background(), message) 131 s.Require().NoError(err) 132 133 _, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool { 134 return len(mr.messages) > 0 && mr.Messages()[0].ID == message.ID 135 }, "message not received") 136 s.Require().NoError(err) 137 } 138 139 func (s *MessengerCommunitiesShardingSuite) TestPostToCommunityChat() { 140 community, chat := createCommunity(&s.Suite, s.owner) 141 142 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice) 143 joinCommunity(&s.Suite, community.ID(), s.owner, s.alice, alicePassword, []string{aliceAddress1}) 144 145 // Members should be able to receive messages in a community with sharding enabled. 146 { 147 shard := &shard.Shard{ 148 Cluster: shard.MainStatusShardCluster, 149 Index: 128, 150 } 151 s.testPostToCommunityChat(shard, community, chat) 152 } 153 154 // Members should be able to receive messages in a community where the sharding configuration has been edited. 155 { 156 shard := &shard.Shard{ 157 Cluster: shard.MainStatusShardCluster, 158 Index: 256, 159 } 160 s.testPostToCommunityChat(shard, community, chat) 161 } 162 163 // Members should continue to receive messages in a community if it is moved back to default shard. 164 { 165 shard := &shard.Shard{ 166 Cluster: shard.MainStatusShardCluster, 167 Index: 32, 168 } 169 s.testPostToCommunityChat(shard, community, chat) 170 } 171 } 172 173 func (s *MessengerCommunitiesShardingSuite) TestIgnoreOutdatedShardKey() { 174 community, _ := createCommunity(&s.Suite, s.owner) 175 176 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice) 177 joinCommunity(&s.Suite, community.ID(), s.owner, s.alice, alicePassword, []string{aliceAddress1}) 178 179 shard := &shard.Shard{ 180 Cluster: shard.MainStatusShardCluster, 181 Index: 128, 182 } 183 184 // Members should receive shard update. 185 { 186 response, err := s.owner.SetCommunityShard(&requests.SetCommunityShard{ 187 CommunityID: community.ID(), 188 Shard: shard, 189 }) 190 s.Require().NoError(err) 191 s.Require().Len(response.Communities(), 1) 192 community = response.Communities()[0] 193 194 _, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool { 195 return len(mr.communities) > 0 && mr.Communities()[0].Shard() != nil && mr.Communities()[0].Shard().Index == shard.Index 196 }, "shard info not updated") 197 s.Require().NoError(err) 198 } 199 200 // Members should ignore outdated shard update. 201 { 202 // Simulate outdated CommunityShardKey message. 203 shard.Index = 256 204 communityShardKey := &protobuf.CommunityShardKey{ 205 Clock: community.Clock() - 1, // simulate outdated clock 206 CommunityId: community.ID(), 207 Shard: shard.Protobuffer(), 208 } 209 210 encodedMessage, err := proto.Marshal(communityShardKey) 211 s.Require().NoError(err) 212 213 rawMessage := common.RawMessage{ 214 Recipients: []*ecdsa.PublicKey{&s.alice.identity.PublicKey}, 215 ResendType: common.ResendTypeDataSync, 216 MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_SHARD_KEY, 217 Payload: encodedMessage, 218 } 219 220 _, err = s.owner.sender.SendPubsubTopicKey(context.Background(), &rawMessage) 221 s.Require().NoError(err) 222 223 _, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool { 224 msgType := protobuf.ApplicationMetadataMessage_COMMUNITY_SHARD_KEY 225 msgs, exists := s.aliceUnhandledMessagesTracker.messages[msgType] 226 if !exists { 227 return false 228 } 229 230 for _, msg := range msgs { 231 p := &protobuf.CommunityShardKey{} 232 err := proto.Unmarshal(msg.ApplicationLayer.Payload, p) 233 if err != nil { 234 panic(err) 235 } 236 237 if msg.err == communities.ErrOldShardInfo && p.Shard != nil && p.Shard.Index == int32(shard.Index) { 238 return true 239 } 240 } 241 242 return false 243 }, "shard info with outdated clock either not received or not ignored") 244 s.Require().NoError(err) 245 } 246 }