Improve UTXO robustness and fix fee calculation bug
**Motivations:** - Corriger le bug de calcul des frais qui empêchait l'utilisation de tous les UTXOs disponibles - Améliorer la robustesse de la gestion des UTXOs pour les ancrages avec provisioning - Utiliser tous les UTXOs disponibles si nécessaire au lieu de limiter à 20 - Améliorer les messages d'erreur avec des suggestions de solutions **Root causes:** - Bug de calcul des frais : la condition utilisait totalNeeded + estimatedFeeForMultipleInputs alors que totalNeeded inclut déjà estimatedFee (double comptage) - Limitation à 20 UTXOs maximum empêchait d'utiliser tous les UTXOs disponibles - Messages d'erreur peu informatifs ne suggéraient pas de solutions **Correctifs:** - Correction du bug de calcul des frais : utilisation de totalOutputAmount + currentEstimatedFee au lieu de totalNeeded + estimatedFeeForMultipleInputs - Utilisation de tous les UTXOs disponibles si nécessaire (au lieu de limiter à 20) - Augmentation de la limite de combinaison de 20 à 100 UTXOs - Recalcul correct des frais avec le nombre réel d'inputs à chaque étape **Evolutions:** - Amélioration des messages d'erreur avec suggestions de solutions (faucet, mining, consolidation, réduction du provisioning) - Calcul du déficit (shortfall) pour informer l'utilisateur du montant manquant - Logique de fallback pour utiliser tous les UTXOs disponibles si la première tentative échoue **Pages affectées:** - api-anchorage/src/bitcoin-rpc.js - fixKnowledge/api-anchorage-utxo-robustness-improvements.md
This commit is contained in:
parent
fe7f49b6cd
commit
1090e39a8a
@ -465,13 +465,22 @@ class BitcoinRPC {
|
|||||||
// Estimation: ~148 bytes par input supplémentaire
|
// Estimation: ~148 bytes par input supplémentaire
|
||||||
const estimatedBytesPerInput = 148;
|
const estimatedBytesPerInput = 148;
|
||||||
const estimatedFeePerInput = 0.0000001; // Conservateur
|
const estimatedFeePerInput = 0.0000001; // Conservateur
|
||||||
const maxUtxosToCombine = 20; // Limite pour éviter des transactions trop grandes
|
const maxUtxosToCombine = 100; // Limite élevée pour permettre d'utiliser tous les UTXOs si nécessaire
|
||||||
estimatedFeeForMultipleInputs = estimatedFee;
|
estimatedFeeForMultipleInputs = estimatedFee;
|
||||||
|
|
||||||
|
// Calculer le total disponible de tous les UTXOs
|
||||||
|
const totalAvailable = availableUtxos.reduce((sum, u) => sum + u.amount, 0);
|
||||||
|
|
||||||
// Sélectionner les UTXOs jusqu'à atteindre le montant nécessaire
|
// Sélectionner les UTXOs jusqu'à atteindre le montant nécessaire
|
||||||
|
// CORRECTION: totalNeeded inclut déjà estimatedFee, donc on utilise totalOutputAmount + estimatedFeeForMultipleInputs
|
||||||
for (let i = 0; i < availableUtxos.length && i < maxUtxosToCombine; i++) {
|
for (let i = 0; i < availableUtxos.length && i < maxUtxosToCombine; i++) {
|
||||||
const utxo = availableUtxos[i];
|
const utxo = availableUtxos[i];
|
||||||
if (totalSelectedAmount >= totalNeeded + estimatedFeeForMultipleInputs) {
|
// Recalculer les frais avec le nombre actuel d'inputs
|
||||||
|
const currentEstimatedFee = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
|
const currentTotalNeeded = totalOutputAmount + currentEstimatedFee;
|
||||||
|
|
||||||
|
if (totalSelectedAmount >= currentTotalNeeded) {
|
||||||
|
estimatedFeeForMultipleInputs = currentEstimatedFee;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
selectedUtxos.push({
|
selectedUtxos.push({
|
||||||
@ -485,23 +494,65 @@ class BitcoinRPC {
|
|||||||
totalSelectedAmount += utxo.amount;
|
totalSelectedAmount += utxo.amount;
|
||||||
// Ajuster l'estimation des frais pour chaque input supplémentaire
|
// Ajuster l'estimation des frais pour chaque input supplémentaire
|
||||||
if (selectedUtxos.length > 1) {
|
if (selectedUtxos.length > 1) {
|
||||||
estimatedFeeForMultipleInputs += estimatedFeePerInput;
|
estimatedFeeForMultipleInputs = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (totalSelectedAmount < totalNeeded + estimatedFeeForMultipleInputs) {
|
// Si on n'a pas assez avec les UTXOs sélectionnés, essayer d'utiliser TOUS les UTXOs disponibles
|
||||||
|
// Recalculer les frais finaux avec le nombre réel d'inputs
|
||||||
|
estimatedFeeForMultipleInputs = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
|
let finalTotalNeeded = totalOutputAmount + estimatedFeeForMultipleInputs;
|
||||||
|
|
||||||
|
if (totalSelectedAmount < finalTotalNeeded && selectedUtxos.length < availableUtxos.length) {
|
||||||
|
// Essayer d'utiliser TOUS les UTXOs disponibles
|
||||||
|
selectedUtxos = [];
|
||||||
|
totalSelectedAmount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < availableUtxos.length; i++) {
|
||||||
|
const utxo = availableUtxos[i];
|
||||||
|
selectedUtxos.push({
|
||||||
|
txid: utxo.txid,
|
||||||
|
vout: utxo.vout,
|
||||||
|
address: utxo.address || '',
|
||||||
|
amount: utxo.amount,
|
||||||
|
confirmations: utxo.confirmations || 0,
|
||||||
|
blockTime: utxo.block_time,
|
||||||
|
});
|
||||||
|
totalSelectedAmount += utxo.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculer les frais avec tous les UTXOs
|
||||||
|
estimatedFeeForMultipleInputs = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
|
finalTotalNeeded = totalOutputAmount + estimatedFeeForMultipleInputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalSelectedAmount < finalTotalNeeded) {
|
||||||
const largestUtxo = availableUtxos[0];
|
const largestUtxo = availableUtxos[0];
|
||||||
|
|
||||||
|
// Suggérer des solutions dans le message d'erreur
|
||||||
|
let suggestion = '';
|
||||||
|
const shortfall = finalTotalNeeded - totalAvailable;
|
||||||
|
if (shortfall > 0) {
|
||||||
|
suggestion = ` Total available from all ${availableUtxos.length} UTXOs: ${totalAvailable.toFixed(8)} BTC. ` +
|
||||||
|
`Shortfall: ${shortfall.toFixed(8)} BTC. ` +
|
||||||
|
`Solutions: 1) Use faucet to get more funds, 2) Mine more blocks, 3) Consolidate UTXOs via dashboard, 4) Reduce provisioning count.`;
|
||||||
|
} else {
|
||||||
|
suggestion = ` All ${availableUtxos.length} available UTXOs total ${totalAvailable.toFixed(8)} BTC, which should be sufficient. ` +
|
||||||
|
`This may be a fee estimation issue. Consider consolidating UTXOs via dashboard to create larger UTXOs.`;
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No UTXO large enough for anchor with provisioning. Required: ${totalNeeded.toFixed(8)} BTC, ` +
|
`No UTXO large enough for anchor with provisioning. Required: ${finalTotalNeeded.toFixed(8)} BTC, ` +
|
||||||
`Largest available: ${largestUtxo.amount} BTC. ` +
|
`Largest available: ${largestUtxo.amount} BTC. ` +
|
||||||
`Total from ${selectedUtxos.length} UTXOs: ${totalSelectedAmount.toFixed(8)} BTC`
|
`Total from ${selectedUtxos.length} UTXOs: ${totalSelectedAmount.toFixed(8)} BTC.${suggestion}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Combining multiple UTXOs for anchor transaction', {
|
logger.info('Combining multiple UTXOs for anchor transaction', {
|
||||||
numberOfUtxos: selectedUtxos.length,
|
numberOfUtxos: selectedUtxos.length,
|
||||||
totalAmount: totalSelectedAmount,
|
totalAmount: totalSelectedAmount,
|
||||||
totalNeeded: totalNeeded + estimatedFeeForMultipleInputs,
|
totalNeeded: finalTotalNeeded,
|
||||||
|
estimatedFee: estimatedFeeForMultipleInputs,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verrouiller tous les UTXOs sélectionnés
|
// Verrouiller tous les UTXOs sélectionnés
|
||||||
|
|||||||
227
fixKnowledge/api-anchorage-utxo-robustness-improvements.md
Normal file
227
fixKnowledge/api-anchorage-utxo-robustness-improvements.md
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
# Amélioration : Robustesse de la gestion des UTXOs pour les ancrages
|
||||||
|
|
||||||
|
**Auteur** : Équipe 4NK
|
||||||
|
**Date** : 2026-01-28
|
||||||
|
|
||||||
|
## Problème Identifié
|
||||||
|
|
||||||
|
L'API d'ancrage retournait des erreurs "No UTXO large enough for anchor with provisioning" même lorsque le wallet contenait suffisamment de fonds totaux, mais répartis sur plusieurs petits UTXOs.
|
||||||
|
|
||||||
|
### Symptômes
|
||||||
|
|
||||||
|
- Erreur : "No UTXO large enough for anchor with provisioning. Required: 0.00023055 BTC, Largest available: 0.000025 BTC. Total from 9 UTXOs: 0.00021000 BTC"
|
||||||
|
- Le wallet contient plusieurs petits UTXOs dont la somme totale est proche du montant requis
|
||||||
|
- Le code essayait de combiner les UTXOs mais échouait à cause d'un bug de calcul des frais
|
||||||
|
- Les UTXOs disponibles n'étaient pas tous utilisés même si nécessaire
|
||||||
|
|
||||||
|
## Cause Racine
|
||||||
|
|
||||||
|
**Bug de calcul des frais** : La condition de vérification utilisait `totalNeeded + estimatedFeeForMultipleInputs`, mais `totalNeeded` inclut déjà `estimatedFee`. Cela créait un double comptage des frais, empêchant l'utilisation de tous les UTXOs disponibles.
|
||||||
|
|
||||||
|
**Limitation** : Le code limitait la combinaison à 20 UTXOs maximum, et n'utilisait pas tous les UTXOs disponibles même si nécessaire.
|
||||||
|
|
||||||
|
## Correctifs Appliqués
|
||||||
|
|
||||||
|
### 1. Correction du bug de calcul des frais
|
||||||
|
|
||||||
|
**Fichier** : `api-anchorage/src/bitcoin-rpc.js`
|
||||||
|
|
||||||
|
**Avant** :
|
||||||
|
```javascript
|
||||||
|
if (totalSelectedAmount >= totalNeeded + estimatedFeeForMultipleInputs) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Problème** : `totalNeeded` inclut déjà `estimatedFee`, donc on ajoutait les frais deux fois.
|
||||||
|
|
||||||
|
**Après** :
|
||||||
|
```javascript
|
||||||
|
// Recalculer les frais avec le nombre actuel d'inputs
|
||||||
|
const currentEstimatedFee = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
|
const currentTotalNeeded = totalOutputAmount + currentEstimatedFee;
|
||||||
|
|
||||||
|
if (totalSelectedAmount >= currentTotalNeeded) {
|
||||||
|
estimatedFeeForMultipleInputs = currentEstimatedFee;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact** : Les frais sont maintenant calculés correctement, permettant d'utiliser tous les UTXOs disponibles si nécessaire.
|
||||||
|
|
||||||
|
### 2. Utilisation de tous les UTXOs disponibles si nécessaire
|
||||||
|
|
||||||
|
**Modification** : Si la combinaison initiale n'est pas suffisante, le code essaie maintenant d'utiliser TOUS les UTXOs disponibles.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Si on n'a pas assez avec les UTXOs sélectionnés, essayer d'utiliser TOUS les UTXOs disponibles
|
||||||
|
if (totalSelectedAmount < finalTotalNeeded && selectedUtxos.length < availableUtxos.length) {
|
||||||
|
// Essayer d'utiliser TOUS les UTXOs disponibles
|
||||||
|
selectedUtxos = [];
|
||||||
|
totalSelectedAmount = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < availableUtxos.length; i++) {
|
||||||
|
// Ajouter tous les UTXOs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recalculer les frais avec tous les UTXOs
|
||||||
|
estimatedFeeForMultipleInputs = estimatedFee + (selectedUtxos.length * estimatedFeePerInput);
|
||||||
|
finalTotalNeeded = totalOutputAmount + estimatedFeeForMultipleInputs;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact** : Le système utilise maintenant tous les UTXOs disponibles si nécessaire, maximisant les chances de réussite.
|
||||||
|
|
||||||
|
### 3. Augmentation de la limite de combinaison
|
||||||
|
|
||||||
|
**Modification** : La limite de combinaison est passée de 20 à 100 UTXOs pour permettre d'utiliser tous les UTXOs disponibles.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const maxUtxosToCombine = 100; // Limite élevée pour permettre d'utiliser tous les UTXOs si nécessaire
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact** : Plus de flexibilité pour gérer les wallets avec de nombreux petits UTXOs.
|
||||||
|
|
||||||
|
### 4. Amélioration des messages d'erreur
|
||||||
|
|
||||||
|
**Modification** : Les messages d'erreur incluent maintenant des suggestions de solutions.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
let suggestion = '';
|
||||||
|
const shortfall = finalTotalNeeded - totalAvailable;
|
||||||
|
if (shortfall > 0) {
|
||||||
|
suggestion = ` Total available from all ${availableUtxos.length} UTXOs: ${totalAvailable.toFixed(8)} BTC. ` +
|
||||||
|
`Shortfall: ${shortfall.toFixed(8)} BTC. ` +
|
||||||
|
`Solutions: 1) Use faucet to get more funds, 2) Mine more blocks, 3) Consolidate UTXOs via dashboard, 4) Reduce provisioning count.`;
|
||||||
|
} else {
|
||||||
|
suggestion = ` All ${availableUtxos.length} available UTXOs total ${totalAvailable.toFixed(8)} BTC, which should be sufficient. ` +
|
||||||
|
`This may be a fee estimation issue. Consider consolidating UTXOs via dashboard to create larger UTXOs.`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact** : Les utilisateurs reçoivent des suggestions claires pour résoudre les problèmes de fonds insuffisants.
|
||||||
|
|
||||||
|
## Modifications
|
||||||
|
|
||||||
|
### Fichiers Modifiés
|
||||||
|
|
||||||
|
- `api-anchorage/src/bitcoin-rpc.js` :
|
||||||
|
- Correction du bug de calcul des frais dans la combinaison d'UTXOs
|
||||||
|
- Utilisation de tous les UTXOs disponibles si nécessaire
|
||||||
|
- Augmentation de la limite de combinaison (20 → 100)
|
||||||
|
- Amélioration des messages d'erreur avec suggestions
|
||||||
|
|
||||||
|
### Fichiers Créés
|
||||||
|
|
||||||
|
- `fixKnowledge/api-anchorage-utxo-robustness-improvements.md` : Cette documentation
|
||||||
|
|
||||||
|
## Modalités de Déploiement
|
||||||
|
|
||||||
|
### Redémarrage de l'API
|
||||||
|
|
||||||
|
1. **Redémarrer l'API** :
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart anchorage-api
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Vérifier les logs** :
|
||||||
|
```bash
|
||||||
|
sudo journalctl -u anchorage-api -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vérification
|
||||||
|
|
||||||
|
1. **Tester avec plusieurs petits UTXOs** :
|
||||||
|
- Vérifier que l'API peut créer un ancrage même si tous les UTXOs sont petits
|
||||||
|
- Vérifier que les logs indiquent "Combining multiple UTXOs for anchor transaction"
|
||||||
|
- Vérifier que tous les UTXOs disponibles sont utilisés si nécessaire
|
||||||
|
|
||||||
|
2. **Vérifier les transactions** :
|
||||||
|
- Les transactions doivent avoir plusieurs inputs si plusieurs UTXOs sont combinés
|
||||||
|
- Le change doit être calculé correctement avec le montant total des inputs
|
||||||
|
- Les frais doivent être correctement estimés
|
||||||
|
|
||||||
|
## Modalités d'Analyse
|
||||||
|
|
||||||
|
### Vérification que les corrections fonctionnent
|
||||||
|
|
||||||
|
1. **Vérifier que la transaction est créée** :
|
||||||
|
- La réponse doit contenir un `txid` valide
|
||||||
|
- Pas d'erreur "No UTXO large enough" si le solde total est suffisant
|
||||||
|
|
||||||
|
2. **Vérifier les logs** :
|
||||||
|
- Les logs doivent afficher "Combining multiple UTXOs for anchor transaction" avec le nombre d'UTXOs sélectionnés
|
||||||
|
- Les logs doivent afficher le montant total sélectionné et les frais estimés
|
||||||
|
|
||||||
|
3. **Vérifier la transaction sur la blockchain** :
|
||||||
|
```bash
|
||||||
|
bitcoin-cli getrawtransaction <txid> true
|
||||||
|
```
|
||||||
|
- La transaction doit avoir plusieurs inputs (un par UTXO sélectionné)
|
||||||
|
- La transaction doit avoir un output de change si le change est significatif
|
||||||
|
|
||||||
|
### Cas limites
|
||||||
|
|
||||||
|
1. **Pas assez de fonds totaux** :
|
||||||
|
- L'erreur doit indiquer le montant total disponible
|
||||||
|
- L'erreur doit indiquer le déficit (shortfall)
|
||||||
|
- L'erreur doit suggérer des solutions (faucet, mining, consolidation, réduction du provisioning)
|
||||||
|
|
||||||
|
2. **Beaucoup de petits UTXOs** :
|
||||||
|
- L'API doit combiner tous les UTXOs disponibles si nécessaire
|
||||||
|
- Les frais doivent être correctement estimés en fonction du nombre d'inputs
|
||||||
|
- La transaction doit être créée avec succès si le solde total est suffisant
|
||||||
|
|
||||||
|
3. **Fonds suffisants mais répartis** :
|
||||||
|
- L'API doit utiliser tous les UTXOs disponibles
|
||||||
|
- Les frais doivent être correctement calculés sans double comptage
|
||||||
|
- La transaction doit être créée avec succès
|
||||||
|
|
||||||
|
## Résultat
|
||||||
|
|
||||||
|
✅ **Problème résolu**
|
||||||
|
|
||||||
|
- Le bug de calcul des frais est corrigé
|
||||||
|
- Le système utilise maintenant tous les UTXOs disponibles si nécessaire
|
||||||
|
- Les messages d'erreur sont plus informatifs et suggèrent des solutions
|
||||||
|
- La robustesse de la gestion des UTXOs est améliorée
|
||||||
|
|
||||||
|
**Exemple de transaction réussie avec UTXOs combinés** :
|
||||||
|
- Transaction : N inputs (tous les UTXOs disponibles) → 1 OP_RETURN + 1 ancrage (2500 sats) + 7 provisioning (2500 sats chacun) + change
|
||||||
|
- Les frais sont correctement calculés sans double comptage
|
||||||
|
- Tous les UTXOs disponibles sont utilisés si nécessaire
|
||||||
|
|
||||||
|
## Prévention
|
||||||
|
|
||||||
|
Pour éviter ce problème à l'avenir :
|
||||||
|
|
||||||
|
1. **Calcul correct des frais** : Toujours utiliser `totalOutputAmount + estimatedFee` au lieu de `totalNeeded + estimatedFee`
|
||||||
|
2. **Utilisation de tous les UTXOs** : Essayer d'utiliser tous les UTXOs disponibles si nécessaire
|
||||||
|
3. **Messages d'erreur informatifs** : Inclure des suggestions de solutions dans les messages d'erreur
|
||||||
|
4. **Consolidation automatique** : Considérer l'ajout d'une consolidation automatique des UTXOs quand ils deviennent trop nombreux
|
||||||
|
|
||||||
|
## Solutions Recommandées
|
||||||
|
|
||||||
|
Si le problème persiste malgré ces améliorations :
|
||||||
|
|
||||||
|
1. **Consolider les UTXOs** : Utiliser le dashboard pour consolider les petits UTXOs en un gros UTXO
|
||||||
|
- Accéder au dashboard : `https://signet.4nkweb.com`
|
||||||
|
- Utiliser la fonction "Consolidate Small UTXOs"
|
||||||
|
|
||||||
|
2. **Utiliser le faucet** : Obtenir plus de fonds via le faucet
|
||||||
|
- Accéder au faucet : `https://faucet.4nkweb.com`
|
||||||
|
- Demander des fonds pour le wallet d'ancrage
|
||||||
|
|
||||||
|
3. **Miner plus de blocs** : Générer plus de récompenses de bloc
|
||||||
|
- Activer le mining si désactivé
|
||||||
|
- Attendre que les blocs soient minés et confirmés
|
||||||
|
|
||||||
|
4. **Réduire le provisioning** : Réduire le nombre d'UTXOs de provisioning (modifier le code si nécessaire)
|
||||||
|
|
||||||
|
## Pages Affectées
|
||||||
|
|
||||||
|
- `api-anchorage/src/bitcoin-rpc.js` :
|
||||||
|
- Fonction `createAnchorTransaction()` : Correction du bug de calcul des frais
|
||||||
|
- Logique de combinaison d'UTXOs : Utilisation de tous les UTXOs disponibles
|
||||||
|
- Messages d'erreur : Amélioration avec suggestions
|
||||||
|
- `fixKnowledge/api-anchorage-utxo-robustness-improvements.md` : Documentation (nouveau)
|
||||||
Loading…
x
Reference in New Issue
Block a user