Signed-off-by: Goran Jovic <goranjovic@gmail.com>
This commit is contained in:
parent
861892e776
commit
2b89d1e25c
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
- Improved validation in account recovery. We now show a warning if any words are not from our mnemonic dictionary.
|
||||||
|
|
||||||
## [0.9.25 - Unreleased]
|
## [0.9.25 - Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
:mailserver-connection-error "Could not connect to mailserver"
|
:mailserver-connection-error "Could not connect to mailserver"
|
||||||
:dont-allow "Don't Allow"
|
:dont-allow "Don't Allow"
|
||||||
:custom "Custom"
|
:custom "Custom"
|
||||||
|
:required-field "Required field"
|
||||||
|
:are-you-sure? "Are you sure?"
|
||||||
|
|
||||||
:camera-access-error "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected."
|
:camera-access-error "To grant the required camera permission, please go to your system settings and make sure that Status > Camera is selected."
|
||||||
:photos-access-error "To grant the required photos permission, please go to your system settings and make sure that Status > Photos is selected."
|
:photos-access-error "To grant the required photos permission, please go to your system settings and make sure that Status > Photos is selected."
|
||||||
|
@ -172,7 +174,6 @@
|
||||||
:enter-word "Enter word"
|
:enter-word "Enter word"
|
||||||
:check-your-recovery-phrase "Check your recovery phrase"
|
:check-your-recovery-phrase "Check your recovery phrase"
|
||||||
:wrong-word "Wrong word"
|
:wrong-word "Wrong word"
|
||||||
:are-you-sure? "Are you sure?"
|
|
||||||
:are-you-sure-description "You will not be able to see the whole recovery phrase again"
|
:are-you-sure-description "You will not be able to see the whole recovery phrase again"
|
||||||
:you-are-all-set "You’re all set!"
|
:you-are-all-set "You’re all set!"
|
||||||
:you-are-all-set-description "Now if you lose your phone you can restore your account and wallet using the recovery phrase."
|
:you-are-all-set-description "Now if you lose your phone you can restore your account and wallet using the recovery phrase."
|
||||||
|
@ -393,6 +394,12 @@
|
||||||
:recover "Recover"
|
:recover "Recover"
|
||||||
:twelve-words-in-correct-order "12 words in correct order"
|
:twelve-words-in-correct-order "12 words in correct order"
|
||||||
:enter-12-words "Enter the 12 words of your recovery phrase, separated by single spaces"
|
:enter-12-words "Enter the 12 words of your recovery phrase, separated by single spaces"
|
||||||
|
:recover-password-too-short "Password is too short"
|
||||||
|
:recovery-phrase-invalid "Recovery phrase is invalid"
|
||||||
|
:recovery-phrase-unknown-words "Some words might be misspelled"
|
||||||
|
:recovery-typo-dialog-title "Is the phrase correct?"
|
||||||
|
:recovery-typo-dialog-description "If you enter the wrong words, you will create a new account instead of recovering an old one."
|
||||||
|
:recovery-confirm-phrase "Confirm phrase"
|
||||||
|
|
||||||
;;accounts
|
;;accounts
|
||||||
:recover-access "Recover access"
|
:recover-access "Recover access"
|
||||||
|
|
|
@ -13,6 +13,26 @@
|
||||||
|
|
||||||
;;;; Handlers
|
;;;; Handlers
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:recover/set-phrase
|
||||||
|
(fn [cofx [_ recovery-phrase]]
|
||||||
|
(models/set-phrase recovery-phrase cofx)))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:recover/validate-phrase
|
||||||
|
(fn [cofx _]
|
||||||
|
(models/validate-phrase cofx)))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:recover/set-password
|
||||||
|
(fn [cofx [_ masked-password]]
|
||||||
|
(models/set-password masked-password cofx)))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:recover/validate-password
|
||||||
|
(fn [cofx _]
|
||||||
|
(models/validate-password cofx)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:account-recovered
|
:account-recovered
|
||||||
(fn [cofx [_ result password]]
|
(fn [cofx [_ result password]]
|
||||||
|
@ -25,5 +45,10 @@
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:recover-account
|
:recover-account
|
||||||
(fn [cofx [_ masked-passphrase password]]
|
(fn [cofx _]
|
||||||
(models/recover-account masked-passphrase password cofx)))
|
(models/recover-account cofx)))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:recover-account-with-checks
|
||||||
|
(fn [cofx _]
|
||||||
|
(models/recover-account-with-checks cofx)))
|
||||||
|
|
|
@ -10,13 +10,31 @@
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.utils.signing-phrase.core :as signing-phrase]
|
[status-im.utils.signing-phrase.core :as signing-phrase]
|
||||||
[status-im.utils.hex :as utils.hex]
|
[status-im.utils.hex :as utils.hex]
|
||||||
[status-im.constants :as constants]))
|
[status-im.constants :as constants]
|
||||||
|
[cljs.spec.alpha :as spec]
|
||||||
|
[status-im.ui.screens.accounts.db :as db]
|
||||||
|
[status-im.utils.ethereum.mnemonic :as mnemonic]
|
||||||
|
[status-im.i18n :as i18n]))
|
||||||
|
|
||||||
|
;;;; helpers
|
||||||
|
|
||||||
|
(defn check-password-errors [password]
|
||||||
|
(cond (string/blank? password) :required-field
|
||||||
|
(not (db/valid-length? password)) :recover-password-too-short))
|
||||||
|
|
||||||
|
(defn check-phrase-errors [recovery-phrase]
|
||||||
|
(cond (string/blank? recovery-phrase) :required-field
|
||||||
|
(not (mnemonic/valid-phrase? recovery-phrase)) :recovery-phrase-invalid))
|
||||||
|
|
||||||
|
(defn check-phrase-warnings [recovery-phrase]
|
||||||
|
(when (not (mnemonic/status-generated-phrase? recovery-phrase))
|
||||||
|
:recovery-phrase-unknown-words))
|
||||||
|
|
||||||
;;;; FX
|
;;;; FX
|
||||||
|
|
||||||
(defn recover-account-fx! [masked-passphrase password]
|
(defn recover-account-fx! [masked-passphrase password]
|
||||||
(status/recover-account
|
(status/recover-account
|
||||||
(security/unmask masked-passphrase)
|
(mnemonic/sanitize-passphrase (security/unmask masked-passphrase))
|
||||||
password
|
password
|
||||||
(fn [result]
|
(fn [result]
|
||||||
;; here we deserialize result, dissoc mnemonic and serialize the result again
|
;; here we deserialize result, dissoc mnemonic and serialize the result again
|
||||||
|
@ -29,6 +47,27 @@
|
||||||
|
|
||||||
;;;; Handlers
|
;;;; Handlers
|
||||||
|
|
||||||
|
(defn set-phrase [recovery-phrase {:keys [db]}]
|
||||||
|
{:db (update db :accounts/recover assoc
|
||||||
|
:passphrase (string/lower-case recovery-phrase)
|
||||||
|
:passphrase-valid? (not (check-phrase-errors recovery-phrase)))})
|
||||||
|
|
||||||
|
(defn validate-phrase [{:keys [db]}]
|
||||||
|
(let [recovery-phrase (get-in db [:accounts/recover :passphrase])]
|
||||||
|
{:db (update db :accounts/recover assoc
|
||||||
|
:passphrase-error (check-phrase-errors recovery-phrase)
|
||||||
|
:passphrase-warning (check-phrase-warnings recovery-phrase))}))
|
||||||
|
|
||||||
|
(defn set-password [masked-password {:keys [db]}]
|
||||||
|
(let [password (security/unmask masked-password)]
|
||||||
|
{:db (update db :accounts/recover assoc
|
||||||
|
:password password
|
||||||
|
:password-valid? (not (check-password-errors password)))}))
|
||||||
|
|
||||||
|
(defn validate-password [{:keys [db]}]
|
||||||
|
(let [password (get-in db [:accounts/recover :password])]
|
||||||
|
{:db (assoc-in db [:accounts/recover :password-error] (check-password-errors password))}))
|
||||||
|
|
||||||
(defn on-account-recovered [result password {:keys [db]}]
|
(defn on-account-recovered [result password {:keys [db]}]
|
||||||
(let [data (types/json->clj result)
|
(let [data (types/json->clj result)
|
||||||
public-key (:pubkey data)
|
public-key (:pubkey data)
|
||||||
|
@ -51,10 +90,20 @@
|
||||||
(assoc :dispatch-later [{:ms 2000 :dispatch [:account-recovered-navigate]}])))))
|
(assoc :dispatch-later [{:ms 2000 :dispatch [:account-recovered-navigate]}])))))
|
||||||
|
|
||||||
(defn account-recovered-navigate [{:keys [db]}]
|
(defn account-recovered-navigate [{:keys [db]}]
|
||||||
{:db (assoc-in db [:accounts/recover :processing] false)
|
{:db (assoc-in db [:accounts/recover :processing?] false)
|
||||||
:dispatch-n [[:navigate-to-clean :home]
|
:dispatch-n [[:navigate-to-clean :home]
|
||||||
[:request-notifications]]})
|
[:request-notifications]]})
|
||||||
|
|
||||||
(defn recover-account [masked-passphrase password {:keys [db]}]
|
(defn recover-account [{:keys [db]}]
|
||||||
{:db (assoc-in db [:accounts/recover :processing] true)
|
(let [{:keys [password passphrase]} (:accounts/recover db)]
|
||||||
:recover-account-fx [masked-passphrase password]})
|
{:db (assoc-in db [:accounts/recover :processing?] true)
|
||||||
|
:recover-account-fx [(security/mask-data passphrase) password]}))
|
||||||
|
|
||||||
|
(defn recover-account-with-checks [{:keys [db] :as cofx}]
|
||||||
|
(let [{:keys [passphrase]} (:accounts/recover db)]
|
||||||
|
(if (mnemonic/status-generated-phrase? passphrase)
|
||||||
|
(recover-account cofx)
|
||||||
|
{:show-confirmation {:title (i18n/label :recovery-typo-dialog-title)
|
||||||
|
:content (i18n/label :recovery-typo-dialog-description)
|
||||||
|
:confirm-button-text (i18n/label :recovery-confirm-phrase)
|
||||||
|
:on-accept #(re-frame/dispatch [:recover-account])}})))
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
|
|
||||||
(defmethod nav/preload-data! :recover
|
(defmethod nav/preload-data! :recover
|
||||||
[db]
|
[db]
|
||||||
(update db :accounts/recover dissoc :password :passphrase :processing))
|
(update db :accounts/recover dissoc :password :passphrase :processing?))
|
|
@ -5,6 +5,12 @@
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:background-color colors/white})
|
:background-color colors/white})
|
||||||
|
|
||||||
|
(def inputs-container
|
||||||
|
{:margin 16})
|
||||||
|
|
||||||
|
(def password-input
|
||||||
|
{:margin-top 10})
|
||||||
|
|
||||||
(def bottom-button-container
|
(def bottom-button-container
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
:margin-horizontal 12
|
:margin-horizontal 12
|
||||||
|
|
|
@ -1,26 +1,22 @@
|
||||||
(ns status-im.ui.screens.accounts.recover.views
|
(ns status-im.ui.screens.accounts.recover.views
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
(:require [clojure.string :as string]
|
(:require [re-frame.core :as re-frame]
|
||||||
[re-frame.core :as re-frame]
|
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[status-im.ui.components.text-input.view :as text-input]
|
[status-im.ui.components.text-input.view :as text-input]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.status-bar.view :as status-bar]
|
[status-im.ui.components.status-bar.view :as status-bar]
|
||||||
[status-im.ui.components.styles :as components.styles]
|
|
||||||
[status-im.ui.components.toolbar.view :as toolbar]
|
[status-im.ui.components.toolbar.view :as toolbar]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.ui.screens.accounts.recover.styles :as styles]
|
[status-im.ui.screens.accounts.recover.styles :as styles]
|
||||||
[status-im.ui.screens.accounts.db :as db]
|
[status-im.ui.components.styles :as components.styles]
|
||||||
[status-im.utils.config :as config]
|
[status-im.utils.config :as config]
|
||||||
[status-im.utils.ethereum.core :as ethereum]
|
[status-im.utils.core :as utils.core]
|
||||||
[status-im.react-native.js-dependencies :as js-dependencies]
|
[status-im.react-native.js-dependencies :as js-dependencies]
|
||||||
[cljs.spec.alpha :as spec]
|
|
||||||
[status-im.ui.components.common.common :as components.common]
|
[status-im.ui.components.common.common :as components.common]
|
||||||
[status-im.utils.security :as security]))
|
[status-im.utils.security :as security]))
|
||||||
|
|
||||||
(defview passphrase-input [passphrase]
|
(defview passphrase-input [passphrase error warning]
|
||||||
(letsubs [error [:get-in [:accounts/recover :passphrase-error]]
|
(letsubs [input-ref (reagent/atom nil)]
|
||||||
input-ref (reagent/atom nil)]
|
|
||||||
{:component-did-mount (fn [_] (when config/testfairy-enabled?
|
{:component-did-mount (fn [_] (when config/testfairy-enabled?
|
||||||
(.hideView js-dependencies/testfairy @input-ref)))}
|
(.hideView js-dependencies/testfairy @input-ref)))}
|
||||||
[text-input/text-input-with-label
|
[text-input/text-input-with-label
|
||||||
|
@ -32,47 +28,47 @@
|
||||||
:multiline true
|
:multiline true
|
||||||
:default-value passphrase
|
:default-value passphrase
|
||||||
:auto-correct false
|
:auto-correct false
|
||||||
:on-change-text #(re-frame/dispatch [:set-in [:accounts/recover :passphrase] (string/lower-case %)])
|
:on-change-text #(re-frame/dispatch [:recover/set-phrase %])
|
||||||
:error error}]))
|
:on-blur #(re-frame/dispatch [:recover/validate-phrase])
|
||||||
|
:error (cond error (i18n/label error)
|
||||||
|
warning (i18n/label warning))}]))
|
||||||
|
|
||||||
(defview password-input [password]
|
(defview password-input [password error]
|
||||||
(letsubs [error [:get-in [:accounts/recover :password-error]]]
|
[react/view {:style styles/password-input
|
||||||
[react/view {:style {:margin-top 10}
|
:important-for-accessibility :no-hide-descendants}
|
||||||
:important-for-accessibility :no-hide-descendants}
|
[text-input/text-input-with-label
|
||||||
[text-input/text-input-with-label
|
{:label (i18n/label :t/password)
|
||||||
{:label (i18n/label :t/password)
|
:placeholder (i18n/label :t/enter-password)
|
||||||
:placeholder (i18n/label :t/enter-password)
|
:default-value password
|
||||||
:default-value password
|
:auto-focus false
|
||||||
:auto-focus false
|
:on-change-text #(re-frame/dispatch [:recover/set-password (security/mask-data %)])
|
||||||
:on-change-text #(re-frame/dispatch [:set-in [:accounts/recover :password] %])
|
:on-blur #(re-frame/dispatch [:recover/validate-password])
|
||||||
:secure-text-entry true
|
:secure-text-entry true
|
||||||
:error error}]]))
|
:error (when error (i18n/label error))}]])
|
||||||
|
|
||||||
(defview recover []
|
(defview recover []
|
||||||
(letsubs [{:keys [passphrase password processing]} [:get :accounts/recover]]
|
(letsubs [{:keys [passphrase password processing passphrase-valid? password-valid?
|
||||||
(let [words (ethereum/passphrase->words passphrase)
|
password-error passphrase-error passphrase-warning processing?]} [:get-recover-account]]
|
||||||
valid-form? (and
|
(let [valid-form? (and password-valid? passphrase-valid?)]
|
||||||
(ethereum/valid-words? words)
|
|
||||||
(spec/valid? ::db/password password))]
|
|
||||||
[react/keyboard-avoiding-view {:style styles/screen-container}
|
[react/keyboard-avoiding-view {:style styles/screen-container}
|
||||||
[status-bar/status-bar]
|
[status-bar/status-bar]
|
||||||
[toolbar/toolbar nil toolbar/default-nav-back
|
[toolbar/toolbar nil toolbar/default-nav-back
|
||||||
[toolbar/content-title (i18n/label :t/sign-in-to-another)]]
|
[toolbar/content-title (i18n/label :t/sign-in-to-another)]]
|
||||||
[components.common/separator]
|
[components.common/separator]
|
||||||
[react/view {:margin 16}
|
[react/view styles/inputs-container
|
||||||
[passphrase-input (or passphrase "")]
|
[passphrase-input (or passphrase "") passphrase-error passphrase-warning]
|
||||||
[password-input (or password "")]]
|
[password-input (or password "") password-error]]
|
||||||
[react/view {:flex 1}]
|
[react/view components.styles/flex]
|
||||||
(if processing
|
(if processing
|
||||||
[react/view styles/processing-view
|
[react/view styles/processing-view
|
||||||
[react/activity-indicator {:animating true}]
|
[react/activity-indicator {:animating true}]
|
||||||
[react/i18n-text {:style styles/sign-you-in :key :sign-you-in}]]
|
[react/i18n-text {:style styles/sign-you-in
|
||||||
|
:key :sign-you-in}]]
|
||||||
[react/view {:style styles/bottom-button-container}
|
[react/view {:style styles/bottom-button-container}
|
||||||
[react/view {:style {:flex 1}}]
|
[react/view {:style components.styles/flex}]
|
||||||
[components.common/bottom-button
|
[components.common/bottom-button
|
||||||
{:forward? true
|
{:forward? true
|
||||||
:label (i18n/label :t/sign-in)
|
:label (i18n/label :t/sign-in)
|
||||||
:disabled? (not valid-form?)
|
:disabled? (or processing? (not valid-form?))
|
||||||
:on-press (fn [_]
|
:on-press (utils.core/wrap-call-once!
|
||||||
(let [masked-passphrase (security/mask-data (ethereum/words->passphrase words))]
|
#(re-frame/dispatch [:recover-account-with-checks]))}]])])))
|
||||||
(re-frame/dispatch [:recover-account masked-passphrase password])))}]])])))
|
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
(ns status-im.ui.screens.accounts.subs
|
(ns status-im.ui.screens.accounts.subs
|
||||||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
(:require [re-frame.core :as re-frame]
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[status-im.ui.screens.accounts.db :as db]
|
[status-im.ui.screens.accounts.db :as db]
|
||||||
[status-im.utils.ethereum.core :as ethereum]
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
[cljs.spec.alpha :as spec]))
|
[cljs.spec.alpha :as spec]))
|
||||||
|
|
||||||
(reg-sub :get-current-public-key
|
(re-frame/reg-sub
|
||||||
(fn [db]
|
:get-current-public-key
|
||||||
(:current-public-key db)))
|
(fn [db]
|
||||||
|
(:current-public-key db)))
|
||||||
|
|
||||||
(reg-sub :get-accounts
|
(re-frame/reg-sub
|
||||||
(fn [db]
|
:get-accounts
|
||||||
(:accounts/accounts db)))
|
(fn [db]
|
||||||
|
(:accounts/accounts db)))
|
||||||
|
|
||||||
(reg-sub :get-current-account
|
(re-frame/reg-sub
|
||||||
(fn [db]
|
:get-current-account
|
||||||
(:account/account db)))
|
(fn [db]
|
||||||
|
(:account/account db)))
|
||||||
|
|
||||||
(reg-sub :get-current-account-hex
|
(re-frame/reg-sub
|
||||||
:<- [:get-current-account]
|
:get-current-account-hex
|
||||||
(fn [{:keys [address]}]
|
:<- [:get-current-account]
|
||||||
(ethereum/normalized-address address)))
|
(fn [{:keys [address]}]
|
||||||
|
(ethereum/normalized-address address)))
|
||||||
|
|
||||||
(reg-sub
|
(re-frame/reg-sub
|
||||||
:get-account-creation-next-enabled?
|
:get-account-creation-next-enabled?
|
||||||
(fn [{:accounts/keys [create]}]
|
(fn [{:accounts/keys [create]}]
|
||||||
(let [{:keys [step password password-confirm name]} create]
|
(let [{:keys [step password password-confirm name]} create]
|
||||||
(or (and password (= :enter-password step) (spec/valid? ::db/password password))
|
(or (and password (= :enter-password step) (spec/valid? ::db/password password))
|
||||||
(and password-confirm (= :confirm-password step) (spec/valid? ::db/password password-confirm))
|
(and password-confirm (= :confirm-password step) (spec/valid? ::db/password password-confirm))
|
||||||
(and name (= :enter-name step) (not (string/blank? name)))))))
|
(and name (= :enter-name step) (not (string/blank? name)))))))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
:get-recover-account
|
||||||
|
(fn [db]
|
||||||
|
(:accounts/recover db)))
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
(ns status-im.ui.screens.browser.subs
|
(ns status-im.ui.screens.browser.subs
|
||||||
(:require [re-frame.core :as re-frame]))
|
(:require [re-frame.core :as re-frame]))
|
||||||
|
|
||||||
(re-frame/reg-sub :browsers :browser/browsers)
|
(re-frame/reg-sub
|
||||||
|
:browsers
|
||||||
|
(fn [db _]
|
||||||
|
(:browser/browsers db)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
:get-current-browser
|
:get-current-browser
|
||||||
|
|
|
@ -25,28 +25,6 @@
|
||||||
(defn network-with-upstream-rpc? [network]
|
(defn network-with-upstream-rpc? [network]
|
||||||
(get-in network [:config :UpstreamConfig :Enabled]))
|
(get-in network [:config :UpstreamConfig :Enabled]))
|
||||||
|
|
||||||
(defn passphrase->words [s]
|
|
||||||
(when s
|
|
||||||
(-> (string/trim s)
|
|
||||||
(string/replace-all #"\s+" " ")
|
|
||||||
(string/split #" "))))
|
|
||||||
|
|
||||||
(defn words->passphrase [v]
|
|
||||||
(string/join " " v))
|
|
||||||
|
|
||||||
(def valid-word-counts #{12 15 18 21 24})
|
|
||||||
|
|
||||||
(defn valid-word-counts? [v]
|
|
||||||
(boolean (valid-word-counts (count v))))
|
|
||||||
|
|
||||||
(defn- valid-word? [s]
|
|
||||||
(re-matches #"^[A-z]+$" s))
|
|
||||||
|
|
||||||
(defn valid-words? [v]
|
|
||||||
(and
|
|
||||||
(valid-word-counts? v)
|
|
||||||
(every? valid-word? v)))
|
|
||||||
|
|
||||||
(def hex-prefix "0x")
|
(def hex-prefix "0x")
|
||||||
|
|
||||||
(defn normalized-address [address]
|
(defn normalized-address [address]
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -36,6 +36,7 @@
|
||||||
[status-im.test.utils.ethereum.eip681]
|
[status-im.test.utils.ethereum.eip681]
|
||||||
[status-im.test.utils.ethereum.core]
|
[status-im.test.utils.ethereum.core]
|
||||||
[status-im.test.utils.ethereum.ens]
|
[status-im.test.utils.ethereum.ens]
|
||||||
|
[status-im.test.utils.ethereum.mnemonic]
|
||||||
[status-im.test.utils.random]
|
[status-im.test.utils.random]
|
||||||
[status-im.test.utils.gfycat.core]
|
[status-im.test.utils.gfycat.core]
|
||||||
[status-im.test.utils.signing-phrase.core]
|
[status-im.test.utils.signing-phrase.core]
|
||||||
|
@ -47,7 +48,8 @@
|
||||||
[status-im.test.utils.universal-links.core]
|
[status-im.test.utils.universal-links.core]
|
||||||
[status-im.test.utils.http]
|
[status-im.test.utils.http]
|
||||||
[status-im.test.ui.screens.events]
|
[status-im.test.ui.screens.events]
|
||||||
[status-im.test.ui.screens.accounts.login.events]
|
[status-im.test.ui.screens.accounts.login.models]
|
||||||
|
[status-im.test.ui.screens.accounts.recover.models]
|
||||||
[status-im.test.ui.screens.wallet.db]))
|
[status-im.test.ui.screens.wallet.db]))
|
||||||
|
|
||||||
(enable-console-print!)
|
(enable-console-print!)
|
||||||
|
@ -94,6 +96,7 @@
|
||||||
'status-im.test.utils.clocks
|
'status-im.test.utils.clocks
|
||||||
'status-im.test.utils.ethereum.eip681
|
'status-im.test.utils.ethereum.eip681
|
||||||
'status-im.test.utils.ethereum.core
|
'status-im.test.utils.ethereum.core
|
||||||
|
'status-im.test.utils.ethereum.mnemonic
|
||||||
'status-im.test.utils.ethereum.ens
|
'status-im.test.utils.ethereum.ens
|
||||||
'status-im.test.utils.random
|
'status-im.test.utils.random
|
||||||
'status-im.test.utils.gfycat.core
|
'status-im.test.utils.gfycat.core
|
||||||
|
@ -105,6 +108,7 @@
|
||||||
'status-im.test.utils.universal-links.core
|
'status-im.test.utils.universal-links.core
|
||||||
'status-im.test.utils.http
|
'status-im.test.utils.http
|
||||||
'status-im.test.ui.screens.events
|
'status-im.test.ui.screens.events
|
||||||
'status-im.test.ui.screens.accounts.login.events
|
'status-im.test.ui.screens.accounts.login.models
|
||||||
|
'status-im.test.ui.screens.accounts.recover.models
|
||||||
'status-im.test.ui.screens.wallet.db
|
'status-im.test.ui.screens.wallet.db
|
||||||
'status-im.test.browser.events)
|
'status-im.test.browser.events)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
(ns status-im.test.ui.screens.accounts.login.events
|
(ns status-im.test.ui.screens.accounts.login.models
|
||||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
[status-im.utils.config :as config]
|
[status-im.utils.config :as config]
|
||||||
[status-im.ui.screens.accounts.login.models :as models]))
|
[status-im.ui.screens.accounts.login.models :as models]))
|
|
@ -0,0 +1,125 @@
|
||||||
|
(ns status-im.test.ui.screens.accounts.recover.models
|
||||||
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
|
[status-im.ui.screens.accounts.recover.models :as models]
|
||||||
|
[clojure.string :as string]
|
||||||
|
[status-im.utils.security :as security]
|
||||||
|
[status-im.i18n :as i18n]))
|
||||||
|
|
||||||
|
;;;; helpers
|
||||||
|
|
||||||
|
(deftest check-password-errors
|
||||||
|
(is (= :required-field (models/check-password-errors nil)))
|
||||||
|
(is (= :required-field (models/check-password-errors " ")))
|
||||||
|
(is (= :required-field (models/check-password-errors " \t\n ")))
|
||||||
|
(is (= :recover-password-too-short) (models/check-password-errors "a"))
|
||||||
|
(is (= :recover-password-too-short) (models/check-password-errors "abc"))
|
||||||
|
(is (= :recover-password-too-short) (models/check-password-errors "12345"))
|
||||||
|
(is (nil? (models/check-password-errors "123456")))
|
||||||
|
(is (nil? (models/check-password-errors "thisisapasswoord"))))
|
||||||
|
|
||||||
|
(deftest check-phrase-errors
|
||||||
|
(is (= :required-field (models/check-phrase-errors nil)))
|
||||||
|
(is (= :required-field (models/check-phrase-errors " ")))
|
||||||
|
(is (= :required-field (models/check-phrase-errors " \t\n ")))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors "phrase with four words")))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors "phrase with five cool words")))
|
||||||
|
(is (nil? (models/check-phrase-errors "monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey")))
|
||||||
|
(is (nil? (models/check-phrase-errors (string/join " " (repeat 15 "monkey")))))
|
||||||
|
(is (nil? (models/check-phrase-errors (string/join " " (repeat 18 "monkey")))))
|
||||||
|
(is (nil? (models/check-phrase-errors (string/join " " (repeat 24 "monkey")))))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors (string/join " " (repeat 14 "monkey")))))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors (string/join " " (repeat 11 "monkey")))))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors (string/join " " (repeat 19 "monkey")))))
|
||||||
|
(is (= :recovery-phrase-invalid (models/check-phrase-errors "monkey monkey monkey 12345 monkey adf+123 monkey monkey monkey monkey monkey monkey")))
|
||||||
|
;;NOTE(goranjovic): the following check should be ok because we sanitize extra whitespace
|
||||||
|
(is (nil? (models/check-phrase-errors " monkey monkey monkey\t monkey monkey monkey monkey monkey monkey monkey monkey monkey \t "))))
|
||||||
|
|
||||||
|
(deftest check-phrase-warnings
|
||||||
|
(is (nil? (models/check-phrase-warnings "monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey monkey")))
|
||||||
|
(is (nil? (models/check-phrase-warnings "game buzz method pretty olympic fat quit display velvet unveil marine crater")))
|
||||||
|
(is (= :recovery-phrase-unknown-words (models/check-phrase-warnings "game buzz method pretty zeus fat quit display velvet unveil marine crater"))))
|
||||||
|
|
||||||
|
;;;; handlers
|
||||||
|
|
||||||
|
(deftest set-phrase
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
|
||||||
|
:passphrase-valid? true}}}
|
||||||
|
(models/set-phrase "game buzz method pretty olympic fat quit display velvet unveil marine crater" {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
|
||||||
|
:passphrase-valid? true}}}
|
||||||
|
(models/set-phrase "Game buzz method pretty Olympic fat quit DISPLAY velvet unveil marine crater" {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
|
||||||
|
:passphrase-valid? true}}}
|
||||||
|
(models/set-phrase "game buzz method pretty zeus fat quit display velvet unveil marine crater" {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater "
|
||||||
|
:passphrase-valid? true}}}
|
||||||
|
(models/set-phrase " game\t buzz method pretty olympic fat quit\t display velvet unveil marine crater " {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"
|
||||||
|
:passphrase-valid? false}}}
|
||||||
|
(models/set-phrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater" {:db {}}))))
|
||||||
|
|
||||||
|
(deftest validate-phrase
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase-error nil
|
||||||
|
:passphrase-warning nil
|
||||||
|
:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"}}}
|
||||||
|
(models/validate-phrase {:db {:accounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"}}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase-error nil
|
||||||
|
:passphrase-warning :recovery-phrase-unknown-words
|
||||||
|
:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}}
|
||||||
|
(models/validate-phrase {:db {:accounts/recover {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"}}})))
|
||||||
|
(is (= {:db {:accounts/recover {:passphrase-error :recovery-phrase-invalid
|
||||||
|
:passphrase-warning :recovery-phrase-unknown-words
|
||||||
|
:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"}}}
|
||||||
|
(models/validate-phrase {:db {:accounts/recover {:passphrase "game buzz method pretty 1234 fat quit display velvet unveil marine crater"}}}))))
|
||||||
|
|
||||||
|
(deftest set-password
|
||||||
|
(is (= {:db {:accounts/recover {:password " "
|
||||||
|
:password-valid? false}}}
|
||||||
|
(models/set-password (security/mask-data " ") {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:password "abc"
|
||||||
|
:password-valid? false}}}
|
||||||
|
(models/set-password (security/mask-data "abc") {:db {}})))
|
||||||
|
(is (= {:db {:accounts/recover {:password "thisisapaswoord"
|
||||||
|
:password-valid? true}}}
|
||||||
|
(models/set-password (security/mask-data "thisisapaswoord") {:db {}}))))
|
||||||
|
|
||||||
|
(deftest validate-password
|
||||||
|
(is (= {:db {:accounts/recover {:password " "
|
||||||
|
:password-error :required-field}}}
|
||||||
|
(models/validate-password {:db {:accounts/recover {:password " "}}})))
|
||||||
|
(is (= {:db {:accounts/recover {:password "abc"
|
||||||
|
:password-error :recover-password-too-short}}}
|
||||||
|
(models/validate-password {:db {:accounts/recover {:password "abc"}}})))
|
||||||
|
(is (= {:db {:accounts/recover {:password "thisisapaswoord"
|
||||||
|
:password-error nil}}}
|
||||||
|
(models/validate-password {:db {:accounts/recover {:password "thisisapaswoord"}}}))))
|
||||||
|
|
||||||
|
(deftest recover-account
|
||||||
|
(let [new-cofx (models/recover-account {:db {:accounts/recover
|
||||||
|
{:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
|
||||||
|
:password "thisisapaswoord"}}})]
|
||||||
|
(is (= {:accounts/recover {:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
|
||||||
|
:password "thisisapaswoord"
|
||||||
|
:processing? true}}
|
||||||
|
(:db new-cofx)))
|
||||||
|
(is (= security/MaskedData
|
||||||
|
(-> new-cofx :recover-account-fx first type)))
|
||||||
|
(is (= "thisisapaswoord" (-> new-cofx :recover-account-fx second)))))
|
||||||
|
|
||||||
|
(deftest recover-account-with-checks
|
||||||
|
(let [new-cofx (models/recover-account-with-checks {:db {:accounts/recover
|
||||||
|
{:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
|
||||||
|
:password "thisisapaswoord"}}})]
|
||||||
|
(is (= {:accounts/recover {:passphrase "game buzz method pretty olympic fat quit display velvet unveil marine crater"
|
||||||
|
:password "thisisapaswoord"
|
||||||
|
:processing? true}}
|
||||||
|
(:db new-cofx)))
|
||||||
|
(is (= security/MaskedData
|
||||||
|
(-> new-cofx :recover-account-fx first type)))
|
||||||
|
(is (= "thisisapaswoord" (-> new-cofx :recover-account-fx second))))
|
||||||
|
(let [new-cofx (models/recover-account-with-checks {:db {:accounts/recover
|
||||||
|
{:passphrase "game buzz method pretty zeus fat quit display velvet unveil marine crater"
|
||||||
|
:password "thisisapaswoord"}}})]
|
||||||
|
(is (= (i18n/label :recovery-typo-dialog-title) (-> new-cofx :show-confirmation :title)))
|
||||||
|
(is (= (i18n/label :recovery-typo-dialog-description) (-> new-cofx :show-confirmation :content)))
|
||||||
|
(is (= (i18n/label :recovery-confirm-phrase) (-> new-cofx :show-confirmation :confirm-button-text)))))
|
|
@ -10,17 +10,6 @@
|
||||||
{:to "0x29b5f6efad2ad701952dfde9f29c960b5d6199c5"
|
{:to "0x29b5f6efad2ad701952dfde9f29c960b5d6199c5"
|
||||||
:data "0x70a08231000000000000000000000000a7cfd581060ec66414790691681732db249502bd"})))))
|
:data "0x70a08231000000000000000000000000a7cfd581060ec66414790691681732db249502bd"})))))
|
||||||
|
|
||||||
(deftest valid-words?
|
|
||||||
(is (not (true? (ethereum/valid-words? ["rate" "rate"]))))
|
|
||||||
(is (not (true? (ethereum/valid-words? ["rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate?"]))))
|
|
||||||
(is (true? (ethereum/valid-words? ["rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate"]))))
|
|
||||||
|
|
||||||
(deftest passphrase->words?
|
|
||||||
(is (= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"]
|
|
||||||
(ethereum/passphrase->words "one two three for five six seven height nine ten eleven twelve"))
|
|
||||||
(= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"]
|
|
||||||
(ethereum/passphrase->words " one two three for five six seven height nine ten eleven twelve "))))
|
|
||||||
|
|
||||||
(deftest chain-id->chain-keyword
|
(deftest chain-id->chain-keyword
|
||||||
(is (= (ethereum/chain-id->chain-keyword 1) :mainnet))
|
(is (= (ethereum/chain-id->chain-keyword 1) :mainnet))
|
||||||
(is (= (ethereum/chain-id->chain-keyword 3) :testnet))
|
(is (= (ethereum/chain-id->chain-keyword 3) :testnet))
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
(ns status-im.test.utils.ethereum.mnemonic
|
||||||
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
|
[status-im.utils.ethereum.mnemonic :as mnemonic]))
|
||||||
|
|
||||||
|
(deftest valid-words?
|
||||||
|
(is (not (mnemonic/valid-words? ["rate" "rate"])))
|
||||||
|
(is (not (mnemonic/valid-words? ["rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate?"])))
|
||||||
|
(is (mnemonic/valid-words? ["rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate" "rate"])))
|
||||||
|
|
||||||
|
(deftest valid-phrase
|
||||||
|
(is (not (mnemonic/valid-phrase? "rate rate")))
|
||||||
|
(is (not (mnemonic/valid-phrase? "rate rate rate rate rate rate rate rate rate rate rate rate?")))
|
||||||
|
(is (mnemonic/valid-phrase? "rate rate rate rate rate rate rate rate rate rate rate rate")))
|
||||||
|
|
||||||
|
(deftest passphrase->words?
|
||||||
|
(is (= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"]
|
||||||
|
(mnemonic/passphrase->words "one two three for five six seven height nine ten eleven twelve"))
|
||||||
|
(= ["one" "two" "three" "for" "five" "six" "seven" "height" "nine" "ten" "eleven" "twelve"]
|
||||||
|
(mnemonic/passphrase->words " one two three for five six seven height nine ten eleven twelve "))))
|
||||||
|
|
||||||
|
(deftest status-generate-phrase?
|
||||||
|
(is (mnemonic/status-generated-phrase? "game buzz method pretty olympic fat quit display velvet unveil marine crater"))
|
||||||
|
(is (not (mnemonic/status-generated-phrase? "game buzz method pretty zeus fat quit display velvet unveil marine crater"))))
|
Loading…
Reference in New Issue