diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index 6b0ba91e..cee50e94 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -2,6 +2,7 @@ package com.sparrowwallet.sparrow.io; import com.google.gson.*; import com.sparrowwallet.drongo.ExtendedKey; +import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.wallet.Wallet; @@ -22,7 +23,9 @@ public class Storage { GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer()); 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() { @@ -76,7 +79,7 @@ public class Storage { } private static byte[] getEncryptionMagic() { - return "BIE1".getBytes(StandardCharsets.UTF_8); + return "SPRW1".getBytes(StandardCharsets.UTF_8); } public File getWalletFile(String walletName) { @@ -84,7 +87,12 @@ public class Storage { } 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() { @@ -108,4 +116,18 @@ public class Storage { return ExtendedKey.fromDescriptor(json.getAsJsonPrimitive().getAsString()); } } + + private static class ByteArraySerializer implements JsonSerializer { + @Override + public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(Utils.bytesToHex(src)); + } + } + + private static class ByteArrayDeserializer implements JsonDeserializer { + @Override + public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return Utils.hexToBytes(json.getAsJsonPrimitive().getAsString()); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportDialog.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportDialog.java index a9da06e3..3b5756d0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/KeystoreImportDialog.java @@ -43,7 +43,7 @@ public class KeystoreImportDialog extends Dialog { @Subscribe public void keystoreImported(KeystoreImportEvent event) { this.keystore = event.getKeystore(); - System.out.println(keystore.getLabel() + " " + keystore.getKeyDerivation().getMasterFingerprint()); + setResult(keystore); this.close(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java index 0cf0c5d8..e4550aa0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/KeystoreController.java @@ -4,6 +4,7 @@ import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.drongo.wallet.KeystoreSource; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog; import com.sparrowwallet.sparrow.event.SettingsChangedEvent; @@ -12,6 +13,7 @@ import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Control; +import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import org.controlsfx.validation.ValidationResult; @@ -27,6 +29,9 @@ import java.util.stream.Collectors; public class KeystoreController extends WalletFormController implements Initializable { private Keystore keystore; + @FXML + private Label type; + @FXML private TextField label; @@ -55,6 +60,8 @@ public class KeystoreController extends WalletFormController implements Initiali public void initializeView() { Platform.runLater(this::setupValidation); + updateType(); + label.setText(keystore.getLabel()); if(keystore.getExtendedPublicKey() != null) { @@ -102,7 +109,7 @@ public class KeystoreController extends WalletFormController implements Initiali validationSupport.registerValidator(label, Validator.combine( 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 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( @@ -123,11 +130,43 @@ public class KeystoreController extends WalletFormController implements Initiali 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) { KeystoreImportDialog dlg = new KeystoreImportDialog(getWalletForm().getWallet()); Optional result = dlg.showAndWait(); 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()); fingerprint.setText(keystore.getKeyDerivation().getMasterFingerprint()); derivation.setText(keystore.getKeyDerivation().getDerivationPath()); diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java index f19687cd..4b28a019 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java @@ -89,9 +89,12 @@ public class SettingsController extends WalletFormController implements Initiali multisigFieldset.setVisible(policyType.equals(PolicyType.MULTI)); if(policyType.equals(PolicyType.MULTI)) { + totalKeystores.unbind(); + totalKeystores.set(0); totalKeystores.bind(multisigControl.highValueProperty()); } else { totalKeystores.unbind(); + totalKeystores.set(0); totalKeystores.set(1); } }); diff --git a/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml b/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml index e181b4e5..7491ef08 100644 --- a/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml +++ b/src/main/resources/com/sparrowwallet/sparrow/wallet/keystore.fxml @@ -16,11 +16,14 @@
- - + +