From ec15232af891181425f983bb65f97e39a11b4938 Mon Sep 17 00:00:00 2001 From: Siddarth Kumar Date: Wed, 4 Jan 2023 18:24:11 +0530 Subject: [PATCH] Make sure mobile understands community share link by Desktop (#14679) * we are now able to navigate to desktop communities https://github.com/status-im/status-go/compare/b4bdfd3d...d2e95eee In this commit we add support for various status-go methods in mobile : - `multiformatSerializePublicKey` - `multiformatDeserializePublicKey` - `compressPublicKey` - `decompressPublicKey` - and my personal favourite `deserializeAndCompressKey` When someone pastes a community joining share url into chat and taps on it, we check whether the community was generated on a desktop or mobile. We need to do this because currently both the clients have different urls for joining a community. Once we have identified that the url is generated via desktop we then convert that url to something that the mobile client is used to. This enables the mobile client to view this community and interact with it. However more work needs to be done in the future to ensure that mobile implements these compressed keys end to end, we need to get this feature in so that by the time RC1 is released mobile client is able to join communities created by Desktop. --- .../status/ethereum/module/StatusModule.java | 89 +++++++++++++++++++ .../ios/RCTStatus/RCTStatus.m | 32 +++++++ src/status_im/constants.cljs | 19 +++- src/status_im/native_module/core.cljs | 52 ++++++++++- src/status_im/router/core.cljs | 9 +- src/status_im/utils/universal_links/core.cljs | 20 ++++- status-go-version.json | 6 +- 7 files changed, 219 insertions(+), 8 deletions(-) diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index a4baf2d207..e1461a791a 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -985,6 +985,95 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL StatusThreadPoolExecutor.getInstance().execute(runnableTask); } + @ReactMethod + public void multiformatSerializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException { + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable runnableTask = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiformatSerializePublicKey(multiCodecKey,base58btc); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(runnableTask); + } + + @ReactMethod + public void multiformatDeserializePublicKey(final String multiCodecKey, final String base58btc, final Callback callback) throws JSONException { + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable runnableTask = new Runnable() { + @Override + public void run() { + String res = Statusgo.multiformatDeserializePublicKey(multiCodecKey,base58btc); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(runnableTask); + } + + @ReactMethod + public void compressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException { + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable runnableTask = new Runnable() { + @Override + public void run() { + String res = Statusgo.compressPublicKey(multiCodecKey); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(runnableTask); + } + + @ReactMethod + public void decompressPublicKey(final String multiCodecKey, final Callback callback) throws JSONException { + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable runnableTask = new Runnable() { + @Override + public void run() { + String res = Statusgo.decompressPublicKey(multiCodecKey); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(runnableTask); + } + + @ReactMethod + public void deserializeAndCompressKey(final String desktopKey, final Callback callback) throws JSONException { + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable runnableTask = new Runnable() { + @Override + public void run() { + String res = Statusgo.deserializeAndCompressKey(desktopKey); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(runnableTask); + } @ReactMethod public void hashTypedData(final String data, final Callback callback) { diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index 51e341db77..0558e10535 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -339,6 +339,38 @@ RCT_EXPORT_METHOD(inputConnectionStringForBootstrapping:(NSString *)cs callback(@[result]); } +RCT_EXPORT_METHOD(multiformatSerializePublicKey:(NSString *)multiCodecKey + base58btc:(NSString *)base58btc + callback:(RCTResponseSenderBlock)callback) { + NSString *result = StatusgoMultiformatSerializePublicKey(multiCodecKey,base58btc); + callback(@[result]); +} + +RCT_EXPORT_METHOD(multiformatDeserializePublicKey:(NSString *)multiCodecKey + base58btc:(NSString *)base58btc + callback:(RCTResponseSenderBlock)callback) { + NSString *result = StatusgoMultiformatDeserializePublicKey(multiCodecKey,base58btc); + callback(@[result]); +} + +RCT_EXPORT_METHOD(decompressPublicKey:(NSString *)multiCodecKey + callback:(RCTResponseSenderBlock)callback) { + NSString *result = StatusgoDecompressPublicKey(multiCodecKey); + callback(@[result]); +} + +RCT_EXPORT_METHOD(compressPublicKey:(NSString *)multiCodecKey + callback:(RCTResponseSenderBlock)callback) { + NSString *result = StatusgoCompressPublicKey(multiCodecKey); + callback(@[result]); +} + +RCT_EXPORT_METHOD(deserializeAndCompressKey:(NSString *)desktopKey + callback:(RCTResponseSenderBlock)callback) { + NSString *result = StatusgoDeserializeAndCompressKey(desktopKey); + callback(@[result]); +} + RCT_EXPORT_METHOD(hashTypedData:(NSString *)data callback:(RCTResponseSenderBlock)callback) { #if DEBUG diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index ce751e2e81..a08a5f756f 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -205,9 +205,26 @@ (def ^:const community-member-role-manage-users 2) (def ^:const community-member-role-moderator 3) -(def local-pairing-connection-string-identifier +(def ^:const local-pairing-connection-string-identifier "If any string begins with cs we know its a connection string. This is useful when we read QR codes we know it is a connection string if it begins with this identifier. An example of a connection string is -> cs2:5vd6J6:Jfc:27xMmHKEYwzRGXcvTtuiLZFfXscMx4Mz8d9wEHUxDj4p7:EG7Z13QScfWBJNJ5cprszzDQ5fBVsYMirXo8MaQFJvpF:3 " "cs") +(def ^:const serialization-key + "We pass this serialization key as a parameter to MultiformatSerializePublicKey + function at status-go, This key determines the output base of the serialization. + according to https://specs.status.im/spec/2#public-key-serialization we serialize + keys with base58btc encoding" + "z") + +(def ^:const deserialization-key + "We pass this deserialization key as a parameter to MultiformatDeserializePublicKey + function at status-go, This key determines the output base of the deserialization. + according to https://specs.status.im/spec/2#public-key-serialization we deserialize + keys with base16 hexadecimal encoding" + "f") + +(def ^:const multi-code-prefix + "We prefix our keys with 0xe701 prior to serialisation them" + "0xe701") diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index a7bd2cc49a..5e76cebd4d 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -5,7 +5,8 @@ [status-im.utils.platform :as platform] [status-im.utils.react-native :as react-native-utils] [status-im.utils.types :as types] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.constants :as constants])) (defn status [] @@ -278,6 +279,55 @@ :connection-string connection-string}) (.inputConnectionStringForBootstrapping ^js (status) connection-string config-json callback)) +(defn deserialize-and-compress-key + "Provides a community id (public key) to status-go which is first deserialized + and then compressed. Example input/output : + input key = zQ3shTAten2v9CwyQD1Kc7VXAqNPDcHZAMsfbLHCZEx6nFqk9 and + output key = 0x025596a7ff87da36860a84b0908191ce60a504afc94aac93c1abd774f182967ce6" + [key callback] + (log/info "[native-module] Deserializing and then compressing public key" + {:fn :deserialize-and-compress-key + :key key}) + (.deserializeAndCompressKey ^js (status) key callback)) + + +(defn public-key->compressed-key + "Provides public key to status-go and gets back a compressed key via serialization" + [public-key callback] + (let [serialization-key constants/serialization-key + multi-code-prefix constants/multi-code-prefix + multi-code-key (str multi-code-prefix (subs public-key 2))] + (log/info "[native-module] Serializing public key" + {:fn :public-key->compressed-key + :public-key public-key + :multi-code-key multi-code-key}) + (.multiformatSerializePublicKey ^js (status) multi-code-key serialization-key callback))) + +(defn compressed-key->public-key + "Provides compressed key to status-go and gets back the uncompressed public key via deserialization" + [public-key callback] + (let [deserialization-key constants/deserialization-key] + (log/info "[native-module] Deserializing compressed key" + {:fn :compressed-key->public-key + :public-key public-key}) + (.multiformatDeserializePublicKey ^js (status) public-key deserialization-key callback))) + +(defn decompress-public-key + "Provides compressed key to status-go and gets back the uncompressed public key" + [public-key callback] + (log/info "[native-module] Decompressing public key" + {:fn :decompress-public-key + :public-key public-key}) + (.decompressPublicKey ^js (status) public-key callback)) + +(defn compress-public-key + "Provides a public key to status-go and gets back a 33bit compressed key back" + [public-key callback] + (log/info "[native-module] Compressing public key" + {:fn :compress-public-key + :public-key public-key}) + (.compressPublicKey ^js (status) public-key callback)) + (defn hash-typed-data "used for keycard" [data callback] diff --git a/src/status_im/router/core.cljs b/src/status_im/router/core.cljs index 18965404ce..2db42f8fe4 100644 --- a/src/status_im/router/core.cljs +++ b/src/status_im/router/core.cljs @@ -199,6 +199,12 @@ {:type :wallet-account :account (when account (string/lower-case account))}) +(defn community-route-type + [route-params] + (if (string/starts-with? (:community-id route-params) "z") + :desktop-community + :community)) + (defn handle-uri [chain chats uri cb] (let [{:keys [handler route-params query-params]} (match-uri uri)] @@ -229,7 +235,8 @@ (cb {:type handler :community-id (:community-id route-params)}) (= handler :community) - (cb {:type handler :community-id (:community-id route-params)}) + (cb {:type (community-route-type route-params) + :community-id (:community-id route-params)}) (= handler :community-chat) (cb {:type handler :chat-id (:chat-id route-params)}) diff --git a/src/status_im/utils/universal_links/core.cljs b/src/status_im/utils/universal_links/core.cljs index b666523c05..5df16ea621 100644 --- a/src/status_im/utils/universal_links/core.cljs +++ b/src/status_im/utils/universal_links/core.cljs @@ -14,7 +14,8 @@ [utils.re-frame :as rf] [status-im.wallet.choose-recipient.core :as choose-recipient] [status-im2.navigation.events :as navigation] - [taoensso.timbre :as log])) + [taoensso.timbre :as log] + [status-im.native-module.core :as status])) ;; TODO(yenda) investigate why `handle-universal-link` event is ;; dispatched 7 times for the same link @@ -76,7 +77,21 @@ (rf/defn handle-community [cofx {:keys [community-id]}] (log/info "universal-links: handling community" community-id) - (navigation/navigate-to-cofx cofx :community {:community-id community-id})) + (navigation/navigate-to-cofx cofx :community {:community-id community-id}) +) + +(rf/defn handle-navigation-to-desktop-community-from-mobile + {:events [:handle-navigation-to-desktop-community-from-mobile]} + [{:keys [db]} cofx deserialized-key] + (navigation/navigate-to-cofx cofx :community {:community-id deserialized-key}) +) + +(rf/defn handle-desktop-community + [cofx {:keys [community-id]}] + (status/deserialize-and-compress-key + community-id + (fn [deserialized-key] + (rf/dispatch [:handle-navigation-to-desktop-community-from-mobile cofx (str deserialized-key)])))) (rf/defn handle-community-chat [cofx {:keys [chat-id]}] @@ -145,6 +160,7 @@ :private-chat (handle-private-chat cofx data) :community-requests (handle-community-requests cofx data) :community (handle-community cofx data) + :desktop-community (handle-desktop-community cofx data) :community-chat (handle-community-chat cofx data) :contact (handle-view-profile cofx data) :browser (handle-browse cofx data) diff --git a/status-go-version.json b/status-go-version.json index 9c9fd7fb22..938093d16c 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.117.1a", - "commit-sha1": "b4bdfd3df6cf5fb91ab2d0e9f3b38e8d1b9703e5", - "src-sha256": "1s50q53p1j5ysnbj88c52zb2rh1ms21pnf6bjc7wja8gzcpih2wl" + "version": "d43e06f4c27a900641cd55c2a46002e6f0909c95", + "commit-sha1": "d43e06f4c27a900641cd55c2a46002e6f0909c95", + "src-sha256": "18n5h3yx6wxb5l1nvra868dhnyl732zvpdn3mqg4zfsjxmx82np7" }