anchorage_layer_simple/scripts/diagnose-bloc-rewards.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

158 lines
5.6 KiB
JavaScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
/**
* Script de diagnostic pour comprendre pourquoi les Bloc Rewards affichent ~4700 BTC au lieu de 50 BTC
* Vérifie : listunspent, getrawtransaction, blockheight, subsidy
*/
import { readFileSync, existsSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { config } from 'dotenv';
const require = createRequire(import.meta.url);
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
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';
const auth = Buffer.from(`${BITCOIN_RPC_USER}:${BITCOIN_RPC_PASSWORD}`).toString('base64');
const rpcUrl = `${BITCOIN_RPC_URL}/wallet/${BITCOIN_RPC_WALLET}`;
async function rpcCall(method, params = []) {
try {
const response = await fetch(rpcUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${auth}`,
},
body: JSON.stringify({
jsonrpc: '1.0',
id: method,
method,
params,
}),
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
if (result.error) {
throw new Error(`RPC error: ${result.error.message}`);
}
return result.result;
} catch (error) {
console.error(`Error calling ${method}:`, error.message);
return null;
}
}
async function diagnoseBlocRewards() {
console.log('🔍 Diagnostic Bloc Rewards - Pourquoi ~4700 BTC au lieu de 50 BTC?\n');
// 1. Lire les Bloc Rewards depuis utxo_list.txt
const utxoListPath = join(__dirname, '../utxo_list.txt');
if (!existsSync(utxoListPath)) {
console.error('❌ utxo_list.txt not found');
return;
}
const content = readFileSync(utxoListPath, 'utf8').trim();
const lines = content.split('\n');
const blocRewards = lines
.filter(line => line.trim().startsWith('bloc_rewards;'))
.slice(0, 5); // Prendre les 5 premiers
if (blocRewards.length === 0) {
console.log('⚠️ Aucun Bloc Reward trouvé dans utxo_list.txt');
return;
}
console.log(`📋 Analyse de ${blocRewards.length} Bloc Rewards depuis utxo_list.txt:\n`);
// 2. Vérifier blockchain info (hauteur actuelle)
console.log('1⃣ Informations blockchain:');
const blockchainInfo = await rpcCall('getblockchaininfo');
if (blockchainInfo) {
console.log(` Hauteur actuelle: ${blockchainInfo.blocks}`);
console.log(` Chain: ${blockchainInfo.chain}`);
console.log(` Subsidy attendu (blocs 0-209999): 50 BTC\n`);
}
// 3. Analyser chaque Bloc Reward
for (let i = 0; i < blocRewards.length; i++) {
const line = blocRewards[i];
const parts = line.split(';');
if (parts.length < 4) continue;
const txid = parts[1];
const amount = parseFloat(parts[3]);
const confirmations = parseInt(parts[4], 10) || 0;
console.log(`\n${i + 1}. Transaction: ${txid}`);
console.log(` Montant dans fichier: ${amount} BTC`);
// 4. Vérifier listunspent
const unspent = await rpcCall('listunspent', [1]);
if (unspent) {
const utxo = unspent.find(u => u.txid === txid && u.vout === 0);
if (utxo) {
console.log(` Montant listunspent: ${utxo.amount} BTC`);
console.log(` Confirmations listunspent: ${utxo.confirmations}`);
} else {
console.log(` ⚠️ UTXO non trouvé dans listunspent (peut être dépensé)`);
}
}
// 5. Vérifier getrawtransaction
const rawTx = await rpcCall('getrawtransaction', [txid, true]);
if (rawTx) {
console.log(` Blockheight: ${rawTx.blockheight || 'non confirmé'}`);
console.log(` Blocktime: ${rawTx.blocktime ? new Date(rawTx.blocktime * 1000).toISOString() : 'N/A'}`);
if (rawTx.vout && rawTx.vout.length > 0) {
const vout0 = rawTx.vout[0];
console.log(` Vout[0].value: ${vout0.value} BTC`);
console.log(` Vout[0].scriptPubKey.type: ${vout0.scriptPubKey?.type || 'N/A'}`);
// Vérifier si coinbase
if (rawTx.vin && rawTx.vin.length === 1 && rawTx.vin[0].coinbase) {
console.log(` ✅ Transaction coinbase détectée`);
console.log(` Coinbase: ${rawTx.vin[0].coinbase}`);
}
}
// 6. Calculer subsidy attendu
if (rawTx.blockheight !== null && rawTx.blockheight !== undefined) {
const height = rawTx.blockheight;
if (height < 210000) {
const expectedSubsidy = 50;
const fees = amount - expectedSubsidy;
console.log(` Subsidy attendu (hauteur ${height}): ${expectedSubsidy} BTC`);
console.log(` Frais calculés (montant - subsidy): ${fees.toFixed(8)} BTC`);
if (Math.abs(fees) > 1) {
console.log(` ⚠️ ÉCART IMPORTANT: ${fees.toFixed(2)} BTC de frais (anormal pour un bloc)`);
}
} else {
console.log(` ⚠️ Bloc après 210000, subsidy devrait être réduit`);
}
}
}
}
console.log('\n\n📝 Conclusion:');
console.log(' - Si montants listunspent/getrawtransaction = montants fichier → source correcte');
console.log(' - Si montants >> 50 BTC → signet custom avec subsidy différent ou bug nœud');
console.log(' - Vérifier chain params du signet (subsidy, halving)');
}
diagnoseBlocRewards().catch(console.error);