From 2f19badc6c1a8eda9ef05ac9139db2b16315b8dd Mon Sep 17 00:00:00 2001 From: Jamie Caprani Date: Mon, 13 Mar 2023 10:02:25 +0000 Subject: [PATCH] Add seed phrase flow & customization color --- .../status/ethereum/module/StatusModule.java | 12 ++- .../ios/RCTStatus/RCTStatus.m | 7 ++ src/status_im/ethereum/mnemonic.cljs | 2 +- src/status_im/native_module/core.cljs | 4 + src/status_im2/constants.cljs | 2 + .../onboarding/create_profile/view.cljs | 6 +- .../onboarding/enter_seed_phrase/style.cljs | 14 ++++ .../onboarding/enter_seed_phrase/view.cljs | 79 ++++++++++++++++++ .../contexts/onboarding/events.cljs | 80 ++++++++++++++----- .../onboarding/new_to_status/view.cljs | 11 ++- src/status_im2/events.cljs | 15 +++- src/status_im2/navigation/screens.cljs | 7 ++ status-go-version.json | 6 +- 13 files changed, 213 insertions(+), 32 deletions(-) create mode 100644 src/status_im2/contexts/onboarding/enter_seed_phrase/style.cljs create mode 100644 src/status_im2/contexts/onboarding/enter_seed_phrase/view.cljs diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index e1e58b773d..dca8805b05 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -335,7 +335,17 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL } } - + @ReactMethod + public void restoreAccountAndLogin(final String restoreAccountRequest) { + Log.d(TAG, "restoreAccountAndLogin"); + String result = Statusgo.restoreAccountAndLogin(restoreAccountRequest); + if (result.startsWith("{\"error\":\"\"")) { + Log.d(TAG, "restoreAccountAndLogin success: " + result); + Log.d(TAG, "Geth node started"); + } else { + Log.e(TAG, "restoreAccountAndLogin failed: " + result); + } + } @ReactMethod public void saveAccountAndLogin(final String multiaccountData, final String password, final String settings, final String config, final String accountsData) { diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 484dbb2245..24126d452f 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -974,6 +974,13 @@ RCT_EXPORT_METHOD(createAccountAndLogin:(NSString *)request) { StatusgoCreateAccountAndLogin(request); } +RCT_EXPORT_METHOD(restoreAccountAndLogin:(NSString *)request) { +#if DEBUG + NSLog(@"restoreAccountAndLogin() method called"); +#endif + StatusgoRestoreAccountAndLogin(request); +} + RCT_EXPORT_METHOD(generateAliasAndIdenticonAsync:(NSString *)publicKey callback:(RCTResponseSenderBlock)callback) { #if DEBUG diff --git a/src/status_im/ethereum/mnemonic.cljs b/src/status_im/ethereum/mnemonic.cljs index 0551094940..e02d7e6c61 100644 --- a/src/status_im/ethereum/mnemonic.cljs +++ b/src/status_im/ethereum/mnemonic.cljs @@ -202,7 +202,7 @@ (defn words-count [s] (if (empty? s) - nil + 0 (-> s passphrase->words count))) diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index a16fc46280..0a88fdba8f 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -95,6 +95,10 @@ [request] (.createAccountAndLogin ^js (status) (types/clj->json request))) +(defn restore-account-and-login + [request] + (.restoreAccountAndLogin ^js (status) (types/clj->json request))) + (defn export-db "NOTE: beware, the password has to be sha3 hashed" [key-uid account-data hashed-password callback] diff --git a/src/status_im2/constants.cljs b/src/status_im2/constants.cljs index 0cf3b516f5..26b4c8532d 100644 --- a/src/status_im2/constants.cljs +++ b/src/status_im2/constants.cljs @@ -295,3 +295,5 @@ (def ^:const everyone-mention-id "0x00001") (def ^:const empty-category-id :communities/not-categorized) + +(def ^:const seed-phrase-valid-length #{12 18 24}) diff --git a/src/status_im2/contexts/onboarding/create_profile/view.cljs b/src/status_im2/contexts/onboarding/create_profile/view.cljs index 233296aa2e..e8fe4a3907 100644 --- a/src/status_im2/contexts/onboarding/create_profile/view.cljs +++ b/src/status_im2/contexts/onboarding/create_profile/view.cljs @@ -1,9 +1,9 @@ (ns status-im2.contexts.onboarding.create-profile.view - (:require [clojure.string :as string] - [utils.i18n :as i18n] - [quo2.core :as quo] + (:require [quo2.core :as quo] + [clojure.string :as string] [quo2.foundations.colors :as colors] [status-im2.contexts.onboarding.create-profile.style :as style] + [utils.i18n :as i18n] [react-native.core :as rn] [reagent.core :as reagent] [status-im2.contexts.onboarding.common.background.view :as background] diff --git a/src/status_im2/contexts/onboarding/enter_seed_phrase/style.cljs b/src/status_im2/contexts/onboarding/enter_seed_phrase/style.cljs new file mode 100644 index 0000000000..33f051b788 --- /dev/null +++ b/src/status_im2/contexts/onboarding/enter_seed_phrase/style.cljs @@ -0,0 +1,14 @@ +(ns status-im2.contexts.onboarding.enter-seed-phrase.style + (:require [quo2.foundations.colors :as colors] + [react-native.platform :as platform])) + +(def page-container + {:padding-top (if platform/ios? 44 0) + :position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0 + :background-color colors/neutral-80-opa-80-blur}) + +(def navigation-bar {:height 56}) diff --git a/src/status_im2/contexts/onboarding/enter_seed_phrase/view.cljs b/src/status_im2/contexts/onboarding/enter_seed_phrase/view.cljs new file mode 100644 index 0000000000..282a66f4bf --- /dev/null +++ b/src/status_im2/contexts/onboarding/enter_seed_phrase/view.cljs @@ -0,0 +1,79 @@ +(ns status-im2.contexts.onboarding.enter-seed-phrase.view + (:require [quo2.core :as quo] + [quo.core :as quo1] + [clojure.string :as string] + [status-im.ethereum.mnemonic :as mnemonic] + [status-im2.constants :as constants] + [utils.security.core :as security] + [utils.re-frame :as rf] + [reagent.core :as reagent] + [react-native.core :as rn] + [status-im2.contexts.onboarding.enter-seed-phrase.style :as style] + [status-im2.contexts.onboarding.common.background.view :as background] + [utils.i18n :as i18n])) + +(defn navigation-bar + [] + [rn/view {:style style/navigation-bar} + [quo/page-nav + {:align-mid? true + :left-section {:type :blur-bg + :icon :i/arrow-left + :icon-override-theme :dark + :on-press #(rf/dispatch [:navigate-back])} + :mid-section {:type :text-only :main-text ""}}]]) + +(def button-disabled? + (comp not constants/seed-phrase-valid-length mnemonic/words-count)) + +(defn clean-seed-phrase + [s] + (as-> s $ + (string/lower-case $) + (string/split $ #"\s") + (filter #(not (string/blank? %)) $) + (string/join " " $))) + +(defn page + [] + (let [seed-phrase (reagent/atom "") + error-message (reagent/atom "") + on-invalid-seed-phrase #(reset! error-message (i18n/label :t/custom-seed-phrase))] + (fn [] + [rn/view {:style style/page-container} + [navigation-bar] + [rn/view {:style {:padding-horizontal 20}} + [quo/text + {:weight :bold + :align :center} + (i18n/label :t/use-recovery-phrase)] + [quo/text + (i18n/label-pluralize (mnemonic/words-count @seed-phrase) :t/words-n)] + [:<> + [quo1/text-input + {:on-change-text (fn [t] + (reset! seed-phrase (clean-seed-phrase t)) + (reset! error-message "")) + :auto-focus true + :accessibility-label :passphrase-input + :placeholder (i18n/label :t/seed-phrase-placeholder) + :show-cancel false + :bottom-value 40 + :multiline true + :auto-correct false + :keyboard-type :visible-password + :monospace true}]] + [quo/button + {:disabled (button-disabled? @seed-phrase) + :on-press #(rf/dispatch [:onboarding-2/seed-phrase-entered + (security/mask-data @seed-phrase) + on-invalid-seed-phrase])} + (i18n/label :t/continue)] + (when (seq @error-message) + [quo/text @error-message])]]))) + +(defn enter-seed-phrase + [] + [rn/view {:style {:flex 1}} + [background/view true] + [page]]) diff --git a/src/status_im2/contexts/onboarding/events.cljs b/src/status_im2/contexts/onboarding/events.cljs index 660a0913d1..4a8ac323b2 100644 --- a/src/status_im2/contexts/onboarding/events.cljs +++ b/src/status_im2/contexts/onboarding/events.cljs @@ -2,6 +2,7 @@ (:require [utils.re-frame :as rf] [re-frame.core :as re-frame] + [status-im.utils.types :as types] [status-im2.config :as config] [clojure.string :as string] [utils.security.core :as security] @@ -9,27 +10,32 @@ [status-im.ethereum.core :as ethereum])) (re-frame/reg-fx - ::create-account-and-login + :multiaccount/create-account-and-login (fn [request] (status/create-account-and-login request))) -(rf/defn on-delete-profile-success - {:events [:onboarding-2/on-delete-profile-success]} - [{:keys [db]} key-uid] - {:db (update-in db [:multiaccounts/multiaccounts] dissoc key-uid)}) +(re-frame/reg-fx + :multiaccount/validate-mnemonic + (fn [[mnemonic on-success on-error]] + (status/validate-mnemonic + (security/safe-unmask-data mnemonic) + (fn [result] + (let [{:keys [error]} (types/json->clj result)] + (if (seq error) + (when on-error (on-error error)) + (on-success mnemonic))))))) + +(re-frame/reg-fx + :multiaccount/restore-account-and-login + (fn [request] + (status/restore-account-and-login request))) (rf/defn profile-data-set {:events [:onboarding-2/profile-data-set]} [{:keys [db]} onboarding-data] - {:db (assoc db :onboarding-2/profile onboarding-data) + {:db (update db :onboarding-2/profile merge onboarding-data) :dispatch [:navigate-to :create-profile-password]}) -(rf/defn password-set - {:events [:onboarding-2/password-set]} - [{:keys [db]} password] - {:db (assoc-in db [:onboarding-2/profile :password] password) - :dispatch [:navigate-to :enable-biometrics]}) - (defn strip-file-prefix [path] (when path @@ -39,14 +45,20 @@ {:events [:onboarding-2/create-account-and-login]} [{:keys [db]}] (let [{:keys [display-name + seed-phrase password image-path color]} (:onboarding-2/profile db) log-enabled? (boolean (not-empty config/log-level)) + effect (if seed-phrase + :multiaccount/restore-account-and-login + :multiaccount/create-account-and-login) request {:displayName display-name :password (ethereum/sha3 (security/safe-unmask-data password)) + :mnemonic (when seed-phrase + (security/safe-unmask-data seed-phrase)) :imagePath (strip-file-prefix image-path) - :color color + :customizationColor color :backupDisabledDataDir (status/backup-disabled-data-dir) :rootKeystoreDir (status/keystore-dir) ;; Temporary fix until https://github.com/status-im/status-go/issues/3024 is @@ -60,8 +72,40 @@ :verifyENSURL config/verify-ens-url :verifyENSContractAddress config/verify-ens-contract-address :verifyTransactionChainID config/verify-transaction-chain-id}] - {::create-account-and-login request - :dispatch [:navigate-to :generating-keys] - :db (-> db - (dissoc :onboarding-2/profile) - (assoc :onboarding-2/new-account? true))})) + {effect request + :dispatch [:navigate-to :generating-keys] + :db (-> db + (dissoc :onboarding-2/profile) + (assoc :onboarding-2/new-account? true))})) + +(rf/defn on-delete-profile-success + {:events [:onboarding-2/on-delete-profile-success]} + [{:keys [db]} key-uid] + {:db (update-in db [:multiaccounts/multiaccounts] dissoc key-uid)}) + +(rf/defn password-set + {:events [:onboarding-2/password-set]} + [{:keys [db]} password] + {:db (assoc-in db [:onboarding-2/profile :password] password) + :dispatch [:navigate-to :enable-biometrics]}) + +(rf/defn seed-phrase-entered + {:events [:onboarding-2/seed-phrase-entered]} + [_ seed-phrase on-error] + {:multiaccount/validate-mnemonic [seed-phrase + #(re-frame/dispatch [:onboarding-2/seed-phrase-validated + seed-phrase]) + on-error]}) + +(rf/defn seed-phrase-validated + {:events [:onboarding-2/seed-phrase-validated]} + [{:keys [db]} seed-phrase] + {:db (assoc-in db [:onboarding-2/profile :seed-phrase] seed-phrase) + :dispatch [:navigate-to :create-profile]}) + +(rf/defn navigate-to-create-profile + {:events [:onboarding-2/navigate-to-create-profile]} + [{:keys [db]}] + ;; Restart the flow + {:db (dissoc db :onboarding-2/profile) + :dispatch [:navigate-to :create-profile]}) diff --git a/src/status_im2/contexts/onboarding/new_to_status/view.cljs b/src/status_im2/contexts/onboarding/new_to_status/view.cljs index 70f2597584..a3e85d7276 100644 --- a/src/status_im2/contexts/onboarding/new_to_status/view.cljs +++ b/src/status_im2/contexts/onboarding/new_to_status/view.cljs @@ -49,7 +49,7 @@ (* 2 56) ;; two other list items (* 2 16) ;; spacing between items 220) ;; extra spacing (top bar) - :on-press #(rf/dispatch [:navigate-to :create-profile])}] + :on-press #(rf/dispatch [:onboarding-2/navigate-to-create-profile])}] [rn/view {:style style/subtitle-container} [quo/text @@ -69,11 +69,18 @@ [rn/view {:style style/space-between-suboptions}] [quo/small-option-card {:variant :icon - :title (i18n/label :t/use-recovery-phrase) + :title "Temporary (old) recover phrase flow" :subtitle (i18n/label :t/use-recovery-phrase-subtitle) :image (resources/get-image :ethereum-address) :on-press #(rf/dispatch [::multiaccounts.recover/enter-phrase-pressed])}] [rn/view {:style style/space-between-suboptions}] + [quo/small-option-card + {:variant :icon + :title (i18n/label :t/use-recovery-phrase) + :subtitle (i18n/label :t/use-recovery-phrase-subtitle) + :image (resources/get-image :ethereum-address) + :on-press #(rf/dispatch [:navigate-to :enter-seed-phrase])}] + [rn/view {:style style/space-between-suboptions}] [quo/small-option-card {:variant :icon :title (i18n/label :t/use-keycard) diff --git a/src/status_im2/events.cljs b/src/status_im2/events.cljs index ecf8faa74c..a7a66c01c0 100644 --- a/src/status_im2/events.cljs +++ b/src/status_im2/events.cljs @@ -52,16 +52,23 @@ (keychain/get-auth-method (:key-uid multiaccount)))) (navigation/init-root cofx :intro)))) +(defn rpc->multiaccount + [{:keys [customizationColor keycard-pairing] :as multiaccount}] + (-> multiaccount + (dissoc :customizationColor) + (assoc :customization-color (keyword customizationColor)) + (assoc :keycard-pairing + (when-not + (string/blank? keycard-pairing) + keycard-pairing)))) + (rf/defn initialize-multiaccounts {:events [:setup/initialize-multiaccounts]} [{:keys [db] :as cofx} all-multiaccounts {:keys [logout?]}] (let [multiaccounts (reduce (fn [acc {:keys [key-uid keycard-pairing] :as multiaccount}] - (-> (assoc acc key-uid multiaccount) - (assoc-in [key-uid :keycard-pairing] - (when-not (string/blank? keycard-pairing) - keycard-pairing)))) + (assoc acc key-uid (rpc->multiaccount multiaccount))) {} all-multiaccounts)] (rf/merge cofx diff --git a/src/status_im2/navigation/screens.cljs b/src/status_im2/navigation/screens.cljs index 57007323a2..b65239c128 100644 --- a/src/status_im2/navigation/screens.cljs +++ b/src/status_im2/navigation/screens.cljs @@ -22,6 +22,7 @@ [status-im2.contexts.onboarding.sign-in.view :as sign-in] [status-im2.contexts.onboarding.syncing.syncing-devices.view :as syncing-devices] [status-im2.contexts.onboarding.generating-keys.view :as generating-keys] + [status-im2.contexts.onboarding.enter-seed-phrase.view :as enter-seed-phrase] [status-im2.contexts.onboarding.profiles.view :as profiles] [status-im2.contexts.quo-preview.main :as quo.preview] [status-im2.contexts.shell.view :as shell] @@ -168,6 +169,12 @@ :insets {:top false} :component generating-keys/generating-keys} + {:name :enter-seed-phrase + :options {:statusBar {:style :light} + :topBar {:visible false} + :navigationBar {:backgroundColor colors/black}} + :insets {:top false} + :component enter-seed-phrase/enter-seed-phrase} {:name :enable-notifications :options {:statusBar {:style :light} diff --git a/status-go-version.json b/status-go-version.json index 8eebee8374..b9ba220730 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.140.0", - "commit-sha1": "7cd7430d3141b08f7c455d7918f4160ea8fd0559", - "src-sha256": "0lgkzrl7zas9mxpngksmh5r26j5811x9xxx9byrcqnyirmks3622" + "version": "v0.140.1", + "commit-sha1": "e2082bf5bdd2c43bf1f140ddc4d3eece6de88bc0", + "src-sha256": "1gfykjs51862kbfjqr2sslsdhmn4j19hj67zr34gm3xdcy9ci0xx" }