diff --git a/modules/react-native-status/nodejs/status.cpp b/modules/react-native-status/nodejs/status.cpp index 87f07516d2b..e6881e3c7e7 100644 --- a/modules/react-native-status/nodejs/status.cpp +++ b/modules/react-native-status/nodejs/status.cpp @@ -1888,6 +1888,36 @@ void _InitLogging(const FunctionCallbackInfo& args) { delete c; } +void _RestoreAccountAndLogin(const FunctionCallbackInfo& args) { + Isolate* isolate = args.GetIsolate(); + Local context = isolate->GetCurrentContext(); + + if (args.Length() != 1) { + // Throw an Error that is passed back to JavaScript + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong number of arguments for RestoreAccountAndLogin"))); + return; + } + + // Check the argument types + + if (!args[0]->IsString()) { + isolate->ThrowException(Exception::TypeError( + String::NewFromUtf8Literal(isolate, "Wrong argument type for 'RestoreAccountAndLogin'"))); + return; + } + + String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked()); + char *arg0 = *arg0Obj; + + // Call exported Go function, which returns a C string + char *c = RestoreAccountAndLogin(arg0); + + Local ret = String::NewFromUtf8(isolate, c).ToLocalChecked(); + args.GetReturnValue().Set(ret); + delete c; +} + void init(Local exports) { NODE_SET_METHOD(exports, "multiAccountGenerateAndDeriveAddresses", _MultiAccountGenerateAndDeriveAddresses); @@ -1945,6 +1975,7 @@ void init(Local exports) { NODE_SET_METHOD(exports, "connectionChange", _ConnectionChange); NODE_SET_METHOD(exports, "pollSignal", _PollSignal); NODE_SET_METHOD(exports, "initLogging", _InitLogging); + NODE_SET_METHOD(exports, "restoreAccountAndLogin", _RestoreAccountAndLogin); } NODE_MODULE(NODE_GYP_MODULE_NAME, init) diff --git a/src/mocks/js_dependencies.cljs b/src/mocks/js_dependencies.cljs index 50c8e547423..a6b37ae690c 100644 --- a/src/mocks/js_dependencies.cljs +++ b/src/mocks/js_dependencies.cljs @@ -2,7 +2,7 @@ (:require-macros [legacy.status-im.utils.slurp :refer [slurp]]) (:require [legacy.status-im.fleet.default-fleet :refer (default-fleets)]) - (:require [legacy.status-im.utils.test :as utils.test])) + (:require [tests.test-utils :as utils.test])) ;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and ;; return itself diff --git a/src/test_helpers/integration.clj b/src/test_helpers/integration.clj index 0018e6dae5f..a975aca8f5d 100644 --- a/src/test_helpers/integration.clj +++ b/src/test_helpers/integration.clj @@ -5,7 +5,7 @@ (defmacro with-app-initialized [& body] `(do - (legacy.status-im.utils.test/init!) + (tests.test-utils/init!) (if (test-helpers.integration/app-initialized) (do ~@body) (do @@ -22,3 +22,15 @@ (rf-test/wait-for [:messenger-started] (test-helpers.integration/assert-messenger-started) ~@body)))) + +(defmacro with-recovered-account + [& body] + `(if (test-helpers.integration/messenger-started) + (do ~@body) + (do + (test-helpers.integration/recover-multiaccount!) + (rf-test/wait-for + [:messenger-started] + (test-helpers.integration/enable-testnet!) + (test-helpers.integration/assert-messenger-started) + ~@body)))) diff --git a/src/test_helpers/integration.cljs b/src/test_helpers/integration.cljs index ff76b628fd0..185a23a6ae8 100644 --- a/src/test_helpers/integration.cljs +++ b/src/test_helpers/integration.cljs @@ -15,7 +15,19 @@ status-im.subs.root [taoensso.timbre :as log] [tests.integration-test.constants :as constants] - [utils.collection :as collection])) + [utils.collection :as collection] + [utils.security.core :as security] + [utils.transforms :as transforms])) + +(defn validate-mnemonic + [mnemonic on-success on-error] + (native-module/validate-mnemonic + (security/safe-unmask-data mnemonic) + (fn [result] + (let [{:keys [error keyUID]} (transforms/json->clj result)] + (if (seq error) + (when on-error (on-error error)) + (on-success mnemonic keyUID)))))) (def default-re-frame-wait-for-timeout-ms "Controls the maximum time allowed to wait for all events to be processed by @@ -223,3 +235,30 @@ (set! rf.interop/debug-enabled? false)) :after (fn [] (set! rf.interop/debug-enabled? true))}) + +(defn recover-and-login + [seed-phrase] + (rf/dispatch [:profile.recover/recover-and-login + {:display-name (:name constants/recovery-account) + :seed-phrase seed-phrase + :password constants/password + :color "blue"}])) + +(defn enable-testnet! + [] + (rf/dispatch [:profile.settings/profile-update :test-networks-enabled? + true {}]) + (rf/dispatch [:wallet/initialize])) + +(defn recover-multiaccount! + [] + (let [masked-seed-phrase (security/mask-data (:seed-phrase constants/recovery-account))] + (validate-mnemonic + masked-seed-phrase + (fn [mnemonic key-uid] + (rf/dispatch [:onboarding/seed-phrase-validated + (security/mask-data mnemonic) key-uid]) + (rf/dispatch [:pop-to-root :profiles]) + (rf/dispatch [:profile/profile-selected key-uid]) + (recover-and-login mnemonic)) + #()))) diff --git a/src/tests/contract_test/core_test.cljs b/src/tests/contract_test/core_test.cljs index 046f291b833..2776e1e5d1e 100644 --- a/src/tests/contract_test/core_test.cljs +++ b/src/tests/contract_test/core_test.cljs @@ -5,12 +5,12 @@ legacy.status-im.events [legacy.status-im.multiaccounts.logout.core :as logout] legacy.status-im.subs.root - [legacy.status-im.utils.test :as utils.test] [re-frame.core :as rf] status-im.events status-im.navigation.core status-im.subs.root - [test-helpers.integration :as h])) + [test-helpers.integration :as h] + [tests.test-utils :as utils.test])) (deftest initialize-app-test (h/log-headline :initialize-app-test) diff --git a/src/tests/contract_test/wallet_test.cljs b/src/tests/contract_test/wallet_test.cljs index 2efaf165e13..8d8ebc47360 100644 --- a/src/tests/contract_test/wallet_test.cljs +++ b/src/tests/contract_test/wallet_test.cljs @@ -104,7 +104,7 @@ (h/log-headline :wallet/create-derived-addresses) (rf-test/run-test-async (h/with-app-initialized - (h/with-account + (h/with-recovered-account (let [sha3-pwd (native-module/sha3 test-password) derivation-path ["m/43'/60'/1581'/0'/0"] main-account (contract-utils/call-rpc-endpoint diff --git a/src/tests/integration_test/constants.cljs b/src/tests/integration_test/constants.cljs index 4f72fb39352..0f5becbd3b6 100644 --- a/src/tests/integration_test/constants.cljs +++ b/src/tests/integration_test/constants.cljs @@ -5,3 +5,10 @@ (def community {:membership 1 :name "foo" :description "bar"}) (def account-name "account-abc") + +(def recovery-account + {:name "wallet-abc" + :seed-phrase "destroy end torch puzzle develop note wise island disease chaos kind bus" + :key-uid "0x6827f3d1e21ae723a24e0dcbac1853b5ed4d651c821a2326492ce8f2367ec905" + :public-key "0x48b8b9e2a8f71e1beae729d83bcd385ffc6af0d5" + :main-account "0x48b8b9e2a8f71e1beae729d83bcd385ffc6af0d5"}) diff --git a/src/tests/test_utils.cljs b/src/tests/test_utils.cljs new file mode 100644 index 00000000000..85b2dcd7423 --- /dev/null +++ b/src/tests/test_utils.cljs @@ -0,0 +1,136 @@ +(ns tests.test-utils + (:require + [legacy.status-im.utils.deprecated-types :as types] + [re-frame.core :as re-frame])) + +(def native-status (js/require "../../modules/react-native-status/nodejs/bindings")) + +(def fs (js/require "fs")) +(def path (js/require "path")) +(def os (js/require "os")) + +(def tmpdir (.tmpdir os)) + +(def test-dir-prefix (.join path tmpdir "status-mobile-tests")) + +(def test-dir (.mkdtempSync fs test-dir-prefix)) + +(def initialized? (atom false)) + +(defn signal-received-callback + [a] + (re-frame/dispatch [:signals/signal-received a])) + +;; We poll for signals, could not get callback working +(defn init! + [] + (when-not @initialized? + (.setSignalEventCallback native-status) + (reset! initialized? true) + (js/setInterval (fn [] + (.pollSignal native-status signal-received-callback) + 100)))) + +(def ui-helper + (clj->js + {:clearCookies identity + :clearStorageAPIs identity})) + +(def encryption-utils + (clj->js + {:sha3 + (fn [s] (.sha3 native-status s)) + :setBlankPreviewFlag + identity + :encodeTransfer + (fn [to-norm amount-hex] + (.encodeTransfer native-status to-norm amount-hex)) + :hexToNumber + (fn [hex] (.hexToNumber native-status hex)) + :decodeParameters + (fn [decode-param-json] + (.decodeParameters native-status decode-param-json)) + :numberToHex + (fn [num-str] (.numberToHex native-status num-str)) + :initKeystore + (fn [key-uid callback] + (callback (.initKeystore native-status + (str test-dir "/keystore/" key-uid)))) + :multiformatDeserializePublicKey + (fn [public-key deserialization-key callback] + (callback (.multiformatDeserializePublicKey + native-status + public-key + deserialization-key)))})) + +(def account-manager + (clj->js + {:openAccounts + (fn [callback] + (callback (.openAccounts native-status test-dir))) + :createAccountAndLogin + (fn [request] (.createAccountAndLogin native-status request)) + :restoreAccountAndLogin + (fn [request] + (prn native-status) + (.restoreAccountAndLogin native-status request)) + :loginAccount + (fn [request] (.loginAccount native-status request)) + :logout + (fn [] (.logout native-status)) + :multiAccountImportMnemonic + (fn [json callback] + (callback (.multiAccountImportMnemonic native-status json))) + :multiAccountLoadAccount + (fn [json callback] + (callback (.multiAccountLoadAccount native-status json))) + :multiAccountDeriveAddresses + (fn [json callback] + (callback (.multiAccountDeriveAddresses native-status json))) + :multiAccountGenerateAndDeriveAddresses + (fn [json callback] + (callback (.multiAccountGenerateAndDeriveAddresses native-status json))) + :multiAccountStoreDerived + (fn [json callback] + (callback (.multiAccountStoreDerivedAccounts native-status json)))})) + +(def utils + (clj->js + {:backupDisabledDataDir + (fn [] (str test-dir "/backup")) + :keystoreDir (fn [] "") + :toChecksumAddress + (fn [address] (.toChecksumAddress native-status address)) + :checkAddressChecksum + (fn [address] (.checkAddressChecksum native-status address)) + :validateMnemonic + (fn [json callback] (callback (.validateMnemonic native-status json))) + :isAddress + (fn [address] (.isAddress native-status address))})) + +(def log-manager + (clj->js + {:logFileDirectory + (fn [] (str test-dir "/log")) + :initLogging + (fn [enabled mobile-system log-level callback] + (callback (.initLogging native-status + (types/clj->json {:Enabled enabled + :MobileSystem mobile-system + :Level log-level + :File (str test-dir "/geth.log")}))))})) + +(def network + (clj->js + {:callPrivateRPC + (fn [payload callback] + (callback (.callPrivateRPC native-status payload)))})) + +(def status + (clj->js + {:getNodeConfig + (fn [] (types/clj->json {:WakuV2Config ""})) + :fleets + (fn [] (.fleets native-status)) + :startLocalNotifications + identity}))