anchorage_layer_simple/fixKnowledge/api-anchorage-mutex-blockage-analysis.md
ncantu fe7f49b6cd Update API anchorage, services, and website skeleton
**Motivations:**
- Synchronisation des modifications sur l'API anchorage, les services et le website skeleton
- Ajout de scripts de monitoring et de diagnostic pour l'API anchorage
- Documentation des problèmes de mutex et de provisioning UTXO

**Root causes:**
- N/A (commit de synchronisation)

**Correctifs:**
- N/A (commit de synchronisation)

**Evolutions:**
- Ajout de scripts de monitoring et de diagnostic pour l'API anchorage
- Amélioration de la gestion des mutex et des UTXOs
- Mise à jour de la documentation

**Pages affectées:**
- api-anchorage/src/bitcoin-rpc.js
- api-anchorage/src/routes/anchor.js
- api-anchorage/src/routes/health.js
- api-anchorage/src/server.js
- api-anchorage/README-MONITORING.md
- api-anchorage/cleanup-stale-locks.mjs
- api-anchorage/diagnose.mjs
- api-anchorage/unlock-utxos.mjs
- service-login-verify/src/persistentNonceCache.ts
- signet-dashboard/src/server.js
- signet-dashboard/public/*
- userwallet/src/hooks/useChannel.ts
- userwallet/src/services/relayNotificationService.ts
- userwallet/src/utils/defaultContract.ts
- website-skeleton/src/*
- docs/DOMAINS_AND_PORTS.md
- docs/INTERFACES.md
- features/*
- fixKnowledge/*
2026-01-28 15:11:59 +01:00

477 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Analyse: Causes possibles du blocage du mutex et UTXOs verrouillés
**Date:** 2026-01-28
**Auteur:** Équipe 4NK
## Vue d'ensemble
Ce document analyse les causes possibles du blocage du mutex et des UTXOs verrouillés dans l'API d'ancrage, ainsi que les moyens de contrôle et de correction.
## Causes possibles identifiées
### 1. Timeout RPC Bitcoin (ESOCKETTIMEDOUT)
**Cause:**
- Les appels RPC vers Bitcoin Core timeout (défaut: 30s, configuré: 60s)
- Si un appel RPC bloque indéfiniment, le code ne peut pas continuer
- Le mutex reste acquis et les UTXOs restent verrouillés
**Scénarios:**
- Bitcoin Core surchargé ou non réactif
- Problème réseau entre l'API et Bitcoin Core
- Bitcoin Core en cours de synchronisation ou de traitement lourd
- Wallet Bitcoin verrouillé ou non accessible
**Code concerné:**
- `bitcoin-rpc.js` ligne 23: `timeout: parseInt(process.env.BITCOIN_RPC_TIMEOUT || '30000')`
- `.env` ligne 6: `BITCOIN_RPC_TIMEOUT=60000`
**Indicateurs:**
- Logs: `Error getting balance: ESOCKETTIMEDOUT`
- Logs: `Failed to get balance: ESOCKETTIMEDOUT`
- Mutex bloqué pendant plus de 60 secondes
### 2. Promise.all() qui ne se résout jamais
**Cause:**
- Ligne 253: `await Promise.all(addressPromises)` peut bloquer si une Promise ne se résout jamais
- Si `getNewAddress()` timeout ou échoue silencieusement, la Promise reste en attente
- Le mutex reste acquis
**Scénarios:**
- Bitcoin RPC ne répond pas à `getNewAddress()`
- Timeout RPC trop court pour certaines opérations
- Erreur réseau non gérée
**Code concerné:**
- `bitcoin-rpc.js` lignes 248-253: Génération des adresses en parallèle
**Indicateurs:**
- Logs montrent "Anchor request received" mais pas de suite
- Pas d'erreur dans les logs, mais la requête ne se termine jamais
### 3. Erreur non gérée avant le bloc finally
**Cause:**
- Si une erreur se produit avant d'atteindre le bloc `finally` (ligne 908), le mutex peut ne pas être libéré
- Exemple: erreur fatale Node.js, OOM (Out of Memory), crash du processus
**Scénarios:**
- Erreur fatale Node.js (segfault, assertion failed)
- Manque de mémoire (OOM killer)
- Arrêt brutal du processus (kill -9)
- Exception non catchée dans une fonction asynchrone
**Code concerné:**
- `bitcoin-rpc.js` ligne 908-914: Bloc `finally` qui libère le mutex
**Indicateurs:**
- Processus Node.js redémarré par systemd
- Logs montrent un arrêt brutal sans message d'erreur
- UTXOs verrouillés après redémarrage
### 4. Erreur dans le bloc finally lui-même
**Cause:**
- Si `releaseMutex()` échoue dans le bloc `finally`, le mutex reste bloqué
- Actuellement, l'erreur est seulement loggée en WARN, mais le mutex n'est pas libéré
**Scénarios:**
- Exception lors de l'appel à `releaseMutex()`
- Problème avec la Promise du mutex
**Code concerné:**
- `bitcoin-rpc.js` lignes 910-914: Gestion d'erreur dans le `finally`
**Indicateurs:**
- Logs: `Error releasing mutex`
- Mutex reste bloqué malgré le `finally`
### 5. Déverrouillage des UTXOs qui échoue silencieusement
**Cause:**
- Ligne 903: `this.unlockUtxo()` peut échouer silencieusement (ligne 152-158)
- Si la mise à jour de la base de données échoue, l'UTXO reste verrouillé
- Le mutex est libéré, mais les UTXOs restent verrouillés en base
**Scénarios:**
- Base de données verrouillée (WAL mode, autre transaction en cours)
- Erreur SQL (contrainte, table verrouillée)
- Problème de permissions sur la base de données
**Code concerné:**
- `bitcoin-rpc.js` lignes 142-159: `unlockUtxo()` avec gestion d'erreur silencieuse
- `bitcoin-rpc.js` lignes 899-905: Déverrouillage en cas d'erreur
**Indicateurs:**
- Mutex libéré (pas de timeout)
- UTXOs toujours verrouillés dans la base de données
- Logs: `Error updating UTXO unlock status in database`
### 6. Transaction SQL qui échoue partiellement
**Cause:**
- Si plusieurs UTXOs sont verrouillés mais que le déverrouillage échoue pour certains
- La boucle continue mais certains UTXOs restent verrouillés
**Scénarios:**
- Erreur SQL pour un UTXO spécifique
- UTXO supprimé de la base pendant le déverrouillage
- Contrainte de base de données violée
**Code concerné:**
- `bitcoin-rpc.js` lignes 900-904: Boucle de déverrouillage
**Indicateurs:**
- Certains UTXOs déverrouillés, d'autres non
- Logs montrant des erreurs pour certains UTXOs
### 7. Race condition lors de l'acquisition du mutex
**Cause:**
- Si plusieurs requêtes arrivent simultanément, elles peuvent toutes acquérir le mutex
- Le timeout de 180s peut être atteint avant que toutes les requêtes ne se terminent
**Scénarios:**
- Pic de charge avec de nombreuses requêtes simultanées
- Requêtes qui prennent plus de 180s chacune
- Accumulation de requêtes en attente
**Code concerné:**
- `bitcoin-rpc.js` lignes 42-76: `acquireUtxoMutex()` avec timeout de 180s
**Indicateurs:**
- Nombreux timeouts de mutex dans les logs
- Requêtes qui attendent plus de 180s
### 8. Problème de connexion à la base de données
**Cause:**
- Si la connexion à la base de données est perdue, les opérations de verrouillage/déverrouillage échouent
- Le mutex peut être libéré en mémoire, mais les UTXOs restent verrouillés en base
**Scénarios:**
- Base de données corrompue
- Connexion SQLite fermée
- Problème de permissions
**Code concerné:**
- `database.js`: Gestion de la connexion SQLite
- Toutes les opérations de verrouillage/déverrouillage
**Indicateurs:**
- Erreurs SQL dans les logs
- Base de données inaccessible
## Moyens de contrôle
### 1. Monitoring des UTXOs verrouillés
**Script de vérification:**
```bash
# Vérifier le nombre d'UTXOs verrouillés
curl http://localhost:3010/api/anchor/locked-utxos | jq '.count'
# Si > 0, il y a un problème
```
**Alertes recommandées:**
- Warning si > 5 UTXOs verrouillés
- Critical si > 10 UTXOs verrouillés
- Critical si UTXOs verrouillés depuis > 10 minutes
### 2. Monitoring des timeouts de mutex
**Vérification dans les logs:**
```bash
# Compter les timeouts de mutex dans les dernières heures
sudo journalctl -u anchorage-api --since "1 hour ago" | grep -c "Mutex acquisition timeout"
# Si > 0, il y a un problème
```
**Alertes recommandées:**
- Warning si > 1 timeout par heure
- Critical si > 5 timeouts par heure
### 3. Monitoring des erreurs RPC Bitcoin
**Vérification dans les logs:**
```bash
# Compter les erreurs RPC Bitcoin
sudo journalctl -u anchorage-api --since "1 hour ago" | grep -c "ESOCKETTIMEDOUT\|ETIMEDOUT\|ECONNRESET"
# Si > 0, problème de connexion Bitcoin RPC
```
**Alertes recommandées:**
- Warning si > 1 erreur RPC par heure
- Critical si > 5 erreurs RPC par heure
### 4. Monitoring de la durée des opérations
**Vérification:**
- Logger le temps d'exécution de `createAnchorTransaction()`
- Alerter si > 30 secondes (normal: < 10 secondes)
**Code à ajouter:**
```javascript
const startTime = Date.now();
try {
// ... opération ...
} finally {
const duration = Date.now() - startTime;
if (duration > 30000) {
logger.warn('Anchor transaction took too long', { duration, hash });
}
}
```
### 5. Health check amélioré
**Endpoint à créer:**
```javascript
GET /health/detailed
{
"ok": true,
"mutex": {
"locked": false,
"waiting": 0
},
"utxos": {
"locked": 0,
"locked_since": null
},
"bitcoin": {
"connected": true,
"rpc_timeout": 60000
}
}
```
### 6. Script de maintenance automatique
**Script cron à créer:**
```bash
#!/bin/bash
# Déverrouiller les UTXOs verrouillés depuis plus de 10 minutes
LOCKED_COUNT=$(curl -s http://localhost:3010/api/anchor/locked-utxos | jq '.count')
if [ "$LOCKED_COUNT" -gt 0 ]; then
# Vérifier l'âge des verrouillages
# Si > 10 minutes, déverrouiller automatiquement
cd /home/ncantu/Bureau/code/bitcoin/api-anchorage
node unlock-utxos.mjs
fi
```
## Moyens de correction
### 1. Correction immédiate (déjà implémentée)
**Script de déverrouillage:**
- `api-anchorage/unlock-utxos.mjs`: Déverrouille tous les UTXOs verrouillés
**Utilisation:**
```bash
cd /home/ncantu/Bureau/code/bitcoin/api-anchorage
node unlock-utxos.mjs
```
### 2. Améliorations préventives à implémenter
#### A. Timeout de sécurité sur le mutex
**Problème:** Le mutex peut rester acquis indéfiniment si l'opération se bloque.
**Solution:** Ajouter un timeout de sécurité qui libère automatiquement le mutex après un délai maximum.
**Code à ajouter:**
```javascript
async createAnchorTransaction(...) {
const releaseMutex = await this.acquireUtxoMutex();
let mutexSafetyTimeout;
// Timeout de sécurité: libérer le mutex après 5 minutes maximum
mutexSafetyTimeout = setTimeout(() => {
logger.error('Mutex held for too long, forcing release', { hash });
releaseMutex();
}, 300000); // 5 minutes
try {
// ... opération ...
} finally {
clearTimeout(mutexSafetyTimeout);
releaseMutex();
}
}
```
#### B. Timeout sur Promise.all()
**Problème:** `Promise.all()` peut bloquer indéfiniment si une Promise ne se résout jamais.
**Solution:** Ajouter un timeout sur `Promise.all()`.
**Code à modifier:**
```javascript
// Ligne 253: Remplacer
const allAddresses = await Promise.all(addressPromises);
// Par:
const allAddresses = await Promise.race([
Promise.all(addressPromises),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Address generation timeout')), 30000)
)
]);
```
#### C. Déverrouillage automatique des UTXOs anciens
**Problème:** Les UTXOs peuvent rester verrouillés si le processus crash.
**Solution:** Déverrouiller automatiquement les UTXOs verrouillés depuis plus de 10 minutes au démarrage.
**Code à ajouter dans `server.js`:**
```javascript
// Au démarrage du serveur
const db = getDatabase();
const result = db.prepare(`
UPDATE utxos
SET is_locked_in_mutex = 0
WHERE is_locked_in_mutex = 1
AND updated_at < datetime('now', '-10 minutes')
`).run();
if (result.changes > 0) {
logger.info('Unlocked stale UTXOs on startup', { count: result.changes });
}
```
#### D. Retry avec backoff pour les appels RPC
**Problème:** Les timeouts RPC peuvent être temporaires.
**Solution:** Implémenter un retry avec backoff exponentiel.
**Code à ajouter:**
```javascript
async callRPCWithRetry(method, params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await this.client[method](...params);
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = Math.min(1000 * Math.pow(2, i), 10000);
logger.warn(`RPC call failed, retrying in ${delay}ms`, { method, attempt: i + 1 });
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
```
#### E. Monitoring et alertes
**Problème:** Les problèmes ne sont détectés qu'après coup.
**Solution:** Implémenter un système de monitoring proactif.
**À implémenter:**
- Endpoint `/health/detailed` avec état du mutex et UTXOs
- Script cron pour vérifier et déverrouiller automatiquement
- Alertes (email, webhook) en cas de problème
### 3. Scripts de diagnostic
#### A. Script de diagnostic complet
**Créer:** `api-anchorage/diagnose.mjs`
```javascript
#!/usr/bin/env node
import { getDatabase } from './src/database.js';
const db = getDatabase();
// UTXOs verrouillés
const locked = db.prepare(`
SELECT txid, vout, address, amount, updated_at,
(julianday('now') - julianday(updated_at)) * 24 * 60 as minutes_locked
FROM utxos
WHERE is_locked_in_mutex = 1
ORDER BY updated_at
`).all();
console.log(`\n📊 UTXOs verrouillés: ${locked.length}`);
if (locked.length > 0) {
console.table(locked.map(u => ({
txid: u.txid.substring(0, 16) + '...',
vout: u.vout,
amount: u.amount,
locked_for_minutes: Math.round(u.minutes_locked * 100) / 100
})));
}
// UTXOs verrouillés depuis plus de 10 minutes
const stale = locked.filter(u => u.minutes_locked > 10);
if (stale.length > 0) {
console.log(`\n⚠ UTXOs verrouillés depuis plus de 10 minutes: ${stale.length}`);
}
db.close();
```
#### B. Script de nettoyage automatique
**Créer:** `api-anchorage/cleanup-stale-locks.mjs`
```javascript
#!/usr/bin/env node
import { getDatabase } from './src/database.js';
const db = getDatabase();
const result = db.prepare(`
UPDATE utxos
SET is_locked_in_mutex = 0
WHERE is_locked_in_mutex = 1
AND updated_at < datetime('now', '-10 minutes')
`).run();
console.log(`✅ UTXOs déverrouillés: ${result.changes}`);
db.close();
```
**Cron job:**
```bash
# Toutes les 5 minutes
*/5 * * * * cd /home/ncantu/Bureau/code/bitcoin/api-anchorage && node cleanup-stale-locks.mjs
```
## Recommandations prioritaires
### Priorité 1 (Critique - à implémenter immédiatement)
1. **Déverrouillage automatique au démarrage** (solution C)
2. **Timeout de sécurité sur le mutex** (solution A)
3. **Script cron de nettoyage automatique** (solution B)
### Priorité 2 (Important - à implémenter rapidement)
4. **Health check amélioré** (solution 5)
5. **Monitoring des UTXOs verrouillés** (solution 1)
6. **Timeout sur Promise.all()** (solution B)
### Priorité 3 (Amélioration - à planifier)
7. **Retry avec backoff pour RPC** (solution D)
8. **Monitoring et alertes** (solution E)
9. **Scripts de diagnostic** (solution 3)
## Pages affectées
- `api-anchorage/src/bitcoin-rpc.js`: Améliorations du mutex et gestion d'erreurs
- `api-anchorage/src/server.js`: Déverrouillage au démarrage
- `api-anchorage/unlock-utxos.mjs`: Script de déverrouillage (existant)
- `api-anchorage/cleanup-stale-locks.mjs`: Script de nettoyage automatique (à créer)
- `api-anchorage/diagnose.mjs`: Script de diagnostic (à créer)
- `fixKnowledge/api-anchorage-mutex-blockage-analysis.md`: Documentation (nouveau)