Compare commits

...
14 changed files with 201 additions and 71 deletions
@@ -314,6 +314,14 @@ class AccountManager(private val reactContext: ReactApplicationContext) : ReactC
)
}
@ReactMethod
fun createAccountFromPrivateKey(params: String, callback: Callback) {
utils.executeRunnableStatusGoMethod(
{ Statusgo.createAccountFromPrivateKey(params) },
callback
)
}
companion object {
private const val TAG = "AccountManager"
private const val gethLogFileName = "geth.log"
@@ -233,4 +233,12 @@ RCT_EXPORT_METHOD(createAccountFromMnemonicAndDeriveAccountsForPaths:(NSString *
callback(@[result]);
}
RCT_EXPORT_METHOD(createAccountFromPrivateKey:(NSString *)params callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"createAccountFromPrivateKey() method called");
#endif
NSString *result = StatusgoCreateAccountFromPrivateKey(params);
callback(@[result]);
}
@end
+11
View File
@@ -3,6 +3,7 @@
["react-native" :as react-native]
[clojure.string :as string]
[native-module.utils :as native-utils]
[promesa.core :as promesa]
[react-native.platform :as platform]
[taoensso.timbre :as log]
[utils.transforms :as types]))
@@ -610,6 +611,16 @@
(types/clj->json mnemonic)
#(callback (types/json->clj %))))
(defn create-account-from-private-key
([private-key]
(-> (native-utils/promisify-native-module-call create-account-from-private-key private-key)
(promesa/then types/json->clj)))
([private-key callback]
(log/debug "[native-module] create-account-from-private-key")
(.createAccountFromPrivateKey ^js (account-manager)
(types/clj->json {:privateKey private-key})
callback)))
(defn get-connection-string-for-exporting-keypairs-keystores
"Generates connection string form status-go for the purpose of exporting keypairs and keystores on sender side"
([config-json]
+2 -1
View File
@@ -228,7 +228,8 @@
(def regx-string-public-key "0x04[0-9a-f]{128}")
(def regx-compressed-key (re-pattern (str "^" regx-string-compressed-key "$")))
(def regx-public-key (re-pattern (str "^" regx-string-public-key "$")))
(def regx-private-key #"^0x[0-9a-fA-F]{64}$")
(def regx-private-key #"^[0-9a-fA-F]{64}$")
(def regx-private-key-hex #"^0x[0-9a-fA-F]{64}$")
(def regx-emoji
#"^((?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC69\uDC6E\uDC70-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD18-\uDD1C\uDD1E\uDD1F\uDD26\uDD30-\uDD39\uDD3D\uDD3E\uDDD1-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])?|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDEEB\uDEEC\uDEF4-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u2660\u2663\u2665\u2666\u2668\u267B\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEF8]|\uD83E[\uDD10-\uDD3A\uDD3C-\uDD3E\uDD40-\uDD45\uDD47-\uDD4C\uDD50-\uDD6B\uDD80-\uDD97\uDDC0\uDDD0-\uDDE6])\uFE0F|[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF])+$")
(def regx-bold #"\*[^*]+\*")
@@ -68,7 +68,7 @@
(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]}]]
(let [new-account (update new-account-data :mnemonic security/mask-data)]
{:db (-> db
@@ -82,24 +82,25 @@
:random-phrase))
: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]}]]
(let [seed-phrase (-> db :wallet :ui :create-account :new-keypair :seed-phrase)]
{:fx [[:effects.wallet/create-account-from-mnemonic
{:mnemonic-phrase (security/safe-unmask-data seed-phrase)
:paths [constants/path-default-wallet]
: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
: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
[{:keys [db]}]
{:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair)})
{:db (update-in db [:wallet :ui :create-account] dissoc :new-keypair :workflow)})
(rf/reg-event-fx :wallet/clear-create-account clear-create-account-data)
@@ -156,14 +157,19 @@
(rf/reg-event-fx
:wallet/create-keypair-with-account
(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)
keypair-type (if (= workflow :workflow-new-keypair/import-private-key)
:key
:seed)
keypair-with-account (create-account.utils/prepare-new-account
{:keypair-name keypair-name
:keypair-type keypair-type
:account-data new-account-data
:account-preferences account-preferences})
new-address (some-> new-account-data
(create-account.utils/first-derived-account)
(create-account.utils/first-derived-account keypair-type)
(:address)
(string/lower-case))
unmasked-password (security/safe-unmask-data password)]
@@ -173,7 +179,7 @@
:on-success [:wallet/add-account-success new-address]
: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]}]]
(let [account-data (-> db :wallet :ui :create-account :new-keypair :new-account-data)
unmasked-mnemonic (security/safe-unmask-data (:mnemonic account-data))
@@ -184,7 +190,8 @@
:on-success #(rf/dispatch
[: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
:wallet/derive-address-and-add-account
@@ -202,3 +209,52 @@
derived-from-address
%)}]]]}))
(rf/reg-event-fx
:wallet/set-seed-phrase
(fn [{:keys [db]} [{:keys [seed-phrase on-success]}]]
{:db (assoc-in db [:wallet :ui :create-account :new-keypair :seed-phrase] seed-phrase)
:fx [on-success]}))
(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 :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-mnemonic
{:keypair-name keypair-name
:new-account-data new-account-data}]))}]]})))
@@ -37,7 +37,7 @@
{:new-account-data {"test" "data"
:mnemonic masked-mnemonic}
: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)
remove-mnemonic #(update-in %
[:wallet :ui :create-account :new-keypair :new-account-data]
@@ -61,7 +61,7 @@
expected-effects [[:effects.wallet/create-account-from-mnemonic
{:mnemonic-phrase "test-secret"
: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?
(update-in effects [:fx 0 1] dissoc :on-success)
{:fx expected-effects}))
@@ -90,7 +90,7 @@
:style style/indicator)
(i18n/label message)])))
(defn on-unmount
(defn on-mount
[]
(rf/dispatch [:wallet/clear-private-key-data]))
@@ -112,7 +112,7 @@
public-address (rf/sub [:wallet/public-address])
[flow-state set-flow-state] (rn/use-state nil)
error? (= :invalid-private-key flow-state)]
(rn/use-unmount on-unmount)
(rn/use-mount on-mount)
[rn/view {:flex 1}
[floating-button-page/view
{:customization-color customization-color
@@ -4,7 +4,6 @@
[quo.core :as quo]
[react-native.core :as rn]
[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.constants :as constants]
[status-im.contexts.wallet.add-account.create-account.key-pair-name.style :as style]
@@ -25,12 +24,12 @@
(defn- next-workflow-step
[workflow key-pair-name]
(case workflow
;; TODO issue #19759. Implement creation account from 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)
(rf/dispatch [:wallet/generate-account-for-keypair
(rf/dispatch [:wallet/generate-account-for-keypair-with-mnemonic
{:keypair-name key-pair-name}])
(do
@@ -23,7 +23,8 @@
:add-divider? true
:on-press #(rf/dispatch [:navigate-to :screen/wallet.enter-seed-phrase
{:recovering-keypair? true}])}
(when (ff/enabled? ::wallet.import-private-key)
;; TODO FIX before merge
(when (or true (ff/enabled? ::wallet.import-private-key))
{:icon :i/key
:accessibility-label :import-private-key
:label (i18n/label :t/import-private-key)
@@ -2,19 +2,21 @@
(:require [status-im.constants :as constants]))
(defn first-derived-account
[account-data]
(-> account-data :derived first val))
[account-data keypair-type]
(if (= keypair-type :seed)
(some-> account-data :derived first val)
account-data))
(defn prepare-new-account
[{keypair-name :keypair-name
[{:keys [keypair-name keypair-type]
{:keys [keyUid address] :as account} :account-data
{:keys [account-name color emoji]} :account-preferences}]
(let [account-to-create (first-derived-account account)
(let [account-to-create (first-derived-account account keypair-type)
account-config {:address (:address account-to-create)
:key-uid keyUid
:wallet false
:chat false
:type :seed
:type keypair-type
:path constants/path-default-wallet
:public-key (:publicKey account-to-create)
:name account-name
@@ -23,7 +25,7 @@
:hidden false}]
{:key-uid keyUid
:name keypair-name
:type :seed
:type keypair-type
:derived-from address
:last-used-derivation-index 0
:accounts [account-config]}))
@@ -21,38 +21,40 @@
(defn- get-keypair-data
[{: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
:image (if primary-keypair? :avatar :icon)
:image-props (if primary-keypair?
{:full-name (utils.string/get-initials title 1)
:size :xxs
:customization-color customization-color}
:i/seed)
:action (when-not new-keypair? :button)
:action-props {:on-press #(rf/dispatch [:navigate-to :screen/wallet.select-keypair])
:button-text (i18n/label :t/edit)
:alignment :flex-start}
:description :text
:description-props {:text (i18n/label :t/on-device)}}
{:title (i18n/label :t/derivation-path)
:image :icon
:image-props :i/derivated-path
:action (if (ff/enabled? ::ff/wallet.edit-derivation-path) :button :none)
:action-props {:on-press #(rf/dispatch [:standard-auth/authorize
{:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/continue)}])
[{:title title
:image (if primary-keypair? :avatar :icon)
:image-props (if primary-keypair?
{:full-name (utils.string/get-initials title 1)
:size :xxs
:customization-color customization-color}
:i/seed)
:action (when-not new-keypair? :button)
:action-props {:on-press #(rf/dispatch [:navigate-to :screen/wallet.select-keypair])
:button-text (i18n/label :t/edit)
:alignment :flex-start}
:description :text
:description-props {:text (i18n/label :t/on-device)}}
:button-text (i18n/label :t/edit)
:icon-left :i/face-id
:alignment :flex-start}
:description :text
:description-props {:text formatted-path}}]))
(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)
:image :icon
:image-props :i/derivated-path
:action (if (ff/enabled? ::ff/wallet.edit-derivation-path) :button :none)
:action-props {:on-press #(rf/dispatch [:standard-auth/authorize
{:on-auth-success on-auth-success
:auth-button-label (i18n/label :t/continue)}])
:button-text (i18n/label :t/edit)
:icon-left :i/face-id
:alignment :flex-start}
:description :text
:description-props {:text formatted-path}}))])
(defn- avatar
[{:keys [account-color emoji on-select-emoji]}]
@@ -157,15 +159,29 @@
[{:keys [on-change-text set-account-color set-emoji]
{: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 [customization-color keypair-name]}]
(let [{:keys [new-account-data]} (rf/sub [:wallet/create-account-new-keypair])]
(let [on-auth-success--mnemonic
(fn [password]
(rf/dispatch
[:wallet/import-mnemonic-and-create-keypair-with-account
{:password password
:account-preferences {:account-name @account-name
:color @account-color
:emoji @emoji}}]))
on-auth-success--import-private-key
(fn [password]
(rf/dispatch
[:wallet/import-private-key-and-create-keypair-with-account
{:password password
:account-preferences {:account-name @account-name
:color @account-color
:emoji @emoji}}]))]
(fn [{:keys [customization-color keypair-name workflow]}]
(let [{:keys [new-account-data]} (rf/sub [:wallet/create-account-new-keypair])
derivation-path (when (not= workflow :workflow-new-keypair/import-private-key)
constants/path-default-wallet)
on-auth-success (if (= workflow :workflow-new-keypair/import-private-key)
on-auth-success--import-private-key
on-auth-success--mnemonic)]
[floating-button
{:account-color @account-color
:slide-button-props {:on-auth-success on-auth-success
@@ -183,7 +199,7 @@
{:account-color @account-color
:set-account-color set-account-color}]
[new-account-origin
{:derivation-path constants/path-default-wallet
{:derivation-path derivation-path
:customization-color customization-color
:keypair-title keypair-name}]]))))
@@ -247,9 +263,9 @@
:account-color account-color
:emoji emoji}]
(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.
{:keys [keypair-name]} (rf/sub [:wallet/create-account-new-keypair])]
{:keys [keypair-name workflow]} (rf/sub [:wallet/create-account-new-keypair])]
(rn/use-unmount #(rf/dispatch [:wallet/clear-create-account]))
@@ -260,7 +276,9 @@
:set-account-color set-account-color
:set-emoji set-emoji
:state state
:keypair-name keypair-name}]
:keypair-name keypair-name
:workflow workflow}]
[derive-account-variant
{:customization-color customization-color
:on-change-text on-change-text
@@ -3,4 +3,5 @@
(defn ens-name? [s] (boolean (re-find constants/regx-ens s)))
(defn eth-address? [s] (re-find constants/regx-multichain-address s))
(defn private-key? [s] (re-find constants/regx-private-key s))
(defn private-key? [s] (or (re-find constants/regx-private-key s)
(re-find constants/regx-private-key-hex s)))
@@ -13,6 +13,11 @@
[kw]
(-> kw symbol str))
(rf/reg-fx
:effects.wallet/get-random-mnemonic
(fn [{:keys [on-success]}]
(native-module/get-random-mnemonic on-success)))
(rf/reg-fx
:effects.wallet/create-account-from-mnemonic
(fn [{:keys [mnemonic-phrase paths on-success]
@@ -26,6 +31,26 @@
:paths paths}
on-success))))
(defn create-account-from-private-key
[private-key]
(-> private-key
(security/safe-unmask-data)
(native-module/create-account-from-private-key)
(promesa/then (fn [result]
(update result :privateKey security/mask-data)))))
(rf/reg-fx
:effects.wallet/create-account-from-private-key
(fn [{:keys [private-key on-success on-error]}]
(-> (create-account-from-private-key private-key)
;; TODO: use `rf/call-continuation` to support event vectors
(promesa/then (fn [result]
(when (fn? on-success)
(on-success result))))
(promesa/catch (fn [error]
(when (and error (fn? on-error))
(on-error error)))))))
(defn validate-mnemonic
[mnemonic]
(-> mnemonic