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