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

14 KiB
Raw Blame History

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:

# 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/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

#!/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)

  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)

  1. Health check amélioré (solution 5)
  2. Monitoring des UTXOs verrouillés (solution 1)
  3. Timeout sur Promise.all() (solution B)

Priorité 3 (Amélioration - à planifier)

  1. Retry avec backoff pour RPC (solution D)
  2. Monitoring et alertes (solution E)
  3. 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)