Merge branch 'feature/mute-chats-2' into develop
This commit is contained in:
commit
717ed3e1cf
|
@ -0,0 +1,6 @@
|
|||
Specs changes:
|
||||
- Use application metadata wrapper
|
||||
- Encrypt of payload instead of signature + public key of server
|
||||
- Removed preferencs + each device registers individually
|
||||
- Add grant
|
||||
- Add version in PushNotificationInfo
|
|
@ -12,6 +12,7 @@
|
|||
// 0005_waku_mode.up.sql (146B)
|
||||
// 0006_appearance.up.sql (67B)
|
||||
// 0007_enable_waku_default.up.sql (38B)
|
||||
// 0008_add_push_notifications.up.sql (349B)
|
||||
// doc.go (74B)
|
||||
|
||||
package migrations
|
||||
|
@ -216,7 +217,7 @@ func _0004_pending_stickersDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -236,7 +237,7 @@ func _0004_pending_stickersUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "0004_pending_stickers.up.sql", size: 61, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3c, 0xed, 0x25, 0xdf, 0x75, 0x2, 0x6c, 0xf0, 0xa2, 0xa8, 0x37, 0x62, 0x65, 0xad, 0xfd, 0x98, 0xa0, 0x9d, 0x63, 0x94, 0xdf, 0x6b, 0x46, 0xe0, 0x68, 0xec, 0x9c, 0x7f, 0x77, 0xdd, 0xb3, 0x6}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -256,7 +257,7 @@ func _0005_waku_modeDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "0005_waku_mode.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -276,7 +277,7 @@ func _0005_waku_modeUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "0005_waku_mode.up.sql", size: 146, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa6, 0x91, 0xc, 0xd7, 0x89, 0x61, 0x2e, 0x4c, 0x5a, 0xb6, 0x67, 0xd1, 0xc1, 0x42, 0x24, 0x38, 0xd6, 0x1b, 0x75, 0x41, 0x9c, 0x23, 0xb0, 0xca, 0x5c, 0xf1, 0x5c, 0xd0, 0x13, 0x92, 0x3e, 0xe1}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -296,7 +297,7 @@ func _0006_appearanceUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1585895847, 0)}
|
||||
info := bindataFileInfo{name: "0006_appearance.up.sql", size: 67, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xae, 0x6, 0x25, 0x6c, 0xe4, 0x9d, 0xa7, 0x72, 0xe8, 0xbc, 0xe4, 0x1f, 0x1e, 0x2d, 0x7c, 0xb7, 0xf6, 0xa3, 0xec, 0x3b, 0x4e, 0x93, 0x2e, 0xa4, 0xec, 0x6f, 0xe5, 0x95, 0x94, 0xe8, 0x4, 0xfb}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -316,11 +317,31 @@ func _0007_enable_waku_defaultUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1585895900, 0)}
|
||||
info := bindataFileInfo{name: "0007_enable_waku_default.up.sql", size: 38, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd4, 0x42, 0xb6, 0xe5, 0x48, 0x41, 0xeb, 0xc0, 0x7e, 0x3b, 0xe6, 0x8e, 0x96, 0x33, 0x20, 0x92, 0x24, 0x5a, 0x60, 0xfa, 0xa0, 0x3, 0x5e, 0x76, 0x4b, 0x89, 0xaa, 0x37, 0x66, 0xbc, 0x26, 0x11}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __0008_add_push_notificationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xce\x51\x0e\x82\x30\x0c\x00\xd0\x7f\x4f\xd1\x7b\xf8\x35\x64\x7c\x55\x48\x70\x7c\x2f\x08\x45\x97\x40\x6b\xd6\x6a\xe2\xed\x3d\x80\x33\x9a\x78\x81\x97\xe7\x30\xf8\x1e\x82\xab\xd0\x83\x92\x59\xe2\x8b\x82\xab\x6b\x38\x74\x38\x1c\x5b\xc8\xb4\x89\x51\xbc\xdd\xf5\x1a\x59\x2c\x2d\x69\x1a\x2d\x09\x6b\x24\x1e\xcf\x2b\xcd\x50\x75\x1d\x7a\xd7\x42\xed\x1b\x37\x60\x80\xc6\xe1\xc9\xef\x77\xdf\x60\x25\x9e\x0b\xec\x1b\x17\xfa\xe1\x07\xad\xf0\x53\xca\x0f\xca\xff\x36\x0b\xf0\x92\x65\x8b\x93\xb0\x8d\x93\x69\x14\x5e\x9f\x9f\xf0\x57\x00\x00\x00\xff\xff\x30\xc0\x56\xbd\x5d\x01\x00\x00")
|
||||
|
||||
func _0008_add_push_notificationsUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__0008_add_push_notificationsUpSql,
|
||||
"0008_add_push_notifications.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _0008_add_push_notificationsUpSql() (*asset, error) {
|
||||
bytes, err := _0008_add_push_notificationsUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "0008_add_push_notifications.up.sql", size: 349, mode: os.FileMode(0644), modTime: time.Unix(1595832401, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5a, 0x0, 0xbf, 0xd0, 0xdd, 0xcd, 0x73, 0xe0, 0x7c, 0x56, 0xef, 0xdc, 0x57, 0x61, 0x94, 0x64, 0x70, 0xb9, 0xfa, 0xa1, 0x2a, 0x36, 0xc, 0x2f, 0xf8, 0x95, 0xa, 0x57, 0x3e, 0x7a, 0xd7, 0x12}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
|
@ -456,6 +477,8 @@ var _bindata = map[string]func() (*asset, error){
|
|||
|
||||
"0007_enable_waku_default.up.sql": _0007_enable_waku_defaultUpSql,
|
||||
|
||||
"0008_add_push_notifications.up.sql": _0008_add_push_notificationsUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
|
@ -500,19 +523,20 @@ type bintree struct {
|
|||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}},
|
||||
"0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}},
|
||||
"0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}},
|
||||
"0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}},
|
||||
"0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}},
|
||||
"0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}},
|
||||
"0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}},
|
||||
"0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}},
|
||||
"0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}},
|
||||
"0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}},
|
||||
"0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}},
|
||||
"0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
"0001_app.down.sql": &bintree{_0001_appDownSql, map[string]*bintree{}},
|
||||
"0001_app.up.sql": &bintree{_0001_appUpSql, map[string]*bintree{}},
|
||||
"0002_tokens.down.sql": &bintree{_0002_tokensDownSql, map[string]*bintree{}},
|
||||
"0002_tokens.up.sql": &bintree{_0002_tokensUpSql, map[string]*bintree{}},
|
||||
"0003_settings.down.sql": &bintree{_0003_settingsDownSql, map[string]*bintree{}},
|
||||
"0003_settings.up.sql": &bintree{_0003_settingsUpSql, map[string]*bintree{}},
|
||||
"0004_pending_stickers.down.sql": &bintree{_0004_pending_stickersDownSql, map[string]*bintree{}},
|
||||
"0004_pending_stickers.up.sql": &bintree{_0004_pending_stickersUpSql, map[string]*bintree{}},
|
||||
"0005_waku_mode.down.sql": &bintree{_0005_waku_modeDownSql, map[string]*bintree{}},
|
||||
"0005_waku_mode.up.sql": &bintree{_0005_waku_modeUpSql, map[string]*bintree{}},
|
||||
"0006_appearance.up.sql": &bintree{_0006_appearanceUpSql, map[string]*bintree{}},
|
||||
"0007_enable_waku_default.up.sql": &bintree{_0007_enable_waku_defaultUpSql, map[string]*bintree{}},
|
||||
"0008_add_push_notifications.up.sql": &bintree{_0008_add_push_notificationsUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE settings ADD COLUMN remote_push_notifications_enabled BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE settings ADD COLUMN send_push_notifications BOOLEAN DEFAULT TRUE;
|
||||
ALTER TABLE settings ADD COLUMN push_notifications_server_enabled BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE settings ADD COLUMN push_notifications_from_contacts_only BOOLEAN DEFAULT FALSE;
|
|
@ -59,25 +59,34 @@ type Settings struct {
|
|||
Mnemonic *string `json:"mnemonic,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Networks *json.RawMessage `json:"networks/networks"`
|
||||
NotificationsEnabled bool `json:"notifications-enabled?,omitempty"`
|
||||
PhotoPath string `json:"photo-path"`
|
||||
PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"`
|
||||
PreferredName *string `json:"preferred-name,omitempty"`
|
||||
PreviewPrivacy bool `json:"preview-privacy?"`
|
||||
PublicKey string `json:"public-key"`
|
||||
RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"`
|
||||
SigningPhrase string `json:"signing-phrase"`
|
||||
StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"`
|
||||
StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"`
|
||||
StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"`
|
||||
SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"`
|
||||
Appearance uint `json:"appearance"`
|
||||
Usernames *json.RawMessage `json:"usernames,omitempty"`
|
||||
WalletRootAddress types.Address `json:"wallet-root-address,omitempty"`
|
||||
WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"`
|
||||
WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"`
|
||||
WakuEnabled bool `json:"waku-enabled,omitempty"`
|
||||
WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"`
|
||||
// NotificationsEnabled indicates whether local notifications should be enabled (android only)
|
||||
NotificationsEnabled bool `json:"notifications-enabled?,omitempty"`
|
||||
PhotoPath string `json:"photo-path"`
|
||||
PinnedMailserver *json.RawMessage `json:"pinned-mailservers,omitempty"`
|
||||
PreferredName *string `json:"preferred-name,omitempty"`
|
||||
PreviewPrivacy bool `json:"preview-privacy?"`
|
||||
PublicKey string `json:"public-key"`
|
||||
// PushNotificationsServerEnabled indicates whether we should be running a push notification server
|
||||
PushNotificationsServerEnabled bool `json:"push-notifications-server-enabled?,omitempty"`
|
||||
// PushNotificationsFromContactsOnly indicates whether we should only receive push notifications from contacts
|
||||
PushNotificationsFromContactsOnly bool `json:"push-notifications-from-contacts-only?,omitempty"`
|
||||
RememberSyncingChoice bool `json:"remember-syncing-choice?,omitempty"`
|
||||
// RemotePushNotificationsEnabled indicates whether we should be using remote notifications (ios only for now)
|
||||
RemotePushNotificationsEnabled bool `json:"remote-push-notifications-enabled?,omitempty"`
|
||||
SigningPhrase string `json:"signing-phrase"`
|
||||
StickerPacksInstalled *json.RawMessage `json:"stickers/packs-installed,omitempty"`
|
||||
StickerPacksPending *json.RawMessage `json:"stickers/packs-pending,omitempty"`
|
||||
StickersRecentStickers *json.RawMessage `json:"stickers/recent-stickers,omitempty"`
|
||||
SyncingOnMobileNetwork bool `json:"syncing-on-mobile-network?,omitempty"`
|
||||
// SendPushNotifications indicates whether we should send push notifications for other clients
|
||||
SendPushNotifications bool `json:"send-push-notifications?,omitempty"`
|
||||
Appearance uint `json:"appearance"`
|
||||
Usernames *json.RawMessage `json:"usernames,omitempty"`
|
||||
WalletRootAddress types.Address `json:"wallet-root-address,omitempty"`
|
||||
WalletSetUpPassed bool `json:"wallet-set-up-passed?,omitempty"`
|
||||
WalletVisibleTokens *json.RawMessage `json:"wallet/visible-tokens,omitempty"`
|
||||
WakuEnabled bool `json:"waku-enabled,omitempty"`
|
||||
WakuBloomFilterMode bool `json:"waku-bloom-filter-mode,omitempty"`
|
||||
}
|
||||
|
||||
func NewDB(db *sql.DB) *Database {
|
||||
|
@ -246,6 +255,30 @@ func (db *Database) SaveSetting(setting string, value interface{}) error {
|
|||
return ErrInvalidConfig
|
||||
}
|
||||
update, err = db.db.Prepare("UPDATE settings SET remember_syncing_choice = ? WHERE synthetic_id = 'id'")
|
||||
case "remote-push-notifications-enabled?":
|
||||
_, ok := value.(bool)
|
||||
if !ok {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
update, err = db.db.Prepare("UPDATE settings SET remote_push_notifications_enabled = ? WHERE synthetic_id = 'id'")
|
||||
case "push-notifications-server-enabled?":
|
||||
_, ok := value.(bool)
|
||||
if !ok {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
update, err = db.db.Prepare("UPDATE settings SET push_notifications_server_enabled = ? WHERE synthetic_id = 'id'")
|
||||
case "push-notifications-from-contacts-only?":
|
||||
_, ok := value.(bool)
|
||||
if !ok {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
update, err = db.db.Prepare("UPDATE settings SET push_notifications_from_contacts_only = ? WHERE synthetic_id = 'id'")
|
||||
case "send-push-notifications?":
|
||||
_, ok := value.(bool)
|
||||
if !ok {
|
||||
return ErrInvalidConfig
|
||||
}
|
||||
update, err = db.db.Prepare("UPDATE settings SET send_push_notifications = ? WHERE synthetic_id = 'id'")
|
||||
case "stickers/packs-installed":
|
||||
value = &sqlite.JSONBlob{value}
|
||||
update, err = db.db.Prepare("UPDATE settings SET stickers_packs_installed = ? WHERE synthetic_id = 'id'")
|
||||
|
@ -304,7 +337,7 @@ func (db *Database) GetNodeConfig(nodecfg interface{}) error {
|
|||
|
||||
func (db *Database) GetSettings() (Settings, error) {
|
||||
var s Settings
|
||||
err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan(
|
||||
err := db.db.QueryRow("SELECT address, chaos_mode, currency, current_network, custom_bootnodes, custom_bootnodes_enabled, dapps_address, eip1581_address, fleet, hide_home_tooltip, installation_id, key_uid, keycard_instance_uid, keycard_paired_on, keycard_pairing, last_updated, latest_derived_path, log_level, mnemonic, name, networks, notifications_enabled, push_notifications_server_enabled, push_notifications_from_contacts_only, remote_push_notifications_enabled, send_push_notifications, photo_path, pinned_mailservers, preferred_name, preview_privacy, public_key, remember_syncing_choice, signing_phrase, stickers_packs_installed, stickers_packs_pending, stickers_recent_stickers, syncing_on_mobile_network, usernames, appearance, wallet_root_address, wallet_set_up_passed, wallet_visible_tokens, waku_enabled, waku_bloom_filter_mode FROM settings WHERE synthetic_id = 'id'").Scan(
|
||||
&s.Address,
|
||||
&s.ChaosMode,
|
||||
&s.Currency,
|
||||
|
@ -327,6 +360,10 @@ func (db *Database) GetSettings() (Settings, error) {
|
|||
&s.Name,
|
||||
&s.Networks,
|
||||
&s.NotificationsEnabled,
|
||||
&s.PushNotificationsServerEnabled,
|
||||
&s.PushNotificationsFromContactsOnly,
|
||||
&s.RemotePushNotificationsEnabled,
|
||||
&s.SendPushNotifications,
|
||||
&s.PhotoPath,
|
||||
&s.PinnedMailserver,
|
||||
&s.PreferredName,
|
||||
|
|
|
@ -22,19 +22,20 @@ var (
|
|||
|
||||
networks = json.RawMessage("{}")
|
||||
settings = Settings{
|
||||
Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
|
||||
CurrentNetwork: "mainnet_rpc",
|
||||
DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"),
|
||||
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
|
||||
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
|
||||
LatestDerivedPath: 0,
|
||||
Name: "Jittery Cornflowerblue Kingbird",
|
||||
Networks: &networks,
|
||||
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
|
||||
PreviewPrivacy: false,
|
||||
PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
|
||||
SigningPhrase: "yurt joey vibe",
|
||||
WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
|
||||
Address: types.HexToAddress("0xdC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
|
||||
CurrentNetwork: "mainnet_rpc",
|
||||
DappsAddress: types.HexToAddress("0xD1300f99fDF7346986CbC766903245087394ecd0"),
|
||||
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
|
||||
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
|
||||
LatestDerivedPath: 0,
|
||||
Name: "Jittery Cornflowerblue Kingbird",
|
||||
Networks: &networks,
|
||||
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
|
||||
PreviewPrivacy: false,
|
||||
PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
|
||||
SigningPhrase: "yurt joey vibe",
|
||||
SendPushNotifications: true,
|
||||
WalletRootAddress: types.HexToAddress("0x3B591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
|
||||
)
|
||||
|
||||
func setupTestDB(t *testing.T) (*Database, func()) {
|
||||
|
|
|
@ -30,6 +30,21 @@ import (
|
|||
"github.com/status-im/status-go/whisper/v6"
|
||||
)
|
||||
|
||||
// GetFreePort asks the kernel for a free open port that is ready to use.
|
||||
func getFreePort() (int, error) {
|
||||
addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
l, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer l.Close()
|
||||
return l.Addr().(*net.TCPAddr).Port, nil
|
||||
}
|
||||
|
||||
type PeerPoolSimulationSuite struct {
|
||||
suite.Suite
|
||||
|
||||
|
@ -41,14 +56,20 @@ type PeerPoolSimulationSuite struct {
|
|||
}
|
||||
|
||||
func TestPeerPoolSimulationSuite(t *testing.T) {
|
||||
s := new(PeerPoolSimulationSuite)
|
||||
s.port = 33731
|
||||
s := &PeerPoolSimulationSuite{}
|
||||
port, err := getFreePort()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.port = uint16(port)
|
||||
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
func (s *PeerPoolSimulationSuite) nextPort() uint16 {
|
||||
s.port++
|
||||
return s.port
|
||||
port, err := getFreePort()
|
||||
s.Require().NoError(err)
|
||||
return uint16(port)
|
||||
}
|
||||
|
||||
func (s *PeerPoolSimulationSuite) SetupTest() {
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: message.proto
|
||||
|
||||
package applicationmetadata
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type Message struct {
|
||||
Signature []byte `protobuf:"bytes,4001,opt,name=signature,proto3" json:"signature,omitempty"`
|
||||
Payload []byte `protobuf:"bytes,4002,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Message) Reset() { *m = Message{} }
|
||||
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||
func (*Message) ProtoMessage() {}
|
||||
func (*Message) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_33c57e4bae7b9afd, []int{0}
|
||||
}
|
||||
|
||||
func (m *Message) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Message.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Message.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Message) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Message.Merge(m, src)
|
||||
}
|
||||
func (m *Message) XXX_Size() int {
|
||||
return xxx_messageInfo_Message.Size(m)
|
||||
}
|
||||
func (m *Message) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Message.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Message proto.InternalMessageInfo
|
||||
|
||||
func (m *Message) GetSignature() []byte {
|
||||
if m != nil {
|
||||
return m.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Message) GetPayload() []byte {
|
||||
if m != nil {
|
||||
return m.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Message)(nil), "applicationmetadata.Message")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) }
|
||||
|
||||
var fileDescriptor_33c57e4bae7b9afd = []byte{
|
||||
// 112 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4d, 0x2d, 0x2e,
|
||||
0x4e, 0x4c, 0x4f, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x4e, 0x2c, 0x28, 0xc8, 0xc9,
|
||||
0x4c, 0x4e, 0x2c, 0xc9, 0xcc, 0xcf, 0xcb, 0x4d, 0x2d, 0x49, 0x4c, 0x49, 0x2c, 0x49, 0x54, 0x72,
|
||||
0xe6, 0x62, 0xf7, 0x85, 0xa8, 0x12, 0x92, 0xe5, 0xe2, 0x2c, 0xce, 0x4c, 0xcf, 0x4b, 0x2c, 0x29,
|
||||
0x2d, 0x4a, 0x95, 0x58, 0x28, 0xaf, 0xc0, 0xa8, 0xc1, 0x13, 0x84, 0x10, 0x11, 0x92, 0xe4, 0x62,
|
||||
0x2f, 0x48, 0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x91, 0x58, 0x04, 0x91, 0x84, 0xf1, 0x93, 0xd8, 0xc0,
|
||||
0x16, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb0, 0x7f, 0x4a, 0x96, 0x71, 0x00, 0x00, 0x00,
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package applicationmetadata;
|
||||
|
||||
message Message {
|
||||
bytes signature = 4001;
|
||||
bytes payload = 4002;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package applicationmetadata
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
)
|
||||
|
||||
func (m *Message) RecoverKey() (*ecdsa.PublicKey, error) {
|
||||
if m.Signature == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
recoveredKey, err := crypto.SigToPub(
|
||||
crypto.Keccak256(m.Payload),
|
||||
m.Signature,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return recoveredKey, nil
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package applicationmetadata
|
||||
|
||||
import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
//go:generate protoc --go_out=. ./message.proto
|
||||
|
||||
func Unmarshal(payload []byte) (*Message, error) {
|
||||
var message Message
|
||||
err := proto.Unmarshal(payload, &message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &message, nil
|
||||
}
|
|
@ -64,6 +64,10 @@ type Chat struct {
|
|||
Alias string `json:"alias,omitempty"`
|
||||
// Identicon generated from public key
|
||||
Identicon string `json:"identicon"`
|
||||
|
||||
// Muted is used to check whether we want to receive
|
||||
// push notifications for this chat
|
||||
Muted bool `json:"muted,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Chat) PublicKey() (*ecdsa.PublicKey, error) {
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
)
|
||||
|
||||
const nonceLength = 12
|
||||
|
||||
var ErrInvalidCiphertextLength = errors.New("invalid cyphertext length")
|
||||
|
||||
func HashPublicKey(pk *ecdsa.PublicKey) []byte {
|
||||
return Shake256(crypto.CompressPubkey(pk))
|
||||
}
|
||||
|
||||
func Decrypt(cyphertext []byte, key []byte) ([]byte, error) {
|
||||
if len(cyphertext) < nonceLength {
|
||||
return nil, ErrInvalidCiphertextLength
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := cyphertext[:nonceLength]
|
||||
return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil)
|
||||
}
|
||||
|
||||
func Encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
func Shake256(buf []byte) []byte {
|
||||
h := make([]byte, 64)
|
||||
sha3.ShakeSum256(h, buf)
|
||||
return h
|
||||
}
|
||||
|
||||
// IsPubKeyEqual checks that two public keys are equal
|
||||
func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
||||
// the curve is always the same, just compare the points
|
||||
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package common
|
||||
|
||||
type FeatureFlags struct {
|
||||
// Datasync indicates whether direct messages should be sent exclusively
|
||||
// using datasync, breaking change for non-v1 clients. Public messages
|
||||
// are not impacted
|
||||
Datasync bool
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
package protocol
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
@ -34,24 +35,41 @@ const (
|
|||
whisperPoWTime = 5
|
||||
)
|
||||
|
||||
type messageProcessor struct {
|
||||
// SentMessage reprent a message that has been passed to the transport layer
|
||||
type SentMessage struct {
|
||||
PublicKey *ecdsa.PublicKey
|
||||
Spec *encryption.ProtocolMessageSpec
|
||||
MessageIDs [][]byte
|
||||
}
|
||||
|
||||
type MessageProcessor struct {
|
||||
identity *ecdsa.PrivateKey
|
||||
datasync *datasync.DataSync
|
||||
protocol *encryption.Protocol
|
||||
transport transport.Transport
|
||||
logger *zap.Logger
|
||||
|
||||
featureFlags featureFlags
|
||||
// ephemeralKeys is a map that contains the ephemeral keys of the client, used
|
||||
// to decrypt messages
|
||||
ephemeralKeys map[string]*ecdsa.PrivateKey
|
||||
ephemeralKeysMutex sync.Mutex
|
||||
|
||||
// sentMessagesSubscriptions contains all the subscriptions for sent messages
|
||||
sentMessagesSubscriptions []chan<- *SentMessage
|
||||
// sentMessagesSubscriptions contains all the subscriptions for scheduled messages
|
||||
scheduledMessagesSubscriptions []chan<- *RawMessage
|
||||
|
||||
featureFlags FeatureFlags
|
||||
}
|
||||
|
||||
func newMessageProcessor(
|
||||
func NewMessageProcessor(
|
||||
identity *ecdsa.PrivateKey,
|
||||
database *sql.DB,
|
||||
enc *encryption.Protocol,
|
||||
transport transport.Transport,
|
||||
logger *zap.Logger,
|
||||
features featureFlags,
|
||||
) (*messageProcessor, error) {
|
||||
features FeatureFlags,
|
||||
) (*MessageProcessor, error) {
|
||||
dataSyncTransport := datasync.NewNodeTransport()
|
||||
dataSyncNode, err := datasyncnode.NewPersistentNode(
|
||||
database,
|
||||
|
@ -64,22 +82,23 @@ func newMessageProcessor(
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := datasync.New(dataSyncNode, dataSyncTransport, features.datasync, logger)
|
||||
ds := datasync.New(dataSyncNode, dataSyncTransport, features.Datasync, logger)
|
||||
|
||||
p := &messageProcessor{
|
||||
identity: identity,
|
||||
datasync: ds,
|
||||
protocol: enc,
|
||||
transport: transport,
|
||||
logger: logger,
|
||||
featureFlags: features,
|
||||
p := &MessageProcessor{
|
||||
identity: identity,
|
||||
datasync: ds,
|
||||
protocol: enc,
|
||||
transport: transport,
|
||||
logger: logger,
|
||||
ephemeralKeys: make(map[string]*ecdsa.PrivateKey),
|
||||
featureFlags: features,
|
||||
}
|
||||
|
||||
// Initializing DataSync is required to encrypt and send messages.
|
||||
// With DataSync enabled, messages are added to the DataSync
|
||||
// but actual encrypt and send calls are postponed.
|
||||
// sendDataSync is responsible for encrypting and sending postponed messages.
|
||||
if features.datasync {
|
||||
if features.Datasync {
|
||||
ds.Init(p.sendDataSync)
|
||||
ds.Start(300 * time.Millisecond)
|
||||
}
|
||||
|
@ -87,27 +106,42 @@ func newMessageProcessor(
|
|||
return p, nil
|
||||
}
|
||||
|
||||
func (p *messageProcessor) Stop() {
|
||||
func (p *MessageProcessor) Stop() {
|
||||
for _, c := range p.sentMessagesSubscriptions {
|
||||
close(c)
|
||||
}
|
||||
p.datasync.Stop() // idempotent op
|
||||
}
|
||||
|
||||
// SendPrivate takes encoded data, encrypts it and sends through the wire.
|
||||
func (p *messageProcessor) SendPrivate(
|
||||
func (p *MessageProcessor) SendPrivate(
|
||||
ctx context.Context,
|
||||
recipient *ecdsa.PublicKey,
|
||||
rawMessage *RawMessage,
|
||||
) ([]byte, error) {
|
||||
p.logger.Debug(
|
||||
"sending a private message",
|
||||
zap.Binary("public-key", crypto.FromECDSAPub(recipient)),
|
||||
zap.String("public-key", types.EncodeHex(crypto.FromECDSAPub(recipient))),
|
||||
zap.String("site", "SendPrivate"),
|
||||
)
|
||||
// Currently we don't support sending through datasync and setting custom waku fields,
|
||||
// as the datasync interface is not rich enough to propagate that information, so we
|
||||
// would have to add some complexity to handle this.
|
||||
if rawMessage.ResendAutomatically && (rawMessage.Sender != nil || rawMessage.SkipEncryption) {
|
||||
return nil, errors.New("setting identity, skip-encryption and datasync not supported")
|
||||
}
|
||||
|
||||
// Set sender identity if not specified
|
||||
if rawMessage.Sender == nil {
|
||||
rawMessage.Sender = p.identity
|
||||
}
|
||||
|
||||
return p.sendPrivate(ctx, recipient, rawMessage)
|
||||
}
|
||||
|
||||
// SendGroupRaw takes encoded data, encrypts it and sends through the wire,
|
||||
// SendGroup takes encoded data, encrypts it and sends through the wire,
|
||||
// always return the messageID
|
||||
func (p *messageProcessor) SendGroup(
|
||||
func (p *MessageProcessor) SendGroup(
|
||||
ctx context.Context,
|
||||
recipients []*ecdsa.PublicKey,
|
||||
rawMessage *RawMessage,
|
||||
|
@ -116,12 +150,18 @@ func (p *messageProcessor) SendGroup(
|
|||
"sending a private group message",
|
||||
zap.String("site", "SendGroup"),
|
||||
)
|
||||
// Calculate messageID first
|
||||
// Set sender if not specified
|
||||
if rawMessage.Sender == nil {
|
||||
rawMessage.Sender = p.identity
|
||||
}
|
||||
|
||||
// Calculate messageID first and set on raw message
|
||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap message")
|
||||
}
|
||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||
rawMessage.ID = types.EncodeHex(messageID)
|
||||
|
||||
// Send to each recipients
|
||||
for _, recipient := range recipients {
|
||||
|
@ -134,51 +174,67 @@ func (p *messageProcessor) SendGroup(
|
|||
}
|
||||
|
||||
// sendPrivate sends data to the recipient identifying with a given public key.
|
||||
func (p *messageProcessor) sendPrivate(
|
||||
func (p *MessageProcessor) sendPrivate(
|
||||
ctx context.Context,
|
||||
recipient *ecdsa.PublicKey,
|
||||
rawMessage *RawMessage,
|
||||
) ([]byte, error) {
|
||||
p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient)))
|
||||
p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient))))
|
||||
|
||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap message")
|
||||
}
|
||||
|
||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||
rawMessage.ID = types.EncodeHex(messageID)
|
||||
|
||||
if p.featureFlags.datasync {
|
||||
// Notify before dispatching, otherwise the dispatch subscription might happen
|
||||
// earlier than the scheduled
|
||||
p.notifyOnScheduledMessage(rawMessage)
|
||||
|
||||
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 {
|
||||
return nil, errors.Wrap(err, "failed to send message with datasync")
|
||||
}
|
||||
|
||||
// No need to call transport tracking.
|
||||
// It is done in a data sync dispatch step.
|
||||
} else {
|
||||
messageSpec, err := p.protocol.BuildDirectMessage(p.identity, recipient, wrappedMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec)
|
||||
} else if rawMessage.SkipEncryption {
|
||||
// When SkipEncryption is set we don't pass the message to the encryption layer
|
||||
messageIDs := [][]byte{messageID}
|
||||
hash, newMessage, err := p.sendRawMessage(ctx, recipient, wrappedMessage, messageIDs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||
}
|
||||
|
||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
||||
p.transport.Track(messageIDs, hash, newMessage)
|
||||
|
||||
} else {
|
||||
messageSpec, err := p.protocol.BuildDirectMessage(rawMessage.Sender, recipient, wrappedMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
messageIDs := [][]byte{messageID}
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||
}
|
||||
|
||||
p.transport.Track(messageIDs, hash, newMessage)
|
||||
}
|
||||
|
||||
return messageID, nil
|
||||
}
|
||||
|
||||
// sendPairInstallation sends data to the recipients, using DH
|
||||
func (p *messageProcessor) SendPairInstallation(
|
||||
func (p *MessageProcessor) SendPairInstallation(
|
||||
ctx context.Context,
|
||||
recipient *ecdsa.PublicKey,
|
||||
rawMessage *RawMessage,
|
||||
) ([]byte, error) {
|
||||
p.logger.Debug("sending private message", zap.Binary("recipient", crypto.FromECDSAPub(recipient)))
|
||||
p.logger.Debug("sending private message", zap.String("recipient", types.EncodeHex(crypto.FromECDSAPub(recipient))))
|
||||
|
||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||
if err != nil {
|
||||
|
@ -190,20 +246,22 @@ func (p *messageProcessor) SendPairInstallation(
|
|||
return nil, errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec)
|
||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||
messageIDs := [][]byte{messageID}
|
||||
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, recipient, messageSpec, messageIDs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send a message spec")
|
||||
}
|
||||
|
||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
||||
p.transport.Track(messageIDs, hash, newMessage)
|
||||
|
||||
return messageID, nil
|
||||
}
|
||||
|
||||
// EncodeMembershipUpdate takes a group and an optional chat message and returns the protobuf representation to be sent on the wire.
|
||||
// All the events in a group are encoded and added to the payload
|
||||
func (p *messageProcessor) EncodeMembershipUpdate(
|
||||
func (p *MessageProcessor) EncodeMembershipUpdate(
|
||||
group *v1protocol.Group,
|
||||
chatMessage *protobuf.ChatMessage,
|
||||
) ([]byte, error) {
|
||||
|
@ -222,43 +280,50 @@ func (p *messageProcessor) EncodeMembershipUpdate(
|
|||
}
|
||||
|
||||
// SendPublic takes encoded data, encrypts it and sends through the wire.
|
||||
func (p *messageProcessor) SendPublic(
|
||||
func (p *MessageProcessor) SendPublic(
|
||||
ctx context.Context,
|
||||
chatName string,
|
||||
rawMessage *RawMessage,
|
||||
) ([]byte, error) {
|
||||
var newMessage *types.NewMessage
|
||||
// Set sender
|
||||
if rawMessage.Sender == nil {
|
||||
rawMessage.Sender = p.identity
|
||||
}
|
||||
|
||||
wrappedMessage, err := p.wrapMessageV1(rawMessage)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap message")
|
||||
}
|
||||
|
||||
newMessage = &types.NewMessage{
|
||||
newMessage := &types.NewMessage{
|
||||
TTL: whisperTTL,
|
||||
Payload: wrappedMessage,
|
||||
PowTarget: calculatePoW(wrappedMessage),
|
||||
PowTime: whisperPoWTime,
|
||||
}
|
||||
|
||||
messageID := v1protocol.MessageID(&rawMessage.Sender.PublicKey, wrappedMessage)
|
||||
rawMessage.ID = types.EncodeHex(messageID)
|
||||
|
||||
// notify before dispatching
|
||||
p.notifyOnScheduledMessage(rawMessage)
|
||||
|
||||
hash, err := p.transport.SendPublic(ctx, newMessage, chatName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
messageID := v1protocol.MessageID(&p.identity.PublicKey, wrappedMessage)
|
||||
|
||||
p.transport.Track([][]byte{messageID}, hash, newMessage)
|
||||
|
||||
return messageID, nil
|
||||
}
|
||||
|
||||
// handleMessages expects a whisper message as input, and it will go through
|
||||
// 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, error) {
|
||||
logger := p.logger.With(zap.String("site", "handleMessages"))
|
||||
hlogger := logger.With(zap.ByteString("hash", shhMessage.Hash))
|
||||
var statusMessage v1protocol.StatusMessage
|
||||
|
@ -296,12 +361,32 @@ func (p *messageProcessor) handleMessages(shhMessage *types.Message, application
|
|||
return statusMessages, nil
|
||||
}
|
||||
|
||||
func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error {
|
||||
// fetchDecryptionKey returns the private key associated with this public key, and returns true if it's an ephemeral key
|
||||
func (p *MessageProcessor) fetchDecryptionKey(destination *ecdsa.PublicKey) (*ecdsa.PrivateKey, bool) {
|
||||
destinationID := types.EncodeHex(crypto.FromECDSAPub(destination))
|
||||
|
||||
p.ephemeralKeysMutex.Lock()
|
||||
decryptionKey, ok := p.ephemeralKeys[destinationID]
|
||||
p.ephemeralKeysMutex.Unlock()
|
||||
|
||||
// the key is not there, fallback on identity
|
||||
if !ok {
|
||||
return p.identity, false
|
||||
}
|
||||
return decryptionKey, true
|
||||
}
|
||||
|
||||
func (p *MessageProcessor) handleEncryptionLayer(ctx context.Context, message *v1protocol.StatusMessage) error {
|
||||
logger := p.logger.With(zap.String("site", "handleEncryptionLayer"))
|
||||
publicKey := message.SigPubKey()
|
||||
|
||||
err := message.HandleEncryption(p.identity, publicKey, p.protocol)
|
||||
if err == encryption.ErrDeviceNotFound {
|
||||
// if it's an ephemeral key, we don't negotiate a topic
|
||||
decryptionKey, skipNegotiation := p.fetchDecryptionKey(message.Dst)
|
||||
|
||||
err := message.HandleEncryption(decryptionKey, publicKey, p.protocol, skipNegotiation)
|
||||
|
||||
// if it's an ephemeral key, we don't have to handle a device not found error
|
||||
if err == encryption.ErrDeviceNotFound && !skipNegotiation {
|
||||
if err := p.handleErrDeviceNotFound(ctx, publicKey); err != nil {
|
||||
logger.Error("failed to handle ErrDeviceNotFound", zap.Error(err))
|
||||
}
|
||||
|
@ -313,7 +398,7 @@ func (p *messageProcessor) handleEncryptionLayer(ctx context.Context, message *v
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||
func (p *MessageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||
now := time.Now().Unix()
|
||||
advertise, err := p.protocol.ShouldAdvertiseBundle(publicKey, now)
|
||||
if err != nil {
|
||||
|
@ -330,7 +415,9 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe
|
|||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
_, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec)
|
||||
// We don't pass an array of messageIDs as no action needs to be taken
|
||||
// when sending a bundle
|
||||
_, _, err = p.sendMessageSpec(ctx, publicKey, messageSpec, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -340,15 +427,15 @@ func (p *messageProcessor) handleErrDeviceNotFound(ctx context.Context, publicKe
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *messageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
||||
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, p.identity)
|
||||
func (p *MessageProcessor) wrapMessageV1(rawMessage *RawMessage) ([]byte, error) {
|
||||
wrappedMessage, err := v1protocol.WrapMessageV1(rawMessage.Payload, rawMessage.MessageType, rawMessage.Sender)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to wrap message")
|
||||
}
|
||||
return wrappedMessage, nil
|
||||
}
|
||||
|
||||
func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error {
|
||||
func (p *MessageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []byte) error {
|
||||
groupID := datasync.ToOneToOneGroupID(&p.identity.PublicKey, publicKey)
|
||||
peerID := datasyncpeer.PublicKeyToPeerID(*publicKey)
|
||||
exist, err := p.datasync.IsPeerInGroup(groupID, peerID)
|
||||
|
@ -370,7 +457,8 @@ func (p *messageProcessor) addToDataSync(publicKey *ecdsa.PublicKey, message []b
|
|||
|
||||
// sendDataSync sends a message scheduled by the data sync layer.
|
||||
// Data Sync layer calls this method "dispatch" function.
|
||||
func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error {
|
||||
func (p *MessageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.PublicKey, encodedMessage []byte, payload *datasyncproto.Payload) error {
|
||||
// Calculate the messageIDs
|
||||
messageIDs := make([][]byte, 0, len(payload.Messages))
|
||||
for _, payload := range payload.Messages {
|
||||
messageIDs = append(messageIDs, v1protocol.MessageID(&p.identity.PublicKey, payload.Body))
|
||||
|
@ -381,7 +469,7 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
|
|||
return errors.Wrap(err, "failed to encrypt message")
|
||||
}
|
||||
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec)
|
||||
hash, newMessage, err := p.sendMessageSpec(ctx, publicKey, messageSpec, messageIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -391,9 +479,26 @@ func (p *messageProcessor) sendDataSync(ctx context.Context, publicKey *ecdsa.Pu
|
|||
return nil
|
||||
}
|
||||
|
||||
// sendRawMessage sends a message not wrapped in an encryption layer
|
||||
func (p *MessageProcessor) sendRawMessage(ctx context.Context, publicKey *ecdsa.PublicKey, payload []byte, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||
newMessage := &types.NewMessage{
|
||||
TTL: whisperTTL,
|
||||
Payload: payload,
|
||||
PowTarget: calculatePoW(payload),
|
||||
PowTime: whisperPoWTime,
|
||||
}
|
||||
|
||||
hash, err := p.transport.SendPrivateWithPartitioned(ctx, newMessage, publicKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return hash, newMessage, nil
|
||||
}
|
||||
|
||||
// sendMessageSpec analyses the spec properties and selects a proper transport method.
|
||||
func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec) ([]byte, *types.NewMessage, error) {
|
||||
newMessage, err := messageSpecToWhisper(messageSpec)
|
||||
func (p *MessageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa.PublicKey, messageSpec *encryption.ProtocolMessageSpec, messageIDs [][]byte) ([]byte, *types.NewMessage, error) {
|
||||
newMessage, err := MessageSpecToWhisper(messageSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -414,10 +519,70 @@ func (p *messageProcessor) sendMessageSpec(ctx context.Context, publicKey *ecdsa
|
|||
return nil, nil, err
|
||||
}
|
||||
|
||||
sentMessage := &SentMessage{
|
||||
PublicKey: publicKey,
|
||||
Spec: messageSpec,
|
||||
MessageIDs: messageIDs,
|
||||
}
|
||||
|
||||
p.notifyOnSentMessage(sentMessage)
|
||||
|
||||
return hash, newMessage, nil
|
||||
}
|
||||
|
||||
func messageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) {
|
||||
// SubscribeToSentMessages returns a channel where we publish every time a message is sent
|
||||
func (p *MessageProcessor) SubscribeToSentMessages() <-chan *SentMessage {
|
||||
c := make(chan *SentMessage, 100)
|
||||
p.sentMessagesSubscriptions = append(p.sentMessagesSubscriptions, c)
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *MessageProcessor) notifyOnSentMessage(sentMessage *SentMessage) {
|
||||
// Publish on channels, drop if buffer is full
|
||||
for _, c := range p.sentMessagesSubscriptions {
|
||||
select {
|
||||
case c <- sentMessage:
|
||||
default:
|
||||
p.logger.Warn("sent messages subscription channel full, dropping message")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SubscribeToScheduledMessages returns a channel where we publish every time a message is scheduled for sending
|
||||
func (p *MessageProcessor) SubscribeToScheduledMessages() <-chan *RawMessage {
|
||||
c := make(chan *RawMessage, 100)
|
||||
p.scheduledMessagesSubscriptions = append(p.scheduledMessagesSubscriptions, c)
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *MessageProcessor) notifyOnScheduledMessage(message *RawMessage) {
|
||||
// Publish on channels, drop if buffer is full
|
||||
for _, c := range p.scheduledMessagesSubscriptions {
|
||||
select {
|
||||
case c <- message:
|
||||
default:
|
||||
p.logger.Warn("scheduled messages subscription channel full, dropping message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *MessageProcessor) JoinPublic(chatID string) error {
|
||||
return p.transport.JoinPublic(chatID)
|
||||
}
|
||||
|
||||
// AddEphemeralKey adds an ephemeral key that we will be listening to
|
||||
// note that we never removed them from now, as waku/whisper does not
|
||||
// recalculate topics on removal, so effectively there's no benefit.
|
||||
// On restart they will be gone.
|
||||
func (p *MessageProcessor) AddEphemeralKey(privateKey *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||
p.ephemeralKeysMutex.Lock()
|
||||
p.ephemeralKeys[types.EncodeHex(crypto.FromECDSAPub(&privateKey.PublicKey))] = privateKey
|
||||
p.ephemeralKeysMutex.Unlock()
|
||||
return p.transport.LoadKeyFilters(privateKey)
|
||||
}
|
||||
|
||||
func MessageSpecToWhisper(spec *encryption.ProtocolMessageSpec) (*types.NewMessage, error) {
|
||||
var newMessage *types.NewMessage
|
||||
|
||||
payload, err := proto.Marshal(spec.Message)
|
||||
|
@ -444,9 +609,3 @@ func calculatePoW(payload []byte) float64 {
|
|||
}
|
||||
return whisperDefaultPoW
|
||||
}
|
||||
|
||||
// isPubKeyEqual checks that two public keys are equal
|
||||
func isPubKeyEqual(a, b *ecdsa.PublicKey) bool {
|
||||
// the curve is always the same, just compare the points
|
||||
return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package protocol
|
||||
package common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -33,22 +33,20 @@ func TestMessageProcessorSuite(t *testing.T) {
|
|||
type MessageProcessorSuite struct {
|
||||
suite.Suite
|
||||
|
||||
processor *messageProcessor
|
||||
processor *MessageProcessor
|
||||
tmpDir string
|
||||
testMessage Message
|
||||
testMessage protobuf.ChatMessage
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *MessageProcessorSuite) SetupTest() {
|
||||
s.testMessage = Message{
|
||||
ChatMessage: protobuf.ChatMessage{
|
||||
Text: "abc123",
|
||||
ChatId: "testing-adamb",
|
||||
ContentType: protobuf.ChatMessage_TEXT_PLAIN,
|
||||
MessageType: protobuf.ChatMessage_PUBLIC_GROUP,
|
||||
Clock: 154593077368201,
|
||||
Timestamp: 1545930773682,
|
||||
},
|
||||
s.testMessage = protobuf.ChatMessage{
|
||||
Text: "abc123",
|
||||
ChatId: "testing-adamb",
|
||||
ContentType: protobuf.ChatMessage_TEXT_PLAIN,
|
||||
MessageType: protobuf.ChatMessage_PUBLIC_GROUP,
|
||||
Clock: 154593077368201,
|
||||
Timestamp: 1545930773682,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
@ -81,8 +79,6 @@ func (s *MessageProcessorSuite) SetupTest() {
|
|||
whisperConfig.MinimumAcceptedPOW = 0
|
||||
shh := whisper.New(&whisperConfig)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
config := &config{}
|
||||
s.Require().NoError(WithDatasync()(config))
|
||||
|
||||
whisperTransport, err := transport.NewTransport(
|
||||
gethbridge.NewGethWhisperWrapper(shh),
|
||||
|
@ -94,13 +90,13 @@ func (s *MessageProcessorSuite) SetupTest() {
|
|||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.processor, err = newMessageProcessor(
|
||||
s.processor, err = NewMessageProcessor(
|
||||
identity,
|
||||
database,
|
||||
encryptionProtocol,
|
||||
whisperTransport,
|
||||
s.logger,
|
||||
featureFlags{},
|
||||
FeatureFlags{},
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
@ -127,7 +123,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))
|
||||
|
@ -135,7 +131,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesWrapped() {
|
|||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
||||
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||
}
|
||||
|
||||
|
@ -163,7 +159,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
|
||||
|
@ -172,7 +168,7 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasync() {
|
|||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
||||
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||
}
|
||||
|
||||
|
@ -230,7 +226,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,
|
||||
|
@ -240,6 +236,6 @@ func (s *MessageProcessorSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
|
|||
s.Require().Equal(v1protocol.MessageID(&authorKey.PublicKey, wrappedPayload), decodedMessages[0].ID)
|
||||
s.Require().Equal(encodedPayload, decodedMessages[0].DecryptedPayload)
|
||||
parsedMessage := decodedMessages[0].ParsedMessage.(protobuf.ChatMessage)
|
||||
s.Require().True(proto.Equal(&s.testMessage.ChatMessage, &parsedMessage))
|
||||
s.Require().True(proto.Equal(&s.testMessage, &parsedMessage))
|
||||
s.Require().Equal(protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, decodedMessages[0].Type)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
// RawMessage represent a sent or received message, kept for being able
|
||||
// to re-send/propagate
|
||||
type RawMessage struct {
|
||||
ID string
|
||||
LocalChatID string
|
||||
LastSent uint64
|
||||
SendCount int
|
||||
Sent bool
|
||||
ResendAutomatically bool
|
||||
SkipEncryption bool
|
||||
SendPushNotification bool
|
||||
MessageType protobuf.ApplicationMetadataMessage_Type
|
||||
Payload []byte
|
||||
Sender *ecdsa.PrivateKey
|
||||
Recipients []*ecdsa.PublicKey
|
||||
}
|
|
@ -109,20 +109,6 @@ type Message struct {
|
|||
SigPubKey *ecdsa.PublicKey `json:"-"`
|
||||
}
|
||||
|
||||
// RawMessage represent a sent or received message, kept for being able
|
||||
// to re-send/propagate
|
||||
type RawMessage struct {
|
||||
ID string
|
||||
LocalChatID string
|
||||
LastSent uint64
|
||||
SendCount int
|
||||
Sent bool
|
||||
ResendAutomatically bool
|
||||
MessageType protobuf.ApplicationMetadataMessage_Type
|
||||
Payload []byte
|
||||
Recipients []*ecdsa.PublicKey
|
||||
}
|
||||
|
||||
func (m *Message) MarshalJSON() ([]byte, error) {
|
||||
type StickerAlias struct {
|
||||
Hash string `json:"hash"`
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
|
@ -146,7 +147,7 @@ func (m *MessageHandler) handleCommandMessage(state *ReceivedMessageState, messa
|
|||
message.LocalChatID = chat.ID
|
||||
|
||||
// Increase unviewed count
|
||||
if !isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey) {
|
||||
chat.UnviewedMessagesCount++
|
||||
message.OutgoingStatus = ""
|
||||
} else {
|
||||
|
@ -332,7 +333,7 @@ func (m *MessageHandler) HandleChatMessage(state *ReceivedMessageState) error {
|
|||
receivedMessage.LocalChatID = chat.ID
|
||||
|
||||
// Increase unviewed count
|
||||
if !isPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(receivedMessage.SigPubKey, &m.identity.PublicKey) {
|
||||
chat.UnviewedMessagesCount++
|
||||
} else {
|
||||
// Our own message, mark as sent
|
||||
|
@ -582,7 +583,7 @@ func (m *MessageHandler) matchMessage(message *Message, chats map[string]*Chat,
|
|||
return nil, errors.New("received a public message from non-existing chat")
|
||||
}
|
||||
return chat, nil
|
||||
case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && isPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
|
||||
case message.MessageType == protobuf.ChatMessage_ONE_TO_ONE && common.IsPubKeyEqual(message.SigPubKey, &m.identity.PublicKey):
|
||||
// It's a private message coming from us so we rely on Message.ChatID
|
||||
// If chat does not exist, it should be created to support multidevice synchronization.
|
||||
chatID := message.ChatId
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -18,6 +19,7 @@ import (
|
|||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
enstypes "github.com/status-im/status-go/eth-node/types/ens"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"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"
|
||||
|
@ -25,6 +27,8 @@ import (
|
|||
"github.com/status-im/status-go/protocol/identity/identicon"
|
||||
"github.com/status-im/status-go/protocol/images"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
wakutransp "github.com/status-im/status-go/protocol/transport/waku"
|
||||
|
@ -55,11 +59,13 @@ type Messenger struct {
|
|||
persistence *sqlitePersistence
|
||||
transport transport.Transport
|
||||
encryptor *encryption.Protocol
|
||||
processor *messageProcessor
|
||||
processor *common.MessageProcessor
|
||||
handler *MessageHandler
|
||||
pushNotificationClient *pushnotificationclient.Client
|
||||
pushNotificationServer *pushnotificationserver.Server
|
||||
logger *zap.Logger
|
||||
verifyTransactionClient EthClient
|
||||
featureFlags featureFlags
|
||||
featureFlags common.FeatureFlags
|
||||
messagesPersistenceEnabled bool
|
||||
shutdownTasks []func() error
|
||||
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
||||
|
@ -68,6 +74,8 @@ type Messenger struct {
|
|||
allInstallations map[string]*multidevice.Installation
|
||||
modifiedInstallations map[string]bool
|
||||
installationID string
|
||||
mailserver []byte
|
||||
database *sql.DB
|
||||
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
@ -88,111 +96,11 @@ func (m *MessengerResponse) IsEmpty() bool {
|
|||
return len(m.Chats) == 0 && len(m.Messages) == 0 && len(m.Contacts) == 0 && len(m.Installations) == 0
|
||||
}
|
||||
|
||||
type featureFlags struct {
|
||||
// datasync indicates whether direct messages should be sent exclusively
|
||||
// using datasync, breaking change for non-v1 clients. Public messages
|
||||
// are not impacted
|
||||
datasync bool
|
||||
}
|
||||
|
||||
type dbConfig struct {
|
||||
dbPath string
|
||||
dbKey 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
|
||||
// won't be pulling messages from mailservers until it reloads the chats/filters
|
||||
onNegotiatedFilters func([]*transport.Filter)
|
||||
// DEPRECATED: no need to expose it
|
||||
onSendContactCodeHandler func(*encryption.ProtocolMessageSpec)
|
||||
|
||||
// systemMessagesTranslations holds translations for system-messages
|
||||
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
||||
// Config for the envelopes monitor
|
||||
envelopesMonitorConfig *transport.EnvelopesMonitorConfig
|
||||
|
||||
messagesPersistenceEnabled bool
|
||||
featureFlags featureFlags
|
||||
|
||||
// A path to a database or a database instance is required.
|
||||
// The database instance has a higher priority.
|
||||
dbConfig dbConfig
|
||||
db *sql.DB
|
||||
|
||||
verifyTransactionClient EthClient
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type Option func(*config) error
|
||||
|
||||
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
|
||||
// nolint: unused
|
||||
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
|
||||
return func(c *config) error {
|
||||
c.systemMessagesTranslations = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option {
|
||||
return func(c *config) error {
|
||||
c.onNegotiatedFilters = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomLogger(logger *zap.Logger) Option {
|
||||
return func(c *config) error {
|
||||
c.logger = logger
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithMessagesPersistenceEnabled() Option {
|
||||
return func(c *config) error {
|
||||
c.messagesPersistenceEnabled = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatabaseConfig(dbPath, dbKey string) Option {
|
||||
return func(c *config) error {
|
||||
c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithVerifyTransactionClient(client EthClient) Option {
|
||||
return func(c *config) error {
|
||||
c.verifyTransactionClient = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatabase(db *sql.DB) Option {
|
||||
return func(c *config) error {
|
||||
c.db = db
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatasync() func(c *config) error {
|
||||
return func(c *config) error {
|
||||
c.featureFlags.datasync = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option {
|
||||
return func(c *config) error {
|
||||
c.envelopesMonitorConfig = emc
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func NewMessenger(
|
||||
identity *ecdsa.PrivateKey,
|
||||
node types.Node,
|
||||
|
@ -245,7 +153,7 @@ func NewMessenger(
|
|||
slogger := logger.With(zap.String("site", "onSendContactCodeHandler"))
|
||||
slogger.Debug("received a SendContactCode request")
|
||||
|
||||
newMessage, err := messageSpecToWhisper(messageSpec)
|
||||
newMessage, err := common.MessageSpecToWhisper(messageSpec)
|
||||
if err != nil {
|
||||
slogger.Warn("failed to convert spec to Whisper message", zap.Error(err))
|
||||
return
|
||||
|
@ -328,7 +236,7 @@ func NewMessenger(
|
|||
logger,
|
||||
)
|
||||
|
||||
processor, err := newMessageProcessor(
|
||||
processor, err := common.NewMessageProcessor(
|
||||
identity,
|
||||
database,
|
||||
encryptionProtocol,
|
||||
|
@ -340,6 +248,30 @@ func NewMessenger(
|
|||
return nil, errors.Wrap(err, "failed to create messageProcessor")
|
||||
}
|
||||
|
||||
// Initialize push notification server
|
||||
var pushNotificationServer *pushnotificationserver.Server
|
||||
if c.pushNotificationServerConfig != nil {
|
||||
c.pushNotificationServerConfig.Identity = identity
|
||||
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(database)
|
||||
pushNotificationServer = pushnotificationserver.New(c.pushNotificationServerConfig, pushNotificationServerPersistence, processor)
|
||||
}
|
||||
|
||||
// Initialize push notification client
|
||||
pushNotificationClientPersistence := pushnotificationclient.NewPersistence(database)
|
||||
pushNotificationClientConfig := c.pushNotificationClientConfig
|
||||
if pushNotificationClientConfig == nil {
|
||||
pushNotificationClientConfig = &pushnotificationclient.Config{}
|
||||
}
|
||||
|
||||
// Overriding until we handle different identities
|
||||
pushNotificationClientConfig.Identity = identity
|
||||
// Hardcoding this for now, as it's the only one we support
|
||||
pushNotificationClientConfig.TokenType = protobuf.PushNotificationRegistration_APN_TOKEN
|
||||
pushNotificationClientConfig.Logger = logger
|
||||
pushNotificationClientConfig.InstallationID = installationID
|
||||
|
||||
pushNotificationClient := pushnotificationclient.New(pushNotificationClientPersistence, pushNotificationClientConfig, processor)
|
||||
|
||||
handler := newMessageHandler(identity, logger, &sqlitePersistence{db: database})
|
||||
|
||||
messenger = &Messenger{
|
||||
|
@ -350,6 +282,8 @@ func NewMessenger(
|
|||
encryptor: encryptionProtocol,
|
||||
processor: processor,
|
||||
handler: handler,
|
||||
pushNotificationClient: pushNotificationClient,
|
||||
pushNotificationServer: pushNotificationServer,
|
||||
featureFlags: c.featureFlags,
|
||||
systemMessagesTranslations: c.systemMessagesTranslations,
|
||||
allChats: make(map[string]*Chat),
|
||||
|
@ -359,8 +293,10 @@ func NewMessenger(
|
|||
modifiedInstallations: make(map[string]bool),
|
||||
messagesPersistenceEnabled: c.messagesPersistenceEnabled,
|
||||
verifyTransactionClient: c.verifyTransactionClient,
|
||||
database: database,
|
||||
shutdownTasks: []func() error{
|
||||
database.Close,
|
||||
pushNotificationClient.Stop,
|
||||
transp.ResetFilters,
|
||||
transp.Stop,
|
||||
func() error { processor.Stop(); return nil },
|
||||
|
@ -377,6 +313,21 @@ func NewMessenger(
|
|||
}
|
||||
|
||||
func (m *Messenger) Start() error {
|
||||
m.logger.Info("starting messenger", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&m.identity.PublicKey))))
|
||||
// Start push notification server
|
||||
if m.pushNotificationServer != nil {
|
||||
if err := m.pushNotificationServer.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start push notification client
|
||||
if m.pushNotificationClient != nil {
|
||||
if err := m.pushNotificationClient.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return m.encryptor.Start(m.identity)
|
||||
}
|
||||
|
||||
|
@ -674,7 +625,7 @@ func (m *Messenger) CreateGroupChatWithMembers(ctx context.Context, name string,
|
|||
}
|
||||
m.allChats[chat.ID] = &chat
|
||||
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -740,7 +691,7 @@ func (m *Messenger) RemoveMemberFromGroupChat(ctx context.Context, chatID string
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -803,7 +754,7 @@ func (m *Messenger) AddMembersToGroupChat(ctx context.Context, chatID string, me
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -868,7 +819,7 @@ func (m *Messenger) ChangeGroupChatName(ctx context.Context, chatID string, name
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -934,7 +885,7 @@ func (m *Messenger) AddAdminsToGroupChat(ctx context.Context, chatID string, mem
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -1002,7 +953,7 @@ func (m *Messenger) ConfirmJoiningGroup(ctx context.Context, chatID string) (*Me
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -1070,7 +1021,7 @@ func (m *Messenger) LeaveGroupChat(ctx context.Context, chatID string, remove bo
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_MEMBERSHIP_UPDATE_MESSAGE,
|
||||
|
@ -1174,6 +1125,14 @@ func (m *Messenger) isNewContact(contact *Contact) bool {
|
|||
return contact.IsAdded() && (!ok || !previousContact.IsAdded())
|
||||
}
|
||||
|
||||
func (m *Messenger) removedContact(contact *Contact) bool {
|
||||
previousContact, ok := m.allContacts[contact.ID]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return previousContact.IsAdded() && !contact.IsAdded()
|
||||
}
|
||||
|
||||
func (m *Messenger) saveContact(contact *Contact) error {
|
||||
name, identicon, err := generateAliasAndIdenticon(contact.ID)
|
||||
if err != nil {
|
||||
|
@ -1190,6 +1149,9 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
|||
}
|
||||
}
|
||||
|
||||
// We check if it should re-register with the push notification server
|
||||
shouldReregisterForPushNotifications := m.pushNotificationClient != nil && (m.isNewContact(contact) || m.removedContact(contact))
|
||||
|
||||
err = m.persistence.SaveContact(contact, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1197,6 +1159,16 @@ func (m *Messenger) saveContact(contact *Contact) error {
|
|||
|
||||
m.allContacts[contact.ID] = contact
|
||||
|
||||
// Reregister only when data has changed
|
||||
if shouldReregisterForPushNotifications {
|
||||
m.logger.Info("contact state changed, re-registering for push notification")
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
err := m.pushNotificationClient.Reregister(contactIDs, mutedChatIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1256,11 +1228,12 @@ func (m *Messenger) ReSendChatMessage(ctx context.Context, messageID string) err
|
|||
return errors.New("chat not found")
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: message.Payload,
|
||||
MessageType: message.MessageType,
|
||||
Recipients: message.Recipients,
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: message.Payload,
|
||||
MessageType: message.MessageType,
|
||||
Recipients: message.Recipients,
|
||||
ResendAutomatically: message.ResendAutomatically,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -1276,7 +1249,7 @@ func (m *Messenger) hasPairedDevices() bool {
|
|||
}
|
||||
|
||||
// sendToPairedDevices will check if we have any paired devices and send to them if necessary
|
||||
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) error {
|
||||
func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *common.RawMessage) error {
|
||||
hasPairedDevices := m.hasPairedDevices()
|
||||
// We send a message to any paired device
|
||||
if hasPairedDevices {
|
||||
|
@ -1288,7 +1261,7 @@ func (m *Messenger) sendToPairedDevices(ctx context.Context, spec *RawMessage) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *RawMessage) ([]byte, error) {
|
||||
func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) {
|
||||
var err error
|
||||
var id []byte
|
||||
|
||||
|
@ -1307,7 +1280,7 @@ func (m *Messenger) dispatchPairInstallationMessage(ctx context.Context, spec *R
|
|||
return id, nil
|
||||
}
|
||||
|
||||
func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]byte, error) {
|
||||
func (m *Messenger) dispatchMessage(ctx context.Context, spec *common.RawMessage) ([]byte, error) {
|
||||
var err error
|
||||
var id []byte
|
||||
logger := m.logger.With(zap.String("site", "dispatchMessage"), zap.String("chatID", spec.LocalChatID))
|
||||
|
@ -1322,7 +1295,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isPubKeyEqual(publicKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(publicKey, &m.identity.PublicKey) {
|
||||
id, err = m.processor.SendPrivate(ctx, publicKey, spec)
|
||||
|
||||
if err != nil {
|
||||
|
@ -1357,7 +1330,7 @@ func (m *Messenger) dispatchMessage(ctx context.Context, spec *RawMessage) ([]by
|
|||
// Filter out my key from the recipients
|
||||
n := 0
|
||||
for _, recipient := range spec.Recipients {
|
||||
if !isPubKeyEqual(recipient, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(recipient, &m.identity.PublicKey) {
|
||||
spec.Recipients[n] = recipient
|
||||
n++
|
||||
}
|
||||
|
@ -1457,10 +1430,12 @@ func (m *Messenger) SendChatMessage(ctx context.Context, message *Message) (*Mes
|
|||
return nil, errors.New("chat type not supported")
|
||||
}
|
||||
|
||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
||||
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
SendPushNotification: !chat.Public(),
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_CHAT_MESSAGE,
|
||||
ResendAutomatically: true,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1564,7 +1539,7 @@ func (m *Messenger) sendContactUpdate(ctx context.Context, chatID, ensName, prof
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_CONTACT_UPDATE,
|
||||
|
@ -1658,7 +1633,7 @@ func (m *Messenger) SendPairInstallation(ctx context.Context) (*MessengerRespons
|
|||
return nil, err
|
||||
}
|
||||
|
||||
_, err = m.dispatchPairInstallationMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchPairInstallationMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_PAIR_INSTALLATION,
|
||||
|
@ -1705,7 +1680,7 @@ func (m *Messenger) syncPublicChat(ctx context.Context, publicChat *Chat) error
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT,
|
||||
|
@ -1748,7 +1723,7 @@ func (m *Messenger) syncContact(ctx context.Context, contact *Contact) error {
|
|||
return err
|
||||
}
|
||||
|
||||
_, err = m.dispatchMessage(ctx, &RawMessage{
|
||||
_, err = m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chatID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT,
|
||||
|
@ -1828,7 +1803,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
for _, messages := range chatWithMessages {
|
||||
for _, shhMessage := range messages {
|
||||
// TODO: fix this to use an exported method.
|
||||
statusMessages, err := m.processor.handleMessages(shhMessage, true)
|
||||
statusMessages, err := m.processor.HandleMessages(shhMessage, true)
|
||||
if err != nil {
|
||||
logger.Info("failed to decode messages", zap.Error(err))
|
||||
continue
|
||||
|
@ -1897,7 +1872,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
continue
|
||||
}
|
||||
case protobuf.PairInstallation:
|
||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
logger.Warn("not coming from us, ignoring")
|
||||
continue
|
||||
}
|
||||
|
@ -1910,7 +1885,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
}
|
||||
|
||||
case protobuf.SyncInstallationContact:
|
||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
logger.Warn("not coming from us, ignoring")
|
||||
continue
|
||||
}
|
||||
|
@ -1923,7 +1898,7 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
continue
|
||||
}
|
||||
case protobuf.SyncInstallationPublicChat:
|
||||
if !isPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
if !common.IsPubKeyEqual(messageState.CurrentMessageState.PublicKey, &m.identity.PublicKey) {
|
||||
logger.Warn("not coming from us, ignoring")
|
||||
continue
|
||||
}
|
||||
|
@ -1996,8 +1971,80 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
logger.Warn("failed to handle ContactUpdate", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
case protobuf.PushNotificationQuery:
|
||||
logger.Debug("Received PushNotificationQuery")
|
||||
if m.pushNotificationServer == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationQuery")
|
||||
if err := m.pushNotificationServer.HandlePushNotificationQuery(publicKey, msg.ID, msg.ParsedMessage.(protobuf.PushNotificationQuery)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationQuery", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
case protobuf.PushNotificationRegistrationResponse:
|
||||
logger.Debug("Received PushNotificationRegistrationResponse")
|
||||
if m.pushNotificationClient == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationRegistrationResponse")
|
||||
if err := m.pushNotificationClient.HandlePushNotificationRegistrationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRegistrationResponse)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationRegistrationResponse", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
case protobuf.PushNotificationResponse:
|
||||
logger.Debug("Received PushNotificationResponse")
|
||||
if m.pushNotificationClient == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationResponse")
|
||||
if err := m.pushNotificationClient.HandlePushNotificationResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationResponse)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationResponse", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
|
||||
case protobuf.PushNotificationQueryResponse:
|
||||
logger.Debug("Received PushNotificationQueryResponse")
|
||||
if m.pushNotificationClient == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationQueryResponse")
|
||||
if err := m.pushNotificationClient.HandlePushNotificationQueryResponse(publicKey, msg.ParsedMessage.(protobuf.PushNotificationQueryResponse)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationQueryResponse", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
|
||||
case protobuf.PushNotificationRequest:
|
||||
logger.Debug("Received PushNotificationRequest")
|
||||
if m.pushNotificationServer == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationRequest")
|
||||
if err := m.pushNotificationServer.HandlePushNotificationRequest(publicKey, msg.ParsedMessage.(protobuf.PushNotificationRequest)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationRequest", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
|
||||
default:
|
||||
logger.Debug("message not handled")
|
||||
// Check if is an encrypted PushNotificationRegistration
|
||||
if msg.Type == protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION {
|
||||
logger.Debug("Received PushNotificationRegistration")
|
||||
if m.pushNotificationServer == nil {
|
||||
continue
|
||||
}
|
||||
logger.Debug("Handling PushNotificationRegistration")
|
||||
if err := m.pushNotificationServer.HandlePushNotificationRegistration(publicKey, msg.ParsedMessage.([]byte)); err != nil {
|
||||
logger.Warn("failed to handle PushNotificationRegistration", zap.Error(err))
|
||||
}
|
||||
// We continue in any case, no changes to messenger
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Debug("message not handled", zap.Any("messageType", reflect.TypeOf(msg.ParsedMessage)))
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2076,13 +2123,20 @@ func (m *Messenger) handleRetrievedMessages(chatWithMessages map[transport.Filte
|
|||
return messageState.Response, nil
|
||||
}
|
||||
|
||||
// SetMailserver sets the currently used mailserver
|
||||
func (m *Messenger) SetMailserver(peer []byte) {
|
||||
m.mailserver = peer
|
||||
}
|
||||
|
||||
func (m *Messenger) RequestHistoricMessages(
|
||||
ctx context.Context,
|
||||
peer []byte, // should be removed after mailserver logic is ported
|
||||
from, to uint32,
|
||||
cursor []byte,
|
||||
) ([]byte, error) {
|
||||
return m.transport.SendMessagesRequest(ctx, peer, from, to, cursor)
|
||||
if m.mailserver == nil {
|
||||
return nil, errors.New("no mailserver selected")
|
||||
}
|
||||
return m.transport.SendMessagesRequest(ctx, m.mailserver, from, to, cursor)
|
||||
}
|
||||
|
||||
func (m *Messenger) LoadFilters(filters []*transport.Filter) ([]*transport.Filter, error) {
|
||||
|
@ -2163,6 +2217,46 @@ func (m *Messenger) MarkAllRead(chatID string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MuteChat signals to the messenger that we don't want to be notified
|
||||
// on new messages from this chat
|
||||
func (m *Messenger) MuteChat(chatID string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
chat, ok := m.allChats[chatID]
|
||||
if !ok {
|
||||
return errors.New("chat not found")
|
||||
}
|
||||
|
||||
err := m.persistence.MuteChat(chatID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.Muted = true
|
||||
m.allChats[chat.ID] = chat
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmuteChat signals to the messenger that we want to be notified
|
||||
// on new messages from this chat
|
||||
func (m *Messenger) UnmuteChat(chatID string) error {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
chat, ok := m.allChats[chatID]
|
||||
if !ok {
|
||||
return errors.New("chat not found")
|
||||
}
|
||||
|
||||
err := m.persistence.UnmuteChat(chatID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chat.Muted = false
|
||||
m.allChats[chat.ID] = chat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) UpdateMessageOutgoingStatus(id, newOutgoingStatus string) error {
|
||||
return m.persistence.UpdateMessageOutgoingStatus(id, newOutgoingStatus)
|
||||
}
|
||||
|
@ -2274,7 +2368,7 @@ func (m *Messenger) RequestTransaction(ctx context.Context, chatID, value, contr
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_TRANSACTION,
|
||||
|
@ -2350,7 +2444,7 @@ func (m *Messenger) RequestAddressForTransaction(ctx context.Context, chatID, fr
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
id, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||
|
@ -2452,7 +2546,7 @@ func (m *Messenger) AcceptRequestAddressForTransaction(ctx context.Context, mess
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_ACCEPT_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||
|
@ -2535,7 +2629,7 @@ func (m *Messenger) DeclineRequestTransaction(ctx context.Context, messageID str
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_TRANSACTION,
|
||||
|
@ -2617,7 +2711,7 @@ func (m *Messenger) DeclineRequestAddressForTransaction(ctx context.Context, mes
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_DECLINE_REQUEST_ADDRESS_FOR_TRANSACTION,
|
||||
|
@ -2714,7 +2808,7 @@ func (m *Messenger) AcceptRequestTransaction(ctx context.Context, transactionHas
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
||||
|
@ -2791,7 +2885,7 @@ func (m *Messenger) SendTransaction(ctx context.Context, chatID, value, contract
|
|||
return nil, err
|
||||
}
|
||||
|
||||
newMessageID, err := m.dispatchMessage(ctx, &RawMessage{
|
||||
newMessageID, err := m.dispatchMessage(ctx, &common.RawMessage{
|
||||
LocalChatID: chat.ID,
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_SEND_TRANSACTION,
|
||||
|
@ -2965,6 +3059,142 @@ func (m *Messenger) Timesource() TimeSource {
|
|||
return m.getTimesource()
|
||||
}
|
||||
|
||||
// AddPushNotificationsServer adds a push notification server
|
||||
func (m *Messenger) AddPushNotificationsServer(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
return m.pushNotificationClient.AddPushNotificationsServer(publicKey)
|
||||
}
|
||||
|
||||
// RemovePushNotificationServer removes a push notification server
|
||||
func (m *Messenger) RemovePushNotificationServer(ctx context.Context, publicKey *ecdsa.PublicKey) error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
return m.pushNotificationClient.RemovePushNotificationServer(publicKey)
|
||||
}
|
||||
|
||||
// UnregisterFromPushNotifications unregister from any server
|
||||
func (m *Messenger) UnregisterFromPushNotifications(ctx context.Context) error {
|
||||
return m.pushNotificationClient.Unregister()
|
||||
}
|
||||
|
||||
// DisableSendingPushNotifications signals the client not to send any push notification
|
||||
func (m *Messenger) DisableSendingPushNotifications() error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
m.pushNotificationClient.DisableSending()
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnableSendingPushNotifications signals the client to send push notifications
|
||||
func (m *Messenger) EnableSendingPushNotifications() error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
m.pushNotificationClient.EnableSending()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Messenger) addedContactsAndMutedChatIDs() ([]*ecdsa.PublicKey, []string) {
|
||||
var contactIDs []*ecdsa.PublicKey
|
||||
var mutedChatIDs []string
|
||||
|
||||
for _, contact := range m.allContacts {
|
||||
if contact.IsAdded() {
|
||||
pk, err := contact.PublicKey()
|
||||
if err != nil {
|
||||
m.logger.Warn("could not parse contact public key")
|
||||
continue
|
||||
}
|
||||
contactIDs = append(contactIDs, pk)
|
||||
} else if contact.IsBlocked() {
|
||||
mutedChatIDs = append(mutedChatIDs, contact.ID)
|
||||
}
|
||||
}
|
||||
for _, chat := range m.allChats {
|
||||
if chat.Muted {
|
||||
mutedChatIDs = append(mutedChatIDs, chat.ID)
|
||||
}
|
||||
|
||||
}
|
||||
return contactIDs, mutedChatIDs
|
||||
}
|
||||
|
||||
// RegisterForPushNotification register deviceToken with any push notification server enabled
|
||||
func (m *Messenger) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("push notification client not enabled")
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
return m.pushNotificationClient.Register(deviceToken, contactIDs, mutedChatIDs)
|
||||
}
|
||||
|
||||
// RegisteredForPushNotifications returns whether we successfully registered with all the servers
|
||||
func (m *Messenger) RegisteredForPushNotifications() (bool, error) {
|
||||
if m.pushNotificationClient == nil {
|
||||
return false, errors.New("no push notification client")
|
||||
}
|
||||
return m.pushNotificationClient.Registered()
|
||||
}
|
||||
|
||||
// EnablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications only from contacts
|
||||
func (m *Messenger) EnablePushNotificationsFromContactsOnly() error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("no push notification client")
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
return m.pushNotificationClient.EnablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs)
|
||||
}
|
||||
|
||||
// DisablePushNotificationsFromContactsOnly is used to indicate that we want to received push notifications from anyone
|
||||
func (m *Messenger) DisablePushNotificationsFromContactsOnly() error {
|
||||
if m.pushNotificationClient == nil {
|
||||
return errors.New("no push notification client")
|
||||
}
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
contactIDs, mutedChatIDs := m.addedContactsAndMutedChatIDs()
|
||||
return m.pushNotificationClient.DisablePushNotificationsFromContactsOnly(contactIDs, mutedChatIDs)
|
||||
}
|
||||
|
||||
// GetPushNotificationServers returns the servers used for push notifications
|
||||
func (m *Messenger) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
||||
if m.pushNotificationClient == nil {
|
||||
return nil, errors.New("no push notification client")
|
||||
}
|
||||
return m.pushNotificationClient.GetServers()
|
||||
}
|
||||
|
||||
// StartPushNotificationsServer initialize and start a push notification server, using the current messenger identity key
|
||||
func (m *Messenger) StartPushNotificationsServer() error {
|
||||
if m.pushNotificationServer == nil {
|
||||
pushNotificationServerPersistence := pushnotificationserver.NewSQLitePersistence(m.database)
|
||||
config := &pushnotificationserver.Config{
|
||||
Logger: m.logger,
|
||||
Identity: m.identity,
|
||||
}
|
||||
m.pushNotificationServer = pushnotificationserver.New(config, pushNotificationServerPersistence, m.processor)
|
||||
}
|
||||
|
||||
return m.pushNotificationServer.Start()
|
||||
}
|
||||
|
||||
// StopPushNotificationServer stops the push notification server if running
|
||||
func (m *Messenger) StopPushNotificationsServer() error {
|
||||
m.pushNotificationServer = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func generateAliasAndIdenticon(pk string) (string, string, error) {
|
||||
identicon, err := identicon.GenerateBase64(pk)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/encryption"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
)
|
||||
|
||||
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
|
||||
// won't be pulling messages from mailservers until it reloads the chats/filters
|
||||
onNegotiatedFilters func([]*transport.Filter)
|
||||
// DEPRECATED: no need to expose it
|
||||
onSendContactCodeHandler func(*encryption.ProtocolMessageSpec)
|
||||
|
||||
// systemMessagesTranslations holds translations for system-messages
|
||||
systemMessagesTranslations map[protobuf.MembershipUpdateEvent_EventType]string
|
||||
// Config for the envelopes monitor
|
||||
envelopesMonitorConfig *transport.EnvelopesMonitorConfig
|
||||
|
||||
messagesPersistenceEnabled bool
|
||||
featureFlags common.FeatureFlags
|
||||
|
||||
// A path to a database or a database instance is required.
|
||||
// The database instance has a higher priority.
|
||||
dbConfig dbConfig
|
||||
db *sql.DB
|
||||
|
||||
verifyTransactionClient EthClient
|
||||
|
||||
pushNotificationServerConfig *pushnotificationserver.Config
|
||||
pushNotificationClientConfig *pushnotificationclient.Config
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type Option func(*config) error
|
||||
|
||||
// WithSystemMessagesTranslations is required for Group Chats which are currently disabled.
|
||||
// nolint: unused
|
||||
func WithSystemMessagesTranslations(t map[protobuf.MembershipUpdateEvent_EventType]string) Option {
|
||||
return func(c *config) error {
|
||||
c.systemMessagesTranslations = t
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithOnNegotiatedFilters(h func([]*transport.Filter)) Option {
|
||||
return func(c *config) error {
|
||||
c.onNegotiatedFilters = h
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithCustomLogger(logger *zap.Logger) Option {
|
||||
return func(c *config) error {
|
||||
c.logger = logger
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithMessagesPersistenceEnabled() Option {
|
||||
return func(c *config) error {
|
||||
c.messagesPersistenceEnabled = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatabaseConfig(dbPath, dbKey string) Option {
|
||||
return func(c *config) error {
|
||||
c.dbConfig = dbConfig{dbPath: dbPath, dbKey: dbKey}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithVerifyTransactionClient(client EthClient) Option {
|
||||
return func(c *config) error {
|
||||
c.verifyTransactionClient = client
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatabase(db *sql.DB) Option {
|
||||
return func(c *config) error {
|
||||
c.db = db
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithPushNotificationServerConfig(pushNotificationServerConfig *pushnotificationserver.Config) Option {
|
||||
return func(c *config) error {
|
||||
c.pushNotificationServerConfig = pushNotificationServerConfig
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithPushNotificationClientConfig(pushNotificationClientConfig *pushnotificationclient.Config) Option {
|
||||
return func(c *config) error {
|
||||
c.pushNotificationClientConfig = pushNotificationClientConfig
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDatasync() func(c *config) error {
|
||||
return func(c *config) error {
|
||||
c.featureFlags.Datasync = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithEnvelopesMonitorConfig(emc *transport.EnvelopesMonitorConfig) Option {
|
||||
return func(c *config) error {
|
||||
c.envelopesMonitorConfig = emc
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ 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/tt"
|
||||
"github.com/status-im/status-go/whisper/v6"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
func TestMessengerContactUpdateSuite(t *testing.T) {
|
||||
|
@ -27,8 +27,8 @@ type MessengerContactUpdateSuite struct {
|
|||
m *Messenger // main instance of Messenger
|
||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Whisper service should be shared.
|
||||
shh types.Whisper
|
||||
// a single waku service should be shared.
|
||||
shh types.Waku
|
||||
tmpFiles []*os.File // files to clean up
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
@ -36,17 +36,17 @@ type MessengerContactUpdateSuite struct {
|
|||
func (s *MessengerContactUpdateSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := whisper.DefaultConfig
|
||||
config.MinimumAcceptedPOW = 0
|
||||
shh := whisper.New(&config)
|
||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
}
|
||||
|
||||
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -72,7 +72,7 @@ func (s *MessengerContactUpdateSuite) newMessengerWithKey(shh types.Whisper, pri
|
|||
return m
|
||||
}
|
||||
|
||||
func (s *MessengerContactUpdateSuite) newMessenger(shh types.Whisper) *Messenger {
|
||||
func (s *MessengerContactUpdateSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
|
|
@ -17,9 +17,11 @@ import (
|
|||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
"github.com/status-im/status-go/whisper/v6"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
const statusChatID = "status"
|
||||
|
||||
func TestMessengerInstallationSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerInstallationSuite))
|
||||
}
|
||||
|
@ -30,8 +32,8 @@ type MessengerInstallationSuite struct {
|
|||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Whisper service should be shared.
|
||||
shh types.Whisper
|
||||
// a single Waku service should be shared.
|
||||
shh types.Waku
|
||||
|
||||
tmpFiles []*os.File // files to clean up
|
||||
logger *zap.Logger
|
||||
|
@ -40,17 +42,17 @@ type MessengerInstallationSuite struct {
|
|||
func (s *MessengerInstallationSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := whisper.DefaultConfig
|
||||
config.MinimumAcceptedPOW = 0
|
||||
shh := whisper.New(&config)
|
||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -77,7 +79,7 @@ func (s *MessengerInstallationSuite) newMessengerWithKey(shh types.Whisper, priv
|
|||
return m
|
||||
}
|
||||
|
||||
func (s *MessengerInstallationSuite) newMessenger(shh types.Whisper) *Messenger {
|
||||
func (s *MessengerInstallationSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -136,7 +138,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
|||
s.Require().Equal(contact.ID, actualContact.ID)
|
||||
s.Require().True(actualContact.IsAdded())
|
||||
|
||||
chat := CreatePublicChat("status", s.m.transport)
|
||||
chat := CreatePublicChat(statusChatID, s.m.transport)
|
||||
err = s.m.SaveChat(&chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -149,7 +151,7 @@ func (s *MessengerInstallationSuite) TestReceiveInstallation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
actualChat := response.Chats[0]
|
||||
s.Require().Equal("status", actualChat.ID)
|
||||
s.Require().Equal(statusChatID, actualChat.ID)
|
||||
s.Require().True(actualChat.Active)
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// add chat
|
||||
chat := CreatePublicChat("status", s.m.transport)
|
||||
chat := CreatePublicChat(statusChatID, s.m.transport)
|
||||
err = s.m.SaveChat(&chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -230,7 +232,7 @@ func (s *MessengerInstallationSuite) TestSyncInstallation() {
|
|||
|
||||
var statusChat *Chat
|
||||
for _, c := range allChats {
|
||||
if c.ID == "status" {
|
||||
if c.ID == statusChatID {
|
||||
statusChat = c
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/zap"
|
||||
|
||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
func TestMessengerMuteSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerMuteSuite))
|
||||
}
|
||||
|
||||
type MessengerMuteSuite struct {
|
||||
suite.Suite
|
||||
m *Messenger // main instance of Messenger
|
||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Waku service should be shared.
|
||||
shh types.Waku
|
||||
|
||||
tmpFiles []*os.File // files to clean up
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
options := []Option{
|
||||
WithCustomLogger(s.logger),
|
||||
WithMessagesPersistenceEnabled(),
|
||||
WithDatabaseConfig(tmpFile.Name(), "some-key"),
|
||||
WithDatasync(),
|
||||
}
|
||||
installationID := uuid.New().String()
|
||||
m, err := NewMessenger(
|
||||
privateKey,
|
||||
&testNode{shh: shh},
|
||||
installationID,
|
||||
options...,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = m.Init()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.tmpFiles = append(s.tmpFiles, tmpFile)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return s.newMessengerWithKey(s.shh, privateKey)
|
||||
}
|
||||
|
||||
func (s *MessengerMuteSuite) TestSetMute() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
theirMessenger := s.newMessengerWithKey(s.shh, key)
|
||||
|
||||
chatID := "status"
|
||||
|
||||
chat := CreatePublicChat(chatID, s.m.transport)
|
||||
|
||||
err = s.m.SaveChat(&chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.m.Join(chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = theirMessenger.SaveChat(&chat)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NoError(s.m.MuteChat(chatID))
|
||||
|
||||
s.Require().Len(s.m.Chats(), 1)
|
||||
s.Require().True(s.m.Chats()[0].Muted)
|
||||
|
||||
s.Require().NoError(s.m.UnmuteChat(chatID))
|
||||
s.Require().False(s.m.Chats()[0].Muted)
|
||||
}
|
|
@ -27,7 +27,7 @@ import (
|
|||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
v1protocol "github.com/status-im/status-go/protocol/v1"
|
||||
"github.com/status-im/status-go/whisper/v6"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -62,13 +62,13 @@ type MessengerSuite struct {
|
|||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Whisper service should be shared.
|
||||
shh types.Whisper
|
||||
shh types.Waku
|
||||
tmpFiles []*os.File // files to clean up
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type testNode struct {
|
||||
shh types.Whisper
|
||||
shh types.Waku
|
||||
}
|
||||
|
||||
func (n *testNode) NewENSVerifier(_ *zap.Logger) enstypes.ENSVerifier {
|
||||
|
@ -84,27 +84,27 @@ func (n *testNode) RemovePeer(_ string) error {
|
|||
}
|
||||
|
||||
func (n *testNode) GetWaku(_ interface{}) (types.Waku, error) {
|
||||
panic("not implemented")
|
||||
return n.shh, nil
|
||||
}
|
||||
|
||||
func (n *testNode) GetWhisper(_ interface{}) (types.Whisper, error) {
|
||||
return n.shh, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := whisper.DefaultConfig
|
||||
config.MinimumAcceptedPOW = 0
|
||||
shh := whisper.New(&config)
|
||||
s.shh = gethbridge.NewGethWhisperWrapper(shh)
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
func (s *MessengerSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
@ -132,7 +132,7 @@ func (s *MessengerSuite) newMessengerWithKey(shh types.Whisper, privateKey *ecds
|
|||
return m
|
||||
}
|
||||
|
||||
func (s *MessengerSuite) newMessenger(shh types.Whisper) *Messenger {
|
||||
func (s *MessengerSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
return s.newMessengerWithKey(shh, privateKey)
|
||||
|
@ -2056,7 +2056,7 @@ type MockEthClient struct {
|
|||
}
|
||||
|
||||
type mockSendMessagesRequest struct {
|
||||
types.Whisper
|
||||
types.Waku
|
||||
req types.MessagesRequest
|
||||
}
|
||||
|
||||
|
@ -2101,12 +2101,13 @@ func (s *MessengerSuite) TestMessageJSON() {
|
|||
|
||||
func (s *MessengerSuite) TestRequestHistoricMessagesRequest() {
|
||||
shh := &mockSendMessagesRequest{
|
||||
Whisper: s.shh,
|
||||
Waku: s.shh,
|
||||
}
|
||||
m := s.newMessenger(shh)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
cursor, err := m.RequestHistoricMessages(ctx, nil, 10, 20, []byte{0x01})
|
||||
m.mailserver = []byte("mailserver-id")
|
||||
cursor, err := m.RequestHistoricMessages(ctx, 10, 20, []byte{0x01})
|
||||
s.EqualError(err, ctx.Err().Error())
|
||||
s.Empty(cursor)
|
||||
// verify request is correct
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
// 1589365189_add_pow_target.up.sql (66B)
|
||||
// 1591277220_add_index_messages.down.sql (237B)
|
||||
// 1591277220_add_index_messages.up.sql (240B)
|
||||
// 1593087212_add_mute_chat_and_raw_message_fields.down.sql (0)
|
||||
// 1593087212_add_mute_chat_and_raw_message_fields.up.sql (215B)
|
||||
// doc.go (850B)
|
||||
|
||||
package migrations
|
||||
|
@ -136,7 +138,7 @@ func _000002_add_last_ens_clock_valueDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -156,7 +158,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(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "000002_add_last_ens_clock_value.up.sql", size: 77, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
@ -176,7 +178,7 @@ func _1586358095_add_replaceDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)}
|
||||
info := bindataFileInfo{name: "1586358095_add_replace.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -196,7 +198,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(1589265610, 0)}
|
||||
info := bindataFileInfo{name: "1586358095_add_replace.up.sql", size: 224, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
@ -216,7 +218,7 @@ func _1588665364_add_image_dataDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
||||
info := bindataFileInfo{name: "1588665364_add_image_data.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -236,7 +238,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(1591361643, 0)}
|
||||
info := bindataFileInfo{name: "1588665364_add_image_data.up.sql", size: 186, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
@ -256,7 +258,7 @@ func _1589365189_add_pow_targetDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
||||
info := bindataFileInfo{name: "1589365189_add_pow_target.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -276,7 +278,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(1591361643, 0)}
|
||||
info := bindataFileInfo{name: "1589365189_add_pow_target.up.sql", size: 66, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
@ -296,7 +298,7 @@ func _1591277220_add_index_messagesDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1591361643, 0)}
|
||||
info := bindataFileInfo{name: "1591277220_add_index_messages.down.sql", size: 237, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x79, 0xe5, 0x42, 0x56, 0x64, 0x1d, 0xb7, 0x8a, 0x1b, 0x0, 0x99, 0xf0, 0x18, 0x8c, 0x69, 0xe3, 0x14, 0x3a, 0x7f, 0x78, 0xfe, 0xe3, 0x2e, 0xcb, 0x6e, 0x5c, 0x8c, 0x1f, 0x7b, 0xfc, 0x21, 0xc7}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -316,11 +318,51 @@ 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(1591361844, 0)}
|
||||
info := bindataFileInfo{name: "1591277220_add_index_messages.up.sql", size: 240, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
||||
var __1593087212_add_mute_chat_and_raw_message_fieldsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
|
||||
|
||||
func _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593087212_add_mute_chat_and_raw_message_fieldsDownSql,
|
||||
"1593087212_add_mute_chat_and_raw_message_fields.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593087212_add_mute_chat_and_raw_message_fieldsDownSql() (*asset, error) {
|
||||
bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1593087212_add_mute_chat_and_raw_message_fields.down.sql", size: 0, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1593087212_add_mute_chat_and_raw_message_fieldsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xa4\xcc\x31\x0e\xc2\x30\x0c\x00\xc0\x9d\x57\xf8\x1f\x4c\x2e\x71\x27\xd3\x48\x90\xce\x51\x94\x1a\x1a\xa1\xa6\x51\xed\x0a\xf1\x7b\x56\x58\x58\x78\xc0\x1d\x72\xa0\x0b\x04\xec\x98\x20\xcf\xc9\x14\xd0\x39\x38\x79\x1e\xcf\x03\x2c\xbb\xc9\x04\x9d\xf7\x4c\x38\x80\xa3\x1e\x47\x0e\xd0\x23\x5f\xe9\x78\xf8\x94\x5b\x7a\xc6\x45\x54\xd3\x5d\xbe\x02\x7d\x94\x16\xa5\xe6\xed\xd5\xac\xac\xf5\xaf\x4a\xea\x14\xdb\xae\x73\xac\xab\x95\x5b\xc9\xe9\xd7\xf8\x0e\x00\x00\xff\xff\xd9\x47\x38\x58\xd7\x00\x00\x00")
|
||||
|
||||
func _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
|
||||
"1593087212_add_mute_chat_and_raw_message_fields.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593087212_add_mute_chat_and_raw_message_fieldsUpSql() (*asset, error) {
|
||||
bytes, err := _1593087212_add_mute_chat_and_raw_message_fieldsUpSqlBytes()
|
||||
if err != nil {
|
||||
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(1595832279, 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
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x52\x3f\x8f\xdb\x3e\x0c\xdd\xf3\x29\x1e\x6e\xb9\xe5\x22\x07\xf8\xfd\xa6\xdb\x3a\x74\xe8\xd2\x2e\xd9\x0b\x46\xa6\x6d\x22\x32\xe5\x8a\xf4\x39\xf9\xf6\x85\x74\x17\x9c\x51\x14\xe8\x4a\x89\x8f\xef\x5f\xd7\xe1\x3c\x89\x61\x90\xc4\x10\x83\x72\x64\x33\x2a\x77\x5c\x38\xd2\x6a\x8c\xa7\x51\x7c\x5a\x2f\x21\xe6\xb9\x33\x27\x5f\xed\x28\x73\x37\xcb\x58\xc8\xb9\x7b\xfb\xff\xe9\xd0\x75\x88\xa4\xcf\x8e\x89\xb4\x4f\xdc\xb0\x0c\xe6\x54\x5c\x74\xc4\x26\x3e\x81\xb0\x14\x1e\xe4\x16\xf0\xc5\x91\x98\xcc\xe1\x13\xf9\xb3\xc1\x27\x46\x24\xe3\x0a\x33\xe4\x82\x31\x1f\x2f\xa2\x3d\x39\x85\x3a\xfa\x36\xec\x26\x95\x61\xa4\x94\xb8\xc7\x50\xf2\xdc\x76\x8d\x66\x46\x2f\x85\xa3\xe7\x72\x7f\x01\x99\xb1\x43\x69\x66\xab\xfb\x13\xbd\x31\x34\x7f\x9c\x07\x69\xff\x6f\x45\xd8\x72\xb9\x1a\xc8\xc0\xb7\x85\xa3\x73\x1f\x0e\x15\xeb\xfb\x8f\xf3\xd7\x57\x9c\x27\xae\xf0\x55\x5a\x1e\x1a\x85\x66\x9e\x32\xf7\x06\xcf\x18\x72\x4a\x79\x6b\x0f\xab\xca\x0d\x2e\x33\x9b\xd3\xbc\x20\x66\x7d\x63\x75\xc9\x5a\xd1\x56\x4d\x72\xe5\xf6\xcf\xb7\x0c\x51\x71\xa1\xf4\xee\x5e\x93\x7e\x7e\x37\xe8\x11\x44\x5c\x4b\x61\xf5\x74\x6f\x2b\xac\xb1\xdc\x97\x8a\x85\x77\xe6\x92\xd5\x9a\xbc\xa5\x64\xcf\x31\xa7\xdd\xbc\xa2\xd9\x44\x85\x3f\x1d\x73\xba\x24\x7e\xc1\x36\x49\x9c\x30\x33\xa9\xb5\x40\xda\x87\x44\xce\xe6\x9f\xfb\x10\x85\x73\x99\xad\x0a\xae\xfc\xaa\xbb\x15\xb3\x16\xe7\x91\xc3\x8e\x50\x33\x7f\xa1\xf8\x51\x85\xc7\x95\xd5\xd8\x40\x7f\x98\xf2\x08\x79\x63\x50\xdf\xe3\x74\x3a\x9d\xfe\xfb\x19\x42\x68\x5d\xe0\x1b\xcd\x4b\xa5\xe9\xb5\xa3\x9b\xa4\x84\x0b\x43\x46\xcd\x85\xfb\xca\x8a\x6f\x62\xad\x64\x31\x09\xab\xd7\xcc\x2a\x5e\x4e\x3d\x97\xaa\x47\xf7\x7a\xfe\x66\x59\x38\x1c\x16\x8a\x57\x1a\x19\xf6\x2b\x89\x73\x0d\x7a\xcc\xaf\x23\x2b\xd7\x3a\xec\xcb\x77\x5c\xae\xe3\xde\xec\x63\x46\x08\xdd\xe7\x20\x8c\x19\xe1\xf0\x3b\x00\x00\xff\xff\x12\xcd\x7f\xc4\x52\x03\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
|
@ -336,7 +378,7 @@ func docGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1589265610, 0)}
|
||||
info := bindataFileInfo{name: "doc.go", size: 850, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 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
|
||||
}
|
||||
|
@ -456,6 +498,10 @@ var _bindata = map[string]func() (*asset, error){
|
|||
|
||||
"1591277220_add_index_messages.up.sql": _1591277220_add_index_messagesUpSql,
|
||||
|
||||
"1593087212_add_mute_chat_and_raw_message_fields.down.sql": _1593087212_add_mute_chat_and_raw_message_fieldsDownSql,
|
||||
|
||||
"1593087212_add_mute_chat_and_raw_message_fields.up.sql": _1593087212_add_mute_chat_and_raw_message_fieldsUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
|
@ -500,19 +546,21 @@ 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.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}},
|
||||
"000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}},
|
||||
"1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}},
|
||||
"1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}},
|
||||
"1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}},
|
||||
"1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}},
|
||||
"1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}},
|
||||
"1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}},
|
||||
"1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, map[string]*bintree{}},
|
||||
"1591277220_add_index_messages.up.sql": &bintree{_1591277220_add_index_messagesUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, 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.down.sql": &bintree{_000002_add_last_ens_clock_valueDownSql, map[string]*bintree{}},
|
||||
"000002_add_last_ens_clock_value.up.sql": &bintree{_000002_add_last_ens_clock_valueUpSql, map[string]*bintree{}},
|
||||
"1586358095_add_replace.down.sql": &bintree{_1586358095_add_replaceDownSql, map[string]*bintree{}},
|
||||
"1586358095_add_replace.up.sql": &bintree{_1586358095_add_replaceUpSql, map[string]*bintree{}},
|
||||
"1588665364_add_image_data.down.sql": &bintree{_1588665364_add_image_dataDownSql, map[string]*bintree{}},
|
||||
"1588665364_add_image_data.up.sql": &bintree{_1588665364_add_image_dataUpSql, map[string]*bintree{}},
|
||||
"1589365189_add_pow_target.down.sql": &bintree{_1589365189_add_pow_targetDownSql, map[string]*bintree{}},
|
||||
"1589365189_add_pow_target.up.sql": &bintree{_1589365189_add_pow_targetUpSql, map[string]*bintree{}},
|
||||
"1591277220_add_index_messages.down.sql": &bintree{_1591277220_add_index_messagesDownSql, 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.down.sql": &bintree{_1593087212_add_mute_chat_and_raw_message_fieldsDownSql, 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{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE chats ADD COLUMN muted BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE raw_messages ADD COLUMN skip_encryption BOOLEAN DEFAULT FALSE;
|
||||
ALTER TABLE raw_messages ADD COLUMN send_push_notification BOOLEAN DEFAULT FALSE;
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -103,8 +104,8 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error {
|
|||
}
|
||||
|
||||
// Insert record
|
||||
stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||
stmt, err := tx.Prepare(`INSERT INTO chats(id, name, color, active, type, timestamp, deleted_at_clock_value, unviewed_message_count, last_clock_value, last_message, members, membership_updates, muted)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -123,6 +124,7 @@ func (db sqlitePersistence) saveChat(tx *sql.Tx, chat Chat) error {
|
|||
chat.LastMessage,
|
||||
encodedMembers.Bytes(),
|
||||
encodedMembershipUpdates.Bytes(),
|
||||
chat.Muted,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -136,6 +138,16 @@ func (db sqlitePersistence) DeleteChat(chatID string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) MuteChat(chatID string) error {
|
||||
_, err := db.db.Exec("UPDATE chats SET muted = 1 WHERE id = ?", chatID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) UnmuteChat(chatID string) error {
|
||||
_, err := db.db.Exec("UPDATE chats SET muted = 0 WHERE id = ?", chatID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) Chats() ([]*Chat, error) {
|
||||
return db.chats(nil)
|
||||
}
|
||||
|
@ -170,6 +182,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
|
|||
chats.last_message,
|
||||
chats.members,
|
||||
chats.membership_updates,
|
||||
chats.muted,
|
||||
contacts.identicon,
|
||||
contacts.alias
|
||||
FROM chats LEFT JOIN contacts ON chats.id = contacts.id
|
||||
|
@ -201,6 +214,7 @@ func (db sqlitePersistence) chats(tx *sql.Tx) (chats []*Chat, err error) {
|
|||
&chat.LastMessage,
|
||||
&encodedMembers,
|
||||
&encodedMembershipUpdates,
|
||||
&chat.Muted,
|
||||
&identicon,
|
||||
&alias,
|
||||
)
|
||||
|
@ -251,7 +265,8 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
|
|||
last_clock_value,
|
||||
last_message,
|
||||
members,
|
||||
membership_updates
|
||||
membership_updates,
|
||||
muted
|
||||
FROM chats
|
||||
WHERE id = ?
|
||||
`, chatID).Scan(&chat.ID,
|
||||
|
@ -266,6 +281,7 @@ func (db sqlitePersistence) Chat(chatID string) (*Chat, error) {
|
|||
&chat.LastMessage,
|
||||
&encodedMembers,
|
||||
&encodedMembershipUpdates,
|
||||
&chat.Muted,
|
||||
)
|
||||
switch err {
|
||||
case sql.ErrNoRows:
|
||||
|
@ -362,7 +378,7 @@ func (db sqlitePersistence) Contacts() ([]*Contact, error) {
|
|||
return response, nil
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
||||
func (db sqlitePersistence) SaveRawMessage(message *common.RawMessage) error {
|
||||
var pubKeys [][]byte
|
||||
for _, pk := range message.Recipients {
|
||||
pubKeys = append(pubKeys, crypto.CompressPubkey(pk))
|
||||
|
@ -387,9 +403,11 @@ func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
|||
message_type,
|
||||
resend_automatically,
|
||||
recipients,
|
||||
skip_encryption,
|
||||
send_push_notification,
|
||||
payload
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
message.ID,
|
||||
message.LocalChatID,
|
||||
message.LastSent,
|
||||
|
@ -398,14 +416,16 @@ func (db sqlitePersistence) SaveRawMessage(message *RawMessage) error {
|
|||
message.MessageType,
|
||||
message.ResendAutomatically,
|
||||
encodedRecipients.Bytes(),
|
||||
message.SkipEncryption,
|
||||
message.SendPushNotification,
|
||||
message.Payload)
|
||||
return err
|
||||
}
|
||||
|
||||
func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
||||
func (db sqlitePersistence) RawMessageByID(id string) (*common.RawMessage, error) {
|
||||
var rawPubKeys [][]byte
|
||||
var encodedRecipients []byte
|
||||
message := &RawMessage{}
|
||||
message := &common.RawMessage{}
|
||||
|
||||
err := db.db.QueryRow(`
|
||||
SELECT
|
||||
|
@ -417,6 +437,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
|||
message_type,
|
||||
resend_automatically,
|
||||
recipients,
|
||||
skip_encryption,
|
||||
send_push_notification,
|
||||
payload
|
||||
FROM
|
||||
raw_messages
|
||||
|
@ -432,6 +454,8 @@ func (db sqlitePersistence) RawMessageByID(id string) (*RawMessage, error) {
|
|||
&message.MessageType,
|
||||
&message.ResendAutomatically,
|
||||
&encodedRecipients,
|
||||
&message.SkipEncryption,
|
||||
&message.SendPushNotification,
|
||||
&message.Payload,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -38,6 +38,13 @@ const (
|
|||
ApplicationMetadataMessage_SYNC_INSTALLATION_CONTACT ApplicationMetadataMessage_Type = 12
|
||||
ApplicationMetadataMessage_SYNC_INSTALLATION_ACCOUNT ApplicationMetadataMessage_Type = 13
|
||||
ApplicationMetadataMessage_SYNC_INSTALLATION_PUBLIC_CHAT ApplicationMetadataMessage_Type = 14
|
||||
ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT ApplicationMetadataMessage_Type = 15
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION ApplicationMetadataMessage_Type = 16
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE ApplicationMetadataMessage_Type = 17
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY ApplicationMetadataMessage_Type = 18
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE ApplicationMetadataMessage_Type = 19
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST ApplicationMetadataMessage_Type = 20
|
||||
ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE ApplicationMetadataMessage_Type = 21
|
||||
)
|
||||
|
||||
var ApplicationMetadataMessage_Type_name = map[int32]string{
|
||||
|
@ -56,6 +63,13 @@ var ApplicationMetadataMessage_Type_name = map[int32]string{
|
|||
12: "SYNC_INSTALLATION_CONTACT",
|
||||
13: "SYNC_INSTALLATION_ACCOUNT",
|
||||
14: "SYNC_INSTALLATION_PUBLIC_CHAT",
|
||||
15: "CONTACT_CODE_ADVERTISEMENT",
|
||||
16: "PUSH_NOTIFICATION_REGISTRATION",
|
||||
17: "PUSH_NOTIFICATION_REGISTRATION_RESPONSE",
|
||||
18: "PUSH_NOTIFICATION_QUERY",
|
||||
19: "PUSH_NOTIFICATION_QUERY_RESPONSE",
|
||||
20: "PUSH_NOTIFICATION_REQUEST",
|
||||
21: "PUSH_NOTIFICATION_RESPONSE",
|
||||
}
|
||||
|
||||
var ApplicationMetadataMessage_Type_value = map[string]int32{
|
||||
|
@ -74,6 +88,13 @@ var ApplicationMetadataMessage_Type_value = map[string]int32{
|
|||
"SYNC_INSTALLATION_CONTACT": 12,
|
||||
"SYNC_INSTALLATION_ACCOUNT": 13,
|
||||
"SYNC_INSTALLATION_PUBLIC_CHAT": 14,
|
||||
"CONTACT_CODE_ADVERTISEMENT": 15,
|
||||
"PUSH_NOTIFICATION_REGISTRATION": 16,
|
||||
"PUSH_NOTIFICATION_REGISTRATION_RESPONSE": 17,
|
||||
"PUSH_NOTIFICATION_QUERY": 18,
|
||||
"PUSH_NOTIFICATION_QUERY_RESPONSE": 19,
|
||||
"PUSH_NOTIFICATION_REQUEST": 20,
|
||||
"PUSH_NOTIFICATION_RESPONSE": 21,
|
||||
}
|
||||
|
||||
func (x ApplicationMetadataMessage_Type) String() string {
|
||||
|
@ -150,29 +171,34 @@ func init() {
|
|||
func init() { proto.RegisterFile("application_metadata_message.proto", fileDescriptor_ad09a6406fcf24c7) }
|
||||
|
||||
var fileDescriptor_ad09a6406fcf24c7 = []byte{
|
||||
// 377 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xcf, 0x8e, 0xd3, 0x30,
|
||||
0x10, 0xc6, 0xc9, 0x36, 0x6c, 0x77, 0x67, 0x4b, 0x65, 0x06, 0x10, 0xe1, 0xcf, 0x6a, 0x97, 0x22,
|
||||
0xc1, 0x02, 0x52, 0x0e, 0x70, 0xe6, 0xe0, 0x75, 0x0c, 0x1b, 0x91, 0x38, 0xc1, 0x76, 0x84, 0x38,
|
||||
0x59, 0x2e, 0x0d, 0x55, 0xa5, 0xb6, 0x89, 0xda, 0xf4, 0xd0, 0x07, 0xe3, 0x29, 0x78, 0x29, 0x94,
|
||||
0xd0, 0xd2, 0x96, 0x82, 0x7a, 0xb2, 0xe6, 0xfb, 0x7e, 0x9f, 0x47, 0x33, 0x03, 0x3d, 0x5b, 0x96,
|
||||
0xe3, 0xd1, 0x37, 0x5b, 0x8d, 0x8a, 0xa9, 0x99, 0xe4, 0x95, 0x1d, 0xd8, 0xca, 0x9a, 0x49, 0x3e,
|
||||
0x9f, 0xdb, 0x61, 0xee, 0x97, 0xb3, 0xa2, 0x2a, 0xf0, 0xa4, 0x79, 0xfa, 0x8b, 0xef, 0xbd, 0x9f,
|
||||
0x2e, 0x3c, 0xa6, 0x9b, 0x40, 0xbc, 0xe2, 0xe3, 0xdf, 0x38, 0x3e, 0x85, 0xd3, 0xf9, 0x68, 0x38,
|
||||
0xb5, 0xd5, 0x62, 0x96, 0x7b, 0xce, 0xa5, 0x73, 0xd5, 0x91, 0x1b, 0x01, 0x3d, 0x68, 0x97, 0x76,
|
||||
0x39, 0x2e, 0xec, 0xc0, 0x3b, 0x6a, 0xbc, 0x75, 0x89, 0xef, 0xc1, 0xad, 0x96, 0x65, 0xee, 0xb5,
|
||||
0x2e, 0x9d, 0xab, 0xee, 0xdb, 0x57, 0xfe, 0xba, 0x9f, 0xff, 0xff, 0x5e, 0xbe, 0x5e, 0x96, 0xb9,
|
||||
0x6c, 0x62, 0xbd, 0x1f, 0x2d, 0x70, 0xeb, 0x12, 0xcf, 0xa0, 0x9d, 0x89, 0x4f, 0x22, 0xf9, 0x22,
|
||||
0xc8, 0x2d, 0x24, 0xd0, 0x61, 0x37, 0x54, 0x9b, 0x98, 0x2b, 0x45, 0x3f, 0x72, 0xe2, 0x20, 0x42,
|
||||
0x97, 0x25, 0x42, 0x53, 0xa6, 0x4d, 0x96, 0x06, 0x54, 0x73, 0x72, 0x84, 0xe7, 0xf0, 0x28, 0xe6,
|
||||
0xf1, 0x35, 0x97, 0xea, 0x26, 0x4c, 0x57, 0xf2, 0x9f, 0x48, 0x0b, 0x1f, 0xc0, 0xdd, 0x94, 0x86,
|
||||
0xd2, 0x84, 0x42, 0x69, 0x1a, 0x45, 0x54, 0x87, 0x89, 0x20, 0x6e, 0x2d, 0xab, 0xaf, 0x82, 0xed,
|
||||
0xca, 0xb7, 0xf1, 0x39, 0x5c, 0x48, 0xfe, 0x39, 0xe3, 0x4a, 0x1b, 0x1a, 0x04, 0x92, 0x2b, 0x65,
|
||||
0x3e, 0x24, 0xd2, 0x68, 0x49, 0x85, 0xa2, 0xac, 0x81, 0x8e, 0xf1, 0x35, 0xbc, 0xa0, 0x8c, 0xf1,
|
||||
0x54, 0x9b, 0x43, 0x6c, 0x1b, 0xdf, 0xc0, 0xcb, 0x80, 0xb3, 0x28, 0x14, 0xfc, 0x20, 0x7c, 0x82,
|
||||
0x0f, 0xe1, 0xde, 0x1a, 0xda, 0x36, 0x4e, 0xf1, 0x3e, 0x10, 0xc5, 0x45, 0xb0, 0xa3, 0x02, 0x5e,
|
||||
0xc0, 0x93, 0xbf, 0xff, 0xde, 0x06, 0xce, 0xea, 0xd5, 0xec, 0x0d, 0x69, 0x56, 0x0b, 0x24, 0x9d,
|
||||
0x7f, 0xdb, 0x94, 0xb1, 0x24, 0x13, 0x9a, 0xdc, 0xc1, 0x67, 0x70, 0xbe, 0x6f, 0xa7, 0xd9, 0x75,
|
||||
0x14, 0x32, 0x53, 0xdf, 0x85, 0x74, 0xfb, 0xc7, 0xcd, 0x9d, 0xdf, 0xfd, 0x0a, 0x00, 0x00, 0xff,
|
||||
0xff, 0xb7, 0x6c, 0xd6, 0xba, 0x84, 0x02, 0x00, 0x00,
|
||||
// 463 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x51, 0x4f, 0x13, 0x41,
|
||||
0x10, 0xc7, 0x2d, 0x94, 0x16, 0x86, 0x5a, 0x97, 0x01, 0x42, 0x05, 0x81, 0x5a, 0x8d, 0xa2, 0x26,
|
||||
0x7d, 0xd0, 0x67, 0x1f, 0x96, 0xbd, 0x81, 0x6e, 0xec, 0xed, 0x1d, 0xbb, 0x7b, 0x1a, 0x9e, 0x36,
|
||||
0x87, 0x9c, 0xa4, 0x09, 0xd0, 0x0b, 0x3d, 0x1e, 0xfa, 0x8d, 0xfc, 0x14, 0x7e, 0x36, 0x73, 0xd7,
|
||||
0xd6, 0x82, 0x2d, 0xf0, 0x74, 0xd9, 0xff, 0xff, 0x37, 0x33, 0x99, 0xff, 0x1c, 0xb4, 0xe2, 0x34,
|
||||
0xbd, 0xec, 0xfd, 0x8c, 0xb3, 0x5e, 0xff, 0xda, 0x5d, 0x25, 0x59, 0x7c, 0x1e, 0x67, 0xb1, 0xbb,
|
||||
0x4a, 0x06, 0x83, 0xf8, 0x22, 0x69, 0xa7, 0x37, 0xfd, 0xac, 0x8f, 0xcb, 0xc5, 0xe7, 0xec, 0xf6,
|
||||
0x57, 0xeb, 0x4f, 0x05, 0xb6, 0xf9, 0xb4, 0xc0, 0x1f, 0xf3, 0xfe, 0x08, 0xc7, 0x57, 0xb0, 0x32,
|
||||
0xe8, 0x5d, 0x5c, 0xc7, 0xd9, 0xed, 0x4d, 0xd2, 0x28, 0x35, 0x4b, 0x07, 0x35, 0x3d, 0x15, 0xb0,
|
||||
0x01, 0xd5, 0x34, 0x1e, 0x5e, 0xf6, 0xe3, 0xf3, 0xc6, 0x42, 0xe1, 0x4d, 0x9e, 0xf8, 0x15, 0xca,
|
||||
0xd9, 0x30, 0x4d, 0x1a, 0x8b, 0xcd, 0xd2, 0x41, 0xfd, 0xf3, 0x87, 0xf6, 0x64, 0x5e, 0xfb, 0xe1,
|
||||
0x59, 0x6d, 0x3b, 0x4c, 0x13, 0x5d, 0x94, 0xb5, 0x7e, 0x2f, 0x41, 0x39, 0x7f, 0xe2, 0x2a, 0x54,
|
||||
0x23, 0xf5, 0x4d, 0x05, 0x3f, 0x14, 0x7b, 0x86, 0x0c, 0x6a, 0xa2, 0xc3, 0xad, 0xf3, 0xc9, 0x18,
|
||||
0x7e, 0x4c, 0xac, 0x84, 0x08, 0x75, 0x11, 0x28, 0xcb, 0x85, 0x75, 0x51, 0xe8, 0x71, 0x4b, 0x6c,
|
||||
0x01, 0x77, 0xe1, 0xa5, 0x4f, 0xfe, 0x21, 0x69, 0xd3, 0x91, 0xe1, 0x58, 0xfe, 0x57, 0xb2, 0x88,
|
||||
0x9b, 0xb0, 0x16, 0x72, 0xa9, 0x9d, 0x54, 0xc6, 0xf2, 0x6e, 0x97, 0x5b, 0x19, 0x28, 0x56, 0xce,
|
||||
0x65, 0x73, 0xaa, 0xc4, 0x7d, 0x79, 0x09, 0xdf, 0xc0, 0xbe, 0xa6, 0x93, 0x88, 0x8c, 0x75, 0xdc,
|
||||
0xf3, 0x34, 0x19, 0xe3, 0x8e, 0x02, 0xed, 0xac, 0xe6, 0xca, 0x70, 0x51, 0x40, 0x15, 0xfc, 0x08,
|
||||
0xef, 0xb8, 0x10, 0x14, 0x5a, 0xf7, 0x14, 0x5b, 0xc5, 0x4f, 0xf0, 0xde, 0x23, 0xd1, 0x95, 0x8a,
|
||||
0x9e, 0x84, 0x97, 0x71, 0x0b, 0xd6, 0x27, 0xd0, 0x5d, 0x63, 0x05, 0x37, 0x80, 0x19, 0x52, 0xde,
|
||||
0x3d, 0x15, 0x70, 0x1f, 0x76, 0xfe, 0xef, 0x7d, 0x17, 0x58, 0xcd, 0xa3, 0x99, 0x59, 0xd2, 0x8d,
|
||||
0x03, 0x64, 0xb5, 0xf9, 0x36, 0x17, 0x22, 0x88, 0x94, 0x65, 0xcf, 0xf1, 0x35, 0xec, 0xce, 0xda,
|
||||
0x61, 0x74, 0xd8, 0x95, 0xc2, 0xe5, 0x77, 0x61, 0x75, 0xdc, 0x83, 0xed, 0xc9, 0x3d, 0x44, 0xe0,
|
||||
0x91, 0xe3, 0xde, 0x77, 0xd2, 0x56, 0x1a, 0xf2, 0x49, 0x59, 0xf6, 0x02, 0x5b, 0xb0, 0x17, 0x46,
|
||||
0xa6, 0xe3, 0x54, 0x60, 0xe5, 0x91, 0x14, 0xa3, 0x16, 0x9a, 0x8e, 0xa5, 0xb1, 0x7a, 0x14, 0x39,
|
||||
0xcb, 0x13, 0x7a, 0x9c, 0x71, 0x9a, 0x4c, 0x18, 0x28, 0x43, 0x6c, 0x0d, 0x77, 0x60, 0x6b, 0x16,
|
||||
0x3e, 0x89, 0x48, 0x9f, 0x32, 0xc4, 0xb7, 0xd0, 0x7c, 0xc0, 0x9c, 0xb6, 0x58, 0xcf, 0xb7, 0x9e,
|
||||
0x37, 0xaf, 0xc8, 0x8f, 0x6d, 0xe4, 0x2b, 0xcd, 0xb3, 0xc7, 0xe5, 0x9b, 0x67, 0x95, 0xe2, 0xd7,
|
||||
0xfe, 0xf2, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe7, 0xcd, 0xf5, 0xf1, 0x77, 0x03, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -27,5 +27,12 @@ message ApplicationMetadataMessage {
|
|||
SYNC_INSTALLATION_CONTACT = 12;
|
||||
SYNC_INSTALLATION_ACCOUNT = 13;
|
||||
SYNC_INSTALLATION_PUBLIC_CHAT = 14;
|
||||
CONTACT_CODE_ADVERTISEMENT = 15;
|
||||
PUSH_NOTIFICATION_REGISTRATION = 16;
|
||||
PUSH_NOTIFICATION_REGISTRATION_RESPONSE = 17;
|
||||
PUSH_NOTIFICATION_QUERY = 18;
|
||||
PUSH_NOTIFICATION_QUERY_RESPONSE = 19;
|
||||
PUSH_NOTIFICATION_REQUEST = 20;
|
||||
PUSH_NOTIFICATION_RESPONSE = 21;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,813 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: push_notifications.proto
|
||||
|
||||
package protobuf
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type PushNotificationRegistration_TokenType int32
|
||||
|
||||
const (
|
||||
PushNotificationRegistration_UNKNOWN_TOKEN_TYPE PushNotificationRegistration_TokenType = 0
|
||||
PushNotificationRegistration_APN_TOKEN PushNotificationRegistration_TokenType = 1
|
||||
PushNotificationRegistration_FIREBASE_TOKEN PushNotificationRegistration_TokenType = 2
|
||||
)
|
||||
|
||||
var PushNotificationRegistration_TokenType_name = map[int32]string{
|
||||
0: "UNKNOWN_TOKEN_TYPE",
|
||||
1: "APN_TOKEN",
|
||||
2: "FIREBASE_TOKEN",
|
||||
}
|
||||
|
||||
var PushNotificationRegistration_TokenType_value = map[string]int32{
|
||||
"UNKNOWN_TOKEN_TYPE": 0,
|
||||
"APN_TOKEN": 1,
|
||||
"FIREBASE_TOKEN": 2,
|
||||
}
|
||||
|
||||
func (x PushNotificationRegistration_TokenType) String() string {
|
||||
return proto.EnumName(PushNotificationRegistration_TokenType_name, int32(x))
|
||||
}
|
||||
|
||||
func (PushNotificationRegistration_TokenType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{0, 0}
|
||||
}
|
||||
|
||||
type PushNotificationRegistrationResponse_ErrorType int32
|
||||
|
||||
const (
|
||||
PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE PushNotificationRegistrationResponse_ErrorType = 0
|
||||
PushNotificationRegistrationResponse_MALFORMED_MESSAGE PushNotificationRegistrationResponse_ErrorType = 1
|
||||
PushNotificationRegistrationResponse_VERSION_MISMATCH PushNotificationRegistrationResponse_ErrorType = 2
|
||||
PushNotificationRegistrationResponse_UNSUPPORTED_TOKEN_TYPE PushNotificationRegistrationResponse_ErrorType = 3
|
||||
PushNotificationRegistrationResponse_INTERNAL_ERROR PushNotificationRegistrationResponse_ErrorType = 4
|
||||
)
|
||||
|
||||
var PushNotificationRegistrationResponse_ErrorType_name = map[int32]string{
|
||||
0: "UNKNOWN_ERROR_TYPE",
|
||||
1: "MALFORMED_MESSAGE",
|
||||
2: "VERSION_MISMATCH",
|
||||
3: "UNSUPPORTED_TOKEN_TYPE",
|
||||
4: "INTERNAL_ERROR",
|
||||
}
|
||||
|
||||
var PushNotificationRegistrationResponse_ErrorType_value = map[string]int32{
|
||||
"UNKNOWN_ERROR_TYPE": 0,
|
||||
"MALFORMED_MESSAGE": 1,
|
||||
"VERSION_MISMATCH": 2,
|
||||
"UNSUPPORTED_TOKEN_TYPE": 3,
|
||||
"INTERNAL_ERROR": 4,
|
||||
}
|
||||
|
||||
func (x PushNotificationRegistrationResponse_ErrorType) String() string {
|
||||
return proto.EnumName(PushNotificationRegistrationResponse_ErrorType_name, int32(x))
|
||||
}
|
||||
|
||||
func (PushNotificationRegistrationResponse_ErrorType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{1, 0}
|
||||
}
|
||||
|
||||
type PushNotificationReport_ErrorType int32
|
||||
|
||||
const (
|
||||
PushNotificationReport_UNKNOWN_ERROR_TYPE PushNotificationReport_ErrorType = 0
|
||||
PushNotificationReport_WRONG_TOKEN PushNotificationReport_ErrorType = 1
|
||||
PushNotificationReport_INTERNAL_ERROR PushNotificationReport_ErrorType = 2
|
||||
PushNotificationReport_NOT_REGISTERED PushNotificationReport_ErrorType = 3
|
||||
)
|
||||
|
||||
var PushNotificationReport_ErrorType_name = map[int32]string{
|
||||
0: "UNKNOWN_ERROR_TYPE",
|
||||
1: "WRONG_TOKEN",
|
||||
2: "INTERNAL_ERROR",
|
||||
3: "NOT_REGISTERED",
|
||||
}
|
||||
|
||||
var PushNotificationReport_ErrorType_value = map[string]int32{
|
||||
"UNKNOWN_ERROR_TYPE": 0,
|
||||
"WRONG_TOKEN": 1,
|
||||
"INTERNAL_ERROR": 2,
|
||||
"NOT_REGISTERED": 3,
|
||||
}
|
||||
|
||||
func (x PushNotificationReport_ErrorType) String() string {
|
||||
return proto.EnumName(PushNotificationReport_ErrorType_name, int32(x))
|
||||
}
|
||||
|
||||
func (PushNotificationReport_ErrorType) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{8, 0}
|
||||
}
|
||||
|
||||
type PushNotificationRegistration struct {
|
||||
TokenType PushNotificationRegistration_TokenType `protobuf:"varint,1,opt,name=token_type,json=tokenType,proto3,enum=protobuf.PushNotificationRegistration_TokenType" json:"token_type,omitempty"`
|
||||
DeviceToken string `protobuf:"bytes,2,opt,name=device_token,json=deviceToken,proto3" json:"device_token,omitempty"`
|
||||
InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
AccessToken string `protobuf:"bytes,4,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
Enabled bool `protobuf:"varint,5,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"`
|
||||
AllowedKeyList [][]byte `protobuf:"bytes,7,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"`
|
||||
BlockedChatList [][]byte `protobuf:"bytes,8,rep,name=blocked_chat_list,json=blockedChatList,proto3" json:"blocked_chat_list,omitempty"`
|
||||
Unregister bool `protobuf:"varint,9,opt,name=unregister,proto3" json:"unregister,omitempty"`
|
||||
Grant []byte `protobuf:"bytes,10,opt,name=grant,proto3" json:"grant,omitempty"`
|
||||
AllowFromContactsOnly bool `protobuf:"varint,11,opt,name=allow_from_contacts_only,json=allowFromContactsOnly,proto3" json:"allow_from_contacts_only,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) Reset() { *m = PushNotificationRegistration{} }
|
||||
func (m *PushNotificationRegistration) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationRegistration) ProtoMessage() {}
|
||||
func (*PushNotificationRegistration) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{0}
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationRegistration.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationRegistration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationRegistration.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationRegistration) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationRegistration.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationRegistration) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationRegistration.Size(m)
|
||||
}
|
||||
func (m *PushNotificationRegistration) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationRegistration.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationRegistration proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationRegistration) GetTokenType() PushNotificationRegistration_TokenType {
|
||||
if m != nil {
|
||||
return m.TokenType
|
||||
}
|
||||
return PushNotificationRegistration_UNKNOWN_TOKEN_TYPE
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetDeviceToken() string {
|
||||
if m != nil {
|
||||
return m.DeviceToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetAccessToken() string {
|
||||
if m != nil {
|
||||
return m.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetEnabled() bool {
|
||||
if m != nil {
|
||||
return m.Enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetVersion() uint64 {
|
||||
if m != nil {
|
||||
return m.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetAllowedKeyList() [][]byte {
|
||||
if m != nil {
|
||||
return m.AllowedKeyList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetBlockedChatList() [][]byte {
|
||||
if m != nil {
|
||||
return m.BlockedChatList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetUnregister() bool {
|
||||
if m != nil {
|
||||
return m.Unregister
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetGrant() []byte {
|
||||
if m != nil {
|
||||
return m.Grant
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistration) GetAllowFromContactsOnly() bool {
|
||||
if m != nil {
|
||||
return m.AllowFromContactsOnly
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type PushNotificationRegistrationResponse struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
Error PushNotificationRegistrationResponse_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationRegistrationResponse_ErrorType" json:"error,omitempty"`
|
||||
RequestId []byte `protobuf:"bytes,3,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistrationResponse) Reset() { *m = PushNotificationRegistrationResponse{} }
|
||||
func (m *PushNotificationRegistrationResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationRegistrationResponse) ProtoMessage() {}
|
||||
func (*PushNotificationRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{1}
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistrationResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationRegistrationResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationRegistrationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationRegistrationResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationRegistrationResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationRegistrationResponse.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationRegistrationResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationRegistrationResponse.Size(m)
|
||||
}
|
||||
func (m *PushNotificationRegistrationResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationRegistrationResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationRegistrationResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationRegistrationResponse) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistrationResponse) GetError() PushNotificationRegistrationResponse_ErrorType {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return PushNotificationRegistrationResponse_UNKNOWN_ERROR_TYPE
|
||||
}
|
||||
|
||||
func (m *PushNotificationRegistrationResponse) GetRequestId() []byte {
|
||||
if m != nil {
|
||||
return m.RequestId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ContactCodeAdvertisement struct {
|
||||
PushNotificationInfo []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=push_notification_info,json=pushNotificationInfo,proto3" json:"push_notification_info,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ContactCodeAdvertisement) Reset() { *m = ContactCodeAdvertisement{} }
|
||||
func (m *ContactCodeAdvertisement) String() string { return proto.CompactTextString(m) }
|
||||
func (*ContactCodeAdvertisement) ProtoMessage() {}
|
||||
func (*ContactCodeAdvertisement) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{2}
|
||||
}
|
||||
|
||||
func (m *ContactCodeAdvertisement) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ContactCodeAdvertisement.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ContactCodeAdvertisement) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ContactCodeAdvertisement.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ContactCodeAdvertisement) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ContactCodeAdvertisement.Merge(m, src)
|
||||
}
|
||||
func (m *ContactCodeAdvertisement) XXX_Size() int {
|
||||
return xxx_messageInfo_ContactCodeAdvertisement.Size(m)
|
||||
}
|
||||
func (m *ContactCodeAdvertisement) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ContactCodeAdvertisement.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ContactCodeAdvertisement proto.InternalMessageInfo
|
||||
|
||||
func (m *ContactCodeAdvertisement) GetPushNotificationInfo() []*PushNotificationQueryInfo {
|
||||
if m != nil {
|
||||
return m.PushNotificationInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushNotificationQuery struct {
|
||||
PublicKeys [][]byte `protobuf:"bytes,1,rep,name=public_keys,json=publicKeys,proto3" json:"public_keys,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationQuery) Reset() { *m = PushNotificationQuery{} }
|
||||
func (m *PushNotificationQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationQuery) ProtoMessage() {}
|
||||
func (*PushNotificationQuery) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{3}
|
||||
}
|
||||
|
||||
func (m *PushNotificationQuery) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationQuery.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationQuery) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationQuery.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationQuery) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationQuery.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationQuery) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationQuery.Size(m)
|
||||
}
|
||||
func (m *PushNotificationQuery) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationQuery.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationQuery proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationQuery) GetPublicKeys() [][]byte {
|
||||
if m != nil {
|
||||
return m.PublicKeys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushNotificationQueryInfo struct {
|
||||
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||
AllowedKeyList [][]byte `protobuf:"bytes,4,rep,name=allowed_key_list,json=allowedKeyList,proto3" json:"allowed_key_list,omitempty"`
|
||||
Grant []byte `protobuf:"bytes,5,opt,name=grant,proto3" json:"grant,omitempty"`
|
||||
Version uint64 `protobuf:"varint,6,opt,name=version,proto3" json:"version,omitempty"`
|
||||
ServerPublicKey []byte `protobuf:"bytes,7,opt,name=server_public_key,json=serverPublicKey,proto3" json:"server_public_key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) Reset() { *m = PushNotificationQueryInfo{} }
|
||||
func (m *PushNotificationQueryInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationQueryInfo) ProtoMessage() {}
|
||||
func (*PushNotificationQueryInfo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{4}
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationQueryInfo.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationQueryInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationQueryInfo.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationQueryInfo) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationQueryInfo.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationQueryInfo) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationQueryInfo.Size(m)
|
||||
}
|
||||
func (m *PushNotificationQueryInfo) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationQueryInfo.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationQueryInfo proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetAccessToken() string {
|
||||
if m != nil {
|
||||
return m.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetPublicKey() []byte {
|
||||
if m != nil {
|
||||
return m.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetAllowedKeyList() [][]byte {
|
||||
if m != nil {
|
||||
return m.AllowedKeyList
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetGrant() []byte {
|
||||
if m != nil {
|
||||
return m.Grant
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetVersion() uint64 {
|
||||
if m != nil {
|
||||
return m.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryInfo) GetServerPublicKey() []byte {
|
||||
if m != nil {
|
||||
return m.ServerPublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushNotificationQueryResponse struct {
|
||||
Info []*PushNotificationQueryInfo `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"`
|
||||
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||
Success bool `protobuf:"varint,3,opt,name=success,proto3" json:"success,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryResponse) Reset() { *m = PushNotificationQueryResponse{} }
|
||||
func (m *PushNotificationQueryResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationQueryResponse) ProtoMessage() {}
|
||||
func (*PushNotificationQueryResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{5}
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationQueryResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationQueryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationQueryResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationQueryResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationQueryResponse.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationQueryResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationQueryResponse.Size(m)
|
||||
}
|
||||
func (m *PushNotificationQueryResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationQueryResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationQueryResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationQueryResponse) GetInfo() []*PushNotificationQueryInfo {
|
||||
if m != nil {
|
||||
return m.Info
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryResponse) GetMessageId() []byte {
|
||||
if m != nil {
|
||||
return m.MessageId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationQueryResponse) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type PushNotification struct {
|
||||
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||
ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"`
|
||||
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
Message []byte `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotification) Reset() { *m = PushNotification{} }
|
||||
func (m *PushNotification) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotification) ProtoMessage() {}
|
||||
func (*PushNotification) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{6}
|
||||
}
|
||||
|
||||
func (m *PushNotification) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotification.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotification) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotification.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotification) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotification.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotification) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotification.Size(m)
|
||||
}
|
||||
func (m *PushNotification) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotification.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotification proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotification) GetAccessToken() string {
|
||||
if m != nil {
|
||||
return m.AccessToken
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotification) GetChatId() string {
|
||||
if m != nil {
|
||||
return m.ChatId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotification) GetPublicKey() []byte {
|
||||
if m != nil {
|
||||
return m.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotification) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *PushNotification) GetMessage() []byte {
|
||||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushNotificationRequest struct {
|
||||
Requests []*PushNotification `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty"`
|
||||
MessageId []byte `protobuf:"bytes,2,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationRequest) Reset() { *m = PushNotificationRequest{} }
|
||||
func (m *PushNotificationRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationRequest) ProtoMessage() {}
|
||||
func (*PushNotificationRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{7}
|
||||
}
|
||||
|
||||
func (m *PushNotificationRequest) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationRequest.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationRequest.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationRequest) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationRequest.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationRequest) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationRequest.Size(m)
|
||||
}
|
||||
func (m *PushNotificationRequest) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationRequest.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationRequest proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationRequest) GetRequests() []*PushNotification {
|
||||
if m != nil {
|
||||
return m.Requests
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationRequest) GetMessageId() []byte {
|
||||
if m != nil {
|
||||
return m.MessageId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PushNotificationReport struct {
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
Error PushNotificationReport_ErrorType `protobuf:"varint,2,opt,name=error,proto3,enum=protobuf.PushNotificationReport_ErrorType" json:"error,omitempty"`
|
||||
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||
InstallationId string `protobuf:"bytes,4,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationReport) Reset() { *m = PushNotificationReport{} }
|
||||
func (m *PushNotificationReport) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationReport) ProtoMessage() {}
|
||||
func (*PushNotificationReport) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{8}
|
||||
}
|
||||
|
||||
func (m *PushNotificationReport) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationReport.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationReport) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationReport.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationReport) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationReport.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationReport) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationReport.Size(m)
|
||||
}
|
||||
func (m *PushNotificationReport) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationReport.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationReport proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationReport) GetSuccess() bool {
|
||||
if m != nil {
|
||||
return m.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *PushNotificationReport) GetError() PushNotificationReport_ErrorType {
|
||||
if m != nil {
|
||||
return m.Error
|
||||
}
|
||||
return PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||
}
|
||||
|
||||
func (m *PushNotificationReport) GetPublicKey() []byte {
|
||||
if m != nil {
|
||||
return m.PublicKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationReport) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type PushNotificationResponse struct {
|
||||
MessageId []byte `protobuf:"bytes,1,opt,name=message_id,json=messageId,proto3" json:"message_id,omitempty"`
|
||||
Reports []*PushNotificationReport `protobuf:"bytes,2,rep,name=reports,proto3" json:"reports,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *PushNotificationResponse) Reset() { *m = PushNotificationResponse{} }
|
||||
func (m *PushNotificationResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*PushNotificationResponse) ProtoMessage() {}
|
||||
func (*PushNotificationResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_200acd86044eaa5d, []int{9}
|
||||
}
|
||||
|
||||
func (m *PushNotificationResponse) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_PushNotificationResponse.Unmarshal(m, b)
|
||||
}
|
||||
func (m *PushNotificationResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_PushNotificationResponse.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *PushNotificationResponse) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_PushNotificationResponse.Merge(m, src)
|
||||
}
|
||||
func (m *PushNotificationResponse) XXX_Size() int {
|
||||
return xxx_messageInfo_PushNotificationResponse.Size(m)
|
||||
}
|
||||
func (m *PushNotificationResponse) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_PushNotificationResponse.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_PushNotificationResponse proto.InternalMessageInfo
|
||||
|
||||
func (m *PushNotificationResponse) GetMessageId() []byte {
|
||||
if m != nil {
|
||||
return m.MessageId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PushNotificationResponse) GetReports() []*PushNotificationReport {
|
||||
if m != nil {
|
||||
return m.Reports
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterEnum("protobuf.PushNotificationRegistration_TokenType", PushNotificationRegistration_TokenType_name, PushNotificationRegistration_TokenType_value)
|
||||
proto.RegisterEnum("protobuf.PushNotificationRegistrationResponse_ErrorType", PushNotificationRegistrationResponse_ErrorType_name, PushNotificationRegistrationResponse_ErrorType_value)
|
||||
proto.RegisterEnum("protobuf.PushNotificationReport_ErrorType", PushNotificationReport_ErrorType_name, PushNotificationReport_ErrorType_value)
|
||||
proto.RegisterType((*PushNotificationRegistration)(nil), "protobuf.PushNotificationRegistration")
|
||||
proto.RegisterType((*PushNotificationRegistrationResponse)(nil), "protobuf.PushNotificationRegistrationResponse")
|
||||
proto.RegisterType((*ContactCodeAdvertisement)(nil), "protobuf.ContactCodeAdvertisement")
|
||||
proto.RegisterType((*PushNotificationQuery)(nil), "protobuf.PushNotificationQuery")
|
||||
proto.RegisterType((*PushNotificationQueryInfo)(nil), "protobuf.PushNotificationQueryInfo")
|
||||
proto.RegisterType((*PushNotificationQueryResponse)(nil), "protobuf.PushNotificationQueryResponse")
|
||||
proto.RegisterType((*PushNotification)(nil), "protobuf.PushNotification")
|
||||
proto.RegisterType((*PushNotificationRequest)(nil), "protobuf.PushNotificationRequest")
|
||||
proto.RegisterType((*PushNotificationReport)(nil), "protobuf.PushNotificationReport")
|
||||
proto.RegisterType((*PushNotificationResponse)(nil), "protobuf.PushNotificationResponse")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("push_notifications.proto", fileDescriptor_200acd86044eaa5d) }
|
||||
|
||||
var fileDescriptor_200acd86044eaa5d = []byte{
|
||||
// 878 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x41, 0x6f, 0xeb, 0x44,
|
||||
0x17, 0xfd, 0x9c, 0xa4, 0x4d, 0x72, 0x93, 0x2f, 0x4d, 0x47, 0x6d, 0x9f, 0x79, 0xa2, 0x10, 0x0c,
|
||||
0x12, 0x51, 0x17, 0x11, 0x2a, 0x12, 0xef, 0x89, 0x15, 0xa1, 0x75, 0x8b, 0xd5, 0xc6, 0x0e, 0x13,
|
||||
0x97, 0xa7, 0x27, 0x21, 0x59, 0x8e, 0x3d, 0x69, 0xad, 0xba, 0x1e, 0x33, 0x33, 0x2e, 0xca, 0x8e,
|
||||
0x1f, 0xc0, 0x86, 0x2d, 0x1b, 0xfe, 0x02, 0xe2, 0x17, 0x22, 0x8f, 0xed, 0xe0, 0x36, 0x6e, 0x5a,
|
||||
0x24, 0x56, 0xf6, 0x9c, 0xb9, 0xf7, 0xce, 0xcc, 0x39, 0xf7, 0x5c, 0x50, 0xe3, 0x84, 0xdf, 0x38,
|
||||
0x11, 0x15, 0xc1, 0x22, 0xf0, 0x5c, 0x11, 0xd0, 0x88, 0x8f, 0x62, 0x46, 0x05, 0x45, 0x2d, 0xf9,
|
||||
0x99, 0x27, 0x0b, 0xed, 0x8f, 0x06, 0x7c, 0x38, 0x4d, 0xf8, 0x8d, 0x59, 0x8a, 0xc2, 0xe4, 0x3a,
|
||||
0xe0, 0x82, 0xc9, 0x7f, 0x64, 0x01, 0x08, 0x7a, 0x4b, 0x22, 0x47, 0x2c, 0x63, 0xa2, 0x2a, 0x03,
|
||||
0x65, 0xd8, 0x3b, 0xfe, 0x62, 0x54, 0xe4, 0x8f, 0x36, 0xe5, 0x8e, 0xec, 0x34, 0xd1, 0x5e, 0xc6,
|
||||
0x04, 0xb7, 0x45, 0xf1, 0x8b, 0x3e, 0x81, 0xae, 0x4f, 0xee, 0x03, 0x8f, 0x38, 0x12, 0x53, 0x6b,
|
||||
0x03, 0x65, 0xd8, 0xc6, 0x9d, 0x0c, 0x93, 0x19, 0xe8, 0x73, 0xd8, 0x09, 0x22, 0x2e, 0xdc, 0x30,
|
||||
0x94, 0x75, 0x9c, 0xc0, 0x57, 0xeb, 0x32, 0xaa, 0x57, 0x86, 0x0d, 0x3f, 0xad, 0xe5, 0x7a, 0x1e,
|
||||
0xe1, 0x3c, 0xaf, 0xd5, 0xc8, 0x6a, 0x65, 0x58, 0x56, 0x4b, 0x85, 0x26, 0x89, 0xdc, 0x79, 0x48,
|
||||
0x7c, 0x75, 0x6b, 0xa0, 0x0c, 0x5b, 0xb8, 0x58, 0xa6, 0x3b, 0xf7, 0x84, 0xf1, 0x80, 0x46, 0xea,
|
||||
0xf6, 0x40, 0x19, 0x36, 0x70, 0xb1, 0x44, 0x43, 0xe8, 0xbb, 0x61, 0x48, 0x7f, 0x26, 0xbe, 0x73,
|
||||
0x4b, 0x96, 0x4e, 0x18, 0x70, 0xa1, 0x36, 0x07, 0xf5, 0x61, 0x17, 0xf7, 0x72, 0xfc, 0x82, 0x2c,
|
||||
0x2f, 0x03, 0x2e, 0xd0, 0x11, 0xec, 0xce, 0x43, 0xea, 0xdd, 0x12, 0xdf, 0xf1, 0x6e, 0x5c, 0x91,
|
||||
0x85, 0xb6, 0x64, 0xe8, 0x4e, 0xbe, 0x71, 0x72, 0xe3, 0x0a, 0x19, 0xfb, 0x11, 0x40, 0x12, 0x31,
|
||||
0xc9, 0x0f, 0x61, 0x6a, 0x5b, 0x5e, 0xa6, 0x84, 0xa0, 0x3d, 0xd8, 0xba, 0x66, 0x6e, 0x24, 0x54,
|
||||
0x18, 0x28, 0xc3, 0x2e, 0xce, 0x16, 0xe8, 0x0d, 0xa8, 0xf2, 0x4c, 0x67, 0xc1, 0xe8, 0x9d, 0xe3,
|
||||
0xd1, 0x48, 0xb8, 0x9e, 0xe0, 0x0e, 0x8d, 0xc2, 0xa5, 0xda, 0x91, 0x35, 0xf6, 0xe5, 0xfe, 0x19,
|
||||
0xa3, 0x77, 0x27, 0xf9, 0xae, 0x15, 0x85, 0x4b, 0xed, 0x0c, 0xda, 0x2b, 0xfe, 0xd1, 0x01, 0xa0,
|
||||
0x2b, 0xf3, 0xc2, 0xb4, 0xde, 0x99, 0x8e, 0x6d, 0x5d, 0xe8, 0xa6, 0x63, 0xbf, 0x9f, 0xea, 0xfd,
|
||||
0xff, 0xa1, 0xff, 0x43, 0x7b, 0x3c, 0xcd, 0xb1, 0xbe, 0x82, 0x10, 0xf4, 0xce, 0x0c, 0xac, 0x7f,
|
||||
0x3b, 0x9e, 0xe9, 0x39, 0x56, 0xd3, 0xfe, 0xaa, 0xc1, 0x67, 0x9b, 0x54, 0xc6, 0x84, 0xc7, 0x34,
|
||||
0xe2, 0x24, 0xe5, 0x93, 0x27, 0x92, 0x79, 0xd9, 0x26, 0x2d, 0x5c, 0x2c, 0x91, 0x09, 0x5b, 0x84,
|
||||
0x31, 0xca, 0xa4, 0xd6, 0xbd, 0xe3, 0xb7, 0x2f, 0x6b, 0x9f, 0xa2, 0xf0, 0x48, 0x4f, 0x73, 0x65,
|
||||
0x1b, 0x65, 0x65, 0xd0, 0x21, 0x00, 0x23, 0x3f, 0x25, 0x84, 0x8b, 0xa2, 0x35, 0xba, 0xb8, 0x9d,
|
||||
0x23, 0x86, 0xaf, 0xfd, 0xa2, 0x40, 0x7b, 0x95, 0x53, 0x7e, 0xba, 0x8e, 0xb1, 0x85, 0x8b, 0xa7,
|
||||
0xef, 0xc3, 0xee, 0x64, 0x7c, 0x79, 0x66, 0xe1, 0x89, 0x7e, 0xea, 0x4c, 0xf4, 0xd9, 0x6c, 0x7c,
|
||||
0xae, 0xf7, 0x15, 0xb4, 0x07, 0xfd, 0x1f, 0x74, 0x3c, 0x33, 0x2c, 0xd3, 0x99, 0x18, 0xb3, 0xc9,
|
||||
0xd8, 0x3e, 0xf9, 0xae, 0x5f, 0x43, 0xaf, 0xe1, 0xe0, 0xca, 0x9c, 0x5d, 0x4d, 0xa7, 0x16, 0xb6,
|
||||
0xf5, 0xd3, 0x32, 0x87, 0xf5, 0x94, 0x34, 0xc3, 0xb4, 0x75, 0x6c, 0x8e, 0x2f, 0xb3, 0x13, 0xfa,
|
||||
0x0d, 0x2d, 0x01, 0x35, 0x17, 0xe3, 0x84, 0xfa, 0x64, 0xec, 0xdf, 0x13, 0x26, 0x02, 0x4e, 0xee,
|
||||
0x48, 0x24, 0xd0, 0x7b, 0x38, 0x58, 0x33, 0xa6, 0x13, 0x44, 0x0b, 0xaa, 0x2a, 0x83, 0xfa, 0xb0,
|
||||
0x73, 0xfc, 0xe9, 0xd3, 0xf4, 0x7c, 0x9f, 0x10, 0xb6, 0x34, 0xa2, 0x05, 0xc5, 0x7b, 0xf1, 0xa3,
|
||||
0xad, 0x14, 0xd5, 0xde, 0xc2, 0x7e, 0x65, 0x0a, 0xfa, 0x18, 0x3a, 0x71, 0x32, 0x0f, 0x03, 0x2f,
|
||||
0x6d, 0x68, 0x2e, 0x0f, 0xea, 0x62, 0xc8, 0xa0, 0x0b, 0xb2, 0xe4, 0xda, 0xaf, 0x35, 0xf8, 0xe0,
|
||||
0xc9, 0xd3, 0xd6, 0x7c, 0xa6, 0xac, 0xfb, 0xac, 0xc2, 0xb3, 0xb5, 0x4a, 0xcf, 0x1e, 0x02, 0xfc,
|
||||
0x73, 0x95, 0x42, 0xbc, 0xd5, 0x4d, 0x2a, 0xbd, 0xd7, 0xa8, 0xf4, 0xde, 0xca, 0x2f, 0x5b, 0x65,
|
||||
0xbf, 0x3c, 0xed, 0xea, 0x23, 0xd8, 0xe5, 0x84, 0xdd, 0x13, 0xe6, 0x94, 0xce, 0x6f, 0xca, 0xdc,
|
||||
0x9d, 0x6c, 0x63, 0x5a, 0xdc, 0x42, 0xfb, 0x4d, 0x81, 0xc3, 0x4a, 0x3a, 0x56, 0xdd, 0xfe, 0x06,
|
||||
0x1a, 0xff, 0x56, 0x33, 0x99, 0x90, 0xbe, 0xff, 0x8e, 0x70, 0xee, 0x5e, 0x93, 0x82, 0xa3, 0x2e,
|
||||
0x6e, 0xe7, 0x88, 0xe1, 0x97, 0x5d, 0x54, 0x7f, 0xe0, 0x22, 0xed, 0x4f, 0x05, 0xfa, 0x8f, 0x8b,
|
||||
0xbf, 0x44, 0x99, 0x57, 0xd0, 0x94, 0xb3, 0x69, 0xa5, 0xc8, 0x76, 0xba, 0x7c, 0x5e, 0x89, 0x0a,
|
||||
0x45, 0x1b, 0x95, 0x8a, 0xaa, 0xd0, 0xcc, 0xef, 0x9f, 0x4b, 0x51, 0x2c, 0xb5, 0x18, 0x5e, 0xad,
|
||||
0x3b, 0x5c, 0xda, 0x14, 0x7d, 0x05, 0xad, 0xdc, 0xb1, 0x3c, 0xe7, 0xf0, 0xf5, 0x86, 0xb1, 0xb0,
|
||||
0x8a, 0x7d, 0x86, 0x3e, 0xed, 0xf7, 0x1a, 0x1c, 0xac, 0x1f, 0x19, 0x53, 0x26, 0x36, 0xcc, 0xa7,
|
||||
0x6f, 0x1e, 0xce, 0xa7, 0xa3, 0x4d, 0xf3, 0x29, 0x2d, 0x55, 0x39, 0x91, 0xfe, 0x0b, 0x2a, 0xb5,
|
||||
0x1f, 0x5f, 0x32, 0xb9, 0x76, 0xa0, 0xf3, 0x0e, 0x5b, 0xe6, 0x79, 0x79, 0x6c, 0x3f, 0x9a, 0x40,
|
||||
0xb5, 0x14, 0x33, 0x2d, 0xdb, 0xc1, 0xfa, 0xb9, 0x31, 0xb3, 0x75, 0xac, 0x9f, 0xf6, 0xeb, 0xe9,
|
||||
0x54, 0x5a, 0x7f, 0x50, 0xde, 0xcf, 0x0f, 0x79, 0x55, 0x1e, 0xb7, 0xe5, 0xd7, 0xd0, 0x64, 0xf2,
|
||||
0xed, 0x5c, 0xad, 0x49, 0xb5, 0x06, 0xcf, 0x91, 0x84, 0x8b, 0x84, 0xf9, 0xb6, 0x8c, 0xfc, 0xf2,
|
||||
0xef, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6f, 0xe8, 0xb9, 0x16, 0x90, 0x08, 0x00, 0x00,
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package protobuf;
|
||||
|
||||
message PushNotificationRegistration {
|
||||
enum TokenType {
|
||||
UNKNOWN_TOKEN_TYPE = 0;
|
||||
APN_TOKEN = 1;
|
||||
FIREBASE_TOKEN = 2;
|
||||
}
|
||||
TokenType token_type = 1;
|
||||
string device_token = 2;
|
||||
string installation_id = 3;
|
||||
string access_token = 4;
|
||||
bool enabled = 5;
|
||||
uint64 version = 6;
|
||||
repeated bytes allowed_key_list = 7;
|
||||
repeated bytes blocked_chat_list = 8;
|
||||
bool unregister = 9;
|
||||
bytes grant = 10;
|
||||
bool allow_from_contacts_only = 11;
|
||||
}
|
||||
|
||||
message PushNotificationRegistrationResponse {
|
||||
bool success = 1;
|
||||
ErrorType error = 2;
|
||||
bytes request_id = 3;
|
||||
|
||||
enum ErrorType {
|
||||
UNKNOWN_ERROR_TYPE = 0;
|
||||
MALFORMED_MESSAGE = 1;
|
||||
VERSION_MISMATCH = 2;
|
||||
UNSUPPORTED_TOKEN_TYPE = 3;
|
||||
INTERNAL_ERROR = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message ContactCodeAdvertisement {
|
||||
repeated PushNotificationQueryInfo push_notification_info = 1;
|
||||
}
|
||||
|
||||
message PushNotificationQuery {
|
||||
repeated bytes public_keys = 1;
|
||||
}
|
||||
|
||||
message PushNotificationQueryInfo {
|
||||
string access_token = 1;
|
||||
string installation_id = 2;
|
||||
bytes public_key = 3;
|
||||
repeated bytes allowed_key_list = 4;
|
||||
bytes grant = 5;
|
||||
uint64 version = 6;
|
||||
bytes server_public_key = 7;
|
||||
}
|
||||
|
||||
message PushNotificationQueryResponse {
|
||||
repeated PushNotificationQueryInfo info = 1;
|
||||
bytes message_id = 2;
|
||||
bool success = 3;
|
||||
}
|
||||
|
||||
message PushNotification {
|
||||
string access_token = 1;
|
||||
string chat_id = 2;
|
||||
bytes public_key = 3;
|
||||
string installation_id = 4;
|
||||
bytes message = 5;
|
||||
}
|
||||
|
||||
message PushNotificationRequest {
|
||||
repeated PushNotification requests = 1;
|
||||
bytes message_id = 2;
|
||||
}
|
||||
|
||||
message PushNotificationReport {
|
||||
bool success = 1;
|
||||
ErrorType error = 2;
|
||||
enum ErrorType {
|
||||
UNKNOWN_ERROR_TYPE = 0;
|
||||
WRONG_TOKEN = 1;
|
||||
INTERNAL_ERROR = 2;
|
||||
NOT_REGISTERED = 3;
|
||||
}
|
||||
bytes public_key = 3;
|
||||
string installation_id = 4;
|
||||
}
|
||||
|
||||
message PushNotificationResponse {
|
||||
bytes message_id = 1;
|
||||
repeated PushNotificationReport reports = 2;
|
||||
}
|
|
@ -4,7 +4,7 @@ import (
|
|||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto
|
||||
//go:generate protoc --go_out=. ./chat_message.proto ./application_metadata_message.proto ./membership_update_message.proto ./command.proto ./contact.proto ./pairing.proto ./push_notifications.proto
|
||||
|
||||
func Unmarshal(payload []byte) (*ApplicationMetadataMessage, error) {
|
||||
var message ApplicationMetadataMessage
|
||||
|
|
|
@ -0,0 +1,797 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/zap"
|
||||
|
||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
"github.com/status-im/status-go/waku"
|
||||
)
|
||||
|
||||
const (
|
||||
bob1DeviceToken = "token-1"
|
||||
bob2DeviceToken = "token-2"
|
||||
)
|
||||
|
||||
func TestMessengerPushNotificationSuite(t *testing.T) {
|
||||
suite.Run(t, new(MessengerPushNotificationSuite))
|
||||
}
|
||||
|
||||
type MessengerPushNotificationSuite struct {
|
||||
suite.Suite
|
||||
m *Messenger // main instance of Messenger
|
||||
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
|
||||
// If one wants to send messages between different instances of Messenger,
|
||||
// a single Waku service should be shared.
|
||||
shh types.Waku
|
||||
tmpFiles []*os.File // files to clean up
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) SetupTest() {
|
||||
s.logger = tt.MustCreateTestLogger()
|
||||
|
||||
config := waku.DefaultConfig
|
||||
config.MinimumAcceptedPoW = 0
|
||||
shh := waku.New(&config, s.logger)
|
||||
s.shh = gethbridge.NewGethWakuWrapper(shh)
|
||||
s.Require().NoError(shh.Start(nil))
|
||||
|
||||
s.m = s.newMessenger(s.shh)
|
||||
s.privateKey = s.m.identity
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TearDownTest() {
|
||||
s.Require().NoError(s.m.Shutdown())
|
||||
for _, f := range s.tmpFiles {
|
||||
_ = os.Remove(f.Name())
|
||||
}
|
||||
_ = s.logger.Sync()
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, options []Option) *Messenger {
|
||||
m, err := NewMessenger(
|
||||
privateKey,
|
||||
&testNode{shh: shh},
|
||||
uuid.New().String(),
|
||||
options...,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = m.Init()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
options := []Option{
|
||||
WithCustomLogger(s.logger),
|
||||
WithMessagesPersistenceEnabled(),
|
||||
WithDatabaseConfig(tmpFile.Name(), ""),
|
||||
WithDatasync(),
|
||||
}
|
||||
return s.newMessengerWithOptions(shh, privateKey, options)
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) newMessenger(shh types.Waku) *Messenger {
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return s.newMessengerWithKey(s.shh, privateKey)
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) newPushNotificationServer(shh types.Waku, privateKey *ecdsa.PrivateKey) *Messenger {
|
||||
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
|
||||
serverConfig := &pushnotificationserver.Config{
|
||||
Logger: s.logger,
|
||||
Identity: privateKey,
|
||||
}
|
||||
|
||||
options := []Option{
|
||||
WithCustomLogger(s.logger),
|
||||
WithMessagesPersistenceEnabled(),
|
||||
WithDatabaseConfig(tmpFile.Name(), "some-key"),
|
||||
WithPushNotificationServerConfig(serverConfig),
|
||||
WithDatasync(),
|
||||
}
|
||||
return s.newMessengerWithOptions(shh, privateKey, options)
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TestReceivePushNotification() {
|
||||
|
||||
bob1 := s.m
|
||||
bob2 := s.newMessengerWithKey(s.shh, s.m.identity)
|
||||
|
||||
serverKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||
|
||||
alice := s.newMessenger(s.shh)
|
||||
// start alice and enable sending push notifications
|
||||
s.Require().NoError(alice.Start())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
||||
|
||||
// Register bob1
|
||||
err = bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob1.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered, err := bob1.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bob1Servers, err := bob1.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Register bob2
|
||||
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob2.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registered, err := bob2.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bob2Servers, err := bob2.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create one to one chat & send message
|
||||
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||
s.Require().NoError(alice.SaveChat(&chat))
|
||||
inputMessage := buildTestMessage(chat)
|
||||
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||
s.Require().NoError(err)
|
||||
messageIDString := response.Messages[0].ID
|
||||
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||
s.Require().NoError(err)
|
||||
|
||||
var info []*pushnotificationclient.PushNotificationInfo
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check we have replies for both bob1 and bob2
|
||||
if len(info) != 2 {
|
||||
return errors.New("info not fetched")
|
||||
}
|
||||
return nil
|
||||
|
||||
})
|
||||
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Check we have replies for both bob1 and bob2
|
||||
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
|
||||
|
||||
if info[0].AccessToken == bob1Servers[0].AccessToken {
|
||||
bob1Info = info[0]
|
||||
bob2Info = info[1]
|
||||
} else {
|
||||
bob2Info = info[0]
|
||||
bob1Info = info[1]
|
||||
}
|
||||
|
||||
s.Require().NotNil(bob1Info)
|
||||
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
|
||||
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
|
||||
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
|
||||
|
||||
s.Require().NotNil(bob2Info)
|
||||
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
|
||||
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
|
||||
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
|
||||
|
||||
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedNotificationInfo)
|
||||
s.Require().Len(retrievedNotificationInfo, 2)
|
||||
|
||||
var sentNotification *pushnotificationclient.SentNotification
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sentNotification == nil {
|
||||
return errors.New("sent notification not found")
|
||||
}
|
||||
if !sentNotification.Success {
|
||||
return errors.New("sent notification not successul")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(bob2.Shutdown())
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
s.Require().NoError(server.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationFromContactOnly() {
|
||||
|
||||
bob := s.m
|
||||
|
||||
serverKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||
|
||||
alice := s.newMessenger(s.shh)
|
||||
// start alice and enable push notifications
|
||||
s.Require().NoError(alice.Start())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
bobInstallationIDs := []string{bob.installationID}
|
||||
|
||||
// Register bob
|
||||
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Add alice has a contact
|
||||
aliceContact := &Contact{
|
||||
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
||||
Name: "Some Contact",
|
||||
SystemTags: []string{contactAdded},
|
||||
}
|
||||
|
||||
err = bob.SaveContact(aliceContact)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Enable from contacts only
|
||||
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered, err := bob.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bobServers, err := bob.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create one to one chat & send message
|
||||
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||
s.Require().NoError(alice.SaveChat(&chat))
|
||||
inputMessage := buildTestMessage(chat)
|
||||
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||
s.Require().NoError(err)
|
||||
messageIDString := response.Messages[0].ID
|
||||
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||
s.Require().NoError(err)
|
||||
|
||||
var info []*pushnotificationclient.PushNotificationInfo
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check we have replies for bob
|
||||
if len(info) != 1 {
|
||||
return errors.New("info not fetched")
|
||||
}
|
||||
return nil
|
||||
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NotNil(info)
|
||||
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
|
||||
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||
|
||||
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedNotificationInfo)
|
||||
s.Require().Len(retrievedNotificationInfo, 1)
|
||||
|
||||
var sentNotification *pushnotificationclient.SentNotification
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sentNotification == nil {
|
||||
return errors.New("sent notification not found")
|
||||
}
|
||||
if !sentNotification.Success {
|
||||
return errors.New("sent notification not successul")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
s.Require().NoError(server.Shutdown())
|
||||
}
|
||||
|
||||
func (s *MessengerPushNotificationSuite) TestReceivePushNotificationRetries() {
|
||||
|
||||
bob := s.m
|
||||
|
||||
serverKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
server := s.newPushNotificationServer(s.shh, serverKey)
|
||||
|
||||
alice := s.newMessenger(s.shh)
|
||||
// another contact to invalidate the token
|
||||
frank := s.newMessenger(s.shh)
|
||||
// start alice and enable push notifications
|
||||
s.Require().NoError(alice.Start())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
bobInstallationIDs := []string{bob.installationID}
|
||||
|
||||
// Register bob
|
||||
err = bob.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Add alice has a contact
|
||||
aliceContact := &Contact{
|
||||
ID: types.EncodeHex(crypto.FromECDSAPub(&alice.identity.PublicKey)),
|
||||
Name: "Some Contact",
|
||||
SystemTags: []string{contactAdded},
|
||||
}
|
||||
|
||||
err = bob.SaveContact(aliceContact)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Add frank has a contact
|
||||
frankContact := &Contact{
|
||||
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
|
||||
Name: "Some Contact",
|
||||
SystemTags: []string{contactAdded},
|
||||
}
|
||||
|
||||
err = bob.SaveContact(frankContact)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Enable from contacts only
|
||||
err = bob.EnablePushNotificationsFromContactsOnly()
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered, err := bob.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bobServers, err := bob.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create one to one chat & send message
|
||||
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||
s.Require().NoError(alice.SaveChat(&chat))
|
||||
inputMessage := buildTestMessage(chat)
|
||||
_, err = alice.SendChatMessage(context.Background(), inputMessage)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We check that alice retrieves the info from the server
|
||||
var info []*pushnotificationclient.PushNotificationInfo
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check we have replies for bob
|
||||
if len(info) != 1 {
|
||||
return errors.New("info not fetched")
|
||||
}
|
||||
return nil
|
||||
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NotNil(info)
|
||||
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||
s.Require().Equal(bobServers[0].AccessToken, info[0].AccessToken)
|
||||
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||
|
||||
// The message has been sent, but not received, now we remove a contact so that the token is invalidated
|
||||
frankContact = &Contact{
|
||||
ID: types.EncodeHex(crypto.FromECDSAPub(&frank.identity.PublicKey)),
|
||||
Name: "Some Contact",
|
||||
SystemTags: []string{},
|
||||
}
|
||||
err = bob.SaveContact(frankContact)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Re-registration should be triggered, pull from server and bob to check we are correctly registered
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered, err := bob.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
newBobServers, err := bob.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
// Make sure access token is not the same
|
||||
s.Require().NotEqual(newBobServers[0].AccessToken, bobServers[0].AccessToken)
|
||||
|
||||
// Send another message, here the token will not be valid
|
||||
inputMessage = buildTestMessage(chat)
|
||||
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||
s.Require().NoError(err)
|
||||
messageIDString := response.Messages[0].ID
|
||||
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check we have replies for bob
|
||||
if len(info) != 1 {
|
||||
return errors.New("info not fetched")
|
||||
}
|
||||
if newBobServers[0].AccessToken != info[0].AccessToken {
|
||||
return errors.New("still using the old access token")
|
||||
}
|
||||
return nil
|
||||
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NotNil(info)
|
||||
s.Require().Equal(bob.installationID, info[0].InstallationID)
|
||||
s.Require().Equal(newBobServers[0].AccessToken, info[0].AccessToken)
|
||||
s.Require().Equal(&bob.identity.PublicKey, info[0].PublicKey)
|
||||
|
||||
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob.identity.PublicKey, bobInstallationIDs)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedNotificationInfo)
|
||||
s.Require().Len(retrievedNotificationInfo, 1)
|
||||
|
||||
var sentNotification *pushnotificationclient.SentNotification
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob.identity.PublicKey), bob.installationID, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sentNotification == nil {
|
||||
return errors.New("sent notification not found")
|
||||
}
|
||||
if !sentNotification.Success {
|
||||
return errors.New("sent notification not successul")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
s.Require().NoError(server.Shutdown())
|
||||
}
|
||||
|
||||
// Here bob acts as his own server
|
||||
func (s *MessengerPushNotificationSuite) TestActAsYourOwnPushNotificationServer() {
|
||||
bob1 := s.m
|
||||
server := s.newPushNotificationServer(s.shh, s.m.identity)
|
||||
bob2 := server
|
||||
alice := s.newMessenger(s.shh)
|
||||
// start alice and enable sending push notifications
|
||||
s.Require().NoError(alice.Start())
|
||||
s.Require().NoError(alice.EnableSendingPushNotifications())
|
||||
bobInstallationIDs := []string{bob1.installationID, bob2.installationID}
|
||||
|
||||
// Register bob1
|
||||
err := bob1.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob1.RegisterForPushNotifications(context.Background(), bob1DeviceToken)
|
||||
|
||||
// Pull servers and check we registered
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob1.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registered, err := bob1.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bob1Servers, err := bob1.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Register bob2
|
||||
err = bob2.AddPushNotificationsServer(context.Background(), &server.identity.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = bob2.RegisterForPushNotifications(context.Background(), bob2DeviceToken)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = bob2.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
registered, err := bob2.RegisteredForPushNotifications()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !registered {
|
||||
return errors.New("not registered")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Make sure we receive it
|
||||
s.Require().NoError(err)
|
||||
bob2Servers, err := bob2.GetPushNotificationServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create one to one chat & send message
|
||||
pkString := hex.EncodeToString(crypto.FromECDSAPub(&s.m.identity.PublicKey))
|
||||
chat := CreateOneToOneChat(pkString, &s.m.identity.PublicKey, alice.transport)
|
||||
s.Require().NoError(alice.SaveChat(&chat))
|
||||
inputMessage := buildTestMessage(chat)
|
||||
response, err := alice.SendChatMessage(context.Background(), inputMessage)
|
||||
s.Require().NoError(err)
|
||||
messageIDString := response.Messages[0].ID
|
||||
messageID, err := hex.DecodeString(messageIDString[2:])
|
||||
s.Require().NoError(err)
|
||||
|
||||
var info []*pushnotificationclient.PushNotificationInfo
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err = alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check we have replies for both bob1 and bob2
|
||||
if len(info) != 2 {
|
||||
return errors.New("info not fetched")
|
||||
}
|
||||
return nil
|
||||
|
||||
})
|
||||
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Check we have replies for both bob1 and bob2
|
||||
var bob1Info, bob2Info *pushnotificationclient.PushNotificationInfo
|
||||
|
||||
if info[0].AccessToken == bob1Servers[0].AccessToken {
|
||||
bob1Info = info[0]
|
||||
bob2Info = info[1]
|
||||
} else {
|
||||
bob2Info = info[0]
|
||||
bob1Info = info[1]
|
||||
}
|
||||
|
||||
s.Require().NotNil(bob1Info)
|
||||
s.Require().Equal(bob1.installationID, bob1Info.InstallationID)
|
||||
s.Require().Equal(bob1Servers[0].AccessToken, bob1Info.AccessToken)
|
||||
s.Require().Equal(&bob1.identity.PublicKey, bob1Info.PublicKey)
|
||||
|
||||
s.Require().NotNil(bob2Info)
|
||||
s.Require().Equal(bob2.installationID, bob2Info.InstallationID)
|
||||
s.Require().Equal(bob2Servers[0].AccessToken, bob2Info.AccessToken)
|
||||
s.Require().Equal(&bob2.identity.PublicKey, bob2Info.PublicKey)
|
||||
|
||||
retrievedNotificationInfo, err := alice.pushNotificationClient.GetPushNotificationInfo(&bob1.identity.PublicKey, bobInstallationIDs)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedNotificationInfo)
|
||||
s.Require().Len(retrievedNotificationInfo, 2)
|
||||
|
||||
var sentNotification *pushnotificationclient.SentNotification
|
||||
err = tt.RetryWithBackOff(func() error {
|
||||
_, err = server.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = alice.RetrieveAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sentNotification, err = alice.pushNotificationClient.GetSentNotification(common.HashPublicKey(&bob1.identity.PublicKey), bob1.installationID, messageID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sentNotification == nil {
|
||||
return errors.New("sent notification not found")
|
||||
}
|
||||
if !sentNotification.Success {
|
||||
return errors.New("sent notification not successul")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(bob2.Shutdown())
|
||||
s.Require().NoError(alice.Shutdown())
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,230 @@
|
|||
package pushnotificationclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
)
|
||||
|
||||
const testDeviceToken = "test-token"
|
||||
|
||||
type ClientSuite struct {
|
||||
suite.Suite
|
||||
tmpFile *os.File
|
||||
persistence *Persistence
|
||||
identity *ecdsa.PrivateKey
|
||||
installationID string
|
||||
client *Client
|
||||
}
|
||||
|
||||
func TestClientSuite(t *testing.T) {
|
||||
s := new(ClientSuite)
|
||||
s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
func (s *ClientSuite) SetupTest() {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
s.tmpFile = tmpFile
|
||||
|
||||
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||
s.Require().NoError(err)
|
||||
s.persistence = NewPersistence(database)
|
||||
|
||||
identity, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
s.identity = identity
|
||||
|
||||
config := &Config{
|
||||
Identity: identity,
|
||||
Logger: tt.MustCreateTestLogger(),
|
||||
RemoteNotificationsEnabled: true,
|
||||
InstallationID: s.installationID,
|
||||
}
|
||||
|
||||
s.client = New(s.persistence, config, nil)
|
||||
}
|
||||
|
||||
func (s *ClientSuite) TestBuildPushNotificationRegisterMessage() {
|
||||
mutedChatList := []string{"a", "b"}
|
||||
|
||||
// build chat lish hashes
|
||||
var mutedChatListHashes [][]byte
|
||||
for _, chatID := range mutedChatList {
|
||||
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
||||
}
|
||||
|
||||
contactKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
||||
|
||||
// Set random generator for uuid
|
||||
var seed int64 = 1
|
||||
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||
|
||||
// Get token
|
||||
expectedUUID := uuid.New().String()
|
||||
|
||||
// Reset random generator
|
||||
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||
|
||||
s.client.deviceToken = testDeviceToken
|
||||
// Set reader
|
||||
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
||||
|
||||
options := &protobuf.PushNotificationRegistration{
|
||||
Version: 1,
|
||||
AccessToken: expectedUUID,
|
||||
DeviceToken: testDeviceToken,
|
||||
InstallationId: s.installationID,
|
||||
Enabled: true,
|
||||
BlockedChatList: mutedChatListHashes,
|
||||
}
|
||||
|
||||
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(options, actualMessage)
|
||||
}
|
||||
|
||||
func (s *ClientSuite) TestBuildPushNotificationRegisterMessageAllowFromContactsOnly() {
|
||||
mutedChatList := []string{"a", "b"}
|
||||
|
||||
// build chat lish hashes
|
||||
var mutedChatListHashes [][]byte
|
||||
for _, chatID := range mutedChatList {
|
||||
mutedChatListHashes = append(mutedChatListHashes, common.Shake256([]byte(chatID)))
|
||||
}
|
||||
|
||||
contactKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
contactIDs := []*ecdsa.PublicKey{&contactKey.PublicKey}
|
||||
|
||||
// Set random generator for uuid
|
||||
var seed int64 = 1
|
||||
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||
|
||||
// Get token
|
||||
expectedUUID := uuid.New().String()
|
||||
|
||||
// set up reader
|
||||
reader := bytes.NewReader([]byte(expectedUUID))
|
||||
|
||||
sharedKey, err := ecies.ImportECDSA(s.identity).GenerateShared(
|
||||
ecies.ImportECDSAPublic(&contactKey.PublicKey),
|
||||
accessTokenKeyLength,
|
||||
accessTokenKeyLength,
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
// build encrypted token
|
||||
encryptedToken, err := encryptAccessToken([]byte(expectedUUID), sharedKey, reader)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Reset random generator
|
||||
uuid.SetRand(rand.New(rand.NewSource(seed)))
|
||||
|
||||
s.client.config.AllowFromContactsOnly = true
|
||||
s.client.deviceToken = testDeviceToken
|
||||
// Set reader
|
||||
s.client.reader = bytes.NewReader([]byte(expectedUUID))
|
||||
|
||||
options := &protobuf.PushNotificationRegistration{
|
||||
Version: 1,
|
||||
AccessToken: expectedUUID,
|
||||
DeviceToken: testDeviceToken,
|
||||
InstallationId: s.installationID,
|
||||
AllowFromContactsOnly: true,
|
||||
Enabled: true,
|
||||
BlockedChatList: mutedChatListHashes,
|
||||
AllowedKeyList: [][]byte{encryptedToken},
|
||||
}
|
||||
|
||||
actualMessage, err := s.client.buildPushNotificationRegistrationMessage(contactIDs, mutedChatList)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(options, actualMessage)
|
||||
}
|
||||
|
||||
func (s *ClientSuite) TestHandleMessageScheduled() {
|
||||
messageID := []byte("message-id")
|
||||
chatID := "chat-id"
|
||||
installationID1 := "1"
|
||||
installationID2 := "2"
|
||||
rawMessage := &common.RawMessage{
|
||||
ID: types.EncodeHex(messageID),
|
||||
SendPushNotification: true,
|
||||
LocalChatID: chatID,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.client.handleMessageScheduled(rawMessage))
|
||||
|
||||
key1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// First time, should notify
|
||||
response, err := s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(response)
|
||||
|
||||
// Save notification
|
||||
s.Require().NoError(s.client.notifiedOn(&key1.PublicKey, installationID1, messageID))
|
||||
|
||||
// Second time, should not notify
|
||||
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().False(response)
|
||||
|
||||
// Different installationID
|
||||
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID2, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(response)
|
||||
|
||||
key2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
// different key, should notify
|
||||
response, err = s.client.shouldNotifyOn(&key2.PublicKey, installationID1, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(response)
|
||||
|
||||
// non tracked message id
|
||||
response, err = s.client.shouldNotifyOn(&key1.PublicKey, installationID1, []byte("not-existing"))
|
||||
s.Require().NoError(err)
|
||||
s.Require().False(response)
|
||||
}
|
||||
|
||||
func (s *ClientSuite) TestShouldRefreshToken() {
|
||||
key1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
key2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
key3, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
key4, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Contacts are added
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey, &key4.PublicKey}))
|
||||
|
||||
// everything the same
|
||||
s.Require().False(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey, &key1.PublicKey}))
|
||||
|
||||
// A contact is removed
|
||||
s.Require().True(s.client.shouldRefreshToken([]*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}, []*ecdsa.PublicKey{&key2.PublicKey}))
|
||||
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
// Code generated by go-bindata. DO NOT EDIT.
|
||||
// sources:
|
||||
// 1593601729_initial_schema.down.sql (144B)
|
||||
// 1593601729_initial_schema.up.sql (1.773kB)
|
||||
// doc.go (382B)
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
digest [sha256.Size]byte
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var __1593601729_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x4f\xce\xc9\x4c\xcd\x2b\x89\x2f\x4e\x2d\x2a\x4b\x2d\x2a\xb6\xe6\x22\x46\x71\x66\x5e\x5a\x3e\x54\xa5\xa7\x9f\x8b\x6b\x84\x42\x66\x4a\x45\x3c\x5e\xd5\xf1\x05\xa5\x49\x39\x99\xc9\xf1\xd9\xa9\x95\xd6\x5c\x80\x00\x00\x00\xff\xff\x6d\xb4\xf8\x65\x90\x00\x00\x00")
|
||||
|
||||
func _1593601729_initial_schemaDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593601729_initial_schemaDownSql,
|
||||
"1593601729_initial_schema.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593601729_initial_schemaDownSql() (*asset, error) {
|
||||
bytes, err := _1593601729_initial_schemaDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1593601729_initial_schema.down.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa, 0x95, 0x55, 0x64, 0x38, 0x40, 0x16, 0xbf, 0x8b, 0x1c, 0x18, 0xb4, 0xc5, 0x7f, 0xd0, 0xb8, 0xf0, 0x3c, 0xa2, 0x82, 0xf8, 0x8d, 0x5a, 0xd3, 0xb6, 0x6e, 0xa3, 0xb4, 0xc, 0x9, 0x33, 0x0}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1593601729_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x54\xc1\x8e\xdb\x20\x10\xbd\xfb\x2b\xe6\xb8\x91\x72\xe8\x7d\x4f\x4e\x96\x54\x96\x10\x6e\x13\x22\xe5\x86\x28\x9e\x5d\xa3\xb8\x78\x0b\x78\xd5\xfc\x7d\x85\x9d\x78\x93\xc5\xc5\x55\xba\x17\x4b\x1e\x1e\xa3\x79\x6f\x1e\x6f\xbd\x25\x39\x27\xc0\xf3\x15\x25\x50\x6c\x80\x95\x1c\xc8\xa1\xd8\xf1\x1d\xbc\x76\xae\x16\xa6\xf5\xfa\x59\x2b\xe9\x75\x6b\x84\x6a\x34\x1a\x2f\x1c\xda\x37\xb4\x0e\x1e\x32\x80\xd7\xee\x47\xa3\x95\x38\xe2\x09\x56\xb4\x5c\xf5\xf7\xd9\x9e\xd2\x65\x06\x60\xf1\x45\x3b\x8f\x16\x2b\x58\x95\x25\x25\x39\x83\x27\xb2\xc9\xf7\x94\xc3\x26\xa7\x3b\x72\x8b\x11\xd2\x43\xc1\xf8\xd8\x61\xc4\x7e\x09\xb8\x46\x3a\x2f\x2c\x7a\xab\xe7\x90\x01\x74\x12\xaa\xed\x4c\x0a\x25\x95\x42\xe7\x84\x6f\x8f\x68\x80\x93\x03\x0f\xc5\x3d\x2b\xbe\xef\xc9\xc3\x3b\xa7\x05\x94\x0c\xd6\x25\xdb\xd0\x62\xcd\x61\x4b\xbe\xd1\x7c\x4d\xb2\xc5\x63\x96\xdd\xa3\xdb\xaf\x0e\xad\xc6\x79\xdd\x06\x5c\x44\xf3\x72\x74\x12\xba\x8a\x2f\x45\xb3\x2f\x2f\xd8\xcf\x25\xa1\xcd\x73\x3b\xcb\x60\x70\x88\x48\x41\xb4\x71\x5e\x36\xcd\xd0\x5b\x57\xfd\x0e\x6e\x00\xd1\x86\x3e\x78\x2b\x58\xe1\x6d\x5a\xa5\xe0\x4e\xdd\x9a\xa8\x1e\x6b\xf4\x71\x8c\x65\x3c\xfa\xe7\xca\xe7\xad\x54\x47\xac\xc4\x4f\x74\x4e\xbe\x9c\xcd\x70\xfe\x99\xdc\xab\xaa\xa5\x9f\xd4\xe7\xd2\x69\x82\xff\x99\xe7\x7b\xdb\x5b\x0e\xc5\x57\x56\x6e\x49\x06\x70\x2f\x09\x17\x3e\xd7\x07\xf3\x34\x52\x56\xa8\xa5\xab\xb1\xfa\x3f\xb7\xf4\xf9\x30\x99\x0e\xff\x9e\x09\xae\xeb\x2d\x37\x86\x55\x84\x1a\x53\x0b\xad\x6d\x6d\xa2\x53\xb4\x80\x25\x24\x4c\x37\xed\xb0\xfb\xd7\x33\x64\xaa\xbd\xda\xcc\x25\x67\x87\x5a\x2c\x2f\x80\x6a\x8d\x97\x2a\x38\xcd\xf5\xc7\x43\xd5\x9d\x8c\xaf\xd1\x6b\x15\x34\xff\x3b\xdd\x91\xf0\x35\x7e\xf6\xdd\x14\xec\x89\x1c\x40\x57\xbf\x45\x32\x6c\xae\x7d\x51\xb2\x74\x30\xa5\x9e\xf6\xe2\x31\xfb\x13\x00\x00\xff\xff\xfb\x06\xc2\x3d\xed\x06\x00\x00")
|
||||
|
||||
func _1593601729_initial_schemaUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593601729_initial_schemaUpSql,
|
||||
"1593601729_initial_schema.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593601729_initial_schemaUpSql() (*asset, error) {
|
||||
bytes, err := _1593601729_initial_schemaUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1593601729_initial_schema.up.sql", size: 1773, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4e, 0x1e, 0x5, 0x35, 0x9, 0xb2, 0x2d, 0x6f, 0x33, 0x63, 0xa2, 0x7a, 0x5b, 0xd2, 0x2d, 0xcb, 0x79, 0x7e, 0x6, 0xb4, 0x9d, 0x35, 0xd8, 0x9b, 0x55, 0xe5, 0xf8, 0x44, 0xca, 0xa6, 0xf3, 0xd3}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_docGo,
|
||||
"doc.go",
|
||||
)
|
||||
}
|
||||
|
||||
func docGo() (*asset, error) {
|
||||
bytes, err := docGoBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||
func AssetString(name string) (string, error) {
|
||||
data, err := Asset(name)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// MustAssetString is like AssetString but panics when Asset would return an
|
||||
// error. It simplifies safe initialization of global variables.
|
||||
func MustAssetString(name string) string {
|
||||
return string(MustAsset(name))
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||
// error if the asset could not be found or the digest could not be loaded.
|
||||
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.digest, nil
|
||||
}
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||
}
|
||||
|
||||
// Digests returns a map of all known files and their checksums.
|
||||
func Digests() (map[string][sha256.Size]byte, error) {
|
||||
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||
for name := range _bindata {
|
||||
a, err := _bindata[name]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp[name] = a.digest
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"1593601729_initial_schema.down.sql": _1593601729_initial_schemaDownSql,
|
||||
|
||||
"1593601729_initial_schema.up.sql": _1593601729_initial_schemaUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
// 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
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(canonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"1593601729_initial_schema.down.sql": &bintree{_1593601729_initial_schemaDownSql, map[string]*bintree{}},
|
||||
"1593601729_initial_schema.up.sql": &bintree{_1593601729_initial_schemaUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively.
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
DROP TABLE push_notification_client_servers;
|
||||
DROP TABLE push_notification_client_info;
|
||||
DROP INDEX idx_push_notification_client_info_public_key;
|
|
@ -0,0 +1,54 @@
|
|||
CREATE TABLE IF NOT EXISTS push_notification_client_servers (
|
||||
public_key BLOB NOT NULL,
|
||||
registered BOOLEAN DEFAULT FALSE,
|
||||
registered_at INT NOT NULL DEFAULT 0,
|
||||
last_retried_at INT NOT NULL DEFAULT 0,
|
||||
retry_count INT NOT NULL DEFAULT 0,
|
||||
access_token TEXT,
|
||||
UNIQUE(public_key) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_client_queries (
|
||||
public_key BLOB NOT NULL,
|
||||
queried_at INT NOT NULL,
|
||||
query_id BLOB NOT NULL,
|
||||
UNIQUE(public_key,query_id) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_client_info (
|
||||
public_key BLOB NOT NULL,
|
||||
server_public_key BLOB NOT NULL,
|
||||
installation_id TEXT NOT NULL,
|
||||
access_token TEXT NOT NULL,
|
||||
retrieved_at INT NOT NULL,
|
||||
version INT NOT NULL,
|
||||
UNIQUE(public_key, installation_id, server_public_key) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_client_tracked_messages (
|
||||
message_id BLOB NOT NULL,
|
||||
chat_id TEXT NOT NULL,
|
||||
tracked_at INT NOT NULL,
|
||||
UNIQUE(message_id) ON CONFLICT IGNORE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_client_sent_notifications (
|
||||
message_id BLOB NOT NULL,
|
||||
public_key BLOB NOT NULL,
|
||||
hashed_public_key BLOB NOT NULL,
|
||||
installation_id TEXT NOT NULL,
|
||||
last_tried_at INT NOT NULL,
|
||||
retry_count INT NOT NULL DEFAULT 0,
|
||||
success BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
error INT NOT NULL DEFAULT 0,
|
||||
UNIQUE(message_id, public_key, installation_id) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_client_registrations (
|
||||
registration BLOB NOT NULL,
|
||||
contact_ids BLOB,
|
||||
synthetic_id INT NOT NULL DEFAULT 0,
|
||||
UNIQUE(synthetic_id) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_push_notification_client_info_public_key ON push_notification_client_info(public_key, installation_id);
|
|
@ -0,0 +1,9 @@
|
|||
// This file is necessary because "github.com/status-im/migrate/v4"
|
||||
// can't handle files starting with a prefix. At least that's the case
|
||||
// for go-bindata.
|
||||
// If go-bindata is called from the same directory, asset names
|
||||
// have no prefix and "github.com/status-im/migrate/v4" works as expected.
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate go-bindata -pkg migrations -o ../migrations.go ./
|
|
@ -0,0 +1,396 @@
|
|||
package pushnotificationclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"encoding/gob"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
type Persistence struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewPersistence(db *sql.DB) *Persistence {
|
||||
return &Persistence{db: db}
|
||||
}
|
||||
|
||||
func (p *Persistence) GetLastPushNotificationRegistration() (*protobuf.PushNotificationRegistration, []*ecdsa.PublicKey, error) {
|
||||
var registrationBytes []byte
|
||||
var contactIDsBytes []byte
|
||||
err := p.db.QueryRow(`SELECT registration,contact_ids FROM push_notification_client_registrations LIMIT 1`).Scan(®istrationBytes, &contactIDsBytes)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil, nil
|
||||
} else if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var publicKeyBytes [][]byte
|
||||
var contactIDs []*ecdsa.PublicKey
|
||||
// Restore contactIDs
|
||||
contactIDsDecoder := gob.NewDecoder(bytes.NewBuffer(contactIDsBytes))
|
||||
err = contactIDsDecoder.Decode(&publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for _, pkBytes := range publicKeyBytes {
|
||||
pk, err := crypto.DecompressPubkey(pkBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
contactIDs = append(contactIDs, pk)
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
|
||||
err = proto.Unmarshal(registrationBytes, registration)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return registration, contactIDs, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) SaveLastPushNotificationRegistration(registration *protobuf.PushNotificationRegistration, contactIDs []*ecdsa.PublicKey) error {
|
||||
var encodedContactIDs bytes.Buffer
|
||||
var contactIDsBytes [][]byte
|
||||
for _, pk := range contactIDs {
|
||||
contactIDsBytes = append(contactIDsBytes, crypto.CompressPubkey(pk))
|
||||
}
|
||||
pkEncoder := gob.NewEncoder(&encodedContactIDs)
|
||||
if err := pkEncoder.Encode(contactIDsBytes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
marshaledRegistration, err := proto.Marshal(registration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = p.db.Exec(`INSERT INTO push_notification_client_registrations (registration,contact_ids) VALUES (?, ?)`, marshaledRegistration, encodedContactIDs.Bytes())
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) TrackPushNotification(chatID string, messageID []byte) error {
|
||||
trackedAt := time.Now().Unix()
|
||||
_, err := p.db.Exec(`INSERT INTO push_notification_client_tracked_messages (chat_id, message_id, tracked_at) VALUES (?,?,?)`, chatID, messageID, trackedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) TrackedMessage(messageID []byte) (bool, error) {
|
||||
var count uint64
|
||||
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) SavePushNotificationQuery(publicKey *ecdsa.PublicKey, queryID []byte) error {
|
||||
queriedAt := time.Now().Unix()
|
||||
_, err := p.db.Exec(`INSERT INTO push_notification_client_queries (public_key, query_id, queried_at) VALUES (?,?,?)`, crypto.CompressPubkey(publicKey), queryID, queriedAt)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) GetQueriedAt(publicKey *ecdsa.PublicKey) (int64, error) {
|
||||
var queriedAt int64
|
||||
err := p.db.QueryRow(`SELECT queried_at FROM push_notification_client_queries WHERE public_key = ? ORDER BY queried_at DESC LIMIT 1`, crypto.CompressPubkey(publicKey)).Scan(&queriedAt)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return queriedAt, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) GetQueryPublicKey(queryID []byte) (*ecdsa.PublicKey, error) {
|
||||
var publicKeyBytes []byte
|
||||
err := p.db.QueryRow(`SELECT public_key FROM push_notification_client_queries WHERE query_id = ?`, queryID).Scan(&publicKeyBytes)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) SavePushNotificationInfo(infos []*PushNotificationInfo) error {
|
||||
tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{})
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = tx.Commit()
|
||||
return
|
||||
}
|
||||
// don't shadow original error
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
for _, info := range infos {
|
||||
var latestVersion uint64
|
||||
clientCompressedKey := crypto.CompressPubkey(info.PublicKey)
|
||||
err := tx.QueryRow(`SELECT IFNULL(MAX(version),0) FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? LIMIT 1`, clientCompressedKey, info.InstallationID).Scan(&latestVersion)
|
||||
if err != sql.ErrNoRows && err != nil {
|
||||
return err
|
||||
}
|
||||
if latestVersion > info.Version {
|
||||
// Nothing to do
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove anything that as a lower version
|
||||
_, err = tx.Exec(`DELETE FROM push_notification_client_info WHERE public_key = ? AND installation_id = ? AND version < ?`, clientCompressedKey, info.InstallationID, info.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert
|
||||
_, err = tx.Exec(`INSERT INTO push_notification_client_info (public_key, server_public_key, installation_id, access_token, retrieved_at, version) VALUES (?, ?, ?, ?, ?,?)`, clientCompressedKey, crypto.CompressPubkey(info.ServerPublicKey), info.InstallationID, info.AccessToken, info.RetrievedAt, info.Version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Persistence) GetPushNotificationInfo(publicKey *ecdsa.PublicKey, installationIDs []string) ([]*PushNotificationInfo, error) {
|
||||
queryArgs := make([]interface{}, 0, len(installationIDs)+1)
|
||||
queryArgs = append(queryArgs, crypto.CompressPubkey(publicKey))
|
||||
for _, installationID := range installationIDs {
|
||||
queryArgs = append(queryArgs, installationID)
|
||||
}
|
||||
|
||||
inVector := strings.Repeat("?, ", len(installationIDs)-1) + "?"
|
||||
|
||||
rows, err := p.db.Query(`SELECT server_public_key, installation_id, version, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ? AND installation_id IN (`+inVector+`)`, queryArgs...) //nolint: gosec
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var infos []*PushNotificationInfo
|
||||
for rows.Next() {
|
||||
var serverPublicKeyBytes []byte
|
||||
info := &PushNotificationInfo{PublicKey: publicKey}
|
||||
err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.Version, &info.AccessToken, &info.RetrievedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info.ServerPublicKey = serverPublicKey
|
||||
infos = append(infos, info)
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) GetPushNotificationInfoByPublicKey(publicKey *ecdsa.PublicKey) ([]*PushNotificationInfo, error) {
|
||||
rows, err := p.db.Query(`SELECT server_public_key, installation_id, access_token, retrieved_at FROM push_notification_client_info WHERE public_key = ?`, crypto.CompressPubkey(publicKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var infos []*PushNotificationInfo
|
||||
for rows.Next() {
|
||||
var serverPublicKeyBytes []byte
|
||||
info := &PushNotificationInfo{PublicKey: publicKey}
|
||||
err := rows.Scan(&serverPublicKeyBytes, &info.InstallationID, &info.AccessToken, &info.RetrievedAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverPublicKey, err := crypto.DecompressPubkey(serverPublicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info.ServerPublicKey = serverPublicKey
|
||||
infos = append(infos, info)
|
||||
}
|
||||
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) ShouldSendNotificationFor(publicKey *ecdsa.PublicKey, installationID string, messageID []byte) (bool, error) {
|
||||
// First we check that we are tracking this message, next we check that we haven't already sent this
|
||||
var count uint64
|
||||
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? AND installation_id = ? `, messageID, crypto.CompressPubkey(publicKey), installationID).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count == 0, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) ShouldSendNotificationToAllInstallationIDs(publicKey *ecdsa.PublicKey, messageID []byte) (bool, error) {
|
||||
// First we check that we are tracking this message, next we check that we haven't already sent this
|
||||
var count uint64
|
||||
err := p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_tracked_messages WHERE message_id = ?`, messageID).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = p.db.QueryRow(`SELECT COUNT(1) FROM push_notification_client_sent_notifications WHERE message_id = ? AND public_key = ? `, messageID, crypto.CompressPubkey(publicKey)).Scan(&count)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return count == 0, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) UpsertSentNotification(n *SentNotification) error {
|
||||
_, err := p.db.Exec(`INSERT INTO push_notification_client_sent_notifications (public_key, installation_id, message_id, last_tried_at, retry_count, success, error, hashed_public_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, crypto.CompressPubkey(n.PublicKey), n.InstallationID, n.MessageID, n.LastTriedAt, n.RetryCount, n.Success, n.Error, n.HashedPublicKey())
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) GetSentNotification(hashedPublicKey []byte, installationID string, messageID []byte) (*SentNotification, error) {
|
||||
var publicKeyBytes []byte
|
||||
sentNotification := &SentNotification{
|
||||
InstallationID: installationID,
|
||||
MessageID: messageID,
|
||||
}
|
||||
err := p.db.QueryRow(`SELECT retry_count, last_tried_at, error, success, public_key FROM push_notification_client_sent_notifications WHERE hashed_public_key = ?`, hashedPublicKey).Scan(&sentNotification.RetryCount, &sentNotification.LastTriedAt, &sentNotification.Error, &sentNotification.Success, &publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sentNotification.PublicKey = publicKey
|
||||
|
||||
return sentNotification, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) UpdateNotificationResponse(messageID []byte, response *protobuf.PushNotificationReport) error {
|
||||
_, err := p.db.Exec(`UPDATE push_notification_client_sent_notifications SET success = ?, error = ? WHERE hashed_public_key = ? AND installation_id = ? AND message_id = ? AND NOT success`, response.Success, response.Error, response.PublicKey, response.InstallationId, messageID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *Persistence) GetRetriablePushNotifications() ([]*SentNotification, error) {
|
||||
var notifications []*SentNotification
|
||||
rows, err := p.db.Query(`SELECT retry_count, last_tried_at, error, success, public_key, installation_id, message_id FROM push_notification_client_sent_notifications WHERE NOT success AND error = ?`, protobuf.PushNotificationReport_WRONG_TOKEN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var publicKeyBytes []byte
|
||||
notification := &SentNotification{}
|
||||
err = rows.Scan(¬ification.RetryCount, ¬ification.LastTriedAt, ¬ification.Error, ¬ification.Success, &publicKeyBytes, ¬ification.InstallationID, ¬ification.MessageID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicKey, err := crypto.DecompressPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
notification.PublicKey = publicKey
|
||||
notifications = append(notifications, notification)
|
||||
}
|
||||
return notifications, err
|
||||
}
|
||||
|
||||
func (p *Persistence) UpsertServer(server *PushNotificationServer) error {
|
||||
_, err := p.db.Exec(`INSERT INTO push_notification_client_servers (public_key, registered, registered_at, access_token, last_retried_at, retry_count) VALUES (?,?,?,?,?,?)`, crypto.CompressPubkey(server.PublicKey), server.Registered, server.RegisteredAt, server.AccessToken, server.LastRetriedAt, server.RetryCount)
|
||||
return err
|
||||
|
||||
}
|
||||
|
||||
func (p *Persistence) GetServers() ([]*PushNotificationServer, error) {
|
||||
rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token,last_retried_at, retry_count FROM push_notification_client_servers`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var servers []*PushNotificationServer
|
||||
for rows.Next() {
|
||||
server := &PushNotificationServer{}
|
||||
var key []byte
|
||||
err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken, &server.LastRetriedAt, &server.RetryCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedKey, err := crypto.DecompressPubkey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.PublicKey = parsedKey
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
func (p *Persistence) GetServersByPublicKey(keys []*ecdsa.PublicKey) ([]*PushNotificationServer, error) {
|
||||
|
||||
keyArgs := make([]interface{}, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
keyArgs = append(keyArgs, crypto.CompressPubkey(key))
|
||||
}
|
||||
|
||||
inVector := strings.Repeat("?, ", len(keys)-1) + "?"
|
||||
rows, err := p.db.Query(`SELECT public_key, registered, registered_at,access_token FROM push_notification_client_servers WHERE public_key IN (`+inVector+")", keyArgs...) //nolint: gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var servers []*PushNotificationServer
|
||||
for rows.Next() {
|
||||
server := &PushNotificationServer{}
|
||||
var key []byte
|
||||
err := rows.Scan(&key, &server.Registered, &server.RegisteredAt, &server.AccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parsedKey, err := crypto.DecompressPubkey(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server.PublicKey = parsedKey
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers, nil
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
package pushnotificationclient
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
)
|
||||
|
||||
const (
|
||||
testAccessToken = "token"
|
||||
installationID1 = "installation-id-1"
|
||||
installationID2 = "installation-id-2"
|
||||
installationID3 = "installation-id-3"
|
||||
)
|
||||
|
||||
func TestSQLitePersistenceSuite(t *testing.T) {
|
||||
suite.Run(t, new(SQLitePersistenceSuite))
|
||||
}
|
||||
|
||||
type SQLitePersistenceSuite struct {
|
||||
suite.Suite
|
||||
tmpFile *os.File
|
||||
persistence *Persistence
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) SetupTest() {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
s.tmpFile = tmpFile
|
||||
|
||||
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||
s.Require().NoError(err)
|
||||
s.persistence = NewPersistence(database)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TearDownTest() {
|
||||
_ = os.Remove(s.tmpFile.Name())
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveServer() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
server := &PushNotificationServer{
|
||||
PublicKey: &key.PublicKey,
|
||||
Registered: true,
|
||||
RegisteredAt: 1,
|
||||
AccessToken: testAccessToken,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.UpsertServer(server))
|
||||
|
||||
retrievedServers, err := s.persistence.GetServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Len(retrievedServers, 1)
|
||||
s.Require().True(retrievedServers[0].Registered)
|
||||
s.Require().Equal(int64(1), retrievedServers[0].RegisteredAt)
|
||||
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
|
||||
s.Require().Equal(testAccessToken, retrievedServers[0].AccessToken)
|
||||
|
||||
server.Registered = false
|
||||
server.RegisteredAt = 2
|
||||
|
||||
s.Require().NoError(s.persistence.UpsertServer(server))
|
||||
|
||||
retrievedServers, err = s.persistence.GetServers()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Len(retrievedServers, 1)
|
||||
s.Require().False(retrievedServers[0].Registered)
|
||||
s.Require().Equal(int64(2), retrievedServers[0].RegisteredAt)
|
||||
s.Require().True(common.IsPubKeyEqual(retrievedServers[0].PublicKey, &key.PublicKey))
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfo() {
|
||||
key1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
key2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
serverKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
infos := []*PushNotificationInfo{
|
||||
{
|
||||
PublicKey: &key1.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID1,
|
||||
},
|
||||
{
|
||||
PublicKey: &key1.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID2,
|
||||
},
|
||||
{
|
||||
PublicKey: &key1.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID3,
|
||||
},
|
||||
{
|
||||
PublicKey: &key2.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID1,
|
||||
},
|
||||
{
|
||||
PublicKey: &key2.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID2,
|
||||
},
|
||||
{
|
||||
PublicKey: &key2.PublicKey,
|
||||
ServerPublicKey: &serverKey.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID3,
|
||||
},
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||
|
||||
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key1.PublicKey, []string{installationID1, installationID2})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Len(retrievedInfos, 2)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveInfoWithVersion() {
|
||||
installationID := "installation-id-1"
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
serverKey1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
serverKey2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
infos := []*PushNotificationInfo{
|
||||
{
|
||||
PublicKey: &key.PublicKey,
|
||||
ServerPublicKey: &serverKey1.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID,
|
||||
},
|
||||
{
|
||||
PublicKey: &key.PublicKey,
|
||||
ServerPublicKey: &serverKey2.PublicKey,
|
||||
RetrievedAt: 1,
|
||||
Version: 1,
|
||||
AccessToken: testAccessToken,
|
||||
InstallationID: installationID,
|
||||
},
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||
|
||||
retrievedInfos, err := s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We should retrieve both
|
||||
s.Require().Len(retrievedInfos, 2)
|
||||
s.Require().Equal(uint64(1), retrievedInfos[0].Version)
|
||||
|
||||
// Bump version
|
||||
infos[0].Version = 2
|
||||
|
||||
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||
|
||||
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Only one should be retrieved now
|
||||
s.Require().Len(retrievedInfos, 1)
|
||||
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
|
||||
|
||||
// Lower version
|
||||
infos[0].Version = 1
|
||||
|
||||
s.Require().NoError(s.persistence.SavePushNotificationInfo(infos))
|
||||
|
||||
retrievedInfos, err = s.persistence.GetPushNotificationInfo(&key.PublicKey, []string{installationID})
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Len(retrievedInfos, 1)
|
||||
s.Require().Equal(uint64(2), retrievedInfos[0].Version)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestNotifiedOnAndUpdateNotificationResponse() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
installationID := "installation-id"
|
||||
messageID := []byte("message-id")
|
||||
|
||||
sentNotification := &SentNotification{
|
||||
PublicKey: &key.PublicKey,
|
||||
InstallationID: installationID,
|
||||
MessageID: messageID,
|
||||
LastTriedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.UpsertSentNotification(sentNotification))
|
||||
|
||||
retrievedNotification, err := s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(sentNotification, retrievedNotification)
|
||||
|
||||
retriableNotifications, err := s.persistence.GetRetriablePushNotifications()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(retriableNotifications, 0)
|
||||
|
||||
response := &protobuf.PushNotificationReport{
|
||||
Success: false,
|
||||
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
|
||||
PublicKey: sentNotification.HashedPublicKey(),
|
||||
InstallationId: installationID,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||
// This notification should be retriable
|
||||
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(retriableNotifications, 1)
|
||||
|
||||
sentNotification.Error = protobuf.PushNotificationReport_WRONG_TOKEN
|
||||
|
||||
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(sentNotification, retrievedNotification)
|
||||
|
||||
// Update with a successful notification
|
||||
response = &protobuf.PushNotificationReport{
|
||||
Success: true,
|
||||
PublicKey: sentNotification.HashedPublicKey(),
|
||||
InstallationId: installationID,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||
|
||||
sentNotification.Success = true
|
||||
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||
|
||||
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(sentNotification, retrievedNotification)
|
||||
|
||||
// This notification should not be retriable
|
||||
retriableNotifications, err = s.persistence.GetRetriablePushNotifications()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Len(retriableNotifications, 0)
|
||||
|
||||
// Update with a unsuccessful notification, it should be ignored
|
||||
response = &protobuf.PushNotificationReport{
|
||||
Success: false,
|
||||
Error: protobuf.PushNotificationReport_WRONG_TOKEN,
|
||||
PublicKey: sentNotification.HashedPublicKey(),
|
||||
InstallationId: installationID,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.UpdateNotificationResponse(messageID, response))
|
||||
|
||||
sentNotification.Success = true
|
||||
sentNotification.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||
|
||||
retrievedNotification, err = s.persistence.GetSentNotification(sentNotification.HashedPublicKey(), installationID, messageID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(sentNotification, retrievedNotification)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveRegistration() {
|
||||
// Try with nil first
|
||||
retrievedRegistration, retrievedContactIDs, err := s.persistence.GetLastPushNotificationRegistration()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Nil(retrievedRegistration)
|
||||
s.Require().Nil(retrievedContactIDs)
|
||||
|
||||
// Save & retrieve registration
|
||||
registration := &protobuf.PushNotificationRegistration{
|
||||
AccessToken: "test",
|
||||
Version: 3,
|
||||
}
|
||||
|
||||
key1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
key2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
key3, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
publicKeys := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey}
|
||||
|
||||
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
|
||||
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||
s.Require().Equal(publicKeys, retrievedContactIDs)
|
||||
|
||||
// Override and retrieve
|
||||
|
||||
registration.Version = 5
|
||||
publicKeys = append(publicKeys, &key3.PublicKey)
|
||||
s.Require().NoError(s.persistence.SaveLastPushNotificationRegistration(registration, publicKeys))
|
||||
retrievedRegistration, retrievedContactIDs, err = s.persistence.GetLastPushNotificationRegistration()
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||
s.Require().Equal(publicKeys, retrievedContactIDs)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrInvalidPushNotificationRegistrationVersion = errors.New("invalid version")
|
||||
var ErrEmptyPushNotificationRegistrationPayload = errors.New("empty payload")
|
||||
var ErrMalformedPushNotificationRegistrationInstallationID = errors.New("invalid installationID")
|
||||
var ErrEmptyPushNotificationRegistrationPublicKey = errors.New("no public key")
|
||||
var ErrCouldNotUnmarshalPushNotificationRegistration = errors.New("could not unmarshal preferences")
|
||||
var ErrMalformedPushNotificationRegistrationDeviceToken = errors.New("invalid device token")
|
||||
var ErrMalformedPushNotificationRegistrationGrant = errors.New("invalid grant")
|
||||
var ErrMalformedPushNotificationRegistrationAccessToken = errors.New("invalid access token")
|
||||
var ErrUnknownPushNotificationRegistrationTokenType = errors.New("invalid token type")
|
|
@ -0,0 +1,77 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
const defaultNotificationMessage = "You have a new message"
|
||||
|
||||
type GoRushRequestData struct {
|
||||
EncryptedMessage string `json:"encryptedMessage"`
|
||||
ChatID string `json:"chatId"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
}
|
||||
|
||||
type GoRushRequestNotification struct {
|
||||
Tokens []string `json:"tokens"`
|
||||
Platform uint `json:"platform"`
|
||||
Message string `json:"message"`
|
||||
Data *GoRushRequestData `json:"data"`
|
||||
}
|
||||
|
||||
type GoRushRequest struct {
|
||||
Notifications []*GoRushRequestNotification `json:"notifications"`
|
||||
}
|
||||
|
||||
type RequestAndRegistration struct {
|
||||
Request *protobuf.PushNotification
|
||||
Registration *protobuf.PushNotificationRegistration
|
||||
}
|
||||
|
||||
func tokenTypeToGoRushPlatform(tokenType protobuf.PushNotificationRegistration_TokenType) uint {
|
||||
switch tokenType {
|
||||
case protobuf.PushNotificationRegistration_APN_TOKEN:
|
||||
return 1
|
||||
case protobuf.PushNotificationRegistration_FIREBASE_TOKEN:
|
||||
return 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func PushNotificationRegistrationToGoRushRequest(requestAndRegistrations []*RequestAndRegistration) *GoRushRequest {
|
||||
goRushRequests := &GoRushRequest{}
|
||||
for _, requestAndRegistration := range requestAndRegistrations {
|
||||
request := requestAndRegistration.Request
|
||||
registration := requestAndRegistration.Registration
|
||||
goRushRequests.Notifications = append(goRushRequests.Notifications,
|
||||
&GoRushRequestNotification{
|
||||
Tokens: []string{registration.DeviceToken},
|
||||
Platform: tokenTypeToGoRushPlatform(registration.TokenType),
|
||||
Message: defaultNotificationMessage,
|
||||
Data: &GoRushRequestData{
|
||||
EncryptedMessage: hex.EncodeToString(request.Message),
|
||||
ChatID: request.ChatId,
|
||||
PublicKey: hex.EncodeToString(request.PublicKey),
|
||||
},
|
||||
})
|
||||
}
|
||||
return goRushRequests
|
||||
}
|
||||
|
||||
func sendGoRushNotification(request *GoRushRequest, url string) error {
|
||||
payload, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = http.Post(url+"/api/push", "application/json", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
func TestPushNotificationRegistrationToGoRushRequest(t *testing.T) {
|
||||
message1 := []byte("message-1")
|
||||
message2 := []byte("message-2")
|
||||
message3 := []byte("message-3")
|
||||
hexMessage1 := hex.EncodeToString(message1)
|
||||
hexMessage2 := hex.EncodeToString(message2)
|
||||
hexMessage3 := hex.EncodeToString(message3)
|
||||
chatID := "chat-id"
|
||||
publicKey1 := []byte("public-key-1")
|
||||
publicKey2 := []byte("public-key-2")
|
||||
installationID1 := "installation-id-1"
|
||||
installationID2 := "installation-id-2"
|
||||
installationID3 := "installation-id-3"
|
||||
var platform1 uint = 1
|
||||
var platform2 uint = 2
|
||||
var platform3 uint = 2
|
||||
token1 := "token-1"
|
||||
token2 := "token-2"
|
||||
token3 := "token-3"
|
||||
|
||||
requestAndRegistrations := []*RequestAndRegistration{
|
||||
{
|
||||
Request: &protobuf.PushNotification{
|
||||
ChatId: chatID,
|
||||
PublicKey: publicKey1,
|
||||
InstallationId: installationID1,
|
||||
Message: message1,
|
||||
},
|
||||
Registration: &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: token1,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: &protobuf.PushNotification{
|
||||
ChatId: chatID,
|
||||
PublicKey: publicKey1,
|
||||
InstallationId: installationID2,
|
||||
Message: message2,
|
||||
},
|
||||
Registration: &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: token2,
|
||||
TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN,
|
||||
},
|
||||
},
|
||||
{
|
||||
Request: &protobuf.PushNotification{
|
||||
ChatId: chatID,
|
||||
PublicKey: publicKey2,
|
||||
InstallationId: installationID3,
|
||||
Message: message3,
|
||||
},
|
||||
Registration: &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: token3,
|
||||
TokenType: protobuf.PushNotificationRegistration_FIREBASE_TOKEN,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedRequests := &GoRushRequest{
|
||||
Notifications: []*GoRushRequestNotification{
|
||||
{
|
||||
Tokens: []string{token1},
|
||||
Platform: platform1,
|
||||
Message: defaultNotificationMessage,
|
||||
Data: &GoRushRequestData{
|
||||
EncryptedMessage: hexMessage1,
|
||||
ChatID: chatID,
|
||||
PublicKey: hex.EncodeToString(publicKey1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Tokens: []string{token2},
|
||||
Platform: platform2,
|
||||
Message: defaultNotificationMessage,
|
||||
Data: &GoRushRequestData{
|
||||
EncryptedMessage: hexMessage2,
|
||||
ChatID: chatID,
|
||||
PublicKey: hex.EncodeToString(publicKey1),
|
||||
},
|
||||
},
|
||||
{
|
||||
Tokens: []string{token3},
|
||||
Platform: platform3,
|
||||
Message: defaultNotificationMessage,
|
||||
Data: &GoRushRequestData{
|
||||
EncryptedMessage: hexMessage3,
|
||||
ChatID: chatID,
|
||||
PublicKey: hex.EncodeToString(publicKey2),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
actualRequests := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
|
||||
require.Equal(t, expectedRequests, actualRequests)
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
// Code generated by go-bindata. DO NOT EDIT.
|
||||
// sources:
|
||||
// 1593601728_initial_schema.down.sql (200B)
|
||||
// 1593601728_initial_schema.up.sql (675B)
|
||||
// doc.go (382B)
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
digest [sha256.Size]byte
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var __1593601728_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x28\x2d\xce\x88\xcf\xcb\x2f\xc9\x4c\xcb\x4c\x4e\x2c\xc9\xcc\xcf\x8b\x2f\x4e\x2d\x2a\x4b\x2d\x8a\x2f\x4a\x4d\xcf\x2c\x2e\x29\x02\x8b\x15\x5b\x73\x81\xb5\x78\xfa\xb9\xb8\x46\x28\x64\xa6\x54\xc4\x13\xa7\x2d\xbe\xa0\x34\x29\x27\x33\x39\x3e\x3b\xb5\x92\x72\x13\xe2\x33\xf3\x8a\x4b\x12\x73\x72\x20\x8a\x33\x53\xac\xb9\xb8\x00\x01\x00\x00\xff\xff\x90\x39\xe0\x1c\xc8\x00\x00\x00")
|
||||
|
||||
func _1593601728_initial_schemaDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593601728_initial_schemaDownSql,
|
||||
"1593601728_initial_schema.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593601728_initial_schemaDownSql() (*asset, error) {
|
||||
bytes, err := _1593601728_initial_schemaDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1593601728_initial_schema.down.sql", size: 200, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x8a, 0x61, 0x81, 0x57, 0x45, 0x9b, 0x97, 0x9b, 0x1f, 0xf6, 0x94, 0x8a, 0x20, 0xb3, 0x2b, 0xff, 0x69, 0x49, 0xf4, 0x58, 0xcc, 0xd0, 0x55, 0xcc, 0x9a, 0x8b, 0xb6, 0x7f, 0x29, 0x53, 0xc1}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1593601728_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x91\x31\x6b\xc3\x30\x14\x84\x77\xfd\x8a\x37\xc6\x90\xa1\xbb\x27\xd9\x91\xa9\x40\x48\xad\x23\x97\x6c\xc2\xb5\xd5\xe6\x51\x23\x07\x49\x31\xf5\xbf\x2f\x71\x86\x2a\x69\x87\x10\xb2\x1e\x8f\xbb\xf7\xdd\x95\x35\xa3\x9a\x81\xa6\x85\x60\xc0\x2b\x90\x4a\x03\xdb\xf1\xad\xde\xc2\xe1\x18\xf6\xc6\x8d\x11\x3f\xb0\x6b\x23\x8e\xce\x04\xeb\x27\xeb\x8d\xb7\x9f\x18\xa2\x5f\xb4\x00\x2b\x02\x70\x38\xbe\x0f\xd8\x99\x2f\x3b\x43\x21\x54\xb1\xb8\xc8\x46\x88\x35\x01\x40\x17\x62\x3b\x0c\x67\x07\xec\xe1\x8d\xd6\xe5\x33\xad\x2f\x6e\x26\xeb\x03\x8e\x0e\xb8\xd4\x17\x7a\x9a\xb4\x38\x9f\xc4\x46\xf2\xd7\x86\xad\x7e\x33\xd7\xd7\x19\x19\x28\x09\xa5\x92\x95\xe0\xa5\x86\x9a\xbd\x08\x5a\x32\x92\xe5\x84\xdc\x83\x8b\xbd\x75\x11\xe3\x7c\x26\xf5\x38\xb5\xd1\xfe\x8f\x1a\x66\x17\xf7\x36\x62\x77\xe2\x4c\x59\x60\xc3\x2a\xda\x08\x0d\x4f\x09\x40\x7a\x9d\xa5\xdf\x71\xb9\x61\x3b\xc0\xfe\xdb\xdc\x36\x81\x49\xea\x57\xf2\xc6\xdd\x92\xfe\xb2\xfc\x01\xc9\xe6\x7a\xe7\x7b\x3e\xf9\xbb\x64\x4e\xc8\x4f\x00\x00\x00\xff\xff\xcc\xa0\x4d\x54\xa3\x02\x00\x00")
|
||||
|
||||
func _1593601728_initial_schemaUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1593601728_initial_schemaUpSql,
|
||||
"1593601728_initial_schema.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1593601728_initial_schemaUpSql() (*asset, error) {
|
||||
bytes, err := _1593601728_initial_schemaUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1593601728_initial_schema.up.sql", size: 675, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x61, 0x90, 0x79, 0xd9, 0x14, 0x65, 0xe9, 0x96, 0x53, 0x17, 0x33, 0x54, 0xeb, 0x8b, 0x5d, 0x95, 0x99, 0x10, 0x36, 0x58, 0xdd, 0xb2, 0xbf, 0x45, 0xd9, 0xbb, 0xc4, 0x92, 0xe, 0xce, 0x2}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\x3d\x6e\xec\x30\x0c\x84\x7b\x9d\x62\xb0\xcd\x36\xcf\x52\xf3\xaa\x74\x29\xd3\xe7\x02\x5c\x89\x96\x88\xb5\x24\x43\xa4\xf7\xe7\xf6\x81\x37\x01\xe2\x2e\xed\x87\xf9\x86\xc3\x10\xf0\x59\x44\x31\xcb\xc2\x10\x45\xe3\xc8\xaa\x34\x9e\xb8\x70\xa4\x4d\x19\xa7\x2c\x56\xb6\x8b\x8f\xbd\x06\x35\xb2\x4d\x27\xa9\xa1\x4a\x1e\x64\x1c\x6e\xff\x4f\x2e\x04\x44\x6a\x67\x43\xa1\x96\x16\x7e\x75\x29\xd4\x68\x98\xb4\x8c\xbb\x58\x01\x61\x1d\x3c\xcb\xc3\xe3\xdd\xb0\x30\xa9\xc1\x0a\xd9\x59\x61\x85\x11\x49\x79\xaf\x99\xfb\x40\xee\xd3\x45\x5a\x22\x23\xbf\xa3\x8f\xf9\x40\xf6\x85\x91\x96\x85\x13\xe6\xd1\xeb\xcb\x55\xaa\x8c\x24\x83\xa3\xf5\xf1\xfc\x07\x52\x65\x43\xa3\xca\xba\xfb\x85\x6e\x8c\xd6\x7f\xce\x83\x5a\xfa\xfb\x23\xdc\xfb\xb8\x2a\x48\xc1\x8f\x95\xa3\x71\xf2\xce\xad\x14\xaf\x94\x19\xdf\x39\xe9\x4d\x9d\x0b\x21\xf7\xb7\xcc\x8d\x77\xf3\xb8\x73\x5a\xaf\xf9\x90\xc4\xd4\xe1\x7d\xf8\x05\x3e\x77\xf8\xe0\xbe\x02\x00\x00\xff\xff\x4d\x1d\x5d\x50\x7e\x01\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_docGo,
|
||||
"doc.go",
|
||||
)
|
||||
}
|
||||
|
||||
func docGo() (*asset, error) {
|
||||
bytes, err := docGoBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 382, mode: os.FileMode(0644), modTime: time.Unix(1595832279, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc0, 0x2f, 0x1e, 0x64, 0x9, 0x93, 0xe4, 0x8b, 0xf2, 0x98, 0x5a, 0x45, 0xe2, 0x80, 0x88, 0x67, 0x7a, 0x2d, 0xd7, 0x4b, 0xd1, 0x73, 0xb6, 0x6d, 0x15, 0xc2, 0x0, 0x34, 0xcd, 0xa0, 0xdb, 0x20}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||
func AssetString(name string) (string, error) {
|
||||
data, err := Asset(name)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// MustAssetString is like AssetString but panics when Asset would return an
|
||||
// error. It simplifies safe initialization of global variables.
|
||||
func MustAssetString(name string) string {
|
||||
return string(MustAsset(name))
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||
// error if the asset could not be found or the digest could not be loaded.
|
||||
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.digest, nil
|
||||
}
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||
}
|
||||
|
||||
// Digests returns a map of all known files and their checksums.
|
||||
func Digests() (map[string][sha256.Size]byte, error) {
|
||||
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||
for name := range _bindata {
|
||||
a, err := _bindata[name]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp[name] = a.digest
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"1593601728_initial_schema.down.sql": _1593601728_initial_schemaDownSql,
|
||||
|
||||
"1593601728_initial_schema.up.sql": _1593601728_initial_schemaUpSql,
|
||||
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
// 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
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(canonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"1593601728_initial_schema.down.sql": &bintree{_1593601728_initial_schemaDownSql, map[string]*bintree{}},
|
||||
"1593601728_initial_schema.up.sql": &bintree{_1593601728_initial_schemaUpSql, map[string]*bintree{}},
|
||||
"doc.go": &bintree{docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively.
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
DROP TABLE push_notification_server_registrations;
|
||||
DROP INDEX idx_push_notification_server_registrations_public_key;
|
||||
DROP INDEX idx_push_notification_server_registrations_public_key_installation_id;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
CREATE TABLE IF NOT EXISTS push_notification_server_registrations (
|
||||
public_key BLOB NOT NULL,
|
||||
installation_id VARCHAR NOT NULL,
|
||||
version INT NOT NULL,
|
||||
registration BLOB,
|
||||
UNIQUE(public_key, installation_id) ON CONFLICT REPLACE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_notification_server_identity (
|
||||
private_key BLOB NOT NULL,
|
||||
synthetic_id INT NOT NULL DEFAULT 0,
|
||||
UNIQUE(synthetic_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_push_notification_server_registrations_public_key ON push_notification_server_registrations(public_key);
|
||||
CREATE INDEX idx_push_notification_server_registrations_public_key_installation_id ON push_notification_server_registrations(public_key, installation_id);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// This file is necessary because "github.com/status-im/migrate/v4"
|
||||
// can't handle files starting with a prefix. At least that's the case
|
||||
// for go-bindata.
|
||||
// If go-bindata is called from the same directory, asset names
|
||||
// have no prefix and "github.com/status-im/migrate/v4" works as expected.
|
||||
|
||||
package migrations
|
||||
|
||||
//go:generate go-bindata -pkg migrations -o ../migrations.go ./
|
|
@ -0,0 +1,154 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
type Persistence interface {
|
||||
// GetPushNotificationRegistrationByPublicKeyAndInstallationID retrieve a push notification registration from storage given a public key and installation id
|
||||
GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error)
|
||||
// GetPushNotificationRegistrationByPublicKey retrieve all the push notification registrations from storage given a public key
|
||||
GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error)
|
||||
//GetPushNotificationRegistrationPublicKeys return all the public keys stored
|
||||
GetPushNotificationRegistrationPublicKeys() ([][]byte, error)
|
||||
|
||||
// DeletePushNotificationRegistration deletes a push notification registration from storage given a public key and installation id
|
||||
DeletePushNotificationRegistration(publicKey []byte, installationID string) error
|
||||
// SavePushNotificationRegistration saves a push notification option to the db
|
||||
SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error
|
||||
// GetIdentity returns the server identity key
|
||||
GetIdentity() (*ecdsa.PrivateKey, error)
|
||||
// SaveIdentity saves the server identity key
|
||||
SaveIdentity(*ecdsa.PrivateKey) error
|
||||
}
|
||||
|
||||
type SQLitePersistence struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewSQLitePersistence(db *sql.DB) Persistence {
|
||||
return &SQLitePersistence{db: db}
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeyAndInstallationID(publicKey []byte, installationID string) (*protobuf.PushNotificationRegistration, error) {
|
||||
var marshaledRegistration []byte
|
||||
err := p.db.QueryRow(`SELECT registration FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID).Scan(&marshaledRegistration)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
|
||||
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registration, nil
|
||||
}
|
||||
|
||||
type PushNotificationIDAndRegistration struct {
|
||||
ID []byte
|
||||
Registration *protobuf.PushNotificationRegistration
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetPushNotificationRegistrationByPublicKeys(publicKeys [][]byte) ([]*PushNotificationIDAndRegistration, error) {
|
||||
// TODO: check for a max number of keys
|
||||
|
||||
publicKeyArgs := make([]interface{}, 0, len(publicKeys))
|
||||
for _, pk := range publicKeys {
|
||||
publicKeyArgs = append(publicKeyArgs, pk)
|
||||
}
|
||||
|
||||
inVector := strings.Repeat("?, ", len(publicKeys)-1) + "?"
|
||||
|
||||
rows, err := p.db.Query(`SELECT public_key,registration FROM push_notification_server_registrations WHERE public_key IN (`+inVector+`)`, publicKeyArgs...) // nolint: gosec
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var registrations []*PushNotificationIDAndRegistration
|
||||
for rows.Next() {
|
||||
response := &PushNotificationIDAndRegistration{}
|
||||
var marshaledRegistration []byte
|
||||
err := rows.Scan(&response.ID, &marshaledRegistration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
|
||||
if err := proto.Unmarshal(marshaledRegistration, registration); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Registration = registration
|
||||
registrations = append(registrations, response)
|
||||
}
|
||||
return registrations, nil
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetPushNotificationRegistrationPublicKeys() ([][]byte, error) {
|
||||
rows, err := p.db.Query(`SELECT public_key FROM push_notification_server_registrations`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var publicKeys [][]byte
|
||||
for rows.Next() {
|
||||
var publicKey []byte
|
||||
err := rows.Scan(&publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKeys = append(publicKeys, publicKey)
|
||||
}
|
||||
return publicKeys, nil
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) SavePushNotificationRegistration(publicKey []byte, registration *protobuf.PushNotificationRegistration) error {
|
||||
marshaledRegistration, err := proto.Marshal(registration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = p.db.Exec(`INSERT INTO push_notification_server_registrations (public_key, installation_id, version, registration) VALUES (?, ?, ?, ?)`, publicKey, registration.InstallationId, registration.Version, marshaledRegistration)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) DeletePushNotificationRegistration(publicKey []byte, installationID string) error {
|
||||
_, err := p.db.Exec(`DELETE FROM push_notification_server_registrations WHERE public_key = ? AND installation_id = ?`, publicKey, installationID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) SaveIdentity(privateKey *ecdsa.PrivateKey) error {
|
||||
_, err := p.db.Exec(`INSERT INTO push_notification_server_identity (private_key) VALUES (?)`, crypto.FromECDSA(privateKey))
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *SQLitePersistence) GetIdentity() (*ecdsa.PrivateKey, error) {
|
||||
var pkBytes []byte
|
||||
err := p.db.QueryRow(`SELECT private_key FROM push_notification_server_identity LIMIT 1`).Scan(&pkBytes)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk, err := crypto.ToECDSA(pkBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pk, nil
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
)
|
||||
|
||||
func TestSQLitePersistenceSuite(t *testing.T) {
|
||||
suite.Run(t, new(SQLitePersistenceSuite))
|
||||
}
|
||||
|
||||
type SQLitePersistenceSuite struct {
|
||||
suite.Suite
|
||||
tmpFile *os.File
|
||||
persistence Persistence
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) SetupTest() {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
s.tmpFile = tmpFile
|
||||
|
||||
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||
s.Require().NoError(err)
|
||||
s.persistence = NewSQLitePersistence(database)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TearDownTest() {
|
||||
_ = os.Remove(s.tmpFile.Name())
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieve() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
installationID := "54242d02-bb92-11ea-b3de-0242ac130004"
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{
|
||||
InstallationId: installationID,
|
||||
Version: 5,
|
||||
}
|
||||
|
||||
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&key.PublicKey), registration))
|
||||
|
||||
retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&key.PublicKey), installationID)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().True(proto.Equal(registration, retrievedRegistration))
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveAndRetrieveIdentity() {
|
||||
retrievedKey, err := s.persistence.GetIdentity()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Nil(retrievedKey)
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
s.Require().NoError(s.persistence.SaveIdentity(key))
|
||||
|
||||
retrievedKey, err = s.persistence.GetIdentity()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(key, retrievedKey)
|
||||
}
|
||||
|
||||
func (s *SQLitePersistenceSuite) TestSaveDifferentIdenities() {
|
||||
key1, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
key2, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// First one should be successul, second should fail
|
||||
s.Require().NoError(s.persistence.SaveIdentity(key1))
|
||||
s.Require().Error(s.persistence.SaveIdentity(key2))
|
||||
}
|
|
@ -0,0 +1,434 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
)
|
||||
|
||||
const encryptedPayloadKeyLength = 16
|
||||
const defaultGorushURL = "https://gorush.status.im"
|
||||
|
||||
type Config struct {
|
||||
// Identity is our identity key
|
||||
Identity *ecdsa.PrivateKey
|
||||
// GorushUrl is the url for the gorush service
|
||||
GorushURL string
|
||||
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
persistence Persistence
|
||||
config *Config
|
||||
messageProcessor *common.MessageProcessor
|
||||
}
|
||||
|
||||
func New(config *Config, persistence Persistence, messageProcessor *common.MessageProcessor) *Server {
|
||||
if len(config.GorushURL) == 0 {
|
||||
config.GorushURL = defaultGorushURL
|
||||
|
||||
}
|
||||
return &Server{persistence: persistence, config: config, messageProcessor: messageProcessor}
|
||||
}
|
||||
|
||||
func (s *Server) Start() error {
|
||||
s.config.Logger.Info("starting push notification server")
|
||||
if s.config.Identity == nil {
|
||||
s.config.Logger.Info("Identity nil")
|
||||
// Pull identity from database
|
||||
identity, err := s.persistence.GetIdentity()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if identity == nil {
|
||||
identity, err = crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.persistence.SaveIdentity(identity); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.config.Identity = identity
|
||||
}
|
||||
|
||||
pks, err := s.persistence.GetPushNotificationRegistrationPublicKeys()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// listen to all topics for users registered
|
||||
for _, pk := range pks {
|
||||
if err := s.listenToPublicKeyQueryTopic(pk); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.config.Logger.Info("started push notification server", zap.String("identity", types.EncodeHex(crypto.FromECDSAPub(&s.config.Identity.PublicKey))))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HandlePushNotificationRegistration builds a response for the registration and sends it back to the user
|
||||
func (s *Server) HandlePushNotificationRegistration(publicKey *ecdsa.PublicKey, payload []byte) error {
|
||||
response := s.buildPushNotificationRegistrationResponse(publicKey, payload)
|
||||
if response == nil {
|
||||
return nil
|
||||
}
|
||||
encodedMessage, err := proto.Marshal(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawMessage := &common.RawMessage{
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE,
|
||||
// we skip encryption as might be sent from an ephemeral key
|
||||
SkipEncryption: true,
|
||||
}
|
||||
|
||||
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||
return err
|
||||
}
|
||||
|
||||
// HandlePushNotificationQuery builds a response for the query and sends it back to the user
|
||||
func (s *Server) HandlePushNotificationQuery(publicKey *ecdsa.PublicKey, messageID []byte, query protobuf.PushNotificationQuery) error {
|
||||
response := s.buildPushNotificationQueryResponse(&query)
|
||||
if response == nil {
|
||||
return nil
|
||||
}
|
||||
response.MessageId = messageID
|
||||
encodedMessage, err := proto.Marshal(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawMessage := &common.RawMessage{
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE,
|
||||
// we skip encryption as sent from an ephemeral key
|
||||
SkipEncryption: true,
|
||||
}
|
||||
|
||||
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||
return err
|
||||
}
|
||||
|
||||
// HandlePushNotificationRequest will send a gorush notification and send a response back to the user
|
||||
func (s *Server) HandlePushNotificationRequest(publicKey *ecdsa.PublicKey,
|
||||
request protobuf.PushNotificationRequest) error {
|
||||
s.config.Logger.Debug("handling pn request")
|
||||
response := s.buildPushNotificationRequestResponseAndSendNotification(&request)
|
||||
if response == nil {
|
||||
return nil
|
||||
}
|
||||
encodedMessage, err := proto.Marshal(response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rawMessage := &common.RawMessage{
|
||||
Payload: encodedMessage,
|
||||
MessageType: protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE,
|
||||
// We skip encryption here as the message has been sent from an ephemeral key
|
||||
SkipEncryption: true,
|
||||
}
|
||||
|
||||
_, err = s.messageProcessor.SendPrivate(context.Background(), publicKey, rawMessage)
|
||||
return err
|
||||
}
|
||||
|
||||
// buildGrantSignatureMaterial builds a grant for a specific server.
|
||||
// We use 3 components:
|
||||
// 1) The client public key. Not sure this applies to our signature scheme, but best to be conservative. https://crypto.stackexchange.com/questions/15538/given-a-message-and-signature-find-a-public-key-that-makes-the-signature-valid
|
||||
// 2) The server public key
|
||||
// 3) The access token
|
||||
// By verifying this signature, a client can trust the server was instructed to store this access token.
|
||||
func (s *Server) buildGrantSignatureMaterial(clientPublicKey *ecdsa.PublicKey, serverPublicKey *ecdsa.PublicKey, accessToken string) []byte {
|
||||
var signatureMaterial []byte
|
||||
signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(clientPublicKey)...)
|
||||
signatureMaterial = append(signatureMaterial, crypto.CompressPubkey(serverPublicKey)...)
|
||||
signatureMaterial = append(signatureMaterial, []byte(accessToken)...)
|
||||
a := crypto.Keccak256(signatureMaterial)
|
||||
return a
|
||||
}
|
||||
|
||||
func (s *Server) verifyGrantSignature(clientPublicKey *ecdsa.PublicKey, accessToken string, grant []byte) error {
|
||||
signatureMaterial := s.buildGrantSignatureMaterial(clientPublicKey, &s.config.Identity.PublicKey, accessToken)
|
||||
recoveredPublicKey, err := crypto.SigToPub(signatureMaterial, grant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !common.IsPubKeyEqual(recoveredPublicKey, clientPublicKey) {
|
||||
return errors.New("pubkey mismatch")
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) generateSharedKey(publicKey *ecdsa.PublicKey) ([]byte, error) {
|
||||
return ecies.ImportECDSA(s.config.Identity).GenerateShared(
|
||||
ecies.ImportECDSAPublic(publicKey),
|
||||
encryptedPayloadKeyLength,
|
||||
encryptedPayloadKeyLength,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *Server) validateUUID(u string) error {
|
||||
if len(u) == 0 {
|
||||
return errors.New("empty uuid")
|
||||
}
|
||||
_, err := uuid.Parse(u)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) decryptRegistration(publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||
sharedKey, err := s.generateSharedKey(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return common.Decrypt(payload, sharedKey)
|
||||
}
|
||||
|
||||
// validateRegistration validates a new message against the last one received for a given installationID and and public key
|
||||
// and return the decrypted message
|
||||
func (s *Server) validateRegistration(publicKey *ecdsa.PublicKey, payload []byte) (*protobuf.PushNotificationRegistration, error) {
|
||||
if payload == nil {
|
||||
return nil, ErrEmptyPushNotificationRegistrationPayload
|
||||
}
|
||||
|
||||
if publicKey == nil {
|
||||
return nil, ErrEmptyPushNotificationRegistrationPublicKey
|
||||
}
|
||||
|
||||
decryptedPayload, err := s.decryptRegistration(publicKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{}
|
||||
|
||||
if err := proto.Unmarshal(decryptedPayload, registration); err != nil {
|
||||
return nil, ErrCouldNotUnmarshalPushNotificationRegistration
|
||||
}
|
||||
|
||||
if registration.Version < 1 {
|
||||
return nil, ErrInvalidPushNotificationRegistrationVersion
|
||||
}
|
||||
|
||||
if err := s.validateUUID(registration.InstallationId); err != nil {
|
||||
return nil, ErrMalformedPushNotificationRegistrationInstallationID
|
||||
}
|
||||
|
||||
previousRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(publicKey), registration.InstallationId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if previousRegistration != nil && registration.Version <= previousRegistration.Version {
|
||||
return nil, ErrInvalidPushNotificationRegistrationVersion
|
||||
}
|
||||
|
||||
// unregistering message
|
||||
if registration.Unregister {
|
||||
return registration, nil
|
||||
}
|
||||
|
||||
if err := s.validateUUID(registration.AccessToken); err != nil {
|
||||
return nil, ErrMalformedPushNotificationRegistrationAccessToken
|
||||
}
|
||||
|
||||
if len(registration.Grant) == 0 {
|
||||
return nil, ErrMalformedPushNotificationRegistrationGrant
|
||||
}
|
||||
|
||||
if err := s.verifyGrantSignature(publicKey, registration.AccessToken, registration.Grant); err != nil {
|
||||
|
||||
s.config.Logger.Error("failed to verify grant", zap.Error(err))
|
||||
return nil, ErrMalformedPushNotificationRegistrationGrant
|
||||
}
|
||||
|
||||
if len(registration.DeviceToken) == 0 {
|
||||
return nil, ErrMalformedPushNotificationRegistrationDeviceToken
|
||||
}
|
||||
|
||||
if registration.TokenType == protobuf.PushNotificationRegistration_UNKNOWN_TOKEN_TYPE {
|
||||
return nil, ErrUnknownPushNotificationRegistrationTokenType
|
||||
}
|
||||
|
||||
return registration, nil
|
||||
}
|
||||
|
||||
// buildPushNotificationQueryResponse check if we have the client information and send them back
|
||||
func (s *Server) buildPushNotificationQueryResponse(query *protobuf.PushNotificationQuery) *protobuf.PushNotificationQueryResponse {
|
||||
|
||||
s.config.Logger.Debug("handling push notification query")
|
||||
response := &protobuf.PushNotificationQueryResponse{}
|
||||
if query == nil || len(query.PublicKeys) == 0 {
|
||||
return response
|
||||
}
|
||||
|
||||
registrations, err := s.persistence.GetPushNotificationRegistrationByPublicKeys(query.PublicKeys)
|
||||
if err != nil {
|
||||
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
|
||||
return response
|
||||
}
|
||||
|
||||
for _, idAndResponse := range registrations {
|
||||
|
||||
registration := idAndResponse.Registration
|
||||
info := &protobuf.PushNotificationQueryInfo{
|
||||
PublicKey: idAndResponse.ID,
|
||||
Grant: registration.Grant,
|
||||
Version: registration.Version,
|
||||
InstallationId: registration.InstallationId,
|
||||
}
|
||||
|
||||
// if instructed to only allow from contacts, send back a list
|
||||
if registration.AllowFromContactsOnly {
|
||||
info.AllowedKeyList = registration.AllowedKeyList
|
||||
} else {
|
||||
info.AccessToken = registration.AccessToken
|
||||
}
|
||||
response.Info = append(response.Info, info)
|
||||
}
|
||||
|
||||
response.Success = true
|
||||
return response
|
||||
}
|
||||
|
||||
// buildPushNotificationRequestResponseAndSendNotification will build a response
|
||||
// and fire-and-forget send a query to the gorush instance
|
||||
func (s *Server) buildPushNotificationRequestResponseAndSendNotification(request *protobuf.PushNotificationRequest) *protobuf.PushNotificationResponse {
|
||||
response := &protobuf.PushNotificationResponse{}
|
||||
// We don't even send a response in this case
|
||||
if request == nil || len(request.MessageId) == 0 {
|
||||
s.config.Logger.Warn("empty message id")
|
||||
return nil
|
||||
}
|
||||
|
||||
response.MessageId = request.MessageId
|
||||
|
||||
// TODO: filter by chat id
|
||||
// collect successful requests & registrations
|
||||
var requestAndRegistrations []*RequestAndRegistration
|
||||
|
||||
for _, pn := range request.Requests {
|
||||
registration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(pn.PublicKey, pn.InstallationId)
|
||||
report := &protobuf.PushNotificationReport{
|
||||
PublicKey: pn.PublicKey,
|
||||
InstallationId: pn.InstallationId,
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.config.Logger.Error("failed to retrieve registration", zap.Error(err))
|
||||
report.Error = protobuf.PushNotificationReport_UNKNOWN_ERROR_TYPE
|
||||
} else if registration == nil {
|
||||
s.config.Logger.Warn("empty registration")
|
||||
report.Error = protobuf.PushNotificationReport_NOT_REGISTERED
|
||||
} else if registration.AccessToken != pn.AccessToken {
|
||||
s.config.Logger.Warn("invalid access token")
|
||||
report.Error = protobuf.PushNotificationReport_WRONG_TOKEN
|
||||
} else {
|
||||
// For now we just assume that the notification will be successful
|
||||
requestAndRegistrations = append(requestAndRegistrations, &RequestAndRegistration{
|
||||
Request: pn,
|
||||
Registration: registration,
|
||||
})
|
||||
report.Success = true
|
||||
}
|
||||
|
||||
response.Reports = append(response.Reports, report)
|
||||
}
|
||||
|
||||
s.config.Logger.Debug("built pn request")
|
||||
if len(requestAndRegistrations) == 0 {
|
||||
s.config.Logger.Warn("no request and registration")
|
||||
return response
|
||||
}
|
||||
|
||||
// This can be done asynchronously
|
||||
goRushRequest := PushNotificationRegistrationToGoRushRequest(requestAndRegistrations)
|
||||
err := sendGoRushNotification(goRushRequest, s.config.GorushURL)
|
||||
if err != nil {
|
||||
s.config.Logger.Error("failed to send go rush notification", zap.Error(err))
|
||||
// TODO: handle this error?
|
||||
// GoRush will not let us know that the sending of the push notification has failed,
|
||||
// so this likely mean that the actual HTTP request has failed, or there was some unexpected error
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// listenToPublicKeyQueryTopic listen to a topic derived from the hashed public key
|
||||
func (s *Server) listenToPublicKeyQueryTopic(hashedPublicKey []byte) error {
|
||||
if s.messageProcessor == nil {
|
||||
return nil
|
||||
}
|
||||
encodedPublicKey := hex.EncodeToString(hashedPublicKey)
|
||||
return s.messageProcessor.JoinPublic(encodedPublicKey)
|
||||
}
|
||||
|
||||
// buildPushNotificationRegistrationResponse will check the registration is valid, save it, and listen to the topic for the queries
|
||||
func (s *Server) buildPushNotificationRegistrationResponse(publicKey *ecdsa.PublicKey, payload []byte) *protobuf.PushNotificationRegistrationResponse {
|
||||
|
||||
s.config.Logger.Info("handling push notification registration")
|
||||
response := &protobuf.PushNotificationRegistrationResponse{
|
||||
RequestId: common.Shake256(payload),
|
||||
}
|
||||
|
||||
registration, err := s.validateRegistration(publicKey, payload)
|
||||
|
||||
if err != nil {
|
||||
if err == ErrInvalidPushNotificationRegistrationVersion {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH
|
||||
} else {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE
|
||||
}
|
||||
s.config.Logger.Warn("registration did not validate", zap.Error(err))
|
||||
return response
|
||||
}
|
||||
|
||||
if registration.Unregister {
|
||||
// We save an empty registration, only keeping version and installation-id
|
||||
emptyRegistration := &protobuf.PushNotificationRegistration{
|
||||
Version: registration.Version,
|
||||
InstallationId: registration.InstallationId,
|
||||
}
|
||||
if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), emptyRegistration); err != nil {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||
s.config.Logger.Error("failed to unregister ", zap.Error(err))
|
||||
return response
|
||||
}
|
||||
|
||||
} else if err := s.persistence.SavePushNotificationRegistration(common.HashPublicKey(publicKey), registration); err != nil {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||
s.config.Logger.Error("failed to save registration", zap.Error(err))
|
||||
return response
|
||||
}
|
||||
|
||||
if err := s.listenToPublicKeyQueryTopic(common.HashPublicKey(publicKey)); err != nil {
|
||||
response.Error = protobuf.PushNotificationRegistrationResponse_INTERNAL_ERROR
|
||||
s.config.Logger.Error("failed to listen to topic", zap.Error(err))
|
||||
return response
|
||||
|
||||
}
|
||||
response.Success = true
|
||||
|
||||
s.config.Logger.Info("handled push notification registration successfully")
|
||||
|
||||
return response
|
||||
}
|
|
@ -0,0 +1,584 @@
|
|||
package pushnotificationserver
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/protocol/common"
|
||||
"github.com/status-im/status-go/protocol/protobuf"
|
||||
"github.com/status-im/status-go/protocol/sqlite"
|
||||
"github.com/status-im/status-go/protocol/tt"
|
||||
)
|
||||
|
||||
func TestServerSuite(t *testing.T) {
|
||||
s := new(ServerSuite)
|
||||
s.accessToken = "b6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||
s.installationID = "c6ae4fde-bb65-11ea-b3de-0242ac130004"
|
||||
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
type ServerSuite struct {
|
||||
suite.Suite
|
||||
tmpFile *os.File
|
||||
persistence Persistence
|
||||
accessToken string
|
||||
installationID string
|
||||
identity *ecdsa.PrivateKey
|
||||
key *ecdsa.PrivateKey
|
||||
sharedKey []byte
|
||||
grant []byte
|
||||
server *Server
|
||||
}
|
||||
|
||||
func (s *ServerSuite) SetupTest() {
|
||||
tmpFile, err := ioutil.TempFile("", "")
|
||||
s.Require().NoError(err)
|
||||
s.tmpFile = tmpFile
|
||||
|
||||
database, err := sqlite.Open(s.tmpFile.Name(), "")
|
||||
s.Require().NoError(err)
|
||||
s.persistence = NewSQLitePersistence(database)
|
||||
|
||||
identity, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
s.identity = identity
|
||||
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
s.key = key
|
||||
|
||||
config := &Config{
|
||||
Identity: identity,
|
||||
Logger: tt.MustCreateTestLogger(),
|
||||
}
|
||||
|
||||
s.server = New(config, s.persistence, nil)
|
||||
|
||||
sharedKey, err := s.server.generateSharedKey(&s.key.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
s.sharedKey = sharedKey
|
||||
signatureMaterial := s.server.buildGrantSignatureMaterial(&s.key.PublicKey, &identity.PublicKey, s.accessToken)
|
||||
grant, err := crypto.Sign(signatureMaterial, s.key)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.grant = grant
|
||||
|
||||
}
|
||||
|
||||
func (s *ServerSuite) TestPushNotificationServerValidateRegistration() {
|
||||
|
||||
// Empty payload
|
||||
_, err := s.server.validateRegistration(&s.key.PublicKey, nil)
|
||||
s.Require().Equal(ErrEmptyPushNotificationRegistrationPayload, err)
|
||||
|
||||
// Empty key
|
||||
_, err = s.server.validateRegistration(nil, []byte("payload"))
|
||||
s.Require().Equal(ErrEmptyPushNotificationRegistrationPublicKey, err)
|
||||
|
||||
// Invalid cyphertext length
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short"))
|
||||
s.Require().Equal(common.ErrInvalidCiphertextLength, err)
|
||||
|
||||
// Invalid cyphertext length
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("too short"))
|
||||
s.Require().Equal(common.ErrInvalidCiphertextLength, err)
|
||||
|
||||
// Invalid ciphertext
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, []byte("not too short but invalid"))
|
||||
s.Require().Error(common.ErrInvalidCiphertextLength, err)
|
||||
|
||||
// Different key ciphertext
|
||||
cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Error(err)
|
||||
|
||||
// Right cyphertext but non unmarshable payload
|
||||
cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrCouldNotUnmarshalPushNotificationRegistration, err)
|
||||
|
||||
// Missing installationID
|
||||
payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err)
|
||||
|
||||
// Malformed installationID
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Grant: s.grant,
|
||||
InstallationId: "abc",
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationInstallationID, err)
|
||||
|
||||
// Version set to 0
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err)
|
||||
|
||||
// Version lower than previous one
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Setup persistence
|
||||
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
Version: 2}))
|
||||
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrInvalidPushNotificationRegistrationVersion, err)
|
||||
|
||||
// Cleanup persistence
|
||||
s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID))
|
||||
|
||||
// Unregistering message
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
InstallationId: s.installationID,
|
||||
Unregister: true,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Nil(err)
|
||||
|
||||
// Missing access token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
InstallationId: s.installationID,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err)
|
||||
|
||||
// Invalid access token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: "bc",
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationAccessToken, err)
|
||||
|
||||
// Missing device token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationDeviceToken, err)
|
||||
|
||||
// Missing grant
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
DeviceToken: "device-token",
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err)
|
||||
|
||||
// Invalid grant
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
DeviceToken: "device-token",
|
||||
Grant: crypto.Keccak256([]byte("invalid")),
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrMalformedPushNotificationRegistrationGrant, err)
|
||||
|
||||
// Missing token type
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
DeviceToken: "device-token",
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().Equal(ErrUnknownPushNotificationRegistrationTokenType, err)
|
||||
|
||||
// Successful
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
DeviceToken: "abc",
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
_, err = s.server.validateRegistration(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *ServerSuite) TestPushNotificationHandleRegistration() {
|
||||
// Empty payload
|
||||
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, nil)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Empty key
|
||||
response = s.server.buildPushNotificationRegistrationResponse(nil, []byte("payload"))
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Invalid cyphertext length
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short"))
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Invalid cyphertext length
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("too short"))
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Invalid ciphertext
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, []byte("not too short but invalid"))
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Different key ciphertext
|
||||
cyphertext, err := common.Encrypt([]byte("plaintext"), make([]byte, 32), rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Right cyphertext but non unmarshable payload
|
||||
cyphertext, err = common.Encrypt([]byte("plaintext"), s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Missing installationID
|
||||
payload, err := proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Malformed installationID
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
InstallationId: "abc",
|
||||
Grant: s.grant,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Version set to 0
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH)
|
||||
|
||||
// Version lower than previous one
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Setup persistence
|
||||
s.Require().NoError(s.persistence.SavePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), &protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 2}))
|
||||
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_VERSION_MISMATCH)
|
||||
|
||||
// Cleanup persistence
|
||||
s.Require().NoError(s.persistence.DeletePushNotificationRegistration(common.HashPublicKey(&s.key.PublicKey), s.installationID))
|
||||
|
||||
// Missing access token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
InstallationId: s.installationID,
|
||||
Grant: s.grant,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Invalid access token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: "bc",
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Missing device token
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().False(response.Success)
|
||||
s.Require().Equal(response.Error, protobuf.PushNotificationRegistrationResponse_MALFORMED_MESSAGE)
|
||||
|
||||
// Successful
|
||||
registration := &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: "abc",
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
}
|
||||
payload, err = proto.Marshal(registration)
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().True(response.Success)
|
||||
|
||||
// Pull from the db
|
||||
retrievedRegistration, err := s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedRegistration)
|
||||
s.Require().True(proto.Equal(retrievedRegistration, registration))
|
||||
|
||||
// Unregistering message
|
||||
payload, err = proto.Marshal(&protobuf.PushNotificationRegistration{
|
||||
DeviceToken: "token",
|
||||
InstallationId: s.installationID,
|
||||
Unregister: true,
|
||||
Version: 2,
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err = common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response = s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().True(response.Success)
|
||||
|
||||
// Check is gone from the db
|
||||
retrievedRegistration, err = s.persistence.GetPushNotificationRegistrationByPublicKeyAndInstallationID(common.HashPublicKey(&s.key.PublicKey), s.installationID)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(retrievedRegistration)
|
||||
s.Require().Empty(retrievedRegistration.AccessToken)
|
||||
s.Require().Empty(retrievedRegistration.DeviceToken)
|
||||
s.Require().Equal(uint64(2), retrievedRegistration.Version)
|
||||
s.Require().Equal(s.installationID, retrievedRegistration.InstallationId)
|
||||
s.Require().Equal(common.Shake256(cyphertext), response.RequestId)
|
||||
}
|
||||
|
||||
func (s *ServerSuite) TestbuildPushNotificationQueryResponseNoFiltering() {
|
||||
hashedPublicKey := common.HashPublicKey(&s.key.PublicKey)
|
||||
// Successful
|
||||
registration := &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: "abc",
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
Version: 1,
|
||||
}
|
||||
payload, err := proto.Marshal(registration)
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().True(response.Success)
|
||||
|
||||
query := &protobuf.PushNotificationQuery{
|
||||
PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey},
|
||||
}
|
||||
|
||||
queryResponse := s.server.buildPushNotificationQueryResponse(query)
|
||||
s.Require().NotNil(queryResponse)
|
||||
s.Require().True(queryResponse.Success)
|
||||
s.Require().Len(queryResponse.Info, 1)
|
||||
s.Require().Equal(s.accessToken, queryResponse.Info[0].AccessToken)
|
||||
s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey)
|
||||
s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId)
|
||||
s.Require().Nil(queryResponse.Info[0].AllowedKeyList)
|
||||
}
|
||||
|
||||
func (s *ServerSuite) TestbuildPushNotificationQueryResponseWithFiltering() {
|
||||
hashedPublicKey := common.HashPublicKey(&s.key.PublicKey)
|
||||
allowedKeyList := [][]byte{[]byte("a")}
|
||||
// Successful
|
||||
|
||||
registration := &protobuf.PushNotificationRegistration{
|
||||
DeviceToken: "abc",
|
||||
AccessToken: s.accessToken,
|
||||
Grant: s.grant,
|
||||
TokenType: protobuf.PushNotificationRegistration_APN_TOKEN,
|
||||
InstallationId: s.installationID,
|
||||
AllowFromContactsOnly: true,
|
||||
AllowedKeyList: allowedKeyList,
|
||||
Version: 1,
|
||||
}
|
||||
payload, err := proto.Marshal(registration)
|
||||
s.Require().NoError(err)
|
||||
|
||||
cyphertext, err := common.Encrypt(payload, s.sharedKey, rand.Reader)
|
||||
s.Require().NoError(err)
|
||||
response := s.server.buildPushNotificationRegistrationResponse(&s.key.PublicKey, cyphertext)
|
||||
s.Require().NotNil(response)
|
||||
s.Require().True(response.Success)
|
||||
|
||||
query := &protobuf.PushNotificationQuery{
|
||||
PublicKeys: [][]byte{[]byte("non-existing"), hashedPublicKey},
|
||||
}
|
||||
|
||||
queryResponse := s.server.buildPushNotificationQueryResponse(query)
|
||||
s.Require().NotNil(queryResponse)
|
||||
s.Require().True(queryResponse.Success)
|
||||
s.Require().Len(queryResponse.Info, 1)
|
||||
s.Require().Equal(hashedPublicKey, queryResponse.Info[0].PublicKey)
|
||||
s.Require().Equal(s.installationID, queryResponse.Info[0].InstallationId)
|
||||
s.Require().Equal(allowedKeyList, queryResponse.Info[0].AllowedKeyList)
|
||||
}
|
|
@ -7,6 +7,8 @@ import (
|
|||
|
||||
encryptmigrations "github.com/status-im/status-go/protocol/encryption/migrations"
|
||||
appmigrations "github.com/status-im/status-go/protocol/migrations"
|
||||
push_notification_client_migrations "github.com/status-im/status-go/protocol/pushnotificationclient/migrations"
|
||||
push_notification_server_migrations "github.com/status-im/status-go/protocol/pushnotificationserver/migrations"
|
||||
wakumigrations "github.com/status-im/status-go/protocol/transport/waku/migrations"
|
||||
whispermigrations "github.com/status-im/status-go/protocol/transport/whisper/migrations"
|
||||
)
|
||||
|
@ -35,6 +37,14 @@ var defaultMigrations = []migrationsWithGetter{
|
|||
Names: appmigrations.AssetNames(),
|
||||
Getter: appmigrations.Asset,
|
||||
},
|
||||
{
|
||||
Names: push_notification_server_migrations.AssetNames(),
|
||||
Getter: push_notification_server_migrations.Asset,
|
||||
},
|
||||
{
|
||||
Names: push_notification_client_migrations.AssetNames(),
|
||||
Getter: push_notification_client_migrations.Asset,
|
||||
},
|
||||
}
|
||||
|
||||
func prepareMigrations(migrations []migrationsWithGetter) ([]string, getter, error) {
|
||||
|
|
|
@ -218,15 +218,15 @@ func (s *FiltersManager) Remove(filters ...*Filter) error {
|
|||
}
|
||||
|
||||
// LoadPartitioned creates a filter for a partitioned topic.
|
||||
func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey) (*Filter, error) {
|
||||
return s.loadPartitioned(publicKey, false)
|
||||
func (s *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
|
||||
return s.loadPartitioned(publicKey, identity, listen)
|
||||
}
|
||||
|
||||
func (s *FiltersManager) loadMyPartitioned() (*Filter, error) {
|
||||
return s.loadPartitioned(&s.privateKey.PublicKey, true)
|
||||
return s.loadPartitioned(&s.privateKey.PublicKey, s.privateKey, true)
|
||||
}
|
||||
|
||||
func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool) (*Filter, error) {
|
||||
func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
|
@ -237,7 +237,7 @@ func (s *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, listen bool
|
|||
|
||||
// We set up a filter so we can publish,
|
||||
// but we discard envelopes if listen is false.
|
||||
filter, err := s.addAsymmetric(chatID, listen)
|
||||
filter, err := s.addAsymmetric(chatID, identity, listen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ func (s *FiltersManager) LoadDiscovery() ([]*Filter, error) {
|
|||
OneToOne: true,
|
||||
}
|
||||
|
||||
discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, true)
|
||||
discoveryResponse, err := s.addAsymmetric(personalDiscoveryChat.ChatID, s.privateKey, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -439,7 +439,7 @@ func (s *FiltersManager) addSymmetric(chatID string) (*RawFilter, error) {
|
|||
|
||||
// addAsymmetricFilter adds a filter with our private key
|
||||
// and set minPow according to the listen parameter.
|
||||
func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter, error) {
|
||||
func (s *FiltersManager) addAsymmetric(chatID string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) {
|
||||
var (
|
||||
err error
|
||||
pow = 1.0 // use PoW high enough to discard all messages for the filter
|
||||
|
@ -452,7 +452,7 @@ func (s *FiltersManager) addAsymmetric(chatID string, listen bool) (*RawFilter,
|
|||
topic := ToTopic(chatID)
|
||||
topics := [][]byte{topic}
|
||||
|
||||
privateKeyID, err := s.service.AddKeyPair(s.privateKey)
|
||||
privateKeyID, err := s.service.AddKeyPair(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ type Transport interface {
|
|||
RemoveFilters(filters []*Filter) error
|
||||
ResetFilters() error
|
||||
Filters() []*Filter
|
||||
LoadKeyFilters(*ecdsa.PrivateKey) (*Filter, error)
|
||||
ProcessNegotiatedSecret(secret types.NegotiatedSecret) (*Filter, error)
|
||||
RetrieveRawAll() (map[Filter][]*types.Message, error)
|
||||
}
|
||||
|
|
|
@ -204,61 +204,6 @@ func (a *Transport) LeaveGroup(publicKeys []*ecdsa.PublicKey) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Message *types.Message
|
||||
Public bool
|
||||
}
|
||||
|
||||
func (a *Transport) RetrieveAllMessages() ([]Message, error) {
|
||||
var messages []Message
|
||||
|
||||
for _, filter := range a.filters.Filters() {
|
||||
filterMsgs, err := a.api.GetFilterMessages(filter.FilterID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, m := range filterMsgs {
|
||||
messages = append(messages, Message{
|
||||
Message: m,
|
||||
Public: filter.IsPublic(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func (a *Transport) RetrievePublicMessages(chatID string) ([]*types.Message, error) {
|
||||
filter, err := a.filters.LoadPublic(chatID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.api.GetFilterMessages(filter.FilterID)
|
||||
}
|
||||
|
||||
func (a *Transport) RetrievePrivateMessages(publicKey *ecdsa.PublicKey) ([]*types.Message, error) {
|
||||
chats := a.filters.FiltersByPublicKey(publicKey)
|
||||
discoveryChats, err := a.filters.Init(nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result []*types.Message
|
||||
|
||||
for _, chat := range append(chats, discoveryChats...) {
|
||||
filterMsgs, err := a.api.GetFilterMessages(chat.FilterID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = append(result, filterMsgs...)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (a *Transport) RetrieveRawAll() (map[transport.Filter][]*types.Message, error) {
|
||||
result := make(map[transport.Filter][]*types.Message)
|
||||
|
||||
|
@ -318,7 +263,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||
return nil, err
|
||||
}
|
||||
|
||||
filter, err := a.filters.LoadPartitioned(publicKey)
|
||||
filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -329,6 +274,10 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||
return a.api.Post(ctx, *newMessage)
|
||||
}
|
||||
|
||||
func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||
return a.filters.LoadPartitioned(&key.PublicKey, key, true)
|
||||
}
|
||||
|
||||
func (a *Transport) SendPrivateOnDiscovery(ctx context.Context, newMessage *types.NewMessage, publicKey *ecdsa.PublicKey) ([]byte, error) {
|
||||
if err := a.addSig(newMessage); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -318,7 +318,7 @@ func (a *Transport) SendPrivateWithPartitioned(ctx context.Context, newMessage *
|
|||
return nil, err
|
||||
}
|
||||
|
||||
filter, err := a.filters.LoadPartitioned(publicKey)
|
||||
filter, err := a.filters.LoadPartitioned(publicKey, a.keysManager.privateKey, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -406,6 +406,10 @@ func (a *Transport) SendMessagesRequest(
|
|||
return
|
||||
}
|
||||
|
||||
func (a *Transport) LoadKeyFilters(key *ecdsa.PrivateKey) (*transport.Filter, error) {
|
||||
return a.filters.LoadPartitioned(&key.PublicKey, key, true)
|
||||
}
|
||||
|
||||
func (a *Transport) waitForRequestCompleted(ctx context.Context, requestID []byte, events chan types.EnvelopeEvent) (*types.MailServerResponse, error) {
|
||||
for {
|
||||
select {
|
||||
|
|
|
@ -37,6 +37,9 @@ type StatusMessage struct {
|
|||
// Hash is the transport layer hash
|
||||
Hash []byte `json:"-"`
|
||||
|
||||
// Dst is the targeted public key
|
||||
Dst *ecdsa.PublicKey
|
||||
|
||||
// TransportLayerSigPubKey contains the public key provided by the transport layer
|
||||
TransportLayerSigPubKey *ecdsa.PublicKey `json:"-"`
|
||||
// ApplicationMetadataLayerPubKey contains the public key provided by the application metadata layer
|
||||
|
@ -87,13 +90,25 @@ func (m *StatusMessage) HandleTransport(shhMessage *types.Message) error {
|
|||
m.TransportLayerSigPubKey = publicKey
|
||||
m.TransportPayload = shhMessage.Payload
|
||||
|
||||
if shhMessage.Dst != nil {
|
||||
publicKey, err := crypto.UnmarshalPubkey(shhMessage.Dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Dst = publicKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol) error {
|
||||
func (m *StatusMessage) HandleEncryption(myKey *ecdsa.PrivateKey, senderKey *ecdsa.PublicKey, enc *encryption.Protocol, skipNegotiation bool) error {
|
||||
// As we handle non-encrypted messages, we make sure that DecryptPayload
|
||||
// is set regardless of whether this step is successful
|
||||
m.DecryptedPayload = m.TransportPayload
|
||||
// Nothing to do
|
||||
if skipNegotiation {
|
||||
return nil
|
||||
}
|
||||
|
||||
var protocolMessage encryption.ProtocolMessage
|
||||
err := proto.Unmarshal(m.TransportPayload, &protocolMessage)
|
||||
|
@ -319,6 +334,82 @@ func (m *StatusMessage) HandleApplication() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION:
|
||||
// This message is a bit different as it's encrypted, so we pass it straight through
|
||||
|
||||
m.ParsedMessage = m.DecryptedPayload
|
||||
|
||||
return nil
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_CONTACT_CODE_ADVERTISEMENT:
|
||||
var message protobuf.ContactCodeAdvertisement
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode ContactCodeAdvertisement: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REQUEST:
|
||||
var message protobuf.PushNotificationRequest
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode PushNotificationRequest: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_REGISTRATION_RESPONSE:
|
||||
var message protobuf.PushNotificationRegistrationResponse
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode PushNotificationRegistrationResponse: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY:
|
||||
var message protobuf.PushNotificationQuery
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode PushNotificationQuery: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_QUERY_RESPONSE:
|
||||
var message protobuf.PushNotificationQueryResponse
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode PushNotificationQueryResponse: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
case protobuf.ApplicationMetadataMessage_PUSH_NOTIFICATION_RESPONSE:
|
||||
var message protobuf.PushNotificationResponse
|
||||
err := proto.Unmarshal(m.DecryptedPayload, &message)
|
||||
if err != nil {
|
||||
m.ParsedMessage = nil
|
||||
log.Printf("[message::DecodeMessage] could not decode PushNotificationResponse: %#x, err: %v", m.Hash, err.Error())
|
||||
} else {
|
||||
m.ParsedMessage = message
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,10 +12,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/mailserver"
|
||||
"github.com/status-im/status-go/protocol"
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
"github.com/status-im/status-go/services/ext/mailservers"
|
||||
)
|
||||
|
@ -247,6 +249,14 @@ func (api *PublicAPI) DeleteChat(parent context.Context, chatID string) error {
|
|||
return api.service.messenger.DeleteChat(chatID)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) MuteChat(parent context.Context, chatID string) error {
|
||||
return api.service.messenger.MuteChat(chatID)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) UnmuteChat(parent context.Context, chatID string) error {
|
||||
return api.service.messenger.UnmuteChat(chatID)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) SaveContact(parent context.Context, contact *protocol.Contact) error {
|
||||
return api.service.messenger.SaveContact(contact)
|
||||
}
|
||||
|
@ -393,6 +403,114 @@ func (api *PublicAPI) UpdateMailservers(enodes []string) error {
|
|||
return api.service.UpdateMailservers(nodes)
|
||||
}
|
||||
|
||||
// PushNotifications server endpoints
|
||||
|
||||
func (api *PublicAPI) StartPushNotificationsServer() error {
|
||||
err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.StartPushNotificationsServer()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) StopPushNotificationsServer() error {
|
||||
err := api.service.accountsDB.SaveSetting("push-notifications-server-enabled?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.StopPushNotificationsServer()
|
||||
}
|
||||
|
||||
// PushNotification client endpoints
|
||||
|
||||
func (api *PublicAPI) RegisterForPushNotifications(ctx context.Context, deviceToken string) error {
|
||||
// We set both for now as they are equivalent
|
||||
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = api.service.accountsDB.SaveSetting("notifications-enabled?", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.RegisterForPushNotifications(ctx, deviceToken)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) UnregisterForPushNotifications(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("remote-push-notifications-enabled?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = api.service.accountsDB.SaveSetting("notifications-enabled?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.UnregisterFromPushNotifications(ctx)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) DisableSendingNotifications(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("send-push-notifications?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.DisableSendingPushNotifications()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) EnableSendingNotifications(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("send-push-notifications?", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.service.messenger.EnableSendingPushNotifications()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) EnablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.service.messenger.EnablePushNotificationsFromContactsOnly()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) DisablePushNotificationsFromContactsOnly(ctx context.Context) error {
|
||||
err := api.service.accountsDB.SaveSetting("push-notifications-from-contacts-only?", false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return api.service.messenger.DisablePushNotificationsFromContactsOnly()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) AddPushNotificationsServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
||||
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.AddPushNotificationsServer(ctx, publicKey)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) RemovePushNotificationServer(ctx context.Context, publicKeyBytes types.HexBytes) error {
|
||||
publicKey, err := crypto.UnmarshalPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return api.service.messenger.RemovePushNotificationServer(ctx, publicKey)
|
||||
}
|
||||
|
||||
func (api *PublicAPI) GetPushNotificationServers() ([]*pushnotificationclient.PushNotificationServer, error) {
|
||||
return api.service.messenger.GetPushNotificationServers()
|
||||
}
|
||||
|
||||
func (api *PublicAPI) RegisteredForPushNotifications() (bool, error) {
|
||||
return api.service.messenger.RegisteredForPushNotifications()
|
||||
}
|
||||
|
||||
// Echo is a method for testing purposes.
|
||||
func (api *PublicAPI) Echo(ctx context.Context, message string) (string, error) {
|
||||
return message, nil
|
||||
|
|
|
@ -33,6 +33,8 @@ import (
|
|||
coretypes "github.com/status-im/status-go/eth-node/core/types"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/protocol"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationclient"
|
||||
"github.com/status-im/status-go/protocol/pushnotificationserver"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
)
|
||||
|
||||
|
@ -144,7 +146,12 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
|
|||
EnvelopeEventsHandler: EnvelopeSignalHandler{},
|
||||
Logger: logger,
|
||||
}
|
||||
options := buildMessengerOptions(s.config, db, envelopesMonitorConfig, logger)
|
||||
s.accountsDB = accounts.NewDB(db)
|
||||
|
||||
options, err := buildMessengerOptions(s.config, identity, db, envelopesMonitorConfig, s.accountsDB, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
messenger, err := protocol.NewMessenger(
|
||||
identity,
|
||||
|
@ -155,7 +162,6 @@ func (s *Service) InitProtocol(identity *ecdsa.PrivateKey, db *sql.DB, logger *z
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.accountsDB = accounts.NewDB(db)
|
||||
s.messenger = messenger
|
||||
return messenger.Init()
|
||||
}
|
||||
|
@ -338,6 +344,9 @@ func (s *Service) DisableInstallation(installationID string) error {
|
|||
|
||||
// UpdateMailservers updates information about selected mail servers.
|
||||
func (s *Service) UpdateMailservers(nodes []*enode.Node) error {
|
||||
if len(nodes) > 0 && s.messenger != nil {
|
||||
s.messenger.SetMailserver(nodes[0].ID().Bytes())
|
||||
}
|
||||
if err := s.peerStore.Update(nodes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -439,10 +448,12 @@ func onNegotiatedFilters(filters []*transport.Filter) {
|
|||
|
||||
func buildMessengerOptions(
|
||||
config params.ShhextConfig,
|
||||
identity *ecdsa.PrivateKey,
|
||||
db *sql.DB,
|
||||
envelopesMonitorConfig *transport.EnvelopesMonitorConfig,
|
||||
accountsDB *accounts.Database,
|
||||
logger *zap.Logger,
|
||||
) []protocol.Option {
|
||||
) ([]protocol.Option, error) {
|
||||
options := []protocol.Option{
|
||||
protocol.WithCustomLogger(logger),
|
||||
protocol.WithDatabase(db),
|
||||
|
@ -453,6 +464,23 @@ func buildMessengerOptions(
|
|||
if config.DataSyncEnabled {
|
||||
options = append(options, protocol.WithDatasync())
|
||||
}
|
||||
settings, err := accountsDB.GetSettings()
|
||||
if err != sql.ErrNoRows && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if settings.PushNotificationsServerEnabled {
|
||||
config := &pushnotificationserver.Config{
|
||||
Logger: logger,
|
||||
}
|
||||
options = append(options, protocol.WithPushNotificationServerConfig(config))
|
||||
}
|
||||
|
||||
options = append(options, protocol.WithPushNotificationClientConfig(&pushnotificationclient.Config{
|
||||
SendEnabled: settings.SendPushNotifications,
|
||||
AllowFromContactsOnly: settings.PushNotificationsFromContactsOnly,
|
||||
RemoteNotificationsEnabled: settings.RemotePushNotificationsEnabled,
|
||||
}))
|
||||
|
||||
if config.VerifyTransactionURL != "" {
|
||||
client := &verifyTransactionClient{
|
||||
|
@ -462,5 +490,5 @@ func buildMessengerOptions(
|
|||
options = append(options, protocol.WithVerifyTransactionClient(client))
|
||||
}
|
||||
|
||||
return options
|
||||
return options, nil
|
||||
}
|
||||
|
|
|
@ -24,12 +24,12 @@ import (
|
|||
"github.com/syndtr/goleveldb/leveldb"
|
||||
"github.com/syndtr/goleveldb/leveldb/storage"
|
||||
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/ext"
|
||||
"github.com/status-im/status-go/sqlite"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/whisper/v6"
|
||||
)
|
||||
|
@ -209,7 +209,7 @@ func TestInitProtocol(t *testing.T) {
|
|||
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
||||
require.NoError(t, err)
|
||||
|
||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
||||
|
@ -262,7 +262,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
|||
s.Require().NoError(err)
|
||||
nodeWrapper := ext.NewTestNodeWrapper(gethbridge.NewGethWhisperWrapper(whisper), nil)
|
||||
service := New(config, nodeWrapper, nil, nil, db)
|
||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||
s.Require().NoError(err)
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
|
|
|
@ -23,12 +23,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/services/ext"
|
||||
"github.com/status-im/status-go/sqlite"
|
||||
"github.com/status-im/status-go/t/helpers"
|
||||
"github.com/status-im/status-go/waku"
|
||||
wakucommon "github.com/status-im/status-go/waku/common"
|
||||
|
@ -126,7 +126,7 @@ func TestInitProtocol(t *testing.T) {
|
|||
tmpdir, err := ioutil.TempDir("", "test-shhext-service-init-protocol")
|
||||
require.NoError(t, err)
|
||||
|
||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/db.sql", tmpdir), "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
err = service.InitProtocol(privateKey, sqlDB, zap.NewNop())
|
||||
|
@ -179,7 +179,7 @@ func (s *ShhExtSuite) createAndAddNode() {
|
|||
s.Require().NoError(err)
|
||||
nodeWrapper := ext.NewTestNodeWrapper(nil, gethbridge.NewGethWakuWrapper(w))
|
||||
service := New(config, nodeWrapper, nil, nil, db)
|
||||
sqlDB, err := sqlite.OpenDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||
sqlDB, err := appdatabase.InitializeDB(fmt.Sprintf("%s/%d", s.dir, idx), "password")
|
||||
s.Require().NoError(err)
|
||||
privateKey, err := crypto.GenerateKey()
|
||||
s.NoError(err)
|
||||
|
|
|
@ -97,7 +97,7 @@ func ConfigReadmeMd() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1584434371, 0)}
|
||||
info := bindataFileInfo{name: "../config/README.md", size: 3132, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd0, 0x71, 0x14, 0x3e, 0x9d, 0x9c, 0x11, 0x40, 0xe1, 0xe9, 0x8b, 0xcc, 0x38, 0x17, 0x69, 0xb, 0xa7, 0xf2, 0x91, 0xa6, 0x4a, 0x9f, 0xd2, 0xc6, 0xf4, 0xff, 0x37, 0x5d, 0xd3, 0xed, 0x7c, 0xbd}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func ConfigCliFleetEthProdJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.prod.json", size: 3625, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1c, 0x86, 0x93, 0x3b, 0x8c, 0xb4, 0x1b, 0x8b, 0x70, 0xe6, 0x90, 0x3, 0xc9, 0x93, 0x99, 0x97, 0x18, 0xb7, 0x49, 0xbf, 0x2d, 0x35, 0x98, 0xb8, 0x49, 0xa8, 0x73, 0x30, 0xf5, 0x2d, 0x7f, 0x5c}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ func ConfigCliFleetEthStagingJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1846, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xef, 0x3d, 0x5c, 0x41, 0xb1, 0xc8, 0x54, 0xd3, 0x82, 0x60, 0xcf, 0x5f, 0x8b, 0x92, 0xd0, 0x41, 0xa7, 0xff, 0xfd, 0x2c, 0x95, 0x44, 0x68, 0xd3, 0xad, 0x16, 0x8c, 0x49, 0x10, 0x4e, 0xb7}}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func ConfigCliFleetEthTestJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1585559578, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1855, mode: os.FileMode(0644), modTime: time.Unix(1595493797, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe4, 0x90, 0x4, 0x6, 0x2b, 0xe4, 0x67, 0x7a, 0xfc, 0x70, 0xa9, 0xb, 0x85, 0x8c, 0xb3, 0x8, 0x8e, 0xee, 0xec, 0x64, 0xb7, 0xac, 0x4c, 0x6, 0x4d, 0xcc, 0x4f, 0xda, 0xbd, 0xed, 0x17, 0x53}}
|
||||
return a, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue