refactor transaction diagram to dispatch on output wrapper types
What changed, and why it matters
This commit is a code cleanup in Sparrow Wallet's transaction diagram display. It changes how the wallet classifies and draws transaction outputs on screen, introducing new output wrapper types for silent-payment change and consolidation. There is no direct evidence in the commit that it fixes a security vulnerability; it appears to be a UI refactor with minor correctness improvements.
Treat as a normal refactor. If reviewing for security, verify that the new output wrapper construction in HeadersController preserves the existing transaction-fee and change-address semantics, and that no downstream code relies on the removed getOutputIndex method. No urgent action required.
Security signals we found
No security-relevant keywords in commit title or message
Refactor-only: no cryptographic, signing, or network changes
New output wrapper types may affect how change/consolidation outputs are rendered and labeled
Potential minor UI correctness issue addressed: previous code could misclassify or duplicate output rendering for silent payment change/consolidation
No input validation, deserialization, or privilege changes observed
Evidence from the diff
The patch refactors TransactionDiagram and related controllers to dispatch on WalletTransaction.Output subtypes (PaymentOutput, ChangeOutput, SilentPaymentChangeOutput, ConsolidationOutput, SilentPaymentConsolidationOutput, NonAddressOutput) instead of iterating over Payment and change maps separately. HeadersController now constructs these wrappers more consistently, including new SilentPaymentConsolidationOutput handling and a consolidated change path for single silent-payment wallets. The visible behavior changes are limited to labeling/tooltip text and diagram row sizing.
Changed components
TransactionDiagram.javaTransactionDiagramLabel.javaGlyphUtils.javaHeadersController.javaOutputController.javadrongo (referenced submodule dependency)Inspect captured patch +291 / −251
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java
index 3bfe6d5..3722cbd 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java
@@ -229,7 +229,7 @@ public class TransactionDiagram extends GridPane {
if(diagram.isExpanded()) {
List<Map<BlockTransactionHashIndex, WalletNode>> utxoSets = diagram.getDisplayedUtxoSets();
int maxSetSize = utxoSets.stream().mapToInt(Map::size).max().orElse(0);
- int maxRows = Math.max(maxSetSize * utxoSets.size(), walletTx.getPayments().size() + 2);
+ int maxRows = Math.max(maxSetSize * utxoSets.size(), diagram.getDisplayedOutputs().size() + 1);
double diagramHeight = Math.max(DIAGRAM_HEIGHT, Math.min(EXPANDED_DIAGRAM_HEIGHT, maxRows * ROW_HEIGHT));
diagram.setMinHeight(diagramHeight);
diagram.setMaxHeight(diagramHeight);
@@ -257,12 +257,12 @@ public class TransactionDiagram extends GridPane {
Pane txPane = getTransactionPane();
GridPane.setConstraints(txPane, 3, 0);
- List<Payment> displayedPayments = getDisplayedPayments();
+ List<WalletTransaction.Output> displayedOutputs = getDisplayedOutputs();
- Pane outputsLinesPane = getOutputsLines(displayedPayments);
+ Pane outputsLinesPane = getOutputsLines(displayedOutputs);
GridPane.setConstraints(outputsLinesPane, 4, 0);
- Pane outputsPane = getOutputsLabels(displayedPayments);
+ Pane outputsPane = getOutputsLabels(displayedOutputs);
GridPane.setConstraints(outputsPane, 5, 0);
getChildren().clear();
@@ -653,33 +653,48 @@ public class TransactionDiagram extends GridPane {
return value * (1.0 - scaleFactor) + additional;
}
- private List<Payment> getDisplayedPayments() {
- List<Payment> payments = walletTx.getPayments();
-
+ private List<WalletTransaction.Output> getDisplayedOutputs() {
+ List<WalletTransaction.Output> outputs = walletTx.getOutputs().stream().filter(o -> !(o instanceof WalletTransaction.NonAddressOutput)).toList();
int maxPayments = getMaxPayments();
- if(payments.size() > maxPayments) {
- List<Payment> displayedPayments = new ArrayList<>();
- List<Payment> additional = new ArrayList<>();
- for(Payment payment : payments) {
- if(displayedPayments.size() < maxPayments - 1) {
- displayedPayments.add(payment);
+ long paginableCount = outputs.stream().filter(this::isPaymentAndNotChange).count();
+
+ if(paginableCount <= maxPayments) {
+ return outputs;
+ }
+
+ List<WalletTransaction.Output> displayedOutputs = new ArrayList<>();
+ List<Payment> additional = new ArrayList<>();
+ int kept = 0;
+ int additionalIdx = 0;
+ for(WalletTransaction.Output output : outputs) {
+ if(isPaymentAndNotChange(output)) {
+ if(kept < maxPayments - 1) {
+ displayedOutputs.add(output);
+ kept++;
+ additionalIdx = displayedOutputs.size();
} else {
- additional.add(payment);
+ additional.add(output instanceof WalletTransaction.PaymentOutput po ? po.getPayment() : ((WalletTransaction.ConsolidationOutput)output).getWalletNodePayment());
}
+ } else {
+ displayedOutputs.add(output);
}
-
- displayedPayments.add(new AdditionalPayment(additional));
- return displayedPayments;
- } else {
- return payments;
}
+ Payment additionalPayment = new AdditionalPayment(additional);
+ TransactionOutput additionalOutput = new TransactionOutput(null, additionalPayment.getAmount(), new byte[0]);
+ displayedOutputs.add(additionalIdx, new WalletTransaction.PaymentOutput(additionalOutput, additionalPayment));
+
+ return displayedOutputs;
+ }
+
+ boolean isPaymentAndNotChange(WalletTransaction.Output output) {
+ return (output instanceof WalletTransaction.PaymentOutput && !(output instanceof WalletTransaction.SilentPaymentChangeOutput)) || output instanceof WalletTransaction.ConsolidationOutput;
}
private List<Payment> getUserPayments() {
return walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.DEFAULT || payment.getType() == Payment.Type.ANCHOR).toList();
}
- private Pane getOutputsLines(List<Payment> displayedPayments) {
+ private Pane getOutputsLines(List<WalletTransaction.Output> displayedOutputs) {
VBox pane = new VBox();
Group group = new Group();
VBox.setVgrow(group, Priority.ALWAYS);
@@ -694,10 +709,9 @@ public class TransactionDiagram extends GridPane {
double width = 140.0;
long sum = walletTx.getTotal();
- List<Long> values = walletTx.getOutputs().stream().filter(output -> !(output instanceof WalletTransaction.NonAddressOutput))
- .map(output -> output.getTransactionOutput().getValue()).collect(Collectors.toList());
+ List<Long> values = displayedOutputs.stream().map(o -> o.getTransactionOutput().getValue()).collect(Collectors.toList());
values.add(walletTx.getFee());
- int numOutputs = displayedPayments.size() + walletTx.getChangeMap().size() + walletTx.getSilentPaymentChangeOutputs().size() + 1;
+ int numOutputs = displayedOutputs.size() + 1;
for(int i = 1; i <= numOutputs; i++) {
CubicCurve curve = new CubicCurve();
curve.getStyleClass().add("output-line");
@@ -729,153 +743,23 @@ public class TransactionDiagram extends GridPane {
return pane;
}
- private Pane getOutputsLabels(List<Payment> displayedPayments) {
+ private Pane getOutputsLabels(List<WalletTransaction.Output> displayedOutputs) {
VBox outputsBox = new VBox();
outputsBox.setPadding(new Insets(0, 20, 0, 10));
outputsBox.setAlignment(Pos.BASELINE_LEFT);
outputsBox.getChildren().add(createSpacer());
List<OutputNode> outputNodes = new ArrayList<>();
- for(Payment payment : displayedPayments) {
- Glyph outputGlyph = GlyphUtils.getOutputGlyph(walletTx, payment);
- boolean labelledPayment = outputGlyph.getStyleClass().stream().anyMatch(style -> List.of("premix-icon", "badbank-icon", "whirlpoolfee-icon", "anchor-icon").contains(style)) || payment instanceof AdditionalPayment || payment.getLabel() != null;
- boolean addressLabel = payment.getLabel() == null || payment.getType() == Payment.Type.FAKE_MIX || payment.getType() == Payment.Type.MIX;
- Label recipientLabel = new Label(addressLabel ? payment.toString().substring(0, 8) + "..." : payment.getLabel(), outputGlyph);
- recipientLabel.getStyleClass().add("output-label");
- recipientLabel.getStyleClass().add(labelledPayment ? "payment-label" : "recipient-label");
- if(addressLabel) {
- recipientLabel.setSkin(new AddressLabelSkin(recipientLabel));
- }
- Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment);
- WalletNode toNode = payment instanceof WalletNodePayment walletNodePayment ? walletNodePayment.getWalletNode() : null;
- Wallet toBip47Wallet = getBip47SendWallet(payment);
- DnsPayment dnsPayment = DnsPaymentCache.getDnsPayment(payment);
- Tooltip recipientTooltip = new Tooltip((toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ")
- + getCoinValue(payment.getAmount()) + " to "
- + (payment instanceof AdditionalPayment ? (isExpanded() ? "\n" : "(click to expand)\n") + payment : (toWallet == null ? (dnsPayment == null ? (payment.getLabel() == null ? (toNode != null ? toNode : (toBip47Wallet == null ? "external address" : toBip47Wallet.getDisplayName())) : payment.getLabel()) : dnsPayment.toString()) : toWallet.getFullDisplayName()) + "\n" + payment.getDisplayAddress())
- + (walletTx.isDuplicateAddress(payment) ? " (Duplicate)" : ""));
- recipientTooltip.getStyleClass().add("recipient-label");
- recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
- recipientTooltip.setShowDuration(Duration.INDEFINITE);
- recipientTooltip.setWrapText(true);
- recipientTooltip.setSkin(new AddressTooltipSkin(recipientTooltip));
- Window activeWindow = AppServices.getActiveWindow();
- if(activeWindow != null) {
- recipientTooltip.setMaxWidth(activeWindow.getWidth());
- }
- recipientLabel.setTooltip(recipientTooltip);
- HBox paymentBox = new HBox();
- paymentBox.setAlignment(Pos.CENTER_LEFT);
- paymentBox.getChildren().add(recipientLabel);
- if(isExpanded()) {
- recipientLabel.setMinWidth(120);
- Region region = new Region();
- region.setMinWidth(20);
- HBox.setHgrow(region, Priority.ALWAYS);
- CoinLabel amountLabel = new CoinLabel();
- amountLabel.setValue(payment.getAmount());
- amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
- paymentBox.getChildren().addAll(region, amountLabel);
- }
-
- if(payment instanceof SilentPayment silentPayment) {
- outputNodes.add(new OutputNode(paymentBox, silentPayment.isAddressComputed() ? silentPayment.getAddress() : null, payment.getAmount(), null, silentPayment.getSilentPaymentAddress()));
- } else {
- Wallet bip47Wallet = toWallet != null && toWallet.isBip47() ? toWallet : (toBip47Wallet != null && toBip47Wallet.isBip47() ? toBip47Wallet : null);
- PaymentCode paymentCode = bip47Wallet == null ? null : bip47Wallet.getKeystores().getFirst().getExternalPaymentCode();
- outputNodes.add(new OutputNode(paymentBox, payment.getAddress(), payment.getAmount(), paymentCode, null));
+ for(WalletTransaction.Output output : displayedOutputs) {
+ if(output instanceof WalletTransaction.SilentPaymentChangeOutput spChangeOutput) {
+ outputNodes.add(buildSpChangeNode(spChangeOutput));
+ } else if(output instanceof WalletTransaction.ChangeOutput changeOutput) {
+ outputNodes.add(buildHdChangeNode(changeOutput));
+ } else if(output instanceof WalletTransaction.PaymentOutput || output instanceof WalletTransaction.ConsolidationOutput) {
+ outputNodes.add(buildPaymentNode(output));
}
}
- Set<Integer> seenIndexes = new HashSet<>();
- for(Map.Entry<WalletNode, Long> changeEntry : walletTx.getChangeMap().entrySet()) {
- WalletNode changeNode = changeEntry.getKey();
- boolean overGapLimit = walletTx.getWallet().getPolicyType() != PolicyType.SINGLE_SP &&
- (changeNode.getIndex() - walletTx.getWallet().getFreshNode(KeyPurpose.CHANGE).getIndex()) > walletTx.getWallet().getGapLimit();
-
- HBox actionBox = new HBox();
- actionBox.setAlignment(Pos.CENTER_LEFT);
- Address changeAddress = walletTx.getChangeAddress(changeNode);
- String changeDesc = changeAddress.toString().substring(0, 8) + "...";
- Label changeLabel = new Label(changeDesc, overGapLimit ? getChangeWarningGlyph() : getChangeGlyph());
- changeLabel.getStyleClass().addAll("output-label", "change-label");
- changeLabel.setSkin(new AddressLabelSkin(changeLabel));
- Tooltip changeTooltip = new Tooltip("Change of " + getCoinValue(changeEntry.getValue()) + " to " + changeNode + "\n" + walletTx.getChangeAddress(changeNode).toString() + (overGapLimit ? "\nAddress is beyond the gap limit!" : ""));
- changeTooltip.getStyleClass().add("change-label");
- changeTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
- changeTooltip.setShowDuration(Duration.INDEFINITE);
- changeTooltip.setSkin(new AddressTooltipSkin(changeTooltip));
- changeLabel.setTooltip(changeTooltip);
- actionBox.getChildren().add(changeLabel);
-
- if(!isFinal()) {
- 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().add(replaceChangeLabel);
- }
-
- if(isExpanded()) {
- changeLabel.setMinWidth(120);
- Region region = new Region();
- region.setMinWidth(20);
- HBox.setHgrow(region, Priority.ALWAYS);
- CoinLabel amountLabel = new CoinLabel();
- amountLabel.setValue(changeEntry.getValue());
- amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
- actionBox.getChildren().addAll(region, amountLabel);
- }
-
- int changeIndex = outputNodes.size();
- if(isFinal()) {
- changeIndex = getOutputIndex(changeAddress, changeEntry.getValue(), seenIndexes);
- seenIndexes.add(changeIndex);
- if(changeIndex > outputNodes.size()) {
- changeIndex = outputNodes.size();
- }
- }
- outputNodes.add(changeIndex, new OutputNode(actionBox, changeAddress, changeEntry.getValue()));
- }
-
- for(WalletTransaction.SilentPaymentChangeOutput spChangeOutput : walletTx.getSilentPaymentChangeOutputs()) {
- HBox actionBox = new HBox();
- actionBox.setAlignment(Pos.CENTER_LEFT);
- SilentPayment silentPayment = spChangeOutput.getSilentPayment();
- SilentPaymentAddress spAddress = silentPayment.getSilentPaymentAddress();
- Label changeLabel = new Label("Change", getChangeGlyph());
- changeLabel.getStyleClass().addAll("output-label", "change-label");
- changeLabel.setSkin(new AddressLabelSkin(changeLabel));
- Tooltip changeTooltip = new Tooltip("Change of " + getCoinValue(silentPayment.getAmount()) + "\n" + spAddress);
- changeTooltip.getStyleClass().add("change-label");
- changeTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
- changeTooltip.setShowDuration(Duration.INDEFINITE);
- changeTooltip.setSkin(new AddressTooltipSkin(changeTooltip));
- changeLabel.setTooltip(changeTooltip);
- actionBox.getChildren().add(changeLabel);
-
- if(isExpanded()) {
- changeLabel.setMinWidth(120);
- Region region = new Region();
- region.setMinWidth(20);
- HBox.setHgrow(region, Priority.ALWAYS);
- CoinLabel amountLabel = new CoinLabel();
- amountLabel.setValue(silentPayment.getAmount());
- amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
- actionBox.getChildren().addAll(region, amountLabel);
- }
-
- outputNodes.add(new OutputNode(actionBox, silentPayment.isAddressComputed() ? silentPayment.getAddress() : null, silentPayment.getAmount(), null, spAddress));
- }
-
for(OutputNode outputNode : outputNodes) {
outputsBox.getChildren().add(outputNode.outputLabel);
outputsBox.getChildren().add(createSpacer());
@@ -920,6 +804,143 @@ public class TransactionDiagram extends GridPane {
return outputsBox;
}
+ private OutputNode buildPaymentNode(WalletTransaction.Output output) {
+ Payment payment = output instanceof WalletTransaction.PaymentOutput po ? po.getPayment() : ((WalletTransaction.ConsolidationOutput)output).getWalletNodePayment();
+ boolean spConsolidation = output instanceof WalletTransaction.SilentPaymentConsolidationOutput;
+
+ Glyph outputGlyph = GlyphUtils.getOutputGlyph(walletTx, payment);
+ boolean labelledPayment = outputGlyph.getStyleClass().stream().anyMatch(style -> List.of("premix-icon", "badbank-icon", "whirlpoolfee-icon", "anchor-icon").contains(style)) || payment instanceof AdditionalPayment || payment.getLabel() != null;
+ boolean addressLabel = payment.getLabel() == null || payment.getType() == Payment.Type.MIX;
+ Label recipientLabel = new Label(payment.getType() == Payment.Type.FAKE_MIX ? payment.getType().toDisplayString() : (addressLabel ? payment.toString().substring(0, 8) + "..." : payment.getLabel()), outputGlyph);
+ recipientLabel.getStyleClass().add("output-label");
+ recipientLabel.getStyleClass().add(labelledPayment ? "payment-label" : "recipient-label");
+ if(addressLabel) {
+ recipientLabel.setSkin(new AddressLabelSkin(recipientLabel));
+ }
+ Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment);
+ WalletNode toNode = payment instanceof WalletNodePayment walletNodePayment ? walletNodePayment.getWalletNode() : null;
+ Wallet toBip47Wallet = getBip47SendWallet(payment);
+ DnsPayment dnsPayment = DnsPaymentCache.getDnsPayment(payment);
+ Tooltip recipientTooltip = new Tooltip((toNode != null || spConsolidation ? "Consolidate " : (toWallet == null ? "Pay " : "Receive "))
+ + getCoinValue(payment.getAmount()) + " to "
+ + (payment instanceof AdditionalPayment ? (isExpanded() ? "\n" : "(click to expand)\n") + payment : (toNode != null ? toNode : (spConsolidation ? walletTx.getWallet().getFullDisplayName() : (toWallet == null ? (dnsPayment == null ? (payment.getLabel() == null ? (toBip47Wallet == null ? "external address" : toBip47Wallet.getDisplayName()) : payment.getLabel()) : dnsPayment.toString()) : toWallet.getFullDisplayName()))) + "\n" + payment.getDisplayAddress())
+ + (walletTx.isDuplicateAddress(payment) ? " (Duplicate)" : ""));
+ recipientTooltip.getStyleClass().add("recipient-label");
+ recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
+ recipientTooltip.setShowDuration(Duration.INDEFINITE);
+ recipientTooltip.setWrapText(true);
+ recipientTooltip.setSkin(new AddressTooltipSkin(recipientTooltip));
+ Window activeWindow = AppServices.getActiveWindow();
+ if(activeWindow != null) {
+ recipientTooltip.setMaxWidth(activeWindow.getWidth());
+ }
+ recipientLabel.setTooltip(recipientTooltip);
+ HBox paymentBox = new HBox();
+ paymentBox.setAlignment(Pos.CENTER_LEFT);
+ paymentBox.getChildren().add(recipientLabel);
+ if(isExpanded()) {
+ recipientLabel.setMinWidth(120);
+ Region region = new Region();
+ region.setMinWidth(20);
+ HBox.setHgrow(region, Priority.ALWAYS);
+ CoinLabel amountLabel = new CoinLabel();
+ amountLabel.setValue(payment.getAmount());
+ amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
+ paymentBox.getChildren().addAll(region, amountLabel);
+ }
+
+ if(payment instanceof SilentPayment silentPayment) {
+ return new OutputNode(paymentBox, silentPayment.isAddressComputed() ? silentPayment.getAddress() : null, payment.getAmount(), null, silentPayment.getSilentPaymentAddress());
+ }
+
+ Wallet bip47Wallet = toWallet != null && toWallet.isBip47() ? toWallet : (toBip47Wallet != null && toBip47Wallet.isBip47() ? toBip47Wallet : null);
+ PaymentCode paymentCode = bip47Wallet == null ? null : bip47Wallet.getKeystores().getFirst().getExternalPaymentCode();
+
+ return new OutputNode(paymentBox, payment.getAddress(), payment.getAmount(), paymentCode, null);
+ }
+
+ private OutputNode buildHdChangeNode(WalletTransaction.ChangeOutput changeOutput) {
+ WalletNode changeNode = changeOutput.getWalletNode();
+ long value = changeOutput.getValue();
+ boolean overGapLimit = walletTx.getWallet().getPolicyType() != PolicyType.SINGLE_SP &&
+ (changeNode.getIndex() - walletTx.getWallet().getFreshNode(KeyPurpose.CHANGE).getIndex()) > walletTx.getWallet().getGapLimit();
+
+ HBox actionBox = new HBox();
+ actionBox.setAlignment(Pos.CENTER_LEFT);
+ Address changeAddress = walletTx.getChangeAddress(changeNode);
+ String changeDesc = changeAddress.toString().substring(0, 8) + "...";
+ Label changeLabel = new Label(changeDesc, overGapLimit ? getChangeWarningGlyph() : getChangeGlyph());
+ changeLabel.getStyleClass().addAll("output-label", "change-label");
+ changeLabel.setSkin(new AddressLabelSkin(changeLabel));
+ Tooltip changeTooltip = new Tooltip("Change of " + getCoinValue(value) + " to " + changeNode + "\n" + changeAddress.toString() + (overGapLimit ? "\nAddress is beyond the gap limit!" : ""));
+ changeTooltip.getStyleClass().add("change-label");
+ changeTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
+ changeTooltip.setShowDuration(Duration.INDEFINITE);
+ changeTooltip.setSkin(new AddressTooltipSkin(changeTooltip));
+ changeLabel.setTooltip(changeTooltip);
+ actionBox.getChildren().add(changeLabel);
+
+ if(!isFinal()) {
+ 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().add(replaceChangeLabel);
+ }
+
+ if(isExpanded()) {
+ changeLabel.setMinWidth(120);
+ Region region = new Region();
+ region.setMinWidth(20);
+ HBox.setHgrow(region, Priority.ALWAYS);
+ CoinLabel amountLabel = new CoinLabel();
+ amountLabel.setValue(value);
+ amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
+ actionBox.getChildren().addAll(region, amountLabel);
+ }
+
+ return new OutputNode(actionBox, changeAddress, value);
+ }
+
+ private OutputNode buildSpChangeNode(WalletTransaction.SilentPaymentChangeOutput spChangeOutput) {
+ SilentPayment silentPayment = spChangeOutput.getSilentPayment();
+ SilentPaymentAddress spAddress = silentPayment.getSilentPaymentAddress();
+
+ HBox actionBox = new HBox();
+ actionBox.setAlignment(Pos.CENTER_LEFT);
+ Label changeLabel = new Label("Change", getChangeGlyph());
+ changeLabel.getStyleClass().addAll("output-label", "payment-label");
+ changeLabel.setSkin(new AddressLabelSkin(changeLabel));
+ Tooltip changeTooltip = new Tooltip("Change of " + getCoinValue(silentPayment.getAmount()) + "\n" + silentPayment.getDisplayAddress());
+ changeTooltip.getStyleClass().add("change-label");
+ changeTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
+ changeTooltip.setShowDuration(Duration.INDEFINITE);
+ changeTooltip.setSkin(new AddressTooltipSkin(changeTooltip));
+ changeLabel.setTooltip(changeTooltip);
+ actionBox.getChildren().add(changeLabel);
+
+ if(isExpanded()) {
+ changeLabel.setMinWidth(120);
+ Region region = new Region();
+ region.setMinWidth(20);
+ HBox.setHgrow(region, Priority.ALWAYS);
+ CoinLabel amountLabel = new CoinLabel();
+ amountLabel.setValue(silentPayment.getAmount());
+ amountLabel.setMinWidth(TextUtils.computeTextWidth(amountLabel.getFont(), amountLabel.getText(), 0.0D) + 2);
+ actionBox.getChildren().addAll(region, amountLabel);
+ }
+
+ return new OutputNode(actionBox, silentPayment.isAddressComputed() ? silentPayment.getAddress() : null, silentPayment.getAmount(), null, spAddress);
+ }
+
private Pane getTransactionPane() {
VBox txPane = new VBox();
txPane.setPadding(new Insets(0, 5, 0, 5));
@@ -1001,8 +1022,8 @@ public class TransactionDiagram extends GridPane {
}
private String getDiagramTitle() {
- if(!isFinal() && walletTx.getPayments().size() > 0 && walletTx.getPayments().get(0).getLabel() != null) {
- return walletTx.getPayments().get(0).getLabel();
+ if(!isFinal() && !walletTx.getPayments().isEmpty() && walletTx.getPayments().getFirst().getLabel() != null) {
+ return walletTx.getPayments().getFirst().getLabel();
} else {
return "[" + walletTx.getTransaction().getTxId().toString().substring(0, 6) + "]";
}
@@ -1054,15 +1075,6 @@ public class TransactionDiagram extends GridPane {
return spacer;
}
- private int getOutputIndex(Address address, long amount, Collection<Integer> seenIndexes) {
- List<TransactionOutput> addressOutputs = walletTx.getOutputs().stream().filter(output -> !(output instanceof WalletTransaction.NonAddressOutput))
- .map(WalletTransaction.Output::getTransactionOutput).collect(Collectors.toList());
- TransactionOutput output = addressOutputs.stream()
- .filter(txOutput -> address.equals(txOutput.getScript().getToAddress()) && txOutput.getValue() == amount && !seenIndexes.contains(txOutput.getIndex()))
- .findFirst().orElseThrow();
- return addressOutputs.indexOf(output);
- }
-
private Wallet getBip47SendWallet(Payment payment) {
if(walletTx.getWallet() != null) {
for(Wallet childWallet : walletTx.getWallet().getChildWallets()) {
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java
index 56010c3..462221a 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagramLabel.java
@@ -96,23 +96,38 @@ public class TransactionDiagramLabel extends HBox {
outputLabels.add(remixOutputLabel);
}
} else {
- List<Payment> payments = walletTx.getExternalPayments().stream().filter(payment -> payment.getType() == Payment.Type.DEFAULT).collect(Collectors.toList());
- List<OutputLabel> paymentLabels = payments.stream().map(payment -> getOutputLabel(transactionDiagram, payment)).collect(Collectors.toList());
+ List<OutputLabel> externalLabels = new ArrayList<>();
+ List<OutputLabel> consolidationLabels = new ArrayList<>();
+ List<OutputLabel> mixLabels = new ArrayList<>();
+ for(WalletTransaction.Output output : walletTx.getOutputs()) {
+ if(transactionDiagram.isPaymentAndNotChange(output)) {
+ Payment payment = output instanceof WalletTransaction.PaymentOutput po ? po.getPayment() : ((WalletTransaction.ConsolidationOutput)output).getWalletNodePayment();
+ if(payment.getType() == Payment.Type.MIX || payment.getType() == Payment.Type.FAKE_MIX) {
+ mixLabels.add(getOutputLabel(transactionDiagram, output));
+ } else if(payment.getType() == Payment.Type.DEFAULT || payment.getType() == Payment.Type.ANCHOR) {
+ if(output instanceof WalletTransaction.ConsolidationOutput || output instanceof WalletTransaction.SilentPaymentConsolidationOutput) {
+ consolidationLabels.add(getOutputLabel(transactionDiagram, output));
+ } else {
+ externalLabels.add(getOutputLabel(transactionDiagram, output));
+ }
+ }
+ }
+ }
if(walletTx.getSelectedUtxos().values().stream().allMatch(Objects::isNull)) {
- paymentLabels.sort(Comparator.comparingInt(paymentLabel -> (paymentLabel.text.startsWith("Receive") ? 0 : 1)));
+ externalLabels.sort(Comparator.comparingInt(paymentLabel -> (paymentLabel.text.startsWith("Receive") ? 0 : 1)));
}
- outputLabels.addAll(paymentLabels);
-
- List<Payment> consolidations = walletTx.getWalletNodePayments().stream().filter(payment -> payment.getType() == Payment.Type.DEFAULT).collect(Collectors.toList());
- outputLabels.addAll(consolidations.stream().map(consolidation -> getOutputLabel(transactionDiagram, consolidation)).collect(Collectors.toList()));
-
- List<Payment> mixes = walletTx.getPayments().stream().filter(payment -> payment.getType() == Payment.Type.MIX || payment.getType() == Payment.Type.FAKE_MIX).collect(Collectors.toList());
- outputLabels.addAll(mixes.stream().map(payment -> getOutputLabel(transactionDiagram, payment)).collect(Collectors.toList()));
+ outputLabels.addAll(externalLabels);
+ outputLabels.addAll(consolidationLabels);
+ outputLabels.addAll(mixLabels);
}
- Map<WalletNode, Long> changeMap = walletTx.getChangeMap();
- outputLabels.addAll(changeMap.entrySet().stream().map(changeEntry -> getOutputLabel(transactionDiagram, changeEntry)).collect(Collectors.toList()));
- outputLabels.addAll(walletTx.getSilentPaymentChangeOutputs().stream().map(spChange -> getOutputLabel(transactionDiagram, spChange)).collect(Collectors.toList()));
+ for(WalletTransaction.Output output : walletTx.getOutputs()) {
+ if(output instanceof WalletTransaction.SilentPaymentChangeOutput spChange) {
+ outputLabels.add(getOutputLabel(transactionDiagram, spChange));
+ } else if(output instanceof WalletTransaction.ChangeOutput changeOutput) {
+ outputLabels.add(getOutputLabel(transactionDiagram, changeOutput));
+ }
+ }
OutputLabel feeOutputLabel = getFeeOutputLabel(transactionDiagram);
if(feeOutputLabel != null) {
@@ -201,22 +216,24 @@ public class TransactionDiagramLabel extends HBox {
return getOutputLabel(glyph, text);
}
- private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Payment payment) {
+ private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, WalletTransaction.Output output) {
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
+ Payment payment = output instanceof WalletTransaction.PaymentOutput po ? po.getPayment() : ((WalletTransaction.ConsolidationOutput)output).getWalletNodePayment();
+ boolean spConsolidation = output instanceof WalletTransaction.SilentPaymentConsolidationOutput;
Wallet toWallet = walletTx.getToWallet(AppServices.get().getOpenWallets().keySet(), payment);
WalletNode toNode = payment instanceof WalletNodePayment walletNodePayment ? walletNodePayment.getWalletNode() : null;
- Glyph glyph = GlyphUtils.getOutputGlyph(transactionDiagram.getWalletTransaction(), payment);
- String text = (toWallet == null ? (toNode != null ? "Consolidate " : "Pay ") : "Receive ") + transactionDiagram.getCoinValue(payment.getAmount()) + " to " + payment;
+ Glyph glyph = GlyphUtils.getOutputGlyph(walletTx, payment);
+ String text = (toNode != null || spConsolidation ? "Consolidate " : (toWallet == null ? "Pay " : "Receive ")) + transactionDiagram.getCoinValue(payment.getAmount()) + " to " + payment;
return getOutputLabel(glyph, text);
}
- private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, Map.Entry<WalletNode, Long> changeEntry) {
+ private OutputLabel getOutputLabel(TransactionDiagram transactionDiagram, WalletTransaction.ChangeOutput changeOutput) {
WalletTransaction walletTx = transactionDiagram.getWalletTransaction();
Glyph glyph = GlyphUtils.getChangeGlyph();
- String text = "Change of " + transactionDiagram.getCoinValue(changeEntry.getValue()) + " to " + walletTx.getChangeAddress(changeEntry.getKey()).toString();
+ String text = "Change of " + transactionDiagram.getCoinValue(changeOutput.getValue()) + " to " + walletTx.getChangeAddress(changeOutput.getWalletNode()).toString();
return getOutputLabel(glyph, text);
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java
index ac29d81..eee93a8 100644
--- a/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java
+++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/GlyphUtils.java
@@ -1,7 +1,6 @@
package com.sparrowwallet.sparrow.glyphfont;
import com.sparrowwallet.drongo.wallet.Payment;
-import com.sparrowwallet.drongo.wallet.WalletNodePayment;
import com.sparrowwallet.drongo.wallet.WalletTransaction;
import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.control.TransactionDiagram;
@@ -16,7 +15,7 @@ public class GlyphUtils {
return getFakeMixGlyph();
} else if(payment.getType().equals(Payment.Type.ANCHOR)) {
return getAnchorGlyph();
- } else if(payment instanceof WalletNodePayment) {
+ } else if(walletTx.isConsolidation(payment)) {
return getConsolidationGlyph();
} else if(walletTx.isPremixSend(payment)) {
return getPremixGlyph();
diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
index e1c2fe7..c771cd4 100644
--- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
@@ -642,17 +642,18 @@ public class HeadersController extends TransactionFormController implements Init
private WalletTransaction getWalletTransaction(Map<Sha256Hash, BlockTransaction> inputTransactions) {
Wallet wallet = getWalletFromTransactionInputs();
+ Transaction transaction = headersForm.getTransaction();
if(wallet != null) {
Map<Sha256Hash, BlockTransaction> walletInputTransactions = inputTransactions;
if(walletInputTransactions == null) {
- Set<Sha256Hash> refs = headersForm.getTransaction().getInputs().stream().map(txInput -> txInput.getOutpoint().getHash()).collect(Collectors.toSet());
+ Set<Sha256Hash> refs = transaction.getInputs().stream().map(txInput -> txInput.getOutpoint().getHash()).collect(Collectors.toSet());
walletInputTransactions = wallet.getWalletTransactions();
walletInputTransactions.keySet().retainAll(refs);
}
Map<BlockTransactionHashIndex, WalletNode> selectedTxos = new LinkedHashMap<>();
Map<BlockTransactionHashIndex, WalletNode> walletTxos = wallet.getWalletTxos();
- for(TransactionInput txInput : headersForm.getTransaction().getInputs()) {
+ for(TransactionInput txInput : transaction.getInputs()) {
BlockTransactionHashIndex selectedTxo = walletTxos.keySet().stream().filter(txo -> txInput.getOutpoint().getHash().equals(txo.getHash()) && txInput.getOutpoint().getIndex() == txo.getIndex())
.findFirst().orElse(getBlockTransactionInput(walletInputTransactions, txInput));
selectedTxos.put(selectedTxo, walletTxos.get(selectedTxo));
@@ -663,73 +664,86 @@ public class HeadersController extends TransactionFormController implements Init
Map<WalletNode, Long> changeMap = new LinkedHashMap<>();
Map<Script, WalletNode> receiveOutputScripts = wallet.getWalletOutputScripts(KeyPurpose.RECEIVE);
Map<Script, WalletNode> changeOutputScripts = wallet.getWalletOutputScripts(wallet.getChangeKeyPurpose());
- for(TransactionOutput txOutput : headersForm.getTransaction().getOutputs()) {
+ for(TransactionOutput txOutput : transaction.getOutputs()) {
WalletNode changeNode = changeOutputScripts.get(txOutput.getScript());
if(changeNode != null) {
- if(headersForm.getTransaction().getOutputs().size() == 4 && headersForm.getTransaction().getOutputs().stream().anyMatch(txo -> txo != txOutput && txo.getValue() == txOutput.getValue())) {
- if(selectedTxos.values().stream().allMatch(Objects::nonNull)) {
- payments.add(new WalletNodePayment(changeNode, ".." + changeNode + " (Fake Mix)", txOutput.getValue(), false, Payment.Type.FAKE_MIX));
- } else {
- payments.add(new WalletNodePayment(changeNode, ".." + changeNode + " (Mix)", txOutput.getValue(), false, Payment.Type.MIX));
- }
+ if(transaction.getOutputs().size() == 4 && transaction.getOutputs().stream().anyMatch(txo -> txo != txOutput && txo.getValue() == txOutput.getValue())) {
+ Payment.Type type = selectedTxos.values().stream().allMatch(Objects::nonNull) ? Payment.Type.FAKE_MIX : Payment.Type.MIX;
+ WalletNodePayment mixPayment = new WalletNodePayment(changeNode, ".." + changeNode + " (" + type.toDisplayString() + ")", txOutput.getValue(), false, type);
+ payments.add(mixPayment);
+ outputs.add(new WalletTransaction.ConsolidationOutput(txOutput, mixPayment, txOutput.getValue()));
} else {
- if(changeMap.containsKey(changeNode)) {
- payments.add(new WalletNodePayment(changeNode, headersForm.getName(), txOutput.getValue(), false, Payment.Type.DEFAULT));
- } else {
+ if(!changeMap.containsKey(changeNode)) {
changeMap.put(changeNode, txOutput.getValue());
}
+ outputs.add(new WalletTransaction.ChangeOutput(txOutput, changeNode, txOutput.getValue()));
}
- outputs.add(new WalletTransaction.ChangeOutput(txOutput, changeNode, txOutput.getValue()));
} else {
- Payment.Type paymentType = Payment.Type.DEFAULT;
- Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
- Wallet premixWallet = masterWallet.getChildWallet(StandardAccount.WHIRLPOOL_PREMIX);
- if(premixWallet != null && headersForm.getTransaction().getOutputs().stream().anyMatch(premixWallet::isWalletTxo) && txOutput.getIndex() == 1) {
- paymentType = Payment.Type.WHIRLPOOL_FEE;
- }
-
- BlockTransactionHashIndex receivedTxo = walletTxos.keySet().stream().filter(txo -> txo.getHash().equals(txOutput.getHash()) && txo.getIndex() == txOutput.getIndex()).findFirst().orElse(null);
- String label = headersForm.getName() == null || (headersForm.getName().startsWith("[") && headersForm.getName().endsWith("]") && headersForm.getName().length() == 8) ? null : headersForm.getName();
- Address address = txOutput.getScript().getToAddress();
- WalletNode receiveNode = receiveOutputScripts.get(txOutput.getScript());
- SilentPaymentAddress silentPaymentAddress = headersForm.getSilentPaymentAddress(txOutput);
- label = receivedTxo != null ? receivedTxo.getLabel() : label;
- if(address != null || silentPaymentAddress != null) {
- Payment payment;
- if(silentPaymentAddress != null) {
- payment = new SilentPayment(silentPaymentAddress, address, label, txOutput.getValue(), false);
- } else if(receiveNode != null) {
- payment = new WalletNodePayment(receiveNode, label, txOutput.getValue(), false, paymentType);
+ Address toAddress = txOutput.getScript().getToAddress();
+ SilentPaymentAddress spAddress = headersForm.getSilentPaymentAddress(txOutput);
+ if(spAddress != null && wallet.getPolicyType() == PolicyType.SINGLE_SP && wallet.getSilentPaymentScanAddress().getChangeAddress().getSilentPaymentAddress().equals(spAddress)) {
+ if(transaction.getOutputs().size() == 4 && transaction.getOutputs().stream().anyMatch(txo -> txo != txOutput && txo.getValue() == txOutput.getValue())) {
+ Payment.Type type = selectedTxos.values().stream().allMatch(Objects::nonNull) ? Payment.Type.FAKE_MIX : Payment.Type.MIX;
+ SilentPayment mixPayment = new SilentPayment(spAddress, toAddress, "(" + type.toDisplayString() + ")", txOutput.getValue(), false, type);
+ payments.add(mixPayment);
+ outputs.add(new WalletTransaction.SilentPaymentConsolidationOutput(txOutput, mixPayment));
} else {
- payment = new Payment(address, label, txOutput.getValue(), false, paymentType);
+ SilentPayment changePayment = new SilentPayment(spAddress, toAddress, null, txOutput.getValue(), false);
+ outputs.add(new WalletTransaction.SilentPaymentChangeOutput(txOutput, changePayment));
}
- WalletTransaction createdTx = AppServices.get().getCreatedTransaction(selectedTxos.keySet());
- if(createdTx != null) {
- Optional<String> optLabel = createdTx.getPayments().stream()
- .filter(pymt -> (pymt instanceof SilentPayment silentPayment ? silentPayment.getSilentPaymentAddress().equals(silentPaymentAddress) :
- pymt.getAddress().equals(payment.getAddress())) && pymt.getAmount() == payment.getAmount()).map(Payment::getLabel).findFirst();
- if(optLabel.isPresent()) {
- payment.setLabel(optLabel.get());
- outputIndexLabels.put(txOutput.getIndex(), optLabel.get());
- }
+ } else {
+ Payment.Type paymentType = Payment.Type.DEFAULT;
+ Wallet masterWallet = wallet.isMasterWallet() ? wallet : wallet.getMasterWallet();
+ Wallet premixWallet = masterWallet.getChildWallet(StandardAccount.WHIRLPOOL_PREMIX);
+ if(premixWallet != null && transaction.getOutputs().stream().anyMatch(premixWallet::isWalletTxo) && txOutput.getIndex() == 1) {
+ paymentType = Payment.Type.WHIRLPOOL_FEE;
}
- payments.add(payment);
- if(payment instanceof SilentPayment silentPayment) {
- outputs.add(new WalletTransaction.SilentPaymentOutput(txOutput, silentPayment));
- } else if(payment instanceof WalletNodePayment walletNodePayment) {
- outputs.add(new WalletTransaction.ConsolidationOutput(txOutput, walletNodePayment, walletNodePayment.getAmount()));
+
+ BlockTransactionHashIndex receivedTxo = walletTxos.keySet().stream().filter(txo -> txo.getHash().equals(txOutput.getHash()) && txo.getIndex() == txOutput.getIndex()).findFirst().orElse(null);
+ String label = headersForm.getName() == null || (headersForm.getName().startsWith("[") && headersForm.getName().endsWith("]") && headersForm.getName().length() == 8) ? null : headersForm.getName();
+ WalletNode receiveNode = receiveOutputScripts.get(txOutput.getScript());
+ label = receivedTxo != null ? receivedTxo.getLabel() : label;
+ if(toAddress != null || spAddress != null) {
+ Payment payment;
+ if(spAddress != null) {
+ payment = new SilentPayment(spAddress, toAddress, label, txOutput.getValue(), false);
+ } else if(receiveNode != null) {
+ payment = new WalletNodePayment(receiveNode, label, txOutput.getValue(), false, paymentType);
+ } else {
+ payment = new Payment(toAddress, label, txOutput.getValue(), false, paymentType);
+ }
+ WalletTransaction createdTx = AppServices.get().getCreatedTransaction(selectedTxos.keySet());
+ if(createdTx != null) {
+ Optional<String> optLabel = createdTx.getPayments().stream()
+ .filter(pymt -> (pymt instanceof SilentPayment silentPayment ? silentPayment.getSilentPaymentAddress().equals(spAddress) :
+ pymt.getAddress().equals(payment.getAddress())) && pymt.getAmount() == payment.getAmount()).map(Payment::getLabel).findFirst();
+ if(optLabel.isPresent()) {
+ payment.setLabel(optLabel.get());
+ outputIndexLabels.put(txOutput.getIndex(), optLabel.get());
+ }
+ }
+ payments.add(payment);
+ if(payment instanceof SilentPayment silentPayment) {
+ if(wallet.getPolicyType() == PolicyType.SINGLE_SP && wallet.getSilentPaymentScanAddress().getSilentPaymentAddress().equals(spAddress)) {
+ outputs.add(new WalletTransaction.SilentPaymentConsolidationOutput(txOutput, silentPayment));
+ } else {
+ outputs.add(new WalletTransaction.SilentPaymentOutput(txOutput, silentPayment));
+ }
+ } else if(payment instanceof WalletNodePayment walletNodePayment) {
+ outputs.add(new WalletTransaction.ConsolidationOutput(txOutput, walletNodePayment, walletNodePayment.getAmount()));
+ } else {
+ outputs.add(new WalletTransaction.PaymentOutput(txOutput, payment));
+ }
} else {
- outputs.add(new WalletTransaction.PaymentOutput(txOutput, payment));
+ outputs.add(new WalletTransaction.NonAddressOutput(txOutput));
}
- } else {
- outputs.add(new WalletTransaction.NonAddressOutput(txOutput));
}
}
}
- return new WalletTransaction(wallet, headersForm.getTransaction(), Collections.emptyList(), List.of(selectedTxos), payments, outputs, changeMap, fee.getValue(), walletInputTransactions);
+ return new WalletTransaction(wallet, transaction, Collections.emptyList(), List.of(selectedTxos), payments, outputs, changeMap, fee.getValue(), walletInputTransactions);
} else {
- Map<BlockTransactionHashIndex, WalletNode> selectedTxos = headersForm.getTransaction().getInputs().stream()
+ Map<BlockTransactionHashIndex, WalletNode> selectedTxos = transaction.getInputs().stream()
.collect(Collectors.toMap(txInput -> getBlockTransactionInput(inputTransactions, txInput),
txInput -> new WalletNode("m/0"),
(u, v) -> { throw new IllegalStateException("Duplicate TXOs"); },
@@ -738,7 +752,7 @@ public class HeadersController extends TransactionFormController implements Init
List<Payment> payments = new ArrayList<>();
List<WalletTransaction.Output> outputs = new ArrayList<>();
- for(TransactionOutput txOutput : headersForm.getTransaction().getOutputs()) {
+ for(TransactionOutput txOutput : transaction.getOutputs()) {
Address address = txOutput.getScript().getToAddress();
SilentPaymentAddress silentPaymentAddress = headersForm.getSilentPaymentAddress(txOutput);
BlockTransactionHashIndex receivedTxo = getBlockTransactionOutput(txOutput);
@@ -755,7 +769,7 @@ public class HeadersController extends TransactionFormController implements Init
}
}
- return new WalletTransaction(null, headersForm.getTransaction(), Collections.emptyList(), List.of(selectedTxos), payments, outputs, Collections.emptyMap(), fee.getValue(), inputTransactions);
+ return new WalletTransaction(null, transaction, Collections.emptyList(), List.of(selectedTxos), payments, outputs, Collections.emptyMap(), fee.getValue(), inputTransactions);
}
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java
index 75c2ccc..27a8dca 100644
--- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java
@@ -127,10 +127,8 @@ public class OutputController extends TransactionFormController implements Initi
if(output instanceof WalletTransaction.NonAddressOutput) {
outputFieldset.setText(baseText);
} else if(output instanceof WalletTransaction.SilentPaymentChangeOutput) {
- outputFieldset.setText(baseText + " - Silent Payment Change");
- } else if(output instanceof WalletTransaction.SilentPaymentOutput) {
- outputFieldset.setText(baseText + " - Silent Payment");
- } else if(output instanceof WalletTransaction.ConsolidationOutput) {
+ outputFieldset.setText(baseText + " - Change");
+ } else if(output instanceof WalletTransaction.ConsolidationOutput || output instanceof WalletTransaction.SilentPaymentConsolidationOutput) {
outputFieldset.setText(baseText + " - Consolidation");
} else if(output instanceof WalletTransaction.PaymentOutput paymentOutput) {
Payment payment = paymentOutput.getPayment();
Why this scored 11/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.