From 113f036838cbc1a09dc1fb747e7ba20187d5515e Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 18:56:59 +0100 Subject: [PATCH 1/6] Add restoreSecrets --- src/router.ts | 1 + src/services/service.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/router.ts b/src/router.ts index e451b5f..f600f40 100755 --- a/src/router.ts +++ b/src/router.ts @@ -104,6 +104,7 @@ async function init(): Promise { await services.restoreDevice(device); } await services.restoreProcesses(); + await services.restoreSecrets(); if (services.isPaired()) { await navigate('process'); diff --git a/src/services/service.ts b/src/services/service.ts index ac4a137..41d355a 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -488,6 +488,22 @@ export default class Services { } + public async restoreSecrets() { + const db = await Database.getInstance(); + try { + const sharedSecrets: Record = 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(): INotification[] { return [ { From b822f351fde7980547653b42f6784c24190f7e31 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 20:07:27 +0100 Subject: [PATCH 2/6] Pairing works --- src/services/modal.service.ts | 30 +++++++++--------------- src/services/service.ts | 43 ++++++++++++++++++++++++----------- src/utils/sp-address.utils.ts | 10 ++------ 3 files changed, 43 insertions(+), 40 deletions(-) diff --git a/src/services/modal.service.ts b/src/services/modal.service.ts index ee171ec..98ae36d 100755 --- a/src/services/modal.service.ts +++ b/src/services/modal.service.ts @@ -53,23 +53,11 @@ export default class ModalService { } // this is kind of too specific for pairing though - public async openConfirmationModal(pcd: any, commitmentTx: string, merkleRoot: string) { - let map: Record; - if (pcd['roles']) { - const roles = pcd['roles']; - try { - map = JSON.parse(roles); - } catch (e) { - throw new Error(`Failed to parse roles: ${e}`); - } - } else { - throw new Error('Pcd doesn\'t have a \"roles\" field'); - } - + public async openPairingConfirmationModal(roleDefinition: Record, commitmentTx: string, merkleRoot: string) { // pairing specifics let members; - if (map['owner']) { - const owner = map['owner']; + if (roleDefinition['owner']) { + const owner = roleDefinition['owner']; members = owner.members; } else { throw new Error('No \"owner\" role'); @@ -83,7 +71,6 @@ export default class ModalService { // We take all the addresses except our own const service = await Services.getInstance(); const localAddress = await service.getDeviceAddress(); - console.log('🚀 ~ Routing ~ openConfirmationModal ~ pcd:', pcd); for (const member of members) { for (const address of member['sp_addresses']) { if (address !== localAddress) { @@ -127,7 +114,7 @@ export default class ModalService { // We send the prd update if (this.currentPcdCommitment) { try { - const createPrdUpdateReturn = await service.createPrdUpdate(this.currentPcdCommitment); + const createPrdUpdateReturn = service.createPrdUpdate(this.currentPcdCommitment); await service.handleApiReturn(createPrdUpdateReturn); } catch (e) { throw e @@ -138,13 +125,18 @@ export default class ModalService { // We send confirmation that we validate the change try { - const approveChangeReturn = await service.approveChange(this.currentPcdCommitment!); + const approveChangeReturn = service.approveChange(this.currentPcdCommitment!); await service.handleApiReturn(approveChangeReturn); } catch (e) { throw e } - service.pairDevice(this.paired_addresses); + try { + service.pairDevice(this.paired_addresses); + } catch (e) { + throw e; + } + this.paired_addresses = []; this.currentOutpoint = null; this.currentPcdCommitment = null; diff --git a/src/services/service.ts b/src/services/service.ts index 41d355a..ae6eacc 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -3,7 +3,7 @@ import { INotification } from '~/models/notification.model'; import { IProcess } from '~/models/process.model'; // import Database from './database'; import { initWebsocket, sendMessage } from '../websockets'; -import { ApiReturn, Member, Process } from '../../dist/pkg/sdk_client'; +import { ApiReturn, Member, PcdUpdates, Process, RoleDefinition } from '../../dist/pkg/sdk_client'; import ModalService from './modal.service'; import { navigate } from '../router'; import Database from './database.service'; @@ -16,7 +16,7 @@ export default class Services { private static initializing: Promise | null = null; private static instance: Services; private currentProcess: string | null = null; - private pendingUpdates: Record = {}; + private pendingUpdates: PcdUpdates | null = null; private currentUpdateMerkleRoot: string | null = null; private localAddress: string | null = null; private pairedAddresses: string[] = []; @@ -265,10 +265,10 @@ export default class Services { public getUpdateProposals(commitmentOutpoint: string) { try { - const proposals: ApiReturn = this.sdkClient.get_update_proposals(commitmentOutpoint); + const proposals: PcdUpdates = this.sdkClient.get_update_proposals(commitmentOutpoint); if (proposals.decrypted_pcds && proposals.decrypted_pcds.length != 0) { this.currentProcess = commitmentOutpoint; - this.pendingUpdates = proposals.decrypted_pcds; + this.pendingUpdates = proposals; } else { throw new Error('No pending proposals'); } @@ -315,8 +315,13 @@ export default class Services { throw e; } - // do we need to act? - updatedProcess.user_validation_required + if (updatedProcess.modified_state || updatedProcess.new_state) { + this.currentProcess = updatedProcess.commitment_tx; + + this.getUpdateProposals(this.currentProcess!); + + await this.evaluatePendingUpdates(); + } } if (apiReturn.commit_to_send) { @@ -343,16 +348,28 @@ export default class Services { } private async openConfirmationModal() { - if (this.pendingUpdates.length === 0) { - console.log('No pending updates'); + if (!this.pendingUpdates || this.pendingUpdates.modified_values.length === 0) { + console.log('No pending updates to validate'); } - try { - for (const [merkleRoot, actual_proposal] of Object.entries(this.pendingUpdates)) { - await this.routingInstance.openConfirmationModal(actual_proposal, this.currentProcess!, merkleRoot); + for (const value of this.pendingUpdates!.modified_values) { + if (value.notify_user) { + // TODO notification pop up + } + if (!value.need_validation) { + continue; + } + if (value.proof) { + // It seems we already validated that, check the proof and if valid just notify user + continue; + } + const actualProposal: Record = JSON.parse(value.new_value); + const merkleRoot: string = value.new_state_merkle_root; + try { + await this.routingInstance.openPairingConfirmationModal(actualProposal, this.currentProcess!, merkleRoot); + } catch (e) { + throw new Error(`${e}`); } - } catch (e) { - throw new Error(`${e}`); } } diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index 1c8b535..1b54708 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -161,18 +161,12 @@ async function onOkButtonClick() { // Create the process const relayAddress = await service.getRelayAddresses(); // Get one (or more?) relay addresses const createPairingProcessReturn = await service.createPairingProcess([addressInput], relayAddress[0], 1); - await service.handleApiReturn(createPairingProcessReturn); - + if (!createPairingProcessReturn.updated_process) { throw new Error('createPairingProcess returned an empty new process'); // This should never happen } - const commitmentOutpoint = createPairingProcessReturn.updated_process.commitment_tx; - - // We set the service to the process - service.getUpdateProposals(commitmentOutpoint); - - await service.evaluatePendingUpdates(); + await service.handleApiReturn(createPairingProcessReturn); } catch (e) { console.error(`onOkButtonClick error: ${e}`); } From 9efc5b089acd81feae84c5c5771923530f74165b Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 20:07:56 +0100 Subject: [PATCH 3/6] Replace is_linked() by is_paired() --- src/services/service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/service.ts b/src/services/service.ts index ae6eacc..27ef0c1 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -69,7 +69,7 @@ export default class Services { public isPaired(): boolean { try { - return this.sdkClient.is_linking(); + return this.sdkClient.is_paired(); } catch (e) { throw new Error(`isPaired ~ Error: ${e}`); } From bd72302c25be4e31f4a9f51554b1c572ad77cdd0 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 20:08:32 +0100 Subject: [PATCH 4/6] Minor fixes --- src/services/service.ts | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/services/service.ts b/src/services/service.ts index 27ef0c1..097d6ad 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -215,17 +215,17 @@ export default class Services { } async sendNewTxMessage(message: string) { - await sendMessage('NewTx', message); + sendMessage('NewTx', message); } async sendCommitMessage(message: string) { - await sendMessage('Commit', message); + sendMessage('Commit', message); } async sendCipherMessages(ciphers: string[]) { for (let i = 0; i < ciphers.length; i++) { const cipher = ciphers[i]; - await sendMessage('Cipher', cipher); + sendMessage('Cipher', cipher); } } @@ -235,8 +235,8 @@ export default class Services { async parseCipher(message: string) { try { - console.log('parsing new cipher'); - const apiReturn = await this.sdkClient.parse_cipher(message); + // console.log('parsing new cipher'); + const apiReturn = this.sdkClient.parse_cipher(message); console.log('🚀 ~ Services ~ parseCipher ~ apiReturn:', apiReturn); await this.handleApiReturn(apiReturn); } catch (e) { @@ -247,12 +247,11 @@ export default class Services { async parseNewTx(tx: string) { try { - const parsedTx = await this.sdkClient.parse_new_tx(tx, 0); + const parsedTx = this.sdkClient.parse_new_tx(tx, 0); if (parsedTx) { - console.log('🚀 ~ Services ~ parseNewTx ~ parsedTx:', parsedTx); try { await this.handleApiReturn(parsedTx); - const newDevice = await this.dumpDevice(); + const newDevice = this.dumpDevice(); await this.saveDevice(newDevice); } catch (e) { console.error('Failed to update device with new tx'); @@ -296,11 +295,18 @@ export default class Services { } const entries = Object.entries(confirmedSecrets).map(([key, value]) => ({ key, value })); for (const entry of entries) { - db.addObject({ - storeName: 'shared_secrets', - object: entry.value, - key: entry.key, - }); + try { + 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); } } From d5aa9aff431e1d7d1a22f5a84a7f362b9064c78f Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 23:28:16 +0100 Subject: [PATCH 5/6] Add timeout on createPairingProcess to prevent double spends --- src/utils/sp-address.utils.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index 1b54708..3f14b15 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -159,14 +159,16 @@ async function onOkButtonClick() { await service.handleApiReturn(connectMemberResult); } // Create the process - const relayAddress = await service.getRelayAddresses(); // Get one (or more?) relay addresses - const createPairingProcessReturn = await service.createPairingProcess([addressInput], relayAddress[0], 1); + setTimeout(async () => { + const relayAddress = await service.getRelayAddresses(); // Get one (or more?) relay addresses + const createPairingProcessReturn = await service.createPairingProcess([addressInput], relayAddress[0], 1); - if (!createPairingProcessReturn.updated_process) { - throw new Error('createPairingProcess returned an empty new process'); // This should never happen - } + if (!createPairingProcessReturn.updated_process) { + throw new Error('createPairingProcess returned an empty new process'); // This should never happen + } - await service.handleApiReturn(createPairingProcessReturn); + await service.handleApiReturn(createPairingProcessReturn); + }, 1000); } catch (e) { console.error(`onOkButtonClick error: ${e}`); } From 5bbf0d33f51bc9ccdc1b2817d95bb751c51de26a Mon Sep 17 00:00:00 2001 From: Sosthene Date: Sat, 30 Nov 2024 23:28:51 +0100 Subject: [PATCH 6/6] Handle modified states --- src/services/service.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/services/service.ts b/src/services/service.ts index 097d6ad..baf3413 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -321,12 +321,24 @@ export default class Services { throw e; } - if (updatedProcess.modified_state || updatedProcess.new_state) { + if (updatedProcess.new_state) { this.currentProcess = updatedProcess.commitment_tx; this.getUpdateProposals(this.currentProcess!); await this.evaluatePendingUpdates(); + } else if (updatedProcess.modified_state) { + // We added validation tokens + // We check if the state is now valid + // If enough validation tokens we shoot a commit msg to the relay + const [previous_state, new_state] = updatedProcess.modified_state; + const init_commitment = updatedProcess.commitment_tx; + try { + const apiReturn = this.sdkClient.evaluate_state(init_commitment, null, JSON.stringify(new_state)); + await this.handleApiReturn(apiReturn); + } catch (e) { + throw e + } } }