handle support for multiple payments in a tx

This commit is contained in:
Craig Raw 2020-10-19 11:06:59 +02:00
parent 4ea5ec6c0a
commit ee9247c066
4 changed files with 59 additions and 25 deletions

View file

@ -52,7 +52,9 @@ dependencies {
exclude group: 'org.slf4j' exclude group: 'org.slf4j'
} }
implementation('com.sparrowwallet:hummingbird:1.2') implementation('com.sparrowwallet:hummingbird:1.2')
implementation('com.nativelibs4java:bridj:0.7-20140918-3') implementation('com.nativelibs4java:bridj:0.7-20140918-3') {
exclude group: 'com.google.android.tools', module: 'dx'
}
implementation('com.github.sarxos:webcam-capture:0.3.13-SNAPSHOT') { implementation('com.github.sarxos:webcam-capture:0.3.13-SNAPSHOT') {
exclude group: 'com.nativelibs4java', module: 'bridj' exclude group: 'com.nativelibs4java', module: 'bridj'
} }

2
drongo

@ -1 +1 @@
Subproject commit 661e88447f232e44f96f69ee795f151233099ed6 Subproject commit 8b07336d71f32094acb8eb8c162ebd8621ffc4aa

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.protocol.Sha256Hash; import com.sparrowwallet.drongo.protocol.Sha256Hash;
import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex;
import com.sparrowwallet.drongo.wallet.Payment;
import com.sparrowwallet.drongo.wallet.WalletNode; import com.sparrowwallet.drongo.wallet.WalletNode;
import com.sparrowwallet.drongo.wallet.WalletTransaction; import com.sparrowwallet.drongo.wallet.WalletTransaction;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
@ -26,6 +27,7 @@ import java.util.*;
public class TransactionDiagram extends GridPane { public class TransactionDiagram extends GridPane {
private static final int MAX_UTXOS = 8; private static final int MAX_UTXOS = 8;
private static final int MAX_PAYMENTS = 6;
private static final double DIAGRAM_HEIGHT = 230.0; private static final double DIAGRAM_HEIGHT = 230.0;
private static final int TOOLTIP_SHOW_DELAY = 50; private static final int TOOLTIP_SHOW_DELAY = 50;
@ -55,10 +57,12 @@ public class TransactionDiagram extends GridPane {
Pane txPane = getTransactionPane(); Pane txPane = getTransactionPane();
GridPane.setConstraints(txPane, 3, 0); GridPane.setConstraints(txPane, 3, 0);
Pane outputsLinesPane = getOutputsLines(); List<Payment> displayedPayments = getDisplayedPayments();
Pane outputsLinesPane = getOutputsLines(displayedPayments);
GridPane.setConstraints(outputsLinesPane, 4, 0); GridPane.setConstraints(outputsLinesPane, 4, 0);
Pane outputsPane = getOutputsLabels(); Pane outputsPane = getOutputsLabels(displayedPayments);
GridPane.setConstraints(outputsPane, 5, 0); GridPane.setConstraints(outputsPane, 5, 0);
getChildren().clear(); getChildren().clear();
@ -241,7 +245,28 @@ public class TransactionDiagram extends GridPane {
return value * (1.0 - scaleFactor) + additional; return value * (1.0 - scaleFactor) + additional;
} }
private Pane getOutputsLines() { private List<Payment> getDisplayedPayments() {
List<Payment> payments = walletTx.getPayments();
if(payments.size() > MAX_PAYMENTS) {
List<Payment> displayedPayments = new ArrayList<>();
List<Payment> additional = new ArrayList<>();
for(Payment payment : payments) {
if(displayedPayments.size() < MAX_PAYMENTS) {
displayedPayments.add(payment);
} else {
additional.add(payment);
}
}
displayedPayments.add(new AdditionalPayment(additional));
return displayedPayments;
} else {
return payments;
}
}
private Pane getOutputsLines(List<Payment> displayedPayments) {
VBox pane = new VBox(); VBox pane = new VBox();
Group group = new Group(); Group group = new Group();
VBox.setVgrow(group, Priority.ALWAYS); VBox.setVgrow(group, Priority.ALWAYS);
@ -255,7 +280,7 @@ public class TransactionDiagram extends GridPane {
group.getChildren().add(yaxisLine); group.getChildren().add(yaxisLine);
double width = 140.0; double width = 140.0;
int numOutputs = (walletTx.getChangeNode() == null ? 2 : 3); int numOutputs = displayedPayments.size() + (walletTx.getChangeNode() == null ? 1 : 2);
for(int i = 1; i <= numOutputs; i++) { for(int i = 1; i <= numOutputs; i++) {
CubicCurve curve = new CubicCurve(); CubicCurve curve = new CubicCurve();
curve.getStyleClass().add("output-line"); curve.getStyleClass().add("output-line");
@ -280,23 +305,25 @@ public class TransactionDiagram extends GridPane {
return pane; return pane;
} }
private Pane getOutputsLabels() { private Pane getOutputsLabels(List<Payment> displayedPayments) {
VBox outputsBox = new VBox(); VBox outputsBox = new VBox();
outputsBox.setMaxWidth(150); outputsBox.setMaxWidth(150);
outputsBox.setPadding(new Insets(0, 20, 0, 10)); outputsBox.setPadding(new Insets(0, 20, 0, 10));
outputsBox.setAlignment(Pos.CENTER_LEFT); outputsBox.setAlignment(Pos.CENTER_LEFT);
outputsBox.getChildren().add(createSpacer()); outputsBox.getChildren().add(createSpacer());
boolean isConsolidation = walletTx.isConsolidationSend(); for(Payment payment : displayedPayments) {
String recipientDesc = walletTx.getRecipientAddress().toString().substring(0, 8) + "..."; boolean isConsolidation = walletTx.isConsolidationSend(payment);
String recipientDesc = payment instanceof AdditionalPayment ? payment.getLabel() : payment.getAddress().toString().substring(0, 8) + "...";
Label recipientLabel = new Label(recipientDesc, isConsolidation ? getConsolidationGlyph() : getPaymentGlyph()); Label recipientLabel = new Label(recipientDesc, isConsolidation ? getConsolidationGlyph() : getPaymentGlyph());
recipientLabel.getStyleClass().addAll("output-label", "recipient-label"); recipientLabel.getStyleClass().addAll("output-label", "recipient-label");
Tooltip recipientTooltip = new Tooltip((isConsolidation ? "Consolidate " : "Pay ") + getSatsValue(walletTx.getRecipientAmount()) + " sats to\n" + walletTx.getRecipientAddress().toString()); Tooltip recipientTooltip = new Tooltip((isConsolidation ? "Consolidate " : "Pay ") + getSatsValue(payment.getAmount()) + " sats to\n" + payment.getAddress().toString());
recipientTooltip.getStyleClass().add("recipient-label"); recipientTooltip.getStyleClass().add("recipient-label");
recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY)); recipientTooltip.setShowDelay(new Duration(TOOLTIP_SHOW_DELAY));
recipientLabel.setTooltip(recipientTooltip); recipientLabel.setTooltip(recipientTooltip);
outputsBox.getChildren().add(recipientLabel); outputsBox.getChildren().add(recipientLabel);
outputsBox.getChildren().add(createSpacer()); outputsBox.getChildren().add(createSpacer());
}
if(walletTx.getChangeNode() != null) { if(walletTx.getChangeNode() != null) {
String changeDesc = walletTx.getChangeAddress().toString().substring(0, 8) + "..."; String changeDesc = walletTx.getChangeAddress().toString().substring(0, 8) + "...";
@ -412,4 +439,10 @@ public class TransactionDiagram extends GridPane {
return additionalInputs; return additionalInputs;
} }
} }
private static class AdditionalPayment extends Payment {
public AdditionalPayment(List<Payment> additionalPayments) {
super(null, additionalPayments.size() + " more...", additionalPayments.stream().map(Payment::getAmount).mapToLong(v -> v).sum(), false);
}
}
} }

View file

@ -275,9 +275,9 @@ public class SendController extends WalletFormController implements Initializabl
walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> { walletTransactionProperty.addListener((observable, oldValue, walletTransaction) -> {
if(walletTransaction != null) { if(walletTransaction != null) {
if(getRecipientValueSats() == null || walletTransaction.getRecipientAmount() != getRecipientValueSats()) { if(getRecipientValueSats() == null || walletTransaction.getPayments().get(0).getAmount() != getRecipientValueSats()) {
setRecipientValueSats(walletTransaction.getRecipientAmount()); setRecipientValueSats(walletTransaction.getPayments().get(0).getAmount());
setFiatAmount(AppController.getFiatCurrencyExchangeRate(), walletTransaction.getRecipientAmount()); setFiatAmount(AppController.getFiatCurrencyExchangeRate(), walletTransaction.getPayments().get(0).getAmount());
} }
double feeRate = walletTransaction.getFeeRate(); double feeRate = walletTransaction.getFeeRate();
@ -329,11 +329,12 @@ public class SendController extends WalletFormController implements Initializabl
Long recipientAmount = sendAll ? Long.valueOf(recipientDustThreshold + 1) : getRecipientValueSats(); Long recipientAmount = sendAll ? Long.valueOf(recipientDustThreshold + 1) : getRecipientValueSats();
if(recipientAmount != null && recipientAmount > recipientDustThreshold && (!userFeeSet.get() || (getFeeValueSats() != null && getFeeValueSats() > 0))) { if(recipientAmount != null && recipientAmount > recipientDustThreshold && (!userFeeSet.get() || (getFeeValueSats() != null && getFeeValueSats() > 0))) {
Wallet wallet = getWalletForm().getWallet(); Wallet wallet = getWalletForm().getWallet();
List<Payment> payments = List.of(new Payment(recipientAddress, label.getText(), recipientAmount, sendAll));
Long userFee = userFeeSet.get() ? getFeeValueSats() : null; Long userFee = userFeeSet.get() ? getFeeValueSats() : null;
Integer currentBlockHeight = AppController.getCurrentBlockHeight(); Integer currentBlockHeight = AppController.getCurrentBlockHeight();
boolean groupByAddress = Config.get().isGroupByAddress(); boolean groupByAddress = Config.get().isGroupByAddress();
boolean includeMempoolChange = Config.get().isIncludeMempoolChange(); boolean includeMempoolChange = Config.get().isIncludeMempoolChange();
WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), getUtxoFilters(), recipientAddress, recipientAmount, getFeeRate(), getMinimumFeeRate(), userFee, currentBlockHeight, sendAll, groupByAddress, includeMempoolChange); WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), getUtxoFilters(), payments, getFeeRate(), getMinimumFeeRate(), userFee, currentBlockHeight, groupByAddress, includeMempoolChange);
walletTransactionProperty.setValue(walletTransaction); walletTransactionProperty.setValue(walletTransaction);
insufficientInputsProperty.set(false); insufficientInputsProperty.set(false);
@ -611,10 +612,8 @@ public class SendController extends WalletFormController implements Initializabl
WalletTransaction walletTransaction = walletTransactionProperty.get(); WalletTransaction walletTransaction = walletTransactionProperty.get();
Set<WalletNode> nodes = new LinkedHashSet<>(walletTransaction.getSelectedUtxos().values()); Set<WalletNode> nodes = new LinkedHashSet<>(walletTransaction.getSelectedUtxos().values());
nodes.add(walletTransaction.getChangeNode()); nodes.add(walletTransaction.getChangeNode());
WalletNode consolidationNode = walletTransaction.getConsolidationSendNode(); List<WalletNode> consolidationNodes = walletTransaction.getConsolidationSendNodes();
if(consolidationNode != null) { nodes.addAll(consolidationNodes);
nodes.add(consolidationNode);
}
//All wallet nodes applicable to this transaction are stored so when the subscription status for one is updated, the history for all can be fetched in one atomic update //All wallet nodes applicable to this transaction are stored so when the subscription status for one is updated, the history for all can be fetched in one atomic update
walletForm.addWalletTransactionNodes(nodes); walletForm.addWalletTransactionNodes(nodes);