remove status-im.utils.clocks (#17434)
This commit is contained in:
parent
a847f508f9
commit
db787f9d4b
|
@ -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))
|
|
|
@ -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)))))
|
|
|
@ -13,14 +13,14 @@
|
||||||
[status-im.chat.models.loading :as loading]
|
[status-im.chat.models.loading :as loading]
|
||||||
[status-im.data-store.chats :as chats-store]
|
[status-im.data-store.chats :as chats-store]
|
||||||
[status-im2.contexts.contacts.events :as contacts-store]
|
[status-im2.contexts.contacts.events :as contacts-store]
|
||||||
[status-im.utils.clocks :as utils.clocks]
|
|
||||||
[utils.transforms :as transforms]
|
[utils.transforms :as transforms]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[quo2.foundations.colors :as colors]
|
[quo2.foundations.colors :as colors]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[react-native.async-storage :as async-storage]
|
[react-native.async-storage :as async-storage]
|
||||||
[status-im2.contexts.shell.jump-to.constants :as shell.constants]
|
[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
|
(defn- get-chat
|
||||||
[cofx chat-id]
|
[cofx chat-id]
|
||||||
|
@ -126,7 +126,7 @@
|
||||||
0
|
0
|
||||||
(or (:clock-value last-message)
|
(or (:clock-value last-message)
|
||||||
deleted-at-clock-value
|
deleted-at-clock-value
|
||||||
(utils.clocks/send 0)))]
|
(datetime/timestamp)))]
|
||||||
{:db (-> db
|
{:db (-> db
|
||||||
(assoc-in [:messages chat-id] {})
|
(assoc-in [:messages chat-id] {})
|
||||||
(update-in [:message-lists] dissoc chat-id)
|
(update-in [:message-lists] dissoc chat-id)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
(ns status-im2.contexts.chat.events-test
|
(ns status-im2.contexts.chat.events-test
|
||||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
[status-im2.contexts.chat.events :as chat]
|
[status-im2.contexts.chat.events :as chat]
|
||||||
[status-im.utils.clocks :as utils.clocks]))
|
[utils.datetime :as datetime]))
|
||||||
|
|
||||||
(deftest clear-history-test
|
(deftest clear-history-test
|
||||||
(let [chat-id "1"
|
(let [chat-id "1"
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
true)]
|
true)]
|
||||||
(is (= 100 (get-in actual [:db :chats chat-id :deleted-at-clock-value])))))
|
(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"
|
(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
|
(let [actual (chat/clear-history (update-in cofx
|
||||||
[:db :chats chat-id]
|
[:db :chats chat-id]
|
||||||
assoc
|
assoc
|
||||||
|
|
Loading…
Reference in New Issue