add specter diy keystore import

This commit is contained in:
Craig Raw 2020-12-09 17:16:44 +02:00
parent ccead92388
commit 582065e7f0
10 changed files with 105 additions and 16 deletions

2
drongo

@ -1 +1 @@
Subproject commit deb45687c08d4ff824ab7559233249c345d9c86a
Subproject commit 05674097428d25de043310f8ecddf06d998b3943

View file

@ -668,7 +668,7 @@ public class AppController implements Initializable {
}
private boolean attemptImportWallet(File file, SecureString password) {
List<WalletImport> walletImporters = List.of(new ColdcardSinglesig(), new ColdcardMultisig(), new Electrum(), new Specter(), new CoboVaultSinglesig(), new CoboVaultMultisig());
List<WalletImport> walletImporters = List.of(new ColdcardSinglesig(), new ColdcardMultisig(), new Electrum(), new SpecterDesktop(), new CoboVaultSinglesig(), new CoboVaultMultisig());
for(WalletImport importer : walletImporters) {
try(FileInputStream inputStream = new FileInputStream(file)) {
if(importer.isEncrypted(file) && password == null) {

View file

@ -41,9 +41,9 @@ public class WalletExportDialog extends Dialog<Wallet> {
List<WalletExport> exporters;
if(wallet.getPolicyType() == PolicyType.SINGLE) {
exporters = List.of(new Electrum(), new Specter());
exporters = List.of(new Electrum(), new SpecterDesktop());
} else if(wallet.getPolicyType() == PolicyType.MULTI) {
exporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new Specter());
exporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new SpecterDesktop());
} else {
throw new UnsupportedOperationException("Cannot export wallet with policy type " + wallet.getPolicyType());
}

View file

@ -45,7 +45,7 @@ public class WalletImportDialog extends Dialog<Wallet> {
importAccordion.getPanes().add(importPane);
}
List<WalletImport> walletImporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new Specter());
List<WalletImport> walletImporters = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(), new SpecterDesktop());
for(WalletImport importer : walletImporters) {
FileWalletImportPane importPane = new FileWalletImportPane(importer);
importAccordion.getPanes().add(importPane);

View file

@ -0,0 +1,64 @@
package com.sparrowwallet.sparrow.io;
import com.google.common.io.CharStreams;
import com.sparrowwallet.drongo.OutputDescriptor;
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.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class SpecterDIY implements KeystoreFileImport {
@Override
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
try {
String text = CharStreams.toString(new InputStreamReader(inputStream));
String outputDesc = "sh(" + text + ")";
OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(outputDesc);
Wallet wallet = outputDescriptor.toWallet();
if(wallet.getKeystores().size() != 1) {
throw new ImportException("Could not determine keystore from import");
}
Keystore keystore = wallet.getKeystores().get(0);
keystore.setLabel(getName());
keystore.setWalletModel(WalletModel.SPECTER_DIY);
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
return keystore;
} catch(IOException e) {
throw new ImportException(e);
}
}
@Override
public boolean isKeystoreImportScannable() {
return true;
}
@Override
public String getKeystoreImportDescription() {
return "Import file or QR created by using the Master Public Keys feature on your Specter DIY device. Note the default is P2WPKH for Single Signature, and P2WSH for Multi Signature.";
}
@Override
public boolean isEncrypted(File file) {
return false;
}
@Override
public String getName() {
return "Specter DIY";
}
@Override
public WalletModel getWalletModel() {
return WalletModel.SPECTER_DIY;
}
}

View file

@ -13,7 +13,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class Specter implements WalletImport, WalletExport {
public class SpecterDesktop implements WalletImport, WalletExport {
@Override
public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException {
try {
@ -70,7 +70,7 @@ public class Specter implements WalletImport, WalletExport {
throw new ImportException(e);
}
throw new ImportException("File was not a valid Specter wallet");
throw new ImportException("File was not a valid Specter Desktop wallet");
}
@Override
@ -85,12 +85,12 @@ public class Specter implements WalletImport, WalletExport {
@Override
public String getName() {
return "Specter";
return "Specter Desktop";
}
@Override
public WalletModel getWalletModel() {
return WalletModel.SPECTER;
return WalletModel.SPECTER_DESKTOP;
}
public static class SpecterWallet {

View file

@ -16,9 +16,9 @@ public class HwAirgappedController extends KeystoreImportDetailController {
public void initializeView() {
List<KeystoreFileImport> importers = Collections.emptyList();
if(getMasterController().getWallet().getPolicyType().equals(PolicyType.SINGLE)) {
importers = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig());
importers = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new SpecterDIY());
} else if(getMasterController().getWallet().getPolicyType().equals(PolicyType.MULTI)) {
importers = List.of(new ColdcardMultisig(), new CoboVaultMultisig());
importers = List.of(new ColdcardMultisig(), new CoboVaultMultisig(), new SpecterDIY());
}
for(KeystoreImport importer : importers) {

View file

@ -0,0 +1,24 @@
package com.sparrowwallet.sparrow.io;
import com.sparrowwallet.drongo.ExtendedKey;
import com.sparrowwallet.drongo.Network;
import com.sparrowwallet.drongo.protocol.ScriptType;
import com.sparrowwallet.drongo.wallet.Keystore;
import org.junit.Assert;
import org.junit.Test;
public class SpecterDIYTest extends IoTest {
@Test
public void testImport() throws ImportException {
Network.set(Network.TESTNET);
SpecterDIY specterDIY = new SpecterDIY();
Keystore keystore = specterDIY.getKeystore(ScriptType.P2WPKH, getInputStream("specter-diy-keystore.txt"), null);
Assert.assertEquals("Specter DIY", keystore.getLabel());
Assert.assertEquals("m/84'/1'/0'", keystore.getKeyDerivation().getDerivationPath());
Assert.assertEquals("b317ec86", keystore.getKeyDerivation().getMasterFingerprint());
Assert.assertEquals(ExtendedKey.fromDescriptor("vpub5YHLPnkkpPW1ecL7Di7Gv2wDHDtBNqRdt17gMULpxJ27ZA1MmW7xbZjdg1S7d5JKaJ8CiZEmRUHrEB6CGuLomA6ioVa1Pcke6fEb5CzDBU1"), keystore.getExtendedPublicKey());
Assert.assertTrue(keystore.isValid());
Network.set(Network.MAINNET);
}
}

View file

@ -6,11 +6,11 @@ import com.sparrowwallet.drongo.wallet.Wallet;
import org.junit.Assert;
import org.junit.Test;
public class SpecterTest extends IoTest {
public class SpecterDesktopTest extends IoTest {
@Test
public void testImport() throws ImportException {
Specter specter = new Specter();
Wallet wallet = specter.importWallet(getInputStream("specter-wallet.json"), null);
SpecterDesktop specterDesktop = new SpecterDesktop();
Wallet wallet = specterDesktop.importWallet(getInputStream("specter-wallet.json"), null);
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
@ -24,8 +24,8 @@ public class SpecterTest extends IoTest {
@Test
public void testMultisigImport() throws ImportException {
Specter specter = new Specter();
Wallet wallet = specter.importWallet(getInputStream("specter-multisig-wallet.json"), null);
SpecterDesktop specterDesktop = new SpecterDesktop();
Wallet wallet = specterDesktop.importWallet(getInputStream("specter-multisig-wallet.json"), null);
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());

View file

@ -0,0 +1 @@
[b317ec86/84h/1h/0h]vpub5YHLPnkkpPW1ecL7Di7Gv2wDHDtBNqRdt17gMULpxJ27ZA1MmW7xbZjdg1S7d5JKaJ8CiZEmRUHrEB6CGuLomA6ioVa1Pcke6fEb5CzDBU1