ihm_client/docs/PROCESS_SYSTEM_ARCHITECTURE.md
NicolasCantu f7c2f86d30 Fix pairing credentials restoration and wallet keys management
**Motivations :**
- Le pairing échouait avec l'erreur 'Wallet keys not available - WebAuthn decryption required'
- Le device stocké en base ne contenait pas les clés spend_key et scan_key dans sp_wallet
- Ces clés étaient stockées séparément dans les credentials chiffrés
- Il fallait restaurer ces clés dans le device en mémoire avant de pouvoir les utiliser

**Modifications :**
- ensureWalletKeysAvailable() : Maintenant asynchrone, vérifie si les clés sont disponibles dans le device en mémoire, sinon les restaure depuis les credentials
- Restauration des clés : Si les clés ne sont pas en mémoire, la méthode récupère les credentials, restaure les clés dans le device, et le restaure via restoreDevice()
- Méthodes asynchrones : getAmount() et getDeviceAddress() sont maintenant asynchrones pour supporter la restauration des clés
- Appels mis à jour : Tous les appels à ces méthodes ont été mis à jour avec await

**Pages affectées :**
- src/services/service.ts : Restauration automatique des clés depuis les credentials
- src/utils/sp-address.utils.ts : Appels asynchrones à getDeviceAddress()
- src/router.ts : Appels asynchrones à getDeviceAddress()
- src/components/device-management/device-management.ts : Appels asynchrones à getDeviceAddress()
- src/services/iframe-pairing.service.ts : Appels asynchrones à getDeviceAddress()
2025-10-29 20:26:33 +01:00

16 KiB

Architecture du Système de Processus et Updates

Vue d'ensemble

Le système de processus est un système générique et réutilisable pour créer des "contrats" entre des membres avec des niveaux d'accès différents aux champs de données. C'est la fondation qui permet d'implémenter des fonctionnalités comme le pairing, mais aussi n'importe quel autre type de contrat décentralisé.

Concepts Fondamentaux

1. Processus (Process)

Un processus est un contrat décentralisé entre plusieurs membres, commité sur la blockchain. Il représente un accord ou une entité partagée avec :

  • Identifiant unique : process_id
  • États successifs : Historique des modifications
  • Membres : Participants au processus
  • Rôles et permissions : Définition des accès

2. État (State)

Chaque processus contient une liste d'états représentant l'évolution du processus dans le temps. Chaque état contient :

Données Publiques (public_data)

  • Accessibles à tous les membres du processus
  • Inchangées dans tous les nouveaux états (portées automatiquement)
  • Encodées mais non chiffrées (encodage JSON/Binary)
  • Exemple : Nom du processus, adresses appairées, métadonnées

Données Privées (via pcd_commitment)

  • Chiffrées et accessibles uniquement aux membres autorisés
  • Commitment : Hash des données privées (pcd_commitment[field])
  • Clés de déchiffrement : Stockées dans state.keys[field] pour chaque membre autorisé
  • Exemple : Secrets, clés privées, données sensibles

Rôles (roles)

  • Définition des permissions par rôle
  • Validation rules : Quorum et champs accessibles par rôle
  • Membres : Liste des IDs de pairing process pour chaque rôle

Métadonnées d'État

  • state_id : Identifiant unique de l'état
  • validation_tokens : Tokens nécessaires pour la validation
  • validation_result : Résultat de la validation

3. Rôles et Permissions (RoleDefinition)

Un rôle définit qui peut accéder à quels champs et comment :

interface RoleDefinition {
  members: string[];              // IDs de pairing process (identifiants des membres)
  validation_rules: ValidationRule[];  // Règles de validation
}

interface ValidationRule {
  quorum: number;                 // Quorum requis (ex: 1.0 = tous, 0.5 = 50%)
  fields: string[];                // Champs accessibles pour ce rôle
}

Exemples de rôles :

  • Administrateur : Quorum 1.0, accès à tous les champs
  • Membre : Quorum 0.5, accès aux champs non critiques
  • Lecteur : Quorum 0, accès en lecture seule aux champs publics

4. Membres

Les membres sont identifiés par leur pairing process ID (l'identité numérique vérifiable créée lors du pairing).

  • Un membre peut participer à plusieurs processus
  • Un processus peut avoir plusieurs membres
  • Les adresses SP (Silent Payment) sont associées aux membres pour la communication

Cycle de Vie d'un Processus

Phase 1 : Création

createProcess(
  privateData: Record<string, any>,    // Données privées initiales
  publicData: Record<string, any>,      // Données publiques initiales
  roles: Record<string, RoleDefinition>  // Définition des rôles
): Promise<ApiReturn>

Étapes :

  1. Encodage des données (JSON/Binary)
  2. Création du processus via SDK (WebAssembly)
  3. Génération du premier état (state 0)
  4. Établissement des connexions avec les membres

Phase 2 : Mise à Jour

updateProcess(
  process: Process,
  privateData: Record<string, any>,    // Nouvelles données privées
  publicData: Record<string, any>,      // Nouvelles données publiques
  roles: Record<string, RoleDefinition> | null  // Nouveaux rôles (optionnel)
): Promise<ApiReturn>

Logique de classification des champs :

Le système détermine automatiquement si un champ est public ou privé :

  1. Champ existant dans public_data → Reste public
  2. Champ nouveau dans privateFields → Privé
  3. Champ existant dans pcd_commitment → Reste privé
  4. Sinon → Nouveau champ public
// Logique dans handleUpdateProcess (router.ts:811-846)
for (const field of Object.keys(newData)) {
  // 1. Vérifier si c'est déjà public
  if (lastState.public_data[field]) {
    publicData[field] = newData[field];
    continue;
  }

  // 2. Vérifier si c'est un nouveau champ privé
  if (privateFields.includes(field)) {
    privateData[field] = newData[field];
    continue;
  }

  // 3. Vérifier si c'était privé dans un état précédent
  for (let i = lastStateIndex; i >= 0; i--) {
    if (process.states[i].pcd_commitment[field]) {
      privateData[field] = newData[field];
      break;
    }
  }

  // 4. Sinon, c'est un nouveau champ public
  if (!privateData[field]) {
    publicData[field] = newData[field];
  }
}

Phase 3 : Synchronisation (PRD Update)

PRD = Private Data Relay

createPrdUpdate(
  processId: string,
  stateId: string
): Promise<ApiReturn>

Objectif :

  • Synchroniser les clés de déchiffrement des données privées avec tous les membres autorisés
  • Distribuer les données privées aux membres qui ont les permissions
  • Mettre à jour les state.keys[field] pour chaque membre autorisé

Processus :

  1. Création d'un message de mise à jour PRD
  2. Transmission via les relais aux membres autorisés
  3. Chaque membre reçoit les clés pour les champs auxquels il a accès

Phase 4 : Validation (Approbation)

approveChange(
  processId: string,
  stateId: string
): Promise<ApiReturn>

Objectif :

  • Valider un état du processus selon le quorum requis
  • S'assurer que suffisamment de membres ont approuvé le changement
  • Marquer l'état comme validé

Quorum :

  • Si quorum = 1.0 → Tous les membres du rôle doivent approuver
  • Si quorum = 0.5 → 50% des membres doivent approuver
  • Si quorum = 0 → Auto-approbation (pas de validation requise)

Phase 5 : Commit sur Blockchain

Une fois validé, l'état est committé sur la blockchain :

  • Création d'une transaction Bitcoin commitant l'état
  • Le pcd_commitment est inclut dans la transaction
  • L'état devient immuable et vérifiable

Phase 6 : Accès aux Données

Données Publiques

Accessibles directement depuis state.public_data après décodage :

const publicData = service.getPublicData(process);
const decodedValue = service.decodeValue(publicData['fieldName']);

Données Privées

Nécessitent :

  1. Permission : Vérifier que le membre a accès au champ
  2. Clé de déchiffrement : Récupérer state.keys[field]
  3. Commitment : Vérifier state.pcd_commitment[field]
  4. Déchiffrement : Décrypter avec la clé
async decryptAttribute(
  processId: string,
  state: ProcessState,
  attribute: string
): Promise<any | null>

Vérification des permissions :

// Vérifier si le membre a accès au champ
for (const role of Object.values(state.roles)) {
  for (const rule of Object.values(role.validation_rules)) {
    if (rule.fields.includes(attribute)) {
      if (role.members.includes(pairingProcessId)) {
        // Le membre a accès
      }
    }
  }
}

Si la clé est manquante, le système demande automatiquement aux autres membres via requestDataFromPeers().

Flux Complet d'un Update

1. Mise à jour demandée
   ↓
2. Classification automatique des champs (public/privé)
   ↓
3. updateProcess() → Création d'un nouvel état
   ↓
4. createPrdUpdate() → Synchronisation des clés privées
   ↓
5. approveChange() → Validation selon quorum
   ↓
6. Commit sur blockchain → État immuable
   ↓
7. Accès aux données via getPublicData() / decryptAttribute()

Exemples d'Utilisation

Exemple 1 : Pairing (Identité Multi-Appareils)

// Création d'un processus de pairing
const pairingProcess = await service.createPairingProcess(
  '',                           // memberPublicName (vide pour pairing)
  [creatorAddress]              // pairedAddresses (liste des appareils)
);

// Données publiques : Liste des adresses appairées
// Données privées : Secrets Silent Payment partagés
// Rôles : Tous les appareils ont le même niveau d'accès (quorum 1.0)

Exemple 2 : Contrat de Partage avec Niveaux d'Accès

// Création d'un contrat avec différents niveaux
const contract = await service.createProcess(
  {
    secretKey: '...',           // Privé : Clé secrète
    internalNotes: '...'        // Privé : Notes internes
  },
  {
    contractName: 'Mon Contrat',  // Public : Nom
    description: '...'             // Public : Description
  },
  {
    admin: {
      members: [adminPairingId],
      validation_rules: [
        { quorum: 1.0, fields: ['secretKey', 'internalNotes', 'contractName'] }
      ]
    },
    member: {
      members: [memberPairingId1, memberPairingId2],
      validation_rules: [
        { quorum: 0.5, fields: ['contractName', 'description'] }  // Lecture seule des publics
      ]
    }
  }
);

Exemple 3 : Vote Décisionnel

const voteProcess = await service.createProcess(
  {
    votes: {}                   // Privé : Votes individuels
  },
  {
    proposal: 'Proposition...',  // Public : Proposition
    result: null                 // Public : Résultat
  },
  {
    voter: {
      members: [...voterIds],
      validation_rules: [
        { quorum: 0.5, fields: ['votes'] }  // 50% des votants doivent valider
      ]
    }
  }
);

Points Importants

1. Immutabilité

Une fois qu'un état est committé, il devient immuable. Les nouveaux états ajoutent des modifications mais ne modifient jamais les états précédents.

2. Portage des Données Publiques

Les données publiques sont automatiquement portées dans chaque nouvel état. Pas besoin de les réenvoyer à chaque update.

3. Synchronisation Automatique

Le système gère automatiquement la distribution des clés privées aux membres autorisés via les relais.

4. Quorum Flexible

Le système supporte différents niveaux de quorum selon les besoins :

  • Sécurisé : Quorum 1.0 (tous doivent approuver)
  • Démocratique : Quorum 0.5 (majorité)
  • Auto : Quorum 0 (auto-approbation)

5. Extensibilité

Ce système peut être utilisé pour n'importe quel type de contrat :

  • Gestion documentaire
  • Votes décisionnels
  • Partage de fichiers
  • Contrats intelligents décentralisés
  • etc.

Méthodes Utilitaires

Récupération des Processus

// Récupérer un processus spécifique
getProcess(processId: string): Promise<Process | null>

// Récupérer tous les processus
getProcesses(): Promise<Record<string, Process>>

// Récupérer mes processus (où je suis membre)
getMyProcesses(): Promise<string[] | null>

État Commité

// Récupérer le dernier état commité
getLastCommitedState(process: Process): ProcessState | null

// Récupérer l'index du dernier état commité
getLastCommitedStateIndex(process: Process): number | null

Important : Les états non commités sont des "pending states" qui attendent validation.

Rôles et Membres

// Récupérer les rôles d'un processus (depuis le dernier état commité)
getRoles(process: Process): Record<string, RoleDefinition> | null

// Vérifier si je suis membre d'un processus
rolesContainsUs(roles: Record<string, RoleDefinition>): boolean

// Vérifier si un membre spécifique fait partie des rôles
rolesContainsMember(roles: Record<string, RoleDefinition>, pairingProcessId: string): boolean

// Récupérer tous les membres connus
getAllMembers(): Record<string, Member>

Données Publiques

// Récupérer les données publiques (depuis le dernier état commité)
getPublicData(process: Process): Record<string, any> | null

// Décoder une valeur encodée
decodeValue(value: number[]): any | null

Données Privées

// Déchiffrer un attribut privé
async decryptAttribute(
  processId: string,
  state: ProcessState,
  attribute: string
): Promise<any | null>

Cette méthode :

  1. Vérifie les permissions (rôles)
  2. Récupère la clé de déchiffrement (state.keys[attribute])
  3. Demande aux autres membres si la clé est manquante
  4. Déchiffre la donnée

Gestion du Cache et Synchronisation

Cache des Processus

Les processus sont mis en cache localement pour améliorer les performances :

processesCache: Record<string, Process>

Synchronisation avec les Relais

Les relais synchronisent :

  • Les nouveaux processus
  • Les mises à jour d'états
  • Les clés privées (PRD updates)
  • Les validations

Connexion entre Membres

Avant de créer ou mettre à jour un processus, le système établit des connexions (secrets partagés) entre tous les membres :

checkConnections(process: Process, stateId?: string): Promise<void>

Cela crée des secrets Silent Payment entre les membres pour permettre la communication chiffrée.

Avantages du Système

  1. Décentralisé : Pas de tiers de confiance, tout sur la blockchain
  2. Vérifiable : Chaque état est commité et vérifiable
  3. Flexible : Permissions granulaires par champ et par rôle
  4. Sécurisé : Chiffrement des données privées, distribution via relais
  5. Générique : Réutilisable pour n'importe quel type de contrat
  6. Sans tiers : Les utilisateurs contrôlent leurs propres processus

Cas d'Usage Avancés

Gestion Documentaire Collaborative

const documentProcess = await service.createProcess(
  {
    documentContent: encryptedContent,  // Privé : Contenu chiffré
    versionHistory: []                    // Privé : Historique des versions
  },
  {
    documentTitle: 'Document Important',  // Public : Titre
    lastModified: timestamp,               // Public : Dernière modification
    author: authorAddress                  // Public : Auteur
  },
  {
    owner: {
      members: [ownerPairingId],
      validation_rules: [
        { quorum: 1.0, fields: ['documentContent', 'documentTitle'] }
      ]
    },
    editor: {
      members: [...editorPairingIds],
      validation_rules: [
        { quorum: 0.5, fields: ['documentContent'] }  // 50% des éditeurs doivent valider
      ]
    },
    viewer: {
      members: [...viewerPairingIds],
      validation_rules: [
        { quorum: 0, fields: ['documentTitle'] }  // Lecture seule, pas de validation
      ]
    }
  }
);

Système de Votation

const votingProcess = await service.createProcess(
  {
    votes: {},                    // Privé : Votes individuels
    voterIds: []                 // Privé : Liste des votants
  },
  {
    question: 'Question...',     // Public : Question
    options: ['A', 'B', 'C'],    // Public : Options
    deadline: timestamp,          // Public : Deadline
    result: null                  // Public : Résultat (mis à jour après)
  },
  {
    voter: {
      members: [...allVoterIds],
      validation_rules: [
        { quorum: 0.5, fields: ['votes'] }  // 50% des votants doivent valider
      ]
    },
    organizer: {
      members: [organizerPairingId],
      validation_rules: [
        { quorum: 1.0, fields: ['question', 'options', 'deadline', 'result'] }
      ]
    }
  }
);

Contrat Intelligent Décentralisé

Le système peut implémenter n'importe quel type de contrat intelligent décentralisé avec :

  • Conditions de validation personnalisées (quorum)
  • Permissions granulaires par champ
  • Audit trail complet (historique des états)
  • Vérifiabilité sur la blockchain