diff --git a/src/status_im/utils/clocks.cljs b/src/status_im/utils/clocks.cljs deleted file mode 100644 index 92a37a96b9..0000000000 --- a/src/status_im/utils/clocks.cljs +++ /dev/null @@ -1,103 +0,0 @@ -(ns status-im.utils.clocks - (:require [utils.datetime :as datetime])) - -;; We use Lamport clocks to ensure correct ordering of events in chats. This is -;; necessary because we operate in a distributed system and there is no central -;; coordinator for what happened before what. -;; -;; We can't rely uniquely on timestamps as clocks might be different on each device. -;; -;; Received time cannot be used as it does not work with out-of-order messages. -;; If we used received time also each client could potentially have a different -;; ordering of messages, which would lead to some difficult misunderstanding -;; among participants. -;; -;; Lamport timestamps offer a consistent view across client, at the expenses of -;; understanding exactly at what time something has happened. -;; They satisfy the property: if a caused b then T(a) < T(b) -;; -;; In chat terms: -;; -;; Any message I send will always be displayed after any message I have seen, -;; including the messages I have sent. -;; This is a necessary condition to have a meaningful conversation with someone -;; and ought to be always true. -;; -;; We need to address another issue here: -;; -;; Even if I don't see all the messages, if I post a message I want that message -;; to be displayed last in a chat. -;; -;; That's were the basic algorithm of Lamport timestamp would fall short, as -;; it's only meant to order causally related events. -;; -;; If I join a public chat and I have not received any messages or I have missed -;; many messages because I was offline, when I post a new message it would be -;; displayed back in the history ( I would have to wait to receive a message -;; to bring my timestamp up-to-date). -;; -;; We cannot completely solve this as there's no way to know what the chat -;; current timestamp is without having to contact other peers ( which might all be offline) -;; -;; But what we can do, is to use our time to make a "bid", hoping that it will -;; beat the current chat-timestamp. So our Lamport timestamp format is: -;; {unix-timestamp-ms} -;; -;; We always need to make sure we take the max value between the last-clock-value -;; for the chat and the bid-timestamp. -;; -;; This will still satisfy Lamport requirement, namely: a -> b then T(a) < T(b) -;; -;; One way to think of this is as as Lamport timestamps where at every ms -;; an internal event is generated. -;; -;; In whisper v6 any message with a timestamp older than 20 seconds will be discarded. -;; -;; So worst case scenario is: -;; Your clock is 20 seconds behind, you join a public chat where everyone's clock -;; is 20 seconds ahead, you have not received 40s of inflight messages, you -;; publish. drama. -;; Your post will be displayed before any non-received inflight message. -;; -;; Once received the posts you will be able to communicate effectively, much rejoicing. -;; If there are no inflight messages then your post will be last. -;; -;; Posts sent when offline are more troublesome, as they would carry an old -;; timestamp, so the timestamp should be refreshed before retrying. -;; -;; Details: -;; https://en.wikipedia.org/wiki/Lamport_timestamps -;; http://amturing.acm.org/p558-lamport.pdf - -(def one-month-in-ms (* 60 60 24 31 1000)) - -(defn- ->timestamp-bid - [] - (datetime/timestamp)) - -(defn max-timestamp - [] - (+ one-month-in-ms (datetime/timestamp))) -; The timestamp has an upper limit of Number.MAX_SAFE_INTEGER -; A malicious client could send a crafted message with timestamp = Number.MAX_SAFE_INTEGER -; which effectively would DoS the chat, as any new message would get -; a timestamp of Number.MAX_SAFE_INTEGER (inc becomes a noop). -; We should never receive messages from untrusted peers with a timestamp greater -; then now + 20s. -; We cap the timestamp to time now + 1 month to give some room for trusted peers -(defn safe-timestamp - [t] - (min t (max-timestamp))) - -(defn safe-timestamp? - [t] - (<= t (max-timestamp))) - -(defn send - [local-clock] - (inc (max local-clock (->timestamp-bid)))) - -(defn receive - [message-clock local-clock] - (-> (max (or message-clock 0) (or local-clock 0)) - safe-timestamp)) diff --git a/src/status_im/utils/clocks_test.cljs b/src/status_im/utils/clocks_test.cljs deleted file mode 100644 index 69c5606f26..0000000000 --- a/src/status_im/utils/clocks_test.cljs +++ /dev/null @@ -1,14 +0,0 @@ -(ns status-im.utils.clocks-test - (:require [cljs.test :refer-macros [deftest is testing]] - [status-im.utils.clocks :as clocks])) - -(deftest safe-timestamp - (testing "it caps the timestamp when a value too large is provided" - (is (< (clocks/receive js/Number.MAX_SAFE_INTEGER 0) - js/Number.MAX_SAFE_INTEGER)))) - -(deftest safe-timestamp?-test - (testing "it returns false for a high number" - (is (not (clocks/safe-timestamp? js/Number.MAX_SAFE_INTEGER)))) - (testing "it returns true for a normal timestamp number" - (is (clocks/safe-timestamp? (clocks/send 0))))) diff --git a/src/status_im2/contexts/chat/events.cljs b/src/status_im2/contexts/chat/events.cljs index 383b258365..fae4162249 100644 --- a/src/status_im2/contexts/chat/events.cljs +++ b/src/status_im2/contexts/chat/events.cljs @@ -13,14 +13,14 @@ [status-im.chat.models.loading :as loading] [status-im.data-store.chats :as chats-store] [status-im2.contexts.contacts.events :as contacts-store] - [status-im.utils.clocks :as utils.clocks] [utils.transforms :as transforms] [reagent.core :as reagent] [quo2.foundations.colors :as colors] [re-frame.core :as re-frame] [react-native.async-storage :as async-storage] [status-im2.contexts.shell.jump-to.constants :as shell.constants] - [status-im2.common.muting.helpers :refer [format-mute-till]])) + [status-im2.common.muting.helpers :refer [format-mute-till]] + [utils.datetime :as datetime])) (defn- get-chat [cofx chat-id] @@ -126,7 +126,7 @@ 0 (or (:clock-value last-message) deleted-at-clock-value - (utils.clocks/send 0)))] + (datetime/timestamp)))] {:db (-> db (assoc-in [:messages chat-id] {}) (update-in [:message-lists] dissoc chat-id) diff --git a/src/status_im2/contexts/chat/events_test.cljs b/src/status_im2/contexts/chat/events_test.cljs index a61a0d5e81..2efcee8293 100644 --- a/src/status_im2/contexts/chat/events_test.cljs +++ b/src/status_im2/contexts/chat/events_test.cljs @@ -1,7 +1,7 @@ (ns status-im2.contexts.chat.events-test (:require [cljs.test :refer-macros [deftest is testing]] [status-im2.contexts.chat.events :as chat] - [status-im.utils.clocks :as utils.clocks])) + [utils.datetime :as datetime])) (deftest clear-history-test (let [chat-id "1" @@ -30,7 +30,7 @@ true)] (is (= 100 (get-in actual [:db :chats chat-id :deleted-at-clock-value]))))) (testing "it set the deleted-at-clock-value to now the chat has no messages nor previous deleted-at" - (with-redefs [utils.clocks/send (constantly 42)] + (with-redefs [datetime/timestamp (constantly 42)] (let [actual (chat/clear-history (update-in cofx [:db :chats chat-id] assoc