diff --git a/backends/LNC/credentialStore.ts b/backends/LNC/credentialStore.ts index 1f792b845..d7c26ab22 100644 --- a/backends/LNC/credentialStore.ts +++ b/backends/LNC/credentialStore.ts @@ -1,18 +1,18 @@ -import EncryptedStorage from 'react-native-encrypted-storage'; +import Storage from '../../storage'; import { CredentialStore } from '../../zeus_modules/@lightninglabs/lnc-rn'; import hashjs from 'hash.js'; -const STORAGE_KEY = 'lnc-rn'; +const LNC_STORAGE_KEY = 'lnc-rn'; const hash = (stringToHash: string) => hashjs.sha256().update(stringToHash).digest('hex'); /** - * A wrapper around `EncryptedStorage` used to store sensitive data required + * A wrapper around `Storage` used to store sensitive data required * by LNC to reconnect after the initial pairing process has been completed. */ export default class LncCredentialStore implements CredentialStore { - // the data to store in EncryptedStorage + // the data to store in Storage private persisted = { serverHost: '', localKey: '', @@ -44,28 +44,22 @@ export default class LncCredentialStore implements CredentialStore { private async _migrateServerHost() { try { - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; const hostKey = `${baseKey}:host`; - const hasNewFormat = await EncryptedStorage.getItem(hostKey); + const hasNewFormat = await Storage.getItem(hostKey); // Only migrate if new format doesn't exist yet if (!hasNewFormat) { - const oldData = await EncryptedStorage.getItem(baseKey); + const oldData = await Storage.getItem(baseKey); if (oldData) { const parsed = JSON.parse(oldData); if (parsed.serverHost) { - await EncryptedStorage.setItem( - hostKey, - parsed.serverHost - ); + await Storage.setItem(hostKey, parsed.serverHost); // Remove serverHost from old format delete parsed.serverHost; - await EncryptedStorage.setItem( - baseKey, - JSON.stringify(parsed) - ); + await Storage.setItem(baseKey, parsed); } } } @@ -136,9 +130,9 @@ export default class LncCredentialStore implements CredentialStore { /** Clears any persisted data in the store */ clear() { - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; - EncryptedStorage.removeItem(baseKey); - EncryptedStorage.removeItem(`${baseKey}:host`); + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; + Storage.removeItem(baseKey); + Storage.removeItem(`${baseKey}:host`); this.persisted = { serverHost: this.persisted.serverHost, localKey: '', @@ -150,15 +144,15 @@ export default class LncCredentialStore implements CredentialStore { this._pairingPhrase = ''; } - /** Loads persisted data from EncryptedStorage */ + /** Loads persisted data from Storage */ async load(pairingPhrase?: string) { // only load if pairingPhrase is set if (!pairingPhrase) return; try { - const baseKey = `${STORAGE_KEY}:${hash(pairingPhrase)}`; + const baseKey = `${LNC_STORAGE_KEY}:${hash(pairingPhrase)}`; const hostKey = `${baseKey}:host`; - const json = await EncryptedStorage.getItem(baseKey); - const serverHost = await EncryptedStorage.getItem(hostKey); + const json = await Storage.getItem(baseKey); + const serverHost = await Storage.getItem(hostKey); if (json) { this.persisted = JSON.parse(json); if (serverHost) { @@ -179,18 +173,18 @@ export default class LncCredentialStore implements CredentialStore { // private _saveServerHost() { - const hostKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}:host`; - EncryptedStorage.setItem(hostKey, this.persisted.serverHost); + const hostKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}:host`; + Storage.setItem(hostKey, this.persisted.serverHost); } - /** Saves persisted data to EncryptedStorage */ + /** Saves persisted data to Storage */ private _save() { // only save if localKey and remoteKey is set if (!this._localKey) return; if (!this._remoteKey) return; - const baseKey = `${STORAGE_KEY}:${hash(this._pairingPhrase)}`; - EncryptedStorage.setItem(baseKey, JSON.stringify(this.persisted)); + const baseKey = `${LNC_STORAGE_KEY}:${hash(this._pairingPhrase)}`; + Storage.setItem(baseKey, this.persisted); } } -export { STORAGE_KEY, hash }; +export { LNC_STORAGE_KEY, hash }; diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f4a25eaa2..0fd52e66c 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -20,6 +20,11 @@ PODS: - fmt (= 9.1.0) - glog - RCT-Folly/Default (= 2024.01.01.00) + - RCT-Folly/Default (2024.01.01.00): + - boost + - DoubleConversion + - fmt (= 9.1.0) + - glog - RCT-Folly/Fabric (2024.01.01.00): - boost - DoubleConversion @@ -1628,14 +1633,14 @@ SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 BVLinearGradient: 7815a70ab485b7b155186dd0cc836363e0288cad CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 - DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 + DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 FBLazyVector: 4bc164e5b5e6cfc288d2b5ff28643ea15fa1a589 - fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be - glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a + fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120 + glog: fdfdfe5479092de0c4bdbebedd9056951f092c4f hermes-engine: 01d3e052018c2a13937aca1860fbedbccd4a41b7 lottie-ios: 8f97d3271e155c2d688875c29cd3c74908aef5f8 lottie-react-native: 5d89c05930d4180a1e39b1757d46e6c0eec90255 - RCT-Folly: 84578c8756030547307e4572ab1947de1685c599 + RCT-Folly: 5dc73daec3476616d19e8a53f0156176f7b55461 RCTDeprecation: b03c35057846b685b3ccadc9bfe43e349989cdb2 RCTRequired: 194626909cfa8d39ca6663138c417bc6c431648c RCTTypeSafety: 552aff5b8e8341660594db00e53ac889682bc120 diff --git a/ios/zeus.xcodeproj/project.pbxproj b/ios/zeus.xcodeproj/project.pbxproj index b20bb6826..89aff8165 100644 --- a/ios/zeus.xcodeproj/project.pbxproj +++ b/ios/zeus.xcodeproj/project.pbxproj @@ -1493,7 +1493,6 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-zeus-zeusTests/Pods-zeus-zeusTests-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", @@ -1514,11 +1513,9 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/SwiftProtobuf/SwiftProtobuf.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", @@ -1539,7 +1536,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SwiftProtobuf.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -1570,7 +1566,6 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-zeus/Pods-zeus-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNDeviceInfo/RNDeviceInfoPrivacyInfo.bundle", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", @@ -1591,11 +1586,9 @@ "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/SwiftProtobuf/SwiftProtobuf.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNDeviceInfoPrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", @@ -1616,7 +1609,6 @@ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SwiftProtobuf.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/storage/index.ts b/storage/index.ts index 981d9f09a..605c600c1 100644 --- a/storage/index.ts +++ b/storage/index.ts @@ -17,6 +17,11 @@ class Storage { ); return response; }; + + removeItem = async (key: string) => { + const response = await Keychain.resetInternetCredentials(key); + return response; + }; } const storage = new Storage(); diff --git a/stores/SettingsStore.ts b/stores/SettingsStore.ts index ae71473b8..935d0aa30 100644 --- a/stores/SettingsStore.ts +++ b/stores/SettingsStore.ts @@ -16,7 +16,7 @@ import Storage from '../storage'; import LoginRequest from '../models/LoginRequest'; const LEGACY_STORAGE_KEY = 'zeus-settings'; -export const MODERN_STORAGE_KEY = 'zeus-settings-v2'; +export const STORAGE_KEY = 'zeus-settings-v2'; export const LEGACY_CURRENCY_CODES_KEY = 'currency-codes'; export const CURRENCY_CODES_KEY = 'zeus-currency-codes'; @@ -1394,9 +1394,7 @@ export default class SettingsStore { public async getSettings(silentUpdate: boolean = false) { if (!silentUpdate) this.loading = true; try { - const modernSettings: any = await Storage.getItem( - MODERN_STORAGE_KEY - ); + const modernSettings: any = await Storage.getItem(STORAGE_KEY); if (modernSettings) { console.log('attempting to load modern settings'); @@ -1420,13 +1418,13 @@ export default class SettingsStore { await MigrationsUtils.storageMigrationV2(newSettings); } else { - console.log('No settings stored'); + console.log('No legacy settings stored'); } } const node: any = - this.settings.nodes?.length && - this.settings.nodes[this.settings.selectedNode || 0]; + this.settings?.nodes?.length && + this.settings?.nodes[this.settings.selectedNode || 0]; if (node) { this.host = node.host; this.port = node.port; @@ -1461,9 +1459,9 @@ export default class SettingsStore { } @action - public async setSettings(settings: string) { + public async setSettings(settings: any) { this.loading = true; - await EncryptedStorage.setItem(LEGACY_STORAGE_KEY, settings); + await Storage.setItem(STORAGE_KEY, settings); this.loading = false; return settings; } @@ -1476,7 +1474,7 @@ export default class SettingsStore { ...newSetting }; - await this.setSettings(JSON.stringify(newSettings)); + await this.setSettings(newSettings); // ensure we get the enhanced settings set const settings = await this.getSettings(true); return settings; @@ -1650,7 +1648,7 @@ export default class SettingsStore { public checkBiometricsStatus = async () => { const biometryType = await getSupportedBiometryType(); if (this.settings.supportedBiometryType !== biometryType) { - this.updateSettings({ + await this.updateSettings({ supportedBiometryType: biometryType }); } diff --git a/utils/MigrationUtils.test.ts b/utils/MigrationUtils.test.ts index df90f127a..aa5aa63b6 100644 --- a/utils/MigrationUtils.test.ts +++ b/utils/MigrationUtils.test.ts @@ -52,7 +52,7 @@ jest.mock('../stores/SettingsStore', () => ({ 'wss://nostr.lnproxy.org' ], DEFAULT_SLIDE_TO_PAY_THRESHOLD: 10000, - MODERN_STORAGE_KEY: 'zeus-settings-v2', + STORAGE_KEY: 'zeus-settings-v2', LEGACY_CURRENCY_CODES_KEY: 'currency-codes', CURRENCY_CODES_KEY: 'zeus-currency-codes', PosEnabled: { diff --git a/utils/MigrationUtils.ts b/utils/MigrationUtils.ts index b42001a15..d9d53092b 100644 --- a/utils/MigrationUtils.ts +++ b/utils/MigrationUtils.ts @@ -19,7 +19,7 @@ import { DEFAULT_NOSTR_RELAYS_2023, PosEnabled, DEFAULT_SLIDE_TO_PAY_THRESHOLD, - MODERN_STORAGE_KEY, + STORAGE_KEY, LEGACY_CURRENCY_CODES_KEY, CURRENCY_CODES_KEY } from '../stores/SettingsStore'; @@ -61,6 +61,8 @@ import { import { LEGACY_LSPS1_ORDERS_KEY, LSPS1_ORDERS_KEY } from '../stores/LSPStore'; +import { LNC_STORAGE_KEY, hash } from '../backends/LNC/credentialStore'; + import EncryptedStorage from 'react-native-encrypted-storage'; import Storage from '../storage'; @@ -273,10 +275,7 @@ class MigrationsUtils { public async storageMigrationV2(settings: any) { // Settings migration console.log('Attemping settings migration'); - const writeSuccess = await Storage.setItem( - MODERN_STORAGE_KEY, - settings - ); + const writeSuccess = await Storage.setItem(STORAGE_KEY, settings); console.log('Settings migration status', writeSuccess); // Contacts migration @@ -536,6 +535,45 @@ class MigrationsUtils { error ); } + + // LNC migrations + try { + settings?.nodes.forEach(async (node: any) => { + if (node.implementation === 'lightning-node-connect') { + const baseKey = `${LNC_STORAGE_KEY}:${hash( + node.pairingPhrase + )}`; + const hostKey = `${baseKey}:host`; + console.log('baseKey', baseKey); + console.log('hostKey', hostKey); + + const baseKeyData = await EncryptedStorage.getItem(baseKey); + console.log('baseKeyData', baseKeyData); + + console.log('Attemping LNC base key migration', baseKey); + const writeSuccess1 = await Storage.setItem( + baseKey, + baseKeyData + ); + console.log('LNC base key migration status', writeSuccess1); + + const hostKeyData = await EncryptedStorage.getItem(hostKey); + console.log('hostKeyData', hostKeyData); + + console.log('Attemping LNC host key migration', baseKey); + const writeSuccess2 = await Storage.setItem( + hostKey, + hostKeyData + ); + console.log('LNC host key migration status', writeSuccess2); + } + }); + } catch (error) { + console.error( + 'Error loading LNC data from encrypted storage', + error + ); + } } } diff --git a/views/Settings/WalletConfiguration.tsx b/views/Settings/WalletConfiguration.tsx index 36ea549c8..fec4c8e73 100644 --- a/views/Settings/WalletConfiguration.tsx +++ b/views/Settings/WalletConfiguration.tsx @@ -10,13 +10,12 @@ import { } from 'react-native'; import Clipboard from '@react-native-clipboard/clipboard'; import { inject, observer } from 'mobx-react'; -import EncryptedStorage from 'react-native-encrypted-storage'; import cloneDeep from 'lodash/cloneDeep'; import differenceBy from 'lodash/differenceBy'; import { Route } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; -import { hash, STORAGE_KEY } from '../../backends/LNC/credentialStore'; +import { hash, LNC_STORAGE_KEY } from '../../backends/LNC/credentialStore'; import AddressUtils, { CUSTODIAL_LNDHUBS } from '../../utils/AddressUtils'; import ConnectionFormatUtils from '../../utils/ConnectionFormatUtils'; @@ -24,6 +23,8 @@ import { localeString } from '../../utils/LocaleUtils'; import BackendUtils from '../../utils/BackendUtils'; import { themeColor } from '../../utils/ThemeUtils'; +import Storage from '../../storage'; + import Button from '../../components/Button'; import CollapsedQR from '../../components/CollapsedQR'; import DropdownSetting from '../../components/DropdownSetting'; @@ -279,9 +280,10 @@ export default class WalletConfiguration extends React.Component< const { implementation, pairingPhrase } = this.state; if (implementation === 'lightning-node-connect') { - const key = `${STORAGE_KEY}:${hash(pairingPhrase)}`; - const json: any = await EncryptedStorage.getItem(key); + const key = `${LNC_STORAGE_KEY}:${hash(pairingPhrase)}`; + const json: any = await Storage.getItem(key); const parsed = JSON.parse(json); + console.log('parsed', parsed); if (parsed) { if (parsed.localKey && parsed.remoteKey) { this.setState({