ncantu 937646cc45 Daily backup to git cron, backup/restore scripts, docs
**Motivations:**
- Export Signet and mining wallet backups to git with only 2 versions kept
- Document and add backup/restore scripts for signet and mining wallet

**Correctifs:**
- Backup-to-git uses SSH URL for passwordless cron; copy timestamped files only; prune to 2 versions; remove *-latest from backup repo

**Evolutions:**
- data/backup-to-git-cron.sh: daily export to git.4nkweb.com/4nk/backup
- save-signet-datadir-backup.sh, restore-signet-from-backup.sh, export-mining-wallet.sh, import-mining-wallet.sh
- features/backup-to-git-daily-cron.md, docs/MAINTENANCE.md backup section
- .gitignore: data/backup-to-git.log

**Pages affectées:**
- .gitignore, data/backup-to-git-cron.sh, docs/MAINTENANCE.md, features/backup-to-git-daily-cron.md
- save-signet-datadir-backup.sh, restore-signet-from-backup.sh, export-mining-wallet.sh, import-mining-wallet.sh
- Plus autres fichiers modifiés ou non suivis déjà présents dans le working tree
2026-02-04 03:07:57 +01:00

972 lines
44 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apprendre Bitcoin - Dashboard de Supervision</title>
<link rel="stylesheet" href="styles.css">
<style>
.learn-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.learn-header {
text-align: center;
margin-bottom: 40px;
padding: 30px 0;
background: linear-gradient(135deg, #f7931a, #ff6b35);
color: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5);
}
.learn-header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
.back-link {
display: inline-block;
margin-top: 15px;
padding: 10px 20px;
background-color: rgba(255, 255, 255, 0.2);
color: white;
text-decoration: none;
border-radius: 5px;
transition: background-color 0.3s;
}
.back-link:hover {
background-color: rgba(255, 255, 255, 0.3);
}
.concept-section {
margin-bottom: 50px;
background: var(--card-background);
border-radius: 10px;
padding: 30px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
border: 1px solid var(--border-color);
}
.concept-section h2 {
font-size: 2em;
margin-bottom: 20px;
color: var(--primary-color);
border-bottom: 3px solid var(--primary-color);
padding-bottom: 10px;
}
.concept-section h3 {
font-size: 1.5em;
margin-top: 25px;
margin-bottom: 15px;
color: var(--secondary-color);
}
.concept-explanation {
font-size: 1.1em;
line-height: 1.8;
margin-bottom: 20px;
color: var(--text-color);
}
.example-box {
background: var(--background-color);
border-left: 4px solid var(--primary-color);
padding: 20px;
margin: 20px 0;
border-radius: 5px;
}
.example-box h4 {
margin-top: 0;
color: var(--primary-color);
}
.example-data {
font-family: 'Courier New', monospace;
background: #1a1a1a;
color: #00ff00;
padding: 15px;
border-radius: 5px;
overflow-x: auto;
margin: 10px 0;
font-size: 0.9em;
}
.example-data.loading {
color: #ffc107;
}
.example-data.error {
color: var(--error-color);
}
.visual-diagram {
background: var(--background-color);
padding: 20px;
border-radius: 5px;
margin: 20px 0;
text-align: center;
}
.block-visual {
border: 3px solid var(--primary-color);
border-radius: 10px;
padding: 20px;
margin: 15px 0;
background: linear-gradient(135deg, rgba(247, 147, 26, 0.1), rgba(255, 107, 53, 0.1));
}
.block-header {
font-weight: bold;
color: var(--primary-color);
margin-bottom: 10px;
font-size: 1.2em;
}
.transaction-box {
border: 2px solid var(--border-color);
border-radius: 5px;
padding: 15px;
margin: 10px 0;
background: var(--card-background);
}
.tx-input {
background: rgba(13, 202, 240, 0.15);
border-left: 3px solid #0dcaf0;
padding: 10px;
margin: 5px 0;
color: var(--text-color);
}
.tx-output {
background: rgba(40, 167, 69, 0.15);
border-left: 3px solid #28a745;
padding: 10px;
margin: 5px 0;
color: var(--text-color);
}
.tx-opreturn {
background: rgba(255, 193, 7, 0.15);
border-left: 3px solid #ffc107;
padding: 10px;
margin: 5px 0;
color: var(--text-color);
}
.interactive-button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
font-size: 1em;
cursor: pointer;
transition: background-color 0.3s;
font-weight: 600;
margin: 10px 5px;
}
.interactive-button:hover {
background-color: #e8840a;
}
.interactive-button:disabled {
background-color: #555;
color: #888;
cursor: not-allowed;
}
.highlight {
background: rgba(255, 193, 7, 0.2);
padding: 2px 6px;
border-radius: 3px;
font-weight: 600;
}
.code-snippet {
background: var(--card-background);
border: 1px solid var(--border-color);
border-radius: 5px;
padding: 15px;
margin: 15px 0;
font-family: 'Courier New', monospace;
font-size: 0.9em;
overflow-x: auto;
color: var(--text-color);
}
.flow-diagram {
display: flex;
flex-direction: column;
align-items: center;
margin: 20px 0;
}
.flow-step {
background: var(--card-background);
border: 2px solid var(--primary-color);
border-radius: 10px;
padding: 20px;
margin: 10px 0;
width: 100%;
max-width: 600px;
text-align: center;
}
.flow-arrow {
font-size: 2em;
color: var(--primary-color);
margin: 5px 0;
}
.utxo-list {
list-style: none;
padding: 0;
}
.utxo-item {
background: var(--background-color);
border-left: 4px solid var(--primary-color);
padding: 15px;
margin: 10px 0;
border-radius: 5px;
}
.utxo-item.spent {
opacity: 0.6;
border-left-color: var(--border-color);
}
code {
background: var(--card-background);
color: var(--primary-color);
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
border: 1px solid var(--border-color);
}
.utxo-item.locked {
border-left-color: var(--warning-color);
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.loading-animation {
animation: pulse 1.5s ease-in-out infinite;
}
.anchor-flow {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin: 20px 0;
}
.anchor-step {
background: var(--card-background);
border: 2px solid var(--primary-color);
border-radius: 10px;
padding: 20px;
text-align: center;
}
.anchor-step-number {
background: var(--primary-color);
color: white;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 15px;
font-weight: bold;
font-size: 1.2em;
}
</style>
</head>
<body>
<div class="learn-container">
<div class="learn-header">
<h1>📚 Apprendre Bitcoin</h1>
<p class="subtitle">Découvrez les concepts fondamentaux de Bitcoin avec des exemples réels</p>
<a href="/" class="back-link">← Retour au Dashboard</a>
</div>
<!-- Section: Qu'est-ce qu'un bloc -->
<section class="concept-section" id="block">
<h2>🔗 Qu'est-ce qu'un Bloc ?</h2>
<div class="concept-explanation">
<p>Un <span class="highlight">bloc</span> est un conteneur qui regroupe plusieurs transactions Bitcoin.
Chaque bloc contient :</p>
<ul>
<li>Un <strong>en-tête</strong> avec des métadonnées (hash du bloc précédent, timestamp, difficulté)</li>
<li>Une liste de <strong>transactions</strong> validées</li>
<li>Une <strong>preuve de travail</strong> (proof-of-work) qui prouve que le mineur a effectué un travail computationnel</li>
</ul>
<p>Les blocs sont liés entre eux dans une chaîne (blockchain), formant un registre immuable de toutes les transactions.</p>
</div>
<div class="visual-diagram">
<div class="block-visual">
<div class="block-header">Bloc #<span id="example-block-height">-</span></div>
<div style="font-size: 0.9em; color: var(--text-color); margin-bottom: 15px;">
Hash: <span id="example-block-hash" class="example-data">Chargement...</span>
</div>
<div style="font-size: 0.9em; color: var(--text-color); margin-bottom: 15px;">
Timestamp: <span id="example-block-time">-</span>
</div>
<div style="font-size: 0.9em; color: var(--text-color); margin-bottom: 15px;">
Transactions: <span id="example-block-tx-count">-</span>
</div>
<div style="font-size: 0.9em; color: var(--text-color);">
Taille: <span id="example-block-size">-</span> bytes
</div>
</div>
</div>
<button class="interactive-button" onclick="loadExampleBlock()">📥 Charger un Bloc Réel</button>
</section>
<!-- Section: Le Mining -->
<section class="concept-section" id="mining">
<h2>⛏️ Qu'est-ce que le Mining ?</h2>
<div class="concept-explanation">
<p>Le <span class="highlight">mining</span> (minage) est le processus par lequel de nouveaux blocs sont ajoutés à la blockchain Bitcoin.</p>
<p>Les mineurs :</p>
<ul>
<li>Collectent des transactions en attente depuis le <strong>mempool</strong></li>
<li>Créent un nouveau bloc avec ces transactions</li>
<li>Résolvent un problème cryptographique complexe (proof-of-work)</li>
<li>Reçoivent une <strong>récompense</strong> (block reward) en Bitcoin pour leur travail</li>
</ul>
<p>La difficulté du problème s'ajuste automatiquement pour maintenir un temps moyen de 10 minutes entre chaque bloc (sur le réseau principal).</p>
</div>
<div class="example-box">
<h4>Récompense de Minage (Block Reward)</h4>
<p>Chaque bloc miné génère une transaction spéciale appelée <strong>coinbase</strong> qui crée de nouveaux bitcoins.</p>
<p>Sur Bitcoin Signet (réseau de test), la récompense est de <strong>50 000 sats (0.0005 BTC)</strong> par bloc.</p>
<p>Sur le réseau principal Bitcoin, la récompense diminue de moitié tous les 210 000 blocs (environ tous les 4 ans) :</p>
<ul>
<li>2009-2012 : 50 BTC par bloc</li>
<li>2012-2016 : 25 BTC par bloc</li>
<li>2016-2020 : 12.5 BTC par bloc</li>
<li>2020-2024 : 6.25 BTC par bloc</li>
<li>2024-2028 : 3.125 BTC par bloc</li>
</ul>
</div>
<div class="flow-diagram">
<div class="flow-step">
<strong>1. Transactions en attente</strong><br>
Les transactions sont collectées depuis le mempool
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>2. Création du bloc</strong><br>
Le mineur assemble les transactions dans un bloc
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>3. Proof-of-Work</strong><br>
Le mineur résout le problème cryptographique
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>4. Bloc miné</strong><br>
Le bloc est ajouté à la blockchain, le mineur reçoit la récompense
</div>
</div>
<h3>Comment fonctionne le Miner ?</h3>
<div class="concept-explanation">
<p>Le <span class="highlight">miner</span> (mineur) est le logiciel ou le nœud qui produit effectivement les blocs. Il écoute le réseau, construit des blocs candidats et les diffuse une fois quils sont valides.</p>
<p><strong>Sur le réseau principal Bitcoin</strong>, le miner :</p>
<ul>
<li>Obtient un <strong>modèle de bloc</strong> (block template) depuis son nœud Bitcoin Core : en-tête du bloc, transactions du mempool, difficulté cible</li>
<li>Sélectionne les transactions à inclure (souvent par ordre de frais par octet)</li>
<li>Construit le bloc (transaction coinbase + liste de transactions)</li>
<li>Cherche un <strong>nonce</strong> (et varie le coinbase si besoin) pour que le hash du bloc soit sous la cible de difficulté (proof-of-work)</li>
<li>Diffuse le bloc miné au réseau ; les autres nœuds le valident et lajoutent à leur chaîne</li>
</ul>
<p><strong>Sur Bitcoin Signet</strong> (réseau de test comme celui de ce dashboard), le principe est le même, mais la « preuve » change : au lieu dun proof-of-work coûteux, un bloc valide doit contenir une <strong>signature</strong> dune clé autorisée (signet). Le miner Signet récupère un template, construit le bloc, le signe avec la clé du signet, puis le diffuse. La récompense par bloc est fixe (par ex. 50 000 sats sur ce Signet).</p>
</div>
<div class="flow-diagram">
<div class="flow-step">
<strong>1. Modèle de bloc</strong><br>
Le nœud (Bitcoin Core) fournit un template : bloc précédent, mempool, difficulté
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>2. Construction du bloc</strong><br>
Le miner ajoute la transaction coinbase (récompense vers son adresse) et des transactions du mempool
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>3. Preuve</strong><br>
Mainnet : recherche dun nonce (proof-of-work). Signet : signature du bloc avec la clé autorisée
</div>
<div class="flow-arrow"></div>
<div class="flow-step">
<strong>4. Diffusion</strong><br>
Le bloc valide est envoyé au réseau ; les nœuds le valident et lajoutent à la blockchain
</div>
</div>
</section>
<!-- Section: Entrée de Transaction -->
<section class="concept-section" id="tx-input">
<h2>📥 Qu'est-ce qu'une Entrée de Transaction ?</h2>
<div class="concept-explanation">
<p>Une <span class="highlight">entrée de transaction</span> (transaction input) référence une sortie de transaction précédente (UTXO) qui sera dépensée.</p>
<p>Chaque entrée contient :</p>
<ul>
<li>Le <strong>TXID</strong> (identifiant) de la transaction précédente</li>
<li>L'<strong>index</strong> (vout) de la sortie à dépenser</li>
<li>Un <strong>script de déverrouillage</strong> (scriptSig) qui prouve la propriété</li>
</ul>
<p>Les entrées "consomment" des UTXO existants pour créer de nouvelles sorties.</p>
</div>
<div class="example-box">
<h4>Exemple d'Entrée de Transaction</h4>
<div class="code-snippet">
{
"txid": "a1b2c3d4e5f6...",
"vout": 0,
"scriptSig": {
"asm": "...",
"hex": "..."
},
"value": 0.00002500
}
</div>
<p>Cette entrée référence la sortie #0 de la transaction <code>a1b2c3d4e5f6...</code> d'un montant de 2500 sats.</p>
</div>
<div class="transaction-box">
<div class="tx-input">
<strong>📥 Entrée 1</strong><br>
TXID: <span id="example-input-txid" class="example-data">Chargement...</span><br>
Index: <span id="example-input-vout">-</span><br>
Montant: <span id="example-input-amount">-</span> sats
</div>
</div>
<button class="interactive-button" onclick="loadExampleTransaction()">📥 Charger une Transaction Réelle</button>
</section>
<!-- Section: Sortie de Transaction -->
<section class="concept-section" id="tx-output">
<h2>📤 Qu'est-ce qu'une Sortie de Transaction ?</h2>
<div class="concept-explanation">
<p>Une <span class="highlight">sortie de transaction</span> (transaction output) crée un nouvel UTXO qui peut être dépensé dans une future transaction.</p>
<p>Chaque sortie contient :</p>
<ul>
<li>Un <strong>montant</strong> en satoshis (1 BTC = 100 000 000 sats)</li>
<li>Un <strong>script de verrouillage</strong> (scriptPubKey) qui définit les conditions de dépense</li>
<li>Une <strong>adresse Bitcoin</strong> (dérivée du scriptPubKey)</li>
</ul>
<p>Les sorties créent de nouveaux UTXO qui peuvent être utilisés comme entrées dans de futures transactions.</p>
</div>
<div class="example-box">
<h4>Exemple de Sortie de Transaction</h4>
<div class="code-snippet">
{
"value": 0.00002500,
"n": 0,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 ... OP_EQUALVERIFY OP_CHECKSIG",
"hex": "...",
"address": "tb1q..."
}
}
</div>
<p>Cette sortie crée un UTXO de 2500 sats envoyé à l'adresse <code>tb1q...</code></p>
</div>
<div class="transaction-box">
<div class="tx-output">
<strong>📤 Sortie 1</strong><br>
Adresse: <span id="example-output-address" class="example-data">Chargement...</span><br>
Montant: <span id="example-output-amount">-</span> sats
</div>
</div>
</section>
<!-- Section: Les Frais -->
<section class="concept-section" id="fees">
<h2>💰 Qu'est-ce qu'un Frais de Transaction ?</h2>
<div class="concept-explanation">
<p>Les <span class="highlight">frais de transaction</span> (transaction fees) sont payés aux mineurs pour inclure votre transaction dans un bloc.</p>
<p>Les frais sont calculés comme la différence entre :</p>
<ul>
<li>La <strong>somme des entrées</strong> (UTXO dépensés)</li>
<li>La <strong>somme des sorties</strong> (nouveaux UTXO créés)</li>
</ul>
<p><strong>Frais = Entrées - Sorties</strong></p>
<p>Les frais incitent les mineurs à inclure votre transaction rapidement dans un bloc. Plus les frais sont élevés, plus la transaction a de chances d'être incluse rapidement.</p>
</div>
<div class="example-box">
<h4>Exemple de Calcul de Frais</h4>
<div class="code-snippet">
Entrées totales: 50 000 sats
Sorties totales: 48 800 sats
─────────────────────────────
Frais payés: 1 200 sats
</div>
<p>Dans une transaction d'ancrage typique, les frais sont généralement de <strong>1200 sats</strong>.</p>
</div>
<div class="visual-diagram">
<div class="transaction-box">
<div class="tx-input">
<strong>📥 Entrées</strong><br>
Total: <span id="example-fee-inputs">-</span> sats
</div>
<div style="text-align: center; margin: 10px 0; font-size: 1.5em;">-</div>
<div class="tx-output">
<strong>📤 Sorties</strong><br>
Total: <span id="example-fee-outputs">-</span> sats
</div>
<div style="text-align: center; margin: 10px 0; font-size: 1.5em;">=</div>
<div style="background: rgba(255, 193, 7, 0.15); border-left: 3px solid #ffc107; padding: 15px; margin: 10px 0; border-radius: 5px;">
<strong>💰 Frais</strong><br>
<span id="example-fee-amount" style="font-size: 1.2em; color: var(--primary-color);">-</span> sats
</div>
</div>
</div>
</section>
<!-- Section: Le Change -->
<section class="concept-section" id="change">
<h2>🔄 Qu'est-ce que le Change ?</h2>
<div class="concept-explanation">
<p>Le <span class="highlight">change</span> (monnaie de retour) est la différence entre les entrées et les sorties (hors frais) qui est renvoyée à l'expéditeur.</p>
<p>Dans Bitcoin, vous ne pouvez pas dépenser partiellement un UTXO. Si vous avez un UTXO de 50 000 sats et que vous voulez envoyer 10 000 sats, vous devez :</p>
<ul>
<li>Dépenser tout l'UTXO de 50 000 sats</li>
<li>Créer une sortie de 10 000 sats pour le destinataire</li>
<li>Créer une sortie de <strong>change</strong> de ~38 800 sats (50 000 - 10 000 - 1 200 frais) vers votre propre adresse</li>
</ul>
<p>Le change est essentiel pour récupérer la monnaie non dépensée et éviter de perdre des fonds.</p>
</div>
<div class="example-box">
<h4>Exemple de Transaction avec Change</h4>
<div class="code-snippet">
UTXO disponible: 50 000 sats
Montant à envoyer: 10 000 sats
Frais: 1 200 sats
─────────────────────────────
Change renvoyé: 38 800 sats
</div>
</div>
<div class="visual-diagram">
<div class="transaction-box">
<div class="tx-input">
<strong>📥 UTXO dépensé</strong><br>
50 000 sats
</div>
<div style="text-align: center; margin: 10px 0; font-size: 1.5em;"></div>
<div class="tx-output">
<strong>📤 Sortie 1 - Destinataire</strong><br>
10 000 sats → tb1q...
</div>
<div class="tx-output">
<strong>📤 Sortie 2 - Change</strong><br>
38 800 sats → tb1q... (votre adresse)
</div>
<div style="background: rgba(255, 193, 7, 0.15); border-left: 3px solid #ffc107; padding: 10px; margin: 10px 0; border-radius: 5px;">
<strong>💰 Frais</strong><br>
1 200 sats → Mineur
</div>
</div>
</div>
</section>
<!-- Section: Le Reward -->
<section class="concept-section" id="reward">
<h2>🎁 Qu'est-ce que la Récompense de Bloc (Block Reward) ?</h2>
<div class="concept-explanation">
<p>La <span class="highlight">récompense de bloc</span> (block reward) est la récompense que reçoit le mineur qui réussit à miner un nouveau bloc.</p>
<p>La récompense est créée dans une transaction spéciale appelée <strong>coinbase</strong> qui n'a pas d'entrées (elle crée de nouveaux bitcoins à partir de rien).</p>
<p>Sur Bitcoin Signet, la récompense est de <strong>50 000 sats (0.0005 BTC)</strong> par bloc.</p>
<p>Cette récompense est la seule façon de créer de nouveaux bitcoins. Elle diminue de moitié tous les 210 000 blocs (environ tous les 4 ans) dans un processus appelé <strong>halving</strong>.</p>
</div>
<div class="example-box">
<h4>Transaction Coinbase (Récompense de Minage)</h4>
<div class="code-snippet">
{
"vin": [{
"coinbase": "04ffff001d0104...",
"sequence": 4294967295
}],
"vout": [{
"value": 0.00050000,
"scriptPubKey": {
"address": "tb1q..." // Adresse du mineur
}
}]
}
</div>
<p>Cette transaction n'a <strong>pas d'entrées</strong> (coinbase), elle crée directement 50 000 sats pour le mineur.</p>
</div>
<div class="visual-diagram">
<div class="block-visual">
<div class="block-header">Bloc Miné</div>
<div class="transaction-box">
<div class="tx-input" style="background: rgba(255, 193, 7, 0.15); border-left-color: #ffc107;">
<strong>🎁 Coinbase (Aucune entrée)</strong><br>
Crée de nouveaux bitcoins
</div>
<div style="text-align: center; margin: 10px 0; font-size: 1.5em;"></div>
<div class="tx-output">
<strong>📤 Récompense</strong><br>
50 000 sats → Mineur
</div>
</div>
</div>
</div>
</section>
<!-- Section: Les UTXO -->
<section class="concept-section" id="utxo">
<h2>💎 Qu'est-ce qu'un UTXO ?</h2>
<div class="concept-explanation">
<p><span class="highlight">UTXO</span> signifie <strong>Unspent Transaction Output</strong> (Sortie de Transaction Non Dépensée).</p>
<p>Un UTXO est une sortie de transaction qui n'a pas encore été dépensée. C'est l'unité de base de Bitcoin :</p>
<ul>
<li>Chaque UTXO a un <strong>montant</strong> fixe en satoshis</li>
<li>Chaque UTXO est <strong>verrouillé</strong> par un script (scriptPubKey) qui définit qui peut le dépenser</li>
<li>Un UTXO ne peut être dépensé qu'<strong>entièrement</strong> (pas de dépense partielle)</li>
<li>Quand un UTXO est dépensé, il est <strong>consommé</strong> et ne peut plus être utilisé</li>
</ul>
<p>Le solde d'un wallet est la somme de tous ses UTXO non dépensés.</p>
</div>
<div class="example-box">
<h4>Exemple d'UTXO</h4>
<div class="code-snippet">
{
"txid": "a1b2c3d4e5f6...",
"vout": 0,
"address": "tb1q...",
"amount": 0.00002500,
"confirmations": 6,
"spendable": true
}
</div>
<p>Cet UTXO provient de la sortie #0 de la transaction <code>a1b2c3d4e5f6...</code> et peut être dépensé.</p>
</div>
<div class="visual-diagram">
<h3>Types d'UTXO dans le Wallet</h3>
<ul class="utxo-list">
<li class="utxo-item">
<strong>💰 Récompenses de Bloc (Bloc Rewards)</strong><br>
UTXO provenant des transactions coinbase (minage)<br>
<span id="utxo-rewards-count" class="example-data">Chargement...</span>
</li>
<li class="utxo-item">
<strong>🔗 Ancrages (Anchors)</strong><br>
UTXO créés par les transactions d'ancrage (2500 sats chacun)<br>
<span id="utxo-anchors-count" class="example-data">Chargement...</span>
</li>
<li class="utxo-item">
<strong>🔄 Change</strong><br>
UTXO provenant de la monnaie de retour des transactions<br>
<span id="utxo-changes-count" class="example-data">Chargement...</span>
</li>
</ul>
</div>
<h3>Gestion des UTXOs dans le Dashboard</h3>
<div class="concept-explanation">
<p>Le dashboard offre plusieurs fonctionnalités pour gérer et visualiser les UTXOs :</p>
<ul>
<li><strong>Capacité d'ancrage restante</strong> : Affiche le nombre d'ancrages possibles avec les UTXOs disponibles (confirmés uniquement pour éviter les erreurs)</li>
<li><strong>Pagination</strong> : Les listes d'UTXOs sont paginées par 100 éléments pour une navigation facile</li>
<li><strong>Tri</strong> : Possibilité de trier les UTXOs par montant ou confirmations (croissant/décroissant) en cliquant sur les en-têtes de colonnes</li>
<li><strong>Consolidation</strong> : Fonction pour consolider les petits UTXOs (< 2500 sats) en un gros UTXO pour optimiser le wallet</li>
<li><strong>Filtrage confirmés</strong> : Seuls les UTXOs avec au moins 1 confirmation sont utilisés pour éviter les erreurs "too-long-mempool-chain"</li>
</ul>
</div>
<div class="example-box">
<h4>Types de UTXOs dans le Wallet</h4>
<ul style="margin-left: 20px;">
<li><strong>💰 Bloc Rewards</strong> : UTXOs provenant des transactions coinbase (récompenses de minage)</li>
<li><strong>🔗 Ancrages</strong> : UTXOs créés par les transactions d'ancrage (2500 sats chacun)</li>
<li><strong>🔄 Changes</strong> : UTXOs provenant de la monnaie de retour des transactions</li>
<li><strong>💸 Frais</strong> : Transactions avec frais onchain enregistrés</li>
</ul>
</div>
<button class="interactive-button" onclick="loadUtxoList()">📥 Charger la Liste des UTXO</button>
</section>
<!-- Section: L'Ancrage Simple -->
<section class="concept-section" id="anchoring">
<h2>⚓ Comment Fonctionne l'Ancrage Simple ?</h2>
<div class="concept-explanation">
<p>L'<span class="highlight">ancrage simple</span> permet d'enregistrer de manière permanente un hash (empreinte) d'un document dans la blockchain Bitcoin.</p>
<p>Le processus d'ancrage :</p>
<ol>
<li>Générer un <strong>hash SHA256</strong> du document à ancrer</li>
<li>Créer une transaction Bitcoin avec un output <strong>OP_RETURN</strong> contenant le hash</li>
<li>Envoyer la transaction sur le réseau Bitcoin</li>
<li>Attendre la confirmation dans un bloc</li>
</ol>
<p>Une fois confirmé, le hash est inscrit de manière <strong>immuable</strong> dans la blockchain, prouvant l'existence du document à un moment donné.</p>
</div>
<div class="anchor-flow">
<div class="anchor-step">
<div class="anchor-step-number">1</div>
<h4>Document</h4>
<p>Votre document (texte, fichier, etc.)</p>
</div>
<div class="anchor-step">
<div class="anchor-step-number">2</div>
<h4>Hash SHA256</h4>
<p>Génération de l'empreinte cryptographique</p>
<div class="code-snippet" style="font-size: 0.8em; margin-top: 10px;">
hash = SHA256(document)
</div>
</div>
<div class="anchor-step">
<div class="anchor-step-number">3</div>
<h4>Transaction OP_RETURN</h4>
<p>Création d'une transaction avec le hash dans OP_RETURN</p>
</div>
<div class="anchor-step">
<div class="anchor-step-number">4</div>
<h4>Bloc Miné</h4>
<p>La transaction est incluse dans un bloc</p>
</div>
<div class="anchor-step">
<div class="anchor-step-number">5</div>
<h4>Ancrage Confirmé</h4>
<p>Le hash est immuablement enregistré</p>
</div>
</div>
<div class="example-box">
<h4>Structure d'une Transaction d'Ancrage</h4>
<div class="transaction-box">
<div class="tx-input">
<strong>📥 Entrées</strong><br>
UTXO disponibles pour payer les frais et créer les outputs
</div>
<div style="text-align: center; margin: 10px 0; font-size: 1.5em;"></div>
<div class="tx-opreturn">
<strong>📝 OP_RETURN</strong><br>
Contient: "ANCHOR:" + hash (64 caractères hex)<br>
<span style="font-size: 0.9em; color: var(--text-color);">Cet output n'est pas dépensable, il sert uniquement à stocker le hash</span>
</div>
<div class="tx-output">
<strong>📤 Output d'Ancrage</strong><br>
2500 sats → Adresse d'ancrage
</div>
<div class="tx-output">
<strong>📤 Outputs de Provisionnement</strong><br>
7 × 2500 sats → Pour futures transactions
</div>
<div class="tx-output">
<strong>📤 Change (si nécessaire)</strong><br>
Monnaie de retour
</div>
<div style="background: rgba(255, 193, 7, 0.15); border-left: 3px solid #ffc107; padding: 10px; margin: 10px 0; border-radius: 5px;">
<strong>💰 Frais</strong><br>
1200 sats → Mineur
</div>
</div>
</div>
<div class="example-box">
<h4>Exemple d'OP_RETURN d'Ancrage</h4>
<div class="code-snippet">
OP_RETURN "ANCHOR:abc123def456..."
</div>
<p>Le préfixe "ANCHOR:" suivi du hash SHA256 (64 caractères hexadécimaux) permet d'identifier facilement les transactions d'ancrage.</p>
</div>
<div class="example-box">
<h4>Avantages de l'Ancrage</h4>
<ul>
<li><strong>Preuve d'existence</strong> : Prouve qu'un document existait à un moment donné</li>
<li><strong>Immutabilité</strong> : Une fois dans la blockchain, le hash ne peut plus être modifié</li>
<li><strong>Décentralisation</strong> : Pas de dépendance à un serveur central</li>
<li><strong>Transparence</strong> : Tous les ancrages sont publics et vérifiables</li>
<li><strong>Coût faible</strong> : Seulement quelques milliers de satoshis par ancrage</li>
</ul>
</div>
</section>
<footer style="text-align: center; padding: 40px 20px; margin-top: 60px; background: var(--card-background); border-radius: 10px;">
<p style="color: var(--text-color); margin-bottom: 10px;">Bitcoin Ancrage Dashboard - Équipe 4NK</p>
<a href="/" style="color: var(--primary-color); text-decoration: none;">← Retour au Dashboard</a>
</footer>
</div>
<script>
const API_BASE_URL = window.location.origin;
/**
* Charge un exemple de bloc réel
*/
async function loadExampleBlock() {
const button = event.target;
button.disabled = true;
button.textContent = '⏳ Chargement...';
try {
const response = await fetch(`${API_BASE_URL}/api/blockchain/latest-block`);
const data = await response.json();
document.getElementById('example-block-height').textContent = data.height || '-';
document.getElementById('example-block-hash').textContent = data.hash || '-';
document.getElementById('example-block-hash').className = 'example-data';
if (data.time) {
const date = new Date(data.time * 1000);
document.getElementById('example-block-time').textContent = date.toLocaleString('fr-FR');
}
document.getElementById('example-block-tx-count').textContent = data.tx_count || 0;
document.getElementById('example-block-size').textContent = data.size || '-';
} catch (error) {
console.error('Error loading block:', error);
document.getElementById('example-block-hash').textContent = 'Erreur de chargement';
document.getElementById('example-block-hash').className = 'example-data error';
} finally {
button.disabled = false;
button.textContent = '📥 Charger un Bloc Réel';
}
}
/**
* Charge un exemple de transaction réelle
*/
async function loadExampleTransaction() {
const button = event.target;
button.disabled = true;
button.textContent = '⏳ Chargement...';
try {
// Récupérer une transaction d'ancrage récente
const response = await fetch(`${API_BASE_URL}/api/anchor/example`);
if (!response.ok) {
throw new Error('API endpoint not available');
}
const data = await response.json();
if (data.inputs && data.inputs.length > 0) {
const input = data.inputs[0];
document.getElementById('example-input-txid').textContent = (input.txid || '-').substring(0, 20) + '...';
document.getElementById('example-input-vout').textContent = input.vout || '-';
document.getElementById('example-input-amount').textContent = input.value ? Math.round(input.value * 100000000) : '-';
document.getElementById('example-input-txid').className = 'example-data';
}
if (data.outputs && data.outputs.length > 0) {
const output = data.outputs[0];
document.getElementById('example-output-address').textContent = output.address || '-';
document.getElementById('example-output-amount').textContent = output.value ? Math.round(output.value * 100000000) : '-';
document.getElementById('example-output-address').className = 'example-data';
}
// Calculer les frais
let totalInputs = 0;
let totalOutputs = 0;
if (data.inputs) {
totalInputs = data.inputs.reduce((sum, input) => sum + (input.value || 0), 0);
}
if (data.outputs) {
totalOutputs = data.outputs.reduce((sum, output) => sum + (output.value || 0), 0);
}
const fees = totalInputs - totalOutputs;
document.getElementById('example-fee-inputs').textContent = Math.round(totalInputs * 100000000);
document.getElementById('example-fee-outputs').textContent = Math.round(totalOutputs * 100000000);
document.getElementById('example-fee-amount').textContent = Math.round(fees * 100000000);
} catch (error) {
console.error('Error loading transaction:', error);
// Utiliser des valeurs d'exemple si l'API n'est pas disponible
document.getElementById('example-input-txid').textContent = 'a1b2c3d4e5f6...';
document.getElementById('example-input-txid').className = 'example-data';
document.getElementById('example-input-vout').textContent = '0';
document.getElementById('example-input-amount').textContent = '50000';
document.getElementById('example-output-address').textContent = 'tb1q...';
document.getElementById('example-output-address').className = 'example-data';
document.getElementById('example-output-amount').textContent = '2500';
document.getElementById('example-fee-inputs').textContent = '50000';
document.getElementById('example-fee-outputs').textContent = '48800';
document.getElementById('example-fee-amount').textContent = '1200';
} finally {
button.disabled = false;
button.textContent = '📥 Charger une Transaction Réelle';
}
}
/**
* Charge la liste des UTXO
*/
async function loadUtxoList() {
const button = event.target;
button.disabled = true;
button.textContent = '⏳ Chargement...';
try {
const response = await fetch(`${API_BASE_URL}/api/utxo/list`);
const data = await response.json();
document.getElementById('utxo-rewards-count').textContent = `${data.counts?.blocRewards || 0} UTXO`;
document.getElementById('utxo-rewards-count').className = 'example-data';
document.getElementById('utxo-anchors-count').textContent = `${data.counts?.anchors || 0} UTXO`;
document.getElementById('utxo-anchors-count').className = 'example-data';
document.getElementById('utxo-changes-count').textContent = `${data.counts?.changes || 0} UTXO`;
document.getElementById('utxo-changes-count').className = 'example-data';
} catch (error) {
console.error('Error loading UTXO list:', error);
document.getElementById('utxo-rewards-count').textContent = 'Erreur';
document.getElementById('utxo-rewards-count').className = 'example-data error';
document.getElementById('utxo-anchors-count').textContent = 'Erreur';
document.getElementById('utxo-anchors-count').className = 'example-data error';
document.getElementById('utxo-changes-count').textContent = 'Erreur';
document.getElementById('utxo-changes-count').className = 'example-data error';
} finally {
button.disabled = false;
button.textContent = '📥 Charger la Liste des UTXO';
}
}
// Charger automatiquement les données au chargement de la page
document.addEventListener('DOMContentLoaded', () => {
loadExampleBlock();
loadUtxoList();
});
</script>
</body>
</html>