refactor inbox

Signed-off-by: yenda <eric@status.im>
This commit is contained in:
yenda 2018-10-09 10:10:47 +02:00
parent dc8976a8b2
commit efdd76b364
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
49 changed files with 946 additions and 1051 deletions

View File

@ -26,7 +26,6 @@
(def snoopy-filter #js {})
(def snoopy-bars #js {})
(def snoopy-buffer #js {})
(def background-timer #js {:setTimeout (fn [])})
(def background-timer #js {:setTimeout (fn [cb ms] (js/setTimeout cb ms))})
(def testfairy #js {})
(def react-navigation #js {:NavigationActions #js {}})

View File

@ -1,7 +1,7 @@
(ns status-im.accounts.update.core
(:require [status-im.data-store.accounts :as accounts-store]
[status-im.transport.message.core :as transport]
[status-im.transport.message.v1.contact :as message.contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.contact :as message.contact]
[status-im.utils.fx :as fx]))
(fx/defn account-update
@ -18,7 +18,7 @@
(if (or (:name new-account-fields) (:photo-path new-account-fields))
(fx/merge cofx
fx
#(transport/send (message.contact/ContactUpdate. name photo-path address fcm-token) nil %))
#(protocol/send (message.contact/ContactUpdate. name photo-path address fcm-token) nil %))
fx)))
(fx/defn clean-seed-phrase

View File

@ -5,8 +5,8 @@
;; Seen messages
(fx/defn receive-seen
[{:keys [db js-obj]} chat-id sender {:keys [message-ids]}]
(merge {:confirm-messages-processed [{:web3 (:web3 db)
:js-obj js-obj}]}
(merge {:transport/confirm-messages-processed [{:web3 (:web3 db)
:js-obj js-obj}]}
(when-let [seen-messages-ids (-> (get-in db [:chats chat-id :messages])
(select-keys message-ids)
keys)]

View File

@ -1,23 +1,18 @@
(ns status-im.chat.models
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[status-im.data-store.chats :as chats-store]
[status-im.data-store.messages :as messages-store]
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.transport.message.core :as transport.message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.message.v1.public-chat :as public-chat]
[status-im.transport.utils :as transport.utils]
[status-im.ui.components.styles :as styles]
[status-im.ui.screens.navigation :as navigation]
[status-im.i18n :as i18n]
[status-im.utils.utils :as utils]
[status-im.transport.chat.core :as transport.chat]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.public-chat :as public-chat]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.clocks :as utils.clocks]
[status-im.utils.datetime :as time]
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.fx :as fx]
[status-im.ui.components.colors :as colors]))
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.utils :as utils]))
(defn multi-user-chat? [cofx chat-id]
(get-in cofx [:db :chats chat-id :group-chat]))
@ -105,10 +100,6 @@
:data-store/tx [(chats-store/clear-history-tx chat-id last-message-clock-value)
(messages-store/delete-messages-tx chat-id)]}))
(fx/defn remove-transport
[cofx chat-id]
(transport.utils/unsubscribe-from-chat cofx chat-id))
(fx/defn deactivate-chat
[{:keys [db now] :as cofx} chat-id]
{:db (-> db
@ -127,7 +118,7 @@
[{:keys [db now] :as cofx} chat-id]
(fx/merge cofx
#(when (public-chat? % chat-id)
(remove-transport % chat-id))
(transport.chat/unsubscribe-from-chat % chat-id))
(deactivate-chat chat-id)
(clear-history chat-id)
(navigation/navigate-to-cofx :home {})))
@ -135,7 +126,7 @@
(fx/defn send-messages-seen
[{:keys [db] :as cofx} chat-id message-ids]
(when (not (get-in db [:chats chat-id :group-chat]))
(transport.message/send (protocol/map->MessagesSeen {:message-ids message-ids}) chat-id cofx)))
(protocol/send (protocol/map->MessagesSeen {:message-ids message-ids}) chat-id cofx)))
;; TODO (janherich) - ressurect `constants/system` messages for group chats in the future
(fx/defn mark-messages-seen

View File

@ -1,16 +1,16 @@
(ns status-im.chat.models.input
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[goog.object :as object]
[status-im.constants :as constants]
[status-im.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[re-frame.core :as re-frame]
[status-im.chat.commands.core :as commands]
[status-im.chat.commands.input :as commands.input]
[status-im.chat.commands.sending :as commands.sending]
[status-im.utils.datetime :as datetime]
[status-im.chat.constants :as chat.constants]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[status-im.constants :as constants]
[status-im.js-dependencies :as dependencies]
[status-im.utils.datetime :as datetime]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))

View File

@ -1,11 +1,11 @@
(ns status-im.chat.models.loading
(:require [clojure.set :as set]
[status-im.chat.commands.core :as commands]
[status-im.chat.models :as chat-model]
[status-im.constants :as constants]
[status-im.data-store.contacts :as contacts-store]
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.utils.contacts :as utils.contacts]
[status-im.chat.commands.core :as commands]
[status-im.chat.models :as chat-model]
[status-im.utils.datetime :as time]
[status-im.utils.fx :as fx]))

View File

@ -17,8 +17,7 @@
[status-im.utils.types :as types]
[status-im.notifications.core :as notifications]
[status-im.transport.utils :as transport.utils]
[status-im.transport.message.core :as transport]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.protocol :as protocol]
[status-im.data-store.messages :as messages-store]
[status-im.data-store.user-statuses :as user-statuses-store]
[status-im.utils.fx :as fx]
@ -94,7 +93,7 @@
(fx/defn send-message-seen
[cofx chat-id message-id send-seen?]
(when send-seen?
(transport/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx)))
(protocol/send (protocol/map->MessagesSeen {:message-ids #{message-id}}) chat-id cofx)))
(defn ensure-clock-value [{:keys [clock-value] :as message} {:keys [last-clock-value]}]
(if clock-value
@ -127,8 +126,8 @@
;; TODO (cammellos): Refactor so it's not computed twice
(add-outgoing-status cofx))]
(fx/merge cofx
{:confirm-messages-processed [{:web3 web3
:js-obj js-obj}]}
{:transport/confirm-messages-processed [{:web3 web3
:js-obj js-obj}]}
(add-message batch? message current-chat?)
;; Checking :outgoing here only works for now as we don't have a :seen
;; status for public chats, if we add processing of our own messages
@ -216,7 +215,7 @@
(group-chats/wrap-group-message cofx chat-id send-record)
send-record)]
(transport/send wrapped-record chat-id cofx))))
(protocol/send wrapped-record chat-id cofx))))
(defn add-message-type [message {:keys [chat-id group-chat public?]}]
(cond-> message

View File

@ -1,6 +1,7 @@
(ns status-im.data-store.realm.schemas.account.core
(:require [status-im.data-store.realm.schemas.account.chat :as chat]
[status-im.data-store.realm.schemas.account.transport :as transport]
[status-im.data-store.realm.schemas.account.transport-inbox-topic :as transport-inbox-topic]
[status-im.data-store.realm.schemas.account.contact :as contact]
[status-im.data-store.realm.schemas.account.message :as message]
[status-im.data-store.realm.schemas.account.user-status :as user-status]
@ -143,18 +144,22 @@
browser/v8
dapp-permissions/v9])
(def v14 [chat/v6
(def v14 v13)
(def v15 [chat/v7
transport/v6
contact/v1
message/v7
mailserver/v11
user-status/v1
membership-update/v1
local-storage/v1
browser/v8
dapp-permissions/v9])
(def v15 [chat/v7
transport/v6
(def v16 [chat/v7
transport/v7
transport-inbox-topic/v1
contact/v1
message/v7
mailserver/v11
@ -209,4 +214,7 @@
:migration migrations/v14}
{:schema v15
:schemaVersion 15
:migration migrations/v15}])
:migration migrations/v15}
{:schema v16
:schemaVersion 16
:migration migrations/v16}])

View File

@ -97,3 +97,6 @@
(defn v15 [old-realm new-realm]
(log/debug "migrating v15 account database"))
(defn v16 [old-realm new-realm]
(log/debug "migrating v16 account database"))

View File

@ -47,3 +47,20 @@
;;TODO (yenda) remove once go implements persistence
:sym-key {:type :string
:optional true}}})
(def v7 {:name :transport
:primaryKey :chat-id
:properties {:chat-id :string
:ack :string
:seen :string
:pending-ack :string
:pending-send :string
:topic {:type :string
:optional true}
:resend? {:type :string
:optional true}
:sym-key-id {:type :string
:optional true}
;;TODO (yenda) remove once go implements persistence
:sym-key {:type :string
:optional true}}})

View File

@ -0,0 +1,7 @@
(ns status-im.data-store.realm.schemas.account.transport-inbox-topic)
(def v1 {:name :transport-inbox-topic
:primaryKey :topic
:properties {:topic :string
:chat-ids :string
:last-request {:type :int :default 1}}})

View File

@ -44,3 +44,39 @@
(let [transport (core/single
(core/get-by-field realm :transport :chat-id chat-id))]
(core/delete realm transport))))
(defn deserialize-inbox-topic [serialized-inbox-topic]
(-> serialized-inbox-topic
(dissoc :topic)
(update :chat-ids edn/read-string)))
(re-frame/reg-cofx
:data-store/transport-inbox-topics
(fn [cofx _]
(assoc cofx
:data-store/transport-inbox-topics
(reduce (fn [acc {:keys [topic] :as inbox-topic}]
(assoc acc topic (deserialize-inbox-topic inbox-topic)))
{}
(-> @core/account-realm
(core/get-all :transport-inbox-topic)
(core/all-clj :transport-inbox-topic))))))
(defn save-transport-inbox-topic-tx
"Returns tx function for saving transport inbox topic"
[{:keys [topic inbox-topic]}]
(fn [realm]
(core/create realm
:transport-inbox-topic
(-> inbox-topic
(assoc :topic topic)
(update :chat-ids pr-str))
true)))
(defn delete-transport-inbox-topic-tx
"Returns tx function for deleting transport inbox-topic"
[topic]
(fn [realm]
(let [transport-inbox-topic (core/single
(core/get-by-field realm :transport-inbox-topic :topic topic))]
(core/delete realm transport-inbox-topic))))

View File

@ -9,11 +9,11 @@
[status-im.bootnodes.core :as bootnodes]
[status-im.browser.core :as browser]
[status-im.browser.permissions :as browser.permissions]
[status-im.chat.models :as chat]
[status-im.chat.models.message :as chat.message]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.input :as chat.input]
[status-im.chat.commands.input :as commands.input]
[status-im.chat.models :as chat]
[status-im.chat.models.input :as chat.input]
[status-im.chat.models.loading :as chat.loading]
[status-im.chat.models.message :as chat.message]
[status-im.data-store.core :as data-store]
[status-im.fleet.core :as fleet]
[status-im.group-chats.core :as group-chats]
@ -28,6 +28,8 @@
[status-im.protocol.core :as protocol]
[status-im.qr-scanner.core :as qr-scanner]
[status-im.signals.core :as signals]
[status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as transport.message]
[status-im.ui.screens.currency-settings.models
:as
currency-settings.models]
@ -89,6 +91,7 @@
(re-frame/inject-cofx :data-store/get-all-contacts)
(re-frame/inject-cofx :data-store/get-all-mailservers)
(re-frame/inject-cofx :data-store/transport)
(re-frame/inject-cofx :data-store/transport-inbox-topics)
(re-frame/inject-cofx :data-store/all-browsers)
(re-frame/inject-cofx :data-store/all-dapp-permissions)]
(fn [cofx [_ address]]
@ -938,7 +941,65 @@
(fn [cofx [_ group-update sender-signature]]
(group-chats/handle-membership-update cofx group-update sender-signature)))
;; profile module
(handlers/register-handler-fx
:profile.ui/ens-names-button-pressed
(fn [cofx]
(browser/open-url cofx "names.statusnet.eth")))
;; inbox module
(handlers/register-handler-fx
:inbox.ui/reconnect-mailserver-pressed
(fn [cofx [_ args]]
(inbox/connect-to-mailserver cofx)))
(handlers/register-handler-fx
:inbox/check-connection-timeout
(fn [cofx _]
(inbox/check-connection cofx)))
(handlers/register-handler-fx
:inbox.callback/generate-mailserver-symkey-success
(fn [cofx [_ wnode sym-key-id]]
(inbox/add-mailserver-sym-key cofx wnode sym-key-id)))
(handlers/register-handler-fx
:inbox.callback/mark-trusted-peer-success
(fn [cofx _]
(inbox/add-mailserver-trusted cofx)))
(handlers/register-handler-fx
:inbox.callback/mark-trusted-peer-error
(fn [cofx [_ error]]
(log/error "Error on mark-trusted-peer: " error)
(inbox/check-connection cofx)))
(handlers/register-handler-fx
:inbox.callback/request-messages-success
(fn [cofx [_ request]]
(inbox/add-request cofx request)))
;; transport module
(handlers/register-handler-fx
:transport/messages-received
[handlers/logged-in (re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ js-error js-messages chat-id]]
(transport.message/receive-whisper-messages cofx js-error js-messages chat-id)))
(handlers/register-handler-fx
:transport/send-status-message-error
(fn [{:keys [db] :as cofx} [_ err]]
(log/error :send-status-message-error err)))
(handlers/register-handler-fx
:transport/message-sent
(fn [cofx [_ chat-id message-id message-type envelope-hash-js]]
(transport.message/set-message-envelope-hash cofx chat-id message-id message-type envelope-hash-js)))
(handlers/register-handler-fx
:transport/contact-message-sent
(fn [cofx [_ chat-id envelope-hash]]
(transport.message/set-contact-message-envelope-hash cofx chat-id envelope-hash)))

View File

@ -11,9 +11,8 @@
[status-im.transport.utils :as transport.utils]
[status-im.transport.db :as transport.db]
[status-im.transport.utils :as transport.utils]
[status-im.transport.message.core :as protocol.message]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.message.v1.protocol :as transport.protocol]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.group-chat :as message.group-chat]
[status-im.utils.fx :as fx]
[status-im.chat.models :as models.chat]))
@ -105,7 +104,7 @@
"Wrap a group message in a membership update"
[cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])]
(transport/map->GroupMembershipUpdate.
(message.group-chat/map->GroupMembershipUpdate.
{:chat-id chat-id
:membership-updates (:membership-updates chat)
:message message})))
@ -123,7 +122,7 @@
{:shh/send-group-message {:web3 web3
:src current-public-key
:dsts (disj members current-public-key)
:success-event [:transport/set-message-envelope-hash
:success-event [:transport/message-sent
chat-id
(transport.utils/message-id (:message payload))
:group-user-message]
@ -137,8 +136,8 @@
(defn chat->group-update
"Transform a chat in a GroupMembershipUpdate"
[chat-id {:keys [membership-updates]}]
(transport/map->GroupMembershipUpdate. {:chat-id chat-id
:membership-updates membership-updates}))
(message.group-chat/map->GroupMembershipUpdate. {:chat-id chat-id
:membership-updates membership-updates}))
(defn handle-sign-response
"Callback to dispatch on sign response"
@ -312,9 +311,9 @@
(update-membership previous-chat membership-update)
#(when (and message
;; don't allow anything but group messages
(instance? transport.protocol/Message message)
(instance? protocol/Message message)
(= :group-user-message (:message-type message)))
(protocol.message/receive message chat-id sender-signature nil %))))))
(protocol/receive message chat-id sender-signature nil %))))))
(defn handle-sign-success
"Upsert chat and send signed payload to group members"

View File

@ -110,11 +110,6 @@
{:db (assoc db :inbox/current-id
(selected-or-random-id cofx))})
(fx/defn set-initial-last-request
[{:keys [db now] :as cofx}]
{:db (update-in db [:account/account :last-request]
(fnil identity (quot now 1000)))})
(fx/defn add-custom-mailservers
[{:keys [db]} mailservers]
{:db (reduce (fn [db {:keys [id fleet] :as mailserver}]

View File

@ -1,7 +1,7 @@
(ns status-im.models.contact
(:require [status-im.data-store.contacts :as contacts-store]
[status-im.transport.message.core :as transport]
[status-im.transport.message.v1.contact :as message.v1.contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.contact :as message.contact]
[status-im.utils.contacts :as utils.contacts]
[status-im.utils.fx :as fx]))
@ -39,8 +39,8 @@
[{:keys [db] :as cofx} {:keys [whisper-identity pending? dapp?] :as contact}]
(when-not dapp?
(if pending?
(transport/send (message.v1.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx)
(transport/send (message.v1.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(protocol/send (message.contact/map->ContactRequestConfirmed (own-info db)) whisper-identity cofx)
(protocol/send (message.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(fx/defn add-contact [{:keys [db] :as cofx} whisper-id]
(let [contact (build-contact whisper-id cofx)]

View File

@ -157,7 +157,7 @@
[{:keys [db] :as cofx} is-connected?]
(fx/merge cofx
{:db (assoc db :network-status (if is-connected? :online :offline))}
(inbox/request-messages)))
(inbox/request-messages nil)))
(defn- navigate-to-network-details
[cofx network show-warning?]

View File

@ -4,9 +4,9 @@
[status-im.transport.core :as transport]
[status-im.transport.inbox :as transport.inbox]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.fx :as fx]
[status-im.utils.semaphores :as semaphores]
[status-im.utils.utils :as utils]
[status-im.utils.fx :as fx]))
[status-im.utils.utils :as utils]))
(fx/defn update-sync-state
[{{:keys [sync-state sync-data] :as db} :db} error sync]
@ -44,13 +44,15 @@
(semaphores/lock :check-sync-state?))))
(fx/defn initialize-protocol
[{:data-store/keys [transport mailservers] :keys [db web3] :as cofx} address]
[{:data-store/keys [transport transport-inbox-topics mailservers]
:keys [db web3] :as cofx} address]
(let [network (get-in db [:account/account :network])
network-id (str (get-in db [:account/account :networks network :config :NetworkId]))]
(fx/merge cofx
{:db (assoc db
:rpc-url constants/ethereum-rpc-url
:transport/chats transport)
:transport/chats transport
:transport.inbox/topics transport-inbox-topics)
:protocol/assert-correct-network {:web3 web3
:network-id network-id}}
(start-check-sync-state)

View File

@ -3,11 +3,11 @@
[status-im.accounts.login.core :as accounts.login]
[status-im.init.core :as init]
[status-im.node.core :as node]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as transport.message]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[status-im.utils.fx :as fx]))
[taoensso.timbre :as log]))
(fx/defn status-node-started
[{db :db :as cofx}]
@ -33,7 +33,7 @@
{:db (assoc db
:peers-summary peers-summary
:peers-count peers-count)}
(transport.handlers/resend-contact-messages previous-summary)
(transport.message/resend-contact-messages previous-summary)
(inbox/peers-summary-change previous-summary))))
(fx/defn process
@ -43,7 +43,12 @@
"node.ready" (status-node-started cofx)
"node.stopped" (status-node-stopped cofx)
"module.initialized" (status-module-initialized cofx)
"envelope.sent" (transport.handlers/update-envelope-status cofx (:hash event) :sent)
"envelope.expired" (transport.handlers/update-envelope-status cofx (:hash event) :sent)
"envelope.sent" (transport.message/update-envelope-status cofx (:hash event) :sent)
"envelope.expired" (transport.message/update-envelope-status cofx (:hash event) :sent)
"mailserver.request.completed" (when (accounts.db/logged-in? cofx)
(inbox/update-inbox-topic cofx {:request-id (:requestID event)
:cursor (:cursor event)}))
"mailserver.request.expired" (when (accounts.db/logged-in? cofx)
(inbox/resend-request cofx {:request-id (:hash event)}))
"discovery.summary" (summary cofx event)
(log/debug "Event " type " not handled"))))

View File

@ -0,0 +1,17 @@
(ns status-im.transport.chat.core
(:require [status-im.data-store.transport :as transport-store]
[status-im.transport.inbox :as inbox]
[status-im.utils.fx :as fx]))
(fx/defn remove-transport-chat
[{:keys [db]} chat-id]
{:db (update db :transport/chats dissoc chat-id)
:data-store/tx [(transport-store/delete-transport-tx chat-id)]
:shh/remove-filter (get-in db [:transport/filters chat-id])})
(fx/defn unsubscribe-from-chat
"Unsubscribe from chat on transport layer"
[cofx chat-id]
(fx/merge cofx
(inbox/remove-chat-from-inbox-topic chat-id)
(remove-transport-chat chat-id)))

View File

@ -4,12 +4,13 @@
[re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.transport :as transport-store]
[status-im.transport.handlers :as transport.handlers]
[status-im.transport.inbox :as inbox]
[status-im.transport.message.core :as message]
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[taoensso.timbre :as log]
[status-im.utils.fx :as fx]))
[taoensso.timbre :as log]))
(fx/defn init-whisper
"Initialises whisper protocol by:
@ -19,6 +20,7 @@
[{:keys [db web3] :as cofx} current-account-id]
(log/debug :init-whisper)
(when-let [public-key (get-in db [:account/account :public-key])]
(let [sym-key-added-callback (fn [chat-id sym-key sym-key-id]
(re-frame/dispatch [::sym-key-added {:chat-id chat-id
:sym-key sym-key
@ -32,7 +34,7 @@
:transport (:transport/chats db)
:on-success sym-key-added-callback}}
(inbox/connect-to-mailserver)
(transport.handlers/resend-contact-messages [])))))
(message/resend-contact-messages [])))))
;;TODO (yenda) remove once go implements persistence
;;Since symkeys are not persisted, we restore them via add sym-keys,
@ -67,7 +69,5 @@
to clean-up after logout. When logging out of account A and logging in account B, account B would receive
account A messages without this."
[{:keys [db]}]
(let [{:transport/keys [chats discovery-filter]} db
chat-filters (keep :filter (vals chats))
all-filters (conj chat-filters discovery-filter)]
{:shh/remove-filters all-filters}))
(let [{:transport/keys [filters]} db]
{:shh/remove-filters (vals filters)}))

View File

@ -1,41 +1,61 @@
(ns ^{:doc "DB spec and utils for the transport layer"}
status-im.transport.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]
[clojure.string :as s]
status-im.ui.screens.contacts.db
[status-im.utils.clocks :as utils.clocks]
[clojure.string :as s]))
[status-im.utils.clocks :as utils.clocks])
(:require-macros [status-im.utils.db :refer [allowed-keys]]))
;; required
(spec/def ::ack (spec/coll-of string? :kind vector?))
(spec/def ::seen (spec/coll-of string? :kind vector?))
(spec/def ::pending-ack (spec/coll-of string? :kind vector?))
(spec/def ::pending-send (spec/coll-of string? :kind vector?))
(spec/def ::topic string?)
(spec/def ::fetch-history? boolean?)
(spec/def ::resend? (spec/nilable #{"contact-request" "contact-request-confirmation" "contact-update"}))
(spec/def ::request-from pos-int?)
;; optional
(spec/def ::topic (spec/nilable string?))
(spec/def ::request-id (spec/nilable string?))
(spec/def ::request-to (spec/nilable pos-int?))
(spec/def ::sym-key-id (spec/nilable string?))
;;TODO (yenda) remove once go implements persistence
(spec/def ::sym-key (spec/nilable string?))
(spec/def ::filter any?)
(spec/def :transport/filter-id (spec/or :keyword keyword?
:chat-id :global/not-empty-string))
(spec/def :transport/filter any?)
(spec/def :request/from pos-int?)
(spec/def :request/to pos-int?)
(spec/def :request/cursor :global/not-empty-string)
(spec/def :transport.inbox/request (spec/keys :req-un [:request/from :request/to ::topic]))
(spec/def ::request-from pos-int?)
(spec/def :transport.inbox.topic/last-request ::request-from)
(spec/def :transport.inbox.topic/chat-id (spec/or :keyword keyword?
:chat-id :global/not-empty-string))
(spec/def :transport.inbox.topic/chat-ids (spec/coll-of :transport.inbox.topic/chat-id
:kind set?
:min-count 1))
(spec/def :transport.inbox.topic/request-pending? boolean?)
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic ::fetch-history?]
:opt-un [::sym-key-id ::sym-key ::filter ::resend?]))
(spec/def :transport.inbox/topic (allowed-keys :req-un [:transport.inbox.topic/last-request
:transport.inbox.topic/chat-ids]
:opt-un [:transport.inbox.topic/request-pending?]))
(spec/def :transport/chat (allowed-keys :req-un [::ack ::seen ::pending-ack ::pending-send ::topic]
:opt-un [::sym-key-id ::sym-key ::resend?]))
(spec/def :transport/chats (spec/map-of :global/not-empty-string :transport/chat))
(spec/def :transport/discovery-filter (spec/nilable any?))
(spec/def :transport/filters (spec/map-of :transport/filter-id :transport/filter))
(spec/def :transport.inbox/topics (spec/map-of :global/not-empty-string :transport.inbox/topic))
(spec/def :transport.inbox/requests (spec/map-of :global/not-empty-string :transport.inbox/request))
(defn create-chat
"Initialize datastructure for chat representation at the transport level
Currently only :topic is actually used"
[{:keys [topic resend?]}]
[{:keys [topic resend? now]}]
{:ack []
:seen []
:pending-ack []
:pending-send []
:fetch-history? true
:resend? resend?
:topic topic})

View File

@ -1,9 +1,10 @@
(ns ^{:doc "API for whisper filters"}
status-im.transport.filters
(:require [re-frame.core :as re-frame]
[status-im.utils.handlers :as handlers]
[status-im.transport.inbox :as inbox]
[status-im.transport.utils :as utils]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[taoensso.timbre :as log]))
(defn remove-filter! [filter]
@ -14,47 +15,41 @@
(log/debug :removed-filter filter))))
(log/debug :stop-watching filter))
(defn add-shh-filter!
[web3 options callback]
(.newMessageFilter (utils/shh web3) (clj->js options)
callback
#(log/warn :add-filter-error (.stringify js/JSON (clj->js options)) %)))
(defn add-filter!
[web3 {:keys [topics to] :as options} callback]
[web3 {:keys [topics to] :as options} callback chat-id]
(let [options (assoc options :allowP2P true)]
(log/debug :add-filter options)
(add-shh-filter! web3 options callback)))
(when-let [filter (.newMessageFilter (utils/shh web3)
(clj->js options)
callback
#(log/warn :add-filter-error (.stringify js/JSON (clj->js options)) %))]
(re-frame/dispatch [:shh.callback/filter-added (first topics) chat-id filter]))))
(re-frame/reg-fx
:shh/add-filter
(fn [{:keys [web3 sym-key-id topic chat-id]}]
(when-let [filter (add-filter! web3
{:topics [topic]
:symKeyID sym-key-id}
(fn [js-error js-message]
(re-frame/dispatch [:protocol/receive-whisper-message js-error js-message chat-id])))]
(re-frame/dispatch [::filter-added chat-id filter]))))
(handlers/register-handler-fx
::filter-added
(fn [{:keys [db]} [_ chat-id filter]]
{:db (assoc-in db [:transport/chats chat-id :filter] filter)}))
(let [params {:topics [topic]
:symKeyID sym-key-id}
callback (fn [js-error js-message]
(re-frame/dispatch [:transport/messages-received js-error js-message chat-id]))]
(add-filter! web3 params callback chat-id))))
(re-frame/reg-fx
:shh/add-discovery-filter
(fn [{:keys [web3 private-key-id topic]}]
(when-let [filter (add-filter! web3
{:topics [topic]
:privateKeyID private-key-id}
(fn [js-error js-message]
(re-frame/dispatch [:protocol/receive-whisper-message js-error js-message])))]
(re-frame/dispatch [::discovery-filter-added filter]))))
(let [params {:topics [topic]
:privateKeyID private-key-id}
callback (fn [js-error js-message]
(re-frame/dispatch [:transport/messages-received js-error js-message]))]
(add-filter! web3 params callback :discovery-topic))))
(handlers/register-handler-fx
::discovery-filter-added
(fn [{:keys [db]} [_ filter]]
{:db (assoc db :transport/discovery-filter filter)}))
:shh.callback/filter-added
(fn [{:keys [db] :as cofx} [_ topic chat-id filter]]
(fx/merge cofx
{:db (assoc-in db [:transport/filters chat-id] filter)}
(inbox/upsert-inbox-topic {:topic topic
:chat-id chat-id}))))
(re-frame/reg-fx
:shh/remove-filter

View File

@ -1,214 +0,0 @@
(ns ^{:doc "Events for message handling"}
status-im.transport.handlers
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message :as models.message]
[status-im.data-store.transport :as transport-store]
[status-im.transport.message.core :as message]
[status-im.transport.message.transit :as transit]
[status-im.transport.message.v1.contact :as v1.contact]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.shh :as shh]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[taoensso.timbre :as log]))
(fx/defn update-last-received-from-inbox
"Distinguishes messages that are expired from those that are not
Expired messages are coming from offline inboxing"
[{:keys [db now] :as cofx} now-in-s timestamp ttl]
(when (> (- now-in-s timestamp) ttl)
{:db (assoc db :inbox/last-received now)}))
(fx/defn receive-message
[cofx now-in-s chat-id js-message]
(let [{:keys [payload sig timestamp ttl]} (js->clj js-message :keywordize-keys true)
status-message (-> payload
transport.utils/to-utf8
transit/deserialize)]
(when (and sig status-message)
(try
(when-let [valid-message (message/validate status-message)]
(fx/merge (assoc cofx :js-obj js-message)
#(message/receive valid-message (or chat-id sig) sig timestamp %)
(update-last-received-from-inbox now-in-s timestamp ttl)))
(catch :default e nil))))) ; ignore unknown message types
(defn- js-array->seq [array]
(for [i (range (.-length array))]
(aget array i)))
(fx/defn receive-whisper-messages
[{:keys [now] :as cofx} js-error js-messages chat-id]
(if (and (not js-error)
js-messages)
(let [now-in-s (quot now 1000)
receive-message-fxs (map (fn [message]
(receive-message now-in-s chat-id message))
(js-array->seq js-messages))]
(apply fx/merge cofx receive-message-fxs))
(do (log/error "Something went wrong" js-error js-messages)
cofx)))
(handlers/register-handler-fx
:protocol/receive-whisper-message
[handlers/logged-in (re-frame/inject-cofx :random-id-generator)]
(fn [cofx [_ js-error js-messages chat-id]]
(receive-whisper-messages cofx js-error js-messages chat-id)))
(handlers/register-handler-fx
:protocol/send-status-message-error
(fn [{:keys [db] :as cofx} [_ err]]
(log/error :send-status-message-error err)))
(handlers/register-handler-fx
:contact/send-new-sym-key
(fn [{:keys [db] :as cofx}
[_ {:keys [chat-id topic message sym-key sym-key-id]}]]
(let [{:keys [web3 current-public-key]} db
chat-transport-info (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id
:sym-key sym-key
:topic topic))]
(fx/merge cofx
{:db (assoc-in db [:transport/chats chat-id] chat-transport-info)
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic topic
:chat-id chat-id}
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat chat-transport-info})]}
#(message/send (v1.contact/NewContactKey. sym-key topic message)
chat-id %)))))
(handlers/register-handler-fx
:contact/add-new-sym-key
(fn [{:keys [db] :as cofx} [_ {:keys [sym-key-id sym-key chat-id topic timestamp message]}]]
(let [{:keys [web3 current-public-key]} db
chat-transport-info (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id
:sym-key sym-key
:topic topic))]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id]
chat-transport-info)
:dispatch [:inbox/request-chat-history chat-id]
:shh/add-filter {:web3 web3
:sym-key-id sym-key-id
:topic topic
:chat-id chat-id}
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat chat-transport-info})]}
#(message/receive message chat-id chat-id timestamp %)))))
(handlers/register-handler-fx
:group/unsubscribe-from-chat
(fn [cofx [_ chat-id]]
(transport.utils/unsubscribe-from-chat chat-id cofx)))
(re-frame/reg-fx
;; TODO(janherich): this should be called after `:data-store/tx` actually
:confirm-messages-processed
(fn [messages]
(let [{:keys [web3]} (first messages)
js-messages (->> messages
(keep :js-obj)
(apply array))]
(when (pos? (.-length js-messages))
(.confirmMessagesProcessed (transport.utils/shh web3)
js-messages
(fn [err resp]
(when err
(log/info "Confirming messages processed failed"))))))))
(handlers/register-handler-fx
:transport/set-message-envelope-hash
;; message-type is used for tracking
(fn [{:keys [db]} [_ chat-id message-id message-type envelope-hash-js]]
;; TODO (cammellos): For group messages it returns multiple hashes, for now
;; we naively assume that if one is sent the batch is ok
(let [envelope-hash (js->clj envelope-hash-js)
hash (if (vector? envelope-hash)
(last envelope-hash)
envelope-hash)]
{:db (assoc-in db [:transport/message-envelopes hash]
{:chat-id chat-id
:message-id message-id
:message-type message-type})})))
(handlers/register-handler-fx
:transport/set-contact-message-envelope-hash
(fn [{:keys [db]} [_ chat-id envelope-hash]]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-type :contact-message})}))
(fx/defn remove-hash [{:keys [db] :as cofx} envelope-hash]
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
(fx/defn update-resend-contact-message [{:keys [db] :as cofx} chat-id]
(let [chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? nil)]
{:db (assoc-in db [:transport/chats chat-id :resend?] nil)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}))
(fx/defn update-envelope-status
[{:keys [db] :as cofx} envelope-hash status]
(let [{:keys [chat-id message-type message-id]}
(get-in db [:transport/message-envelopes envelope-hash])]
(case message-type
:contact-message
(when (= :sent status)
(fx/merge cofx
(remove-hash envelope-hash)
(update-resend-contact-message chat-id)))
(when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])]
(let [{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(fx/merge cofx
(remove-hash envelope-hash)
(models.message/update-message-status chat-id message-id status)
(models.message/send-push-notification fcm-token status)))))))
(defn- own-info [db]
(let [{:keys [name photo-path address]} (:account/account db)
fcm-token (get-in db [:notifications :fcm-token])]
{:name name
:profile-image photo-path
:address address
:fcm-token fcm-token}))
(fx/defn resend-contact-request [cofx own-info chat-id {:keys [sym-key topic]}]
(message/send (v1.contact/NewContactKey. sym-key
topic
(v1.contact/map->ContactRequest own-info))
chat-id cofx))
(fx/defn resend-contact-message
[cofx own-info chat-id]
(let [{:keys [resend?] :as chat} (get-in cofx [:db :transport/chats chat-id])]
(case resend?
"contact-request"
(resend-contact-request cofx own-info chat-id chat)
"contact-request-confirmation"
(message/send (v1.contact/map->ContactRequestConfirmed own-info)
chat-id
cofx)
"contact-update"
(protocol/send cofx
{:chat-id chat-id
:payload (v1.contact/map->ContactUpdate own-info)})
nil)))
(fx/defn resend-contact-messages
[{:keys [db] :as cofx} previous-summary]
(when (and (zero? (count previous-summary))
(= :online (:network-status db))
(pos? (count (:peers-summary db))))
(let [own-info (own-info db)
resend-contact-message-fxs (map (fn [chat-id]
(resend-contact-message own-info chat-id))
(keys (:transport/chats db)))]
(apply fx/merge cofx resend-contact-message-fxs))))

View File

@ -1,27 +1,26 @@
(ns status-im.transport.impl.receive
(:require
[status-im.models.contact :as models.contact]
[status-im.group-chats.core :as group-chats]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.contact :as transport.contact]
[status-im.transport.message.v1.core :as transport.protocol]))
(:require [status-im.group-chats.core :as group-chats]
[status-im.models.contact :as models.contact]
[status-im.transport.message.contact :as transport.contact]
[status-im.transport.message.group-chat :as transport.group-chat]
[status-im.transport.message.protocol :as protocol]))
(extend-type transport.protocol/GroupMembershipUpdate
message/StatusMessage
(extend-type transport.group-chat/GroupMembershipUpdate
protocol/StatusMessage
(receive [this _ signature _ cofx]
(group-chats/handle-membership-update-received cofx this signature)))
(extend-type transport.contact/ContactRequest
message/StatusMessage
protocol/StatusMessage
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request signature timestamp this cofx)))
(extend-type transport.contact/ContactRequestConfirmed
message/StatusMessage
protocol/StatusMessage
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-request-confirmation signature timestamp this cofx)))
(extend-type transport.contact/ContactUpdate
message/StatusMessage
protocol/StatusMessage
(receive [this _ signature timestamp cofx]
(models.contact/receive-contact-update signature timestamp this cofx)))

View File

@ -1,10 +1,9 @@
(ns status-im.transport.impl.send
(:require
[status-im.group-chats.core :as group-chats]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.core :as transport]))
(:require [status-im.group-chats.core :as group-chats]
[status-im.transport.message.group-chat :as transport.group-chat]
[status-im.transport.message.protocol :as protocol]))
(extend-type transport/GroupMembershipUpdate
message/StatusMessage
(extend-type transport.group-chat/GroupMembershipUpdate
protocol/StatusMessage
(send [this chat-id cofx]
(group-chats/send-membership-update cofx this chat-id)))

View File

@ -2,7 +2,6 @@
status-im.transport.inbox
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.data-store.accounts :as accounts-store]
[status-im.data-store.core :as data-store]
[status-im.data-store.transport :as transport-store]
[status-im.fleet.core :as fleet]
@ -10,38 +9,28 @@
[status-im.native-module.core :as status]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]))
;; How does offline inboxing work ?
;;
;; - We send a request to the mailserver, we are only interested in the
;; messages since `last-request`, the time of the last successful request,
;; messages since `last-request` up to the last seven days
;; and the last 24 hours for topics that were just joined
;; - The mailserver doesn't directly respond to the request and
;; instead we start receiving messages in the filters for the requested
;; topics.
;; - These messages are expired that is how we differentiate them from
;; normal whisper messages to update last-received
;; - After fetching-timeout is reached since the last mailserver message
;; was received without a connection incident, we consider the request
;; successfull and update `last-request` and `fetch-history?` fields of each
;; topic to false
;; - If the mailserver was not ready when we tried for instance to request
;; the history of a topic after joining a chat, the request will be done
;; as soon as the mailserver becomes available
(def one-day (* 24 3600))
(def seven-days (* 7 one-day))
(def connection-timeout
"Time after which mailserver connection is considered to have failed"
15000)
(def fetching-timeout
"Time we should wait after last message was fetch from mailserver before we
consider it done
Needs to be at least 10 seconds because that is the time it takes for the app
to realize it was disconnected"
10000)
5000)
(defn- parse-json
;; NOTE(dmitryn) Expects JSON response like:
@ -61,7 +50,7 @@
(catch :default e
{:error (.-message e)})))
(defn- response-handler [error-fn success-fn]
(defn- response-handler [success-fn error-fn]
(fn handle-response
([response]
(let [{:keys [error result]} (parse-json response)]
@ -71,126 +60,86 @@
(error-fn error)
(success-fn result)))))
(fx/defn add-sym-key-id-to-wnode
[{:keys [db]} {:keys [id]} sym-key-id]
(let [current-fleet (fleet/current-fleet db)]
{:db (assoc-in db [:inbox/wnodes current-fleet id :sym-key-id] sym-key-id)}))
(defn add-peer! [wnode]
(status/add-peer wnode
(response-handler #(log/debug "offline inbox: add-peer success" %)
#(log/error "offline inbox: add-peer error" %))))
(defn registered-peer? [peers enode]
(re-frame/reg-fx
:transport.inbox/add-peer
(fn [wnode]
(add-peer! wnode)))
(defn mark-trusted-peer! [web3 enode]
(.markTrustedPeer (transport.utils/shh web3)
enode
(fn [error response]
(if error
(re-frame/dispatch [:inbox.callback/mark-trusted-peer-error error])
(re-frame/dispatch [:inbox.callback/mark-trusted-peer-success response])))))
(re-frame/reg-fx
:transport.inbox/mark-trusted-peer
(fn [{:keys [wnode web3]}]
(mark-trusted-peer! web3 wnode)))
(fx/defn generate-mailserver-symkey
[{:keys [db] :as cofx} {:keys [password id] :as wnode}]
(let [current-fleet (fleet/current-fleet db)]
{:db (assoc-in db [:inbox/wnodes current-fleet id :generating-sym-key?] true)
:shh/generate-sym-key-from-password
[{:password password
:web3 (:web3 db)
:on-success (fn [_ sym-key-id]
(re-frame/dispatch [:inbox.callback/generate-mailserver-symkey-success wnode sym-key-id]))
:on-error #(log/error "offline inbox: get-sym-key error" %)}]}))
(defn registered-peer?
"truthy if the enode is a registered peer"
[peers enode]
(let [peer-ids (into #{} (map :id) peers)
enode-id (transport.utils/extract-enode-id enode)]
(contains? peer-ids enode-id)))
(defn add-peer [enode success-fn error-fn]
(status/add-peer enode (response-handler error-fn success-fn)))
(defn update-mailserver-status [db state]
(assoc db :mailserver-status state))
(defn mark-trusted-peer [web3 enode success-fn error-fn]
(.markTrustedPeer (transport.utils/shh web3)
enode
(fn [err resp]
(if-not err
(success-fn resp)
(error-fn err)))))
(fx/defn mark-trusted-peer
[{:keys [db] :as cofx}]
(let [{:keys [address sym-key-id generating-sym-key?] :as wnode} (mailserver/fetch-current cofx)]
(fx/merge cofx
{:db (update-mailserver-status db :added)
:transport.inbox/mark-trusted-peer {:web3 (:web3 db)
:wnode address}}
(when-not (or sym-key-id generating-sym-key?)
(generate-mailserver-symkey wnode)))))
(def one-day (* 24 3600))
(def seven-days (* 7 one-day))
(defn request-inbox-messages-params [mailserver from to topics]
(let [days (conj
(into [] (range from to one-day))
to)
day-ranges (map vector days (drop 1 days))]
(for [topic topics
[current-from current-to] day-ranges]
{:topic topic
:mailServerPeer (:address mailserver)
:symKeyID (:sym-key-id mailserver)
:from current-from
:to current-to})))
(defn request-inbox-messages
[web3 mailserver topics start-from end-to success-fn error-fn]
(log/info "offline inbox: request-messages request for topics " topics " from " start-from " to " end-to)
(doseq [{:keys [topic] :as params} (request-inbox-messages-params
mailserver
start-from
end-to
topics)]
(log/info "offline inbox: request-messages for: "
" topic " topic
" from " (:from params)
" to " (:to params))
(.requestMessages (transport.utils/shh web3)
(clj->js params)
(fn [err resp]
(if-not err
(success-fn resp topic)
(error-fn err topic))))))
(re-frame/reg-fx
::add-peer
(fn [{:keys [wnode]}]
(add-peer wnode
#(log/debug "offline inbox: add-peer success" %)
#(log/error "offline inbox: add-peer error" %))))
(re-frame/reg-fx
::mark-trusted-peer
(fn [{:keys [wnode web3]}]
(mark-trusted-peer web3
wnode
#(re-frame/dispatch [:inbox/mailserver-trusted %])
#(re-frame/dispatch [:inbox/check-connection]))))
(re-frame/reg-fx
::request-messages
(fn [params]
(doseq [{:keys [wnode topics to from web3]} params]
(request-inbox-messages web3
wnode
topics
from
to
#(log/info "offline inbox: request-messages response" %1 %2 from to)
#(log/error "offline inbox: request-messages error" %1 %2 from to)))))
(fx/defn update-mailserver-status [{:keys [db]} transition]
(let [state transition]
{:db (assoc db
:mailserver-status state
:inbox/fetching? false)}))
(fx/defn generate-mailserver-symkey [{:keys [db] :as cofx} wnode]
(when-not (:sym-key-id wnode)
{:shh/generate-sym-key-from-password
[{:password (:password wnode)
:web3 (:web3 db)
:on-success (fn [_ sym-key-id]
(re-frame/dispatch [:inbox/get-sym-key-success wnode sym-key-id]))
:on-error #(log/error "offline inbox: get-sym-key error" %)}]}))
(fx/defn add-peer
[{:keys [db] :as cofx}]
(let [{:keys [address sym-key-id generating-sym-key?] :as wnode} (mailserver/fetch-current cofx)]
(fx/merge cofx
{:db (update-mailserver-status db :connecting)
:transport.inbox/add-peer address
:utils/dispatch-later [{:ms connection-timeout
:dispatch [:inbox/check-connection-timeout]}]}
(when-not (or sym-key-id generating-sym-key?)
(generate-mailserver-symkey wnode)))))
(fx/defn connect-to-mailserver
"Add mailserver as a peer using ::add-peer cofx and generate sym-key when
"Add mailserver as a peer using `::add-peer` cofx and generate sym-key when
it doesn't exists
Peer summary will change and we will receive a signal from status go when
this is successful
A connection-check is made after `connection timeout` is reached and
mailserver-status is changed to error if it is not connected by then"
[{:keys [db] :as cofx}]
(let [web3 (:web3 db)
{:keys [address] :as wnode} (mailserver/fetch-current cofx)
peers-summary (:peers-summary db)
connected? (registered-peer? peers-summary address)]
(if connected?
(fx/merge cofx
(update-mailserver-status :connected)
(generate-mailserver-symkey wnode))
(fx/merge cofx
{::add-peer {:wnode address}
:utils/dispatch-later [{:ms connection-timeout
:dispatch [:inbox/check-connection]}]}
(update-mailserver-status :connecting)
(generate-mailserver-symkey wnode)))))
(let [{:keys [address] :as wnode} (mailserver/fetch-current cofx)
{:keys [peers-summary]} db
added? (registered-peer? peers-summary
address)]
(if added?
(mark-trusted-peer cofx)
(add-peer cofx))))
(fx/defn peers-summary-change
"There is only 2 summary changes that require offline inboxing action:
@ -199,183 +148,191 @@
[{:keys [db] :as cofx} previous-summary]
(when (:account/account db)
(let [{:keys [peers-summary peers-count]} db
wnode (:address (mailserver/fetch-current cofx))
mailserver-was-registered? (registered-peer? previous-summary
wnode)
mailserver-is-registered? (registered-peer? peers-summary
wnode)
;; the mailserver just connected
mailserver-connected? (and mailserver-is-registered?
(not mailserver-was-registered?))
;; the mailserver just disconnected
mailserver-disconnected? (and mailserver-was-registered?
(not mailserver-is-registered?))]
{:keys [address sym-key-id] :as wnode} (mailserver/fetch-current cofx)
mailserver-was-registered? (registered-peer? previous-summary
address)
mailserver-is-registered? (registered-peer? peers-summary
address)
mailserver-added? (and mailserver-is-registered?
(not mailserver-was-registered?))
mailserver-removed? (and mailserver-was-registered?
(not mailserver-is-registered?))]
(cond
mailserver-disconnected?
(connect-to-mailserver cofx)
mailserver-added?
(mark-trusted-peer cofx)
mailserver-removed?
(connect-to-mailserver cofx)))))
mailserver-connected?
{::mark-trusted-peer {:web3 (:web3 db)
:wnode wnode}}))))
(defn request-messages! [web3 {:keys [sym-key-id address]} {:keys [topic to from]}]
(log/info "offline inbox: request-messages for: "
" topic " topic
" from " from
" to " to)
(.requestMessages (transport.utils/shh web3)
(clj->js {:topic topic
:mailServerPeer address
:symKeyID sym-key-id
:timeout 20
:from from
:to to})
(fn [err request-id]
(if-not err
(re-frame/dispatch [:inbox.callback/request-messages-success {:topic topic
:request-id request-id
:from from
:to to}])
(log/error "offline inbox: messages request error for topic " topic ": " err)))))
(defn inbox-ready? [{:keys [sym-key-id]} {:keys [db]}]
(let [mailserver-status (:mailserver-status db)]
(and (= :connected mailserver-status)
sym-key-id)))
(re-frame/reg-fx
:transport.inbox/request-messages
(fn [{:keys [web3 wnode requests]}]
(doseq [request requests]
(request-messages! web3 wnode request))))
(defn get-request-messages-topics
"Returns topics for which full history has already been recovered"
[db]
(conj (map :topic
(remove :fetch-history?
(vals (:transport/chats db))))
(transport.utils/get-topic constants/contact-discovery)))
(defn prepare-request [now-in-s topic {:keys [last-request request-pending?]}]
(when-not request-pending?
{:from (max last-request
(- now-in-s one-day))
:to now-in-s
:topic topic}))
(defn get-request-history-topics
"Returns topics for which full history has not been recovered"
[db]
(map :topic
(filter :fetch-history?
(vals (:transport/chats db)))))
(defn prepare-requests [now-in-s topics]
(remove nil? (map (fn [[topic inbox-topic]]
(prepare-request now-in-s topic inbox-topic))
topics)))
(defn request-history-span [now-in-s]
(- now-in-s one-day))
(defn get-wnode-when-ready
"return the wnode if the inbox is ready"
[{:keys [db] :as cofx}]
(let [{:keys [sym-key-id] :as wnode} (mailserver/fetch-current cofx)
mailserver-status (:mailserver-status db)]
(when (and (= :connected mailserver-status)
sym-key-id)
wnode)))
(fx/defn request-messages
[{:keys [db now] :as cofx}]
(let [wnode (mailserver/fetch-current cofx)
web3 (:web3 db)
now-in-s (quot now 1000)
last-request (max
(get-in db [:account/account :last-request])
(- now-in-s seven-days))
request-messages-topics (get-request-messages-topics db)
request-history-topics (get-request-history-topics db)]
(when (inbox-ready? wnode cofx)
{::request-messages [{:wnode wnode
:topics request-messages-topics
:from last-request
:to now-in-s
:web3 web3}
{:wnode wnode
:from (request-history-span now-in-s)
:to now-in-s
:topics request-history-topics
:web3 web3}]
:db (assoc db :inbox/fetching? true)
:dispatch-later [{:ms fetching-timeout
:dispatch [:inbox/check-fetching now-in-s]}]})))
"request messages if the inbox is ready"
[{:keys [db now] :as cofx} topic]
(when-let [wnode (get-wnode-when-ready cofx)]
(let [web3 (:web3 db)
now-in-s (quot now 1000)
requests (if topic
[(prepare-request now-in-s topic (get-in db [:transport.inbox/topics topic]))]
(prepare-requests now-in-s (:transport.inbox/topics db)))]
{:transport.inbox/request-messages {:web3 web3
:wnode wnode
:requests requests}})))
(fx/defn request-chat-history [{:keys [db now] :as cofx} chat-id]
(let [wnode (mailserver/fetch-current cofx)
web3 (:web3 db)
topic (get-in db [:transport/chats chat-id :topic])
now-in-s (quot now 1000)]
(when (inbox-ready? wnode cofx)
{::request-messages [{:wnode wnode
:topics [topic]
:from (request-history-span now-in-s)
:to now-in-s
:web3 web3}]
:db (assoc db :inbox/fetching? true)
:dispatch-later [{:ms fetching-timeout
:dispatch [:inbox/check-fetching now-in-s chat-id]}]})))
;;;; Handlers
(handlers/register-handler-fx
:inbox/mailserver-trusted
(fn [{:keys [db] :as cofx} _]
(fx/merge cofx
(update-mailserver-status :connected)
(request-messages))))
(handlers/register-handler-fx
:inbox/get-sym-key-success
(fn [{:keys [db] :as cofx} [_ wnode sym-key-id]]
(fx/merge cofx
(add-sym-key-id-to-wnode wnode sym-key-id)
(request-messages))))
(handlers/register-handler-fx
:inbox/request-chat-history
(fn [{:keys [db] :as cofx} [_ chat-id]]
(request-chat-history cofx chat-id)))
(handlers/register-handler-fx
:inbox/check-connection
(fn [{:keys [db] :as cofx} _]
(when (= :connecting (:mailserver-status db))
(if (mailserver/preferred-mailserver-id cofx)
(update-mailserver-status cofx :error)
(fx/merge cofx
(mailserver/set-current-mailserver)
(connect-to-mailserver))))))
(fx/defn update-last-request
[{:keys [db]} last-request]
(let [chats (:transport/chats db)
transport-txs (reduce (fn [txs [chat-id chat]]
(if (:fetch-history? chat)
(conj txs
(transport-store/save-transport-tx
{:chat-id chat-id
:chat (assoc chat
:fetch-history? false)}))
txs))
[]
chats)
chats-update (reduce (fn [acc [chat-id chat]]
(if (:fetch-history? chat)
(assoc acc chat-id (assoc chat :fetch-history? false))
(assoc acc chat-id chat)))
{}
chats)]
{:db (-> db
(assoc :transport/chats chats-update)
(assoc-in [:account/account :last-request]
last-request))
:data-store/base-tx [(accounts-store/save-account-tx
(assoc (:account/account db)
:last-request last-request))]
:data-store/tx transport-txs}))
(fx/defn update-fetch-history [{:keys [db]} chat-id]
{:db (assoc-in db
[:transport/chats chat-id :fetch-history?]
false)
:data-store/tx [(transport-store/save-transport-tx
{:chat-id chat-id
:chat (assoc (get-in db [:transport/chats chat-id])
:fetch-history? false)})]})
(fx/defn initialize-offline-inbox [cofx custom-mailservers]
(fx/defn add-mailserver-trusted
"the current mailserver has been trusted
update mailserver status to `:connected` and request messages
if wnode is ready"
[{:keys [db] :as cofx}]
(fx/merge cofx
(mailserver/add-custom-mailservers custom-mailservers)
(mailserver/set-initial-last-request)
(mailserver/set-current-mailserver)))
{:db (update-mailserver-status db :connected)}
(request-messages nil)))
(handlers/register-handler-fx
:inbox/check-fetching
(fn [{:keys [db now] :as cofx} [_ last-request chat-id]]
(when (and (:inbox/fetching? db)
;; if chat was removed before messages were fetched no need
;; to proceed with further actions
(or (not chat-id) (contains? (:transport/chats db) chat-id)))
(let [time-since-last-received (- now (:inbox/last-received db))]
(if (> time-since-last-received fetching-timeout)
(if chat-id
(fx/merge cofx
{:db (assoc db :inbox/fetching? false)}
(update-fetch-history chat-id))
(fx/merge cofx
{:db (assoc db :inbox/fetching? false)}
(update-last-request last-request)))
{:dispatch-later [{:ms (- fetching-timeout
time-since-last-received)
:dispatch [:inbox/check-fetching last-request chat-id]}]})))))
(fx/defn add-mailserver-sym-key
"the current mailserver sym-key has been generated
add sym-key to the wnode in app-db and request messages if
wnode is ready"
[{:keys [db] :as cofx} {:keys [id]} sym-key-id]
(let [current-fleet (fleet/current-fleet db)]
(fx/merge cofx
{:db (-> db
(assoc-in [:inbox/wnodes current-fleet id :sym-key-id] sym-key-id)
(update-in [:inbox/wnodes current-fleet id] dissoc :generating-sym-key?))}
(request-messages nil))))
(handlers/register-handler-fx
:inbox/reconnect
(fn [cofx [_ args]]
(connect-to-mailserver cofx)))
(fx/defn check-connection
"check if mailserver is connected
mark mailserver status as `:error` if custom mailserver is used
otherwise try to reconnect to another mailserver"
[{:keys [db] :as cofx}]
(when (= :connecting (:mailserver-status db))
(if (mailserver/preferred-mailserver-id cofx)
{:db (update-mailserver-status db :error)}
(fx/merge cofx
(mailserver/set-current-mailserver)
(connect-to-mailserver)))))
(fx/defn remove-chat-from-inbox-topic
"if the chat is the only chat of the inbox topic delete the inbox topic
otherwise remove the chat-id of the chat from the inbox topic and save"
[{:keys [db now] :as cofx} chat-id]
(let [topic (get-in db [:transport/chats chat-id :topic])
{:keys [chat-ids] :as inbox-topic} (update (get-in db [:transport.inbox/topics topic])
:chat-ids
disj chat-id)]
(if (empty? chat-ids)
{:db (update db :transport.inbox/topics dissoc topic)
:data-store/tx [(transport-store/delete-transport-inbox-topic-tx topic)]}
{:db (assoc-in db [:transport.inbox/topics topic] inbox-topic)
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]})))
(fx/defn update-inbox-topic
"TODO: add support for cursors
if there is a cursor, do not update `request-to` and `request-from`"
[{:keys [db now] :as cofx} {:keys [request-id cursor]}]
(let [{:keys [from to topic]} (get-in db [:transport.inbox/requests request-id])
inbox-topic (-> (get-in db [:transport.inbox/topics topic])
(assoc :last-request to)
(dissoc :request-pending?))]
(fx/merge cofx
{:db (-> db
(update :transport.inbox/requests dissoc request-id)
(assoc-in [:transport.inbox/topics topic] inbox-topic))
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]})))
(fx/defn upsert-inbox-topic
"if the chat-id is already in the topic we do nothing, otherwise we update
the topic
if the topic already existed we add the chat-id andreset last-request
because there was no filter for the chat and messages were ignored
if the topic didn't exist we created"
[{:keys [db] :as cofx} {:keys [topic chat-id]}]
(let [{:keys [chat-ids last-request] :as current-inbox-topic}
(get-in db [:transport.inbox/topics topic] {:chat-ids #{}})]
(when-let [inbox-topic (when-not (chat-ids chat-id)
(-> current-inbox-topic
(assoc :last-request 1)
(update :chat-ids conj chat-id)))]
(fx/merge cofx
{:db (assoc-in db [:transport.inbox/topics topic] inbox-topic)
:data-store/tx [(transport-store/save-transport-inbox-topic-tx
{:topic topic
:inbox-topic inbox-topic})]}
(request-messages topic)))))
(fx/defn resend-request
[{:keys [db] :as cofx} {:keys [request-id]}]
(let [{:keys [from to topic]} (get-in db [:transport.inbox/requests request-id])]
(log/info "offline inbox: message request" request-id " expired for inbox topic" topic "from" from "to" to)
(fx/merge cofx
{:db (-> db
(update :transport.inbox/requests dissoc request-id)
(update-in [:transport.inbox/topics topic] dissoc :request-pending?))}
(request-messages topic))))
(fx/defn add-request
[{:keys [db] :as cofx} {:keys [topic request-id from to]}]
(log/info "offline inbox: message request " request-id "sent for inbox topic" topic "from" from "to" to)
{:db (-> db
(assoc-in [:transport.inbox/requests request-id] {:from from
:to to
:topic topic})
(assoc-in [:transport.inbox/topics topic :request-pending?] true))})
(fx/defn initialize-offline-inbox
[cofx custom-mailservers]
(let [discovery-topic (transport.utils/get-topic constants/contact-discovery)]
(fx/merge cofx
(mailserver/add-custom-mailservers custom-mailservers)
(mailserver/set-current-mailserver)
(when-not (get-in cofx [:db :transport.inbox/topics discovery-topic])
(upsert-inbox-topic {:topic discovery-topic
:chat-id :discovery-topic})))))

View File

@ -0,0 +1,96 @@
(ns ^{:doc "Contact request and update API"}
status-im.transport.message.contact
(:require [cljs.spec.alpha :as spec]
[status-im.data-store.transport :as transport-store]
[status-im.transport.db :as transport.db]
[status-im.transport.message.protocol :as protocol]
[status-im.utils.fx :as fx]))
(defrecord ContactRequest [name profile-image address fcm-token]
protocol/StatusMessage
(send [this chat-id {:keys [db random-id-generator] :as cofx}]
(fx/merge cofx
(protocol/init-chat {:chat-id chat-id
:resend? "contact-request"})
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event [:transport/contact-message-sent chat-id]})))
(validate [this]
(when (spec/valid? :message/contact-request this)
this)))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
protocol/StatusMessage
(send [this chat-id {:keys [db] :as cofx}]
(let [success-event [:transport/contact-message-sent chat-id]
chat (get-in db [:transport/chats chat-id])
updated-chat (if chat
(assoc chat :resend? "contact-request-confirmation")
(transport.db/create-chat {:resend? "contact-request-confirmation"}))]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id] updated-chat)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}))))
(validate [this]
(when (spec/valid? :message/contact-request-confirmed this)
this)))
(fx/defn send-contact-update
[{:keys [db] :as cofx} chat-id payload]
(when-let [chat (get-in cofx [:db :transport/chats chat-id])]
(let [updated-chat (assoc chat :resend? "contact-update")
tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]
success-event [:transport/contact-message-sent chat-id]]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send-with-pubkey {:chat-id chat-id
:payload payload
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image address fcm-token]
protocol/StatusMessage
(send [this _ {:keys [db] :as cofx}]
;;TODO: here we look for contact which have a :public-key to differentiate
;;actual contacts from dapps
;;This is not an ideal solution and we should think about a more reliable way
;;to do this when we refactor app-db
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key pending?]}]]
(if (and public-key
(not pending?))
(conj acc public-key)
acc))
#{}
(:contacts/contacts db))
;;NOTE: chats with contacts use public-key as chat-id
send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)]
(apply fx/merge cofx send-contact-update-fxs)))
(validate [this]
(when (spec/valid? :message/contact-update this)
this)))
(fx/defn remove-chat-filter
"Stops the filter for the given chat-id"
[{:keys [db]} chat-id]
(when-let [filter (get-in db [:transport/filters chat-id])]
{:shh/remove-filter filter}))
(defrecord NewContactKey [sym-key topic message]
protocol/StatusMessage
(send
;; no-op, we don't send NewContactKey anymore
[this chat-id cofx])
(receive
;;for compatibility with old clients, we only care about the message within
[this chat-id _ timestamp {:keys [db] :as cofx}]
(protocol/receive message chat-id chat-id timestamp cofx))
(validate [this]
(when (spec/valid? :message/new-contact-key this)
this)))

View File

@ -1,8 +1,143 @@
(ns ^{:doc "Definition of the StatusMessage protocol"}
status-im.transport.message.core)
status-im.transport.message.core
(:require [re-frame.core :as re-frame]
[status-im.chat.models.message :as models.message]
[status-im.data-store.transport :as transport-store]
[status-im.transport.message.contact :as contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defprotocol StatusMessage
"Protocol for the messages that are sent through the transport layer"
(send [this chat-id cofx] "Method producing all effects necessary for sending the message record")
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record")
(validate [this] "Method returning the message if it is valid or nil if it is not"))
(fx/defn receive-message
[cofx now-in-s chat-id js-message]
(let [{:keys [payload sig timestamp ttl]} (js->clj js-message :keywordize-keys true)
status-message (-> payload
transport.utils/to-utf8
transit/deserialize)]
(when (and sig status-message)
(try
(when-let [valid-message (protocol/validate status-message)]
(fx/merge (assoc cofx :js-obj js-message)
#(protocol/receive valid-message (or chat-id sig) sig timestamp %)))
(catch :default e nil))))) ; ignore unknown message types
(defn- js-array->seq [array]
(for [i (range (.-length array))]
(aget array i)))
(fx/defn receive-whisper-messages
[{:keys [now] :as cofx} js-error js-messages chat-id]
(if (and (not js-error)
js-messages)
(let [now-in-s (quot now 1000)
receive-message-fxs (map (fn [message]
(receive-message now-in-s chat-id message))
(js-array->seq js-messages))]
(apply fx/merge cofx receive-message-fxs))
(do (log/error "Something went wrong" js-error js-messages)
cofx)))
(fx/defn remove-hash
[{:keys [db] :as cofx} envelope-hash]
{:db (update db :transport/message-envelopes dissoc envelope-hash)})
(fx/defn update-resend-contact-message
[{:keys [db] :as cofx} chat-id]
(let [chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? nil)]
{:db (assoc-in db [:transport/chats chat-id :resend?] nil)
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}))
(fx/defn update-envelope-status
[{:keys [db] :as cofx} envelope-hash status]
(let [{:keys [chat-id message-type message-id]}
(get-in db [:transport/message-envelopes envelope-hash])]
(case message-type
:contact-message
(when (= :sent status)
(fx/merge cofx
(remove-hash envelope-hash)
(update-resend-contact-message chat-id)))
(when-let [{:keys [from]} (get-in db [:chats chat-id :messages message-id])]
(let [{:keys [fcm-token]} (get-in db [:contacts/contacts chat-id])]
(fx/merge cofx
(remove-hash envelope-hash)
(models.message/update-message-status chat-id message-id status)
(models.message/send-push-notification fcm-token status)))))))
(fx/defn set-contact-message-envelope-hash
[{:keys [db] :as cofx} chat-id envelope-hash]
{:db (assoc-in db [:transport/message-envelopes envelope-hash]
{:chat-id chat-id
:message-type :contact-message})})
(fx/defn set-message-envelope-hash
"message-type is used for tracking
TODO (cammellos): For group messages it returns multiple hashes, for now
we naively assume that if one is sent the batch is ok"
[{:keys [db] :as cofx} chat-id message-id message-type envelope-hash-js]
(let [envelope-hash (js->clj envelope-hash-js)
hash (if (vector? envelope-hash)
(last envelope-hash)
envelope-hash)]
{:db (assoc-in db [:transport/message-envelopes hash]
{:chat-id chat-id
:message-id message-id
:message-type message-type})}))
(defn- own-info [db]
(let [{:keys [name photo-path address]} (:account/account db)
fcm-token (get-in db [:notifications :fcm-token])]
{:name name
:profile-image photo-path
:address address
:fcm-token fcm-token}))
(fx/defn resend-contact-request [cofx own-info chat-id {:keys [sym-key topic]}]
(protocol/send (contact/map->ContactRequest own-info)
chat-id cofx))
(fx/defn resend-contact-message
[cofx own-info chat-id]
(let [{:keys [resend?] :as chat} (get-in cofx [:db :transport/chats chat-id])]
(case resend?
"contact-request"
(resend-contact-request cofx own-info chat-id chat)
"contact-request-confirmation"
(protocol/send (contact/map->ContactRequestConfirmed own-info)
chat-id
cofx)
"contact-update"
(protocol/send-with-pubkey cofx
{:chat-id chat-id
:payload (contact/map->ContactUpdate own-info)})
nil)))
(fx/defn resend-contact-messages
[{:keys [db] :as cofx} previous-summary]
(when (and (zero? (count previous-summary))
(= :online (:network-status db))
(pos? (count (:peers-summary db))))
(let [own-info (own-info db)
resend-contact-message-fxs (map (fn [chat-id]
(resend-contact-message own-info chat-id))
(keys (:transport/chats db)))]
(apply fx/merge cofx resend-contact-message-fxs))))
(re-frame/reg-fx
;; TODO(janherich): this should be called after `:data-store/tx` actually
:transport/confirm-messages-processed
(fn [messages]
(let [{:keys [web3]} (first messages)
js-messages (->> messages
(keep :js-obj)
(apply array))]
(when (pos? (.-length js-messages))
(.confirmMessagesProcessed (transport.utils/shh web3)
js-messages
(fn [err resp]
(when err
(log/info "Confirming messages processed failed"))))))))

View File

@ -1,11 +1,11 @@
(ns status-im.transport.message.v1.core
(:require [status-im.transport.message.core :as message]
[taoensso.timbre :as log]
[cljs.spec.alpha :as spec]))
(ns status-im.transport.message.group-chat
(:require [cljs.spec.alpha :as spec]
[status-im.transport.message.protocol :as protocol]
[taoensso.timbre :as log]))
(defrecord GroupMembershipUpdate
[chat-id membership-updates message]
message/StatusMessage
protocol/StatusMessage
(validate [this]
(if (spec/valid? :message/group-membership-update this)
this

View File

@ -1,17 +1,19 @@
(ns ^{:doc "Protocol API and protocol utils"}
status-im.transport.message.v1.protocol
(:require [status-im.utils.config :as config]
[status-im.constants :as constants]
[taoensso.timbre :as log]
[status-im.utils.config :as config]
status-im.transport.message.protocol
(:require [cljs.spec.alpha :as spec]
[status-im.chat.core :as chat]
[status-im.utils.clocks :as utils.clocks]
[status-im.constants :as constants]
[status-im.transport.db :as transport.db]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.core :as transport]
[status-im.transport.utils :as transport.utils]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[cljs.spec.alpha :as spec]))
[taoensso.timbre :as log]))
(defprotocol StatusMessage
"Protocol for the messages that are sent through the transport layer"
(send [this chat-id cofx] "Method producing all effects necessary for sending the message record")
(receive [this chat-id signature timestamp cofx] "Method producing all effects necessary for receiving the message record")
(validate [this] "Method returning the message if it is valid or nil if it is not"))
(def ^:private whisper-opts
{:ttl 10 ;; ttl of 10 sec
@ -21,15 +23,15 @@
(fx/defn init-chat
"Initialises chat on protocol layer.
If topic is not passed as argument it is derived from `chat-id`"
[{:keys [db]}
{:keys [chat-id topic resend?]
:or {topic (transport.utils/get-topic chat-id)}}]
[{:keys [db now]}
{:keys [chat-id topic resend?]}]
{:db (assoc-in db
[:transport/chats chat-id]
(transport.db/create-chat {:topic topic
:resend? resend?}))})
:resend? resend?
:now now}))})
(fx/defn send
(fx/defn send-with-sym-key
"Sends the payload using symetric key and topic from db (looked up by `chat-id`)"
[{:keys [db] :as cofx} {:keys [payload chat-id success-event]}]
;; we assume that the chat contains the contact public-key
@ -76,11 +78,11 @@
whisper-opts)}]}))
(defrecord Message [content content-type message-type clock-value timestamp]
message/StatusMessage
StatusMessage
(send [this chat-id cofx]
(let [params {:chat-id chat-id
:payload this
:success-event [:transport/set-message-envelope-hash
:success-event [:transport/message-sent
chat-id
(transport.utils/message-id this)
message-type]}]
@ -92,7 +94,7 @@
chat-id
(:success-event params)
this)
(send cofx params))
(send-with-sym-key cofx params))
:user-message
(if config/pfs-encryption-enabled?
@ -116,15 +118,15 @@
(log/warn "failed to validate Message" (spec/explain :message/message this)))))
(defrecord MessagesSeen [message-ids]
message/StatusMessage
StatusMessage
(send [this chat-id cofx]
(if config/pfs-encryption-enabled?
(send-direct-message cofx
chat-id
nil
this)
(send cofx {:chat-id chat-id
:payload this})))
(send-with-sym-key cofx {:chat-id chat-id
:payload this})))
(receive [this chat-id signature _ cofx]
(chat/receive-seen cofx chat-id signature this))
(validate [this]

View File

@ -1,8 +1,8 @@
(ns ^{:doc "Public chat API"}
status-im.transport.message.v1.public-chat
status-im.transport.message.public-chat
(:require [re-frame.core :as re-frame]
[status-im.data-store.transport :as transport-store]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[status-im.utils.handlers :as handlers]))
@ -22,7 +22,8 @@
{:shh/generate-sym-key-from-password [{:web3 (:web3 db)
:password chat-id
:on-success on-success}]}
(protocol/init-chat {:chat-id chat-id})))))
(protocol/init-chat {:chat-id chat-id
:topic (transport.utils/get-topic chat-id)})))))
(handlers/register-handler-fx
::add-new-sym-key
@ -39,5 +40,4 @@
:chat (-> (get-in db [:transport/chats chat-id])
(assoc :sym-key-id sym-key-id)
;;TODO (yenda) remove once go implements persistence
(assoc :sym-key sym-key))})]
:dispatch [:inbox/request-chat-history chat-id]})))
(assoc :sym-key sym-key))})]})))

View File

@ -1,8 +1,8 @@
(ns ^{:doc "Transit custom readers and writers, required when adding a new record implementing StatusMessage protocol"}
status-im.transport.message.transit
(:require [status-im.transport.message.v1.contact :as v1.contact]
[status-im.transport.message.v1.protocol :as v1.protocol]
[status-im.transport.message.v1.core :as v1]
(:require [status-im.transport.message.contact :as contact]
[status-im.transport.message.protocol :as protocol]
[status-im.transport.message.group-chat :as group-chat]
[status-im.constants :as constants]
[cognitect.transit :as transit]))
@ -89,13 +89,13 @@
(def writer (transit/writer :json
{:handlers
{v1.contact/NewContactKey (NewContactKeyHandler.)
v1.contact/ContactRequest (ContactRequestHandler.)
v1.contact/ContactRequestConfirmed (ContactRequestConfirmedHandler.)
v1.contact/ContactUpdate (ContactUpdateHandler.)
v1.protocol/Message (MessageHandler.)
v1.protocol/MessagesSeen (MessagesSeenHandler.)
v1/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}}))
{contact/NewContactKey (NewContactKeyHandler.)
contact/ContactRequest (ContactRequestHandler.)
contact/ContactRequestConfirmed (ContactRequestConfirmedHandler.)
contact/ContactUpdate (ContactUpdateHandler.)
protocol/Message (MessageHandler.)
protocol/MessagesSeen (MessagesSeenHandler.)
group-chat/GroupMembershipUpdate (GroupMembershipUpdateHandler.)}}))
;;
;; Reader handlers
@ -131,22 +131,22 @@
(def reader (transit/reader :json
{:handlers
{"c1" (fn [[sym-key topic message]]
(v1.contact/NewContactKey. sym-key topic message))
(contact/NewContactKey. sym-key topic message))
"c2" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactRequest. name profile-image address fcm-token))
(contact/ContactRequest. name profile-image address fcm-token))
"c3" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactRequestConfirmed. name profile-image address fcm-token))
(contact/ContactRequestConfirmed. name profile-image address fcm-token))
"c4" (fn [[legacy-content content-type message-type clock-value timestamp content]]
(let [[new-content new-content-type] (legacy->new-message-data (or content legacy-content) content-type)]
(v1.protocol/Message. new-content new-content-type message-type clock-value timestamp)))
(protocol/Message. new-content new-content-type message-type clock-value timestamp)))
"c7" (fn [[content content-type message-type clock-value timestamp]]
(v1.protocol/Message. content content-type message-type clock-value timestamp))
(protocol/Message. content content-type message-type clock-value timestamp))
"c5" (fn [message-ids]
(v1.protocol/MessagesSeen. message-ids))
(protocol/MessagesSeen. message-ids))
"c6" (fn [[name profile-image address fcm-token]]
(v1.contact/ContactUpdate. name profile-image address fcm-token))
(contact/ContactUpdate. name profile-image address fcm-token))
"g5" (fn [[chat-id membership-updates message]]
(v1/GroupMembershipUpdate. chat-id membership-updates message))}}))
(group-chat/GroupMembershipUpdate. chat-id membership-updates message))}}))
(defn serialize
"Serializes a record implementing the StatusMessage protocol using the custom writers"

View File

@ -1,142 +0,0 @@
(ns ^{:doc "Contact request and update API"}
status-im.transport.message.v1.contact
(:require [re-frame.core :as re-frame]
[status-im.data-store.transport :as transport-store]
[status-im.transport.message.core :as message]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.utils :as transport.utils]
[status-im.utils.fx :as fx]
[cljs.spec.alpha :as spec]))
(defrecord ContactRequest [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id {:keys [db random-id-generator] :as cofx}]
(let [topic (transport.utils/get-topic (random-id-generator))
on-success (fn [sym-key sym-key-id]
(re-frame/dispatch [:contact/send-new-sym-key
{:sym-key-id sym-key-id
:sym-key sym-key
:chat-id chat-id
:topic topic
:message this}]))]
(fx/merge cofx
{:shh/get-new-sym-keys [{:web3 (:web3 db)
:on-success on-success}]}
(protocol/init-chat {:chat-id chat-id
:topic topic
:resend? "contact-request"}))))
(validate [this]
(when (spec/valid? :message/contact-request this)
this)))
(defrecord ContactRequestConfirmed [name profile-image address fcm-token]
message/StatusMessage
(send [this chat-id {:keys [db] :as cofx}]
(let [success-event [:transport/set-contact-message-envelope-hash chat-id]
chat (get-in db [:transport/chats chat-id])
updated-chat (assoc chat :resend? "contact-request-confirmation")]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-request-confirmation")
:data-store/tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]}
(protocol/send-with-pubkey {:chat-id chat-id
:payload this
:success-event success-event}))))
(validate [this]
(when (spec/valid? :message/contact-request-confirmed this)
this)))
(fx/defn send-contact-update
[{:keys [db] :as cofx} chat-id payload]
(when-let [chat (get-in cofx [:db :transport/chats chat-id])]
(let [updated-chat (assoc chat :resend? "contact-update")
tx [(transport-store/save-transport-tx {:chat-id chat-id
:chat updated-chat})]
success-event [:transport/set-contact-message-envelope-hash chat-id]]
(fx/merge cofx
{:db (assoc-in db
[:transport/chats chat-id :resend?]
"contact-update")
:data-store/tx tx}
(protocol/send-with-pubkey {:chat-id chat-id
:payload payload
:success-event success-event})))))
(defrecord ContactUpdate [name profile-image address fcm-token]
message/StatusMessage
(send [this _ {:keys [db] :as cofx}]
;;TODO: here we look for contact which have a :public-key to differentiate
;;actual contacts from dapps
;;This is not an ideal solution and we should think about a more reliable way
;;to do this when we refactor app-db
(let [contact-public-keys (reduce (fn [acc [_ {:keys [public-key pending?]}]]
(if (and public-key
(not pending?))
(conj acc public-key)
acc))
#{}
(:contacts/contacts db))
;;NOTE: chats with contacts use public-key as chat-id
send-contact-update-fxs (map #(send-contact-update % this) contact-public-keys)]
(apply fx/merge cofx send-contact-update-fxs)))
(validate [this]
(when (spec/valid? :message/contact-update this)
this)))
(fx/defn remove-chat-filter
"Stops the filter for the given chat-id"
[{:keys [db]} chat-id]
(when-let [filter (get-in db [:transport/chats chat-id :filter])]
{:shh/remove-filter filter}))
(fx/defn init-chat
[cofx chat-id topic]
(when-not (get-in cofx [:db :transport/chats chat-id])
(protocol/init-chat cofx
{:chat-id chat-id
:topic topic})))
(defrecord NewContactKey [sym-key topic message]
message/StatusMessage
(send [this chat-id cofx]
(let [success-event [:transport/set-contact-message-envelope-hash chat-id]]
(protocol/send-with-pubkey cofx
{:chat-id chat-id
:payload this
:success-event success-event})))
(receive [this chat-id _ timestamp {:keys [db] :as cofx}]
(let [current-sym-key (get-in db [:transport/chats chat-id :sym-key])
;; NOTE(yenda) to support concurrent contact request without additional
;; interactions we don't save the new key if these conditions are met:
;; - the message is a contact request
;; - we already have a sym-key
;; - this sym-key is first in alphabetical order compared to the new one
save-key? (not (and (= ContactRequest (type message))
current-sym-key
(= current-sym-key
(first (sort [current-sym-key sym-key])))))]
(if save-key?
(let [on-success (fn [sym-key sym-key-id]
(re-frame/dispatch [:contact/add-new-sym-key
{:sym-key-id sym-key-id
:timestamp timestamp
:sym-key sym-key
:chat-id chat-id
:topic topic
:message message}]))]
(fx/merge cofx
{:shh/add-new-sym-keys [{:web3 (:web3 db)
:sym-key sym-key
:on-success on-success}]}
(init-chat chat-id topic)
;; in case of concurrent contact request we want
;; to stop the filter for the previous key before
;; dereferrencing it
(remove-chat-filter chat-id)))
;; if we don't save the key, we read the message directly
(message/receive message chat-id chat-id timestamp cofx))))
(validate [this]
(when (spec/valid? :message/new-contact-key this)
this)))

View File

@ -1,9 +1,9 @@
(ns ^{:doc "Whisper API and events for managing keys and posting messages"}
status-im.transport.shh
(:require [taoensso.timbre :as log]
[re-frame.core :as re-frame]
(:require [re-frame.core :as re-frame]
[status-im.transport.message.transit :as transit]
[status-im.transport.utils :as transport.utils]
[status-im.transport.message.transit :as transit]))
[taoensso.timbre :as log]))
(defn get-new-key-pair [{:keys [web3 on-success on-error]}]
(if web3
@ -70,7 +70,7 @@
:shh/send-direct-message
(fn [post-calls]
(doseq [{:keys [web3 payload src dst success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls]
:or {error-event :transport/send-status-message-error}} post-calls]
(let [direct-message (clj->js {:pubKey dst
:sig src
:payload (-> payload
@ -86,7 +86,7 @@
:shh/send-group-message
(fn [params]
(let [{:keys [web3 payload src dsts success-event error-event]
:or {error-event :protocol/send-status-message-error}} params
:or {error-event :transport/send-status-message-error}} params
message (clj->js {:pubKeys dsts
:sig src
:payload (-> payload
@ -102,7 +102,7 @@
:shh/send-public-message
(fn [post-calls]
(doseq [{:keys [web3 payload src chat success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls]
:or {error-event :transport/send-status-message-error}} post-calls]
(let [message (clj->js {:chat chat
:sig src
:payload (-> payload
@ -118,7 +118,7 @@
:shh/post
(fn [post-calls]
(doseq [{:keys [web3 message success-event error-event]
:or {error-event :protocol/send-status-message-error}} post-calls]
:or {error-event :transport/send-status-message-error}} post-calls]
(post-message {:web3 web3
:whisper-message (update message :payload (comp transport.utils/from-utf8
transit/serialize))
@ -127,28 +127,6 @@
#(log/debug :shh/post-success))
:on-error #(re-frame/dispatch [error-event %])}))))
;; This event params contain a recipients key because it's a vector of map with public-key and topic keys.
;; the :shh/post event has public-key and topic keys at the top level of the args map.
;; This event is used to send messages to multiple recipients when you can't send it on a topic.
;; It is used for renewing keys in a private group chat, because if someone leaves/join.
;; We want to change the symmetric key but we can't do that in the group topic with the old key
;; otherwise leavers can still eavesdrop / joiners can read past history."
(re-frame/reg-fx
:shh/multi-post
(fn [{:keys [web3 message recipients success-event error-event]
:or {error-event :protocol/send-status-message-error}}]
(let [whisper-message (update message :payload (comp transport.utils/from-utf8
transit/serialize))]
(doseq [{:keys [sym-key-id topic]} recipients]
(post-message {:web3 web3
:whisper-message (assoc whisper-message
:topic topic
:symKeyID sym-key-id)
:on-success (if success-event
#(re-frame/dispatch success-event)
#(log/debug :shh/post-success))
:on-error #(re-frame/dispatch [error-event %])})))))
(defn add-sym-key
[{:keys [web3 sym-key on-success on-error]}]
(.. web3

View File

@ -1,19 +1,7 @@
(ns ^{:doc "Utils for transport layer"}
status-im.transport.utils
(:require [cljs-time.coerce :refer [to-long]]
[cljs-time.core :refer [now]]
[clojure.string :as string]
[status-im.js-dependencies :as dependencies]
[status-im.data-store.transport :as transport-store]
[status-im.utils.fx :as fx]))
(fx/defn unsubscribe-from-chat
"Unsubscribe from chat on transport layer"
[{:keys [db]} chat-id]
(let [filter (get-in db [:transport/chats chat-id :filter])]
{:db (update db :transport/chats dissoc chat-id)
:data-store/tx [(transport-store/delete-transport-tx chat-id)]
:shh/remove-filter filter}))
(:require [clojure.string :as string]
[status-im.js-dependencies :as dependencies]))
(defn from-utf8 [s]
(.fromUtf8 dependencies/Web3.prototype s))

View File

@ -19,7 +19,7 @@
:accessibility-label :connection-status-text}
[react/text {:style styles/text
:on-press (when mailserver-error?
#(re-frame/dispatch [:inbox/reconnect]))}
#(re-frame/dispatch [:inbox.ui/reconnect-mailserver-pressed]))}
(i18n/label label)]]))
(defview error-view [{:keys [top]}]

View File

@ -49,7 +49,10 @@
:inbox/wnodes fleet/default-wnodes
:my-profile/editing? false
:transport/chats {}
:transport/filters {}
:transport/message-envelopes {}
:transport.inbox/topics {}
:transport.inbox/requests {}
:chat/cooldowns 0
:chat/cooldown-enabled? false
:chat/last-outgoing-message-sent-at 0
@ -86,7 +89,7 @@
;;:online - presence of internet connection in the phone
(spec/def ::network-status (spec/nilable keyword?))
(spec/def ::mailserver-status (spec/nilable keyword?))
(spec/def ::mailserver-status (spec/nilable #{:disconnected :connecting :added :connected :error}))
(spec/def ::app-state string?)
@ -212,7 +215,6 @@
:mailservers/manage
:bootnodes/manage
:inbox/wnodes
:inbox/last-received
:inbox/current-id
:inbox/fetching?
:universal-links/url
@ -227,7 +229,9 @@
:chat/spam-messages-frequency
:transport/message-envelopes
:transport/chats
:transport/discovery-filter
:transport/filters
:transport.inbox/topics
:transport.inbox/requests
:desktop/desktop
:dimensions/window
:dapps/permissions]

View File

@ -104,7 +104,7 @@
(fx/defn on-return-from-background [cofx]
(fx/merge cofx
(inbox/request-messages)
(inbox/request-messages nil)
(hardwallet/return-back-from-nfc-settings)))
(defn app-state-change [state {:keys [db] :as cofx}]

View File

@ -3,8 +3,6 @@
[status-im.i18n :as i18n]
[status-im.chat.models.message :as models.message]
[status-im.ui.screens.navigation :as navigation]
[status-im.transport.message.v1.core :as protocol]
[status-im.transport.message.core :as transport]
[status-im.utils.handlers :as handlers]
[status-im.data-store.chats :as chats-store]
[status-im.utils.fx :as fx]))

View File

@ -12,10 +12,11 @@
(spec/def :wnode/user-defined boolean?)
(spec/def :wnode/password ::not-blank-string)
(spec/def :wnode/sym-key-id string?)
(spec/def :wnode/generating-sym-key? boolean?)
(spec/def :wnode/wnode (allowed-keys :req-un [:wnode/address :wnode/name :wnode/id]
:opt-un [:wnode/sym-key-id
:wnode/generating-sym-key?
:wnode/user-defined
:wnode/password]))
(spec/def :inbox/wnodes (spec/nilable (spec/map-of keyword? (spec/map-of :wnode/id :wnode/wnode))))
(spec/def :inbox/last-received integer?)

View File

@ -48,7 +48,7 @@
(reg-sub :fetching?
(fn [db]
(get db :inbox/fetching?)))
(pos-int? (count (get db :transport.inbox/requests)))))
(reg-sub :offline?
:<- [:network-status]

View File

@ -12,7 +12,7 @@
(def ^:private mergable-keys
#{:data-store/tx :data-store/base-tx :chat-received-message/add-fx
:shh/add-new-sym-keys :shh/get-new-sym-keys :shh/post
:shh/generate-sym-key-from-password :confirm-messages-processed
:shh/generate-sym-key-from-password :transport/confirm-messages-processed
:group-chats/extract-membership-signature :utils/dispatch-later})
(defn- safe-merge [fx new-fx]

View File

@ -1,6 +1,6 @@
(ns status-im.test.chat.models.message
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.message.v1.protocol :as protocol]
[status-im.transport.message.protocol :as protocol]
[status-im.chat.models.message :as message]
[status-im.utils.datetime :as time]))
@ -62,7 +62,7 @@
(testing "it marks the message as outgoing"
(is (= true (:outgoing message))))
(testing "it confirm the message as processed"
(is (:confirm-messages-processed actual)))
(is (:transport/confirm-messages-processed actual)))
(testing "it stores the message"
(is (:data-store/tx actual)))
(testing "it does not send a seen confirmation"

View File

@ -8,7 +8,7 @@
[status-im.transport.utils :as transport.utils]
[day8.re-frame.test :refer-macros [run-test-async run-test-sync] :as rf-test]
[status-im.test.protocol.node :as node]
[status-im.transport.message.v1.contact :as transport.contact]
[status-im.transport.message.contact :as transport.contact]
[status-im.test.protocol.utils :as utils]))
;; NOTE(oskarth): All these tests are evaluated in NodeJS
@ -45,7 +45,7 @@
(rf-test/wait-for [::transport.contact/send-new-sym-key]
(rf/dispatch [:set-chat-input-text "test message"])
(rf/dispatch [:send-current-message])
(rf-test/wait-for [:update-message-status :protocol/send-status-message-error]
(rf-test/wait-for [:update-message-status :transport/send-status-message-error]
(is true)))))))
(deftest test-whisper-version!

View File

@ -19,7 +19,6 @@
[status-im.test.models.wallet]
[status-im.test.transport.core]
[status-im.test.transport.inbox]
[status-im.test.transport.handlers]
[status-im.test.chat.models]
[status-im.test.chat.models.input]
[status-im.test.chat.models.loading]
@ -97,7 +96,6 @@
'status-im.test.i18n
'status-im.test.transport.core
'status-im.test.transport.inbox
'status-im.test.transport.handlers
'status-im.test.protocol.web3.inbox
'status-im.test.utils.utils
'status-im.test.utils.money

View File

@ -1,7 +1,7 @@
(ns status-im.test.transport.core
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.protocol.core :as protocol]
[status-im.transport.core :as transport]))
[status-im.transport.message.core :as message]))
(deftest init-whisper
(let [cofx {:db {:account/account {:public-key "1"}
@ -41,6 +41,30 @@
ms-2
ms-3])]
(is (= expected-wnodes
(get-in
(protocol/initialize-protocol cofx-with-ms "user-address")
[:db :inbox/wnodes])))))))
(-> (get-in
(protocol/initialize-protocol cofx-with-ms "user-address")
[:db :inbox/wnodes])
(update-in [:eth.beta "1"] dissoc :generating-sym-key?)
(update-in [:eth.beta "2"] dissoc :generating-sym-key?)
(update-in [:eth.test "3"] dissoc :generating-sym-key?))))))))
(def sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4")
(def messages #js [{:sig sig
:ttl 10
:timestamp 1527692015
:topic "0x9c22ff5f"
:payload "0x5b227e236334222c5b2246222c22746578742f706c61696e222c227e3a7075626c69632d67726f75702d757365722d6d657373616765222c3135323736393230313433383130312c313532373639323031343337375d5d"
:padding "0xbf06347cc7f9aa18b4a846032264a88f559d9b14079975d14b10648847c0543a77a80624e101c082d19b502ae3b4f97958d18abf59eb0a82afc1301aa22470495fac739a30c2f563599fa8d8e09363a43d39311596b7f119dee7b046989c08224f1ef5cdc385"
:pow 0.002631578947368421
:hash "0x220ef9994a4fae64c112b27ed07ef910918159cbe6fcf8ac515ee2bf9a6711a0"}])
(deftest receive-whisper-messages-test
(testing "an error is reported"
(is (nil? (:chat-received-message/add-fx (message/receive-whisper-messages {:db {}} "error" #js [] nil)))))
(testing "messages is undefined"
(is (nil? (:chat-received-message/add-fx (message/receive-whisper-messages {:db {}} nil js/undefined nil)))))
(testing "happy path"
(let [actual (message/receive-whisper-messages {:db {}} nil messages sig)]
(testing "it add an fx for the message"
(is (:chat-received-message/add-fx actual))))))

View File

@ -1,24 +0,0 @@
(ns status-im.test.transport.handlers
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.transport.handlers :as handlers]))
(def sig "0x04325367620ae20dd878dbb39f69f02c567d789dd21af8a88623dc5b529827c2812571c380a2cd8236a2851b8843d6486481166c39debf60a5d30b9099c66213e4")
(def messages #js [{:sig sig
:ttl 10
:timestamp 1527692015
:topic "0x9c22ff5f"
:payload "0x5b227e236334222c5b2246222c22746578742f706c61696e222c227e3a7075626c69632d67726f75702d757365722d6d657373616765222c3135323736393230313433383130312c313532373639323031343337375d5d"
:padding "0xbf06347cc7f9aa18b4a846032264a88f559d9b14079975d14b10648847c0543a77a80624e101c082d19b502ae3b4f97958d18abf59eb0a82afc1301aa22470495fac739a30c2f563599fa8d8e09363a43d39311596b7f119dee7b046989c08224f1ef5cdc385"
:pow 0.002631578947368421
:hash "0x220ef9994a4fae64c112b27ed07ef910918159cbe6fcf8ac515ee2bf9a6711a0"}])
(deftest receive-whisper-messages-test
(testing "an error is reported"
(is (nil? (:chat-received-message/add-fx (handlers/receive-whisper-messages {:db {}} "error" #js [] nil)))))
(testing "messages is undefined"
(is (nil? (:chat-received-message/add-fx (handlers/receive-whisper-messages {:db {}} nil js/undefined nil)))))
(testing "happy path"
(let [actual (handlers/receive-whisper-messages {:db {}} nil messages sig)]
(testing "it add an fx for the message"
(is (:chat-received-message/add-fx actual))))))

View File

@ -20,20 +20,20 @@
[])))
(deftest peers-summary-change
(testing "Mailserver connected"
(testing "Mailserver added, sym-key doesn't exist"
(let [result (peers-summary-change-result false true false)]
(is (= (into #{} (keys result))
#{:status-im.transport.inbox/mark-trusted-peer}))))
#{:transport.inbox/mark-trusted-peer :shh/generate-sym-key-from-password :db}))))
(testing "Mailserver disconnected, sym-key exists"
(let [result (peers-summary-change-result true false true)]
(is (= (into #{} (keys result))
#{:db :status-im.transport.inbox/add-peer :utils/dispatch-later}))
#{:db :transport.inbox/add-peer :utils/dispatch-later}))
(is (= (get-in result [:db :mailserver-status])
:connecting))))
(testing "Mailserver disconnected, sym-key doesn't exists (unlikely situation in practice)"
(let [result (peers-summary-change-result false false true)]
(is (= (into #{} (keys result))
#{:db :status-im.transport.inbox/add-peer :utils/dispatch-later :shh/generate-sym-key-from-password}))
#{:db :transport.inbox/add-peer :utils/dispatch-later :shh/generate-sym-key-from-password}))
(is (= (get-in result [:db :mailserver-status])
:connecting))))
(testing "Mailserver isn't concerned by peer summary changes"
@ -51,8 +51,8 @@
{:settings {:fleet :eth.beta
:wnode {:eth.beta "wnodeid"}}}}]
(testing "it adds the peer"
(is (= {:wnode "wnode-address"}
(::inbox/add-peer (inbox/connect-to-mailserver {:db db})))))
(is (= "wnode-address"
(:transport.inbox/add-peer (inbox/connect-to-mailserver {:db db})))))
(testing "it generates a sym key if hasn't been generated before"
(is (= "wnode-password"
(-> (inbox/connect-to-mailserver {:db db})
@ -67,118 +67,60 @@
:shh/generate-sym-key-from-password
first)))))))
(deftest request-messages
(let [db {:mailserver-status :connected
:inbox/current-id "wnodeid"
:inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}
:account/account {:settings {:fleet :eth.beta}}
:transport/chats
{:dont-fetch-history {:topic "dont-fetch-history"}
:fetch-history {:topic "fetch-history"
:fetch-history? true}}}
cofx {:db db :now 1000000000}]
(testing "inbox is ready"
(testing "last request is > the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it uses last request"
(is (= 400000 (get-in actual [::inbox/request-messages 0 :from]))))))
(testing "last request is < the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2)
actual (inbox/request-messages cofx-with-last-request)]
(testing "it uses last 7 days for catching up"
(is (= 395200 (get-in actual [::inbox/request-messages 0 :from]))))
(testing "it only uses topics that dont have fetch history set"
(is (= ["0xf8946aac" "dont-fetch-history"]
(get-in actual [::inbox/request-messages 0 :topics]))))
(testing "it uses the last 24 hours to request history"
(is (= 913600
(get-in actual [::inbox/request-messages 1 :from]))))
(testing "it fetches the right topic for history"
(is (= ["fetch-history"]
(get-in actual [::inbox/request-messages 1 :topics])))))))
(testing "inbox is not ready"
(testing "it does not do anything"
(is (nil? (inbox/request-messages {:db {}})))))))
#_(deftest request-messages
(let [db {:mailserver-status :connected
:inbox/current-id "wnodeid"
:inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}
:account/account {:settings {:fleet :eth.beta}}
:transport/chats
{:dont-fetch-history {:topic "dont-fetch-history"}
:fetch-history {:topic "fetch-history"}}}
cofx {:db db :now 1000000000}]
(testing "inbox is ready"
(testing "last request is > the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 400000)
actual (inbox/request-messages cofx-with-last-request nil)]
(testing "it uses last request"
(is (= 400000 (get-in actual [:transport.inbox/request-messages :requests]))))))
(testing "last request is < the 7 days ago"
(let [cofx-with-last-request (assoc-in cofx [:db :account/account :last-request] 2)
actual (inbox/request-messages cofx-with-last-request nil)]
(testing "it uses last 7 days for catching up"
(is (= 395200 (get-in actual [:transport.inbox/request-messages :requests]))))
(testing "it only uses topics that dont have fetch history set"
(is (= ["0xf8946aac" "dont-fetch-history"]
(get-in actual [:transport.inbox/request-messages :requests]))))
(testing "it uses the last 24 hours to request history"
(is (= 913600
(get-in actual [:transport.inbox/request-messages :requests]))))
(testing "it fetches the right topic for history"
(is (= ["fetch-history"]
(get-in actual [:transport.inbox/request-messages :requests])))))))
(testing "inbox is not ready"
(testing "it does not do anything"
(is (nil? (inbox/request-messages {:db {}} nil)))))))
(deftest request-messages-params
(let [mailserver {:address "peer"
:sym-key-id "id"}]
(testing "from is greater that to"
(testing "it returns an empty sequence"
(is (empty? (inbox/request-inbox-messages-params mailserver 2 0 ["a" "b" "c"])))))
(testing "from is equal to to"
(testing "it returns an empty sequence"
(is (empty? (inbox/request-inbox-messages-params mailserver 2 2 ["a" "b" "c"])))))
(testing "to is less than the step"
(is (= #{{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 3}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 3}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 3 ["a" "b"])))))
(testing "to is equal the step"
(is (= #{{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 86400 ["a" "b"])))))
(testing "to is greather than the step"
(is (= #{{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 0
:to 86400}
{:topic "a"
:mailServerPeer "peer"
:symKeyID "id"
:from 86400
:to 90000}
{:topic "b"
:mailServerPeer "peer"
:symKeyID "id"
:from 86400
:to 90000}}
(into #{} (inbox/request-inbox-messages-params mailserver 0 90000 ["a" "b"])))))))
(deftest initialize-offline-inbox
(let [db {:mailserver-status :connected
:account/account {:settings {:fleet :eth.beta}}
:inbox/current-id "wnodeid"
:inbox/wnodes
{:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}}]
(testing "last-request is not set"
(testing "it sets it to now in seconds"
(is (= 10
(get-in
(inbox/initialize-offline-inbox {:now 10000 :db db} [])
[:db :account/account :last-request])))))
(testing "last-request is set"
(testing "leaves it unchanged"
(is (= "sometimeago"
(get-in
(inbox/initialize-offline-inbox
{:now "now"
:db (assoc-in db [:account/account :last-request] "sometimeago")}
[])
[:db :account/account :last-request])))))))
#_(deftest initialize-offline-inbox
(let [db {:mailserver-status :connected
:account/account {:settings {:fleet :eth.beta}}
:inbox/current-id "wnodeid"
:inbox/wnodes {:eth.beta {"wnodeid" {:address "wnode-address"
:sym-key-id "something"
:password "wnode-password"}}}}]
(testing "last-request is not set"
(testing "it sets it to now in seconds"
(is (= 10
(get-in
(inbox/initialize-offline-inbox {:now 10000 :db db} [])
[:db :account/account :last-request])))))
(testing "last-request is set"
(testing "leaves it unchanged"
(is (= "sometimeago"
(get-in
(inbox/initialize-offline-inbox
{:now "now"
:db (assoc-in db [:account/account :last-request] "sometimeago")}
[])
[:db :account/account :last-request])))))))