chore(wallet): import private key - backend integration

Co-authored-by: Jamie Caprani <jamiecaprani@gmail.com>
This commit is contained in:
Nikolay 2024-07-09 13:49:31 +03:00 committed by GitHub
parent 7774c4eac1
commit a2178951d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 236 additions and 131 deletions

View File

@ -71,7 +71,7 @@
(rf/reg-event-fx :wallet/seed-phrase-entered seed-phrase-entered) (rf/reg-event-fx :wallet/seed-phrase-entered seed-phrase-entered)
(defn store-account-generated (defn store-account-generated-with-mnemonic
[{:keys [db]} [{:keys [new-account-data keypair-name]}]] [{:keys [db]} [{:keys [new-account-data keypair-name]}]]
(let [new-account (update new-account-data :mnemonic security/mask-data)] (let [new-account (update new-account-data :mnemonic security/mask-data)]
{:db (-> db {:db (-> db
@ -85,20 +85,21 @@
:random-phrase)) :random-phrase))
:fx [[:dispatch [:navigate-back-to :screen/wallet.create-account]]]})) :fx [[:dispatch [:navigate-back-to :screen/wallet.create-account]]]}))
(rf/reg-event-fx :wallet/store-account-generated store-account-generated) (rf/reg-event-fx :wallet/store-account-generated-with-mnemonic store-account-generated-with-mnemonic)
(defn generate-account-for-keypair (defn generate-account-for-keypair-with-mnemonic
[{:keys [db]} [{:keys [keypair-name]}]] [{:keys [db]} [{:keys [keypair-name]}]]
(let [seed-phrase (-> db :wallet :ui :create-account :new-keypair :seed-phrase)] (let [seed-phrase (-> db :wallet :ui :create-account :new-keypair :seed-phrase)]
{:fx [[:effects.wallet/create-account-from-mnemonic {:fx [[:effects.wallet/create-account-from-mnemonic
{:mnemonic-phrase (security/safe-unmask-data seed-phrase) {:mnemonic-phrase (security/safe-unmask-data seed-phrase)
:paths [constants/path-default-wallet] :paths [constants/path-default-wallet]
:on-success (fn [new-account-data] :on-success (fn [new-account-data]
(rf/dispatch [:wallet/store-account-generated (rf/dispatch [:wallet/store-account-generated-with-mnemonic
{:new-account-data new-account-data {:new-account-data new-account-data
:keypair-name keypair-name}]))}]]})) :keypair-name keypair-name}]))}]]}))
(rf/reg-event-fx :wallet/generate-account-for-keypair generate-account-for-keypair) (rf/reg-event-fx :wallet/generate-account-for-keypair-with-mnemonic
generate-account-for-keypair-with-mnemonic)
(defn clear-create-account-data (defn clear-create-account-data
[{:keys [db]}] [{:keys [db]}]
@ -159,14 +160,19 @@
(rf/reg-event-fx (rf/reg-event-fx
:wallet/create-keypair-with-account :wallet/create-keypair-with-account
(fn [{db :db} [password account-preferences]] (fn [{db :db} [password account-preferences]]
(let [{:keys [keypair-name (let [{:keys [workflow
keypair-name
new-account-data]} (-> db :wallet :ui :create-account :new-keypair) new-account-data]} (-> db :wallet :ui :create-account :new-keypair)
keypair-type (if (= workflow :workflow/new-keypair.import-private-key)
:key
:seed)
keypair-with-account (create-account.utils/prepare-new-account keypair-with-account (create-account.utils/prepare-new-account
{:keypair-name keypair-name {:keypair-name keypair-name
:keypair-type keypair-type
:account-data new-account-data :account-data new-account-data
:account-preferences account-preferences}) :account-preferences account-preferences})
new-address (some-> new-account-data new-address (some-> new-account-data
(create-account.utils/first-derived-account) (create-account.utils/first-derived-account keypair-type)
(:address) (:address)
(string/lower-case)) (string/lower-case))
unmasked-password (security/safe-unmask-data password)] unmasked-password (security/safe-unmask-data password)]
@ -176,7 +182,7 @@
:on-success [:wallet/add-account-success new-address] :on-success [:wallet/add-account-success new-address]
:on-error #(log/error "Failed to add Keypair and create account" %)}]]]}))) :on-error #(log/error "Failed to add Keypair and create account" %)}]]]})))
(defn import-and-create-keypair-with-account (defn import-mnemonic-and-create-keypair-with-account
[{db :db} [{:keys [password account-preferences]}]] [{db :db} [{:keys [password account-preferences]}]]
(let [account-data (-> db :wallet :ui :create-account :new-keypair :new-account-data) (let [account-data (-> db :wallet :ui :create-account :new-keypair :new-account-data)
unmasked-mnemonic (security/safe-unmask-data (:mnemonic account-data)) unmasked-mnemonic (security/safe-unmask-data (:mnemonic account-data))
@ -187,7 +193,8 @@
:on-success #(rf/dispatch :on-success #(rf/dispatch
[:wallet/create-keypair-with-account password account-preferences])}]]]})) [:wallet/create-keypair-with-account password account-preferences])}]]]}))
(rf/reg-event-fx :wallet/import-and-create-keypair-with-account import-and-create-keypair-with-account) (rf/reg-event-fx :wallet/import-mnemonic-and-create-keypair-with-account
import-mnemonic-and-create-keypair-with-account)
(rf/reg-event-fx (rf/reg-event-fx
:wallet/derive-address-and-add-account :wallet/derive-address-and-add-account
@ -206,3 +213,49 @@
:on-error [:wallet/log-rpc-error :on-error [:wallet/log-rpc-error
{:event :wallet/derive-address-and-add-account {:event :wallet/derive-address-and-add-account
:params [derived-from-address [derivation-path]]}]}]]]})) :params [derived-from-address [derivation-path]]}]}]]]}))
(defn import-private-key-and-create-keypair-with-account
[{:keys [db]} [{:keys [password account-preferences]}]]
(let [private-key (get-in db
[:wallet :ui :create-account :new-keypair
:new-account-data :private-key])
unmasked-private-key (security/safe-unmask-data private-key)
unmasked-password (security/safe-unmask-data password)]
{:json-rpc/call
[{:method "accounts_importPrivateKey"
:params [unmasked-private-key unmasked-password]
:on-success [:wallet/create-keypair-with-account password account-preferences]
:on-error #(log/error "Failed to import private key" %)}]}))
(rf/reg-event-fx
:wallet/import-private-key-and-create-keypair-with-account
import-private-key-and-create-keypair-with-account)
(defn store-account-generated-with-private-key
[{:keys [db]} [{:keys [new-account-data keypair-name]}]]
{:db (-> db
(update-in [:wallet :ui :create-account :new-keypair]
assoc
:new-account-data new-account-data
:keypair-name keypair-name)
(update-in [:wallet :ui :create-account :new-keypair]
dissoc
:private-key))
:fx [[:dispatch [:navigate-back-to :screen/wallet.create-account]]]})
(rf/reg-event-fx :wallet/store-account-generated-with-private-key
store-account-generated-with-private-key)
(rf/reg-event-fx
:wallet/import-private-key-and-generate-account-for-keypair
(fn [{:keys [db]} [{:keys [keypair-name]}]]
(let [private-key (get-in db [:wallet :ui :create-account :private-key])]
{:db (assoc-in db
[:wallet :ui :create-account :new-keypair :workflow]
:workflow/new-keypair.import-private-key)
:fx [[:effects.wallet/create-account-from-private-key
{:private-key private-key
:on-success (fn [new-account-data]
(rf/dispatch [:wallet/store-account-generated-with-private-key
{:keypair-name keypair-name
:new-account-data new-account-data}]))}]]})))

View File

@ -37,7 +37,7 @@
{:new-account-data {"test" "data" {:new-account-data {"test" "data"
:mnemonic masked-mnemonic} :mnemonic masked-mnemonic}
:keypair-name "new-keypair-name"}}}}} :keypair-name "new-keypair-name"}}}}}
effects (events/store-account-generated {:db db} props) effects (events/store-account-generated-with-mnemonic {:db db} props)
result-db (:db effects) result-db (:db effects)
remove-mnemonic #(update-in % remove-mnemonic #(update-in %
[:wallet :ui :create-account :new-keypair :new-account-data] [:wallet :ui :create-account :new-keypair :new-account-data]
@ -61,7 +61,7 @@
expected-effects [[:effects.wallet/create-account-from-mnemonic expected-effects [[:effects.wallet/create-account-from-mnemonic
{:mnemonic-phrase "test-secret" {:mnemonic-phrase "test-secret"
:paths [constants/path-default-wallet]}]] :paths [constants/path-default-wallet]}]]
effects (events/generate-account-for-keypair {:db db} props)] effects (events/generate-account-for-keypair-with-mnemonic {:db db} props)]
(is (match? (is (match?
(update-in effects [:fx 0 1] dissoc :on-success) (update-in effects [:fx 0 1] dissoc :on-success)
{:fx expected-effects})) {:fx expected-effects}))

View File

@ -63,31 +63,31 @@
(let [{:keys [message] :as props} (let [{:keys [message] :as props}
(case state (case state
:scanning :scanning
{:type :info {:status :default
:icon :i/scanning :icon :i/scanning
:message :t/scanning-for-activity} :message :t/scanning-for-activity}
:inactive-address :inactive-address
{:type :warning {:status :warning
:icon :i/info :icon :i/info
:message :t/this-account-has-no-activity} :message :t/this-account-has-no-activity}
:active-address :active-address
{:type :success {:status :success
:icon :i/done :icon :i/done
:message :t/this-address-has-activity} :message :t/this-address-has-activity}
:invalid-private-key :invalid-private-key
{:type :error {:status :error
:icon :i/info :icon :i/info
:message :t/invalid-private-key} :message :t/invalid-private-key}
nil)] nil)]
(when props (when props
[quo/info-message [quo/info-message
(assoc props (-> props
:size :default (assoc :size :default :container-style style/indicator)
:style style/indicator) (dissoc :message))
(i18n/label message)]))) (i18n/label message)])))
(defn on-unmount (defn on-unmount

View File

@ -4,7 +4,6 @@
[quo.core :as quo] [quo.core :as quo]
[react-native.core :as rn] [react-native.core :as rn]
[status-im.common.floating-button-page.view :as floating-button-page] [status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.not-implemented :as not-implemented]
[status-im.common.validation.general :as validators] [status-im.common.validation.general :as validators]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.contexts.wallet.add-account.create-account.key-pair-name.style :as style] [status-im.contexts.wallet.add-account.create-account.key-pair-name.style :as style]
@ -25,12 +24,12 @@
(defn- next-workflow-step (defn- next-workflow-step
[workflow key-pair-name] [workflow key-pair-name]
(case workflow (case workflow
;; TODO issue #19759. Implement creation account from private key
:import-private-key :import-private-key
(not-implemented/alert) (rf/dispatch [:wallet/import-private-key-and-generate-account-for-keypair
{:keypair-name key-pair-name}])
(:new-keypair :recovery-phrase) (:new-keypair :recovery-phrase)
(rf/dispatch [:wallet/generate-account-for-keypair (rf/dispatch [:wallet/generate-account-for-keypair-with-mnemonic
{:keypair-name key-pair-name}]) {:keypair-name key-pair-name}])
(do (do

View File

@ -2,28 +2,57 @@
(:require [status-im.constants :as constants])) (:require [status-im.constants :as constants]))
(defn first-derived-account (defn first-derived-account
[account-data] [account-data keypair-type]
(-> account-data :derived first val)) (if (= keypair-type :seed)
(some-> account-data
:derived
first
val)
account-data))
(defn prepare-new-account (defn get-account-details
[{keypair-name :keypair-name [keypair-type account account-to-create]
{:keys [keyUid address] :as account} :account-data (if (= keypair-type :key)
{:keys [account-name color emoji]} :account-preferences}] {:key-uid (:key-uid account)
(let [account-to-create (first-derived-account account) :public-key (:public-key account)
account-config {:address (:address account-to-create) :account-address (:address account)}
:key-uid keyUid {:key-uid (:keyUid account)
:public-key (:publicKey account-to-create)
:account-address (:address account-to-create)}))
(defn get-account-config
[{:keys [account-address key-uid keypair-type public-key account-name emoji color]}]
{:address account-address
:key-uid key-uid
:wallet false :wallet false
:chat false :chat false
:type :seed :type keypair-type
:path constants/path-default-wallet :path constants/path-default-wallet
:public-key (:publicKey account-to-create) :public-key public-key
:name account-name :name account-name
:emoji emoji :emoji emoji
:colorID color :colorID color
:hidden false}] :hidden false})
{:key-uid keyUid
(defn prepare-new-account
[{:keys [keypair-name keypair-type]
{:keys [address] :as account} :account-data
{:keys [account-name color emoji]} :account-preferences}]
(let [account-to-create (first-derived-account account keypair-type)
{:keys [key-uid public-key account-address]} (get-account-details keypair-type
account
account-to-create)
account-config (get-account-config {:account-address
account-address
:key-uid key-uid
:keypair-type keypair-type
:public-key public-key
:account-name account-name
:emoji emoji
:color color})]
{:key-uid key-uid
:name keypair-name :name keypair-name
:type :seed :type keypair-type
:derived-from address :derived-from address
:last-used-derivation-index 0 :last-used-derivation-index 0
:accounts [account-config]})) :accounts [account-config]}))

View File

@ -22,12 +22,6 @@
(defn- get-keypair-data (defn- get-keypair-data
[{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}] [{:keys [title primary-keypair? new-keypair? derivation-path customization-color]}]
(let [formatted-path (string/replace derivation-path #"/" " / ")
on-auth-success (fn [password]
(rf/dispatch [:navigate-to
:screen/wallet.edit-derivation-path
{:password password
:current-derivation-path formatted-path}]))]
[{:title title [{:title title
:image (if primary-keypair? :avatar :icon) :image (if primary-keypair? :avatar :icon)
:image-props (if primary-keypair? :image-props (if primary-keypair?
@ -41,6 +35,13 @@
:alignment :flex-start} :alignment :flex-start}
:description :text :description :text
:description-props {:text (i18n/label :t/on-device)}} :description-props {:text (i18n/label :t/on-device)}}
(when-not (string/blank? derivation-path)
(let [formatted-path (string/replace derivation-path #"/" " / ")
on-auth-success (fn [password]
(rf/dispatch [:navigate-to
:screen/wallet.edit-derivation-path
{:password password
:current-derivation-path formatted-path}]))]
{:title (i18n/label :t/derivation-path) {:title (i18n/label :t/derivation-path)
:image :icon :image :icon
:image-props :i/derivated-path :image-props :i/derivated-path
@ -52,7 +53,7 @@
:icon-left :i/face-id :icon-left :i/face-id
:alignment :flex-start} :alignment :flex-start}
:description :text :description :text
:description-props {:text formatted-path}}])) :description-props {:text formatted-path}}))])
(defn- avatar (defn- avatar
[{:keys [account-color emoji on-select-emoji]}] [{:keys [account-color emoji on-select-emoji]}]
@ -167,20 +168,41 @@
:container-style (style/slide-button-container bottom))]}] :container-style (style/slide-button-container bottom))]}]
children)))) children))))
(defn- on-auth-success-mnemonic
[password account-preferences]
(rf/dispatch
[:wallet/import-mnemonic-and-create-keypair-with-account
{:password password
:account-preferences account-preferences}]))
(defn- on-auth-success-import-private-key
[password account-preferences]
(rf/dispatch
[:wallet/import-private-key-and-create-keypair-with-account
{:password password
:account-preferences account-preferences}]))
(defn add-new-keypair-variant (defn add-new-keypair-variant
[{{:keys [account-name account-color emoji]} :state}] [{{:keys [account-name account-color emoji]} :state}]
(let [on-auth-success (fn [password]
(rf/dispatch
[:wallet/import-and-create-keypair-with-account
{:password password
:account-preferences {:account-name @account-name
:color @account-color
:emoji @emoji}}]))]
(fn [{:keys [on-change-text set-account-color set-emoji customization-color keypair-name error]}] (fn [{:keys [on-change-text set-account-color set-emoji customization-color keypair-name error]}]
(let [{:keys [new-account-data]} (rf/sub [:wallet/create-account-new-keypair])] (let [{:keys [new-account-data workflow]} (rf/sub [:wallet/create-account-new-keypair])
derivation-path (when (not= workflow
:workflow/new-keypair.import-private-key)
constants/path-default-wallet)]
[floating-button [floating-button
{:account-color @account-color {:account-color @account-color
:slide-button-props {:on-auth-success on-auth-success :slide-button-props {:on-auth-success (fn on-auth-success [password]
(let [account-preferences {:account-name @account-name
:color @account-color
:emoji @emoji}]
(if (= workflow
:workflow/new-keypair.import-private-key)
(on-auth-success-import-private-key
password
account-preferences)
(on-auth-success-mnemonic
password
account-preferences))))
:disabled? (empty? @account-name) :disabled? (empty? @account-name)
:dependencies [new-account-data]}} :dependencies [new-account-data]}}
[avatar [avatar
@ -196,9 +218,9 @@
{:account-color @account-color {:account-color @account-color
:set-account-color set-account-color}] :set-account-color set-account-color}]
[new-account-origin [new-account-origin
{:derivation-path constants/path-default-wallet {:derivation-path derivation-path
:customization-color customization-color :customization-color customization-color
:keypair-title keypair-name}]])))) :keypair-title keypair-name}]])))
(defn derive-account-variant (defn derive-account-variant
[{{:keys [account-name account-color emoji]} :state}] [{{:keys [account-name account-color emoji]} :state}]
@ -264,7 +286,7 @@
(fn [] (fn []
(let [customization-color (rf/sub [:profile/customization-color]) (let [customization-color (rf/sub [:profile/customization-color])
;; Having a keypair means the user is importing it or creating it. ;; Having a keypair means the user is importing it or creating it.
{:keys [keypair-name]} (rf/sub [:wallet/create-account-new-keypair]) {:keys [keypair-name workflow]} (rf/sub [:wallet/create-account-new-keypair])
accounts-names (rf/sub [:wallet/accounts-names]) accounts-names (rf/sub [:wallet/accounts-names])
accounts-emojis-and-colors (rf/sub [:wallet/accounts-emojis-and-colors]) accounts-emojis-and-colors (rf/sub [:wallet/accounts-emojis-and-colors])
on-change-text (rn/use-callback on-change-text (rn/use-callback
@ -300,8 +322,10 @@
:on-change-text on-change-text :on-change-text on-change-text
:set-account-color set-account-color :set-account-color set-account-color
:set-emoji set-emoji :set-emoji set-emoji
:keypair-name keypair-name :keypair-name keypair-name
:error error}] :error error
:workflow workflow}]
[derive-account-variant [derive-account-variant
{:state state {:state state
:customization-color customization-color :customization-color customization-color

View File

@ -58,11 +58,11 @@
:emoji-hash emojiHash :emoji-hash emojiHash
:key-uid keyUid :key-uid keyUid
:public-key publicKey :public-key publicKey
:private-key privateKey}))))) :private-key (security/mask-data privateKey)})))))
(rf/reg-fx (rf/reg-fx
:effects.wallet/create-account-from-private-key :effects.wallet/create-account-from-private-key
(fn [[private-key on-success on-error]] (fn [{:keys [private-key on-success on-error]}]
(-> (create-account-from-private-key private-key) (-> (create-account-from-private-key private-key)
(promesa/then (partial rf/call-continuation on-success)) (promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error))))) (promesa/catch (partial rf/call-continuation on-error)))))