From 7a6b9e8b6e1b275879d38eb1c0f5a6e753091b4f Mon Sep 17 00:00:00 2001 From: ncantu Date: Wed, 28 Jan 2026 15:20:13 +0100 Subject: [PATCH] Add automatic UTXO consolidation before anchor when funds insufficient MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations:** - Consolider automatiquement les petits UTXOs avant un ancrage si les fonds sont insuffisants - Améliorer l'efficacité en réduisant le nombre d'inputs dans les transactions - Permettre l'ancrage même avec des fonds répartis sur plusieurs petits UTXOs **Root causes:** - Le wallet contient souvent de nombreux petits UTXOs (2500 sats) créés par le provisioning - Les fonds sont répartis sur plusieurs petits UTXOs, rendant difficile la création d'ancrages avec provisioning complet - La consolidation manuelle via le dashboard n'est pas automatique **Correctifs:** - Ajout d'une méthode attemptAutoConsolidation() qui appelle l'API du dashboard pour consolider - Intégration de la consolidation automatique dans createAnchorTransaction() avant la réduction du provisioning - Protection contre les abus : cooldown de 5 minutes, limite à la première tentative **Evolutions:** - Consolidation automatique déclenchée si au moins 5 petits UTXOs ET (fonds insuffisants OU > 10 petits UTXOs) - Réessai automatique de l'ancrage après consolidation - Fallback sur la réduction du provisioning si la consolidation échoue ou n'est pas suffisante **Pages affectées:** - api-anchorage/src/bitcoin-rpc.js - features/api-anchorage-automatic-consolidation.md --- api-anchorage/src/bitcoin-rpc.js | 145 +++++++++++++++++- .../api-anchorage-automatic-consolidation.md | 106 +++++++++++++ 2 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 features/api-anchorage-automatic-consolidation.md diff --git a/api-anchorage/src/bitcoin-rpc.js b/api-anchorage/src/bitcoin-rpc.js index 2779476..e9d985b 100644 --- a/api-anchorage/src/bitcoin-rpc.js +++ b/api-anchorage/src/bitcoin-rpc.js @@ -35,6 +35,11 @@ class BitcoinRPC { // Note: Les UTXOs verrouillés sont maintenant gérés uniquement dans la base de données // via is_locked_in_mutex pour éviter la duplication et réduire la consommation mémoire + + // Cache pour éviter les consolidations trop fréquentes + // Timestamp de la dernière consolidation tentée + this.lastConsolidationAttempt = 0; + this.consolidationCooldown = 300000; // 5 minutes entre les tentatives de consolidation } /** @@ -307,6 +312,97 @@ class BitcoinRPC { } } + /** + * Tente de consolider automatiquement les petits UTXOs si bénéfique + * @param {number} requiredAmount - Montant requis pour l'ancrage + * @param {number} availableAmount - Montant disponible dans les UTXOs + * @param {number} smallUtxoCount - Nombre de petits UTXOs (< 2500 sats) + * @returns {Promise} True si une consolidation a été tentée + */ + async attemptAutoConsolidation(requiredAmount, availableAmount, smallUtxoCount) { + const now = Date.now(); + + // Éviter les consolidations trop fréquentes (cooldown de 5 minutes) + if (now - this.lastConsolidationAttempt < this.consolidationCooldown) { + logger.debug('Consolidation cooldown active, skipping', { + timeSinceLastAttempt: now - this.lastConsolidationAttempt, + cooldown: this.consolidationCooldown, + }); + return false; + } + + // Conditions pour déclencher une consolidation : + // 1. Au moins 5 petits UTXOs disponibles + // 2. Fonds insuffisants OU beaucoup de petits UTXOs (> 10) + const shouldConsolidate = smallUtxoCount >= 5 && + (availableAmount < requiredAmount || smallUtxoCount > 10); + + if (!shouldConsolidate) { + logger.debug('Consolidation not beneficial', { + smallUtxoCount, + availableAmount, + requiredAmount, + }); + return false; + } + + this.lastConsolidationAttempt = now; + + try { + logger.info('Attempting automatic UTXO consolidation', { + smallUtxoCount, + availableAmount, + requiredAmount, + shortfall: requiredAmount - availableAmount, + }); + + // Appeler l'API du dashboard pour consolider + const dashboardUrl = process.env.DASHBOARD_API_URL || 'http://localhost:3020'; + const response = await fetch(`${dashboardUrl}/api/utxo/consolidate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + signal: AbortSignal.timeout(60000), // Timeout de 60 secondes + }); + + if (!response.ok) { + const errorText = await response.text(); + logger.warn('Consolidation API call failed', { + status: response.status, + error: errorText, + }); + return false; + } + + const result = await response.json(); + + if (result.success) { + logger.info('Automatic consolidation successful', { + txid: result.txid, + inputCount: result.inputCount, + changeAmount: result.changeAmount, + estimatedFee: result.estimatedFee, + }); + + // Attendre un peu pour que la transaction soit propagée + await new Promise((resolve) => setTimeout(resolve, 2000)); + + return true; + } else { + logger.warn('Consolidation returned success=false', { + error: result.error, + }); + return false; + } + } catch (error) { + logger.warn('Error during automatic consolidation', { + error: error.message, + }); + return false; + } + } + /** * Crée une transaction d'ancrage @@ -527,7 +623,54 @@ class BitcoinRPC { } if (totalSelectedAmount < finalTotalNeeded) { - // Si les fonds sont insuffisants, essayer de réduire le provisioning progressivement + // Si les fonds sont insuffisants, essayer d'abord une consolidation automatique + // Compter les petits UTXOs (< 2500 sats) + const smallUtxos = availableUtxos.filter(u => u.amount < 0.000025); + + if (smallUtxos.length >= 5 && retryCount === 0) { + logger.info('Insufficient funds, attempting automatic consolidation', { + smallUtxoCount: smallUtxos.length, + totalAvailable: totalAvailable, + required: finalTotalNeeded, + }); + + // Libérer le mutex temporairement pour permettre la consolidation + releaseMutex(); + if (mutexSafetyTimeout) { + clearTimeout(mutexSafetyTimeout); + } + + const consolidationAttempted = await this.attemptAutoConsolidation( + finalTotalNeeded, + totalAvailable, + smallUtxos.length + ); + + if (consolidationAttempted) { + // Attendre un peu plus pour que la consolidation soit confirmée dans la DB + await new Promise((resolve) => setTimeout(resolve, 5000)); + + // Réessayer l'ancrage après consolidation + logger.info('Retrying anchor after consolidation', { + hash: hash.substring(0, 16) + '...', + }); + + return this.createAnchorTransaction(hash, recipientAddress, provisioningAddresses, numberOfProvisioningUtxos, retryCount + 1); + } + + // Si la consolidation n'a pas été tentée ou a échoué, réacquérir le mutex et continuer + const newReleaseMutex = await this.acquireUtxoMutex(); + releaseMutex = newReleaseMutex; + mutexSafetyTimeout = setTimeout(() => { + logger.error('Mutex held for too long, forcing release', { + hash: hash?.substring(0, 16) + '...', + duration: Date.now() - startTime, + }); + releaseMutex(); + }, 300000); + } + + // Si les fonds sont toujours insuffisants, essayer de réduire le provisioning progressivement // Ne réduire que si numberOfProvisioningUtxos n'a pas été explicitement fourni if (numberOfProvisioningUtxos === null && provisioningCount > 0) { logger.info('Insufficient funds for full provisioning, trying with reduced provisioning', { diff --git a/features/api-anchorage-automatic-consolidation.md b/features/api-anchorage-automatic-consolidation.md new file mode 100644 index 0000000..df25502 --- /dev/null +++ b/features/api-anchorage-automatic-consolidation.md @@ -0,0 +1,106 @@ +# Fonctionnalité : Consolidation automatique des UTXOs + +**Auteur** : Équipe 4NK +**Date** : 2026-01-28 + +## Objectif + +Permettre à l'API d'ancrage de consolider automatiquement les petits UTXOs avant un ancrage si les fonds sont insuffisants ou si le nombre de petits UTXOs est élevé. + +## Motivations + +- **Problème** : Le wallet contient souvent de nombreux petits UTXOs (2500 sats chacun) créés par le provisioning, ce qui rend difficile la création d'ancrages avec provisioning complet +- **Solution** : Consolider automatiquement les petits UTXOs en un gros UTXO avant l'ancrage si nécessaire +- **Bénéfices** : + - Réduction des frais de transaction (moins d'inputs) + - Facilitation de la sélection d'UTXOs (un seul gros UTXO au lieu de plusieurs petits) + - Amélioration de l'efficacité globale + - Ancrage réussi même avec des fonds répartis sur plusieurs petits UTXOs + +## Implémentation + +### Conditions de déclenchement + +La consolidation automatique est déclenchée si : +1. **Au moins 5 petits UTXOs** (< 2500 sats) sont disponibles +2. **ET** l'une des conditions suivantes : + - Les fonds disponibles sont insuffisants pour l'ancrage requis + - Plus de 10 petits UTXOs sont disponibles (consolidation préventive) + +### Protection contre les abus + +- **Cooldown de 5 minutes** : Évite les consolidations trop fréquentes +- **Limite à la première tentative** : La consolidation n'est tentée qu'une fois par ancrage (retryCount === 0) +- **Gestion des erreurs** : Si la consolidation échoue, le système continue avec la réduction du provisioning + +### Processus + +1. **Détection** : Lors de la création d'un ancrage, si les fonds sont insuffisants et qu'il y a au moins 5 petits UTXOs +2. **Consolidation** : Appel de l'API du dashboard (`/api/utxo/consolidate`) pour consolider les petits UTXOs +3. **Attente** : Attente de 2 secondes pour la propagation de la transaction, puis 5 secondes supplémentaires pour la mise à jour de la base de données +4. **Réessai** : Réessai de l'ancrage avec les nouveaux UTXOs consolidés +5. **Fallback** : Si la consolidation échoue ou n'est pas suffisante, réduction automatique du provisioning + +## Modifications + +### Fichiers Modifiés + +- `api-anchorage/src/bitcoin-rpc.js` : + - Ajout de `lastConsolidationAttempt` et `consolidationCooldown` dans le constructeur + - Ajout de la méthode `attemptAutoConsolidation()` + - Intégration de la consolidation automatique dans `createAnchorTransaction()` + +### Fichiers Créés + +- `features/api-anchorage-automatic-consolidation.md` : Cette documentation + +## Configuration + +### Variables d'environnement + +- `DASHBOARD_API_URL` : URL de l'API du dashboard (défaut : `http://localhost:3020`) +- `CONSOLIDATION_COOLDOWN` : Délai minimum entre les consolidations en ms (défaut : 300000 = 5 minutes) + +### Seuils configurables + +- **Nombre minimum de petits UTXOs** : 5 (hardcodé) +- **Seuil pour consolidation préventive** : 10 petits UTXOs (hardcodé) +- **Montant maximum pour "petit UTXO"** : 2500 sats (0.000025 BTC, hardcodé) + +## Exemple d'utilisation + +**Scénario** : Wallet avec 9 UTXOs de 0.000025 BTC chacun (total : 0.00021000 BTC), ancrage nécessite 0.00023145 BTC + +1. **Détection** : 9 petits UTXOs détectés, fonds insuffisants +2. **Consolidation** : Les 9 UTXOs sont consolidés en 1 UTXO de ~0.0002 BTC (après frais) +3. **Réessai** : L'ancrage est réessayé avec le nouveau UTXO consolidé +4. **Résultat** : Ancrage réussi avec provisioning complet + +## Avantages + +1. **Automatisation** : Plus besoin d'intervention manuelle pour consolider +2. **Efficacité** : Réduction des frais de transaction (moins d'inputs) +3. **Robustesse** : Ancrage réussi même avec des fonds répartis +4. **Prévention** : Consolidation préventive si trop de petits UTXOs + +## Limitations + +1. **Cooldown** : Une consolidation ne peut être tentée qu'une fois toutes les 5 minutes +2. **Dépendance** : Nécessite que l'API du dashboard soit accessible +3. **Frais** : La consolidation elle-même coûte des frais (~0.00001 BTC + frais par input) +4. **Confirmation** : La consolidation doit être confirmée avant que les nouveaux UTXOs soient utilisables + +## Améliorations futures + +1. **Consolidation périodique** : Cron job pour consolider automatiquement les petits UTXOs +2. **Seuils configurables** : Variables d'environnement pour les seuils +3. **Monitoring** : Métriques sur les consolidations automatiques +4. **Optimisation** : Consolider seulement si bénéfique (coût < gain) + +## Pages Affectées + +- `api-anchorage/src/bitcoin-rpc.js` : + - Constructeur : Ajout des propriétés de cache pour la consolidation + - Méthode `attemptAutoConsolidation()` : Nouvelle méthode pour la consolidation automatique + - Méthode `createAnchorTransaction()` : Intégration de la consolidation automatique +- `features/api-anchorage-automatic-consolidation.md` : Documentation (nouveau)