fix: don't store ourselves as a contact (#3627)

This commit is contained in:
Igor Sirotin 2023-07-12 12:46:56 +03:00 committed by GitHub
parent ff0628c23b
commit 244b4273de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 287 additions and 45 deletions

View File

@ -451,6 +451,12 @@ func NewMessenger(
savedAddressesManager := wallet.NewSavedAddressesManager(c.db) savedAddressesManager := wallet.NewSavedAddressesManager(c.db)
myPublicKeyString := types.EncodeHex(crypto.FromECDSAPub(&identity.PublicKey))
myContact, err := buildContact(myPublicKeyString, &identity.PublicKey)
if err != nil {
return nil, errors.New("failed to build contact of ourself: " + err.Error())
}
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
messenger = &Messenger{ messenger = &Messenger{
@ -472,17 +478,20 @@ func NewMessenger(
featureFlags: c.featureFlags, featureFlags: c.featureFlags,
systemMessagesTranslations: c.systemMessagesTranslations, systemMessagesTranslations: c.systemMessagesTranslations,
allChats: new(chatMap), allChats: new(chatMap),
allContacts: new(contactMap), allContacts: &contactMap{
allInstallations: new(installationMap), logger: logger,
installationID: installationID, me: myContact,
modifiedInstallations: new(stringBoolMap), },
verifyTransactionClient: c.verifyTransactionClient, allInstallations: new(installationMap),
database: database, installationID: installationID,
multiAccounts: c.multiAccount, modifiedInstallations: new(stringBoolMap),
settings: settings, verifyTransactionClient: c.verifyTransactionClient,
peerStore: peerStore, database: database,
verificationDatabase: verification.NewPersistence(database), multiAccounts: c.multiAccount,
mailservers: mailservers, settings: settings,
peerStore: peerStore,
verificationDatabase: verification.NewPersistence(database),
mailservers: mailservers,
mailserverCycle: mailserverCycle{ mailserverCycle: mailserverCycle{
peers: make(map[string]peerStatus), peers: make(map[string]peerStatus),
availabilitySubscriptions: make([]chan struct{}, 0), availabilitySubscriptions: make([]chan struct{}, 0),
@ -825,6 +834,10 @@ func (m *Messenger) IdentityPublicKeyCompressed() []byte {
return crypto.CompressPubkey(m.IdentityPublicKey()) return crypto.CompressPubkey(m.IdentityPublicKey())
} }
func (m *Messenger) IdentityPublicKeyString() string {
return types.EncodeHex(crypto.FromECDSAPub(m.IdentityPublicKey()))
}
// cleanTopics remove any topic that does not have a Listen flag set // cleanTopics remove any topic that does not have a Listen flag set
func (m *Messenger) cleanTopics() error { func (m *Messenger) cleanTopics() error {
if m.mailserversDatabase == nil { if m.mailserversDatabase == nil {
@ -3423,10 +3436,13 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
} }
senderID := contactIDFromPublicKey(publicKey) senderID := contactIDFromPublicKey(publicKey)
m.logger.Info("processing message", zap.Any("type", msg.Type), zap.String("senderID", senderID))
contact, contactFound := messageState.AllContacts.Load(senderID)
if _, ok := m.requestedContacts[senderID]; !ok { if _, ok := m.requestedContacts[senderID]; !ok {
// Check for messages from blocked users // Check for messages from blocked users
if contact, ok := messageState.AllContacts.Load(senderID); ok && contact.Blocked { if contactFound && contact.Blocked {
continue continue
} }
} }
@ -3442,10 +3458,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
continue continue
} }
var contact *Contact if !contactFound {
if c, ok := messageState.AllContacts.Load(senderID); ok {
contact = c
} else {
c, err := buildContact(senderID, publicKey) c, err := buildContact(senderID, publicKey)
if err != nil { if err != nil {
logger.Info("failed to build contact", zap.Error(err)) logger.Info("failed to build contact", zap.Error(err))

View File

@ -62,12 +62,7 @@ func (s *MessengerContactRequestSuite) newMessenger(shh types.Waku) *Messenger {
} }
func (s *MessengerContactRequestSuite) findFirstByContentType(messages []*common.Message, contentType protobuf.ChatMessage_ContentType) *common.Message { func (s *MessengerContactRequestSuite) findFirstByContentType(messages []*common.Message, contentType protobuf.ChatMessage_ContentType) *common.Message {
for _, message := range messages { return FindFirstByContentType(messages, contentType)
if message.ContentType == contentType {
return message
}
}
return nil
} }
func (s *MessengerContactRequestSuite) sendContactRequest(request *requests.SendContactRequest, messenger *Messenger) { func (s *MessengerContactRequestSuite) sendContactRequest(request *requests.SendContactRequest, messenger *Messenger) {

View File

@ -267,8 +267,12 @@ func (m *Messenger) SendContactRequest(ctx context.Context, request *requests.Se
} }
func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID string) (*MessengerResponse, error) { func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, contactRequestID string) (*MessengerResponse, error) {
m.logger.Debug("updateAcceptedContactRequest", zap.String("contactRequestID", contactRequestID))
contactRequest, err := m.persistence.MessageByID(contactRequestID) contactRequest, err := m.persistence.MessageByID(contactRequestID)
if err != nil { if err != nil {
m.logger.Error("contact request not found", zap.String("contactRequestID", contactRequestID), zap.Error(err))
return nil, err return nil, err
} }
@ -279,7 +283,11 @@ func (m *Messenger) updateAcceptedContactRequest(response *MessengerResponse, co
return nil, err return nil, err
} }
contact, _ := m.allContacts.Load(contactRequest.From) contact, ok := m.allContacts.Load(contactRequest.From)
if !ok {
m.logger.Error("failed to update contact request: contact not found", zap.String("contact id", contactRequest.From))
return nil, errors.New("failed to update contact request: contact not found")
}
_, clock, err := m.getOneToOneAndNextClock(contact) _, clock, err := m.getOneToOneAndNextClock(contact)
if err != nil { if err != nil {

View File

@ -456,6 +456,7 @@ func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *c
} }
func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, state *ReceivedMessageState, chat *Chat, outgoing bool) error { func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, state *ReceivedMessageState, chat *Chat, outgoing bool) error {
if chat == nil { if chat == nil {
return fmt.Errorf("no chat restored during the contact synchronisation, contact.ID = %s", contact.ID) return fmt.Errorf("no chat restored during the contact synchronisation, contact.ID = %s", contact.ID)
} }
@ -466,6 +467,7 @@ func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, s
} }
if contactRequestID != "" { if contactRequestID != "" {
m.logger.Warn("syncContactRequestForInstallationContact: skipping as contact request found", zap.String("contactRequestID", contactRequestID))
return nil return nil
} }
@ -501,7 +503,9 @@ func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, s
func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, message protobuf.SyncInstallationContactV2) error { func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, message protobuf.SyncInstallationContactV2) error {
// Ignore own contact installation // Ignore own contact installation
if message.Id == m.myHexIdentity() { if message.Id == m.myHexIdentity() {
m.logger.Warn("HandleSyncInstallationContact: skipping own contact")
return nil return nil
} }

View File

@ -3,6 +3,8 @@ package protocol
import ( import (
"sync" "sync"
"go.uber.org/zap"
"github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
) )
@ -53,10 +55,16 @@ func (cm *chatMap) Delete(chatID string) {
*/ */
type contactMap struct { type contactMap struct {
sm sync.Map sm sync.Map
me *Contact
logger *zap.Logger
} }
func (cm *contactMap) Load(contactID string) (*Contact, bool) { func (cm *contactMap) Load(contactID string) (*Contact, bool) {
if contactID == cm.me.ID {
cm.logger.Warn("contacts map: loading own identity", zap.String("contactID", contactID))
return cm.me, true
}
contact, ok := cm.sm.Load(contactID) contact, ok := cm.sm.Load(contactID)
if contact == nil { if contact == nil {
return nil, ok return nil, ok
@ -65,6 +73,10 @@ func (cm *contactMap) Load(contactID string) (*Contact, bool) {
} }
func (cm *contactMap) Store(contactID string, contact *Contact) { func (cm *contactMap) Store(contactID string, contact *Contact) {
if contactID == cm.me.ID {
cm.logger.Warn("contacts map: storing own identity", zap.String("contactID", contactID))
return
}
cm.sm.Store(contactID, contact) cm.sm.Store(contactID, contact)
} }
@ -76,6 +88,10 @@ func (cm *contactMap) Range(f func(contactID string, contact *Contact) (shouldCo
} }
func (cm *contactMap) Delete(contactID string) { func (cm *contactMap) Delete(contactID string) {
if contactID == cm.me.ID {
cm.logger.Warn("contacts map: deleting own identity", zap.String("contactID", contactID))
return
}
cm.sm.Delete(contactID) cm.sm.Delete(contactID)
} }

View File

@ -2439,24 +2439,3 @@ type testTimeSource struct{}
func (t *testTimeSource) GetCurrentTime() uint64 { func (t *testTimeSource) GetCurrentTime() uint64 {
return uint64(time.Now().Unix()) return uint64(time.Now().Unix())
} }
// WaitOnMessengerResponse Wait until the condition is true or the timeout is reached.
func WaitOnMessengerResponse(m *Messenger, condition func(*MessengerResponse) bool, errorMessage string) (*MessengerResponse, error) {
response := &MessengerResponse{}
err := tt.RetryWithBackOff(func() error {
var err error
r, err := m.RetrieveAll()
if err := response.Merge(r); err != nil {
panic(err)
}
if err == nil && !condition(response) {
err = errors.New(errorMessage)
}
return err
})
if err != nil {
return nil, err
}
return response, nil
}

View File

@ -0,0 +1,39 @@
package protocol
import (
"errors"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/tt"
)
// WaitOnMessengerResponse Wait until the condition is true or the timeout is reached.
func WaitOnMessengerResponse(m *Messenger, condition func(*MessengerResponse) bool, errorMessage string) (*MessengerResponse, error) {
response := &MessengerResponse{}
err := tt.RetryWithBackOff(func() error {
var err error
r, err := m.RetrieveAll()
if err := response.Merge(r); err != nil {
panic(err)
}
if err == nil && !condition(response) {
err = errors.New(errorMessage)
}
return err
})
if err != nil {
return nil, err
}
return response, nil
}
func FindFirstByContentType(messages []*common.Message, contentType protobuf.ChatMessage_ContentType) *common.Message {
for _, message := range messages {
if message.ContentType == contentType {
return message
}
}
return nil
}

View File

@ -7,6 +7,11 @@ import (
"testing" "testing"
"time" "time"
"go.uber.org/zap"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/protocol/tt"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -48,15 +53,19 @@ func TestSyncDeviceSuite(t *testing.T) {
type SyncDeviceSuite struct { type SyncDeviceSuite struct {
suite.Suite suite.Suite
logger *zap.Logger
password string password string
clientAsSenderTmpdir string clientAsSenderTmpdir string
clientAsReceiverTmpdir string clientAsReceiverTmpdir string
pairThreeDevicesTmpdir string
} }
func (s *SyncDeviceSuite) SetupTest() { func (s *SyncDeviceSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
s.password = "password" s.password = "password"
s.clientAsSenderTmpdir = s.T().TempDir() s.clientAsSenderTmpdir = s.T().TempDir()
s.clientAsReceiverTmpdir = s.T().TempDir() s.clientAsReceiverTmpdir = s.T().TempDir()
s.pairThreeDevicesTmpdir = s.T().TempDir()
} }
func (s *SyncDeviceSuite) prepareBackendWithAccount(tmpdir string) *api.GethStatusBackend { func (s *SyncDeviceSuite) prepareBackendWithAccount(tmpdir string) *api.GethStatusBackend {
@ -123,6 +132,128 @@ func (s *SyncDeviceSuite) prepareBackendWithoutAccount(tmpdir string) *api.GethS
return backend return backend
} }
func (s *SyncDeviceSuite) pairAccounts(serverBackend *api.GethStatusBackend, serverDir string,
clientBackend *api.GethStatusBackend, clientDir string) {
// Start sender server
serverActiveAccount, err := serverBackend.GetActiveAccount()
require.NoError(s.T(), err)
serverKeystorePath := filepath.Join(serverDir, keystoreDir, serverActiveAccount.KeyUID)
serverConfig := &SenderServerConfig{
SenderConfig: &SenderConfig{
KeystorePath: serverKeystorePath,
DeviceType: "desktop",
KeyUID: serverActiveAccount.KeyUID,
Password: s.password,
},
ServerConfig: new(ServerConfig),
}
configBytes, err := json.Marshal(serverConfig)
require.NoError(s.T(), err)
connectionString, err := StartUpSenderServer(serverBackend, string(configBytes))
require.NoError(s.T(), err)
// Start receiving client
err = clientBackend.AccountManager().InitKeystore(filepath.Join(clientDir, keystoreDir))
require.NoError(s.T(), err)
err = clientBackend.OpenAccounts()
require.NoError(s.T(), err)
clientNodeConfig, err := defaultNodeConfig(uuid.New().String(), "")
require.NoError(s.T(), err)
expectedKDFIterations := 2048
clientKeystoreDir := filepath.Join(clientDir, keystoreDir)
clientPayloadSourceConfig := ReceiverClientConfig{
ReceiverConfig: &ReceiverConfig{
KeystorePath: clientKeystoreDir,
DeviceType: "desktop",
KDFIterations: expectedKDFIterations,
NodeConfig: clientNodeConfig,
SettingCurrentNetwork: currentNetwork,
},
ClientConfig: new(ClientConfig),
}
clientNodeConfig.RootDataDir = clientDir
clientConfigBytes, err := json.Marshal(clientPayloadSourceConfig)
require.NoError(s.T(), err)
err = StartUpReceivingClient(clientBackend, connectionString, string(clientConfigBytes))
require.NoError(s.T(), err)
require.True(s.T(), serverBackend.Messenger().HasPairedDevices())
require.True(s.T(), clientBackend.Messenger().HasPairedDevices())
}
func (s *SyncDeviceSuite) sendContactRequest(request *requests.SendContactRequest, messenger *protocol.Messenger) {
senderPublicKey := common.PubkeyToHex(messenger.IdentityPublicKey())
s.logger.Info("sendContactRequest", zap.String("sender", senderPublicKey), zap.String("receiver", request.ID))
resp, err := messenger.SendContactRequest(context.Background(), request)
s.Require().NoError(err)
s.Require().NotNil(resp)
}
func (s *SyncDeviceSuite) receiveContactRequest(messageText string, messenger *protocol.Messenger) *common.Message {
receiverPublicKey := types.EncodeHex(crypto.FromECDSAPub(messenger.IdentityPublicKey()))
s.logger.Info("receiveContactRequest", zap.String("receiver", receiverPublicKey))
// Wait for the message to reach its destination
resp, err := protocol.WaitOnMessengerResponse(
messenger,
func(r *protocol.MessengerResponse) bool {
return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1
},
"no messages",
)
s.Require().NoError(err)
s.Require().NotNil(resp)
contactRequest := protocol.FindFirstByContentType(resp.Messages(), protobuf.ChatMessage_CONTACT_REQUEST)
s.Require().NotNil(contactRequest)
return contactRequest
}
func (s *SyncDeviceSuite) acceptContactRequest(contactRequest *common.Message, sender *protocol.Messenger, receiver *protocol.Messenger) {
senderPublicKey := types.EncodeHex(crypto.FromECDSAPub(sender.IdentityPublicKey()))
receiverPublicKey := types.EncodeHex(crypto.FromECDSAPub(receiver.IdentityPublicKey()))
s.logger.Info("acceptContactRequest", zap.String("sender", senderPublicKey), zap.String("receiver", receiverPublicKey))
_, err := receiver.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequest.ID)})
s.Require().NoError(err)
// Wait for the message to reach its destination
resp, err := protocol.WaitOnMessengerResponse(
sender,
func(r *protocol.MessengerResponse) bool {
return len(r.Contacts) == 1 && len(r.Messages()) == 2 && len(r.ActivityCenterNotifications()) == 1
},
"no messages",
)
s.Require().NoError(err)
s.Require().NotNil(resp)
}
func (s *SyncDeviceSuite) checkMutualContact(backend *api.GethStatusBackend, contactPublicKey string) {
messenger := backend.Messenger()
contacts := messenger.MutualContacts()
s.Require().Len(contacts, 1)
contact := contacts[0]
s.Require().Equal(contactPublicKey, contact.ID)
s.Require().Equal(protocol.ContactRequestStateSent, contact.ContactRequestLocalState)
s.Require().Equal(protocol.ContactRequestStateReceived, contact.ContactRequestRemoteState)
s.Require().NotNil(contact.DisplayName)
}
func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() { func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
clientTmpDir := filepath.Join(s.clientAsSenderTmpdir, "client") clientTmpDir := filepath.Join(s.clientAsSenderTmpdir, "client")
clientBackend := s.prepareBackendWithAccount(clientTmpDir) clientBackend := s.prepareBackendWithAccount(clientTmpDir)
@ -379,6 +510,63 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsReceiver() {
require.NoError(s.T(), err) require.NoError(s.T(), err)
} }
func (s *SyncDeviceSuite) TestPairingThreeDevices() {
bobTmpDir := filepath.Join(s.pairThreeDevicesTmpdir, "bob")
bobBackend := s.prepareBackendWithAccount(bobTmpDir)
bobMessenger := bobBackend.Messenger()
_, err := bobMessenger.Start()
s.Require().NoError(err)
alice1TmpDir := filepath.Join(s.pairThreeDevicesTmpdir, "alice1")
alice1Backend := s.prepareBackendWithAccount(alice1TmpDir)
alice1Messenger := alice1Backend.Messenger()
_, err = alice1Messenger.Start()
s.Require().NoError(err)
alice2TmpDir := filepath.Join(s.pairThreeDevicesTmpdir, "alice2")
alice2Backend := s.prepareBackendWithoutAccount(alice2TmpDir)
alice3TmpDir := filepath.Join(s.pairThreeDevicesTmpdir, "alice3")
alice3Backend := s.prepareBackendWithAccount(alice3TmpDir)
defer func() {
require.NoError(s.T(), bobBackend.Logout())
require.NoError(s.T(), alice1Backend.Logout())
require.NoError(s.T(), alice2Backend.Logout())
require.NoError(s.T(), alice3Backend.Logout())
}()
// Make Alice and Bob mutual contacts
messageText := "hello!"
bobPublicKey := types.EncodeHex(crypto.FromECDSAPub(bobMessenger.IdentityPublicKey()))
request := &requests.SendContactRequest{
ID: bobPublicKey,
Message: messageText,
}
s.sendContactRequest(request, alice1Messenger)
contactRequest := s.receiveContactRequest(messageText, bobMessenger)
s.acceptContactRequest(contactRequest, alice1Messenger, bobMessenger)
s.checkMutualContact(alice1Backend, bobPublicKey)
// We shouldn't sync ourselves as a contact, so we check there's only Bob
// https://github.com/status-im/status-go/issues/3667
s.Require().Equal(1, len(alice1Backend.Messenger().Contacts()))
// Pair alice-1 <-> alice-2
s.logger.Info("pairing Alice-1 and Alice-2")
s.pairAccounts(alice1Backend, alice1TmpDir, alice2Backend, alice2TmpDir)
s.checkMutualContact(alice2Backend, bobPublicKey)
s.Require().Equal(1, len(alice2Backend.Messenger().Contacts()))
// Pair Alice-2 <-> ALice-3
s.logger.Info("pairing Alice-2 and Alice-3")
s.pairAccounts(alice2Backend, alice2TmpDir, alice3Backend, alice3TmpDir)
s.checkMutualContact(alice3Backend, bobPublicKey)
s.Require().Equal(1, len(alice3Backend.Messenger().Contacts()))
}
func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*settings.Settings, error) { func defaultSettings(generatedAccountInfo generator.GeneratedAccountInfo, derivedAddresses map[string]generator.AccountInfo, mnemonic *string) (*settings.Settings, error) {
chatKeyString := derivedAddresses[pathDefaultChat].PublicKey chatKeyString := derivedAddresses[pathDefaultChat].PublicKey