From acaea3855170d1c8039bc6c7984f234eb59bf2d0 Mon Sep 17 00:00:00 2001 From: Vitaliy Vlasov Date: Thu, 14 Nov 2019 12:43:32 +0200 Subject: [PATCH] Add pin screen for new wallet accounts for Keycard Signed-off-by: Vitaliy Vlasov --- externs.js | 1 + mobile/js_files/package.json | 2 +- mobile/js_files/yarn.lock | 7 +- src/status_im/hardwallet/card.cljs | 8 + src/status_im/hardwallet/core.cljs | 151 +++++++++++++----- src/status_im/hardwallet/fx.cljs | 4 + src/status_im/ui/screens/events.cljs | 1 + src/status_im/ui/screens/keycard/views.cljs | 3 + .../ui/screens/routing/back_actions.cljs | 3 +- .../ui/screens/routing/profile_stack.cljs | 1 + src/status_im/ui/screens/routing/screens.cljs | 2 + .../ui/screens/routing/wallet_stack.cljs | 37 +++-- .../ui/screens/wallet/accounts/views.cljs | 7 +- .../ui/screens/wallet/add_new/views.cljs | 17 +- src/status_im/wallet/accounts/core.cljs | 18 ++- 15 files changed, 189 insertions(+), 73 deletions(-) diff --git a/externs.js b/externs.js index cbcbe142a2..f89775cb1a 100644 --- a/externs.js +++ b/externs.js @@ -140,6 +140,7 @@ var TopLevel = { "exec" : function () {}, "execute" : function () {}, "exists" : function () {}, + "exportKeyWithPath" : function () {}, "extPost" : function () {}, "extractGroupMembershipSignatures" : function () {}, "identicon": function() {}, diff --git a/mobile/js_files/package.json b/mobile/js_files/package.json index 99072fb0e8..86e3ae87e0 100644 --- a/mobile/js_files/package.json +++ b/mobile/js_files/package.json @@ -38,7 +38,7 @@ "react-native-screens": "^1.0.0-alpha.23", "react-native-shake": "^3.3.1", "react-native-splash-screen": "^3.2.0", - "react-native-status-keycard": "^2.5.14", + "react-native-status-keycard": "git+https://github.com/status-im/react-native-status-keycard.git#v2.5.17", "react-native-svg": "^9.8.4", "react-native-touch-id": "^4.4.1", "react-native-webview": "^6.11.1", diff --git a/mobile/js_files/yarn.lock b/mobile/js_files/yarn.lock index 5e3a1f835e..a04ef5ef10 100644 --- a/mobile/js_files/yarn.lock +++ b/mobile/js_files/yarn.lock @@ -4900,10 +4900,9 @@ react-native-splash-screen@^3.2.0: resolved "https://registry.yarnpkg.com/react-native-splash-screen/-/react-native-splash-screen-3.2.0.tgz#d47ec8557b1ba988ee3ea98d01463081b60fff45" integrity sha512-Ls9qiNZzW/OLFoI25wfjjAcrf2DZ975hn2vr6U9gyuxi2nooVbzQeFoQS5vQcbCt9QX5NY8ASEEAtlLdIa6KVg== -react-native-status-keycard@^2.5.14: - version "2.5.14" - resolved "https://registry.yarnpkg.com/react-native-status-keycard/-/react-native-status-keycard-2.5.14.tgz#105ac4642ae3df777b87020dd5d484445fff46ba" - integrity sha512-qt8+BcDMwtjhAwVahF5WryeLRU0auUcYrRhCci+O6rX/Roz0a+NHZ8a23NAducvNco7uOBuleYL7yyhMbwxXQw== +"react-native-status-keycard@git+https://github.com/status-im/react-native-status-keycard.git#v2.5.17": + version "2.5.17" + resolved "git+https://github.com/status-im/react-native-status-keycard.git#8fb12850d72918d3e89e4fcfc8fa699c2ce3f203" react-native-svg@^9.8.4: version "9.11.1" diff --git a/src/status_im/hardwallet/card.cljs b/src/status_im/hardwallet/card.cljs index cb3295c7d2..0e5bc4addc 100644 --- a/src/status_im/hardwallet/card.cljs +++ b/src/status_im/hardwallet/card.cljs @@ -173,6 +173,14 @@ (then #(re-frame/dispatch [:hardwallet.callback/on-remove-key-success %])) (catch #(re-frame/dispatch [:hardwallet.callback/on-remove-key-error (error-object->map %)])))) +(defn export-key + [{:keys [pin pairing path]}] + (log/debug "[keycard] export-key pairing:" pairing "pin:" pin "path:" path) + (.. keycard + (exportKeyWithPath pairing pin path) + (then #(re-frame/dispatch [:hardwallet.callback/on-export-key-success %])) + (catch #(re-frame/dispatch [:hardwallet.callback/on-export-key-error (error-object->map %)])))) + (defn unpair-and-delete [{:keys [pin pairing]}] (log/debug "[keycard] unpair-and-delete") diff --git a/src/status_im/hardwallet/core.cljs b/src/status_im/hardwallet/core.cljs index f6cabc484a..ee30cb3e07 100644 --- a/src/status_im/hardwallet/core.cljs +++ b/src/status_im/hardwallet/core.cljs @@ -4,6 +4,7 @@ [status-im.multiaccounts.create.core :as multiaccounts.create] [status-im.multiaccounts.logout.core :as multiaccounts.logout] [status-im.ethereum.core :as ethereum] + [status-im.ui.components.colors :as colors] [status-im.ethereum.mnemonic :as mnemonic] [status-im.i18n :as i18n] [status-im.ui.screens.navigation :as navigation] @@ -972,6 +973,55 @@ (navigation/navigate-to-cofx :keycard-connection-lost nil)) (show-wrong-keycard-alert 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 + (clear-on-card-connected)))) + +(def pin-mismatch-error #"Unexpected error SW, 0x63C\d+") + +(fx/defn 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-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)}} + (set-on-card-connected :wallet.accounts/generate-new-keycard-account) + (navigation/navigate-to-cofx :keycard-connection-lost nil)) + (re-matches 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) + (get-application-info (get-pairing db) nil)) + :else (show-wrong-keycard-alert cofx true)))) + (fx/defn on-delete-success [{:keys [db] :as cofx}] (let [key-uid (get-in db [:multiaccount :key-uid])] @@ -1131,22 +1181,12 @@ :error-label :t/puk-mismatch :enter-step :puk :puk []})})) -(fx/defn 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}})) (defn- tag-lost-exception? [code error] (or (= code "android.nfc.TagLostException") (= error "Tag was lost."))) -(def pin-mismatch-error #"Unexpected error SW, 0x63C\d+") - (fx/defn on-verify-pin-success [{:keys [db] :as cofx}] (let [on-verified (get-in db [:hardwallet :pin :on-verified]) @@ -1166,32 +1206,34 @@ (defn on-verify-pin-error [{:keys [db] :as cofx} error] (let [tag-was-lost? (= "Tag was lost." (:error error)) - setup? (boolean (get-in db [:hardwallet :setup-step]))] + setup? (boolean (get-in db [:hardwallet :setup-step])) + exporting? (get-in db [:hardwallet :on-export-success])] (log/debug "[hardwallet] verify pin error" error) - (fx/merge cofx - (when 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)}} - (set-on-card-connected :hardwallet/verify-pin) - (navigation/navigate-to-cofx (if setup? - :keycard-connection-lost-setup - :keycard-connection-lost) nil)) - (if (re-matches 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})} - (when-not setup? - (navigation/navigate-to-cofx :enter-pin-settings nil)) - (get-application-info (get-pairing db) nil)) - (show-wrong-keycard-alert true)))))) + (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)}} + (set-on-card-connected :hardwallet/verify-pin) + (navigation/navigate-to-cofx (if setup? + :keycard-connection-lost-setup + :keycard-connection-lost) nil)) + (re-matches 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})} + (when-not setup? + (if exporting? + (navigation/navigate-back) + (navigation/navigate-to-cofx :enter-pin-settings nil))) + (get-application-info (get-pairing db) nil)) + :else (show-wrong-keycard-alert true)))) (fx/defn on-change-pin-success [{:keys [db] :as cofx}] @@ -1275,14 +1317,15 @@ card-connected? (get-in db [:hardwallet :card-connected?]) setup? (boolean (get-in db [:hardwallet :setup-step]))] (if card-connected? - {:db (assoc-in db [:hardwallet :pin :status] :verifying) - :hardwallet/verify-pin {:pin pin - :pairing pairing}} + (fx/merge cofx + {:db (assoc-in db [:hardwallet :pin :status] :verifying) + :hardwallet/verify-pin {:pin pin + :pairing pairing}}) (fx/merge cofx (set-on-card-connected :hardwallet/verify-pin) (navigation/navigate-to-cofx (if setup? :keycard-connection-lost-setup - :keycard-connection-lost) nil))))) + :keycard-processing) nil))))) (defn unblock-pin [{:keys [db] :as cofx}] @@ -1403,6 +1446,31 @@ (navigation/navigate-to-cofx :keycard-recovery-recovering nil) (navigation/navigate-to-cofx :keycard-connection-lost-setup nil))))) +(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 (vector->string (get-in db [:hardwallet :pin :export-key])) + pairing (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) + (set-on-card-connected :wallet.accounts/generate-new-keycard-account)) + (fx/merge cofx + (set-on-card-connected :wallet.accounts/generate-new-keycard-account) + (navigation/navigate-to-cofx :keycard-processing nil))))) + ; PIN enter steps: ; login - PIN is used to login ; sign - PIN for transaction sign @@ -1439,6 +1507,10 @@ (= pin-code-length numbers-entered)) (verify-pin) + (and (= enter-step :export-key) + (= pin-code-length numbers-entered)) + (generate-new-keycard-account) + (and (= enter-step :sign) (= pin-code-length numbers-entered)) (prepare-to-sign) @@ -1984,3 +2056,4 @@ (assoc-in [:signing/sign :keycard-step] :pin))} (get-application-info (get-pairing db) nil))) (show-wrong-keycard-alert true))))) + diff --git a/src/status_im/hardwallet/fx.cljs b/src/status_im/hardwallet/fx.cljs index d907a85b0a..003afc051b 100644 --- a/src/status_im/hardwallet/fx.cljs +++ b/src/status_im/hardwallet/fx.cljs @@ -78,6 +78,10 @@ :hardwallet/remove-key-with-unpair card/remove-key-with-unpair) +(re-frame/reg-fx + :hardwallet/export-key + card/export-key) + (re-frame/reg-fx :hardwallet/unpair-and-delete card/unpair-and-delete) diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index f6690aa903..a1b2719a94 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -214,6 +214,7 @@ :enter-pin-settings (hardwallet/enter-pin-screen-did-load %) :enter-pin-modal (hardwallet/enter-pin-screen-did-load %) :keycard-login-pin (hardwallet/enter-pin-screen-did-load %) + :add-new-account-pin (hardwallet/enter-pin-screen-did-load %) :hardwallet-connect (hardwallet/hardwallet-connect-screen-did-load %) :hardwallet-connect-sign (hardwallet/hardwallet-connect-screen-did-load %) :hardwallet-connect-settings (hardwallet/hardwallet-connect-screen-did-load %) diff --git a/src/status_im/ui/screens/keycard/views.cljs b/src/status_im/ui/screens/keycard/views.cljs index 100d4d2c1d..21f0c38c04 100644 --- a/src/status_im/ui/screens/keycard/views.cljs +++ b/src/status_im/ui/screens/keycard/views.cljs @@ -152,6 +152,9 @@ (defn pairing [] (loading :t/keycard-onboarding-pairing-header)) +(defn processing [] + (loading :t/processing)) + (defn welcome [] [react/view {:flex 1 :justify-content :space-between diff --git a/src/status_im/ui/screens/routing/back_actions.cljs b/src/status_im/ui/screens/routing/back_actions.cljs index b879267826..0745737b1a 100644 --- a/src/status_im/ui/screens/routing/back_actions.cljs +++ b/src/status_im/ui/screens/routing/back_actions.cljs @@ -10,6 +10,7 @@ :add-new-account :default :add-watch-account :default :add-new-account-password :default + :add-new-account-pin :default :about-app :default :help-center :default :advanced-settings :default @@ -27,4 +28,4 @@ :wallet-settings-assets :default :wallet-add-custom-token :default :currency-settings :default - :backup-seed :default}) \ No newline at end of file + :backup-seed :default}) diff --git a/src/status_im/ui/screens/routing/profile_stack.cljs b/src/status_im/ui/screens/routing/profile_stack.cljs index 8ee24b3e1e..9209c415aa 100644 --- a/src/status_im/ui/screens/routing/profile_stack.cljs +++ b/src/status_im/ui/screens/routing/profile_stack.cljs @@ -41,5 +41,6 @@ (concat [:keycard-settings :reset-card :keycard-connection-lost + :keycard-processing :enter-pin-settings])) :config {:initialRouteName :my-profile}}) diff --git a/src/status_im/ui/screens/routing/screens.cljs b/src/status_im/ui/screens/routing/screens.cljs index 72305cba83..6c1f964ed2 100644 --- a/src/status_im/ui/screens/routing/screens.cljs +++ b/src/status_im/ui/screens/routing/screens.cljs @@ -101,6 +101,7 @@ :keycard-nfc-on keycard/nfc-on :keycard-connection-lost keycard/connection-lost :keycard-connection-lost-setup keycard/connection-lost-setup + :keycard-processing keycard/processing :keycard-recovery-intro keycard.recovery/intro :keycard-recovery-start keycard.recovery/start :keycard-recovery-pair keycard.recovery/pair @@ -181,6 +182,7 @@ :add-new-account add-account/add-account :add-watch-account add-account/add-watch-account :add-new-account-password add-account/password + :add-new-account-pin add-account/pin :account-added account-settings/account-added :account-settings account-settings/account-settings}) diff --git a/src/status_im/ui/screens/routing/wallet_stack.cljs b/src/status_im/ui/screens/routing/wallet_stack.cljs index bed9430037..f746aa4bc5 100644 --- a/src/status_im/ui/screens/routing/wallet_stack.cljs +++ b/src/status_im/ui/screens/routing/wallet_stack.cljs @@ -1,20 +1,25 @@ -(ns status-im.ui.screens.routing.wallet-stack) +(ns status-im.ui.screens.routing.wallet-stack + (:require [status-im.utils.config :as config])) (def wallet-stack {:name :wallet-stack - :screens [:wallet - :wallet-account - :add-new-account - :add-watch-account - :add-new-account-password - :account-added - :account-settings - :collectibles-list - :wallet-onboarding-setup - :wallet-transaction-details - :wallet-settings-hook - :wallet-settings-assets - :wallet-add-custom-token - :wallet-custom-token-details - :currency-settings] + :screens (cond-> [:wallet + :wallet-account + :add-new-account + :add-watch-account + :add-new-account-password + :add-new-account-pin + :account-added + :account-settings + :collectibles-list + :wallet-onboarding-setup + :wallet-transaction-details + :wallet-settings-hook + :wallet-settings-assets + :wallet-add-custom-token + :wallet-custom-token-details + :currency-settings] + config/hardwallet-enabled? + (concat [:keycard-connection-lost + :keycard-processing])) :config {:initialRouteName :wallet}}) diff --git a/src/status_im/ui/screens/wallet/accounts/views.cljs b/src/status_im/ui/screens/wallet/accounts/views.cljs index bf4ea67af6..6bd56bd533 100644 --- a/src/status_im/ui/screens/wallet/accounts/views.cljs +++ b/src/status_im/ui/screens/wallet/accounts/views.cljs @@ -156,16 +156,13 @@ [icons/icon :main-icons/send {:color :white}]]]])) (views/defview accounts [] - (views/letsubs [{:keys [keycard-pairing]} [:multiaccount] - accounts [:multiaccount/accounts]] + (views/letsubs [accounts [:multiaccount/accounts]] [react/scroll-view {:horizontal true} [react/view {:flex-direction :row :padding-top 11 :padding-bottom 12} (for [account accounts] ^{:key account} [account-card account]) - ;; TODO: enable keycard support for adding new accounts - (when-not keycard-pairing - [add-card])]])) + [add-card]]])) (defn accounts-overview [] [react/view {:flex 1} diff --git a/src/status_im/ui/screens/wallet/add_new/views.cljs b/src/status_im/ui/screens/wallet/add_new/views.cljs index 0b444bceb1..c09050fec3 100644 --- a/src/status_im/ui/screens/wallet/add_new/views.cljs +++ b/src/status_im/ui/screens/wallet/add_new/views.cljs @@ -1,6 +1,7 @@ (ns status-im.ui.screens.wallet.add-new.views (:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require [status-im.ui.components.react :as react] + [status-im.ui.screens.hardwallet.pin.views :as pin.views] [status-im.i18n :as i18n] [re-frame.core :as re-frame] [status-im.ui.components.colors :as colors] @@ -85,6 +86,20 @@ :on-press #(re-frame/dispatch [:wallet.accounts/add-watch-account]) :disabled? (not (ethereum/address? address))}}]])) +(defview pin [] + (letsubs [pin [:hardwallet/pin] + status [:hardwallet/pin-status] + error-label [:hardwallet/pin-error-label]] + [react/keyboard-avoiding-view {:style {:flex 1}} + [topbar/topbar] + [pin.views/pin-view + {:pin pin + :status status + :title-label :t/current-pin + :description-label :t/current-pin-description + :error-label error-label + :step :export-key}]])) + (defview password [] (letsubs [{:keys [error]} [:add-account] entered-password (reagent/atom "")] @@ -111,4 +126,4 @@ :right {:type :next :label :t/generate-account :on-press #(re-frame/dispatch [:wallet.accounts/generate-new-account @entered-password]) - :disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))}}]])) \ No newline at end of file + :disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))}}]])) diff --git a/src/status_im/wallet/accounts/core.cljs b/src/status_im/wallet/accounts/core.cljs index 65e4e970d2..1f308be12d 100644 --- a/src/status_im/wallet/accounts/core.cljs +++ b/src/status_im/wallet/accounts/core.cljs @@ -41,9 +41,9 @@ (fn [result] (let [{:keys [publicKey address]} (get (types/json->clj result) (keyword path))] - (re-frame/dispatch [::account-generated - {:name (str "Account " path-num) - :address address + (re-frame/dispatch [:wallet.accounts/account-generated + {:name (str "Account " path-num) + :address address :public-key publicKey :path (str constants/path-wallet-root "/" path-num) :color (rand-nth colors/account-colors)}]))))))))))))) @@ -79,7 +79,7 @@ {:error (i18n/label :t/add-account-incorrect-password)})}) (fx/defn account-generated - {:events [::account-generated]} + {:events [:wallet.accounts/account-generated]} [{:keys [db] :as cofx} account] (fx/merge cofx {:db (update db :add-account assoc :account account :step :generated)} @@ -138,9 +138,15 @@ (fx/defn start-adding-new-account {:events [:wallet.accounts/start-adding-new-account]} [{:keys [db] :as cofx} {:keys [type] :as add-account}] - (let [screen (case type :generate :add-new-account-password :watch :add-watch-account)] + (let [{:keys [keycard-pairing]} (:multiaccount db) + screen (case type + :generate (if keycard-pairing :add-new-account-pin + :add-new-account-password) + :watch :add-watch-account)] (fx/merge cofx - {:db (assoc db :add-account add-account)} + {:db (cond-> (assoc db :add-account add-account) + keycard-pairing + (assoc-in [:hardwallet :pin :enter-step] :export-key))} (navigation/navigate-to-cofx screen nil)))) (fx/defn enter-phrase-next-pressed