mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2026-08-09 16:24:53 +00:00
import and support master private key keystores
This commit is contained in:
@@ -787,7 +787,7 @@ public class AppController implements Initializable {
|
||||
}
|
||||
|
||||
private void restorePublicKeysFromSeed(Wallet wallet, Key key) throws MnemonicException {
|
||||
if(wallet.containsSeeds()) {
|
||||
if(wallet.containsPrivateKeys()) {
|
||||
//Derive xpub and master fingerprint from seed, potentially with passphrase
|
||||
Wallet copy = wallet.copy();
|
||||
for(Keystore copyKeystore : copy.getKeystores()) {
|
||||
@@ -823,6 +823,12 @@ public class AppController implements Initializable {
|
||||
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
||||
keystore.getSeed().setPassphrase(copyKeystore.getSeed().getPassphrase());
|
||||
copyKeystore.getSeed().clear();
|
||||
} else if(keystore.hasMasterPrivateExtendedKey()) {
|
||||
Keystore copyKeystore = copy.getKeystores().get(i);
|
||||
Keystore derivedKeystore = Keystore.fromMasterPrivateExtendedKey(copyKeystore.getMasterPrivateExtendedKey(), copyKeystore.getKeyDerivation().getDerivation());
|
||||
keystore.setKeyDerivation(derivedKeystore.getKeyDerivation());
|
||||
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
||||
copyKeystore.getMasterPrivateKey().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -966,7 +972,7 @@ public class AppController implements Initializable {
|
||||
WalletTabData walletTabData = (WalletTabData)tab.getUserData();
|
||||
Wallet wallet = walletTabData.getWallet();
|
||||
if(wallet.getKeystores().size() == 1 &&
|
||||
(wallet.getKeystores().get(0).hasSeed() || wallet.getKeystores().get(0).getSource() == KeystoreSource.HW_USB)) {
|
||||
(wallet.getKeystores().get(0).hasPrivateKey() || wallet.getKeystores().get(0).getSource() == KeystoreSource.HW_USB)) {
|
||||
//Can sign and verify
|
||||
messageSignDialog = new MessageSignDialog(wallet);
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
|
||||
actionBox.getChildren().add(receiveButton);
|
||||
|
||||
if(nodeEntry.getWallet().getKeystores().size() == 1 &&
|
||||
(nodeEntry.getWallet().getKeystores().get(0).hasSeed() || nodeEntry.getWallet().getKeystores().get(0).getSource() == KeystoreSource.HW_USB)) {
|
||||
(nodeEntry.getWallet().getKeystores().get(0).hasPrivateKey() || nodeEntry.getWallet().getKeystores().get(0).getSource() == KeystoreSource.HW_USB)) {
|
||||
Button signMessageButton = new Button("");
|
||||
Glyph signMessageGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.PEN_FANCY);
|
||||
signMessageGlyph.setFontSize(12);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
public class MasterKeyDisplayDialog extends Dialog<Void> {
|
||||
public MasterKeyDisplayDialog(Keystore decryptedKeystore) {
|
||||
final DialogPane dialogPane = getDialogPane();
|
||||
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
|
||||
AppServices.setStageIcon(dialogPane.getScene().getWindow());
|
||||
|
||||
StackPane stackPane = new StackPane();
|
||||
dialogPane.setContent(stackPane);
|
||||
|
||||
AnchorPane anchorPane = new AnchorPane();
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
scrollPane.getStyleClass().add("edge-to-edge");
|
||||
scrollPane.setPrefHeight(200);
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
anchorPane.getChildren().add(scrollPane);
|
||||
scrollPane.setFitToWidth(true);
|
||||
AnchorPane.setLeftAnchor(scrollPane, 0.0);
|
||||
AnchorPane.setRightAnchor(scrollPane, 0.0);
|
||||
|
||||
Accordion keystoreAccordion = new Accordion();
|
||||
scrollPane.setContent(keystoreAccordion);
|
||||
|
||||
XprvKeystoreImportPane keystorePane = new XprvKeystoreImportPane(decryptedKeystore);
|
||||
keystorePane.setAnimated(false);
|
||||
keystoreAccordion.getPanes().add(keystorePane);
|
||||
|
||||
stackPane.getChildren().addAll(anchorPane);
|
||||
|
||||
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
dialogPane.getButtonTypes().addAll(cancelButtonType);
|
||||
|
||||
dialogPane.setPrefWidth(500);
|
||||
dialogPane.setPrefHeight(260);
|
||||
|
||||
Platform.runLater(() -> keystoreAccordion.setExpandedPane(keystorePane));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
|
||||
if(wallet.getKeystores().size() != 1) {
|
||||
throw new IllegalArgumentException("Cannot sign messages using a wallet with multiple keystores - a single key is required");
|
||||
}
|
||||
if(!wallet.getKeystores().get(0).hasSeed() && wallet.getKeystores().get(0).getSource() != KeystoreSource.HW_USB) {
|
||||
if(!wallet.getKeystores().get(0).hasPrivateKey() && wallet.getKeystores().get(0).getSource() != KeystoreSource.HW_USB) {
|
||||
throw new IllegalArgumentException("Cannot sign messages using a wallet without a seed or USB keystore");
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
|
||||
return;
|
||||
}
|
||||
|
||||
if(wallet.containsSeeds()) {
|
||||
if(wallet.containsPrivateKeys()) {
|
||||
if(wallet.isEncrypted()) {
|
||||
EventManager.get().post(new RequestOpenWalletsEvent());
|
||||
} else {
|
||||
@@ -230,6 +230,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
|
||||
String signatureText = privKey.signMessage(message.getText().trim(), decryptedWallet.getScriptType(), null);
|
||||
signature.clear();
|
||||
signature.appendText(signatureText);
|
||||
privKey.clear();
|
||||
} catch(Exception e) {
|
||||
log.error("Could not sign message", e);
|
||||
AppServices.showErrorDialog("Could not sign message", e.getMessage());
|
||||
@@ -316,6 +317,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Done"));
|
||||
Wallet decryptedWallet = decryptWalletService.getValue();
|
||||
signUnencryptedKeystore(decryptedWallet);
|
||||
decryptedWallet.clearPrivate();
|
||||
});
|
||||
decryptWalletService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Failed"));
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.MnemonicException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.ImportException;
|
||||
import com.sparrowwallet.sparrow.io.KeystoreXprvImport;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import org.controlsfx.validation.ValidationResult;
|
||||
import org.controlsfx.validation.ValidationSupport;
|
||||
import org.controlsfx.validation.Validator;
|
||||
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class XprvKeystoreImportPane extends TitledDescriptionPane {
|
||||
protected final Wallet wallet;
|
||||
protected final KeystoreXprvImport importer;
|
||||
|
||||
private Button enterXprvButton;
|
||||
private SplitMenuButton importButton;
|
||||
|
||||
private ExtendedKey xprv;
|
||||
|
||||
public XprvKeystoreImportPane(Wallet wallet, KeystoreXprvImport importer) {
|
||||
super(importer.getName(), "Extended key import", importer.getKeystoreImportDescription(), "image/" + importer.getWalletModel().getType() + ".png");
|
||||
this.wallet = wallet;
|
||||
this.importer = importer;
|
||||
|
||||
createImportButton();
|
||||
buttonBox.getChildren().add(importButton);
|
||||
}
|
||||
|
||||
public XprvKeystoreImportPane(Keystore keystore) {
|
||||
super("Master Private Key", "BIP32 key", "", "image/" + WalletModel.SEED.getType() + ".png");
|
||||
this.wallet = null;
|
||||
this.importer = null;
|
||||
|
||||
try {
|
||||
this.xprv = keystore.getExtendedMasterPrivateKey();
|
||||
} catch(MnemonicException e) {
|
||||
//can't happen
|
||||
}
|
||||
|
||||
showHideLink.setVisible(false);
|
||||
buttonBox.getChildren().clear();
|
||||
setContent(getXprvEntry(true));
|
||||
setExpanded(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createButton() {
|
||||
enterXprvButton = new Button("Enter Private Key");
|
||||
enterXprvButton.managedProperty().bind(enterXprvButton.visibleProperty());
|
||||
enterXprvButton.setOnAction(event -> {
|
||||
enterXprvButton.setDisable(true);
|
||||
enterXprv();
|
||||
});
|
||||
|
||||
return enterXprvButton;
|
||||
}
|
||||
|
||||
private void createImportButton() {
|
||||
importButton = new SplitMenuButton();
|
||||
importButton.setAlignment(Pos.CENTER_RIGHT);
|
||||
importButton.setText("Import Keystore");
|
||||
importButton.getStyleClass().add("default-button");
|
||||
importButton.setOnAction(event -> {
|
||||
importButton.setDisable(true);
|
||||
importKeystore(wallet.getScriptType().getDefaultDerivation());
|
||||
});
|
||||
String[] accounts = new String[] {"Import Default Account #0", "Import Account #1", "Import Account #2", "Import Account #3", "Import Account #4", "Import Account #5", "Import Account #6", "Import Account #7", "Import Account #8", "Import 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.setVisible(false);
|
||||
}
|
||||
|
||||
private void enterXprv() {
|
||||
setDescription("Enter master private key");
|
||||
showHideLink.setVisible(false);
|
||||
setContent(getXprvEntry(false));
|
||||
setExpanded(true);
|
||||
}
|
||||
|
||||
private void importKeystore(List<ChildNumber> derivation) {
|
||||
importButton.setDisable(true);
|
||||
try {
|
||||
Keystore keystore = importer.getKeystore(derivation, xprv);
|
||||
EventManager.get().post(new KeystoreImportEvent(keystore));
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private Node getXprvEntry(boolean displayOnly) {
|
||||
TextArea xprvField = new TextArea();
|
||||
xprvField.setPrefRowCount(2);
|
||||
xprvField.setWrapText(true);
|
||||
xprvField.getStyleClass().add("fixed-width");
|
||||
xprvField.setPromptText(ExtendedKey.Header.fromScriptType(ScriptType.P2PKH, true).getName() + (wallet != null ? "/" + ExtendedKey.Header.fromScriptType(wallet.getScriptType(), true).getName() : "") + "...");
|
||||
HBox.setHgrow(xprvField, Priority.ALWAYS);
|
||||
|
||||
if(xprv != null) {
|
||||
xprvField.setText(xprv.toString());
|
||||
}
|
||||
if(displayOnly) {
|
||||
xprvField.setEditable(false);
|
||||
}
|
||||
|
||||
ValidationSupport validationSupport = new ValidationSupport();
|
||||
validationSupport.registerValidator(xprvField, Validator.combine(
|
||||
Validator.createEmptyValidator("xprv is required"),
|
||||
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid private key", !ExtendedKey.isValid(newValue) || ExtendedKey.fromDescriptor(newValue).getKey().isPubKeyOnly())
|
||||
));
|
||||
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
|
||||
|
||||
Button importXprvButton = new Button("Import");
|
||||
importXprvButton.setMinWidth(80);
|
||||
importXprvButton.setDisable(true);
|
||||
importXprvButton.setOnAction(event -> {
|
||||
enterXprvButton.setVisible(false);
|
||||
importButton.setVisible(true);
|
||||
setDescription("Ready to import");
|
||||
xprv = ExtendedKey.fromDescriptor(xprvField.getText());
|
||||
setContent(getDerivationEntry(wallet.getScriptType().getDefaultDerivation()));
|
||||
});
|
||||
|
||||
xprvField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
importXprvButton.setDisable(newValue.isEmpty() || !ExtendedKey.isValid(newValue) || ExtendedKey.fromDescriptor(newValue).getKey().isPubKeyOnly());
|
||||
});
|
||||
|
||||
HBox contentBox = new HBox();
|
||||
contentBox.setAlignment(Pos.TOP_RIGHT);
|
||||
contentBox.setSpacing(20);
|
||||
contentBox.getChildren().add(xprvField);
|
||||
if(!displayOnly) {
|
||||
contentBox.getChildren().add(importXprvButton);
|
||||
}
|
||||
contentBox.setPadding(new Insets(10, 30, 10, 30));
|
||||
contentBox.setPrefHeight(100);
|
||||
|
||||
return contentBox;
|
||||
}
|
||||
|
||||
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 Custom Derivation Keystore");
|
||||
importDerivationButton.setDisable(true);
|
||||
importDerivationButton.setOnAction(event -> {
|
||||
showHideLink.setVisible(true);
|
||||
setExpanded(false);
|
||||
List<ChildNumber> importDerivation = KeyDerivation.parsePath(derivationField.getText());
|
||||
importKeystore(importDerivation);
|
||||
});
|
||||
|
||||
derivationField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
importButton.setDisable(newValue.isEmpty() || !KeyDerivation.isValid(newValue) || !KeyDerivation.parsePath(newValue).equals(derivation));
|
||||
importDerivationButton.setDisable(newValue.isEmpty() || !KeyDerivation.isValid(newValue) || KeyDerivation.parsePath(newValue).equals(derivation));
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.MasterPrivateExtendedKey;
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Bip32 implements KeystoreXprvImport {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Master Private Key (BIP32)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public WalletModel getWalletModel() {
|
||||
return WalletModel.SEED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeystoreImportDescription() {
|
||||
return "Import an extended master private key (BIP 32 xprv)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Keystore getKeystore(List<ChildNumber> derivation, ExtendedKey xprv) throws ImportException {
|
||||
try {
|
||||
MasterPrivateExtendedKey masterPrivateExtendedKey = new MasterPrivateExtendedKey(xprv.getKey().getPrivKeyBytes(), xprv.getKey().getChainCode());
|
||||
return Keystore.fromMasterPrivateExtendedKey(masterPrivateExtendedKey, derivation);
|
||||
} catch(Exception e) {
|
||||
throw new ImportException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,11 +328,11 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
||||
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||
ek.xprv = keystore.getExtendedPrivateKey().toString(xprvHeader);
|
||||
ek.pw_hash_version = 1;
|
||||
if(keystore.getSeed().getType() == DeterministicSeed.Type.ELECTRUM) {
|
||||
if(keystore.getSeed() == null || keystore.getSeed().getType() == DeterministicSeed.Type.BIP39) {
|
||||
ew.seed_type = "bip39";
|
||||
} else if(keystore.getSeed().getType() == DeterministicSeed.Type.ELECTRUM) {
|
||||
ek.seed = keystore.getSeed().getMnemonicString().asString();
|
||||
ek.passphrase = keystore.getSeed().getPassphrase() == null ? null : keystore.getSeed().getPassphrase().asString();
|
||||
} else if(keystore.getSeed().getType() == DeterministicSeed.Type.BIP39) {
|
||||
ew.seed_type = "bip39";
|
||||
}
|
||||
ew.use_encryption = false;
|
||||
} else if(keystore.getSource() == KeystoreSource.SW_WATCH) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KeystoreXprvImport extends KeystoreImport {
|
||||
Keystore getKeystore(List<ChildNumber> derivation, ExtendedKey xprv) throws ImportException;
|
||||
}
|
||||
@@ -527,7 +527,7 @@ public class Storage {
|
||||
@Override
|
||||
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
||||
JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore);
|
||||
if(keystore.hasSeed()) {
|
||||
if(keystore.hasPrivateKey()) {
|
||||
jsonObject.remove("extendedPublicKey");
|
||||
jsonObject.getAsJsonObject("keyDerivation").remove("masterFingerprint");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.keystoreimport;
|
||||
import com.sparrowwallet.sparrow.control.FileKeystoreImportPane;
|
||||
import com.sparrowwallet.sparrow.control.MnemonicKeystoreImportPane;
|
||||
import com.sparrowwallet.sparrow.control.TitledDescriptionPane;
|
||||
import com.sparrowwallet.sparrow.control.XprvKeystoreImportPane;
|
||||
import com.sparrowwallet.sparrow.io.*;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Accordion;
|
||||
@@ -14,7 +15,7 @@ public class SwController extends KeystoreImportDetailController {
|
||||
private Accordion importAccordion;
|
||||
|
||||
public void initializeView() {
|
||||
List<KeystoreImport> importers = List.of(new Bip39(), new Electrum());
|
||||
List<KeystoreImport> importers = List.of(new Bip39(), new Electrum(), new Bip32());
|
||||
|
||||
for(KeystoreImport importer : importers) {
|
||||
TitledDescriptionPane importPane = null;
|
||||
@@ -23,6 +24,8 @@ public class SwController extends KeystoreImportDetailController {
|
||||
importPane = new FileKeystoreImportPane(getMasterController().getWallet(), (KeystoreFileImport)importer);
|
||||
} else if(importer instanceof KeystoreMnemonicImport) {
|
||||
importPane = new MnemonicKeystoreImportPane(getMasterController().getWallet(), (KeystoreMnemonicImport)importer);
|
||||
} else if(importer instanceof KeystoreXprvImport) {
|
||||
importPane = new XprvKeystoreImportPane(getMasterController().getWallet(), (KeystoreXprvImport)importer);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not create ImportPane for importer of type " + importer.getClass());
|
||||
}
|
||||
|
||||
@@ -698,7 +698,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||
}
|
||||
|
||||
private void signSoftwareKeystores() {
|
||||
if(headersForm.getSigningWallet().getKeystores().stream().noneMatch(Keystore::hasSeed)) {
|
||||
if(headersForm.getSigningWallet().getKeystores().stream().noneMatch(Keystore::hasPrivateKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,7 @@ import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.AppServices;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.control.QRDisplayDialog;
|
||||
import com.sparrowwallet.sparrow.control.QRScanDialog;
|
||||
import com.sparrowwallet.sparrow.control.SeedDisplayDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||
import com.sparrowwallet.sparrow.event.TimedEvent;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
@@ -57,6 +54,9 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
@FXML
|
||||
private Button viewSeedButton;
|
||||
|
||||
@FXML
|
||||
private Button viewKeyButton;
|
||||
|
||||
@FXML
|
||||
private Button importButton;
|
||||
|
||||
@@ -106,6 +106,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
}
|
||||
|
||||
viewSeedButton.managedProperty().bind(viewSeedButton.visibleProperty());
|
||||
viewKeyButton.managedProperty().bind(viewKeyButton.visibleProperty());
|
||||
scanXpubQR.managedProperty().bind(scanXpubQR.visibleProperty());
|
||||
displayXpubQR.managedProperty().bind(displayXpubQR.visibleProperty());
|
||||
displayXpubQR.visibleProperty().bind(scanXpubQR.visibleProperty().not());
|
||||
@@ -246,7 +247,8 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
private void updateType() {
|
||||
type.setText(getTypeLabel(keystore));
|
||||
type.setGraphic(getTypeIcon(keystore));
|
||||
viewSeedButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED);
|
||||
viewSeedButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasSeed());
|
||||
viewKeyButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasMasterPrivateExtendedKey());
|
||||
|
||||
importButton.setText(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import..." : "Replace...");
|
||||
importButton.setTooltip(new Tooltip(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import a keystore from an external source" : "Replace this keystore with another source"));
|
||||
@@ -317,6 +319,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
keystore.setLabel(importedKeystore.getLabel());
|
||||
keystore.setKeyDerivation(importedKeystore.getKeyDerivation());
|
||||
keystore.setExtendedPublicKey(importedKeystore.getExtendedPublicKey());
|
||||
keystore.setMasterPrivateExtendedKey(importedKeystore.getMasterPrivateExtendedKey());
|
||||
keystore.setSeed(importedKeystore.getSeed());
|
||||
|
||||
updateType();
|
||||
@@ -333,7 +336,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
}
|
||||
}
|
||||
|
||||
public void showSeed(ActionEvent event) {
|
||||
public void showPrivate(ActionEvent event) {
|
||||
int keystoreIndex = getWalletForm().getWallet().getKeystores().indexOf(keystore);
|
||||
Wallet copy = getWalletForm().getWallet().copy();
|
||||
|
||||
@@ -345,7 +348,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
decryptWalletService.setOnSucceeded(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Done"));
|
||||
Wallet decryptedWallet = decryptWalletService.getValue();
|
||||
showSeed(decryptedWallet.getKeystores().get(keystoreIndex));
|
||||
showPrivate(decryptedWallet.getKeystores().get(keystoreIndex));
|
||||
});
|
||||
decryptWalletService.setOnFailed(workerStateEvent -> {
|
||||
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Failed"));
|
||||
@@ -355,13 +358,18 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||
decryptWalletService.start();
|
||||
}
|
||||
} else {
|
||||
showSeed(keystore);
|
||||
showPrivate(keystore);
|
||||
}
|
||||
}
|
||||
|
||||
private void showSeed(Keystore keystore) {
|
||||
SeedDisplayDialog dlg = new SeedDisplayDialog(keystore);
|
||||
dlg.showAndWait();
|
||||
private void showPrivate(Keystore keystore) {
|
||||
if(keystore.hasSeed()) {
|
||||
SeedDisplayDialog dlg = new SeedDisplayDialog(keystore);
|
||||
dlg.showAndWait();
|
||||
} else if(keystore.hasMasterPrivateExtendedKey()) {
|
||||
MasterKeyDisplayDialog dlg = new MasterKeyDisplayDialog(keystore);
|
||||
dlg.showAndWait();
|
||||
}
|
||||
}
|
||||
|
||||
public void scanXpubQR(ActionEvent event) {
|
||||
|
||||
Reference in New Issue
Block a user