**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)
4.3 KiB
4.3 KiB
UserWallet – Collecte distante (2 devices)
Author: Équipe 4NK
Date: 2026-01-26
Objectif
Supporter cardinalite_minimale > 1 avec deux appareils : signature locale sur le 1ᵉʳ, signature sur le 2ᵉ, récupération des sigs via les relais, puis publication de la preuve lorsque toutes les sigs requises sont réunies.
Flux
- Device 1 : construire le challenge, signer localement (pairs locaux du membre), publier message + sigs locales sur les relais.
- Device 1 : boucle de collecte — fetch des sigs par hash sur les relais, merge avec les sigs locales, dédup par pair. Dès que
hasEnoughSignatures(assez de pairs distincts par membre par rapport àcardinalite_minimale), on arrête. - Device 2 : obtenir hash et nonce (lien ou QR émis par Device 1 pendant la collecte). Ouvrir
/login-sign?hash=...&nonce=..., signerhash-nonceavec la clé du pair local, poster la signature sur les relais. - Device 1 : une fois assez de sigs (dont celle du 2ᵉ), si au moins une signature provient du 2ᵉ appareil (pair non local) → confirmation manuelle : « Les mots ont pu être visibles à l’écran et interceptés. Confirmer que c’est bien vous qui avez validé sur l’autre appareil ? » [Accepter] / [Refuser]. Accepter → vérification (dépendances, clés autorisées, strict), marquage du nonce, envoi de la preuve au parent. Refuser → pas d’envoi, transition vers
S_LOGIN_FAILURE. - Device 1 : si aucune sig distante (cardinalité 1, sig locale seule), pas de confirmation ; vérification et envoi directement.
Modifications
loginBuilder.signChallenge: signature dehash-nonceuniquement (pluspairUuid), pour alignement vérif + relais.loginValidation:requiredSigsPerMember,hasEnoughSignatures,hasRemoteSignatures(au moins une sig d’un pair non local). Suppression du refus systématiquecardinalite > 1.collectSignatures:fetchSignaturesForHash,buildPairToMembers,buildPubkeyToPair,mapMsgSignaturesToProofFormat,runCollectLoop(poll + timeout).loginPublish:publishMessageAndSigs(message + sigs locales vers relais).LoginScreen: signature pour les pairs locaux du membre uniquement ; après publication, boucle de collecte siloginPath; sihasRemoteSignatures→ écran de confirmation [Accepter] / [Refuser], puis vérification et envoi uniquement sur Accepter ; sinon vérification et envoi directs. Affichage « En attente des signatures des autres appareils… » +LoginCollectShare(lien + QR vers/login-sign). Retour désactivé pendant la confirmation.- Machine à états :
E_LOCAL_VERDICT_REJECTdepuisS_LOGIN_PUBLISH_PROOF→S_LOGIN_FAILURE(refus des sigs distantes sans envoi). LoginSignScreen: route/login-sign?hash=...&nonce=...; signature et publication de la sig sur les relais.LoginCollectShare: lien et QR vers/login-sign?hash=...&nonce=...pendant la collecte.
Modalités de déploiement
Déploiement classique du front userwallet. Aucune évolution côte relais.
Confirmation manuelle (signatures 2ᵉ appareil)
Les mots (lien/QR) sont affichés à l’écran et peuvent être interceptés (épaule, tiers). Quand au moins une signature reçue provient d’un pair non local (2ᵉ device), Device 1 n’envoie pas la preuve tant que l’utilisateur n’a pas manuellement accepté (« c’est bien moi qui ai validé sur l’autre appareil »). [Refuser] → pas d’envoi, S_LOGIN_FAILURE.
Modalités d’analyse
- Device 1 : construction du chemin, challenge, publication → collecte → affichage du lien/QR. Ouvrir le lien sur le 2ᵉ appareil, signer → retour sur le 1ᵉʳ, la collecte doit finir. Si sigs distantes : affichage de la confirmation Accepter/Refuser ; Accepter → envoi preuve, Refuser → échec.
- Device 2 : aller sur
/login-sign?hash=...&nonce=...(ou scanner le QR), vérifier « Signature publiée ». - Vérifier timeout de collecte (5 min) si le 2ᵉ ne signe pas.
- Vérifier que [Refuser] n’envoie pas la preuve et mène à l’état d’échec.
Références
features/userwallet-validation-conformite.md(§ Cardinalite_minimale)userwallet/docs/specs.md(collecte signatures mFA, S_LOGIN_COLLECT_SIGNATURES)