**Motivations:** - Aligner la doc API du dashboard avec les évolutions (pagination serveur, base SQLite, frais, mining, hash, UTXO, etc.) - Documenter le paramètre skipIfExists de l'API d'ancrage - Corriger les références /health pour le dashboard (utiliser /api/blockchain/info) **Root causes:** - N/A (évolution documentation) **Correctifs:** - N/A **Evolutions:** - Section API Dashboard dans api-docs.html ; endpoints utxo/count, utxo/list (pagination, category), utxo/fees, fees/update, hash/list, hash/generate, mining, transactions, anchor/example - Paramètre skipIfExists et réponses old: true/false pour POST /api/anchor/document - DASHBOARD.md : liste endpoints à jour, tests sans /health - DOMAINS_AND_PORTS.md : tests dashboard via /api/blockchain/info - features/dashboard-api-docs-update.md **Pages affectées:** - signet-dashboard/public/api-docs.html - docs/DASHBOARD.md - docs/DOMAINS_AND_PORTS.md - features/dashboard-api-docs-update.md - api-anchorage/src/bitcoin-rpc.js, bitcoin-rpc.js.backup - data/sync-utxos.log - fixKnowledge/api-anchorage-null-includes-error.md, api-anchorage-rbf-replacement-error.md
183 lines
7.6 KiB
Markdown
183 lines
7.6 KiB
Markdown
# Correction: Erreur "insufficient fee, rejecting replacement" dans l'API d'ancrage
|
|
|
|
**Auteur** : Équipe 4NK
|
|
**Date** : 2026-01-28
|
|
**Fichier concerné** : `api-anchorage/src/bitcoin-rpc.js`
|
|
|
|
## Problème
|
|
|
|
L'API d'ancrage retournait une erreur `500 Internal Server Error` avec le message :
|
|
```
|
|
insufficient fee, rejecting replacement e14ed98d2285fd3a5342d0bb2b0c43bea633042ebf264531252c0890618495ab; new feerate 0.00002743 BTC/kvB <= old feerate 0.00002743 BTC/kvB
|
|
```
|
|
|
|
### Symptômes
|
|
|
|
- Erreur lors de l'envoi d'une transaction au mempool
|
|
- L'erreur se produit quand une transaction avec les mêmes inputs existe déjà dans le mempool
|
|
- Bitcoin Core rejette la nouvelle transaction car les frais ne sont pas plus élevés que la transaction existante (RBF - Replace By Fee)
|
|
|
|
### Impact
|
|
|
|
- Les requêtes d'ancrage échouent avec une erreur 500
|
|
- Aucune transaction n'est créée même si une transaction similaire existe déjà dans le mempool
|
|
- Les utilisateurs ne peuvent pas ancrer de documents dans ce cas
|
|
|
|
## Root Cause
|
|
|
|
Bitcoin Core implémente la politique RBF (Replace By Fee) qui permet de remplacer une transaction non confirmée dans le mempool par une nouvelle transaction avec les mêmes inputs, mais uniquement si les frais de la nouvelle transaction sont **strictement supérieurs** à ceux de l'ancienne.
|
|
|
|
Quand l'API essaie d'envoyer une transaction et qu'une transaction avec les mêmes inputs existe déjà dans le mempool avec des frais identiques ou supérieurs, Bitcoin Core rejette la nouvelle transaction avec l'erreur "insufficient fee, rejecting replacement".
|
|
|
|
Le code ne gérait pas cette erreur spécifique et la propageait directement, causant un échec de l'opération d'ancrage.
|
|
|
|
## Correctifs
|
|
|
|
### Modification du code
|
|
|
|
**Fichier** : `api-anchorage/src/bitcoin-rpc.js`
|
|
|
|
**Lignes 645-697** : Ajout de la gestion de l'erreur de remplacement RBF lors de l'envoi de la transaction.
|
|
|
|
**Avant :**
|
|
```javascript
|
|
// Envoyer la transaction au mempool
|
|
const txid = await this.client.command('sendrawtransaction', signedTx.hex, 0);
|
|
```
|
|
|
|
**Après :**
|
|
```javascript
|
|
// Envoyer la transaction au mempool
|
|
let txid;
|
|
try {
|
|
txid = await this.client.command('sendrawtransaction', signedTx.hex, 0);
|
|
} catch (sendError) {
|
|
// Gérer l'erreur de remplacement RBF (Replace By Fee)
|
|
const errorMessage = sendError.message || sendError.toString();
|
|
if (errorMessage.includes('insufficient fee') && errorMessage.includes('rejecting replacement')) {
|
|
// Extraire le txid de la transaction existante depuis le message d'erreur
|
|
const replacementMatch = errorMessage.match(/rejecting replacement ([a-fA-F0-9]{64})/);
|
|
if (replacementMatch && replacementMatch[1]) {
|
|
const existingTxid = replacementMatch[1];
|
|
|
|
// Vérifier si la transaction existe dans le mempool ou dans la blockchain
|
|
try {
|
|
const mempoolEntry = await this.client.command('getmempoolentry', existingTxid);
|
|
if (mempoolEntry) {
|
|
// La transaction existe dans le mempool, utiliser cette transaction
|
|
txid = existingTxid;
|
|
} else {
|
|
throw new Error('Transaction not in mempool');
|
|
}
|
|
} catch (mempoolError) {
|
|
// Si la transaction n'est pas dans le mempool, vérifier si elle est confirmée
|
|
if (mempoolError.message.includes('not in mempool')) {
|
|
try {
|
|
const txInfo = await this.client.getTransaction(existingTxid);
|
|
if (txInfo && txInfo.txid) {
|
|
// La transaction existe dans la blockchain (confirmée), utiliser cette transaction
|
|
txid = existingTxid;
|
|
} else {
|
|
throw sendError;
|
|
}
|
|
} catch (txError) {
|
|
throw sendError;
|
|
}
|
|
} else {
|
|
throw sendError;
|
|
}
|
|
}
|
|
} else {
|
|
throw sendError;
|
|
}
|
|
} else {
|
|
throw sendError;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Explication
|
|
|
|
1. **Détection de l'erreur RBF** : Le code détecte l'erreur "insufficient fee, rejecting replacement" dans le message d'erreur
|
|
2. **Extraction du txid** : Le txid de la transaction existante est extrait depuis le message d'erreur avec une expression régulière
|
|
3. **Vérification dans le mempool** : Le code vérifie d'abord si la transaction existe dans le mempool avec `getmempoolentry`
|
|
4. **Vérification dans la blockchain** : Si la transaction n'est pas dans le mempool, le code vérifie si elle est confirmée dans la blockchain avec `getTransaction`
|
|
5. **Utilisation de la transaction existante** : Si la transaction existe (dans le mempool ou confirmée), son txid est utilisé au lieu de créer une nouvelle transaction
|
|
6. **Propagation de l'erreur** : Si la transaction n'existe ni dans le mempool ni dans la blockchain, ou si l'erreur est d'un autre type, l'erreur originale est relancée
|
|
|
|
### Comportement
|
|
|
|
- Si une transaction avec les mêmes inputs existe déjà dans le mempool, l'API utilise cette transaction existante au lieu d'échouer
|
|
- Si la transaction n'est plus dans le mempool mais a été confirmée, l'API utilise la transaction confirmée
|
|
- Les informations de la transaction existante sont récupérées et retournées normalement
|
|
- L'utilisateur reçoit une réponse réussie avec les informations de la transaction existante (mempool ou confirmée)
|
|
- Les UTXOs sont toujours marqués comme dépensés (car ils sont utilisés dans la transaction existante)
|
|
|
|
## Modifications
|
|
|
|
### Fichiers Modifiés
|
|
|
|
- `api-anchorage/src/bitcoin-rpc.js` : Ajout de la gestion de l'erreur RBF lors de l'envoi de transaction (lignes 645-697)
|
|
|
|
## Modalités de Déploiement
|
|
|
|
1. **Redémarrer le service systemd** :
|
|
```bash
|
|
sudo systemctl restart anchorage-api
|
|
```
|
|
|
|
2. **Vérifier que le service est actif** :
|
|
```bash
|
|
sudo systemctl status anchorage-api
|
|
```
|
|
|
|
3. **Tester l'endpoint** :
|
|
```bash
|
|
curl -X POST http://localhost:3010/api/anchor/document \
|
|
-H "Content-Type: application/json" \
|
|
-H "x-api-key: <your-api-key>" \
|
|
-d '{
|
|
"hash": "a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890"
|
|
}'
|
|
```
|
|
|
|
## Modalités d'Analyse
|
|
|
|
### Vérification du problème
|
|
|
|
1. **Consulter les logs** :
|
|
```bash
|
|
sudo journalctl -u anchorage-api -n 100 --no-pager
|
|
```
|
|
|
|
2. **Rechercher l'erreur** :
|
|
- Chercher "insufficient fee, rejecting replacement"
|
|
- Vérifier si le code détecte et gère correctement l'erreur
|
|
- Vérifier les logs "Transaction replacement rejected, using existing transaction"
|
|
|
|
3. **Vérifier le mempool** :
|
|
```bash
|
|
bitcoin-cli getrawmempool
|
|
```
|
|
|
|
### Tests de validation
|
|
|
|
1. **Test avec transaction existante** :
|
|
- Créer une transaction d'ancrage
|
|
- Essayer de créer une autre transaction avec les mêmes inputs (même hash)
|
|
- Vérifier que l'API utilise la transaction existante au lieu d'échouer
|
|
|
|
2. **Test avec transaction confirmée** :
|
|
- Si la transaction existante a été confirmée, l'erreur doit être relancée normalement
|
|
- Vérifier que l'API gère correctement ce cas
|
|
|
|
3. **Test avec erreur différente** :
|
|
- Vérifier que les autres erreurs (solde insuffisant, etc.) sont toujours propagées correctement
|
|
|
|
## Notes
|
|
|
|
- Cette correction permet à l'API de gérer gracieusement les cas où une transaction similaire existe déjà dans le mempool
|
|
- L'utilisateur reçoit toujours une réponse réussie avec les informations de la transaction (existante ou nouvelle)
|
|
- Les frais de la transaction existante sont utilisés, ce qui est acceptable car la transaction est déjà dans le mempool
|
|
- Cette approche évite d'avoir à augmenter les frais pour remplacer la transaction, ce qui serait plus complexe et coûteux
|