From fa5ac7917b33677e3e6c8265a6d6f240e6505be6 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 7 Oct 2020 12:31:58 +0200 Subject: [PATCH] add cobo vault singlesig and multisig file import and export --- drongo | 2 +- .../sparrowwallet/sparrow/AppController.java | 2 +- .../sparrow/control/FileImportPane.java | 3 +- .../sparrow/control/WalletExportDialog.java | 11 ++-- .../sparrow/control/WalletImportDialog.java | 8 +-- .../sparrow/io/CoboVaultMultisig.java | 55 ++++++++++++++++++ .../sparrow/io/CoboVaultSinglesig.java | 33 +++++++++++ .../sparrow/io/ColdcardMultisig.java | 7 +-- .../keystoreimport/HwAirgappedController.java | 4 +- src/main/resources/image/cobovault-orig.png | Bin 0 -> 34655 bytes src/main/resources/image/cobovault.png | Bin 0 -> 1978 bytes src/main/resources/image/cobovault@2x.png | Bin 0 -> 2933 bytes src/main/resources/image/cobovault@3x.png | Bin 0 -> 5285 bytes 13 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/io/CoboVaultMultisig.java create mode 100644 src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java create mode 100644 src/main/resources/image/cobovault-orig.png create mode 100644 src/main/resources/image/cobovault.png create mode 100644 src/main/resources/image/cobovault@2x.png create mode 100644 src/main/resources/image/cobovault@3x.png diff --git a/drongo b/drongo index 3642ddc9..290fbabb 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 3642ddc9581c4485b13d4d0fffee6290703a5768 +Subproject commit 290fbabb54f0a334db15b66c968c9311122817e7 diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 6a1f8336..b6628243 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -885,7 +885,7 @@ public class AppController implements Initializable { } private boolean attemptImportWallet(File file, SecureString password) { - List walletImporters = List.of(new ColdcardSinglesig(), new ColdcardMultisig(), new Electrum(), new Specter()); + List walletImporters = List.of(new ColdcardSinglesig(), new ColdcardMultisig(), new Electrum(), new Specter(), new CoboVaultSinglesig(), new CoboVaultMultisig()); for(WalletImport importer : walletImporters) { try(FileInputStream inputStream = new FileInputStream(file)) { if(importer.isEncrypted(file) && password == null) { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/FileImportPane.java b/src/main/java/com/sparrowwallet/sparrow/control/FileImportPane.java index d77a15d5..2175ed09 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/FileImportPane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/FileImportPane.java @@ -53,7 +53,8 @@ public abstract class FileImportPane extends TitledDescriptionPane { fileChooser.setTitle("Open " + importer.getWalletModel().toDisplayString() + " File"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("All Files", "*.*"), - new FileChooser.ExtensionFilter("JSON", "*.json") + new FileChooser.ExtensionFilter("JSON", "*.json"), + new FileChooser.ExtensionFilter("TXT", "*.txt") ); File file = fileChooser.showOpenDialog(window); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java index c61a5c64..f4bebeef 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WalletExportDialog.java @@ -6,10 +6,7 @@ import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.AppController; 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 com.sparrowwallet.sparrow.io.*; import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.StackPane; @@ -35,7 +32,7 @@ public class WalletExportDialog extends Dialog { stackPane.getChildren().add(anchorPane); ScrollPane scrollPane = new ScrollPane(); - scrollPane.setPrefHeight(280); + scrollPane.setPrefHeight(400); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); anchorPane.getChildren().add(scrollPane); scrollPane.setFitToWidth(true); @@ -46,7 +43,7 @@ public class WalletExportDialog extends Dialog { if(wallet.getPolicyType() == PolicyType.SINGLE) { exporters = List.of(new Electrum(), new Specter()); } else if(wallet.getPolicyType() == PolicyType.MULTI) { - exporters = List.of(new ColdcardMultisig(), new Electrum(), new Specter()); + exporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new Specter()); } else { throw new UnsupportedOperationException("Cannot export wallet with policy type " + wallet.getPolicyType()); } @@ -61,7 +58,7 @@ public class WalletExportDialog extends Dialog { final ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); dialogPane.getButtonTypes().addAll(cancelButtonType); dialogPane.setPrefWidth(500); - dialogPane.setPrefHeight(360); + dialogPane.setPrefHeight(480); setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null); } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/WalletImportDialog.java index 65598149..3f145159 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(400); + scrollPane.setPrefHeight(420); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); anchorPane.getChildren().add(scrollPane); scrollPane.setFitToWidth(true); @@ -39,13 +39,13 @@ public class WalletImportDialog extends Dialog { AnchorPane.setRightAnchor(scrollPane, 0.0); Accordion importAccordion = new Accordion(); - List keystoreImporters = List.of(new ColdcardSinglesig()); + List keystoreImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig()); for(KeystoreFileImport importer : keystoreImporters) { FileWalletKeystoreImportPane importPane = new FileWalletKeystoreImportPane(importer); importAccordion.getPanes().add(importPane); } - List walletImporters = List.of(new ColdcardMultisig(), new Electrum(), new Specter()); + List walletImporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), 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(480); + dialogPane.setPrefHeight(500); setResultConverter(dialogButton -> dialogButton != cancelButtonType ? wallet : null); } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultMultisig.java b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultMultisig.java new file mode 100644 index 00000000..c5d61ff2 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultMultisig.java @@ -0,0 +1,55 @@ +package com.sparrowwallet.sparrow.io; + +import com.sparrowwallet.drongo.protocol.ScriptType; +import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.drongo.wallet.Wallet; +import com.sparrowwallet.drongo.wallet.WalletModel; + +import java.io.InputStream; + +public class CoboVaultMultisig extends ColdcardMultisig { + @Override + public String getName() { + return "Cobo Vault Multisig"; + } + + @Override + public WalletModel getWalletModel() { + return WalletModel.COBO_VAULT; + } + + @Override + public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException { + Keystore keystore = super.getKeystore(scriptType, inputStream, password); + keystore.setLabel("Cobo Vault"); + keystore.setWalletModel(getWalletModel()); + + return keystore; + } + + @Override + public String getKeystoreImportDescription() { + return "Import file created by using the Multisig Wallet > Show/Export XPUB > Export All > Export feature on your Cobo Vault."; + } + + @Override + public Wallet importWallet(InputStream inputStream, String password) throws ImportException { + Wallet wallet = super.importWallet(inputStream, password); + for(Keystore keystore : wallet.getKeystores()) { + keystore.setLabel(keystore.getLabel().replace("Coldcard", "Cobo Vault")); + keystore.setWalletModel(WalletModel.COBO_VAULT); + } + + return wallet; + } + + @Override + public String getWalletImportDescription() { + return "Import file created by using the Multisig Wallet > Create Multisig Wallet feature on your Cobo Vault."; + } + + @Override + public String getWalletExportDescription() { + return "Export file that can be read by your Cobo Vault using the Multisig Wallet > Import Multisig Wallet feature."; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java new file mode 100644 index 00000000..631fd4f7 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java @@ -0,0 +1,33 @@ +package com.sparrowwallet.sparrow.io; + +import com.sparrowwallet.drongo.protocol.ScriptType; +import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.drongo.wallet.WalletModel; + +import java.io.InputStream; + +public class CoboVaultSinglesig extends ColdcardSinglesig { + @Override + public String getName() { + return "Cobo Vault"; + } + + @Override + public String getKeystoreImportDescription() { + return "Import file created by using the Watch-Only Wallet > Generic Wallet > Export Wallet feature on your Cobo Vault."; + } + + @Override + public WalletModel getWalletModel() { + return WalletModel.COBO_VAULT; + } + + @Override + public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException { + Keystore keystore = super.getKeystore(scriptType, inputStream, password); + keystore.setLabel("Cobo Vault"); + keystore.setWalletModel(getWalletModel()); + + return keystore; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ColdcardMultisig.java b/src/main/java/com/sparrowwallet/sparrow/io/ColdcardMultisig.java index ede49697..b7f1bcf8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ColdcardMultisig.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ColdcardMultisig.java @@ -1,7 +1,6 @@ package com.sparrowwallet.sparrow.io; import com.google.common.io.CharStreams; -import com.google.gson.Gson; import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.Utils; @@ -19,8 +18,6 @@ import java.util.List; import java.util.Set; public class ColdcardMultisig implements WalletImport, KeystoreFileImport, WalletExport { - private final Gson gson = new Gson(); - @Override public String getName() { return "Coldcard Multisig"; @@ -153,7 +150,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle } if(!wallet.getPolicyType().equals(PolicyType.MULTI)) { - throw new ExportException("Coldcard multisig import requires a multisig wallet"); + throw new ExportException(getName() + " import requires a multisig wallet"); } boolean multipleDerivations = false; @@ -167,7 +164,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle try { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream)); - writer.append("# Coldcard Multisig setup file (created by Sparrow)\n"); + writer.append("# " + getName() + " setup file (created by Sparrow)\n"); writer.append("#\n"); writer.append("Name: ").append(wallet.getName()).append("\n"); writer.append("Policy: ").append(Integer.toString(wallet.getDefaultPolicy().getNumSignaturesRequired())).append(" of ").append(Integer.toString(wallet.getKeystores().size())).append("\n"); diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java index b79a92f2..d66735e7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java @@ -16,9 +16,9 @@ public class HwAirgappedController extends KeystoreImportDetailController { public void initializeView() { List importers = Collections.emptyList(); if(getMasterController().getWallet().getPolicyType().equals(PolicyType.SINGLE)) { - importers = List.of(new ColdcardSinglesig()); + importers = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig()); } else if(getMasterController().getWallet().getPolicyType().equals(PolicyType.MULTI)) { - importers = List.of(new ColdcardMultisig()); + importers = List.of(new ColdcardMultisig(), new CoboVaultMultisig()); } for(KeystoreImport importer : importers) { diff --git a/src/main/resources/image/cobovault-orig.png b/src/main/resources/image/cobovault-orig.png new file mode 100644 index 0000000000000000000000000000000000000000..4516c4821347c0fec31d1252b2d423782079517a GIT binary patch literal 34655 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 QJNrHEE^ue6Q}{3c22dLx`Tzg` literal 0 HcmV?d00001 diff --git a/src/main/resources/image/cobovault.png b/src/main/resources/image/cobovault.png new file mode 100644 index 0000000000000000000000000000000000000000..46a0d4a0cf8aca47ec509f98acd0067a80140f28 GIT binary patch literal 1978 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-mUKs7M+SzC{oH>NS%LfmsS%!O zzP=1vKsE;hJEIT-Gmymygba*Q3?LOi7DE!FG@PBqr~y^O1e7#n0Fn&+KwQLV1ZIl> z*-h=43@ktzq<0z+gMbGRgZN-NAhRMhCpWPqzevGY&l2cH10w?~Ljx-Va|Iv?L}oyj zGcRC7xMl$p+(e@V%m_Bf=!+XB3j!(50*}aI1_o|n5N2eUHAe#IU!}~Dh>{3jAFJg2 zT)o7U{G?R9irfN_E(V(lD*7iAWdWaj57fXq!y$}cUkRZ;?31P2gz zrX<7dDJU(<0o#(2te={bnwy$eQmk*NXQ+=&AJDgOjd{h;&;W*uennydP?G^xO{o=` z2ydb20?FXiR+5>9LmNm2q758~5Wm@^BwH1iCS|22mmrA(<-lR=l$frMBy6LPRkaP! ze^$QvDVb@NE{P?ncA!M1YiOWrU>*WXR5to(Y7n6d*6m!B3QCkA`T03^sA&|U6ImWf zD<~wa!0IDhGJ$bW42(oOQv(})tP)r?M&i#vJ-LJf#9~I>^ z(LqIk(}f9xy5PcedFE!uTeD{T_V<06W_~~6Q^MP<6A8L8YpqSb?hkv+d@c8j6sgKd8T=Zl*h_u}XOU6#5?Z{O;u#EMtdLUHxH zpD#t|WoJ~JWw)DpOMB(gZt6#0Y;u(--FKc`I^ub-WYvfk^`f0mAwzq!Ceo4MY z{*A)(oHfmtyZzU(|F`R6P-U!ny*SUgFW#x=dx}2z-?j`D)_v;yO>Ai`!Z`nWgS4t9$ zc}wiiYact_^!~f_hll*luS=SX=QaF~|570p|A6DgPL8Uog|h#=f@jt;#~36R*|}UZ zkuq0|-~P=}Jmu#^>#(YI7w1pnz4FiIy0(Af|EphKNL}>s;XV*AzQ5J`gOJzDNDkqL zk}oP5ms-6z({A{Iq3Xb7-KEE?BjuZ3Uy*CW5zPnc=N>3mXP6(hZ1qM^HOb)V>gTe~ HDWM4fn-7uq literal 0 HcmV?d00001 diff --git a/src/main/resources/image/cobovault@2x.png b/src/main/resources/image/cobovault@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..911083460cdc8d8781fd5f62647b2bec35bcadfa GIT binary patch literal 2933 zcmcImd011|62C#DfTFKLeH0{yXl3!sifl0nkcCac5(2WSAqxp6Bql*nD~Kox%BJj! zLfAJ^DOyFPSW)3U5wze!Em~A$D>Z^o5U@7^CFSX#z4t70X683DXU?4a-6U^M7d>4g zT>t>|+*~PiaAqsJwiY;wf6roolV-RR%?SX`r7f9aE(X^>aRS`9G#X$D(%QgRkQKlp zkb;0O05JlT8XyIPL5x4q5fJk^9Su;30l*Ph4t5CytD+4+djBClpb5@OY&O`Hk1N=f zYt`({kKqV}OqoamvqIVf02Yl!<1lC(+7@*3XMf!u?SEDA>9=zW5ysNrE6x5e6&O1NLdoTSjHDc z!jyKIVWKD*0S;F>`t;1k$!2}>BaV^?R9@ID6i2`jaw26?6b6YweGUii_7gBtI+qPF zFQ^!%7zCj|3*yA^=ePAYlv46Vn2gW;0!%5Hg{hJ_yEidb`zF9{zkK&A=+#-nne&ReKI zZ{IJHFC%_*v4Fi06*w=S4@;dn2f(A0_ekwt&0bm+7reDZUGPjlegTNOrBb>M0B9Mz zQJfetkdYkz;VwVJo3-oi2-(f>q?FI#tZlW*FYYt19^?=?59*qdYn3wHSWVN!2F%8Vr+Sve?}rfh>XRflG* zD>*XtG}dTSUF^>OE#r}=+8^tkxGv7@HI2R<$01!08GiUEn+Tm@-q^U(4@kxF4pQc=iQ|`B zUfKMTT=htM)hSA^+`yrqXLtm(B;!SO+tPP?2O}NJi_h_}le#TVUWKbh zLUbQbcJ}|f&8maydJgG=D*XP11G%7vN0Q+QN081qe*@9Vd($i8MhR_=rgam{kAa+~?_~ zn|){WzKesbPLeU6_jQ)6Z2tO}QJ>`EF>Fa|ZeCY4&E+}7+;&HY{O}H$Luu=s>5IX; zLTt2a&t_XTUD(|Ho2UKtE*Atft6i4=;Zi*usO1I{O{0aC$f-Sg2uyo$W_Wm1#OVY} z2-M?!d0>-#@xh+A=gM`A9bR6>#l7TC*>W*sKIH9(i~GfR$xGkvuz>yda=SAeH2lu{ z8#NgFo|X*sWoaC^5L|Efzr+iP?D533)rBU$)lU*w!_`-I=B8?nEHiP)5{Qg87a8>F zD}qXQko+?TT`lvWS{9zCh>ksfRMdtYK&14!g~jc?+jKr+^;UziteR(k)`Xm?=-qAb zSSX|~Ip)xL@=Nl-)WnIX&*Z#jlJ|6-9lTq z+gsp_$%g72PhaH8$GYR!EPNtk)TynUvFb# zYgQyF3*Y{Imd>k($5_`~W92g^>A|N?x>IeF`yMPRHfiM+JuG`Y)ZSWqF!`4ESXUpZ zt}d}BWh!jdVTA}zSncIlUYNwgv`urp%hNJO4qbEX47cW(e9P+C;ks?HMN{LJVkAPN zWzV7(yCDlNa$kpbNz8IM&429v+U>X2-XG;(q@1Q^*14D!>vv}Hh{?pZi^Z`RNbV5@ zq_QUM5u@WRdVJ!wP5x!;#WTjbgE>A92Gotm_NV4bSZ!uj6FfyjqvQmMsz@4e9(>d4 zKH}Axz0wt0XUYjZI`P&s;Xl5$-977%y7!++t-iALl7ck#ST~Gs_I&_r)z$wxe{;iU zoLy2}^Q&Q4L8|A-jra-+0lqk3Uz=VmVthBDAhGIEk(~#1Yw^2gi(a?sf{Jha&J*~< zjvA*XZ!+}j)|~q_u9qQV<)6*z#2K*6>!@#SE(7C9FI-$i2@z3)Gj^H-w+Ee17iEE8 znyd2qNy8GC=H;%(ivpVDLA0Fe_V>?t+JjEGL0O%Sm|(LsyyYIIlI8Yc$==j#_z9id geg8RModKIl*7ry*s_-`6tWFv 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 H`5XTO*x_ZF literal 0 HcmV?d00001