**Motivations:** - Complete documentation for dashboard, domains, ports and environment configuration - Add new services (ClamAV API, Watermark API) to the infrastructure - Enhance dashboard with new pages and improved functionality - Improve deployment scripts and service configurations **Root causes:** - Missing comprehensive documentation for infrastructure setup - Need for antivirus scanning service integration - Need for watermark service integration - Dashboard required additional pages and features **Correctifs:** - Added comprehensive documentation in docs/ (DASHBOARD.md, DOMAINS_AND_PORTS.md, ENVIRONMENT.md) - Updated systemd service files with proper environment variables - Enhanced nginx proxy configuration script - Updated maintenance documentation **Evolutions:** - Added new ClamAV API service (api-clamav) for file scanning - Added new Watermark API service (api-filigrane) for document watermarking - Enhanced signet-dashboard with new learn.html page - Improved dashboard UI with better styles and navigation - Enhanced app.js with new functionality and better error handling - Updated API documentation page with complete endpoint descriptions - Added deployment scripts for watermark and nginx configuration - Updated hash and UTXO lists with latest data - Enhanced server.js with new routes and improved Bitcoin RPC integration **Pages affectées:** - docs/DASHBOARD.md: New comprehensive dashboard documentation - docs/DOMAINS_AND_PORTS.md: New infrastructure domains and ports documentation - docs/ENVIRONMENT.md: New environment variables documentation - docs/MAINTENANCE.md: Updated maintenance procedures - docs/README.md: Updated main documentation - signet-dashboard/public/app.js: Enhanced with new features - signet-dashboard/public/styles.css: Improved styling - signet-dashboard/public/index.html: Enhanced main page - signet-dashboard/public/learn.html: New educational page - signet-dashboard/public/api-docs.html: Enhanced API documentation - signet-dashboard/public/hash-list.html: Updated hash list page - signet-dashboard/public/utxo-list.html: Updated UTXO list page - signet-dashboard/public/join-signet.html: Updated join signet page - signet-dashboard/src/server.js: Enhanced server with new routes - signet-dashboard/start.sh: Updated startup script - signet-dashboard/signet-dashboard.service: Updated systemd service - api-anchorage/anchorage-api.service: Updated systemd service - api-faucet/faucet-api.service: Updated systemd service - configure-nginx-proxy.sh: Enhanced nginx configuration script - add-watermark-certificate.sh: New watermark certificate script - deploy-watermark-nginx.sh: New deployment script - api-clamav/: New ClamAV API service - api-filigrane/: New Watermark API service - hash_list.txt, utxo_list.txt: Updated with latest data - anchor_count.txt: Updated anchor count
898 lines
39 KiB
HTML
898 lines
39 KiB
HTML
<!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.1);
|
||
}
|
||
|
||
.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.1);
|
||
}
|
||
|
||
.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: white;
|
||
}
|
||
|
||
.tx-input {
|
||
background: #e3f2fd;
|
||
border-left: 3px solid #2196f3;
|
||
padding: 10px;
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.tx-output {
|
||
background: #e8f5e9;
|
||
border-left: 3px solid #4caf50;
|
||
padding: 10px;
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.tx-opreturn {
|
||
background: #fff3e0;
|
||
border-left: 3px solid #ff9800;
|
||
padding: 10px;
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.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: #ccc;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.highlight {
|
||
background: #fff3cd;
|
||
padding: 2px 6px;
|
||
border-radius: 3px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.code-snippet {
|
||
background: #f5f5f5;
|
||
border: 1px solid #ddd;
|
||
border-radius: 5px;
|
||
padding: 15px;
|
||
margin: 15px 0;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9em;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.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: #999;
|
||
}
|
||
|
||
.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: #666; margin-bottom: 15px;">
|
||
Hash: <span id="example-block-hash" class="example-data">Chargement...</span>
|
||
</div>
|
||
<div style="font-size: 0.9em; color: #666; margin-bottom: 15px;">
|
||
Timestamp: <span id="example-block-time">-</span>
|
||
</div>
|
||
<div style="font-size: 0.9em; color: #666; margin-bottom: 15px;">
|
||
Transactions: <span id="example-block-tx-count">-</span>
|
||
</div>
|
||
<div style="font-size: 0.9em; color: #666;">
|
||
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>
|
||
</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: #fff3cd; border-left: 3px solid #ff9800; 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: #fff3cd; border-left: 3px solid #ff9800; 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: #fff3cd; border-left-color: #ff9800;">
|
||
<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>
|
||
|
||
<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: #666;">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: #fff3cd; border-left: 3px solid #ff9800; 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: #666; 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>
|