diff --git a/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java index 631fd4f7..58e5bb2f 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesig.java @@ -1,12 +1,21 @@ package com.sparrowwallet.sparrow.io; +import com.google.gson.Gson; +import com.sparrowwallet.drongo.ExtendedKey; +import com.sparrowwallet.drongo.KeyDerivation; +import com.sparrowwallet.drongo.policy.Policy; +import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; import com.sparrowwallet.drongo.wallet.Keystore; +import com.sparrowwallet.drongo.wallet.KeystoreSource; +import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.WalletModel; +import java.io.File; import java.io.InputStream; +import java.io.InputStreamReader; -public class CoboVaultSinglesig extends ColdcardSinglesig { +public class CoboVaultSinglesig implements KeystoreFileImport, WalletImport { @Override public String getName() { return "Cobo Vault"; @@ -24,10 +33,59 @@ public class CoboVaultSinglesig extends ColdcardSinglesig { @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()); + try { + Gson gson = new Gson(); + CoboVaultKeystore coboKeystore = gson.fromJson(new InputStreamReader(inputStream), CoboVaultKeystore.class); - return keystore; + Keystore keystore = new Keystore(); + keystore.setLabel(getName()); + keystore.setSource(KeystoreSource.HW_AIRGAPPED); + keystore.setWalletModel(WalletModel.COBO_VAULT); + keystore.setKeyDerivation(new KeyDerivation(coboKeystore.MasterFingerprint.toLowerCase(), "m/" + coboKeystore.AccountKeyPath)); + keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(coboKeystore.ExtPubKey)); + + ExtendedKey.Header header = ExtendedKey.Header.fromExtendedKey(coboKeystore.ExtPubKey); + if(header.getDefaultScriptType() != scriptType) { + throw new ImportException("This wallet's script type (" + scriptType + ") does not match the " + getName() + " script type (" + header.getDefaultScriptType() + ")"); + } + + return keystore; + } catch (Exception e) { + throw new ImportException(e); + } + } + + @Override + public String getWalletImportDescription() { + return getKeystoreImportDescription(); + } + + @Override + public Wallet importWallet(InputStream inputStream, String password) throws ImportException { + //Use default of P2WPKH + Keystore keystore = getKeystore(ScriptType.P2WPKH, inputStream, ""); + + Wallet wallet = new Wallet(); + wallet.setPolicyType(PolicyType.SINGLE); + wallet.setScriptType(ScriptType.P2WPKH); + wallet.getKeystores().add(keystore); + wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, wallet.getKeystores(), null)); + + if(!wallet.isValid()) { + throw new ImportException("Wallet is in an inconsistent state."); + } + + return wallet; + } + + @Override + public boolean isEncrypted(File file) { + return false; + } + + private static class CoboVaultKeystore { + public String ExtPubKey; + public String MasterFingerprint; + public String AccountKeyPath; } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ColdcardSinglesig.java b/src/main/java/com/sparrowwallet/sparrow/io/ColdcardSinglesig.java index 4588c3e4..790bb454 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ColdcardSinglesig.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/ColdcardSinglesig.java @@ -62,7 +62,7 @@ public class ColdcardSinglesig implements KeystoreFileImport, WalletImport { ScriptType ckScriptType = ScriptType.valueOf(ck.name.replace("p2wpkh-p2sh", "p2sh_p2wpkh").toUpperCase()); if(ckScriptType.equals(scriptType)) { Keystore keystore = new Keystore(); - keystore.setLabel("Coldcard"); + keystore.setLabel(getName()); keystore.setSource(KeystoreSource.HW_AIRGAPPED); keystore.setWalletModel(WalletModel.COLDCARD); keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, ck.deriv)); diff --git a/src/test/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesigTest.java b/src/test/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesigTest.java new file mode 100644 index 00000000..80acfe8d --- /dev/null +++ b/src/test/java/com/sparrowwallet/sparrow/io/CoboVaultSinglesigTest.java @@ -0,0 +1,33 @@ +package com.sparrowwallet.sparrow.io; + +import com.sparrowwallet.drongo.ExtendedKey; +import com.sparrowwallet.drongo.protocol.ScriptType; +import com.sparrowwallet.drongo.wallet.Keystore; +import org.junit.Assert; +import org.junit.Test; + +public class CoboVaultSinglesigTest extends IoTest { + @Test + public void testImport() throws ImportException { + CoboVaultSinglesig coboSingleSig = new CoboVaultSinglesig(); + Keystore keystore = coboSingleSig.getKeystore(ScriptType.P2WPKH, getInputStream("cobo-singlesig-keystore-1.json"), null); + + Assert.assertEquals("Cobo Vault", keystore.getLabel()); + Assert.assertEquals("m/84'/0'/0'", keystore.getKeyDerivation().getDerivationPath()); + Assert.assertEquals("73c5da0a", keystore.getKeyDerivation().getMasterFingerprint()); + Assert.assertEquals(ExtendedKey.fromDescriptor("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs"), keystore.getExtendedPublicKey()); + Assert.assertTrue(keystore.isValid()); + } + + @Test(expected = ImportException.class) + public void testIncorrectScriptType() throws ImportException { + CoboVaultSinglesig coboSingleSig = new CoboVaultSinglesig(); + Keystore keystore = coboSingleSig.getKeystore(ScriptType.P2SH_P2WPKH, getInputStream("cobo-singlesig-keystore-1.json"), null); + + Assert.assertEquals("Cobo Vault", keystore.getLabel()); + Assert.assertEquals("m/84'/0'/0'", keystore.getKeyDerivation().getDerivationPath()); + Assert.assertEquals("73c5da0a", keystore.getKeyDerivation().getMasterFingerprint()); + Assert.assertEquals(ExtendedKey.fromDescriptor("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs"), keystore.getExtendedPublicKey()); + Assert.assertTrue(keystore.isValid()); + } +} diff --git a/src/test/resources/com/sparrowwallet/sparrow/io/cobo-singlesig-keystore-1.json b/src/test/resources/com/sparrowwallet/sparrow/io/cobo-singlesig-keystore-1.json new file mode 100644 index 00000000..065bcb05 --- /dev/null +++ b/src/test/resources/com/sparrowwallet/sparrow/io/cobo-singlesig-keystore-1.json @@ -0,0 +1,6 @@ +{ + "ExtPubKey": "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs", + "MasterFingerprint": "73C5DA0A", + "AccountKeyPath": "84'/0'/0'", + "CoboVaultFirmwareVersion": "1.7.1(BTC-Only)" +} \ No newline at end of file