mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
bip39 keystore import
This commit is contained in:
parent
910dfcdeb1
commit
a409c28b20
17 changed files with 613 additions and 248 deletions
|
@ -48,7 +48,7 @@ dependencies {
|
||||||
mainClassName = 'com.sparrowwallet.sparrow/com.sparrowwallet.sparrow.MainApp'
|
mainClassName = 'com.sparrowwallet.sparrow/com.sparrowwallet.sparrow.MainApp'
|
||||||
|
|
||||||
run {
|
run {
|
||||||
applicationDefaultJvmArgs = ["-Xdock:name=Sparrow", "-Xdock:icon=/Users/scy/git/sparrow/src/main/resources/sparrow.png", "--add-opens=javafx.graphics/com.sun.javafx.css=org.controlsfx.controls", "--add-opens=javafx.graphics/javafx.scene=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls", "--add-opens=javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls"]
|
applicationDefaultJvmArgs = ["-Xdock:name=Sparrow", "-Xdock:icon=/Users/scy/git/sparrow/src/main/resources/sparrow.png", "--add-opens=javafx.graphics/com.sun.javafx.css=org.controlsfx.controls", "--add-opens=javafx.graphics/javafx.scene=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls", "--add-opens=javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls", "--add-opens=javafx.base/com.sun.javafx.event=org.controlsfx.controls"]
|
||||||
}
|
}
|
||||||
|
|
||||||
jlink {
|
jlink {
|
||||||
|
@ -63,7 +63,7 @@ jlink {
|
||||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--ignore-signing-information', '--exclude-files', '**.png']
|
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages', '--ignore-signing-information', '--exclude-files', '**.png']
|
||||||
launcher {
|
launcher {
|
||||||
name = 'sparrow'
|
name = 'sparrow'
|
||||||
jvmArgs = ["--add-opens=javafx.graphics/com.sun.javafx.css=org.controlsfx.controls", "--add-opens=javafx.graphics/javafx.scene=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls", "--add-opens=javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls"]
|
jvmArgs = ["--add-opens=javafx.graphics/com.sun.javafx.css=org.controlsfx.controls", "--add-opens=javafx.graphics/javafx.scene=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.behavior=org.controlsfx.controls", "--add-opens=javafx.controls/com.sun.javafx.scene.control.inputmap=org.controlsfx.controls", "--add-opens=javafx.graphics/com.sun.javafx.scene.traversal=org.controlsfx.controls", "--add-opens=javafx.base/com.sun.javafx.event=org.controlsfx.controls"]
|
||||||
}
|
}
|
||||||
addExtraDependencies("javafx")
|
addExtraDependencies("javafx")
|
||||||
jpackage {
|
jpackage {
|
||||||
|
|
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit d394c25a3c05c02d984b1f709623a311c2afb7a1
|
Subproject commit be0c4d1176da41671c2629e65f812fd28fab202b
|
|
@ -41,9 +41,9 @@ public class MainApp extends Application {
|
||||||
wallet.setScriptType(ScriptType.P2WPKH);
|
wallet.setScriptType(ScriptType.P2WPKH);
|
||||||
|
|
||||||
KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
|
||||||
//dlg.showAndWait();
|
dlg.showAndWait();
|
||||||
|
|
||||||
stage.show();
|
//stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -128,7 +128,8 @@ public class DevicePane extends TitledPane {
|
||||||
listItem.getChildren().add(buttonBox);
|
listItem.getChildren().add(buttonBox);
|
||||||
|
|
||||||
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
|
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
listItem.setPrefWidth(newValue.getWidth());
|
//Hack to force listItem to expand to full available width less border
|
||||||
|
listItem.setPrefWidth(newValue.getWidth() - 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
return listItem;
|
return listItem;
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||||
|
import com.sparrowwallet.sparrow.io.KeystoreFileImport;
|
||||||
|
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||||
|
import org.controlsfx.control.textfield.TextFields;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class FileKeystoreImportPane extends KeystoreImportPane {
|
||||||
|
private final KeystoreFileImport importer;
|
||||||
|
private Button importButton;
|
||||||
|
private final SimpleStringProperty password = new SimpleStringProperty("");
|
||||||
|
|
||||||
|
public FileKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) {
|
||||||
|
super(importAccordion, wallet, importer);
|
||||||
|
this.importer = importer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node getTitle(KeystoreImport importer) {
|
||||||
|
Node title = super.getTitle(importer);
|
||||||
|
|
||||||
|
setDescription("Keystore file import");
|
||||||
|
|
||||||
|
importButton = new Button("Import File...");
|
||||||
|
importButton.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
importButton.setOnAction(event -> {
|
||||||
|
importFile();
|
||||||
|
});
|
||||||
|
buttonBox.getChildren().add(importButton);
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importFile() {
|
||||||
|
Stage window = new Stage();
|
||||||
|
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
|
||||||
|
fileChooser.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
||||||
|
new FileChooser.ExtensionFilter("JSON", "*.json")
|
||||||
|
);
|
||||||
|
|
||||||
|
File file = fileChooser.showOpenDialog(window);
|
||||||
|
if(file != null) {
|
||||||
|
importFile(file, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importFile(File file, String password) {
|
||||||
|
if(file.exists()) {
|
||||||
|
try {
|
||||||
|
if(importer.isEncrypted(file) && password == null) {
|
||||||
|
setDescription("Password Required");
|
||||||
|
showHideLink.setVisible(false);
|
||||||
|
setContent(getPasswordEntry(file));
|
||||||
|
importButton.setDisable(true);
|
||||||
|
setExpanded(true);
|
||||||
|
} else {
|
||||||
|
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||||
|
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password);
|
||||||
|
EventManager.get().post(new KeystoreImportEvent(keystore));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
String errorMessage = e.getMessage();
|
||||||
|
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
||||||
|
errorMessage = e.getCause().getMessage();
|
||||||
|
}
|
||||||
|
if(e instanceof ECKey.InvalidPasswordException || e.getCause() instanceof ECKey.InvalidPasswordException) {
|
||||||
|
errorMessage = "Invalid wallet password";
|
||||||
|
}
|
||||||
|
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
|
||||||
|
errorMessage = "File was not in JSON format";
|
||||||
|
}
|
||||||
|
setError("Import Error", errorMessage);
|
||||||
|
importButton.setDisable(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node getPasswordEntry(File file) {
|
||||||
|
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
|
||||||
|
passwordField.setPromptText("Wallet password");
|
||||||
|
password.bind(passwordField.textProperty());
|
||||||
|
HBox.setHgrow(passwordField, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Button importEncryptedButton = new Button("Import");
|
||||||
|
importEncryptedButton.setOnAction(event -> {
|
||||||
|
showHideLink.setVisible(true);
|
||||||
|
setExpanded(false);
|
||||||
|
importFile(file, password.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
HBox contentBox = new HBox();
|
||||||
|
contentBox.setAlignment(Pos.TOP_RIGHT);
|
||||||
|
contentBox.setSpacing(20);
|
||||||
|
contentBox.getChildren().add(passwordField);
|
||||||
|
contentBox.getChildren().add(importEncryptedButton);
|
||||||
|
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||||
|
contentBox.setPrefHeight(60);
|
||||||
|
|
||||||
|
return contentBox;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,229 +0,0 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
|
||||||
|
|
||||||
import com.google.gson.JsonParseException;
|
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
|
||||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
|
||||||
import com.sparrowwallet.sparrow.io.KeystoreFileImport;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Node;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Hyperlink;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.TitledPane;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.stage.FileChooser;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
|
||||||
import org.controlsfx.control.textfield.TextFields;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
public class KeystoreFileImportPane extends TitledPane {
|
|
||||||
private final KeystoreImportAccordion importAccordion;
|
|
||||||
private final Wallet wallet;
|
|
||||||
private final KeystoreFileImport importer;
|
|
||||||
|
|
||||||
private Label mainLabel;
|
|
||||||
private Label descriptionLabel;
|
|
||||||
private Hyperlink showHideLink;
|
|
||||||
private Button importButton;
|
|
||||||
|
|
||||||
private final SimpleStringProperty password = new SimpleStringProperty("");
|
|
||||||
|
|
||||||
public KeystoreFileImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreFileImport importer) {
|
|
||||||
this.importAccordion = importAccordion;
|
|
||||||
this.wallet = wallet;
|
|
||||||
this.importer = importer;
|
|
||||||
|
|
||||||
setPadding(Insets.EMPTY);
|
|
||||||
|
|
||||||
setGraphic(getTitle());
|
|
||||||
getStyleClass().add("importpane");
|
|
||||||
setContent(getContentBox(importer.getKeystoreImportDescription()));
|
|
||||||
|
|
||||||
removeArrow();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeArrow() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
Node arrow = this.lookup(".arrow");
|
|
||||||
if (arrow != null) {
|
|
||||||
arrow.setVisible(false);
|
|
||||||
arrow.setManaged(false);
|
|
||||||
} else {
|
|
||||||
removeArrow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getTitle() {
|
|
||||||
HBox listItem = new HBox();
|
|
||||||
listItem.setPadding(new Insets(10, 20, 10, 10));
|
|
||||||
listItem.setSpacing(10);
|
|
||||||
|
|
||||||
HBox imageBox = new HBox();
|
|
||||||
imageBox.setMinWidth(50);
|
|
||||||
imageBox.setMinHeight(50);
|
|
||||||
listItem.getChildren().add(imageBox);
|
|
||||||
|
|
||||||
Image image = new Image("image/" + importer.getWalletModel().getType() + ".png", 50, 50, true, true);
|
|
||||||
if (!image.isError()) {
|
|
||||||
ImageView imageView = new ImageView();
|
|
||||||
imageView.setImage(image);
|
|
||||||
imageBox.getChildren().add(imageView);
|
|
||||||
}
|
|
||||||
|
|
||||||
VBox labelsBox = new VBox();
|
|
||||||
labelsBox.setSpacing(5);
|
|
||||||
labelsBox.setAlignment(Pos.CENTER_LEFT);
|
|
||||||
this.mainLabel = new Label();
|
|
||||||
mainLabel.setText(importer.getName());
|
|
||||||
mainLabel.getStyleClass().add("main-label");
|
|
||||||
labelsBox.getChildren().add(mainLabel);
|
|
||||||
|
|
||||||
HBox descriptionBox = new HBox();
|
|
||||||
descriptionBox.setSpacing(7);
|
|
||||||
labelsBox.getChildren().add(descriptionBox);
|
|
||||||
|
|
||||||
descriptionLabel = new Label("Keystore file import");
|
|
||||||
descriptionLabel.getStyleClass().add("description-label");
|
|
||||||
showHideLink = new Hyperlink("View Details...");
|
|
||||||
showHideLink.managedProperty().bind(showHideLink.visibleProperty());
|
|
||||||
showHideLink.setOnAction(event -> {
|
|
||||||
if(showHideLink.getText().contains("View")) {
|
|
||||||
setExpanded(true);
|
|
||||||
showHideLink.setText("Hide Details...");
|
|
||||||
} else {
|
|
||||||
setExpanded(false);
|
|
||||||
showHideLink.setText("View Details...");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
descriptionBox.getChildren().addAll(descriptionLabel, showHideLink);
|
|
||||||
|
|
||||||
listItem.getChildren().add(labelsBox);
|
|
||||||
HBox.setHgrow(labelsBox, Priority.ALWAYS);
|
|
||||||
|
|
||||||
HBox buttonBox = new HBox();
|
|
||||||
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
|
|
||||||
importButton = new Button("Import File...");
|
|
||||||
importButton.setAlignment(Pos.CENTER_RIGHT);
|
|
||||||
importButton.setOnAction(event -> {
|
|
||||||
importFile();
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonBox.getChildren().add(importButton);
|
|
||||||
listItem.getChildren().add(buttonBox);
|
|
||||||
|
|
||||||
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
listItem.setPrefWidth(newValue.getWidth());
|
|
||||||
});
|
|
||||||
|
|
||||||
return listItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void importFile() {
|
|
||||||
Stage window = new Stage();
|
|
||||||
|
|
||||||
FileChooser fileChooser = new FileChooser();
|
|
||||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
|
|
||||||
fileChooser.getExtensionFilters().addAll(
|
|
||||||
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
|
||||||
new FileChooser.ExtensionFilter("JSON", "*.json")
|
|
||||||
);
|
|
||||||
|
|
||||||
File file = fileChooser.showOpenDialog(window);
|
|
||||||
if(file != null) {
|
|
||||||
importFile(file, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void importFile(File file, String password) {
|
|
||||||
if(file.exists()) {
|
|
||||||
try {
|
|
||||||
if(importer.isEncrypted(file) && password == null) {
|
|
||||||
descriptionLabel.getStyleClass().remove("description-error");
|
|
||||||
descriptionLabel.getStyleClass().add("description-label");
|
|
||||||
descriptionLabel.setText("Password Required");
|
|
||||||
showHideLink.setVisible(false);
|
|
||||||
setContent(getPasswordEntry(file));
|
|
||||||
importButton.setDisable(true);
|
|
||||||
setExpanded(true);
|
|
||||||
} else {
|
|
||||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
|
||||||
Keystore keystore = importer.getKeystore(wallet.getScriptType(), inputStream, password);
|
|
||||||
EventManager.get().post(new KeystoreImportEvent(keystore));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
descriptionLabel.getStyleClass().remove("description-label");
|
|
||||||
descriptionLabel.getStyleClass().add("description-error");
|
|
||||||
descriptionLabel.setText("Import Error");
|
|
||||||
String errorMessage = e.getMessage();
|
|
||||||
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
|
||||||
errorMessage = e.getCause().getMessage();
|
|
||||||
}
|
|
||||||
if(e instanceof ECKey.InvalidPasswordException || e.getCause() instanceof ECKey.InvalidPasswordException) {
|
|
||||||
errorMessage = "Invalid wallet password";
|
|
||||||
}
|
|
||||||
if(e instanceof JsonParseException || e.getCause() instanceof JsonParseException) {
|
|
||||||
errorMessage = "File was not in JSON format";
|
|
||||||
}
|
|
||||||
setContent(getContentBox(errorMessage));
|
|
||||||
setExpanded(true);
|
|
||||||
showHideLink.setText("Hide Details...");
|
|
||||||
importButton.setDisable(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getContentBox(String message) {
|
|
||||||
Label details = new Label(message);
|
|
||||||
details.setWrapText(true);
|
|
||||||
|
|
||||||
HBox contentBox = new HBox();
|
|
||||||
contentBox.setAlignment(Pos.TOP_LEFT);
|
|
||||||
contentBox.getChildren().add(details);
|
|
||||||
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
|
||||||
|
|
||||||
double width = TextUtils.computeTextWidth(details.getFont(), message, 0.0D);
|
|
||||||
double numLines = Math.max(1, width / 400);
|
|
||||||
double height = Math.max(60, numLines * 40);
|
|
||||||
contentBox.setPrefHeight(height);
|
|
||||||
|
|
||||||
return contentBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Node getPasswordEntry(File file) {
|
|
||||||
CustomPasswordField passwordField = (CustomPasswordField) TextFields.createClearablePasswordField();
|
|
||||||
passwordField.setPromptText("Wallet password");
|
|
||||||
password.bind(passwordField.textProperty());
|
|
||||||
HBox.setHgrow(passwordField, Priority.ALWAYS);
|
|
||||||
|
|
||||||
Button importEncryptedButton = new Button("Import");
|
|
||||||
importEncryptedButton.setOnAction(event -> {
|
|
||||||
showHideLink.setVisible(true);
|
|
||||||
setExpanded(false);
|
|
||||||
importFile(file, password.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
HBox contentBox = new HBox();
|
|
||||||
contentBox.setAlignment(Pos.TOP_RIGHT);
|
|
||||||
contentBox.setSpacing(20);
|
|
||||||
contentBox.getChildren().add(passwordField);
|
|
||||||
contentBox.getChildren().add(importEncryptedButton);
|
|
||||||
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
|
||||||
contentBox.setPrefHeight(60);
|
|
||||||
|
|
||||||
return contentBox;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,12 +16,12 @@ public class KeystoreImportAccordion extends Accordion {
|
||||||
this.importers = importers;
|
this.importers = importers;
|
||||||
|
|
||||||
for(KeystoreImport importer : importers) {
|
for(KeystoreImport importer : importers) {
|
||||||
KeystoreFileImportPane importPane = null;
|
KeystoreImportPane importPane = null;
|
||||||
|
|
||||||
if(importer instanceof KeystoreFileImport) {
|
if(importer instanceof KeystoreFileImport) {
|
||||||
importPane = new KeystoreFileImportPane(this, wallet, (KeystoreFileImport)importer);
|
importPane = new FileKeystoreImportPane(this, wallet, (KeystoreFileImport)importer);
|
||||||
} else if(importer instanceof KeystoreMnemonicImport) {
|
} else if(importer instanceof KeystoreMnemonicImport) {
|
||||||
//TODO: Import from the new Bip39KeystoreImport
|
importPane = new MnemonicKeystoreImportPane(this, wallet, (KeystoreMnemonicImport)importer);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass());
|
throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.Hyperlink;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TitledPane;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
public abstract class KeystoreImportPane extends TitledPane {
|
||||||
|
protected final KeystoreImportAccordion importAccordion;
|
||||||
|
protected final Wallet wallet;
|
||||||
|
|
||||||
|
private Label mainLabel;
|
||||||
|
private Label descriptionLabel;
|
||||||
|
protected Hyperlink showHideLink;
|
||||||
|
protected HBox buttonBox;
|
||||||
|
|
||||||
|
public KeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreImport importer) {
|
||||||
|
this.importAccordion = importAccordion;
|
||||||
|
this.wallet = wallet;
|
||||||
|
|
||||||
|
setPadding(Insets.EMPTY);
|
||||||
|
setGraphic(getTitle(importer));
|
||||||
|
getStyleClass().add("importpane");
|
||||||
|
setContent(getContentBox(importer.getKeystoreImportDescription()));
|
||||||
|
removeArrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeArrow() {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
Node arrow = this.lookup(".arrow");
|
||||||
|
if (arrow != null) {
|
||||||
|
arrow.setVisible(false);
|
||||||
|
arrow.setManaged(false);
|
||||||
|
} else {
|
||||||
|
removeArrow();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Node getTitle(KeystoreImport importer) {
|
||||||
|
HBox listItem = new HBox();
|
||||||
|
listItem.setPadding(new Insets(10, 20, 10, 10));
|
||||||
|
listItem.setSpacing(10);
|
||||||
|
|
||||||
|
HBox imageBox = new HBox();
|
||||||
|
imageBox.setMinWidth(50);
|
||||||
|
imageBox.setMinHeight(50);
|
||||||
|
listItem.getChildren().add(imageBox);
|
||||||
|
|
||||||
|
Image image = new Image("image/" + importer.getWalletModel().getType() + ".png", 50, 50, true, true);
|
||||||
|
if (!image.isError()) {
|
||||||
|
ImageView imageView = new ImageView();
|
||||||
|
imageView.setImage(image);
|
||||||
|
imageBox.getChildren().add(imageView);
|
||||||
|
}
|
||||||
|
|
||||||
|
VBox labelsBox = new VBox();
|
||||||
|
labelsBox.setSpacing(5);
|
||||||
|
labelsBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
this.mainLabel = new Label();
|
||||||
|
mainLabel.setText(importer.getName());
|
||||||
|
mainLabel.getStyleClass().add("main-label");
|
||||||
|
labelsBox.getChildren().add(mainLabel);
|
||||||
|
|
||||||
|
HBox descriptionBox = new HBox();
|
||||||
|
descriptionBox.setSpacing(7);
|
||||||
|
labelsBox.getChildren().add(descriptionBox);
|
||||||
|
|
||||||
|
descriptionLabel = new Label("Keystore Import");
|
||||||
|
descriptionLabel.getStyleClass().add("description-label");
|
||||||
|
showHideLink = new Hyperlink("Show Details...");
|
||||||
|
showHideLink.managedProperty().bind(showHideLink.visibleProperty());
|
||||||
|
showHideLink.setOnAction(event -> {
|
||||||
|
if(this.isExpanded()) {
|
||||||
|
setExpanded(false);
|
||||||
|
} else {
|
||||||
|
setExpanded(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.expandedProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
if(newValue) {
|
||||||
|
showHideLink.setText(showHideLink.getText().replace("Show", "Hide"));
|
||||||
|
} else {
|
||||||
|
showHideLink.setText(showHideLink.getText().replace("Hide", "Show"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
descriptionBox.getChildren().addAll(descriptionLabel, showHideLink);
|
||||||
|
|
||||||
|
listItem.getChildren().add(labelsBox);
|
||||||
|
HBox.setHgrow(labelsBox, Priority.ALWAYS);
|
||||||
|
|
||||||
|
buttonBox = new HBox();
|
||||||
|
buttonBox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
listItem.getChildren().add(buttonBox);
|
||||||
|
|
||||||
|
this.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
//Hack to force listItem to expand to full available width less border
|
||||||
|
listItem.setPrefWidth(newValue.getWidth() - 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
return listItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDescription(String text) {
|
||||||
|
descriptionLabel.getStyleClass().remove("description-error");
|
||||||
|
descriptionLabel.getStyleClass().add("description-label");
|
||||||
|
descriptionLabel.setText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setError(String title, String detail) {
|
||||||
|
descriptionLabel.getStyleClass().remove("description-label");
|
||||||
|
descriptionLabel.getStyleClass().add("description-error");
|
||||||
|
descriptionLabel.setText(title);
|
||||||
|
setContent(getContentBox(detail));
|
||||||
|
setExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Node getContentBox(String message) {
|
||||||
|
Label details = new Label(message);
|
||||||
|
details.setWrapText(true);
|
||||||
|
|
||||||
|
HBox contentBox = new HBox();
|
||||||
|
contentBox.setAlignment(Pos.TOP_LEFT);
|
||||||
|
contentBox.getChildren().add(details);
|
||||||
|
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||||
|
|
||||||
|
double width = TextUtils.computeTextWidth(details.getFont(), message, 0.0D);
|
||||||
|
double numLines = Math.max(1, width / 400);
|
||||||
|
double height = Math.max(60, numLines * 40);
|
||||||
|
contentBox.setPrefHeight(height);
|
||||||
|
|
||||||
|
return contentBox;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Bip39Calculator;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
|
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||||
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.geometry.Orientation;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import org.controlsfx.control.textfield.AutoCompletionBinding;
|
||||||
|
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||||
|
import org.controlsfx.control.textfield.CustomTextField;
|
||||||
|
import org.controlsfx.control.textfield.TextFields;
|
||||||
|
import org.controlsfx.validation.ValidationResult;
|
||||||
|
import org.controlsfx.validation.ValidationSupport;
|
||||||
|
import org.controlsfx.validation.Validator;
|
||||||
|
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MnemonicKeystoreImportPane extends KeystoreImportPane {
|
||||||
|
private final KeystoreMnemonicImport importer;
|
||||||
|
|
||||||
|
private SplitMenuButton enterMnemonicButton;
|
||||||
|
private SplitMenuButton importButton;
|
||||||
|
|
||||||
|
private SimpleListProperty<String> wordEntriesProperty;
|
||||||
|
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
|
public MnemonicKeystoreImportPane(KeystoreImportAccordion importAccordion, Wallet wallet, KeystoreMnemonicImport importer) {
|
||||||
|
super(importAccordion, wallet, importer);
|
||||||
|
this.importer = importer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Node getTitle(KeystoreImport importer) {
|
||||||
|
Node title = super.getTitle(importer);
|
||||||
|
setDescription("Keystore file import");
|
||||||
|
|
||||||
|
createEnterMnemonicButton();
|
||||||
|
createImportButton();
|
||||||
|
buttonBox.getChildren().addAll(enterMnemonicButton, importButton);
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createEnterMnemonicButton() {
|
||||||
|
enterMnemonicButton = new SplitMenuButton();
|
||||||
|
enterMnemonicButton.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
enterMnemonicButton.setText("Enter Mnemonic");
|
||||||
|
enterMnemonicButton.setOnAction(event -> {
|
||||||
|
enterMnemonic(24);
|
||||||
|
});
|
||||||
|
int[] numberWords = new int[] {24, 21, 18, 15, 12};
|
||||||
|
for(int i = 0; i < numberWords.length; i++) {
|
||||||
|
MenuItem item = new MenuItem(numberWords[i] + " words");
|
||||||
|
final int words = numberWords[i];
|
||||||
|
item.setOnAction(event -> {
|
||||||
|
enterMnemonic(words);
|
||||||
|
});
|
||||||
|
enterMnemonicButton.getItems().add(item);
|
||||||
|
}
|
||||||
|
enterMnemonicButton.managedProperty().bind(enterMnemonicButton.visibleProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createImportButton() {
|
||||||
|
importButton = new SplitMenuButton();
|
||||||
|
importButton.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
importButton.setText("Import Keystore");
|
||||||
|
importButton.setOnAction(event -> {
|
||||||
|
importButton.setDisable(true);
|
||||||
|
importKeystore(wallet.getScriptType().getDefaultDerivation(), false);
|
||||||
|
});
|
||||||
|
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"};
|
||||||
|
for(int i = 0; i < accounts.length; i++) {
|
||||||
|
MenuItem item = new MenuItem(accounts[i]);
|
||||||
|
final List<ChildNumber> derivation = wallet.getScriptType().getDefaultDerivation(i);
|
||||||
|
item.setOnAction(event -> {
|
||||||
|
importButton.setDisable(true);
|
||||||
|
importKeystore(derivation, false);
|
||||||
|
});
|
||||||
|
importButton.getItems().add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
importButton.managedProperty().bind(importButton.visibleProperty());
|
||||||
|
importButton.setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enterMnemonic(int numWords) {
|
||||||
|
setDescription("Enter mnemonic word list");
|
||||||
|
showHideLink.setVisible(false);
|
||||||
|
setContent(getMnemonicWordsEntry(numWords));
|
||||||
|
setExpanded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node getMnemonicWordsEntry(int numWords) {
|
||||||
|
VBox vBox = new VBox();
|
||||||
|
vBox.setSpacing(10);
|
||||||
|
|
||||||
|
TilePane tilePane = new TilePane();
|
||||||
|
tilePane.setPrefRows(numWords/3);
|
||||||
|
tilePane.setHgap(10);
|
||||||
|
tilePane.setVgap(10);
|
||||||
|
tilePane.setOrientation(Orientation.VERTICAL);
|
||||||
|
|
||||||
|
List<String> words = new ArrayList<>();
|
||||||
|
for(int i = 0; i < numWords; i++) {
|
||||||
|
words.add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
ObservableList<String> wordEntryList = FXCollections.observableArrayList(words);
|
||||||
|
wordEntriesProperty = new SimpleListProperty<>(wordEntryList);
|
||||||
|
for(int i = 0; i < numWords; i++) {
|
||||||
|
WordEntry wordEntry = new WordEntry(i, wordEntryList);
|
||||||
|
tilePane.getChildren().add(wordEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
vBox.getChildren().add(tilePane);
|
||||||
|
|
||||||
|
AnchorPane anchorPane = new AnchorPane();
|
||||||
|
anchorPane.setPadding(new Insets(0, 32, 0, 10));
|
||||||
|
|
||||||
|
PassphraseEntry passphraseEntry = new PassphraseEntry();
|
||||||
|
AnchorPane.setLeftAnchor(passphraseEntry, 0.0);
|
||||||
|
|
||||||
|
Button okButton = new Button("Ok");
|
||||||
|
okButton.setPrefWidth(70);
|
||||||
|
okButton.setDisable(true);
|
||||||
|
okButton.setOnAction(event -> {
|
||||||
|
prepareImport();
|
||||||
|
});
|
||||||
|
|
||||||
|
wordEntriesProperty.addListener((ListChangeListener<String>) c -> {
|
||||||
|
for(String word : wordEntryList) {
|
||||||
|
if(!WordEntry.isValid(word)) {
|
||||||
|
okButton.setDisable(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
okButton.setDisable(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
AnchorPane.setRightAnchor(okButton, 0.0);
|
||||||
|
|
||||||
|
anchorPane.getChildren().addAll(passphraseEntry, okButton);
|
||||||
|
vBox.getChildren().add(anchorPane);
|
||||||
|
|
||||||
|
return vBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareImport() {
|
||||||
|
if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) {
|
||||||
|
setExpanded(false);
|
||||||
|
enterMnemonicButton.setVisible(false);
|
||||||
|
importButton.setVisible(true);
|
||||||
|
importButton.setDisable(false);
|
||||||
|
setDescription("Ready to import");
|
||||||
|
showHideLink.setText("Show Derivation...");
|
||||||
|
showHideLink.setVisible(true);
|
||||||
|
setContent(getDerivationEntry(wallet.getScriptType().getDefaultDerivation()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean importKeystore(List<ChildNumber> derivation, boolean dryrun) {
|
||||||
|
importButton.setDisable(true);
|
||||||
|
try {
|
||||||
|
Keystore keystore = importer.getKeystore(derivation, wordEntriesProperty.get(), passphraseProperty.get());
|
||||||
|
if(!dryrun) {
|
||||||
|
EventManager.get().post(new KeystoreImportEvent(keystore));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (ImportException e) {
|
||||||
|
String errorMessage = e.getMessage();
|
||||||
|
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
||||||
|
errorMessage = e.getCause().getMessage();
|
||||||
|
}
|
||||||
|
setError("Import Error", errorMessage);
|
||||||
|
importButton.setDisable(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WordEntry extends HBox {
|
||||||
|
private static List<String> wordList;
|
||||||
|
|
||||||
|
public WordEntry(int wordNumber, ObservableList<String> wordEntryList) {
|
||||||
|
super();
|
||||||
|
setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
|
setSpacing(10);
|
||||||
|
Label label = new Label((wordNumber+1) + ".");
|
||||||
|
label.setPrefWidth(20);
|
||||||
|
label.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
TextField wordField = new TextField();
|
||||||
|
wordField.setMaxWidth(100);
|
||||||
|
|
||||||
|
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
||||||
|
wordList = bip39Calculator.getWordList();
|
||||||
|
TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList));
|
||||||
|
|
||||||
|
ValidationSupport validationSupport = new ValidationSupport();
|
||||||
|
validationSupport.registerValidator(wordField, Validator.combine(
|
||||||
|
Validator.createEmptyValidator("Word is required"),
|
||||||
|
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid word", !wordList.contains(newValue))
|
||||||
|
));
|
||||||
|
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
|
||||||
|
|
||||||
|
wordField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
wordEntryList.set(wordNumber, newValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.getChildren().addAll(label, wordField);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isValid(String word) {
|
||||||
|
return wordList.contains(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class WordlistSuggestionProvider implements Callback<AutoCompletionBinding.ISuggestionRequest, Collection<String>> {
|
||||||
|
private final List<String> wordList;
|
||||||
|
|
||||||
|
public WordlistSuggestionProvider(List<String> wordList) {
|
||||||
|
this.wordList = wordList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> call(AutoCompletionBinding.ISuggestionRequest request) {
|
||||||
|
List<String> suggestions = new ArrayList<>();
|
||||||
|
if(!request.getUserText().isEmpty()) {
|
||||||
|
for(String word : wordList) {
|
||||||
|
if(word.startsWith(request.getUserText())) {
|
||||||
|
suggestions.add(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PassphraseEntry extends HBox {
|
||||||
|
public PassphraseEntry() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
setSpacing(10);
|
||||||
|
Label passphraseLabel = new Label("Passphrase:");
|
||||||
|
CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField();
|
||||||
|
passphraseProperty.bind(passphraseField.textProperty());
|
||||||
|
|
||||||
|
getChildren().addAll(passphraseLabel, passphraseField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node getDerivationEntry(List<ChildNumber> derivation) {
|
||||||
|
TextField derivationField = new TextField();
|
||||||
|
derivationField.setPromptText("Derivation path");
|
||||||
|
derivationField.setText(KeyDerivation.writePath(derivation));
|
||||||
|
HBox.setHgrow(derivationField, Priority.ALWAYS);
|
||||||
|
|
||||||
|
ValidationSupport validationSupport = new ValidationSupport();
|
||||||
|
validationSupport.registerValidator(derivationField, Validator.combine(
|
||||||
|
Validator.createEmptyValidator("Derivation is required"),
|
||||||
|
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid derivation", !KeyDerivation.isValid(newValue))
|
||||||
|
));
|
||||||
|
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
|
||||||
|
|
||||||
|
Button importDerivationButton = new Button("Import");
|
||||||
|
importDerivationButton.setOnAction(event -> {
|
||||||
|
showHideLink.setVisible(true);
|
||||||
|
setExpanded(false);
|
||||||
|
List<ChildNumber> importDerivation = KeyDerivation.parsePath(derivationField.getText());
|
||||||
|
importKeystore(importDerivation, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
derivationField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
importDerivationButton.setDisable(newValue.isEmpty() || !KeyDerivation.isValid(newValue));
|
||||||
|
});
|
||||||
|
|
||||||
|
HBox contentBox = new HBox();
|
||||||
|
contentBox.setAlignment(Pos.TOP_RIGHT);
|
||||||
|
contentBox.setSpacing(20);
|
||||||
|
contentBox.getChildren().add(derivationField);
|
||||||
|
contentBox.getChildren().add(importDerivationButton);
|
||||||
|
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||||
|
contentBox.setPrefHeight(60);
|
||||||
|
|
||||||
|
return contentBox;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@ public class Bip39 implements KeystoreMnemonicImport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WalletModel getWalletModel() {
|
public WalletModel getWalletModel() {
|
||||||
return WalletModel.SPARROW;
|
return WalletModel.SEED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -25,8 +25,12 @@ public class Bip39 implements KeystoreMnemonicImport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
||||||
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
try {
|
||||||
byte[] seed = bip39Calculator.getSeed(mnemonicWords, passphrase);
|
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
||||||
return Keystore.fromSeed(seed, derivation);
|
byte[] seed = bip39Calculator.getSeed(mnemonicWords, passphrase);
|
||||||
|
return Keystore.fromSeed(seed, derivation);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ImportException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,12 @@ public class KeystoreImportController implements Initializable {
|
||||||
public void initializeView(Wallet wallet) {
|
public void initializeView(Wallet wallet) {
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
importMenu.selectedToggleProperty().addListener((observable, oldValue, selectedToggle) -> {
|
importMenu.selectedToggleProperty().addListener((observable, oldValue, selectedToggle) -> {
|
||||||
|
if(selectedToggle == null) {
|
||||||
|
oldValue.setSelected(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
KeystoreSource importType = (KeystoreSource) selectedToggle.getUserData();
|
KeystoreSource importType = (KeystoreSource) selectedToggle.getUserData();
|
||||||
System.out.println(importType);
|
|
||||||
String fxmlName = importType.toString().toLowerCase();
|
String fxmlName = importType.toString().toLowerCase();
|
||||||
if(importType == KeystoreSource.SW_SEED || importType == KeystoreSource.SW_WATCH) {
|
if(importType == KeystoreSource.SW_SEED || importType == KeystoreSource.SW_WATCH) {
|
||||||
fxmlName = "sw";
|
fxmlName = "sw";
|
||||||
|
|
|
@ -31,8 +31,8 @@ public class KeystoreImportDialog extends Dialog<Keystore> {
|
||||||
|
|
||||||
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);
|
dialogPane.getButtonTypes().addAll(cancelButtonType);
|
||||||
dialogPane.setPrefWidth(620);
|
dialogPane.setPrefWidth(650);
|
||||||
dialogPane.setPrefHeight(500);
|
dialogPane.setPrefHeight(600);
|
||||||
|
|
||||||
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? keystore : null);
|
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? keystore : null);
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow.keystoreimport;
|
package com.sparrowwallet.sparrow.keystoreimport;
|
||||||
|
|
||||||
import com.sparrowwallet.sparrow.control.KeystoreImportAccordion;
|
import com.sparrowwallet.sparrow.control.KeystoreImportAccordion;
|
||||||
|
import com.sparrowwallet.sparrow.io.Bip39;
|
||||||
import com.sparrowwallet.sparrow.io.Electrum;
|
import com.sparrowwallet.sparrow.io.Electrum;
|
||||||
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
import com.sparrowwallet.sparrow.io.KeystoreImport;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
@ -13,7 +14,7 @@ public class SwController extends KeystoreImportDetailController {
|
||||||
private KeystoreImportAccordion importAccordion;
|
private KeystoreImportAccordion importAccordion;
|
||||||
|
|
||||||
public void initializeView() {
|
public void initializeView() {
|
||||||
List<KeystoreImport> importers = List.of(new Electrum());
|
List<KeystoreImport> importers = List.of(new Bip39(), new Electrum());
|
||||||
importAccordion.setKeystoreImporters(getMasterController().getWallet(), FXCollections.observableList(importers));
|
importAccordion.setKeystoreImporters(getMasterController().getWallet(), FXCollections.observableList(importers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,11 @@ public class WalletController extends WalletFormController implements Initializa
|
||||||
|
|
||||||
public void initializeView() {
|
public void initializeView() {
|
||||||
walletMenu.selectedToggleProperty().addListener((observable, oldValue, selectedToggle) -> {
|
walletMenu.selectedToggleProperty().addListener((observable, oldValue, selectedToggle) -> {
|
||||||
|
if(selectedToggle == null) {
|
||||||
|
oldValue.setSelected(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Function function = (Function)selectedToggle.getUserData();
|
Function function = (Function)selectedToggle.getUserData();
|
||||||
|
|
||||||
boolean existing = false;
|
boolean existing = false;
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-menu {
|
.list-menu {
|
||||||
-fx-pref-width: 130;
|
-fx-pref-width: 160;
|
||||||
-fx-background-color: #3da0e3;
|
-fx-background-color: #3da0e3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-item {
|
.list-item {
|
||||||
-fx-pref-width: 130;
|
-fx-pref-width: 160;
|
||||||
-fx-padding: 0 20 0 20;
|
-fx-padding: 0 20 0 20;
|
||||||
-fx-background-color: #3da0e3;
|
-fx-background-color: #3da0e3;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@
|
||||||
.titled-pane > .title {
|
.titled-pane > .title {
|
||||||
-fx-background-color: white;
|
-fx-background-color: white;
|
||||||
-fx-padding: 0;
|
-fx-padding: 0;
|
||||||
|
-fx-border-color: #e5e5e6;
|
||||||
|
/*-fx-border-width: 1;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.titled-pane > .title > .arrow-button {
|
.titled-pane > .title > .arrow-button {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3 KiB |
BIN
src/main/resources/image/seed.png
Normal file
BIN
src/main/resources/image/seed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
Loading…
Reference in a new issue