**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()
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'étatvalidation_tokens: Tokens nécessaires pour la validationvalidation_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 :
- Encodage des données (JSON/Binary)
- Création du processus via SDK (WebAssembly)
- Génération du premier état (state 0)
- É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é :
- Champ existant dans
public_data→ Reste public - Champ nouveau dans
privateFields→ Privé - Champ existant dans
pcd_commitment→ Reste privé - 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 :
- Création d'un message de mise à jour PRD
- Transmission via les relais aux membres autorisés
- 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_commitmentest 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 :
- Permission : Vérifier que le membre a accès au champ
- Clé de déchiffrement : Récupérer
state.keys[field] - Commitment : Vérifier
state.pcd_commitment[field] - 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 :
- Vérifie les permissions (rôles)
- Récupère la clé de déchiffrement (
state.keys[attribute]) - Demande aux autres membres si la clé est manquante
- 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
- Décentralisé : Pas de tiers de confiance, tout sur la blockchain
- Vérifiable : Chaque état est commité et vérifiable
- Flexible : Permissions granulaires par champ et par rôle
- Sécurisé : Chiffrement des données privées, distribution via relais
- Générique : Réutilisable pour n'importe quel type de contrat
- 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