**Motivations:** - Synchronisation des modifications sur l'API anchorage, les services et le website skeleton - Ajout de scripts de monitoring et de diagnostic pour l'API anchorage - Documentation des problèmes de mutex et de provisioning UTXO **Root causes:** - N/A (commit de synchronisation) **Correctifs:** - N/A (commit de synchronisation) **Evolutions:** - Ajout de scripts de monitoring et de diagnostic pour l'API anchorage - Amélioration de la gestion des mutex et des UTXOs - Mise à jour de la documentation **Pages affectées:** - api-anchorage/src/bitcoin-rpc.js - api-anchorage/src/routes/anchor.js - api-anchorage/src/routes/health.js - api-anchorage/src/server.js - api-anchorage/README-MONITORING.md - api-anchorage/cleanup-stale-locks.mjs - api-anchorage/diagnose.mjs - api-anchorage/unlock-utxos.mjs - service-login-verify/src/persistentNonceCache.ts - signet-dashboard/src/server.js - signet-dashboard/public/* - userwallet/src/hooks/useChannel.ts - userwallet/src/services/relayNotificationService.ts - userwallet/src/utils/defaultContract.ts - website-skeleton/src/* - docs/DOMAINS_AND_PORTS.md - docs/INTERFACES.md - features/* - fixKnowledge/*
1864 lines
80 KiB
HTML
1864 lines
80 KiB
HTML
<!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 /health/detailed</code> - Vérification de santé détaillée avec état mutex et UTXOs</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: Health Detailed -->
|
||
<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/detailed</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Vérifie l'état détaillé de l'API, incluant l'état du mutex, des UTXOs verrouillés et de la connexion Bitcoin. Cet endpoint est public et ne nécessite pas d'authentification.</p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK) - Tout fonctionne</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"ok": true,
|
||
"service": "anchor-api",
|
||
"mutex": {
|
||
"locked": false,
|
||
"waiting": 0,
|
||
"timeout": 180000
|
||
},
|
||
"utxos": {
|
||
"locked": 0,
|
||
"locked_since": null,
|
||
"stale_locks": 0,
|
||
"stale_locks_details": []
|
||
},
|
||
"bitcoin": {
|
||
"connected": true,
|
||
"blocks": 12345,
|
||
"chain": "signet",
|
||
"rpc_timeout": 60000
|
||
},
|
||
"timestamp": "2026-01-25T12:00:00.000Z"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK) - Warning (UTXOs verrouillés mais < 10 min)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"ok": true,
|
||
"service": "anchor-api",
|
||
"mutex": {
|
||
"locked": false,
|
||
"waiting": 0,
|
||
"timeout": 180000
|
||
},
|
||
"utxos": {
|
||
"locked": 3,
|
||
"locked_since": "2026-01-25T11:55:00.000Z",
|
||
"stale_locks": 0,
|
||
"stale_locks_details": []
|
||
},
|
||
"bitcoin": {
|
||
"connected": true,
|
||
"blocks": 12345,
|
||
"chain": "signet",
|
||
"rpc_timeout": 60000
|
||
},
|
||
"timestamp": "2026-01-25T12:00:00.000Z"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (503 Service Unavailable) - UTXOs verrouillés depuis > 10 min</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"ok": false,
|
||
"service": "anchor-api",
|
||
"mutex": {
|
||
"locked": false,
|
||
"waiting": 0,
|
||
"timeout": 180000
|
||
},
|
||
"utxos": {
|
||
"locked": 5,
|
||
"locked_since": "2026-01-25T11:45:00.000Z",
|
||
"stale_locks": 5,
|
||
"stale_locks_details": [
|
||
{
|
||
"txid": "abc123...",
|
||
"vout": 0,
|
||
"minutes_locked": 15.5
|
||
}
|
||
]
|
||
},
|
||
"bitcoin": {
|
||
"connected": true,
|
||
"blocks": 12345,
|
||
"chain": "signet",
|
||
"rpc_timeout": 60000
|
||
},
|
||
"timestamp": "2026-01-25T12:00:00.000Z"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
<p><strong>Codes de statut :</strong></p>
|
||
<ul style="margin-left: 20px; margin-top: 10px;">
|
||
<li><code>200</code> - Tout fonctionne correctement</li>
|
||
<li><code>200</code> - Warning si 5-10 UTXOs verrouillés (mais < 10 min)</li>
|
||
<li><code>503</code> - Service Unavailable si UTXOs verrouillés depuis > 10 min ou > 10 UTXOs verrouillés</li>
|
||
<li><code>503</code> - Service Unavailable si Bitcoin non connecté</li>
|
||
</ul>
|
||
</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>
|
||
<tr>
|
||
<td class="param-name">skipIfExists</td>
|
||
<td>boolean</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Si <code>true</code>, ne réancrera pas un hash déjà ancré ; retourne les infos existantes en base avec <code>old: true</code>. Défaut <code>false</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/anchor/document \
|
||
-H "Content-Type: application/json" \
|
||
-H "X-API-Key: votre-clé-api" \
|
||
-d '{
|
||
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
|
||
"documentUid": "doc-12345",
|
||
"skipIfExists": true
|
||
}'</pre>
|
||
</div>
|
||
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK) – nouvelle transaction</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"ok": true,
|
||
"txid": "abc123def456...",
|
||
"status": "pending",
|
||
"confirmations": 0,
|
||
"block_height": null,
|
||
"outputs": [...],
|
||
"fee": 0.000012,
|
||
"fee_sats": 1200,
|
||
"old": false
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK) – hash déjà ancré (<code>skipIfExists: true</code>)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"ok": true,
|
||
"txid": "abc123def456...",
|
||
"status": "confirmed",
|
||
"confirmations": 42,
|
||
"block_height": 12345,
|
||
"old": true
|
||
}</pre>
|
||
</div>
|
||
<p>Aucune transaction n'est créée ; les données viennent de la base. <code>old: true</code> indique un ancrage préexistant.</p>
|
||
</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 (<code>old: false</code>), ou hash déjà ancré avec <code>skipIfExists: true</code> (<code>old: true</code>)</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>ℹ️ skipIfExists</h4>
|
||
<ul style="margin-left: 20px; margin-top: 10px;">
|
||
<li>Avec <code>skipIfExists: true</code>, l'API consulte la base ; si le hash existe déjà, elle retourne <code>old: true</code> et les infos (txid, block_height, confirmations, etc.) sans créer de transaction.</li>
|
||
<li>Utile pour éviter les réancrages en double (retries, idempotence). Par défaut <code>false</code> : comportement inchangé.</li>
|
||
</ul>
|
||
</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>
|
||
|
||
<!-- Section API Dashboard -->
|
||
<section class="api-docs-section">
|
||
<h2>📊 API Dashboard (signet-dashboard)</h2>
|
||
<p>Les endpoints ci-dessous sont servis par le Dashboard (<code>https://dashboard.certificator.4nkweb.com</code>, port 3020). Données issues de la base SQLite et du RPC Bitcoin. Pas d’authentification requise.</p>
|
||
</section>
|
||
|
||
<!-- Endpoint: UTXO Count -->
|
||
<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/count</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Retourne uniquement les compteurs UTXO (ancrages, disponibles pour ancrage, confirmés). Optimisé pour éviter de charger la liste complète.</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/count</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>{
|
||
"availableForAnchor": 180,
|
||
"confirmedAvailableForAnchor": 175,
|
||
"anchors": 150
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
<h4>ℹ️ Notes</h4>
|
||
<ul style="margin-left: 20px; margin-top: 10px;">
|
||
<li><code>anchors</code> : UTXOs catégorie ancrages (≥ 2000 sats, confirmés, non dépensés, non verrouillés)</li>
|
||
<li><code>confirmedAvailableForAnchor</code> : comme <code>availableForAnchor</code> mais avec ≥ 6 confirmations</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>Liste des UTXOs du wallet par catégorie, avec pagination côté serveur. Données en base SQLite.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="endpoint-params">
|
||
<h4>Paramètres (query)</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">category</td>
|
||
<td>string</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td><code>all</code> (défaut) | <code>bloc_rewards</code> | <code>ancrages</code> | <code>changes</code> | <code>fees</code>. <code>anchor</code> / <code>change</code> acceptés.</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="param-name">page</td>
|
||
<td>number</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Numéro de page (défaut 1). Ignoré si <code>category=all</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="param-name">limit</td>
|
||
<td>number</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Éléments par page (défaut 50, max 1000). Ignoré si <code>category=all</code>.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Exemple (counts seulement, category=all)</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) – category=all</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
|
||
},
|
||
"page": 1,
|
||
"limit": 0,
|
||
"totalPages": 0,
|
||
"message": "Use ?category=bloc_rewards|ancrages|changes|fees&page=X&limit=Y to get paginated data"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Exemple (données paginées)</h4>
|
||
<div class="code-block">
|
||
<pre>curl -X GET "https://dashboard.certificator.4nkweb.com/api/utxo/list?category=ancrages&page=1&limit=50"</pre>
|
||
</div>
|
||
<button class="copy-button" onclick="copyCode(this)">📋 Copier</button>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK) – category=ancrages (ex.)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"blocRewards": [],
|
||
"anchors": [{"txid":"...","vout":0,"address":"...","amount":0.000025,"confirmations":12,"category":"ancrages","isSpentOnchain":false,"isLockedInMutex":false,"blockHeight":null,"blockTime":1234567890,"isAnchorChange":false}],
|
||
"changes": [],
|
||
"fees": [],
|
||
"counts": {...},
|
||
"pagination": {
|
||
"category": "ancrages",
|
||
"page": 1,
|
||
"limit": 50,
|
||
"total": 150,
|
||
"totalPages": 3
|
||
}
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
<h4>ℹ️ Notes</h4>
|
||
<ul style="margin-left: 20px; margin-top: 10px;">
|
||
<li><code>category=fees</code> : objets <code>txid</code>, <code>fee</code>, <code>fee_sats</code>, <code>blockHeight</code>, <code>blockTime</code>, <code>confirmations</code>, <code>changeAddress</code>, <code>changeAmount</code>.</li>
|
||
<li><code>availableForAnchor</code> / <code>confirmedAvailableForAnchor</code> : mêmes définitions que <code>/api/utxo/count</code>.</li>
|
||
<li>Tri : bloc_rewards/ancrages par montant décroissant ; changes par <code>is_anchor_change</code> puis montant ; fees par <code>block_height</code> décroissant.</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>
|
||
|
||
<!-- Endpoint: UTXO Fees -->
|
||
<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/fees</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Liste des frais issus des transactions d'ancrage (métadonnées OP_RETURN). Données fournies par le RPC / cache.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"fees": [
|
||
{"txid":"...","fee":0.000012,"fee_sats":1200,"blockHeight":12345,"blockTime":1234567890,"confirmations":10,"changeAddress":"...","changeAmount":0.00002}
|
||
],
|
||
"count": 1
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: UTXO Fees Update -->
|
||
<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/fees/update</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Récupère les frais depuis les transactions d'ancrage (OP_RETURN) et les enregistre. Optionnel : limite au bloc donné.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.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">sinceBlockHeight</td>
|
||
<td>number</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Ne traiter que les ancrages depuis ce bloc (inclus). Si absent, tous.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"success": true,
|
||
"newFees": 5,
|
||
"totalFees": 120,
|
||
"processed": 50
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (500)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"success": false,
|
||
"error": "Error message"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Hash 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/hash/list</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Liste des hash ancrés, paginée (base SQLite).</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="endpoint-params">
|
||
<h4>Paramètres (query)</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">page</td>
|
||
<td>number</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Numéro de page (défaut 1).</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="param-name">limit</td>
|
||
<td>number</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Éléments par page (défaut 50, max 1000).</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Exemple</h4>
|
||
<div class="code-block">
|
||
<pre>curl -X GET "https://dashboard.certificator.4nkweb.com/api/hash/list?page=1&limit=50"</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>{
|
||
"hashes": [
|
||
{"hash":"a1b2...","txid":"...","blockHeight":12345,"confirmations":10,"date":"2026-01-28T12:00:00.000Z"}
|
||
],
|
||
"count": 50,
|
||
"total": 32000,
|
||
"page": 1,
|
||
"limit": 50,
|
||
"totalPages": 640
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Hash Generate -->
|
||
<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/hash/generate</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Calcule le hash SHA256 d'un texte ou d'un fichier (base64). Un seul des deux doit être fourni.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.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">text</td>
|
||
<td>string</td>
|
||
<td><span class="param-optional">Conditionnel</span></td>
|
||
<td>Texte UTF-8 à hasher. Exclure si <code>fileContent</code> fourni.</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="param-name">fileContent</td>
|
||
<td>string</td>
|
||
<td><span class="param-optional">Conditionnel</span></td>
|
||
<td>Contenu fichier (base64 ou UTF-8). Exclure si <code>text</code> fourni.</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="param-name">isBase64</td>
|
||
<td>boolean</td>
|
||
<td><span class="param-optional">Non</span></td>
|
||
<td>Si <code>true</code>, <code>fileContent</code> est décodé en base64 avant hash. Sinon traité en UTF-8.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"hash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Codes de statut</h4>
|
||
<ul style="margin-left: 20px;">
|
||
<li><span class="status-code status-200">200</span> Succès</li>
|
||
<li><span class="status-code status-400">400</span> <code>text</code> et <code>fileContent</code> absents ou tous deux fournis ; format invalide.</li>
|
||
<li><span class="status-code status-500">500</span> Erreur serveur</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Mining Difficulty -->
|
||
<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/mining/difficulty</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Difficulté de minage courante (RPC <code>getblockchaininfo</code>).</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"difficulty": 0.0002441371325370144
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Mining Avg Block Time -->
|
||
<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/mining/avg-block-time</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Temps moyen entre blocs (source : Mempool <code>/api/v1/difficulty-adjustment</code>).</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"timeAvg": 600000,
|
||
"timeAvgSeconds": 600,
|
||
"formatted": "10m"
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Transactions Avg Fee / Avg Amount -->
|
||
<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/transactions/avg-fee</span> · <span class="endpoint-path">/api/transactions/avg-amount</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Frais moyen (sats) et montant moyen (sats) pour les ancrages. Valeurs fixes (1200 sats / 1000 sats) pour ce contexte.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse avg-fee (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"avgFee": 1200,
|
||
"avgFeeRate": 0,
|
||
"txCount": 0
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse avg-amount (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"avgAmount": 1000,
|
||
"txCount": 0
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Endpoint: Anchor Example -->
|
||
<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/example</span>
|
||
</div>
|
||
|
||
<div class="endpoint-description">
|
||
<p>Exemple de transaction d'ancrage (la plus récente de la liste des hash) : txid, bloc, confirmations, entrées/sorties. Pour la page Learn.</p>
|
||
<p><strong>Base URL :</strong> <code>https://dashboard.certificator.4nkweb.com</code></p>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (200 OK)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"txid": "...",
|
||
"blockHeight": 12345,
|
||
"confirmations": 10,
|
||
"hash": "a1b2...",
|
||
"inputs": [{"txid":"...","vout":0,"value":0.001}],
|
||
"outputs": [{"address":"...","value":0.000025,"type":"nulldata","isOpReturn":true}]
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="response-example">
|
||
<h4>Réponse (404)</h4>
|
||
<div class="code-block">
|
||
<pre>{
|
||
"error": "No anchor transactions found"
|
||
}</pre>
|
||
</div>
|
||
</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>Dashboard : https://dashboard.certificator.4nkweb.com (port 3020)
|
||
API d'Ancrage : https://anchorage.certificator.4nkweb.com (port 3010, machine bitcoin 192.168.1.105)
|
||
API Faucet : https://faucet.certificator.4nkweb.com (port 3021)
|
||
API Filigrane : https://watermark.certificator.4nkweb.com (port 3022)
|
||
API ClamAV : https://antivir.certificator.4nkweb.com (port 3023)</pre>
|
||
</div>
|
||
<p><code>/health</code> est exposé par l’API d’Ancrage et l’API Faucet, pas par le Dashboard.</p>
|
||
</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>Dashboard : Port 3020</li>
|
||
<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>
|
||
</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>
|