Merge branch 'feature/mute-chats-2' into develop

This commit is contained in:
Andrea Maria Piana 2020-07-27 10:58:23 +02:00
commit 717ed3e1cf
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
65 changed files with 7732 additions and 613 deletions

View File

@ -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

View File

@ -1 +1 @@
0.55.3
0.56.0

View File

@ -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.

View File

@ -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;

View File

@ -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,

View File

@ -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()) {

View File

@ -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() {

View File

@ -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,
}

View File

@ -1,8 +0,0 @@
syntax = "proto3";
package applicationmetadata;
message Message {
bytes signature = 4001;
bytes payload = 4002;
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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) {

71
protocol/common/crypto.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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"`

View File

@ -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

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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)

View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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 {

View File

@ -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,
}

View File

@ -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;
}
}

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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}))
}

View File

@ -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, "/")...)...)
}

View File

@ -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;

View File

@ -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);

View File

@ -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 ./

View File

@ -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(&registrationBytes, &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(&notification.RetryCount, &notification.LastTriedAt, &notification.Error, &notification.Success, &publicKeyBytes, &notification.InstallationID, &notification.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
}

View File

@ -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)
}

View File

@ -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")

View File

@ -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
}

View File

@ -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)
}

View File

@ -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, "/")...)...)
}

View File

@ -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;

View File

@ -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);

View File

@ -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 ./

View File

@ -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
}

View File

@ -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))
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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
}