diff --git a/drongo b/drongo index ba869245..eddd6406 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit ba8692450335f2e5293ddd3951a394244315803f +Subproject commit eddd6406efc20b83e659d59faa16189417b0f5ed diff --git a/src/main/java/com/sparrowwallet/sparrow/AppServices.java b/src/main/java/com/sparrowwallet/sparrow/AppServices.java index e7c7c29f..b5e491f3 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppServices.java @@ -690,6 +690,10 @@ public class AppServices { payjoinURIs.put(bitcoinURI.getAddress(), bitcoinURI); } + public static void clearPayjoinURI(Address address) { + payjoinURIs.remove(address); + } + public static void clearTransactionHistoryCache(Wallet wallet) { ElectrumServer.clearRetrievedScriptHashes(wallet); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java index 8d41ecf4..ad411042 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java @@ -206,14 +206,16 @@ public class EntryCell extends TreeTableCell { .map(e -> e.getBlockTransaction().getTransaction().getOutputs().get((int)e.getHashIndex().getIndex())) .collect(Collectors.toList()); - long changeTotal = ourOutputs.stream().mapToLong(TransactionOutput::getValue).sum(); + long changeTotal = ourOutputs.stream().mapToLong(TransactionOutput::getValue).sum() - consolidationOutputs.stream().mapToLong(TransactionOutput::getValue).sum(); Transaction tx = blockTransaction.getTransaction(); double vSize = tx.getVirtualSize(); int inputSize = tx.getInputs().get(0).getLength() + (tx.getInputs().get(0).hasWitness() ? tx.getInputs().get(0).getWitness().getLength() / Transaction.WITNESS_SCALE_FACTOR : 0); List walletUtxos = new ArrayList<>(transactionEntry.getWallet().getWalletUtxos().keySet()); + //Remove any UTXOs created by the transaction that is to be replaced + walletUtxos.removeIf(utxo -> ourOutputs.stream().anyMatch(output -> output.getHash().equals(utxo.getHash()) && output.getIndex() == utxo.getIndex())); Collections.shuffle(walletUtxos); while((double)changeTotal / vSize < getMaxFeeRate() && !walletUtxos.isEmpty()) { - //If there is insufficent change output, include another random UTXO so the fee can be increased + //If there is insufficient change output, include another random UTXO so the fee can be increased BlockTransactionHashIndex utxo = walletUtxos.remove(0); utxos.add(utxo); changeTotal += utxo.getValue(); @@ -223,6 +225,7 @@ public class EntryCell extends TreeTableCell { List externalOutputs = new ArrayList<>(blockTransaction.getTransaction().getOutputs()); externalOutputs.removeAll(ourOutputs); externalOutputs.addAll(consolidationOutputs); + final long rbfChange = changeTotal; List payments = externalOutputs.stream().map(txOutput -> { try { String label = transactionEntry.getLabel() == null ? "" : transactionEntry.getLabel(); @@ -240,7 +243,8 @@ public class EntryCell extends TreeTableCell { } if(txOutput.getScript().getToAddress() != null) { - return new Payment(txOutput.getScript().getToAddress(), label, txOutput.getValue(), blockTransaction.getTransaction().getOutputs().size() == 1); + //Disable change creation by enabling max payment when there is only one output and no additional UTXOs included + return new Payment(txOutput.getScript().getToAddress(), label, txOutput.getValue(), blockTransaction.getTransaction().getOutputs().size() == 1 && rbfChange == 0); } return null; diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java index 6c1cf58b..6fe61ded 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java @@ -499,6 +499,12 @@ public class PaymentController extends WalletFormController implements Initializ } public void clear() { + try { + AppServices.clearPayjoinURI(getRecipientAddress()); + } catch(InvalidAddressException e) { + //ignore + } + address.setText(""); label.setText(""); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java index 87959cdb..132e09b9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java @@ -985,7 +985,8 @@ public class SendController extends WalletFormController implements Initializabl private boolean isMixPossible(List payments) { return (utxoSelectorProperty.get() == null || SorobanServices.canWalletMix(walletForm.getWallet())) && payments.size() == 1 - && (payments.get(0).getAddress().getScriptType() == getWalletForm().getWallet().getFreshNode(KeyPurpose.RECEIVE).getAddress().getScriptType()); + && (payments.get(0).getAddress().getScriptType() == getWalletForm().getWallet().getFreshNode(KeyPurpose.RECEIVE).getAddress().getScriptType()) + && AppServices.getPayjoinURI(payments.get(0).getAddress()) == null; } private void updateOptimizationButtons(List payments) { @@ -1633,6 +1634,7 @@ public class SendController extends WalletFormController implements Initializabl boolean roundPaymentAmounts = userPayments.stream().anyMatch(payment -> payment.getAmount() % 100 == 0); boolean mixedAddressTypes = userPayments.stream().anyMatch(payment -> payment.getAddress().getScriptType() != getWalletForm().getWallet().getFreshNode(KeyPurpose.RECEIVE).getAddress().getScriptType()); boolean addressReuse = userPayments.stream().anyMatch(payment -> walletAddresses.get(payment.getAddress()) != null && !walletAddresses.get(payment.getAddress()).getTransactionOutputs().isEmpty()); + boolean payjoinPresent = userPayments.stream().anyMatch(payment -> AppServices.getPayjoinURI(payment.getAddress()) != null); if(optimizationStrategy == OptimizationStrategy.PRIVACY) { if(payNymPresent) { @@ -1644,6 +1646,8 @@ public class SendController extends WalletFormController implements Initializabl addLabel("Cannot coinjoin due to mixed address types", getInfoGlyph()); } else if(userPayments.size() > 1) { addLabel("Cannot coinjoin due to multiple payments", getInfoGlyph()); + } else if(payjoinPresent) { + addLabel("Cannot coinjoin due to payjoin", getInfoGlyph()); } else { if(utxoSelectorProperty().get() != null) { addLabel("Cannot fake coinjoin due to coin control", getInfoGlyph()); diff --git a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java index 40082321..e5e1c443 100644 --- a/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java +++ b/src/main/java/com/sparrowwallet/sparrow/whirlpool/WhirlpoolServices.java @@ -162,7 +162,7 @@ public class WhirlpoolServices { public static boolean canWalletMix(Wallet wallet) { return Whirlpool.WHIRLPOOL_NETWORKS.contains(Network.get()) - && wallet.getScriptType() == ScriptType.P2WPKH + && wallet.getScriptType() != ScriptType.P2TR //Taproot not yet supported && wallet.getKeystores().size() == 1 && wallet.getKeystores().get(0).hasSeed() && wallet.getKeystores().get(0).getSeed().getType() == DeterministicSeed.Type.BIP39