**Motivations:** - Réduction drastique de la consommation mémoire lors des ancrages - Élimination du chargement de 173k+ UTXOs à chaque requête - Stabilisation de la mémoire système sous charge élevée (50+ ancrages/minute) **Root causes:** - api-anchorage chargeait tous les UTXOs (173k+) via listunspent RPC à chaque ancrage - Filtrage et tri de 173k+ objets en mémoire pour sélectionner un seul UTXO - Croissance mémoire de ~16 MB toutes les 12 secondes avec 50 ancrages/minute - Saturation mémoire système en quelques minutes **Correctifs:** - Création du module database.js pour gérer la base de données SQLite partagée - Remplacement de listunspent RPC par requête SQL directe avec LIMIT 1 - Sélection directe d'un UTXO depuis la DB au lieu de charger/filtrer 173k+ objets - Marquage des UTXOs comme dépensés dans la DB après utilisation - Fermeture propre de la base de données lors de l'arrêt **Evolutions:** - Utilisation de la base de données SQLite partagée avec signet-dashboard - Réduction mémoire de 99.999% (173k+ objets → 1 objet par requête) - Amélioration des performances (requête SQL indexée vs filtrage en mémoire) - Optimisation mémoire de signet-dashboard (chargement UTXOs seulement si nécessaire) - Monitoring de lockedUtxos dans api-anchorage pour détecter les fuites - Nettoyage des intervalles frontend pour éviter les fuites mémoire **Pages affectées:** - api-anchorage/src/database.js (nouveau) - api-anchorage/src/bitcoin-rpc.js - api-anchorage/src/server.js - api-anchorage/package.json - signet-dashboard/src/bitcoin-rpc.js - signet-dashboard/public/app.js - features/optimisation-memoire-applications.md (nouveau) - features/api-anchorage-optimisation-base-donnees.md (nouveau)
70 lines
5.2 KiB
Markdown
70 lines
5.2 KiB
Markdown
# UserWallet – DH systématique et flux scan → fetch par hash → déchiffrer
|
||
|
||
**Author:** Équipe 4NK
|
||
**Date:** 2026-01-26
|
||
|
||
## Question
|
||
|
||
Le DH est-il systématiquement mis en place pour les types de messages envoyés (sauf DH) afin que l’utilisateur **scanne** → **aille chercher le hash** avec le message → qu’il **sache alors déchiffrer** ?
|
||
|
||
## Réponse courte (avant implémentation)
|
||
|
||
**Non.** Aujourd’hui le DH n’est pas systématique pour tous les types de messages. Seul le **pairing** utilise ECDH + MsgCle lorsque la clé publique du pair distant est connue ; le **login** et le **sync générique** ne suivent pas ce schéma.
|
||
|
||
## État après implémentation (DH systématique + flux scan → fetch → déchiffrer)
|
||
|
||
### 1. Pairing (membre finaliser)
|
||
|
||
- **Envoi** : DH **obligatoire**. `recipientPublicKey` (clé du pair distant) et `senderIdentity` requis. Chiffrement ECDH, POST `MsgChiffre` + POST `MsgCle` (`df_ecdh_scannable` = clé publique de l’émetteur). Plus de base64.
|
||
- **Réception** : `fetchPairingMessage` → fetch messages → fetch keys par hash → déchiffrement ECDH uniquement (plus de base64). Si pas de `senderPublicKey` / identité → message ignoré.
|
||
- **UX** : formulaire pairing avec saisie « Clé publique (hex, 66 car.) » du pair distant ; affichage de la clé publique locale pour copie sur l’autre appareil.
|
||
|
||
### 2. Login (challenge)
|
||
|
||
- **Envoi** : ECDH vers l’identité (destinataire = nous). POST `MsgChiffre` + POST `MsgCle` (`df_ecdh_scannable` = clé publique émetteur) + POST signatures. Plus de `encryptForAll` ni clé symétrique.
|
||
- **Réception** : preuve envoyée au parent (iframe). Message login sur le relais déchiffrable via sync (scan keys → fetch par hash → ECDH).
|
||
|
||
### 3. Sync générique
|
||
|
||
- **Flux** : **scan des MsgCle** (`GET /keys?start=&end=`) → regroupement par `hash_message` → **fetch message par hash** (`GET /messages/:hash`) → **déchiffrement ECDH** (`tryDecryptWithKeys` avec `df_ecdh_scannable` + identité). Plus de base64.
|
||
- **Identité** : `SyncService` reçoit `LocalIdentity | null` ; sans identité, messages traités comme indéchiffrables.
|
||
|
||
---
|
||
|
||
## Ce que prévoient les specs
|
||
|
||
- **Message individuel de déchiffrement** : hash + clé de chiffrement + **df** = « diffie-hellman à scanner » pour obtenir la clé de déchiffrement (secret partagé).
|
||
- **Flux** : « Récupérer et scanner tous les messages de clés » (depuis date anniversaire / checkpoint) ; les messages déchiffrables sont identifiés **via les DH** ; puis on va **chercher le message par hash** et on déchiffre.
|
||
- **Publish to all** : tout est chiffré ; seuls les pairs qui peuvent dériver la clé (via ECDH) peuvent lire. Le « matériel DH » est publiable car inexploitable par les tiers.
|
||
|
||
Donc, pour les types de messages **hors DH** : chiffrement + MsgCle avec **df_ecdh_scannable** systématique, et flux **scan des clés → fetch message par hash → déchiffrer**.
|
||
|
||
---
|
||
|
||
## Écarts principaux (avant implémentation)
|
||
|
||
| Aspect | Specs | Actuel (avant) |
|
||
|--------|--------|--------|
|
||
| DH pour tous les messages (hors DH) | Oui, via MsgCle + df | Non : login sans MsgCle ; pairing optionnel ; sync en base64 |
|
||
| Flux | Scan MsgCle → hashes déchiffrables → fetch message par hash | Fetch messages → fetch keys par hash ; pas de scan MsgCle centré ECDH |
|
||
| Utilisation des clés en sync | Déchiffrement ECDH avec df | Base64 si `keys.length > 0` ; clés non utilisées |
|
||
| Login | Sous-entendu publish to all + MsgCle/DH | `encryptForAll` seul, pas de MsgCle |
|
||
|
||
## Implémentation réalisée
|
||
|
||
1. **api-relay** : `GET /keys?start=&end=` (fenêtre `received_at`), `StorageService.getKeysInWindow`.
|
||
2. **userwallet relay** : `getKeysInWindow`, `getMessageByHash`.
|
||
3. **Login** : `LoginBuilder` utilise `encryptWithECDH` (identité comme destinataire), `challengeToMsgCle`, `loginPublish` POST `MsgCle` en plus de `MsgChiffre` et signatures.
|
||
4. **Pairing** : DH obligatoire ; `publishPairingMessage` exige `recipientPublicKey` et `senderIdentity` ; plus de base64. `fetchPairingMessage` ECDH uniquement. UX : saisie clé publique (hex 66 car.) + affichage clé locale.
|
||
5. **Sync** : flux scan-first. `SyncService(relays, graphResolver, identity)`. Pour chaque relais : `getKeysInWindow` → regroupement par hash → `getMessageByHash` → `tryDecryptWithKeys` (ECDH) → validation → mise à jour graphe. `syncDecrypt.tryDecryptWithKeys` utilise `decryptWithECDH` avec `df_ecdh_scannable`.
|
||
|
||
## Références
|
||
|
||
- `userwallet/docs/specs.md` : Message individuel de déchiffrement, df DH à scanner, « récupérer et scanner tous les messages de clés », fenêtre de scan, fetch par hash.
|
||
- `userwallet/features/userwallet-pairing-connecte.md` : chiffrement pairing ECDH vs base64, MsgCle.
|
||
- `userwallet/src/utils/encryption.ts` : `encryptWithECDH` / `decryptWithECDH`.
|
||
- `userwallet/src/services/pairingConfirm.ts` : publication pairing + MsgCle (DH obligatoire), `fetchPairingMessage` (ECDH seul).
|
||
- `userwallet/src/services/syncService.ts` : flux scan-first, `getKeysInWindow` → `getMessageByHash` → `tryDecryptWithKeys`.
|
||
- `userwallet/src/services/syncDecrypt.ts` : `tryDecryptWithKeys` (ECDH).
|
||
- `userwallet/src/services/loginBuilder.ts` : ECDH + `challengeToMsgCle`.
|