Backup removed & added by them contacts

This commit is contained in:
Andrea Maria Piana 2021-10-22 15:20:42 +01:00
parent 301325a22e
commit a7b0c6c933
20 changed files with 341 additions and 504 deletions

View File

@ -185,7 +185,7 @@ func main() {
return return
} }
err = wakuext.SaveContact(context.Background(), contact) _, err = wakuext.AddContact(context.Background(), contact.ID)
if err != nil { if err != nil {
return return
} }

View File

@ -764,3 +764,15 @@ func (db *Database) SetLastBackup(time uint64) error {
_, err := db.db.Exec("UPDATE settings SET last_backup = ?", time) _, err := db.db.Exec("UPDATE settings SET last_backup = ?", time)
return err return err
} }
func (db *Database) ENSName() (string, error) {
var result sql.NullString
err := db.db.QueryRow("SELECT preferred_name FROM settings WHERE synthetic_id = 'id'").Scan(&result)
if err == sql.ErrNoRows {
return "", nil
}
if result.Valid {
return result.String, nil
}
return "", err
}

View File

@ -28,6 +28,7 @@ var (
DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"), DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"),
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51", InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab", KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
BackupEnabled: true,
LatestDerivedPath: 0, LatestDerivedPath: 0,
Name: "Jittery Cornflowerblue Kingbird", Name: "Jittery Cornflowerblue Kingbird",
Networks: &networks, Networks: &networks,

View File

@ -1,209 +0,0 @@
// In order to run these tests, you must run a PostgreSQL database.
//
// Using Docker:
// docker run -e POSTGRES_HOST_AUTH_METHOD=trust -d -p 5432:5432 postgres:9.6-alpine
//
package protocol
import (
"context"
"crypto/ecdsa"
"testing"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
bindata "github.com/status-im/migrate/v4/source/go_bindata"
appmetricsDB "github.com/status-im/status-go/appmetrics"
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/postgres"
"github.com/status-im/status-go/protocol/anonmetrics"
"github.com/status-im/status-go/protocol/anonmetrics/migrations"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/appmetrics"
"github.com/status-im/status-go/waku"
)
func TestMessengerAnonMetricsSuite(t *testing.T) {
suite.Run(t, new(MessengerAnonMetricsSuite))
}
type MessengerAnonMetricsSuite struct {
suite.Suite
alice *Messenger // client instance of Messenger
bob *Messenger // server instance of Messenger
aliceKey *ecdsa.PrivateKey // private key for the alice instance of Messenger
bobKey *ecdsa.PrivateKey // private key for the bob instance of 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
}
func (s *MessengerAnonMetricsSuite) SetupSuite() {
// ResetDefaultTestPostgresDB Required to completely reset the Postgres DB
err := postgres.ResetDefaultTestPostgresDB()
s.NoError(err)
}
func (s *MessengerAnonMetricsSuite) SetupTest() {
var err error
s.logger = tt.MustCreateTestLogger()
// Setup Waku things
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
// Generate private keys for Alice and Bob
s.aliceKey, err = crypto.GenerateKey()
s.Require().NoError(err)
s.bobKey, err = crypto.GenerateKey()
s.Require().NoError(err)
// Generate Alice Messenger as the client
amcc := &anonmetrics.ClientConfig{
ShouldSend: true,
SendAddress: &s.bobKey.PublicKey,
Active: anonmetrics.ActiveClientPhrase,
}
s.alice, err = newMessengerWithKey(s.shh, s.aliceKey, s.logger, []Option{WithAnonMetricsClientConfig(amcc)})
s.Require().NoError(err)
_, err = s.alice.Start()
s.Require().NoError(err)
// Generate Bob Messenger as the Server
amsc := &anonmetrics.ServerConfig{
Enabled: true,
PostgresURI: postgres.DefaultTestURI,
Active: anonmetrics.ActiveServerPhrase,
}
s.bob, err = newMessengerWithKey(s.shh, s.bobKey, s.logger, []Option{WithAnonMetricsServerConfig(amsc)})
s.Require().NoError(err)
_, err = s.bob.Start()
s.Require().NoError(err)
}
func (s *MessengerAnonMetricsSuite) TearDownTest() {
// Down migrate the DB
if s.bob.anonMetricsServer != nil {
postgresMigration := bindata.Resource(migrations.AssetNames(), migrations.Asset)
m, err := anonmetrics.MakeMigration(s.bob.anonMetricsServer.PostgresDB, postgresMigration)
s.NoError(err)
err = m.Down()
s.NoError(err)
}
// Shutdown messengers
s.NoError(s.alice.Shutdown())
s.alice = nil
s.NoError(s.bob.Shutdown())
s.bob = nil
_ = s.logger.Sync()
}
func (s *MessengerAnonMetricsSuite) TestReceiveAnonMetric() {
// Create the appmetrics API to simulate incoming metrics from `status-react`
ama := appmetrics.NewAPI(appmetricsDB.NewDB(s.alice.database))
// Generate and store some metrics to Alice
ams := appmetricsDB.GenerateMetrics(10)
err := ama.SaveAppMetrics(context.Background(), ams)
s.Require().NoError(err)
// Check that we have what we stored
amsdb, err := ama.GetAppMetrics(context.Background(), 100, 0)
s.Require().NoError(err)
s.Require().Len(amsdb.AppMetrics, 10)
// Wait for messages to arrive at bob
_, err = WaitOnMessengerResponse(
s.bob,
func(r *MessengerResponse) bool { return len(r.AnonymousMetrics) > 0 },
"no anonymous metrics received",
)
s.Require().NoError(err)
// Get app metrics from postgres DB
bobMetrics, err := s.bob.anonMetricsServer.GetAppMetrics(100, 0)
s.Require().NoError(err)
s.Require().Len(bobMetrics, 5)
// Check the values of received and stored metrics against the broadcast metrics
for i, bobMetric := range bobMetrics {
s.Require().True(bobMetric.CreatedAt.Equal(amsdb.AppMetrics[i].CreatedAt), "created_at values are equal")
s.Require().Exactly(bobMetric.SessionID, amsdb.AppMetrics[i].SessionID, "session_id matched exactly")
s.Require().Exactly(bobMetric.Value, amsdb.AppMetrics[i].Value, "value matches exactly")
s.Require().Exactly(bobMetric.Event, amsdb.AppMetrics[i].Event, "event matches exactly")
s.Require().Exactly(bobMetric.OS, amsdb.AppMetrics[i].OS, "operating system matches exactly")
s.Require().Exactly(bobMetric.AppVersion, amsdb.AppMetrics[i].AppVersion, "app version matches exactly")
}
}
// TestActivationIsOff tests if using the incorrect activation phrase for the anon metric client / server deactivates
// the client / server. This test can be removed when / if the anon metrics functionality is reintroduced / re-approved.
func (s *MessengerAnonMetricsSuite) TestActivationIsOff() {
var err error
// Check the set up messengers are in the expected state with the correct activation phrases
s.NotNil(s.alice.anonMetricsClient)
s.NotNil(s.bob.anonMetricsServer)
// Generate Alice Messenger as the client with an incorrect phrase
amcc := &anonmetrics.ClientConfig{
ShouldSend: true,
SendAddress: &s.bobKey.PublicKey,
Active: "the wrong client phrase",
}
s.alice, err = newMessengerWithKey(s.shh, s.aliceKey, s.logger, []Option{WithAnonMetricsClientConfig(amcc)})
s.NoError(err)
_, err = s.alice.Start()
s.Require().NoError(err)
s.Nil(s.alice.anonMetricsClient)
// Generate Alice Messenger as the client with an no activation phrase
amcc = &anonmetrics.ClientConfig{
ShouldSend: true,
SendAddress: &s.bobKey.PublicKey,
}
s.alice, err = newMessengerWithKey(s.shh, s.aliceKey, s.logger, []Option{WithAnonMetricsClientConfig(amcc)})
s.NoError(err)
_, err = s.alice.Start()
s.Require().NoError(err)
s.Nil(s.alice.anonMetricsClient)
// Generate Bob Messenger as the Server with an incorrect phrase
amsc := &anonmetrics.ServerConfig{
Enabled: true,
PostgresURI: postgres.DefaultTestURI,
Active: "the wrong server phrase",
}
s.bob, err = newMessengerWithKey(s.shh, s.bobKey, s.logger, []Option{WithAnonMetricsServerConfig(amsc)})
s.Require().NoError(err)
s.Nil(s.bob.anonMetricsServer)
// Generate Bob Messenger as the Server with no activation phrase
amsc = &anonmetrics.ServerConfig{
Enabled: true,
PostgresURI: postgres.DefaultTestURI,
}
s.bob, err = newMessengerWithKey(s.shh, s.bobKey, s.logger, []Option{WithAnonMetricsServerConfig(amsc)})
s.Require().NoError(err)
s.Nil(s.bob.anonMetricsServer)
}

View File

@ -73,6 +73,9 @@ type Contact struct {
// updates should be discarded if last updated is less than the one stored // updates should be discarded if last updated is less than the one stored
LastUpdated uint64 `json:"lastUpdated"` LastUpdated uint64 `json:"lastUpdated"`
// LastUpdatedLocally is the last time we updated the contact locally
LastUpdatedLocally uint64 `json:"lastUpdatedLocally"`
LocalNickname string `json:"localNickname,omitempty"` LocalNickname string `json:"localNickname,omitempty"`
Images map[string]images.IdentityImage `json:"images"` Images map[string]images.IdentityImage `json:"images"`

View File

@ -2459,14 +2459,15 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
} }
syncMessage := &protobuf.SyncInstallationContactV2{ syncMessage := &protobuf.SyncInstallationContactV2{
Clock: clock, LastUpdatedLocally: contact.LastUpdatedLocally,
Id: contact.ID, LastUpdated: contact.LastUpdated,
EnsName: ensName, Id: contact.ID,
LocalNickname: contact.LocalNickname, EnsName: ensName,
Added: contact.Added, LocalNickname: contact.LocalNickname,
Blocked: contact.Blocked, Added: contact.Added,
Muted: muted, Blocked: contact.Blocked,
Removed: contact.Removed, Muted: muted,
Removed: contact.Removed,
} }
encodedMessage, err := proto.Marshal(syncMessage) encodedMessage, err := proto.Marshal(syncMessage)

View File

@ -68,6 +68,7 @@ func (m *Messenger) startBackupLoop() {
m.logger.Error("failed to backup data", zap.Error(err)) m.logger.Error("failed to backup data", zap.Error(err))
} }
case <-m.quit: case <-m.quit:
ticker.Stop()
return return
} }
} }
@ -135,7 +136,6 @@ func (m *Messenger) syncBackupContact(ctx context.Context, contact *Contact) *pr
if contact.IsSyncing { if contact.IsSyncing {
return nil return nil
} }
clock, _ := m.getLastClockWithRelatedChat()
var ensName string var ensName string
if contact.ENSVerified { if contact.ENSVerified {
@ -149,13 +149,15 @@ func (m *Messenger) syncBackupContact(ctx context.Context, contact *Contact) *pr
} }
return &protobuf.SyncInstallationContactV2{ return &protobuf.SyncInstallationContactV2{
Clock: clock, LastUpdatedLocally: contact.LastUpdatedLocally,
Id: contact.ID, LastUpdated: contact.LastUpdated,
EnsName: ensName, Id: contact.ID,
LocalNickname: contact.LocalNickname, EnsName: ensName,
Added: contact.Added, LocalNickname: contact.LocalNickname,
Blocked: contact.Blocked, Added: contact.Added,
Muted: muted, Blocked: contact.Blocked,
Removed: contact.Removed, Muted: muted,
HasAddedUs: contact.HasAddedUs,
Removed: contact.Removed,
} }
} }

View File

@ -186,3 +186,86 @@ func (s *MessengerBackupSuite) TestBackupContactsGreaterThanBatch() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2) s.Require().Len(bob2.AddedContacts(), BackupContactsPerBatch*2)
} }
func (s *MessengerBackupSuite) TestBackupRemovedContact() {
bob1 := s.m
// Create bob2
bob2, err := newMessengerWithKey(s.shh, bob1.identity, s.logger, nil)
s.Require().NoError(err)
_, err = bob2.Start()
s.Require().NoError(err)
// Create 2 contacts on bob 1
contact1Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID1 := types.EncodeHex(crypto.FromECDSAPub(&contact1Key.PublicKey))
_, err = bob1.AddContact(context.Background(), contactID1)
s.Require().NoError(err)
contact2Key, err := crypto.GenerateKey()
s.Require().NoError(err)
contactID2 := types.EncodeHex(crypto.FromECDSAPub(&contact2Key.PublicKey))
_, err = bob1.AddContact(context.Background(), contactID2)
s.Require().NoError(err)
s.Require().Len(bob1.Contacts(), 2)
actualContacts := bob1.Contacts()
if actualContacts[0].ID == contactID1 {
s.Require().Equal(actualContacts[0].ID, contactID1)
s.Require().Equal(actualContacts[1].ID, contactID2)
} else {
s.Require().Equal(actualContacts[0].ID, contactID2)
s.Require().Equal(actualContacts[1].ID, contactID1)
}
// Bob 2 add one of the same contacts
_, err = bob2.AddContact(context.Background(), contactID2)
s.Require().NoError(err)
// Bob 1 now removes one of the contact that was also on bob 2
_, err = bob1.RemoveContact(context.Background(), contactID2)
s.Require().NoError(err)
// Backup
clock, err := bob1.BackupData(context.Background())
s.Require().NoError(err)
// Safety check
s.Require().Len(bob2.Contacts(), 1)
// Wait for the message to reach its destination
_, err = WaitOnMessengerResponse(
bob2,
func(r *MessengerResponse) bool {
_, err := s.m.RetrieveAll()
if err != nil {
s.logger.Info("Failed")
return false
}
if len(bob2.Contacts()) != 1 && bob2.Contacts()[0].ID != contactID1 {
return false
}
return true
},
"contacts not backed up",
)
// Bob 2 should remove the contact
s.Require().NoError(err)
s.Require().Len(bob2.AddedContacts(), 1)
s.Require().Equal(contactID1, bob2.AddedContacts()[0].ID)
lastBackup, err := bob1.lastBackup()
s.Require().NoError(err)
s.Require().NotEmpty(lastBackup)
s.Require().Equal(clock, lastBackup)
}

View File

@ -59,30 +59,36 @@ func (s *MessengerContactUpdateSuite) newMessenger(shh types.Waku) *Messenger {
func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() { func (s *MessengerContactUpdateSuite) TestReceiveContactUpdate() {
theirName := "ens-name.stateofus.eth" theirName := "ens-name.stateofus.eth"
theirPicture := "their-picture"
contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey)) contactID := types.EncodeHex(crypto.FromECDSAPub(&s.m.identity.PublicKey))
theirMessenger := s.newMessenger(s.shh) theirMessenger := s.newMessenger(s.shh)
_, err := theirMessenger.Start() _, err := theirMessenger.Start()
s.Require().NoError(err) s.Require().NoError(err)
// Set ENS name
err = theirMessenger.settings.SaveSetting("preferred-name", theirName)
s.Require().NoError(err)
theirContactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey)) theirContactID := types.EncodeHex(crypto.FromECDSAPub(&theirMessenger.identity.PublicKey))
response, err := theirMessenger.SendContactUpdate(context.Background(), contactID, theirName, theirPicture) response, err := theirMessenger.AddContact(context.Background(), contactID)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(response) s.Require().NotNil(response)
s.Require().Len(response.Contacts, 1) s.Require().Len(response.Contacts, 1)
contact := response.Contacts[0] contact := response.Contacts[0]
// It should not add the contact, as that's left to `SaveContact` // It should add the contact
s.Require().False(contact.Added) s.Require().True(contact.Added)
// add contact // It should create a profile chat & a one to one chat
contact.Added = true s.Require().Len(response.Chats(), 2)
s.Require().NoError(theirMessenger.SaveContact(contact)) chats := response.Chats()
if chats[0].ChatType == ChatTypeOneToOne {
s.Require().Len(response.Chats(), 1) s.Require().False(chats[0].Active)
chat := response.Chats()[0] } else {
s.Require().False(chat.Active, "It does not create an active chat") s.Require().False(chats[1].Active)
}
// Wait for the message to reach its destination // Wait for the message to reach its destination
response, err = WaitOnMessengerResponse( response, err = WaitOnMessengerResponse(

View File

@ -3,6 +3,7 @@ package protocol
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"errors"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
@ -11,10 +12,6 @@ import (
"github.com/status-im/status-go/protocol/transport" "github.com/status-im/status-go/protocol/transport"
) )
func (m *Messenger) SaveContact(contact *Contact) error {
return m.saveContact(contact)
}
func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerResponse, error) { func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerResponse, error) {
contact, ok := m.allContacts.Load(pubKey) contact, ok := m.allContacts.Load(pubKey)
if !ok { if !ok {
@ -28,6 +25,7 @@ func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerRe
if !contact.Added { if !contact.Added {
contact.Added = true contact.Added = true
} }
contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
// We sync the contact with the other devices // We sync the contact with the other devices
err := m.syncContact(context.Background(), contact) err := m.syncContact(context.Background(), contact)
@ -63,21 +61,45 @@ func (m *Messenger) AddContact(ctx context.Context, pubKey string) (*MessengerRe
return nil, err return nil, err
} }
// Fetch contact code
publicKey, err := contact.PublicKey()
if err != nil {
return nil, err
}
filter, err := m.transport.JoinPrivate(publicKey)
if err != nil {
return nil, err
}
m.scheduleSyncFilter(filter)
// Finally we send a contact update so they are notified we added them // Finally we send a contact update so they are notified we added them
// TODO: ens and picture are both blank for now ensName, err := m.settings.ENSName()
response, err := m.sendContactUpdate(context.Background(), pubKey, "", "") if err != nil {
return nil, err
}
response, err := m.sendContactUpdate(context.Background(), pubKey, ensName, "")
if err != nil {
return nil, err
}
// Send profile picture with contact request
chat, ok := m.allChats.Load(contact.ID)
if !ok {
chat = OneToOneFromPublicKey(publicKey, m.getTimesource())
chat.Active = false
if err := m.saveChat(chat); err != nil {
return nil, err
}
}
err = m.handleStandaloneChatIdentity(chat)
if err != nil { if err != nil {
return nil, err return nil, err
} }
response.AddChat(profileChat) response.AddChat(profileChat)
publicKey, err := contact.PublicKey()
if err != nil {
return nil, err
}
// TODO: Add filters to response
_, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey}) _, err = m.transport.InitFilters([]string{profileChat.ID}, []*ecdsa.PublicKey{publicKey})
if err != nil { if err != nil {
return nil, err return nil, err
@ -106,6 +128,7 @@ func (m *Messenger) removeContact(ctx context.Context, response *MessengerRespon
} }
contact.Remove() contact.Remove()
contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
err := m.persistence.SaveContact(contact, nil) err := m.persistence.SaveContact(contact, nil)
if err != nil { if err != nil {
@ -181,8 +204,32 @@ func (m *Messenger) GetContactByID(pubKey string) *Contact {
return contact return contact
} }
func (m *Messenger) SetLocalNickname(pubKey string, nickname string) (*MessengerResponse, error) {
contact, ok := m.allContacts.Load(pubKey)
if !ok {
return nil, errors.New("not existing contact")
}
clock := m.getTimesource().GetCurrentTime()
contact.LocalNickname = nickname
contact.LastUpdatedLocally = clock
response := &MessengerResponse{}
response.Contacts = []*Contact{contact}
err := m.syncContact(context.Background(), contact)
if err != nil {
return nil, err
}
return response, nil
}
func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) { func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) {
contact.Block() contact.Block()
contact.LastUpdatedLocally = m.getTimesource().GetCurrentTime()
chats, err := m.persistence.BlockContact(contact) chats, err := m.persistence.BlockContact(contact)
if err != nil { if err != nil {
return nil, err return nil, err
@ -208,61 +255,6 @@ func (m *Messenger) BlockContact(contact *Contact) ([]*Chat, error) {
return chats, nil return chats, nil
} }
func (m *Messenger) saveContact(contact *Contact) error {
name, identicon, err := generateAliasAndIdenticon(contact.ID)
if err != nil {
return err
}
contact.Identicon = identicon
contact.Alias = name
if m.shouldSyncContact(contact) {
if m.isNewContact(contact) {
publicKey, err := contact.PublicKey()
if err != nil {
return err
}
filter, err := m.transport.JoinPrivate(publicKey)
if err != nil {
return err
}
m.scheduleSyncFilter(filter)
}
err := m.syncContact(context.Background(), contact)
if err != nil {
return err
}
}
// We check if it should re-register with the push notification server
shouldReregisterForPushNotifications := (m.isNewContact(contact) || m.removedContact(contact))
err = m.persistence.SaveContact(contact, nil)
if err != nil {
return err
}
m.allContacts.Store(contact.ID, contact)
// Reregister only when data has changed
if shouldReregisterForPushNotifications {
err := m.resetLastPublishedTimeForChatIdentity()
if err != nil {
return err
}
// Publish contact code
err = m.publishContactCode()
if err != nil {
return err
}
return m.reregisterForPushNotifications()
}
return nil
}
// Send contact updates to all contacts added by us // Send contact updates to all contacts added by us
func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string) (err error) { func (m *Messenger) SendContactUpdates(ctx context.Context, ensName, profileImage string) (err error) {
myID := contactIDFromPublicKey(&m.identity.PublicKey) myID := contactIDFromPublicKey(&m.identity.PublicKey)
@ -298,12 +290,8 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
var response MessengerResponse var response MessengerResponse
contact, ok := m.allContacts.Load(chatID) contact, ok := m.allContacts.Load(chatID)
if !ok { if !ok || !contact.Added {
var err error return nil, nil
contact, err = buildContactFromPkString(chatID)
if err != nil {
return nil, err
}
} }
chat, ok := m.allChats.Load(chatID) chat, ok := m.allChats.Load(chatID)
@ -348,29 +336,5 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &response, m.saveContact(contact) return &response, nil
}
func (m *Messenger) isNewContact(contact *Contact) bool {
previousContact, ok := m.allContacts.Load(contact.ID)
return contact.Added && (!ok || !previousContact.Added)
}
func (m *Messenger) shouldSyncContact(contact *Contact) bool {
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
return contact.Added
}
return contact.LocalNickname != previousContact.LocalNickname ||
contact.Added != previousContact.Added ||
previousContact.Blocked != contact.Blocked
}
func (m *Messenger) removedContact(contact *Contact) bool {
previousContact, ok := m.allContacts.Load(contact.ID)
if !ok {
return false
}
return previousContact.Added && !contact.Added
} }

View File

@ -312,7 +312,11 @@ func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, m
} }
} }
if contact.LastUpdated < message.Clock { if contact.LastUpdated < message.LastUpdated {
contact.HasAddedUs = message.HasAddedUs
}
if contact.LastUpdatedLocally < message.LastUpdatedLocally {
contact.IsSyncing = true contact.IsSyncing = true
defer func() { defer func() {
contact.IsSyncing = false contact.IsSyncing = false
@ -334,7 +338,7 @@ func (m *Messenger) HandleSyncInstallationContact(state *ReceivedMessageState, m
} }
contact.ENSVerified = true contact.ENSVerified = true
} }
contact.LastUpdated = message.Clock contact.LastUpdatedLocally = message.LastUpdatedLocally
contact.LocalNickname = message.LocalNickname contact.LocalNickname = message.LocalNickname
if message.Blocked != contact.Blocked { if message.Blocked != contact.Blocked {

View File

@ -103,8 +103,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
contact, err := BuildContactFromPublicKey(&contactKey.PublicKey) contact, err := BuildContactFromPublicKey(&contactKey.PublicKey)
s.Require().NoError(err) s.Require().NoError(err)
contact.Added = true _, err = s.m.AddContact(context.Background(), contact.ID)
err = s.m.SaveContact(contact)
s.Require().NoError(err) s.Require().NoError(err)
// Wait for the message to reach its destination // Wait for the message to reach its destination
@ -145,9 +144,10 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
contact, err := BuildContactFromPublicKey(&contactKey.PublicKey) contact, err := BuildContactFromPublicKey(&contactKey.PublicKey)
s.Require().NoError(err) s.Require().NoError(err)
contact.Added = true
contact.LocalNickname = "Test Nickname" contact.LocalNickname = "Test Nickname"
err = s.m.SaveContact(contact) _, err = s.m.AddContact(context.Background(), contact.ID)
s.Require().NoError(err)
_, err = s.m.SetLocalNickname(contact.ID, contact.LocalNickname)
s.Require().NoError(err) s.Require().NoError(err)
// add chat // add chat
@ -215,7 +215,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
actualContact = response.Contacts[0] actualContact = response.Contacts[0]
} }
if len(allChats) >= 3 && actualContact != nil { if len(allChats) >= 2 && actualContact != nil {
return nil return nil
} }

View File

@ -39,8 +39,6 @@ const (
testContract = "0x314159265dd8dbb310642f98f50c066173c1259b" testContract = "0x314159265dd8dbb310642f98f50c066173c1259b"
testValue = "2000" testValue = "2000"
testTransactionHash = "0x412a851ac2ae51cad34a56c8a9cfee55d577ac5e1ac71cf488a2f2093a373799" testTransactionHash = "0x412a851ac2ae51cad34a56c8a9cfee55d577ac5e1ac71cf488a2f2093a373799"
testIdenticon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAnElEQVR4nOzXQaqDMBRG4bZkLR10e12H23PgZuJUjJAcE8kdnG/44IXDhZ9iyjm/4vnMDrhmFmEWYRZhFpH6n1jW7fSX/+/b+WbQa5lFmEVUljhqZfSdoNcyizCLeNMvn3JTLeh+g17LLMIsorLElt2VK7v3X0dBr2UWYRaBfxNLfifOZhYRNGvAEp8Q9FpmEWYRZhFmEXsAAAD//5K5JFhu0M0nAAAAAElFTkSuQmCC"
testAlias = "Concrete Lavender Xiphias"
newName = "new-name" newName = "new-name"
) )
@ -256,44 +254,10 @@ func (s *MessengerSuite) TestInit() {
Prep: func() { Prep: func() {
key, err := crypto.GenerateKey() key, err := crypto.GenerateKey()
s.Require().NoError(err) s.Require().NoError(err)
contact := Contact{ _, err = s.m.AddContact(context.Background(), types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)))
ID: types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)),
Name: "Some Contact",
Added: true,
}
err = s.m.SaveContact(&contact)
s.Require().NoError(err) s.Require().NoError(err)
}, },
AddedFilters: 1, AddedFilters: 2,
},
{
Name: "added and blocked contact",
Prep: func() {
key, err := crypto.GenerateKey()
s.Require().NoError(err)
contact := Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)),
Name: "Some Contact",
Blocked: true,
}
err = s.m.SaveContact(&contact)
s.Require().NoError(err)
},
AddedFilters: 0,
},
{
Name: "added by them contact",
Prep: func() {
key, err := crypto.GenerateKey()
s.Require().NoError(err)
contact := Contact{
ID: types.EncodeHex(crypto.FromECDSAPub(&key.PublicKey)),
Name: "Some Contact",
}
err = s.m.SaveContact(&contact)
s.Require().NoError(err)
},
AddedFilters: 0,
}, },
} }
@ -747,7 +711,8 @@ func (s *MessengerSuite) TestRetrieveBlockedContact() {
Blocked: true, Blocked: true,
} }
s.Require().NoError(s.m.SaveContact(&blockedContact)) _, err = s.m.BlockContact(&blockedContact)
s.Require().NoError(err)
inputMessage := buildTestMessage(*chat) inputMessage := buildTestMessage(*chat)
@ -1221,9 +1186,6 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() {
LastMessage: &common.Message{}, LastMessage: &common.Message{},
Highlight: false, Highlight: false,
} }
contact := Contact{
ID: testPK,
}
publicKeyBytes, err := hex.DecodeString(testPK[2:]) publicKeyBytes, err := hex.DecodeString(testPK[2:])
s.Require().NoError(err) s.Require().NoError(err)
@ -1232,7 +1194,6 @@ func (s *MessengerSuite) TestChatPersistenceOneToOne() {
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NoError(s.m.SaveChat(chat)) s.Require().NoError(s.m.SaveChat(chat))
s.Require().NoError(s.m.SaveContact(&contact))
savedChats := s.m.Chats() savedChats := s.m.Chats()
s.Require().Equal(3, len(savedChats)) s.Require().Equal(3, len(savedChats))
@ -1382,7 +1343,8 @@ func (s *MessengerSuite) TestBlockContact() {
s.Require().NoError(s.m.SaveChat(chat2)) s.Require().NoError(s.m.SaveChat(chat2))
s.Require().NoError(s.m.SaveChat(chat3)) s.Require().NoError(s.m.SaveChat(chat3))
s.Require().NoError(s.m.SaveContact(&contact)) _, err := s.m.AddContact(context.Background(), contact.ID)
s.Require().NoError(err)
contact.Name = "blocked" contact.Name = "blocked"
@ -1464,7 +1426,7 @@ func (s *MessengerSuite) TestBlockContact() {
}, },
} }
err := s.m.SaveMessages(messages) err = s.m.SaveMessages(messages)
s.Require().NoError(err) s.Require().NoError(err)
response, err := s.m.BlockContact(&contact) response, err := s.m.BlockContact(&contact)
@ -1511,56 +1473,13 @@ func (s *MessengerSuite) TestBlockContact() {
} }
func (s *MessengerSuite) TestContactPersistence() { func (s *MessengerSuite) TestContactPersistence() {
contact := Contact{ _, err := s.m.AddContact(context.Background(), testPK)
ID: testPK, s.Require().NoError(err)
Name: "contact-name",
LastUpdated: 20,
Added: true,
}
s.Require().NoError(s.m.SaveContact(&contact))
savedContacts := s.m.Contacts() savedContacts := s.m.Contacts()
s.Require().Equal(1, len(savedContacts)) s.Require().Equal(1, len(savedContacts))
actualContact := savedContacts[0] s.Require().True(savedContacts[0].Added)
expectedContact := &contact
expectedContact.Alias = testAlias
expectedContact.Identicon = testIdenticon
s.Require().Equal(expectedContact, actualContact)
}
func (s *MessengerSuite) TestContactPersistenceUpdate() {
contactID := testPK
contact := Contact{
ID: contactID,
Name: "contact-name",
LastUpdated: 20,
Added: true,
}
s.Require().NoError(s.m.SaveContact(&contact))
savedContacts := s.m.Contacts()
s.Require().Equal(1, len(savedContacts))
actualContact := savedContacts[0]
expectedContact := &contact
expectedContact.Alias = testAlias
expectedContact.Identicon = testIdenticon
s.Require().Equal(expectedContact, actualContact)
contact.Name = "updated-name-2"
s.Require().NoError(s.m.SaveContact(&contact))
updatedContact := s.m.Contacts()
s.Require().Equal(1, len(updatedContact))
actualUpdatedContact := updatedContact[0]
expectedUpdatedContact := &contact
s.Require().Equal(expectedUpdatedContact, actualUpdatedContact)
} }
func (s *MessengerSuite) TestSharedSecretHandler() { func (s *MessengerSuite) TestSharedSecretHandler() {

View File

@ -46,6 +46,7 @@
// 1632303896_modify_contacts_table.up.sql (1.574kB) // 1632303896_modify_contacts_table.up.sql (1.574kB)
// 1633349838_add_emoji_column_in_chats.up.sql (52B) // 1633349838_add_emoji_column_in_chats.up.sql (52B)
// 1634831235_add_highlight_column_in_chats.up.sql (62B) // 1634831235_add_highlight_column_in_chats.up.sql (62B)
// 1634896007_add_last_updated_locally_and_removed.up.sql (131B)
// 1635840039_add_clock_read_at_column_in_chats.up.sql (245B) // 1635840039_add_clock_read_at_column_in_chats.up.sql (245B)
// README.md (554B) // README.md (554B)
// doc.go (850B) // doc.go (850B)
@ -1037,6 +1038,26 @@ func _1634831235_add_highlight_column_in_chatsUpSql() (*asset, error) {
return a, nil return a, nil
} }
var __1634896007_add_last_updated_locally_and_removedUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x48\xce\xcf\x2b\x49\x4c\x2e\x29\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x4a\xcd\xcd\x2f\x4b\x4d\x51\x70\xf2\xf7\xf7\x71\x75\xf4\x53\x70\x71\x75\x73\x0c\xf5\x09\x51\x70\x73\xf4\x09\x76\xb5\xe6\x22\xa4\x3d\x27\xb1\xb8\x24\xbe\xb4\x20\x25\xb1\x24\x35\x25\x3e\x27\x3f\x39\x31\x27\xa7\x52\xc1\xd3\x2f\x04\x6e\x8e\x81\x35\x17\x20\x00\x00\xff\xff\xab\xe8\x7d\xf0\x83\x00\x00\x00")
func _1634896007_add_last_updated_locally_and_removedUpSqlBytes() ([]byte, error) {
return bindataRead(
__1634896007_add_last_updated_locally_and_removedUpSql,
"1634896007_add_last_updated_locally_and_removed.up.sql",
)
}
func _1634896007_add_last_updated_locally_and_removedUpSql() (*asset, error) {
bytes, err := _1634896007_add_last_updated_locally_and_removedUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1634896007_add_last_updated_locally_and_removed.up.sql", size: 131, mode: os.FileMode(0644), modTime: time.Unix(1635867803, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2e, 0xa8, 0x34, 0xe2, 0xc0, 0x62, 0xc8, 0xd6, 0x5a, 0x87, 0xe3, 0x70, 0xe1, 0xc4, 0x16, 0x9c, 0x60, 0x2e, 0x98, 0xf0, 0x91, 0x84, 0xbe, 0xe0, 0xdf, 0x3e, 0x4d, 0x24, 0xc4, 0x6c, 0x40, 0x17}}
return a, nil
}
var __1635840039_add_clock_read_at_column_in_chatsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\xcc\xc1\x6a\x83\x30\x18\x07\xf0\xbb\x4f\xf1\x3f\x6e\xe0\x61\x77\xd9\x21\x33\x19\x08\x59\x1c\x2e\xc2\x6e\x1f\x21\xf9\x68\xa5\x51\xc1\x68\xe9\xe3\x97\x1c\x2a\xf6\xd2\x07\xf8\xfd\x84\xb6\xaa\x83\x15\x5f\x5a\xc1\x9f\xdd\x9a\x20\xa4\x44\xdd\xea\xfe\xc7\x60\x61\x17\x68\xe4\x94\xdc\x89\x13\xb9\x95\x7c\x9c\xfd\x85\xae\x2e\x6e\x8c\xc6\x58\x48\xf5\x2d\x7a\x6d\xf1\x51\x15\xfd\xaf\x14\xf6\x71\xfc\x29\xfb\x1a\x7f\x66\x52\x77\x2a\x93\xc6\x48\xf5\x8f\x38\x7b\x17\x29\x73\x1a\x02\x25\xe6\x89\x46\x9e\xd6\x61\x9e\x38\x1c\x29\x0d\xe1\x86\xd6\x60\x4b\xbc\xec\xfd\xdb\x93\x2e\x91\x79\x89\xdd\x97\x38\x04\xef\x55\x71\x0f\x00\x00\xff\xff\xab\x82\x7c\xe1\xf5\x00\x00\x00") var __1635840039_add_clock_read_at_column_in_chatsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\xcc\xc1\x6a\x83\x30\x18\x07\xf0\xbb\x4f\xf1\x3f\x6e\xe0\x61\x77\xd9\x21\x33\x19\x08\x59\x1c\x2e\xc2\x6e\x1f\x21\xf9\x68\xa5\x51\xc1\x68\xe9\xe3\x97\x1c\x2a\xf6\xd2\x07\xf8\xfd\x84\xb6\xaa\x83\x15\x5f\x5a\xc1\x9f\xdd\x9a\x20\xa4\x44\xdd\xea\xfe\xc7\x60\x61\x17\x68\xe4\x94\xdc\x89\x13\xb9\x95\x7c\x9c\xfd\x85\xae\x2e\x6e\x8c\xc6\x58\x48\xf5\x2d\x7a\x6d\xf1\x51\x15\xfd\xaf\x14\xf6\x71\xfc\x29\xfb\x1a\x7f\x66\x52\x77\x2a\x93\xc6\x48\xf5\x8f\x38\x7b\x17\x29\x73\x1a\x02\x25\xe6\x89\x46\x9e\xd6\x61\x9e\x38\x1c\x29\x0d\xe1\x86\xd6\x60\x4b\xbc\xec\xfd\xdb\x93\x2e\x91\x79\x89\xdd\x97\x38\x04\xef\x55\x71\x0f\x00\x00\xff\xff\xab\x82\x7c\xe1\xf5\x00\x00\x00")
func _1635840039_add_clock_read_at_column_in_chatsUpSqlBytes() ([]byte, error) { func _1635840039_add_clock_read_at_column_in_chatsUpSqlBytes() ([]byte, error) {
@ -1280,6 +1301,8 @@ var _bindata = map[string]func() (*asset, error){
"1634831235_add_highlight_column_in_chats.up.sql": _1634831235_add_highlight_column_in_chatsUpSql, "1634831235_add_highlight_column_in_chats.up.sql": _1634831235_add_highlight_column_in_chatsUpSql,
"1634896007_add_last_updated_locally_and_removed.up.sql": _1634896007_add_last_updated_locally_and_removedUpSql,
"1635840039_add_clock_read_at_column_in_chats.up.sql": _1635840039_add_clock_read_at_column_in_chatsUpSql, "1635840039_add_clock_read_at_column_in_chats.up.sql": _1635840039_add_clock_read_at_column_in_chatsUpSql,
"README.md": readmeMd, "README.md": readmeMd,
@ -1374,6 +1397,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1632303896_modify_contacts_table.up.sql": &bintree{_1632303896_modify_contacts_tableUpSql, map[string]*bintree{}}, "1632303896_modify_contacts_table.up.sql": &bintree{_1632303896_modify_contacts_tableUpSql, map[string]*bintree{}},
"1633349838_add_emoji_column_in_chats.up.sql": &bintree{_1633349838_add_emoji_column_in_chatsUpSql, map[string]*bintree{}}, "1633349838_add_emoji_column_in_chats.up.sql": &bintree{_1633349838_add_emoji_column_in_chatsUpSql, map[string]*bintree{}},
"1634831235_add_highlight_column_in_chats.up.sql": &bintree{_1634831235_add_highlight_column_in_chatsUpSql, map[string]*bintree{}}, "1634831235_add_highlight_column_in_chats.up.sql": &bintree{_1634831235_add_highlight_column_in_chatsUpSql, map[string]*bintree{}},
"1634896007_add_last_updated_locally_and_removed.up.sql": &bintree{_1634896007_add_last_updated_locally_and_removedUpSql, map[string]*bintree{}},
"1635840039_add_clock_read_at_column_in_chats.up.sql": &bintree{_1635840039_add_clock_read_at_column_in_chatsUpSql, map[string]*bintree{}}, "1635840039_add_clock_read_at_column_in_chats.up.sql": &bintree{_1635840039_add_clock_read_at_column_in_chatsUpSql, map[string]*bintree{}},
"README.md": &bintree{readmeMd, map[string]*bintree{}}, "README.md": &bintree{readmeMd, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}}, "doc.go": &bintree{docGo, map[string]*bintree{}},

View File

@ -0,0 +1,2 @@
ALTER TABLE contacts ADD COLUMN removed BOOLEAN DEFAULT FALSE;
ALTER TABLE contacts ADD COLUMN last_updated_locally INT DEFAULT 0;

View File

@ -467,8 +467,10 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
c.alias, c.alias,
c.identicon, c.identicon,
c.last_updated, c.last_updated,
c.last_updated_locally,
c.added, c.added,
c.blocked, c.blocked,
c.removed,
c.has_added_us, c.has_added_us,
c.local_nickname, c.local_nickname,
i.image_type, i.image_type,
@ -485,15 +487,17 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
for rows.Next() { for rows.Next() {
var ( var (
contact Contact contact Contact
nickname sql.NullString nickname sql.NullString
imageType sql.NullString imageType sql.NullString
ensName sql.NullString ensName sql.NullString
ensVerified sql.NullBool ensVerified sql.NullBool
added sql.NullBool added sql.NullBool
blocked sql.NullBool blocked sql.NullBool
hasAddedUs sql.NullBool removed sql.NullBool
imagePayload []byte hasAddedUs sql.NullBool
lastUpdatedLocally sql.NullInt64
imagePayload []byte
) )
contact.Images = make(map[string]images.IdentityImage) contact.Images = make(map[string]images.IdentityImage)
@ -506,8 +510,10 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
&contact.Alias, &contact.Alias,
&contact.Identicon, &contact.Identicon,
&contact.LastUpdated, &contact.LastUpdated,
&lastUpdatedLocally,
&added, &added,
&blocked, &blocked,
&removed,
&hasAddedUs, &hasAddedUs,
&nickname, &nickname,
&imageType, &imageType,
@ -537,6 +543,14 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
contact.Blocked = blocked.Bool contact.Blocked = blocked.Bool
} }
if removed.Valid {
contact.Removed = removed.Bool
}
if lastUpdatedLocally.Valid {
contact.LastUpdatedLocally = uint64(lastUpdatedLocally.Int64)
}
if hasAddedUs.Valid { if hasAddedUs.Valid {
contact.HasAddedUs = hasAddedUs.Bool contact.HasAddedUs = hasAddedUs.Bool
} }
@ -678,14 +692,16 @@ func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error
alias, alias,
identicon, identicon,
last_updated, last_updated,
last_updated_locally,
local_nickname, local_nickname,
added, added,
blocked, blocked,
removed,
has_added_us, has_added_us,
name, name,
photo, photo,
tribute_to_talk tribute_to_talk
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`) `)
if err != nil { if err != nil {
return return
@ -698,9 +714,11 @@ func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error
contact.Alias, contact.Alias,
contact.Identicon, contact.Identicon,
contact.LastUpdated, contact.LastUpdated,
contact.LastUpdatedLocally,
contact.LocalNickname, contact.LocalNickname,
contact.Added, contact.Added,
contact.Blocked, contact.Blocked,
contact.Removed,
contact.HasAddedUs, contact.HasAddedUs,
//TODO we need to drop these columns //TODO we need to drop these columns
"", "",

View File

@ -226,7 +226,7 @@ func (m *SyncInstallationContact) GetLocalNickname() string {
} }
type SyncInstallationContactV2 struct { type SyncInstallationContactV2 struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` LastUpdatedLocally uint64 `protobuf:"varint,1,opt,name=last_updated_locally,json=lastUpdatedLocally,proto3" json:"last_updated_locally,omitempty"`
Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"`
ProfileImage string `protobuf:"bytes,3,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"` ProfileImage string `protobuf:"bytes,3,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"`
EnsName string `protobuf:"bytes,4,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` EnsName string `protobuf:"bytes,4,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"`
@ -237,6 +237,7 @@ type SyncInstallationContactV2 struct {
Blocked bool `protobuf:"varint,10,opt,name=blocked,proto3" json:"blocked,omitempty"` Blocked bool `protobuf:"varint,10,opt,name=blocked,proto3" json:"blocked,omitempty"`
Muted bool `protobuf:"varint,11,opt,name=muted,proto3" json:"muted,omitempty"` Muted bool `protobuf:"varint,11,opt,name=muted,proto3" json:"muted,omitempty"`
Removed bool `protobuf:"varint,12,opt,name=removed,proto3" json:"removed,omitempty"` Removed bool `protobuf:"varint,12,opt,name=removed,proto3" json:"removed,omitempty"`
HasAddedUs bool `protobuf:"varint,13,opt,name=has_added_us,json=hasAddedUs,proto3" json:"has_added_us,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -267,9 +268,9 @@ func (m *SyncInstallationContactV2) XXX_DiscardUnknown() {
var xxx_messageInfo_SyncInstallationContactV2 proto.InternalMessageInfo var xxx_messageInfo_SyncInstallationContactV2 proto.InternalMessageInfo
func (m *SyncInstallationContactV2) GetClock() uint64 { func (m *SyncInstallationContactV2) GetLastUpdatedLocally() uint64 {
if m != nil { if m != nil {
return m.Clock return m.LastUpdatedLocally
} }
return 0 return 0
} }
@ -344,6 +345,13 @@ func (m *SyncInstallationContactV2) GetRemoved() bool {
return false return false
} }
func (m *SyncInstallationContactV2) GetHasAddedUs() bool {
if m != nil {
return m.HasAddedUs
}
return false
}
type SyncInstallationAccount struct { type SyncInstallationAccount struct {
Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"`
ProfileImage string `protobuf:"bytes,2,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"` ProfileImage string `protobuf:"bytes,2,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"`
@ -804,51 +812,53 @@ func init() {
} }
var fileDescriptor_d61ab7221f0b5518 = []byte{ var fileDescriptor_d61ab7221f0b5518 = []byte{
// 725 bytes of a gzipped FileDescriptorProto // 763 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x54, 0xcd, 0x6e, 0xdb, 0x38, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x54, 0xcd, 0x6e, 0xdb, 0x38,
0x10, 0x86, 0x64, 0xc7, 0x76, 0xc6, 0x3f, 0x09, 0x88, 0x60, 0xc3, 0x64, 0xb1, 0x88, 0xa3, 0x6c, 0x10, 0x86, 0x6c, 0xc7, 0x76, 0xc6, 0xb2, 0x13, 0x10, 0xc1, 0x86, 0xc9, 0x62, 0x11, 0x47, 0xd9,
0xb0, 0x3e, 0x79, 0x81, 0xec, 0x61, 0x51, 0xa4, 0x45, 0xd1, 0xe4, 0x50, 0xb8, 0x45, 0xd3, 0x40, 0x60, 0x7d, 0xf2, 0x2e, 0xb2, 0x87, 0xc5, 0x22, 0xbb, 0x28, 0x92, 0x1c, 0x0a, 0xf7, 0x27, 0x0d,
0x4d, 0x7b, 0xe8, 0x45, 0xa0, 0x29, 0xc6, 0x61, 0x2d, 0x89, 0xaa, 0x48, 0xb9, 0xf0, 0x0b, 0xf4, 0xd4, 0xa4, 0x87, 0x5e, 0x04, 0x5a, 0x62, 0x6c, 0xd6, 0x92, 0xa8, 0x8a, 0x94, 0x0b, 0xbd, 0x40,
0x01, 0xda, 0xd7, 0x2a, 0xd0, 0xf7, 0xe8, 0x53, 0x14, 0x24, 0x65, 0x45, 0x49, 0xea, 0xc4, 0xe7, 0x0f, 0x3d, 0xb6, 0x0f, 0xd6, 0xf7, 0xe8, 0x53, 0x14, 0x24, 0x65, 0x45, 0x49, 0xea, 0xc4, 0xd7,
0x9e, 0xa4, 0xf9, 0x38, 0xe4, 0x7c, 0xf3, 0xcd, 0x0f, 0x74, 0x53, 0xc2, 0x33, 0x9e, 0x4c, 0x86, 0x9e, 0xa4, 0xf9, 0x66, 0xc8, 0xf9, 0xf8, 0xcd, 0x0f, 0x74, 0x13, 0xc2, 0x52, 0x16, 0x4f, 0x86,
0x69, 0x26, 0x94, 0x40, 0x2d, 0xf3, 0x19, 0xe7, 0x97, 0x9e, 0x80, 0xc6, 0x09, 0xa1, 0xd3, 0x3c, 0x49, 0xca, 0x25, 0x47, 0x6d, 0xfd, 0x19, 0x67, 0xd7, 0x0e, 0x87, 0xe6, 0x29, 0xf1, 0x67, 0x59,
0x45, 0x5b, 0xb0, 0x46, 0x23, 0x41, 0xa7, 0xd8, 0xe9, 0x3b, 0x83, 0xba, 0x6f, 0x0d, 0xd4, 0x03, 0x82, 0xb6, 0x60, 0xcd, 0x0f, 0xb9, 0x3f, 0xc3, 0x56, 0xdf, 0x1a, 0x34, 0x5c, 0x63, 0xa0, 0x1e,
0x97, 0x87, 0xd8, 0xed, 0x3b, 0x83, 0x75, 0xdf, 0xe5, 0x21, 0x7a, 0x0a, 0x2d, 0x2a, 0x12, 0x45, 0xd4, 0x58, 0x80, 0x6b, 0x7d, 0x6b, 0xb0, 0xee, 0xd6, 0x58, 0x80, 0x9e, 0x40, 0xdb, 0xe7, 0xb1,
0xa8, 0x92, 0xb8, 0xd6, 0xaf, 0x0d, 0xda, 0x47, 0x07, 0xc3, 0xc5, 0x63, 0xc3, 0x37, 0xf3, 0x84, 0x24, 0xbe, 0x14, 0xb8, 0xde, 0xaf, 0x0f, 0x3a, 0x47, 0x07, 0xc3, 0xc5, 0x65, 0xc3, 0xd7, 0x79,
0x8e, 0x12, 0xa9, 0x48, 0x14, 0x11, 0xc5, 0x45, 0x72, 0x6a, 0x3d, 0xdf, 0x1d, 0xf9, 0xe5, 0x25, 0xec, 0x8f, 0x62, 0x21, 0x49, 0x18, 0x12, 0xc9, 0x78, 0x7c, 0x66, 0x22, 0xdf, 0x1c, 0xb9, 0xe5,
0xef, 0xb3, 0x03, 0x9b, 0xe7, 0x84, 0x67, 0x55, 0xbf, 0x25, 0xb1, 0xff, 0x81, 0x0d, 0x5e, 0xf1, 0x21, 0xe7, 0xa3, 0x05, 0x9b, 0x17, 0x84, 0xa5, 0xd5, 0xb8, 0x25, 0xb9, 0xff, 0x80, 0x0d, 0x56,
0x0a, 0x4a, 0x22, 0xbd, 0x2a, 0x3c, 0x0a, 0xd1, 0x1e, 0xb4, 0x43, 0x36, 0xe3, 0x94, 0x05, 0x6a, 0x89, 0xf2, 0x4a, 0x22, 0xbd, 0x2a, 0x3c, 0x0a, 0xd0, 0x1e, 0x74, 0x02, 0x3a, 0x67, 0x3e, 0xf5,
0x9e, 0x32, 0x5c, 0x33, 0x4e, 0x60, 0xa1, 0x8b, 0x79, 0xca, 0x10, 0x82, 0x7a, 0x42, 0x62, 0x86, 0x64, 0x9e, 0x50, 0x5c, 0xd7, 0x41, 0x60, 0xa0, 0xcb, 0x3c, 0xa1, 0x08, 0x41, 0x23, 0x26, 0x11,
0xeb, 0xe6, 0xc4, 0xfc, 0x7b, 0x3f, 0x1c, 0xd8, 0x5e, 0x42, 0x78, 0x45, 0x2d, 0x0e, 0xa0, 0x9b, 0xc5, 0x0d, 0xed, 0xd1, 0xff, 0xce, 0x37, 0x0b, 0xb6, 0x97, 0x10, 0x5e, 0x51, 0x8b, 0x03, 0xe8,
0x66, 0xe2, 0x92, 0x47, 0x2c, 0xe0, 0x31, 0x99, 0x2c, 0x02, 0x77, 0x0a, 0x70, 0xa4, 0x31, 0xb4, 0x26, 0x29, 0xbf, 0x66, 0x21, 0xf5, 0x58, 0x44, 0x26, 0x8b, 0xc4, 0x76, 0x01, 0x8e, 0x14, 0x86,
0x03, 0x2d, 0x96, 0xc8, 0xa0, 0x12, 0xbe, 0xc9, 0x12, 0x79, 0x46, 0x62, 0x86, 0xf6, 0xa1, 0x13, 0x76, 0xa0, 0x4d, 0x63, 0xe1, 0x55, 0xd2, 0xb7, 0x68, 0x2c, 0xce, 0x49, 0x44, 0xd1, 0x3e, 0xd8,
0x11, 0xa9, 0x82, 0x3c, 0x0d, 0x89, 0x62, 0x21, 0x5e, 0x33, 0xc1, 0xda, 0x1a, 0x7b, 0x6b, 0x21, 0x21, 0x11, 0xd2, 0xcb, 0x92, 0x80, 0x48, 0x1a, 0xe0, 0x35, 0x9d, 0xac, 0xa3, 0xb0, 0x2b, 0x03,
0x9d, 0x99, 0x9c, 0x4b, 0xc5, 0xe2, 0x40, 0x91, 0x89, 0xc4, 0x8d, 0x7e, 0x4d, 0x67, 0x66, 0xa1, 0xa9, 0x97, 0x89, 0x5c, 0x48, 0x1a, 0x79, 0x92, 0x4c, 0x04, 0x6e, 0xf6, 0xeb, 0xea, 0x65, 0x06,
0x0b, 0x32, 0x91, 0xe8, 0x10, 0x7a, 0x91, 0xa0, 0x24, 0x0a, 0x12, 0x4e, 0xa7, 0x26, 0x48, 0xd3, 0xba, 0x24, 0x13, 0x81, 0x0e, 0xa1, 0x17, 0x72, 0x9f, 0x84, 0x5e, 0xcc, 0xfc, 0x99, 0x4e, 0xd2,
0x04, 0xe9, 0x1a, 0xf4, 0xac, 0x00, 0xbd, 0x6f, 0x2e, 0xec, 0x2c, 0xad, 0xce, 0x6f, 0x97, 0xae, 0xd2, 0x49, 0xba, 0x1a, 0x3d, 0x2f, 0x40, 0xe7, 0x53, 0x1d, 0x76, 0x96, 0x56, 0x07, 0xfd, 0x05,
0x4e, 0x88, 0x84, 0x21, 0x0b, 0xf1, 0x7a, 0xdf, 0x19, 0xb4, 0x7c, 0x6b, 0x20, 0x0c, 0xcd, 0xb1, 0x5b, 0x55, 0x22, 0x9e, 0x3e, 0x1b, 0xe6, 0xc5, 0xeb, 0x51, 0x85, 0xd0, 0x0b, 0xe3, 0xf9, 0x89,
0xce, 0x8c, 0x85, 0x18, 0x0c, 0xbe, 0x30, 0xb5, 0x7f, 0x9c, 0x6b, 0x4e, 0x6d, 0xeb, 0x6f, 0x0c, 0xa5, 0x50, 0xb5, 0x25, 0x41, 0x40, 0x03, 0xbc, 0xde, 0xb7, 0x06, 0x6d, 0xd7, 0x18, 0x08, 0x43,
0xed, 0x9f, 0xb1, 0x58, 0xcc, 0x58, 0x88, 0x3b, 0xd6, 0xbf, 0x30, 0xbd, 0x4f, 0x77, 0x5b, 0xe7, 0x6b, 0xac, 0x8a, 0x4c, 0x03, 0x0c, 0x1a, 0x5f, 0x98, 0x2a, 0x3e, 0xca, 0x14, 0xa7, 0x8e, 0x89,
0x19, 0xa5, 0x22, 0x4f, 0x96, 0xb5, 0xce, 0x1d, 0xed, 0xdc, 0x5f, 0x68, 0x77, 0x5b, 0xa0, 0xda, 0xd7, 0x86, 0x8a, 0x4f, 0x69, 0xc4, 0xe7, 0x34, 0xc0, 0xb6, 0x89, 0x2f, 0x4c, 0xd4, 0x07, 0x7b,
0x1d, 0x81, 0xbc, 0x13, 0xd8, 0xbd, 0x1d, 0xf8, 0x3c, 0x1f, 0x47, 0x9c, 0x9e, 0x5e, 0x91, 0x15, 0x4a, 0x84, 0xa7, 0xaf, 0xf5, 0x32, 0x81, 0xbb, 0xda, 0x0d, 0x53, 0x22, 0x4e, 0x14, 0x74, 0x25,
0xdb, 0xd6, 0xfb, 0xea, 0x42, 0x57, 0x3f, 0x72, 0x2a, 0xe2, 0x38, 0x4f, 0xb8, 0x9a, 0x3f, 0x78, 0x9c, 0x0f, 0xf7, 0x1b, 0xef, 0xc4, 0xf7, 0x79, 0x16, 0x2f, 0x6b, 0xbc, 0x7b, 0xea, 0xd6, 0x7e,
0xaf, 0x63, 0xea, 0xbf, 0x07, 0xed, 0x34, 0xe3, 0x33, 0xa2, 0x58, 0x30, 0x65, 0x73, 0xc3, 0xae, 0xa0, 0xee, 0x5d, 0x09, 0xeb, 0xf7, 0x24, 0x74, 0x4e, 0x61, 0xf7, 0x6e, 0xe2, 0x8b, 0x6c, 0x1c,
0xe3, 0x43, 0x01, 0xbd, 0x64, 0x73, 0xd4, 0xd7, 0x63, 0x28, 0x69, 0xc6, 0x53, 0xcd, 0xcb, 0x94, 0x32, 0xff, 0x6c, 0x4a, 0x56, 0x6c, 0x7a, 0xe7, 0x4b, 0x0d, 0xba, 0xea, 0x92, 0x33, 0x1e, 0x45,
0xbf, 0xe3, 0x57, 0x21, 0xf4, 0x07, 0x34, 0x3e, 0x08, 0x9e, 0x14, 0xc5, 0x6f, 0xf9, 0x85, 0x85, 0x59, 0xcc, 0x64, 0xfe, 0xe8, 0x39, 0x5b, 0x77, 0xc8, 0x1e, 0x74, 0x92, 0x94, 0xcd, 0x89, 0xa4,
0x76, 0xa1, 0x35, 0x63, 0x19, 0xbf, 0xe4, 0x2c, 0xc4, 0x0d, 0x73, 0x52, 0xda, 0xd7, 0xb5, 0x69, 0xde, 0x8c, 0xe6, 0x9a, 0x9d, 0xed, 0x42, 0x01, 0x3d, 0xa7, 0x39, 0xea, 0xab, 0x21, 0x16, 0x7e,
0x56, 0x6b, 0xf3, 0x1a, 0x36, 0x33, 0xf6, 0x31, 0x67, 0x52, 0xc9, 0x40, 0x89, 0x40, 0xbf, 0x83, 0xca, 0x12, 0xc5, 0x4b, 0x37, 0x88, 0xed, 0x56, 0x21, 0xf4, 0x0b, 0x34, 0xdf, 0x71, 0x16, 0x17,
0x5b, 0x66, 0x1f, 0x1d, 0xde, 0xdc, 0x47, 0x65, 0x96, 0x7e, 0xe1, 0x7e, 0x21, 0x5e, 0x08, 0x9e, 0xed, 0xd1, 0x76, 0x0b, 0x0b, 0xed, 0x42, 0x7b, 0x4e, 0x53, 0x76, 0xcd, 0x68, 0x80, 0x9b, 0xda,
0xf8, 0xbd, 0xec, 0x86, 0xed, 0x7d, 0x77, 0xe0, 0xcf, 0x7b, 0xfc, 0x0b, 0x35, 0x9c, 0x52, 0x8d, 0x53, 0xda, 0x37, 0xd5, 0x6b, 0x55, 0xab, 0xf7, 0x0a, 0x36, 0x53, 0xfa, 0x3e, 0xa3, 0x42, 0x0a,
0xbf, 0x00, 0x52, 0xa3, 0xbc, 0x11, 0xc3, 0xaa, 0xbb, 0x6e, 0x11, 0xad, 0x45, 0x29, 0x69, 0xad, 0x4f, 0x72, 0x4f, 0xdd, 0x83, 0xdb, 0x7a, 0x9b, 0x1d, 0xde, 0xde, 0x66, 0xe5, 0x2b, 0xdd, 0x22,
0x2a, 0xe9, 0x3d, 0xd3, 0xb1, 0x0d, 0x4d, 0x7a, 0x45, 0x94, 0x5e, 0x72, 0x6b, 0xe6, 0xa4, 0xa1, 0xfc, 0x92, 0x3f, 0xe3, 0x2c, 0x76, 0x7b, 0xe9, 0x2d, 0xdb, 0xf9, 0x6a, 0xc1, 0xaf, 0x0f, 0xc4,
0xcd, 0x51, 0xa8, 0xbb, 0x82, 0x2e, 0x38, 0xe9, 0xd3, 0x86, 0x95, 0xb5, 0xc4, 0x46, 0x46, 0x22, 0x17, 0x6a, 0x58, 0xa5, 0x1a, 0xbf, 0x01, 0x24, 0x5a, 0x79, 0x2d, 0x86, 0x51, 0x77, 0xdd, 0x20,
0xa9, 0x88, 0xb2, 0xc3, 0x50, 0xf7, 0xad, 0xe1, 0x7d, 0x71, 0x61, 0xf3, 0x76, 0xb3, 0xa0, 0x27, 0x4a, 0x8b, 0x52, 0xd2, 0x7a, 0x55, 0xd2, 0x07, 0xe6, 0x67, 0x1b, 0x5a, 0xfe, 0x94, 0x48, 0xb5,
0x95, 0xfd, 0xed, 0x18, 0xbd, 0xf6, 0x1f, 0xdc, 0xdf, 0xd7, 0xdb, 0x1b, 0x3d, 0x87, 0x4e, 0x91, 0x22, 0xd7, 0xb4, 0xa7, 0xa9, 0xcc, 0x51, 0xa0, 0xba, 0xc2, 0x5f, 0x70, 0x52, 0xde, 0xa6, 0x91,
0xb5, 0x66, 0x27, 0xb1, 0x6b, 0x9e, 0xf8, 0x7b, 0xf9, 0x13, 0xd7, 0xdd, 0xe9, 0xb7, 0xd3, 0xf2, 0xb5, 0xc4, 0x46, 0x5a, 0x22, 0x21, 0x89, 0x34, 0xe3, 0xd2, 0x70, 0x8d, 0xe1, 0x7c, 0xae, 0xc1,
0x5f, 0xa2, 0x63, 0x68, 0x12, 0x3b, 0x31, 0x46, 0xa1, 0x7b, 0x69, 0x14, 0xa3, 0xe5, 0x2f, 0x6e, 0xe6, 0xdd, 0x66, 0x41, 0xff, 0x57, 0xb6, 0xbf, 0xa5, 0xf5, 0xda, 0x7f, 0x74, 0xfb, 0xdf, 0xec,
0xa0, 0x47, 0x50, 0xa6, 0xcf, 0x99, 0xc4, 0x75, 0x43, 0x62, 0x7b, 0x59, 0xdd, 0xab, 0xbe, 0xde, 0x7e, 0xf4, 0x14, 0xec, 0xe2, 0xd5, 0x8a, 0x9d, 0xc0, 0x35, 0x7d, 0xc5, 0xef, 0xcb, 0xaf, 0xb8,
0xff, 0xb0, 0x61, 0x4e, 0x35, 0x21, 0x3b, 0xcc, 0x2b, 0x4e, 0xcd, 0x63, 0xd8, 0x5a, 0x5c, 0x7c, 0xe9, 0x4e, 0xb7, 0x93, 0x94, 0xff, 0x02, 0x1d, 0x43, 0x8b, 0x98, 0x89, 0xd1, 0x0a, 0x3d, 0x48,
0xc5, 0xa4, 0x24, 0x13, 0x26, 0x7d, 0x46, 0x56, 0xbc, 0x7d, 0xd2, 0x7d, 0xdf, 0x1e, 0xfe, 0x7b, 0xa3, 0x18, 0x2d, 0x77, 0x71, 0x02, 0xfd, 0x0b, 0xe5, 0xf3, 0x19, 0x15, 0xb8, 0xa1, 0x49, 0x6c,
0xbc, 0x20, 0x38, 0x6e, 0x98, 0xbf, 0xff, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x54, 0x79, 0x7f, 0x2f, 0xab, 0x7b, 0x35, 0xd6, 0xf9, 0x07, 0x36, 0xb4, 0x57, 0x11, 0x2a, 0xc6, 0x7d, 0xb5, 0xa9,
0x98, 0x97, 0x07, 0x00, 0x00, 0xf9, 0x0f, 0xb6, 0x16, 0x07, 0x5f, 0x52, 0x21, 0xc8, 0x84, 0x0a, 0x97, 0x92, 0x15, 0x4f, 0x9f,
0x76, 0xdf, 0x76, 0x86, 0x7f, 0x1e, 0x2f, 0x08, 0x8e, 0x9b, 0xfa, 0xef, 0xef, 0xef, 0x01, 0x00,
0x00, 0xff, 0xff, 0x9c, 0x69, 0xcf, 0x28, 0xd5, 0x07, 0x00, 0x00,
} }

View File

@ -28,7 +28,7 @@ message SyncInstallationContact {
} }
message SyncInstallationContactV2 { message SyncInstallationContactV2 {
uint64 clock = 1; uint64 last_updated_locally = 1;
string id = 2; string id = 2;
string profile_image = 3; string profile_image = 3;
string ens_name = 4; string ens_name = 4;
@ -39,6 +39,7 @@ message SyncInstallationContactV2 {
bool blocked = 10; bool blocked = 10;
bool muted = 11; bool muted = 11;
bool removed = 12; bool removed = 12;
bool has_added_us = 13;
} }
message SyncInstallationAccount { message SyncInstallationAccount {

View File

@ -300,7 +300,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactO
Added: true, Added: true,
} }
err = bob.SaveContact(aliceContact) _, err = bob.AddContact(context.Background(), aliceContact.ID)
s.Require().NoError(err) s.Require().NoError(err)
// Enable from contacts only // Enable from contacts only
@ -446,7 +446,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
Added: true, Added: true,
} }
err = bob.SaveContact(aliceContact) _, err = bob.AddContact(context.Background(), aliceContact.ID)
s.Require().NoError(err) s.Require().NoError(err)
// Add frank has a contact // Add frank has a contact
@ -456,7 +456,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
Added: true, Added: true,
} }
err = bob.SaveContact(frankContact) _, err = bob.AddContact(context.Background(), frankContact.ID)
s.Require().NoError(err) s.Require().NoError(err)
// Enable from contacts only // Enable from contacts only
@ -542,7 +542,7 @@ func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)), ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
Name: "Some Contact", Name: "Some Contact",
} }
err = bob.SaveContact(frankContact) _, err = bob.RemoveContact(context.Background(), frankContact.ID)
s.Require().NoError(err) s.Require().NoError(err)
// Re-registration should be triggered, pull from server and bob to check we are correctly registered // Re-registration should be triggered, pull from server and bob to check we are correctly registered

View File

@ -293,10 +293,6 @@ func (api *PublicAPI) UnmuteChat(parent context.Context, chatID string) error {
return api.service.messenger.UnmuteChat(chatID) return api.service.messenger.UnmuteChat(chatID)
} }
func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error {
return api.service.messenger.SaveContact(contact)
}
func (api *PublicAPI) BlockContact(parent context.Context, contact *protocol.Contact) ([]*protocol.Chat, error) { func (api *PublicAPI) BlockContact(parent context.Context, contact *protocol.Contact) ([]*protocol.Chat, error) {
api.log.Info("blocking contact", "contact", contact.ID) api.log.Info("blocking contact", "contact", contact.ID)
return api.service.messenger.BlockContact(contact) return api.service.messenger.BlockContact(contact)