#!/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'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Charger les variables d'environnement depuis .env si disponible let envVars = {}; try { const envPath = join(__dirname, '../signet-dashboard/.env'); if (existsSync(envPath)) { const envContent = readFileSync(envPath, 'utf8'); for (const line of envContent.split('\n')) { const match = line.match(/^([^#=]+)=(.*)$/); if (match) { envVars[match[1].trim()] = match[2].trim(); } } } } catch (error) { console.warn('Could not load .env file, using defaults'); } const BITCOIN_RPC_URL = envVars.BITCOIN_RPC_URL || process.env.BITCOIN_RPC_URL || 'http://127.0.0.1:38332'; const BITCOIN_RPC_USER = envVars.BITCOIN_RPC_USER || process.env.BITCOIN_RPC_USER || 'bitcoin'; const BITCOIN_RPC_PASSWORD = envVars.BITCOIN_RPC_PASSWORD || process.env.BITCOIN_RPC_PASSWORD || 'bitcoin'; const BITCOIN_RPC_WALLET = envVars.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);