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/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'); diff --git a/src/services/service.ts b/src/services/service.ts index 706ef73..8596d76 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'); @@ -147,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() { @@ -252,25 +268,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); 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); }