anchorage_layer_simple/api-anchorage/fix-utxos-status.mjs
ncantu 9929653ec2 Add automatic UTXO synchronization when no UTXOs available
**Motivations:**
- Synchroniser automatiquement les UTXOs depuis Bitcoin RPC si aucun UTXO n'est disponible dans la base de données
- Corriger les UTXOs mal marqués comme dépensés
- Améliorer la robustesse en cas de désynchronisation de la base de données

**Root causes:**
- La base de données peut être désynchronisée avec Bitcoin RPC
- Tous les UTXOs peuvent être marqués comme dépensés alors qu'ils sont disponibles dans Bitcoin
- Aucune synchronisation automatique n'était effectuée avant de chercher des UTXOs

**Correctifs:**
- Ajout d'une synchronisation automatique dans createAnchorTransaction() si aucun UTXO n'est disponible
- Création d'un script fix-utxos-status.mjs pour corriger les UTXOs mal marqués
- Création d'un script sync-utxos-from-bitcoin.mjs pour synchroniser depuis Bitcoin RPC

**Evolutions:**
- Synchronisation automatique des UTXOs disponibles depuis Bitcoin RPC avant de chercher des UTXOs
- Mise à jour automatique du statut is_spent_onchain pour les UTXOs disponibles dans Bitcoin
- Scripts de diagnostic et de correction pour maintenir la cohérence de la base de données

**Pages affectées:**
- api-anchorage/src/bitcoin-rpc.js
- api-anchorage/fix-utxos-status.mjs
- api-anchorage/sync-utxos-from-bitcoin.mjs
2026-01-28 15:30:18 +01:00

127 lines
4.2 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Script pour corriger les UTXOs mal marqués comme dépensés
*
* Ce script vérifie tous les UTXOs marqués comme dépensés dans la base de données
* et les compare avec listunspent pour corriger leur statut is_spent_onchain.
*
* Usage: node api-anchorage/fix-utxos-status.mjs
*/
import { getDatabase } from './src/database.js';
import { bitcoinRPC } from './src/bitcoin-rpc.js';
async function fixUtxosStatus() {
console.log('🔍 Démarrage de la correction des UTXOs mal marqués...\n');
const db = getDatabase();
try {
// Compter les UTXOs marqués comme dépensés
const spentCount = db.prepare(`
SELECT COUNT(*) as count
FROM utxos
WHERE is_spent_onchain = 1
`).get().count;
console.log(`📊 UTXOs marqués comme dépensés: ${spentCount}`);
if (spentCount === 0) {
console.log('✅ Aucun UTXO à vérifier');
return;
}
// Récupérer tous les UTXOs disponibles depuis Bitcoin
console.log('📡 Récupération des UTXOs depuis Bitcoin...');
const unspent = await bitcoinRPC.callRPCCommandWithRetry('listunspent', 1, 9999999); // Minimum 1 confirmation
console.log(`📊 UTXOs disponibles dans Bitcoin: ${unspent.length}`);
if (unspent.length === 0) {
console.log('⚠️ Aucun UTXO disponible dans Bitcoin');
return;
}
// Créer un Set pour recherche rapide des UTXOs disponibles dans Bitcoin
const availableUtxosSet = new Set();
for (const utxo of unspent) {
availableUtxosSet.add(`${utxo.txid}:${utxo.vout}`);
}
// Récupérer tous les UTXOs marqués comme dépensés dans la DB
console.log('💾 Vérification des UTXOs marqués comme dépensés...');
const spentUtxos = db.prepare(`
SELECT txid, vout, address, amount, confirmations, category, block_time
FROM utxos
WHERE is_spent_onchain = 1
`).all();
console.log(`📊 UTXOs marqués comme dépensés: ${spentUtxos.length}`);
// Vérifier et corriger les UTXOs mal marqués
const updateStmt = db.prepare(`
UPDATE utxos
SET is_spent_onchain = 0, updated_at = CURRENT_TIMESTAMP
WHERE txid = ? AND vout = ?
`);
let correctedCount = 0;
// Traiter par batch pour éviter la surcharge
const BATCH_SIZE = 1000;
for (let i = 0; i < spentUtxos.length; i += BATCH_SIZE) {
const batch = spentUtxos.slice(i, i + BATCH_SIZE);
for (const utxo of batch) {
const utxoKey = `${utxo.txid}:${utxo.vout}`;
if (availableUtxosSet.has(utxoKey)) {
// L'UTXO est disponible dans Bitcoin mais marqué comme dépensé dans la DB
updateStmt.run(utxo.txid, utxo.vout);
correctedCount++;
}
}
if ((i + BATCH_SIZE) % 10000 === 0) {
console.log(` ⏳ Traitement: ${Math.min(i + BATCH_SIZE, spentUtxos.length)}/${spentUtxos.length} UTXOs...`);
}
}
console.log(`\n📊 Résumé:`);
console.log(` - UTXOs disponibles dans Bitcoin: ${unspent.length}`);
console.log(` - UTXOs marqués comme dépensés dans DB: ${spentUtxos.length}`);
console.log(` - UTXOs corrigés (marqués comme non dépensés): ${correctedCount}`);
// Afficher les statistiques finales
const finalStats = {
total: db.prepare('SELECT COUNT(*) as count FROM utxos').get().count,
spent: db.prepare('SELECT COUNT(*) as count FROM utxos WHERE is_spent_onchain = 1').get().count,
notSpent: db.prepare('SELECT COUNT(*) as count FROM utxos WHERE is_spent_onchain = 0').get().count,
};
console.log(`\n📈 Statistiques finales:`);
console.log(` - Total UTXOs: ${finalStats.total}`);
console.log(` - Dépensés: ${finalStats.spent}`);
console.log(` - Non dépensés: ${finalStats.notSpent}`);
} catch (error) {
console.error('❌ Erreur lors de la correction:', error.message);
console.error(error.stack);
process.exit(1);
} finally {
db.close();
}
}
// Exécuter la correction
fixUtxosStatus()
.then(() => {
console.log('\n✅ Correction terminée');
process.exit(0);
})
.catch((error) => {
console.error('❌ Erreur fatale:', error);
process.exit(1);
});