anchorage_layer_simple/scripts/complete-utxo-list-blocktime.js
ncantu cad73cb265 UTXO-list: dates/blockTime historiques, récupération frais depuis ancrages, diagnostic Bloc Rewards
**Motivations:**
- Ajouter dates manquantes dans hash_list.txt et compléter historique
- Compléter blockTime manquants dans utxo_list.txt et compléter historique
- Récupérer frais depuis transactions d'ancrage (OP_RETURN) et les stocker
- Bouton UI pour déclencher récupération frais
- Diagnostic Bloc Rewards (pourquoi ~4700 BTC au lieu de 50 BTC)

**Root causes:**
- hash_list.txt sans date (format ancien)
- utxo_list.txt blockTime souvent vide
- Frais absents du fichier (métadonnées OP_RETURN non stockées)
- Pas de moyen de récupérer/compléter frais depuis UI

**Correctifs:**
- hash_list.txt : format étendu avec date (rétrocompatible)
- utxo_list.txt : blockTime complété automatiquement lors écritures
- fees_list.txt : nouveau fichier pour stocker frais
- updateFeesFromAnchors() : récupère frais depuis OP_RETURN ancrages
- Endpoint /api/utxo/fees/update pour déclencher récupération
- Bouton "Récupérer les frais depuis les ancrages" dans section Frais (spinner)
- Scripts batch : complete-hash-list-dates.js, complete-utxo-list-blocktime.js
- Script diagnostic : diagnose-bloc-rewards.js (subsidy, coinbase, listunspent)

**Evolutions:**
- Frais chargés depuis fees_list.txt dans getUtxoList
- Complétion automatique dates/blockTime lors écritures futures

**Pages affectées:**
- signet-dashboard/src/bitcoin-rpc.js
- signet-dashboard/src/server.js
- signet-dashboard/public/utxo-list.html
- scripts/complete-hash-list-dates.js
- scripts/complete-utxo-list-blocktime.js
- scripts/diagnose-bloc-rewards.js
- features/utxo-list-fees-update-and-historical-completion.md
2026-01-26 01:59:46 +01:00

124 lines
3.6 KiB
JavaScript
Executable File

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