#!/usr/bin/env node /** * Script batch pour compléter les blockTime manquants dans utxo_list.txt * Récupère blockTime depuis RPC pour les UTXOs confirmés sans blockTime */ import { readFileSync, writeFileSync, existsSync } from 'fs'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const utxoListPath = join(__dirname, '../utxo_list.txt'); // Charger les variables d'environnement import { config } from 'dotenv'; config({ path: join(__dirname, '../signet-dashboard/.env') }); const BITCOIN_RPC_URL = process.env.BITCOIN_RPC_URL || 'http://127.0.0.1:38332'; const BITCOIN_RPC_USER = process.env.BITCOIN_RPC_USER || 'bitcoin'; const BITCOIN_RPC_PASSWORD = process.env.BITCOIN_RPC_PASSWORD || 'bitcoin'; const BITCOIN_RPC_WALLET = process.env.BITCOIN_RPC_WALLET || 'custom_signet'; if (!existsSync(utxoListPath)) { console.error('utxo_list.txt not found'); process.exit(1); } async function getTransactionBlockTime(txid) { try { const auth = Buffer.from(`${BITCOIN_RPC_USER}:${BITCOIN_RPC_PASSWORD}`).toString('base64'); const rpcUrl = `${BITCOIN_RPC_URL}/wallet/${BITCOIN_RPC_WALLET}`; const response = await fetch(rpcUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Basic ${auth}`, }, body: JSON.stringify({ jsonrpc: '1.0', id: 'gettx', method: 'gettransaction', params: [txid], }), }); if (!response.ok) { return null; } const result = await response.json(); if (result.error) { return null; } return result.result.blocktime || null; } catch (error) { return null; } } try { const content = readFileSync(utxoListPath, 'utf8').trim(); if (!content) { console.log('utxo_list.txt is empty'); process.exit(0); } const lines = content.split('\n'); let updated = 0; let processed = 0; const total = lines.length; console.log(`📊 Traitement de ${total} lignes...`); const outputLines = await Promise.all(lines.map(async (line) => { if (!line.trim()) return line; const parts = line.split(';'); // Format attendu: category;txid;vout;amount;confirmations;isAnchorChange;blockTime if (parts.length >= 6) { const confirmations = parseInt(parts[4], 10) || 0; const blockTime = parts.length >= 7 ? parts[6] : ''; // Si confirmé mais blockTime manquant, récupérer depuis RPC if (confirmations > 0 && (!blockTime || blockTime.trim() === '')) { const txid = parts[1]; processed++; if (processed % 10 === 0) { console.log(`⏳ Traitement: ${processed}/${total}...`); } const txBlockTime = await getTransactionBlockTime(txid); if (txBlockTime) { updated++; // Reconstruire la ligne avec blockTime const newParts = [...parts]; if (newParts.length === 6) { newParts.push(txBlockTime.toString()); } else { newParts[6] = txBlockTime.toString(); } return newParts.join(';'); } } } return line; })); if (updated > 0) { writeFileSync(utxoListPath, outputLines.join('\n'), 'utf8'); console.log(`✅ ${updated} ligne(s) mise(s) à jour avec blockTime dans utxo_list.txt`); } else { console.log('✅ Toutes les lignes ont déjà un blockTime dans utxo_list.txt'); } } catch (error) { console.error('Error:', error.message); process.exit(1); }