From 7c6daf2e1133945eb71c7fe4dbbc53fff3c5f397 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 15 Mar 2021 13:03:09 +0200 Subject: [PATCH] scan and import connected devices from import wallet dialog --- .../sparrow/control/DevicePane.java | 62 ++++++++++++++----- .../sparrow/control/WalletImportDialog.java | 43 ++++++++++++- 2 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java index 042c8f19..916810eb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java @@ -4,16 +4,15 @@ import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.OutputDescriptor; import com.sparrowwallet.drongo.crypto.ChildNumber; +import com.sparrowwallet.drongo.policy.Policy; +import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.KeystoreSource; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.EventManager; -import com.sparrowwallet.sparrow.event.AddressDisplayedEvent; -import com.sparrowwallet.sparrow.event.KeystoreImportEvent; -import com.sparrowwallet.sparrow.event.MessageSignedEvent; -import com.sparrowwallet.sparrow.event.PSBTSignedEvent; +import com.sparrowwallet.sparrow.event.*; import com.sparrowwallet.sparrow.io.Device; import com.sparrowwallet.sparrow.io.Hwi; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; @@ -34,6 +33,7 @@ import org.controlsfx.validation.decoration.StyleClassValidationDecoration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -197,18 +197,31 @@ public class DevicePane extends TitledDescriptionPane { importButton.setText("Import Keystore"); importButton.setOnAction(event -> { importButton.setDisable(true); - importKeystore(wallet.getScriptType().getDefaultDerivation()); + importKeystore(wallet.getScriptType() == null ? ScriptType.P2WPKH.getDefaultDerivation() : wallet.getScriptType().getDefaultDerivation()); }); - String[] accounts = new String[] {"Default Account #0", "Account #1", "Account #2", "Account #3", "Account #4", "Account #5", "Account #6", "Account #7", "Account #8", "Account #9"}; - int scriptAccountsLength = ScriptType.P2SH.equals(wallet.getScriptType()) ? 1 : accounts.length; - for(int i = 0; i < scriptAccountsLength; i++) { - MenuItem item = new MenuItem(accounts[i]); - final List derivation = wallet.getScriptType().getDefaultDerivation(i); - item.setOnAction(event -> { - importButton.setDisable(true); - importKeystore(derivation); - }); - importButton.getItems().add(item); + if(wallet.getScriptType() == null) { + ScriptType[] scriptTypes = new ScriptType[] {ScriptType.P2WPKH, ScriptType.P2SH_P2WPKH, ScriptType.P2PKH}; + for(ScriptType scriptType : scriptTypes) { + MenuItem item = new MenuItem(scriptType.getDescription()); + final List derivation = scriptType.getDefaultDerivation(); + item.setOnAction(event -> { + importButton.setDisable(true); + importKeystore(derivation); + }); + importButton.getItems().add(item); + } + } else { + String[] accounts = new String[] {"Default Account #0", "Account #1", "Account #2", "Account #3", "Account #4", "Account #5", "Account #6", "Account #7", "Account #8", "Account #9"}; + int scriptAccountsLength = ScriptType.P2SH.equals(wallet.getScriptType()) ? 1 : accounts.length; + for(int i = 0; i < scriptAccountsLength; i++) { + MenuItem item = new MenuItem(accounts[i]); + final List derivation = wallet.getScriptType().getDefaultDerivation(i); + item.setOnAction(event -> { + importButton.setDisable(true); + importKeystore(derivation); + }); + importButton.getItems().add(item); + } } importButton.managedProperty().bind(importButton.visibleProperty()); importButton.setVisible(false); @@ -450,7 +463,18 @@ public class DevicePane extends TitledDescriptionPane { keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath)); keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(xpub)); - EventManager.get().post(new KeystoreImportEvent(keystore)); + if(wallet.getScriptType() == null) { + ScriptType scriptType = Arrays.stream(ScriptType.SINGLE_HASH_TYPES).filter(type -> type.getDefaultDerivation().get(0).equals(derivation.get(0))).findFirst().orElse(ScriptType.P2PKH); + wallet.setName(device.getModel().toDisplayString()); + wallet.setPolicyType(PolicyType.SINGLE); + wallet.setScriptType(scriptType); + wallet.getKeystores().add(keystore); + wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, scriptType, wallet.getKeystores(), null)); + + EventManager.get().post(new WalletImportEvent(wallet)); + } else { + EventManager.get().post(new KeystoreImportEvent(keystore)); + } } catch(Exception e) { setError("Could not retrieve xpub", e.getMessage()); } @@ -513,7 +537,7 @@ public class DevicePane extends TitledDescriptionPane { importButton.setVisible(true); showHideLink.setText("Show derivation..."); showHideLink.setVisible(true); - setContent(getDerivationEntry(wallet.getScriptType().getDefaultDerivation())); + setContent(getDerivationEntry(wallet.getScriptType() == null ? ScriptType.P2WPKH.getDefaultDerivation() : wallet.getScriptType().getDefaultDerivation())); } else if(deviceOperation.equals(DeviceOperation.SIGN)) { signButton.setVisible(true); showHideLink.setVisible(false); @@ -562,6 +586,10 @@ public class DevicePane extends TitledDescriptionPane { return contentBox; } + public Device getDevice() { + return device; + } + public enum DeviceOperation { IMPORT, SIGN, DISPLAY_ADDRESS, SIGN_MESSAGE; } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java index f431b11e..1d9b1bf6 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java @@ -4,16 +4,24 @@ import com.google.common.eventbus.Subscribe; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.EventManager; +import com.sparrowwallet.sparrow.event.UsbDeviceEvent; import com.sparrowwallet.sparrow.event.WalletImportEvent; +import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; +import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands; import com.sparrowwallet.sparrow.io.*; +import javafx.application.Platform; +import javafx.event.ActionEvent; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.StackPane; +import org.controlsfx.glyphfont.Glyph; import java.util.List; public class WalletImportDialog extends Dialog { private Wallet wallet; + private final Accordion importAccordion; + private final Button scanButton; public WalletImportDialog() { EventManager.get().register(this); @@ -38,7 +46,7 @@ public class WalletImportDialog extends Dialog { AnchorPane.setLeftAnchor(scrollPane, 0.0); AnchorPane.setRightAnchor(scrollPane, 0.0); - Accordion importAccordion = new Accordion(); + importAccordion = new Accordion(); List keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new PassportSinglesig()); for(KeystoreFileImport importer : keystoreImporters) { FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer); @@ -53,7 +61,16 @@ public class WalletImportDialog extends Dialog { scrollPane.setContent(importAccordion); final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); - dialogPane.getButtonTypes().addAll(cancelButtonType); + final ButtonType scanButtonType = new javafx.scene.control.ButtonType("Scan for Connected Devices", ButtonBar.ButtonData.LEFT); + dialogPane.getButtonTypes().addAll(scanButtonType, cancelButtonType); + + scanButton = (Button) dialogPane.lookupButton(scanButtonType); + scanButton.setGraphic(new Glyph(FontAwesome5Brands.FONT_NAME, FontAwesome5Brands.Glyph.USB)); + scanButton.addEventFilter(ActionEvent.ACTION, event -> { + scan(); + event.consume(); + }); + dialogPane.setPrefWidth(500); dialogPane.setPrefHeight(500); @@ -65,4 +82,26 @@ public class WalletImportDialog extends Dialog { wallet = event.getWallet(); setResult(wallet); } + + private void scan() { + Hwi.EnumerateService enumerateService = new Hwi.EnumerateService(null); + enumerateService.setOnSucceeded(workerStateEvent -> { + scanButton.setGraphic(new Glyph(FontAwesome5Brands.FONT_NAME, FontAwesome5Brands.Glyph.USB)); + scanButton.setTooltip(null); + List devices = enumerateService.getValue(); + importAccordion.getPanes().removeIf(titledPane -> titledPane instanceof DevicePane); + for(Device device : devices) { + DevicePane devicePane = new DevicePane(new Wallet(), device); + importAccordion.getPanes().add(0, devicePane); + } + Platform.runLater(() -> EventManager.get().post(new UsbDeviceEvent(devices))); + }); + enumerateService.setOnFailed(workerStateEvent -> { + Glyph glyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.EXCLAMATION_CIRCLE); + glyph.getStyleClass().add("failure"); + scanButton.setGraphic(glyph); + scanButton.setTooltip(new Tooltip(workerStateEvent.getSource().getException().getMessage())); + }); + enumerateService.start(); + } }