One device pairing

This commit is contained in:
NicolasCantu 2025-03-26 14:39:59 +01:00
parent 2855365851
commit f2e2aeaa9a
4 changed files with 163 additions and 129 deletions

View File

@ -135,7 +135,6 @@ export async function init(): Promise<void> {
const services = await Services.getInstance(); const services = await Services.getInstance();
(window as any).myService = services; (window as any).myService = services;
await Database.getInstance(); await Database.getInstance();
setTimeout(async () => {
let device = await services.getDeviceFromDatabase(); let device = await services.getDeviceFromDatabase();
console.log('🚀 ~ setTimeout ~ device:', device); console.log('🚀 ~ setTimeout ~ device:', device);
@ -147,11 +146,37 @@ export async function init(): Promise<void> {
await services.restoreProcessesFromDB(); await services.restoreProcessesFromDB();
await services.restoreSecretsFromDB(); await services.restoreSecretsFromDB();
if (services.isPaired()) { // If we have a service redirection flag, we intercept it here
await navigate('chat');
} else {
const queryString = window.location.search; const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString); const urlParams = new URLSearchParams(queryString);
const source = urlParams.get('source');
if (source === "lecoffre") {
console.log('Detected source \'lecoffre\'')
// We pair first if necessary
if (!services.isPaired()) {
await prepareAndSendPairingTx(false);
await services.confirmPairing();
}
if (services.isPaired()) {
// pop up to inform user that they will be redirected to lecoffre with their 4nk identity
alert("User successfully created, you will be redirected to lecoffre.io");
// Once paired we get a copy of the device without the spend key
const neuteredDevice = services.dumpNeuteredDevice();
if (!neuteredDevice) {
console.error('Failed to get a neutered device');
return;
}
// We send the neutered device to the main frame
window.parent.postMessage({ type: 'RESPONSE', payload: JSON.stringify(neuteredDevice) }, 'https://lecoffreio.4nkweb.com');
}
} else {
console.log('No source provided');
}
if (services.isPaired()) {
await navigate('account');
} else {
const pairingAddress = urlParams.get('sp_address'); const pairingAddress = urlParams.get('sp_address');
if (pairingAddress) { if (pairingAddress) {
setTimeout(async () => { setTimeout(async () => {
@ -161,11 +186,12 @@ export async function init(): Promise<void> {
} catch (e) { } catch (e) {
console.error('Failed to pair:', e); console.error('Failed to pair:', e);
} }
await navigate('account');
}, 2000); }, 2000);
} }
console.log('here');
await navigate('home'); await navigate('home');
} }
}, 200);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
await navigate('home'); await navigate('home');

View File

@ -164,65 +164,13 @@ export default class ModalService {
if (this.modal) this.modal.style.display = 'none'; if (this.modal) this.modal.style.display = 'none';
} }
async confirmPairing() { // async confirmPairing() {
const service = await Services.getInstance(); // const service = await Services.getInstance();
if (this.modal) this.modal.style.display = 'none'; // if (this.modal) this.modal.style.display = 'none';
if (service.device1) {
console.log("Device 1 detected");
// We send the prd update
if (this.stateId && this.processId) {
try {
// Device B shouldn't do this again
const createPrdUpdateReturn = service.createPrdUpdate(this.processId, this.stateId);
await service.handleApiReturn(createPrdUpdateReturn);
} catch (e) {
throw e;
}
} else {
throw new Error('No currentPcdCommitment');
}
// We send confirmation that we validate the change
try {
const approveChangeReturn = await service.approveChange(this.processId!, this.stateId!);
await service.handleApiReturn(approveChangeReturn);
await this.injectWaitingModal();
const waitingModal = document.getElementById('waiting-modal');
if (waitingModal) waitingModal.style.display = 'flex';
if (!service.device2Ready) {
while (!service.device2Ready) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log("Device 2 is ready - Device 1 can now proceed");
}
service.pairDevice(this.paired_addresses, this.processId);
this.paired_addresses = [];
this.processId = null;
this.stateId = null;
const newDevice = service.dumpDeviceFromMemory();
console.log(newDevice);
await service.saveDeviceInDatabase(newDevice);
navigate('chat');
service.resetState();
} catch (e) {
throw e;
}
// try {
// service.pairDevice(this.paired_addresses);
// } catch (e) {
// throw e;
// }
} else {
console.log("Device 2 detected");
// // We send the prd update
// if (this.stateId && this.processId) { // if (this.stateId && this.processId) {
// try { // try {
// // Device B shouldn't do this again
// const createPrdUpdateReturn = service.createPrdUpdate(this.processId, this.stateId); // const createPrdUpdateReturn = service.createPrdUpdate(this.processId, this.stateId);
// await service.handleApiReturn(createPrdUpdateReturn); // await service.handleApiReturn(createPrdUpdateReturn);
// } catch (e) { // } catch (e) {
@ -232,25 +180,21 @@ export default class ModalService {
// throw new Error('No currentPcdCommitment'); // throw new Error('No currentPcdCommitment');
// } // }
// We send confirmation that we validate the change // try {
try { // const approveChangeReturn = await service.approveChange(this.processId!, this.stateId!);
const approveChangeReturn = await service.approveChange(this.processId!, this.stateId!); // await service.handleApiReturn(approveChangeReturn);
await service.handleApiReturn(approveChangeReturn);
} catch (e) {
throw e;
}
service.pairDevice(this.paired_addresses, this.processId!); // service.pairDevice(this.paired_addresses, this.processId);
// this.paired_addresses = [];
this.paired_addresses = []; // this.processId = null;
this.processId = null; // this.stateId = null;
this.stateId = null; // const newDevice = service.dumpDeviceFromMemory();
const newDevice = service.dumpDeviceFromMemory(); // console.log(newDevice);
console.log(newDevice); // await service.saveDeviceInDatabase(newDevice);
await service.saveDeviceInDatabase(newDevice); // } catch (e) {
navigate('chat'); // throw e;
} // }
} // }
async closeConfirmationModal() { async closeConfirmationModal() {
const service = await Services.getInstance(); const service = await Services.getInstance();

View File

@ -20,8 +20,8 @@ const EMPTY32BYTES = String('').padStart(64, '0');
export default class Services { export default class Services {
private static initializing: Promise<Services> | null = null; private static initializing: Promise<Services> | null = null;
private static instance: Services; private static instance: Services;
private currentProcess: string | null = null; private processId: string | null = null;
private currentUpdateMerkleRoot: string | null = null; private stateId: string | null = null;
private sdkClient: any; private sdkClient: any;
private myProcesses: Set = new Set(); private myProcesses: Set = new Set();
private notifications: any[] | null = null; private notifications: any[] | null = null;
@ -64,6 +64,24 @@ export default class Services {
await this.connectAllRelays(); await this.connectAllRelays();
} }
public setProcessId(processId: string | null) {
console.log('this.processId', this.processId);
this.processId = processId;
}
public setStateId(stateId: string | null) {
console.log('this.stateId', this.stateId);
this.stateId = stateId;
}
public getProcessId(): string | null {
return this.processId;
}
public getStateId(): string | null {
return this.stateId;
}
/** /**
* Calls `this.addWebsocketConnection` for each `wsurl` in relayAddresses. * Calls `this.addWebsocketConnection` for each `wsurl` in relayAddresses.
*/ */
@ -246,11 +264,11 @@ export default class Services {
pairWith.push(myAddress); pairWith.push(myAddress);
const roles: Record<string, RoleDefinition> = { const roles: Record<string, RoleDefinition> = {
pairing: { pairing: {
members: [{ sp_addresses: pairWith }], members: [],
validation_rules: [ validation_rules: [
{ {
quorum: 1.0, quorum: 1.0,
fields: ['description', 'counter', 'roles', 'memberPublicName'], fields: ['description', 'counter', 'roles', 'memberPublicName', 'pairedAddresses'],
min_sig_member: 1.0, min_sig_member: 1.0,
}, },
], ],
@ -262,15 +280,17 @@ export default class Services {
counter: 0, counter: 0,
}; };
const publicData = { const publicData = {
memberPublicName: userName memberPublicName: userName,
} pairedAddresses: pairWith,
};
try { try {
return this.sdkClient.create_new_process( return this.sdkClient.create_new_process(
pairingTemplate, pairingTemplate,
roles, roles,
publicData, publicData,
relayAddress, relayAddress,
feeRate feeRate,
this.getAllMembers()
); );
} catch (e) { } catch (e) {
throw new Error(`Creating process failed:, ${e}`); throw new Error(`Creating process failed:, ${e}`);
@ -719,9 +739,51 @@ export default class Services {
} }
} }
pairDevice(spAddressList: string[], pairingProcess: string) { public async confirmPairing() {
if (!this.processId || !this.stateId) {
console.error('Missing process and/or state ID');
return;
}
let createPrdUpdateReturn;
try { try {
this.sdkClient.pair_device(pairingProcess, spAddressList); createPrdUpdateReturn = await this.createPrdUpdate(this.processId, this.stateId);
} catch (e) {
throw new Error(`createPrdUpdate failed: ${e}`);
}
await this.handleApiReturn(createPrdUpdateReturn);
let approveChangeReturn;
try {
approveChangeReturn = await this.approveChange(this.processId, this.stateId);
} catch (e) {
throw new Error(`approveChange failed: ${e}`);
}
await this.handleApiReturn(approveChangeReturn);
await this.pairDevice();
this.processId = null;
this.stateId = null;
const newDevice = this.dumpDeviceFromMemory();
console.log(newDevice);
await this.saveDeviceInDatabase(newDevice);
navigate('account');
}
public async pairDevice() {
if (!this.processId) {
console.error('No processId set');
return;
}
const process = await this.getProcess(this.processId);
if (!process) {
console.error('Unknown process');
return;
}
const spAddressList = process.states[0].public_data['pairedAddresses'];
console.log(spAddressList);
try {
this.sdkClient.pair_device(this.processId, spAddressList);
} catch (e) { } catch (e) {
throw new Error(`Failed to pair device: ${e}`); throw new Error(`Failed to pair device: ${e}`);
} }

View File

@ -168,17 +168,16 @@ async function onOkButtonClick() {
async function onCreateButtonClick() { async function onCreateButtonClick() {
try { try {
await prepareAndSendPairingTx(); await prepareAndSendPairingTx();
const service = await Services.getInstance();
await service.confirmPairing();
} catch (e) { } catch (e) {
console.error(`onCreateButtonClick error: ${e}`); console.error(`onCreateButtonClick error: ${e}`);
} }
} }
export async function prepareAndSendPairingTx(promptName: boolean = false) { export async function prepareAndSendPairingTx(promptName: boolean = false): Promise<void> {
const service = await Services.getInstance(); const service = await Services.getInstance();
// Device 1 wait Device 2
// service.device1 = true;
try { try {
await service.checkConnections([]); await service.checkConnections([]);
} catch (e) { } catch (e) {
@ -193,11 +192,8 @@ export async function prepareAndSendPairingTx(promptName: boolean = false) {
userName = ""; userName = "";
} }
// Create the process after a delay. try {
setTimeout(async () => {
const relayAddress = service.getAllRelays(); const relayAddress = service.getAllRelays();
// Pass the userName as an additional parameter.
const createPairingProcessReturn = await service.createPairingProcess( const createPairingProcessReturn = await service.createPairingProcess(
userName, userName,
[], [],
@ -207,11 +203,17 @@ export async function prepareAndSendPairingTx(promptName: boolean = false) {
); );
if (!createPairingProcessReturn.updated_process) { if (!createPairingProcessReturn.updated_process) {
throw new Error('createPairingProcess returned an empty new process'); // This should never happen throw new Error('createPairingProcess returned an empty new process');
} }
service.setProcessId(createPairingProcessReturn.updated_process.process_id);
service.setStateId(createPairingProcessReturn.updated_process.current_process.states[0].state_id);
await service.handleApiReturn(createPairingProcessReturn); await service.handleApiReturn(createPairingProcessReturn);
}, 1000);
} catch (err) {
console.error(err);
}
} }
export async function generateQRCode(spAddress: string) { export async function generateQRCode(spAddress: string) {