scan and import connected devices from import wallet dialog

This commit is contained in:
Craig Raw 2021-03-15 13:03:09 +02:00
parent e1f405d886
commit 7c6daf2e11
2 changed files with 86 additions and 19 deletions

View file

@ -4,16 +4,15 @@ import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.OutputDescriptor; import com.sparrowwallet.drongo.OutputDescriptor;
import com.sparrowwallet.drongo.crypto.ChildNumber; 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.protocol.ScriptType;
import com.sparrowwallet.drongo.psbt.PSBT; import com.sparrowwallet.drongo.psbt.PSBT;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.KeystoreSource; import com.sparrowwallet.drongo.wallet.KeystoreSource;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.AddressDisplayedEvent; import com.sparrowwallet.sparrow.event.*;
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
import com.sparrowwallet.sparrow.event.MessageSignedEvent;
import com.sparrowwallet.sparrow.event.PSBTSignedEvent;
import com.sparrowwallet.sparrow.io.Device; import com.sparrowwallet.sparrow.io.Device;
import com.sparrowwallet.sparrow.io.Hwi; import com.sparrowwallet.sparrow.io.Hwi;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5; import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
@ -34,6 +33,7 @@ import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -197,18 +197,31 @@ public class DevicePane extends TitledDescriptionPane {
importButton.setText("Import Keystore"); importButton.setText("Import Keystore");
importButton.setOnAction(event -> { importButton.setOnAction(event -> {
importButton.setDisable(true); 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"}; if(wallet.getScriptType() == null) {
int scriptAccountsLength = ScriptType.P2SH.equals(wallet.getScriptType()) ? 1 : accounts.length; ScriptType[] scriptTypes = new ScriptType[] {ScriptType.P2WPKH, ScriptType.P2SH_P2WPKH, ScriptType.P2PKH};
for(int i = 0; i < scriptAccountsLength; i++) { for(ScriptType scriptType : scriptTypes) {
MenuItem item = new MenuItem(accounts[i]); MenuItem item = new MenuItem(scriptType.getDescription());
final List<ChildNumber> derivation = wallet.getScriptType().getDefaultDerivation(i); final List<ChildNumber> derivation = scriptType.getDefaultDerivation();
item.setOnAction(event -> { item.setOnAction(event -> {
importButton.setDisable(true); importButton.setDisable(true);
importKeystore(derivation); importKeystore(derivation);
}); });
importButton.getItems().add(item); 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<ChildNumber> derivation = wallet.getScriptType().getDefaultDerivation(i);
item.setOnAction(event -> {
importButton.setDisable(true);
importKeystore(derivation);
});
importButton.getItems().add(item);
}
} }
importButton.managedProperty().bind(importButton.visibleProperty()); importButton.managedProperty().bind(importButton.visibleProperty());
importButton.setVisible(false); importButton.setVisible(false);
@ -450,7 +463,18 @@ public class DevicePane extends TitledDescriptionPane {
keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath)); keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath));
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(xpub)); 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) { } catch(Exception e) {
setError("Could not retrieve xpub", e.getMessage()); setError("Could not retrieve xpub", e.getMessage());
} }
@ -513,7 +537,7 @@ public class DevicePane extends TitledDescriptionPane {
importButton.setVisible(true); importButton.setVisible(true);
showHideLink.setText("Show derivation..."); showHideLink.setText("Show derivation...");
showHideLink.setVisible(true); 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)) { } else if(deviceOperation.equals(DeviceOperation.SIGN)) {
signButton.setVisible(true); signButton.setVisible(true);
showHideLink.setVisible(false); showHideLink.setVisible(false);
@ -562,6 +586,10 @@ public class DevicePane extends TitledDescriptionPane {
return contentBox; return contentBox;
} }
public Device getDevice() {
return device;
}
public enum DeviceOperation { public enum DeviceOperation {
IMPORT, SIGN, DISPLAY_ADDRESS, SIGN_MESSAGE; IMPORT, SIGN, DISPLAY_ADDRESS, SIGN_MESSAGE;
} }

View file

@ -4,16 +4,24 @@ import com.google.common.eventbus.Subscribe;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.AppServices; import com.sparrowwallet.sparrow.AppServices;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.UsbDeviceEvent;
import com.sparrowwallet.sparrow.event.WalletImportEvent; import com.sparrowwallet.sparrow.event.WalletImportEvent;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
import com.sparrowwallet.sparrow.io.*; import com.sparrowwallet.sparrow.io.*;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.controlsfx.glyphfont.Glyph;
import java.util.List; import java.util.List;
public class WalletImportDialog extends Dialog<Wallet> { public class WalletImportDialog extends Dialog<Wallet> {
private Wallet wallet; private Wallet wallet;
private final Accordion importAccordion;
private final Button scanButton;
public WalletImportDialog() { public WalletImportDialog() {
EventManager.get().register(this); EventManager.get().register(this);
@ -38,7 +46,7 @@ public class WalletImportDialog extends Dialog<Wallet> {
AnchorPane.setLeftAnchor(scrollPane, 0.0); AnchorPane.setLeftAnchor(scrollPane, 0.0);
AnchorPane.setRightAnchor(scrollPane, 0.0); AnchorPane.setRightAnchor(scrollPane, 0.0);
Accordion importAccordion = new Accordion(); importAccordion = new Accordion();
List<KeystoreFileImport> keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new PassportSinglesig()); List<KeystoreFileImport> keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new PassportSinglesig());
for(KeystoreFileImport importer : keystoreImporters) { for(KeystoreFileImport importer : keystoreImporters) {
FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer); FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer);
@ -53,7 +61,16 @@ public class WalletImportDialog extends Dialog<Wallet> {
scrollPane.setContent(importAccordion); scrollPane.setContent(importAccordion);
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); 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.setPrefWidth(500);
dialogPane.setPrefHeight(500); dialogPane.setPrefHeight(500);
@ -65,4 +82,26 @@ public class WalletImportDialog extends Dialog<Wallet> {
wallet = event.getWallet(); wallet = event.getWallet();
setResult(wallet); 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<Device> 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();
}
} }