mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
import and support master private key keystores
This commit is contained in:
parent
702d92d4f2
commit
3fc2127337
17 changed files with 366 additions and 35 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 1aeaacaf59484c76d5bf485dabb4a632c5230032
|
Subproject commit 85e8b97a8c8d21bfbb76096285eec95d28384090
|
|
@ -787,7 +787,7 @@ public class AppController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restorePublicKeysFromSeed(Wallet wallet, Key key) throws MnemonicException {
|
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
|
//Derive xpub and master fingerprint from seed, potentially with passphrase
|
||||||
Wallet copy = wallet.copy();
|
Wallet copy = wallet.copy();
|
||||||
for(Keystore copyKeystore : copy.getKeystores()) {
|
for(Keystore copyKeystore : copy.getKeystores()) {
|
||||||
|
@ -823,6 +823,12 @@ public class AppController implements Initializable {
|
||||||
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
||||||
keystore.getSeed().setPassphrase(copyKeystore.getSeed().getPassphrase());
|
keystore.getSeed().setPassphrase(copyKeystore.getSeed().getPassphrase());
|
||||||
copyKeystore.getSeed().clear();
|
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();
|
WalletTabData walletTabData = (WalletTabData)tab.getUserData();
|
||||||
Wallet wallet = walletTabData.getWallet();
|
Wallet wallet = walletTabData.getWallet();
|
||||||
if(wallet.getKeystores().size() == 1 &&
|
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
|
//Can sign and verify
|
||||||
messageSignDialog = new MessageSignDialog(wallet);
|
messageSignDialog = new MessageSignDialog(wallet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ public class EntryCell extends TreeTableCell<Entry, Entry> {
|
||||||
actionBox.getChildren().add(receiveButton);
|
actionBox.getChildren().add(receiveButton);
|
||||||
|
|
||||||
if(nodeEntry.getWallet().getKeystores().size() == 1 &&
|
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("");
|
Button signMessageButton = new Button("");
|
||||||
Glyph signMessageGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.PEN_FANCY);
|
Glyph signMessageGlyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.PEN_FANCY);
|
||||||
signMessageGlyph.setFontSize(12);
|
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) {
|
if(wallet.getKeystores().size() != 1) {
|
||||||
throw new IllegalArgumentException("Cannot sign messages using a wallet with multiple keystores - a single key is required");
|
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");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(wallet.containsSeeds()) {
|
if(wallet.containsPrivateKeys()) {
|
||||||
if(wallet.isEncrypted()) {
|
if(wallet.isEncrypted()) {
|
||||||
EventManager.get().post(new RequestOpenWalletsEvent());
|
EventManager.get().post(new RequestOpenWalletsEvent());
|
||||||
} else {
|
} else {
|
||||||
|
@ -230,6 +230,7 @@ public class MessageSignDialog extends Dialog<ButtonBar.ButtonData> {
|
||||||
String signatureText = privKey.signMessage(message.getText().trim(), decryptedWallet.getScriptType(), null);
|
String signatureText = privKey.signMessage(message.getText().trim(), decryptedWallet.getScriptType(), null);
|
||||||
signature.clear();
|
signature.clear();
|
||||||
signature.appendText(signatureText);
|
signature.appendText(signatureText);
|
||||||
|
privKey.clear();
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.error("Could not sign message", e);
|
log.error("Could not sign message", e);
|
||||||
AppServices.showErrorDialog("Could not sign message", e.getMessage());
|
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"));
|
EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Done"));
|
||||||
Wallet decryptedWallet = decryptWalletService.getValue();
|
Wallet decryptedWallet = decryptWalletService.getValue();
|
||||||
signUnencryptedKeystore(decryptedWallet);
|
signUnencryptedKeystore(decryptedWallet);
|
||||||
|
decryptedWallet.clearPrivate();
|
||||||
});
|
});
|
||||||
decryptWalletService.setOnFailed(workerStateEvent -> {
|
decryptWalletService.setOnFailed(workerStateEvent -> {
|
||||||
EventManager.get().post(new StorageEvent(storage.getWalletFile(), TimedEvent.Action.END, "Failed"));
|
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;
|
||||||
|
}
|
||||||
|
}
|
36
src/main/java/com/sparrowwallet/sparrow/io/Bip32.java
Normal file
36
src/main/java/com/sparrowwallet/sparrow/io/Bip32.java
Normal file
|
@ -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.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||||
ek.xprv = keystore.getExtendedPrivateKey().toString(xprvHeader);
|
ek.xprv = keystore.getExtendedPrivateKey().toString(xprvHeader);
|
||||||
ek.pw_hash_version = 1;
|
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.seed = keystore.getSeed().getMnemonicString().asString();
|
||||||
ek.passphrase = keystore.getSeed().getPassphrase() == null ? null : keystore.getSeed().getPassphrase().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;
|
ew.use_encryption = false;
|
||||||
} else if(keystore.getSource() == KeystoreSource.SW_WATCH) {
|
} 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
|
@Override
|
||||||
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore);
|
JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore);
|
||||||
if(keystore.hasSeed()) {
|
if(keystore.hasPrivateKey()) {
|
||||||
jsonObject.remove("extendedPublicKey");
|
jsonObject.remove("extendedPublicKey");
|
||||||
jsonObject.getAsJsonObject("keyDerivation").remove("masterFingerprint");
|
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.FileKeystoreImportPane;
|
||||||
import com.sparrowwallet.sparrow.control.MnemonicKeystoreImportPane;
|
import com.sparrowwallet.sparrow.control.MnemonicKeystoreImportPane;
|
||||||
import com.sparrowwallet.sparrow.control.TitledDescriptionPane;
|
import com.sparrowwallet.sparrow.control.TitledDescriptionPane;
|
||||||
|
import com.sparrowwallet.sparrow.control.XprvKeystoreImportPane;
|
||||||
import com.sparrowwallet.sparrow.io.*;
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Accordion;
|
import javafx.scene.control.Accordion;
|
||||||
|
@ -14,7 +15,7 @@ public class SwController extends KeystoreImportDetailController {
|
||||||
private Accordion importAccordion;
|
private Accordion importAccordion;
|
||||||
|
|
||||||
public void initializeView() {
|
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) {
|
for(KeystoreImport importer : importers) {
|
||||||
TitledDescriptionPane importPane = null;
|
TitledDescriptionPane importPane = null;
|
||||||
|
@ -23,6 +24,8 @@ public class SwController extends KeystoreImportDetailController {
|
||||||
importPane = new FileKeystoreImportPane(getMasterController().getWallet(), (KeystoreFileImport)importer);
|
importPane = new FileKeystoreImportPane(getMasterController().getWallet(), (KeystoreFileImport)importer);
|
||||||
} else if(importer instanceof KeystoreMnemonicImport) {
|
} else if(importer instanceof KeystoreMnemonicImport) {
|
||||||
importPane = new MnemonicKeystoreImportPane(getMasterController().getWallet(), (KeystoreMnemonicImport)importer);
|
importPane = new MnemonicKeystoreImportPane(getMasterController().getWallet(), (KeystoreMnemonicImport)importer);
|
||||||
|
} else if(importer instanceof KeystoreXprvImport) {
|
||||||
|
importPane = new XprvKeystoreImportPane(getMasterController().getWallet(), (KeystoreXprvImport)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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -698,7 +698,7 @@ public class HeadersController extends TransactionFormController implements Init
|
||||||
}
|
}
|
||||||
|
|
||||||
private void signSoftwareKeystores() {
|
private void signSoftwareKeystores() {
|
||||||
if(headersForm.getSigningWallet().getKeystores().stream().noneMatch(Keystore::hasSeed)) {
|
if(headersForm.getSigningWallet().getKeystores().stream().noneMatch(Keystore::hasPrivateKey)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,7 @@ import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||||
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.control.QRDisplayDialog;
|
import com.sparrowwallet.sparrow.control.*;
|
||||||
import com.sparrowwallet.sparrow.control.QRScanDialog;
|
|
||||||
import com.sparrowwallet.sparrow.control.SeedDisplayDialog;
|
|
||||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
|
||||||
import com.sparrowwallet.sparrow.event.StorageEvent;
|
import com.sparrowwallet.sparrow.event.StorageEvent;
|
||||||
import com.sparrowwallet.sparrow.event.TimedEvent;
|
import com.sparrowwallet.sparrow.event.TimedEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
|
@ -57,6 +54,9 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
@FXML
|
@FXML
|
||||||
private Button viewSeedButton;
|
private Button viewSeedButton;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button viewKeyButton;
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Button importButton;
|
private Button importButton;
|
||||||
|
|
||||||
|
@ -106,6 +106,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
}
|
}
|
||||||
|
|
||||||
viewSeedButton.managedProperty().bind(viewSeedButton.visibleProperty());
|
viewSeedButton.managedProperty().bind(viewSeedButton.visibleProperty());
|
||||||
|
viewKeyButton.managedProperty().bind(viewKeyButton.visibleProperty());
|
||||||
scanXpubQR.managedProperty().bind(scanXpubQR.visibleProperty());
|
scanXpubQR.managedProperty().bind(scanXpubQR.visibleProperty());
|
||||||
displayXpubQR.managedProperty().bind(displayXpubQR.visibleProperty());
|
displayXpubQR.managedProperty().bind(displayXpubQR.visibleProperty());
|
||||||
displayXpubQR.visibleProperty().bind(scanXpubQR.visibleProperty().not());
|
displayXpubQR.visibleProperty().bind(scanXpubQR.visibleProperty().not());
|
||||||
|
@ -246,7 +247,8 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
private void updateType() {
|
private void updateType() {
|
||||||
type.setText(getTypeLabel(keystore));
|
type.setText(getTypeLabel(keystore));
|
||||||
type.setGraphic(getTypeIcon(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.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"));
|
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.setLabel(importedKeystore.getLabel());
|
||||||
keystore.setKeyDerivation(importedKeystore.getKeyDerivation());
|
keystore.setKeyDerivation(importedKeystore.getKeyDerivation());
|
||||||
keystore.setExtendedPublicKey(importedKeystore.getExtendedPublicKey());
|
keystore.setExtendedPublicKey(importedKeystore.getExtendedPublicKey());
|
||||||
|
keystore.setMasterPrivateExtendedKey(importedKeystore.getMasterPrivateExtendedKey());
|
||||||
keystore.setSeed(importedKeystore.getSeed());
|
keystore.setSeed(importedKeystore.getSeed());
|
||||||
|
|
||||||
updateType();
|
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);
|
int keystoreIndex = getWalletForm().getWallet().getKeystores().indexOf(keystore);
|
||||||
Wallet copy = getWalletForm().getWallet().copy();
|
Wallet copy = getWalletForm().getWallet().copy();
|
||||||
|
|
||||||
|
@ -345,7 +348,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
decryptWalletService.setOnSucceeded(workerStateEvent -> {
|
decryptWalletService.setOnSucceeded(workerStateEvent -> {
|
||||||
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Done"));
|
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Done"));
|
||||||
Wallet decryptedWallet = decryptWalletService.getValue();
|
Wallet decryptedWallet = decryptWalletService.getValue();
|
||||||
showSeed(decryptedWallet.getKeystores().get(keystoreIndex));
|
showPrivate(decryptedWallet.getKeystores().get(keystoreIndex));
|
||||||
});
|
});
|
||||||
decryptWalletService.setOnFailed(workerStateEvent -> {
|
decryptWalletService.setOnFailed(workerStateEvent -> {
|
||||||
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Failed"));
|
EventManager.get().post(new StorageEvent(getWalletForm().getWalletFile(), TimedEvent.Action.END, "Failed"));
|
||||||
|
@ -355,13 +358,18 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
decryptWalletService.start();
|
decryptWalletService.start();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showSeed(keystore);
|
showPrivate(keystore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSeed(Keystore keystore) {
|
private void showPrivate(Keystore keystore) {
|
||||||
|
if(keystore.hasSeed()) {
|
||||||
SeedDisplayDialog dlg = new SeedDisplayDialog(keystore);
|
SeedDisplayDialog dlg = new SeedDisplayDialog(keystore);
|
||||||
dlg.showAndWait();
|
dlg.showAndWait();
|
||||||
|
} else if(keystore.hasMasterPrivateExtendedKey()) {
|
||||||
|
MasterKeyDisplayDialog dlg = new MasterKeyDisplayDialog(keystore);
|
||||||
|
dlg.showAndWait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scanXpubQR(ActionEvent event) {
|
public void scanXpubQR(ActionEvent event) {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<Fieldset inputGrow="SOMETIMES" text="">
|
<Fieldset inputGrow="SOMETIMES" text="">
|
||||||
<Field text="Type:">
|
<Field text="Type:">
|
||||||
<Label fx:id="type" graphicTextGap="8"/>
|
<Label fx:id="type" graphicTextGap="8"/>
|
||||||
<Button fx:id="viewSeedButton" text="View Seed..." graphicTextGap="5" onAction="#showSeed">
|
<Button fx:id="viewSeedButton" text="View Seed..." graphicTextGap="5" onAction="#showPrivate">
|
||||||
<graphic>
|
<graphic>
|
||||||
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="KEY" />
|
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="KEY" />
|
||||||
</graphic>
|
</graphic>
|
||||||
|
@ -30,6 +30,14 @@
|
||||||
<Tooltip text="View mnemonic seed words"/>
|
<Tooltip text="View mnemonic seed words"/>
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button fx:id="viewKeyButton" text="View Key..." graphicTextGap="5" onAction="#showPrivate">
|
||||||
|
<graphic>
|
||||||
|
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="KEY" />
|
||||||
|
</graphic>
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="View master private key"/>
|
||||||
|
</tooltip>
|
||||||
|
</Button>
|
||||||
<Pane HBox.hgrow="ALWAYS" />
|
<Pane HBox.hgrow="ALWAYS" />
|
||||||
<Button fx:id="importButton" text="Import..." graphicTextGap="5" onAction="#importKeystore">
|
<Button fx:id="importButton" text="Import..." graphicTextGap="5" onAction="#importKeystore">
|
||||||
<graphic>
|
<graphic>
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("wsh(sortedmulti(2,coldcard1,coldcard2,coldcard3,coldcard4))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("wsh(sortedmulti(2,coldcard1,coldcard2,coldcard3,coldcard4))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertTrue(wallet.isValid());
|
Assert.assertTrue(wallet.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard1,coldcard2,coldcard3,coldcard4)))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard1,coldcard2,coldcard3,coldcard4)))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertTrue(wallet.isValid());
|
Assert.assertTrue(wallet.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(3, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(3, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("wsh(sortedmulti(3,coldcard1,coldcard2,coldcard3))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("wsh(sortedmulti(3,coldcard1,coldcard2,coldcard3))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("06b57041", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("06b57041", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/48'/0'/0'/2'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/0'/0'/2'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
|
|
@ -24,7 +24,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wpkh(trezortest))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wpkh(trezortest))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -44,7 +44,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wpkh(trezortest))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wpkh(trezortest))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -59,7 +59,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard6ba6cfd,coldcard747b698,coldcard7bb026b,coldcard0f05694)))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard6ba6cfd,coldcard747b698,coldcard7bb026b,coldcard0f05694)))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -82,7 +82,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard6ba6cfd,coldcard747b698,coldcard7bb026b,coldcard0f05694)))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wsh(sortedmulti(2,coldcard6ba6cfd,coldcard747b698,coldcard7bb026b,coldcard0f05694)))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -100,7 +100,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("wpkh(electrum)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("wpkh(electrum)", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("f881eac5", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("f881eac5", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub69iSRreMB6fu24sU8Tdxv7yYGqzPkDwPkwqUfKJTxW3p8afW7XvTewVCapuX3dQjdD197iF65WcjYaNpFbwWT3RyuZ1KJ3ToJNVWKWyAJ6f", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub69iSRreMB6fu24sU8Tdxv7yYGqzPkDwPkwqUfKJTxW3p8afW7XvTewVCapuX3dQjdD197iF65WcjYaNpFbwWT3RyuZ1KJ3ToJNVWKWyAJ6f", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -120,7 +120,7 @@ public class ElectrumTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("wpkh(electrum)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("wpkh(electrum)", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("59c5474f", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("59c5474f", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub68YmVxWbxqjpxbUqqaPrgkBQPBSJuq6gEaL22uuytSEojtS2x5eLPN2uspUuyigtnMkoHrFSF1KwoXPwjzuaUjErUwztxfHquAwuaQhSd9J", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub68YmVxWbxqjpxbUqqaPrgkBQPBSJuq6gEaL22uuytSEojtS2x5eLPN2uspUuyigtnMkoHrFSF1KwoXPwjzuaUjErUwztxfHquAwuaQhSd9J", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class SpecterDesktopTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("sh(wpkh(keystore1))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("sh(wpkh(keystore1))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("4df18faa", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("4df18faa", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/49'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub6BgwyseZdeGJj2vB3FPHSGPxR1LLkr8AsAJqedrgjwBXKXXVWkH31fhwtQXgrM7uMrWjLwXhuDhhenNAh5eBdUSjrHkrKfaXutcJdAfgQ8D", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub6BgwyseZdeGJj2vB3FPHSGPxR1LLkr8AsAJqedrgjwBXKXXVWkH31fhwtQXgrM7uMrWjLwXhuDhhenNAh5eBdUSjrHkrKfaXutcJdAfgQ8D", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
@ -30,7 +30,7 @@ public class SpecterDesktopTest extends IoTest {
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
Assert.assertEquals(3, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals(3, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
Assert.assertEquals("wsh(sortedmulti(3,keystore1,keystore2,keystore3,keystore4))", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals("wsh(sortedmulti(3,keystore1,keystore2,keystore3,keystore4))", wallet.getDefaultPolicy().getMiniscript().getScript().toLowerCase());
|
||||||
Assert.assertEquals("ca9a2b19", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("ca9a2b19", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("m/48'/0'/0'/2'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/0'/0'/2'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("xpub6EhbRDNhmMX863W8RujJyAMw1vtM4MHXnsk14paK1ZBEH75k44gWqfaraXCrzg6w9pzC2yLc28vAdUfpB9ShuEB1HA9xMs6BjmRi4PKbt1K", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
Assert.assertEquals("xpub6EhbRDNhmMX863W8RujJyAMw1vtM4MHXnsk14paK1ZBEH75k44gWqfaraXCrzg6w9pzC2yLc28vAdUfpB9ShuEB1HA9xMs6BjmRi4PKbt1K", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
|
Loading…
Reference in a new issue