# 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: " \ -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