Add automatic UTXO consolidation before anchor when funds insufficient
**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
This commit is contained in:
parent
ae99d12f29
commit
7a6b9e8b6e
@ -35,6 +35,11 @@ class BitcoinRPC {
|
|||||||
|
|
||||||
// Note: Les UTXOs verrouillés sont maintenant gérés uniquement dans la base de données
|
// 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
|
// 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<boolean>} 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
|
* Crée une transaction d'ancrage
|
||||||
@ -527,7 +623,54 @@ class BitcoinRPC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (totalSelectedAmount < finalTotalNeeded) {
|
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
|
// Ne réduire que si numberOfProvisioningUtxos n'a pas été explicitement fourni
|
||||||
if (numberOfProvisioningUtxos === null && provisioningCount > 0) {
|
if (numberOfProvisioningUtxos === null && provisioningCount > 0) {
|
||||||
logger.info('Insufficient funds for full provisioning, trying with reduced provisioning', {
|
logger.info('Insufficient funds for full provisioning, trying with reduced provisioning', {
|
||||||
|
|||||||
106
features/api-anchorage-automatic-consolidation.md
Normal file
106
features/api-anchorage-automatic-consolidation.md
Normal file
@ -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)
|
||||||
Loading…
x
Reference in New Issue
Block a user