ihm_client/src/services/service.ts

1035 lines
31 KiB
TypeScript
Executable File

// import { WebSocketClient } from '../websockets';
import { INotification } from '~/models/notification.model';
import { IProcess } from '~/models/process.model';
// import Database from './database';
import { initWebsocket, sendMessage } from '../websockets';
import { ApiReturn, Device, HandshakeMessage, Member, Process, RoleDefinition, SecretsStore, UserDiff } from '../../pkg/sdk_client';
import ModalService from './modal.service';
import Database from './database.service';
import { storeData, retrieveData } from './storage.service';
import { BackUp } from '~/models/backup.model';
export const U32_MAX = 4294967295;
const storageUrl = `/storage`;
const BOOTSTRAPURL = [`https://demo.4nkweb.com/ws/`];
const DEFAULTAMOUNT = 1000n;
export default class Services {
private static initializing: Promise<Services> | null = null;
private static instance: Services;
private currentProcess: string | null = null;
private pendingUpdates: any | null = null;
private currentUpdateMerkleRoot: string | null = null;
private localAddress: string | null = null;
private pairedAddresses: string[] = [];
private sdkClient: any;
private processes: IProcess[] | null = null;
private notifications: any[] | null = null;
private subscriptions: { element: Element; event: string; eventHandler: string }[] = [];
private database: any;
private routingInstance!: ModalService;
private relayAddresses: { [wsurl: string]: string } = {};
private membersList: Record<any, Process> = {};
// Private constructor to prevent direct instantiation from outside
private constructor() {}
// Method to access the singleton instance of Services
public static async getInstance(): Promise<Services> {
if (Services.instance) {
return Services.instance;
}
if (!Services.initializing) {
Services.initializing = (async () => {
const instance = new Services();
await instance.init();
instance.routingInstance = await ModalService.getInstance();
return instance;
})();
}
console.log('initializing services');
Services.instance = await Services.initializing;
Services.initializing = null; // Reset for potential future use
return Services.instance;
}
public async init(): Promise<void> {
this.notifications = this.getNotifications();
this.sdkClient = await import('../../pkg/sdk_client');
this.sdkClient.setup();
for (const wsurl of Object.values(BOOTSTRAPURL)) {
this.updateRelay(wsurl, '');
}
await this.connectAllRelays();
}
/**
* Calls `this.addWebsocketConnection` for each `wsurl` in relayAddresses.
*/
public async connectAllRelays(): Promise<void> {
for (const wsurl of Object.keys(this.relayAddresses)) {
try {
console.log(`Connecting to: ${wsurl}`);
await this.addWebsocketConnection(wsurl);
console.log(`Successfully connected to: ${wsurl}`);
} catch (error) {
console.error(`Failed to connect to ${wsurl}:`, error);
}
}
}
public async addWebsocketConnection(url: string): Promise<void> {
console.log('Opening new websocket connection');
await initWebsocket(url);
}
/**
* Add or update a key/value pair in relayAddresses.
* @param wsurl - The WebSocket URL (key).
* @param spAddress - The SP Address (value).
*/
public updateRelay(wsurl: string, spAddress: string): void {
this.relayAddresses[wsurl] = spAddress;
console.log(`Updated: ${wsurl} -> ${spAddress}`);
}
/**
* Retrieve the spAddress for a given wsurl.
* @param wsurl - The WebSocket URL to look up.
* @returns The SP Address if found, or undefined if not.
*/
public getSpAddress(wsurl: string): string | undefined {
return this.relayAddresses[wsurl];
}
/**
* Get all key/value pairs from relayAddresses.
* @returns An array of objects containing wsurl and spAddress.
*/
public getAllRelays(): { wsurl: string; spAddress: string }[] {
return Object.entries(this.relayAddresses).map(([wsurl, spAddress]) => ({
wsurl,
spAddress,
}));
}
/**
* Print all key/value pairs for debugging.
*/
public printAllRelays(): void {
console.log("Current relay addresses:");
for (const [wsurl, spAddress] of Object.entries(this.relayAddresses)) {
console.log(`${wsurl} -> ${spAddress}`);
}
}
public isPaired(): boolean {
try {
return this.sdkClient.is_paired();
} catch (e) {
throw new Error(`isPaired ~ Error: ${e}`);
}
}
public async unpairDevice(): Promise<void> {
try {
this.sdkClient.unpair_device();
const newDevice = this.dumpDeviceFromMemory();
await this.saveDeviceInDatabase(newDevice);
} catch (e) {
throw new Error(`Failed to unpair device: ${e}`);
}
}
public async getSecretForAddress(address: string): Promise<string | null> {
const db = await Database.getInstance();
return await db.getObject('shared_secrets', address);
}
public async getAllSecrets(): Promise<SecretsStore> {
const db = await Database.getInstance();
const sharedSecrets = await db.dumpStore('shared_secrets');
const unconfirmedSecrets = await db.dumpStore('unconfirmed_secrets'); // keys are numeric values
const secretsStore = {
shared_secrets: sharedSecrets,
unconfirmed_secrets: Object.values(unconfirmedSecrets),
};
return secretsStore;
}
public async getAllDiffs(): Promise<Record<string, UserDiff>> {
const db = await Database.getInstance();
return await db.dumpStore('diffs');
}
public async getDiffByValue(value: string): Promise<UserDiff | null> {
const db = await Database.getInstance();
const store = 'diffs';
const res = await db.getObject(store, value);
return res;
}
public async checkConnections(members: Member[]): Promise<void> {
const memberPromises = members.map(async (member) => {
const addressPromises = member.sp_addresses.map(async (address) => {
const sharedSecret = await this.getSecretForAddress(address);
if (!sharedSecret) {
const connectMemberResult = await this.connectMember([{ sp_addresses: [address] }]);
await this.handleApiReturn(connectMemberResult);
}
});
await Promise.all(addressPromises);
});
await Promise.all(memberPromises);
}
public async connectMember(members: Member[]): Promise<ApiReturn> {
if (members.length === 0) {
throw new Error('Trying to connect to empty members list');
}
const members_str = members.map((member) => JSON.stringify(member));
try {
// Ensure the amount is available before proceeding
await this.ensureSufficientAmount();
return this.sdkClient.create_connect_transaction(members_str, 1);
} catch (e) {
console.error('Failed to connect member:', e);
throw e;
}
}
private async ensureSufficientAmount(): Promise<void> {
let availableAmt = this.getAmount();
const target: BigInt = DEFAULTAMOUNT * BigInt(2);
if (availableAmt < target) {
const faucetMsg = this.createFaucetMessage();
this.sendFaucetMessage(faucetMsg);
await this.waitForAmount(target);
}
}
private async waitForAmount(target: BigInt): Promise<BigInt> {
let attempts = 3;
while (attempts > 0) {
const amount = this.getAmount();
if (amount >= target) {
return amount;
}
attempts--;
if (attempts > 0) {
await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait for 1 second
}
}
throw new Error('Amount is still 0 after 3 attempts');
}
public async createMessagingProcess(otherMembers: Member[],relayAddress: string, feeRate: number): Promise<ApiReturn> {
if (!this.isPaired()) {
throw new Error('Device not paired');
}
const me = await this.getMemberFromDevice();
if (!me) {
throw new Error('No paired member in device');
}
const allMembers: Member[] = otherMembers;
allMembers.push({ sp_addresses: me });
const meAndOne = [{ sp_addresses: me }, otherMembers.pop()!];
const everyOneElse = otherMembers;
const messagingTemplate = {
description: 'messaging',
roles: {
public: {
members: allMembers,
validation_rules: [
{
quorum: 0.0,
fields: ['description', 'roles'],
min_sig_member: 0.0,
},
],
storages: [storageUrl]
},
owner: {
members: meAndOne,
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles'],
min_sig_member: 1.0,
},
],
storages: [storageUrl]
},
users: {
members: everyOneElse,
validation_rules: [
{
quorum: 0.0,
fields: ['description', 'roles'],
min_sig_member: 0.0,
},
],
storages: [storageUrl]
},
},
};
try {
return this.sdkClient.create_new_process(JSON.stringify(messagingTemplate), null, relayAddress, feeRate);
} catch (e) {
throw new Error(`Creating process failed: ${e}`);
}
}
public async createPairingProcess(pairWith: string[], relayAddress: string, feeRate: number): Promise<ApiReturn> {
if (this.sdkClient.is_paired()) {
throw new Error('Device already paired');
}
const myAddress: string = this.sdkClient.get_address();
pairWith.push(myAddress);
const newKey = this.sdkClient.get_new_keypair();
const pairingTemplate = {
description: 'pairing',
roles: {
owner: {
members: [{ sp_addresses: pairWith }],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'roles', 'session_privkey', 'session_pubkey', 'key_parity'],
min_sig_member: 1.0,
},
],
storages: [storageUrl]
},
},
session_privkey: newKey['private_key'],
session_pubkey: newKey['x_only_public_key'],
key_parity: newKey['key_parity'],
};
try {
return this.sdkClient.create_new_process(JSON.stringify(pairingTemplate), null, relayAddress, feeRate);
} catch (e) {
throw new Error(`Creating process failed:, ${e}`);
}
}
public async createDmProcess(
otherMember: string[],
parentProcess: string,
): Promise<ApiReturn> {
try {
console.log('🚀 Début createMessagingProcess');
console.log('📝 Parent Process ID:', parentProcess);
console.log('👥 Other Member:', otherMember);
if (!this.isPaired()) {
throw new Error('Device not paired');
}
const myAddresses = await this.getMemberFromDevice();
console.log('🔑 Mes adresses:', myAddresses);
if (!myAddresses) {
throw new Error('No paired member found');
}
const messagingTemplate = {
parent_id: parentProcess,
message: '',
roles: {
dm: {
members: [
{ sp_addresses: myAddresses },
{ sp_addresses: otherMember }
],
validation_rules: [
{
quorum: 0.01,
fields: ['message'],
min_sig_member: 0.01,
},
],
storages: [storageUrl]
}
}
};
console.log('📋 Template final:', JSON.stringify(messagingTemplate, null, 2));
console.log('👥 Création du process message comme enfant de:', parentProcess);
const relayAddress = this.getAllRelays()[0]['spAddress'];
const feeRate = 1;
const initState = JSON.stringify(messagingTemplate);
const result = this.sdkClient.create_new_process(
JSON.stringify(messagingTemplate),
null,
relayAddress,
feeRate
);
// console.log('✅ Process message créé:', result);
return result;
} catch (e) {
console.error('❌ Erreur:', e);
throw e;
}
}
// Create prd update for current process and update
public createPrdUpdate(processId: string, stateId: string): ApiReturn {
try {
return this.sdkClient.create_update_message(processId, stateId);
} catch (e) {
throw new Error(`Failed to create prd update: ${e}`);
}
}
public createPrdResponse(pcdMerkleRoot: string): ApiReturn {
if (!this.currentProcess) {
throw new Error('No current process defined');
}
try {
return this.sdkClient.create_response_prd(this.currentProcess, pcdMerkleRoot);
} catch (e) {
throw e;
}
}
public approveChange(processId: string, stateId: string): ApiReturn {
try {
return this.sdkClient.validate_state(processId, stateId);
} catch (e) {
throw new Error(`Failed to create prd response: ${e}`);
}
}
public rejectChange(): ApiReturn {
if (!this.currentProcess || !this.currentUpdateMerkleRoot) {
throw new Error('No current process and/or current update defined');
}
try {
return this.sdkClient.refuse_state(this.currentProcess, this.currentUpdateMerkleRoot);
} catch (e) {
throw new Error(`Failed to create prd response: ${e}`);
}
}
async resetDevice() {
this.sdkClient.reset_device();
// Clear all stores
const db = await Database.getInstance();
await db.clearStore('wallet');
await db.clearStore('shared_secrets');
await db.clearStore('unconfirmed_secrets');
await db.clearStore('processes');
await db.clearStore('diffs');
}
async sendNewTxMessage(message: string) {
sendMessage('NewTx', message);
}
async sendCommitMessage(message: string) {
sendMessage('Commit', message);
}
async sendCipherMessages(ciphers: string[]) {
for (let i = 0; i < ciphers.length; i++) {
const cipher = ciphers[i];
sendMessage('Cipher', cipher);
}
}
sendFaucetMessage(message: string): void {
sendMessage('Faucet', message);
}
async parseCipher(message: string) {
try {
// console.log('parsing new cipher');
const apiReturn = this.sdkClient.parse_cipher(message);
console.log('🚀 ~ Services ~ parseCipher ~ apiReturn:', apiReturn);
await this.handleApiReturn(apiReturn);
// Device 1 wait Device 2
const waitingModal = document.getElementById('waiting-modal');
if (waitingModal) {
this.device2Ready = true;
}
} catch (e) {
console.error(`Parsed cipher with error: ${e}`);
}
// await this.saveCipherTxToDb(parsedTx)
}
async parseNewTx(tx: string) {
try {
const parsedTx = this.sdkClient.parse_new_tx(tx, 0);
if (parsedTx) {
try {
await this.handleApiReturn(parsedTx);
const newDevice = this.dumpDeviceFromMemory();
await this.saveDeviceInDatabase(newDevice);
} catch (e) {
console.error('Failed to update device with new tx');
}
}
} catch (e) {
console.trace(e);
}
}
public async tryFetchDiffValue(diffs: UserDiff[]): Promise<[UserDiff[], Record<string, string>]>{
if (diffs.length === 0) {
return [[], {}];
}
// We check if we have the value in diffs
let retrievedValues: Record<string, string> = {};
for (const diff of diffs) {
// Check if `new_value` is missing
if (diff.new_value === null) {
const hash = diff.value_commitment;
if (!hash) {
console.error('No commitment for diff');
}
try {
const res = await this.fetchValueFromStorage(hash);
if (!res) {
console.error('Failed to fetch value for hash', hash);
} else {
diff.new_value = res['value'];
retrievedValues[hash] = res['value'];
}
} catch (error) {
console.error(`Failed to fetch new_value for diff: ${JSON.stringify(diff)}`, error);
}
}
}
return [diffs, retrievedValues];
}
public async handleApiReturn(apiReturn: ApiReturn) {
if (apiReturn.new_tx_to_send && apiReturn.new_tx_to_send.transaction.length != 0) {
await this.sendNewTxMessage(JSON.stringify(apiReturn.new_tx_to_send));
}
if (apiReturn.secrets) {
const unconfirmedSecrets = apiReturn.secrets.unconfirmed_secrets;
const confirmedSecrets = apiReturn.secrets.shared_secrets;
const db = await Database.getInstance();
for (const secret of unconfirmedSecrets) {
await db.addObject({
storeName: 'unconfirmed_secrets',
object: secret,
key: null,
});
}
const entries = Object.entries(confirmedSecrets).map(([key, value]) => ({ key, value }));
for (const entry of entries) {
try {
await db.addObject({
storeName: 'shared_secrets',
object: entry.value,
key: entry.key,
});
} catch (e) {
throw e;
}
// We don't want to throw an error, it could simply be that we registered directly the shared secret
// this.removeUnconfirmedSecret(entry.value);
}
}
setTimeout(async () => {
if (apiReturn.updated_process) {
const updatedProcess = apiReturn.updated_process;
const processId: string = updatedProcess.commitment_tx;
// Save process to storage
try {
await this.saveProcess(processId, updatedProcess.current_process);
} catch (e) {
throw e;
}
const isPaired = this.isPaired();
if (updatedProcess.new_diffs.length != 0) {
const [updatedDiffs, retrievedValues] = await this.tryFetchDiffValue(updatedProcess.new_diffs);
if (Object.entries(retrievedValues).length != 0) {
const stateId = updatedDiffs[0].state_id;
const processId = updatedDiffs[0].process_id;
// We update the process with the value we retrieved
const hashToValues = JSON.stringify(retrievedValues);
const apiReturn = this.sdkClient.update_process_state(processId, stateId, hashToValues);
await this.handleApiReturn(apiReturn);
} else {
try {
await this.saveDiffs(updatedDiffs);
} catch (e) {
throw e;
}
if (!isPaired) {
await this.openPairingConfirmationModal(updatedDiffs);
}
}
}
if (updatedProcess.modified_state) {
const responsePrdReturn = this.sdkClient.create_response_prd(processId, updatedProcess.modified_state);
await this.handleApiReturn(responsePrdReturn);
}
}
if (apiReturn.commit_to_send) {
const commit = apiReturn.commit_to_send;
await this.sendCommitMessage(JSON.stringify(commit));
}
if (apiReturn.ciphers_to_send && apiReturn.ciphers_to_send.length != 0) {
await this.sendCipherMessages(apiReturn.ciphers_to_send);
}
}, 0);
}
public async openPairingConfirmationModal(diffs: UserDiff[]) {
const rolesDiff = diffs.find((diff) => diff.field === 'roles');
if (!rolesDiff) {
throw new Error('Pairing process must have roles');
}
const processId = rolesDiff.process_id;
const stateId = rolesDiff.state_id;
try {
await this.routingInstance.openPairingConfirmationModal(rolesDiff.new_value, processId, stateId);
} catch (e) {
throw new Error(`${e}`);
}
}
pairDevice(spAddressList: string[], pairingProcess: string) {
try {
this.sdkClient.pair_device(pairingProcess, spAddressList);
} catch (e) {
throw new Error(`Failed to pair device: ${e}`);
}
}
public getAmount(): BigInt {
const amount = this.sdkClient.get_available_amount();
return amount;
}
async getDeviceAddress() {
return await this.sdkClient.get_address();
}
public dumpDeviceFromMemory(): string {
try {
return this.sdkClient.dump_device();
} catch (e) {
throw new Error(`Failed to dump device: ${e}`);
}
}
async saveDeviceInDatabase(device: any): Promise<void> {
const db = await Database.getInstance();
const walletStore = 'wallet';
try {
const prevDevice = await this.getDeviceFromDatabase();
if (prevDevice) {
await db.deleteObject(walletStore, "1");
}
await db.addObject({
storeName: walletStore,
object: { pre_id: '1', device },
key: null,
});
} catch (e) {
console.error(e);
}
}
async getDeviceFromDatabase(): Promise<string | null> {
const db = await Database.getInstance();
const walletStore = 'wallet';
try {
const dbRes = await db.getObject(walletStore, '1');
if (dbRes) {
const wallet = dbRes['device'];
return wallet;
} else {
return null;
}
} catch (e) {
throw new Error(`Failed to retrieve device from db: ${e}`);
}
}
async getMemberFromDevice(): Promise<string[] | null> {
try {
const device = await this.getDeviceFromDatabase();
if (device) {
const parsed: Device = JSON.parse(device);
const pairedMember = parsed['paired_member'];
return pairedMember.sp_addresses;
} else {
return null;
}
} catch (e) {
throw new Error(`Failed to retrieve paired_member from device: ${e}`);
}
}
isChildRole(parent: any, child: any): boolean {
try {
this.sdkClient.is_child_role(JSON.stringify(parent), JSON.stringify(child));
} catch (e) {
console.error(e);
return false;
}
return true;
}
rolesContainsUs(roles: any): boolean {
try {
this.sdkClient.roles_contains_us(JSON.stringify(roles));
} catch (e) {
console.error(e);
return false;
}
return true;
}
rolesContainsMember(roles: any, member: string[]): boolean {
try {
this.sdkClient.roles_contains_member(JSON.stringify(roles), member);
} catch (e) {
console.error(e);
return false;
}
return true;
}
async dumpWallet() {
const wallet = await this.sdkClient.dump_wallet();
console.log('🚀 ~ Services ~ dumpWallet ~ wallet:', wallet);
return wallet;
}
public createFaucetMessage() {
const message = this.sdkClient.create_faucet_msg();
console.log('🚀 ~ Services ~ createFaucetMessage ~ message:', message);
return message;
}
async createNewDevice() {
let spAddress = '';
try {
spAddress = await this.sdkClient.create_new_device(0, 'signet');
const device = this.dumpDeviceFromMemory();
console.log('🚀 ~ Services ~ device:', device);
await this.saveDeviceInDatabase(device);
} catch (e) {
console.error('Services ~ Error:', e);
}
return spAddress;
}
restoreDevice(device: string) {
try {
this.sdkClient.restore_device(device);
const spAddress = this.sdkClient.get_address();
} catch (e) {
console.error(e);
}
}
public async saveProcess(commitedIn: string, process: Process) {
const db = await Database.getInstance();
try {
await db.addObject({
storeName: 'processes',
object: process,
key: commitedIn,
});
} catch (e) {
throw new Error(`Failed to save process: ${e}`);
}
// We check how many copies in storage nodes
// We check the storage nodes in the process itself
// this.sdkClient.get_storages(commitedIn);
const storages = [storageUrl];
for (const state of process.states) {
if (state.state_id === "") {
continue;
}
if (!state.encrypted_pcd) {
console.warn('Empty encrypted pcd, skipping...');
continue;
}
for (const [field, hash] of Object.entries(state.pcd_commitment)) {
// get the encrypted value with the field name
const value = state.encrypted_pcd[field];
await storeData(storages, hash, value, null);
}
}
}
public async fetchValueFromStorage(hash: string): Promise<any | null> {
const storages = [storageUrl];
return await retrieveData(storages, hash);
}
public async saveDiffs(diffs: UserDiff[]) {
const db = await Database.getInstance();
try {
for (const diff of diffs) {
await db.addObject({
storeName: 'diffs',
object: diff,
key: null,
});
}
} catch (e) {
throw new Error(`Failed to save process: ${e}`);
}
}
public async getProcess(commitedIn: string): Promise<Process> {
const db = await Database.getInstance();
return await db.getObject('processes', commitedIn);
}
public async getProcesses(): Promise<Record<string, Process>> {
const db = await Database.getInstance();
const processes: Record<string, Process> = await db.dumpStore('processes');
return processes;
}
public async getChildrenOfProcess(processId: string): Promise<string[]> {
const processes = await this.getProcesses();
const res = [];
for (const [hash, process] of Object.entries(processes)) {
const firstState = process.states[0];
const pcdCommitment = firstState['pcd_commitment'];
try {
const parentIdHash = pcdCommitment['parent_id'];
const diff = await this.getDiffByValue(parentIdHash);
if (diff && diff['new_value'] === processId) {
res.push(JSON.stringify(process));
}
} catch (e) {
continue;
}
}
return res;
}
public async restoreProcessesFromBackUp(processes: Record<string, Process>) {
const db = await Database.getInstance();
for (const [commitedIn, process] of Object.entries(processes)) {
await db.addObject({ storeName: 'processes', object: process, key: commitedIn});
}
await this.restoreProcessesFromDB();
}
// Restore process in wasm with persistent storage
public async restoreProcessesFromDB() {
const db = await Database.getInstance();
try {
const processes: Record<string, Process> = await db.dumpStore('processes');
if (processes && Object.keys(processes).length != 0) {
console.log(`Restoring ${Object.keys(processes).length} processes`);
this.sdkClient.set_process_cache(JSON.stringify(processes));
} else {
console.log('No processes to restore!');
}
} catch (e) {
throw e;
}
}
public async clearSecretsFromDB() {
const db = await Database.getInstance();
try {
await db.clearStore('shared_secrets');
await db.clearStore('unconfirmed_secrets');
} catch (e) {
console.error(e);
}
}
public async restoreSecretsFromBackUp(secretsStore: SecretsStore) {
const db = await Database.getInstance();
for (const secret of secretsStore.unconfirmed_secrets) {
await db.addObject({
storeName: 'unconfirmed_secrets',
object: secret,
key: null,
});
}
const entries = Object.entries(secretsStore.shared_secrets).map(([key, value]) => ({ key, value }));
for (const entry of entries) {
await db.addObject({
storeName: 'shared_secrets',
object: entry.value,
key: entry.key,
});
}
// Now we can transfer them to memory
await this.restoreSecretsFromDB();
}
public async restoreSecretsFromDB() {
const db = await Database.getInstance();
try {
const sharedSecrets: Record<string, string> = await db.dumpStore('shared_secrets');
const unconfirmedSecrets = await db.dumpStore('unconfirmed_secrets');
const secretsStore = {
shared_secrets: sharedSecrets,
unconfirmed_secrets: Object.values(unconfirmedSecrets),
};
this.sdkClient.set_shared_secrets(JSON.stringify(secretsStore));
} catch (e) {
throw e;
}
}
getNotifications(): any[] | null {
// return [
// {
// id: 1,
// title: 'Notif 1',
// description: 'A normal notification',
// sendToNotificationPage: false,
// path: '/notif1',
// },
// {
// id: 2,
// title: 'Notif 2',
// description: 'A normal notification',
// sendToNotificationPage: false,
// path: '/notif2',
// },
// {
// id: 3,
// title: 'Notif 3',
// description: 'A normal notification',
// sendToNotificationPage: false,
// path: '/notif3',
// },
// ];
return this.notifications;
}
setNotifications(notifications: any[]) {
this.notifications = notifications;
}
async importJSON(backup: BackUp): Promise<void> {
const device = JSON.stringify(backup.device);
// Reset current device
await this.resetDevice();
await this.saveDeviceInDatabase(device);
this.restoreDevice(device);
// TODO restore secrets and processes from file
const secretsStore = backup.secrets;
await this.restoreSecretsFromBackUp(secretsStore);
const processes = backup.processes;
await this.restoreProcessesFromBackUp(processes);
}
public async createBackUp(): Promise<BackUp | null> {
// Get the device from indexedDB
const deviceStr = await this.getDeviceFromDatabase();
if (!deviceStr) {
console.error('No device loaded');
return null;
}
const device: Device = JSON.parse(deviceStr);
// Get the processes
const processes = await this.getProcesses();
// Get the shared secrets
const secrets = await this.getAllSecrets();
// Create a backup object
const backUp = {
device: device,
secrets: secrets,
processes: processes,
};
return backUp;
}
// Device 1 wait Device 2
public device1: boolean = false;
public device2Ready: boolean = false;
public resetState() {
this.device1 = false;
this.device2Ready = false;
}
public async handleHandshakeMsg(url: string, parsedMsg: any) {
try {
const handshakeMsg: HandshakeMessage = JSON.parse(parsedMsg);
this.updateRelay(url, handshakeMsg.sp_address);
const processes = handshakeMsg.processes_list;
this.membersList = handshakeMsg.peers_list;
// Write processes to db
setTimeout(async () => {
await this.restoreProcessesFromBackUp(processes);
}, 500)
} catch (e) {
console.error('Failed to parse init message:', e);
}
}
}