From 0adf94bf6278992d72abb2cad33f6be2b397f92d Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 7 Sep 2020 17:23:12 +0200 Subject: [PATCH] initial specter wallet import and export functionality --- drongo | 2 +- .../sparrow/control/WalletExportDialog.java | 5 +- .../sparrow/control/WalletImportDialog.java | 6 +- .../com/sparrowwallet/sparrow/io/Specter.java | 93 ++++++++++++++++++ src/main/resources/image/specter.png | Bin 0 -> 2523 bytes src/main/resources/image/specter@2x.png | Bin 0 -> 3293 bytes src/main/resources/image/specter@3x.png | Bin 0 -> 4850 bytes 7 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/io/Specter.java create mode 100644 src/main/resources/image/specter.png create mode 100644 src/main/resources/image/specter@2x.png create mode 100644 src/main/resources/image/specter@3x.png diff --git a/drongo b/drongo index 10ebfe46..70fdecf9 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 10ebfe463d504f39be2aacec41d48c2cf5ba4c56 +Subproject commit 70fdecf919ea9dcae46cc35df23e0a20ba90683f diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java index 788f7c53..c61a5c64 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java @@ -8,6 +8,7 @@ import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.event.WalletExportEvent; import com.sparrowwallet.sparrow.io.ColdcardMultisig; import com.sparrowwallet.sparrow.io.Electrum; +import com.sparrowwallet.sparrow.io.Specter; import com.sparrowwallet.sparrow.io.WalletExport; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; @@ -43,9 +44,9 @@ public class WalletExportDialog extends Dialog { List exporters; if(wallet.getPolicyType() == PolicyType.SINGLE) { - exporters = List.of(new Electrum()); + exporters = List.of(new Electrum(), new Specter()); } else if(wallet.getPolicyType() == PolicyType.MULTI) { - exporters = List.of(new ColdcardMultisig(), new Electrum()); + exporters = List.of(new ColdcardMultisig(), new Electrum(), new Specter()); } else { throw new UnsupportedOperationException("Cannot export wallet with policy type " + wallet.getPolicyType()); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java index 69978ff4..65598149 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java @@ -31,7 +31,7 @@ public class WalletImportDialog extends Dialog { stackPane.getChildren().add(anchorPane); ScrollPane scrollPane = new ScrollPane(); - scrollPane.setPrefHeight(320); + scrollPane.setPrefHeight(400); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); anchorPane.getChildren().add(scrollPane); scrollPane.setFitToWidth(true); @@ -45,7 +45,7 @@ public class WalletImportDialog extends Dialog { importAccordion.getPanes().add(importPane); } - List walletImporters = List.of(new ColdcardMultisig(), new Electrum()); + List walletImporters = List.of(new ColdcardMultisig(), new Electrum(), new Specter()); for(WalletImport importer : walletImporters) { FileWalletImportPane importPane = new FileWalletImportPane(importer); importAccordion.getPanes().add(importPane); @@ -55,7 +55,7 @@ public class WalletImportDialog extends Dialog { final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); dialogPane.getButtonTypes().addAll(cancelButtonType); dialogPane.setPrefWidth(500); - dialogPane.setPrefHeight(400); + dialogPane.setPrefHeight(480); setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null); } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Specter.java b/src/main/java/com/sparrowwallet/sparrow/io/Specter.java new file mode 100644 index 00000000..0fc71858 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/io/Specter.java @@ -0,0 +1,93 @@ +package com.sparrowwallet.sparrow.io; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.sparrowwallet.drongo.OutputDescriptor; +import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.drongo.wallet.WalletModel; + +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class Specter implements WalletImport, WalletExport { + @Override + public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException { + try { + SpecterWallet specterWallet = new SpecterWallet(); + specterWallet.label = wallet.getName(); + specterWallet.blockheight = wallet.getStoredBlockHeight(); + specterWallet.descriptor = OutputDescriptor.getOutputDescriptor(wallet).toString(); + + Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + String json = gson.toJson(specterWallet); + outputStream.write(json.getBytes(StandardCharsets.UTF_8)); + outputStream.flush(); + outputStream.close(); + } catch(Exception e) { + throw new ExportException(e); + } + } + + @Override + public String getWalletExportDescription() { + return "Export a Specter wallet that can be read by Specter Desktop using Add new wallet > Import from wallet software"; + } + + @Override + public String getExportFileExtension() { + return "json"; + } + + @Override + public String getWalletImportDescription() { + return "Import a Specter wallet created by using the Wallets > Settings > Export to Wallet Software in Specter Desktop"; + } + + @Override + public Wallet importWallet(InputStream inputStream, String password) throws ImportException { + try { + Gson gson = new Gson(); + SpecterWallet specterWallet = gson.fromJson(new InputStreamReader(inputStream), SpecterWallet.class); + + if(specterWallet.descriptor != null) { + OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(specterWallet.descriptor); + Wallet wallet = outputDescriptor.toWallet(); + wallet.setName(specterWallet.label); + + if(!wallet.isValid()) { + throw new ImportException("Specter wallet file did not contain a valid wallet"); + } + + return wallet; + } + } catch(Exception e) { + throw new ImportException(e); + } + + throw new ImportException("Could not import Specter wallet"); + } + + @Override + public boolean isEncrypted(File file) { + return false; + } + + @Override + public String getName() { + return "Specter"; + } + + @Override + public WalletModel getWalletModel() { + return WalletModel.SPECTER; + } + + public static class SpecterWallet { + public String label; + public Integer blockheight; + public String descriptor; + } +} diff --git a/src/main/resources/image/specter.png b/src/main/resources/image/specter.png new file mode 100644 index 0000000000000000000000000000000000000000..71badb56c72e6b93e81953fc4f4028ab59e6740c GIT binary patch literal 2523 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 RH+g^39qnC_9J`>T{{Xic3^V`$ literal 0 HcmV?d00001 diff --git a/src/main/resources/image/specter@3x.png b/src/main/resources/image/specter@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1d95cdfedba733ea35a3ad02e4505d9591579fa7 GIT binary patch literal 4850 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