**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/*
14 KiB
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.jsligne 23:timeout: parseInt(process.env.BITCOIN_RPC_TIMEOUT || '30000').envligne 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.jslignes 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.jsligne 908-914: Blocfinallyqui 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 blocfinally, 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.jslignes 910-914: Gestion d'erreur dans lefinally
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.jslignes 142-159:unlockUtxo()avec gestion d'erreur silencieusebitcoin-rpc.jslignes 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.jslignes 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.jslignes 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:
# 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:
# 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:
# 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:
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:
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:
#!/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:
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:
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:
// 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:
// 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:
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/detailedavec é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
#!/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
#!/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:
# 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)
- Déverrouillage automatique au démarrage (solution C)
- Timeout de sécurité sur le mutex (solution A)
- Script cron de nettoyage automatique (solution B)
Priorité 2 (Important - à implémenter rapidement)
- Health check amélioré (solution 5)
- Monitoring des UTXOs verrouillés (solution 1)
- Timeout sur Promise.all() (solution B)
Priorité 3 (Amélioration - à planifier)
- Retry avec backoff pour RPC (solution D)
- Monitoring et alertes (solution E)
- Scripts de diagnostic (solution 3)
Pages affectées
api-anchorage/src/bitcoin-rpc.js: Améliorations du mutex et gestion d'erreursapi-anchorage/src/server.js: Déverrouillage au démarrageapi-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)