diff --git a/drongo b/drongo index 4c5166a6..72a2a8bd 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 4c5166a6ea886622d57254444e95efa043ba302f +Subproject commit 72a2a8bd1becaa77371d1e644c3b9e76b757a8ed diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index b8aebf05..1f243e60 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -404,6 +404,8 @@ public class AppController implements Initializable { return onlineProperty.get(); } + public static BooleanProperty onlineProperty() { return onlineProperty; } + public static Integer getCurrentBlockHeight() { return currentBlockHeight; } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationProgressIndicator.java b/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationProgressIndicator.java index 2de1b232..416d890b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationProgressIndicator.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/ConfirmationProgressIndicator.java @@ -50,7 +50,7 @@ public class ConfirmationProgressIndicator extends StackPane { Timeline arcLengthTimeline = new Timeline(); KeyValue arcLengthValue = new KeyValue(arc.lengthProperty(), getDegrees(newValue.intValue())); - KeyFrame arcLengthFrame = new KeyFrame(Duration.millis(1000), arcLengthValue); + KeyFrame arcLengthFrame = new KeyFrame(Duration.millis(3000), arcLengthValue); arcLengthTimeline.getKeyFrames().add(arcLengthFrame); sequence.getChildren().add(arcLengthTimeline); @@ -86,7 +86,7 @@ public class ConfirmationProgressIndicator extends StackPane { upTickLineTimeline.getKeyFrames().add(upTickLineFrame); sequence.getChildren().add(upTickLineTimeline); - FadeTransition groupFadeOut = new FadeTransition(Duration.minutes(1), confirmationGroup); + FadeTransition groupFadeOut = new FadeTransition(Duration.minutes(10), confirmationGroup); groupFadeOut.setFromValue(1); groupFadeOut.setToValue(0); sequence.getChildren().add(groupFadeOut); diff --git a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java index 421101e4..0bbe61fe 100644 --- a/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java +++ b/src/main/java/com/sparrowwallet/sparrow/glyphfont/FontAwesome5.java @@ -15,6 +15,7 @@ public class FontAwesome5 extends GlyphFont { * The individual glyphs offered by the FontAwesome5 font. */ public static enum Glyph implements INamedCharacter { + ARROW_DOWN('\uf063'), ARROW_UP('\uf062'), CAMERA('\uf030'), CHECK_CIRCLE('\uf058'), diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java index f4942bab..21892dbc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ElectrumServer.java @@ -246,8 +246,7 @@ public class ElectrumServer { for(WalletNode node : nodes) { if(node.getIndex() >= startIndex) { String scriptHash = getScriptHash(wallet, node); - if(!subscribedScriptHashes.containsKey(scriptHash)) { - scriptHashes.add(scriptHash); + if(!subscribedScriptHashes.containsKey(scriptHash) && scriptHashes.add(scriptHash)) { batchRequest.add(node.getDerivationPath(), "blockchain.scripthash.subscribe", scriptHash); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java index 5d58a605..8ea3b3c0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java @@ -1,6 +1,7 @@ package com.sparrowwallet.sparrow.transaction; import com.sparrowwallet.drongo.SecureString; +import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.protocol.*; import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBTInput; @@ -12,6 +13,7 @@ import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; import com.sparrowwallet.sparrow.io.ElectrumServer; import com.sparrowwallet.sparrow.io.Storage; +import com.sparrowwallet.sparrow.wallet.TransactionEntry; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -105,7 +107,7 @@ public class HeadersController extends TransactionFormController implements Init private Form blockchainForm; @FXML - private CopyableLabel blockStatus; + private Label blockStatus; @FXML private Field blockHeightField; @@ -173,6 +175,9 @@ public class HeadersController extends TransactionFormController implements Init @FXML private Button broadcastButton; + @FXML + private Button saveFinalButton; + @Override public void initialize(URL location, ResourceBundle resources) { EventManager.get().register(this); @@ -308,6 +313,11 @@ public class HeadersController extends TransactionFormController implements Init broadcastProgressBar.managedProperty().bind(broadcastProgressBar.visibleProperty()); broadcastProgressBar.visibleProperty().bind(signaturesProgressBar.visibleProperty().not()); + broadcastButton.managedProperty().bind(broadcastButton.visibleProperty()); + saveFinalButton.managedProperty().bind(saveFinalButton.visibleProperty()); + saveFinalButton.visibleProperty().bind(broadcastButton.visibleProperty().not()); + broadcastButton.visibleProperty().bind(AppController.onlineProperty()); + blockchainForm.setVisible(false); signingWalletForm.setVisible(false); sigHashForm.setVisible(false); @@ -433,6 +443,17 @@ public class HeadersController extends TransactionFormController implements Init } else { blockStatus.setText(confirmations + " Confirmations"); } + + if(confirmations <= TransactionEntry.BLOCKS_TO_CONFIRM) { + ConfirmationProgressIndicator indicator; + if(blockStatus.getGraphic() == null) { + indicator = new ConfirmationProgressIndicator(confirmations); + blockStatus.setGraphic(indicator); + } else { + indicator = (ConfirmationProgressIndicator)blockStatus.getGraphic(); + indicator.setConfirmations(confirmations); + } + } } blockHeightField.managedProperty().bind(blockHeightField.visibleProperty()); @@ -644,6 +665,12 @@ public class HeadersController extends TransactionFormController implements Init broadcastButton.setDisable(true); extractTransaction(event); + if(headersForm.getSigningWallet() instanceof FinalizingPSBTWallet) { + //Ensure the script hashes of the UTXOs in FinalizingPSBTWallet are subscribed to + ElectrumServer.TransactionHistoryService historyService = new ElectrumServer.TransactionHistoryService(headersForm.getSigningWallet()); + historyService.start(); + } + ElectrumServer.BroadcastTransactionService broadcastTransactionService = new ElectrumServer.BroadcastTransactionService(headersForm.getTransaction()); broadcastTransactionService.setOnSucceeded(workerStateEvent -> { //Do nothing and wait for WalletNodeHistoryChangedEvent to indicate tx is in mempool @@ -659,6 +686,29 @@ public class HeadersController extends TransactionFormController implements Init broadcastTransactionService.start(); } + public void saveFinalTransaction(ActionEvent event) { + Stage window = new Stage(); + + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save Final Transaction"); + + if(headersForm.getName() != null && !headersForm.getName().isEmpty()) { + fileChooser.setInitialFileName(headersForm.getName().replace(".psbt", "") + ".txn"); + } + + File file = fileChooser.showSaveDialog(window); + if(file != null) { + try { + try(PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8)) { + Transaction finalTx = headersForm.getPsbt().extractTransaction(); + writer.print(Utils.bytesToHex(finalTx.bitcoinSerialize())); + } + } catch(IOException e) { + AppController.showErrorDialog("Error saving transaction", "Cannot write to " + file.getAbsolutePath()); + } + } + } + @Subscribe public void transactionChanged(TransactionChangedEvent event) { if(headersForm.getTransaction().equals(event.getTransaction())) { @@ -830,9 +880,9 @@ public class HeadersController extends TransactionFormController implements Init } @Subscribe - public void walletBlockHeightChanged(WalletBlockHeightChangedEvent event) { - if(headersForm.getSigningWallet() != null && event.getWallet() == headersForm.getSigningWallet() && headersForm.getBlockTransaction() != null) { - updateBlockchainForm(headersForm.getBlockTransaction(), event.getBlockHeight()); + public void newBlock(NewBlockEvent event) { + if(headersForm.getBlockTransaction() != null) { + updateBlockchainForm(headersForm.getBlockTransaction(), event.getHeight()); } } } \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java index bc4d1cba..0bd97adb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/WalletController.java @@ -79,14 +79,18 @@ public class WalletController extends WalletFormController implements Initializa configure(walletForm.getWallet().isValid()); } - public void configure(boolean isWalletValid) { + public void configure(boolean validWallet) { for(Toggle toggle : walletMenu.getToggles()) { if(toggle.getUserData().equals(Function.SETTINGS)) { - if(!isWalletValid) { + if(!validWallet) { toggle.setSelected(true); } } else { - ((ToggleButton)toggle).setDisable(!isWalletValid); + if(toggle.getUserData().equals(Function.TRANSACTIONS) && validWallet && walletMenu.getSelectedToggle() == null) { + toggle.setSelected(true); + } + + ((ToggleButton)toggle).setDisable(!validWallet); } } } diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.css b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.css index 9c999125..eb9ad593 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.css +++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.css @@ -24,6 +24,18 @@ -fx-text-fill: #a0a1a7; } +#blockchainForm .input-container { + -fx-alignment: center-left; +} + +.confirmation-progress-circle, .confirmation-progress-tick { + -fx-stroke: -fx-text-base-color; +} + +.confirmation-progress-arc { + -fx-fill: -fx-text-base-color; +} + #signingWalletForm .input-container { -fx-alignment: center-left; -fx-pref-height: 30; diff --git a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml index ca4d827d..10bb4197 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/transaction/headers.fxml @@ -116,7 +116,7 @@