From c38439e66427d9bd29ca203d190dc0187a5d5b2d Mon Sep 17 00:00:00 2001 From: Volodymyr Kozieiev Date: Tue, 23 Feb 2021 17:47:45 +0200 Subject: [PATCH] Listen for delivered messages (#2150) --- VERSION | 2 +- go.sum | 1 + protocol/common/message.go | 7 +- protocol/common/message_processor.go | 56 +++-- protocol/common/message_processor_test.go | 6 +- protocol/common/raw_message.go | 1 + protocol/datasync/datasync.go | 20 +- protocol/messenger.go | 49 ++++- protocol/messenger_config.go | 11 + protocol/migrations/migrations.go | 202 +++++++++--------- .../sqlite/1612870480_add_datasync_id.up.sql | 2 + protocol/persistence.go | 34 ++- protocol/persistence_test.go | 22 ++ protocol/pushnotificationclient/client.go | 4 +- protocol/pushnotificationserver/server.go | 6 +- protocol/v1/status_message.go | 23 -- services/ext/service.go | 5 +- signal/events_messenger.go | 18 ++ 18 files changed, 301 insertions(+), 168 deletions(-) create mode 100644 protocol/migrations/sqlite/1612870480_add_datasync_id.up.sql create mode 100644 signal/events_messenger.go diff --git a/VERSION b/VERSION index c9da17123..c7767849e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.71.4 +0.71.5 diff --git a/go.sum b/go.sum index 8d79b516e..0159d9cf0 100644 --- a/go.sum +++ b/go.sum @@ -203,6 +203,7 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= diff --git a/protocol/common/message.go b/protocol/common/message.go index 346294c7e..8d8e2ed4f 100644 --- a/protocol/common/message.go +++ b/protocol/common/message.go @@ -74,8 +74,9 @@ func (c *CommandParameters) IsTokenTransfer() bool { } const ( - OutgoingStatusSending = "sending" - OutgoingStatusSent = "sent" + OutgoingStatusSending = "sending" + OutgoingStatusSent = "sent" + OutgoingStatusDelivered = "delivered" ) // Message represents a message record in the database, @@ -95,7 +96,7 @@ type Message struct { Identicon string `json:"identicon"` // The chat id to be stored locally LocalChatID string `json:"localChatId"` - + // Seen set to true when user have read this message already Seen bool `json:"seen"` OutgoingStatus string `json:"outgoingStatus,omitempty"` diff --git a/protocol/common/message_processor.go b/protocol/common/message_processor.go index 3f0d2013e..9f2b7f54b 100644 --- a/protocol/common/message_processor.go +++ b/protocol/common/message_processor.go @@ -130,7 +130,7 @@ func (p *MessageProcessor) SetHandleSharedSecrets(handler func([]*sharedsecret.S func (p *MessageProcessor) SendPrivate( ctx context.Context, recipient *ecdsa.PublicKey, - rawMessage RawMessage, + rawMessage *RawMessage, ) ([]byte, error) { p.logger.Debug( "sending a private message", @@ -149,7 +149,7 @@ func (p *MessageProcessor) SendPrivate( rawMessage.Sender = p.identity } - return p.sendPrivate(ctx, recipient, &rawMessage) + return p.sendPrivate(ctx, recipient, rawMessage) } // SendGroup takes encoded data, encrypts it and sends through the wire, @@ -209,10 +209,11 @@ func (p *MessageProcessor) sendPrivate( if p.featureFlags.Datasync && rawMessage.ResendAutomatically { // No need to call transport tracking. // It is done in a data sync dispatch step. - if err := p.addToDataSync(recipient, wrappedMessage); err != nil { + datasyncID, err := p.addToDataSync(recipient, wrappedMessage) + if err != nil { return nil, errors.Wrap(err, "failed to send message with datasync") } - + rawMessage.DataSyncID = datasyncID } else if rawMessage.SkipEncryption { // When SkipEncryption is set we don't pass the message to the encryption layer messageIDs := [][]byte{messageID} @@ -396,20 +397,45 @@ func (p *MessageProcessor) SendPublic( return messageID, nil } +// unwrapDatasyncMessage tries to unwrap message as datasync one and in case of success +// returns cloned messages with replaced payloads +func unwrapDatasyncMessage(m *v1protocol.StatusMessage, datasync *datasync.DataSync) ([]*v1protocol.StatusMessage, [][]byte, error) { + var statusMessages []*v1protocol.StatusMessage + + payloads, acks, err := datasync.UnwrapPayloadsAndAcks( + m.SigPubKey(), + m.DecryptedPayload, + ) + if err != nil { + return nil, nil, err + } + + for _, payload := range payloads { + message, err := m.Clone() + if err != nil { + return nil, nil, err + } + message.DecryptedPayload = payload + statusMessages = append(statusMessages, message) + } + return statusMessages, acks, nil +} + // HandleMessages expects a whisper message as input, and it will go through // a series of transformations until the message is parsed into an application // layer message, or in case of Raw methods, the processing stops at the layer // before. // It returns an error only if the processing of required steps failed. -func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, error) { +func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, applicationLayer bool) ([]*v1protocol.StatusMessage, [][]byte, error) { logger := p.logger.With(zap.String("site", "handleMessages")) hlogger := logger.With(zap.ByteString("hash", shhMessage.Hash)) var statusMessage v1protocol.StatusMessage + var statusMessages []*v1protocol.StatusMessage err := statusMessage.HandleTransport(shhMessage) if err != nil { hlogger.Error("failed to handle transport layer message", zap.Error(err)) - return nil, err + return nil, nil, err } err = p.handleEncryptionLayer(context.Background(), &statusMessage) @@ -417,9 +443,11 @@ func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, application hlogger.Debug("failed to handle an encryption message", zap.Error(err)) } - statusMessages, err := statusMessage.HandleDatasync(p.datasync) + statusMessages, acks, err := unwrapDatasyncMessage(&statusMessage, p.datasync) if err != nil { hlogger.Debug("failed to handle datasync message", zap.Error(err)) + //that wasn't a datasync message, so use the original payload + statusMessages = append(statusMessages, &statusMessage) } for _, statusMessage := range statusMessages { @@ -436,7 +464,7 @@ func (p *MessageProcessor) HandleMessages(shhMessage *types.Message, application } } - return statusMessages, nil + return statusMessages, acks, nil } // fetchDecryptionKey returns the private key associated with this public key, and returns true if it's an ephemeral key @@ -513,24 +541,24 @@ func (p *MessageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) return wrappedMessage, nil } -func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error { +func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) ([]byte, error) { groupID := datasync.ToOneToOneGroupID(&p.identity.PublicKey, publicKey) peerID := datasyncpeer.PublicKeyToPeerID(*publicKey) exist, err := p.datasync.IsPeerInGroup(groupID, peerID) if err != nil { - return errors.Wrap(err, "failed to check if peer is in group") + return nil, errors.Wrap(err, "failed to check if peer is in group") } if !exist { if err := p.datasync.AddPeer(groupID, peerID); err != nil { - return errors.Wrap(err, "failed to add peer") + return nil, errors.Wrap(err, "failed to add peer") } } - _, err = p.datasync.AppendMessage(groupID, message) + id, err := p.datasync.AppendMessage(groupID, message) if err != nil { - return errors.Wrap(err, "failed to append message to datasync") + return nil, errors.Wrap(err, "failed to append message to datasync") } - return nil + return id[:], nil } // sendDataSync sends a message scheduled by the data sync layer. diff --git a/protocol/common/message_processor_test.go b/protocol/common/message_processor_test.go index 600ac2703..baa22a1a7 100644 --- a/protocol/common/message_processor_test.go +++ b/protocol/common/message_processor_test.go @@ -115,7 +115,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = wrappedPayload - decodedMessages, err := s.processor.HandleMessages(message, true) + decodedMessages, _, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) s.Require().Equal(1, len(decodedMessages)) @@ -151,7 +151,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = marshalledDataSyncMessage - decodedMessages, err := s.processor.HandleMessages(message, true) + decodedMessages, _, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) // We send two messages, the unwrapped one will be attributed to the relayer, while the wrapped one will be attributed to the author @@ -215,7 +215,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() { message.Sig = crypto.FromECDSAPub(&relayerKey.PublicKey) message.Payload = encryptedPayload - decodedMessages, err := s.processor.HandleMessages(message, true) + decodedMessages, _, err := s.processor.HandleMessages(message, true) s.Require().NoError(err) // We send two messages, the unwrapped one will be attributed to the relayer, diff --git a/protocol/common/raw_message.go b/protocol/common/raw_message.go index 0d4e82a8b..8c3505616 100644 --- a/protocol/common/raw_message.go +++ b/protocol/common/raw_message.go @@ -21,6 +21,7 @@ type RawMessage struct { Payload []byte Sender *ecdsa.PrivateKey Recipients []*ecdsa.PublicKey + DataSyncID []byte SkipGroupMessageWrap bool SendOnPersonalTopic bool } diff --git a/protocol/datasync/datasync.go b/protocol/datasync/datasync.go index be1cec1f7..528e5368e 100644 --- a/protocol/datasync/datasync.go +++ b/protocol/datasync/datasync.go @@ -2,6 +2,7 @@ package datasync import ( "crypto/ecdsa" + "errors" "github.com/golang/protobuf/proto" datasyncnode "github.com/vacp2p/mvds/node" @@ -24,28 +25,35 @@ func New(node *datasyncnode.Node, transport *NodeTransport, sendingEnabled bool, return &DataSync{Node: node, NodeTransport: transport, sendingEnabled: sendingEnabled, logger: logger} } -func (d *DataSync) Handle(sender *ecdsa.PublicKey, payload []byte) [][]byte { +// UnwrapPayloadsAndAcks tries to unwrap datasync message and return messages payloads +// and acknowledgements for previously sent messages +func (d *DataSync) UnwrapPayloadsAndAcks(sender *ecdsa.PublicKey, payload []byte) ([][]byte, [][]byte, error) { var payloads [][]byte + var acks [][]byte logger := d.logger.With(zap.String("site", "Handle")) datasyncMessage, err := unwrap(payload) // If it failed to decode is not a protobuf message, if it successfully decoded but body is empty, is likedly a protobuf wrapped message - if err != nil || !datasyncMessage.IsValid() { - logger.Debug("handling non-datasync message", zap.Error(err), zap.Bool("datasyncMessage.IsValid()", datasyncMessage.IsValid()), zap.Any("message", datasyncMessage)) - // Not a datasync message, return unchanged - payloads = append(payloads, payload) + if err != nil { + logger.Debug("Unwrapping datasync message failed", zap.Error(err)) + return nil, nil, err + } else if !datasyncMessage.IsValid() { + return nil, nil, errors.New("handling non-datasync message") } else { logger.Debug("handling datasync message") // datasync message for _, message := range datasyncMessage.Messages { payloads = append(payloads, message.Body) } + + acks = append(acks, datasyncMessage.Acks...) + if d.sendingEnabled { d.add(sender, datasyncMessage) } } - return payloads + return payloads, acks, nil } func (d *DataSync) Stop() { diff --git a/protocol/messenger.go b/protocol/messenger.go index 408210cf7..a19a9465d 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -825,7 +825,7 @@ func (m *Messenger) publishOrgInvitation(org *communities.Community, invitation SkipEncryption: true, MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_INVITATION, } - _, err = m.processor.SendPrivate(context.Background(), pk, rawMessage) + _, err = m.processor.SendPrivate(context.Background(), pk, &rawMessage) return err } @@ -1583,7 +1583,7 @@ func (m *Messenger) SendGroupChatInvitationRequest(ctx context.Context, chatID s return nil, err } - id, err := m.processor.SendPrivate(ctx, adminpk, spec) + id, err := m.processor.SendPrivate(ctx, adminpk, &spec) if err != nil { return nil, err } @@ -1654,7 +1654,7 @@ func (m *Messenger) SendGroupChatInvitationRejection(ctx context.Context, invita return nil, err } - id, err := m.processor.SendPrivate(ctx, userpk, spec) + id, err := m.processor.SendPrivate(ctx, userpk, &spec) if err != nil { return nil, err } @@ -2235,7 +2235,7 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, spec common.RawMess hasPairedDevices := m.hasPairedDevices() // We send a message to any paired device if hasPairedDevices { - _, err := m.processor.SendPrivate(ctx, &m.identity.PublicKey, spec) + _, err := m.processor.SendPrivate(ctx, &m.identity.PublicKey, &spec) if err != nil { return err } @@ -2277,15 +2277,19 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec common.RawMessage) if err != nil { return nil, err } + + //SendPrivate will alter message identity and possibly datasyncid, so we save an unchanged + //message for sending to paired devices later + specCopyForPairedDevices := spec if !common.IsPubKeyEqual(publicKey, &m.identity.PublicKey) { - id, err = m.processor.SendPrivate(ctx, publicKey, spec) + id, err = m.processor.SendPrivate(ctx, publicKey, &spec) if err != nil { return nil, err } } - err = m.sendToPairedDevices(ctx, spec) + err = m.sendToPairedDevices(ctx, specCopyForPairedDevices) if err != nil { return nil, err @@ -2753,6 +2757,33 @@ type ReceivedMessageState struct { Timesource common.TimeSource } +func (m *Messenger) markDeliveredMessages(acks [][]byte) { + for _, ack := range acks { + //get message ID from database by datasync ID + messageID, err := m.persistence.RawMessageIDFromDatasyncID(ack) + if err != nil { + m.logger.Info("got datasync acknowledge for message we don't have in db", zap.String("ack", hex.EncodeToString(ack))) + continue + } + + //mark messages as delivered + err = m.UpdateMessageOutgoingStatus(messageID, common.OutgoingStatusDelivered) + if err != nil { + m.logger.Debug("Can't set message status as delivered", zap.Error(err)) + } + + //send signal to client that message status updated + if m.config.messageDeliveredHandler != nil { + message, err := m.persistence.MessageByID(messageID) + if err != nil { + m.logger.Debug("Can't get message from database", zap.Error(err)) + continue + } + m.config.messageDeliveredHandler(message.LocalChatID, messageID) + } + } +} + // addNewMessageNotification takes a common.Message and generates a new MessageNotificationBody and appends it to the // []Response.Notifications if the message is m.New func (r *ReceivedMessageState) addNewMessageNotification(m *common.Message) error { @@ -2798,16 +2829,18 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } logger := m.logger.With(zap.String("site", "RetrieveAll")) + for _, messages := range chatWithMessages { var processedMessages []string for _, shhMessage := range messages { // Indicates tha all messages in the batch have been processed correctly allMessagesProcessed := true - statusMessages, err := m.processor.HandleMessages(shhMessage, true) + statusMessages, acks, err := m.processor.HandleMessages(shhMessage, true) if err != nil { logger.Info("failed to decode messages", zap.Error(err)) continue } + m.markDeliveredMessages(acks) logger.Debug("processing messages further", zap.Int("count", len(statusMessages))) @@ -2826,6 +2859,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte if _, ok := messageState.AllContacts[senderID]; ok && messageState.AllContacts[senderID].IsBlocked() { continue } + // Don't process duplicates messageID := types.EncodeHex(msg.ID) exists, err := m.handler.messageExists(messageID, messageState.ExistingMessagesMap) @@ -2859,6 +2893,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte } if msg.ParsedMessage != nil { + logger.Debug("Handling parsed message") switch msg.ParsedMessage.Interface().(type) { case protobuf.MembershipUpdateMessage: diff --git a/protocol/messenger_config.go b/protocol/messenger_config.go index 7d33f77fe..906ad5d0f 100644 --- a/protocol/messenger_config.go +++ b/protocol/messenger_config.go @@ -14,6 +14,8 @@ import ( "github.com/status-im/status-go/services/mailservers" ) +type MessageDeliveredHandler func(string, string) + type config struct { // This needs to be exposed until we move here mailserver logic // as otherwise the client is not notified of a new filter and @@ -41,6 +43,8 @@ type config struct { pushNotificationClientConfig *pushnotificationclient.Config logger *zap.Logger + + messageDeliveredHandler MessageDeliveredHandler } type Option func(*config) error @@ -144,3 +148,10 @@ func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option { return nil } } + +func WithDeliveredHandler(h MessageDeliveredHandler) Option { + return func(c *config) error { + c.messageDeliveredHandler = h + return nil + } +} diff --git a/protocol/migrations/migrations.go b/protocol/migrations/migrations.go index 85c22ddd2..ba7959547 100644 --- a/protocol/migrations/migrations.go +++ b/protocol/migrations/migrations.go @@ -23,6 +23,7 @@ // 1610117927_add_message_cache.up.sql (142B) // 1610959908_add_dont_wrap_to_raw_messages.up.sql (83B) // 1610960912_add_send_on_personal_topic.up.sql (82B) +// 1612870480_add_datasync_id.up.sql (111B) // README.md (554B) // doc.go (850B) @@ -44,7 +45,7 @@ import ( func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %w", name, err) } var buf bytes.Buffer @@ -52,7 +53,7 @@ func bindataRead(data []byte, name string) ([]byte, error) { clErr := gz.Close() if err != nil { - return nil, fmt.Errorf("read %q: %v", name, err) + return nil, fmt.Errorf("read %q: %w", name, err) } if clErr != nil { return nil, err @@ -108,7 +109,7 @@ func _000001_initDownDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000001_init.down.db.sql", size: 65, mode: os.FileMode(0644), modTime: time.Unix(1610007618, 0)} + info := bindataFileInfo{name: "000001_init.down.db.sql", size: 65, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5e, 0xbb, 0x3f, 0x1, 0x75, 0x19, 0x70, 0x86, 0xa7, 0x34, 0x40, 0x17, 0x34, 0x3e, 0x18, 0x51, 0x79, 0xd4, 0x22, 0xad, 0x8f, 0x80, 0xcc, 0xa6, 0xcc, 0x6, 0x2b, 0x62, 0x2, 0x47, 0xba, 0xf9}} return a, nil } @@ -128,7 +129,7 @@ func _000001_initUpDbSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000001_init.up.db.sql", size: 2719, mode: os.FileMode(0644), modTime: time.Unix(1610007618, 0)} + info := bindataFileInfo{name: "000001_init.up.db.sql", size: 2719, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x60, 0xdc, 0xeb, 0xe, 0xc2, 0x4f, 0x75, 0xa, 0xf6, 0x3e, 0xc7, 0xc4, 0x4, 0xe2, 0xe1, 0xa4, 0x73, 0x2f, 0x4a, 0xad, 0x1a, 0x0, 0xc3, 0x93, 0x9d, 0x77, 0x3e, 0x31, 0x91, 0x77, 0x2e, 0xc8}} return a, nil } @@ -148,7 +149,7 @@ func _000002_add_last_ens_clock_valueUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1610007618, 0)} + info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4d, 0x3, 0x8f, 0xd5, 0x85, 0x83, 0x47, 0xbe, 0xf9, 0x82, 0x7e, 0x81, 0xa4, 0xbd, 0xaa, 0xd5, 0x98, 0x18, 0x5, 0x2d, 0x82, 0x42, 0x3b, 0x3, 0x50, 0xc3, 0x1e, 0x84, 0x35, 0xf, 0xb6, 0x2b}} return a, nil } @@ -168,7 +169,7 @@ func _1586358095_add_replaceUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1611588719, 0)} + info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd2, 0xb3, 0xa9, 0xc7, 0x7f, 0x9d, 0x8f, 0x43, 0x8c, 0x9e, 0x58, 0x8d, 0x44, 0xbc, 0xfa, 0x6b, 0x5f, 0x3f, 0x5a, 0xbe, 0xe8, 0xb1, 0x16, 0xf, 0x91, 0x2a, 0xa0, 0x71, 0xbb, 0x8d, 0x6b, 0xcb}} return a, nil } @@ -188,7 +189,7 @@ func _1588665364_add_image_dataUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1611588719, 0)} + info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd6, 0xc6, 0x35, 0xb4, 0x4c, 0x39, 0x96, 0x29, 0x30, 0xda, 0xf4, 0x8f, 0xcb, 0xf1, 0x9f, 0x84, 0xdc, 0x88, 0xd4, 0xd5, 0xbc, 0xb6, 0x5b, 0x46, 0x78, 0x67, 0x76, 0x1a, 0x5, 0x36, 0xdc, 0xe5}} return a, nil } @@ -208,7 +209,7 @@ func _1589365189_add_pow_targetUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1611588719, 0)} + info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x3a, 0xe2, 0x2e, 0x7d, 0xaf, 0xbb, 0xcc, 0x21, 0xa1, 0x7a, 0x41, 0x9a, 0xd0, 0xbb, 0xa9, 0xc8, 0x35, 0xf9, 0x32, 0x34, 0x46, 0x44, 0x9a, 0x86, 0x40, 0x7c, 0xb9, 0x23, 0xc7, 0x3, 0x3f}} return a, nil } @@ -228,7 +229,7 @@ func _1591277220_add_index_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1611588719, 0)} + info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9c, 0xfe, 0xbe, 0xd5, 0xb8, 0x8f, 0xdd, 0xef, 0xbb, 0xa8, 0xad, 0x7f, 0xed, 0x5b, 0x5b, 0x2f, 0xe6, 0x82, 0x27, 0x78, 0x1f, 0xb9, 0x57, 0xdc, 0x8, 0xc2, 0xb2, 0xa9, 0x9a, 0x4, 0xe1, 0x7a}} return a, nil } @@ -248,7 +249,7 @@ func _1593087212_add_mute_chat_and_raw_message_fieldsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.up.sql", size: 215, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x73, 0x99, 0x61, 0xd1, 0xaa, 0xb4, 0xbf, 0xaf, 0xd7, 0x20, 0x17, 0x40, 0xf9, 0x2, 0xfb, 0xcc, 0x40, 0x2a, 0xd, 0x86, 0x36, 0x30, 0x88, 0x89, 0x25, 0x80, 0x42, 0xb0, 0x5b, 0xe9, 0x73, 0x78}} return a, nil } @@ -268,7 +269,7 @@ func _1595862781_add_audio_dataUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1595862781_add_audio_data.up.sql", size: 246, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1595862781_add_audio_data.up.sql", size: 246, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0xd2, 0xee, 0x55, 0xfb, 0x36, 0xa4, 0x92, 0x66, 0xe, 0x81, 0x62, 0x1e, 0x7a, 0x69, 0xa, 0xd5, 0x4b, 0xa5, 0x6a, 0x8d, 0x1d, 0xce, 0xf3, 0x3e, 0xc0, 0x5f, 0x9c, 0x66, 0x1b, 0xb4, 0xed}} return a, nil } @@ -288,7 +289,7 @@ func _1595865249_create_emoji_reactions_tableUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1595865249_create_emoji_reactions_table.up.sql", size: 300, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1595865249_create_emoji_reactions_table.up.sql", size: 300, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xc5, 0x43, 0x5c, 0x3d, 0x53, 0x43, 0x2c, 0x1a, 0xa5, 0xb6, 0xbf, 0x7, 0x4, 0x5a, 0x3e, 0x40, 0x8b, 0xa4, 0x57, 0x12, 0x58, 0xbc, 0x42, 0xe2, 0xc3, 0xde, 0x76, 0x98, 0x80, 0xe2, 0xbe}} return a, nil } @@ -308,7 +309,7 @@ func _1596805115_create_group_chat_invitations_tableUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1596805115_create_group_chat_invitations_table.up.sql", size: 231, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1596805115_create_group_chat_invitations_table.up.sql", size: 231, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6d, 0xb1, 0x14, 0x6d, 0x54, 0x28, 0x67, 0xc3, 0x23, 0x6a, 0xfc, 0x80, 0xdf, 0x9e, 0x4c, 0x35, 0x36, 0xf, 0xf8, 0xf3, 0x5f, 0xae, 0xad, 0xb, 0xc1, 0x51, 0x8e, 0x17, 0x7, 0xe5, 0x7f, 0x91}} return a, nil } @@ -328,7 +329,7 @@ func _1597322655_add_invitation_admin_chat_fieldUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1597322655_add_invitation_admin_chat_field.up.sql", size: 54, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1597322655_add_invitation_admin_chat_field.up.sql", size: 54, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa9, 0x7a, 0xa0, 0xf2, 0xdb, 0x13, 0x91, 0x91, 0xa8, 0x34, 0x1a, 0xa1, 0x49, 0x68, 0xd5, 0xae, 0x2c, 0xd8, 0xd5, 0xea, 0x8f, 0x8c, 0xc7, 0x2, 0x4e, 0x58, 0x2c, 0x3a, 0x14, 0xd4, 0x4f, 0x2c}} return a, nil } @@ -348,7 +349,7 @@ func _1597757544_add_nicknameUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1597757544_add_nickname.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1597757544_add_nickname.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xa2, 0x64, 0x50, 0xc5, 0x4, 0xb9, 0x8b, 0xd1, 0x18, 0x9b, 0xc3, 0x91, 0x36, 0x2a, 0x1f, 0xc3, 0x6c, 0x2d, 0x92, 0xf8, 0x5e, 0xff, 0xb1, 0x59, 0x61, 0x2, 0x1c, 0xe1, 0x85, 0x90, 0xa4}} return a, nil } @@ -368,7 +369,7 @@ func _1598955122_add_mentionsUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1598955122_add_mentions.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1598955122_add_mentions.up.sql", size: 52, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8d, 0x22, 0x17, 0x92, 0xd2, 0x11, 0x4e, 0x7, 0x93, 0x9a, 0x55, 0xfd, 0xb, 0x97, 0xc4, 0x63, 0x6a, 0x81, 0x97, 0xcd, 0xb2, 0xf8, 0x4b, 0x5f, 0x3c, 0xfa, 0x3a, 0x38, 0x53, 0x10, 0xed, 0x9d}} return a, nil } @@ -388,7 +389,7 @@ func _1599641390_add_emoji_reactions_indexUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1599641390_add_emoji_reactions_index.up.sql", size: 126, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xd8, 0xdc, 0xa7, 0xb, 0x92, 0x7a, 0x61, 0x37, 0x24, 0x1c, 0x77, 0x5e, 0xe, 0x7e, 0xfc, 0x9f, 0x98, 0x7b, 0x65, 0xe7, 0xf9, 0x71, 0x57, 0x89, 0x2d, 0x90, 0x1b, 0xf6, 0x5e, 0x37, 0xe8}} return a, nil } @@ -408,7 +409,7 @@ func _1599720851_add_seen_index_remove_long_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1599720851_add_seen_index_remove_long_messages.up.sql", size: 150, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1599720851_add_seen_index_remove_long_messages.up.sql", size: 150, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x24, 0x1c, 0xc4, 0x78, 0x91, 0xc7, 0xeb, 0xfe, 0xc8, 0xa0, 0xd8, 0x13, 0x27, 0x97, 0xc8, 0x96, 0x56, 0x97, 0x33, 0x2c, 0x1e, 0x16, 0x8a, 0xd3, 0x49, 0x99, 0x3, 0xe9, 0xbb, 0xc4, 0x5, 0x3c}} return a, nil } @@ -428,7 +429,7 @@ func _1603198582_add_profile_chat_fieldUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1603198582_add_profile_chat_field.up.sql", size: 45, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1603198582_add_profile_chat_field.up.sql", size: 45, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xaa, 0xca, 0xe, 0x46, 0xa0, 0x9, 0x9d, 0x47, 0x57, 0xe9, 0xfb, 0x17, 0xeb, 0x9c, 0xf6, 0xb8, 0x1d, 0xe9, 0xd, 0x0, 0xd5, 0xe5, 0xd8, 0x9e, 0x60, 0xa, 0xbf, 0x32, 0x2c, 0x52, 0x7f, 0x6a}} return a, nil } @@ -448,7 +449,7 @@ func _1603816533_add_linksUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1603816533_add_links.up.sql", size: 48, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1603816533_add_links.up.sql", size: 48, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc9, 0x24, 0xd6, 0x1d, 0xa, 0x83, 0x1e, 0x4d, 0xf, 0xae, 0x4d, 0x8c, 0x51, 0x32, 0xa8, 0x37, 0xb0, 0x14, 0xfb, 0x32, 0x34, 0xc8, 0xc, 0x4e, 0x5b, 0xc5, 0x15, 0x65, 0x73, 0x0, 0x0, 0x1d}} return a, nil } @@ -468,7 +469,7 @@ func _1603888149_create_chat_identity_last_published_tableUpSql() (*asset, error return nil, err } - info := bindataFileInfo{name: "1603888149_create_chat_identity_last_published_table.up.sql", size: 407, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1603888149_create_chat_identity_last_published_table.up.sql", size: 407, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x7f, 0x9, 0xf, 0xfb, 0xdb, 0x3c, 0x86, 0x70, 0x82, 0xda, 0x10, 0x25, 0xe2, 0x4e, 0x40, 0x45, 0xab, 0x8b, 0x1c, 0x91, 0x7c, 0xf1, 0x70, 0x2e, 0x81, 0xf3, 0x71, 0x45, 0xda, 0xe2, 0xa4, 0x57}} return a, nil } @@ -488,7 +489,7 @@ func _1605075346_add_communitiesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1605075346_add_communities.up.sql", size: 6971, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1605075346_add_communities.up.sql", size: 6971, mode: os.FileMode(0644), modTime: time.Unix(1610097152, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1f, 0x64, 0xea, 0xb4, 0xae, 0x9e, 0xdb, 0x9, 0x58, 0xb6, 0x5c, 0x7a, 0x50, 0xc5, 0xfe, 0x93, 0x5d, 0x36, 0x85, 0x5d, 0x6a, 0xba, 0xc9, 0x7e, 0x84, 0xd7, 0xbf, 0x2a, 0x53, 0xf3, 0x97, 0xf1}} return a, nil } @@ -508,7 +509,7 @@ func _1610117927_add_message_cacheUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1610117927_add_message_cache.up.sql", size: 142, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "1610117927_add_message_cache.up.sql", size: 142, mode: os.FileMode(0644), modTime: time.Unix(1613123938, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0xf1, 0xf0, 0x82, 0x79, 0x28, 0x19, 0xc2, 0x39, 0x6a, 0xa5, 0x96, 0x59, 0x23, 0xa0, 0xed, 0x60, 0x58, 0x86, 0x9, 0xb9, 0xad, 0xfb, 0xa, 0xe3, 0x47, 0x6e, 0xa1, 0x18, 0xe8, 0x39, 0x2c}} return a, nil } @@ -528,7 +529,7 @@ func _1610959908_add_dont_wrap_to_raw_messagesUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1610959908_add_dont_wrap_to_raw_messages.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1611646365, 0)} + info := bindataFileInfo{name: "1610959908_add_dont_wrap_to_raw_messages.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1613123938, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x71, 0x2, 0x9a, 0xca, 0xd4, 0x38, 0x44, 0x30, 0x2b, 0xa8, 0x27, 0x32, 0x63, 0x53, 0x22, 0x60, 0x59, 0x84, 0x23, 0x96, 0x77, 0xf0, 0x56, 0xd7, 0x94, 0xe0, 0x95, 0x28, 0x6, 0x1d, 0x4e, 0xb1}} return a, nil } @@ -548,11 +549,31 @@ func _1610960912_add_send_on_personal_topicUpSql() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "1610960912_add_send_on_personal_topic.up.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1611646381, 0)} + info := bindataFileInfo{name: "1610960912_add_send_on_personal_topic.up.sql", size: 82, mode: os.FileMode(0644), modTime: time.Unix(1613123938, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0xac, 0x2f, 0xc4, 0xd, 0xa7, 0x1b, 0x37, 0x30, 0xc2, 0x68, 0xee, 0xde, 0x54, 0x5e, 0xbf, 0x3f, 0xa0, 0xd6, 0xc6, 0x9f, 0xd4, 0x34, 0x12, 0x76, 0x1e, 0x66, 0x4a, 0xfc, 0xf, 0xee, 0xc9}} return a, nil } +var __1612870480_add_datasync_idUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\x28\x4a\x2c\x8f\xcf\x4d\x2d\x2e\x4e\x4c\x4f\x2d\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\x49\x2c\x49\x2c\xae\xcc\x4b\x8e\xcf\x4c\x51\x70\xf2\xf1\x77\xb2\xe6\x72\x0e\x72\x75\x0c\x71\x55\xf0\xf4\x73\x71\x8d\x50\xc8\x4c\xa9\x88\x4f\x49\x2c\x81\xa9\xf0\xf7\x43\x31\x48\x03\x49\xb7\xa6\x35\x20\x00\x00\xff\xff\x27\x9d\xbe\x49\x6f\x00\x00\x00") + +func _1612870480_add_datasync_idUpSqlBytes() ([]byte, error) { + return bindataRead( + __1612870480_add_datasync_idUpSql, + "1612870480_add_datasync_id.up.sql", + ) +} + +func _1612870480_add_datasync_idUpSql() (*asset, error) { + bytes, err := _1612870480_add_datasync_idUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1612870480_add_datasync_id.up.sql", size: 111, mode: os.FileMode(0644), modTime: time.Unix(1613649828, 0)} + a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x34, 0x9a, 0xbc, 0xfa, 0xaa, 0x8c, 0x9c, 0x37, 0x67, 0x15, 0x9c, 0x7e, 0x78, 0x75, 0x66, 0x82, 0x18, 0x72, 0x10, 0xbc, 0xd4, 0xab, 0x44, 0xfe, 0x57, 0x85, 0x6d, 0x19, 0xf5, 0x96, 0x8a, 0xbe}} + return a, nil +} + var _readmeMd = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\x91\xc1\xce\xd3\x30\x10\x84\xef\x7e\x8a\x91\x7a\x01\xa9\x2a\x8f\xc0\x0d\x71\x82\x03\x48\x1c\xc9\x36\x9e\x36\x96\x1c\x6f\xf0\xae\x93\xe6\xed\x91\xa3\xc2\xdf\xff\x66\xed\xd8\x33\xdf\x78\x4f\xa7\x13\xbe\xea\x06\x57\x6c\x35\x39\x31\xa7\x7b\x15\x4f\x5a\xec\x73\x08\xbf\x08\x2d\x79\x7f\x4a\x43\x5b\x86\x17\xfd\x8c\x21\xea\x56\x5e\x47\x90\x4a\x14\x75\x48\xde\x64\x37\x2c\x6a\x96\xae\x99\x48\x05\xf6\x27\x77\x13\xad\x08\xae\x8a\x51\xe7\x25\xf3\xf1\xa9\x9f\xf9\x58\x58\x2c\xad\xbc\xe0\x8b\x56\xf0\x21\x5d\xeb\x4c\x95\xb3\xae\x84\x60\xd4\xdc\xe6\x82\x5d\x1b\x36\x6d\x39\x62\x92\xf5\xb8\x11\xdb\x92\xd3\x28\xce\xe0\x13\xe1\x72\xcd\x3c\x63\xd4\x65\x87\xae\xac\xe8\xc3\x28\x2e\x67\x44\x66\x3a\x21\x25\xa2\x72\xac\x14\x67\xbc\x84\x9f\x53\x32\x8c\x52\x70\x25\x56\xd6\xfd\x8d\x05\x37\xad\x30\x9d\x9f\xa6\x86\x0f\xcd\x58\x7f\xcf\x34\x93\x3b\xed\x90\x9f\xa4\x1f\xcf\x30\x85\x4d\x07\x58\xaf\x7f\x25\xc4\x9d\xf3\x72\x64\x84\xd0\x7f\xf9\x9b\x3a\x2d\x84\xef\x85\x48\x66\x8d\xd8\x88\x9b\x8c\x8c\x98\x5b\xf6\x74\x14\x4e\x33\x0d\xc9\xe0\x93\x38\xda\x12\xc5\x69\xbd\xe4\xf0\x2e\x7a\x78\x07\x1c\xfe\x13\x9f\x91\x29\x31\x95\x7b\x7f\x62\x59\x37\xb4\xe5\x5e\x25\xfe\x33\xee\xd5\x53\x71\xd6\xda\x3a\xd8\xcb\xde\x2e\xf8\xa1\x90\x55\x53\x0c\xc7\xaa\x0d\xe9\x76\x14\x29\x1c\x7b\x68\xdd\x2f\xe1\x6f\x00\x00\x00\xff\xff\x3c\x0a\xc2\xfe\x2a\x02\x00\x00") func readmeMdBytes() ([]byte, error) { @@ -568,7 +589,7 @@ func readmeMd() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "README.md", size: 554, mode: os.FileMode(0644), modTime: time.Unix(1611588835, 0)} + info := bindataFileInfo{name: "README.md", size: 554, mode: os.FileMode(0644), modTime: time.Unix(1610097152, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x6e, 0xfb, 0xcc, 0x81, 0x94, 0x4d, 0x8c, 0xa0, 0x3b, 0x5, 0xb0, 0x18, 0xd6, 0xbb, 0xb3, 0x79, 0xc8, 0x8f, 0xff, 0xc1, 0x10, 0xf9, 0xf, 0x20, 0x1b, 0x4a, 0x74, 0x96, 0x42, 0xd7, 0xa8}} return a, nil } @@ -588,7 +609,7 @@ func docGo() (*asset, error) { return nil, err } - info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1611588719, 0)} + info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1608547984, 0)} a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa0, 0xcc, 0x41, 0xe1, 0x61, 0x12, 0x97, 0xe, 0x36, 0x8c, 0xa7, 0x9e, 0xe0, 0x6e, 0x59, 0x9e, 0xee, 0xd5, 0x4a, 0xcf, 0x1e, 0x60, 0xd6, 0xc3, 0x3a, 0xc9, 0x6c, 0xf2, 0x86, 0x5a, 0xb4, 0x1e}} return a, nil } @@ -684,57 +705,37 @@ func AssetNames() []string { // _bindata is a table, holding each asset generator, mapped to its name. var _bindata = map[string]func() (*asset, error){ - "000001_init.down.db.sql": _000001_initDownDbSql, - - "000001_init.up.db.sql": _000001_initUpDbSql, - - "000002_add_last_ens_clock_value.up.sql": _000002_add_last_ens_clock_valueUpSql, - - "1586358095_add_replace.up.sql": _1586358095_add_replaceUpSql, - - "1588665364_add_image_data.up.sql": _1588665364_add_image_dataUpSql, - - "1589365189_add_pow_target.up.sql": _1589365189_add_pow_targetUpSql, - - "1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql, - - "1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql, - - "1595862781_add_audio_data.up.sql": _1595862781_add_audio_dataUpSql, - - "1595865249_create_emoji_reactions_table.up.sql": _1595865249_create_emoji_reactions_tableUpSql, - - "1596805115_create_group_chat_invitations_table.up.sql": _1596805115_create_group_chat_invitations_tableUpSql, - - "1597322655_add_invitation_admin_chat_field.up.sql": _1597322655_add_invitation_admin_chat_fieldUpSql, - - "1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql, - - "1598955122_add_mentions.up.sql": _1598955122_add_mentionsUpSql, - - "1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql, - - "1599720851_add_seen_index_remove_long_messages.up.sql": _1599720851_add_seen_index_remove_long_messagesUpSql, - - "1603198582_add_profile_chat_field.up.sql": _1603198582_add_profile_chat_fieldUpSql, - - "1603816533_add_links.up.sql": _1603816533_add_linksUpSql, - + "000001_init.down.db.sql": _000001_initDownDbSql, + "000001_init.up.db.sql": _000001_initUpDbSql, + "000002_add_last_ens_clock_value.up.sql": _000002_add_last_ens_clock_valueUpSql, + "1586358095_add_replace.up.sql": _1586358095_add_replaceUpSql, + "1588665364_add_image_data.up.sql": _1588665364_add_image_dataUpSql, + "1589365189_add_pow_target.up.sql": _1589365189_add_pow_targetUpSql, + "1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql, + "1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql, + "1595862781_add_audio_data.up.sql": _1595862781_add_audio_dataUpSql, + "1595865249_create_emoji_reactions_table.up.sql": _1595865249_create_emoji_reactions_tableUpSql, + "1596805115_create_group_chat_invitations_table.up.sql": _1596805115_create_group_chat_invitations_tableUpSql, + "1597322655_add_invitation_admin_chat_field.up.sql": _1597322655_add_invitation_admin_chat_fieldUpSql, + "1597757544_add_nickname.up.sql": _1597757544_add_nicknameUpSql, + "1598955122_add_mentions.up.sql": _1598955122_add_mentionsUpSql, + "1599641390_add_emoji_reactions_index.up.sql": _1599641390_add_emoji_reactions_indexUpSql, + "1599720851_add_seen_index_remove_long_messages.up.sql": _1599720851_add_seen_index_remove_long_messagesUpSql, + "1603198582_add_profile_chat_field.up.sql": _1603198582_add_profile_chat_fieldUpSql, + "1603816533_add_links.up.sql": _1603816533_add_linksUpSql, "1603888149_create_chat_identity_last_published_table.up.sql": _1603888149_create_chat_identity_last_published_tableUpSql, - - "1605075346_add_communities.up.sql": _1605075346_add_communitiesUpSql, - - "1610117927_add_message_cache.up.sql": _1610117927_add_message_cacheUpSql, - - "1610959908_add_dont_wrap_to_raw_messages.up.sql": _1610959908_add_dont_wrap_to_raw_messagesUpSql, - - "1610960912_add_send_on_personal_topic.up.sql": _1610960912_add_send_on_personal_topicUpSql, - - "README.md": readmeMd, - - "doc.go": docGo, + "1605075346_add_communities.up.sql": _1605075346_add_communitiesUpSql, + "1610117927_add_message_cache.up.sql": _1610117927_add_message_cacheUpSql, + "1610959908_add_dont_wrap_to_raw_messages.up.sql": _1610959908_add_dont_wrap_to_raw_messagesUpSql, + "1610960912_add_send_on_personal_topic.up.sql": _1610960912_add_send_on_personal_topicUpSql, + "1612870480_add_datasync_id.up.sql": _1612870480_add_datasync_idUpSql, + "README.md": readmeMd, + "doc.go": docGo, } +// AssetDebug is true if the assets were built with the debug flag enabled. +const AssetDebug = false + // AssetDir returns the file names below a certain // directory embedded in the file by go-bindata. // For example if you run go-bindata on data/... and data contains the @@ -776,31 +777,32 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "000001_init.down.db.sql": &bintree{_000001_initDownDbSql, map[string]*bintree{}}, - "000001_init.up.db.sql": &bintree{_000001_initUpDbSql, map[string]*bintree{}}, - "000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}}, - "1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}}, - "1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}}, - "1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}}, - "1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}}, - "1593087212_add_mute_chat_and_raw_message_fields.up.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsUpSql, map[string]*bintree{}}, - "1595862781_add_audio_data.up.sql": &bintree{_1595862781_add_audio_dataUpSql, map[string]*bintree{}}, - "1595865249_create_emoji_reactions_table.up.sql": &bintree{_1595865249_create_emoji_reactions_tableUpSql, map[string]*bintree{}}, - "1596805115_create_group_chat_invitations_table.up.sql": &bintree{_1596805115_create_group_chat_invitations_tableUpSql, map[string]*bintree{}}, - "1597322655_add_invitation_admin_chat_field.up.sql": &bintree{_1597322655_add_invitation_admin_chat_fieldUpSql, map[string]*bintree{}}, - "1597757544_add_nickname.up.sql": &bintree{_1597757544_add_nicknameUpSql, map[string]*bintree{}}, - "1598955122_add_mentions.up.sql": &bintree{_1598955122_add_mentionsUpSql, map[string]*bintree{}}, - "1599641390_add_emoji_reactions_index.up.sql": &bintree{_1599641390_add_emoji_reactions_indexUpSql, map[string]*bintree{}}, - "1599720851_add_seen_index_remove_long_messages.up.sql": &bintree{_1599720851_add_seen_index_remove_long_messagesUpSql, map[string]*bintree{}}, - "1603198582_add_profile_chat_field.up.sql": &bintree{_1603198582_add_profile_chat_fieldUpSql, map[string]*bintree{}}, - "1603816533_add_links.up.sql": &bintree{_1603816533_add_linksUpSql, map[string]*bintree{}}, - "1603888149_create_chat_identity_last_published_table.up.sql": &bintree{_1603888149_create_chat_identity_last_published_tableUpSql, map[string]*bintree{}}, - "1605075346_add_communities.up.sql": &bintree{_1605075346_add_communitiesUpSql, map[string]*bintree{}}, - "1610117927_add_message_cache.up.sql": &bintree{_1610117927_add_message_cacheUpSql, map[string]*bintree{}}, - "1610959908_add_dont_wrap_to_raw_messages.up.sql": &bintree{_1610959908_add_dont_wrap_to_raw_messagesUpSql, map[string]*bintree{}}, - "1610960912_add_send_on_personal_topic.up.sql": &bintree{_1610960912_add_send_on_personal_topicUpSql, map[string]*bintree{}}, - "README.md": &bintree{readmeMd, map[string]*bintree{}}, - "doc.go": &bintree{docGo, map[string]*bintree{}}, + "000001_init.down.db.sql": {_000001_initDownDbSql, map[string]*bintree{}}, + "000001_init.up.db.sql": {_000001_initUpDbSql, map[string]*bintree{}}, + "000002_add_last_ens_clock_value.up.sql": {_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}}, + "1586358095_add_replace.up.sql": {_1586358095_add_replaceUpSql, map[string]*bintree{}}, + "1588665364_add_image_data.up.sql": {_1588665364_add_image_dataUpSql, map[string]*bintree{}}, + "1589365189_add_pow_target.up.sql": {_1589365189_add_pow_targetUpSql, map[string]*bintree{}}, + "1591277220_add_index_messages.up.sql": {_1591277220_add_index_messagesUpSql, map[string]*bintree{}}, + "1593087212_add_mute_chat_and_raw_message_fields.up.sql": {_1593087212_add_mute_chat_and_raw_message_fieldsUpSql, map[string]*bintree{}}, + "1595862781_add_audio_data.up.sql": {_1595862781_add_audio_dataUpSql, map[string]*bintree{}}, + "1595865249_create_emoji_reactions_table.up.sql": {_1595865249_create_emoji_reactions_tableUpSql, map[string]*bintree{}}, + "1596805115_create_group_chat_invitations_table.up.sql": {_1596805115_create_group_chat_invitations_tableUpSql, map[string]*bintree{}}, + "1597322655_add_invitation_admin_chat_field.up.sql": {_1597322655_add_invitation_admin_chat_fieldUpSql, map[string]*bintree{}}, + "1597757544_add_nickname.up.sql": {_1597757544_add_nicknameUpSql, map[string]*bintree{}}, + "1598955122_add_mentions.up.sql": {_1598955122_add_mentionsUpSql, map[string]*bintree{}}, + "1599641390_add_emoji_reactions_index.up.sql": {_1599641390_add_emoji_reactions_indexUpSql, map[string]*bintree{}}, + "1599720851_add_seen_index_remove_long_messages.up.sql": {_1599720851_add_seen_index_remove_long_messagesUpSql, map[string]*bintree{}}, + "1603198582_add_profile_chat_field.up.sql": {_1603198582_add_profile_chat_fieldUpSql, map[string]*bintree{}}, + "1603816533_add_links.up.sql": {_1603816533_add_linksUpSql, map[string]*bintree{}}, + "1603888149_create_chat_identity_last_published_table.up.sql": {_1603888149_create_chat_identity_last_published_tableUpSql, map[string]*bintree{}}, + "1605075346_add_communities.up.sql": {_1605075346_add_communitiesUpSql, map[string]*bintree{}}, + "1610117927_add_message_cache.up.sql": {_1610117927_add_message_cacheUpSql, map[string]*bintree{}}, + "1610959908_add_dont_wrap_to_raw_messages.up.sql": {_1610959908_add_dont_wrap_to_raw_messagesUpSql, map[string]*bintree{}}, + "1610960912_add_send_on_personal_topic.up.sql": {_1610960912_add_send_on_personal_topicUpSql, map[string]*bintree{}}, + "1612870480_add_datasync_id.up.sql": {_1612870480_add_datasync_idUpSql, map[string]*bintree{}}, + "README.md": {readmeMd, map[string]*bintree{}}, + "doc.go": {docGo, map[string]*bintree{}}, }} // RestoreAsset restores an asset under the given directory. diff --git a/protocol/migrations/sqlite/1612870480_add_datasync_id.up.sql b/protocol/migrations/sqlite/1612870480_add_datasync_id.up.sql new file mode 100644 index 000000000..1af18b1a7 --- /dev/null +++ b/protocol/migrations/sqlite/1612870480_add_datasync_id.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE raw_messages ADD COLUMN datasync_id BLOB; +CREATE INDEX idx_datsync_id ON raw_messages(datasync_id); \ No newline at end of file diff --git a/protocol/persistence.go b/protocol/persistence.go index fb5472daa..c8bdf077c 100644 --- a/protocol/persistence.go +++ b/protocol/persistence.go @@ -593,9 +593,10 @@ func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error { send_push_notification, skip_group_message_wrap, send_on_personal_topic, - payload + payload, + datasync_id ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, message.ID, message.LocalChatID, message.LastSent, @@ -608,7 +609,8 @@ func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error { message.SendPushNotification, message.SkipGroupMessageWrap, message.SendOnPersonalTopic, - message.Payload) + message.Payload, + message.DataSyncID) return err } @@ -633,7 +635,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error send_push_notification, skip_group_message_wrap, send_on_personal_topic, - payload + payload, + datasync_id FROM raw_messages WHERE @@ -653,6 +656,7 @@ func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error &skipGroupMessageWrap, &sendOnPersonalTopic, &message.Payload, + &message.DataSyncID, ) if err != nil { return nil, err @@ -739,6 +743,28 @@ func (db sqlitePersistence) ExpiredEmojiReactionsIDs(maxSendCount int) ([]string return ids, nil } +func (db sqlitePersistence) RawMessageIDFromDatasyncID(datasyncID []byte) (id string, err error) { + row := db.db.QueryRow( + `SELECT + id + FROM + raw_messages + WHERE + datasync_id = ? + `, datasyncID) + + err = row.Scan(&id) + + switch err { + case sql.ErrNoRows: + return "", common.ErrRecordNotFound + case nil: + return id, nil + default: + return "", err + } +} + func (db sqlitePersistence) SaveContact(contact *Contact, tx *sql.Tx) (err error) { if tx == nil { tx, err = db.db.BeginTx(context.Background(), &sql.TxOptions{}) diff --git a/protocol/persistence_test.go b/protocol/persistence_test.go index 065c2e06c..4fbf0fd72 100644 --- a/protocol/persistence_test.go +++ b/protocol/persistence_test.go @@ -777,6 +777,28 @@ func TestHideMessage(t *testing.T) { require.True(t, actualSeen) } +func TestDatasyncIdSaved(t *testing.T) { + db, err := openTestDB() + require.NoError(t, err) + p := sqlitePersistence{db: db} + + messageID := "message-id" + messageDataSyncID := []byte("message-datasync-id") + + _, err = p.RawMessageIDFromDatasyncID(messageDataSyncID) + require.Error(t, err) + + //save message with datasync id + rawChatMessage := minimalRawMessage(messageID, protobuf.ApplicationMetadataMessage_CHAT_MESSAGE) + rawChatMessage.DataSyncID = messageDataSyncID + err = p.SaveRawMessage(rawChatMessage) + require.NoError(t, err) + + id, err := p.RawMessageIDFromDatasyncID(messageDataSyncID) + require.NoError(t, err) + require.Equal(t, messageID, id) +} + func TestDeactivatePublicChat(t *testing.T) { db, err := openTestDB() require.NoError(t, err) diff --git a/protocol/pushnotificationclient/client.go b/protocol/pushnotificationclient/client.go index fc4f7392f..b85e0edd9 100644 --- a/protocol/pushnotificationclient/client.go +++ b/protocol/pushnotificationclient/client.go @@ -1272,7 +1272,7 @@ func (c *Client) registerWithServer(registration *protobuf.PushNotificationRegis SkipEncryption: true, } - _, err = c.messageProcessor.SendPrivate(context.Background(), server.PublicKey, rawMessage) + _, err = c.messageProcessor.SendPrivate(context.Background(), server.PublicKey, &rawMessage) if err != nil { return err @@ -1374,7 +1374,7 @@ func (c *Client) sendNotification(publicKey *ecdsa.PublicKey, installationIDs [] MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST, } - _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, rawMessage) + _, err = c.messageProcessor.SendPrivate(context.Background(), serverPublicKey, &rawMessage) if err != nil { return nil, err diff --git a/protocol/pushnotificationserver/server.go b/protocol/pushnotificationserver/server.go index 718ccfe9a..0c126be7a 100644 --- a/protocol/pushnotificationserver/server.go +++ b/protocol/pushnotificationserver/server.go @@ -110,7 +110,7 @@ func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, SkipEncryption: true, } - _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, &rawMessage) return err } @@ -133,7 +133,7 @@ func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, message SkipEncryption: true, } - _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, &rawMessage) return err } @@ -176,7 +176,7 @@ func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey, SkipEncryption: true, } - _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage) + _, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, &rawMessage) return err } diff --git a/protocol/v1/status_message.go b/protocol/v1/status_message.go index 3f07677f8..4a35dc271 100644 --- a/protocol/v1/status_message.go +++ b/protocol/v1/status_message.go @@ -12,7 +12,6 @@ import ( "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/types" - "github.com/status-im/status-go/protocol/datasync" "github.com/status-im/status-go/protocol/encryption" "github.com/status-im/status-go/protocol/encryption/multidevice" "github.com/status-im/status-go/protocol/encryption/sharedsecret" @@ -143,28 +142,6 @@ func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecd return nil } -// HandleDatasync processes StatusMessage through data sync layer. -// This is optional and DataSync might be nil. In such a case, -// only one payload will be returned equal to DecryptedPayload. -func (m *StatusMessage) HandleDatasync(datasync *datasync.DataSync) ([]*StatusMessage, error) { - var statusMessages []*StatusMessage - - payloads := datasync.Handle( - m.SigPubKey(), - m.DecryptedPayload, - ) - - for _, payload := range payloads { - message, err := m.Clone() - if err != nil { - return nil, err - } - message.DecryptedPayload = payload - statusMessages = append(statusMessages, message) - } - return statusMessages, nil -} - func (m *StatusMessage) HandleApplicationMetadata() error { message, err := protobuf.Unmarshal(m.DecryptedPayload) if err != nil { diff --git a/services/ext/service.go b/services/ext/service.go index be5927c79..56ccdddcd 100644 --- a/services/ext/service.go +++ b/services/ext/service.go @@ -153,7 +153,7 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, multiAcco s.multiAccountsDB = multiAccountDb s.account = acc - options, err := buildMessengerOptions(s.config, identity, db, s.multiAccountsDB, acc, envelopesMonitorConfig, s.accountsDB, logger) + options, err := buildMessengerOptions(s.config, identity, db, s.multiAccountsDB, acc, envelopesMonitorConfig, s.accountsDB, logger, signal.SendMessageDelivered) if err != nil { return err } @@ -467,6 +467,7 @@ func buildMessengerOptions( envelopesMonitorConfig *transport.EnvelopesMonitorConfig, accountsDB *accounts.Database, logger *zap.Logger, + messageDeliveredHandler protocol.MessageDeliveredHandler, ) ([]protocol.Option, error) { options := []protocol.Option{ protocol.WithCustomLogger(logger), @@ -477,7 +478,7 @@ func buildMessengerOptions( protocol.WithAccount(account), protocol.WithEnvelopesMonitorConfig(envelopesMonitorConfig), protocol.WithOnNegotiatedFilters(onNegotiatedFilters), - } + protocol.WithDeliveredHandler(messageDeliveredHandler)} if config.DataSyncEnabled { options = append(options, protocol.WithDatasync()) diff --git a/signal/events_messenger.go b/signal/events_messenger.go new file mode 100644 index 000000000..6475c2899 --- /dev/null +++ b/signal/events_messenger.go @@ -0,0 +1,18 @@ +package signal + +const ( + + // EventMesssageDelivered triggered when we got acknowledge from datasync level, that means peer got message + EventMesssageDelivered = "message.delivered" +) + +// MessageDeliveredSignal specifies chat and message that was delivered +type MessageDeliveredSignal struct { + ChatID string `json:"chatID"` + MessageID string `json:"messageID"` +} + +// SendMessageDelivered notifies about delivered message +func SendMessageDelivered(chatID string, messageID string) { + send(EventMesssageDelivered, MessageDeliveredSignal{ChatID: chatID, MessageID: messageID}) +}