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,37 +135,63 @@ export async function init(): Promise<void> {
const services = await Services.getInstance();
(window as any).myService = services;
await Database.getInstance();
setTimeout(async () => {
let device = await services.getDeviceFromDatabase();
console.log('🚀 ~ setTimeout ~ device:', device);
let device = await services.getDeviceFromDatabase();
console.log('🚀 ~ setTimeout ~ device:', device);
if (!device) {
device = await services.createNewDevice();
} else {
services.restoreDevice(device);
if (!device) {
device = await services.createNewDevice();
} else {
services.restoreDevice(device);
}
await services.restoreProcessesFromDB();
await services.restoreSecretsFromDB();
// If we have a service redirection flag, we intercept it here
const queryString = window.location.search;
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();
}
await services.restoreProcessesFromDB();
await services.restoreSecretsFromDB();
if (services.isPaired()) {
await navigate('chat');
} else {
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const pairingAddress = urlParams.get('sp_address');
if (pairingAddress) {
setTimeout(async () => {
try {
// check if we have a shared secret with that address
await prepareAndSendPairingTx(pairingAddress);
} catch (e) {
console.error('Failed to pair:', e);
}
}, 2000);
// 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;
}
await navigate('home');
// We send the neutered device to the main frame
window.parent.postMessage({ type: 'RESPONSE', payload: JSON.stringify(neuteredDevice) }, 'https://lecoffreio.4nkweb.com');
}
}, 200);
} else {
console.log('No source provided');
}
if (services.isPaired()) {
await navigate('account');
} else {
const pairingAddress = urlParams.get('sp_address');
if (pairingAddress) {
setTimeout(async () => {
try {
// check if we have a shared secret with that address
await prepareAndSendPairingTx(pairingAddress);
} catch (e) {
console.error('Failed to pair:', e);
}
await navigate('account');
}, 2000);
}
console.log('here');
await navigate('home');
}
} catch (error) {
console.error(error);
await navigate('home');

View File

@ -164,93 +164,37 @@ export default class ModalService {
if (this.modal) this.modal.style.display = 'none';
}
async confirmPairing() {
const service = await Services.getInstance();
if (this.modal) this.modal.style.display = 'none';
// async confirmPairing() {
// const service = await Services.getInstance();
// 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 the prd update
// if (this.stateId && this.processId) {
// try {
// 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);
// 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");
// 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);
} catch (e) {
throw e;
}
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.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);
// } catch (e) {
// throw e;
// }
// }
async closeConfirmationModal() {
const service = await Services.getInstance();

View File

@ -20,8 +20,8 @@ const EMPTY32BYTES = String('').padStart(64, '0');
export default class Services {
private static initializing: Promise<Services> | null = null;
private static instance: Services;
private currentProcess: string | null = null;
private currentUpdateMerkleRoot: string | null = null;
private processId: string | null = null;
private stateId: string | null = null;
private sdkClient: any;
private myProcesses: Set = new Set();
private notifications: any[] | null = null;
@ -64,6 +64,24 @@ export default class Services {
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.
*/
@ -246,11 +264,11 @@ export default class Services {
pairWith.push(myAddress);
const roles: Record<string, RoleDefinition> = {
pairing: {
members: [{ sp_addresses: pairWith }],
members: [],
validation_rules: [
{
quorum: 1.0,
fields: ['description', 'counter', 'roles', 'memberPublicName'],
fields: ['description', 'counter', 'roles', 'memberPublicName', 'pairedAddresses'],
min_sig_member: 1.0,
},
],
@ -262,15 +280,17 @@ export default class Services {
counter: 0,
};
const publicData = {
memberPublicName: userName
}
memberPublicName: userName,
pairedAddresses: pairWith,
};
try {
return this.sdkClient.create_new_process(
pairingTemplate,
roles,
publicData,
relayAddress,
feeRate
feeRate,
this.getAllMembers()
);
} catch (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 {
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) {
throw new Error(`Failed to pair device: ${e}`);
}

View File

@ -168,17 +168,16 @@ async function onOkButtonClick() {
async function onCreateButtonClick() {
try {
await prepareAndSendPairingTx();
const service = await Services.getInstance();
await service.confirmPairing();
} catch (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();
// Device 1 wait Device 2
// service.device1 = true;
try {
await service.checkConnections([]);
} catch (e) {
@ -193,11 +192,8 @@ export async function prepareAndSendPairingTx(promptName: boolean = false) {
userName = "";
}
// Create the process after a delay.
setTimeout(async () => {
try {
const relayAddress = service.getAllRelays();
// Pass the userName as an additional parameter.
const createPairingProcessReturn = await service.createPairingProcess(
userName,
[],
@ -207,11 +203,17 @@ export async function prepareAndSendPairingTx(promptName: boolean = false) {
);
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);
}, 1000);
} catch (err) {
console.error(err);
}
}
export async function generateQRCode(spAddress: string) {