ncantu 970b06ee8f Fix: Automatic UTXO provisioning on each Bitcoin anchor transaction
**Motivations:**
- Fix insufficient UTXO amount error in anchor API
- Ensure continuous availability of usable UTXOs for anchor transactions
- Improve anchor transaction reliability and efficiency

**Root causes:**
- UTXO selection logic was too restrictive, rejecting UTXOs larger than needed
- No automatic provisioning of new usable UTXOs when existing ones were not suitable
- Algorithm prevented efficient use of available UTXOs

**Correctifs:**
- Refactored createAnchorTransaction() to provision 7 UTXOs of 2500 sats on each anchor
- Use single large UTXO to create 1 anchor output + 7 provisioning outputs + change
- Simplified UTXO selection: single large UTXO per transaction instead of multiple small ones
- Added UTXO provisioning logic in signet-dashboard
- Enhanced Bitcoin RPC methods in both api-anchorage and signet-dashboard
- Added documentation in fixKnowledge/api-anchorage-utxo-provisioning.md

**Evolutions:**
- Enhanced signet-dashboard with new pages (hash-list, utxo-list, join-signet, api-docs)
- Improved Bitcoin RPC client with better error handling and UTXO management
- Added cache files for hash and UTXO lists
- Updated api-faucet with improved server configuration
- Enhanced anchor count tracking

**Pages affectées:**
- api-anchorage/src/bitcoin-rpc.js: Complete refactor of createAnchorTransaction()
- api-anchorage/src/routes/anchor.js: Enhanced anchor route
- api-anchorage/src/server.js: Server configuration updates
- signet-dashboard/src/bitcoin-rpc.js: Added comprehensive Bitcoin RPC client
- signet-dashboard/src/server.js: Enhanced server with new routes
- signet-dashboard/public/: Added new HTML pages and updated app.js
- api-faucet/src/server.js: Server improvements
- api-faucet/README.md: Documentation updates
- fixKnowledge/api-anchorage-utxo-provisioning.md: New documentation
- anchor_count.txt, hash_list.txt, utxo_list.txt: Tracking files
2026-01-25 14:25:49 +01:00

702 lines
27 KiB
HTML
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.

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>Documentation API d'Ancrage - Bitcoin Ancrage</title>
<link rel="stylesheet" href="styles.css">
<style>
.api-docs-section {
margin-bottom: 40px;
}
.endpoint-card {
background: var(--card-background);
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.endpoint-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 2px solid var(--border-color);
}
.method-badge {
display: inline-block;
padding: 5px 15px;
border-radius: 5px;
font-weight: bold;
font-size: 0.9em;
margin-right: 15px;
min-width: 70px;
text-align: center;
}
.method-get {
background: #28a745;
color: white;
}
.method-post {
background: #007bff;
color: white;
}
.endpoint-path {
font-family: 'Courier New', monospace;
font-size: 1.2em;
color: var(--primary-color);
font-weight: bold;
}
.endpoint-description {
margin: 20px 0;
color: var(--text-color);
line-height: 1.6;
}
.endpoint-params {
margin: 20px 0;
}
.endpoint-params h4 {
color: var(--primary-color);
margin-bottom: 10px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
}
.param-table th,
.param-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.param-table th {
background: #f5f5f5;
font-weight: bold;
}
.param-name {
font-family: 'Courier New', monospace;
color: var(--primary-color);
font-weight: bold;
}
.param-required {
color: var(--error-color);
font-weight: bold;
}
.param-optional {
color: #666;
}
.code-block {
background: #1e1e1e;
color: #d4d4d4;
padding: 20px;
border-radius: 5px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
line-height: 1.6;
overflow-x: auto;
margin: 20px 0;
}
.code-block pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
.response-example {
margin: 20px 0;
}
.response-example h4 {
color: var(--primary-color);
margin-bottom: 10px;
}
.info-box {
background: #e7f3ff;
border-left: 4px solid #2196F3;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
}
.warning-box {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
}
.error-box {
background: #f8d7da;
border-left: 4px solid #dc3545;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
}
.auth-section {
background: var(--card-background);
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.copy-button {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
margin-top: 10px;
transition: background 0.3s;
}
.copy-button:hover {
background: #e0820d;
}
.status-code {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 0.85em;
font-weight: bold;
margin-right: 10px;
}
.status-200 {
background: #28a745;
color: white;
}
.status-400 {
background: #ffc107;
color: #000;
}
.status-401 {
background: #dc3545;
color: white;
}
.status-402 {
background: #ff9800;
color: white;
}
.status-500 {
background: #dc3545;
color: white;
}
.status-503 {
background: #ff9800;
color: white;
}
</style>
</head>
<body>
<div class="container">
<a href="/" class="back-link">← Retour au Dashboard</a>
<header>
<h1>Documentation API d'Ancrage</h1>
<p class="subtitle">API REST pour ancrer et vérifier des documents sur Bitcoin Signet, et utiliser le faucet</p>
</header>
<main>
<!-- Section Authentification -->
<section class="api-docs-section">
<div class="auth-section">
<h2>🔐 Authentification</h2>
<p>Toutes les requêtes vers l'API d'ancrage (sauf les endpoints publics) nécessitent une clé API dans le header <code>X-API-Key</code>.</p>
<div class="code-block">
<pre>X-API-Key: votre-clé-api-ici</pre>
</div>
<div class="info-box">
<p><strong>Endpoints publics (sans authentification) :</strong></p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li><code>GET /health</code> - Vérification de santé (API d'ancrage)</li>
<li><code>GET /api/anchor/locked-utxos</code> - Liste des UTXO verrouillés</li>
<li><code>GET /health</code> - Vérification de santé (API faucet)</li>
</ul>
<p style="margin-top: 10px;"><strong>Endpoints nécessitant une clé API :</strong></p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li><code>POST /api/anchor/document</code> - Ancrer un document</li>
<li><code>POST /api/anchor/verify</code> - Vérifier un hash</li>
<li><code>POST /api/faucet/request</code> - Demander des sats</li>
</ul>
</div>
<div class="warning-box">
<p><strong>⚠️ Important :</strong> Conservez votre clé API secrète et ne la partagez jamais publiquement.</p>
</div>
</div>
</section>
<!-- Endpoint: Health -->
<section class="api-docs-section">
<div class="endpoint-card">
<div class="endpoint-header">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/health</span>
</div>
<div class="endpoint-description">
<p>Vérifie l'état de santé de l'API. Cet endpoint est public et ne nécessite pas d'authentification.</p>
</div>
<div class="response-example">
<h4>Réponse (200 OK)</h4>
<div class="code-block">
<pre>{
"ok": true,
"service": "anchor-api",
"bitcoin": {
"connected": true,
"blocks": 12345
},
"timestamp": "2026-01-25T12:00:00.000Z"
}</pre>
</div>
</div>
<div class="response-example">
<h4>Réponse (503 Service Unavailable) - Bitcoin non connecté</h4>
<div class="code-block">
<pre>{
"ok": false,
"service": "anchor-api",
"error": "Bitcoin RPC connection failed",
"timestamp": "2026-01-25T12:00:00.000Z"
}</pre>
</div>
</div>
</div>
</section>
<!-- Endpoint: Anchor Document -->
<section class="api-docs-section">
<div class="endpoint-card">
<div class="endpoint-header">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/api/anchor/document</span>
</div>
<div class="endpoint-description">
<p>Ancre un document sur la blockchain Bitcoin Signet en créant une transaction qui inclut le hash du document dans un OP_RETURN.</p>
</div>
<div class="endpoint-params">
<h4>Paramètres (Body JSON)</h4>
<table class="param-table">
<thead>
<tr>
<th>Paramètre</th>
<th>Type</th>
<th>Requis</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="param-name">hash</td>
<td>string</td>
<td><span class="param-required">Oui</span></td>
<td>Hash SHA256 du document en hexadécimal (64 caractères)</td>
</tr>
<tr>
<td class="param-name">documentUid</td>
<td>string</td>
<td><span class="param-optional">Non</span></td>
<td>Identifiant optionnel du document (pour le logging)</td>
</tr>
</tbody>
</table>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X POST https://certificator.4nkweb.com/api/anchor/document \
-H "Content-Type: application/json" \
-H "X-API-Key: votre-clé-api" \
-d '{
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"documentUid": "doc-12345"
}'</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK)</h4>
<div class="code-block">
<pre>{
"txid": "abc123def456...",
"status": "pending",
"confirmations": 0,
"block_height": null
}</pre>
</div>
</div>
<div class="response-example">
<h4>Codes de statut possibles</h4>
<ul style="margin-left: 20px;">
<li><span class="status-code status-200">200</span> Succès - Transaction créée et envoyée au mempool</li>
<li><span class="status-code status-400">400</span> Requête invalide - Hash manquant ou format incorrect</li>
<li><span class="status-code status-401">401</span> Non autorisé - Clé API manquante ou invalide</li>
<li><span class="status-code status-402">402</span> Solde insuffisant - Pas assez de fonds pour créer la transaction</li>
<li><span class="status-code status-500">500</span> Erreur serveur - Erreur interne lors de la création de la transaction</li>
</ul>
</div>
<div class="error-box">
<h4>Exemple d'erreur (402 Payment Required)</h4>
<div class="code-block">
<pre>{
"error": "Insufficient Balance",
"message": "Insufficient balance to create anchor transaction"
}</pre>
</div>
</div>
</div>
</section>
<!-- Endpoint: Verify -->
<section class="api-docs-section">
<div class="endpoint-card">
<div class="endpoint-header">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/api/anchor/verify</span>
</div>
<div class="endpoint-description">
<p>Vérifie si un hash est ancré sur la blockchain Bitcoin Signet. Recherche dans les transactions OP_RETURN pour trouver le hash.</p>
</div>
<div class="endpoint-params">
<h4>Paramètres (Body JSON)</h4>
<table class="param-table">
<thead>
<tr>
<th>Paramètre</th>
<th>Type</th>
<th>Requis</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="param-name">hash</td>
<td>string</td>
<td><span class="param-required">Oui</span></td>
<td>Hash SHA256 à vérifier (64 caractères hex)</td>
</tr>
<tr>
<td class="param-name">txid</td>
<td>string</td>
<td><span class="param-optional">Non</span></td>
<td>ID de transaction optionnel pour accélérer la recherche</td>
</tr>
</tbody>
</table>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X POST https://certificator.4nkweb.com/api/anchor/verify \
-H "Content-Type: application/json" \
-H "X-API-Key: votre-clé-api" \
-d '{
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
"txid": "abc123def456..."
}'</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK) - Hash trouvé</h4>
<div class="code-block">
<pre>{
"found": true,
"txid": "abc123def456...",
"block_height": 12345,
"confirmations": 100,
"timestamp": "2026-01-25T10:00:00.000Z"
}</pre>
</div>
</div>
<div class="response-example">
<h4>Réponse (200 OK) - Hash non trouvé</h4>
<div class="code-block">
<pre>{
"found": false,
"txid": null,
"block_height": null,
"confirmations": null,
"timestamp": null
}</pre>
</div>
</div>
<div class="response-example">
<h4>Codes de statut possibles</h4>
<ul style="margin-left: 20px;">
<li><span class="status-code status-200">200</span> Succès - Vérification effectuée</li>
<li><span class="status-code status-400">400</span> Requête invalide - Hash manquant ou format incorrect</li>
<li><span class="status-code status-401">401</span> Non autorisé - Clé API manquante ou invalide</li>
<li><span class="status-code status-500">500</span> Erreur serveur - Erreur interne lors de la vérification</li>
</ul>
</div>
</div>
</section>
<!-- Endpoint: Locked UTXOs -->
<section class="api-docs-section">
<div class="endpoint-card">
<div class="endpoint-header">
<span class="method-badge method-get">GET</span>
<span class="endpoint-path">/api/anchor/locked-utxos</span>
</div>
<div class="endpoint-description">
<p>Retourne la liste des UTXO actuellement verrouillés par le mutex de l'API. Cet endpoint est public et ne nécessite pas d'authentification.</p>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X GET https://certificator.4nkweb.com/api/anchor/locked-utxos</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK)</h4>
<div class="code-block">
<pre>{
"locked": [
{
"txid": "abc123def456...",
"vout": 0
},
{
"txid": "def456abc123...",
"vout": 1
}
],
"count": 2
}</pre>
</div>
</div>
</div>
</section>
<!-- Endpoint: Faucet Request -->
<section class="api-docs-section">
<div class="endpoint-card">
<div class="endpoint-header">
<span class="method-badge method-post">POST</span>
<span class="endpoint-path">/api/faucet/request</span>
</div>
<div class="endpoint-description">
<p>Demande des sats (testnet coins) via le faucet. Distribue 50 000 sats (0.0005 BTC) par défaut sur une adresse Bitcoin Signet valide. Nécessite une clé API valide dans le header <code>x-api-key</code>.</p>
</div>
<div class="endpoint-params">
<h4>Paramètres (Body JSON)</h4>
<table class="param-table">
<thead>
<tr>
<th>Paramètre</th>
<th>Type</th>
<th>Requis</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="param-name">address</td>
<td>string</td>
<td><span class="param-required">Oui</span></td>
<td>Adresse Bitcoin Signet valide (commence par <code>tb1</code>, <code>bcrt1</code>, <code>2</code> ou <code>3</code>)</td>
</tr>
</tbody>
</table>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X POST https://certificator.4nkweb.com/api/faucet/request \
-H "Content-Type: application/json" \
-H "x-api-key: votre-clé-api" \
-d '{
"address": "tb1qwe0nv3s0ewedd63w20r8kwnv22uw8dp2tnj3qc"
}'</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK)</h4>
<div class="code-block">
<pre>{
"success": true,
"txid": "a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890",
"address": "tb1qwe0nv3s0ewedd63w20r8kwnv22uw8dp2tnj3qc",
"amount": 0.0005,
"amount_sats": 50000,
"status": "pending",
"confirmations": 0,
"block_height": null
}</pre>
</div>
</div>
<div class="response-example">
<h4>Codes de statut possibles</h4>
<ul style="margin-left: 20px;">
<li><span class="status-code status-200">200</span> Succès - Transaction créée et envoyée au mempool</li>
<li><span class="status-code status-400">400</span> Requête invalide - Adresse manquante ou format incorrect</li>
<li><span class="status-code status-401">401</span> Non autorisé - Clé API manquante ou invalide</li>
<li><span class="status-code status-503">503</span> Service indisponible - Solde insuffisant dans le wallet du faucet</li>
<li><span class="status-code status-500">500</span> Erreur serveur - Erreur interne lors de la création de la transaction</li>
</ul>
</div>
<div class="error-box">
<h4>Exemple d'erreur (401 Unauthorized)</h4>
<div class="code-block">
<pre>{
"error": "Unauthorized",
"message": "Invalid or missing API key"
}</pre>
</div>
</div>
<div class="error-box">
<h4>Exemple d'erreur (503 Service Unavailable)</h4>
<div class="code-block">
<pre>{
"error": "Insufficient Balance",
"message": "Insufficient balance to send coins"
}</pre>
</div>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Le montant par défaut est de 50 000 sats (0.0005 BTC)</li>
<li>L'adresse doit être une adresse Bitcoin Signet valide</li>
<li>La transaction est envoyée au mempool immédiatement</li>
<li>Le statut "pending" signifie que la transaction est dans le mempool mais pas encore confirmée</li>
<li>Les confirmations augmentent à mesure que les blocs sont minés</li>
</ul>
</div>
</div>
</section>
<!-- Section Informations -->
<section class="api-docs-section">
<div class="endpoint-card">
<h2> Informations Complémentaires</h2>
<div class="info-box">
<h4>Format du Hash</h4>
<p>Le hash doit être un hash SHA256 en format hexadécimal, exactement 64 caractères.</p>
<div class="code-block">
<pre>Exemple valide: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
Longueur: 64 caractères
Format: hexadécimal (0-9, a-f, A-F)</pre>
</div>
</div>
<div class="info-box">
<h4>Format de la Transaction</h4>
<p>Les transactions d'ancrage incluent :</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Un output OP_RETURN contenant "ANCHOR:" suivi du hash</li>
<li>Un output d'ancrage de 2500 sats</li>
<li>7 outputs de provisionnement de 2500 sats chacun</li>
<li>Un output de change (si nécessaire)</li>
</ul>
</div>
<div class="info-box">
<h4>Base URL</h4>
<p>L'API est accessible à l'adresse :</p>
<div class="code-block">
<pre>https://certificator.4nkweb.com</pre>
</div>
</div>
<div class="warning-box">
<h4>⚠️ Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Les transactions sont envoyées au mempool immédiatement</li>
<li>Le statut "pending" signifie que la transaction est dans le mempool mais pas encore confirmée</li>
<li>Les confirmations augmentent à mesure que les blocs sont minés</li>
<li>En cas d'erreur 402 (Solde insuffisant), vous devez approvisionner le wallet de l'API</li>
</ul>
</div>
</div>
</section>
</main>
<footer>
<p>Bitcoin Ancrage Dashboard - Équipe 4NK</p>
</footer>
</div>
<script>
function copyCode(button) {
const codeBlock = button.previousElementSibling;
const code = codeBlock.querySelector('pre')?.textContent || codeBlock.textContent;
navigator.clipboard.writeText(code).then(() => {
const originalText = button.textContent;
button.textContent = '✅ Copié !';
setTimeout(() => {
button.textContent = originalText;
}, 2000);
}).catch(err => {
console.error('Erreur lors de la copie:', err);
alert('Erreur lors de la copie. Veuillez sélectionner et copier manuellement.');
});
}
</script>
</body>
</html>