diff --git a/VERSION b/VERSION index 222985a35b..d4ca2c6b16 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.26.0-beta.0 +0.27.0-beta.0 diff --git a/api/backend.go b/api/backend.go index 5af3dab41c..b2a5bec6d1 100644 --- a/api/backend.go +++ b/api/backend.go @@ -26,7 +26,6 @@ import ( "github.com/status-im/status-go/services/personal" "github.com/status-im/status-go/services/rpcfilters" "github.com/status-im/status-go/services/shhext/chat/crypto" - "github.com/status-im/status-go/services/shhext/filter" "github.com/status-im/status-go/services/subscriptions" "github.com/status-im/status-go/services/typeddata" "github.com/status-im/status-go/signal" @@ -659,36 +658,6 @@ func (b *StatusBackend) SignGroupMembership(content string) (string, error) { return crypto.Sign(content, selectedChatAccount.AccountKey.PrivateKey) } -// LoadFilters loads filter on sshext -func (b *StatusBackend) LoadFilters(chats []*filter.Chat) ([]*filter.Chat, error) { - st, err := b.statusNode.ShhExtService() - if err != nil { - return nil, err - } - - return st.LoadFilters(chats) -} - -// LoadFilter loads filter on sshext -func (b *StatusBackend) LoadFilter(chat *filter.Chat) ([]*filter.Chat, error) { - st, err := b.statusNode.ShhExtService() - if err != nil { - return nil, err - } - - return st.LoadFilter(chat) -} - -// RemoveFilter remove a filter -func (b *StatusBackend) RemoveFilter(chat *filter.Chat) error { - st, err := b.statusNode.ShhExtService() - if err != nil { - return err - } - - return st.RemoveFilter(chat) -} - // EnableInstallation enables an installation for multi-device sync. func (b *StatusBackend) EnableInstallation(installationID string) error { selectedChatAccount, err := b.AccountManager().SelectedChatAccount() diff --git a/lib/library.go b/lib/library.go index 91d76af0e3..b1faeef9f5 100644 --- a/lib/library.go +++ b/lib/library.go @@ -18,7 +18,6 @@ import ( "github.com/status-im/status-go/params" "github.com/status-im/status-go/profiling" "github.com/status-im/status-go/services/personal" - "github.com/status-im/status-go/services/shhext/filter" "github.com/status-im/status-go/services/typeddata" "github.com/status-im/status-go/signal" "github.com/status-im/status-go/transactions" @@ -52,79 +51,6 @@ func StopNode() *C.char { return makeJSONResponse(nil) } -// LoadFilters load all whisper filters -//export LoadFilters -func LoadFilters(chatsStr *C.char) *C.char { - var chats []*filter.Chat - - if err := json.Unmarshal([]byte(C.GoString(chatsStr)), &chats); err != nil { - return makeJSONResponse(err) - } - - response, err := statusBackend.LoadFilters(chats) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Chats []*filter.Chat `json:"result"` - }{Chats: response}) - if err != nil { - return makeJSONResponse(err) - } - - return C.CString(string(data)) -} - -// LoadFilter load a whisper filter -//export LoadFilter -func LoadFilter(chatStr *C.char) *C.char { - var chat *filter.Chat - - if err := json.Unmarshal([]byte(C.GoString(chatStr)), &chat); err != nil { - return makeJSONResponse(err) - } - - response, err := statusBackend.LoadFilter(chat) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Chats []*filter.Chat `json:"result"` - }{Chats: response}) - - if err != nil { - return makeJSONResponse(err) - } - - return C.CString(string(data)) -} - -// RemoveFilter load a whisper filter -//export RemoveFilter -func RemoveFilter(chatStr *C.char) *C.char { - var chat *filter.Chat - - if err := json.Unmarshal([]byte(C.GoString(chatStr)), &chat); err != nil { - return makeJSONResponse(err) - } - - err := statusBackend.RemoveFilter(chat) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Response string `json:"response"` - }{Response: "ok"}) - if err != nil { - return makeJSONResponse(err) - } - - return C.CString(string(data)) -} - // ExtractGroupMembershipSignatures extract public keys from tuples of content/signature //export ExtractGroupMembershipSignatures func ExtractGroupMembershipSignatures(signaturePairsStr *C.char) *C.char { diff --git a/mobile/status.go b/mobile/status.go index 24ee9b6b92..598affc54f 100644 --- a/mobile/status.go +++ b/mobile/status.go @@ -16,7 +16,6 @@ import ( "github.com/status-im/status-go/params" "github.com/status-im/status-go/profiling" "github.com/status-im/status-go/services/personal" - "github.com/status-im/status-go/services/shhext/filter" "github.com/status-im/status-go/services/typeddata" "github.com/status-im/status-go/signal" "github.com/status-im/status-go/transactions" @@ -614,73 +613,3 @@ func SignHash(hexEncodedHash string) string { return hexEncodedSignature } - -// LoadFilters load all whisper filters -func LoadFilters(chatsStr string) string { - var chats []*filter.Chat - - if err := json.Unmarshal([]byte(chatsStr), &chats); err != nil { - return makeJSONResponse(err) - } - - response, err := statusBackend.LoadFilters(chats) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Chats []*filter.Chat `json:"result"` - }{Chats: response}) - if err != nil { - return makeJSONResponse(err) - } - - return string(data) -} - -// LoadFilter load a whisper filter -func LoadFilter(chatStr string) string { - var chat *filter.Chat - - if err := json.Unmarshal([]byte(chatStr), &chat); err != nil { - return makeJSONResponse(err) - } - - response, err := statusBackend.LoadFilter(chat) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Chats []*filter.Chat `json:"result"` - }{Chats: response}) - if err != nil { - return makeJSONResponse(err) - } - - return string(data) -} - -// RemoveFilter load a whisper filter -//export RemoveFilter -func RemoveFilter(chatStr string) string { - var chat *filter.Chat - - if err := json.Unmarshal([]byte(chatStr), &chat); err != nil { - return makeJSONResponse(err) - } - - err := statusBackend.RemoveFilter(chat) - if err != nil { - return makeJSONResponse(err) - } - - data, err := json.Marshal(struct { - Response string `json:"response"` - }{Response: "ok"}) - if err != nil { - return makeJSONResponse(err) - } - - return string(data) -} diff --git a/services/shhext/api.go b/services/shhext/api.go index 237610528d..7ba6ceef2d 100644 --- a/services/shhext/api.go +++ b/services/shhext/api.go @@ -19,6 +19,7 @@ import ( "github.com/status-im/status-go/mailserver" "github.com/status-im/status-go/services/shhext/chat" "github.com/status-im/status-go/services/shhext/dedup" + "github.com/status-im/status-go/services/shhext/filter" "github.com/status-im/status-go/services/shhext/mailservers" whisper "github.com/status-im/whisper/whisperv6" ) @@ -466,12 +467,22 @@ func (api *PublicAPI) ConfirmMessagesProcessedByID(messageIDs [][]byte) error { // SendPublicMessage sends a public chat message to the underlying transport func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg chat.SendPublicMessageRPC) (hexutil.Bytes, error) { - return api.service.SendPublicMessage(ctx, msg) + message, err := api.service.CreatePublicMessage(msg.Sig, msg.Chat, msg.Payload, false) + if err != nil { + return nil, err + } + + return api.Post(ctx, *message) } // SendDirectMessage sends a 1:1 chat message to the underlying transport func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirectMessageRPC) (hexutil.Bytes, error) { - return api.service.SendDirectMessage(ctx, msg) + message, err := api.service.CreateDirectMessage(msg.Sig, msg.PubKey, msg.DH, msg.Payload) + if err != nil { + return nil, err + } + + return api.Post(ctx, *message) } func (api *PublicAPI) requestMessagesUsingPayload(request db.HistoryRequest, peer, symkeyID string, payload []byte, force bool, timeout time.Duration, topics []whisper.TopicType) (hash common.Hash, err error) { @@ -582,6 +593,21 @@ func (api *PublicAPI) CompleteRequest(parent context.Context, hex string) (err e return err } +// LoadFilters load all the necessary filters +func (api *PublicAPI) LoadFilters(parent context.Context, chats []*filter.Chat) ([]*filter.Chat, error) { + return api.service.LoadFilters(chats) +} + +// LoadFilter load a single filter +func (api *PublicAPI) LoadFilter(parent context.Context, chat *filter.Chat) ([]*filter.Chat, error) { + return api.service.LoadFilter(chat) +} + +// RemoveFilter remove a single filter +func (api *PublicAPI) RemoveFilters(parent context.Context, chats []*filter.Chat) error { + return api.service.RemoveFilters(chats) +} + // ----- // HELPER // ----- diff --git a/services/shhext/chat/db/db.go b/services/shhext/chat/db/db.go index 4551802212..bb4464c257 100644 --- a/services/shhext/chat/db/db.go +++ b/services/shhext/chat/db/db.go @@ -6,9 +6,9 @@ import ( "os" sqlite "github.com/mutecomm/go-sqlcipher" // We require go sqlcipher that overrides default implementation - "github.com/status-im/migrate" - "github.com/status-im/migrate/database/sqlcipher" - "github.com/status-im/migrate/source/go_bindata" + "github.com/status-im/migrate/v4" + "github.com/status-im/migrate/v4/database/sqlcipher" + "github.com/status-im/migrate/v4/source/go_bindata" "github.com/status-im/status-go/services/shhext/chat/db/migrations" ) diff --git a/services/shhext/chat/db/migrations/bindata.go b/services/shhext/chat/db/migrations/bindata.go index 26eac2e96a..86ceda0bc1 100644 --- a/services/shhext/chat/db/migrations/bindata.go +++ b/services/shhext/chat/db/migrations/bindata.go @@ -11,6 +11,8 @@ // 1558084410_add_secret.down.sql // 1558084410_add_secret.up.sql // 1558588866_add_version.up.sql +// 1559627659_add_contact_code.down.sql +// 1559627659_add_contact_code.up.sql // static.go // DO NOT EDIT! @@ -299,6 +301,46 @@ func _1558588866_add_versionUpSql() (*asset, error) { return a, nil } +var __1559627659_add_contact_codeDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x48\xce\xcf\x2b\x49\x4c\x2e\x89\x4f\xce\x4f\x49\x8d\x4f\xce\xcf\x4b\xcb\x4c\xb7\xe6\x02\x04\x00\x00\xff\xff\x73\x7b\x50\x80\x20\x00\x00\x00") + +func _1559627659_add_contact_codeDownSqlBytes() ([]byte, error) { + return bindataRead( + __1559627659_add_contact_codeDownSql, + "1559627659_add_contact_code.down.sql", + ) +} + +func _1559627659_add_contact_codeDownSql() (*asset, error) { + bytes, err := _1559627659_add_contact_codeDownSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(420), modTime: time.Unix(1560418335, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var __1559627659_add_contact_codeUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xce\xc1\x8e\x82\x30\x18\x04\xe0\x7b\x9f\x62\x6e\x40\xb2\x07\xf6\xcc\xa9\xbb\xfb\xaf\x21\xd6\x62\x4a\x31\x72\x22\xb5\xa0\x34\x21\x45\xa1\xf8\xfc\x06\x13\xe3\xc5\xeb\xe4\x9b\xc9\xfc\x2a\xe2\x9a\xa0\xf9\x8f\x20\xd8\xd1\x07\x63\x43\x63\xc7\xb6\x6b\xec\xe8\xcf\xee\x82\x98\x01\x58\xbc\xbb\x2d\xcf\x68\x0e\x93\x71\x3e\xe0\x6e\x26\xdb\x9b\x29\xfe\x4e\x20\x0b\x0d\x59\x09\x81\xbd\xca\x77\x5c\xd5\xd8\x52\x8d\x3f\xfa\xe7\x95\xd0\x88\x8e\xd1\x17\x03\x06\x33\x87\xe6\xba\x9c\x06\x37\xf7\x5d\x8b\x5c\x6a\xda\x90\x7a\x57\x5f\x3c\x65\x49\xc6\x58\x2e\x4b\x52\x7a\x55\xc5\xc7\x4f\x07\x2e\x2a\x2a\x11\xaf\xe3\x48\x93\x8c\x3d\x02\x00\x00\xff\xff\xdc\x7c\x0c\xd3\xc6\x00\x00\x00") + +func _1559627659_add_contact_codeUpSqlBytes() ([]byte, error) { + return bindataRead( + __1559627659_add_contact_codeUpSql, + "1559627659_add_contact_code.up.sql", + ) +} + +func _1559627659_add_contact_codeUpSql() (*asset, error) { + bytes, err := _1559627659_add_contact_codeUpSqlBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(420), modTime: time.Unix(1560418335, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _staticGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x54\xcc\x41\x6a\x03\x31\x0c\x46\xe1\xbd\x4f\xf1\x2f\x5b\xe8\x58\xfb\x9e\xa0\x94\x16\x0a\xcd\x05\x64\x8f\x90\xc5\x30\xf6\x60\x29\x21\xc7\xcf\x26\x21\x64\xf9\xe0\xf1\x11\xe1\x8f\xeb\xc6\x2a\xf0\xe0\xb0\x0a\xd9\x8b\xac\xfe\xa8\xb7\xef\xff\x0f\x7c\x9d\x7e\x7f\xde\x31\xc5\xc7\x79\x56\x71\x4c\xd3\x16\xb0\x1e\x03\xd1\x04\xc5\x3a\x4f\x13\x4f\xc7\x8b\x94\x12\x91\x8e\x4f\x95\x2e\x93\x43\xa0\x63\x29\xd6\x57\x0e\xc6\x72\x6c\x8a\xdd\x74\x72\xd8\xe8\x8e\x65\x20\x67\xca\x99\x5c\xe6\xc5\xaa\x38\x79\x6b\x72\x0d\xaa\x8d\x83\xd6\x42\xcf\x97\xee\x46\xd6\x81\x9c\x6e\x01\x00\x00\xff\xff\x6c\x21\xbf\x7a\xbf\x00\x00\x00") func staticGoBytes() ([]byte, error) { @@ -382,6 +424,8 @@ var _bindata = map[string]func() (*asset, error){ "1558084410_add_secret.down.sql": _1558084410_add_secretDownSql, "1558084410_add_secret.up.sql": _1558084410_add_secretUpSql, "1558588866_add_version.up.sql": _1558588866_add_versionUpSql, + "1559627659_add_contact_code.down.sql": _1559627659_add_contact_codeDownSql, + "1559627659_add_contact_code.up.sql": _1559627659_add_contact_codeUpSql, "static.go": staticGo, } @@ -436,6 +480,8 @@ var _bintree = &bintree{nil, map[string]*bintree{ "1558084410_add_secret.down.sql": &bintree{_1558084410_add_secretDownSql, map[string]*bintree{}}, "1558084410_add_secret.up.sql": &bintree{_1558084410_add_secretUpSql, map[string]*bintree{}}, "1558588866_add_version.up.sql": &bintree{_1558588866_add_versionUpSql, map[string]*bintree{}}, + "1559627659_add_contact_code.down.sql": &bintree{_1559627659_add_contact_codeDownSql, map[string]*bintree{}}, + "1559627659_add_contact_code.up.sql": &bintree{_1559627659_add_contact_codeUpSql, map[string]*bintree{}}, "static.go": &bintree{staticGo, map[string]*bintree{}}, }} diff --git a/services/shhext/chat/encryption.go b/services/shhext/chat/encryption.go index 6c2744bf60..d6c3ac6d85 100644 --- a/services/shhext/chat/encryption.go +++ b/services/shhext/chat/encryption.go @@ -464,6 +464,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my // We don't have any, send a message with DH if len(installations) == 0 { + s.log.Debug("no installations, sending to all devices") encryptedPayload, err := s.EncryptPayloadWithDH(theirIdentityKey, payload) return encryptedPayload, targetedInstallations, err } diff --git a/services/shhext/chat/encryption_multi_device_test.go b/services/shhext/chat/encryption_multi_device_test.go index 96299abdb0..16554671f9 100644 --- a/services/shhext/chat/encryption_multi_device_test.go +++ b/services/shhext/chat/encryption_multi_device_test.go @@ -69,7 +69,7 @@ func setupUser(user string, s *EncryptionServiceMultiDeviceSuite, n int) error { DefaultEncryptionServiceConfig(installationID)), sharedSecretService, multideviceService, - func(s []multidevice.IdentityAndIDPair) {}, + func(s []*multidevice.IdentityAndID) {}, func(s []*sharedsecret.Secret) {}, ) @@ -111,12 +111,12 @@ func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() { // Add alice2 bundle response, err := s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice2Bundle) s.Require().NoError(err) - s.Require().Equal(multidevice.IdentityAndIDPair{alice2Identity, "alice2"}, response[0]) + s.Require().Equal(multidevice.IdentityAndID{alice2Identity, "alice2"}, *response[0]) // Add alice3 bundle response, err = s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice3Bundle) s.Require().NoError(err) - s.Require().Equal(multidevice.IdentityAndIDPair{alice3Identity, "alice3"}, response[0]) + s.Require().Equal(multidevice.IdentityAndID{alice3Identity, "alice3"}, *response[0]) // No installation is enabled alice1MergedBundle1, err := s.services[aliceUser].services[0].GetBundle(aliceKey) @@ -144,12 +144,12 @@ func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() { response, err = s.services[aliceUser].services[0].ProcessPublicBundle(aliceKey, alice1MergedBundle2) s.Require().NoError(err) sort.Slice(response, func(i, j int) bool { - return response[i][1] < response[j][1] + return response[i].ID < response[j].ID }) // We only get back installationIDs not equal to us s.Require().Equal(2, len(response)) - s.Require().Equal(multidevice.IdentityAndIDPair{alice2Identity, "alice2"}, response[0]) - s.Require().Equal(multidevice.IdentityAndIDPair{alice2Identity, "alice3"}, response[1]) + s.Require().Equal(multidevice.IdentityAndID{alice2Identity, "alice2"}, *response[0]) + s.Require().Equal(multidevice.IdentityAndID{alice2Identity, "alice3"}, *response[1]) // We disable the installations err = s.services[aliceUser].services[0].DisableInstallation(&aliceKey.PublicKey, "alice2") diff --git a/services/shhext/chat/encryption_test.go b/services/shhext/chat/encryption_test.go index d88f237de0..808233e969 100644 --- a/services/shhext/chat/encryption_test.go +++ b/services/shhext/chat/encryption_test.go @@ -80,7 +80,7 @@ func (s *EncryptionServiceTestSuite) initDatabases(baseConfig *EncryptionService aliceEncryptionService, aliceSharedSecretService, aliceMultideviceService, - func(s []multidevice.IdentityAndIDPair) {}, + func(s []*multidevice.IdentityAndID) {}, func(s []*sharedsecret.Secret) {}, ) @@ -106,7 +106,7 @@ func (s *EncryptionServiceTestSuite) initDatabases(baseConfig *EncryptionService bobEncryptionService, bobSharedSecretService, bobMultideviceService, - func(s []multidevice.IdentityAndIDPair) {}, + func(s []*multidevice.IdentityAndID) {}, func(s []*sharedsecret.Secret) {}, ) diff --git a/services/shhext/chat/multidevice/service.go b/services/shhext/chat/multidevice/service.go index 6cf208e671..b2f6b0b230 100644 --- a/services/shhext/chat/multidevice/service.go +++ b/services/shhext/chat/multidevice/service.go @@ -8,7 +8,9 @@ import ( ) type Installation struct { - ID string + // The installation-id of the device + ID string + // The last known protocol version of the device Version uint32 } @@ -30,7 +32,10 @@ type Service struct { config *Config } -type IdentityAndIDPair [2]string +type IdentityAndID struct { + Identity string + ID string +} func (s *Service) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) { identityC := crypto.CompressPubkey(identity) @@ -43,7 +48,7 @@ func (s *Service) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Insta if err != nil { return nil, err } - // Move to layer above + installations = append(installations, &Installation{ ID: s.config.InstallationID, Version: s.config.ProtocolVersion, @@ -63,9 +68,9 @@ func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installati } // ProcessPublicBundle persists a bundle and returns a list of tuples identity/installationID -func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, theirIdentity *ecdsa.PublicKey, b *protobuf.Bundle) ([]IdentityAndIDPair, error) { +func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, theirIdentity *ecdsa.PublicKey, b *protobuf.Bundle) ([]*IdentityAndID, error) { signedPreKeys := b.GetSignedPreKeys() - var response []IdentityAndIDPair + var response []*IdentityAndID var installations []*Installation myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey)) @@ -81,7 +86,7 @@ func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, theirIden ID: installationID, Version: signedPreKey.GetProtocolVersion(), }) - response = append(response, IdentityAndIDPair{theirIdentityStr, installationID}) + response = append(response, &IdentityAndID{theirIdentityStr, installationID}) } } diff --git a/services/shhext/chat/multidevice/sql_lite_persistence.go b/services/shhext/chat/multidevice/sql_lite_persistence.go index ef270091e3..7333e846ed 100644 --- a/services/shhext/chat/multidevice/sql_lite_persistence.go +++ b/services/shhext/chat/multidevice/sql_lite_persistence.go @@ -79,53 +79,52 @@ func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64, return err } - // We update timestamp if present without changing enabled, only if this is a new bundle - // and we set the version to the latest we ever saw - if err != sql.ErrNoRows { - if oldVersion > installation.Version { - latestVersion = oldVersion - } - - stmt, err = tx.Prepare(`UPDATE installations - SET timestamp = ?, enabled = ?, version = ? - WHERE identity = ? - AND installation_id = ? - AND timestamp < ?`) + if err == sql.ErrNoRows { + stmt, err = tx.Prepare(`INSERT INTO installations(identity, installation_id, timestamp, enabled, version) + VALUES (?, ?, ?, ?, ?)`) if err != nil { return err } + defer stmt.Close() _, err = stmt.Exec( - timestamp, - oldEnabled, - latestVersion, identity, installation.ID, timestamp, + defaultEnabled, + latestVersion, ) if err != nil { return err } - defer stmt.Close() - } else { - stmt, err = tx.Prepare(`INSERT INTO installations(identity, installation_id, timestamp, enabled, version) - VALUES (?, ?, ?, ?, ?)`) + // We update timestamp if present without changing enabled, only if this is a new bundle + // and we set the version to the latest we ever saw + if oldVersion > installation.Version { + latestVersion = oldVersion + } + + stmt, err = tx.Prepare(`UPDATE installations + SET timestamp = ?, enabled = ?, version = ? + WHERE identity = ? + AND installation_id = ? + AND timestamp < ?`) if err != nil { return err } + defer stmt.Close() _, err = stmt.Exec( + timestamp, + oldEnabled, + latestVersion, identity, installation.ID, timestamp, - defaultEnabled, - latestVersion, ) if err != nil { return err } - defer stmt.Close() } } diff --git a/services/shhext/chat/multidevice/sql_lite_persistence_test.go b/services/shhext/chat/multidevice/sql_lite_persistence_test.go index 3ff6ce2cf1..1f359cfc8d 100644 --- a/services/shhext/chat/multidevice/sql_lite_persistence_test.go +++ b/services/shhext/chat/multidevice/sql_lite_persistence_test.go @@ -5,7 +5,7 @@ import ( "os" "testing" - appDB "github.com/status-im/status-go/services/shhext/chat/db" + chatDB "github.com/status-im/status-go/services/shhext/chat/db" "github.com/stretchr/testify/suite" ) @@ -27,7 +27,7 @@ type SQLLitePersistenceTestSuite struct { func (s *SQLLitePersistenceTestSuite) SetupTest() { os.Remove(dbPath) - db, err := appDB.Open(dbPath, "", 0) + db, err := chatDB.Open(dbPath, "", 0) s.Require().NoError(err) s.service = NewSQLLitePersistence(db) diff --git a/services/shhext/chat/protocol.go b/services/shhext/chat/protocol.go index f004de885e..0e790983a4 100644 --- a/services/shhext/chat/protocol.go +++ b/services/shhext/chat/protocol.go @@ -13,13 +13,21 @@ import ( const ProtocolVersion = 1 const sharedSecretNegotiationVersion = 1 const partitionedTopicMinVersion = 1 +const defaultMinVersion = 0 + +type PartitionTopic int + +const ( + PartitionTopicNoSupport PartitionTopic = iota + PartitionTopicV1 +) type ProtocolService struct { log log.Logger encryption *EncryptionService secret *sharedsecret.Service multidevice *multidevice.Service - addedBundlesHandler func([]multidevice.IdentityAndIDPair) + addedBundlesHandler func([]*multidevice.IdentityAndID) onNewSharedSecretHandler func([]*sharedsecret.Secret) Enabled bool } @@ -27,7 +35,7 @@ type ProtocolService struct { var ErrNotProtocolMessage = errors.New("Not a protocol message") // NewProtocolService creates a new ProtocolService instance -func NewProtocolService(encryption *EncryptionService, secret *sharedsecret.Service, multidevice *multidevice.Service, addedBundlesHandler func([]multidevice.IdentityAndIDPair), onNewSharedSecretHandler func([]*sharedsecret.Secret)) *ProtocolService { +func NewProtocolService(encryption *EncryptionService, secret *sharedsecret.Service, multidevice *multidevice.Service, addedBundlesHandler func([]*multidevice.IdentityAndID), onNewSharedSecretHandler func([]*sharedsecret.Secret)) *ProtocolService { return &ProtocolService{ log: log.New("package", "status-go/services/sshext.chat"), encryption: encryption, @@ -84,21 +92,25 @@ type ProtocolMessageSpec struct { func (p *ProtocolMessageSpec) MinVersion() uint32 { - var version uint32 + if len(p.Installations) == 0 { + return defaultMinVersion + } + + version := p.Installations[0].Version - for _, installation := range p.Installations { + for _, installation := range p.Installations[1:] { if installation.Version < version { version = installation.Version } } return version - } -func (p *ProtocolMessageSpec) PartitionedTopic() bool { - - return p.MinVersion() >= partitionedTopicMinVersion - +func (p *ProtocolMessageSpec) PartitionedTopic() PartitionTopic { + if p.MinVersion() >= partitionedTopicMinVersion { + return PartitionTopicV1 + } + return PartitionTopicNoSupport } // BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload @@ -181,12 +193,13 @@ func (p *ProtocolService) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destin } // ProcessPublicBundle processes a received X3DH bundle. -func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]multidevice.IdentityAndIDPair, error) { +func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.IdentityAndID, error) { if err := p.encryption.ProcessPublicBundle(myIdentityKey, bundle); err != nil { return nil, err } theirIdentityKey, err := ExtractIdentity(bundle) + p.log.Debug("Processing bundle", "bundle", bundle) if err != nil { return nil, err } @@ -230,6 +243,7 @@ func (p *ProtocolService) ConfirmMessagesProcessed(messageIDs [][]byte) error { // HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message. func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, protocolMessage *protobuf.ProtocolMessage, messageID []byte) ([]byte, error) { + p.log.Debug("Received message from", "public-key", theirPublicKey) if p.encryption == nil { return nil, errors.New("encryption service not initialized") } @@ -258,25 +272,25 @@ func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPu // Check if it's a public message if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil { + p.log.Debug("Public message, nothing to do") // Nothing to do, as already in cleartext return publicMessage, nil } // Decrypt message if directMessage := protocolMessage.GetDirectMessage(); directMessage != nil { + p.log.Debug("Processing direct message") message, err := p.encryption.DecryptPayload(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId(), directMessage, messageID) if err != nil { return nil, err } - var bundles []*protobuf.Bundle - p.log.Info("Checking version") // Handle protocol negotiation for compatible clients - p.log.Info("bundle", "bundles", protocolMessage) - bundles = append(protocolMessage.GetBundles(), protocolMessage.GetBundle()) + bundles := append(protocolMessage.GetBundles(), protocolMessage.GetBundle()) version := getProtocolVersion(bundles, protocolMessage.GetInstallationId()) + p.log.Debug("Message version is", "version", version) if version >= sharedSecretNegotiationVersion { - p.log.Info("Version greater than 1 negotianting") + p.log.Debug("Negotiating shared secret") sharedSecret, err := p.secret.Receive(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId()) if err != nil { return nil, err @@ -294,7 +308,7 @@ func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPu func getProtocolVersion(bundles []*protobuf.Bundle, installationID string) uint32 { if installationID == "" { - return 0 + return defaultMinVersion } for _, bundle := range bundles { @@ -306,12 +320,12 @@ func getProtocolVersion(bundles []*protobuf.Bundle, installationID string) uint3 signedPreKey := signedPreKeys[installationID] if signedPreKey == nil { - return 0 + return defaultMinVersion } return signedPreKey.GetProtocolVersion() } } - return 0 + return defaultMinVersion } diff --git a/services/shhext/chat/protocol_test.go b/services/shhext/chat/protocol_test.go index 3ab845cd88..fe6e36efbf 100644 --- a/services/shhext/chat/protocol_test.go +++ b/services/shhext/chat/protocol_test.go @@ -39,7 +39,7 @@ func (s *ProtocolServiceTestSuite) SetupTest() { panic(err) } - addedBundlesHandler := func(addedBundles []multidevice.IdentityAndIDPair) {} + addedBundlesHandler := func(addedBundles []*multidevice.IdentityAndID) {} onNewSharedSecretHandler := func(secret []*sharedsecret.Secret) {} aliceMultideviceConfig := &multidevice.Config{ diff --git a/services/shhext/chat/sharedsecret/persistence.go b/services/shhext/chat/sharedsecret/persistence.go index 9a7769f0ed..958f47b534 100644 --- a/services/shhext/chat/sharedsecret/persistence.go +++ b/services/shhext/chat/sharedsecret/persistence.go @@ -6,8 +6,12 @@ import ( ) type PersistenceService interface { + // Add adds a shared secret, associated with an identity and an installationID Add(identity []byte, secret []byte, installationID string) error + // Get returns a shared secret associated with multiple installationIDs Get(identity []byte, installationIDs []string) (*Response, error) + // All returns an array of shared secrets, each one of them represented + // as a byte array All() ([][][]byte, error) } diff --git a/services/shhext/chat/sharedsecret/service.go b/services/shhext/chat/sharedsecret/service.go index f674852fec..370463078b 100644 --- a/services/shhext/chat/sharedsecret/service.go +++ b/services/shhext/chat/sharedsecret/service.go @@ -10,15 +10,19 @@ import ( const sskLen = 16 type Service struct { + log log.Logger persistence PersistenceService } func NewService(persistence PersistenceService) *Service { - return &Service{persistence: persistence} + return &Service{ + log: log.New("package", "status-go/services/sshext/chat.sharedsecret"), + persistence: persistence, + } } func (s *Service) setup(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { - log.Info("Setup called for", "installationID", installationID) + s.log.Debug("Setup called for", "installationID", installationID) sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared( ecies.ImportECDSAPublic(theirPublicKey), sskLen, @@ -38,11 +42,13 @@ func (s *Service) setup(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.Pu // Receive will generate a shared secret for a given identity, and return it func (s *Service) Receive(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) { + s.log.Debug("Received message, setting up topic", "public-key", theirPublicKey, "installation-id", installationID) return s.setup(myPrivateKey, theirPublicKey, installationID) } // Send returns a shared key and whether it has been acknowledged from all the installationIDs func (s *Service) Send(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) { + s.log.Debug("Checking against:", "installation-ids", theirInstallationIDs) secret, err := s.setup(myPrivateKey, theirPublicKey, myInstallationID) if err != nil { return nil, false, err @@ -60,10 +66,13 @@ func (s *Service) Send(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, for _, installationID := range theirInstallationIDs { if !response.installationIDs[installationID] { + s.log.Debug("no shared secret with:", "installation-id", installationID) return secret, false, nil } } + s.log.Debug("shared secret found") + return secret, true, nil } diff --git a/services/shhext/chat/sharedsecret/service_test.go b/services/shhext/chat/sharedsecret/service_test.go index 892782a00f..f00af7ba67 100644 --- a/services/shhext/chat/sharedsecret/service_test.go +++ b/services/shhext/chat/sharedsecret/service_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" - appDB "github.com/status-im/status-go/services/shhext/chat/db" + chatDB "github.com/status-im/status-go/services/shhext/chat/db" "github.com/stretchr/testify/suite" ) @@ -25,7 +25,7 @@ func (s *ServiceTestSuite) SetupTest() { s.Require().NoError(err) s.path = dbFile.Name() - db, err := appDB.Open(s.path, "", 0) + db, err := chatDB.Open(s.path, "", 0) s.Require().NoError(err) diff --git a/services/shhext/chat/sql_lite_persistence.go b/services/shhext/chat/sql_lite_persistence.go index ff7aecb2f4..ced190cb62 100644 --- a/services/shhext/chat/sql_lite_persistence.go +++ b/services/shhext/chat/sql_lite_persistence.go @@ -8,11 +8,8 @@ import ( "github.com/ethereum/go-ethereum/crypto" dr "github.com/status-im/doubleratchet" - "github.com/status-im/migrate/v4" - "github.com/status-im/migrate/v4/database/sqlcipher" - "github.com/status-im/migrate/v4/source/go_bindata" ecrypto "github.com/status-im/status-go/services/shhext/chat/crypto" - appDB "github.com/status-im/status-go/services/shhext/chat/db" + chatDB "github.com/status-im/status-go/services/shhext/chat/db" "github.com/status-im/status-go/services/shhext/chat/multidevice" "github.com/status-im/status-go/services/shhext/chat/protobuf" "github.com/status-im/status-go/services/shhext/chat/sharedsecret" @@ -23,7 +20,7 @@ const maxNumberOfRows = 100000000 // SQLLitePersistence represents a persistence service tied to an SQLite database type SQLLitePersistence struct { - db *sql.DB + DB *sql.DB keysStorage dr.KeysStorage sessionStorage dr.SessionStorage secretStorage sharedsecret.PersistenceService @@ -48,13 +45,13 @@ func NewSQLLitePersistence(path string, key string) (*SQLLitePersistence, error) return nil, err } - s.keysStorage = NewSQLLiteKeysStorage(s.db) + s.keysStorage = NewSQLLiteKeysStorage(s.DB) - s.sessionStorage = NewSQLLiteSessionStorage(s.db) + s.sessionStorage = NewSQLLiteSessionStorage(s.DB) - s.secretStorage = sharedsecret.NewSQLLitePersistence(s.db) + s.secretStorage = sharedsecret.NewSQLLitePersistence(s.DB) - s.multideviceStorage = multidevice.NewSQLLitePersistence(s.db) + s.multideviceStorage = multidevice.NewSQLLitePersistence(s.DB) return s, nil } @@ -95,19 +92,19 @@ func (s *SQLLitePersistence) GetMultideviceStorage() multidevice.Persistence { // Open opens a file at the specified path func (s *SQLLitePersistence) Open(path string, key string) error { - db, err := appDB.Open(path, key, appDB.KdfIterationsNumber) + db, err := chatDB.Open(path, key, chatDB.KdfIterationsNumber) if err != nil { return err } - s.db = db + s.DB = db return nil } // AddPrivateBundle adds the specified BundleContainer to the database func (s *SQLLitePersistence) AddPrivateBundle(bc *protobuf.BundleContainer) error { - tx, err := s.db.Begin() + tx, err := s.DB.Begin() if err != nil { return err } @@ -161,7 +158,7 @@ func (s *SQLLitePersistence) AddPrivateBundle(bc *protobuf.BundleContainer) erro // AddPublicBundle adds the specified Bundle to the database func (s *SQLLitePersistence) AddPublicBundle(b *protobuf.Bundle) error { - tx, err := s.db.Begin() + tx, err := s.DB.Begin() if err != nil { return err @@ -220,7 +217,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installat statement := `SELECT identity, private_key, signed_pre_key, installation_id, timestamp, version FROM bundles WHERE expired = 0 AND identity = ? AND installation_id IN (?` + strings.Repeat(",?", len(installations)-1) + ")" - stmt, err := s.db.Prepare(statement) + stmt, err := s.DB.Prepare(statement) if err != nil { return nil, err } @@ -296,7 +293,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installat // GetPrivateKeyBundle retrieves a private key for a bundle from the database func (s *SQLLitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error) { - stmt, err := s.db.Prepare(`SELECT private_key + stmt, err := s.DB.Prepare(`SELECT private_key FROM bundles WHERE signed_pre_key = ? LIMIT 1`) if err != nil { @@ -319,7 +316,7 @@ func (s *SQLLitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error // MarkBundleExpired expires any private bundle for a given identity func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error { - stmt, err := s.db.Prepare(`UPDATE bundles + stmt, err := s.DB.Prepare(`UPDATE bundles SET expired = 1 WHERE identity = ? AND private_key IS NOT NULL`) if err != nil { @@ -347,7 +344,7 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, install FROM bundles WHERE expired = 0 AND identity = ? AND installation_id IN (?` + strings.Repeat(",?", len(installations)-1) + `) ORDER BY version DESC` - stmt, err := s.db.Prepare(statement) + stmt, err := s.DB.Prepare(statement) if err != nil { return nil, err } @@ -407,7 +404,7 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, install // AddRatchetInfo persists the specified ratchet info into the database func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleID []byte, ephemeralKey []byte, installationID string) error { - stmt, err := s.db.Prepare(`INSERT INTO ratchet_info_v2(symmetric_key, identity, bundle_id, ephemeral_key, installation_id) + stmt, err := s.DB.Prepare(`INSERT INTO ratchet_info_v2(symmetric_key, identity, bundle_id, ephemeral_key, installation_id) VALUES(?, ?, ?, ?, ?)`) if err != nil { return err @@ -427,7 +424,7 @@ func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleI // GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key from the database func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byte, installationID string) (*RatchetInfo, error) { - stmt, err := s.db.Prepare(`SELECT ratchet_info_v2.identity, ratchet_info_v2.symmetric_key, bundles.private_key, bundles.signed_pre_key, ratchet_info_v2.ephemeral_key, ratchet_info_v2.installation_id + stmt, err := s.DB.Prepare(`SELECT ratchet_info_v2.identity, ratchet_info_v2.symmetric_key, bundles.private_key, bundles.signed_pre_key, ratchet_info_v2.ephemeral_key, ratchet_info_v2.installation_id FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? AND bundle_id = ? LIMIT 1`) @@ -461,7 +458,7 @@ func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byt // GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key from the database func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID string) (*RatchetInfo, error) { - stmt, err := s.db.Prepare(`SELECT symmetric_key, bundles.private_key, signed_pre_key, bundle_id, ephemeral_key + stmt, err := s.DB.Prepare(`SELECT symmetric_key, bundles.private_key, signed_pre_key, bundle_id, ephemeral_key FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE expired = 0 AND ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? LIMIT 1`) @@ -496,7 +493,7 @@ func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID s // RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo // associated with the specified bundle ID and interlocutor identity public key func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity []byte, installationID string) error { - stmt, err := s.db.Prepare(`UPDATE ratchet_info_v2 + stmt, err := s.DB.Prepare(`UPDATE ratchet_info_v2 SET ephemeral_key = NULL WHERE identity = ? AND bundle_id = ? AND installation_id = ?`) if err != nil { diff --git a/services/shhext/filter/service.go b/services/shhext/filter/service.go index 6bf8469ead..7f215484d2 100644 --- a/services/shhext/filter/service.go +++ b/services/shhext/filter/service.go @@ -42,6 +42,10 @@ type Chat struct { Identity string `json:"identity"` // Topic is the whisper topic Topic whisper.TopicType `json:"topic"` + // Discovery is whether this is a discovery topic + Discovery bool `json:"discovery"` + // Negotiated tells us whether is a negotiated topic + Negotiated bool `json:"negotiated"` } type Service struct { @@ -124,26 +128,29 @@ func (s *Service) Init(chats []*Chat) ([]*Chat, error) { // Stop removes all the filters func (s *Service) Stop() error { + var chats []*Chat for _, chat := range s.chats { - if err := s.Remove(chat); err != nil { - return err - } + chats = append(chats, chat) } - return nil + return s.Remove(chats) } // Remove remove all the filters associated with a chat/identity -func (s *Service) Remove(chat *Chat) error { +func (s *Service) Remove(chats []*Chat) error { s.mutex.Lock() defer s.mutex.Unlock() + log.Debug("Removing chats", "chats", chats) - if err := s.whisper.Unsubscribe(chat.FilterID); err != nil { - return err - } - if chat.SymKeyID != "" { - s.whisper.DeleteSymKey(chat.SymKeyID) + for _, chat := range chats { + log.Debug("Removing chat", "chat", chat) + if err := s.whisper.Unsubscribe(chat.FilterID); err != nil { + return err + } + if chat.SymKeyID != "" { + s.whisper.DeleteSymKey(chat.SymKeyID) + } + delete(s.chats, chat.ChatID) } - delete(s.chats, chat.ChatID) return nil } @@ -165,11 +172,15 @@ func (s *Service) LoadPartitioned(myKey *ecdsa.PrivateKey, theirPublicKey *ecdsa return nil, err } + identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(theirPublicKey)) + chat := &Chat{ - ChatID: chatID, - FilterID: filter.FilterID, - Topic: filter.Topic, - Listen: listen, + ChatID: chatID, + FilterID: filter.FilterID, + Topic: filter.Topic, + Listen: listen, + Identity: identityStr, + Discovery: true, } s.chats[chatID] = chat @@ -191,7 +202,11 @@ func (s *Service) Load(chat *Chat) ([]*Chat, error) { return s.load(myKey, chat) } -// Get returns a negotiated filter given an identity +func ContactCodeTopic(identity string) string { + return "0x" + identity + "-contact-code" +} + +// Get returns a negotiated chat given an identity func (s *Service) GetNegotiated(identity *ecdsa.PublicKey) *Chat { s.mutex.Lock() defer s.mutex.Unlock() @@ -199,7 +214,7 @@ func (s *Service) GetNegotiated(identity *ecdsa.PublicKey) *Chat { return s.chats[negotiatedID(identity)] } -// GetByID returns a filter by chatID +// GetByID returns a chat by chatID func (s *Service) GetByID(chatID string) *Chat { s.mutex.Lock() defer s.mutex.Unlock() @@ -213,7 +228,7 @@ func (s *Service) ProcessNegotiatedSecret(secret *sharedsecret.Secret) (*Chat, e defer s.mutex.Unlock() chatID := negotiatedID(secret.Identity) - // If we already have a filter do nothing + // If we already have a chat do nothing if _, ok := s.chats[chatID]; ok { return s.chats[chatID], nil } @@ -227,15 +242,16 @@ func (s *Service) ProcessNegotiatedSecret(secret *sharedsecret.Secret) (*Chat, e identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(secret.Identity)) chat := &Chat{ - ChatID: chatID, - Topic: filter.Topic, - SymKeyID: filter.SymKeyID, - FilterID: filter.FilterID, - Identity: identityStr, - Listen: true, + ChatID: chatID, + Topic: filter.Topic, + SymKeyID: filter.SymKeyID, + FilterID: filter.FilterID, + Identity: identityStr, + Listen: true, + Negotiated: true, } - log.Info("PROCESSING SECRET", "chat-id", chatID, "topic", filter.Topic, "symKey", keyString) + log.Debug("Processing negotiated secret", "chat-id", chatID, "topic", filter.Topic) s.chats[chat.ChatID] = chat return chat, nil @@ -269,9 +285,13 @@ func (s *Service) loadDiscovery(myKey *ecdsa.PrivateKey) error { return nil } + identityStr := fmt.Sprintf("%x", crypto.FromECDSAPub(&myKey.PublicKey)) + discoveryChat := &Chat{ - ChatID: discoveryTopic, - Listen: true, + ChatID: discoveryTopic, + Listen: true, + Identity: identityStr, + Discovery: true, } discoveryResponse, err := s.addAsymmetricFilter(myKey, discoveryChat.ChatID, true) @@ -346,7 +366,7 @@ func (s *Service) loadContactCode(identity string) (*Chat, error) { s.mutex.Lock() defer s.mutex.Unlock() - chatID := "0x" + identity + "-contact-code" + chatID := ContactCodeTopic(identity) if _, ok := s.chats[chatID]; ok { return s.chats[chatID], nil } diff --git a/services/shhext/filter/service_test.go b/services/shhext/filter/service_test.go index 627c499010..4a8ba27aa8 100644 --- a/services/shhext/filter/service_test.go +++ b/services/shhext/filter/service_test.go @@ -8,7 +8,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" - appDB "github.com/status-im/status-go/services/shhext/chat/db" + chatDB "github.com/status-im/status-go/services/shhext/chat/db" "github.com/status-im/status-go/services/shhext/chat/sharedsecret" whisper "github.com/status-im/whisper/whisperv6" "github.com/stretchr/testify/suite" @@ -63,7 +63,7 @@ func (s *ServiceTestSuite) SetupTest() { s.keys = append(s.keys, testKey) } - db, err := appDB.Open(s.path, "", 0) + db, err := chatDB.Open(s.path, "", 0) s.Require().NoError(err) // Build services diff --git a/services/shhext/publisher/persistence.go b/services/shhext/publisher/persistence.go new file mode 100644 index 0000000000..722a60a828 --- /dev/null +++ b/services/shhext/publisher/persistence.go @@ -0,0 +1,42 @@ +package publisher + +import ( + "database/sql" +) + +type Persistence interface { + Get() (int64, error) + Set(int64) error +} + +type SQLLitePersistence struct { + db *sql.DB +} + +func NewSQLLitePersistence(db *sql.DB) *SQLLitePersistence { + return &SQLLitePersistence{db: db} +} + +func (s *SQLLitePersistence) Get() (int64, error) { + var lastPublished int64 + statement := "SELECT last_published FROM contact_code_config LIMIT 1" + err := s.db.QueryRow(statement).Scan(&lastPublished) + + if err != nil { + return 0, err + } + + return lastPublished, nil +} + +func (s *SQLLitePersistence) Set(lastPublished int64) error { + statement := "UPDATE contact_code_config SET last_published = ?" + stmt, err := s.db.Prepare(statement) + if err != nil { + return err + } + defer stmt.Close() + + _, err = stmt.Exec(lastPublished) + return err +} diff --git a/services/shhext/publisher/service.go b/services/shhext/publisher/service.go index d33638e3ae..44aad96973 100644 --- a/services/shhext/publisher/service.go +++ b/services/shhext/publisher/service.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/golang/protobuf/proto" "github.com/status-im/status-go/services/shhext/chat" - appDB "github.com/status-im/status-go/services/shhext/chat/db" + chatDB "github.com/status-im/status-go/services/shhext/chat/db" "github.com/status-im/status-go/services/shhext/chat/multidevice" "github.com/status-im/status-go/services/shhext/chat/protobuf" "github.com/status-im/status-go/services/shhext/chat/sharedsecret" @@ -26,30 +26,30 @@ import ( ) const ( - tickerInterval = 120 + tickerInterval = 120 + // How often we should publish a contact code in seconds + publishInterval = 21600 maxInstallations = 3 ) var ( - errProtocolNotInitialized = errors.New("procotol is not initialized") + errProtocolNotInitialized = errors.New("protocol is not initialized") // ErrPFSNotEnabled is returned when an endpoint PFS only is called but // PFS is disabled ErrPFSNotEnabled = errors.New("pfs not enabled") ) -//type Persistence interface { -//} - type Service struct { - whisper *whisper.Whisper - whisperAPI *whisper.PublicWhisperAPI - protocol *chat.ProtocolService - // persistence Persistence - log log.Logger - filter *filter.Service - config *Config - quit chan struct{} - ticker *time.Ticker + whisper *whisper.Whisper + online func() bool + whisperAPI *whisper.PublicWhisperAPI + protocol *chat.ProtocolService + persistence Persistence + log log.Logger + filter *filter.Service + config *Config + quit chan struct{} + ticker *time.Ticker } type Config struct { @@ -94,11 +94,11 @@ func (s *Service) initProtocol(address, encKey, password string) error { v4Path := filepath.Join(s.config.DataDir, fmt.Sprintf("%s.v4.db", s.config.InstallationID)) if password != "" { - if err := appDB.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil { + if err := chatDB.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil { return err } - if err := appDB.MigrateDBFile(v1Path, v2Path, password, encKey); err != nil { + if err := chatDB.MigrateDBFile(v1Path, v2Path, password, encKey); err != nil { // Remove db file as created with a blank password and never used, // and there's no need to rekey in this case os.Remove(v1Path) @@ -106,13 +106,13 @@ func (s *Service) initProtocol(address, encKey, password string) error { } } - if err := appDB.MigrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil { + if err := chatDB.MigrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil { os.Remove(v2Path) os.Remove(v3Path) } // Fix IOS not encrypting database - if err := appDB.EncryptDatabase(v3Path, v4Path, encKey); err != nil { + if err := chatDB.EncryptDatabase(v3Path, v4Path, encKey); err != nil { os.Remove(v3Path) os.Remove(v4Path) } @@ -134,13 +134,17 @@ func (s *Service) initProtocol(address, encKey, password string) error { return err } - addedBundlesHandler := func(addedBundles []multidevice.IdentityAndIDPair) { + addedBundlesHandler := func(addedBundles []*multidevice.IdentityAndID) { handler := SignalHandler{} for _, bundle := range addedBundles { - handler.BundleAdded(bundle[0], bundle[1]) + handler.BundleAdded(bundle.Identity, bundle.ID) } } + // Initialize persistence + + s.persistence = NewSQLLitePersistence(persistence.DB) + // Initialize sharedsecret sharedSecretService := sharedsecret.NewService(persistence.GetSharedSecretStorage()) // Initialize filter @@ -167,7 +171,7 @@ func (s *Service) initProtocol(address, encKey, password string) error { return nil } -func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]multidevice.IdentityAndIDPair, error) { +func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.IdentityAndID, error) { if s.protocol == nil { return nil, errProtocolNotInitialized } @@ -209,10 +213,14 @@ func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installati return s.protocol.DisableInstallation(myIdentityKey, installationID) } -func (s *Service) Start() error { - s.startTicker() +func (s *Service) Start(online func() bool, startTicker bool) error { + s.online = online + if startTicker { + s.startTicker() + } return nil } + func (s *Service) Stop() error { if s.filter != nil { if err := s.filter.Stop(); err != nil { @@ -223,7 +231,7 @@ func (s *Service) Stop() error { return nil } -func (s *Service) GetNegotiatedChat(identity *ecdsa.PublicKey) *filter.Chat { +func (s *Service) getNegotiatedChat(identity *ecdsa.PublicKey) *filter.Chat { return s.filter.GetNegotiated(identity) } @@ -235,8 +243,8 @@ func (s *Service) LoadFilter(chat *filter.Chat) ([]*filter.Chat, error) { return s.filter.Load(chat) } -func (s *Service) RemoveFilter(chat *filter.Chat) error { - return s.filter.Remove(chat) +func (s *Service) RemoveFilters(chats []*filter.Chat) error { + return s.filter.Remove(chats) } func (s *Service) onNewSharedSecretHandler(sharedSecrets []*sharedsecret.Secret) { @@ -318,42 +326,42 @@ func (s *Service) ProcessMessage(dedupMessage dedup.DeduplicateMessage) error { return nil } -// SendDirectMessage sends a 1:1 chat message to the underlying transport -func (s *Service) SendDirectMessage(ctx context.Context, msg chat.SendDirectMessageRPC) (hexutil.Bytes, error) { +// CreateDirectMessage creates a 1:1 chat message +func (s *Service) CreateDirectMessage(signature string, destination hexutil.Bytes, DH bool, payload []byte) (*whisper.NewMessage, error) { if !s.config.PfsEnabled { return nil, ErrPFSNotEnabled } - privateKey, err := s.whisper.GetPrivateKey(msg.Sig) + privateKey, err := s.whisper.GetPrivateKey(signature) if err != nil { return nil, err } - publicKey, err := crypto.UnmarshalPubkey(msg.PubKey) + publicKey, err := crypto.UnmarshalPubkey(destination) if err != nil { return nil, err } var msgSpec *chat.ProtocolMessageSpec - if msg.DH { + if DH { s.log.Debug("Building dh message") - msgSpec, err = s.protocol.BuildDHMessage(privateKey, publicKey, msg.Payload) + msgSpec, err = s.protocol.BuildDHMessage(privateKey, publicKey, payload) } else { s.log.Debug("Building direct message") - msgSpec, err = s.protocol.BuildDirectMessage(privateKey, publicKey, msg.Payload) + msgSpec, err = s.protocol.BuildDirectMessage(privateKey, publicKey, payload) } if err != nil { return nil, err } - whisperMessage, err := s.directMessageToWhisper(privateKey, publicKey, msg.PubKey, msg.Sig, msgSpec) + whisperMessage, err := s.directMessageToWhisper(privateKey, publicKey, destination, signature, msgSpec) if err != nil { s.log.Error("sshext-service", "error building whisper message", err) return nil, err } - return s.whisperAPI.Post(ctx, *whisperMessage) + return whisperMessage, nil } func (s *Service) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, destination hexutil.Bytes, signature string, spec *chat.ProtocolMessageSpec) (*whisper.NewMessage, error) { @@ -369,16 +377,16 @@ func (s *Service) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPu whisperMessage.Sig = signature if spec.SharedSecret != nil { - chat := s.GetNegotiatedChat(theirPublicKey) + chat := s.getNegotiatedChat(theirPublicKey) if chat != nil { - s.log.Debug("Sending on negotiated topic") + s.log.Debug("Sending on negotiated topic", "public-key", destination) whisperMessage.SymKeyID = chat.SymKeyID whisperMessage.Topic = chat.Topic whisperMessage.PublicKey = nil return &whisperMessage, nil } - } else if spec.PartitionedTopic() { - s.log.Debug("Sending on partitioned topic") + } else if spec.PartitionedTopic() == chat.PartitionTopicV1 { + s.log.Debug("Sending on partitioned topic", "public-key", destination) // Create filter on demand if _, err := s.filter.LoadPartitioned(myPrivateKey, theirPublicKey, false); err != nil { return nil, err @@ -389,33 +397,58 @@ func (s *Service) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPu return &whisperMessage, nil } - s.log.Debug("Sending on old discovery topic") + s.log.Debug("Sending on old discovery topic", "public-key", destination) whisperMessage.Topic = whisperutils.DiscoveryTopicBytes whisperMessage.PublicKey = destination return &whisperMessage, nil } -// SendPublicMessage sends a public chat message to the underlying transport -func (s *Service) SendPublicMessage(ctx context.Context, msg chat.SendPublicMessageRPC) (hexutil.Bytes, error) { +// CreatePublicMessage sends a public chat message to the underlying transport +func (s *Service) CreatePublicMessage(signature string, chatID string, payload []byte, wrap bool) (*whisper.NewMessage, error) { if !s.config.PfsEnabled { return nil, ErrPFSNotEnabled } - filter := s.filter.GetByID(msg.Chat) + filter := s.filter.GetByID(chatID) if filter == nil { return nil, errors.New("not subscribed to chat") } + s.log.Info("SIG", signature) // Enrich with transport layer info whisperMessage := whisperutils.DefaultWhisperMessage() - whisperMessage.Payload = msg.Payload - whisperMessage.Sig = msg.Sig - whisperMessage.Topic = whisperutils.ToTopic(msg.Chat) + whisperMessage.Sig = signature + whisperMessage.Topic = whisperutils.ToTopic(chatID) whisperMessage.SymKeyID = filter.SymKeyID - // And dispatch - return s.whisperAPI.Post(ctx, whisperMessage) + if wrap { + privateKeyID := s.whisper.SelectedKeyPairID() + if privateKeyID == "" { + return nil, errors.New("no key selected") + } + + privateKey, err := s.whisper.GetPrivateKey(privateKeyID) + if err != nil { + return nil, err + } + + message, err := s.protocol.BuildPublicMessage(privateKey, payload) + if err != nil { + return nil, err + } + marshaledMessage, err := proto.Marshal(message) + if err != nil { + s.log.Error("encryption-service", "error marshaling message", err) + return nil, err + } + whisperMessage.Payload = marshaledMessage + + } else { + whisperMessage.Payload = payload + } + + return &whisperMessage, nil } func (s *Service) ConfirmMessagesProcessed(ids [][]byte) error { @@ -429,7 +462,7 @@ func (s *Service) startTicker() { for { select { case <-s.ticker.C: - err := s.perform() + _, err := s.sendContactCode() if err != nil { s.log.Error("could not execute tick", "err", err) } @@ -441,6 +474,60 @@ func (s *Service) startTicker() { }() } -func (s *Service) perform() error { - return nil +func (s *Service) sendContactCode() (*whisper.NewMessage, error) { + s.log.Info("publishing bundle") + if !s.config.PfsEnabled { + return nil, nil + } + + lastPublished, err := s.persistence.Get() + if err != nil { + s.log.Error("could not fetch config from db", "err", err) + return nil, err + } + + now := time.Now().Unix() + + if now-lastPublished < publishInterval { + fmt.Println("NOTHING") + s.log.Debug("nothing to do") + return nil, nil + } + + if !s.online() { + s.log.Debug("not connected") + return nil, nil + } + + privateKeyID := s.whisper.SelectedKeyPairID() + if privateKeyID == "" { + return nil, errors.New("no key selected") + } + + privateKey, err := s.whisper.GetPrivateKey(privateKeyID) + if err != nil { + return nil, err + } + + identity := fmt.Sprintf("%x", crypto.FromECDSAPub(&privateKey.PublicKey)) + + message, err := s.CreatePublicMessage("0x"+identity, filter.ContactCodeTopic(identity), nil, true) + if err != nil { + s.log.Error("could not build contact code", "identity", identity, "err", err) + return nil, err + } + + _, err = s.whisperAPI.Post(context.TODO(), *message) + if err != nil { + s.log.Error("could not publish contact code on whisper", "identity", identity, "err", err) + return nil, err + } + + err = s.persistence.Set(now) + if err != nil { + s.log.Error("could not set last published", "err", err) + return nil, err + } + + return message, nil } diff --git a/services/shhext/publisher/service_test.go b/services/shhext/publisher/service_test.go new file mode 100644 index 0000000000..07d9fdb663 --- /dev/null +++ b/services/shhext/publisher/service_test.go @@ -0,0 +1,241 @@ +package publisher + +import ( + "crypto/ecdsa" + "fmt" + "io/ioutil" + "testing" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/status-im/status-go/services/shhext/dedup" + "github.com/status-im/status-go/services/shhext/filter" + "github.com/status-im/status-go/services/shhext/whisperutils" + whisper "github.com/status-im/whisper/whisperv6" + "github.com/stretchr/testify/suite" +) + +func TestServiceTestSuite(t *testing.T) { + suite.Run(t, new(ServiceTestSuite)) +} + +type TestKey struct { + privateKey *ecdsa.PrivateKey + keyID string + publicKeyBytes hexutil.Bytes +} + +type ServiceTestSuite struct { + suite.Suite + alice *Service + bob *Service + aliceKey *TestKey + bobKey *TestKey +} + +func (s *ServiceTestSuite) SetupTest() { + + dir1, err := ioutil.TempDir("", "publisher-test") + s.Require().NoError(err) + + config1 := &Config{ + PfsEnabled: true, + DataDir: dir1, + InstallationID: "1", + } + + whisper1 := whisper.New(nil) + err = whisper1.SetMinimumPoW(0) + s.Require().NoError(err) + + service1 := New(config1, whisper1) + + pk1, err := crypto.GenerateKey() + s.Require().NoError(err) + + keyID1, err := whisper1.AddKeyPair(pk1) + s.Require().NoError(err) + + key1 := &TestKey{ + privateKey: pk1, + keyID: keyID1, + publicKeyBytes: crypto.FromECDSAPub(&pk1.PublicKey), + } + + s.Require().NoError(err) + + err = service1.Start(func() bool { return true }, false) + s.Require().NoError(err) + + err = service1.InitProtocolWithPassword("1", "") + s.Require().NoError(err) + _, err = service1.LoadFilters([]*filter.Chat{}) + s.Require().NoError(err) + + dir2, err := ioutil.TempDir("", "publisher-test") + s.Require().NoError(err) + + config2 := &Config{ + PfsEnabled: true, + DataDir: dir2, + InstallationID: "2", + } + + whisper2 := whisper.New(nil) + err = whisper2.SetMinimumPoW(0) + s.Require().NoError(err) + + service2 := New(config2, whisper2) + + pk2, err := crypto.GenerateKey() + s.Require().NoError(err) + + keyID2, err := whisper2.AddKeyPair(pk2) + s.Require().NoError(err) + + key2 := &TestKey{ + privateKey: pk2, + keyID: keyID2, + publicKeyBytes: crypto.FromECDSAPub(&pk2.PublicKey), + } + + err = service2.Start(func() bool { return true }, false) + s.Require().NoError(err) + + err = service2.InitProtocolWithPassword("1", "") + s.Require().NoError(err) + + _, err = service2.LoadFilters([]*filter.Chat{}) + s.Require().NoError(err) + + s.alice = service1 + s.aliceKey = key1 + s.bob = service2 + s.bobKey = key2 +} + +func (s *ServiceTestSuite) TestCreateDirectMessage() { + newMessage, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello")) + s.Require().NoError(err) + + message := &whisper.Message{ + Sig: s.aliceKey.publicKeyBytes, + Topic: newMessage.Topic, + Payload: newMessage.Payload, + Dst: newMessage.PublicKey, + } + dedupMessage := dedup.DeduplicateMessage{ + DedupID: []byte("1"), + Message: message, + } + + err = s.bob.ProcessMessage(dedupMessage) + s.Require().NoError(err) + + s.Require().Equal([]byte("hello"), message.Payload) +} + +func (s *ServiceTestSuite) TestTopic() { + // We build an initial message + newMessage1, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello")) + s.Require().NoError(err) + + message1 := &whisper.Message{ + Sig: s.aliceKey.publicKeyBytes, + Topic: newMessage1.Topic, + Payload: newMessage1.Payload, + Dst: newMessage1.PublicKey, + } + + // We have no information, it should use the discovery topic + s.Require().Equal(whisperutils.DiscoveryTopicBytes, message1.Topic) + + // We build a contact code from user 2 + newMessage2, err := s.bob.sendContactCode() + s.Require().NoError(err) + s.Require().NotNil(newMessage2) + + message2 := &whisper.Message{ + Sig: s.bobKey.publicKeyBytes, + Topic: newMessage2.Topic, + Payload: newMessage2.Payload, + Dst: newMessage2.PublicKey, + } + + // We receive the contact code + dedupMessage2 := dedup.DeduplicateMessage{ + DedupID: []byte("1"), + Message: message2, + } + + err = s.alice.ProcessMessage(dedupMessage2) + s.Require().NoError(err) + + // We build another message, this time it should use the partitioned topic + newMessage3, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello")) + s.Require().NoError(err) + + message3 := &whisper.Message{ + Sig: s.aliceKey.publicKeyBytes, + Topic: newMessage3.Topic, + Payload: newMessage3.Payload, + Dst: newMessage3.PublicKey, + } + expectedTopic3 := whisper.BytesToTopic(filter.PublicKeyToPartitionedTopicBytes(&s.bobKey.privateKey.PublicKey)) + + s.Require().Equal(expectedTopic3, message3.Topic) + + // We receive the message + dedupMessage3 := dedup.DeduplicateMessage{ + DedupID: []byte("1"), + Message: message3, + } + + err = s.bob.ProcessMessage(dedupMessage3) + s.Require().NoError(err) + + // We build another message, this time it should use the negotiated topic + newMessage4, err := s.bob.CreateDirectMessage(s.bobKey.keyID, s.aliceKey.publicKeyBytes, false, []byte("hello")) + s.Require().NoError(err) + + message4 := &whisper.Message{ + Sig: s.bobKey.publicKeyBytes, + Topic: newMessage4.Topic, + Payload: newMessage4.Payload, + Dst: newMessage4.PublicKey, + } + sharedSecret, err := ecies.ImportECDSA(s.bobKey.privateKey).GenerateShared( + ecies.ImportECDSAPublic(&s.aliceKey.privateKey.PublicKey), + 16, + 16) + s.Require().NoError(err) + keyString := fmt.Sprintf("%x", sharedSecret) + + negotiatedTopic := whisper.BytesToTopic(filter.ToTopic(keyString)) + + s.Require().Equal(negotiatedTopic, message4.Topic) + + // We receive the message + dedupMessage4 := dedup.DeduplicateMessage{ + DedupID: []byte("1"), + Message: message4, + } + + err = s.alice.ProcessMessage(dedupMessage4) + s.Require().NoError(err) + + // Alice sends another message to Bob, this time it should use the negotiated topic + newMessage5, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello")) + s.Require().NoError(err) + + message5 := &whisper.Message{ + Sig: s.aliceKey.publicKeyBytes, + Topic: newMessage5.Topic, + Payload: newMessage5.Payload, + Dst: newMessage5.PublicKey, + } + s.Require().NoError(err) + s.Require().Equal(negotiatedTopic, message5.Topic) + +} diff --git a/services/shhext/service.go b/services/shhext/service.go index f431fd9a9e..bd4dc4c73e 100644 --- a/services/shhext/service.go +++ b/services/shhext/service.go @@ -152,12 +152,16 @@ func (s *Service) Start(server *p2p.Server) error { s.mailMonitor.Start() s.nodeID = server.PrivateKey s.server = server - return s.Service.Start() + return s.Service.Start(s.online, true) +} + +func (s *Service) online() bool { + return s.server.PeerCount() != 0 } // Stop is run when a service is stopped. -// It does nothing in this case but is required by `node.Service` interface. func (s *Service) Stop() error { + log.Info("Stopping shhext service") if s.config.EnableConnectionManager { s.connManager.Stop() } diff --git a/services/shhext/signal.go b/services/shhext/signal.go index 614cb747bd..82852586f7 100644 --- a/services/shhext/signal.go +++ b/services/shhext/signal.go @@ -27,15 +27,3 @@ func (h EnvelopeSignalHandler) MailServerRequestCompleted(requestID common.Hash, func (h EnvelopeSignalHandler) MailServerRequestExpired(hash common.Hash) { signal.SendMailServerRequestExpired(hash) } - -func (h EnvelopeSignalHandler) DecryptMessageFailed(pubKey string) { - signal.SendDecryptMessageFailed(pubKey) -} - -func (h EnvelopeSignalHandler) BundleAdded(identity string, installationID string) { - signal.SendBundleAdded(identity, installationID) -} - -func (h EnvelopeSignalHandler) WhisperFilterAdded(filters []*signal.Filter) { - signal.SendWhisperFilterAdded(filters) -} diff --git a/static/chat_db_migrations/1559627659_add_contact_code.down.sql b/static/chat_db_migrations/1559627659_add_contact_code.down.sql new file mode 100644 index 0000000000..5c891a3760 --- /dev/null +++ b/static/chat_db_migrations/1559627659_add_contact_code.down.sql @@ -0,0 +1 @@ +DROP TABLE contact_code_config; diff --git a/static/chat_db_migrations/1559627659_add_contact_code.up.sql b/static/chat_db_migrations/1559627659_add_contact_code.up.sql new file mode 100644 index 0000000000..51ea8a0489 --- /dev/null +++ b/static/chat_db_migrations/1559627659_add_contact_code.up.sql @@ -0,0 +1,6 @@ +CREATE TABLE contact_code_config ( + unique_constraint varchar(1) NOT NULL PRIMARY KEY DEFAULT 'X', + last_published INTEGER NOT NULL DEFAULT 0 +); + +INSERT INTO contact_code_config VALUES ('X', 0); diff --git a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/filter.go b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/filter.go index c37c8997c8..6a5b79674b 100644 --- a/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/filter.go +++ b/vendor/github.com/ethereum/go-ethereum/whisper/whisperv6/filter.go @@ -247,7 +247,6 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { // the message and subsequently call MatchMessage. // Topics are not checked here, since this is done by topic matchers. func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - log.Trace("checking pow", "filter", f.PoW, "envelope", envelope.pow) return f.PoW <= 0 || envelope.pow >= f.PoW } diff --git a/vendor/github.com/status-im/whisper/whisperv6/filter.go b/vendor/github.com/status-im/whisper/whisperv6/filter.go index 1b3849a943..b75f7b3c8c 100644 --- a/vendor/github.com/status-im/whisper/whisperv6/filter.go +++ b/vendor/github.com/status-im/whisper/whisperv6/filter.go @@ -197,7 +197,6 @@ func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { fs.mutex.RLock() defer fs.mutex.RUnlock() - log.Info("Got envelope for topic", "topic", env.Topic) candidates := fs.getWatchersByTopic(env.Topic) for _, watcher := range candidates { if p2pMessage && !watcher.AllowP2P { @@ -280,7 +279,6 @@ func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { // the message and subsequently call MatchMessage. // Topics are not checked here, since this is done by topic matchers. func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - log.Trace("checking pow", "filter", f.PoW, "envelope", envelope.pow) return f.PoW <= 0 || envelope.pow >= f.PoW }