diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp index 3cc2ed881..a0ba261af 100644 --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.cpp @@ -1,250 +1,264 @@ #include "CommRustModule.h" #include "InternalModules/RustPromiseManager.h" #include "JSIRust.h" #include "lib.rs.h" #include namespace comm { using namespace facebook::react; CommRustModule::CommRustModule(std::shared_ptr jsInvoker) : CommRustModuleSchemaCxxSpecJSI(jsInvoker) { } jsi::Value CommRustModule::generateNonce(jsi::Runtime &rt) { return createPromiseAsJSIValue( rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityGenerateNonce(currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::registerUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) { return createPromiseAsJSIValue( rt, [this, &username, &password, &keyPayload, &keyPayloadSignature, &contentPrekey, &contentPrekeySignature, ¬ifPrekey, ¬ifPrekeySignature, &contentOneTimeKeys, ¬ifOneTimeKeys]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityRegisterUser( jsiStringToRustString(username, innerRt), jsiStringToRustString(password, innerRt), jsiStringToRustString(keyPayload, innerRt), jsiStringToRustString(keyPayloadSignature, innerRt), jsiStringToRustString(contentPrekey, innerRt), jsiStringToRustString(contentPrekeySignature, innerRt), jsiStringToRustString(notifPrekey, innerRt), jsiStringToRustString(notifPrekeySignature, innerRt), jsiStringArrayToRustVec(contentOneTimeKeys, innerRt), jsiStringArrayToRustVec(notifOneTimeKeys, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::loginPasswordUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) { return createPromiseAsJSIValue( rt, [this, &username, &password, &keyPayload, &keyPayloadSignature, &contentPrekey, &contentPrekeySignature, ¬ifPrekey, ¬ifPrekeySignature, &contentOneTimeKeys, ¬ifOneTimeKeys]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityLoginPasswordUser( jsiStringToRustString(username, innerRt), jsiStringToRustString(password, innerRt), jsiStringToRustString(keyPayload, innerRt), jsiStringToRustString(keyPayloadSignature, innerRt), jsiStringToRustString(contentPrekey, innerRt), jsiStringToRustString(contentPrekeySignature, innerRt), jsiStringToRustString(notifPrekey, innerRt), jsiStringToRustString(notifPrekeySignature, innerRt), jsiStringArrayToRustVec(contentOneTimeKeys, innerRt), jsiStringArrayToRustVec(notifOneTimeKeys, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::loginWalletUser( jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String socialProof) { return createPromiseAsJSIValue( rt, [this, &siweMessage, &siweSignature, &keyPayload, &keyPayloadSignature, &contentPrekey, &contentPrekeySignature, ¬ifPrekey, ¬ifPrekeySignature, &contentOneTimeKeys, ¬ifOneTimeKeys, &socialProof](jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityLoginWalletUser( jsiStringToRustString(siweMessage, innerRt), jsiStringToRustString(siweSignature, innerRt), jsiStringToRustString(keyPayload, innerRt), jsiStringToRustString(keyPayloadSignature, innerRt), jsiStringToRustString(contentPrekey, innerRt), jsiStringToRustString(contentPrekeySignature, innerRt), jsiStringToRustString(notifPrekey, innerRt), jsiStringToRustString(notifPrekeySignature, innerRt), jsiStringArrayToRustVec(contentOneTimeKeys, innerRt), jsiStringArrayToRustVec(notifOneTimeKeys, innerRt), jsiStringToRustString(socialProof, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::updatePassword( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) { return createPromiseAsJSIValue( rt, [this, &userID, &deviceID, &accessToken, &password]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityUpdateUserPassword( jsiStringToRustString(userID, innerRt), jsiStringToRustString(deviceID, innerRt), jsiStringToRustString(accessToken, innerRt), jsiStringToRustString(password, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::deleteUser( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) { return createPromiseAsJSIValue( rt, [this, &userID, &deviceID, &accessToken]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityDeleteUser( jsiStringToRustString(userID, innerRt), jsiStringToRustString(deviceID, innerRt), jsiStringToRustString(accessToken, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } jsi::Value CommRustModule::getOutboundKeysForUserDevice( jsi::Runtime &rt, jsi::String identifierType, jsi::String identifierValue, jsi::String deviceID) { return createPromiseAsJSIValue( rt, [this, &identifierType, &identifierValue, &deviceID]( jsi::Runtime &innerRt, std::shared_ptr promise) { std::string error; try { auto currentID = RustPromiseManager::instance.addPromise( promise, this->jsInvoker_, innerRt); identityGetOutboundKeysForUserDevice( jsiStringToRustString(identifierType, innerRt), jsiStringToRustString(identifierValue, innerRt), jsiStringToRustString(deviceID, innerRt), currentID); } catch (const std::exception &e) { error = e.what(); }; }); } +jsi::Value CommRustModule::versionSupported(jsi::Runtime &rt) { + return createPromiseAsJSIValue( + rt, [this](jsi::Runtime &innerRt, std::shared_ptr promise) { + std::string error; + try { + auto currentID = RustPromiseManager::instance.addPromise( + promise, this->jsInvoker_, innerRt); + identityVersionSupported(currentID); + } catch (const std::exception &e) { + error = e.what(); + }; + }); +} + } // namespace comm diff --git a/native/cpp/CommonCpp/NativeModules/CommRustModule.h b/native/cpp/CommonCpp/NativeModules/CommRustModule.h index dcf905c16..3c20842b2 100644 --- a/native/cpp/CommonCpp/NativeModules/CommRustModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommRustModule.h @@ -1,73 +1,74 @@ #pragma once #include "../_generated/rustJSI.h" #include #include #include namespace comm { namespace jsi = facebook::jsi; class CommRustModule : public facebook::react::CommRustModuleSchemaCxxSpecJSI { virtual jsi::Value generateNonce(jsi::Runtime &rt) override; virtual jsi::Value registerUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) override; virtual jsi::Value loginPasswordUser( jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) override; virtual jsi::Value loginWalletUser( jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String socialProof) override; virtual jsi::Value updatePassword( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) override; virtual jsi::Value deleteUser( jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) override; virtual jsi::Value getOutboundKeysForUserDevice( jsi::Runtime &rt, jsi::String identifierType, jsi::String identifierValue, jsi::String deviceID) override; + virtual jsi::Value versionSupported(jsi::Runtime &rt) override; public: CommRustModule(std::shared_ptr jsInvoker); }; } // namespace comm diff --git a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp index e14fefbf1..d84ceee48 100644 --- a/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/rustJSI-generated.cpp @@ -1,50 +1,54 @@ /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * * Do not edit this file as changes may cause incorrect behavior and will be lost * once the code is regenerated. * * @generated by codegen project: GenerateModuleH.js */ #include "rustJSI.h" namespace facebook { namespace react { static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_generateNonce(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->generateNonce(rt); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->registerUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_loginPasswordUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->loginPasswordUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_loginWalletUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->loginWalletUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt), args[5].asString(rt), args[6].asString(rt), args[7].asString(rt), args[8].asObject(rt).asArray(rt), args[9].asObject(rt).asArray(rt), args[10].asString(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_updatePassword(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->updatePassword(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_deleteUser(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->deleteUser(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_getOutboundKeysForUserDevice(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->getOutboundKeysForUserDevice(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); } +static jsi::Value __hostFunction_CommRustModuleSchemaCxxSpecJSI_versionSupported(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->versionSupported(rt); +} CommRustModuleSchemaCxxSpecJSI::CommRustModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule("CommRustTurboModule", jsInvoker) { methodMap_["generateNonce"] = MethodMetadata {0, __hostFunction_CommRustModuleSchemaCxxSpecJSI_generateNonce}; methodMap_["registerUser"] = MethodMetadata {10, __hostFunction_CommRustModuleSchemaCxxSpecJSI_registerUser}; methodMap_["loginPasswordUser"] = MethodMetadata {10, __hostFunction_CommRustModuleSchemaCxxSpecJSI_loginPasswordUser}; methodMap_["loginWalletUser"] = MethodMetadata {11, __hostFunction_CommRustModuleSchemaCxxSpecJSI_loginWalletUser}; methodMap_["updatePassword"] = MethodMetadata {4, __hostFunction_CommRustModuleSchemaCxxSpecJSI_updatePassword}; methodMap_["deleteUser"] = MethodMetadata {3, __hostFunction_CommRustModuleSchemaCxxSpecJSI_deleteUser}; methodMap_["getOutboundKeysForUserDevice"] = MethodMetadata {3, __hostFunction_CommRustModuleSchemaCxxSpecJSI_getOutboundKeysForUserDevice}; + methodMap_["versionSupported"] = MethodMetadata {0, __hostFunction_CommRustModuleSchemaCxxSpecJSI_versionSupported}; } } // namespace react } // namespace facebook diff --git a/native/cpp/CommonCpp/_generated/rustJSI.h b/native/cpp/CommonCpp/_generated/rustJSI.h index 438d40d1c..5a182b495 100644 --- a/native/cpp/CommonCpp/_generated/rustJSI.h +++ b/native/cpp/CommonCpp/_generated/rustJSI.h @@ -1,116 +1,125 @@ /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * * Do not edit this file as changes may cause incorrect behavior and will be lost * once the code is regenerated. * * @generated by codegen project: GenerateModuleH.js */ #pragma once #include #include namespace facebook { namespace react { class JSI_EXPORT CommRustModuleSchemaCxxSpecJSI : public TurboModule { protected: CommRustModuleSchemaCxxSpecJSI(std::shared_ptr jsInvoker); public: virtual jsi::Value generateNonce(jsi::Runtime &rt) = 0; virtual jsi::Value registerUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) = 0; virtual jsi::Value loginPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) = 0; virtual jsi::Value loginWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String socialProof) = 0; virtual jsi::Value updatePassword(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) = 0; virtual jsi::Value deleteUser(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) = 0; virtual jsi::Value getOutboundKeysForUserDevice(jsi::Runtime &rt, jsi::String identifierType, jsi::String identifierValue, jsi::String deviceID) = 0; + virtual jsi::Value versionSupported(jsi::Runtime &rt) = 0; }; template class JSI_EXPORT CommRustModuleSchemaCxxSpec : public TurboModule { public: jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { return delegate_.get(rt, propName); } protected: CommRustModuleSchemaCxxSpec(std::shared_ptr jsInvoker) : TurboModule("CommRustTurboModule", jsInvoker), delegate_(static_cast(this), jsInvoker) {} private: class Delegate : public CommRustModuleSchemaCxxSpecJSI { public: Delegate(T *instance, std::shared_ptr jsInvoker) : CommRustModuleSchemaCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {} jsi::Value generateNonce(jsi::Runtime &rt) override { static_assert( bridging::getParameterCount(&T::generateNonce) == 1, "Expected generateNonce(...) to have 1 parameters"); return bridging::callFromJs( rt, &T::generateNonce, jsInvoker_, instance_); } jsi::Value registerUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) override { static_assert( bridging::getParameterCount(&T::registerUser) == 11, "Expected registerUser(...) to have 11 parameters"); return bridging::callFromJs( rt, &T::registerUser, jsInvoker_, instance_, std::move(username), std::move(password), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys)); } jsi::Value loginPasswordUser(jsi::Runtime &rt, jsi::String username, jsi::String password, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys) override { static_assert( bridging::getParameterCount(&T::loginPasswordUser) == 11, "Expected loginPasswordUser(...) to have 11 parameters"); return bridging::callFromJs( rt, &T::loginPasswordUser, jsInvoker_, instance_, std::move(username), std::move(password), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys)); } jsi::Value loginWalletUser(jsi::Runtime &rt, jsi::String siweMessage, jsi::String siweSignature, jsi::String keyPayload, jsi::String keyPayloadSignature, jsi::String contentPrekey, jsi::String contentPrekeySignature, jsi::String notifPrekey, jsi::String notifPrekeySignature, jsi::Array contentOneTimeKeys, jsi::Array notifOneTimeKeys, jsi::String socialProof) override { static_assert( bridging::getParameterCount(&T::loginWalletUser) == 12, "Expected loginWalletUser(...) to have 12 parameters"); return bridging::callFromJs( rt, &T::loginWalletUser, jsInvoker_, instance_, std::move(siweMessage), std::move(siweSignature), std::move(keyPayload), std::move(keyPayloadSignature), std::move(contentPrekey), std::move(contentPrekeySignature), std::move(notifPrekey), std::move(notifPrekeySignature), std::move(contentOneTimeKeys), std::move(notifOneTimeKeys), std::move(socialProof)); } jsi::Value updatePassword(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken, jsi::String password) override { static_assert( bridging::getParameterCount(&T::updatePassword) == 5, "Expected updatePassword(...) to have 5 parameters"); return bridging::callFromJs( rt, &T::updatePassword, jsInvoker_, instance_, std::move(userID), std::move(deviceID), std::move(accessToken), std::move(password)); } jsi::Value deleteUser(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) override { static_assert( bridging::getParameterCount(&T::deleteUser) == 4, "Expected deleteUser(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::deleteUser, jsInvoker_, instance_, std::move(userID), std::move(deviceID), std::move(accessToken)); } jsi::Value getOutboundKeysForUserDevice(jsi::Runtime &rt, jsi::String identifierType, jsi::String identifierValue, jsi::String deviceID) override { static_assert( bridging::getParameterCount(&T::getOutboundKeysForUserDevice) == 4, "Expected getOutboundKeysForUserDevice(...) to have 4 parameters"); return bridging::callFromJs( rt, &T::getOutboundKeysForUserDevice, jsInvoker_, instance_, std::move(identifierType), std::move(identifierValue), std::move(deviceID)); } + jsi::Value versionSupported(jsi::Runtime &rt) override { + static_assert( + bridging::getParameterCount(&T::versionSupported) == 1, + "Expected versionSupported(...) to have 1 parameters"); + + return bridging::callFromJs( + rt, &T::versionSupported, jsInvoker_, instance_); + } private: T *instance_; }; Delegate delegate_; }; } // namespace react } // namespace facebook diff --git a/native/native_rust_library/RustCallback.cpp b/native/native_rust_library/RustCallback.cpp index a0b979fbe..e10e09a12 100644 --- a/native/native_rust_library/RustCallback.cpp +++ b/native/native_rust_library/RustCallback.cpp @@ -1,39 +1,54 @@ #include "RustCallback.h" #include "../cpp/CommonCpp/NativeModules/CommCoreModule.h" #include "../cpp/CommonCpp/NativeModules/InternalModules/RustPromiseManager.h" #include "../cpp/CommonCpp/Tools/Logger.h" #include #include #include namespace comm { void stringCallback(rust::String error, uint32_t promiseID, rust::String ret) { auto it = RustPromiseManager::instance.promises.find(promiseID); if (it == RustPromiseManager::instance.promises.end()) { return; } if (error.size()) { RustPromiseManager::instance.rejectPromise(promiseID, std::string(error)); } else { folly::dynamic retDyn; retDyn = std::string(ret); RustPromiseManager::instance.resolvePromise(promiseID, retDyn); } } void voidCallback(rust::String error, uint32_t promiseID) { auto it = RustPromiseManager::instance.promises.find(promiseID); if (it == RustPromiseManager::instance.promises.end()) { return; } if (error.size()) { RustPromiseManager::instance.rejectPromise(promiseID, std::string(error)); } else { RustPromiseManager::instance.resolvePromise(promiseID, folly::dynamic()); } } +void boolCallback(rust::String error, uint32_t promiseID, bool ret) { + auto it = RustPromiseManager::instance.promises.find(promiseID); + if (it == RustPromiseManager::instance.promises.end()) { + return; + } + + if (error.size()) { + RustPromiseManager::instance.rejectPromise(promiseID, std::string(error)); + } else { + folly::dynamic retDyn; + retDyn = ret; + RustPromiseManager::instance.resolvePromise(promiseID, retDyn); + } +} + } // namespace comm diff --git a/native/native_rust_library/RustCallback.h b/native/native_rust_library/RustCallback.h index 6d69d3a0f..4cf24ec12 100644 --- a/native/native_rust_library/RustCallback.h +++ b/native/native_rust_library/RustCallback.h @@ -1,10 +1,11 @@ #pragma once #include "cxx.h" namespace comm { void stringCallback(rust::String error, uint32_t promiseID, rust::String ret); void voidCallback(rust::String error, uint32_t promiseID); +void boolCallback(rust::String error, uint32_t promiseID, bool ret); } // namespace comm diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs index 92af62bb8..5c927853b 100644 --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -1,743 +1,787 @@ -use crate::ffi::{string_callback, void_callback}; +use crate::ffi::{bool_callback, string_callback, void_callback}; use comm_opaque2::client::{Login, Registration}; use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error; use grpc_clients::identity::get_unauthenticated_client; use grpc_clients::identity::protos::client::{ outbound_keys_for_user_request::Identifier, DeleteUserRequest, DeviceKeyUpload, DeviceType, Empty, IdentityKeyInfo, OpaqueLoginFinishRequest, OpaqueLoginStartRequest, OutboundKeyInfo, OutboundKeysForUserRequest, PreKey, RegistrationFinishRequest, RegistrationStartRequest, UpdateUserPasswordFinishRequest, UpdateUserPasswordStartRequest, WalletLoginRequest, }; use lazy_static::lazy_static; use serde::Serialize; use std::sync::Arc; use tokio::runtime::{Builder, Runtime}; use tonic::Status; use tracing::instrument; mod argon2_tools; use argon2_tools::compute_backup_key; mod generated { // We get the CODE_VERSION from this generated file include!(concat!(env!("OUT_DIR"), "/version.rs")); } pub use generated::CODE_VERSION; #[cfg(not(feature = "android"))] pub const DEVICE_TYPE: DeviceType = DeviceType::Ios; #[cfg(feature = "android")] pub const DEVICE_TYPE: DeviceType = DeviceType::Android; lazy_static! { pub static ref RUNTIME: Arc = Arc::new( Builder::new_multi_thread() .worker_threads(1) .max_blocking_threads(1) .enable_all() .build() .unwrap() ); } #[cxx::bridge] mod ffi { extern "Rust" { #[cxx_name = "identityRegisterUser"] fn register_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityLoginPasswordUser"] fn login_password_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ); #[cxx_name = "identityLoginWalletUser"] fn login_wallet_user( siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, social_proof: String, promise_id: u32, ); #[cxx_name = "identityUpdateUserPassword"] fn update_user_password( user_id: String, device_id: String, access_token: String, password: String, promise_id: u32, ); #[cxx_name = "identityDeleteUser"] fn delete_user( user_id: String, device_id: String, access_token: String, promise_id: u32, ); #[cxx_name = "identityGetOutboundKeysForUserDevice"] fn get_outbound_keys_for_user_device( identifier_type: String, identifier_value: String, device_id: String, promise_id: u32, ); #[cxx_name = "identityGenerateNonce"] fn generate_nonce(promise_id: u32); + #[cxx_name = "identityVersionSupported"] + fn version_supported(promise_id: u32); + // Argon2 fn compute_backup_key(password: &str, backup_id: &str) -> Result<[u8; 32]>; } unsafe extern "C++" { include!("RustCallback.h"); #[namespace = "comm"] #[cxx_name = "stringCallback"] fn string_callback(error: String, promise_id: u32, ret: String); #[namespace = "comm"] #[cxx_name = "voidCallback"] fn void_callback(error: String, promise_id: u32); + + #[namespace = "comm"] + #[cxx_name = "boolCallback"] + fn bool_callback(error: String, promise_id: u32, ret: bool); } } fn handle_string_result_as_callback( result: Result, promise_id: u32, ) where E: std::fmt::Display, { match result { Err(e) => string_callback(e.to_string(), promise_id, "".to_string()), Ok(r) => string_callback("".to_string(), promise_id, r), } } fn handle_void_result_as_callback(result: Result<(), E>, promise_id: u32) where E: std::fmt::Display, { match result { Err(e) => void_callback(e.to_string(), promise_id), Ok(_) => void_callback("".to_string(), promise_id), } } +fn handle_bool_result_as_callback(result: Result, promise_id: u32) +where + E: std::fmt::Display, +{ + match result { + Err(e) => bool_callback(e.to_string(), promise_id, false), + Ok(r) => bool_callback("".to_string(), promise_id, r), + } +} + fn generate_nonce(promise_id: u32) { RUNTIME.spawn(async move { let result = fetch_nonce().await; handle_string_result_as_callback(result, promise_id); }); } async fn fetch_nonce() -> Result { let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let nonce = identity_client .generate_nonce(Empty {}) .await? .into_inner() .nonce; Ok(nonce) } +fn version_supported(promise_id: u32) { + RUNTIME.spawn(async move { + let result = version_supported_helper().await; + handle_bool_result_as_callback(result, promise_id); + }); +} + +async fn version_supported_helper() -> Result { + let mut identity_client = get_unauthenticated_client( + "http://127.0.0.1:50054", + CODE_VERSION, + DEVICE_TYPE.as_str_name().to_lowercase(), + ) + .await?; + let response = identity_client.ping(Empty {}).await; + match response { + Ok(_) => Ok(true), + Err(e) => { + if grpc_clients::error::is_version_unsupported(&e) { + Ok(false) + } else { + Err(e.into()) + } + } + } +} + struct AuthInfo { user_id: String, device_id: String, access_token: String, } #[instrument] fn register_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ) { RUNTIME.spawn(async move { let password_user_info = PasswordUserInfo { username, password, key_payload, key_payload_signature, content_prekey, content_prekey_signature, notif_prekey, notif_prekey_signature, content_one_time_keys, notif_one_time_keys, }; let result = register_user_helper(password_user_info).await; handle_string_result_as_callback(result, promise_id); }); } struct PasswordUserInfo { username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, } #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct UserIDAndDeviceAccessToken { #[serde(rename = "userID")] user_id: String, access_token: String, } async fn register_user_helper( password_user_info: PasswordUserInfo, ) -> Result { let mut client_registration = Registration::new(); let opaque_registration_request = client_registration .start(&password_user_info.password) .map_err(handle_error)?; let registration_start_request = RegistrationStartRequest { opaque_registration_request, username: password_user_info.username, device_key_upload: Some(DeviceKeyUpload { device_key_info: Some(IdentityKeyInfo { payload: password_user_info.key_payload, payload_signature: password_user_info.key_payload_signature, social_proof: None, }), content_upload: Some(PreKey { pre_key: password_user_info.content_prekey, pre_key_signature: password_user_info.content_prekey_signature, }), notif_upload: Some(PreKey { pre_key: password_user_info.notif_prekey, pre_key_signature: password_user_info.notif_prekey_signature, }), one_time_content_prekeys: password_user_info.content_one_time_keys, one_time_notif_prekeys: password_user_info.notif_one_time_keys, device_type: DEVICE_TYPE.into(), }), }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let registration_start_response = identity_client .register_password_user_start(registration_start_request) .await? .into_inner(); let opaque_registration_upload = client_registration .finish( &password_user_info.password, ®istration_start_response.opaque_registration_response, ) .map_err(handle_error)?; let registration_finish_request = RegistrationFinishRequest { session_id: registration_start_response.session_id, opaque_registration_upload, }; let registration_finish_response = identity_client .register_password_user_finish(registration_finish_request) .await? .into_inner(); let user_id_and_access_token = UserIDAndDeviceAccessToken { user_id: registration_finish_response.user_id, access_token: registration_finish_response.access_token, }; Ok(serde_json::to_string(&user_id_and_access_token)?) } #[instrument] fn login_password_user( username: String, password: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, promise_id: u32, ) { RUNTIME.spawn(async move { let password_user_info = PasswordUserInfo { username, password, key_payload, key_payload_signature, content_prekey, content_prekey_signature, notif_prekey, notif_prekey_signature, content_one_time_keys, notif_one_time_keys, }; let result = login_password_user_helper(password_user_info).await; handle_string_result_as_callback(result, promise_id); }); } async fn login_password_user_helper( password_user_info: PasswordUserInfo, ) -> Result { let mut client_login = Login::new(); let opaque_login_request = client_login .start(&password_user_info.password) .map_err(handle_error)?; let login_start_request = OpaqueLoginStartRequest { opaque_login_request, username: password_user_info.username, device_key_upload: Some(DeviceKeyUpload { device_key_info: Some(IdentityKeyInfo { payload: password_user_info.key_payload, payload_signature: password_user_info.key_payload_signature, social_proof: None, }), content_upload: Some(PreKey { pre_key: password_user_info.content_prekey, pre_key_signature: password_user_info.content_prekey_signature, }), notif_upload: Some(PreKey { pre_key: password_user_info.notif_prekey, pre_key_signature: password_user_info.notif_prekey_signature, }), one_time_content_prekeys: password_user_info.content_one_time_keys, one_time_notif_prekeys: password_user_info.notif_one_time_keys, device_type: DEVICE_TYPE.into(), }), }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let login_start_response = identity_client .login_password_user_start(login_start_request) .await? .into_inner(); let opaque_login_upload = client_login .finish(&login_start_response.opaque_login_response) .map_err(handle_error)?; let login_finish_request = OpaqueLoginFinishRequest { session_id: login_start_response.session_id, opaque_login_upload, }; let login_finish_response = identity_client .login_password_user_finish(login_finish_request) .await? .into_inner(); let user_id_and_access_token = UserIDAndDeviceAccessToken { user_id: login_finish_response.user_id, access_token: login_finish_response.access_token, }; Ok(serde_json::to_string(&user_id_and_access_token)?) } struct WalletUserInfo { siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, social_proof: String, } #[instrument] fn login_wallet_user( siwe_message: String, siwe_signature: String, key_payload: String, key_payload_signature: String, content_prekey: String, content_prekey_signature: String, notif_prekey: String, notif_prekey_signature: String, content_one_time_keys: Vec, notif_one_time_keys: Vec, social_proof: String, promise_id: u32, ) { RUNTIME.spawn(async move { let wallet_user_info = WalletUserInfo { siwe_message, siwe_signature, key_payload, key_payload_signature, content_prekey, content_prekey_signature, notif_prekey, notif_prekey_signature, content_one_time_keys, notif_one_time_keys, social_proof, }; let result = login_wallet_user_helper(wallet_user_info).await; handle_string_result_as_callback(result, promise_id); }); } async fn login_wallet_user_helper( wallet_user_info: WalletUserInfo, ) -> Result { let login_request = WalletLoginRequest { siwe_message: wallet_user_info.siwe_message, siwe_signature: wallet_user_info.siwe_signature, device_key_upload: Some(DeviceKeyUpload { device_key_info: Some(IdentityKeyInfo { payload: wallet_user_info.key_payload, payload_signature: wallet_user_info.key_payload_signature, social_proof: Some(wallet_user_info.social_proof), }), content_upload: Some(PreKey { pre_key: wallet_user_info.content_prekey, pre_key_signature: wallet_user_info.content_prekey_signature, }), notif_upload: Some(PreKey { pre_key: wallet_user_info.notif_prekey, pre_key_signature: wallet_user_info.notif_prekey_signature, }), one_time_content_prekeys: wallet_user_info.content_one_time_keys, one_time_notif_prekeys: wallet_user_info.notif_one_time_keys, device_type: DEVICE_TYPE.into(), }), }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let login_response = identity_client .login_wallet_user(login_request) .await? .into_inner(); let user_id_and_access_token = UserIDAndDeviceAccessToken { user_id: login_response.user_id, access_token: login_response.access_token, }; Ok(serde_json::to_string(&user_id_and_access_token)?) } struct UpdatePasswordInfo { user_id: String, device_id: String, access_token: String, password: String, } fn update_user_password( user_id: String, device_id: String, access_token: String, password: String, promise_id: u32, ) { RUNTIME.spawn(async move { let update_password_info = UpdatePasswordInfo { access_token, user_id, device_id, password, }; let result = update_user_password_helper(update_password_info).await; handle_void_result_as_callback(result, promise_id); }); } async fn update_user_password_helper( update_password_info: UpdatePasswordInfo, ) -> Result<(), Error> { let mut client_registration = Registration::new(); let opaque_registration_request = client_registration .start(&update_password_info.password) .map_err(handle_error)?; let update_password_start_request = UpdateUserPasswordStartRequest { opaque_registration_request, access_token: update_password_info.access_token, user_id: update_password_info.user_id, device_id_key: update_password_info.device_id, }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let update_password_start_response = identity_client .update_user_password_start(update_password_start_request) .await? .into_inner(); let opaque_registration_upload = client_registration .finish( &update_password_info.password, &update_password_start_response.opaque_registration_response, ) .map_err(handle_error)?; let update_password_finish_request = UpdateUserPasswordFinishRequest { session_id: update_password_start_response.session_id, opaque_registration_upload, }; identity_client .update_user_password_finish(update_password_finish_request) .await?; Ok(()) } fn delete_user( user_id: String, device_id: String, access_token: String, promise_id: u32, ) { RUNTIME.spawn(async move { let auth_info = AuthInfo { access_token, user_id, device_id, }; let result = delete_user_helper(auth_info).await; handle_void_result_as_callback(result, promise_id); }); } async fn delete_user_helper(auth_info: AuthInfo) -> Result<(), Error> { let delete_user_request = DeleteUserRequest { access_token: auth_info.access_token, user_id: auth_info.user_id, device_id_key: auth_info.device_id, }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; identity_client.delete_user(delete_user_request).await?; Ok(()) } struct GetOutboundKeysRequestInfo { identifier_type: String, identifier_value: String, device_id: String, } // This struct should not be altered without also updating // OutboundKeyInfoResponse in lib/types/identity-service-types.js #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct OutboundKeyInfoResponse { pub payload: String, pub payload_signature: String, pub social_proof: Option, pub content_prekey: String, pub content_prekey_signature: String, pub notif_prekey: String, pub notif_prekey_signature: String, pub one_time_content_prekey: Option, pub one_time_notif_prekey: Option, } impl TryFrom for OutboundKeyInfoResponse { type Error = Error; fn try_from(key_info: OutboundKeyInfo) -> Result { let identity_info = key_info.identity_info.ok_or(Error::MissingResponseData)?; let IdentityKeyInfo { payload, payload_signature, social_proof, } = identity_info; let content_prekey = key_info.content_prekey.ok_or(Error::MissingResponseData)?; let PreKey { pre_key: content_prekey_value, pre_key_signature: content_prekey_signature, } = content_prekey; let notif_prekey = key_info.notif_prekey.ok_or(Error::MissingResponseData)?; let PreKey { pre_key: notif_prekey_value, pre_key_signature: notif_prekey_signature, } = notif_prekey; let one_time_content_prekey = key_info.one_time_content_prekey; let one_time_notif_prekey = key_info.one_time_notif_prekey; Ok(Self { payload, payload_signature, social_proof, content_prekey: content_prekey_value, content_prekey_signature, notif_prekey: notif_prekey_value, notif_prekey_signature, one_time_content_prekey, one_time_notif_prekey, }) } } fn get_outbound_keys_for_user_device( identifier_type: String, identifier_value: String, device_id: String, promise_id: u32, ) { RUNTIME.spawn(async move { let get_outbound_keys_request_info = GetOutboundKeysRequestInfo { identifier_type, identifier_value, device_id, }; let result = get_outbound_keys_for_user_device_helper(get_outbound_keys_request_info) .await; handle_string_result_as_callback(result, promise_id); }); } async fn get_outbound_keys_for_user_device_helper( get_outbound_keys_request_info: GetOutboundKeysRequestInfo, ) -> Result { let identifier = match get_outbound_keys_request_info.identifier_type.as_str() { "walletAddress" => Some(Identifier::WalletAddress( get_outbound_keys_request_info.identifier_value, )), "username" => Some(Identifier::Username( get_outbound_keys_request_info.identifier_value, )), _ => { return Err(Error::TonicGRPC(tonic::Status::invalid_argument( "invalid identifier", ))) } }; let mut identity_client = get_unauthenticated_client( "http://127.0.0.1:50054", CODE_VERSION, DEVICE_TYPE.as_str_name().to_lowercase(), ) .await?; let mut response = identity_client .get_outbound_keys_for_user(OutboundKeysForUserRequest { identifier }) .await? .into_inner(); let outbound_key_info = OutboundKeyInfoResponse::try_from( response .devices .remove(&get_outbound_keys_request_info.device_id) .ok_or(Error::MissingResponseData)?, )?; Ok(serde_json::to_string(&outbound_key_info)?) } #[derive( Debug, derive_more::Display, derive_more::From, derive_more::Error, )] pub enum Error { #[display(...)] TonicGRPC(Status), #[display(...)] SerdeJson(serde_json::Error), #[display(...)] MissingResponseData, GRPClient(grpc_clients::error::Error), } #[cfg(test)] mod tests { use super::CODE_VERSION; #[test] fn test_code_version_exists() { assert!(CODE_VERSION > 0); } } diff --git a/native/schema/CommRustModuleSchema.js b/native/schema/CommRustModuleSchema.js index ba11668b0..09d77ac96 100644 --- a/native/schema/CommRustModuleSchema.js +++ b/native/schema/CommRustModuleSchema.js @@ -1,67 +1,68 @@ // @flow 'use strict'; import { TurboModuleRegistry } from 'react-native'; import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport.js'; export interface Spec extends TurboModule { +generateNonce: () => Promise; +registerUser: ( username: string, password: string, keyPayload: string, keyPayloadSignature: string, contentPrekey: string, contentPrekeySignature: string, notifPrekey: string, notifPrekeySignature: string, contentOneTimeKeys: $ReadOnlyArray, notifOneTimeKeys: $ReadOnlyArray, ) => Promise; +loginPasswordUser: ( username: string, password: string, keyPayload: string, keyPayloadSignature: string, contentPrekey: string, contentPrekeySignature: string, notifPrekey: string, notifPrekeySignature: string, contentOneTimeKeys: $ReadOnlyArray, notifOneTimeKeys: $ReadOnlyArray, ) => Promise; +loginWalletUser: ( siweMessage: string, siweSignature: string, keyPayload: string, keyPayloadSignature: string, contentPrekey: string, contentPrekeySignature: string, notifPrekey: string, notifPrekeySignature: string, contentOneTimeKeys: $ReadOnlyArray, notifOneTimeKeys: $ReadOnlyArray, socialProof: string, ) => Promise; +updatePassword: ( userID: string, deviceID: string, accessToken: string, password: string, ) => Promise; +deleteUser: ( userID: string, deviceID: string, accessToken: string, ) => Promise; +getOutboundKeysForUserDevice: ( identifierType: string, identifierValue: string, deviceID: string, ) => Promise; + +versionSupported: () => Promise; } export default (TurboModuleRegistry.getEnforcing( 'CommRustTurboModule', ): Spec); diff --git a/shared/grpc_clients/src/error.rs b/shared/grpc_clients/src/error.rs index deaae195e..a462ed970 100644 --- a/shared/grpc_clients/src/error.rs +++ b/shared/grpc_clients/src/error.rs @@ -1,24 +1,24 @@ use tonic::{codegen::http::uri::InvalidUri, Code, Status}; #[derive( Debug, derive_more::Display, derive_more::From, derive_more::Error, )] pub enum Error { #[display(fmt = "Transport Error: {:?}", _0)] TransportError(tonic::transport::Error), #[display(fmt = "Invalid Uri: {:?}", _0)] InvalidUri(InvalidUri), #[display(fmt = "Grpc Status: {:?}", _0)] GrpcStatus(Status), #[display(fmt = "Invalid Device Type")] InvalidDeviceType, } pub fn unsupported_version() -> Status { Status::unimplemented("Unsupported version") } -pub fn is_version_unsupported(status: Status) -> bool { +pub fn is_version_unsupported(status: &Status) -> bool { status.code() == Code::Unimplemented && status.message() == "Unsupported version" }