From bc0b4a86f43827845dcaa95212aaad9b3baf3a62 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Fri, 22 Nov 2024 16:12:12 +0100 Subject: [PATCH 1/6] Call connectMember before pairing --- src/pages/home/home.ts | 10 +++++++++- src/router.ts | 9 ++++++++- src/utils/sp-address.utils.ts | 10 +++++++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index fb6c9a3..f2d9bad 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -55,7 +55,15 @@ async function onScanSuccess(decodedText: any, decodedResult: any) { html5QrcodeScanner.clear(); const service = await Services.getInstance(); // Call the sendPairingTx function with the extracted sp_address - await service.sendPairingTx(spAddress); + try { + const member = { + sp_addresses: [spAddress], + } + service.connectMember([member]); + // await service.sendPairingTx(spAddress); + } catch (e) { + console.error('Failed to pair:', e); + } } else { console.error('The scanned URL does not contain the sp_address parameter.'); alert('Invalid QR code: sp_address parameter missing.'); diff --git a/src/router.ts b/src/router.ts index 6b3d8fa..729fc1b 100755 --- a/src/router.ts +++ b/src/router.ts @@ -112,7 +112,14 @@ async function init(): Promise { const urlParams = new URLSearchParams(queryString); const pairingAddress = urlParams.get('sp_address'); if (pairingAddress) { - setTimeout(async () => await services.sendPairingTx(pairingAddress), 2000); + setTimeout(async () => { + // await services.sendPairingTx(pairingAddress) + try { + services.connectMember([{sp_addresses: [pairingAddress]}]); + } catch (e) { + console.error('Failed to pair:', e); + } + }, 2000); } await navigate('home'); } diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index e05471a..93b5b4d 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -148,7 +148,15 @@ export function initAddressInput() { async function onOkButtonClick() { const service = await Services.getInstance(); const addressInput = (document.getElementById('addressInput') as HTMLInputElement).value; - await service.sendPairingTx(addressInput); + const member = { + sp_addresses: [addressInput], + } + try { + service.connectMember([member]); + // await service.sendPairingTx(addressInput); + } catch (e) { + console.error('onOkButtonClick error:', e); + } } export async function generateQRCode(spAddress: string) { From 038a2e349831fb456231bb7655c48a8781a9b412 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Mon, 25 Nov 2024 21:24:04 +0100 Subject: [PATCH 2/6] Add getSecretForAddress --- src/services/service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/services/service.ts b/src/services/service.ts index 706ef73..2736503 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -103,6 +103,11 @@ export default class Services { return apiReturn; } + public async getSecretForAddress(address: string): Promise { + const db = await Database.getInstance(); + return await db.getObject('shared_secrets', address); + } + public async connectMember(members: Member[]): Promise { if (members.length === 0) { throw new Error('Trying to connect to empty members list'); From f99f9a6199e75e17276e1ce9f04864fb307418b7 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Mon, 25 Nov 2024 21:34:39 +0100 Subject: [PATCH 3/6] Fix handleApiReturn --- src/services/service.ts | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/services/service.ts b/src/services/service.ts index 2736503..1a87fc2 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -257,25 +257,23 @@ export default class Services { setTimeout(async () => { if (apiReturn.updated_process && apiReturn.updated_process.length) { - const [processCommitment, process] = apiReturn.updated_process; - console.debug('Updated Process Commitment:', processCommitment); - console.debug('Process Details:', process); + const [commitmentTx, process] = apiReturn.updated_process; // Save process to storage - localStorage.setItem(processCommitment, JSON.stringify(process)); const db = await Database.getInstance(); db.addObject({ storeName: 'process', - object: { id: processCommitment, process }, - key: processCommitment, + object: { id: commitmentTx, process }, + key: null, }); // Check if the newly updated process reveals some new information try { - const proposals: string[] = this.sdkClient.get_update_proposals(processCommitment); - if (proposals && proposals.length != 0) { - const actual_proposal = JSON.parse(proposals[0]); // We just don't acknowledge concurrent proposals for now - console.info(actual_proposal); - await this.routingInstance.openConfirmationModal(actual_proposal, processCommitment); + const proposals: ApiReturn = this.sdkClient.get_update_proposals(commitmentTx); + const decrypted_pcds = proposals.decrypted_pcds; + if (decrypted_pcds && decrypted_pcds.length != 0) { + for (const actual_proposal of Object.values(decrypted_pcds)) { + await this.routingInstance.openConfirmationModal(actual_proposal, commitmentTx); + } } } catch (e) { console.error(e); From da32410c71b9c5efd53033294170b03ddaaf1810 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Mon, 25 Nov 2024 21:35:17 +0100 Subject: [PATCH 4/6] Add createPairingProcess --- src/services/service.ts | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/services/service.ts b/src/services/service.ts index 1a87fc2..8596d76 100755 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -152,24 +152,35 @@ export default class Services { } } - public async sendPairingTx(spAddress: string): Promise { - const localAddress = this.sdkClient.get_address(); - const emptyTxid = '0'.repeat(64); - + public async createPairingProcess(pairWith: string[], relayAddress: string, feeRate: number): Promise { + 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, + }, + ], + }, + }, + session_privkey: newKey['private_key'], + session_pubkey: newKey['x_only_public_key'], + key_parity: newKey['key_parity'], + }; try { - let commitmentOutpoint = `${emptyTxid}:${U32_MAX}`; - this.sdkClient.pair_device(commitmentOutpoint, [spAddress]); + const newProcessReturn = this.sdkClient.create_new_process(JSON.stringify(pairingTemplate), relayAddress, feeRate); + console.log('newProcessReturn:', newProcessReturn); + await this.handleApiReturn(newProcessReturn); } catch (e) { - console.error('Services ~ Error:', e); - return; + throw new Error(`Creating process failed:, ${e}`); } - - setTimeout(async () => { - const apiReturn = this.prepareProcessTx(localAddress, spAddress); - await this.handleApiReturn(apiReturn); - }, 1500); - - return; } async resetDevice() { From d923a188e61adf5baa7a34bf7f3f885e714619f7 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Mon, 25 Nov 2024 21:35:26 +0100 Subject: [PATCH 5/6] Fix confirmation modal --- src/services/modal.service.ts | 44 +++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/services/modal.service.ts b/src/services/modal.service.ts index 52b6545..fc6c155 100755 --- a/src/services/modal.service.ts +++ b/src/services/modal.service.ts @@ -4,11 +4,12 @@ import Services from './service'; import { U32_MAX } from './service'; import { navigate } from '../router'; import { addressToEmoji } from '../utils/sp-address.utils'; +import { RoleDefinition } from 'dist/pkg/sdk_client'; export default class ModalService { private static instance: ModalService; private currentPrd: any; - private currentOutpoint?: string; + private currentOutpoint: string | null = null; private constructor() {} private paired_addresses: string[] = []; @@ -51,11 +52,31 @@ export default class ModalService { } } - public async openConfirmationModal(pcd: any, outpointCommitment: string) { - let roles = JSON.parse(pcd['roles']); // ['members'][0]; - console.log(roles); - let members = roles['owner']['members']; - console.log(members); + public async openConfirmationModal(pcd: any, commitmentTx: 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'); + } + + let members; + if (map['owner']) { + const owner = map['owner']; + members = owner.members; + } else { + throw new Error('No \"owner\" role'); + } + + if (members.length != 1) { + throw new Error('Must have exactly 1 member'); + } + // We take all the addresses except our own const service = await Services.getInstance(); const localAddress = await service.getDeviceAddress(); @@ -65,6 +86,7 @@ export default class ModalService { this.paired_addresses.push(address); } } + this.currentOutpoint = commitmentTx; await this.injectModal(members); const modal = document.getElementById('modal'); if (modal) modal.style.display = 'flex'; @@ -96,13 +118,15 @@ export default class ModalService { const modal = document.getElementById('modal'); // console.log("🚀 ~ Routing ~ confirm ~ prd:", prd) if (modal) modal.style.display = 'none'; - // Just make an empty commitment for now - const emptyTxid = '0'.repeat(64); - const commitmentOutpoint = `${emptyTxid}:${U32_MAX}`; + + if (this.currentOutpoint === null || this.paired_addresses.length === 0) { + throw new Error('Missing outpoint and/or paired addresses'); + } // We take the paired device(s) from the contract - await service.pairDevice(commitmentOutpoint, this.paired_addresses); + await service.pairDevice(this.currentOutpoint, this.paired_addresses); this.paired_addresses = []; + this.currentOutpoint = null; const newDevice = await service.dumpDevice(); await service.saveDevice(newDevice); navigate('process'); From 85931ba350c6ab2e2b77c267a694708b1de0e441 Mon Sep 17 00:00:00 2001 From: Sosthene Date: Mon, 25 Nov 2024 21:35:45 +0100 Subject: [PATCH 6/6] Check before creating shared secret --- src/pages/home/home.ts | 11 +++++++---- src/router.ts | 7 +++++-- src/utils/sp-address.utils.ts | 13 ++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/pages/home/home.ts b/src/pages/home/home.ts index 9ea6fe6..6ffa967 100755 --- a/src/pages/home/home.ts +++ b/src/pages/home/home.ts @@ -55,11 +55,14 @@ async function onScanSuccess(decodedText: any, decodedResult: any) { html5QrcodeScanner.clear(); const service = await Services.getInstance(); // Call the sendPairingTx function with the extracted sp_address - try { - const member = { - sp_addresses: [spAddress], + try { + const sharedSecret = await service.getSecretForAddress(spAddress); + if (!sharedSecret) { + const member = { + sp_addresses: [spAddress], + } + await service.connectMember([member]); } - await service.connectMember([member]); // await service.sendPairingTx(spAddress); } catch (e) { console.error('Failed to pair:', e); diff --git a/src/router.ts b/src/router.ts index 5092575..e451b5f 100755 --- a/src/router.ts +++ b/src/router.ts @@ -113,9 +113,12 @@ async function init(): Promise { const pairingAddress = urlParams.get('sp_address'); if (pairingAddress) { setTimeout(async () => { - // await services.sendPairingTx(pairingAddress) try { - await services.connectMember([{sp_addresses: [pairingAddress]}]); + // check if we have a shared secret with that address + const sharedSecret = await services.getSecretForAddress(pairingAddress); + if (!sharedSecret) { + await services.connectMember([{sp_addresses: [pairingAddress]}]); + } } catch (e) { console.error('Failed to pair:', e); } diff --git a/src/utils/sp-address.utils.ts b/src/utils/sp-address.utils.ts index d34fe60..b0e2cc4 100755 --- a/src/utils/sp-address.utils.ts +++ b/src/utils/sp-address.utils.ts @@ -148,12 +148,15 @@ export function initAddressInput() { async function onOkButtonClick() { const service = await Services.getInstance(); const addressInput = (document.getElementById('addressInput') as HTMLInputElement).value; - const member = { - sp_addresses: [addressInput], - } try { - await service.connectMember([member]); - // await service.sendPairingTx(addressInput); + const sharedSecret = await service.getSecretForAddress(addressInput); + if (!sharedSecret) { + const member = { + sp_addresses: [addressInput], + } + await service.connectMember([member]); + } + await service.createPairingProcess([addressInput], "sprt1qqdg4x69xdyhxpz4weuel0985qyswa0x9ycl4q6xc0fngf78jtj27gqj5vff4fvlt3fydx4g7vv0mh7vqv8jncgusp6n2zv860nufdzkyy59pqrdr", 1); } catch (e) { console.error('onOkButtonClick error:', e); }