ncantu 5689693507 UserWallet: Multiple feature implementations and updates
**Motivations:**
- Implement BIP39 mnemonic import for identity creation
- Add password-based key protection for enhanced security
- Improve pairing workflow with QR code and URL display
- Migrate hash cache from LocalStorage to IndexedDB for better scalability
- Update signet-dashboard and mempool components

**Root causes:**
- N/A (feature implementations)

**Correctifs:**
- N/A (no bug fixes in this commit)

**Evolutions:**
- BIP39 mnemonic import: Support for 12/24 word English mnemonics with BIP32 derivation path m/44'/0'/0'/0/0
- Key protection: Password-based encryption of private keys at rest with unlock/lock functionality
- Pairing workflow: QR code and URL display for device pairing, form-based word exchange between devices
- IndexedDB migration: Hash cache moved from LocalStorage to IndexedDB to avoid size limitations
- Global action bar: URL parameter support for navigation
- Pairing connection: Enhanced pairing status management

**Pages affectées:**
- userwallet/src/utils/identity.ts
- userwallet/src/utils/keyProtection.ts
- userwallet/src/utils/sessionUnlockedKey.ts
- userwallet/src/utils/indexedDbStorage.ts
- userwallet/src/utils/cache.ts
- userwallet/src/utils/pairing.ts
- userwallet/src/components/UnlockScreen.tsx
- userwallet/src/components/PairingDisplayScreen.tsx
- userwallet/src/components/PairingSetupBlock.tsx
- userwallet/src/components/GlobalActionBar.tsx
- userwallet/src/components/HomeScreen.tsx
- userwallet/src/components/ImportIdentityScreen.tsx
- userwallet/src/components/DataExportImportScreen.tsx
- userwallet/src/hooks/useIdentity.ts
- userwallet/src/hooks/usePairingConnected.ts
- userwallet/src/services/syncService.ts
- userwallet/src/services/pairingConfirm.ts
- userwallet/src/App.tsx
- userwallet/package.json
- userwallet/docs/specs.md
- userwallet/docs/storage.md
- userwallet/docs/synthese.md
- signet-dashboard/public/*.html
- signet-dashboard/public/app.js
- signet-dashboard/public/styles.css
- mempool (submodule updates)
- hash_list.txt, hash_list_cache.txt, utxo_list.txt, utxo_list_cache.txt, fees_list.txt
- features/*.md (documentation files)
2026-01-26 10:23:34 +01:00

1254 lines
54 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: var(--card-background);
font-weight: bold;
color: var(--text-color);
}
.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: var(--text-color);
}
.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: rgba(13, 202, 240, 0.15);
border-left: 4px solid #0dcaf0;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
color: var(--text-color);
}
.warning-box {
background: rgba(255, 193, 7, 0.15);
border-left: 4px solid #ffc107;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
color: var(--text-color);
}
.error-box {
background: rgba(220, 53, 69, 0.2);
border-left: 4px solid #dc3545;
padding: 15px;
margin: 20px 0;
border-radius: 5px;
color: #ff6b6b;
}
.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: var(--background-color);
}
.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>
<li><code>POST /api/watermark/document</code> - Ajouter un filigrane et ancrer un document</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-503">503</span> Service indisponible - Erreur "too-long-mempool-chain" (trop d'ancêtres non confirmés)</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 class="error-box">
<h4>Exemple d'erreur (503 Service Unavailable) - Too Long Mempool Chain</h4>
<div class="code-block">
<pre>{
"error": "Service Unavailable",
"message": "too-long-mempool-chain, too many unconfirmed ancestors [limit: 25]"
}</pre>
</div>
<p style="margin-top: 10px;">Cette erreur se produit lorsque l'API tente d'utiliser un UTXO non confirmé qui a trop d'ancêtres non confirmés dans le mempool. Bitcoin Core limite la chaîne d'ancêtres à 25 transactions pour éviter les attaques par spam.</p>
<p><strong>Solution :</strong> L'API utilise maintenant uniquement des UTXOs confirmés (au moins 1 confirmation) pour éviter cette erreur. Attendez qu'un bloc soit miné pour que les UTXOs soient confirmés.</p>
</div>
<div class="info-box">
<h4> Gestion des UTXOs</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>L'API utilise uniquement des UTXOs <strong>confirmés</strong> (au moins 1 confirmation) pour éviter les erreurs "too-long-mempool-chain"</li>
<li>Les UTXOs non confirmés sont automatiquement exclus de la sélection</li>
<li>Chaque transaction d'ancrage provisionne automatiquement 7 UTXOs de 2500 sats pour les ancrages futurs</li>
<li>Les UTXOs provisionnés deviendront utilisables après confirmation dans un bloc</li>
</ul>
</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>
<!-- Endpoint: Watermark 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/watermark/document</span>
</div>
<div class="endpoint-description">
<p>Ajoute un filigrane à un document, le convertit en PDF, et l'ancre sur la blockchain Bitcoin Signet. Les fichiers sont automatiquement scannés avec ClamAV avant traitement.</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">apiKey</td>
<td>string</td>
<td><span class="param-required">Oui</span></td>
<td>Clé API pour l'authentification (peut aussi être dans le header <code>X-API-Key</code>)</td>
</tr>
<tr>
<td class="param-name">fileData</td>
<td>string</td>
<td><span class="param-optional">Conditionnel</span></td>
<td>Fichier encodé en base64 (requis si <code>textContent</code> n'est pas fourni)</td>
</tr>
<tr>
<td class="param-name">fileName</td>
<td>string</td>
<td><span class="param-optional">Non</span></td>
<td>Nom du fichier (requis si <code>fileData</code> est fourni)</td>
</tr>
<tr>
<td class="param-name">mimeType</td>
<td>string</td>
<td><span class="param-optional">Non</span></td>
<td>Type MIME du fichier (requis si <code>fileData</code> est fourni)</td>
</tr>
<tr>
<td class="param-name">textContent</td>
<td>string</td>
<td><span class="param-optional">Conditionnel</span></td>
<td>Contenu texte à convertir en PDF (requis si <code>fileData</code> n'est pas fourni)</td>
</tr>
<tr>
<td class="param-name">watermarkOptions</td>
<td>object</td>
<td><span class="param-required">Oui</span></td>
<td>Options de filigrane (voir détails ci-dessous)</td>
</tr>
</tbody>
</table>
</div>
<div class="info-box">
<h4>Structure de watermarkOptions</h4>
<table class="param-table">
<thead>
<tr>
<th>Paramètre</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="param-name">enabled</td>
<td>boolean</td>
<td>Doit être <code>true</code></td>
</tr>
<tr>
<td class="param-name">text</td>
<td>string</td>
<td>Texte libre du filigrane (optionnel)</td>
</tr>
<tr>
<td class="param-name">signature</td>
<td>string</td>
<td>Signature cryptographique (optionnel)</td>
</tr>
<tr>
<td class="param-name">depositor</td>
<td>string</td>
<td>Nom du dépositaire (optionnel)</td>
</tr>
<tr>
<td class="param-name">dateUTC</td>
<td>boolean</td>
<td>Ajouter la date UTC dans le filigrane</td>
</tr>
<tr>
<td class="param-name">dateLocal</td>
<td>boolean</td>
<td>Ajouter la date locale dans le filigrane</td>
</tr>
<tr>
<td class="param-name">blockNumber</td>
<td>boolean</td>
<td>Ajouter le numéro de bloc dans le filigrane</td>
</tr>
<tr>
<td class="param-name">blockHash</td>
<td>boolean</td>
<td>Ajouter le hash du bloc dans le filigrane</td>
</tr>
<tr>
<td class="param-name">documentHash</td>
<td>boolean</td>
<td>Ajouter le hash du document d'origine dans le filigrane</td>
</tr>
</tbody>
</table>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X POST https://watermark.certificator.4nkweb.com/api/watermark/document \
-H "Content-Type: application/json" \
-H "X-API-Key: votre-clé-api" \
-d '{
"apiKey": "votre-clé-api",
"fileData": "base64_encoded_file_data",
"fileName": "document.pdf",
"mimeType": "application/pdf",
"watermarkOptions": {
"enabled": true,
"text": "Document confidentiel",
"depositor": "John Doe",
"dateUTC": true,
"dateLocal": true,
"blockNumber": true,
"documentHash": true
}
}'</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,
"antivirusScan": {
"scanned": true,
"clean": true,
"infected": false,
"viruses": [],
"error": null
},
"original": {
"txid": "abc123def456...",
"status": "pending",
"confirmations": 0,
"hash": "a1b2c3d4e5f6...",
"file": {
"name": "document.pdf",
"extension": "pdf",
"data": "base64_encoded_pdf"
}
},
"watermarked": {
"txid": "def456abc123...",
"status": "pending",
"confirmations": 0,
"hash": "b2c3d4e5f6a1...",
"file": {
"name": "document.pdf",
"extension": "pdf",
"data": "base64_encoded_pdf"
}
},
"certificate": {
"name": "certificat-document.pdf",
"extension": "pdf",
"data": "base64_encoded_pdf"
},
"merged": {
"name": "document-avec-certificat.pdf",
"extension": "pdf",
"data": "base64_encoded_pdf"
}
}</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 - Document filigrané et ancré</li>
<li><span class="status-code status-400">400</span> Requête invalide - Fichier infecté, paramètres manquants ou incorrects</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 du traitement</li>
</ul>
</div>
<div class="error-box">
<h4>Exemple d'erreur (400 Bad Request) - Fichier infecté</h4>
<div class="code-block">
<pre>{
"error": "Bad Request",
"message": "File contains viruses",
"viruses": ["Trojan.Example"],
"antivirusScan": {
"scanned": true,
"clean": false,
"infected": true,
"viruses": ["Trojan.Example"],
"error": null
}
}</pre>
</div>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Tous les fichiers sont automatiquement scannés avec ClamAV avant traitement</li>
<li>Les fichiers infectés sont rejetés avec une erreur 400</li>
<li>Si ClamAV est indisponible, le traitement continue en mode dégradé (antivirusScan.scanned = false)</li>
<li>Le résultat inclut 4 fichiers PDF : original, filigrané, certificat, et fusionné (filigrané + certificat)</li>
<li>Les fichiers sont retournés en base64 dans le champ <code>data</code></li>
<li>Les TXID sont des liens cliquables vers mempool.4nkweb.com dans les résultats</li>
</ul>
</div>
</div>
</section>
<!-- Endpoint: ClamAV Scan -->
<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/scan/buffer</span>
</div>
<div class="endpoint-description">
<p>Scanne un buffer de données (base64) pour détecter les virus avec ClamAV. Cette API est utilisée en interne par l'API filigrane.</p>
<p><strong>Base URL :</strong> <code>https://antivir.certificator.4nkweb.com</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">data</td>
<td>string</td>
<td><span class="param-required">Oui</span></td>
<td>Données encodées en base64 à scanner</td>
</tr>
<tr>
<td class="param-name">filename</td>
<td>string</td>
<td><span class="param-optional">Non</span></td>
<td>Nom du fichier (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://antivir.certificator.4nkweb.com/api/scan/buffer \
-H "Content-Type: application/json" \
-d '{
"data": "base64_encoded_data",
"filename": "document.pdf"
}'</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK) - Fichier propre</h4>
<div class="code-block">
<pre>{
"clean": true,
"infected": false,
"viruses": [],
"filename": "document.pdf",
"size": 12345
}</pre>
</div>
</div>
<div class="response-example">
<h4>Réponse (200 OK) - Fichier infecté</h4>
<div class="code-block">
<pre>{
"clean": false,
"infected": true,
"viruses": ["Trojan.Example"],
"filename": "document.pdf",
"size": 12345
}</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 - Scan effectué (fichier propre ou infecté)</li>
<li><span class="status-code status-400">400</span> Requête invalide - Données manquantes ou format incorrect</li>
<li><span class="status-code status-503">503</span> Service indisponible - ClamAV non disponible</li>
<li><span class="status-code status-500">500</span> Erreur serveur - Erreur interne lors du scan</li>
</ul>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Cette API est principalement utilisée en interne par l'API filigrane</li>
<li>Le port fixe est 3023</li>
<li>Si ClamAV est indisponible, l'API retourne une erreur 503</li>
<li>Les fichiers sont scannés en mémoire (pas de fichiers temporaires sur disque)</li>
</ul>
</div>
</div>
</section>
<!-- Endpoint: UTXO List -->
<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/utxo/list</span>
</div>
<div class="endpoint-description">
<p>Obtient la liste complète des UTXOs du wallet, catégorisés par type (bloc rewards, ancrages, changes, frais).</p>
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X GET https://dashboard.certificator.4nkweb.com/api/utxo/list</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>{
"blocRewards": [...],
"anchors": [...],
"changes": [...],
"fees": [...],
"counts": {
"blocRewards": 10,
"anchors": 150,
"changes": 25,
"fees": 5,
"total": 190,
"availableForAnchor": 180,
"confirmedAvailableForAnchor": 175
}
}</pre>
</div>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Seuls les UTXOs avec au moins 1 confirmation sont retournés (pour éviter les erreurs "too-long-mempool-chain")</li>
<li><code>availableForAnchor</code> : Nombre d'UTXOs disponibles pour l'ancrage (> 2000 sats, non dépensés, non verrouillés)</li>
<li><code>confirmedAvailableForAnchor</code> : Nombre d'UTXOs confirmés disponibles pour l'ancrage</li>
<li>Les UTXOs sont triés par montant décroissant par défaut</li>
</ul>
</div>
</div>
</section>
<!-- Endpoint: Small UTXOs Info -->
<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/utxo/small-info</span>
</div>
<div class="endpoint-description">
<p>Obtient les informations sur les UTXOs de moins de 2500 sats disponibles pour consolidation (nombre et montant total).</p>
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X GET https://dashboard.certificator.4nkweb.com/api/utxo/small-info</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>{
"count": 45,
"totalAmount": 0.0001125,
"totalSats": 11250
}</pre>
</div>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Seuls les UTXOs confirmés (< 2500 sats, non dépensés, non verrouillés) sont comptés</li>
<li><code>count</code> : Nombre d'UTXOs disponibles pour consolidation</li>
<li><code>totalAmount</code> : Montant total en BTC</li>
<li><code>totalSats</code> : Montant total en satoshis</li>
</ul>
</div>
</div>
</section>
<!-- Endpoint: Consolidate UTXOs -->
<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/utxo/consolidate</span>
</div>
<div class="endpoint-description">
<p>Consolide tous les UTXOs de moins de 2500 sats en un seul gros UTXO. Cette opération optimise le wallet en réduisant le nombre de petits UTXOs.</p>
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
</div>
<div class="response-example">
<h4>Exemple de requête</h4>
<div class="code-block">
<pre>curl -X POST https://dashboard.certificator.4nkweb.com/api/utxo/consolidate</pre>
</div>
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
</div>
<div class="response-example">
<h4>Réponse (200 OK) - Succès</h4>
<div class="code-block">
<pre>{
"success": true,
"txid": "a1b2c3d4e5f6...",
"inputCount": 45,
"totalInputAmount": 0.0001125,
"changeAmount": 0.0001025,
"estimatedFee": 0.00001
}</pre>
</div>
</div>
<div class="response-example">
<h4>Réponse (500) - Erreur</h4>
<div class="code-block">
<pre>{
"success": false,
"error": "No small UTXOs available for consolidation"
}</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 - Consolidation effectuée</li>
<li><span class="status-code status-500">500</span> Erreur - Aucun UTXO disponible ou erreur lors de la consolidation</li>
</ul>
</div>
<div class="info-box">
<h4> Notes importantes</h4>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>Seuls les UTXOs confirmés de moins de 2500 sats sont consolidés</li>
<li>Les UTXOs verrouillés ou déjà dépensés sont exclus</li>
<li>La transaction est envoyée au mempool immédiatement</li>
<li>Le montant consolidé est retourné comme change (moins les frais estimés)</li>
<li>Cette opération optimise le wallet en réduisant le nombre de petits UTXOs</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 URLs</h4>
<p>Les APIs sont accessibles aux adresses suivantes :</p>
<div class="code-block">
<pre>API d'Ancrage : https://certificator.4nkweb.com
API Filigrane : https://watermark.certificator.4nkweb.com
API ClamAV : https://antivir.certificator.4nkweb.com</pre>
</div>
</div>
<div class="info-box">
<h4>Ports fixes</h4>
<p>Tous les ports sont fixes et ne peuvent pas être modifiés :</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>API d'Ancrage : Port 3010</li>
<li>API Faucet : Port 3021</li>
<li>API Filigrane : Port 3022</li>
<li>API ClamAV : Port 3023</li>
<li>Dashboard : Port 3020</li>
</ul>
</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>
<a href="https://git.4nkweb.com/4nk/anchorage_layer_simple.git" target="_blank" rel="noopener noreferrer" class="git-link" title="Voir le code source sur Git">
<svg class="git-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
<path d="M23.546 10.93L13.067.452c-.604-.603-1.582-.603-2.188 0L8.708 2.627l2.76 2.76c.645-.215 1.379-.07 1.889.441.516.515.658 1.258.438 1.9l2.658 2.66c.645-.223 1.387-.083 1.9.435.721.72.721 1.884 0 2.604-.719.719-1.881.719-2.6 0-.539-.541-.674-1.337-.404-1.996L12.86 8.955v6.525c.176.086.342.203.488.348.713.721.713 1.883 0 2.6-.719.721-1.884.721-2.599 0-.72-.719-.72-1.879 0-2.598.182-.18.387-.316.605-.406V8.835c-.217-.091-.424-.222-.6-.401-.545-.545-.676-1.342-.396-2.011L7.636 3.7.45 10.881c-.6.605-.6 1.584 0 2.189l10.48 10.477c.604.604 1.582.604 2.186 0l10.43-10.43c.605-.603.605-1.582 0-2.187"/>
</svg>
</a>
</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>