diff --git a/modules/react-native-status/desktop/rctstatus.cpp b/modules/react-native-status/desktop/rctstatus.cpp index 4f5c17634a..95ba0a69d3 100644 --- a/modules/react-native-status/desktop/rctstatus.cpp +++ b/modules/react-native-status/desktop/rctstatus.cpp @@ -92,15 +92,7 @@ void RCTStatus::startNode(QString configString) { if (!relativeDataDirPath.startsWith("/")) relativeDataDirPath.prepend("/"); - QString statusDataDir = qgetenv("STATUS_DATA_DIR"); - QString rootDirPath; - if (!statusDataDir.isEmpty()) { - rootDirPath = statusDataDir; - } - else { - rootDirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); - } - + QString rootDirPath = getRootDirPath(); QDir rootDir(rootDirPath); QString absDataDirPath = rootDirPath + relativeDataDirPath; QDir dataDir(absDataDirPath); @@ -181,6 +173,19 @@ void RCTStatus::login(QString address, QString password, double callbackId) { }, address, password, callbackId); } +void RCTStatus::verify(QString address, QString password, double callbackId) { + Q_D(RCTStatus); + qCInfo(RCTSTATUS) << "::verify call - callbackId:" << callbackId; + QtConcurrent::run([&](QString address, QString password, double callbackId) { + QDir rootDir(getRootDirPath()); + QString keystorePath = rootDir.absoluteFilePath("keystore"); + const char* result = VerifyAccountPassword(keystorePath.toUtf8().data(), address.toUtf8().data(), password.toUtf8().data()); + logStatusGoResult("::verify VerifyAccountPassword", result); + d->bridge->invokePromiseCallback(callbackId, QVariantList{result}); + }, address, password, callbackId); +} + + void RCTStatus::sendTransaction(QString txArgsJSON, QString password, double callbackId) { Q_D(RCTStatus); @@ -329,3 +334,17 @@ void RCTStatus::updateMailservers(QString enodes, double callbackId) { d->bridge->invokePromiseCallback(callbackId, QVariantList{result}); }, enodes, callbackId); } + +QString RCTStatus::getRootDirPath() { + QString statusDataDir = qgetenv("STATUS_DATA_DIR"); + QString rootDirPath; + if (!statusDataDir.isEmpty()) { + rootDirPath = statusDataDir; + } + else { + rootDirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + } + + return rootDirPath; +} + diff --git a/modules/react-native-status/desktop/rctstatus.h b/modules/react-native-status/desktop/rctstatus.h index 5f5c20ce8e..c33b75f142 100644 --- a/modules/react-native-status/desktop/rctstatus.h +++ b/modules/react-native-status/desktop/rctstatus.h @@ -42,6 +42,7 @@ public: Q_INVOKABLE void addPeer(QString enode, double callbackId); Q_INVOKABLE void recoverAccount(QString passphrase, QString password, double callbackId); Q_INVOKABLE void login(QString address, QString password, double callbackId); + Q_INVOKABLE void verify(QString address, QString password, double callbackId); Q_INVOKABLE void sendTransaction(QString txArgsJSON, QString password, double callbackId); Q_INVOKABLE void signMessage(QString rpcParams, double callbackId); Q_INVOKABLE void signGroupMembership(QString content, double callbackId); @@ -74,6 +75,7 @@ private Q_SLOTS: private: void logStatusGoResult(const char* methodName, const char* result); + QString getRootDirPath(); QScopedPointer d_ptr; }; diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index ff6ee0c212..edcaad07f7 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -229,6 +229,7 @@ (def ^:const api-response "api-response") (def ^:const api-request "api-request") (def ^:const history-state-changed "history-state-changed") +(def ^:const debug-metrics "debug_metrics") (def ^:const web3-send-async "web3-send-async") (def ^:const web3-send-async-read-only "web3-send-async-read-only") (def ^:const web3-send-async-callback "web3-send-async-callback") diff --git a/src/status_im/mailserver/core.cljs b/src/status_im/mailserver/core.cljs index fb79af0dc0..c2c7dfdac8 100644 --- a/src/status_im/mailserver/core.cljs +++ b/src/status_im/mailserver/core.cljs @@ -13,6 +13,7 @@ [clojure.string :as string] [status-im.data-store.mailservers :as data-store.mailservers] [status-im.i18n :as i18n] + [status-im.utils.handlers :as handlers] [status-im.accounts.update.core :as accounts.update] [status-im.ui.screens.navigation :as navigation])) @@ -94,38 +95,10 @@ db mailservers)}) -(defn- parse-json - ;; NOTE(dmitryn) Expects JSON response like: - ;; {"error": "msg"} or {"result": true} - [s] - (try - (let [res (-> s - js/JSON.parse - (js->clj :keywordize-keys true))] - ;; NOTE(dmitryn): AddPeer() may return {"error": ""} - ;; assuming empty error is a success response - ;; by transforming {"error": ""} to {:result true} - (if (and (:error res) - (= (:error res) "")) - {:result true} - res)) - (catch :default e - {:error (.-message e)}))) - -(defn- response-handler [success-fn error-fn] - (fn handle-response - ([response] - (let [{:keys [error result]} (parse-json response)] - (handle-response error result))) - ([error result] - (if error - (error-fn error) - (success-fn result))))) - (defn add-peer! [enode] (status/add-peer enode - (response-handler #(log/debug "mailserver: add-peer success" %) - #(log/error "mailserver: add-peer error" %)))) + (handlers/response-handler #(log/debug "mailserver: add-peer success" %) + #(log/error "mailserver: add-peer error" %)))) ;; We now wait for a confirmation from the mailserver before marking the message ;; as sent. @@ -133,8 +106,8 @@ (defn update-mailservers! [enodes] (status/update-mailservers (.stringify js/JSON (clj->js enodes)) - (response-handler #(log/debug "mailserver: update-mailservers success" %) - #(log/error "mailserver: update-mailservers error" %)))) + (handlers/response-handler #(log/debug "mailserver: update-mailservers success" %) + #(log/error "mailserver: update-mailservers error" %)))) (defn remove-peer! [enode] (let [args {:jsonrpc "2.0" @@ -143,8 +116,8 @@ :params [enode]} payload (.stringify js/JSON (clj->js args))] (status/call-private-rpc payload - (response-handler #(log/debug "mailserver: remove-peer success" %) - #(log/error "mailserver: remove-peer error" %))))) + (handlers/response-handler #(log/debug "mailserver: remove-peer success" %) + #(log/error "mailserver: remove-peer error" %))))) (re-frame/reg-fx :mailserver/add-peer diff --git a/src/status_im/ui/components/desktop/events.cljs b/src/status_im/ui/components/desktop/events.cljs index ae84979a78..d2e3a31d30 100644 --- a/src/status_im/ui/components/desktop/events.cljs +++ b/src/status_im/ui/components/desktop/events.cljs @@ -2,6 +2,11 @@ (:require [status-im.utils.handlers :as handlers] [status-im.utils.fx :as fx] [status-im.ui.screens.navigation :as navigation] + [status-im.constants :as constants] + [re-frame.core :as re-frame] + [status-im.native-module.core :as status] + [taoensso.timbre :as log] + [status-im.utils.handlers :as handlers] [status-im.utils.platform :as platform])) (fx/defn change-tab @@ -26,3 +31,37 @@ :show-desktop-tab (fn [cofx [_ tab-name]] (show-desktop-tab cofx tab-name))) + +(defn packet-keys [direction] + "helper for accessing deeply nested metrics keys" + {:pre [(#{:in :out} direction)]} + [:misc direction :packets :Overall]) + +(handlers/register-handler-fx + :debug-metrics-success + (fn [{:keys [db]} [_ {:keys [p2p mailserver les whisper], :as metrics}]] + {:db (assoc-in db + [:desktop/desktop :debug-metrics] + {:mailserver-request-process-time (get-in mailserver [:requestProcessTime :Overall]) + :mailserver-request-errors (get-in mailserver [:requestErrors :Overall]) + :les-packets-in (get-in les (packet-keys :in)) + :les-packets-out (get-in les (packet-keys :out)) + :p2p-inbound-traffic (get-in p2p [:InboundTraffic :Overall]) + :p2p-outbound-traffic (get-in p2p [:OutboundTraffic :Overall])})})) + +(defn debug-metrics-rpc-call [payload] + "json rpc wrapper for debug metrics; dispatch :debug-metrics-success on success" + (status/call-private-rpc + payload + (handlers/response-handler #(re-frame/dispatch [:debug-metrics-success %]) + #(log/debug "we did not get the debug metrics" %)))) + +(handlers/register-handler-fx + :load-debug-metrics + (fn [{:keys [db]} _] + (let [args {:jsonrpc "2.0" + :id 2 + :method constants/debug-metrics + :params [true]} + payload (.stringify js/JSON (clj->js args))] + (debug-metrics-rpc-call payload)))) diff --git a/src/status_im/ui/screens/desktop/main/tabs/profile/styles.cljs b/src/status_im/ui/screens/desktop/main/tabs/profile/styles.cljs index 79968654cd..7bbfefe47e 100644 --- a/src/status_im/ui/screens/desktop/main/tabs/profile/styles.cljs +++ b/src/status_im/ui/screens/desktop/main/tabs/profile/styles.cljs @@ -164,6 +164,11 @@ :font-size 20}) (def connection-message-text + {:margin-left 24 + :margin-bottom 10 + :font-size 16}) + +(def connection-stats-entry {:margin-left 24 :margin-bottom 10}) @@ -177,5 +182,11 @@ :margin-bottom 16 :font-size 16}) +(def connection-stats-title + {:margin-left 24 + :margin-top 16 + :margin-bottom 16 + :font-size 16}) + (def pair-button {:margin-left 32}) diff --git a/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs b/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs index 63e54b32c8..b584a84a89 100644 --- a/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs +++ b/src/status_im/ui/screens/desktop/main/tabs/profile/views.cljs @@ -15,6 +15,9 @@ [status-im.ui.screens.pairing.views :as pairing.views] [status-im.ui.components.qr-code-viewer.views :as qr-code-viewer] [status-im.ui.screens.desktop.main.tabs.profile.styles :as styles] + [status-im.native-module.core :as status] + [status-im.mailserver.core :as mailserver.core] + [status-im.constants :as constants] [status-im.ui.screens.profile.user.views :as profile] [status-im.ui.screens.profile.seed.views :as profile.recovery] [status-im.ui.components.common.common :as components.common])) @@ -99,6 +102,36 @@ (and peers-disconnected? searching?) "Disconnected and searching" :else "Disconnected"))) +(defn connection-statistics-display + [{:keys [mailserver-request-process-time + mailserver-request-errors + les-packets-in + les-packets-out + p2p-inbound-traffic + p2p-outbound-traffic]}] + [react/view {:style {:flex-direction :row}} + [react/view + [react/text {:style styles/connection-stats-title} + "Mailserver requests"] + [react/text {:style styles/connection-stats-entry} + (str "errors " p2p-inbound-traffic)] + [react/text {:style styles/connection-stats-entry} + (str "process time " p2p-outbound-traffic)]] + [react/view + [react/text {:style styles/connection-stats-title} + "p2p traffic"] + [react/text {:style styles/connection-stats-entry} + (str "inbound " p2p-inbound-traffic)] + [react/text {:style styles/connection-stats-entry} + (str "outbound " p2p-outbound-traffic)]] + [react/view + [react/text {:style styles/connection-stats-title} + "LES packets"] + [react/text {:style styles/connection-stats-entry} + (str "inbound " les-packets-in)] + [react/text {:style styles/connection-stats-entry} + (str "outbound " les-packets-out)]]]) + (views/defview advanced-settings [] (views/letsubs [installations [:pairing/installations] current-mailserver-id [:mailserver/current-id] @@ -106,6 +139,7 @@ mailserver-state [:mailserver/state] node-status [:node-status] peers-count [:peers-count] + connection-stats [:connection-stats] disconnected [:disconnected?]] (let [render-fn (offline-messaging.views/render-row current-mailserver-id) connection-message (connection-status peers-count node-status mailserver-state disconnected)] @@ -123,7 +157,9 @@ [react/view {:style {:margin-vertical 8}} [render-fn mailserver]])] (when (config/pairing-enabled? true) - (installations-section installations))]))) + (installations-section installations)) + [react/view {:style styles/title-separator}] + (connection-statistics-display connection-stats)]))) (views/defview backup-recovery-phrase [] [profile.recovery/backup-seed]) @@ -161,7 +197,9 @@ :value notifications? :on-value-change #(re-frame/dispatch [:accounts.ui/notifications-enabled (not notifications?)])}]] [react/touchable-highlight {:style (styles/profile-row adv-settings-open?) - :on-press #(re-frame/dispatch [:navigate-to (if adv-settings-open? :home :advanced-settings)])} + :on-press #(do + (re-frame/dispatch [:navigate-to (if adv-settings-open? :home :advanced-settings)]) + (re-frame/dispatch [:load-debug-metrics]))} [react/view {:style styles/adv-settings} [react/text {:style (styles/profile-row-text colors/black) :font (if adv-settings-open? :medium :default)} diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index 565d04dcd1..1009b3252b 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -54,6 +54,10 @@ (fn [peers-count] (zero? peers-count))) +(reg-sub :connection-stats + (fn [db _] + (get-in db [:desktop/desktop :debug-metrics]))) + (reg-sub :offline? :<- [:network-status] :<- [:sync-state] diff --git a/src/status_im/utils/handlers.cljs b/src/status_im/utils/handlers.cljs index c009f5c0c3..c02259fb1d 100644 --- a/src/status_im/utils/handlers.cljs +++ b/src/status_im/utils/handlers.cljs @@ -95,6 +95,34 @@ check-spec) (re-frame/inject-cofx :now)]) +(defn- parse-json + ;; NOTE(dmitryn) Expects JSON response like: + ;; {"error": "msg"} or {"result": true} + [s] + (try + (let [res (-> s + js/JSON.parse + (js->clj :keywordize-keys true))] + ;; NOTE(dmitryn): AddPeer() may return {"error": ""} + ;; assuming empty error is a success response + ;; by transforming {"error": ""} to {:result true} + (if (and (:error res) + (= (:error res) "")) + {:result true} + res)) + (catch :default e + {:error (.-message e)}))) + +(defn response-handler [success-fn error-fn] + (fn handle-response + ([response] + (let [{:keys [error result]} (parse-json response)] + (handle-response error result))) + ([error result] + (if error + (error-fn error) + (success-fn result))))) + (defn register-handler-fx ([name handler] (register-handler-fx name nil handler))