From cfed50a2262a07e34371fb8148309e0d7578215f Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Thu, 17 Oct 2024 10:56:22 +0200 Subject: [PATCH] WIP --- src/html/confirmation-modal.js | 2 +- src/html/home.js | 46 ++++---- src/index.ts | 14 ++- src/services/database.ts | 188 -------------------------------- src/services/routing.service.ts | 32 ++++-- src/services/service.ts | 93 ++++++++-------- src/websockets.ts | 1 - vite.config.ts | 4 - 8 files changed, 104 insertions(+), 276 deletions(-) delete mode 100644 src/services/database.ts diff --git a/src/html/confirmation-modal.js b/src/html/confirmation-modal.js index 2349489..298ffd1 100644 --- a/src/html/confirmation-modal.js +++ b/src/html/confirmation-modal.js @@ -2,7 +2,7 @@ import Routing from "/src/services/routing.service.ts"; const router = await Routing.getInstance(); export async function confirm() { - router.confirm() + router.confirmPairing() } export async function closeConfirmationModal() { diff --git a/src/html/home.js b/src/html/home.js index 449a0c8..bc58957 100644 --- a/src/html/home.js +++ b/src/html/home.js @@ -58,30 +58,32 @@ docReady(function () { scanDevice(); var resultContainer = document.getElementById('qr-reader-results'); var lastResult, countResults = 0; - function onScanSuccess(decodedText, decodedResult) { - ++countResults; - lastResult = decodedText; - // Handle on success condition with the decoded message. - console.log(`Scan result ${decodedText}`, decodedResult); - try { - // Attempt to parse the decoded text as a URL - const scannedUrl = new URL(decodedText); + async function onScanSuccess(decodedText, decodedResult) { + if (lastResult === decodedText) { return; } + lastResult = decodedText; + ++countResults; + // Handle on success condition with the decoded message. + console.log(`Scan result ${decodedText}`, decodedResult); + try { + // Attempt to parse the decoded text as a URL + const scannedUrl = new URL(decodedText); - // Extract the 'sp_address' parameter - const spAddress = scannedUrl.searchParams.get('sp_address'); + // Extract the 'sp_address' parameter + const spAddress = scannedUrl.searchParams.get('sp_address'); - if (spAddress) { - // Call the sendPairingTx function with the extracted sp_address - service.sendPairingTx(spAddress); - } else { - console.error('The scanned URL does not contain the sp_address parameter.'); - alert('Invalid QR code: sp_address parameter missing.'); - } - } catch (error) { - // Handle cases where decodedText is not a valid URL - console.error('Scanned text is not a valid URL:', error); - alert('Invalid QR code: Unable to parse URL.'); - } + if (spAddress) { + html5QrcodeScanner.clear(); + // Call the sendPairingTx function with the extracted sp_address + await service.sendPairingTx(spAddress); + } else { + console.error('The scanned URL does not contain the sp_address parameter.'); + alert('Invalid QR code: sp_address parameter missing.'); + } + } catch (error) { + // Handle cases where decodedText is not a valid URL + console.error('Scanned text is not a valid URL:', error); + alert('Invalid QR code: Unable to parse URL.'); + } } var html5QrcodeScanner = new Html5QrcodeScanner( diff --git a/src/index.ts b/src/index.ts index c5a6018..f057c04 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,14 +15,16 @@ document.addEventListener('DOMContentLoaded', async () => { await services.restoreProcesses(); await services.restoreMessages(); - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString) - const pairingAddress = urlParams.get('sp_address') + if (services.isPaired()) { await services.injectProcessListPage() } + else { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString) + const pairingAddress = urlParams.get('sp_address') - if(pairingAddress) { - await services.sendPairingTx(pairingAddress) + if(pairingAddress) { + await services.sendPairingTx(pairingAddress) + } } - }, 500); } catch (error) { console.error(error); diff --git a/src/services/database.ts b/src/services/database.ts deleted file mode 100644 index a246429..0000000 --- a/src/services/database.ts +++ /dev/null @@ -1,188 +0,0 @@ -class Database { - private static instance: Database; - private db: IDBDatabase | null = null; - private dbName: string = '4nk'; - private dbVersion: number = 1; - private storeDefinitions = { - AnkUser: { - name: "user", - options: {'keyPath': 'pre_id'}, - indices: [] - }, - AnkSpAddress: { - name: "address", - options: {'keyPath': 'sp_address'}, - indices: [] - }, - AnkCipherMessages: { - name: "ciphers", - options: {}, - indices: [ {'keyPath': 'id'}] - }, - AnkSession: { - name: "session", - options: {}, - indices: [] - }, - AnkProcess: { - name: "process", - options: {'keyPath': 'id'}, - indices: [{ - name: 'by_name', - keyPath: 'name', - options: { - 'unique': true - } - }] - }, - AnkMessages: { - name: "messages", - options: {'keyPath': 'id'}, - indices: [] - } - } - - // Private constructor to prevent direct instantiation from outside - private constructor() {} - - // Method to access the singleton instance of Database - public static async getInstance(): Promise { - if (!Database.instance) { - Database.instance = new Database(); - await Database.instance.init(); - } - return Database.instance; - } - - // Initialize the database - private async init(): Promise { - return new Promise((resolve, reject) => { - const request = indexedDB.open(this.dbName, this.dbVersion); - - request.onupgradeneeded = () => { - const db = request.result; - - Object.values(this.storeDefinitions).forEach(({name, options, indices}) => { - if (!db.objectStoreNames.contains(name)) { - let store = db.createObjectStore(name, options); - - indices.forEach(({name, keyPath, options}) => { - store.createIndex(name, keyPath, options); - }) - } - }); - }; - - request.onsuccess = () => { - this.db = request.result; - resolve(); - }; - - request.onerror = () => { - console.error("Database error:", request.error); - reject(request.error); - }; - }); - } - - public async getDb(): Promise { - if (!this.db) { - await this.init(); - } - return this.db!; - } - - public getStoreList(): {[key: string]: string} { - const objectList: {[key: string]: string} = {}; - Object.keys(this.storeDefinitions).forEach(key => { - objectList[key] = this.storeDefinitions[key as keyof typeof this.storeDefinitions].name; - }); - return objectList; - } - - public writeObject(db: IDBDatabase, storeName: string, obj: any, key: IDBValidKey | null): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readwrite'); - const store = transaction.objectStore(storeName); - let request: IDBRequest; - if (key) { - request = store.add(obj, key); - } else { - request = store.add(obj); - } - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } - - public getObject(db: IDBDatabase, storeName: string, key: IDBValidKey): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readonly'); - const store = transaction.objectStore(storeName); - const request = store.get(key); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } - - public rmObject(db: IDBDatabase, storeName: string, key: IDBValidKey): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readwrite'); - const store = transaction.objectStore(storeName); - const request = store.delete(key); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } - - public getFirstMatchWithIndex(db: IDBDatabase, storeName: string, indexName: string, lookup: string): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readonly'); - const store = transaction.objectStore(storeName); - const index = store.index(indexName); - const request = index.openCursor(IDBKeyRange.only(lookup)); - - request.onerror = () => reject(request.error); - request.onsuccess = () => { - const cursor = request.result; - if (cursor) { - resolve(cursor.value); - } else { - resolve(null) - } - } - }); - } - - public setObject(db: IDBDatabase, storeName: string, obj: any, key: string | null): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readwrite'); - const store = transaction.objectStore(storeName); - let request: IDBRequest; - if (key) { - request = store.put(obj, key); - } else { - request = store.put(obj); - } - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } - - public getAll(db: IDBDatabase, storeName: string): Promise { - return new Promise((resolve, reject) => { - const transaction = db.transaction(storeName, 'readonly'); - const store = transaction.objectStore(storeName); - const request = store.getAll(); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } -} - -export default Database; diff --git a/src/services/routing.service.ts b/src/services/routing.service.ts index 204675e..8d362b2 100644 --- a/src/services/routing.service.ts +++ b/src/services/routing.service.ts @@ -1,14 +1,13 @@ -import Database from './database'; import modalHtml from '../html/login-modal.html?raw'; import confirmationModalHtml from '../html/confirmation-modal.html?raw'; import modalScript from '../html/login-modal.js?raw'; import confirmationModalScript from '../html/confirmation-modal.js?raw'; import Services from './service'; +import { U32_MAX } from './service'; export default class Routing { private static instance: Routing; - private database: any; private sdkClient: any; private currentPrd: any; private currentOutpoint?: string; @@ -25,7 +24,6 @@ export default class Routing { public async init(): Promise { this.sdkClient = await import("../../dist/pkg/sdk_client"); - this.database = Database.getInstance() } public openLoginModal(myAddress: string, receiverAddress: string) { @@ -45,10 +43,18 @@ export default class Routing { } public openConfirmationModal(pcd: any, outpointCommitment: string) { + console.log("") let roles = JSON.parse(pcd['roles']);// ['members'][0]; console.log(roles); let members = roles['owner']['members']; console.log(members); + // We take all the addresses except our own + const local_address = this.sdkClient.get_address(); + console.log("πŸš€ ~ Routing ~ openConfirmationModal ~ pcd:", pcd) + let paired_addresses: string[] = []; + for (const address of members[0]['sp_addresses']) { + if (address !== local_address) { paired_addresses.push(address) } + } let html = confirmationModalHtml; // html = html.replace('{{device1}}', members[0]['sp_addresses'][0]); // html = html.replace('{{device2}}', members[0]['sp_addresses'][1]); @@ -65,11 +71,11 @@ export default class Routing { // Add correct text - // Close modal when clicking outside of it + // Close modal when clicking outside of it window.onclick = (event) => { const modal = document.getElementById('modal'); if (event.target === modal) { - this.confirm(pcd, outpointCommitment); + this.confirmPairing(paired_addresses); } } } @@ -84,13 +90,21 @@ export default class Routing { if (modal) modal.style.display = 'none'; } - async confirm(prd: any, outpointCommitment: string) { + async confirmPairing(paired_addresses: string[]) { const service = await Services.getInstance() const modal = document.getElementById('modal') - console.log("πŸš€ ~ Routing ~ confirm ~ prd:", prd, outpointCommitment) + // console.log("πŸš€ ~ Routing ~ confirm ~ prd:", prd) if (modal) modal.style.display = 'none'; - await service.pairDevice(prd, outpointCommitment); - } + // Just make an empty commitment for now + const emptyTxid = '0'.repeat(64) + const commitmentOutpoint = `${emptyTxid}:${U32_MAX}`; + + // We take the paired device(s) from the contract + this.sdkClient.pair_device(commitmentOutpoint, paired_addresses) + + service.injectProcessListPage(); + } + async closeConfirmationModal() { const modal = document.getElementById('modal') if (modal) modal.style.display = 'none'; diff --git a/src/services/service.ts b/src/services/service.ts index 40fabb4..a4f6c4e 100644 --- a/src/services/service.ts +++ b/src/services/service.ts @@ -16,7 +16,7 @@ type ProcessesCache = { [key: string]: any; }; -const U32_MAX = 4294967295; +export const U32_MAX = 4294967295; const wsurl = `https://demo.4nkweb.com/ws/`; export default class Services { @@ -83,11 +83,6 @@ export default class Services { newScript.setAttribute('type', 'module') newScript.textContent = homeScript; document.head.appendChild(newScript).parentNode?.removeChild(newScript); - - const btn = container.querySelector('#scan-this-device') - if(btn) { - this.addSubscription(btn, 'click', 'injectProcessListPage') - } } private generateQRCode = async (text: string) => { @@ -103,6 +98,14 @@ export default class Services { } } + public isPaired(): boolean | undefined { + try { + return this.sdkClient.is_linking(); + } catch (e) { + console.error("isPaired ~ Error:", e); + } + } + private prepareProcessTx(myAddress: string, recipientAddress: string) { const initial_session_privkey = new Uint8Array(32); const initial_session_pubkey = new Uint8Array(32); @@ -281,48 +284,48 @@ export default class Services { async pairDevice(prd: any, outpointCommitment: string) { console.log("πŸš€ ~ Services ~ pairDevice ~ prd:", prd) - const service = await Services.getInstance(); - const spAddress = await this.getDeviceAddress() as any; - const sender = JSON.parse(prd?.sender) - const senderAddress = sender?.sp_addresses?.find((address: string) => address !== spAddress) - console.log("πŸš€ ~ Services ~ pairDevice ~ senderAddress:", senderAddress) - if(senderAddress) { - const proposal = service.sdkClient.get_update_proposals(outpointCommitment); - console.log("πŸš€ ~ Services ~ pairDevice ~ proposal:", proposal) - // const pairingTx = proposal.pairing_tx.replace(/^\"+|\"+$/g, '') - const parsedProposal = JSON.parse(proposal[0]) + // const service = await Services.getInstance(); + // const spAddress = await this.getDeviceAddress() as any; + // const sender = JSON.parse(prd?.sender) + // const senderAddress = sender?.sp_addresses?.find((address: string) => address !== spAddress) + // console.log("πŸš€ ~ Services ~ pairDevice ~ senderAddress:", senderAddress) + // if(senderAddress) { + // const proposal = service.sdkClient.get_update_proposals(outpointCommitment); + // console.log("πŸš€ ~ Services ~ pairDevice ~ proposal:", proposal) + // // const pairingTx = proposal.pairing_tx.replace(/^\"+|\"+$/g, '') + // const parsedProposal = JSON.parse(proposal[0]) - console.log("πŸš€ ~ Services ~ pairDevice ~ parsedProposal:", parsedProposal) - const roles = JSON.parse(parsedProposal.roles) - console.log("πŸš€ ~ Services ~ pairDevice ~ roles:", roles, Array.isArray(roles), !roles.owner) - if(Array.isArray(roles) || !roles.owner) return - const proposalMembers = roles?.owner?.members - const isFirstDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === spAddress)) - const isSecondDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === senderAddress)) - console.log("πŸš€ ~ Services ~ pairDevice ~ proposalMembers:", proposalMembers) - if(proposalMembers?.length !== 2 || !isFirstDevice || !isSecondDevice) return - const pairingTx = parsedProposal?.pairing_tx?.replace(/^\"+|\"+$/g, '') + // console.log("πŸš€ ~ Services ~ pairDevice ~ parsedProposal:", parsedProposal) + // const roles = JSON.parse(parsedProposal.roles) + // console.log("πŸš€ ~ Services ~ pairDevice ~ roles:", roles, Array.isArray(roles), !roles.owner) + // if(Array.isArray(roles) || !roles.owner) return + // const proposalMembers = roles?.owner?.members + // const isFirstDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === spAddress)) + // const isSecondDevice = proposalMembers.some((member: Member) => member.sp_addresses.some(address => address === senderAddress)) + // console.log("πŸš€ ~ Services ~ pairDevice ~ proposalMembers:", proposalMembers) + // if(proposalMembers?.length !== 2 || !isFirstDevice || !isSecondDevice) return + // const pairingTx = parsedProposal?.pairing_tx?.replace(/^\"+|\"+$/g, '') - let txid = '0'.repeat(64) - console.log("πŸš€ ~ Services ~ pairDevice ~ pairingTx:", pairingTx, `${txid}:4294967295`) + // let txid = '0'.repeat(64) + // console.log("πŸš€ ~ Services ~ pairDevice ~ pairingTx:", pairingTx, `${txid}:4294967295`) - const pairing = await service.sdkClient.pair_device(`${txid}:4294967295`, [senderAddress]) - const device = this.dumpDevice() - console.log("πŸš€ ~ Services ~ pairDevice ~ device:", device) - this.saveDevice(device) - // await service.sdkClient.pair_device(pairingTx, [senderAddress]) - console.log("πŸš€ ~ Services ~ pairDevice ~ pairing:", pairing) - // const process = await this.prepareProcessTx(spAddress, senderAddress) - console.log("πŸš€ ~ Services ~ pairDevice ~ process:", outpointCommitment, prd, prd.payload) - const prdString = JSON.stringify(prd).trim() - console.log("πŸš€ ~ Services ~ pairDevice ~ prdString:", prdString) - let tx = await service.sdkClient.response_prd(outpointCommitment, prdString, true) - console.log("πŸš€ ~ Services ~ pairDevice ~ tx:", tx) - if(tx.ciphers_to_send) { - tx.ciphers_to_send.forEach((cipher: string) => service.websocketConnection?.sendMessage('Cipher', cipher)) - } - this.injectProcessListPage() - } + // const pairing = await service.sdkClient.pair_device(`${txid}:4294967295`, [senderAddress]) + // const device = this.dumpDevice() + // console.log("πŸš€ ~ Services ~ pairDevice ~ device:", device) + // this.saveDevice(device) + // // await service.sdkClient.pair_device(pairingTx, [senderAddress]) + // console.log("πŸš€ ~ Services ~ pairDevice ~ pairing:", pairing) + // // const process = await this.prepareProcessTx(spAddress, senderAddress) + // console.log("πŸš€ ~ Services ~ pairDevice ~ process:", outpointCommitment, prd, prd.payload) + // const prdString = JSON.stringify(prd).trim() + // console.log("πŸš€ ~ Services ~ pairDevice ~ prdString:", prdString) + // let tx = await service.sdkClient.response_prd(outpointCommitment, prdString, true) + // console.log("πŸš€ ~ Services ~ pairDevice ~ tx:", tx) + // if(tx.ciphers_to_send) { + // tx.ciphers_to_send.forEach((cipher: string) => service.websocketConnection?.sendMessage('Cipher', cipher)) + // } + // this.injectProcessListPage() + // } } // async saveTxToDb(tx: CachedMessage) { diff --git a/src/websockets.ts b/src/websockets.ts index d1b45f2..83236ed 100644 --- a/src/websockets.ts +++ b/src/websockets.ts @@ -1,6 +1,5 @@ import { AnkFlag, CachedMessage } from "dist/pkg/sdk_client"; import Services from "./services/service"; -import Database from "./services/database"; // import { AnkFlag, AnkNetworkMsg, CachedMessage } from "../dist/pkg/sdk_client"; class WebSocketClient { diff --git a/vite.config.ts b/vite.config.ts index 7a4c68a..4793531 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -38,9 +38,5 @@ export default defineConfig({ open: false, port: 3001, strictPort: true, // EmpΓͺche de changer de port si le 3001 est occupΓ© - hmr: { - port: 9050, // Assure que le WebSocket HMR utilise bien le mΓͺme port - protocol: 'wss', - } }, }); \ No newline at end of file