2024-01-22 14:48:05 +00:00
|
|
|
package protocol
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-02-19 18:00:44 +00:00
|
|
|
"crypto/ecdsa"
|
2024-01-22 14:48:05 +00:00
|
|
|
"testing"
|
|
|
|
|
2024-02-19 18:00:44 +00:00
|
|
|
"github.com/golang/protobuf/proto"
|
2024-01-22 14:48:05 +00:00
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
|
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
|
|
|
"github.com/status-im/status-go/eth-node/types"
|
2024-02-19 18:00:44 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common"
|
2024-01-22 14:48:05 +00:00
|
|
|
"github.com/status-im/status-go/protocol/common/shard"
|
|
|
|
"github.com/status-im/status-go/protocol/communities"
|
2024-01-26 15:29:43 +00:00
|
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
2024-01-22 14:48:05 +00:00
|
|
|
"github.com/status-im/status-go/protocol/requests"
|
|
|
|
"github.com/status-im/status-go/protocol/tt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestMessengerCommunitiesShardingSuite(t *testing.T) {
|
|
|
|
suite.Run(t, new(MessengerCommunitiesShardingSuite))
|
|
|
|
}
|
|
|
|
|
|
|
|
type MessengerCommunitiesShardingSuite struct {
|
|
|
|
suite.Suite
|
|
|
|
|
|
|
|
owner *Messenger
|
|
|
|
ownerWaku types.Waku
|
|
|
|
|
2024-01-26 15:29:43 +00:00
|
|
|
alice *Messenger
|
|
|
|
aliceWaku types.Waku
|
|
|
|
aliceUnhandledMessagesTracker *unhandledMessagesTracker
|
2024-01-22 14:48:05 +00:00
|
|
|
|
|
|
|
logger *zap.Logger
|
2024-08-07 15:57:02 +00:00
|
|
|
|
|
|
|
mockedBalances communities.BalancesByChain
|
|
|
|
mockedCollectibles communities.CollectiblesByChain
|
|
|
|
collectiblesServiceMock *CollectiblesServiceMock
|
|
|
|
collectiblesManagerMock *CollectiblesManagerMock
|
|
|
|
accountsTestData map[string][]string
|
|
|
|
accountsPasswords map[string]string
|
2024-01-22 14:48:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MessengerCommunitiesShardingSuite) SetupTest() {
|
|
|
|
s.logger = tt.MustCreateTestLogger()
|
2024-08-07 15:57:02 +00:00
|
|
|
s.collectiblesServiceMock = &CollectiblesServiceMock{}
|
|
|
|
s.mockedCollectibles = make(communities.CollectiblesByChain)
|
|
|
|
s.collectiblesManagerMock = &CollectiblesManagerMock{
|
|
|
|
Collectibles: &s.mockedCollectibles,
|
|
|
|
}
|
|
|
|
s.accountsTestData = make(map[string][]string)
|
|
|
|
s.accountsPasswords = make(map[string]string)
|
2024-01-22 14:48:05 +00:00
|
|
|
|
2024-07-15 15:25:12 +00:00
|
|
|
wakuNodes := CreateWakuV2Network(&s.Suite, s.logger, []string{"owner", "alice"})
|
2024-01-22 14:48:05 +00:00
|
|
|
|
|
|
|
nodeConfig := defaultTestCommunitiesMessengerNodeConfig()
|
|
|
|
|
|
|
|
s.ownerWaku = wakuNodes[0]
|
|
|
|
s.owner = newTestCommunitiesMessenger(&s.Suite, s.ownerWaku, testCommunitiesMessengerConfig{
|
|
|
|
testMessengerConfig: testMessengerConfig{
|
|
|
|
name: "owner",
|
|
|
|
logger: s.logger,
|
|
|
|
},
|
2024-08-07 15:57:02 +00:00
|
|
|
walletAddresses: []string{},
|
|
|
|
password: "",
|
|
|
|
nodeConfig: nodeConfig,
|
|
|
|
mockedBalances: &s.mockedBalances,
|
|
|
|
collectiblesManager: s.collectiblesManagerMock,
|
2024-01-22 14:48:05 +00:00
|
|
|
})
|
|
|
|
|
2024-01-26 15:29:43 +00:00
|
|
|
s.aliceUnhandledMessagesTracker = &unhandledMessagesTracker{
|
|
|
|
messages: map[protobuf.ApplicationMetadataMessage_Type][]*unhandedMessage{},
|
|
|
|
}
|
2024-01-22 14:48:05 +00:00
|
|
|
s.aliceWaku = wakuNodes[1]
|
|
|
|
s.alice = newTestCommunitiesMessenger(&s.Suite, s.aliceWaku, testCommunitiesMessengerConfig{
|
|
|
|
testMessengerConfig: testMessengerConfig{
|
2024-01-26 15:29:43 +00:00
|
|
|
name: "alice",
|
|
|
|
logger: s.logger,
|
|
|
|
unhandledMessagesTracker: s.aliceUnhandledMessagesTracker,
|
2024-01-22 14:48:05 +00:00
|
|
|
},
|
2024-08-07 15:57:02 +00:00
|
|
|
walletAddresses: []string{aliceAddress1},
|
|
|
|
password: alicePassword,
|
|
|
|
nodeConfig: nodeConfig,
|
|
|
|
mockedBalances: &s.mockedBalances,
|
2024-01-22 14:48:05 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
_, err := s.owner.Start()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.alice.Start()
|
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MessengerCommunitiesShardingSuite) TearDownTest() {
|
|
|
|
if s.owner != nil {
|
|
|
|
TearDownMessenger(&s.Suite, s.owner)
|
|
|
|
}
|
|
|
|
if s.ownerWaku != nil {
|
|
|
|
s.Require().NoError(gethbridge.GetGethWakuV2From(s.ownerWaku).Stop())
|
|
|
|
}
|
|
|
|
if s.alice != nil {
|
|
|
|
TearDownMessenger(&s.Suite, s.alice)
|
|
|
|
}
|
|
|
|
if s.aliceWaku != nil {
|
|
|
|
s.Require().NoError(gethbridge.GetGethWakuV2From(s.aliceWaku).Stop())
|
|
|
|
}
|
|
|
|
_ = s.logger.Sync()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MessengerCommunitiesShardingSuite) testPostToCommunityChat(shard *shard.Shard, community *communities.Community, chat *Chat) {
|
|
|
|
_, err := s.owner.SetCommunityShard(&requests.SetCommunityShard{
|
|
|
|
CommunityID: community.ID(),
|
|
|
|
Shard: shard,
|
|
|
|
})
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
_, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool {
|
|
|
|
if len(mr.communities) == 0 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if shard == nil {
|
|
|
|
return mr.Communities()[0].Shard() == nil
|
|
|
|
}
|
|
|
|
return mr.Communities()[0].Shard() != nil && mr.Communities()[0].Shard().Index == shard.Index
|
|
|
|
}, "shard info not updated")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
message := buildTestMessage(*chat)
|
|
|
|
_, err = s.owner.SendChatMessage(context.Background(), message)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
_, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool {
|
|
|
|
return len(mr.messages) > 0 && mr.Messages()[0].ID == message.ID
|
|
|
|
}, "message not received")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *MessengerCommunitiesShardingSuite) TestPostToCommunityChat() {
|
|
|
|
community, chat := createCommunity(&s.Suite, s.owner)
|
|
|
|
|
|
|
|
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
|
2024-08-07 15:57:02 +00:00
|
|
|
joinCommunity(&s.Suite, community.ID(), s.owner, s.alice, alicePassword, []string{aliceAddress1})
|
2024-01-22 14:48:05 +00:00
|
|
|
|
|
|
|
// Members should be able to receive messages in a community with sharding enabled.
|
|
|
|
{
|
|
|
|
shard := &shard.Shard{
|
|
|
|
Cluster: shard.MainStatusShardCluster,
|
|
|
|
Index: 128,
|
|
|
|
}
|
|
|
|
s.testPostToCommunityChat(shard, community, chat)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Members should be able to receive messages in a community where the sharding configuration has been edited.
|
|
|
|
{
|
|
|
|
shard := &shard.Shard{
|
|
|
|
Cluster: shard.MainStatusShardCluster,
|
|
|
|
Index: 256,
|
|
|
|
}
|
|
|
|
s.testPostToCommunityChat(shard, community, chat)
|
|
|
|
}
|
|
|
|
|
2024-07-04 05:04:53 +00:00
|
|
|
// Members should continue to receive messages in a community if it is moved back to default shard.
|
2024-01-22 14:48:05 +00:00
|
|
|
{
|
2024-07-04 05:04:53 +00:00
|
|
|
shard := &shard.Shard{
|
|
|
|
Cluster: shard.MainStatusShardCluster,
|
|
|
|
Index: 32,
|
|
|
|
}
|
|
|
|
s.testPostToCommunityChat(shard, community, chat)
|
2024-01-22 14:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-26 15:29:43 +00:00
|
|
|
|
|
|
|
func (s *MessengerCommunitiesShardingSuite) TestIgnoreOutdatedShardKey() {
|
|
|
|
community, _ := createCommunity(&s.Suite, s.owner)
|
|
|
|
|
|
|
|
advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice)
|
2024-08-07 15:57:02 +00:00
|
|
|
joinCommunity(&s.Suite, community.ID(), s.owner, s.alice, alicePassword, []string{aliceAddress1})
|
2024-01-26 15:29:43 +00:00
|
|
|
|
|
|
|
shard := &shard.Shard{
|
|
|
|
Cluster: shard.MainStatusShardCluster,
|
|
|
|
Index: 128,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Members should receive shard update.
|
|
|
|
{
|
|
|
|
response, err := s.owner.SetCommunityShard(&requests.SetCommunityShard{
|
|
|
|
CommunityID: community.ID(),
|
|
|
|
Shard: shard,
|
|
|
|
})
|
|
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Len(response.Communities(), 1)
|
|
|
|
community = response.Communities()[0]
|
|
|
|
|
|
|
|
_, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool {
|
|
|
|
return len(mr.communities) > 0 && mr.Communities()[0].Shard() != nil && mr.Communities()[0].Shard().Index == shard.Index
|
|
|
|
}, "shard info not updated")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Members should ignore outdated shard update.
|
|
|
|
{
|
|
|
|
// Simulate outdated CommunityShardKey message.
|
|
|
|
shard.Index = 256
|
|
|
|
communityShardKey := &protobuf.CommunityShardKey{
|
|
|
|
Clock: community.Clock() - 1, // simulate outdated clock
|
|
|
|
CommunityId: community.ID(),
|
|
|
|
Shard: shard.Protobuffer(),
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedMessage, err := proto.Marshal(communityShardKey)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
rawMessage := common.RawMessage{
|
2024-03-22 10:55:09 +00:00
|
|
|
Recipients: []*ecdsa.PublicKey{&s.alice.identity.PublicKey},
|
|
|
|
ResendType: common.ResendTypeDataSync,
|
|
|
|
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_SHARD_KEY,
|
|
|
|
Payload: encodedMessage,
|
2024-01-26 15:29:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err = s.owner.sender.SendPubsubTopicKey(context.Background(), &rawMessage)
|
|
|
|
s.Require().NoError(err)
|
|
|
|
|
|
|
|
_, err = WaitOnMessengerResponse(s.alice, func(mr *MessengerResponse) bool {
|
|
|
|
msgType := protobuf.ApplicationMetadataMessage_COMMUNITY_SHARD_KEY
|
|
|
|
msgs, exists := s.aliceUnhandledMessagesTracker.messages[msgType]
|
|
|
|
if !exists {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, msg := range msgs {
|
|
|
|
p := &protobuf.CommunityShardKey{}
|
|
|
|
err := proto.Unmarshal(msg.ApplicationLayer.Payload, p)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.err == communities.ErrOldShardInfo && p.Shard != nil && p.Shard.Index == int32(shard.Index) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}, "shard info with outdated clock either not received or not ignored")
|
|
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
}
|