fix: create a CR on contact sync with received CR state (#3495)

* fix: create a CR on contact sync with received CR state

* fix: create a CR on contact sync with sent CR state

* Review fixes

* Fix: ignore own contact installation or syncing
This commit is contained in:
Mikhail Rogachev 2023-05-22 16:22:33 +04:00 committed by GitHub
parent 8b05ba6d8b
commit 8c90353bc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 259 additions and 22 deletions

View File

@ -158,9 +158,9 @@ func (s *MessengerContactRequestSuite) receiveContactRequest(messageText string,
return contactRequest return contactRequest
} }
func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *common.Message, theirMessenger *Messenger) { func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *common.Message, sender *Messenger, receiver *Messenger) {
// Accept contact request, receiver side // Accept contact request, receiver side
resp, err := theirMessenger.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequest.ID)}) resp, err := receiver.AcceptContactRequest(context.Background(), &requests.AcceptContactRequest{ID: types.Hex2Bytes(contactRequest.ID)})
s.Require().NoError(err) s.Require().NoError(err)
// Make sure the message is updated // Make sure the message is updated
@ -187,16 +187,16 @@ func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *comm
s.Require().True(resp.Chats()[1].Active) s.Require().True(resp.Chats()[1].Active)
// Make sure the sender is added to our contacts // Make sure the sender is added to our contacts
contacts := theirMessenger.AddedContacts() contacts := receiver.AddedContacts()
s.Require().Len(contacts, 1) s.Require().Len(contacts, 1)
// Make sure we consider them a mutual contact, receiver side // Make sure we consider them a mutual contact, receiver side
mutualContacts := theirMessenger.MutualContacts() mutualContacts := receiver.MutualContacts()
s.Require().Len(mutualContacts, 1) s.Require().Len(mutualContacts, 1)
// Wait for the message to reach its destination // Wait for the message to reach its destination
resp, err = WaitOnMessengerResponse( resp, err = WaitOnMessengerResponse(
s.m, sender,
func(r *MessengerResponse) bool { func(r *MessengerResponse) bool {
return len(r.Contacts) == 1 && len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 1 return len(r.Contacts) == 1 && len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 1
}, },
@ -240,7 +240,7 @@ func (s *MessengerContactRequestSuite) acceptContactRequest(contactRequest *comm
// Receiver's side chat should be also active after the accepting the CR // Receiver's side chat should be also active after the accepting the CR
myID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey)) myID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey))
chat, ok = theirMessenger.allChats.Load(myID) chat, ok = receiver.allChats.Load(myID)
s.Require().True(ok) s.Require().True(ok)
s.Require().NotNil(chat) s.Require().NotNil(chat)
s.Require().True(chat.Active) s.Require().True(chat.Active)
@ -314,6 +314,28 @@ func (s *MessengerContactRequestSuite) retractContactRequest(contactID string, t
s.Require().Equal(ContactRequestStateNone, resp.Contacts[0].ContactRequestRemoteState) s.Require().Equal(ContactRequestStateNone, resp.Contacts[0].ContactRequestRemoteState)
} }
func (s *MessengerContactRequestSuite) syncInstallationContactV2FromContact(contact *Contact) protobuf.SyncInstallationContactV2 {
return protobuf.SyncInstallationContactV2{
LastUpdatedLocally: contact.LastUpdatedLocally,
LastUpdated: contact.LastUpdated,
Id: contact.ID,
DisplayName: contact.DisplayName,
EnsName: contact.EnsName,
LocalNickname: contact.LocalNickname,
Added: contact.added(),
Blocked: contact.Blocked,
Muted: false,
HasAddedUs: contact.hasAddedUs(),
Removed: contact.Removed,
ContactRequestLocalState: int64(contact.ContactRequestLocalState),
ContactRequestRemoteState: int64(contact.ContactRequestRemoteState),
ContactRequestRemoteClock: int64(contact.ContactRequestRemoteClock),
ContactRequestLocalClock: int64(contact.ContactRequestLocalClock),
VerificationStatus: int64(contact.VerificationStatus),
TrustStatus: int64(contact.TrustStatus),
}
}
func (s *MessengerContactRequestSuite) TestReceiveAndAcceptContactRequest() { //nolint: unused func (s *MessengerContactRequestSuite) TestReceiveAndAcceptContactRequest() { //nolint: unused
messageText := "hello!" messageText := "hello!"
@ -328,7 +350,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAndAcceptContactRequest() { //
} }
s.sendContactRequest(request, s.m) s.sendContactRequest(request, s.m)
contactRequest := s.receiveContactRequest(messageText, theirMessenger) contactRequest := s.receiveContactRequest(messageText, theirMessenger)
s.acceptContactRequest(contactRequest, theirMessenger) s.acceptContactRequest(contactRequest, s.m, theirMessenger)
} }
func (s *MessengerContactRequestSuite) TestReceiveAndDismissContactRequest() { func (s *MessengerContactRequestSuite) TestReceiveAndDismissContactRequest() {
@ -364,7 +386,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAcceptAndRetractContactRequest
} }
s.sendContactRequest(request, s.m) s.sendContactRequest(request, s.m)
contactRequest := s.receiveContactRequest(messageText, theirMessenger) contactRequest := s.receiveContactRequest(messageText, theirMessenger)
s.acceptContactRequest(contactRequest, theirMessenger) s.acceptContactRequest(contactRequest, s.m, theirMessenger)
s.retractContactRequest(contactID, theirMessenger) s.retractContactRequest(contactID, theirMessenger)
} }
@ -382,7 +404,7 @@ func (s *MessengerContactRequestSuite) TestReceiveAndAcceptContactRequestTwice()
} }
s.sendContactRequest(request, s.m) s.sendContactRequest(request, s.m)
contactRequest := s.receiveContactRequest(messageText, theirMessenger) contactRequest := s.receiveContactRequest(messageText, theirMessenger)
s.acceptContactRequest(contactRequest, theirMessenger) s.acceptContactRequest(contactRequest, s.m, theirMessenger)
// Resend contact request with higher clock value // Resend contact request with higher clock value
resp, err := s.m.SendContactRequest(context.Background(), request) resp, err := s.m.SendContactRequest(context.Background(), request)
@ -540,7 +562,7 @@ func (s *MessengerContactRequestSuite) TestPairedDevicesRemoveContact() {
} }
s.sendContactRequest(request, alice1) s.sendContactRequest(request, alice1)
contactRequest := s.receiveContactRequest(messageText, bob) contactRequest := s.receiveContactRequest(messageText, bob)
s.acceptContactRequest(contactRequest, bob) s.acceptContactRequest(contactRequest, alice1, bob)
// Wait for the message to reach its destination // Wait for the message to reach its destination
resp, err := WaitOnMessengerResponse( resp, err := WaitOnMessengerResponse(
@ -608,7 +630,7 @@ func (s *MessengerContactRequestSuite) TestAliceRecoverStateSendContactRequest()
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
// Bob accepts the contact request // Bob accepts the contact request
s.acceptContactRequest(contactRequest, bob) s.acceptContactRequest(contactRequest, alice1, bob)
// Alice resets her device // Alice resets her device
alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil) alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil)
@ -678,7 +700,7 @@ func (s *MessengerContactRequestSuite) TestAliceRecoverStateReceiveContactReques
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
// Bob accepts the contact request // Bob accepts the contact request
s.acceptContactRequest(contactRequest, bob) s.acceptContactRequest(contactRequest, alice1, bob)
// Alice resets her device // Alice resets her device
alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil) alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil)
@ -757,7 +779,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsCorrectOrd
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
// Bob accepts the contact request // Bob accepts the contact request
s.acceptContactRequest(contactRequest, bob) s.acceptContactRequest(contactRequest, alice1, bob)
// Alice removes Bob from contacts // Alice removes Bob from contacts
_, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())}) _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())})
@ -806,7 +828,7 @@ func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsWrongOrder
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
// Bob accepts the contact request // Bob accepts the contact request
s.acceptContactRequest(contactRequest, bob) s.acceptContactRequest(contactRequest, alice1, bob)
// Alice removes Bob from contacts // Alice removes Bob from contacts
_, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())}) _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ID: types.Hex2Bytes(bob.myHexIdentity())})
@ -856,7 +878,7 @@ func (s *MessengerContactRequestSuite) TestAliceResendsContactRequestAfterRemovi
// Bob accepts the contact request // Bob accepts the contact request
contactRequest := s.receiveContactRequest(messageTextFirst, theirMessenger) contactRequest := s.receiveContactRequest(messageTextFirst, theirMessenger)
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
s.acceptContactRequest(contactRequest, theirMessenger) s.acceptContactRequest(contactRequest, s.m, theirMessenger)
// Alice removes Bob from contacts // Alice removes Bob from contacts
s.retractContactRequest(contactID, theirMessenger) s.retractContactRequest(contactID, theirMessenger)
@ -878,7 +900,7 @@ func (s *MessengerContactRequestSuite) TestAliceResendsContactRequestAfterRemovi
// Bob accepts new contact request // Bob accepts new contact request
contactRequest = s.receiveContactRequest(messageTextSecond, theirMessenger) contactRequest = s.receiveContactRequest(messageTextSecond, theirMessenger)
s.Require().NotNil(contactRequest) s.Require().NotNil(contactRequest)
s.acceptContactRequest(contactRequest, theirMessenger) s.acceptContactRequest(contactRequest, s.m, theirMessenger)
// Make sure bob and alice are not mutual after sending CR // Make sure bob and alice are not mutual after sending CR
s.Require().Len(s.m.MutualContacts(), 1) s.Require().Len(s.m.MutualContacts(), 1)
@ -1052,3 +1074,151 @@ func (s *MessengerContactRequestSuite) TestReceiveAcceptAndRetractContactRequest
s.Require().Len(contacts, 1) s.Require().Len(contacts, 1)
s.Require().Equal(ContactRequestStateReceived, contacts[0].ContactRequestRemoteState) s.Require().Equal(ContactRequestStateReceived, contacts[0].ContactRequestRemoteState)
} }
// The scenario tested is as follow:
// 1) Alice sends a contact request to Bob
// 2) Bob receives CR from Alice
// 3) Bob resets his device
// 4) Bob restores Alice's contact from backup, CR is created
// 5) Bob succesefully accepts restored contact request
// 6) Alice get notified properly
func (s *MessengerContactRequestSuite) TestBobRestoresIncomingContactRequestFromSyncInstallationContactV2() {
messageText := "hello, Bobby!"
alice := s.m
bob1 := s.newMessenger(s.shh)
_, err := bob1.Start()
s.Require().NoError(err)
aliceID := types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey))
bobID := types.EncodeHex(crypto.FromECDSAPub(&bob1.identity.PublicKey))
// Alice sends a contact request to bob
requestFromAlice := &requests.SendContactRequest{
ID: bobID,
Message: messageText,
}
s.sendContactRequest(requestFromAlice, alice)
// Bob receives CR from Alice
contactRequest := s.receiveContactRequest(messageText, bob1)
s.Require().NotNil(contactRequest)
// Bob resets his device
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
_, err = bob2.Start()
s.Require().NoError(err)
// Get bob perspective of alice for backup
aliceFromBob := bob1.Contacts()[0]
state := bob2.buildMessageState()
// Restore alice's contact from backup
sync := s.syncInstallationContactV2FromContact(aliceFromBob)
err = bob2.HandleSyncInstallationContact(state, sync)
s.Require().NoError(err)
// Accept latest CR for a contact
resp, err := bob2.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(aliceID)})
s.Require().NoError(err)
// Make sure the message is updated
s.Require().NotNil(resp)
s.Require().Len(resp.Messages(), 1)
// NOTE: We don't restore CR message
// s.Require().Equal(resp.Messages()[0].ID, contactRequest.ID)
s.Require().Equal(common.ContactRequestStateAccepted, resp.Messages()[0].ContactRequestState)
s.Require().Len(resp.ActivityCenterNotifications(), 1)
s.Require().NotNil(resp.ActivityCenterNotifications()[0].Message)
s.Require().Equal(common.ContactRequestStateAccepted, resp.ActivityCenterNotifications()[0].Message.ContactRequestState)
// Check the contact state is correctly set
s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual())
// Make sure the sender is added to our contacts
contacts := bob2.AddedContacts()
s.Require().Len(contacts, 1)
// Make sure we consider them a mutual contact, receiver side
mutualContacts := bob2.MutualContacts()
s.Require().Len(mutualContacts, 1)
}
// The scenario tested is as follow:
// 1) Alice sends a contact request to Bob
// 2) Bob receives CR from Alice
// 3) Alice resets her device
// 4) Alice restores Bob's contact from backup, CR is created
// 5) Bob accepts contact request
// 6) Alice get notified properly
func (s *MessengerContactRequestSuite) TestAliceRestoresOutgoingContactRequestFromSyncInstallationContactV2() {
messageText := "hello, Bobby!"
alice1 := s.m
bob := s.newMessenger(s.shh)
_, err := bob.Start()
s.Require().NoError(err)
aliceID := types.EncodeHex(crypto.FromECDSAPub(&alice1.identity.PublicKey))
bobID := types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey))
// Alice sends a contact request to bob
requestFromAlice := &requests.SendContactRequest{
ID: bobID,
Message: messageText,
}
s.sendContactRequest(requestFromAlice, alice1)
// Bob receives CR from Alice
contactRequest := s.receiveContactRequest(messageText, bob)
s.Require().NotNil(contactRequest)
// Bob resets his device
alice2, err := newMessengerWithKey(s.shh, alice1.identity, s.logger, nil)
s.Require().NoError(err)
_, err = alice2.Start()
s.Require().NoError(err)
// Get bob perspective of alice for backup
bobFromAlice := alice1.Contacts()[0]
state := alice2.buildMessageState()
// Restore alice's contact from backup
sync := s.syncInstallationContactV2FromContact(bobFromAlice)
err = alice2.HandleSyncInstallationContact(state, sync)
s.Require().NoError(err)
// Accept latest CR for a contact
resp, err := bob.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(aliceID)})
s.Require().NoError(err)
// Make sure the message is updated
s.Require().NotNil(resp)
s.Require().Len(resp.Messages(), 1)
// NOTE: We don't restore CR message
// s.Require().Equal(resp.Messages()[0].ID, contactRequest.ID)
s.Require().Equal(common.ContactRequestStateAccepted, resp.Messages()[0].ContactRequestState)
s.Require().Len(resp.ActivityCenterNotifications(), 1)
s.Require().NotNil(resp.ActivityCenterNotifications()[0].Message)
s.Require().Equal(common.ContactRequestStateAccepted, resp.ActivityCenterNotifications()[0].Message.ContactRequestState)
// Check the contact state is correctly set
s.Require().Len(resp.Contacts, 1)
s.Require().True(resp.Contacts[0].mutual())
// Make sure the sender is added to our contacts
contacts := bob.AddedContacts()
s.Require().Len(contacts, 1)
// Make sure we consider them a mutual contact, receiver side
mutualContacts := bob.MutualContacts()
s.Require().Len(mutualContacts, 1)
}

View File

@ -475,7 +475,7 @@ func (m *Messenger) AddContact(ctx context.Context, request *requests.AddContact
request.Nickname, request.Nickname,
request.DisplayName, request.DisplayName,
"", "",
"Please add me to your contacts", defaultContactRequestText(),
false, false,
true, true,
true, true,
@ -962,6 +962,10 @@ func defaultContactRequestID(contactID string) string {
return "0x" + types.Bytes2Hex(append(types.Hex2Bytes(contactID), 0x20)) return "0x" + types.Bytes2Hex(append(types.Hex2Bytes(contactID), 0x20))
} }
func defaultContactRequestText() string {
return "Please add me to your contacts"
}
func (m *Messenger) BuildContact(request *requests.BuildContact) (*Contact, error) { func (m *Messenger) BuildContact(request *requests.BuildContact) (*Contact, error) {
contact, ok := m.allContacts.Load(request.PublicKey) contact, ok := m.allContacts.Load(request.PublicKey)
if !ok { if !ok {

View File

@ -275,7 +275,7 @@ func (m *Messenger) createContactRequestForContactUpdate(contact *Contact, messa
messageState.CurrentMessageState.Message.Clock, messageState.CurrentMessageState.Message.Clock,
messageState.CurrentMessageState.WhisperTimestamp, messageState.CurrentMessageState.WhisperTimestamp,
contact, contact,
"Please add me to your contacts", defaultContactRequestText(),
false, false,
) )
if err != nil { if err != nil {
@ -328,8 +328,8 @@ func (m *Messenger) createIncomingContactRequestNotification(contact *Contact, m
Name: contact.PrimaryName(), Name: contact.PrimaryName(),
Message: contactRequest, Message: contactRequest,
Type: ActivityCenterNotificationTypeContactRequest, Type: ActivityCenterNotificationTypeContactRequest,
Author: messageState.CurrentMessageState.Contact.ID, Author: contactRequest.From,
Timestamp: messageState.CurrentMessageState.WhisperTimestamp, Timestamp: contactRequest.WhisperTimestamp,
ChatID: contact.ID, ChatID: contact.ID,
Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted || contactRequest.ContactRequestState == common.ContactRequestStateDismissed, Read: contactRequest.ContactRequestState == common.ContactRequestStateAccepted || contactRequest.ContactRequestState == common.ContactRequestStateDismissed,
Accepted: contactRequest.ContactRequestState == common.ContactRequestStateAccepted, Accepted: contactRequest.ContactRequestState == common.ContactRequestStateAccepted,
@ -410,10 +410,59 @@ func (m *Messenger) handleCommandMessage(state *ReceivedMessageState, message *c
return nil return nil
} }
func (m *Messenger) syncContactRequestForInstallationContact(contact *Contact, state *ReceivedMessageState, chat *Chat, outgoing bool) error {
if chat == nil {
return fmt.Errorf("no chat restored during the contact synchronisation, contact.ID = %s", contact.ID)
}
contactRequestID, err := m.persistence.LatestPendingContactRequestIDForContact(contact.ID)
if err != nil {
return err
}
if contactRequestID != "" {
return nil
}
clock, timestamp := chat.NextClockAndTimestamp(m.transport)
contactRequest, err := m.generateContactRequest(clock, timestamp, contact, defaultContactRequestText(), outgoing)
if err != nil {
return err
}
contactRequest.ID = defaultContactRequestID(contact.ID)
state.Response.AddMessage(contactRequest)
err = m.persistence.SaveMessages([]*common.Message{contactRequest})
if err != nil {
return err
}
if outgoing {
notification := m.generateOutgoingContactRequestNotification(contact, contactRequest)
err = m.addActivityCenterNotification(state.Response, notification)
if err != nil {
return err
}
} else {
err = m.createIncomingContactRequestNotification(contact, state, contactRequest, true)
if err != nil {
return err
}
}
return nil
}
func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, message protobuf.SyncInstallationContactV2) error { func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, message protobuf.SyncInstallationContactV2) error {
// Ignore own contact installation
if message.Id == m.myHexIdentity() {
return nil
}
removedOrBlocked := message.Removed || message.Blocked removedOrBlocked := message.Removed || message.Blocked
chat, ok := state.AllChats.Load(message.Id) chat, ok := state.AllChats.Load(message.Id)
if !ok && (message.Added || message.Muted) && !removedOrBlocked { if !ok && (message.Added || message.HasAddedUs || message.Muted) && !removedOrBlocked {
pubKey, err := common.HexToPubkey(message.Id) pubKey, err := common.HexToPubkey(message.Id)
if err != nil { if err != nil {
return err return err
@ -448,16 +497,31 @@ func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, m
uint64(message.ContactRequestLocalClock)) uint64(message.ContactRequestLocalClock))
state.ModifiedContacts.Store(contact.ID, true) state.ModifiedContacts.Store(contact.ID, true)
state.AllContacts.Store(contact.ID, contact) state.AllContacts.Store(contact.ID, contact)
err := m.syncContactRequestForInstallationContact(contact, state, chat, contact.ContactRequestLocalState == ContactRequestStateSent)
if err != nil {
return err
}
} else if message.Added || message.HasAddedUs { } else if message.Added || message.HasAddedUs {
// NOTE(cammellos): this is for handling backward compatibility, old clients // NOTE(cammellos): this is for handling backward compatibility, old clients
// won't propagate ContactRequestRemoteClock or ContactRequestLocalClock // won't propagate ContactRequestRemoteClock or ContactRequestLocalClock
if message.Added && contact.LastUpdatedLocally < message.LastUpdatedLocally { if message.Added && contact.LastUpdatedLocally < message.LastUpdatedLocally {
contact.ContactRequestSent(message.LastUpdatedLocally) contact.ContactRequestSent(message.LastUpdatedLocally)
err := m.syncContactRequestForInstallationContact(contact, state, chat, true)
if err != nil {
return err
}
} }
if message.HasAddedUs && contact.LastUpdated < message.LastUpdated { if message.HasAddedUs && contact.LastUpdated < message.LastUpdated {
contact.ContactRequestReceived(message.LastUpdated) contact.ContactRequestReceived(message.LastUpdated)
err := m.syncContactRequestForInstallationContact(contact, state, chat, false)
if err != nil {
return err
}
} }
if message.Removed && contact.LastUpdatedLocally < message.LastUpdatedLocally { if message.Removed && contact.LastUpdatedLocally < message.LastUpdatedLocally {
@ -466,7 +530,6 @@ func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, m
return err return err
} }
} }
} }
// Sync last updated field // Sync last updated field