mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 13:16:44 +00:00
when constructing rbf and cpfp transactions, add any additional utxos by output group if effective fee is sufficient
This commit is contained in:
parent
5e3f31de30
commit
0a469a380b
3 changed files with 41 additions and 31 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 8484dd397b95d200cf3c363cd48e5751550b3bcb
|
Subproject commit 38b04b8e0b802f6cd43b4e88730d4d3ed31227fc
|
|
@ -249,16 +249,19 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
||||||
vSize += changeOutput.getLength();
|
vSize += changeOutput.getLength();
|
||||||
}
|
}
|
||||||
double inputSize = tx.getInputs().get(0).getLength() + (tx.getInputs().get(0).hasWitness() ? (double)tx.getInputs().get(0).getWitness().getLength() / Transaction.WITNESS_SCALE_FACTOR : 0);
|
double inputSize = tx.getInputs().get(0).getLength() + (tx.getInputs().get(0).hasWitness() ? (double)tx.getInputs().get(0).getWitness().getLength() / Transaction.WITNESS_SCALE_FACTOR : 0);
|
||||||
List<BlockTransactionHashIndex> walletUtxos = new ArrayList<>(transactionEntry.getWallet().getSpendableUtxos(blockTransaction).keySet());
|
List<TxoFilter> txoFilters = List.of(new ExcludeTxoFilter(utxos), new SpentTxoFilter(blockTransaction.getHash()), new FrozenTxoFilter(), new CoinbaseTxoFilter(transactionEntry.getWallet()));
|
||||||
//Remove the UTXOs we are respending
|
double feeRate = blockTransaction.getFeeRate() == null ? AppServices.getMinimumRelayFeeRate() : blockTransaction.getFeeRate();
|
||||||
walletUtxos.removeAll(utxos);
|
List<OutputGroup> outputGroups = transactionEntry.getWallet().getGroupedUtxos(txoFilters, feeRate, AppServices.getMinimumRelayFeeRate(), Config.get().isGroupByAddress())
|
||||||
Collections.shuffle(walletUtxos);
|
.stream().filter(outputGroup -> outputGroup.getEffectiveValue() >= 0).collect(Collectors.toList());
|
||||||
while((double)changeTotal / vSize < getMaxFeeRate() && !walletUtxos.isEmpty() && !cancelTransaction) {
|
Collections.shuffle(outputGroups);
|
||||||
//If there is insufficient change output, include another random UTXO so the fee can be increased
|
while((double)changeTotal / vSize < getMaxFeeRate() && !outputGroups.isEmpty() && !cancelTransaction) {
|
||||||
BlockTransactionHashIndex utxo = walletUtxos.remove(0);
|
//If there is insufficient change output, include another random output group so the fee can be increased
|
||||||
utxos.add(utxo);
|
OutputGroup outputGroup = outputGroups.remove(0);
|
||||||
changeTotal += utxo.getValue();
|
for(BlockTransactionHashIndex utxo : outputGroup.getUtxos()) {
|
||||||
vSize += inputSize;
|
utxos.add(utxo);
|
||||||
|
changeTotal += utxo.getValue();
|
||||||
|
vSize += inputSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Long fee = blockTransaction.getFee();
|
Long fee = blockTransaction.getFee();
|
||||||
|
@ -336,7 +339,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
||||||
|
|
||||||
private static Double getMaxFeeRate() {
|
private static Double getMaxFeeRate() {
|
||||||
if(AppServices.getTargetBlockFeeRates() == null || AppServices.getTargetBlockFeeRates().isEmpty()) {
|
if(AppServices.getTargetBlockFeeRates() == null || AppServices.getTargetBlockFeeRates().isEmpty()) {
|
||||||
return 1.0;
|
return 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppServices.getTargetBlockFeeRates().values().iterator().next();
|
return AppServices.getTargetBlockFeeRates().values().iterator().next();
|
||||||
|
@ -359,23 +362,26 @@ public class EntryCell extends TreeTableCell<Entry, Entry> implements Confirmati
|
||||||
Address freshAddress = transactionEntry.getWallet().getFreshNode(KeyPurpose.RECEIVE).getAddress();
|
Address freshAddress = transactionEntry.getWallet().getFreshNode(KeyPurpose.RECEIVE).getAddress();
|
||||||
TransactionOutput txOutput = new TransactionOutput(new Transaction(), cpfpUtxo.getValue(), freshAddress.getOutputScript());
|
TransactionOutput txOutput = new TransactionOutput(new Transaction(), cpfpUtxo.getValue(), freshAddress.getOutputScript());
|
||||||
long dustThreshold = freshAddress.getScriptType().getDustThreshold(txOutput, Transaction.DUST_RELAY_TX_FEE);
|
long dustThreshold = freshAddress.getScriptType().getDustThreshold(txOutput, Transaction.DUST_RELAY_TX_FEE);
|
||||||
int inputSize = freshAddress.getScriptType().getInputVbytes();
|
double inputSize = freshAddress.getScriptType().getInputVbytes();
|
||||||
long vSize = inputSize + txOutput.getLength();
|
double vSize = inputSize + txOutput.getLength();
|
||||||
|
|
||||||
List<BlockTransactionHashIndex> walletUtxos = new ArrayList<>(transactionEntry.getWallet().getSpendableUtxos().keySet());
|
List<TxoFilter> txoFilters = List.of(new ExcludeTxoFilter(List.of(cpfpUtxo)), new SpentTxoFilter(), new FrozenTxoFilter(), new CoinbaseTxoFilter(transactionEntry.getWallet()));
|
||||||
//Remove the UTXO we are already spending
|
double feeRate = blockTransaction.getFeeRate() == null ? AppServices.getMinimumRelayFeeRate() : blockTransaction.getFeeRate();
|
||||||
walletUtxos.remove(cpfpUtxo);
|
List<OutputGroup> outputGroups = transactionEntry.getWallet().getGroupedUtxos(txoFilters, feeRate, AppServices.getMinimumRelayFeeRate(), Config.get().isGroupByAddress())
|
||||||
Collections.shuffle(walletUtxos);
|
.stream().filter(outputGroup -> outputGroup.getEffectiveValue() >= 0).collect(Collectors.toList());
|
||||||
|
Collections.shuffle(outputGroups);
|
||||||
|
|
||||||
List<BlockTransactionHashIndex> utxos = new ArrayList<>();
|
List<BlockTransactionHashIndex> utxos = new ArrayList<>();
|
||||||
utxos.add(cpfpUtxo);
|
utxos.add(cpfpUtxo);
|
||||||
long inputTotal = cpfpUtxo.getValue();
|
long inputTotal = cpfpUtxo.getValue();
|
||||||
while((inputTotal - (long)(getMaxFeeRate() * vSize)) < dustThreshold && !walletUtxos.isEmpty()) {
|
while((inputTotal - (long)(getMaxFeeRate() * vSize)) < dustThreshold && !outputGroups.isEmpty()) {
|
||||||
//If there is insufficient input value, include another random UTXO so the fee can be increased
|
//If there is insufficient input value, include another random output group so the fee can be increased
|
||||||
BlockTransactionHashIndex utxo = walletUtxos.remove(0);
|
OutputGroup outputGroup = outputGroups.remove(0);
|
||||||
utxos.add(utxo);
|
for(BlockTransactionHashIndex utxo : outputGroup.getUtxos()) {
|
||||||
inputTotal += utxo.getValue();
|
utxos.add(utxo);
|
||||||
vSize += inputSize;
|
inputTotal += utxo.getValue();
|
||||||
|
vSize += inputSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String label = transactionEntry.getLabel() == null ? "" : transactionEntry.getLabel();
|
String label = transactionEntry.getLabel() == null ? "" : transactionEntry.getLabel();
|
||||||
|
|
|
@ -702,13 +702,17 @@ public class SendController extends WalletFormController implements Initializabl
|
||||||
} catch(InsufficientFundsException e) {
|
} catch(InsufficientFundsException e) {
|
||||||
if(e.getTargetValue() != null && replacedTransaction != null && utxoSelectors.size() == 1 && utxoSelectors.get(0) instanceof PresetUtxoSelector presetUtxoSelector) {
|
if(e.getTargetValue() != null && replacedTransaction != null && utxoSelectors.size() == 1 && utxoSelectors.get(0) instanceof PresetUtxoSelector presetUtxoSelector) {
|
||||||
//Creating RBF transaction - include additional UTXOs if available to pay desired fee
|
//Creating RBF transaction - include additional UTXOs if available to pay desired fee
|
||||||
List<BlockTransactionHashIndex> walletUtxos = new ArrayList<>(wallet.getWalletTxos(txoFilters).keySet());
|
List<TxoFilter> filters = new ArrayList<>(txoFilters);
|
||||||
//Remove any UTXOs that have already been added or previously excluded
|
filters.add(presetUtxoSelector.asExcludeTxoFilter());
|
||||||
walletUtxos.removeAll(presetUtxoSelector.getPresetUtxos());
|
List<OutputGroup> outputGroups = wallet.getGroupedUtxos(filters, feeRate, AppServices.getMinimumRelayFeeRate(), Config.get().isGroupByAddress())
|
||||||
walletUtxos.removeAll(presetUtxoSelector.getExcludedUtxos());
|
.stream().filter(outputGroup -> outputGroup.getEffectiveValue() >= 0).collect(Collectors.toList());
|
||||||
Collections.shuffle(walletUtxos);
|
Collections.shuffle(outputGroups);
|
||||||
while(!walletUtxos.isEmpty() && presetUtxoSelector.getPresetUtxos().stream().mapToLong(BlockTransactionHashIndex::getValue).sum() < e.getTargetValue()) {
|
|
||||||
presetUtxoSelector.getPresetUtxos().add(walletUtxos.remove(0));
|
while(!outputGroups.isEmpty() && presetUtxoSelector.getPresetUtxos().stream().mapToLong(BlockTransactionHashIndex::getValue).sum() < e.getTargetValue()) {
|
||||||
|
OutputGroup outputGroup = outputGroups.remove(0);
|
||||||
|
for(BlockTransactionHashIndex utxo : outputGroup.getUtxos()) {
|
||||||
|
presetUtxoSelector.getPresetUtxos().add(utxo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getWalletTransaction();
|
return getWalletTransaction();
|
||||||
|
|
Loading…
Reference in a new issue