From 73777e052ed989bb6f2c63ba0131dafa3aed5a51 Mon Sep 17 00:00:00 2001 From: flexsurfer Date: Fri, 26 Jul 2024 17:45:45 +0200 Subject: [PATCH] Ensure keycard account can send transaction after upgrading from v1 to v2 #20552 (#20845) --- src/status_im/common/signals/events.cljs | 6 +++ .../standard_authentication/events.cljs | 42 +++++++++++++++---- .../standard_auth/slide_button/view.cljs | 3 +- src/status_im/contexts/keycard/effects.cljs | 19 +++++++++ src/status_im/contexts/keycard/events.cljs | 18 ++++++++ .../contexts/keycard/login/events.cljs | 39 +++++------------ .../contexts/keycard/pin/events.cljs | 10 +++-- src/status_im/contexts/keycard/pin/view.cljs | 4 +- .../contexts/keycard/sign/events.cljs | 29 +++++++++++++ src/status_im/contexts/keycard/utils.cljs | 4 +- .../contexts/profile/profiles/view.cljs | 11 ++++- .../contexts/wallet/send/events.cljs | 22 +++++++++- .../send/transaction_confirmation/view.cljs | 6 +-- 13 files changed, 162 insertions(+), 51 deletions(-) create mode 100644 src/status_im/contexts/keycard/sign/events.cljs diff --git a/src/status_im/common/signals/events.cljs b/src/status_im/common/signals/events.cljs index 4d6a701271..e72ba07c3d 100644 --- a/src/status_im/common/signals/events.cljs +++ b/src/status_im/common/signals/events.cljs @@ -38,6 +38,12 @@ "wallet" {:fx [[:dispatch [:wallet/signal-received event-js]]]} + "wallet.sign.transactions" + {:fx [[:dispatch + [:standard-auth/authorize-with-keycard + {:on-complete #(rf/dispatch [:keycard/sign-hash % + (first (transforms/js->clj event-js))])}]]]} + "wallet.suggested.routes" {:fx [[:dispatch [:wallet/handle-suggested-routes (transforms/js->clj event-js)]]]} diff --git a/src/status_im/common/standard_authentication/events.cljs b/src/status_im/common/standard_authentication/events.cljs index f29d139893..435ff8c46c 100644 --- a/src/status_im/common/standard_authentication/events.cljs +++ b/src/status_im/common/standard_authentication/events.cljs @@ -3,21 +3,35 @@ [schema.core :as schema] [status-im.common.standard-authentication.enter-password.view :as enter-password] [status-im.common.standard-authentication.events-schema :as events-schema] + [status-im.contexts.keycard.pin.view :as keycard.pin] [taoensso.timbre :as log] + [utils.address] [utils.i18n :as i18n] [utils.re-frame :as rf] [utils.security.core :as security])) +(rf/reg-fx :effects.keycard/call-on-auth-success + (fn [on-auth-success] + (when on-auth-success (on-auth-success "")))) + (defn authorize - [{:keys [db]} [args]] + [{:keys [db]} [{:keys [on-auth-success keycard-supported?] :as args}]] (let [key-uid (get-in db [:profile/profile :key-uid]) keycard? (get-in db [:profile/profile :keycard-pairing])] - {:fx [[:effects.biometric/check-if-available - {:key-uid key-uid - :on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args]) - :on-fail (if keycard? - #(rf/dispatch [:standard-auth/authorize-with-keycard args]) - #(rf/dispatch [:standard-auth/authorize-with-password args]))}]]})) + {:fx + [(if keycard? + (if keycard-supported? + [:effects.keycard/call-on-auth-success on-auth-success] + [:effects.utils/show-popup + {:title "This feature is not supported yet " + :content + "Keycard support is limited to logging in + and signing the sending transaction. + Use Status Desktop to access all functions."}]) + [:effects.biometric/check-if-available + {:key-uid key-uid + :on-success #(rf/dispatch [:standard-auth/authorize-with-biometric args]) + :on-fail #(rf/dispatch [:standard-auth/authorize-with-password args])}])]})) (schema/=> authorize events-schema/?authorize) (rf/reg-event-fx :standard-auth/authorize authorize) @@ -94,6 +108,16 @@ :button-icon-left auth-button-icon-left :button-label auth-button-label}]))) +(defn authorize-with-keycard + [_ [{:keys [on-complete]}]] + {:fx [[:dispatch + [:show-bottom-sheet + {:hide-on-background-press? false + :on-close #(rf/dispatch [:standard-auth/reset-login-password]) + :content (fn [] + [keycard.pin/auth {:on-complete on-complete}])}]]]}) +(rf/reg-event-fx :standard-auth/authorize-with-keycard authorize-with-keycard) + (defn authorize-with-password [_ [{:keys [on-close theme blur?] :as args}]] {:fx [[:dispatch [:standard-auth/reset-login-password]] @@ -110,7 +134,9 @@ (rf/reg-event-fx :standard-auth/reset-login-password (fn [{:keys [db]}] - {:db (update db :profile/login dissoc :password :error)})) + {:db (-> db + (update :profile/login dissoc :password :error) + (update :keycard dissoc :pin))})) (rf/reg-fx :standard-auth/on-close diff --git a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs index c96eedf17a..8589140413 100644 --- a/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs +++ b/src/status_im/common/standard_authentication/standard_auth/slide_button/view.cljs @@ -8,7 +8,7 @@ (defn view [{:keys [track-text customization-color auth-button-label on-auth-success on-auth-fail - auth-button-icon-left size blur? container-style disabled? dependencies] + auth-button-icon-left size blur? container-style disabled? dependencies keycard-supported?] :or {container-style {:flex 1}}}] (let [theme (quo.theme/use-theme) auth-method (rf/sub [:auth-method]) @@ -21,6 +21,7 @@ :auth-button-icon-left auth-button-icon-left :theme theme :blur? blur? + :keycard-supported? keycard-supported? :biometric-auth? biometric-auth? :on-auth-success on-auth-success :on-auth-fail on-auth-fail diff --git a/src/status_im/contexts/keycard/effects.cljs b/src/status_im/contexts/keycard/effects.cljs index c831984ecc..c9e91f371c 100644 --- a/src/status_im/contexts/keycard/effects.cljs +++ b/src/status_im/contexts/keycard/effects.cljs @@ -109,3 +109,22 @@ [pairings] (keycard/set-pairings pairings)) (rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard) + +(defn sign + [{:keys [on-success on-failure] :as args}] + (log/debug "[keycard] sign") + (keycard/sign + (assoc + args + :on-success + (fn [response] + (log/debug "[keycard response succ] sign") + (when on-success + (on-success (transforms/js->clj response)))) + :on-failure + (fn [response] + (log/warn "[keycard response fail] sign" + (error-object->map response)) + (when on-failure + (on-failure (error-object->map response))))))) +(rf/reg-fx :effects.keycard/sign sign) diff --git a/src/status_im/contexts/keycard/events.cljs b/src/status_im/contexts/keycard/events.cljs index bad13fbf65..e57ba5d2fb 100644 --- a/src/status_im/contexts/keycard/events.cljs +++ b/src/status_im/contexts/keycard/events.cljs @@ -3,6 +3,8 @@ status-im.contexts.keycard.login.events status-im.contexts.keycard.pin.events status-im.contexts.keycard.sheet.events + status-im.contexts.keycard.sign.events + [status-im.contexts.keycard.utils :as keycard.utils] [taoensso.timbre :as log])) (rf/reg-event-fx :keycard/on-check-nfc-enabled-success @@ -55,3 +57,19 @@ (fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]] (log/debug "[keycard] nfc started success") {:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)})) + +(rf/reg-event-fx :keycard/on-action-with-pin-error + (fn [{:keys [db]} [error]] + (log/debug "[keycard] get keys error: " error) + (let [tag-was-lost? (keycard.utils/tag-lost? (:error error)) + pin-retries-count (keycard.utils/pin-retries (:error error))] + (if tag-was-lost? + {:db (assoc-in db [:keycard :pin :status] nil)} + (if (nil? pin-retries-count) + {:effects.utils/show-popup {:title "wrong-keycard"}} + {:db (-> db + (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) + (update-in [:keycard :pin] assoc :status :error)) + :fx [[:dispatch [:keycard/hide-connection-sheet]] + (when (zero? pin-retries-count) + [:effects.utils/show-popup {:title "frozen-keycard"}])]}))))) diff --git a/src/status_im/contexts/keycard/login/events.cljs b/src/status_im/contexts/keycard/login/events.cljs index b241a25d5e..f2d443d432 100644 --- a/src/status_im/contexts/keycard/login/events.cljs +++ b/src/status_im/contexts/keycard/login/events.cljs @@ -3,22 +3,6 @@ [taoensso.timbre :as log] [utils.re-frame :as rf])) -(rf/reg-event-fx :keycard.login/on-get-keys-error - (fn [{:keys [db]} [error]] - (log/debug "[keycard] get keys error: " error) - (let [tag-was-lost? (keycard.utils/tag-lost? (:error error)) - pin-retries-count (keycard.utils/pin-retries (:error error))] - (if tag-was-lost? - {:db (assoc-in db [:keycard :pin :status] nil)} - (if (nil? pin-retries-count) - {:effects.utils/show-popup {:title "wrong-keycard"}} - {:db (-> db - (assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count) - (update-in [:keycard :pin] assoc :status :error)) - :fx [[:dispatch [:keycard/hide-connection-sheet]] - (when (zero? pin-retries-count) - [:effects.utils/show-popup {:title "frozen-keycard"}])]}))))) - (rf/reg-event-fx :keycard.login/on-get-keys-success (fn [{:keys [db]} [data]] (let [{:keys [key-uid encryption-public-key @@ -56,28 +40,25 @@ :key-uid key-uid}]]})))) (rf/reg-event-fx :keycard.login/on-get-application-info-success - (fn [{:keys [db]} [application-info]] - (let [profile (get-in db [:profile/profiles-overview (get-in db [:profile/login :key-uid])]) - pin (get-in db [:keycard :pin :text]) - error (keycard.utils/validate-application-info profile application-info)] + (fn [{:keys [db]} [application-info {:keys [key-uid on-read-fx]}]] + (let [error (keycard.utils/validate-application-info key-uid application-info)] (if error {:effects.utils/show-popup {:title (str error)}} - {:db (-> db - (assoc-in [:keycard :application-info] application-info) - (assoc-in [:keycard :pin :status] :verifying)) - :effects.keycard/get-keys {:pin pin - :on-success #(rf/dispatch [:keycard.login/on-get-keys-success %]) - :on-failure #(rf/dispatch [:keycard.login/on-get-keys-error %])}})))) + {:db (-> db + (assoc-in [:keycard :application-info] application-info) + (assoc-in [:keycard :pin :status] :verifying)) + :fx on-read-fx})))) (rf/reg-event-fx :keycard.login/cancel-reading-card (fn [{:keys [db]}] {:db (assoc-in db [:keycard :on-card-connected-event-vector] nil)})) -(rf/reg-event-fx :keycard/read-card-and-login - (fn [{:keys [db]}] +(rf/reg-event-fx :keycard/read-card + (fn [{:keys [db]} [args]] (let [connected? (get-in db [:keycard :card-connected?]) event-vector [:keycard/get-application-info - {:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success %])}]] + {:on-success #(rf/dispatch [:keycard.login/on-get-application-info-success % + args])}]] (log/debug "[keycard] proceed-to-login") {:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector) :fx [[:dispatch diff --git a/src/status_im/contexts/keycard/pin/events.cljs b/src/status_im/contexts/keycard/pin/events.cljs index eeba5ae4cf..66ee928dbf 100644 --- a/src/status_im/contexts/keycard/pin/events.cljs +++ b/src/status_im/contexts/keycard/pin/events.cljs @@ -9,13 +9,17 @@ (assoc-in [:keycard :pin :text] (.slice pin 0 -1)) (assoc-in [:keycard :pin :status] nil))})))) +(rf/reg-fx :effects.keycard.pin/dispatch-on-complete + (fn [[on-complete new-pin]] + (on-complete new-pin))) + (rf/reg-event-fx :keycard.pin/number-pressed - (fn [{:keys [db]} [number max-numbers on-complete-event]] + (fn [{:keys [db]} [number max-numbers on-complete]] (let [pin (get-in db [:keycard :pin :text]) new-pin (str pin number)] (when (<= (count new-pin) max-numbers) {:db (-> db (assoc-in [:keycard :pin :text] new-pin) (assoc-in [:keycard :pin :status] nil)) - :fx [(when (= (dec max-numbers) (count pin)) - [:dispatch [on-complete-event]])]})))) + :fx [(when (and on-complete (= (dec max-numbers) (count pin))) + [:effects.keycard.pin/dispatch-on-complete [on-complete new-pin]])]})))) diff --git a/src/status_im/contexts/keycard/pin/view.cljs b/src/status_im/contexts/keycard/pin/view.cljs index debc48cc66..09c1951854 100644 --- a/src/status_im/contexts/keycard/pin/view.cljs +++ b/src/status_im/contexts/keycard/pin/view.cljs @@ -6,7 +6,7 @@ [utils.re-frame :as rf])) (defn auth - [callback-event-key] + [{:keys [on-complete]}] (let [{:keys [text status]} (rf/sub [:keycard/pin]) pin-retry-counter (rf/sub [:keycard/pin-retry-counter]) error? (= status :error)] @@ -23,4 +23,4 @@ {:delete-key? true :on-delete #(rf/dispatch [:keycard.pin/delete-pressed]) :on-press #(rf/dispatch [:keycard.pin/number-pressed % constants/pincode-length - callback-event-key])}]])) + on-complete])}]])) diff --git a/src/status_im/contexts/keycard/sign/events.cljs b/src/status_im/contexts/keycard/sign/events.cljs new file mode 100644 index 0000000000..3dc5c6388b --- /dev/null +++ b/src/status_im/contexts/keycard/sign/events.cljs @@ -0,0 +1,29 @@ +(ns status-im.contexts.keycard.sign.events + (:require [utils.address] + [utils.re-frame :as rf])) + +(defn get-signature-map + [tx-hash signature] + {tx-hash {:r (subs signature 0 64) + :s (subs signature 64 128) + :v (str (js/parseInt (subs signature 128 130) 16))}}) + +(rf/reg-event-fx :keycard/sign-hash + (fn [{:keys [db]} [pin-text tx-hash]] + (let [current-address (get-in db [:wallet :current-viewing-account-address]) + path (get-in db [:wallet :accounts current-address :path]) + key-uid (get-in db [:profile/profile :key-uid])] + {:fx [[:dispatch + [:keycard/read-card + {:key-uid key-uid + :on-read-fx [[:effects.keycard/sign + {:pin pin-text + :path path + :hash (utils.address/naked-address tx-hash) + :on-success + #(do + (rf/dispatch [:keycard/hide-connection-sheet]) + (rf/dispatch + [:wallet/proceed-with-transactions-signatures + (get-signature-map tx-hash %)])) + :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]]]}))) diff --git a/src/status_im/contexts/keycard/utils.cljs b/src/status_im/contexts/keycard/utils.cljs index 968c2e8807..7d4da6422a 100644 --- a/src/status_im/contexts/keycard/utils.cljs +++ b/src/status_im/contexts/keycard/utils.cljs @@ -16,8 +16,8 @@ (re-matches #".*NFCError:100.*" error))) (defn validate-application-info - [profile {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}] - (let [profile-mismatch? (or (nil? profile) (not= (:key-uid profile) key-uid))] + [profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}] + (let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))] (log/debug "[keycard] login-with-keycard" "empty application info" (empty? application-info) "no key-uid" (empty? key-uid) diff --git a/src/status_im/contexts/profile/profiles/view.cljs b/src/status_im/contexts/profile/profiles/view.cljs index a844af0b52..1a8f506f11 100644 --- a/src/status_im/contexts/profile/profiles/view.cljs +++ b/src/status_im/contexts/profile/profiles/view.cljs @@ -239,7 +239,16 @@ :profile-picture profile-picture :card-style style/login-profile-card}] (if keycard-pairing - [keycard.pin/auth :keycard/read-card-and-login] + [keycard.pin/auth + {:on-complete + (fn [pin-text] + (rf/dispatch + [:keycard/read-card + {:key-uid key-uid + :on-read-fx [[:effects.keycard/get-keys + {:pin pin-text + :on-success #(rf/dispatch [:keycard.login/on-get-keys-success %]) + :on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}]))}] [password-input])] (when-not keycard-pairing [quo/button diff --git a/src/status_im/contexts/wallet/send/events.cljs b/src/status_im/contexts/wallet/send/events.cljs index 4965065c2d..c93e47a62e 100644 --- a/src/status_im/contexts/wallet/send/events.cljs +++ b/src/status_im/contexts/wallet/send/events.cljs @@ -698,8 +698,9 @@ {:json-rpc/call [{:method "wallet_createMultiTransaction" :params request-params :on-success (fn [result] - (rf/dispatch [:wallet/add-authorized-transaction result]) - (rf/dispatch [:hide-bottom-sheet])) + (when result + (rf/dispatch [:wallet/add-authorized-transaction result]) + (rf/dispatch [:hide-bottom-sheet]))) :on-error (fn [error] (log/error "failed to send transaction" {:event :wallet/send-transaction @@ -710,6 +711,23 @@ :type :negative :text (:message error)}]))}]}))) +(rf/reg-event-fx :wallet/proceed-with-transactions-signatures + (fn [_ [signatures]] + {:json-rpc/call [{:method "wallet_proceedWithTransactionsSignatures" + :params [signatures] + :on-success (fn [result] + (when result + (rf/dispatch [:wallet/add-authorized-transaction result]) + (rf/dispatch [:hide-bottom-sheet]))) + :on-error (fn [error] + (log/error "failed to proceed-with-transactions-signatures" + {:event :wallet/proceed-with-transactions-signatures + :error error}) + (rf/dispatch [:toasts/upsert + {:id :send-transaction-error + :type :negative + :text (:message error)}]))}]})) + (rf/reg-event-fx :wallet/select-from-account (fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]] diff --git a/src/status_im/contexts/wallet/send/transaction_confirmation/view.cljs b/src/status_im/contexts/wallet/send/transaction_confirmation/view.cljs index c35bd8984b..6ce1f42092 100644 --- a/src/status_im/contexts/wallet/send/transaction_confirmation/view.cljs +++ b/src/status_im/contexts/wallet/send/transaction_confirmation/view.cljs @@ -257,7 +257,8 @@ :transaction-type transaction-type}] (when (and (not loading-suggested-routes?) route (seq route)) [standard-auth/slide-button - {:size :size-48 + {:keycard-supported? true + :size :size-48 :track-text (if (= transaction-type :tx/bridge) (i18n/label :t/slide-to-bridge) (i18n/label :t/slide-to-send)) @@ -265,8 +266,7 @@ :customization-color account-color :on-auth-success #(rf/dispatch [:wallet/send-transaction - (security/safe-unmask-data - %)]) + (security/safe-unmask-data %)]) :auth-button-label (i18n/label :t/confirm)}])] :gradient-cover? true :customization-color (:color account)}