disable privacy optimisation button for payjoins, improve rbf behaviour for wallet sweep txes

This commit is contained in:
Craig Raw 2022-05-12 10:57:31 +02:00
parent 5e4d6d5a78
commit 218761c594
6 changed files with 24 additions and 6 deletions

2
drongo

@ -1 +1 @@
Subproject commit ba8692450335f2e5293ddd3951a394244315803f Subproject commit eddd6406efc20b83e659d59faa16189417b0f5ed

View file

@ -690,6 +690,10 @@ public class AppServices {
payjoinURIs.put(bitcoinURI.getAddress(), bitcoinURI); payjoinURIs.put(bitcoinURI.getAddress(), bitcoinURI);
} }
public static void clearPayjoinURI(Address address) {
payjoinURIs.remove(address);
}
public static void clearTransactionHistoryCache(Wallet wallet) { public static void clearTransactionHistoryCache(Wallet wallet) {
ElectrumServer.clearRetrievedScriptHashes(wallet); ElectrumServer.clearRetrievedScriptHashes(wallet);

View file

@ -206,14 +206,16 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
.map(e -> e.getBlockTransaction().getTransaction().getOutputs().get((int)e.getHashIndex().getIndex())) .map(e -> e.getBlockTransaction().getTransaction().getOutputs().get((int)e.getHashIndex().getIndex()))
.collect(Collectors.toList()); .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(); Transaction tx = blockTransaction.getTransaction();
double vSize = tx.getVirtualSize(); 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); int inputSize = tx.getInputs().get(0).getLength() + (tx.getInputs().get(0).hasWitness() ? tx.getInputs().get(0).getWitness().getLength() / Transaction.WITNESS_SCALE_FACTOR : 0);
List<BlockTransactionHashIndex> walletUtxos = new ArrayList<>(transactionEntry.getWallet().getWalletUtxos().keySet()); List<BlockTransactionHashIndex> 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); Collections.shuffle(walletUtxos);
while((double)changeTotal / vSize < getMaxFeeRate() && !walletUtxos.isEmpty()) { 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); BlockTransactionHashIndex utxo = walletUtxos.remove(0);
utxos.add(utxo); utxos.add(utxo);
changeTotal += utxo.getValue(); changeTotal += utxo.getValue();
@ -223,6 +225,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
List<TransactionOutput> externalOutputs = new ArrayList<>(blockTransaction.getTransaction().getOutputs()); List<TransactionOutput> externalOutputs = new ArrayList<>(blockTransaction.getTransaction().getOutputs());
externalOutputs.removeAll(ourOutputs); externalOutputs.removeAll(ourOutputs);
externalOutputs.addAll(consolidationOutputs); externalOutputs.addAll(consolidationOutputs);
final long rbfChange = changeTotal;
List<Payment> payments = externalOutputs.stream().map(txOutput -> { List<Payment> payments = externalOutputs.stream().map(txOutput -> {
try { try {
String label = transactionEntry.getLabel() == null ? "" : transactionEntry.getLabel(); String label = transactionEntry.getLabel() == null ? "" : transactionEntry.getLabel();
@ -240,7 +243,8 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
} }
if(txOutput.getScript().getToAddress() != null) { 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; return null;

View file

@ -499,6 +499,12 @@ public class PaymentController extends WalletFormController implements Initializ
} }
public void clear() { public void clear() {
try {
AppServices.clearPayjoinURI(getRecipientAddress());
} catch(InvalidAddressException e) {
//ignore
}
address.setText(""); address.setText("");
label.setText(""); label.setText("");

View file

@ -985,7 +985,8 @@ public class SendController extends WalletFormController implements Initializabl
private boolean isMixPossible(List<Payment> payments) { private boolean isMixPossible(List<Payment> payments) {
return (utxoSelectorProperty.get() == null || SorobanServices.canWalletMix(walletForm.getWallet())) return (utxoSelectorProperty.get() == null || SorobanServices.canWalletMix(walletForm.getWallet()))
&& payments.size() == 1 && 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<Payment> payments) { private void updateOptimizationButtons(List<Payment> payments) {
@ -1633,6 +1634,7 @@ public class SendController extends WalletFormController implements Initializabl
boolean roundPaymentAmounts = userPayments.stream().anyMatch(payment -> payment.getAmount() % 100 == 0); 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 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 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(optimizationStrategy == OptimizationStrategy.PRIVACY) {
if(payNymPresent) { if(payNymPresent) {
@ -1644,6 +1646,8 @@ public class SendController extends WalletFormController implements Initializabl
addLabel("Cannot coinjoin due to mixed address types", getInfoGlyph()); addLabel("Cannot coinjoin due to mixed address types", getInfoGlyph());
} else if(userPayments.size() > 1) { } else if(userPayments.size() > 1) {
addLabel("Cannot coinjoin due to multiple payments", getInfoGlyph()); addLabel("Cannot coinjoin due to multiple payments", getInfoGlyph());
} else if(payjoinPresent) {
addLabel("Cannot coinjoin due to payjoin", getInfoGlyph());
} else { } else {
if(utxoSelectorProperty().get() != null) { if(utxoSelectorProperty().get() != null) {
addLabel("Cannot fake coinjoin due to coin control", getInfoGlyph()); addLabel("Cannot fake coinjoin due to coin control", getInfoGlyph());

View file

@ -162,7 +162,7 @@ public class WhirlpoolServices {
public static boolean canWalletMix(Wallet wallet) { public static boolean canWalletMix(Wallet wallet) {
return Whirlpool.WHIRLPOOL_NETWORKS.contains(Network.get()) 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().size() == 1
&& wallet.getKeystores().get(0).hasSeed() && wallet.getKeystores().get(0).hasSeed()
&& wallet.getKeystores().get(0).getSeed().getType() == DeterministicSeed.Type.BIP39 && wallet.getKeystores().get(0).getSeed().getType() == DeterministicSeed.Type.BIP39