From 48e741733ba3afd38a974dd0ccf710468e712900 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Sat, 4 Jul 2020 12:52:40 +0200 Subject: [PATCH] tx creation support --- drongo | 2 +- .../sparrow/control/TransactionDiagram.java | 24 +++-- .../sparrow/wallet/SendController.java | 93 +++++++++++-------- .../com/sparrowwallet/sparrow/wallet/send.css | 4 + .../sparrowwallet/sparrow/wallet/send.fxml | 11 ++- 5 files changed, 82 insertions(+), 52 deletions(-) diff --git a/drongo b/drongo index 3ee7cd11..ccf7de9f 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 3ee7cd11eb31da06d79132f0023e6da7e534906d +Subproject commit ccf7de9f625c4cc73efc6948b3e699a7786da276 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java index eb7aa0e2..7a315009 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionDiagram.java @@ -4,12 +4,13 @@ import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.WalletNode; +import com.sparrowwallet.drongo.wallet.WalletTransaction; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.layout.*; -import java.util.Collection; +import java.util.Map; public class TransactionDiagram extends GridPane { public TransactionDiagram() { @@ -23,25 +24,34 @@ public class TransactionDiagram extends GridPane { } } - public void update(Wallet wallet, Collection inputs, Address toAddress, WalletNode changeNode, long fee) { - Pane inputsPane = getInputsLabels(inputs); + public void update(WalletTransaction walletTx) { + if(walletTx == null) { + getChildren().clear(); + } else { + update(walletTx.getWallet(), walletTx.getSelectedUtxos(), walletTx.getRecipientAddress(), walletTx.getChangeNode(), walletTx.getFee()); + } + } + + public void update(Wallet wallet, Map selectedUtxos, Address toAddress, WalletNode changeNode, long fee) { + Pane inputsPane = getInputsLabels(selectedUtxos); GridPane.setConstraints(inputsPane, 0, 0); Pane txPane = getTransactionPane(); - GridPane.setConstraints(inputsPane, 2, 0); + GridPane.setConstraints(txPane, 2, 0); Pane outputsPane = getOutputsLabels(wallet, toAddress, changeNode, fee); - GridPane.setConstraints(inputsPane, 4, 0); + GridPane.setConstraints(outputsPane, 4, 0); getChildren().clear(); getChildren().addAll(inputsPane, txPane, outputsPane); } - private Pane getInputsLabels(Collection inputs) { + private Pane getInputsLabels(Map selectedUtxos) { VBox inputsBox = new VBox(); + inputsBox.minHeightProperty().bind(minHeightProperty()); inputsBox.setAlignment(Pos.CENTER_RIGHT); inputsBox.getChildren().add(createSpacer()); - for(BlockTransactionHashIndex input : inputs) { + for(BlockTransactionHashIndex input : selectedUtxos.keySet()) { String desc = input.getLabel() != null && !input.getLabel().isEmpty() ? input.getLabel() : input.getHashAsString().substring(0, 8) + "...:" + input.getIndex(); Label label = new Label(desc); inputsBox.getChildren().add(label); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java index 55240a8e..56f7b2c0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java @@ -3,18 +3,18 @@ package com.sparrowwallet.sparrow.wallet; import com.google.common.eventbus.Subscribe; import com.sparrowwallet.drongo.address.Address; import com.sparrowwallet.drongo.address.InvalidAddressException; -import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.AppController; import com.sparrowwallet.sparrow.BitcoinUnit; import com.sparrowwallet.sparrow.EventManager; -import com.sparrowwallet.sparrow.control.CopyableLabel; -import com.sparrowwallet.sparrow.control.CopyableTextField; -import com.sparrowwallet.sparrow.control.FeeRatesChart; -import com.sparrowwallet.sparrow.control.TextFieldValidator; +import com.sparrowwallet.sparrow.control.*; import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent; import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -27,11 +27,16 @@ import org.controlsfx.validation.Validator; import org.controlsfx.validation.decoration.StyleClassValidationDecoration; import java.net.URL; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.stream.Collectors; public class SendController extends WalletFormController implements Initializable { public static final List TARGET_BLOCKS_RANGE = List.of(1, 2, 3, 4, 5, 10, 25, 50); + public static final double FALLBACK_FEE_RATE = 20000d / 1000; + @FXML private CopyableTextField address; @@ -60,15 +65,15 @@ public class SendController extends WalletFormController implements Initializabl private FeeRatesChart feeRatesChart; @FXML - private Button clear; + private TransactionDiagram transactionDiagram; @FXML - private Button select; + private Button clear; @FXML private Button create; - private ObservableList inputs; + private final ObjectProperty walletTransactionProperty = new SimpleObjectProperty<>(null); private final BooleanProperty insufficientInputsProperty = new SimpleBooleanProperty(false); @@ -144,19 +149,16 @@ public class SendController extends WalletFormController implements Initializabl updateTransaction(); }); - select.managedProperty().bind(select.visibleProperty()); - create.managedProperty().bind(create.visibleProperty()); - if(inputs == null || inputs.isEmpty()) { - create.setVisible(false); - } else { - select.setVisible(false); - } + walletTransactionProperty.addListener((observable, oldValue, newValue) -> { + transactionDiagram.update(newValue); + create.setDisable(false); + }); } private void addValidation() { ValidationSupport validationSupport = new ValidationSupport(); validationSupport.registerValidator(address, Validator.combine( - (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid Address", !isValidAddress()) + (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid Address", !newValue.isEmpty() && !isValidAddress()) )); validationSupport.registerValidator(amount, Validator.combine( (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Insufficient Inputs", insufficientInputsProperty.get()) @@ -167,32 +169,22 @@ public class SendController extends WalletFormController implements Initializabl private void updateTransaction() { try { - Address address = getAddress(); - Long amount = getAmount(); - if(amount != null) { - Collection selectedInputs = selectInputs(amount); - - Transaction transaction = new Transaction(); + Address recipientAddress = getAddress(); + Long recipientAmount = getAmount(); + if(recipientAmount != null) { + Wallet wallet = getWalletForm().getWallet(); + WalletTransaction walletTransaction = wallet.createWalletTransaction(getUtxoSelectors(), recipientAddress, recipientAmount, getFeeRate()); + walletTransactionProperty.setValue(walletTransaction); + insufficientInputsProperty.set(false); + return; } } catch (InvalidAddressException e) { //ignore - } catch (InvalidTransactionException.InsufficientInputsException e) { + } catch (InsufficientFundsException e) { insufficientInputsProperty.set(true); } - } - private Collection selectInputs(Long targetValue) throws InvalidTransactionException.InsufficientInputsException { - Set utxos = getWalletForm().getWallet().getWalletUtxos().keySet(); - - for(UtxoSelector utxoSelector : getUtxoSelectors()) { - Collection selectedInputs = utxoSelector.select(targetValue, utxos); - long total = selectedInputs.stream().mapToLong(BlockTransactionHashIndex::getValue).sum(); - if(total > targetValue) { - return selectedInputs; - } - } - - throw new InvalidTransactionException.InsufficientInputsException("Not enough inputs for output value " + targetValue); + walletTransactionProperty.setValue(null); } private List getUtxoSelectors() { @@ -225,8 +217,8 @@ public class SendController extends WalletFormController implements Initializabl } private Long getFee() { - BitcoinUnit bitcoinUnit = amountUnit.getSelectionModel().getSelectedItem(); - if(amount.getText() != null && !amount.getText().isEmpty()) { + BitcoinUnit bitcoinUnit = feeAmountUnit.getSelectionModel().getSelectedItem(); + if(fee.getText() != null && !fee.getText().isEmpty()) { Double fieldValue = Double.parseDouble(amount.getText()); return bitcoinUnit.getSatsValue(fieldValue); } @@ -246,7 +238,16 @@ public class SendController extends WalletFormController implements Initializabl } private Map getTargetBlocksFeeRates() { - return AppController.getTargetBlockFeeRates(); + Map retrievedFeeRates = AppController.getTargetBlockFeeRates(); + if(retrievedFeeRates == null) { + retrievedFeeRates = TARGET_BLOCKS_RANGE.stream().collect(Collectors.toMap(java.util.function.Function.identity(), v -> FALLBACK_FEE_RATE)); + } + + return retrievedFeeRates; + } + + private Double getFeeRate() { + return getTargetBlocksFeeRates().get(getTargetBlocks()); } private void setFeeRate(Double feeRateAmt) { @@ -257,6 +258,18 @@ public class SendController extends WalletFormController implements Initializabl } + public void clear(ActionEvent event) { + address.setText(""); + label.setText(""); + amount.setText(""); + fee.setText(""); + walletTransactionProperty.setValue(null); + } + + public void createTransaction(ActionEvent event) { + + } + @Subscribe public void feeRatesUpdated(FeeRatesUpdatedEvent event) { feeRatesChart.update(event.getTargetBlockFeeRates()); diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css index 25d5c4a4..5fe202c9 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.css @@ -31,3 +31,7 @@ #feeRateField .input-container { -fx-alignment: center-left; } + +#transactionDiagram { + -fx-min-height: 230px; +} \ No newline at end of file diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.fxml index 884da75d..62328c00 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/send.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/send.fxml @@ -14,11 +14,12 @@ - + +
@@ -87,6 +88,9 @@ + + +
@@ -96,9 +100,8 @@