mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-23 20:36:44 +00:00
wallet export, settings fixes
This commit is contained in:
parent
bb2ec1882d
commit
d0e5da0ec8
21 changed files with 387 additions and 59 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 766a986abb72ea84317a405b93055abc3d1eaf22
|
||||
Subproject commit f6414a447550eb87a871c54c7e376713cae8eb9c
|
|
@ -13,13 +13,8 @@ import com.sparrowwallet.drongo.protocol.Transaction;
|
|||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.control.TextAreaDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletImportDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletNameDialog;
|
||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||
import com.sparrowwallet.sparrow.event.TabEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionTabChangedEvent;
|
||||
import com.sparrowwallet.sparrow.event.TransactionTabSelectedEvent;
|
||||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
import com.sparrowwallet.sparrow.io.FileType;
|
||||
import com.sparrowwallet.sparrow.io.IOUtils;
|
||||
import com.sparrowwallet.sparrow.io.Storage;
|
||||
|
@ -46,6 +41,9 @@ public class AppController implements Initializable {
|
|||
private static final String TRANSACTION_TAB_TYPE = "transaction";
|
||||
public static final String DRAG_OVER_CLASS = "drag-over";
|
||||
|
||||
@FXML
|
||||
private MenuItem exportWallet;
|
||||
|
||||
@FXML
|
||||
private CheckMenuItem showTxHex;
|
||||
|
||||
|
@ -96,12 +94,19 @@ public class AppController implements Initializable {
|
|||
TabData tabData = (TabData)selectedTab.getUserData();
|
||||
if(tabData.getType() == TabData.TabType.TRANSACTION) {
|
||||
EventManager.get().post(new TransactionTabSelectedEvent(selectedTab));
|
||||
exportWallet.setDisable(true);
|
||||
showTxHex.setDisable(false);
|
||||
} else if(tabData.getType() == TabData.TabType.WALLET) {
|
||||
EventManager.get().post(new WalletTabSelectedEvent(selectedTab));
|
||||
exportWallet.setDisable(false);
|
||||
showTxHex.setDisable(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
showTxHex.setSelected(true);
|
||||
showTxHexProperty = true;
|
||||
exportWallet.setDisable(true);
|
||||
|
||||
//addWalletTab("newWallet", new Wallet(PolicyType.SINGLE, ScriptType.P2WPKH));
|
||||
}
|
||||
|
@ -269,6 +274,19 @@ public class AppController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
public void exportWallet(ActionEvent event) {
|
||||
Tab selectedTab = tabs.getSelectionModel().getSelectedItem();
|
||||
TabData tabData = (TabData)selectedTab.getUserData();
|
||||
if(tabData.getType() == TabData.TabType.WALLET) {
|
||||
WalletTabData walletTabData = (WalletTabData)tabData;
|
||||
WalletExportDialog dlg = new WalletExportDialog(walletTabData.getWallet());
|
||||
Optional<Wallet> wallet = dlg.showAndWait();
|
||||
if(wallet.isPresent()) {
|
||||
//Successful export
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Tab addWalletTab(File walletFile, ECKey encryptionPubKey, Wallet wallet) {
|
||||
try {
|
||||
String name = walletFile.getName();
|
||||
|
@ -276,7 +294,7 @@ public class AppController implements Initializable {
|
|||
name = name.substring(0, name.lastIndexOf('.'));
|
||||
}
|
||||
Tab tab = new Tab(name);
|
||||
TabData tabData = new TabData(TabData.TabType.WALLET);
|
||||
TabData tabData = new WalletTabData(TabData.TabType.WALLET, wallet, walletFile);
|
||||
tab.setUserData(tabData);
|
||||
tab.setContextMenu(getTabContextMenu(tab));
|
||||
tab.setClosable(true);
|
||||
|
@ -351,7 +369,7 @@ public class AppController implements Initializable {
|
|||
}
|
||||
|
||||
Tab tab = new Tab(tabName);
|
||||
TabData tabData = new TabData(TabData.TabType.TRANSACTION);
|
||||
TabData tabData = new TransactionTabData(TabData.TabType.TRANSACTION, transaction);
|
||||
tab.setUserData(tabData);
|
||||
tab.setContextMenu(getTabContextMenu(tab));
|
||||
tab.setClosable(true);
|
||||
|
@ -397,7 +415,7 @@ public class AppController implements Initializable {
|
|||
}
|
||||
|
||||
@Subscribe
|
||||
public void tabSelected(TabEvent event) {
|
||||
public void tabSelected(TabSelectedEvent event) {
|
||||
Tab selectedTab = event.getTab();
|
||||
String tabType = (String)selectedTab.getUserData();
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package com.sparrowwallet.sparrow;
|
||||
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
|
||||
public class TransactionTabData extends TabData {
|
||||
private Transaction transaction;
|
||||
|
||||
public TransactionTabData(TabType type, Transaction transaction) {
|
||||
super(type);
|
||||
this.transaction = transaction;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
}
|
35
src/main/java/com/sparrowwallet/sparrow/WalletTabData.java
Normal file
35
src/main/java/com/sparrowwallet/sparrow/WalletTabData.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package com.sparrowwallet.sparrow;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.event.WalletChangedEvent;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class WalletTabData extends TabData {
|
||||
private Wallet wallet;
|
||||
private final File walletFile;
|
||||
|
||||
public WalletTabData(TabType type, Wallet wallet, File walletFile) {
|
||||
super(type);
|
||||
this.wallet = wallet;
|
||||
this.walletFile = walletFile;
|
||||
|
||||
EventManager.get().register(this);
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public File getWalletFile() {
|
||||
return walletFile;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletChanged(WalletChangedEvent event) {
|
||||
if(event.getWalletFile().equals(walletFile)) {
|
||||
wallet = event.getWallet();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ public abstract class FileImportPane extends TitledDescriptionPane {
|
|||
Stage window = new Stage();
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " keystore");
|
||||
fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " File");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
||||
new FileChooser.ExtensionFilter("JSON", "*.json")
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.WalletExportEvent;
|
||||
import com.sparrowwallet.sparrow.io.WalletExport;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FileWalletExportPane extends TitledDescriptionPane {
|
||||
private final Wallet wallet;
|
||||
private final WalletExport exporter;
|
||||
|
||||
public FileWalletExportPane(Wallet wallet, WalletExport exporter) {
|
||||
super(exporter.getName(), "Wallet file export", exporter.getWalletExportDescription(), "image/" + exporter.getWalletModel().getType() + ".png");
|
||||
this.wallet = wallet;
|
||||
this.exporter = exporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Control createButton() {
|
||||
Button exportButton = new Button("Export Wallet...");
|
||||
exportButton.setAlignment(Pos.CENTER_RIGHT);
|
||||
exportButton.setOnAction(event -> {
|
||||
exportWallet();
|
||||
});
|
||||
return exportButton;
|
||||
}
|
||||
|
||||
private void exportWallet() {
|
||||
Stage window = new Stage();
|
||||
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle("Export " + exporter.getWalletModel().toDisplayString() + " File");
|
||||
|
||||
File file = fileChooser.showSaveDialog(window);
|
||||
if(file != null) {
|
||||
exportWallet(file);
|
||||
}
|
||||
}
|
||||
|
||||
private void exportWallet(File file) {
|
||||
Wallet copy = wallet.copy();
|
||||
|
||||
if(copy.isEncrypted()) {
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<String> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
copy.decrypt(password.get(), "");
|
||||
|
||||
for(Keystore keystore : copy.getKeystores()) {
|
||||
if(keystore.hasSeed() && keystore.getSeed().needPassphrase()) {
|
||||
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(keystore);
|
||||
Optional<String> passphrase = passphraseDialog.showAndWait();
|
||||
if(passphrase.isPresent()) {
|
||||
keystore.setPassphrase(passphrase.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
OutputStream outputStream = new FileOutputStream(file);
|
||||
exporter.exportWallet(copy, outputStream);
|
||||
EventManager.get().post(new WalletExportEvent(copy));
|
||||
} catch(Exception e) {
|
||||
String errorMessage = e.getMessage();
|
||||
if(e.getCause() != null && e.getCause().getMessage() != null && !e.getCause().getMessage().isEmpty()) {
|
||||
errorMessage = e.getCause().getMessage();
|
||||
}
|
||||
setError("Export Error", errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.sparrow.AppController;
|
||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||
import javafx.scene.control.ButtonType;
|
||||
import javafx.scene.control.Dialog;
|
||||
import javafx.scene.control.DialogPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.controlsfx.control.textfield.CustomPasswordField;
|
||||
import org.controlsfx.control.textfield.TextFields;
|
||||
import org.controlsfx.glyphfont.Glyph;
|
||||
|
||||
public class KeystorePassphraseDialog extends Dialog<String> {
|
||||
private final CustomPasswordField passphrase;
|
||||
|
||||
public KeystorePassphraseDialog(Keystore keystore) {
|
||||
this.passphrase = (CustomPasswordField) TextFields.createClearablePasswordField();
|
||||
|
||||
final DialogPane dialogPane = getDialogPane();
|
||||
setTitle("Keystore Passphrase");
|
||||
dialogPane.setHeaderText("Please enter the passphrase for keystore: " + keystore.getLabel());
|
||||
dialogPane.getStylesheets().add(AppController.class.getResource("general.css").toExternalForm());
|
||||
dialogPane.getButtonTypes().addAll(ButtonType.CANCEL, ButtonType.OK);
|
||||
dialogPane.setPrefWidth(380);
|
||||
dialogPane.setPrefHeight(200);
|
||||
|
||||
Glyph lock = new Glyph("FontAwesome5", FontAwesome5.Glyph.KEY);
|
||||
lock.setFontSize(50);
|
||||
dialogPane.setGraphic(lock);
|
||||
|
||||
final VBox content = new VBox(10);
|
||||
content.setPrefHeight(50);
|
||||
content.getChildren().add(passphrase);
|
||||
|
||||
dialogPane.setContent(content);
|
||||
passphrase.requestFocus();
|
||||
|
||||
setResultConverter(dialogButton -> dialogButton == ButtonType.OK ? passphrase.getText() : null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package com.sparrowwallet.sparrow.control;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
import com.sparrowwallet.sparrow.event.WalletExportEvent;
|
||||
import com.sparrowwallet.sparrow.event.WalletImportEvent;
|
||||
import com.sparrowwallet.sparrow.io.ColdcardMultisig;
|
||||
import com.sparrowwallet.sparrow.io.Electrum;
|
||||
import com.sparrowwallet.sparrow.io.WalletExport;
|
||||
import com.sparrowwallet.sparrow.io.WalletImport;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WalletExportDialog extends Dialog<Wallet> {
|
||||
private Wallet wallet;
|
||||
|
||||
public WalletExportDialog(Wallet wallet) {
|
||||
EventManager.get().register(this);
|
||||
|
||||
final DialogPane dialogPane = getDialogPane();
|
||||
|
||||
StackPane stackPane = new StackPane();
|
||||
dialogPane.setContent(stackPane);
|
||||
|
||||
AnchorPane anchorPane = new AnchorPane();
|
||||
stackPane.getChildren().add(anchorPane);
|
||||
|
||||
ScrollPane scrollPane = new ScrollPane();
|
||||
scrollPane.setPrefHeight(280);
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
anchorPane.getChildren().add(scrollPane);
|
||||
scrollPane.setFitToWidth(true);
|
||||
AnchorPane.setLeftAnchor(scrollPane, 0.0);
|
||||
AnchorPane.setRightAnchor(scrollPane, 0.0);
|
||||
|
||||
List<WalletExport> exporters;
|
||||
if(wallet.getPolicyType() == PolicyType.SINGLE) {
|
||||
exporters = List.of(new Electrum());
|
||||
} else if(wallet.getPolicyType() == PolicyType.MULTI) {
|
||||
exporters = List.of(new ColdcardMultisig(), new Electrum());
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Cannot export wallet with policy type " + wallet.getPolicyType());
|
||||
}
|
||||
|
||||
Accordion exportAccordion = new Accordion();
|
||||
for (WalletExport exporter : exporters) {
|
||||
FileWalletExportPane exportPane = new FileWalletExportPane(wallet, exporter);
|
||||
exportAccordion.getPanes().add(exportPane);
|
||||
}
|
||||
scrollPane.setContent(exportAccordion);
|
||||
|
||||
final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE);
|
||||
dialogPane.getButtonTypes().addAll(cancelButtonType);
|
||||
dialogPane.setPrefWidth(500);
|
||||
dialogPane.setPrefHeight(360);
|
||||
|
||||
setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void walletExported(WalletExportEvent event) {
|
||||
wallet = event.getWallet();
|
||||
setResult(wallet);
|
||||
this.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class TabSelectedEvent extends TabEvent {
|
||||
public TabSelectedEvent(Tab tab) {
|
||||
super(tab);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ package com.sparrowwallet.sparrow.event;
|
|||
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class TransactionTabSelectedEvent extends TabEvent {
|
||||
public class TransactionTabSelectedEvent extends TabSelectedEvent {
|
||||
public TransactionTabSelectedEvent(Tab tab) {
|
||||
super(tab);
|
||||
}
|
||||
|
|
|
@ -2,14 +2,22 @@ package com.sparrowwallet.sparrow.event;
|
|||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
public class WalletChangedEvent {
|
||||
private Wallet wallet;
|
||||
import java.io.File;
|
||||
|
||||
public WalletChangedEvent(Wallet wallet) {
|
||||
public class WalletChangedEvent {
|
||||
private final Wallet wallet;
|
||||
private final File walletFile;
|
||||
|
||||
public WalletChangedEvent(Wallet wallet, File walletFile) {
|
||||
this.wallet = wallet;
|
||||
this.walletFile = walletFile;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
|
||||
public File getWalletFile() {
|
||||
return walletFile;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
public class WalletExportEvent {
|
||||
private Wallet wallet;
|
||||
|
||||
public WalletExportEvent(Wallet wallet) {
|
||||
this.wallet = wallet;
|
||||
}
|
||||
|
||||
public Wallet getWallet() {
|
||||
return wallet;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.sparrowwallet.sparrow.event;
|
||||
|
||||
import javafx.scene.control.Tab;
|
||||
|
||||
public class WalletTabSelectedEvent extends TabSelectedEvent {
|
||||
public WalletTabSelectedEvent(Tab tab) {
|
||||
super(tab);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ public class FontAwesome5 extends GlyphFont {
|
|||
CIRCLE('\uf111'),
|
||||
EXCLAMATION_CIRCLE('\uf06a'),
|
||||
EYE('\uf06e'),
|
||||
KEY('\uf084'),
|
||||
LAPTOP('\uf109'),
|
||||
SD_CARD('\uf7c2'),
|
||||
WALLET('\uf555');
|
||||
|
|
|
@ -157,15 +157,36 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
|||
throw new ExportException("Could not export a wallet with a " + wallet.getPolicyType() + " policy");
|
||||
}
|
||||
|
||||
ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromScriptType(wallet.getScriptType());
|
||||
ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromScriptType(wallet.getScriptType(), false);
|
||||
ExtendedKey.Header xprvHeader = ExtendedKey.Header.fromScriptType(wallet.getScriptType(), true);
|
||||
|
||||
int index = 1;
|
||||
for(Keystore keystore : wallet.getKeystores()) {
|
||||
ElectrumKeystore ek = new ElectrumKeystore();
|
||||
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||
ek.derivation = keystore.getKeyDerivation().getDerivationPath();
|
||||
ek.root_fingerprint = keystore.getKeyDerivation().getMasterFingerprint();
|
||||
ek.label = keystore.getLabel();
|
||||
|
||||
if(keystore.getSource() == KeystoreSource.HW_USB || keystore.getSource() == KeystoreSource.HW_AIRGAPPED) {
|
||||
ek.label = keystore.getLabel();
|
||||
ek.derivation = keystore.getKeyDerivation().getDerivationPath();
|
||||
ek.root_fingerprint = keystore.getKeyDerivation().getMasterFingerprint();
|
||||
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||
ek.type = "hardware";
|
||||
ek.hw_type = keystore.getWalletModel().getType();
|
||||
ew.use_encryption = false;
|
||||
} else if(keystore.getSource() == KeystoreSource.SW_SEED) {
|
||||
ek.type = "bip32";
|
||||
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||
ek.xprv = keystore.getExtendedPrivateKey().toString(xprvHeader);
|
||||
ek.pw_hash_version = 1;
|
||||
ew.seed_type = "bip39";
|
||||
ew.use_encryption = false;
|
||||
} else if(keystore.getSource() == KeystoreSource.SW_WATCH) {
|
||||
ek.type = "bip32";
|
||||
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||
ek.pw_hash_version = 1;
|
||||
ew.use_encryption = false;
|
||||
} else {
|
||||
throw new ExportException("Cannot export a keystore of source " + keystore.getSource());
|
||||
}
|
||||
|
||||
if(wallet.getPolicyType().equals(PolicyType.SINGLE)) {
|
||||
ew.keystores.put("keystore", ek);
|
||||
|
@ -179,6 +200,12 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
|||
Gson gson = new Gson();
|
||||
JsonObject eJson = gson.toJsonTree(ew.keystores).getAsJsonObject();
|
||||
eJson.addProperty("wallet_type", ew.wallet_type);
|
||||
if(ew.use_encryption != null) {
|
||||
eJson.addProperty("use_encryption", ew.use_encryption);
|
||||
}
|
||||
if(ew.seed_type != null) {
|
||||
eJson.addProperty("seed_type", ew.seed_type);
|
||||
}
|
||||
|
||||
gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
String json = gson.toJson(eJson);
|
||||
|
@ -203,6 +230,8 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
|||
private static class ElectrumJsonWallet {
|
||||
public Map<String, ElectrumKeystore> keystores = new LinkedHashMap<>();
|
||||
public String wallet_type;
|
||||
public String seed_type;
|
||||
public Boolean use_encryption;
|
||||
}
|
||||
|
||||
public static class ElectrumKeystore {
|
||||
|
@ -216,5 +245,6 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
|||
public String type;
|
||||
public String derivation;
|
||||
public String seed;
|
||||
public Integer pw_hash_version;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||
|
||||
public interface Export {
|
||||
String getName();
|
||||
WalletModel getWalletModel();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
public class ExportException extends Throwable {
|
||||
public class ExportException extends Exception {
|
||||
public ExportException() {
|
||||
super();
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import com.google.common.eventbus.Subscribe;
|
|||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.KeyDerivation;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||
import com.sparrowwallet.sparrow.EventManager;
|
||||
|
@ -20,10 +19,8 @@ import org.controlsfx.validation.ValidationResult;
|
|||
import org.controlsfx.validation.ValidationSupport;
|
||||
import org.controlsfx.validation.Validator;
|
||||
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
|
||||
import tornadofx.control.Form;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -94,7 +91,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
|||
EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet(), SettingsChangedEvent.Type.KEYSTORE_FINGERPRINT));
|
||||
});
|
||||
derivation.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(KeyDerivation.isValid(newValue) && !matchesAnotherScriptType(newValue)) {
|
||||
if(KeyDerivation.isValid(newValue) && !walletForm.getWallet().derivationMatchesAnotherScriptType(newValue)) {
|
||||
keystore.setKeyDerivation(new KeyDerivation(keystore.getKeyDerivation().getMasterFingerprint(), newValue));
|
||||
EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet(), SettingsChangedEvent.Type.KEYSTORE_DERIVATION));
|
||||
}
|
||||
|
@ -140,7 +137,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
|||
validationSupport.registerValidator(derivation, Validator.combine(
|
||||
Validator.createEmptyValidator("Derivation is required"),
|
||||
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Derivation is invalid", !KeyDerivation.isValid(newValue)),
|
||||
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Derivation matches another script type", matchesAnotherScriptType(newValue))
|
||||
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Derivation matches another script type", walletForm.getWallet().derivationMatchesAnotherScriptType(newValue))
|
||||
));
|
||||
|
||||
validationSupport.registerValidator(fingerprint, Validator.combine(
|
||||
|
@ -151,14 +148,6 @@ public class KeystoreController extends WalletFormController implements Initiali
|
|||
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
|
||||
}
|
||||
|
||||
private boolean matchesAnotherScriptType(String derivationPath) {
|
||||
if(walletForm.getWallet().getScriptType() != null && walletForm.getWallet().getScriptType().getAccount(derivationPath) > -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.stream(ScriptType.values()).anyMatch(scriptType -> !scriptType.equals(walletForm.getWallet().getScriptType()) && scriptType.getAccount(derivationPath) > -1);
|
||||
}
|
||||
|
||||
private void updateType() {
|
||||
type.setText(getTypeLabel(keystore));
|
||||
|
||||
|
@ -211,9 +200,9 @@ public class KeystoreController extends WalletFormController implements Initiali
|
|||
|
||||
@Subscribe
|
||||
public void update(SettingsChangedEvent event) {
|
||||
if(event.getType().equals(SettingsChangedEvent.Type.SCRIPT_TYPE) && !derivation.getText().isEmpty()) {
|
||||
if(walletForm.getWallet().equals(event.getWallet()) && event.getType().equals(SettingsChangedEvent.Type.SCRIPT_TYPE) && !derivation.getText().isEmpty()) {
|
||||
String derivationPath = derivation.getText();
|
||||
derivation.setText("");
|
||||
derivation.setText(derivationPath + " ");
|
||||
derivation.setText(derivationPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ public class SettingsController extends WalletFormController implements Initiali
|
|||
walletForm.save();
|
||||
revert.setDisable(true);
|
||||
apply.setDisable(true);
|
||||
EventManager.get().post(new WalletChangedEvent(walletForm.getWallet()));
|
||||
EventManager.get().post(new WalletChangedEvent(walletForm.getWallet(), walletForm.getWalletFile()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
AppController.showErrorDialog("Error saving file", e.getMessage());
|
||||
|
@ -226,13 +226,11 @@ public class SettingsController extends WalletFormController implements Initiali
|
|||
if(result.getErrors().isEmpty()) {
|
||||
tab.getStyleClass().remove("tab-error");
|
||||
tab.setTooltip(null);
|
||||
apply.setDisable(false);
|
||||
} else {
|
||||
if(!tab.getStyleClass().contains("tab-error")) {
|
||||
tab.getStyleClass().add("tab-error");
|
||||
}
|
||||
tab.setTooltip(new Tooltip(result.getErrors().iterator().next().getText()));
|
||||
apply.setDisable(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -242,28 +240,20 @@ public class SettingsController extends WalletFormController implements Initiali
|
|||
}
|
||||
}
|
||||
|
||||
private boolean tabsValidate() {
|
||||
for(Tab tab : keystoreTabs.getTabs()) {
|
||||
if(tab.getStyleClass().contains("tab-error")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void update(SettingsChangedEvent event) {
|
||||
Wallet wallet = event.getWallet();
|
||||
if(wallet.getPolicyType() == PolicyType.SINGLE) {
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), 1));
|
||||
} else if(wallet.getPolicyType() == PolicyType.MULTI) {
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), (int)multisigControl.getLowValue()));
|
||||
}
|
||||
if(walletForm.getWallet().equals(wallet)) {
|
||||
if(wallet.getPolicyType() == PolicyType.SINGLE) {
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), 1));
|
||||
} else if(wallet.getPolicyType() == PolicyType.MULTI) {
|
||||
wallet.setDefaultPolicy(Policy.getPolicy(wallet.getPolicyType(), wallet.getScriptType(), wallet.getKeystores(), (int)multisigControl.getLowValue()));
|
||||
}
|
||||
|
||||
spendingMiniscript.setText(event.getWallet().getDefaultPolicy().getMiniscript().getScript());
|
||||
revert.setDisable(false);
|
||||
Platform.runLater(() -> apply.setDisable(!tabsValidate()));
|
||||
spendingMiniscript.setText(wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||
revert.setDisable(false);
|
||||
apply.setDisable(!wallet.isValid());
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<ECKey> requestEncryption(ECKey existingPubKey) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import java.io.IOException;
|
|||
public class WalletForm {
|
||||
public static final ECKey NO_PASSWORD_KEY = ECKey.fromPublicOnly(ECIESKeyCrypter.deriveECKey(""));
|
||||
|
||||
private File walletFile;
|
||||
private final File walletFile;
|
||||
private ECKey encryptionPubKey;
|
||||
private Wallet oldWallet;
|
||||
private Wallet wallet;
|
||||
|
@ -27,6 +27,10 @@ public class WalletForm {
|
|||
return wallet;
|
||||
}
|
||||
|
||||
public File getWalletFile() {
|
||||
return walletFile;
|
||||
}
|
||||
|
||||
public ECKey getEncryptionPubKey() {
|
||||
return encryptionPubKey;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
</items>
|
||||
</Menu>
|
||||
<MenuItem mnemonicParsing="false" text="Import Wallet..." onAction="#importWallet"/>
|
||||
<MenuItem fx:id="exportWallet" mnemonicParsing="false" text="Export Wallet..." onAction="#exportWallet"/>
|
||||
<MenuItem mnemonicParsing="false" text="Close" onAction="#closeTab"/>
|
||||
</items>
|
||||
</Menu>
|
||||
|
|
Loading…
Reference in a new issue