Separate hardwallet events into multiple ns

Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
This commit is contained in:
Gheorghe Pinzaru 2020-02-25 11:52:40 +03:00
parent 9a713148c7
commit f18e7d746b
No known key found for this signature in database
GPG Key ID: C9A094959935A952
19 changed files with 1866 additions and 2073 deletions

View File

@ -26,7 +26,6 @@
[status-im.ethereum.transactions.core :as ethereum.transactions]
[status-im.fleet.core :as fleet]
[status-im.group-chats.core :as group-chats]
[status-im.hardwallet.core :as hardwallet]
[status-im.signing.keycard :as signing.keycard]
[status-im.i18n :as i18n]
[status-im.init.core :as init]
@ -65,6 +64,7 @@
[status-im.wallet.db :as wallet.db]
[taoensso.timbre :as log]
[status-im.utils.money :as money]
status-im.hardwallet.core
status-im.popover.core))
;; init module
@ -598,183 +598,11 @@
;; hardwallet module
(handlers/register-handler-fx
:hardwallet.callback/on-register-card-events
(fn [cofx [_ listeners]]
(hardwallet/on-register-card-events cofx listeners)))
(handlers/register-handler-fx
:hardwallet/get-application-info
(fn [cofx _]
(hardwallet/get-application-info cofx nil nil)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-application-info-success
(fn [cofx [_ info on-success]]
(hardwallet/on-get-application-info-success cofx info on-success)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-application-info-error
(fn [cofx [_ error]]
(hardwallet/on-get-application-info-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/check-nfc-support-success
(fn [cofx [_ supported?]]
(hardwallet/set-nfc-supported cofx supported?)))
(handlers/register-handler-fx
:hardwallet.callback/on-card-connected
(fn [cofx [_ data]]
(hardwallet/on-card-connected cofx data)))
(handlers/register-handler-fx
:hardwallet.callback/on-card-disconnected
(fn [cofx [_ data]]
(hardwallet/on-card-disconnected cofx data)))
(handlers/register-handler-fx
:hardwallet.callback/on-init-card-success
(fn [cofx [_ secrets]]
(hardwallet/on-init-card-success cofx secrets)))
(handlers/register-handler-fx
:hardwallet.callback/on-init-card-error
(fn [cofx [_ error]]
(hardwallet/on-init-card-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-install-applet-and-init-card-error
(fn [cofx [_ error]]
(hardwallet/on-install-applet-and-init-card-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-pair-success
(fn [cofx [_ pairing]]
(hardwallet/on-pair-success cofx pairing)))
(handlers/register-handler-fx
:hardwallet.callback/on-pair-error
(fn [cofx [_ error]]
(hardwallet/on-pair-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-generate-mnemonic-success
(fn [cofx [_ mnemonic]]
(hardwallet/on-generate-mnemonic-success cofx mnemonic)))
(handlers/register-handler-fx
:hardwallet.callback/on-generate-mnemonic-error
(fn [cofx [_ error]]
(hardwallet/on-generate-mnemonic-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-generate-and-load-key-success
[(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]
(fn [cofx [_ data]]
(hardwallet/on-generate-and-load-key-success cofx data)))
(handlers/register-handler-fx
:hardwallet.callback/on-generate-and-load-key-error
(fn [cofx [_ error]]
(hardwallet/on-generate-and-load-key-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-unblock-pin-success
(fn [cofx _]
(hardwallet/on-unblock-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-unblock-pin-error
(fn [cofx [_ error]]
(hardwallet/on-unblock-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-verify-pin-success
(fn [cofx _]
(hardwallet/on-verify-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-verify-pin-error
(fn [cofx [_ error]]
(hardwallet/on-verify-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-change-pin-success
(fn [cofx _]
(hardwallet/on-change-pin-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-change-pin-error
(fn [cofx [_ error]]
(hardwallet/on-change-pin-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-unpair-success
(fn [cofx _]
(hardwallet/on-unpair-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-unpair-error
(fn [cofx [_ error]]
(hardwallet/on-unpair-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-delete-success
(fn [cofx _]
(hardwallet/on-delete-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-delete-error
(fn [cofx [_ error]]
(hardwallet/on-delete-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-remove-key-success
(fn [cofx _]
(hardwallet/on-remove-key-success cofx)))
(handlers/register-handler-fx
:hardwallet.callback/on-remove-key-error
(fn [cofx [_ error]]
(hardwallet/on-remove-key-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-keys-success
(fn [cofx [_ data]]
(hardwallet/on-get-keys-success cofx data)))
(handlers/register-handler-fx
:hardwallet.callback/on-get-keys-error
(fn [cofx [_ error]]
(hardwallet/on-get-keys-error cofx error)))
(handlers/register-handler-fx
:hardwallet.callback/on-sign-error
(fn [cofx [_ error]]
(hardwallet/on-sign-error cofx error)))
(handlers/register-handler-fx
:hardwallet.ui/password-option-pressed
(fn [cofx _]
(hardwallet/password-option-pressed cofx)))
(handlers/register-handler-fx
:hardwallet.ui/go-to-settings-button-pressed
(fn [_ _]
{:hardwallet/open-nfc-settings nil}))
(handlers/register-handler-fx
:hardwallet.ui/begin-setup-button-pressed
(fn [cofx _]
(hardwallet/begin-setup-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/start-installation
(fn [cofx _]
(hardwallet/start-installation cofx)))
(handlers/register-handler-fx
:hardwallet.ui/pair-card-button-pressed
(fn [{:keys [db]} _]
@ -785,21 +613,6 @@
(fn [{:keys [db]} [_ pair-code]]
{:db (assoc-in db [:hardwallet :secrets :password] pair-code)}))
(handlers/register-handler-fx
:hardwallet.ui/pair-code-next-button-pressed
(fn [cofx]
(hardwallet/pair-code-next-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet.ui/recovery-phrase-next-button-pressed
(fn [cofx _]
(hardwallet/recovery-phrase-next-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet.ui/recovery-phrase-confirm-word-next-button-pressed
(fn [cofx _]
(hardwallet/recovery-phrase-confirm-word cofx)))
(handlers/register-handler-fx
:hardwallet.ui/recovery-phrase-confirm-word-back-button-pressed
(fn [{:keys [db]} _]
@ -810,87 +623,17 @@
(fn [{:keys [db]} [_ input]]
{:db (assoc-in db [:hardwallet :recovery-phrase :input-word] input)}))
(handlers/register-handler-fx
:hardwallet.ui/recovery-phrase-confirm-pressed
(fn [cofx _]
(hardwallet/load-loading-keys-screen cofx)))
(handlers/register-handler-fx
:hardwallet/load-loading-keys-screen
(fn [cofx _]
(hardwallet/load-loading-keys-screen cofx)))
(handlers/register-handler-fx
:hardwallet.ui/recovery-phrase-cancel-pressed
(fn [{:keys [db]} _]
{:db (assoc-in db [:hardwallet :setup-step] :recovery-phrase)}))
(handlers/register-handler-fx
:hardwallet/load-generating-mnemonic-screen
(fn [cofx _]
(hardwallet/load-generating-mnemonic-screen cofx)))
(handlers/register-handler-fx
:hardwallet/pair
(fn [cofx _]
(hardwallet/pair cofx)))
(handlers/register-handler-fx
:hardwallet/verify-pin
(fn [cofx _]
(hardwallet/verify-pin cofx)))
(handlers/register-handler-fx
:hardwallet/change-pin
(fn [cofx _]
(hardwallet/change-pin cofx)))
(handlers/register-handler-fx
:hardwallet.ui/success-button-pressed
(fn [cofx _]
(hardwallet/success-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet.ui/pin-numpad-button-pressed
(fn [cofx [_ number step]]
(hardwallet/update-pin cofx number step)))
(handlers/register-handler-fx
:hardwallet.ui/enter-pin-navigate-back-button-clicked
(fn [cofx _]
(hardwallet/enter-pin-navigate-back-button-clicked cofx)))
(handlers/register-handler-fx
:hardwallet.ui/hardwallet-connect-navigate-back-button-clicked
(fn [cofx _]
(hardwallet/hardwallet-connect-navigate-back-button-clicked cofx)))
(handlers/register-handler-fx
:hardwallet/process-pin-input
(fn [cofx _]
(hardwallet/process-pin-input cofx)))
(handlers/register-handler-fx
:hardwallet.ui/pin-numpad-delete-button-pressed
(fn [{:keys [db]} [_ step]]
(when-not (empty? (get-in db [:hardwallet :pin step]))
{:db (update-in db [:hardwallet :pin step] pop)})))
(handlers/register-handler-fx
:hardwallet.ui/card-ready-next-button-pressed
(fn [cofx _]
(hardwallet/card-ready-next-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/proceed-to-generate-mnemonic
(fn [cofx _]
(hardwallet/proceed-to-generate-mnemonic cofx)))
(handlers/register-handler-fx
:hardwallet/generate-mnemonic
(fn [cofx _]
(hardwallet/generate-mnemonic cofx)))
(handlers/register-handler-fx
:hardwallet.ui/create-pin-button-pressed
(fn [{:keys [db]} _]
@ -898,76 +641,6 @@
(assoc-in [:hardwallet :setup-step] :pin)
(assoc-in [:hardwallet :pin :enter-step] :original))}))
(handlers/register-handler-fx
:hardwallet.ui/error-button-pressed
(fn [cofx _]
(hardwallet/error-button-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/change-pin-pressed
(fn [cofx _]
(hardwallet/change-pin-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/proceed-to-change-pin
(fn [cofx _]
(hardwallet/proceed-to-change-pin cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/unpair-card-pressed
(fn [cofx _]
(hardwallet/unpair-card-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/unpair-card-confirmed
(fn [cofx _]
(hardwallet/unpair-card-confirmed cofx)))
(handlers/register-handler-fx
:hardwallet/unpair
(fn [cofx _]
(hardwallet/unpair cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/reset-card-pressed
(fn [cofx _]
(hardwallet/reset-card-pressed cofx)))
(handlers/register-handler-fx
:keycard-settings.ui/reset-card-next-button-pressed
(fn [cofx _]
(hardwallet/reset-card-next-button-pressed cofx)))
(handlers/register-handler-fx
:hardwallet/proceed-to-reset-card
(fn [cofx _]
(hardwallet/proceed-to-reset-card cofx)))
(handlers/register-handler-fx
:hardwallet/unpair-and-delete
(fn [cofx _]
(hardwallet/unpair-and-delete cofx)))
(handlers/register-handler-fx
:hardwallet/remove-key-with-unpair
(fn [cofx _]
(hardwallet/remove-key-with-unpair cofx)))
(handlers/register-handler-fx
:hardwallet/navigate-to-enter-pin-screen
(fn [cofx _]
(hardwallet/navigate-to-enter-pin-screen cofx)))
(handlers/register-handler-fx
:hardwallet/navigate-to-reset-card-screen
(fn [cofx _]
(hardwallet/navigate-to-reset-card-screen cofx)))
(handlers/register-handler-fx
:hardwallet/unblock-pin
(fn [cofx _]
(hardwallet/unblock-pin cofx)))
;; browser module
(handlers/register-handler-fx
@ -1141,13 +814,6 @@
(fn [cofx [_ chat-id]]
(group-chats/join-chat cofx chat-id)))
;; profile module
(handlers/register-handler-fx
:profile.ui/keycard-settings-button-pressed
(fn [cofx]
(hardwallet/navigate-to-keycard-settings cofx)))
;; transport module
(handlers/register-handler-fx

View File

@ -0,0 +1,100 @@
(ns status-im.hardwallet.change-pin
(:require [status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.hardwallet.onboarding :as onboarding]
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]))
(fx/defn change-pin-pressed
{:events [:keycard-settings.ui/change-pin-pressed]}
[{:keys [db] :as cofx}]
(let [pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
enter-step (if (zero? pin-retry-counter) :puk :current)]
(fx/merge cofx
{:db
(assoc-in db [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:original []
:confirmation []
:status nil
:error-label nil
:on-verified :hardwallet/proceed-to-change-pin})}
(common/navigate-to-enter-pin-screen))))
(fx/defn proceed-to-change-pin
{:events [:hardwallet/proceed-to-change-pin]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] :original)
(assoc-in [:hardwallet :pin :status] nil))}
(navigation/navigate-to-cofx :enter-pin-settings nil)))
(fx/defn change-pin
{:events [:hardwallet/change-pin]}
[{:keys [db] :as cofx}]
(let [pairing (common/get-pairing db)
new-pin (common/vector->string (get-in db [:hardwallet :pin :original]))
current-pin (common/vector->string (get-in db [:hardwallet :pin :current]))
setup-step (get-in db [:hardwallet :setup-step])
card-connected? (get-in db [:hardwallet :card-connected?])]
(if (= setup-step :pin)
(onboarding/load-preparing-screen cofx)
(if card-connected?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/change-pin {:new-pin new-pin
:current-pin current-pin
:pairing pairing}})
(fx/merge cofx
(common/set-on-card-connected :hardwallet/change-pin)
(navigation/navigate-to-cofx :hardwallet-connect nil))))))
(fx/defn on-change-pin-success
{:events [:hardwallet.callback/on-change-pin-success]}
[{:keys [db] :as cofx}]
(let [pin (get-in db [:hardwallet :pin :original])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:status nil
:login pin
:confirmation []
:error-label nil})
:utils/show-popup {:title ""
:content (i18n/label :t/pin-changed)}}
(common/clear-on-card-connected)
(when (:multiaccounts/login db)
(navigation/navigate-to-cofx :keycard-login-pin nil))
(when (:multiaccounts/login db)
(common/get-keys-from-keycard))
(when (:multiaccount/multiaccount db)
(multiaccounts.logout/logout)))))
(fx/defn on-change-pin-error
{:events [:hardwallet.callback/on-change-pin-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] change pin error" error)
(let [tag-was-lost? (= "Tag was lost." (:error error))]
(fx/merge cofx
(if tag-was-lost?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :status] nil)
:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/cannot-read-card)}}
(common/set-on-card-connected :hardwallet/change-pin)
(navigation/navigate-to-cofx :hardwallet-connect nil))
(if (re-matches common/pin-mismatch-error (:error error))
(fx/merge cofx
{:db (update-in db [:hardwallet :pin] merge {:status :error
:enter-step :current
:puk []
:current []
:original []
:confirmation []
:sign []
:error-label :t/pin-mismatch})}
(navigation/navigate-to-cofx :enter-pin-settings nil)
(common/get-application-info (common/get-pairing db) nil))
(common/show-wrong-keycard-alert true))))))

View File

@ -0,0 +1,385 @@
(ns status-im.hardwallet.common
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as platform]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[status-im.utils.keychain.core :as keychain]
[status-im.hardwallet.nfc :as nfc]))
(def default-pin "000000")
(def pin-mismatch-error #"Unexpected error SW, 0x63C\d+")
(fx/defn dispatch-event
[_ event]
{:dispatch [event]})
(defn vector->string
"Converts numbers stored in vector into string,
e.g. [1 2 3 4 5 6] -> \"123456\""
[v]
(apply str v))
(defn hardwallet-supported? []
(and config/hardwallet-enabled?
platform/android?
(nfc/nfc-supported?)))
(defn get-card-state
[{:keys [has-master-key?
applet-installed?
initialized?
free-pairing-slots
paired?]}]
(cond
(not applet-installed?)
:blank
(not initialized?)
:pre-init
(not has-master-key?)
:init
has-master-key?
:multiaccount
(and (not paired?)
(zero? free-pairing-slots))
:no-pairing-slots))
(defn- tag-lost? [error]
(= error "Tag was lost."))
(defn find-multiaccount-by-keycard-instance-uid
[db keycard-instance-uid]
(when keycard-instance-uid
(->> (:multiaccounts/multiaccounts db)
vals
(filter #(= keycard-instance-uid (:keycard-instance-uid %)))
first)))
(defn find-multiaccount-by-key-uid
[db key-uid]
(when key-uid
(->> (:multiaccounts/multiaccounts db)
vals
(filter #(= (ethereum/normalized-hex key-uid) (:key-uid %)))
first)))
(defn get-pairing
([db]
(get-pairing db (get-in db [:hardwallet :application-info :key-uid])))
([db key-uid]
(or
(get-in db [:multiaccount :keycard-pairing])
(get-in db [:hardwallet :secrets :pairing])
(when key-uid
(:keycard-pairing
(find-multiaccount-by-key-uid db key-uid))))))
(re-frame/reg-fx
:hardwallet/set-nfc-supported
(fn [supported?]
(nfc/set-nfc-supported? supported?)))
(fx/defn listen-to-hardware-back-button
[{:keys [db]}]
(when-not (get-in db [:hardwallet :back-button-listener])
{:hardwallet/listen-to-hardware-back-button nil}))
(fx/defn remove-listener-to-hardware-back-button
[{:keys [db]}]
(when-let [listener (get-in db [:hardwallet :back-button-listener])]
{:hardwallet/remove-listener-to-hardware-back-button listener}))
(fx/defn set-on-card-connected
[{:keys [db]} on-connect]
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] on-connect)
(assoc-in [:hardwallet :last-on-card-connected] nil))})
(fx/defn stash-on-card-connected
[{:keys [db]}]
(let [on-connect (get-in db [:hardwallet :on-card-connected])]
{:db (-> db
(assoc-in [:hardwallet :last-on-card-connected] on-connect)
(assoc-in [:hardwallet :on-card-connected] nil))}))
(fx/defn restore-on-card-connected
[{:keys [db]}]
(let [on-connect (or
(get-in db [:hardwallet :on-card-connected])
(get-in db [:hardwallet :last-on-card-connected]))]
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] on-connect)
(assoc-in [:hardwallet :last-on-card-connect] nil))}))
(fx/defn clear-on-card-connected
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :on-card-connected] nil)
(assoc-in [:hardwallet :last-on-card-connected] nil))})
(fx/defn set-on-card-read
[{:keys [db]} on-connect]
{:db (-> db
(assoc-in [:hardwallet :on-card-read] on-connect)
(assoc-in [:hardwallet :last-on-card-read] nil))})
(fx/defn stash-on-card-read
[{:keys [db]}]
(let [on-connect (get-in db [:hardwallet :on-card-read])]
{:db (-> db
(assoc-in [:hardwallet :last-on-card-read] on-connect)
(assoc-in [:hardwallet :on-card-read] nil))}))
(fx/defn restore-on-card-read
[{:keys [db]}]
(let [on-connect (or
(get-in db [:hardwallet :on-card-read])
(get-in db [:hardwallet :last-on-card-read]))]
{:db (-> db
(assoc-in [:hardwallet :on-card-read] on-connect)
(assoc-in [:hardwallet :last-on-card-connect] nil))}))
(fx/defn clear-on-card-read
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :on-card-read] nil)
(assoc-in [:hardwallet :last-on-card-read] nil))})
(fx/defn on-add-listener-to-hardware-back-button
"Adds listener to hardware back button on Android.
During keycard setup we show user a warning that setup will be cancelled
when back button pressed. This prevents user from going back during setup
flow as some of the actions changing keycard step could not be repeated."
{:events [:hardwallet/add-listener-to-hardware-back-button]}
[{:keys [db]} listener]
{:db (assoc-in db [:hardwallet :back-button-listener] listener)})
(fx/defn show-wrong-keycard-alert
[_ card-connected?]
(when card-connected?
{:utils/show-popup {:title (i18n/label :t/wrong-card)
:content (i18n/label :t/wrong-card-text)}}))
(fx/defn unauthorized-operation
[{:keys [db] :as cofx}]
(fx/merge cofx
{:utils/show-popup {:title ""
:content (i18n/label :t/keycard-unauthorized-operation)}}
(clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn navigate-to-enter-pin-screen
{:events [:hardwallet/navigate-to-enter-pin-screen]}
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount-key-uid (get-in db [:multiaccount :key-uid])
keycard-multiaccount? (boolean (get-in db [:multiaccount :keycard-pairing]))]
(if (or (nil? keycard-multiaccount?)
(and key-uid
(= key-uid multiaccount-key-uid)))
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :current] [])}
(navigation/navigate-to-cofx :enter-pin-settings nil))
(unauthorized-operation cofx))))
(defn- tag-lost-exception? [code error]
(or
(= code "android.nfc.TagLostException")
(= error "Tag was lost.")))
(fx/defn process-error [{:keys [db]} code error]
(when-not (tag-lost-exception? code error)
{:db (assoc-in db [:hardwallet :setup-step] :error)}))
(fx/defn get-keys-from-keycard
[{:keys [db]}]
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
pairing (get-in db [:multiaccounts/multiaccounts key-uid :keycard-pairing])
pin (string/join (get-in db [:hardwallet :pin :login]))]
(when (and pairing
(seq pin))
{:db (assoc-in db [:hardwallet :pin :status] :verifying)
:hardwallet/get-keys {:pairing pairing
:pin pin}})))
(fx/defn on-get-keys-success
{:events [:hardwallet.callback/on-get-keys-success]}
[{:keys [db] :as cofx} data]
(let [{:keys [key-uid encryption-public-key whisper-private-key] :as account-data} (js->clj data :keywordize-keys true)
{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts key-uid])
key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount-data (types/clj->json {:name name
:key-uid key-uid
:photo-path photo-path})
save-keys? (get-in db [:multiaccounts/login :save-password?])]
(fx/merge
cofx
{:db
(-> db
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:hardwallet :pin :login] [])
(assoc-in [:hardwallet :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:hardwallet :flow] nil)
(update :multiaccounts/login assoc
:password encryption-public-key
:key-uid key-uid
:photo-path photo-path
:name name))
:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
:hardwallet/login-with-keycard {:multiaccount-data multiaccount-data
:password encryption-public-key
:chat-key whisper-private-key}}
(when save-keys?
(keychain/save-hardwallet-keys key-uid encryption-public-key whisper-private-key))
(clear-on-card-connected)
(clear-on-card-read))))
(fx/defn on-get-keys-error
{:events [:hardwallet.callback/on-get-keys-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] get keys error: " error)
(let [tag-was-lost? (= "Tag was lost." (:error error))
key-uid (get-in db [:hardwallet :application-info :key-uid])
flow (get-in db [:hardwallet :flow])]
(if tag-was-lost?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :status] nil)}
(navigation/navigate-to-cofx :keycard-connection-lost nil))
(if (re-matches pin-mismatch-error (:error error))
(fx/merge cofx
{:hardwallet/get-application-info {:pairing (get-pairing db key-uid)}
:db (update-in db [:hardwallet :pin] merge {:status :error
:login []
:import-multiaccount []
:error-label :t/pin-mismatch})}
(if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-pin nil)
(navigation/navigate-to-cofx :keycard-login-pin nil)))
(show-wrong-keycard-alert true)))))
;; Get application info
(fx/defn get-application-info
{:events [:hardwallet/get-application-info]}
[{:keys [db]} pairing on-card-read]
(let [key-uid (get-in db [:hardwallet :application-info :key-uid])
pairing' (or pairing
(when key-uid
(get-pairing db key-uid)))]
{:hardwallet/get-application-info {:pairing pairing'
:on-success on-card-read}}))
(fx/defn on-get-application-info-success
{:events [:hardwallet.callback/on-get-application-info-success]}
[{:keys [db] :as cofx} info on-success]
(let [info' (-> info
(js->clj :keywordize-keys true)
(update :key-uid ethereum/normalized-hex))
{:keys [pin-retry-counter puk-retry-counter]} info'
view-id (:view-id db)
connect-screen? (contains? #{:hardwallet-connect
:hardwallet-connect-sign
:hardwallet-connect-settings} view-id)
{:keys [on-card-read]} (:hardwallet db)
on-success' (or on-success on-card-read)
enter-step (if (zero? pin-retry-counter)
:puk
(get-in db [:hardwallet :pin :enter-step]))]
(log/debug "[hardwallet] on-get-application-info-success"
"connect-screen?" connect-screen?
"on-success" on-success')
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :enter-step] enter-step)
(update-in [:hardwallet :pin :error-label] #(if (= :puk enter-step)
:t/enter-puk-code-description
%))
(assoc-in [:hardwallet :application-info] info')
(assoc-in [:hardwallet :application-info :applet-installed?] true)
(assoc-in [:hardwallet :application-info-error] nil))}
(stash-on-card-read)
(if (zero? puk-retry-counter)
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/keycard-blocked)}}
(when on-success'
(dispatch-event on-success'))))))
(fx/defn on-get-application-info-error
{:events [:hardwallet.callback/on-get-application-info-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] application info error " error)
(let [on-card-read (get-in db [:hardwallet :on-card-read])
on-card-connected (get-in db [:hardwallet :on-card-connected])
connect-screen? (= (:view-id db) :hardwallet-connect)
login? (= on-card-read :hardwallet/login-with-keycard)
tag-was-lost? (= "Tag was lost." (:error error))]
(if tag-was-lost?
(navigation/navigate-to-cofx cofx :keycard-connection-lost nil)
(if login?
(fx/merge cofx
(clear-on-card-read)
(navigation/navigate-to-cofx :not-keycard nil))
(fx/merge cofx
{:db (assoc-in db [:hardwallet :application-info-error] error)}
(when (= on-card-connected :hardwallet/prepare-to-sign)
(show-wrong-keycard-alert true))
(when-not connect-screen?
(clear-on-card-read))
(when on-card-read
(dispatch-event on-card-read)))))))
(fx/defn on-card-connected
{:events [:hardwallet.callback/on-card-connected]}
[{:keys [db] :as cofx} _]
(log/debug "[hardwallet] card connected")
(let [instance-uid (get-in db [:hardwallet :application-info :instance-uid])
key-uid (get-in db [:hardwallet :application-info :key-uid])
accounts-screen? (= :multiaccounts (:view-id db))
should-read-instance-uid? (nil? instance-uid)
on-card-connected (get-in db [:hardwallet :on-card-connected])
on-card-read (cond
should-read-instance-uid? :hardwallet/get-application-info
:else (get-in db [:hardwallet :on-card-read]))
pairing (get-pairing db key-uid)]
(log/debug "[hardwallet] on-card-connected"
"on-card-connected" on-card-connected
"on-card-read" on-card-read)
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :card-connected?] true)
(assoc-in [:hardwallet :card-read-in-progress?] (boolean on-card-read)))}
(when on-card-connected
(dispatch-event on-card-connected))
(stash-on-card-connected)
(when (and on-card-read
(nil? on-card-connected))
(get-application-info pairing on-card-read)))))
(fx/defn on-card-disconnected
{:events [:hardwallet.callback/on-card-disconnected]}
[{:keys [db] :as cofx} _]
(log/debug "[hardwallet] card disconnected ")
(let [setup-running? (get-in db [:hardwallet :setup-step])
on-card-connected (get-in db [:hardwallet :on-card-connected])]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :card-connected?] false)
(assoc-in [:hardwallet :card-read-in-progress?] false))}
(restore-on-card-connected)
(restore-on-card-read)
(when (and setup-running?
on-card-connected)
(navigation/navigate-to-cofx :keycard-connection-lost-setup nil)))))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
(ns status-im.hardwallet.delete-key
(:require [status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]))
(fx/defn on-delete-success
{:events [:hardwallet.callback/on-delete-success]}
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:multiaccount :key-uid])]
(fx/merge cofx
{:db (-> db
(update :multiaccounts/multiaccounts dissoc key-uid)
(assoc-in [:hardwallet :secrets] nil)
(assoc-in [:hardwallet :application-info] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
;;FIXME delete multiaccount
:utils/show-popup {:title ""
:content (i18n/label :t/card-reseted)}}
(common/clear-on-card-connected)
(multiaccounts.logout/logout))))
(fx/defn on-delete-error
{:events [:hardwallet.callback/on-delete-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] delete error" error)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil})
:hardwallet/get-application-info nil
:utils/show-popup {:title ""
:content (i18n/label :t/something-went-wrong)}}
(common/clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn reset-card-pressed
{:events [:keycard-settings.ui/reset-card-pressed]}
[cofx]
(navigation/navigate-to-cofx cofx :reset-card nil))
(fx/defn delete-card
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount-key-uid (get-in db [:multiaccount :key-uid])]
(if (and key-uid
(= key-uid multiaccount-key-uid))
{:hardwallet/delete nil}
(common/unauthorized-operation cofx))))
(fx/defn navigate-to-reset-card-screen
{:events [:hardwallet/navigate-to-reset-card-screen]}
[cofx]
(navigation/navigate-to-cofx cofx :reset-card nil))
(fx/defn reset-card-next-button-pressed
{:events [:keycard-settings.ui/reset-card-next-button-pressed]}
[{:keys [db]}]
{:db (assoc-in db [:hardwallet :reset-card :disabled?] true)
:dispatch [:hardwallet/proceed-to-reset-card]})
(fx/defn proceed-to-reset-card
{:events [:hardwallet/proceed-to-reset-card]}
[{:keys [db] :as cofx}]
(let [pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
enter-step (if (zero? pin-retry-counter) :puk :current)]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:status nil
:error-label nil
:on-verified :hardwallet/remove-key-with-unpair})}
(common/set-on-card-connected :hardwallet/navigate-to-enter-pin-screen)
(common/navigate-to-enter-pin-screen))))

View File

@ -0,0 +1,45 @@
(ns status-im.hardwallet.export-key
(:require [status-im.utils.fx :as fx]
[re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.hardwallet.common :as common]))
(fx/defn on-export-key-error
{:events [:hardwallet.callback/on-export-key-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] export key error" error)
(let [tag-was-lost? (= "Tag was lost." (:error error))]
(cond tag-was-lost?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :status] nil)
:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/cannot-read-card)}}
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account)
(navigation/navigate-to-cofx :keycard-connection-lost nil))
(re-matches common/pin-mismatch-error (:error error))
(fx/merge cofx
{:db (update-in db [:hardwallet :pin] merge {:status :error
:enter-step :export-key
:puk []
:current []
:original []
:confirmation []
:sign []
:error-label :t/pin-mismatch})}
(navigation/navigate-back)
(common/get-application-info (common/get-pairing db) nil))
:else (common/show-wrong-keycard-alert cofx true))))
(fx/defn on-export-key-success
{:events [:hardwallet.callback/on-export-key-success]}
[{:keys [db] :as cofx} pubkey]
(let [multiaccount-address (get-in db [:multiaccount :address])
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
callback-fn (get-in db [:hardwallet :on-export-success])
pairings (get-in db [:hardwallet :pairings])
event-to-dispatch (callback-fn pubkey)]
(re-frame/dispatch event-to-dispatch)
(fx/merge cofx
(common/clear-on-card-connected))))

View File

@ -118,6 +118,7 @@
(then #(re-frame/dispatch [:hardwallet.callback/on-retrieve-pairings-success
(types/deserialize %)]))))))
;; TODO: Should act differently on different views
(re-frame/reg-fx
:hardwallet/listen-to-hardware-back-button
;;NOTE: not done in view because effect should happen under different conditions and is not dependent on
@ -133,3 +134,12 @@
:hardwallet/remove-listener-to-hardware-back-button
(fn [listener]
(.remove listener)))
(re-frame/reg-fx
:hardwallet/generate-name-and-photo
(fn [{:keys [public-key on-success]}]
(status/gfycat-identicon-async
public-key
(fn [whisper-name photo-path]
(re-frame/dispatch
[on-success whisper-name photo-path])))))

View File

@ -0,0 +1,124 @@
(ns status-im.hardwallet.login
(:require [status-im.ethereum.core :as ethereum]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]
[status-im.hardwallet.recovery :as recovery]
[status-im.hardwallet.onboarding :as onboarding]
status-im.hardwallet.fx
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]))
(fx/defn login-got-it-pressed
{:events [:keycard.login.ui/got-it-pressed
:keycard.login.ui/dismiss-pressed
:keycard.login.pin.ui/cancel-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db db}
(navigation/navigate-to-cofx :multiaccounts nil)))
(fx/defn login-pin-more-icon-pressed
{:events [:keycard.login.pin.ui/more-icon-pressed]}
[cofx]
(bottom-sheet/show-bottom-sheet cofx {:view :keycard.login/more}))
(fx/defn login-create-key-pressed
{:events [:keycard.login.ui/create-new-key-pressed]}
[cofx]
(fx/merge cofx
(bottom-sheet/hide-bottom-sheet)
(onboarding/start-onboarding-flow)))
(fx/defn login-add-key-pressed
{:events [:keycard.login.ui/add-key-pressed]}
[cofx]
(recovery/start-import-flow cofx))
(fx/defn login-remember-me-changed
{:events [:keycard.login.ui/remember-me-changed]}
[{:keys [db] :as cofx} value]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :remember-me?] value)}))
(fx/defn login-pair-card-pressed
{:events [:keycard.login.ui/pair-card-pressed]}
[{:keys [db] :as cofx}]
(log/debug "[hardwallet] load-pair-card-pressed")
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :login)}
(navigation/navigate-to-cofx :keycard-recovery-pair nil)))
(fx/defn login-with-keycard
{:events [:hardwallet/login-with-keycard]}
[{:keys [db] :as cofx}]
(let [application-info (get-in db [:hardwallet :application-info])
key-uid (get-in db [:hardwallet :application-info :key-uid])
multiaccount (get-in db [:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
multiaccount-key-uid (get multiaccount :key-uid)
multiaccount-mismatch? (or (nil? multiaccount)
(not= multiaccount-key-uid key-uid))
pairing (:keycard-pairing multiaccount)]
(cond
(empty? application-info)
(navigation/navigate-to-cofx cofx :not-keycard nil)
(empty? key-uid)
(navigation/navigate-to-cofx cofx :keycard-blank nil)
multiaccount-mismatch?
(navigation/navigate-to-cofx cofx :keycard-wrong nil)
(empty? pairing)
(navigation/navigate-to-cofx cofx :keycard-unpaired nil)
:else
(common/get-keys-from-keycard cofx))))
(fx/defn proceed-to-login
[{:keys [db] :as cofx}]
(let [{:keys [card-connected? nfc-enabled?]} (:hardwallet db)]
(if nfc-enabled?
(fx/merge cofx
(common/set-on-card-connected :hardwallet/get-application-info)
(common/set-on-card-read :hardwallet/login-with-keycard)
(if card-connected?
(login-with-keycard)
(navigation/navigate-to-cofx :keycard-login-connect-card nil)))
(navigation/navigate-to-cofx cofx :keycard-nfc-on nil))))
(fx/defn on-hardwallet-keychain-keys
{:events [:multiaccounts.login.callback/get-hardwallet-keys-success]}
[{:keys [db] :as cofx} key-uid [encryption-public-key whisper-private-key :as creds]]
(if (nil? creds)
(navigation/navigate-to-cofx cofx :keycard-login-pin nil)
(let [{:keys [photo-path name]} (get-in db [:multiaccounts/multiaccounts key-uid])
multiaccount-data (types/clj->json {:name name
:key-uid key-uid
:photo-path photo-path})
account-data {:key-uid key-uid
:encryption-public-key encryption-public-key
:whisper-private-key whisper-private-key}]
{:db
(-> db
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:hardwallet :pin :login] [])
(assoc-in [:hardwallet :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:hardwallet :flow] nil)
(update :multiaccounts/login assoc
:password encryption-public-key
:key-uid key-uid
:photo-path photo-path
:name name
:save-password? true))
:hardwallet/login-with-keycard
{:multiaccount-data multiaccount-data
:password encryption-public-key
:chat-key whisper-private-key}})))
(fx/defn on-login-success
{:events [:keycard.login.callback/login-success]}
[_ result]
(log/debug "loginWithKeycard success: " result))

View File

@ -0,0 +1,90 @@
(ns status-im.hardwallet.mnemonic
(:require [clojure.string :as string]
[status-im.ethereum.mnemonic :as mnemonic]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]
status-im.hardwallet.fx))
(fx/defn on-generate-mnemonic-success
{:events [:hardwallet.callback/on-generate-mnemonic-success]}
[{:keys [db] :as cofx} mnemonic]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :setup-step] :recovery-phrase)
(assoc-in [:hardwallet :secrets :mnemonic] mnemonic))}
(common/clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-onboarding-recovery-phrase nil)))
(fx/defn set-mnemonic
{:events [:test-mnemonic]}
[{:keys [db] :as cofx}]
(let [selected-id (get-in db [:intro-wizard :selected-id])
accounts (get-in db [:intro-wizard :multiaccounts])
mnemonic (->> accounts
(filter (fn [{:keys [id]}]
(= id selected-id)))
first
:mnemonic)]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :secrets :mnemonic] mnemonic)}
(on-generate-mnemonic-success mnemonic))))
(fx/defn generate-mnemonic
{:events [:hardwallet/generate-mnemonic]}
[cofx]
(let [{:keys [pairing]} (get-in cofx [:db :hardwallet :secrets])]
{:hardwallet/generate-mnemonic {:pairing pairing
:words (string/join "\n" mnemonic/dictionary)}}))
(fx/defn proceed-with-generating-mnemonic
[{:keys [db] :as cofx}]
(let [pin (or (get-in db [:hardwallet :secrets :pin])
(common/vector->string (get-in db [:hardwallet :pin :current])))]
(if (empty? pin)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:enter-step :current
:on-verified :hardwallet/generate-mnemonic
:current []})}
(navigation/navigate-to-cofx :keycard-onboarding-pin nil))
(generate-mnemonic cofx))))
(fx/defn load-generating-mnemonic-screen
{:events [:hardwallet/load-generating-mnemonic-screen]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-step] :generating-mnemonic)}
(common/set-on-card-connected :hardwallet/load-generating-mnemonic-screen)
(if card-connected?
(common/dispatch-event :hardwallet/generate-mnemonic)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))
(fx/defn on-generate-mnemonic-error
{:events [:hardwallet.callback/on-generate-mnemonic-error]}
[{:keys [db] :as cofx} {:keys [error code]}]
(log/debug "[hardwallet] generate mnemonic error: " error)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-error] error)}
(common/set-on-card-connected :hardwallet/load-generating-mnemonic-screen)
(common/process-error code error)))
(fx/defn proceed-to-generate-mnemonic
{:events [:hardwallet/proceed-to-generate-mnemonic]}
[{:keys [db] :as cofx}]
(if (= (get-in db [:hardwallet :flow]) :create)
(load-generating-mnemonic-screen cofx)
{:db (assoc-in db [:hardwallet :setup-step] :recovery-phrase)}))
(fx/defn load-loading-keys-screen
{:events [:hardwallet.ui/recovery-phrase-confirm-pressed
:hardwallet/load-loading-keys-screen]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-step] :loading-keys)}
(common/set-on-card-connected :hardwallet/load-loading-keys-screen)
(if card-connected?
(common/dispatch-event :hardwallet/generate-and-load-key)
(navigation/navigate-to-cofx :hardwallet-connect nil)))))

View File

@ -0,0 +1,335 @@
(ns status-im.hardwallet.onboarding
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[status-im.hardwallet.common :as common]
[status-im.hardwallet.mnemonic :as mnemonic]
[taoensso.timbre :as log]
status-im.hardwallet.fx
[status-im.ui.components.react :as react]))
(fx/defn begin-setup-button-pressed
{:keys [:hardwallet.ui/begin-setup-button-pressed]}
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :setup-step] :pin)
(assoc-in [:hardwallet :pin :enter-step] :original)
(assoc-in [:hardwallet :pin :original] [])
(assoc-in [:hardwallet :pin :confirmation] []))})
(fx/defn start-installation
{:events [:hardwallet/start-installation]}
[{:keys [db] :as cofx}]
(let [card-state (get-in db [:hardwallet :card-state])
pin (common/vector->string (get-in db [:hardwallet :pin :original]))]
(case card-state
:pre-init
{:hardwallet/init-card pin}
(do
(log/debug (str "Cannot start keycard installation from state: " card-state))
(fx/merge cofx
{:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/something-went-wrong)}}
(navigation/navigate-to-cofx :hardwallet-authentication-method nil))))))
(fx/defn load-preparing-screen
{:events [:hardwallet/load-preparing-screen]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-step] :preparing)}
(common/set-on-card-connected :hardwallet/load-preparing-screen)
(when card-connected?
(navigation/navigate-to-cofx :keycard-onboarding-preparing nil))
(if card-connected?
(common/dispatch-event :hardwallet/start-installation)
(navigation/navigate-to-cofx :keycard-connection-lost-setup nil)))))
(fx/defn load-pairing-screen
{:events [:hardwallet/load-pairing-screen
:keycard.onboarding.puk-code.ui/confirm-pressed]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-step] :pairing)}
(common/set-on-card-connected :hardwallet/load-pairing-screen)
(when card-connected?
(navigation/navigate-to-cofx :keycard-pairing nil))
(if card-connected?
(common/dispatch-event :hardwallet/pair)
(navigation/navigate-to-cofx :keycard-connection-lost-setup nil)))))
(fx/defn puk-code-next-pressed
{:events [:keycard.onboarding.puk-code.ui/next-pressed]}
[_]
{:ui/show-confirmation {:title (i18n/label :t/secret-keys-confirmation-title)
:content (i18n/label :t/secret-keys-confirmation-text)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/cancel)
:on-accept #(re-frame/dispatch [:keycard.onboarding.puk-code.ui/confirm-pressed])
:on-cancel #()}})
(fx/defn load-finishing-screen
{:events [:keycard.onboarding.recovery-phrase-confirm-word2.ui/next-pressed
:hardwallet/load-finishing-screen]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-step] :loading-keys)}
(common/set-on-card-connected :hardwallet/load-finishing-screen)
(when card-connected?
(navigation/navigate-to-cofx :keycard-onboarding-finishing nil))
(if card-connected?
(common/dispatch-event :hardwallet/generate-and-load-key)
(navigation/navigate-to-cofx :keycard-connection-lost-setup nil)))))
(fx/defn recovery-phrase-learn-more-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/learn-more-pressed]}
[_]
(.openURL react/linking "https://keycard.status.im"))
(fx/defn recovery-phrase-next-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/next-pressed
:hardwallet.ui/recovery-phrase-next-button-pressed]}
[_]
{:ui/show-confirmation {:title (i18n/label :t/keycard-recovery-phrase-confirmation-title)
:content (i18n/label :t/keycard-recovery-phrase-confirmation-text)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/cancel)
:on-accept #(re-frame/dispatch [:keycard.onboarding.recovery-phrase.ui/confirm-pressed])
:on-cancel #()}})
(fx/defn recovery-phrase-start-confirmation
[{:keys [db] :as cofx}]
(let [mnemonic (get-in db [:hardwallet :secrets :mnemonic])
[word1 word2] (shuffle (map-indexed vector (clojure.string/split mnemonic #" ")))
word1 (zipmap [:idx :word] word1)
word2 (zipmap [:idx :word] word2)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :setup-step] :recovery-phrase-confirm-word1)
(assoc-in [:hardwallet :recovery-phrase :step] :word1)
(assoc-in [:hardwallet :recovery-phrase :confirm-error] nil)
(assoc-in [:hardwallet :recovery-phrase :input-word] nil)
(assoc-in [:hardwallet :recovery-phrase :word1] word1)
(assoc-in [:hardwallet :recovery-phrase :word2] word2))}
(common/remove-listener-to-hardware-back-button))))
(fx/defn recovery-phrase-confirm-pressed
{:events [:keycard.onboarding.recovery-phrase.ui/confirm-pressed]}
[cofx]
(fx/merge cofx
(recovery-phrase-start-confirmation)
(navigation/navigate-to-cofx :keycard-onboarding-recovery-phrase-confirm-word1 nil)))
(fx/defn recovery-phrase-next-word
[{:keys [db]}]
{:db (-> db
(assoc-in [:hardwallet :recovery-phrase :step] :word2)
(assoc-in [:hardwallet :recovery-phrase :confirm-error] nil)
(assoc-in [:hardwallet :recovery-phrase :input-word] nil)
(assoc-in [:hardwallet :setup-step] :recovery-phrase-confirm-word2))})
(fx/defn recovery-phrase-confirm-word-back-pressed
{:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/back-pressed]}
[{:keys [db] :as cofx}]
(if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1)
(navigation/navigate-to-cofx cofx :keycard-onboarding-recovery-phrase nil)
(navigation/navigate-to-cofx cofx :keycard-onboarding-recovery-phrase-confirm-word1 nil)))
(fx/defn proceed-with-generating-key
[{:keys [db] :as cofx}]
(let [pin (get-in db [:hardwallet :secrets :pin]
(common/vector->string (get-in db [:hardwallet :pin :current])))]
(if (empty? pin)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:enter-step :current
:on-verified :hardwallet/generate-and-load-key
:current []})}
(navigation/navigate-to-cofx :keycard-onboarding-pin nil))
(load-finishing-screen cofx))))
(fx/defn recovery-phrase-confirm-word-next-pressed
{:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/next-pressed
:keycard.onboarding.recovery-phrase-confirm-word.ui/input-submitted]}
[{:keys [db] :as cofx}]
(let [step (get-in db [:hardwallet :recovery-phrase :step])
input-word (get-in db [:hardwallet :recovery-phrase :input-word])
{:keys [word]} (get-in db [:hardwallet :recovery-phrase step])]
(if (= word input-word)
(if (= (:view-id db) :keycard-onboarding-recovery-phrase-confirm-word1)
(fx/merge cofx
(recovery-phrase-next-word)
(navigation/navigate-to-cofx :keycard-onboarding-recovery-phrase-confirm-word2 nil))
(proceed-with-generating-key cofx))
{:db (assoc-in db [:hardwallet :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
(fx/defn recovery-phrase-confirm-word-input-changed
{:events [:keycard.onboarding.recovery-phrase-confirm-word.ui/input-changed]}
[{:keys [db]} input]
{:db (assoc-in db [:hardwallet :recovery-phrase :input-word] input)})
(fx/defn pair-code-input-changed
{:events [:keycard.onboarding.pair.ui/input-changed]}
[{:keys [db]} input]
{:db (assoc-in db [:hardwallet :secrets :password] input)})
(fx/defn keycard-option-pressed
{:events [:onboarding.ui/keycard-option-pressed]}
[{:keys [db] :as cofx}]
(let [flow (get-in db [:hardwallet :flow])]
(fx/merge cofx
{:hardwallet/check-nfc-enabled nil}
(if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-intro nil)
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))))
(fx/defn start-onboarding-flow
{:events [:keycard/start-onboarding-flow]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :create)
:hardwallet/check-nfc-enabled nil}
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
(fx/defn open-nfc-settings-pressed
{:events [:keycard.onboarding.nfc-on/open-nfc-settings-pressed]}
[_]
{:hardwallet/open-nfc-settings nil})
(defn- show-recover-confirmation []
{:ui/show-confirmation {:title (i18n/label :t/are-you-sure?)
:content (i18n/label :t/are-you-sure-description)
:confirm-button-text (clojure.string/upper-case (i18n/label :t/yes))
:cancel-button-text (i18n/label :t/see-it-again)
:on-accept #(re-frame/dispatch [:hardwallet.ui/recovery-phrase-confirm-pressed])
:on-cancel #(re-frame/dispatch [:hardwallet.ui/recovery-phrase-cancel-pressed])}})
(fx/defn recovery-phrase-confirm-word
{:events [:hardwallet.ui/recovery-phrase-confirm-word-next-button-pressed]}
[{:keys [db]}]
(let [step (get-in db [:hardwallet :recovery-phrase :step])
input-word (get-in db [:hardwallet :recovery-phrase :input-word])
{:keys [word]} (get-in db [:hardwallet :recovery-phrase step])]
(if (= word input-word)
(if (= step :word1)
(recovery-phrase-next-word db)
(show-recover-confirmation))
{:db (assoc-in db [:hardwallet :recovery-phrase :confirm-error] (i18n/label :t/wrong-word))})))
(fx/defn card-ready-next-button-pressed
{:events [:hardwallet.ui/card-ready-next-button-pressed]}
[{:keys [db] :as cofx}]
(let [pin (get-in db [:hardwallet :secrets :pin])
pin-already-set? (boolean pin)]
(if pin-already-set?
(if (= (get-in db [:hardwallet :flow]) :create)
(mnemonic/load-generating-mnemonic-screen cofx)
{:db (assoc-in db [:hardwallet :setup-step] :recovery-phrase)})
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :setup-step] :pin)
(assoc-in [:hardwallet :pin :enter-step] :current)
(assoc-in [:hardwallet :pin :on-verified] :hardwallet/proceed-to-generate-mnemonic)
(assoc-in [:hardwallet :pin :current] [])
(assoc-in [:hardwallet :pin :original] nil))}))))
(fx/defn recovery-phrase-next-button-pressed
[{:keys [db] :as cofx}]
(if (= (get-in db [:hardwallet :flow]) :create)
(recovery-phrase-start-confirmation cofx)
(let [mnemonic (get-in db [:multiaccounts/recover :passphrase])]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :secrets :mnemonic] mnemonic)}
(mnemonic/load-loading-keys-screen)))))
(fx/defn on-install-applet-and-init-card-success
{:events [:hardwallet.callback/on-install-applet-and-init-card-success
:hardwallet.callback/on-init-card-success]}
[{:keys [db] :as cofx} secrets]
(let [secrets' (js->clj secrets :keywordize-keys true)]
(fx/merge cofx
{:hardwallet/get-application-info nil
:db (-> db
(assoc-in [:hardwallet :card-state] :init)
(assoc-in [:hardwallet :setup-step] :secret-keys)
(update-in [:hardwallet :secrets] merge secrets'))}
(common/clear-on-card-connected)
(common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-onboarding-puk-code nil))))
(fx/defn on-install-applet-and-init-card-error
{:events [:hardwallet.callback/on-install-applet-and-init-card-error
:hardwallet.callback/on-init-card-error]}
[{:keys [db] :as cofx} {:keys [code error]}]
(log/debug "[hardwallet] install applet and init card error: " error)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-error] error)}
(common/set-on-card-connected :hardwallet/load-preparing-screen)
(common/process-error code error)))
(fx/defn generate-and-load-key
{:events [:hardwallet/generate-and-load-key]}
[{:keys [db] :as cofx}]
(let [{:keys [mnemonic pairing pin]} (get-in db [:hardwallet :secrets])
{:keys [selected-id multiaccounts]} (:intro-wizard db)
user-selected-mnemonic (->> multiaccounts
(filter #(= (:id %) selected-id))
first
:mnemonic)
recovery-mnemonic (get-in db [:intro-wizard :passphrase])
mnemonic' (or user-selected-mnemonic mnemonic recovery-mnemonic)
pin' (or pin (common/vector->string (get-in db [:hardwallet :pin :current])))]
(fx/merge cofx
{:hardwallet/generate-and-load-key {:mnemonic mnemonic'
:pairing pairing
:pin pin'}}
(navigation/navigate-to-cofx :keycard-onboarding-finishing nil))))
(fx/defn begin-setup-pressed
{:events [:keycard.onboarding.intro.ui/begin-setup-pressed]}
[{:keys [db] :as cofx}]
(let [nfc-enabled? (get-in db [:hardwallet :nfc-enabled?])
flow (get-in db [:hardwallet :flow])]
(fx/merge cofx
{:db (-> db
(update :hardwallet
dissoc :secrets :card-state :multiaccount-wallet-address
:multiaccount-whisper-public-key
:application-info)
(assoc-in [:hardwallet :setup-step] :begin)
(assoc-in [:hardwallet :pin :on-verified] nil))}
(common/set-on-card-connected :hardwallet/get-application-info)
(common/set-on-card-read :hardwallet/check-card-state)
(if nfc-enabled?
(if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-start nil)
(navigation/navigate-to-cofx :keycard-onboarding-start nil))
(navigation/navigate-to-cofx :keycard-nfc-on nil)))))
(fx/defn cancel-setup-pressed
{:events [:keycard.onboarding.ui/cancel-pressed
:hardwallet/back-button-pressed
:keycard.onboarding.recovery-phrase.ui/cancel-pressed
:keycard.onboarding.connection-lost-setup.ui/cancel-setup-pressed]}
[_]
{:ui/show-confirmation {:title (i18n/label :t/keycard-cancel-setup-title)
:content (i18n/label :t/keycard-cancel-setup-text)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/no)
:on-accept #(re-frame/dispatch [:keycard.onboarding.ui/cancel-confirm-pressed])
:on-cancel #()}})
(fx/defn cancel-setup-confirm-pressed
{:events [:keycard.onboarding.ui/cancel-confirm-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
(common/remove-listener-to-hardware-back-button)
(navigation/navigate-reset {:index 0
:actions [{:routeName (if (seq (:multiaccounts/multiaccounts db))
:multiaccounts
:intro)}]})))

View File

@ -0,0 +1,259 @@
(ns status-im.hardwallet.recovery
(:require [status-im.ui.screens.navigation :as navigation]
[status-im.utils.datetime :as utils.datetime]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[status-im.utils.fx :as fx]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]
status-im.hardwallet.fx
[status-im.constants :as constants]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.core :as ethereum]
[status-im.ui.components.bottom-sheet.core :as bottom-sheet]))
(fx/defn pair* [_ password]
{:hardwallet/pair {:password password}})
(fx/defn pair
{:events [:hardwallet/pair]}
[{:keys [db] :as cofx}]
(let [{:keys [password]} (get-in cofx [:db :hardwallet :secrets])
card-connected? (get-in db [:hardwallet :card-connected?])]
(fx/merge cofx
(common/set-on-card-connected :hardwallet/pair)
(when card-connected?
(pair* password))
(if card-connected?
(navigation/navigate-to-cofx :keycard-pairing nil)
(navigation/navigate-to-cofx :keycard-connection-lost-setup nil)))))
(fx/defn pair-code-next-button-pressed
{:events [:keycard.onboarding.pair.ui/input-submitted
:hardwallet.ui/pair-code-next-button-pressed
:keycard.onboarding.pair.ui/next-pressed]}
[{:keys [db] :as cofx}]
(let [pairing (get-in db [:hardwallet :secrets :pairing])
paired-on (get-in db [:hardwallet :secrets :paired-on] (utils.datetime/timestamp))]
(fx/merge cofx
(if pairing
{:db (-> db
(assoc-in [:hardwallet :setup-step] :import-multiaccount)
(assoc-in [:hardwallet :secrets :paired-on] paired-on))}
(pair)))))
(fx/defn load-pair-screen
[{:keys [db] :as cofx}]
(log/debug "[hardwallet] load-pair-screen")
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :setup-step] :pair))
:dispatch [:bottom-sheet/hide-sheet]}
(common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-recovery-pair nil)))
(fx/defn keycard-storage-selected-for-recovery
{:events [:recovery.ui/keycard-storage-selected]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :recovery)}
(navigation/navigate-to-cofx :keycard-recovery-enter-mnemonic nil)))
(fx/defn start-import-flow
{:events [:hardwallet/recover-with-keycard-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :import)
:hardwallet/check-nfc-enabled nil}
(bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :keycard-recovery-intro nil)))
(fx/defn access-key-pressed
{:events [:multiaccounts.recover.ui/recover-multiaccount-button-pressed]}
[_]
{:dispatch [:bottom-sheet/show-sheet :recover-sheet]})
(fx/defn recovery-keycard-selected
{:events [:recovery.ui/keycard-option-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :recovery)
:hardwallet/check-nfc-enabled nil}
(navigation/navigate-to-cofx :keycard-onboarding-intro nil)))
(fx/defn begin-setup-pressed
{:events [:keycard.recovery.intro.ui/begin-recovery-pressed]}
[{:keys [db] :as cofx}]
(let [nfc-enabled? (get-in db [:hardwallet :nfc-enabled?])
flow (get-in db [:hardwallet :flow])]
(fx/merge cofx
{:db (-> db
(update :hardwallet
dissoc :secrets :card-state :multiaccount-wallet-address
:multiaccount-whisper-public-key
:application-info)
(assoc-in [:hardwallet :setup-step] :begin)
(assoc-in [:hardwallet :pin :on-verified] nil))}
(common/set-on-card-connected :hardwallet/get-application-info)
(common/set-on-card-read :hardwallet/check-card-state)
(if nfc-enabled?
(if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-start nil)
(navigation/navigate-to-cofx :keycard-onboarding-start nil))
(navigation/navigate-to-cofx :keycard-nfc-on nil)))))
(fx/defn recovery-success-finish-pressed
{:events [:keycard.recovery.success/finish-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (update db :hardwallet dissoc
:multiaccount-wallet-address
:multiaccount-whisper-public-key)}
(navigation/navigate-to-cofx :welcome nil)))
(fx/defn recovery-no-key
{:events [:keycard.recovery.no-key.ui/generate-key-pressed]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :flow] :create)
:hardwallet/check-nfc-enabled nil}
(multiaccounts.create/intro-wizard)))
(fx/defn create-keycard-multiaccount
[{:keys [db] :as cofx}]
(let [{{:keys [multiaccount secrets flow]} :hardwallet} db
{:keys [address
name
photo-path
public-key
whisper-public-key
wallet-public-key
wallet-root-public-key
whisper-address
wallet-address
wallet-root-address
whisper-private-key
encryption-public-key
instance-uid
key-uid]} multiaccount
{:keys [pairing paired-on]} secrets
{:keys [name photo-path]}
(if (nil? name)
;; name might have been generated during recovery via passphrase
(get-in db [:intro-wizard :derived constants/path-whisper-keyword])
{:name name
:photo-path photo-path})]
;; if a name is still `nil` we have to generate it before multiaccount's
;; creation otherwise spec validation will fail
(if (nil? name)
{::generate-name-and-photo whisper-public-key}
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :setup-step] nil)
(assoc :intro-wizard nil))}
(multiaccounts.create/on-multiaccount-created
{:derived {constants/path-wallet-root-keyword
{:public-key wallet-root-public-key
:address (eip55/address->checksum wallet-root-address)}
constants/path-whisper-keyword
{:public-key whisper-public-key
:address (eip55/address->checksum whisper-address)
:name name
:photo-path photo-path}
constants/path-default-wallet-keyword
{:public-key wallet-public-key
:address (eip55/address->checksum wallet-address)}}
:address address
:public-key public-key
:keycard-instance-uid instance-uid
:key-uid (ethereum/normalized-hex key-uid)
:keycard-pairing pairing
:keycard-paired-on paired-on
:chat-key whisper-private-key}
encryption-public-key
{})
(if (= flow :import)
(navigation/navigate-to-cofx :keycard-recovery-success nil)
(navigation/navigate-to-cofx :welcome nil))))))
(fx/defn on-generate-and-load-key-success
{:events [:hardwallet.callback/on-generate-and-load-key-success]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db random-guid-generator] :as cofx} data]
(let [account-data (js->clj data :keywordize-keys true)]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :multiaccount]
(-> account-data
(update :address ethereum/normalized-hex)
(update :whisper-address ethereum/normalized-hex)
(update :wallet-address ethereum/normalized-hex)
(update :wallet-root-address ethereum/normalized-hex)
(update :public-key ethereum/normalized-hex)
(update :whisper-public-key ethereum/normalized-hex)
(update :wallet-public-key ethereum/normalized-hex)
(update :wallet-root-public-key ethereum/normalized-hex)
(update :instance-uid #(get-in db [:hardwallet :multiaccount :instance-uid] %))))
(assoc-in [:hardwallet :multiaccount-wallet-address] (:wallet-address account-data))
(assoc-in [:hardwallet :multiaccount-whisper-public-key] (:whisper-public-key account-data))
(assoc-in [:hardwallet :application-info :key-uid] (:key-uid account-data))
(update :hardwallet dissoc :recovery-phrase)
(update-in [:hardwallet :secrets] dissoc :pin :puk :password)
(assoc :multiaccounts/new-installation-id (random-guid-generator))
(update-in [:hardwallet :secrets] dissoc :mnemonic))}
(common/clear-on-card-connected)
(common/remove-listener-to-hardware-back-button)
(create-keycard-multiaccount))))
(fx/defn on-generate-and-load-key-error
{:events [:hardwallet.callback/on-generate-and-load-key-error]}
[{:keys [db] :as cofx} {:keys [error code]}]
(log/debug "[hardwallet] generate and load key error: " error)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :setup-error] error)}
(common/set-on-card-connected :hardwallet/load-loading-keys-screen)
(common/process-error code error)))
(fx/defn import-multiaccount
{:events [:hardwallet/import-multiaccount]}
[{:keys [db] :as cofx}]
(let [{:keys [pairing]} (get-in db [:hardwallet :secrets])
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
key-uid (get-in db [:hardwallet :application-info :key-uid])
pairing' (or pairing (common/get-pairing db key-uid))
pin (common/vector->string (get-in db [:hardwallet :pin :import-multiaccount]))]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :multiaccount :instance-uid] instance-uid)
(assoc-in [:hardwallet :secrets] {:pairing pairing'
:paired-on (utils.datetime/timestamp)}))
:hardwallet/get-keys {:pairing pairing'
:pin pin
:on-success :hardwallet.callback/on-generate-and-load-key-success}})))
(fx/defn load-recovering-key-screen
{:events [:hardwallet/load-recovering-key-screen]}
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin] {:enter-step :import-multiaccount
:import-multiaccount []
:current []}))}
(common/listen-to-hardware-back-button)
(navigation/navigate-to-cofx :keycard-recovery-pin nil)))
(fx/defn on-name-and-photo-generated
{:events [::on-name-and-photo-generated]
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx} whisper-name photo-path]
(fx/merge
cofx
{:db (update-in db [:hardwallet :multiaccount]
(fn [multiacc]
(assoc multiacc
:name whisper-name
:photo-path photo-path)))}
(create-keycard-multiaccount)))

View File

@ -0,0 +1,107 @@
(ns status-im.hardwallet.sign
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.ethereum.core :as ethereum]
[status-im.utils.fx :as fx]
[status-im.utils.types :as types]
[status-im.i18n :as i18n]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]))
(fx/defn sign
{:events [:hardwallet/sign]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])
pairing (common/get-pairing db)
multiaccount-keycard-instance-uid (get-in db [:multiaccount :keycard-instance-uid])
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
keycard-match? (= multiaccount-keycard-instance-uid instance-uid)
hash (get-in db [:hardwallet :hash])
pin (common/vector->string (get-in db [:hardwallet :pin :sign]))]
(if (and card-connected?
keycard-match?)
{:db (-> db
(assoc-in [:hardwallet :card-read-in-progress?] true)
(assoc-in [:hardwallet :pin :status] :verifying))
:hardwallet/sign {:hash (ethereum/naked-address hash)
:pairing pairing
:pin pin}}
(fx/merge cofx
{:db (assoc-in db [:signing/sign :keycard-step] :signing)}
(common/set-on-card-connected :hardwallet/sign)
(when-not keycard-match?
(common/show-wrong-keycard-alert card-connected?))))))
(fx/defn prepare-to-sign
{:events [:hardwallet/prepare-to-sign]}
[{:keys [db] :as cofx}]
(let [card-connected? (get-in db [:hardwallet :card-connected?])
pairing (common/get-pairing db)]
(if card-connected?
(fx/merge cofx
{:db (assoc-in db [:signing/sign :keycard-step] :signing)}
(common/get-application-info pairing :hardwallet/sign))
(fx/merge cofx
{:db (assoc-in db [:signing/sign :keycard-step] :connect)}
(common/set-on-card-connected :hardwallet/prepare-to-sign)))))
(fx/defn sign-message-completed
[_ signature]
(let [signature' (-> signature
; add 27 to last byte
; https://github.com/ethereum/go-ethereum/blob/master/internal/ethapi/api.go#L431
(clojure.string/replace-first #"00$", "1b")
(clojure.string/replace-first #"01$", "1c")
(ethereum/normalized-hex))]
{:dispatch
[:signing/sign-message-completed (types/clj->json {:result signature'})]}))
(fx/defn send-transaction-with-signature
[_ data]
{:send-transaction-with-signature data})
(fx/defn on-sign-success
{:events [:hardwallet.callback/on-sign-success]}
[{:keys [db] :as cofx} signature]
(log/debug "[hardwallet] sign success: " signature)
(let [transaction (get-in db [:hardwallet :transaction])
tx-obj (select-keys transaction [:from :to :value :gas :gasPrice])]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :sign] [])
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:hardwallet :hash] nil)
(assoc-in [:hardwallet :transaction] nil))}
(common/clear-on-card-connected)
(common/get-application-info (common/get-pairing db) nil)
(if transaction
(send-transaction-with-signature {:transaction (types/clj->json transaction)
:signature signature
:on-completed #(re-frame/dispatch [:signing/transaction-completed % tx-obj])})
(sign-message-completed signature)))))
(fx/defn on-sign-error
{:events [:hardwallet.callback/on-sign-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] sign error: " error)
(let [tag-was-lost? (= "Tag was lost." (:error error))]
(fx/merge cofx
(when tag-was-lost?
(fn [{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :pin :status] nil)
(assoc-in [:signing/sign :keycard-step] :connect))
:utils/show-popup {:title (i18n/label :t/error)
:content (i18n/label :t/cannot-read-card)}}
(common/set-on-card-connected :hardwallet/prepare-to-sign))))
(if (re-matches common/pin-mismatch-error (:error error))
(fn [{:keys [db] :as cofx}]
(fx/merge cofx
{:db (-> db
(update-in [:hardwallet :pin] merge {:status :error
:sign []
:error-label :t/pin-mismatch})
(assoc-in [:signing/sign :keycard-step] :pin))}
(common/get-application-info (common/get-pairing db) nil)))
(common/show-wrong-keycard-alert true)))))

View File

@ -0,0 +1,142 @@
(ns status-im.hardwallet.unpair
(:require [re-frame.core :as re-frame]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.i18n :as i18n]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]
[status-im.hardwallet.common :as common]))
(fx/defn unpair-card-pressed
{:events [:keycard-settings.ui/unpair-card-pressed]}
[_]
{:ui/show-confirmation {:title (i18n/label :t/unpair-card)
:content (i18n/label :t/unpair-card-confirmation)
:confirm-button-text (i18n/label :t/yes)
:cancel-button-text (i18n/label :t/no)
:on-accept #(re-frame/dispatch [:keycard-settings.ui/unpair-card-confirmed])
:on-cancel #()}})
(fx/defn unpair-card-confirmed
{:events [:keycard-settings.ui/unpair-card-confirmed]}
[{:keys [db] :as cofx}]
(let [pin-retry-counter (get-in db [:hardwallet :application-info :pin-retry-counter])
enter-step (if (zero? pin-retry-counter) :puk :current)]
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:enter-step enter-step
:current []
:puk []
:status nil
:error-label nil
:on-verified :hardwallet/unpair})}
(common/navigate-to-enter-pin-screen))))
(fx/defn unpair
{:events [:hardwallet/unpair]}
[{:keys [db]}]
(let [pin (common/vector->string (get-in db [:hardwallet :pin :current]))
pairing (common/get-pairing db)]
{:hardwallet/unpair {:pin pin
:pairing pairing}}))
(fx/defn unpair-and-delete
{:events [:hardwallet/unpair-and-delete]}
[{:keys [db]}]
(let [pin (common/vector->string (get-in db [:hardwallet :pin :current]))
pairing (common/get-pairing db)]
{:hardwallet/unpair-and-delete {:pin pin
:pairing pairing}}))
(fx/defn remove-pairing-from-multiaccount
[cofx {:keys [remove-instance-uid?]}]
(fx/merge cofx
(multiaccounts.update/multiaccount-update
:keycard-pairing nil {})
(multiaccounts.update/multiaccount-update
:keycard-paired-on nil {})
(when remove-instance-uid?
(multiaccounts.update/multiaccount-update
:keycard-instance-uid nil {}))))
(fx/defn on-unpair-success
{:events [:hardwallet.callback/on-unpair-success]}
[{:keys [db] :as cofx}]
(let [instance-uid (get-in db [:hardwallet :application-info :instance-uid])
pairings (get-in db [:hardwallet :pairings])]
(fx/merge cofx
{:db (-> db
(assoc-in [:hardwallet :secrets] nil)
(update-in [:hardwallet :pairings] dissoc (keyword instance-uid))
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:hardwallet/persist-pairings (dissoc pairings (keyword instance-uid))
:utils/show-popup {:title ""
:content (i18n/label :t/card-unpaired)}}
(common/clear-on-card-connected)
(remove-pairing-from-multiaccount nil)
(navigation/navigate-to-cofx :keycard-settings nil))))
(fx/defn on-unpair-error
{:events [:hardwallet.callback/on-unpair-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] unpair error" error)
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil})
:hardwallet/get-application-info nil
:utils/show-popup {:title ""
:content (i18n/label :t/something-went-wrong)}}
(common/clear-on-card-connected)
(navigation/navigate-to-cofx :keycard-settings nil)))
(fx/defn remove-key-with-unpair
{:events [:hardwallet/remove-key-with-unpair]}
[{:keys [db] :as cofx}]
(let [pin (common/vector->string (get-in db [:hardwallet :pin :current]))
pairing (common/get-pairing db)
card-connected? (get-in db [:hardwallet :card-connected?])]
(if card-connected?
{:hardwallet/remove-key-with-unpair {:pin pin
:pairing pairing}}
(fx/merge cofx
(common/set-on-card-connected :hardwallet/remove-key-with-unpair)
(navigation/navigate-to-cofx :keycard-connection-lost nil)))))
(fx/defn on-remove-key-success
{:events [:hardwallet.callback/on-remove-key-success]}
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:multiaccount :key-uid])
instance-uid (get-in db [:hardwallet :application-info :instance-uid])
pairings (get-in db [:hardwallet :pairings])]
(fx/merge cofx
{:db (-> db
(update :multiaccounts/multiaccounts dissoc key-uid)
(assoc-in [:hardwallet :secrets] nil)
(update-in [:hardwallet :pairings] dissoc (keyword instance-uid))
(assoc-in [:hardwallet :whisper-public-key] nil)
(assoc-in [:hardwallet :wallet-address] nil)
(assoc-in [:hardwallet :application-info] nil)
(assoc-in [:hardwallet :pin] {:status nil
:error-label nil
:on-verified nil}))
:hardwallet/persist-pairings (dissoc pairings (keyword instance-uid))
;;FIXME delete multiaccount
:utils/show-popup {:title ""
:content (i18n/label :t/card-reseted)}}
(common/clear-on-card-connected)
(multiaccounts.logout/logout))))
(fx/defn on-remove-key-error
{:events [:hardwallet.callback/on-remove-key-error]}
[{:keys [db] :as cofx} error]
(log/debug "[hardwallet] remove key error" error)
(let [tag-was-lost? (= "Tag was lost." (:error error))]
(fx/merge cofx
(if tag-was-lost?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :pin :status] nil)}
(common/set-on-card-connected :hardwallet/remove-key-with-unpair))
(common/show-wrong-keycard-alert true)))))

View File

@ -0,0 +1,33 @@
(ns status-im.hardwallet.wallet
(:require [status-im.ethereum.core :as ethereum]
[status-im.ui.components.colors :as colors]
[status-im.utils.fx :as fx]
[status-im.hardwallet.common :as common]
[status-im.ui.screens.navigation :as navigation]
[status-im.constants :as constants]
[status-im.ethereum.eip55 :as eip55]))
(fx/defn generate-new-keycard-account
{:events [:wallet.accounts/generate-new-keycard-account]}
[{:keys [db] :as cofx}]
(let [path-num (inc (get-in db [:multiaccount :latest-derived-path]))
path (str constants/path-wallet-root "/" path-num)
card-connected? (get-in db [:hardwallet :card-connected?])
pin (common/vector->string (get-in db [:hardwallet :pin :export-key]))
pairing (common/get-pairing db)]
(if card-connected?
(fx/merge cofx
{:db (assoc-in db [:hardwallet :on-export-success]
#(vector :wallet.accounts/account-generated
{:name (str "Account " path-num)
;; Strip leading 04 prefix denoting uncompressed key format
:address (eip55/address->checksum (str "0x" (ethereum/public-key->address (subs % 2))))
:public-key (str "0x" %)
:path path
:color (rand-nth colors/account-colors)}))
:hardwallet/export-key {:pin pin :pairing pairing :path path}}
(navigation/navigate-to-cofx :keycard-processing nil)
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account))
(fx/merge cofx
(common/set-on-card-connected :wallet.accounts/generate-new-keycard-account)
(navigation/navigate-to-cofx :keycard-processing nil)))))

View File

@ -68,7 +68,6 @@
::open-multiaccounts #(re-frame/dispatch [::initialize-multiaccounts % {:logout? false}])
:ui/listen-to-window-dimensions-change nil
::network/listen-to-network-info nil
:hardwallet/register-card-events nil
:hardwallet/check-nfc-support nil
:hardwallet/check-nfc-enabled nil
:hardwallet/retrieve-pairings nil}

View File

@ -87,27 +87,26 @@
{::store-multiaccount [selected-id hashed-password callback]}))
(fx/defn prepare-intro-wizard
[{:keys [db] :as cofx} first-time-setup?]
[{:keys [db] :as cofx}]
{:db (assoc db :intro-wizard {:step :generate-key
:weak-password? true
:back-action :intro-wizard/navigate-back
:forward-action :intro-wizard/step-forward-pressed
:encrypt-with-password? true
:first-time-setup? first-time-setup?})})
:encrypt-with-password? true})})
(fx/defn intro-wizard
{:events [:multiaccounts.create.ui/intro-wizard]}
[{:keys [db] :as cofx} first-time-setup?]
[{:keys [db] :as cofx}]
(fx/merge cofx
{:db (update db :hardwallet dissoc :flow)}
(prepare-intro-wizard first-time-setup?)
(prepare-intro-wizard)
(navigation/navigate-to-cofx :create-multiaccount-generate-key nil)))
(fx/defn get-new-key
{:events [:multiaccounts.create.ui/get-new-key]}
[{:keys [db] :as cofx}]
(fx/merge cofx
(prepare-intro-wizard false)
(prepare-intro-wizard)
(bottom-sheet/hide-bottom-sheet)
(navigation/navigate-to-cofx :create-multiaccount-generate-key nil)))

View File

@ -161,7 +161,6 @@
:next-button-disabled? true
:weak-password? true
:encrypt-with-password? true
:first-time-setup? false
:back-action :intro-wizard/navigate-back
:forward-action :multiaccounts.recover/enter-phrase-next-pressed})
(update :hardwallet dissoc :flow))}

View File

@ -1,6 +1,6 @@
(ns status-im.ui.screens.hardwallet.settings.subs
(:require [re-frame.core :as re-frame]
[status-im.hardwallet.core :as core]
[status-im.hardwallet.common :as common]
[status-im.utils.datetime :as utils.datetime]))
(re-frame/reg-sub
@ -12,7 +12,7 @@
(re-frame/reg-sub
:keycard-pairing
(fn [db]
(core/get-pairing db)))
(common/get-pairing db)))
(re-frame/reg-sub
:keycard-multiaccount-pairing

View File

@ -92,7 +92,7 @@
:text :intro-text3}] window-height]
[react/view styles/buttons-container
[components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 16)
:on-press #(re-frame/dispatch [:multiaccounts.create.ui/intro-wizard true])
:on-press #(re-frame/dispatch [:multiaccounts.create.ui/intro-wizard])
:label (i18n/label :t/get-started)}]
[components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 24)
:on-press #(re-frame/dispatch [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])