From 7523ff1104e867d92c15a0191e085b00e071564c Mon Sep 17 00:00:00 2001 From: Andrea Maria Piana Date: Thu, 2 Feb 2023 18:12:20 +0000 Subject: [PATCH] Handle a few edge cases in contact requests --- protocol/chat.go | 2 + protocol/contact.go | 97 ++++- protocol/contact_test.go | 154 +++++++- protocol/messenger.go | 5 + protocol/messenger_contact_requests_test.go | 395 ++++++++++++++++++++ protocol/messenger_contacts.go | 11 +- protocol/messenger_handler.go | 70 +++- protocol/messenger_messages.go | 18 + protocol/migrations/migrations.go | 4 +- protocol/protobuf/chat_message.pb.go | 241 +++++------- protocol/protobuf/chat_message.proto | 10 +- protocol/protobuf/contact.pb.go | 143 ++++--- protocol/protobuf/contact.proto | 12 +- 13 files changed, 920 insertions(+), 242 deletions(-) diff --git a/protocol/chat.go b/protocol/chat.go index 971698f00..02dc063c7 100644 --- a/protocol/chat.go +++ b/protocol/chat.go @@ -325,6 +325,8 @@ func (c *Chat) NextClockAndTimestamp(timesource common.TimeSource) (uint64, uint } else { clock = clock + 1 } + c.LastClockValue = clock + return clock, timestamp } diff --git a/protocol/contact.go b/protocol/contact.go index bb1f01615..a82cf7ca6 100644 --- a/protocol/contact.go +++ b/protocol/contact.go @@ -13,6 +13,7 @@ import ( "github.com/status-im/status-go/protocol/identity" "github.com/status-im/status-go/protocol/identity/alias" "github.com/status-im/status-go/protocol/identity/identicon" + "github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/verification" ) @@ -22,6 +23,9 @@ const ( ContactRequestStateNone ContactRequestState = iota ContactRequestStateMutual ContactRequestStateSent + // Received is a confusing state, we should use + // sent for both, since they are now stored in different + // states ContactRequestStateReceived ContactRequestStateDismissed ) @@ -187,9 +191,14 @@ func (c *Contact) mutual() bool { return c.added() && c.hasAddedUs() } +func (c *Contact) dismissed() bool { + return c.ContactRequestLocalState == ContactRequestStateDismissed +} + type ContactRequestProcessingResponse struct { processed bool newContactRequestReceived bool + sendBackState bool } func (c *Contact) ContactRequestSent(clock uint64) ContactRequestProcessingResponse { @@ -243,28 +252,35 @@ func (c *Contact) DismissContactRequest(clock uint64) ContactRequestProcessingRe // Remote actions -func (c *Contact) ContactRequestRetracted(clock uint64) ContactRequestProcessingResponse { +func (c *Contact) contactRequestRetracted(clock uint64, r ContactRequestProcessingResponse) ContactRequestProcessingResponse { if clock <= c.ContactRequestRemoteClock { - return ContactRequestProcessingResponse{} + return r } // This is a symmetric action, we set both local & remote clock // since we want everything before this point discarded, regardless - // the side it was sent from + // the side it was sent from. The only exception is when the contact + // request has been explicitly dismissed, in which case we don't + // change state + if c.ContactRequestLocalState != ContactRequestStateDismissed { + c.ContactRequestLocalClock = clock + c.ContactRequestLocalState = ContactRequestStateNone + } c.ContactRequestRemoteClock = clock c.ContactRequestRemoteState = ContactRequestStateNone - c.ContactRequestLocalClock = clock - c.ContactRequestLocalState = ContactRequestStateNone - - return ContactRequestProcessingResponse{processed: true} + r.processed = true + return r } -func (c *Contact) ContactRequestReceived(clock uint64) ContactRequestProcessingResponse { - if clock <= c.ContactRequestRemoteClock { - return ContactRequestProcessingResponse{} - } +func (c *Contact) ContactRequestRetracted(clock uint64) ContactRequestProcessingResponse { + return c.contactRequestRetracted(clock, ContactRequestProcessingResponse{}) +} - r := ContactRequestProcessingResponse{processed: true} +func (c *Contact) contactRequestReceived(clock uint64, r ContactRequestProcessingResponse) ContactRequestProcessingResponse { + if clock <= c.ContactRequestRemoteClock { + return r + } + r.processed = true c.ContactRequestRemoteClock = clock switch c.ContactRequestRemoteState { case ContactRequestStateNone: @@ -275,6 +291,10 @@ func (c *Contact) ContactRequestReceived(clock uint64) ContactRequestProcessingR return r } +func (c *Contact) ContactRequestReceived(clock uint64) ContactRequestProcessingResponse { + return c.contactRequestReceived(clock, ContactRequestProcessingResponse{}) +} + func (c *Contact) ContactRequestAccepted(clock uint64) ContactRequestProcessingResponse { if clock <= c.ContactRequestRemoteClock { return ContactRequestProcessingResponse{} @@ -383,3 +403,56 @@ func (c *Contact) MarshalJSON() ([]byte, error) { return json.Marshal(item) } + +// ContactRequestPropagatedStateReceived handles the propagation of state from +// the other end. +func (c *Contact) ContactRequestPropagatedStateReceived(state *protobuf.ContactRequestPropagatedState) ContactRequestProcessingResponse { + + // It's inverted, as their local states is our remote state + expectedLocalState := ContactRequestState(state.RemoteState) + expectedLocalClock := state.RemoteClock + + remoteState := ContactRequestState(state.LocalState) + remoteClock := state.LocalClock + + response := ContactRequestProcessingResponse{} + + // If we notice that the state is not consistent, and their clock is + // outdated, we send back the state so they can catch up. + if expectedLocalClock < c.ContactRequestLocalClock && expectedLocalState != c.ContactRequestLocalState { + response.processed = true + response.sendBackState = true + } + + // If they expect our state to be more up-to-date, we only + // trust it if the state is set to None, in this case we can trust + // it, since a retraction can be initiated by both parties + if expectedLocalClock > c.ContactRequestLocalClock && c.ContactRequestLocalState != ContactRequestStateDismissed && expectedLocalState == ContactRequestStateNone { + response.processed = true + c.ContactRequestLocalClock = expectedLocalClock + c.ContactRequestLocalState = ContactRequestStateNone + // We set they remote state, as this was an implicit retraction + // potentially + c.ContactRequestRemoteState = ContactRequestStateNone + } + + // We always trust this + if remoteClock > c.ContactRequestRemoteClock { + if remoteState == ContactRequestStateSent { + response = c.contactRequestReceived(remoteClock, response) + } else if remoteState == ContactRequestStateNone { + response = c.contactRequestRetracted(remoteClock, response) + } + } + + return response +} + +func (c *Contact) ContactRequestPropagatedState() *protobuf.ContactRequestPropagatedState { + return &protobuf.ContactRequestPropagatedState{ + LocalClock: c.ContactRequestLocalClock, + LocalState: uint64(c.ContactRequestLocalState), + RemoteClock: c.ContactRequestRemoteClock, + RemoteState: uint64(c.ContactRequestRemoteState), + } +} diff --git a/protocol/contact_test.go b/protocol/contact_test.go index aa559da82..e8325468b 100644 --- a/protocol/contact_test.go +++ b/protocol/contact_test.go @@ -10,6 +10,7 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/protocol/common" + "github.com/status-im/status-go/protocol/protobuf" ) type contactTest struct { @@ -381,7 +382,7 @@ func TestContactContactRequestRetracted(t *testing.T) { { actualLocalState: ContactRequestStateDismissed, actualRemoteState: ContactRequestStateNone, - expectedLocalState: ContactRequestStateNone, + expectedLocalState: ContactRequestStateDismissed, expectedRemoteState: ContactRequestStateNone, expectedAdded: false, expectedHasAddedUs: false, @@ -390,7 +391,7 @@ func TestContactContactRequestRetracted(t *testing.T) { { actualLocalState: ContactRequestStateDismissed, actualRemoteState: ContactRequestStateReceived, - expectedLocalState: ContactRequestStateNone, + expectedLocalState: ContactRequestStateDismissed, expectedRemoteState: ContactRequestStateNone, expectedAdded: false, expectedHasAddedUs: false, @@ -559,3 +560,152 @@ func TestMarshalContactJSON(t *testing.T) { require.True(t, strings.Contains(string(encodedContact), "compressedKey\":\"zQ")) } + +func TestContactContactRequestPropagatedStateReceivedOutOfDateLocalStateOnTheirSide(t *testing.T) { + // We receive a message with expected contact request state != our state + // and clock < our clock, we ping back the user to reach consistency + + c := &Contact{} + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + + result := c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 0, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 1, + }, + ) + + require.True(t, result.sendBackState) + + // if the state is the same, it should not send back a message + + c = &Contact{} + c.ContactRequestLocalState = ContactRequestStateNone + c.ContactRequestLocalClock = 1 + + result = c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 0, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 1, + }, + ) + + require.False(t, result.sendBackState) + + // If the clock is the same, it should not send back a message + c = &Contact{} + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + + result = c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 1, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 1, + }, + ) + + require.False(t, result.sendBackState) + +} + +func TestContactContactRequestPropagatedStateReceivedOutOfDateLocalStateOnOurSide(t *testing.T) { + // We receive a message with expected contact request state == none + // and clock > our clock. We consider this a retraction, unless we are in the dismissed state, since that should be only changed by a trusted device + + c := &Contact{} + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + + c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 2, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 1, + }, + ) + + require.False(t, c.added()) + + // But if it's dismissed, we don't change it + c = &Contact{} + c.ContactRequestLocalState = ContactRequestStateDismissed + c.ContactRequestLocalClock = 1 + + c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 1, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 2, + }, + ) + + require.False(t, c.added()) + require.True(t, c.dismissed()) + + // or if it's lower clock + + c = &Contact{} + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + + c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateNone), + RemoteClock: 1, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 0, + }, + ) + + require.True(t, c.added()) +} + +func TestContactContactRequestPropagatedStateReceivedOutOfDateRemoteState(t *testing.T) { + // We receive a message with newer remote state, we process it as we would for a normal contact request + + c := &Contact{} + + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + + c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateSent), + RemoteClock: 1, + LocalState: uint64(ContactRequestStateSent), + LocalClock: 1, + }, + ) + + require.True(t, c.added()) + require.True(t, c.mutual()) + + // and retraction + c = &Contact{} + c.ContactRequestLocalState = ContactRequestStateSent + c.ContactRequestLocalClock = 1 + c.ContactRequestRemoteState = ContactRequestStateReceived + c.ContactRequestRemoteClock = 1 + + c.ContactRequestPropagatedStateReceived( + &protobuf.ContactRequestPropagatedState{ + RemoteState: uint64(ContactRequestStateSent), + RemoteClock: 1, + LocalState: uint64(ContactRequestStateNone), + LocalClock: 2, + }, + ) + + require.False(t, c.added()) + require.False(t, c.hasAddedUs()) + require.False(t, c.mutual()) +} diff --git a/protocol/messenger.go b/protocol/messenger.go index 029b59ed9..14ff659f5 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -1992,6 +1992,11 @@ func (m *Messenger) sendChatMessage(ctx context.Context, message *common.Message return nil, err } + err = m.addContactRequestPropagatedState(message) + if err != nil { + return nil, err + } + encodedMessage, err := m.encodeChatEntity(chat, message) if err != nil { return nil, err diff --git a/protocol/messenger_contact_requests_test.go b/protocol/messenger_contact_requests_test.go index 7b385f1f4..6eb837c66 100644 --- a/protocol/messenger_contact_requests_test.go +++ b/protocol/messenger_contact_requests_test.go @@ -1276,3 +1276,398 @@ func (s *MessengerContactRequestSuite) TestPairedDevicesRemoveContact() { s.Require().Equal(ContactRequestStateNone, resp.Contacts[0].ContactRequestLocalState) s.Require().Equal(ContactRequestStateNone, resp.Contacts[0].ContactRequestRemoteState) } + +// The scenario tested is as follow: +// 1) Alice sends a contact request to Bob +// 2) Bob accepts the contact request +// 3) Alice restores state on a different device +// 4) Alice sends a contact request to bob +// Bob will need to help Alice recover her state, since as far as he can see +// that's an already accepted contact request +func (s *MessengerContactRequestSuite) TestAliceRecoverStateSendContactRequest() { + // Alice sends a contact request to bob + alice1 := s.m + + bob := s.newMessenger(s.shh) + _, err := bob.Start() + s.Require().NoError(err) + + bobID := types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey)) + myID := types.EncodeHex(crypto.FromECDSAPub(&alice1.identity.PublicKey)) + + request := &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + resp, err := WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + + // Check contact request has been received + s.Require().NoError(err) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().Equal(ContactRequestStateReceived, resp.Contacts[0].ContactRequestRemoteState) + + // Bob accepts the contact request + _, err = bob.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(myID)}) + s.Require().NoError(err) + + // Alice receives the accepted confirmation + resp, err = WaitOnMessengerResponse( + alice1, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + + // Make sure we consider them a mutual contact, sender side + mutualContacts := alice1.MutualContacts() + s.Require().Len(mutualContacts, 1) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().True(resp.Contacts[0].mutual()) + + // Alice resets her device + alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil) + s.Require().NoError(err) + + _, err = alice2.Start() + s.Require().NoError(err) + + // adds bob again to her device + request = &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice2.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + _, err = WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + + // Bob should be a mutual contact with alice, nothing has changed + s.Require().Len(bob.MutualContacts(), 1) + + // Alice retrieves her messages, she should have been notified by + // dear bobby that they were contacts + resp, err = WaitOnMessengerResponse( + alice2, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Len(resp.Contacts, 1) + + // Check the contact state is correctly set + s.Require().True(resp.Contacts[0].mutual()) +} + +// The scenario tested is as follow: +// 1) Alice sends a contact request to Bob +// 2) Bob accepts the contact request +// 3) Alice restores state on a different device +// 4) Bob sends a message to alice +// Alice will show a contact request from bob +func (s *MessengerContactRequestSuite) TestAliceRecoverStateReceiveContactRequest() { + // Alice sends a contact request to bob + alice1 := s.m + + bob := s.newMessenger(s.shh) + _, err := bob.Start() + s.Require().NoError(err) + + bobID := types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey)) + myID := types.EncodeHex(crypto.FromECDSAPub(&alice1.identity.PublicKey)) + + request := &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + resp, err := WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + + // Check contact request has been received + s.Require().NoError(err) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().Equal(ContactRequestStateReceived, resp.Contacts[0].ContactRequestRemoteState) + + // Bob accepts the contact request + _, err = bob.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(myID)}) + s.Require().NoError(err) + + // Alice receives the accepted confirmation + resp, err = WaitOnMessengerResponse( + alice1, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + + // Make sure we consider them a mutual contact, sender side + mutualContacts := alice1.MutualContacts() + s.Require().Len(mutualContacts, 1) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().True(resp.Contacts[0].mutual()) + + // Alice resets her device + alice2, err := newMessengerWithKey(s.shh, s.m.identity, s.logger, nil) + s.Require().NoError(err) + + _, err = alice2.Start() + s.Require().NoError(err) + + // We want to facilitate the discovery of the x3dh bundl here, since bob does not know about alice device + + alice2Bundle, err := alice2.encryptor.GetBundle(alice2.identity) + s.Require().NoError(err) + + _, err = bob.encryptor.ProcessPublicBundle(bob.identity, alice2Bundle) + s.Require().NoError(err) + + // Bob sends a chat message to alice + + var chat Chat + chats := bob.Chats() + for i, c := range chats { + if c.ID == alice1.myHexIdentity() && c.OneToOne() { + chat = *chats[i] + } + } + s.Require().NotNil(chat) + + inputMessage := buildTestMessage(chat) + _, err = bob.SendChatMessage(context.Background(), inputMessage) + s.NoError(err) + + // Alice retrieves the chat message, it should be + resp, err = WaitOnMessengerResponse( + alice2, + func(r *MessengerResponse) bool { + return len(r.ActivityCenterNotifications()) == 1 + }, + "no messages", + ) + s.Require().NoError(err) + s.Require().NotNil(resp) + s.Require().Equal(ActivityCenterNotificationTypeContactRequest, resp.ActivityCenterNotifications()[0].Type) + s.Require().Len(resp.Contacts, 1) + + // Check the contact state is correctly set + s.Require().Equal(ContactRequestStateNone, resp.Contacts[0].ContactRequestLocalState) + s.Require().Equal(ContactRequestStateReceived, resp.Contacts[0].ContactRequestRemoteState) +} + +// The scenario tested is as follow: +// 1) Alice sends a contact request to Bob +// 2) Bob accepts the contact request +// 3) Bob goes offline +// 4) Alice retracts the contact request +// 5) Alice adds bob back to her contacts +// 6) Bob goes online, they receive 4 and 5 in the correct order +func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsCorrectOrder() { + // Alice sends a contact request to bob + alice1 := s.m + + bob := s.newMessenger(s.shh) + _, err := bob.Start() + s.Require().NoError(err) + + bobID := types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey)) + myID := types.EncodeHex(crypto.FromECDSAPub(&alice1.identity.PublicKey)) + + request := &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + resp, err := WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + + // Check contact request has been received + s.Require().NoError(err) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().Equal(ContactRequestStateReceived, resp.Contacts[0].ContactRequestRemoteState) + + // Bob accepts the contact request + _, err = bob.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(myID)}) + s.Require().NoError(err) + + // Alice receives the accepted confirmation + resp, err = WaitOnMessengerResponse( + alice1, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + + // Make sure we consider them a mutual contact, sender side + mutualContacts := alice1.MutualContacts() + s.Require().Len(mutualContacts, 1) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().True(resp.Contacts[0].mutual()) + + _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(bob.myHexIdentity())}) + s.Require().NoError(err) + + // adds bob again to her device + request = &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + _, err = WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + +} + +// The scenario tested is as follow: +// 1) Alice sends a contact request to Bob +// 2) Bob accepts the contact request +// 3) Bob goes offline +// 4) Alice retracts the contact request +// 5) Alice adds bob back to her contacts +// 6) Bob goes online, they receive 4 and 5 in the wrong order +func (s *MessengerContactRequestSuite) TestAliceOfflineRetractsAndAddsWrongOrder() { + // Alice sends a contact request to bob + alice1 := s.m + + bob := s.newMessenger(s.shh) + _, err := bob.Start() + s.Require().NoError(err) + + bobID := types.EncodeHex(crypto.FromECDSAPub(&bob.identity.PublicKey)) + myID := types.EncodeHex(crypto.FromECDSAPub(&alice1.identity.PublicKey)) + + request := &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Wait for the message to reach its destination + resp, err := WaitOnMessengerResponse( + bob, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 && len(r.Messages()) > 0 && len(r.ActivityCenterNotifications()) > 0 + }, + "no messages", + ) + + // Check contact request has been received + s.Require().NoError(err) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().Equal(ContactRequestStateReceived, resp.Contacts[0].ContactRequestRemoteState) + + // Bob accepts the contact request + _, err = bob.AcceptLatestContactRequestForContact(context.Background(), &requests.AcceptLatestContactRequestForContact{ID: types.Hex2Bytes(myID)}) + s.Require().NoError(err) + + // Alice receives the accepted confirmation + resp, err = WaitOnMessengerResponse( + alice1, + func(r *MessengerResponse) bool { + return len(r.Contacts) > 0 + }, + "no messages", + ) + s.Require().NoError(err) + + // Make sure we consider them a mutual contact, sender side + mutualContacts := alice1.MutualContacts() + s.Require().Len(mutualContacts, 1) + + // Check the contact state is correctly set + s.Require().Len(resp.Contacts, 1) + s.Require().True(resp.Contacts[0].mutual()) + + _, err = alice1.RetractContactRequest(&requests.RetractContactRequest{ContactID: types.Hex2Bytes(bob.myHexIdentity())}) + s.Require().NoError(err) + + // adds bob again to her device + request = &requests.AddContact{ + ID: types.Hex2Bytes(bobID), + } + + _, err = alice1.AddContact(context.Background(), request) + s.Require().NoError(err) + + // Get alice perspective of bob + bobFromAlice := alice1.AddedContacts()[0] + + // Get bob perspective of alice + aliceFromBob := bob.MutualContacts()[0] + + s.Require().NotNil(bobFromAlice) + s.Require().NotNil(aliceFromBob) + + // We can't simulate out-of-order messages easily, so we need to do + // things manually here + + result := aliceFromBob.ContactRequestPropagatedStateReceived(bobFromAlice.ContactRequestPropagatedState()) + s.Require().True(result.newContactRequestReceived) + +} diff --git a/protocol/messenger_contacts.go b/protocol/messenger_contacts.go index c56697f69..c48e9b61e 100644 --- a/protocol/messenger_contacts.go +++ b/protocol/messenger_contacts.go @@ -740,11 +740,12 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, displayName, } contactUpdate := &protobuf.ContactUpdate{ - Clock: clock, - DisplayName: displayName, - EnsName: ensName, - ProfileImage: profileImage, - ContactRequestClock: contact.ContactRequestLocalClock, + Clock: clock, + DisplayName: displayName, + EnsName: ensName, + ProfileImage: profileImage, + ContactRequestClock: contact.ContactRequestLocalClock, + ContactRequestPropagatedState: contact.ContactRequestPropagatedState(), } encodedMessage, err := proto.Marshal(contactUpdate) if err != nil { diff --git a/protocol/messenger_handler.go b/protocol/messenger_handler.go index ec42b9cdc..d32a49672 100644 --- a/protocol/messenger_handler.go +++ b/protocol/messenger_handler.go @@ -732,6 +732,7 @@ func (m *Messenger) handleAcceptContactRequest( originalRequest *common.Message, message protobuf.AcceptContactRequest) (ContactRequestProcessingResponse, error) { + m.logger.Info("received contact request", zap.Uint64("clock-sent", message.Clock), zap.Uint64("current-clock", contact.ContactRequestRemoteClock), zap.Uint64("current-state", uint64(contact.ContactRequestRemoteState))) if contact.ContactRequestRemoteClock > message.Clock { m.logger.Info("not handling accept since clock lower") return ContactRequestProcessingResponse{}, nil @@ -740,8 +741,6 @@ func (m *Messenger) handleAcceptContactRequest( // The contact request accepted wasn't found, a reason for this might // be that we sent a legacy contact request/contact-update, or another // device has sent it, and we haven't synchronized it - // TODO(cammellos): This might want to show a notification if we haven't - // added the user to contacts already if originalRequest == nil { return contact.ContactRequestAccepted(message.Clock), nil } @@ -817,14 +816,13 @@ func (m *Messenger) HandleAcceptContactRequest(state *ReceivedMessageState, mess return nil } -func (m *Messenger) HandleRetractContactRequest(state *ReceivedMessageState, message protobuf.RetractContactRequest) error { - contact := state.CurrentMessageState.Contact - +func (m *Messenger) handleRetractContactRequest(contact *Contact, message protobuf.RetractContactRequest) error { if contact.ID == m.myHexIdentity() { m.logger.Debug("retraction coming from us, ignoring") return nil } + m.logger.Debug("handling retracted contact request", zap.Uint64("clock", message.Clock)) r := contact.ContactRequestRetracted(message.Clock) if !r.processed { m.logger.Info("not handling retract since clock lower") @@ -837,9 +835,18 @@ func (m *Messenger) HandleRetractContactRequest(state *ReceivedMessageState, mes return err } - state.ModifiedContacts.Store(contact.ID, true) + m.allContacts.Store(contact.ID, contact) - state.AllContacts.Store(contact.ID, contact) + return nil +} + +func (m *Messenger) HandleRetractContactRequest(state *ReceivedMessageState, message protobuf.RetractContactRequest) error { + contact := state.CurrentMessageState.Contact + err := m.handleRetractContactRequest(contact, message) + if err != nil { + return err + } + state.ModifiedContacts.Store(contact.ID, true) return nil } @@ -869,6 +876,29 @@ func (m *Messenger) HandleContactUpdate(state *ReceivedMessageState, message pro logger.Info("Handling contact update") + if message.ContactRequestPropagatedState != nil { + result := contact.ContactRequestPropagatedStateReceived(message.ContactRequestPropagatedState) + if result.sendBackState { + // This is a bit dangerous, since it might trigger a ping-pong of contact updates + // also it should backoff/debounce + _, err = m.sendContactUpdate(context.Background(), contact.ID, "", "", "", m.dispatchMessage) + if err != nil { + return err + } + + } + if result.newContactRequestReceived { + err = m.createContactRequestNotification(contact, state, nil) + if err != nil { + return err + } + + } + state.ModifiedContacts.Store(contact.ID, true) + state.AllContacts.Store(contact.ID, contact) + + } + if contact.LastUpdated < message.Clock { logger.Info("Updating contact") if contact.EnsName != message.EnsName { @@ -1675,6 +1705,27 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error { contact := state.CurrentMessageState.Contact + // If we receive some propagated state from someone who's not + // our paired device, we handle it + if receivedMessage.ContactRequestPropagatedState != nil && !isSyncMessage { + result := contact.ContactRequestPropagatedStateReceived(receivedMessage.ContactRequestPropagatedState) + if result.sendBackState { + _, err = m.sendContactUpdate(context.Background(), contact.ID, "", "", "", m.dispatchMessage) + if err != nil { + return err + } + } + if result.newContactRequestReceived { + err = m.createContactRequestNotification(contact, state, receivedMessage) + if err != nil { + return err + } + + } + state.ModifiedContacts.Store(contact.ID, true) + state.AllContacts.Store(contact.ID, contact) + } + if receivedMessage.ContentType == protobuf.ChatMessage_CONTACT_REQUEST && chat.OneToOne() { chatContact := contact @@ -1749,11 +1800,6 @@ func (m *Messenger) HandleChatMessage(state *ReceivedMessageState) error { } } - // If the chat is not active, create a notification in the center - if !receivedMessage.Deleted && chat.OneToOne() && !chat.Active && receivedMessage.ContentType != protobuf.ChatMessage_CONTACT_REQUEST { - m.createMessageNotification(chat, state) - } - // Set in the modified maps chat state.Response.AddChat(chat) // TODO(samyoul) remove storing of an updated reference pointer? diff --git a/protocol/messenger_messages.go b/protocol/messenger_messages.go index 7532411a4..bdfac5607 100644 --- a/protocol/messenger_messages.go +++ b/protocol/messenger_messages.go @@ -326,3 +326,21 @@ func (m *Messenger) applyDeleteForMeMessage(messageDeletes []*DeleteForMeMessage return nil } + +func (m *Messenger) addContactRequestPropagatedState(message *common.Message) error { + chat, ok := m.allChats.Load(message.LocalChatID) + if !ok { + return ErrChatNotFound + } + if !chat.OneToOne() { + return nil + } + + contact, err := m.BuildContact(chat.ID) + if err != nil { + return err + } + + message.ContactRequestPropagatedState = contact.ContactRequestPropagatedState() + return nil +} diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index 942bb3d97..e8e7f8794 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -1558,7 +1558,7 @@ func _1674210659_add_contact_request_local_clockUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1674210659_add_contact_request_local_clock.up.sql", size: 691, mode: os.FileMode(0644), modTime: time.Unix(1675272378, 0)} + info := bindataFileInfo{name: "1674210659_add_contact_request_local_clock.up.sql", size: 691, mode: os.FileMode(0644), modTime: time.Unix(1675361522, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x92, 0x72, 0x39, 0xfe, 0x72, 0x98, 0xfc, 0x91, 0x20, 0x10, 0xe8, 0xf5, 0xac, 0x79, 0xa8, 0x1c, 0xca, 0x7b, 0x35, 0xa, 0xc1, 0x56, 0x49, 0x9a, 0xfc, 0xbd, 0x64, 0x9d, 0xdf, 0xd2, 0x60, 0x70}} return a, nil } @@ -1598,7 +1598,7 @@ func _1675272329_fix_protocol_migrationUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1675272329_fix_protocol_migration.up.sql", size: 183, mode: os.FileMode(0644), modTime: time.Unix(1675272611, 0)} + info := bindataFileInfo{name: "1675272329_fix_protocol_migration.up.sql", size: 183, mode: os.FileMode(0644), modTime: time.Unix(1675361522, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb6, 0xe0, 0x11, 0x4c, 0x66, 0x55, 0x72, 0xd3, 0xe6, 0x98, 0xa4, 0xe7, 0x44, 0xf9, 0x3b, 0x3a, 0x3f, 0xd9, 0x91, 0x1e, 0x4f, 0xfc, 0x56, 0x63, 0xe5, 0xa4, 0x83, 0xfc, 0x7c, 0xcf, 0x18, 0x99}} return a, nil } diff --git a/protocol/protobuf/chat_message.pb.go b/protocol/protobuf/chat_message.pb.go index 080614f92..953426ebb 100644 --- a/protocol/protobuf/chat_message.pb.go +++ b/protocol/protobuf/chat_message.pb.go @@ -866,13 +866,11 @@ type ChatMessage struct { // Grant for community chat messages Grant []byte `protobuf:"bytes,13,opt,name=grant,proto3" json:"grant,omitempty"` // Message author's display name, introduced in version 1 - DisplayName string `protobuf:"bytes,14,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - SentContactRequestSignature *ContactRequestSignature `protobuf:"bytes,15,opt,name=sent_contact_request_signature,json=sentContactRequestSignature,proto3" json:"sent_contact_request_signature,omitempty"` - ReceivedContactRequestSignature *ContactRequestSignature `protobuf:"bytes,16,opt,name=received_contact_request_signature,json=receivedContactRequestSignature,proto3" json:"received_contact_request_signature,omitempty"` - ContactMessage bool `protobuf:"varint,17,opt,name=contact_message,json=contactMessage,proto3" json:"contact_message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + DisplayName string `protobuf:"bytes,14,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + ContactRequestPropagatedState *ContactRequestPropagatedState `protobuf:"bytes,15,opt,name=contact_request_propagated_state,json=contactRequestPropagatedState,proto3" json:"contact_request_propagated_state,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ChatMessage) Reset() { *m = ChatMessage{} } @@ -1046,27 +1044,13 @@ func (m *ChatMessage) GetDisplayName() string { return "" } -func (m *ChatMessage) GetSentContactRequestSignature() *ContactRequestSignature { +func (m *ChatMessage) GetContactRequestPropagatedState() *ContactRequestPropagatedState { if m != nil { - return m.SentContactRequestSignature + return m.ContactRequestPropagatedState } return nil } -func (m *ChatMessage) GetReceivedContactRequestSignature() *ContactRequestSignature { - if m != nil { - return m.ReceivedContactRequestSignature - } - return nil -} - -func (m *ChatMessage) GetContactMessage() bool { - if m != nil { - return m.ContactMessage - } - return false -} - // XXX_OneofWrappers is for the internal use of the proto package. func (*ChatMessage) XXX_OneofWrappers() []interface{} { return []interface{}{ @@ -1078,53 +1062,6 @@ func (*ChatMessage) XXX_OneofWrappers() []interface{} { } } -type ContactRequestSignature struct { - Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` - Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ContactRequestSignature) Reset() { *m = ContactRequestSignature{} } -func (m *ContactRequestSignature) String() string { return proto.CompactTextString(m) } -func (*ContactRequestSignature) ProtoMessage() {} -func (*ContactRequestSignature) Descriptor() ([]byte, []int) { - return fileDescriptor_263952f55fd35689, []int{11} -} - -func (m *ContactRequestSignature) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ContactRequestSignature.Unmarshal(m, b) -} -func (m *ContactRequestSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ContactRequestSignature.Marshal(b, m, deterministic) -} -func (m *ContactRequestSignature) XXX_Merge(src proto.Message) { - xxx_messageInfo_ContactRequestSignature.Merge(m, src) -} -func (m *ContactRequestSignature) XXX_Size() int { - return xxx_messageInfo_ContactRequestSignature.Size(m) -} -func (m *ContactRequestSignature) XXX_DiscardUnknown() { - xxx_messageInfo_ContactRequestSignature.DiscardUnknown(m) -} - -var xxx_messageInfo_ContactRequestSignature proto.InternalMessageInfo - -func (m *ContactRequestSignature) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *ContactRequestSignature) GetTimestamp() uint64 { - if m != nil { - return m.Timestamp - } - return 0 -} - func init() { proto.RegisterEnum("protobuf.AudioMessage_AudioType", AudioMessage_AudioType_name, AudioMessage_AudioType_value) proto.RegisterEnum("protobuf.ChatMessage_ContentType", ChatMessage_ContentType_name, ChatMessage_ContentType_value) @@ -1139,7 +1076,6 @@ func init() { proto.RegisterType((*DiscordMessageReference)(nil), "protobuf.DiscordMessageReference") proto.RegisterType((*DiscordMessageAttachment)(nil), "protobuf.DiscordMessageAttachment") proto.RegisterType((*ChatMessage)(nil), "protobuf.ChatMessage") - proto.RegisterType((*ContactRequestSignature)(nil), "protobuf.ContactRequestSignature") } func init() { @@ -1147,87 +1083,84 @@ func init() { } var fileDescriptor_263952f55fd35689 = []byte{ - // 1302 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcd, 0x92, 0xdb, 0xc4, - 0x13, 0x5f, 0x7f, 0x5b, 0x2d, 0xdb, 0xab, 0xff, 0x64, 0x93, 0x55, 0xf2, 0xcf, 0x87, 0xa3, 0x4a, - 0x55, 0x7c, 0x32, 0x55, 0x21, 0x50, 0xa9, 0xe2, 0x40, 0x69, 0x6d, 0x65, 0x23, 0x82, 0xed, 0x65, - 0x2c, 0x07, 0x96, 0x8b, 0x4a, 0x2b, 0xcd, 0xae, 0x55, 0x6b, 0x4b, 0x46, 0x1a, 0x07, 0xcc, 0x9d, - 0x23, 0x2f, 0xc0, 0x89, 0xa7, 0xe0, 0x29, 0xb8, 0xf2, 0x0a, 0x3c, 0x01, 0x0f, 0x40, 0xcd, 0x48, - 0x23, 0x4b, 0x26, 0xde, 0xa4, 0x72, 0xd2, 0x74, 0xab, 0xbb, 0xe7, 0xd7, 0xbf, 0xee, 0xe9, 0x19, - 0x40, 0xee, 0xdc, 0xa1, 0xf6, 0x92, 0xc4, 0xb1, 0x73, 0x45, 0xfa, 0xab, 0x28, 0xa4, 0x21, 0x6a, - 0xf2, 0xcf, 0xc5, 0xfa, 0xf2, 0x9e, 0x4c, 0x82, 0xf5, 0x32, 0x4e, 0xd4, 0xda, 0x0b, 0xe8, 0x4c, - 0xa9, 0xef, 0x5e, 0x93, 0x68, 0x94, 0x98, 0x23, 0x04, 0xd5, 0xb9, 0x13, 0xcf, 0xd5, 0x52, 0xb7, - 0xd4, 0x93, 0x30, 0x5f, 0x33, 0xdd, 0xca, 0x71, 0xaf, 0xd5, 0x72, 0xb7, 0xd4, 0xab, 0x61, 0xbe, - 0xd6, 0x7e, 0x2b, 0x41, 0xcb, 0x5c, 0x3a, 0x57, 0x44, 0x38, 0xaa, 0xd0, 0x58, 0x39, 0x9b, 0x45, - 0xe8, 0x78, 0xdc, 0xb7, 0x85, 0x85, 0x88, 0x9e, 0x42, 0x95, 0x6e, 0x56, 0x84, 0xbb, 0x77, 0x9e, - 0xdd, 0xea, 0x0b, 0x28, 0x7d, 0xee, 0x6f, 0x6d, 0x56, 0x04, 0x73, 0x03, 0x74, 0x17, 0x9a, 0xce, - 0xe2, 0x62, 0xbd, 0xb4, 0x7d, 0x4f, 0xad, 0xf0, 0xfd, 0x1b, 0x5c, 0x36, 0x3d, 0x74, 0x04, 0xb5, - 0x1f, 0x7d, 0x8f, 0xce, 0xd5, 0x6a, 0xb7, 0xd4, 0x6b, 0xe3, 0x44, 0x40, 0x77, 0xa0, 0x3e, 0x27, - 0xfe, 0xd5, 0x9c, 0xaa, 0x35, 0xae, 0x4e, 0x25, 0xed, 0x8f, 0x12, 0xb4, 0xf4, 0xb5, 0xe7, 0x87, - 0xef, 0x07, 0xf7, 0xbc, 0x00, 0xae, 0xbb, 0x05, 0x97, 0xf7, 0x4f, 0x84, 0x1c, 0xd2, 0x47, 0x20, - 0x7b, 0xeb, 0xc8, 0xa1, 0x7e, 0x18, 0xd8, 0xcb, 0x98, 0x83, 0xad, 0x62, 0x10, 0xaa, 0x51, 0xac, - 0x7d, 0x06, 0x52, 0xe6, 0x83, 0xee, 0x00, 0x9a, 0x8d, 0x5f, 0x8f, 0x27, 0xdf, 0x8e, 0x6d, 0x7d, - 0x36, 0x34, 0x27, 0xb6, 0x75, 0x7e, 0x66, 0x28, 0x07, 0xa8, 0x01, 0x15, 0x5d, 0x1f, 0x28, 0x25, - 0xbe, 0x18, 0x61, 0xa5, 0xac, 0xfd, 0x52, 0x06, 0xd9, 0xf0, 0x7c, 0x2a, 0x70, 0x1f, 0x41, 0xcd, - 0x5d, 0x84, 0xee, 0x35, 0x47, 0x5d, 0xc5, 0x89, 0xc0, 0xea, 0x41, 0xc9, 0x4f, 0x94, 0x63, 0x96, - 0x30, 0x5f, 0xa3, 0x63, 0x68, 0xf0, 0xb2, 0x67, 0xd4, 0xd5, 0x99, 0x68, 0x7a, 0xe8, 0x01, 0x40, - 0xda, 0x0a, 0xec, 0x5f, 0x95, 0xff, 0x93, 0x52, 0x4d, 0x42, 0xec, 0x55, 0xe4, 0x04, 0x09, 0x83, - 0x2d, 0x9c, 0x08, 0xe8, 0x05, 0xb4, 0x84, 0x13, 0x67, 0xa7, 0xce, 0xd9, 0xb9, 0xbd, 0x65, 0x27, - 0x05, 0xc8, 0x29, 0x91, 0x97, 0x5b, 0x01, 0x0d, 0xa1, 0xe5, 0x86, 0x01, 0x25, 0x01, 0x4d, 0x3c, - 0x1b, 0xdc, 0xf3, 0xf1, 0xd6, 0x73, 0x30, 0x77, 0x44, 0x7a, 0xfd, 0x41, 0x62, 0x99, 0x44, 0x71, - 0xb7, 0x82, 0xf6, 0x67, 0x09, 0xda, 0x43, 0xb2, 0x20, 0x94, 0xdc, 0xcc, 0x44, 0x2e, 0xeb, 0xf2, - 0x0d, 0x59, 0x57, 0xf6, 0x66, 0x5d, 0xbd, 0x29, 0xeb, 0xda, 0x07, 0x67, 0xfd, 0x00, 0xc0, 0xe3, - 0x70, 0x3d, 0xfb, 0x62, 0xc3, 0xd9, 0x92, 0xb0, 0x94, 0x6a, 0x4e, 0x36, 0x9a, 0x09, 0x28, 0xc9, - 0xe6, 0x65, 0x18, 0x8d, 0xde, 0x93, 0x52, 0x11, 0x79, 0x79, 0x07, 0xb9, 0xf6, 0x57, 0x19, 0x3a, - 0x43, 0x3f, 0x76, 0xc3, 0xc8, 0x13, 0x71, 0x3a, 0x50, 0xf6, 0xbd, 0xf4, 0xc0, 0x96, 0x7d, 0x8f, - 0xb7, 0x87, 0x68, 0x69, 0x29, 0x6d, 0xd8, 0xfb, 0x20, 0x51, 0x7f, 0x49, 0x62, 0xea, 0x2c, 0x57, - 0x82, 0x8e, 0x4c, 0x81, 0x7a, 0x70, 0x98, 0x09, 0xac, 0xfd, 0x88, 0x68, 0x94, 0x5d, 0x35, 0x3b, - 0x48, 0x69, 0x9d, 0x38, 0x3b, 0x12, 0x16, 0x22, 0xfa, 0x1c, 0xea, 0xce, 0x9a, 0xce, 0xc3, 0x88, - 0xa7, 0x2f, 0x3f, 0x7b, 0xb8, 0xa5, 0xad, 0x88, 0x57, 0xe7, 0x56, 0x38, 0xb5, 0x46, 0x5f, 0x82, - 0x14, 0x91, 0x4b, 0x12, 0x91, 0xc0, 0x4d, 0xba, 0x45, 0xce, 0x77, 0x4b, 0xd1, 0x15, 0x0b, 0x43, - 0xbc, 0xf5, 0x41, 0x43, 0x90, 0x1d, 0x4a, 0x1d, 0x77, 0xbe, 0x24, 0x01, 0x8d, 0xd5, 0x66, 0xb7, - 0xd2, 0x93, 0x9f, 0x69, 0x7b, 0x77, 0xcf, 0x4c, 0x71, 0xde, 0x4d, 0xfb, 0xbb, 0x04, 0x47, 0xef, - 0xc2, 0xf9, 0x2e, 0x76, 0x03, 0x67, 0x99, 0xb1, 0xcb, 0xd6, 0xe8, 0x09, 0xb4, 0x3d, 0x3f, 0x76, - 0x23, 0x7f, 0xe9, 0x07, 0x0e, 0x0d, 0xa3, 0x94, 0xe1, 0xa2, 0x12, 0xdd, 0x83, 0x66, 0xe0, 0xbb, - 0xd7, 0xdc, 0x3b, 0xa1, 0x37, 0x93, 0x59, 0x7d, 0x9c, 0xb7, 0x0e, 0x75, 0xa2, 0x59, 0xb4, 0x48, - 0x99, 0xdd, 0x2a, 0x50, 0x1f, 0x50, 0x22, 0xf0, 0x89, 0x79, 0x96, 0x4e, 0xb2, 0x3a, 0xef, 0xdd, - 0x77, 0xfc, 0x61, 0x3b, 0x2d, 0x42, 0xd7, 0x59, 0xb0, 0x60, 0x8d, 0x64, 0x27, 0x21, 0x6b, 0x21, - 0x1c, 0xef, 0x21, 0x95, 0x81, 0xc8, 0x1a, 0x2d, 0xcd, 0x38, 0x77, 0x66, 0xee, 0x83, 0xe4, 0xce, - 0x9d, 0x20, 0x20, 0x0b, 0x33, 0xeb, 0xcb, 0x4c, 0xc1, 0x1a, 0xe3, 0x6a, 0xed, 0x2f, 0x3c, 0x33, - 0x1b, 0xdd, 0xa9, 0xa8, 0xfd, 0x53, 0x02, 0x75, 0x5f, 0x0d, 0xfe, 0xc3, 0x6e, 0x01, 0xc2, 0x6e, - 0xf3, 0x23, 0x05, 0x2a, 0xeb, 0x68, 0x91, 0x6e, 0xc0, 0x96, 0x2c, 0xd3, 0x4b, 0x7f, 0x41, 0xc6, - 0x39, 0x4e, 0x85, 0xcc, 0xaa, 0xc2, 0xd6, 0x53, 0xff, 0x67, 0x72, 0xb2, 0xa1, 0x24, 0xe6, 0xbc, - 0x56, 0x71, 0x51, 0x89, 0xba, 0x90, 0x9f, 0x3c, 0xe9, 0xd9, 0xcd, 0xab, 0xf2, 0x97, 0x47, 0xa3, - 0x78, 0x79, 0xe4, 0x79, 0x6e, 0xee, 0xf0, 0xfc, 0xab, 0x04, 0x72, 0x6e, 0xd6, 0xed, 0x39, 0xed, - 0x85, 0x73, 0x59, 0xe6, 0x7f, 0x72, 0xe7, 0x52, 0x0c, 0xfa, 0x4a, 0x6e, 0xd0, 0x3f, 0x02, 0x39, - 0x22, 0xf1, 0x2a, 0x0c, 0x62, 0x62, 0xd3, 0x30, 0x4d, 0x1a, 0x84, 0xca, 0x0a, 0xd9, 0x2d, 0x4a, - 0x82, 0xd8, 0xe6, 0x6d, 0x96, 0x9e, 0x51, 0x12, 0xc4, 0x9c, 0x91, 0xdc, 0xb8, 0xac, 0x17, 0xc6, - 0xe5, 0xee, 0xe4, 0x6b, 0x7c, 0xf4, 0xbc, 0x6f, 0x7e, 0xcc, 0xbc, 0x47, 0xcf, 0xa1, 0x11, 0x27, - 0xef, 0x10, 0x55, 0xe2, 0x23, 0x40, 0xdd, 0x06, 0x28, 0x3e, 0x50, 0x5e, 0x1d, 0x60, 0x61, 0x8a, - 0xfa, 0x50, 0xf3, 0x59, 0xdb, 0xab, 0xc0, 0x7d, 0xee, 0xec, 0xbc, 0x2c, 0xb6, 0x1e, 0x89, 0x19, - 0xb3, 0x77, 0xd8, 0xa5, 0xac, 0xca, 0xbb, 0xf6, 0xf9, 0xcb, 0x9e, 0xd9, 0x73, 0x33, 0xf4, 0x10, - 0x24, 0x37, 0x5c, 0x2e, 0xd7, 0x81, 0x4f, 0x37, 0x6a, 0x8b, 0x95, 0xfe, 0xd5, 0x01, 0xde, 0xaa, - 0xd0, 0x00, 0x0e, 0xbd, 0xa4, 0xb1, 0xc5, 0x6b, 0x4b, 0x75, 0x77, 0xd1, 0x17, 0x3b, 0xff, 0xd5, - 0x01, 0xee, 0x78, 0xc5, 0xe9, 0x9d, 0x5d, 0x45, 0xed, 0xfc, 0x55, 0xf4, 0x18, 0x5a, 0x9e, 0x1f, - 0xaf, 0x16, 0xce, 0x26, 0x29, 0x64, 0x27, 0x69, 0xcb, 0x54, 0xc7, 0x8b, 0x79, 0x09, 0x0f, 0x63, - 0x46, 0x3b, 0xe3, 0xd1, 0x71, 0xa9, 0x1d, 0x91, 0x1f, 0xd6, 0x24, 0xa6, 0x76, 0xec, 0x5f, 0x05, - 0x0e, 0x5d, 0x47, 0x44, 0x3d, 0xdc, 0x9d, 0xa6, 0x83, 0xc4, 0x14, 0x27, 0x96, 0x53, 0x61, 0x88, - 0xff, 0xcf, 0x02, 0xed, 0xf9, 0x89, 0x02, 0xd0, 0x22, 0xe2, 0x12, 0xff, 0x2d, 0xf1, 0x6e, 0xd8, - 0x4b, 0xf9, 0xd0, 0xbd, 0x1e, 0x89, 0x60, 0xfb, 0xf6, 0x7b, 0x0a, 0x87, 0x62, 0x1b, 0xc1, 0xea, - 0xff, 0xba, 0xa5, 0x5e, 0x13, 0x77, 0x52, 0x75, 0xca, 0x9c, 0xf6, 0x7b, 0x19, 0xe4, 0x41, 0xe1, - 0x9c, 0x1e, 0x89, 0x67, 0xd6, 0x60, 0x32, 0xb6, 0x8c, 0xb1, 0x25, 0x1e, 0x5a, 0x1d, 0x00, 0xcb, - 0xf8, 0xce, 0xb2, 0xcf, 0xbe, 0xd6, 0xcd, 0xb1, 0x52, 0x42, 0x32, 0x34, 0xa6, 0x96, 0x39, 0x78, - 0x6d, 0x60, 0xa5, 0x8c, 0x00, 0xea, 0x53, 0x4b, 0xb7, 0x66, 0x53, 0xa5, 0x82, 0x24, 0xa8, 0x19, - 0xa3, 0xc9, 0x57, 0xa6, 0x52, 0x45, 0xc7, 0x70, 0xcb, 0xc2, 0xfa, 0x78, 0xaa, 0x0f, 0x2c, 0x73, - 0xc2, 0x22, 0x8e, 0x46, 0xfa, 0x78, 0xa8, 0xd4, 0x50, 0x0f, 0x9e, 0x4c, 0xcf, 0xa7, 0x96, 0x31, - 0xb2, 0x47, 0xc6, 0x74, 0xaa, 0x9f, 0x1a, 0xd9, 0x6e, 0x67, 0xd8, 0x7c, 0xa3, 0x5b, 0x86, 0x7d, - 0x8a, 0x27, 0xb3, 0x33, 0xa5, 0xce, 0xa2, 0x99, 0x23, 0xfd, 0xd4, 0x50, 0x1a, 0x6c, 0xc9, 0x9f, - 0x7e, 0x4a, 0x13, 0xb5, 0x41, 0x62, 0xc1, 0x66, 0x63, 0xd3, 0x3a, 0x57, 0x24, 0xf6, 0x38, 0xdc, - 0x09, 0x77, 0xaa, 0x9f, 0x29, 0x80, 0x6e, 0xc1, 0x21, 0x8b, 0xab, 0x0f, 0x2c, 0x1b, 0x1b, 0xdf, - 0xcc, 0x8c, 0xa9, 0xa5, 0xc8, 0x4c, 0x39, 0x34, 0xa7, 0x83, 0x09, 0x1e, 0x0a, 0x6b, 0xa5, 0x85, - 0xee, 0xc2, 0x6d, 0x73, 0x68, 0x8c, 0x2d, 0xd3, 0x3a, 0xb7, 0xdf, 0x18, 0xd8, 0x7c, 0x69, 0x0e, - 0x74, 0x86, 0x59, 0x69, 0x9f, 0x48, 0xd9, 0xe8, 0xd2, 0x66, 0x70, 0xbc, 0x8f, 0xf1, 0xfb, 0x20, - 0x6d, 0x0b, 0x99, 0xbc, 0x8f, 0xb7, 0x8a, 0x9b, 0x47, 0xd4, 0x49, 0xfb, 0x7b, 0xb9, 0xff, 0xc9, - 0x17, 0xa2, 0xea, 0x17, 0x75, 0xbe, 0xfa, 0xf4, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x61, - 0xf8, 0x08, 0x84, 0x0c, 0x00, 0x00, + // 1257 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcb, 0x8e, 0xdb, 0x36, + 0x17, 0x1e, 0x7b, 0x7c, 0xd3, 0x91, 0xed, 0x11, 0x98, 0x49, 0xa2, 0x04, 0xb9, 0x38, 0x42, 0x80, + 0xcc, 0xca, 0x3f, 0x90, 0x3f, 0x2d, 0x02, 0x74, 0x51, 0x68, 0x6c, 0x65, 0xa2, 0xa6, 0xbe, 0x94, + 0x92, 0xd3, 0x4e, 0x37, 0x02, 0x47, 0x62, 0xc6, 0xc2, 0x58, 0x92, 0x2b, 0xd1, 0x6d, 0xdd, 0x7d, + 0x5f, 0xa2, 0xab, 0x3e, 0x45, 0x9f, 0xa2, 0xdb, 0xbe, 0x42, 0x9f, 0xa0, 0xeb, 0xa2, 0x20, 0x75, + 0xb1, 0xe4, 0x66, 0x26, 0x45, 0x56, 0xe2, 0x39, 0x3a, 0x87, 0xfc, 0xce, 0xc7, 0x8f, 0x87, 0x04, + 0xe4, 0x2e, 0x09, 0x73, 0x02, 0x9a, 0x24, 0xe4, 0x92, 0x0e, 0xd7, 0x71, 0xc4, 0x22, 0xd4, 0x11, + 0x9f, 0x8b, 0xcd, 0xbb, 0xfb, 0x32, 0x0d, 0x37, 0x41, 0x92, 0xba, 0xef, 0xf7, 0xdc, 0x28, 0x64, + 0xc4, 0x65, 0xa9, 0xa9, 0xbd, 0x84, 0xbe, 0xc5, 0x7c, 0xf7, 0x8a, 0xc6, 0x93, 0x34, 0x1b, 0x21, + 0x68, 0x2c, 0x49, 0xb2, 0x54, 0x6b, 0x83, 0xda, 0x89, 0x84, 0xc5, 0x98, 0xfb, 0xd6, 0xc4, 0xbd, + 0x52, 0xeb, 0x83, 0xda, 0x49, 0x13, 0x8b, 0xb1, 0xf6, 0x4b, 0x0d, 0xba, 0x66, 0x40, 0x2e, 0x69, + 0x9e, 0xa8, 0x42, 0x7b, 0x4d, 0xb6, 0xab, 0x88, 0x78, 0x22, 0xb7, 0x8b, 0x73, 0x13, 0x3d, 0x83, + 0x06, 0xdb, 0xae, 0xa9, 0x48, 0xef, 0x3f, 0xbf, 0x35, 0xcc, 0x91, 0x0d, 0x45, 0xbe, 0xbd, 0x5d, + 0x53, 0x2c, 0x02, 0xd0, 0x3d, 0xe8, 0x90, 0xd5, 0xc5, 0x26, 0x70, 0x7c, 0x4f, 0x3d, 0x14, 0xeb, + 0xb7, 0x85, 0x6d, 0x7a, 0xe8, 0x18, 0x9a, 0x3f, 0xf8, 0x1e, 0x5b, 0xaa, 0x8d, 0x41, 0xed, 0xa4, + 0x87, 0x53, 0x03, 0xdd, 0x81, 0xd6, 0x92, 0xfa, 0x97, 0x4b, 0xa6, 0x36, 0x85, 0x3b, 0xb3, 0xb4, + 0xdf, 0x6a, 0xd0, 0xd5, 0x37, 0x9e, 0x1f, 0x7d, 0x18, 0xdc, 0x8b, 0x0a, 0xb8, 0xc1, 0x0e, 0x5c, + 0x39, 0x3f, 0x35, 0x4a, 0x48, 0x1f, 0x83, 0xec, 0x6d, 0x62, 0xc2, 0xfc, 0x28, 0x74, 0x82, 0x44, + 0x80, 0x6d, 0x60, 0xc8, 0x5d, 0x93, 0x44, 0xfb, 0x04, 0xa4, 0x22, 0x07, 0xdd, 0x01, 0xb4, 0x98, + 0xbe, 0x99, 0xce, 0xbe, 0x9e, 0x3a, 0xfa, 0x62, 0x6c, 0xce, 0x1c, 0xfb, 0x7c, 0x6e, 0x28, 0x07, + 0xa8, 0x0d, 0x87, 0xba, 0x3e, 0x52, 0x6a, 0x62, 0x30, 0xc1, 0x4a, 0x5d, 0xfb, 0xb9, 0x0e, 0xb2, + 0xe1, 0xf9, 0x2c, 0xc7, 0x7d, 0x0c, 0x4d, 0x77, 0x15, 0xb9, 0x57, 0x02, 0x75, 0x03, 0xa7, 0x06, + 0xdf, 0x0f, 0x46, 0x7f, 0x64, 0x02, 0xb3, 0x84, 0xc5, 0x18, 0xdd, 0x85, 0xb6, 0x50, 0x41, 0x41, + 0x5d, 0x8b, 0x9b, 0xa6, 0x87, 0x1e, 0x02, 0x64, 0xca, 0xe0, 0xff, 0x1a, 0xe2, 0x9f, 0x94, 0x79, + 0x52, 0x62, 0x2f, 0x63, 0x12, 0xa6, 0x0c, 0x76, 0x71, 0x6a, 0xa0, 0x97, 0xd0, 0xcd, 0x93, 0x04, + 0x3b, 0x2d, 0xc1, 0xce, 0xed, 0x1d, 0x3b, 0x19, 0x40, 0x41, 0x89, 0x1c, 0xec, 0x0c, 0x34, 0x86, + 0x2e, 0x97, 0x18, 0x0d, 0x59, 0x9a, 0xd9, 0x16, 0x99, 0x4f, 0x76, 0x99, 0xa3, 0x25, 0xc9, 0xcb, + 0x1b, 0x8e, 0xd2, 0xc8, 0x74, 0x16, 0x77, 0x67, 0x68, 0xbf, 0xd7, 0xa0, 0x37, 0xa6, 0x2b, 0xca, + 0xe8, 0xcd, 0x4c, 0x94, 0xaa, 0xae, 0xdf, 0x50, 0xf5, 0xe1, 0xb5, 0x55, 0x37, 0x6e, 0xaa, 0xba, + 0xf9, 0x9f, 0xab, 0x7e, 0x08, 0xe0, 0x09, 0xb8, 0x9e, 0x73, 0xb1, 0x15, 0x6c, 0x49, 0x58, 0xca, + 0x3c, 0xa7, 0x5b, 0xcd, 0x04, 0x94, 0x56, 0xf3, 0x2a, 0x8a, 0x27, 0x1f, 0x28, 0xa9, 0x8a, 0xbc, + 0xbe, 0x87, 0x5c, 0xfb, 0xa3, 0x0e, 0xfd, 0xb1, 0x9f, 0xb8, 0x51, 0xec, 0xe5, 0xf3, 0xf4, 0xa1, + 0xee, 0x7b, 0xd9, 0x81, 0xad, 0xfb, 0x9e, 0x90, 0x47, 0x2e, 0x69, 0x29, 0x13, 0xec, 0x03, 0x90, + 0x98, 0x1f, 0xd0, 0x84, 0x91, 0x60, 0x9d, 0xd3, 0x51, 0x38, 0xd0, 0x09, 0x1c, 0x15, 0x06, 0x97, + 0x1f, 0xcd, 0x85, 0xb2, 0xef, 0xe6, 0x07, 0x29, 0xdb, 0x27, 0xc1, 0x8e, 0x84, 0x73, 0x13, 0x7d, + 0x0a, 0x2d, 0xb2, 0x61, 0xcb, 0x28, 0x16, 0xe5, 0xcb, 0xcf, 0x1f, 0xed, 0x68, 0xab, 0xe2, 0xd5, + 0x45, 0x14, 0xce, 0xa2, 0xd1, 0xe7, 0x20, 0xc5, 0xf4, 0x1d, 0x8d, 0x69, 0xe8, 0xa6, 0x6a, 0x91, + 0xcb, 0x6a, 0xa9, 0xa6, 0xe2, 0x3c, 0x10, 0xef, 0x72, 0xd0, 0x18, 0x64, 0xc2, 0x18, 0x71, 0x97, + 0x01, 0x0d, 0x59, 0xa2, 0x76, 0x06, 0x87, 0x27, 0xf2, 0x73, 0xed, 0xda, 0xd5, 0x8b, 0x50, 0x5c, + 0x4e, 0xd3, 0xfe, 0xac, 0xc1, 0xf1, 0xfb, 0x70, 0xbe, 0x8f, 0xdd, 0x90, 0x04, 0x05, 0xbb, 0x7c, + 0x8c, 0x9e, 0x42, 0xcf, 0xf3, 0x13, 0x37, 0xf6, 0x03, 0x3f, 0x24, 0x2c, 0x8a, 0x33, 0x86, 0xab, + 0x4e, 0x74, 0x1f, 0x3a, 0xa1, 0xef, 0x5e, 0x89, 0xec, 0x94, 0xde, 0xc2, 0xe6, 0xfb, 0x43, 0xbe, + 0x27, 0x8c, 0xc4, 0x8b, 0x78, 0x95, 0x31, 0xbb, 0x73, 0xa0, 0x21, 0xa0, 0xd4, 0x10, 0x1d, 0x73, + 0x9e, 0x75, 0xb2, 0x96, 0xd0, 0xee, 0x7b, 0xfe, 0xf0, 0x95, 0x56, 0x91, 0x4b, 0x56, 0x7c, 0xb2, + 0x76, 0xba, 0x52, 0x6e, 0x6b, 0x11, 0xdc, 0xbd, 0x86, 0x54, 0x0e, 0xa2, 0x10, 0x5a, 0x56, 0x71, + 0xe9, 0xcc, 0x3c, 0x00, 0xc9, 0x5d, 0x92, 0x30, 0xa4, 0x2b, 0xb3, 0xd0, 0x65, 0xe1, 0xe0, 0xc2, + 0xb8, 0xdc, 0xf8, 0x2b, 0xcf, 0x2c, 0x5a, 0x77, 0x66, 0x6a, 0x7f, 0xd5, 0x40, 0xbd, 0x6e, 0x0f, + 0xfe, 0xc5, 0x6e, 0x05, 0xc2, 0xbe, 0xf8, 0x91, 0x02, 0x87, 0x9b, 0x78, 0x95, 0x2d, 0xc0, 0x87, + 0xbc, 0xd2, 0x77, 0xfe, 0x8a, 0x4e, 0x4b, 0x9c, 0xe6, 0x36, 0xdf, 0x15, 0x3e, 0xb6, 0xfc, 0x9f, + 0xe8, 0xe9, 0x96, 0xd1, 0x44, 0xf0, 0xda, 0xc0, 0x55, 0x27, 0x1a, 0x40, 0xb9, 0xf3, 0x64, 0x67, + 0xb7, 0xec, 0x2a, 0x5f, 0x1e, 0xed, 0xea, 0xe5, 0x51, 0xe6, 0xb9, 0xb3, 0xc7, 0xf3, 0xdf, 0x6d, + 0x90, 0x4b, 0xbd, 0xee, 0x9a, 0xd3, 0x5e, 0x39, 0x97, 0x75, 0xf1, 0xa7, 0x74, 0x2e, 0xf3, 0x46, + 0x7f, 0x58, 0x6a, 0xf4, 0x8f, 0x41, 0x8e, 0x69, 0xb2, 0x8e, 0xc2, 0x84, 0x3a, 0x2c, 0xca, 0x8a, + 0x86, 0xdc, 0x65, 0x47, 0xfc, 0x16, 0xa5, 0x61, 0xe2, 0x08, 0x99, 0x65, 0x67, 0x94, 0x86, 0x89, + 0x60, 0xa4, 0xd4, 0x2e, 0x5b, 0x95, 0x76, 0xb9, 0xdf, 0xf9, 0xda, 0x1f, 0xdd, 0xef, 0x3b, 0x1f, + 0xd3, 0xef, 0xd1, 0x0b, 0x68, 0x27, 0xe9, 0x3b, 0x44, 0x95, 0x44, 0x0b, 0x50, 0x77, 0x13, 0x54, + 0x1f, 0x28, 0xaf, 0x0f, 0x70, 0x1e, 0x8a, 0x86, 0xd0, 0xf4, 0xb9, 0xec, 0x55, 0x10, 0x39, 0x77, + 0xf6, 0x5e, 0x16, 0xbb, 0x8c, 0x34, 0x8c, 0xc7, 0x13, 0x7e, 0x29, 0xab, 0xf2, 0x7e, 0x7c, 0xf9, + 0xb2, 0xe7, 0xf1, 0x22, 0x0c, 0x3d, 0x02, 0xc9, 0x8d, 0x82, 0x60, 0x13, 0xfa, 0x6c, 0xab, 0x76, + 0xf9, 0xd6, 0xbf, 0x3e, 0xc0, 0x3b, 0x17, 0x1a, 0xc1, 0x91, 0x97, 0x0a, 0x3b, 0x7f, 0x7c, 0xa9, + 0xee, 0x3e, 0xfa, 0xaa, 0xf2, 0x5f, 0x1f, 0xe0, 0xbe, 0x57, 0xed, 0xde, 0xc5, 0x55, 0xd4, 0x2b, + 0x5f, 0x45, 0x4f, 0xa0, 0xeb, 0xf9, 0xc9, 0x7a, 0x45, 0xb6, 0xe9, 0x46, 0xf6, 0x53, 0x59, 0x66, + 0x3e, 0xb1, 0x99, 0x6b, 0x18, 0x64, 0x8f, 0x39, 0x27, 0xa6, 0xdf, 0x6d, 0x68, 0xc2, 0x9c, 0x75, + 0x1c, 0xad, 0xc9, 0x25, 0xe1, 0xd7, 0x50, 0xc2, 0x08, 0xa3, 0xea, 0x91, 0x80, 0xf3, 0xac, 0xb4, + 0x1b, 0x69, 0x06, 0x4e, 0x13, 0xe6, 0x45, 0xbc, 0xc5, 0xc3, 0xf1, 0x43, 0xf7, 0xa6, 0xdf, 0xda, + 0xaf, 0x75, 0x90, 0x47, 0x95, 0x83, 0x71, 0x9c, 0xbf, 0x6b, 0x46, 0xb3, 0xa9, 0x6d, 0x4c, 0xed, + 0xfc, 0x65, 0xd3, 0x07, 0xb0, 0x8d, 0x6f, 0x6c, 0x67, 0xfe, 0xa5, 0x6e, 0x4e, 0x95, 0x1a, 0x92, + 0xa1, 0x6d, 0xd9, 0xe6, 0xe8, 0x8d, 0x81, 0x95, 0x3a, 0x02, 0x68, 0x59, 0xb6, 0x6e, 0x2f, 0x2c, + 0xe5, 0x10, 0x49, 0xd0, 0x34, 0x26, 0xb3, 0x2f, 0x4c, 0xa5, 0x81, 0xee, 0xc2, 0x2d, 0x1b, 0xeb, + 0x53, 0x4b, 0x1f, 0xd9, 0xe6, 0x8c, 0xcf, 0x38, 0x99, 0xe8, 0xd3, 0xb1, 0xd2, 0x44, 0x27, 0xf0, + 0xd4, 0x3a, 0xb7, 0x6c, 0x63, 0xe2, 0x4c, 0x0c, 0xcb, 0xd2, 0xcf, 0x8c, 0x62, 0xb5, 0x39, 0x36, + 0xdf, 0xea, 0xb6, 0xe1, 0x9c, 0xe1, 0xd9, 0x62, 0xae, 0xb4, 0xf8, 0x6c, 0xe6, 0x44, 0x3f, 0x33, + 0x94, 0x36, 0x1f, 0x8a, 0xb7, 0x96, 0xd2, 0x41, 0x3d, 0x90, 0xf8, 0x64, 0x8b, 0xa9, 0x69, 0x9f, + 0x2b, 0x12, 0x7f, 0x8d, 0xed, 0x4d, 0x77, 0xa6, 0xcf, 0x15, 0x40, 0xb7, 0xe0, 0x88, 0xcf, 0xab, + 0x8f, 0x6c, 0x07, 0x1b, 0x5f, 0x2d, 0x0c, 0xcb, 0x56, 0x64, 0xee, 0x1c, 0x9b, 0xd6, 0x68, 0x86, + 0xc7, 0x79, 0xb4, 0xd2, 0x45, 0xf7, 0xe0, 0xb6, 0x39, 0x36, 0xa6, 0xb6, 0x69, 0x9f, 0x3b, 0x6f, + 0x0d, 0x6c, 0xbe, 0x32, 0x47, 0x3a, 0xc7, 0xac, 0xf4, 0x4e, 0xa5, 0xa2, 0x57, 0x9c, 0xf6, 0xbe, + 0x95, 0x87, 0xff, 0xfb, 0x2c, 0x67, 0xfe, 0xa2, 0x25, 0x46, 0xff, 0xff, 0x27, 0x00, 0x00, 0xff, + 0xff, 0xbb, 0x9e, 0xc6, 0x61, 0xad, 0x0b, 0x00, 0x00, } diff --git a/protocol/protobuf/chat_message.proto b/protocol/protobuf/chat_message.proto index 6c2a38b2f..847a9e847 100644 --- a/protocol/protobuf/chat_message.proto +++ b/protocol/protobuf/chat_message.proto @@ -4,6 +4,7 @@ option go_package = "./;protobuf"; package protobuf; import "enums.proto"; +import "contact.proto"; message StickerMessage { string hash = 1; @@ -142,9 +143,7 @@ message ChatMessage { // Message author's display name, introduced in version 1 string display_name = 14; - ContactRequestSignature sent_contact_request_signature = 15; - ContactRequestSignature received_contact_request_signature = 16; - bool contact_message = 17; + ContactRequestPropagatedState contact_request_propagated_state = 15; enum ContentType { UNKNOWN_CONTENT_TYPE = 0; @@ -165,8 +164,3 @@ message ChatMessage { IDENTITY_VERIFICATION = 13; } } - -message ContactRequestSignature { - bytes signature = 1; - uint64 timestamp = 2; -} diff --git a/protocol/protobuf/contact.pb.go b/protocol/protobuf/contact.pb.go index 64a73197e..e7cf6c41e 100644 --- a/protocol/protobuf/contact.pb.go +++ b/protocol/protobuf/contact.pb.go @@ -20,24 +20,86 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +type ContactRequestPropagatedState struct { + LocalClock uint64 `protobuf:"varint,1,opt,name=local_clock,json=localClock,proto3" json:"local_clock,omitempty"` + LocalState uint64 `protobuf:"varint,2,opt,name=local_state,json=localState,proto3" json:"local_state,omitempty"` + RemoteClock uint64 `protobuf:"varint,3,opt,name=remote_clock,json=remoteClock,proto3" json:"remote_clock,omitempty"` + RemoteState uint64 `protobuf:"varint,4,opt,name=remote_state,json=remoteState,proto3" json:"remote_state,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContactRequestPropagatedState) Reset() { *m = ContactRequestPropagatedState{} } +func (m *ContactRequestPropagatedState) String() string { return proto.CompactTextString(m) } +func (*ContactRequestPropagatedState) ProtoMessage() {} +func (*ContactRequestPropagatedState) Descriptor() ([]byte, []int) { + return fileDescriptor_a5036fff2565fb15, []int{0} +} + +func (m *ContactRequestPropagatedState) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ContactRequestPropagatedState.Unmarshal(m, b) +} +func (m *ContactRequestPropagatedState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ContactRequestPropagatedState.Marshal(b, m, deterministic) +} +func (m *ContactRequestPropagatedState) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContactRequestPropagatedState.Merge(m, src) +} +func (m *ContactRequestPropagatedState) XXX_Size() int { + return xxx_messageInfo_ContactRequestPropagatedState.Size(m) +} +func (m *ContactRequestPropagatedState) XXX_DiscardUnknown() { + xxx_messageInfo_ContactRequestPropagatedState.DiscardUnknown(m) +} + +var xxx_messageInfo_ContactRequestPropagatedState proto.InternalMessageInfo + +func (m *ContactRequestPropagatedState) GetLocalClock() uint64 { + if m != nil { + return m.LocalClock + } + return 0 +} + +func (m *ContactRequestPropagatedState) GetLocalState() uint64 { + if m != nil { + return m.LocalState + } + return 0 +} + +func (m *ContactRequestPropagatedState) GetRemoteClock() uint64 { + if m != nil { + return m.RemoteClock + } + return 0 +} + +func (m *ContactRequestPropagatedState) GetRemoteState() uint64 { + if m != nil { + return m.RemoteState + } + return 0 +} + type ContactUpdate struct { - Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` - EnsName string `protobuf:"bytes,2,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` - ProfileImage string `protobuf:"bytes,3,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"` - DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - ContactRequestClock uint64 `protobuf:"varint,5,opt,name=contact_request_clock,json=contactRequestClock,proto3" json:"contact_request_clock,omitempty"` - SentContactRequestSignature *ContactRequestSignature `protobuf:"bytes,14,opt,name=sent_contact_request_signature,json=sentContactRequestSignature,proto3" json:"sent_contact_request_signature,omitempty"` - ReceivedContactRequestSignature *ContactRequestSignature `protobuf:"bytes,15,opt,name=received_contact_request_signature,json=receivedContactRequestSignature,proto3" json:"received_contact_request_signature,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Clock uint64 `protobuf:"varint,1,opt,name=clock,proto3" json:"clock,omitempty"` + EnsName string `protobuf:"bytes,2,opt,name=ens_name,json=ensName,proto3" json:"ens_name,omitempty"` + ProfileImage string `protobuf:"bytes,3,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"` + DisplayName string `protobuf:"bytes,4,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + ContactRequestClock uint64 `protobuf:"varint,5,opt,name=contact_request_clock,json=contactRequestClock,proto3" json:"contact_request_clock,omitempty"` + ContactRequestPropagatedState *ContactRequestPropagatedState `protobuf:"bytes,6,opt,name=contact_request_propagated_state,json=contactRequestPropagatedState,proto3" json:"contact_request_propagated_state,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ContactUpdate) Reset() { *m = ContactUpdate{} } func (m *ContactUpdate) String() string { return proto.CompactTextString(m) } func (*ContactUpdate) ProtoMessage() {} func (*ContactUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_a5036fff2565fb15, []int{0} + return fileDescriptor_a5036fff2565fb15, []int{1} } func (m *ContactUpdate) XXX_Unmarshal(b []byte) error { @@ -93,16 +155,9 @@ func (m *ContactUpdate) GetContactRequestClock() uint64 { return 0 } -func (m *ContactUpdate) GetSentContactRequestSignature() *ContactRequestSignature { +func (m *ContactUpdate) GetContactRequestPropagatedState() *ContactRequestPropagatedState { if m != nil { - return m.SentContactRequestSignature - } - return nil -} - -func (m *ContactUpdate) GetReceivedContactRequestSignature() *ContactRequestSignature { - if m != nil { - return m.ReceivedContactRequestSignature + return m.ContactRequestPropagatedState } return nil } @@ -119,7 +174,7 @@ func (m *AcceptContactRequest) Reset() { *m = AcceptContactRequest{} } func (m *AcceptContactRequest) String() string { return proto.CompactTextString(m) } func (*AcceptContactRequest) ProtoMessage() {} func (*AcceptContactRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a5036fff2565fb15, []int{1} + return fileDescriptor_a5036fff2565fb15, []int{2} } func (m *AcceptContactRequest) XXX_Unmarshal(b []byte) error { @@ -166,7 +221,7 @@ func (m *RetractContactRequest) Reset() { *m = RetractContactRequest{} } func (m *RetractContactRequest) String() string { return proto.CompactTextString(m) } func (*RetractContactRequest) ProtoMessage() {} func (*RetractContactRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_a5036fff2565fb15, []int{2} + return fileDescriptor_a5036fff2565fb15, []int{3} } func (m *RetractContactRequest) XXX_Unmarshal(b []byte) error { @@ -202,6 +257,7 @@ func (m *RetractContactRequest) GetClock() uint64 { } func init() { + proto.RegisterType((*ContactRequestPropagatedState)(nil), "protobuf.ContactRequestPropagatedState") proto.RegisterType((*ContactUpdate)(nil), "protobuf.ContactUpdate") proto.RegisterType((*AcceptContactRequest)(nil), "protobuf.AcceptContactRequest") proto.RegisterType((*RetractContactRequest)(nil), "protobuf.RetractContactRequest") @@ -212,25 +268,26 @@ func init() { } var fileDescriptor_a5036fff2565fb15 = []byte{ - // 311 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x51, 0x31, 0x4f, 0xf3, 0x30, - 0x14, 0x54, 0xd2, 0xf6, 0xfb, 0xda, 0xd7, 0xa6, 0x48, 0xa6, 0x95, 0x02, 0x48, 0xd0, 0x86, 0xa5, - 0x53, 0x90, 0xca, 0x08, 0x0c, 0xd0, 0x89, 0x85, 0xc1, 0x88, 0x85, 0xc5, 0x72, 0x9d, 0xd7, 0x62, - 0xd1, 0x38, 0xc1, 0x76, 0x91, 0xf8, 0x1f, 0xfc, 0x60, 0x54, 0x3b, 0x11, 0x50, 0xa9, 0x08, 0x31, - 0x25, 0xbe, 0xbb, 0x77, 0xef, 0xec, 0x83, 0x48, 0x14, 0xca, 0x72, 0x61, 0xd3, 0x52, 0x17, 0xb6, - 0x20, 0x6d, 0xf7, 0x99, 0xaf, 0x17, 0x87, 0x44, 0x3c, 0x71, 0xcb, 0x72, 0x34, 0x86, 0x2f, 0xd1, - 0xb3, 0xc9, 0x7b, 0x03, 0xa2, 0x99, 0xd7, 0x3f, 0x94, 0x19, 0xb7, 0x48, 0x06, 0xd0, 0x12, 0xab, - 0x42, 0x3c, 0xc7, 0xc1, 0x28, 0x98, 0x34, 0xa9, 0x3f, 0x90, 0x03, 0x68, 0xa3, 0x32, 0x4c, 0xf1, - 0x1c, 0xe3, 0x70, 0x14, 0x4c, 0x3a, 0xf4, 0x3f, 0x2a, 0x73, 0xc7, 0x73, 0x24, 0xa7, 0x10, 0x95, - 0xba, 0x58, 0xc8, 0x15, 0x32, 0x99, 0xf3, 0x25, 0xc6, 0x0d, 0xc7, 0xf7, 0x2a, 0xf0, 0x76, 0x83, - 0x91, 0x31, 0xf4, 0x32, 0x69, 0xca, 0x15, 0x7f, 0xf3, 0x1e, 0x4d, 0xa7, 0xe9, 0x56, 0x98, 0xf3, - 0x99, 0xc2, 0xb0, 0x4a, 0xce, 0x34, 0xbe, 0xac, 0xd1, 0x58, 0xe6, 0x83, 0xb4, 0x5c, 0x90, 0xfd, - 0x8a, 0xa4, 0x9e, 0x9b, 0xb9, 0x58, 0x0b, 0x38, 0x36, 0xa8, 0x2c, 0xdb, 0x1e, 0x34, 0x72, 0xa9, - 0xb8, 0x5d, 0x6b, 0x8c, 0xfb, 0xa3, 0x60, 0xd2, 0x9d, 0x8e, 0xd3, 0xfa, 0x15, 0xd2, 0xd9, 0x37, - 0x9b, 0xfb, 0x5a, 0x48, 0x8f, 0x36, 0x46, 0x3b, 0x48, 0xa2, 0x20, 0xd1, 0x28, 0x50, 0xbe, 0x62, - 0xf6, 0xc3, 0xae, 0xbd, 0xdf, 0xee, 0x3a, 0xa9, 0xcd, 0x76, 0x08, 0x92, 0x4b, 0x18, 0x5c, 0x0b, - 0x81, 0xe5, 0x56, 0x20, 0xd2, 0x87, 0x50, 0x66, 0xae, 0x99, 0x0e, 0x0d, 0x65, 0xf6, 0x59, 0x56, - 0xf8, 0xa5, 0xac, 0xe4, 0x0a, 0x86, 0x14, 0xad, 0xe6, 0xe2, 0x4f, 0xe3, 0x37, 0xd1, 0x63, 0x37, - 0x3d, 0xbb, 0xa8, 0x2f, 0x31, 0xff, 0xe7, 0xfe, 0xce, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x8b, - 0xda, 0xbb, 0xda, 0x58, 0x02, 0x00, 0x00, + // 329 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x91, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0x95, 0xfc, 0x6d, 0xff, 0xe6, 0xa6, 0x61, 0x08, 0xad, 0x54, 0x86, 0x8a, 0x10, 0x06, + 0x3a, 0x05, 0xa9, 0x8c, 0xc0, 0x00, 0x9d, 0x58, 0x10, 0x32, 0x62, 0x61, 0x89, 0x5c, 0xe7, 0xb6, + 0x8a, 0x48, 0x62, 0xe3, 0xb8, 0x03, 0x4f, 0xc4, 0xc6, 0x33, 0xa2, 0xd8, 0x2e, 0x49, 0x19, 0x3a, + 0x30, 0x25, 0x3e, 0x3e, 0xfe, 0x74, 0xee, 0xb9, 0x10, 0x30, 0x5e, 0x29, 0xca, 0x54, 0x22, 0x24, + 0x57, 0x3c, 0x1c, 0xea, 0xcf, 0x6a, 0xbb, 0x8e, 0x3f, 0x1d, 0x98, 0x2d, 0xcd, 0x1d, 0xc1, 0xf7, + 0x2d, 0xd6, 0xea, 0x49, 0x72, 0x41, 0x37, 0x54, 0x61, 0xf6, 0xac, 0xa8, 0xc2, 0xf0, 0x14, 0xfc, + 0x82, 0x33, 0x5a, 0xa4, 0xac, 0xe0, 0xec, 0x6d, 0xea, 0x44, 0xce, 0xbc, 0x47, 0x40, 0x4b, 0xcb, + 0x46, 0x69, 0x0d, 0x75, 0xe3, 0x9f, 0xba, 0x1d, 0x83, 0x21, 0x9c, 0xc1, 0x48, 0x62, 0xc9, 0x15, + 0x5a, 0xc4, 0x3f, 0xed, 0xf0, 0x8d, 0x66, 0x18, 0xad, 0xc5, 0x40, 0x7a, 0x5d, 0x8b, 0xa6, 0xc4, + 0x5f, 0x2e, 0x04, 0x36, 0xe9, 0x8b, 0xc8, 0x1a, 0xee, 0x18, 0xfa, 0xdd, 0x4c, 0xe6, 0x10, 0x9e, + 0xc0, 0x10, 0xab, 0x3a, 0xad, 0x68, 0x69, 0xb2, 0x78, 0xe4, 0x3f, 0x56, 0xf5, 0x23, 0x2d, 0x31, + 0x3c, 0x87, 0x40, 0x48, 0xbe, 0xce, 0x0b, 0x4c, 0xf3, 0x92, 0x6e, 0x50, 0x27, 0xf1, 0xc8, 0xc8, + 0x8a, 0x0f, 0x8d, 0xd6, 0x44, 0xc9, 0xf2, 0x5a, 0x14, 0xf4, 0xc3, 0x30, 0x7a, 0xda, 0xe3, 0x5b, + 0x4d, 0x73, 0x16, 0x30, 0xb1, 0x7d, 0xa6, 0xd2, 0x94, 0x66, 0x27, 0xeb, 0xeb, 0x20, 0xc7, 0x6c, + 0xaf, 0x50, 0x33, 0xa1, 0x80, 0xe8, 0xf7, 0x1b, 0xf1, 0xd3, 0xb4, 0x9d, 0x7a, 0x10, 0x39, 0x73, + 0x7f, 0x71, 0x91, 0xec, 0xb6, 0x93, 0x1c, 0xdc, 0x0c, 0x99, 0xb1, 0x43, 0xd7, 0xf1, 0x0d, 0x8c, + 0xef, 0x18, 0x43, 0xa1, 0xf6, 0x29, 0xe1, 0x11, 0xb8, 0x79, 0xa6, 0x3b, 0xf3, 0x88, 0x9b, 0x67, + 0x6d, 0x8d, 0x6e, 0xa7, 0xc6, 0xf8, 0x16, 0x26, 0x04, 0x95, 0xa4, 0xec, 0x4f, 0xcf, 0xef, 0x83, + 0x57, 0x3f, 0xb9, 0xbc, 0xde, 0x0d, 0xb2, 0x1a, 0xe8, 0xbf, 0xab, 0xef, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xa0, 0x8b, 0xa7, 0x4a, 0x88, 0x02, 0x00, 0x00, } diff --git a/protocol/protobuf/contact.proto b/protocol/protobuf/contact.proto index cf2008ae7..e5c862cd2 100644 --- a/protocol/protobuf/contact.proto +++ b/protocol/protobuf/contact.proto @@ -2,7 +2,13 @@ syntax = "proto3"; option go_package = "./;protobuf"; package protobuf; -import "chat_message.proto"; + +message ContactRequestPropagatedState { + uint64 local_clock = 1; + uint64 local_state = 2; + uint64 remote_clock = 3; + uint64 remote_state = 4; +} message ContactUpdate { uint64 clock = 1; @@ -10,9 +16,7 @@ message ContactUpdate { string profile_image = 3; string display_name = 4; uint64 contact_request_clock = 5; - - ContactRequestSignature sent_contact_request_signature = 14; - ContactRequestSignature received_contact_request_signature = 15; + ContactRequestPropagatedState contact_request_propagated_state = 6; } message AcceptContactRequest {