From 766a8c267f339d41a4fce2e188513d2ce0cac6c4 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Tue, 17 May 2022 09:18:09 +0200 Subject: [PATCH] scan seed qr to bip39 and watch only keystores --- .../control/MnemonicKeystoreDisplayPane.java | 12 ----- .../sparrow/control/MnemonicKeystorePane.java | 48 +++++++++++++++++-- .../sparrow/control/QRScanDialog.java | 2 +- .../sparrow/wallet/KeystoreController.java | 17 +++++-- 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystoreDisplayPane.java b/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystoreDisplayPane.java index ff9ccdc2..392cbf6c 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystoreDisplayPane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystoreDisplayPane.java @@ -24,18 +24,6 @@ public class MnemonicKeystoreDisplayPane extends MnemonicKeystorePane { showWordList(keystore.getSeed()); } - private void showWordList(DeterministicSeed seed) { - List words = seed.getMnemonicCode(); - setContent(getMnemonicWordsEntry(words.size())); - setExpanded(true); - - for(int i = 0; i < wordsPane.getChildren().size(); i++) { - WordEntry wordEntry = (WordEntry)wordsPane.getChildren().get(i); - wordEntry.getEditor().setText(words.get(i)); - wordEntry.getEditor().setEditable(false); - } - } - protected Node getMnemonicWordsEntry(int numWords) { VBox vBox = new VBox(); vBox.setSpacing(10); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystorePane.java b/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystorePane.java index 7e3f1552..10da4dbc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystorePane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/MnemonicKeystorePane.java @@ -1,6 +1,8 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.wallet.Bip39MnemonicCode; +import com.sparrowwallet.drongo.wallet.DeterministicSeed; +import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import javafx.application.Platform; import javafx.beans.property.IntegerProperty; @@ -26,13 +28,16 @@ import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationSupport; import org.controlsfx.validation.Validator; import org.controlsfx.validation.decoration.StyleClassValidationDecoration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; + +import static com.sparrowwallet.sparrow.AppServices.showErrorDialog; public class MnemonicKeystorePane extends TitledDescriptionPane { + private static final Logger log = LoggerFactory.getLogger(MnemonicKeystorePane.class); + protected SplitMenuButton enterMnemonicButton; protected TilePane wordsPane; protected Label validLabel; @@ -73,9 +78,44 @@ public class MnemonicKeystorePane extends TitledDescriptionPane { }); enterMnemonicButton.getItems().add(item); } + enterMnemonicButton.getItems().add(new SeparatorMenuItem()); + MenuItem scanItem = new MenuItem("Scan QR..."); + scanItem.setOnAction(event -> { + scanQR(); + }); + enterMnemonicButton.getItems().add(scanItem); enterMnemonicButton.managedProperty().bind(enterMnemonicButton.visibleProperty()); } + protected void scanQR() { + QRScanDialog qrScanDialog = new QRScanDialog(); + Optional optionalResult = qrScanDialog.showAndWait(); + if(optionalResult.isPresent()) { + QRScanDialog.Result result = optionalResult.get(); + if(result.seed != null) { + showWordList(result.seed); + Platform.runLater(() -> validLabel.requestFocus()); + } else if(result.exception != null) { + log.error("Error scanning QR", result.exception); + showErrorDialog("Error scanning QR", result.exception.getMessage()); + } else { + AppServices.showErrorDialog("Invalid QR Code", "Cannot parse QR code into a seed."); + } + } + } + + protected void showWordList(DeterministicSeed seed) { + List words = seed.getMnemonicCode(); + setContent(getMnemonicWordsEntry(words.size())); + setExpanded(true); + + for(int i = 0; i < wordsPane.getChildren().size(); i++) { + WordEntry wordEntry = (WordEntry)wordsPane.getChildren().get(i); + wordEntry.getEditor().setText(words.get(i)); + wordEntry.getEditor().setEditable(false); + } + } + protected void enterMnemonic(int numWords) { setDescription("Generate new or enter existing"); showHideLink.setVisible(false); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java index 881db17d..03274a6e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java @@ -549,7 +549,7 @@ public class QRScanDialog extends Dialog { } private DeterministicSeed getSeed(CryptoSeed cryptoSeed) { - return new DeterministicSeed(cryptoSeed.getSeed(), null, cryptoSeed.getBirthdate().getTime()); + return new DeterministicSeed(cryptoSeed.getSeed(), null, cryptoSeed.getBirthdate() == null ? System.currentTimeMillis() : cryptoSeed.getBirthdate().getTime()); } private DeterministicSeed getSeed(CryptoBip39 cryptoBip39) { diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java index ccf76d3e..73b768c9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java @@ -2,10 +2,7 @@ package com.sparrowwallet.sparrow.wallet; import com.google.common.eventbus.Subscribe; import com.sparrowwallet.drongo.*; -import com.sparrowwallet.drongo.wallet.Keystore; -import com.sparrowwallet.drongo.wallet.KeystoreSource; -import com.sparrowwallet.drongo.wallet.Wallet; -import com.sparrowwallet.drongo.wallet.WalletModel; +import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.*; @@ -413,6 +410,18 @@ public class KeystoreController extends WalletFormController implements Initiali } AppServices.showErrorDialog("Missing Script Type", "QR Code did not contain any information for the " + getWalletForm().getWallet().getScriptType().getDescription() + " script type."); + } else if(result.seed != null) { + try { + Keystore keystore = Keystore.fromSeed(result.seed, getWalletForm().getWallet().getScriptType().getDefaultDerivation()); + fingerprint.setText(keystore.getKeyDerivation().getMasterFingerprint()); + derivation.setText(keystore.getKeyDerivation().getDerivationPath()); + xpub.setText(keystore.getExtendedPublicKey().toString()); + } catch(MnemonicException e) { + log.error("Error parsing seed", e); + AppServices.showErrorDialog("Error parsing seed", e.getMessage()); + } finally { + result.seed.clear(); + } } else if(result.exception != null) { log.error("Error scanning QR", result.exception); AppServices.showErrorDialog("Error scanning QR", result.exception.getMessage());