372 lines
11 KiB
Go
372 lines
11 KiB
Go
package protocol
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"testing"
|
|
"time"
|
|
|
|
"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/crypto"
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
"github.com/status-im/status-go/protocol/common"
|
|
"github.com/status-im/status-go/protocol/communities"
|
|
"github.com/status-im/status-go/protocol/peersyncing"
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
"github.com/status-im/status-go/protocol/tt"
|
|
"github.com/status-im/status-go/waku"
|
|
)
|
|
|
|
func TestMessengerPeersyncingSuite(t *testing.T) {
|
|
t.Skip("broken test") // FIXME
|
|
suite.Run(t, new(MessengerPeersyncingSuite))
|
|
}
|
|
|
|
type MessengerPeersyncingSuite struct {
|
|
suite.Suite
|
|
owner *Messenger
|
|
bob *Messenger
|
|
alice *Messenger
|
|
// If one wants to send messages between different instances of Messenger,
|
|
// a single Waku service should be shared.
|
|
shh types.Waku
|
|
logger *zap.Logger
|
|
accountsTestData map[string][]string
|
|
accountsPasswords map[string]string
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) SetupTest() {
|
|
s.logger = tt.MustCreateTestLogger()
|
|
peerSyncingLoopInterval = 500 * time.Millisecond
|
|
|
|
config := waku.DefaultConfig
|
|
config.MinimumAcceptedPoW = 0
|
|
shh := waku.New(&config, s.logger)
|
|
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
|
s.Require().NoError(shh.Start())
|
|
|
|
s.owner = s.newMessenger()
|
|
s.bob = s.newMessenger()
|
|
s.alice = s.newMessenger()
|
|
|
|
s.alice.featureFlags.ResendRawMessagesDisabled = true
|
|
s.bob.featureFlags.ResendRawMessagesDisabled = true
|
|
s.owner.featureFlags.ResendRawMessagesDisabled = true
|
|
|
|
s.owner.communitiesManager.RekeyInterval = 50 * time.Millisecond
|
|
|
|
s.accountsTestData = make(map[string][]string)
|
|
s.accountsTestData[common.PubkeyToHex(&s.bob.identity.PublicKey)] = []string{bobAddress}
|
|
s.accountsTestData[common.PubkeyToHex(&s.alice.identity.PublicKey)] = []string{aliceAddress1}
|
|
|
|
s.accountsPasswords = make(map[string]string)
|
|
s.accountsPasswords[common.PubkeyToHex(&s.bob.identity.PublicKey)] = bobPassword
|
|
s.accountsPasswords[common.PubkeyToHex(&s.alice.identity.PublicKey)] = aliceAddress1
|
|
|
|
_, err := s.owner.Start()
|
|
s.Require().NoError(err)
|
|
_, err = s.bob.Start()
|
|
s.Require().NoError(err)
|
|
_, err = s.alice.Start()
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) TearDownTest() {
|
|
TearDownMessenger(&s.Suite, s.owner)
|
|
TearDownMessenger(&s.Suite, s.bob)
|
|
TearDownMessenger(&s.Suite, s.alice)
|
|
_ = s.logger.Sync()
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) newMessenger() *Messenger {
|
|
return newTestCommunitiesMessenger(&s.Suite, s.shh, testCommunitiesMessengerConfig{
|
|
testMessengerConfig: testMessengerConfig{
|
|
logger: s.logger,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) joinCommunity(community *communities.Community, owner *Messenger, user *Messenger) {
|
|
addresses, exists := s.accountsTestData[user.IdentityPublicKeyString()]
|
|
s.Require().True(exists)
|
|
password, exists := s.accountsPasswords[user.IdentityPublicKeyString()]
|
|
s.Require().True(exists)
|
|
joinCommunity(&s.Suite, community.ID(), s.owner, user, password, addresses)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) thirdPartyTest(community *communities.Community, chat *Chat) {
|
|
// We disable resending to make sure that the message is not re-transmitted
|
|
s.alice.featureFlags.Peersyncing = false
|
|
s.owner.featureFlags.Peersyncing = true
|
|
s.bob.featureFlags.Peersyncing = true
|
|
s.owner.communitiesManager.PermissionChecker = &testPermissionChecker{}
|
|
|
|
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
|
|
|
|
s.joinCommunity(community, s.owner, s.alice)
|
|
|
|
chatID := chat.ID
|
|
inputMessage := common.NewMessage()
|
|
inputMessage.ChatId = chatID
|
|
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
|
|
inputMessage.Text = "some text"
|
|
ctx := context.Background()
|
|
|
|
if community.Encrypted() {
|
|
|
|
_, err := WaitOnMessengerResponse(
|
|
s.alice,
|
|
func(r *MessengerResponse) bool {
|
|
keys, err := s.alice.encryptor.GetKeysForGroup([]byte(chat.ID))
|
|
return err == nil && len(keys) > 0
|
|
},
|
|
"keys not received",
|
|
)
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
// Send message, it should be received
|
|
response, err := s.alice.SendChatMessage(ctx, inputMessage)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(response.Messages(), 1)
|
|
messageID := response.Messages()[0].ID
|
|
|
|
// Make sure the message makes it to the owner
|
|
response, err = WaitOnMessengerResponse(
|
|
s.owner,
|
|
func(r *MessengerResponse) bool {
|
|
return len(r.Communities()) > 0 && len(r.Messages()) == 1 && r.Messages()[0].ID == messageID
|
|
},
|
|
"message not received",
|
|
)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
|
|
msg, err := s.owner.peersyncing.AvailableMessages()
|
|
s.Require().NoError(err)
|
|
s.Require().Len(msg, 1)
|
|
|
|
// Bob joins the community
|
|
advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
|
|
|
|
s.joinCommunity(community, s.owner, s.bob)
|
|
|
|
// Bob should now send an offer
|
|
_, err = WaitOnMessengerResponse(
|
|
s.bob,
|
|
func(r *MessengerResponse) bool {
|
|
return s.bob.peersyncingOffers[messageID[2:]] != 0
|
|
},
|
|
"offer not sent",
|
|
)
|
|
s.Require().NoError(err)
|
|
|
|
// Owner should now reply to the offer
|
|
_, err = WaitOnMessengerResponse(
|
|
s.owner,
|
|
func(r *MessengerResponse) bool {
|
|
return s.owner.peersyncingRequests[s.bob.myHexIdentity()+messageID[2:]] != 0
|
|
},
|
|
"request not sent",
|
|
)
|
|
s.Require().NoError(err)
|
|
|
|
// Bob should receive the message
|
|
_, err = WaitOnMessengerResponse(
|
|
s.bob,
|
|
func(r *MessengerResponse) bool {
|
|
return len(r.Messages()) == 1 && r.Messages()[0].ID == messageID
|
|
},
|
|
"message not received",
|
|
)
|
|
s.Require().NoError(err)
|
|
|
|
}
|
|
|
|
// Owner creates a community
|
|
// Owner sends a message
|
|
// Alice joins
|
|
// Alice receives the message
|
|
func (s *MessengerPeersyncingSuite) TestSyncWithPeerCommunitySender() {
|
|
|
|
s.alice.featureFlags.Peersyncing = true
|
|
s.owner.featureFlags.Peersyncing = true
|
|
|
|
// create community and make alice join it
|
|
community, chat := createCommunity(&s.Suite, s.owner)
|
|
|
|
chatID := chat.ID
|
|
inputMessage := common.NewMessage()
|
|
inputMessage.ChatId = chatID
|
|
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
|
|
inputMessage.Text = "some text"
|
|
|
|
// Send message, it should be received
|
|
response, err := s.owner.SendChatMessage(context.Background(), inputMessage)
|
|
s.Require().NoError(err)
|
|
|
|
messageID := response.Messages()[0].ID
|
|
|
|
msg, err := s.owner.peersyncing.AvailableMessages()
|
|
s.Require().NoError(err)
|
|
s.Require().Len(msg, 1)
|
|
|
|
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
|
|
s.joinCommunity(community, s.owner, s.alice)
|
|
|
|
// Alice should now receive the message
|
|
_, err = WaitOnMessengerResponse(
|
|
s.alice,
|
|
func(r *MessengerResponse) bool {
|
|
_, err := s.owner.RetrieveAll()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return len(r.Messages()) == 1 && r.Messages()[0].ID == messageID
|
|
},
|
|
"message not received",
|
|
)
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
// Owner creates a community
|
|
// Alice joins
|
|
// Alice sends a message
|
|
// Owner receives the message
|
|
// Bob joins the community
|
|
// They should retrieve the message from the owner
|
|
|
|
func (s *MessengerPeersyncingSuite) TestSyncWithPeerCommunityThirdPartyEncrypted() {
|
|
community, chat := createEncryptedCommunity(&s.Suite, s.owner)
|
|
s.thirdPartyTest(community, chat)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) TestSyncWithPeerCommunityThirdPartyNotEncrypted() {
|
|
community, chat := createCommunity(&s.Suite, s.owner)
|
|
s.thirdPartyTest(community, chat)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) TestCanSyncMessageWith() {
|
|
community, chat := createCommunity(&s.Suite, s.owner)
|
|
|
|
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
|
|
s.joinCommunity(community, s.owner, s.alice)
|
|
|
|
syncMessage := peersyncing.SyncMessage{
|
|
ID: []byte("test-id"),
|
|
ChatID: []byte(chat.ID),
|
|
Type: peersyncing.SyncMessageCommunityType,
|
|
Payload: []byte("some-payload"),
|
|
Timestamp: 1,
|
|
}
|
|
s.Require().NoError(s.owner.peersyncing.Add(syncMessage))
|
|
|
|
community, err := s.owner.communitiesManager.GetByID(community.ID())
|
|
s.Require().NoError(err)
|
|
|
|
canSyncWithBob, err := s.owner.canSyncCommunityMessageWith(chat, community, &s.bob.identity.PublicKey)
|
|
s.Require().NoError(err)
|
|
s.Require().False(canSyncWithBob)
|
|
|
|
canSyncWithAlice, err := s.owner.canSyncCommunityMessageWith(chat, community, &s.alice.identity.PublicKey)
|
|
s.Require().NoError(err)
|
|
s.Require().True(canSyncWithAlice)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) TestSyncOneToOne() {
|
|
s.alice.featureFlags.Peersyncing = true
|
|
s.owner.featureFlags.Peersyncing = true
|
|
|
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.alice.identity.PublicKey))
|
|
chat := CreateOneToOneChat(pkString, &s.alice.identity.PublicKey, s.owner.transport)
|
|
|
|
chat.LastClockValue = uint64(100000000000000)
|
|
err := s.owner.SaveChat(chat)
|
|
s.NoError(err)
|
|
_, err = s.alice.Join(chat)
|
|
s.NoError(err)
|
|
|
|
chatID := chat.ID
|
|
inputMessage := common.NewMessage()
|
|
inputMessage.ChatId = chatID
|
|
inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN
|
|
inputMessage.Text = "some text"
|
|
|
|
ctx := context.Background()
|
|
|
|
// Send message, it should be received
|
|
response, err := s.alice.SendChatMessage(ctx, inputMessage)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(response.Messages(), 1)
|
|
messageID := response.Messages()[0].ID
|
|
|
|
// Make sure the message makes it to the owner
|
|
response, err = WaitOnMessengerResponse(
|
|
s.owner,
|
|
func(r *MessengerResponse) bool {
|
|
return len(r.Messages()) == 1 && r.Messages()[0].ID == messageID
|
|
},
|
|
"message not received",
|
|
)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
|
|
msg, err := s.owner.peersyncing.AvailableMessages()
|
|
s.Require().NoError(err)
|
|
s.Require().Len(msg, 1)
|
|
|
|
// Alice should now send an offer
|
|
_, err = WaitOnMessengerResponse(
|
|
s.alice,
|
|
func(r *MessengerResponse) bool {
|
|
return s.alice.peersyncingOffers[messageID[2:]] != 0
|
|
},
|
|
"offer not sent",
|
|
)
|
|
s.Require().NoError(err)
|
|
|
|
// Owner should now reply to the offer
|
|
_, err = WaitOnMessengerResponse(
|
|
s.owner,
|
|
func(r *MessengerResponse) bool {
|
|
return s.owner.peersyncingRequests[s.alice.myHexIdentity()+messageID[2:]] != 0
|
|
},
|
|
"request not sent",
|
|
)
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
func (s *MessengerPeersyncingSuite) TestCanSyncOneToOneMessageWith() {
|
|
s.alice.featureFlags.Peersyncing = true
|
|
s.owner.featureFlags.Peersyncing = true
|
|
|
|
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.alice.identity.PublicKey))
|
|
chat := CreateOneToOneChat(pkString, &s.alice.identity.PublicKey, s.owner.transport)
|
|
|
|
chat.LastClockValue = uint64(100000000000000)
|
|
err := s.owner.SaveChat(chat)
|
|
s.NoError(err)
|
|
_, err = s.alice.Join(chat)
|
|
s.NoError(err)
|
|
|
|
syncMessage := peersyncing.SyncMessage{
|
|
ID: []byte("test-id"),
|
|
ChatID: []byte(chat.ID),
|
|
Type: peersyncing.SyncMessageOneToOneType,
|
|
Payload: []byte("some-payload"),
|
|
Timestamp: chat.LastClockValue,
|
|
}
|
|
s.Require().NoError(s.owner.peersyncing.Add(syncMessage))
|
|
|
|
canSyncWithBob, err := s.owner.canSyncOneToOneMessageWith(chat, &s.bob.identity.PublicKey)
|
|
s.Require().NoError(err)
|
|
s.Require().False(canSyncWithBob)
|
|
|
|
canSyncWithAlice, err := s.owner.canSyncOneToOneMessageWith(chat, &s.alice.identity.PublicKey)
|
|
s.Require().NoError(err)
|
|
s.Require().True(canSyncWithAlice)
|
|
}
|