Skip to content

Commit

Permalink
Migrate LNC data
Browse files Browse the repository at this point in the history
  • Loading branch information
kaloudis committed Jan 17, 2025
1 parent bce1882 commit 72b7af8
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 50 deletions.
52 changes: 23 additions & 29 deletions backends/LNC/credentialStore.ts
Original file line number Diff line number Diff line change
@@ -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: '',
Expand Down Expand Up @@ -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);
}
}
}
Expand All @@ -81,7 +75,7 @@ export default class LncCredentialStore implements CredentialStore {

/** Stores the host:port of the Lightning Node Connect proxy server to connect to */
get serverHost() {
return this.persisted.serverHost;
return this.persisted.serverHost.replace(/['"]+/g, '');
}

/** Stores the host:port of the Lightning Node Connect proxy server to connect to */
Expand Down Expand Up @@ -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: '',
Expand All @@ -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) {
Expand All @@ -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 };
5 changes: 5 additions & 0 deletions storage/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ class Storage {
);
return response;
};

removeItem = async (key: string) => {
const response = await Keychain.resetInternetCredentials(key);
return response;
};
}

const storage = new Storage();
Expand Down
20 changes: 9 additions & 11 deletions stores/SettingsStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -1395,9 +1395,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');
Expand All @@ -1421,13 +1419,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;
Expand Down Expand Up @@ -1462,9 +1460,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;
}
Expand All @@ -1477,7 +1475,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;
Expand Down Expand Up @@ -1651,7 +1649,7 @@ export default class SettingsStore {
public checkBiometricsStatus = async () => {
const biometryType = await getSupportedBiometryType();
if (this.settings.supportedBiometryType !== biometryType) {
this.updateSettings({
await this.updateSettings({
supportedBiometryType: biometryType
});
}
Expand Down
2 changes: 1 addition & 1 deletion utils/MigrationUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
45 changes: 40 additions & 5 deletions utils/MigrationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -536,6 +535,42 @@ 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`;

const baseKeyData: string =
(await EncryptedStorage.getItem(baseKey)) || '{}';

console.log('Attemping LNC base key migration', baseKey);
const writeSuccess1 = await Storage.setItem(
baseKey,
JSON.parse(baseKeyData)
);
console.log('LNC base key migration status', writeSuccess1);

const hostKeyData = await EncryptedStorage.getItem(hostKey);

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
);
}
}
}

Expand Down
9 changes: 5 additions & 4 deletions views/Settings/WalletConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ 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';
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';
Expand Down Expand Up @@ -282,8 +283,8 @@ 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);
if (parsed) {
if (parsed.localKey && parsed.remoteKey) {
Expand Down

0 comments on commit 72b7af8

Please sign in to comment.