Fix pairing system: Add waitForPairingCommitment with device sync and update documentation
- Add waitForPairingCommitment function with automatic device synchronization - Integrate updateDevice() call in waitForPairingCommitment for better sync - Increase retry attempts to 30 with 2s delay (60s total wait time) - Add detailed logging for pairing process synchronization - Update router to call waitForPairingCommitment before confirmPairing - Remove redundant updateDevice() call from router - Update PAIRING_SYSTEM_ANALYSIS.md with coherence issues and recommendations - Identify joiner flow inconsistencies requiring future fixes
This commit is contained in:
parent
79633ed923
commit
7c2c4bfb46
277
doc/PAIRING_SYSTEM_ANALYSIS.md
Normal file
277
doc/PAIRING_SYSTEM_ANALYSIS.md
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
# Analyse du Système de Pairing - Version Actuelle
|
||||||
|
|
||||||
|
## Vue d'ensemble
|
||||||
|
|
||||||
|
Ce document résume l'analyse complète du système de pairing et les corrections apportées pour résoudre les problèmes identifiés.
|
||||||
|
|
||||||
|
## Problèmes Identifiés et Solutions
|
||||||
|
|
||||||
|
### 1. Problème de `checkConnections` pour le Pairing
|
||||||
|
|
||||||
|
**Problème** : La méthode `checkConnections` a été mise à jour il y a un mois pour prendre un `Process` et un `stateId` au lieu d'une liste de membres, mais la gestion des processus de pairing était défaillante.
|
||||||
|
|
||||||
|
**Symptômes** :
|
||||||
|
- `checkConnections` échouait pour les processus de pairing
|
||||||
|
- Les adresses des membres n'étaient pas correctement récupérées
|
||||||
|
- Erreur "Not a pairing process" même pour des processus de pairing valides
|
||||||
|
|
||||||
|
**Solution Appliquée** :
|
||||||
|
```typescript
|
||||||
|
// Correction dans checkConnections pour gérer les pairedAddresses
|
||||||
|
if (members.size === 0) {
|
||||||
|
// This must be a pairing process
|
||||||
|
let publicData: Record<string, any> | null = null;
|
||||||
|
if (!stateId) {
|
||||||
|
publicData = process.states[process.states.length - 2]?.public_data;
|
||||||
|
} else {
|
||||||
|
publicData = process.states.find(state => state.state_id === stateId)?.public_data || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pairedAddresses is not in the current state, look in previous states
|
||||||
|
if (!publicData || !publicData['pairedAddresses']) {
|
||||||
|
// Look for pairedAddresses in previous states
|
||||||
|
for (let i = process.states.length - 1; i >= 0; i--) {
|
||||||
|
const state = process.states[i];
|
||||||
|
if (state.public_data && state.public_data['pairedAddresses']) {
|
||||||
|
publicData = state.public_data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodedAddresses = this.decodeValue(publicData['pairedAddresses']);
|
||||||
|
members.add({ sp_addresses: decodedAddresses });
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Problème de `confirmPairing` avec `getPairingProcessId()`
|
||||||
|
|
||||||
|
**Problème** : `confirmPairing` échouait car `getPairingProcessId()` utilisait `sdkClient.get_pairing_process_id()` qui n'était pas encore disponible car le processus de pairing n'était pas encore committé.
|
||||||
|
|
||||||
|
**Symptômes** :
|
||||||
|
- Erreur "Failed to get pairing process" dans `confirmPairing`
|
||||||
|
- Le SDK n'avait pas encore le processus de pairing disponible
|
||||||
|
- Échec de confirmation du pairing
|
||||||
|
|
||||||
|
**Solution Appliquée** :
|
||||||
|
```typescript
|
||||||
|
public async confirmPairing(pairingId?: string) {
|
||||||
|
try {
|
||||||
|
console.log('confirmPairing');
|
||||||
|
let processId: string;
|
||||||
|
if (pairingId) {
|
||||||
|
processId = pairingId;
|
||||||
|
console.log('pairingId (provided):', processId);
|
||||||
|
} else if (this.processId) {
|
||||||
|
processId = this.processId;
|
||||||
|
console.log('pairingId (from stored processId):', processId);
|
||||||
|
} else {
|
||||||
|
// Try to get pairing process ID, with retry if it fails
|
||||||
|
let retries = 3;
|
||||||
|
while (retries > 0) {
|
||||||
|
try {
|
||||||
|
processId = this.getPairingProcessId();
|
||||||
|
console.log('pairingId (from SDK):', processId);
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
retries--;
|
||||||
|
if (retries === 0) throw e;
|
||||||
|
console.log(`Failed to get pairing process ID, retrying... (${retries} attempts left)`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ... rest of the method
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to confirm pairing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Problème de `pairing_process_commitment` à `null`
|
||||||
|
|
||||||
|
**Problème** : Le `pairing_process_commitment` restait à `null` dans le device dump car le device n'était pas synchronisé avec l'état committé du processus.
|
||||||
|
|
||||||
|
**Symptômes** :
|
||||||
|
- `pairing_process_commitment: null` dans le device dump
|
||||||
|
- Le commitment n'était pas synchronisé avec l'état committé du processus
|
||||||
|
- Échec de la confirmation du pairing
|
||||||
|
|
||||||
|
**Solution Appliquée** :
|
||||||
|
```typescript
|
||||||
|
// Intégration de updateDevice() dans waitForPairingCommitment
|
||||||
|
public async waitForPairingCommitment(processId: string, maxRetries: number = 10, retryDelay: number = 1000): Promise<void> {
|
||||||
|
console.log(`Waiting for pairing process ${processId} to be committed...`);
|
||||||
|
|
||||||
|
// First, try to update the device to sync with the committed state
|
||||||
|
try {
|
||||||
|
await this.updateDevice();
|
||||||
|
console.log('Device updated, checking commitment...');
|
||||||
|
} catch (e) {
|
||||||
|
console.log('Failed to update device, continuing with polling...', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
const device = this.dumpDeviceFromMemory();
|
||||||
|
console.log(`Attempt ${i + 1}/${maxRetries}: pairing_process_commitment =`, device.pairing_process_commitment);
|
||||||
|
|
||||||
|
// Check if the commitment is set and not null/empty
|
||||||
|
if (device.pairing_process_commitment &&
|
||||||
|
device.pairing_process_commitment !== null &&
|
||||||
|
device.pairing_process_commitment !== '') {
|
||||||
|
console.log('Pairing process commitment found:', device.pairing_process_commitment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`Attempt ${i + 1}/${maxRetries}: Device not ready yet - ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < maxRetries - 1) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Pairing process ${processId} was not committed after ${maxRetries} attempts`);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Et simplification du router :
|
||||||
|
```typescript
|
||||||
|
console.log("⏳ Waiting for pairing process to be committed...");
|
||||||
|
await services.waitForPairingCommitment(pairingId);
|
||||||
|
|
||||||
|
console.log("🔁 Confirming pairing...");
|
||||||
|
await services.confirmPairing(pairingId);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture du Système de Pairing
|
||||||
|
|
||||||
|
### Flux de Création du Pairing (Créateur)
|
||||||
|
|
||||||
|
1. **Création du processus** : `createPairingProcess("", [myAddress])`
|
||||||
|
2. **Enregistrement du device** : `pairDevice(pairingId, [myAddress])`
|
||||||
|
3. **Traitement de l'API** : `handleApiReturn(createPairingProcessReturn)`
|
||||||
|
4. **Création de la mise à jour PRD** : `createPrdUpdate(pairingId, stateId)`
|
||||||
|
5. **Approbation du changement** : `approveChange(pairingId, stateId)`
|
||||||
|
6. **Attente du commit avec synchronisation** : `waitForPairingCommitment(pairingId)` (inclut `updateDevice()`)
|
||||||
|
7. **Confirmation du pairing** : `confirmPairing(pairingId)`
|
||||||
|
|
||||||
|
### Flux de Rejoindre le Pairing (Joiner) - ⚠️ INCOHÉRENT
|
||||||
|
|
||||||
|
**Problème identifié** : Le joiner n'a pas de flux de confirmation complet.
|
||||||
|
|
||||||
|
**Flux actuel (incomplet)** :
|
||||||
|
1. **Création avec liste vide** : `createPairingProcess("", [])` ❌
|
||||||
|
2. **Établissement des connexions** : `checkConnections(process)`
|
||||||
|
3. **Pas de confirmation** : Aucun `waitForPairingCommitment` ou `confirmPairing` ❌
|
||||||
|
|
||||||
|
**Flux attendu (cohérent)** :
|
||||||
|
1. **Récupération du processus existant** : `getPairingProcessId()`
|
||||||
|
2. **Rejoindre le processus** : Pas de création, mais participation au processus existant
|
||||||
|
3. **Flux de confirmation complet** : Même flux que le créateur
|
||||||
|
4. **Attente du commit** : `waitForPairingCommitment()`
|
||||||
|
5. **Confirmation du pairing** : `confirmPairing()`
|
||||||
|
|
||||||
|
### Gestion des Connexions
|
||||||
|
|
||||||
|
La méthode `checkConnections` gère maintenant :
|
||||||
|
- **Processus normaux** : Utilise les rôles pour trouver les membres
|
||||||
|
- **Processus de pairing** : Utilise `pairedAddresses` des données publiques
|
||||||
|
- **Recherche dans les états précédents** : Si `pairedAddresses` n'est pas dans l'état actuel
|
||||||
|
- **Décodage des adresses** : Les données publiques sont encodées et nécessitent un décodage
|
||||||
|
|
||||||
|
## Points Clés Appris
|
||||||
|
|
||||||
|
### 1. Encodage des Données Publiques
|
||||||
|
- Les données publiques sont encodées avec `this.sdkClient.encode_json()`
|
||||||
|
- `pairedAddresses` nécessite un décodage avec `this.decodeValue()`
|
||||||
|
- Les données ne sont pas directement utilisables sans décodage
|
||||||
|
|
||||||
|
### 2. Gestion Multi-Hosts
|
||||||
|
- Le créateur et le joiner peuvent être sur des hosts différents
|
||||||
|
- Le joiner doit récupérer les adresses depuis le processus existant
|
||||||
|
- `this.processId` n'est disponible que sur le même host
|
||||||
|
|
||||||
|
### 3. Synchronisation du SDK
|
||||||
|
- Le SDK n'a pas immédiatement le processus de pairing disponible
|
||||||
|
- Il faut attendre que le processus soit committé
|
||||||
|
- `updateDevice()` est nécessaire pour synchroniser l'état
|
||||||
|
|
||||||
|
### 4. Gestion des États
|
||||||
|
- Les processus de pairing peuvent avoir des mises à jour partielles
|
||||||
|
- Il faut chercher `pairedAddresses` dans les états précédents si nécessaire
|
||||||
|
- La logique de fallback est cruciale pour la robustesse
|
||||||
|
|
||||||
|
## Version Actuelle
|
||||||
|
|
||||||
|
### État des Corrections
|
||||||
|
- ✅ `checkConnections` corrigé pour les processus de pairing
|
||||||
|
- ✅ `confirmPairing` avec gestion des paramètres et retry
|
||||||
|
- ✅ `waitForPairingCommitment` avec synchronisation automatique du device
|
||||||
|
- ✅ Intégration de `updateDevice()` dans `waitForPairingCommitment`
|
||||||
|
- ✅ Gestion des cas multi-hosts
|
||||||
|
- ✅ Simplification du flux de création
|
||||||
|
- ⚠️ **Problème identifié** : Incohérence entre créateur et joiner
|
||||||
|
|
||||||
|
### Fonctionnalités Opérationnelles
|
||||||
|
- **Création de pairing** : ✅ Fonctionne avec les adresses correctes
|
||||||
|
- **Rejoindre un pairing** : ❌ Flux incomplet, pas de confirmation
|
||||||
|
- **Établissement des connexions** : ✅ `checkConnections` trouve les membres
|
||||||
|
- **Confirmation du pairing** : ⚠️ Seulement côté créateur, pas côté joiner
|
||||||
|
- **Synchronisation du commitment** : ✅ Côté créateur, ❌ Côté joiner
|
||||||
|
- **Flux simplifié** : ✅ Côté créateur, ❌ Côté joiner
|
||||||
|
|
||||||
|
### Problèmes de Cohérence Identifiés
|
||||||
|
|
||||||
|
#### Incohérence Créateur vs Joiner
|
||||||
|
- **Créateur** : Flux complet avec 7 étapes incluant confirmation
|
||||||
|
- **Joiner** : Flux incomplet avec seulement 2 étapes, pas de confirmation
|
||||||
|
- **Impact** : Le joiner ne suit pas le même processus de validation
|
||||||
|
|
||||||
|
#### Problèmes Spécifiques du Joiner
|
||||||
|
1. **Création avec liste vide** : `createPairingProcess("", [])` ne permet pas de connexions
|
||||||
|
2. **Pas de flux de confirmation** : Aucun `waitForPairingCommitment` ou `confirmPairing`
|
||||||
|
3. **Pas de synchronisation** : Le `pairing_process_commitment` ne sera jamais défini
|
||||||
|
4. **Pas de validation** : Le pairing n'est pas validé côté joiner
|
||||||
|
|
||||||
|
### Améliorations Récentes
|
||||||
|
|
||||||
|
#### Synchronisation Automatique du Device
|
||||||
|
- **Intégration de `updateDevice()`** : Appelé automatiquement dans `waitForPairingCommitment`
|
||||||
|
- **Gestion des erreurs** : Continue le polling même si `updateDevice()` échoue
|
||||||
|
- **Logs détaillés** : Suivi complet du processus de synchronisation
|
||||||
|
- **Temps d'attente augmenté** : 30 tentatives × 2 secondes = 60 secondes max
|
||||||
|
|
||||||
|
#### Simplification du Flux
|
||||||
|
- **Moins d'étapes manuelles** : `updateDevice()` intégré dans `waitForPairingCommitment`
|
||||||
|
- **Flux plus robuste** : Gestion automatique de la synchronisation
|
||||||
|
- **Code plus maintenable** : Logique centralisée dans une seule méthode
|
||||||
|
|
||||||
|
### Points d'Attention
|
||||||
|
- Le système nécessite que les deux côtés soient synchronisés
|
||||||
|
- Les retry automatiques sont implémentés pour la robustesse
|
||||||
|
- La gestion des erreurs est améliorée avec des logs détaillés
|
||||||
|
- Le flux est maintenant plus prévisible et fiable
|
||||||
|
- La synchronisation du device est automatique et robuste
|
||||||
|
|
||||||
|
## Recommandations
|
||||||
|
|
||||||
|
### Corrections Prioritaires
|
||||||
|
1. **Corriger le flux du joiner** : Implémenter le même flux de confirmation que le créateur
|
||||||
|
2. **Unifier les processus** : Le joiner devrait rejoindre un processus existant, pas en créer un nouveau
|
||||||
|
3. **Synchronisation bidirectionnelle** : Les deux côtés doivent avoir le même niveau de validation
|
||||||
|
|
||||||
|
### Tests et Monitoring
|
||||||
|
1. **Tests** : Tester le pairing entre différents hosts avec les deux flux
|
||||||
|
2. **Monitoring** : Surveiller les logs pour identifier les problèmes potentiels
|
||||||
|
3. **Performance** : Optimiser les délais de retry si nécessaire
|
||||||
|
4. **Documentation** : Maintenir cette documentation à jour avec les évolutions
|
||||||
|
|
||||||
|
### Actions Immédiates
|
||||||
|
1. **Analyser le flux du joiner** : Comprendre comment il devrait rejoindre un processus existant
|
||||||
|
2. **Implémenter la cohérence** : Appliquer le même flux de confirmation aux deux côtés
|
||||||
|
3. **Valider la synchronisation** : S'assurer que les deux côtés ont le même `pairing_process_commitment`
|
||||||
|
|
||||||
|
Cette analyse fournit une base solide pour comprendre et maintenir le système de pairing, mais révèle des incohérences importantes qui doivent être corrigées.
|
||||||
@ -5,7 +5,7 @@
|
|||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build_wasm": "wasm-pack build --out-dir ../ihm_client/pkg ../sdk_client --target bundler --dev",
|
"build_wasm": "wasm-pack build --out-dir ../ihm_client_dev3/pkg ../sdk_client --target bundler --dev",
|
||||||
"start": "vite --host 0.0.0.0",
|
"start": "vite --host 0.0.0.0",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"deploy": "sudo cp -r dist/* /var/www/html/",
|
"deploy": "sudo cp -r dist/* /var/www/html/",
|
||||||
|
|||||||
253667
screenlog.0
Normal file
253667
screenlog.0
Normal file
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,8 @@ export enum MessageType {
|
|||||||
// Processes
|
// Processes
|
||||||
CREATE_PROCESS = 'CREATE_PROCESS',
|
CREATE_PROCESS = 'CREATE_PROCESS',
|
||||||
PROCESS_CREATED = 'PROCESS_CREATED',
|
PROCESS_CREATED = 'PROCESS_CREATED',
|
||||||
|
CREATE_CONVERSATION = 'CREATE_CONVERSATION',
|
||||||
|
CONVERSATION_CREATED = 'CONVERSATION_CREATED',
|
||||||
UPDATE_PROCESS = 'UPDATE_PROCESS',
|
UPDATE_PROCESS = 'UPDATE_PROCESS',
|
||||||
PROCESS_UPDATED = 'PROCESS_UPDATED',
|
PROCESS_UPDATED = 'PROCESS_UPDATED',
|
||||||
NOTIFY_UPDATE = 'NOTIFY_UPDATE',
|
NOTIFY_UPDATE = 'NOTIFY_UPDATE',
|
||||||
|
|||||||
115
src/router.ts
115
src/router.ts
@ -148,7 +148,7 @@ export async function init(): Promise<void> {
|
|||||||
} else {
|
} else {
|
||||||
services.restoreDevice(device);
|
services.restoreDevice(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we create a new device, we most probably don't have anything in db, but just in case
|
// If we create a new device, we most probably don't have anything in db, but just in case
|
||||||
await services.restoreProcessesFromDB();
|
await services.restoreProcessesFromDB();
|
||||||
await services.restoreSecretsFromDB();
|
await services.restoreSecretsFromDB();
|
||||||
@ -176,10 +176,10 @@ export async function registerAllListeners() {
|
|||||||
const services = await Services.getInstance();
|
const services = await Services.getInstance();
|
||||||
const tokenService = await TokenService.getInstance();
|
const tokenService = await TokenService.getInstance();
|
||||||
|
|
||||||
const errorResponse = (errorMsg: string, origin: string, messageId?: string) => {
|
const errorResponse = (errorMsg: string, origin: string, messageId?: string) => {
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.ERROR,
|
type: MessageType.ERROR,
|
||||||
error: errorMsg,
|
error: errorMsg,
|
||||||
messageId
|
messageId
|
||||||
},
|
},
|
||||||
@ -213,9 +213,9 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tokens = await tokenService.generateSessionToken(event.origin);
|
const tokens = await tokenService.generateSessionToken(event.origin);
|
||||||
const acceptedMsg = {
|
const acceptedMsg = {
|
||||||
type: MessageType.LINK_ACCEPTED,
|
type: MessageType.LINK_ACCEPTED,
|
||||||
accessToken: tokens.accessToken,
|
accessToken: tokens.accessToken,
|
||||||
refreshToken: tokens.refreshToken,
|
refreshToken: tokens.refreshToken,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -234,63 +234,66 @@ export async function registerAllListeners() {
|
|||||||
if (event.data.type !== MessageType.CREATE_PAIRING) {
|
if (event.data.type !== MessageType.CREATE_PAIRING) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("📨 [Router] Received CREATE_PAIRING request");
|
console.log("📨 [Router] Received CREATE_PAIRING request");
|
||||||
|
|
||||||
if (services.isPaired()) {
|
if (services.isPaired()) {
|
||||||
const errorMsg = "⚠️ Device already paired — ignoring CREATE_PAIRING request";
|
const errorMsg = "⚠️ Device already paired — ignoring CREATE_PAIRING request";
|
||||||
console.warn(errorMsg);
|
console.warn(errorMsg);
|
||||||
errorResponse(errorMsg, event.origin, event.data.messageId);
|
errorResponse(errorMsg, event.origin, event.data.messageId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { accessToken } = event.data;
|
const { accessToken } = event.data;
|
||||||
|
|
||||||
console.log("🔐 Checking access token validity...");
|
console.log("🔐 Checking access token validity...");
|
||||||
const validToken = accessToken && (await tokenService.validateToken(accessToken, event.origin));
|
const validToken = accessToken && (await tokenService.validateToken(accessToken, event.origin));
|
||||||
|
|
||||||
if (!validToken) {
|
if (!validToken) {
|
||||||
throw new Error("❌ Invalid or expired session token");
|
throw new Error("❌ Invalid or expired session token");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("✅ Token validated successfully");
|
console.log("✅ Token validated successfully");
|
||||||
console.log("🚀 Starting pairing process");
|
console.log("🚀 Starting pairing process");
|
||||||
|
|
||||||
const myAddress = services.getDeviceAddress();
|
const myAddress = services.getDeviceAddress();
|
||||||
console.log("📍 Device address:", myAddress);
|
console.log("📍 Device address:", myAddress);
|
||||||
|
|
||||||
console.log("🧱 Creating pairing process...");
|
console.log("🧱 Creating pairing process...");
|
||||||
const createPairingProcessReturn = await services.createPairingProcess("", [myAddress]);
|
const createPairingProcessReturn = await services.createPairingProcess("", [myAddress]);
|
||||||
console.log("🧾 Pairing process created:", createPairingProcessReturn);
|
console.log("🧾 Pairing process created:", createPairingProcessReturn);
|
||||||
|
|
||||||
const pairingId = createPairingProcessReturn.updated_process?.process_id;
|
const pairingId = createPairingProcessReturn.updated_process?.process_id;
|
||||||
const stateId = createPairingProcessReturn.updated_process?.current_process?.states[0]?.state_id as string;
|
const stateId = createPairingProcessReturn.updated_process?.current_process?.states[0]?.state_id as string;
|
||||||
|
|
||||||
console.log("🔗 Pairing ID:", pairingId);
|
console.log("🔗 Pairing ID:", pairingId);
|
||||||
console.log("🧩 State ID:", stateId);
|
console.log("🧩 State ID:", stateId);
|
||||||
|
|
||||||
console.log("🔒 Registering device as paired...");
|
console.log("🔒 Registering device as paired...");
|
||||||
services.pairDevice(pairingId, [myAddress]);
|
services.pairDevice(pairingId, [myAddress]);
|
||||||
|
|
||||||
console.log("🧠 Handling API return for createPairingProcess...");
|
console.log("🧠 Handling API return for createPairingProcess...");
|
||||||
await services.handleApiReturn(createPairingProcessReturn);
|
await services.handleApiReturn(createPairingProcessReturn);
|
||||||
|
|
||||||
console.log("🧰 Creating PRD update...");
|
console.log("🧰 Creating PRD update...");
|
||||||
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
|
const createPrdUpdateReturn = await services.createPrdUpdate(pairingId, stateId);
|
||||||
console.log("🧾 PRD update result:", createPrdUpdateReturn);
|
console.log("🧾 PRD update result:", createPrdUpdateReturn);
|
||||||
await services.handleApiReturn(createPrdUpdateReturn);
|
await services.handleApiReturn(createPrdUpdateReturn);
|
||||||
|
|
||||||
console.log("✅ Approving change...");
|
console.log("✅ Approving change...");
|
||||||
const approveChangeReturn = await services.approveChange(pairingId, stateId);
|
const approveChangeReturn = await services.approveChange(pairingId, stateId);
|
||||||
console.log("📜 Approve change result:", approveChangeReturn);
|
console.log("📜 Approve change result:", approveChangeReturn);
|
||||||
await services.handleApiReturn(approveChangeReturn);
|
await services.handleApiReturn(approveChangeReturn);
|
||||||
|
|
||||||
|
console.log("⏳ Waiting for pairing process to be committed...");
|
||||||
|
await services.waitForPairingCommitment(pairingId);
|
||||||
|
|
||||||
console.log("🔁 Confirming pairing...");
|
console.log("🔁 Confirming pairing...");
|
||||||
await services.confirmPairing();
|
await services.confirmPairing(pairingId);
|
||||||
|
|
||||||
console.log("🎉 Pairing successfully completed!");
|
console.log("🎉 Pairing successfully completed!");
|
||||||
|
|
||||||
// ✅ Send success response to frontend
|
// ✅ Send success response to frontend
|
||||||
const successMsg = {
|
const successMsg = {
|
||||||
type: MessageType.PAIRING_CREATED,
|
type: MessageType.PAIRING_CREATED,
|
||||||
@ -299,7 +302,7 @@ export async function registerAllListeners() {
|
|||||||
};
|
};
|
||||||
console.log("📤 Sending PAIRING_CREATED message to UI:", successMsg);
|
console.log("📤 Sending PAIRING_CREATED message to UI:", successMsg);
|
||||||
window.parent.postMessage(successMsg, event.origin);
|
window.parent.postMessage(successMsg, event.origin);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMsg = `❌ Failed to create pairing process: ${e}`;
|
const errorMsg = `❌ Failed to create pairing process: ${e}`;
|
||||||
console.error(errorMsg);
|
console.error(errorMsg);
|
||||||
@ -326,9 +329,9 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const myProcesses = await services.getMyProcesses();
|
const myProcesses = await services.getMyProcesses();
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.GET_MY_PROCESSES,
|
type: MessageType.GET_MY_PROCESSES,
|
||||||
myProcesses,
|
myProcesses,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -339,8 +342,8 @@ export async function registerAllListeners() {
|
|||||||
const errorMsg = `Failed to get processes: ${e}`;
|
const errorMsg = `Failed to get processes: ${e}`;
|
||||||
errorResponse(errorMsg, event.origin, event.data.messageId);
|
errorResponse(errorMsg, event.origin, event.data.messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleGetProcesses = async (event: MessageEvent) => {
|
const handleGetProcesses = async (event: MessageEvent) => {
|
||||||
if (event.data.type !== MessageType.GET_PROCESSES) {
|
if (event.data.type !== MessageType.GET_PROCESSES) {
|
||||||
return;
|
return;
|
||||||
@ -363,9 +366,9 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const processes = await services.getProcesses();
|
const processes = await services.getProcesses();
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.PROCESSES_RETRIEVED,
|
type: MessageType.PROCESSES_RETRIEVED,
|
||||||
processes,
|
processes,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -376,7 +379,7 @@ export async function registerAllListeners() {
|
|||||||
const errorMsg = `Failed to get processes: ${e}`;
|
const errorMsg = `Failed to get processes: ${e}`;
|
||||||
errorResponse(errorMsg, event.origin, event.data.messageId);
|
errorResponse(errorMsg, event.origin, event.data.messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We got a state for some process and return as many clear attributes as we can
|
/// We got a state for some process and return as many clear attributes as we can
|
||||||
const handleDecryptState = async (event: MessageEvent) => {
|
const handleDecryptState = async (event: MessageEvent) => {
|
||||||
@ -424,7 +427,7 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.DATA_RETRIEVED,
|
type: MessageType.DATA_RETRIEVED,
|
||||||
data: res,
|
data: res,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -466,23 +469,23 @@ export async function registerAllListeners() {
|
|||||||
if (event.data.type !== MessageType.RENEW_TOKEN) {
|
if (event.data.type !== MessageType.RENEW_TOKEN) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const refreshToken = event.data.refreshToken;
|
const refreshToken = event.data.refreshToken;
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
throw new Error('No refresh token provided');
|
throw new Error('No refresh token provided');
|
||||||
}
|
}
|
||||||
|
|
||||||
const newAccessToken = await tokenService.refreshAccessToken(refreshToken, event.origin);
|
const newAccessToken = await tokenService.refreshAccessToken(refreshToken, event.origin);
|
||||||
|
|
||||||
if (!newAccessToken) {
|
if (!newAccessToken) {
|
||||||
throw new Error('Failed to refresh token');
|
throw new Error('Failed to refresh token');
|
||||||
}
|
}
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.RENEW_TOKEN,
|
type: MessageType.RENEW_TOKEN,
|
||||||
accessToken: newAccessToken,
|
accessToken: newAccessToken,
|
||||||
refreshToken: refreshToken,
|
refreshToken: refreshToken,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -512,9 +515,9 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const userPairingId = services.getPairingProcessId();
|
const userPairingId = services.getPairingProcessId();
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.GET_PAIRING_ID,
|
type: MessageType.GET_PAIRING_ID,
|
||||||
userPairingId,
|
userPairingId,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -561,7 +564,7 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.PROCESS_CREATED,
|
type: MessageType.PROCESS_CREATED,
|
||||||
processCreated: res,
|
processCreated: res,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -598,7 +601,7 @@ export async function registerAllListeners() {
|
|||||||
await services.handleApiReturn(res);
|
await services.handleApiReturn(res);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.UPDATE_NOTIFIED,
|
type: MessageType.UPDATE_NOTIFIED,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
},
|
},
|
||||||
@ -630,7 +633,7 @@ export async function registerAllListeners() {
|
|||||||
await services.handleApiReturn(res);
|
await services.handleApiReturn(res);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.STATE_VALIDATED,
|
type: MessageType.STATE_VALIDATED,
|
||||||
validatedProcess: res.updated_process,
|
validatedProcess: res.updated_process,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -642,7 +645,7 @@ export async function registerAllListeners() {
|
|||||||
errorResponse(errorMsg, event.origin, event.data.messageId);
|
errorResponse(errorMsg, event.origin, event.data.messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpdateProcess = async (event: MessageEvent) => {
|
const handleUpdateProcess = async (event: MessageEvent) => {
|
||||||
if (event.data.type !== MessageType.UPDATE_PROCESS) return;
|
if (event.data.type !== MessageType.UPDATE_PROCESS) return;
|
||||||
|
|
||||||
@ -734,12 +737,12 @@ export async function registerAllListeners() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We'll let the wasm check if roles are consistent
|
// We'll let the wasm check if roles are consistent
|
||||||
|
|
||||||
const res = await services.updateProcess(process, privateData, publicData, roles);
|
const res = await services.updateProcess(process, privateData, publicData, roles);
|
||||||
await services.handleApiReturn(res);
|
await services.handleApiReturn(res);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.PROCESS_UPDATED,
|
type: MessageType.PROCESS_UPDATED,
|
||||||
updatedProcess: res.updated_process,
|
updatedProcess: res.updated_process,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -767,11 +770,11 @@ export async function registerAllListeners() {
|
|||||||
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
||||||
throw new Error('Invalid or expired session token');
|
throw new Error('Invalid or expired session token');
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedData = services.decodeValue(encodedData);
|
const decodedData = services.decodeValue(encodedData);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.PUBLIC_DATA_DECODED,
|
type: MessageType.PUBLIC_DATA_DECODED,
|
||||||
decodedData,
|
decodedData,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -795,11 +798,11 @@ export async function registerAllListeners() {
|
|||||||
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
if (!accessToken || !(await tokenService.validateToken(accessToken, event.origin))) {
|
||||||
throw new Error('Invalid or expired session token');
|
throw new Error('Invalid or expired session token');
|
||||||
}
|
}
|
||||||
|
|
||||||
const hash = services.getHashForFile(commitedIn, label, fileBlob);
|
const hash = services.getHashForFile(commitedIn, label, fileBlob);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.VALUE_HASHED,
|
type: MessageType.VALUE_HASHED,
|
||||||
hash,
|
hash,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -825,7 +828,7 @@ export async function registerAllListeners() {
|
|||||||
const proof = services.getMerkleProofForFile(processState, attributeName);
|
const proof = services.getMerkleProofForFile(processState, attributeName);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.MERKLE_PROOF_RETRIEVED,
|
type: MessageType.MERKLE_PROOF_RETRIEVED,
|
||||||
proof,
|
proof,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -860,7 +863,7 @@ export async function registerAllListeners() {
|
|||||||
const res = services.validateMerkleProof(parsedMerkleProof, documentHash);
|
const res = services.validateMerkleProof(parsedMerkleProof, documentHash);
|
||||||
|
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
{
|
{
|
||||||
type: MessageType.MERKLE_PROOF_VALIDATED,
|
type: MessageType.MERKLE_PROOF_VALIDATED,
|
||||||
isValid: res,
|
isValid: res,
|
||||||
messageId: event.data.messageId
|
messageId: event.data.messageId
|
||||||
@ -973,9 +976,9 @@ document.addEventListener('navigate', ((e: Event) => {
|
|||||||
if (event.detail.page === 'chat') {
|
if (event.detail.page === 'chat') {
|
||||||
const container = document.querySelector('.container');
|
const container = document.querySelector('.container');
|
||||||
if (container) container.innerHTML = '';
|
if (container) container.innerHTML = '';
|
||||||
|
|
||||||
//initChat();
|
//initChat();
|
||||||
|
|
||||||
const chatElement = document.querySelector('chat-element');
|
const chatElement = document.querySelector('chat-element');
|
||||||
if (chatElement) {
|
if (chatElement) {
|
||||||
chatElement.setAttribute('process-id', event.detail.processId || '');
|
chatElement.setAttribute('process-id', event.detail.processId || '');
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export default class Services {
|
|||||||
*/
|
*/
|
||||||
public async connectAllRelays(): Promise<void> {
|
public async connectAllRelays(): Promise<void> {
|
||||||
const connectedUrls: string[] = [];
|
const connectedUrls: string[] = [];
|
||||||
|
|
||||||
// Connect to all relays
|
// Connect to all relays
|
||||||
for (const wsurl of Object.keys(this.relayAddresses)) {
|
for (const wsurl of Object.keys(this.relayAddresses)) {
|
||||||
try {
|
try {
|
||||||
@ -101,7 +101,7 @@ export default class Services {
|
|||||||
console.error(`Failed to connect to ${wsurl}:`, error);
|
console.error(`Failed to connect to ${wsurl}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for at least one handshake message if we have connections
|
// Wait for at least one handshake message if we have connections
|
||||||
if (connectedUrls.length > 0) {
|
if (connectedUrls.length > 0) {
|
||||||
await this.waitForHandshakeMessage();
|
await this.waitForHandshakeMessage();
|
||||||
@ -116,13 +116,14 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
return this.relayReadyPromise;
|
return this.relayReadyPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveRelayReady(): void {
|
private resolveRelayReady(): void {
|
||||||
if (this.relayReadyResolver) {
|
if (this.relayReadyResolver) {
|
||||||
this.relayReadyResolver();
|
this.relayReadyResolver();
|
||||||
this.relayReadyResolver = null;
|
this.relayReadyResolver = null;
|
||||||
this.relayReadyPromise = null;
|
this.relayReadyPromise = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async addWebsocketConnection(url: string): Promise<void> {
|
public async addWebsocketConnection(url: string): Promise<void> {
|
||||||
console.log('Opening new websocket connection');
|
console.log('Opening new websocket connection');
|
||||||
@ -255,7 +256,25 @@ export default class Services {
|
|||||||
if (members.size === 0) {
|
if (members.size === 0) {
|
||||||
// This must be a pairing process
|
// This must be a pairing process
|
||||||
// Check if we have a pairedAddresses in the public data
|
// Check if we have a pairedAddresses in the public data
|
||||||
const publicData = process.states[0]?.public_data;
|
let publicData: Record<string, any> | null = null;
|
||||||
|
if (!stateId) {
|
||||||
|
publicData = process.states[process.states.length - 2]?.public_data;
|
||||||
|
} else {
|
||||||
|
publicData = process.states.find(state => state.state_id === stateId)?.public_data || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If pairedAddresses is not in the current state, look in previous states
|
||||||
|
if (!publicData || !publicData['pairedAddresses']) {
|
||||||
|
// Look for pairedAddresses in previous states
|
||||||
|
for (let i = process.states.length - 1; i >= 0; i--) {
|
||||||
|
const state = process.states[i];
|
||||||
|
if (state.public_data && state.public_data['pairedAddresses']) {
|
||||||
|
publicData = state.public_data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!publicData || !publicData['pairedAddresses']) {
|
if (!publicData || !publicData['pairedAddresses']) {
|
||||||
throw new Error('Not a pairing process');
|
throw new Error('Not a pairing process');
|
||||||
}
|
}
|
||||||
@ -409,7 +428,7 @@ export default class Services {
|
|||||||
if (!relayAddress) {
|
if (!relayAddress) {
|
||||||
throw new Error('❌ No relay address available after waiting');
|
throw new Error('❌ No relay address available after waiting');
|
||||||
}
|
}
|
||||||
|
|
||||||
const feeRate = 1;
|
const feeRate = 1;
|
||||||
|
|
||||||
// We can't encode files as the rest because Uint8Array is not valid json
|
// We can't encode files as the rest because Uint8Array is not valid json
|
||||||
@ -417,12 +436,12 @@ export default class Services {
|
|||||||
// TODO encoding of relatively large binaries (=> 1M) is a bit long now and blocking
|
// TODO encoding of relatively large binaries (=> 1M) is a bit long now and blocking
|
||||||
const privateSplitData = this.splitData(privateData);
|
const privateSplitData = this.splitData(privateData);
|
||||||
const publicSplitData = this.splitData(publicData);
|
const publicSplitData = this.splitData(publicData);
|
||||||
const encodedPrivateData = {
|
const encodedPrivateData = {
|
||||||
...this.sdkClient.encode_json(privateSplitData.jsonCompatibleData),
|
...this.sdkClient.encode_json(privateSplitData.jsonCompatibleData),
|
||||||
...this.sdkClient.encode_binary(privateSplitData.binaryData)
|
...this.sdkClient.encode_binary(privateSplitData.binaryData)
|
||||||
};
|
};
|
||||||
const encodedPublicData = {
|
const encodedPublicData = {
|
||||||
...this.sdkClient.encode_json(publicSplitData.jsonCompatibleData),
|
...this.sdkClient.encode_json(publicSplitData.jsonCompatibleData),
|
||||||
...this.sdkClient.encode_binary(publicSplitData.binaryData)
|
...this.sdkClient.encode_binary(publicSplitData.binaryData)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -434,10 +453,10 @@ export default class Services {
|
|||||||
|
|
||||||
await this.getTokensFromFaucet();
|
await this.getTokensFromFaucet();
|
||||||
const result = this.sdkClient.create_new_process (
|
const result = this.sdkClient.create_new_process (
|
||||||
encodedPrivateData,
|
encodedPrivateData,
|
||||||
roles,
|
roles,
|
||||||
encodedPublicData,
|
encodedPublicData,
|
||||||
relayAddress,
|
relayAddress,
|
||||||
feeRate,
|
feeRate,
|
||||||
this.getAllMembers()
|
this.getAllMembers()
|
||||||
);
|
);
|
||||||
@ -461,12 +480,12 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
const privateSplitData = this.splitData(privateData);
|
const privateSplitData = this.splitData(privateData);
|
||||||
const publicSplitData = this.splitData(publicData);
|
const publicSplitData = this.splitData(publicData);
|
||||||
const encodedPrivateData = {
|
const encodedPrivateData = {
|
||||||
...this.sdkClient.encode_json(privateSplitData.jsonCompatibleData),
|
...this.sdkClient.encode_json(privateSplitData.jsonCompatibleData),
|
||||||
...this.sdkClient.encode_binary(privateSplitData.binaryData)
|
...this.sdkClient.encode_binary(privateSplitData.binaryData)
|
||||||
};
|
};
|
||||||
const encodedPublicData = {
|
const encodedPublicData = {
|
||||||
...this.sdkClient.encode_json(publicSplitData.jsonCompatibleData),
|
...this.sdkClient.encode_json(publicSplitData.jsonCompatibleData),
|
||||||
...this.sdkClient.encode_binary(publicSplitData.binaryData)
|
...this.sdkClient.encode_binary(publicSplitData.binaryData)
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
@ -590,7 +609,7 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async parseNewTx(newTxMsg: string) {
|
async parseNewTx(newTxMsg: string) {
|
||||||
const parsedMsg: NewTxMessage = JSON.parse(newTxMsg);
|
const parsedMsg: NewTxMessage = JSON.parse(newTxMsg);
|
||||||
if (parsedMsg.error !== null) {
|
if (parsedMsg.error !== null) {
|
||||||
console.error('Received error in new tx message:', parsedMsg.error);
|
console.error('Received error in new tx message:', parsedMsg.error);
|
||||||
return;
|
return;
|
||||||
@ -754,13 +773,79 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async confirmPairing() {
|
public async waitForPairingCommitment(processId: string, maxRetries: number = 30, retryDelay: number = 2000): Promise<void> {
|
||||||
|
console.log(`⏳ Waiting for pairing process ${processId} to be committed and synchronized...`);
|
||||||
|
console.log(`🔄 This may take some time as we wait for SDK synchronization...`);
|
||||||
|
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
// Try to update device first (may fail if not committed yet)
|
||||||
|
try {
|
||||||
|
await this.updateDevice();
|
||||||
|
console.log(`✅ Device update successful on attempt ${i + 1}`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`⚠️ Device update failed on attempt ${i + 1} (process may not be committed yet): ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = this.dumpDeviceFromMemory();
|
||||||
|
console.log(`🔍 Attempt ${i + 1}/${maxRetries}: pairing_process_commitment =`, device.pairing_process_commitment);
|
||||||
|
|
||||||
|
// Check if the commitment is set and not null/empty
|
||||||
|
if (device.pairing_process_commitment &&
|
||||||
|
device.pairing_process_commitment !== null &&
|
||||||
|
device.pairing_process_commitment !== '') {
|
||||||
|
console.log('✅ Pairing process commitment found:', device.pairing_process_commitment);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`⏳ Still waiting for SDK synchronization... (${i + 1}/${maxRetries})`);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(`❌ Attempt ${i + 1}/${maxRetries}: Error during synchronization - ${e.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < maxRetries - 1) {
|
||||||
|
console.log(`⏳ Waiting ${retryDelay}ms before next attempt...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`❌ Pairing process ${processId} was not synchronized after ${maxRetries} attempts (${maxRetries * retryDelay / 1000}s)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async confirmPairing(pairingId?: string) {
|
||||||
try {
|
try {
|
||||||
// Is the wasm paired?
|
// Is the wasm paired?
|
||||||
const pairingId = this.getPairingProcessId();
|
console.log('confirmPairing');
|
||||||
|
let processId: string;
|
||||||
|
if (pairingId) {
|
||||||
|
processId = pairingId;
|
||||||
|
console.log('pairingId (provided):', processId);
|
||||||
|
} else if (this.processId) {
|
||||||
|
processId = this.processId;
|
||||||
|
console.log('pairingId (from stored processId):', processId);
|
||||||
|
} else {
|
||||||
|
// Try to get pairing process ID, with retry if it fails
|
||||||
|
let retries = 3;
|
||||||
|
while (retries > 0) {
|
||||||
|
try {
|
||||||
|
processId = this.getPairingProcessId();
|
||||||
|
console.log('pairingId (from SDK):', processId);
|
||||||
|
break;
|
||||||
|
} catch (e) {
|
||||||
|
retries--;
|
||||||
|
if (retries === 0) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
console.log(`Failed to get pairing process ID, retrying... (${retries} attempts left)`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second before retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO confirm that the pairing process id is known, commited
|
// TODO confirm that the pairing process id is known, commited
|
||||||
const newDevice = this.dumpDeviceFromMemory();
|
const newDevice = this.dumpDeviceFromMemory();
|
||||||
|
console.log('newDevice:', newDevice);
|
||||||
await this.saveDeviceInDatabase(newDevice);
|
await this.saveDeviceInDatabase(newDevice);
|
||||||
|
console.log('Device saved in database');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to confirm pairing');
|
console.error('Failed to confirm pairing');
|
||||||
return;
|
return;
|
||||||
@ -885,7 +970,7 @@ export default class Services {
|
|||||||
try {
|
try {
|
||||||
const device = await this.getDeviceFromDatabase();
|
const device = await this.getDeviceFromDatabase();
|
||||||
if (device) {
|
if (device) {
|
||||||
const pairedMember = device['paired_member'];
|
const pairedMember = device['paired_member'];
|
||||||
return pairedMember.sp_addresses;
|
return pairedMember.sp_addresses;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -1072,7 +1157,7 @@ export default class Services {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to save data to db: ${e}`);
|
console.error(`Failed to save data to db: ${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getBlobFromDb(hash: string): Promise<Blob | null> {
|
public async getBlobFromDb(hash: string): Promise<Blob | null> {
|
||||||
const db = await Database.getInstance();
|
const db = await Database.getInstance();
|
||||||
@ -1199,7 +1284,7 @@ export default class Services {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we can transfer them to memory
|
// Now we can transfer them to memory
|
||||||
await this.restoreSecretsFromDB();
|
await this.restoreSecretsFromDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1228,19 +1313,21 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null> {
|
async decryptAttribute(processId: string, state: ProcessState, attribute: string): Promise<any | null> {
|
||||||
|
console.log(`[decryptAttribute] Starting decryption for attribute: ${attribute}, processId: ${processId}`);
|
||||||
let hash = state.pcd_commitment[attribute];
|
let hash = state.pcd_commitment[attribute];
|
||||||
if (!hash) {
|
if (!hash) {
|
||||||
// attribute doesn't exist
|
console.log(`[decryptAttribute] No hash found for attribute: ${attribute}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let key = state.keys[attribute];
|
let key = state.keys[attribute];
|
||||||
|
console.log(`[decryptAttribute] Initial key state for ${attribute}:`, key ? 'present' : 'missing');
|
||||||
const pairingProcessId = this.getPairingProcessId();
|
const pairingProcessId = this.getPairingProcessId();
|
||||||
|
|
||||||
// If key is missing, request an update and then retry
|
// If key is missing, request an update and then retry
|
||||||
if (!key) {
|
if (!key) {
|
||||||
const roles = state.roles;
|
const roles = state.roles;
|
||||||
let hasAccess = false;
|
let hasAccess = false;
|
||||||
// If we're not supposed to have access to this attribute, ignore
|
// If we're not supposed to have access to this attribute, ignore
|
||||||
for (const role of Object.values(roles)) {
|
for (const role of Object.values(roles)) {
|
||||||
for (const rule of Object.values(role.validation_rules)) {
|
for (const rule of Object.values(role.validation_rules)) {
|
||||||
if (rule.fields.includes(attribute)) {
|
if (rule.fields.includes(attribute)) {
|
||||||
@ -1253,8 +1340,12 @@ export default class Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasAccess) return null;
|
if (!hasAccess) {
|
||||||
|
console.log(`[decryptAttribute] No access rights for attribute: ${attribute}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[decryptAttribute] Requesting key update for attribute: ${attribute}`);
|
||||||
await this.checkConnections((await this.getProcess(processId))!);
|
await this.checkConnections((await this.getProcess(processId))!);
|
||||||
// We should have the key, so we're going to ask other members for it
|
// We should have the key, so we're going to ask other members for it
|
||||||
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
|
await this.requestDataFromPeers(processId, [state.state_id], [state.roles]);
|
||||||
@ -1262,19 +1353,23 @@ export default class Services {
|
|||||||
const maxRetries = 5;
|
const maxRetries = 5;
|
||||||
const retryDelay = 500; // delay in milliseconds
|
const retryDelay = 500; // delay in milliseconds
|
||||||
let retries = 0;
|
let retries = 0;
|
||||||
|
|
||||||
while ((!hash || !key) && retries < maxRetries) {
|
while ((!hash || !key) && retries < maxRetries) {
|
||||||
|
console.log(`[decryptAttribute] Retry ${retries + 1}/${maxRetries} for attribute: ${attribute}`);
|
||||||
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
||||||
// Re-read hash and key after waiting
|
// Re-read hash and key after waiting
|
||||||
hash = state.pcd_commitment[attribute];
|
hash = state.pcd_commitment[attribute];
|
||||||
key = state.keys[attribute];
|
key = state.keys[attribute];
|
||||||
retries++;
|
retries++;
|
||||||
|
console.log(`[decryptAttribute] After retry ${retries}: hash=${!!hash}, key=${!!key}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash && key) {
|
if (hash && key) {
|
||||||
|
console.log(`[decryptAttribute] Starting decryption process with hash: ${hash.substring(0, 8)}...`);
|
||||||
const blob = await this.getBlobFromDb(hash);
|
const blob = await this.getBlobFromDb(hash);
|
||||||
if (blob) {
|
if (blob) {
|
||||||
|
console.log(`[decryptAttribute] Blob retrieved successfully for ${attribute}`);
|
||||||
// Decrypt the data
|
// Decrypt the data
|
||||||
const buf = await blob.arrayBuffer();
|
const buf = await blob.arrayBuffer();
|
||||||
const cipher = new Uint8Array(buf);
|
const cipher = new Uint8Array(buf);
|
||||||
@ -1291,11 +1386,11 @@ export default class Services {
|
|||||||
throw new Error('decrypt_data returned null');
|
throw new Error('decrypt_data returned null');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`Failed to decrypt data: ${e}`);
|
console.error(`[decryptAttribute] Failed to decrypt data for ${attribute}:`, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1337,7 +1432,7 @@ export default class Services {
|
|||||||
await this.resetDevice();
|
await this.resetDevice();
|
||||||
|
|
||||||
await this.saveDeviceInDatabase(device);
|
await this.saveDeviceInDatabase(device);
|
||||||
|
|
||||||
this.restoreDevice(device);
|
this.restoreDevice(device);
|
||||||
|
|
||||||
// TODO restore secrets and processes from file
|
// TODO restore secrets and processes from file
|
||||||
@ -1391,7 +1486,7 @@ export default class Services {
|
|||||||
this.relayAddresses[url] = handshakeMsg.sp_address;
|
this.relayAddresses[url] = handshakeMsg.sp_address;
|
||||||
this.resolveRelayReady();
|
this.resolveRelayReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('handshakeMsg:', handshakeMsg);
|
console.log('handshakeMsg:', handshakeMsg);
|
||||||
this.currentBlockHeight = handshakeMsg.chain_tip;
|
this.currentBlockHeight = handshakeMsg.chain_tip;
|
||||||
console.log('this.currentBlockHeight:', this.currentBlockHeight);
|
console.log('this.currentBlockHeight:', this.currentBlockHeight);
|
||||||
@ -1521,7 +1616,7 @@ export default class Services {
|
|||||||
private async waitForHandshakeMessage(timeoutMs: number = 10000): Promise<void> {
|
private async waitForHandshakeMessage(timeoutMs: number = 10000): Promise<void> {
|
||||||
const startTime = Date.now();
|
const startTime = Date.now();
|
||||||
const pollInterval = 100; // Check every 100ms
|
const pollInterval = 100; // Check every 100ms
|
||||||
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
const checkForHandshake = () => {
|
const checkForHandshake = () => {
|
||||||
// Check if we have any members or any relays (indicating handshake was received)
|
// Check if we have any members or any relays (indicating handshake was received)
|
||||||
@ -1530,17 +1625,17 @@ export default class Services {
|
|||||||
resolve();
|
resolve();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check timeout
|
// Check timeout
|
||||||
if (Date.now() - startTime >= timeoutMs) {
|
if (Date.now() - startTime >= timeoutMs) {
|
||||||
reject(new Error(`No handshake message received after ${timeoutMs}ms timeout`));
|
reject(new Error(`No handshake message received after ${timeoutMs}ms timeout`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue polling
|
// Continue polling
|
||||||
setTimeout(checkForHandshake, pollInterval);
|
setTimeout(checkForHandshake, pollInterval);
|
||||||
};
|
};
|
||||||
|
|
||||||
checkForHandshake();
|
checkForHandshake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1591,7 +1686,7 @@ export default class Services {
|
|||||||
this.sendCommitMessage(JSON.stringify(content));
|
this.sendCommitMessage(JSON.stringify(content));
|
||||||
}, 1000)
|
}, 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
public getRoles(process: Process): Record<string, RoleDefinition> | null {
|
public getRoles(process: Process): Record<string, RoleDefinition> | null {
|
||||||
const lastCommitedState = this.getLastCommitedState(process);
|
const lastCommitedState = this.getLastCommitedState(process);
|
||||||
if (lastCommitedState && lastCommitedState.roles && Object.keys(lastCommitedState.roles).length != 0) {
|
if (lastCommitedState && lastCommitedState.roles && Object.keys(lastCommitedState.roles).length != 0) {
|
||||||
@ -1771,7 +1866,7 @@ export default class Services {
|
|||||||
return process.states[index + 1];
|
return process.states[index + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public isPairingProcess(roles: Record<string, RoleDefinition>): boolean {
|
public isPairingProcess(roles: Record<string, RoleDefinition>): boolean {
|
||||||
|
|||||||
@ -162,12 +162,6 @@ async function onCreateButtonClick() {
|
|||||||
export async function prepareAndSendPairingTx(): Promise<void> {
|
export async function prepareAndSendPairingTx(): Promise<void> {
|
||||||
const service = await Services.getInstance();
|
const service = await Services.getInstance();
|
||||||
|
|
||||||
try {
|
|
||||||
await service.checkConnections([]);
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const relayAddress = service.getAllRelays();
|
const relayAddress = service.getAllRelays();
|
||||||
const createPairingProcessReturn = await service.createPairingProcess(
|
const createPairingProcessReturn = await service.createPairingProcess(
|
||||||
@ -184,6 +178,12 @@ export async function prepareAndSendPairingTx(): Promise<void> {
|
|||||||
|
|
||||||
await service.handleApiReturn(createPairingProcessReturn);
|
await service.handleApiReturn(createPairingProcessReturn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.checkConnections(createPairingProcessReturn.updated_process.current_process);
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@ -212,5 +212,5 @@ export async function generateCreateBtn() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user