diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js --- a/native/backup/use-client-backup.js +++ b/native/backup/use-client-backup.js @@ -88,12 +88,10 @@ await setMockCommServicesAuthMetadata(); const backupSecret = await getBackupSecret(); - await commCoreModule.restoreBackup(backupSecret); - - const backupVersion = await commCoreModule.getSyncedDatabaseVersion(); - if (!backupVersion || parseInt(backupVersion) > persistConfig.version) { - throw new Error(`Incompatible backup version ${backupVersion ?? -1}`); - } + await commCoreModule.restoreBackup( + backupSecret, + persistConfig.version.toString(), + ); console.info('Backup restored.'); }, [currentUserID, loggedIn, setMockCommServicesAuthMetadata]); diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h @@ -132,7 +132,8 @@ virtual std::string getMetadata(std::string entry_name) const = 0; virtual void restoreFromMainCompaction( std::string mainCompactionPath, - std::string mainCompactionEncryptionKey) const = 0; + std::string mainCompactionEncryptionKey, + std::string maxVersion) const = 0; virtual void restoreFromBackupLog(const std::vector &backupLog) const = 0; virtual void addMessagesToDevice( diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -39,6 +39,8 @@ static SQLiteConnectionManager connectionManager; #endif + int getSyncedDatabaseVersion(sqlite3 *db) const; + public: static std::string sqliteFilePath; static std::string encryptionKey; @@ -142,7 +144,8 @@ std::string getMetadata(std::string entry_name) const override; void restoreFromMainCompaction( std::string mainCompactionPath, - std::string mainCompactionEncryptionKey) const override; + std::string mainCompactionEncryptionKey, + std::string maxVersion) const override; void restoreFromBackupLog( const std::vector &backupLog) const override; void addMessagesToDevice( diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -1842,6 +1842,19 @@ SQLiteQueryExecutor::getConnection(), getAllSyncedMetadataSQL); } +int SQLiteQueryExecutor::getSyncedDatabaseVersion(sqlite3 *db) const { + static std::string getDBVersionSyncedMetadataSQL = + "SELECT * " + "FROM synced_metadata " + "WHERE name=\"db_version\";"; + std::vector entries = + getAllEntities(db, getDBVersionSyncedMetadataSQL); + for (auto &entry : entries) { + return std::stoi(entry.data); + } + return -1; +} + void SQLiteQueryExecutor::replaceAuxUserInfo( const AuxUserInfo &aux_user_info) const { static std::string replaceAuxUserInfoSQL = @@ -2441,7 +2454,8 @@ void SQLiteQueryExecutor::restoreFromMainCompaction( std::string mainCompactionPath, - std::string mainCompactionEncryptionKey) const { + std::string mainCompactionEncryptionKey, + std::string maxVersion) const { if (!file_exists(mainCompactionPath)) { throw std::runtime_error("Restore attempt but backup file does not exist."); @@ -2501,6 +2515,17 @@ set_encryption_key(backupDB, mainCompactionEncryptionKey); #endif + int version = this->getSyncedDatabaseVersion(backupDB); + if (version > std::stoi(maxVersion)) { + std::stringstream error_message; + error_message << "Failed to restore a backup because it was created " + << "with version " << version + << " that is newer than the max supported version " + << maxVersion << std::endl; + sqlite3_close(backupDB); + throw std::runtime_error(error_message.str()); + } + sqlite3_backup *backupObj = sqlite3_backup_init( SQLiteQueryExecutor::getConnection(), "main", backupDB, "main"); if (!backupObj) { diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h @@ -182,13 +182,16 @@ jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) override; - virtual jsi::Value - restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override; + virtual jsi::Value restoreBackup( + jsi::Runtime &rt, + jsi::String backupSecret, + jsi::String maxVersion) override; virtual jsi::Value restoreBackupData( jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, - jsi::String backupLogDataKey) override; + jsi::String backupLogDataKey, + jsi::String maxVersion) override; virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override; virtual jsi::Value setSIWEBackupSecrets( diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -2012,14 +2012,20 @@ return createNewBackupInternal(rt, backupSecretStr, siweBackupMsgStr); } -jsi::Value -CommCoreModule::restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) { +jsi::Value CommCoreModule::restoreBackup( + jsi::Runtime &rt, + jsi::String backupSecret, + jsi::String maxVersion) { std::string backupSecretStr = backupSecret.utf8(rt); + std::string maxVersionStr = maxVersion.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( {promise, this->jsInvoker_, innerRt}); - ::restoreBackup(rust::string(backupSecretStr), currentID); + ::restoreBackup( + rust::string(backupSecretStr), + rust::string(maxVersionStr), + currentID); }); } @@ -2027,10 +2033,12 @@ jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, - jsi::String backupLogDataKey) { + jsi::String backupLogDataKey, + jsi::String maxVersion) { std::string backupIDStr = backupID.utf8(rt); std::string backupDataKeyStr = backupDataKey.utf8(rt); std::string backupLogDataKeyStr = backupLogDataKey.utf8(rt); + std::string maxVersionStr = maxVersion.utf8(rt); return createPromiseAsJSIValue( rt, [=](jsi::Runtime &innerRt, std::shared_ptr promise) { auto currentID = RustPromiseManager::instance.addPromise( @@ -2039,6 +2047,7 @@ rust::string(backupIDStr), rust::string(backupDataKeyStr), rust::string(backupLogDataKeyStr), + rust::string(maxVersionStr), currentID); }); } diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.h @@ -10,6 +10,7 @@ static void restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey, + std::string maxVersion, size_t futureID); static void restoreFromBackupLog( const std::vector &backupLog, diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp --- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp +++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/BackupOperationsUtilities/BackupOperationsExecutor.cpp @@ -26,11 +26,15 @@ void BackupOperationsExecutor::restoreFromMainCompaction( std::string mainCompactionPath, std::string mainCompactionEncryptionKey, + std::string maxVersion, size_t futureID) { - taskType job = [mainCompactionPath, mainCompactionEncryptionKey, futureID]() { + taskType job = [mainCompactionPath, + mainCompactionEncryptionKey, + maxVersion, + futureID]() { try { DatabaseManager::getQueryExecutor().restoreFromMainCompaction( - mainCompactionPath, mainCompactionEncryptionKey); + mainCompactionPath, mainCompactionEncryptionKey, maxVersion); ::resolveUnitFuture(futureID); } catch (const std::exception &e) { std::string errorDetails = std::string(e.what()); diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp --- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp +++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp @@ -164,10 +164,10 @@ return static_cast(&turboModule)->createNewSIWEBackup(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->restoreBackup(rt, args[0].asString(rt)); + return static_cast(&turboModule)->restoreBackup(rt, args[0].asString(rt), args[1].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { - return static_cast(&turboModule)->restoreBackupData(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt)); + return static_cast(&turboModule)->restoreBackupData(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt)); } static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { return static_cast(&turboModule)->retrieveBackupKeys(rt, args[0].asString(rt)); @@ -241,8 +241,8 @@ methodMap_["stopBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler}; methodMap_["createNewBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup}; methodMap_["createNewSIWEBackup"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewSIWEBackup}; - methodMap_["restoreBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup}; - methodMap_["restoreBackupData"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData}; + methodMap_["restoreBackup"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup}; + methodMap_["restoreBackupData"] = MethodMetadata {4, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData}; methodMap_["retrieveBackupKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys}; methodMap_["retrieveLatestSIWEBackupData"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveLatestSIWEBackupData}; methodMap_["setSIWEBackupSecrets"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setSIWEBackupSecrets}; diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h --- a/native/cpp/CommonCpp/_generated/commJSI.h +++ b/native/cpp/CommonCpp/_generated/commJSI.h @@ -68,8 +68,8 @@ virtual void stopBackupHandler(jsi::Runtime &rt) = 0; virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value createNewSIWEBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) = 0; - virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0; - virtual jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) = 0; + virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String maxVersion) = 0; + virtual jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey, jsi::String maxVersion) = 0; virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) = 0; virtual jsi::Value retrieveLatestSIWEBackupData(jsi::Runtime &rt) = 0; virtual jsi::Value setSIWEBackupSecrets(jsi::Runtime &rt, jsi::Object siweBackupSecrets) = 0; @@ -482,21 +482,21 @@ return bridging::callFromJs( rt, &T::createNewSIWEBackup, jsInvoker_, instance_, std::move(backupSecret), std::move(siweBackupMsg)); } - jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override { + jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String maxVersion) override { static_assert( - bridging::getParameterCount(&T::restoreBackup) == 2, - "Expected restoreBackup(...) to have 2 parameters"); + bridging::getParameterCount(&T::restoreBackup) == 3, + "Expected restoreBackup(...) to have 3 parameters"); return bridging::callFromJs( - rt, &T::restoreBackup, jsInvoker_, instance_, std::move(backupSecret)); + rt, &T::restoreBackup, jsInvoker_, instance_, std::move(backupSecret), std::move(maxVersion)); } - jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) override { + jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey, jsi::String maxVersion) override { static_assert( - bridging::getParameterCount(&T::restoreBackupData) == 4, - "Expected restoreBackupData(...) to have 4 parameters"); + bridging::getParameterCount(&T::restoreBackupData) == 5, + "Expected restoreBackupData(...) to have 5 parameters"); return bridging::callFromJs( - rt, &T::restoreBackupData, jsInvoker_, instance_, std::move(backupID), std::move(backupDataKey), std::move(backupLogDataKey)); + rt, &T::restoreBackupData, jsInvoker_, instance_, std::move(backupID), std::move(backupDataKey), std::move(backupLogDataKey), std::move(maxVersion)); } jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override { static_assert( diff --git a/native/native_rust_library/RustBackupExecutor.h b/native/native_rust_library/RustBackupExecutor.h --- a/native/native_rust_library/RustBackupExecutor.h +++ b/native/native_rust_library/RustBackupExecutor.h @@ -14,6 +14,7 @@ void restoreFromMainCompaction( rust::Str mainCompactionPath, rust::Str mainCompactionEncryptionKey, + rust::Str maxVersion, size_t futureID); void restoreFromBackupLog(rust::Vec backupLog, size_t futureID); diff --git a/native/native_rust_library/RustBackupExecutor.cpp b/native/native_rust_library/RustBackupExecutor.cpp --- a/native/native_rust_library/RustBackupExecutor.cpp +++ b/native/native_rust_library/RustBackupExecutor.cpp @@ -39,10 +39,12 @@ void restoreFromMainCompaction( rust::Str mainCompactionPath, rust::Str mainCompactionEncryptionKey, + rust::Str maxVersion, size_t futureID) { BackupOperationsExecutor::restoreFromMainCompaction( std::string(mainCompactionPath), std::string(mainCompactionEncryptionKey), + std::string(maxVersion), futureID); } diff --git a/native/native_rust_library/src/backup.rs b/native/native_rust_library/src/backup.rs --- a/native/native_rust_library/src/backup.rs +++ b/native/native_rust_library/src/backup.rs @@ -75,7 +75,11 @@ }); } - pub fn restore_backup(backup_secret: String, promise_id: u32) { + pub fn restore_backup( + backup_secret: String, + max_version: String, + promise_id: u32, + ) { RUNTIME.spawn(async move { let result = download_backup(backup_secret) .await @@ -93,6 +97,7 @@ restore_from_main_compaction( &result.backup_restoration_path.to_string_lossy(), &result.backup_data_key, + &max_version, future_id, ); @@ -136,6 +141,7 @@ backup_id: String, backup_data_key: String, backup_log_data_key: String, + max_version: String, promise_id: u32, ) { RUNTIME.spawn(async move { @@ -160,6 +166,7 @@ restore_from_main_compaction( &result.backup_restoration_path.to_string_lossy(), &result.backup_data_key, + &max_version, future_id, ); diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs --- a/native/native_rust_library/src/lib.rs +++ b/native/native_rust_library/src/lib.rs @@ -360,13 +360,18 @@ ); #[cxx_name = "restoreBackup"] - fn restore_backup(backup_secret: String, promise_id: u32); + fn restore_backup( + backup_secret: String, + max_version: String, + promise_id: u32, + ); #[cxx_name = "restoreBackupData"] fn restore_backup_data( backup_id: String, backup_data_key: String, backup_log_data_key: String, + max_version: String, promise_id: u32, ); @@ -424,6 +429,7 @@ fn restore_from_main_compaction( main_compaction_path: &str, main_compaction_encryption_key: &str, + max_version: &str, future_id: usize, ); diff --git a/native/qr-code/qr-code-screen.react.js b/native/qr-code/qr-code-screen.react.js --- a/native/qr-code/qr-code-screen.react.js +++ b/native/qr-code/qr-code-screen.react.js @@ -22,6 +22,7 @@ import { olmAPI } from '../crypto/olm-api.js'; import { commCoreModule } from '../native-modules.js'; import type { NavigationRoute } from '../navigation/route-names.js'; +import { persistConfig } from '../redux/persist.js'; import { useStyles } from '../themes/colors.js'; import * as AES from '../utils/aes-crypto-module.js'; import Alert from '../utils/alert.js'; @@ -37,6 +38,7 @@ backupID, backupDataKey, backupLogDataKey, + persistConfig.version.toString(), ); } diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js --- a/native/schema/CommCoreModuleSchema.js +++ b/native/schema/CommCoreModuleSchema.js @@ -128,11 +128,12 @@ backupSecret: string, siweBackupMsg: string, ) => Promise; - +restoreBackup: (backupSecret: string) => Promise; + +restoreBackup: (backupSecret: string, maxVersion: string) => Promise; +restoreBackupData: ( backupID: string, backupDataKey: string, backupLogDataKey: string, + maxVersion: string, ) => Promise; +retrieveBackupKeys: (backupSecret: string) => Promise; +retrieveLatestSIWEBackupData: () => Promise;