integrate keystore importer

This commit is contained in:
Craig Raw 2020-05-09 14:17:48 +02:00
parent 9af17bcac8
commit 6872e069a9
5 changed files with 75 additions and 8 deletions

View file

@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.io;
import com.google.gson.*; import com.google.gson.*;
import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.crypto.ECKey;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
@ -22,7 +23,9 @@ public class Storage {
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer()); gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer()); gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
gson = gsonBuilder.setPrettyPrinting().create(); gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
gson = gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
} }
public static Storage getStorage() { public static Storage getStorage() {
@ -76,7 +79,7 @@ public class Storage {
} }
private static byte[] getEncryptionMagic() { private static byte[] getEncryptionMagic() {
return "BIE1".getBytes(StandardCharsets.UTF_8); return "SPRW1".getBytes(StandardCharsets.UTF_8);
} }
public File getWalletFile(String walletName) { public File getWalletFile(String walletName) {
@ -84,7 +87,12 @@ public class Storage {
} }
public File getWalletsDir() { public File getWalletsDir() {
return new File(getSparrowDir(), WALLETS_DIR); File walletsDir = new File(getSparrowDir(), WALLETS_DIR);
if(!walletsDir.exists()) {
walletsDir.mkdirs();
}
return walletsDir;
} }
private File getSparrowDir() { private File getSparrowDir() {
@ -108,4 +116,18 @@ public class Storage {
return ExtendedKey.fromDescriptor(json.getAsJsonPrimitive().getAsString()); return ExtendedKey.fromDescriptor(json.getAsJsonPrimitive().getAsString());
} }
} }
private static class ByteArraySerializer implements JsonSerializer<byte[]> {
@Override
public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(Utils.bytesToHex(src));
}
}
private static class ByteArrayDeserializer implements JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return Utils.hexToBytes(json.getAsJsonPrimitive().getAsString());
}
}
} }

View file

@ -43,7 +43,7 @@ public class KeystoreImportDialog extends Dialog<Keystore> {
@Subscribe @Subscribe
public void keystoreImported(KeystoreImportEvent event) { public void keystoreImported(KeystoreImportEvent event) {
this.keystore = event.getKeystore(); this.keystore = event.getKeystore();
System.out.println(keystore.getLabel() + " " + keystore.getKeyDerivation().getMasterFingerprint()); setResult(keystore);
this.close(); this.close();
} }
} }

View file

@ -4,6 +4,7 @@ import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.Utils;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.KeystoreSource;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog; import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog;
import com.sparrowwallet.sparrow.event.SettingsChangedEvent; import com.sparrowwallet.sparrow.event.SettingsChangedEvent;
@ -12,6 +13,7 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Control; import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationResult;
@ -27,6 +29,9 @@ import java.util.stream.Collectors;
public class KeystoreController extends WalletFormController implements Initializable { public class KeystoreController extends WalletFormController implements Initializable {
private Keystore keystore; private Keystore keystore;
@FXML
private Label type;
@FXML @FXML
private TextField label; private TextField label;
@ -55,6 +60,8 @@ public class KeystoreController extends WalletFormController implements Initiali
public void initializeView() { public void initializeView() {
Platform.runLater(this::setupValidation); Platform.runLater(this::setupValidation);
updateType();
label.setText(keystore.getLabel()); label.setText(keystore.getLabel());
if(keystore.getExtendedPublicKey() != null) { if(keystore.getExtendedPublicKey() != null) {
@ -102,7 +109,7 @@ public class KeystoreController extends WalletFormController implements Initiali
validationSupport.registerValidator(label, Validator.combine( validationSupport.registerValidator(label, Validator.combine(
Validator.createEmptyValidator("Label is required"), Validator.createEmptyValidator("Label is required"),
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Label is not unique", walletForm.getWallet().getKeystores().stream().filter(k -> k != keystore).map(Keystore::getLabel).collect(Collectors.toList()).contains(newValue)), (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Label is not unique", walletForm.getWallet().getKeystores().stream().filter(k -> k != keystore).map(Keystore::getLabel).collect(Collectors.toList()).contains(newValue)),
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Label is too long", newValue.length() > 16) (Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Label is too long", newValue.replace(" ", "").length() > 16)
)); ));
validationSupport.registerValidator(xpub, Validator.combine( validationSupport.registerValidator(xpub, Validator.combine(
@ -123,11 +130,43 @@ public class KeystoreController extends WalletFormController implements Initiali
validationSupport.setValidationDecorator(new StyleClassValidationDecoration()); validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
} }
private void updateType() {
type.setText(getTypeLabel(keystore));
boolean editable = (keystore.getSource() == KeystoreSource.SW_WATCH);
label.setEditable(editable);
fingerprint.setEditable(editable);
derivation.setEditable(editable);
xpub.setEditable(editable);
}
private String getTypeLabel(Keystore keystore) {
switch (keystore.getSource()) {
case HW_USB:
return "Connected Hardware Wallet (" + keystore.getWalletModel().toDisplayString() + ")";
case HW_AIRGAPPED:
return "Airgapped Hardware Wallet (" + keystore.getWalletModel().toDisplayString() + ")";
case SW_SEED:
return "Software Wallet";
case SW_WATCH:
default:
return "Software Wallet (Watch Only)";
}
}
public void importKeystore(ActionEvent event) { public void importKeystore(ActionEvent event) {
KeystoreImportDialog dlg = new KeystoreImportDialog(getWalletForm().getWallet()); KeystoreImportDialog dlg = new KeystoreImportDialog(getWalletForm().getWallet());
Optional<Keystore> result = dlg.showAndWait(); Optional<Keystore> result = dlg.showAndWait();
if(result.isPresent()) { if(result.isPresent()) {
Keystore keystore = result.get(); Keystore importedKeystore = result.get();
keystore.setSource(importedKeystore.getSource());
keystore.setWalletModel(importedKeystore.getWalletModel());
keystore.setLabel(importedKeystore.getLabel());
keystore.setKeyDerivation(importedKeystore.getKeyDerivation());
keystore.setExtendedPublicKey(importedKeystore.getExtendedPublicKey());
keystore.setSeed(importedKeystore.getSeed());
updateType();
label.setText(keystore.getLabel()); label.setText(keystore.getLabel());
fingerprint.setText(keystore.getKeyDerivation().getMasterFingerprint()); fingerprint.setText(keystore.getKeyDerivation().getMasterFingerprint());
derivation.setText(keystore.getKeyDerivation().getDerivationPath()); derivation.setText(keystore.getKeyDerivation().getDerivationPath());

View file

@ -89,9 +89,12 @@ public class SettingsController extends WalletFormController implements Initiali
multisigFieldset.setVisible(policyType.equals(PolicyType.MULTI)); multisigFieldset.setVisible(policyType.equals(PolicyType.MULTI));
if(policyType.equals(PolicyType.MULTI)) { if(policyType.equals(PolicyType.MULTI)) {
totalKeystores.unbind();
totalKeystores.set(0);
totalKeystores.bind(multisigControl.highValueProperty()); totalKeystores.bind(multisigControl.highValueProperty());
} else { } else {
totalKeystores.unbind(); totalKeystores.unbind();
totalKeystores.set(0);
totalKeystores.set(1); totalKeystores.set(1);
} }
}); });

View file

@ -16,11 +16,14 @@
</padding> </padding>
<Form GridPane.columnIndex="0" GridPane.rowIndex="0"> <Form GridPane.columnIndex="0" GridPane.rowIndex="0">
<Fieldset inputGrow="SOMETIMES" text=""> <Fieldset inputGrow="SOMETIMES" text="">
<Field text="Label:"> <Field text="Type:">
<TextField fx:id="label" maxWidth="160"/> <Label fx:id="type"/>
<Pane HBox.hgrow="ALWAYS" /> <Pane HBox.hgrow="ALWAYS" />
<Button text="Import..." onAction="#importKeystore"/> <Button text="Import..." onAction="#importKeystore"/>
</Field> </Field>
<Field text="Label:">
<TextField fx:id="label" maxWidth="160"/>
</Field>
<Field text="Master fingerprint:"> <Field text="Master fingerprint:">
<TextField fx:id="fingerprint" maxWidth="80"/> <TextField fx:id="fingerprint" maxWidth="80"/>
</Field> </Field>