From b17c15f70280ce721d9a1c50965e93ec10166b39 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 17 May 2021 13:21:56 +0200 Subject: [PATCH] request new change address in the transaction diagram --- drongo | 2 +- .../sparrow/control/TransactionDiagram.java | 42 +++++++++++++++++-- .../event/ReplaceChangeAddressEvent.java | 15 +++++++ .../sparrow/wallet/SendController.java | 19 +++++++-- .../wallet/TransactionsController.java | 2 +- .../com/sparrowwallet/sparrow/wallet/send.css | 6 ++- 6 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/event/ReplaceChangeAddressEvent.java diff --git a/drongo b/drongo index 7dca0d0c..567294a4 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 7dca0d0c39404bd89a5a4589a79ffe0f688e8181 +Subproject commit 567294a4b055cc062650de45fccbbc89db714f39 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java index 71ec1212..c75de9ec 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java @@ -1,5 +1,6 @@ package com.sparrowwallet.sparrow.control; +import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.uri.BitcoinURI; @@ -10,6 +11,7 @@ import com.sparrowwallet.drongo.wallet.WalletTransaction; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.ExcludeUtxoEvent; +import com.sparrowwallet.sparrow.event.ReplaceChangeAddressEvent; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -200,6 +202,7 @@ public class TransactionDiagram extends GridPane { private Pane getInputsLabels(Map displayedUtxos) { VBox inputsBox = new VBox(); inputsBox.setMaxWidth(150); + inputsBox.setPrefWidth(150); inputsBox.setPadding(new Insets(0, 10, 0, 10)); inputsBox.minHeightProperty().bind(minHeightProperty()); inputsBox.setAlignment(Pos.CENTER_RIGHT); @@ -388,14 +391,33 @@ public class TransactionDiagram extends GridPane { } if(walletTx.getChangeNode() != null) { + WalletNode defaultChangeNode = walletTx.getWallet().getFreshNode(KeyPurpose.CHANGE); + boolean overGapLimit = (walletTx.getChangeNode().getIndex() - defaultChangeNode.getIndex()) > walletTx.getWallet().getGapLimit(); + + HBox actionBox = new HBox(); String changeDesc = walletTx.getChangeAddress().toString().substring(0, 8) + "..."; - Label changeLabel = new Label(changeDesc, getChangeGlyph()); + Label changeLabel = new Label(changeDesc, overGapLimit ? getChangeWarningGlyph() : getChangeGlyph()); changeLabel.getStyleClass().addAll("output-label", "change-label"); - Tooltip changeTooltip = new Tooltip("Change of " + getSatsValue(walletTx.getChangeAmount()) + " sats to " + walletTx.getChangeNode().getDerivationPath().replace("m", "..") + "\n" + walletTx.getChangeAddress().toString()); + Tooltip changeTooltip = new Tooltip("Change of " + getSatsValue(walletTx.getChangeAmount()) + " sats to " + walletTx.getChangeNode().getDerivationPath().replace("m", "..") + "\n" + walletTx.getChangeAddress().toString() + (overGapLimit ? "\nAddress is beyond the gap limit!" : "")); changeTooltip.getStyleClass().add("change-label"); changeTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY)); changeLabel.setTooltip(changeTooltip); - outputsBox.getChildren().add(changeLabel); + + Button nextChangeAddressButton = new Button(""); + nextChangeAddressButton.setGraphic(getChangeReplaceGlyph()); + nextChangeAddressButton.setOnAction(event -> { + EventManager.get().post(new ReplaceChangeAddressEvent(walletTx)); + }); + Tooltip replaceChangeTooltip = new Tooltip("Use next change address"); + nextChangeAddressButton.setTooltip(replaceChangeTooltip); + Label replaceChangeLabel = new Label("", nextChangeAddressButton); + replaceChangeLabel.getStyleClass().add("replace-change-label"); + replaceChangeLabel.setVisible(false); + actionBox.setOnMouseEntered(event -> replaceChangeLabel.setVisible(true)); + actionBox.setOnMouseExited(event -> replaceChangeLabel.setVisible(false)); + + actionBox.getChildren().addAll(changeLabel, replaceChangeLabel); + outputsBox.getChildren().add(actionBox); outputsBox.getChildren().add(createSpacer()); } @@ -484,6 +506,20 @@ public class TransactionDiagram extends GridPane { return changeGlyph; } + public static Glyph getChangeWarningGlyph() { + Glyph changeWarningGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_TRIANGLE); + changeWarningGlyph.getStyleClass().add("change-warning-icon"); + changeWarningGlyph.setFontSize(12); + return changeWarningGlyph; + } + + public static Glyph getChangeReplaceGlyph() { + Glyph changeReplaceGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.ARROW_DOWN); + changeReplaceGlyph.getStyleClass().add("change-replace-icon"); + changeReplaceGlyph.setFontSize(12); + return changeReplaceGlyph; + } + private Glyph getFeeGlyph() { Glyph feeGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.HAND_HOLDING); feeGlyph.getStyleClass().add("fee-icon"); diff --git a/src/main/java/com/sparrowwallet/sparrow/event/ReplaceChangeAddressEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/ReplaceChangeAddressEvent.java new file mode 100644 index 00000000..53c3cf2e --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/ReplaceChangeAddressEvent.java @@ -0,0 +1,15 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.drongo.wallet.WalletTransaction; + +public class ReplaceChangeAddressEvent { + private final WalletTransaction walletTransaction; + + public ReplaceChangeAddressEvent(WalletTransaction walletTx) { + this.walletTransaction = walletTx; + } + + public WalletTransaction getWalletTransaction() { + return walletTransaction; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java index 2cee8c88..e40f2bd3 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java @@ -134,6 +134,8 @@ public class SendController extends WalletFormController implements Initializabl private final BooleanProperty includeSpentMempoolOutputsProperty = new SimpleBooleanProperty(false); + private final List excludedChangeNodes = new ArrayList<>(); + private final ChangeListener feeListener = new ChangeListener<>() { @Override public void changed(ObservableValue observable, String oldValue, String newValue) { @@ -494,7 +496,7 @@ public class SendController extends WalletFormController implements Initializabl boolean includeMempoolOutputs = Config.get().isIncludeMempoolOutputs(); boolean includeSpentMempoolOutputs = includeSpentMempoolOutputsProperty.get(); - walletTransactionService = new WalletTransactionService(wallet, getUtxoSelectors(), getUtxoFilters(), payments, feeRate, getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); + walletTransactionService = new WalletTransactionService(wallet, getUtxoSelectors(), getUtxoFilters(), payments, excludedChangeNodes, feeRate, getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); walletTransactionService.setOnSucceeded(event -> { if(!walletTransactionService.isIgnoreResult()) { walletTransactionProperty.setValue(walletTransactionService.getValue()); @@ -545,6 +547,7 @@ public class SendController extends WalletFormController implements Initializabl private final List utxoSelectors; private final List utxoFilters; private final List payments; + private final List excludedChangeNodes; private final double feeRate; private final double longTermFeeRate; private final Long fee; @@ -554,11 +557,12 @@ public class SendController extends WalletFormController implements Initializabl private final boolean includeSpentMempoolOutputs; private boolean ignoreResult; - public WalletTransactionService(Wallet wallet, List utxoSelectors, List utxoFilters, List payments, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean groupByAddress, boolean includeMempoolOutputs, boolean includeSpentMempoolOutputs) { + public WalletTransactionService(Wallet wallet, List utxoSelectors, List utxoFilters, List payments, List excludedChangeNodes, double feeRate, double longTermFeeRate, Long fee, Integer currentBlockHeight, boolean groupByAddress, boolean includeMempoolOutputs, boolean includeSpentMempoolOutputs) { this.wallet = wallet; this.utxoSelectors = utxoSelectors; this.utxoFilters = utxoFilters; this.payments = payments; + this.excludedChangeNodes = excludedChangeNodes; this.feeRate = feeRate; this.longTermFeeRate = longTermFeeRate; this.fee = fee; @@ -572,7 +576,7 @@ public class SendController extends WalletFormController implements Initializabl protected Task createTask() { return new Task<>() { protected WalletTransaction call() throws InsufficientFundsException { - return wallet.createWalletTransaction(utxoSelectors, utxoFilters, payments, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); + return wallet.createWalletTransaction(utxoSelectors, utxoFilters, payments, excludedChangeNodes, feeRate, longTermFeeRate, fee, currentBlockHeight, groupByAddress, includeMempoolOutputs, includeSpentMempoolOutputs); } }; } @@ -898,6 +902,7 @@ public class SendController extends WalletFormController implements Initializabl utxoSelectorProperty.setValue(null); utxoFilterProperty.setValue(null); includeSpentMempoolOutputsProperty.set(false); + excludedChangeNodes.clear(); walletTransactionProperty.setValue(null); createdWalletTransactionProperty.set(null); @@ -1137,6 +1142,14 @@ public class SendController extends WalletFormController implements Initializabl } } + @Subscribe + public void replaceChangeAddress(ReplaceChangeAddressEvent event) { + if(event.getWalletTransaction() == walletTransactionProperty.get()) { + excludedChangeNodes.add(event.getWalletTransaction().getChangeNode()); + updateTransaction(); + } + } + @Subscribe public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) { if(event.getWallet().equals(getWalletForm().getWallet())) { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java index 02a9e9cd..2c5fd7c2 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java @@ -157,7 +157,7 @@ public class TransactionsController extends WalletFormController implements Init private void logMessage(String logMessage) { if(logMessage != null) { - logMessage = logMessage.replace("m/", "/"); + logMessage = logMessage.replace("m/", "../"); String date = LOG_DATE_FORMAT.format(new Date()); String logLine = "\n" + date + " " + logMessage; Platform.runLater(() -> { diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css index 920d0b75..a5444493 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css @@ -89,7 +89,7 @@ -fx-stroke-dash-array: 5px 5px; } -#transactionDiagram .utxo-label .button { +#transactionDiagram .utxo-label .button, #transactionDiagram .replace-change-label .button { -fx-padding: 0; -fx-pref-height: 18; -fx-pref-width: 18; @@ -105,6 +105,10 @@ -fx-fill: -fx-text-base-color; } +#transactionDiagram .change-warning-icon { + -fx-text-fill: rgb(238, 210, 2); +} + #targetBlocks .track { -fx-background-color: -fx-shadow-highlight-color, linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),