mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-04 21:36:45 +00:00
coldcard singlesig new feature, private key handling
This commit is contained in:
parent
6b651ec63a
commit
2de90dfdc1
16 changed files with 186 additions and 249 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 27dda9157649b35bc4450361c2f31d489eef492b
|
Subproject commit c5042cf130457233955aa4c72b1ad543bdfcb171
|
|
@ -1,6 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.control;
|
package com.sparrowwallet.sparrow.control;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
@ -363,7 +363,7 @@ public class DevicePane extends TitledPane {
|
||||||
keystore.setSource(KeystoreSource.HW_USB);
|
keystore.setSource(KeystoreSource.HW_USB);
|
||||||
keystore.setWalletModel(device.getModel());
|
keystore.setWalletModel(device.getModel());
|
||||||
keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath));
|
keystore.setKeyDerivation(new KeyDerivation(device.getFingerprint(), derivationPath));
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(xpub));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(xpub));
|
||||||
|
|
||||||
EventManager.get().post(new KeystoreImportEvent(keystore));
|
EventManager.get().post(new KeystoreImportEvent(keystore));
|
||||||
});
|
});
|
||||||
|
|
33
src/main/java/com/sparrowwallet/sparrow/io/Bip39.java
Normal file
33
src/main/java/com/sparrowwallet/sparrow/io/Bip39.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Bip39Calculator;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Bip39 implements KeystoreMnemonicImport {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Keystore getKeystore(ScriptType scriptType, List<String> mnemonicWords, String passphrase) throws ImportException {
|
||||||
|
Bip39Calculator bip39Calculator = new Bip39Calculator();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WalletModel getWalletModel() {
|
||||||
|
return WalletModel.SPARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeystoreImportDescription() {
|
||||||
|
return "Import your 12 to 24 word mnemonic and optional passphrase";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import com.sparrowwallet.drongo.policy.Policy;
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
|
@ -26,11 +26,6 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
|
||||||
return "Coldcard Multisig";
|
return "Coldcard Multisig";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PolicyType getKeystorePolicyType() {
|
|
||||||
return PolicyType.MULTI;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WalletModel getWalletModel() {
|
public WalletModel getWalletModel() {
|
||||||
return WalletModel.COLDCARD;
|
return WalletModel.COLDCARD;
|
||||||
|
@ -47,13 +42,13 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
|
||||||
|
|
||||||
if(scriptType.equals(ScriptType.P2SH)) {
|
if(scriptType.equals(ScriptType.P2SH)) {
|
||||||
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2sh_deriv));
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2sh_deriv));
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2sh));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(cck.p2sh));
|
||||||
} else if(scriptType.equals(ScriptType.P2SH_P2WSH)) {
|
} else if(scriptType.equals(ScriptType.P2SH_P2WSH)) {
|
||||||
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_p2sh_deriv));
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_p2sh_deriv));
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2wsh_p2sh));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(cck.p2wsh_p2sh));
|
||||||
} else if(scriptType.equals(ScriptType.P2WSH)) {
|
} else if(scriptType.equals(ScriptType.P2WSH)) {
|
||||||
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_deriv));
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_deriv));
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2wsh));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(cck.p2wsh));
|
||||||
} else {
|
} else {
|
||||||
throw new ImportException("Correct derivation not found for script type: " + scriptType);
|
throw new ImportException("Correct derivation not found for script type: " + scriptType);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +56,7 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
|
||||||
return keystore;
|
return keystore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ColdcardKeystore {
|
private static class ColdcardKeystore {
|
||||||
public String p2sh_deriv;
|
public String p2sh_deriv;
|
||||||
public String p2sh;
|
public String p2sh;
|
||||||
public String p2wsh_p2sh_deriv;
|
public String p2wsh_p2sh_deriv;
|
||||||
|
@ -118,7 +113,7 @@ public class ColdcardMultisig implements MultisigWalletImport, KeystoreFileImpor
|
||||||
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
||||||
keystore.setWalletModel(WalletModel.COLDCARD);
|
keystore.setWalletModel(WalletModel.COLDCARD);
|
||||||
keystore.setKeyDerivation(new KeyDerivation(key, derivation));
|
keystore.setKeyDerivation(new KeyDerivation(key, derivation));
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(value));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(value));
|
||||||
wallet.getKeystores().add(keystore);
|
wallet.getKeystores().add(keystore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,30 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.gson.Gson;
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
|
||||||
import com.sparrowwallet.drongo.policy.Policy;
|
|
||||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
import com.sparrowwallet.drongo.wallet.KeystoreSource;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.List;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Map;
|
||||||
import static com.sparrowwallet.drongo.protocol.ScriptType.*;
|
|
||||||
|
|
||||||
public class ColdcardSinglesig implements KeystoreFileImport, SinglesigWalletImport {
|
|
||||||
public static final List<ScriptType> ALLOWED_SCRIPT_TYPES = List.of(P2PKH, P2SH_P2WPKH, P2WPKH);
|
|
||||||
|
|
||||||
|
public class ColdcardSinglesig implements KeystoreFileImport {
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Coldcard";
|
return "Coldcard";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PolicyType getKeystorePolicyType() {
|
|
||||||
return PolicyType.SINGLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getKeystoreImportDescription() {
|
public String getKeystoreImportDescription() {
|
||||||
return "Import file created by using the Advanced > Dump Summary feature on your Coldcard";
|
return "Import file created by using the Advanced > MicroSD > Export Wallet > Generic JSON feature on your Coldcard";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,72 +32,55 @@ public class ColdcardSinglesig implements KeystoreFileImport, SinglesigWalletImp
|
||||||
return WalletModel.COLDCARD;
|
return WalletModel.COLDCARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
|
||||||
Wallet wallet = importWallet(scriptType, inputStream, password);
|
|
||||||
|
|
||||||
return wallet.getKeystores().get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Wallet importWallet(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
|
||||||
if(!ALLOWED_SCRIPT_TYPES.contains(scriptType)) {
|
|
||||||
throw new ImportException("Script type of " + scriptType + " is not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Wallet wallet = new Wallet();
|
|
||||||
wallet.setPolicyType(PolicyType.SINGLE);
|
|
||||||
wallet.setScriptType(scriptType);
|
|
||||||
String masterFingerprint = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
List<String> lines = CharStreams.readLines(new InputStreamReader(inputStream));
|
|
||||||
|
|
||||||
for (String line : lines) {
|
|
||||||
line = line.trim();
|
|
||||||
if (line.isEmpty() || line.startsWith("#")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(line.startsWith("xpub")) {
|
|
||||||
ExtendedPublicKey masterXpub = ExtendedPublicKey.fromDescriptor(line);
|
|
||||||
masterFingerprint = Utils.bytesToHex(masterXpub.getPubKey().getFingerprint()).toUpperCase();
|
|
||||||
wallet.setName("Coldcard " + masterFingerprint);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] keyValue = line.split("=>");
|
|
||||||
if(keyValue.length == 2) {
|
|
||||||
String key = keyValue[0].trim();
|
|
||||||
String value = keyValue[1].trim();
|
|
||||||
|
|
||||||
if(!key.equals("m") && scriptType.getDefaultDerivationPath().startsWith(key)) {
|
|
||||||
ExtendedPublicKey extPubKey = ExtendedPublicKey.fromDescriptor(value);
|
|
||||||
Keystore keystore = new Keystore();
|
|
||||||
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
|
||||||
keystore.setWalletModel(WalletModel.COLDCARD);
|
|
||||||
keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, key));
|
|
||||||
keystore.setExtendedPublicKey(extPubKey);
|
|
||||||
wallet.getKeystores().add(keystore);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, scriptType, wallet.getKeystores(), 1));
|
|
||||||
return wallet;
|
|
||||||
} catch(Exception e) {
|
|
||||||
throw new ImportException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getWalletImportDescription() {
|
|
||||||
return "Import file created by using the Advanced > Dump Summary feature on your Coldcard";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEncrypted(File file) {
|
public boolean isEncrypted(File file) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
||||||
|
try {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Type stringStringMap = new TypeToken<Map<String, JsonElement>>() {
|
||||||
|
}.getType();
|
||||||
|
Map<String, JsonElement> map = gson.fromJson(new InputStreamReader(inputStream), stringStringMap);
|
||||||
|
|
||||||
|
if (map.get("xfp") == null) {
|
||||||
|
throw new ImportException("This is not a valid Coldcard wallet export");
|
||||||
|
}
|
||||||
|
|
||||||
|
String masterFingerprint = map.get("xfp").getAsString();
|
||||||
|
|
||||||
|
for (String key : map.keySet()) {
|
||||||
|
if (key.startsWith("bip")) {
|
||||||
|
ColdcardKeystore ck = gson.fromJson(map.get(key), ColdcardKeystore.class);
|
||||||
|
|
||||||
|
if(ck.name != null) {
|
||||||
|
ScriptType ckScriptType = ScriptType.valueOf(ck.name.replace("p2wpkh-p2sh", "p2sh_p2wpkh").toUpperCase());
|
||||||
|
if(ckScriptType.equals(scriptType)) {
|
||||||
|
Keystore keystore = new Keystore();
|
||||||
|
keystore.setLabel("Coldcard " + masterFingerprint.toUpperCase());
|
||||||
|
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
||||||
|
keystore.setWalletModel(WalletModel.COLDCARD);
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(masterFingerprint, ck.deriv));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(ck.xpub));
|
||||||
|
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ImportException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ImportException("Correct derivation not found for script type: " + scriptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ColdcardKeystore {
|
||||||
|
public String deriv;
|
||||||
|
public String name;
|
||||||
|
public String xpub;
|
||||||
|
public String xfp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
|
@ -27,11 +27,6 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
|
||||||
return "Electrum";
|
return "Electrum";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PolicyType getKeystorePolicyType() {
|
|
||||||
return PolicyType.SINGLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WalletModel getWalletModel() {
|
public WalletModel getWalletModel() {
|
||||||
return WalletModel.ELECTRUM;
|
return WalletModel.ELECTRUM;
|
||||||
|
@ -111,12 +106,12 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
|
||||||
}
|
}
|
||||||
keystore.setWalletModel(WalletModel.ELECTRUM);
|
keystore.setWalletModel(WalletModel.ELECTRUM);
|
||||||
}
|
}
|
||||||
ExtendedPublicKey xPub = ExtendedPublicKey.fromDescriptor(ek.xpub);
|
ExtendedKey xPub = ExtendedKey.fromDescriptor(ek.xpub);
|
||||||
keystore.setKeyDerivation(new KeyDerivation(ek.root_fingerprint, ek.derivation));
|
keystore.setKeyDerivation(new KeyDerivation(ek.root_fingerprint, ek.derivation));
|
||||||
keystore.setExtendedPublicKey(xPub);
|
keystore.setExtendedPublicKey(xPub);
|
||||||
wallet.getKeystores().add(keystore);
|
wallet.getKeystores().add(keystore);
|
||||||
|
|
||||||
ExtendedPublicKey.XpubHeader xpubHeader = ExtendedPublicKey.XpubHeader.fromXpub(ek.xpub);
|
ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromExtendedKey(ek.xpub);
|
||||||
scriptType = xpubHeader.getDefaultScriptType();
|
scriptType = xpubHeader.getDefaultScriptType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +165,7 @@ public class Electrum implements KeystoreFileImport, SinglesigWalletImport, Mult
|
||||||
throw new ExportException("Could not export a wallet with a " + wallet.getPolicyType() + " policy");
|
throw new ExportException("Could not export a wallet with a " + wallet.getPolicyType() + " policy");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtendedPublicKey.XpubHeader xpubHeader = ExtendedPublicKey.XpubHeader.fromScriptType(wallet.getScriptType());
|
ExtendedKey.Header xpubHeader = ExtendedKey.Header.fromScriptType(wallet.getScriptType());
|
||||||
|
|
||||||
int index = 1;
|
int index = 1;
|
||||||
for(Keystore keystore : wallet.getKeystores()) {
|
for(Keystore keystore : wallet.getKeystores()) {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
|
||||||
public interface KeystoreImport extends Import {
|
public interface KeystoreImport extends Import {
|
||||||
PolicyType getKeystorePolicyType();
|
|
||||||
WalletModel getWalletModel();
|
WalletModel getWalletModel();
|
||||||
String getKeystoreImportDescription();
|
String getKeystoreImportDescription();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package com.sparrowwallet.sparrow.io;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface KeystoreMnemonicImport extends KeystoreImport {
|
public interface KeystoreMnemonicImport extends KeystoreImport {
|
||||||
Keystore getKeystore(ScriptType scriptType, String[] mnemonicWords, String passphrase) throws ImportException;
|
Keystore getKeystore(ScriptType scriptType, List<String> mnemonicWords, String passphrase) throws ImportException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ public class Storage {
|
||||||
|
|
||||||
private Storage() {
|
private Storage() {
|
||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
gsonBuilder.registerTypeAdapter(ExtendedPublicKey.class, new ExtendedPublicKeySerializer());
|
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
|
||||||
gsonBuilder.registerTypeAdapter(ExtendedPublicKey.class, new ExtendedPublicKeyDeserializer());
|
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
|
||||||
gson = gsonBuilder.setPrettyPrinting().create();
|
gson = gsonBuilder.setPrettyPrinting().create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,17 +95,17 @@ public class Storage {
|
||||||
return new File(System.getProperty("user.home"));
|
return new File(System.getProperty("user.home"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ExtendedPublicKeySerializer implements JsonSerializer<ExtendedPublicKey> {
|
private static class ExtendedPublicKeySerializer implements JsonSerializer<ExtendedKey> {
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(ExtendedPublicKey src, Type typeOfSrc, JsonSerializationContext context) {
|
public JsonElement serialize(ExtendedKey src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
return new JsonPrimitive(src.toString());
|
return new JsonPrimitive(src.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ExtendedPublicKeyDeserializer implements JsonDeserializer<ExtendedPublicKey> {
|
private static class ExtendedPublicKeyDeserializer implements JsonDeserializer<ExtendedKey> {
|
||||||
@Override
|
@Override
|
||||||
public ExtendedPublicKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public ExtendedKey deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
return ExtendedPublicKey.fromDescriptor(json.getAsJsonPrimitive().getAsString());
|
return ExtendedKey.fromDescriptor(json.getAsJsonPrimitive().getAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.sparrowwallet.sparrow.wallet;
|
package com.sparrowwallet.sparrow.wallet;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.KeyDerivation;
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
@ -83,8 +83,8 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
xpub.textProperty().addListener((observable, oldValue, newValue) -> {
|
xpub.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
if(ExtendedPublicKey.isValid(newValue)) {
|
if(ExtendedKey.isValid(newValue)) {
|
||||||
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(newValue));
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(newValue));
|
||||||
EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet()));
|
EventManager.get().post(new SettingsChangedEvent(walletForm.getWallet()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -107,7 +107,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
|
|
||||||
validationSupport.registerValidator(xpub, Validator.combine(
|
validationSupport.registerValidator(xpub, Validator.combine(
|
||||||
Validator.createEmptyValidator("xPub is required"),
|
Validator.createEmptyValidator("xPub is required"),
|
||||||
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "xPub is invalid", !ExtendedPublicKey.isValid(newValue))
|
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "xPub is invalid", !ExtendedKey.isValid(newValue))
|
||||||
));
|
));
|
||||||
|
|
||||||
validationSupport.registerValidator(derivation, Validator.combine(
|
validationSupport.registerValidator(derivation, Validator.combine(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
@ -15,46 +15,46 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void importKeystore1() throws ImportException {
|
public void importKeystore1() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WSH, getInputStream("cc-multisig-keystore-1.json"));
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WSH, getInputStream("cc-multisig-keystore-1.json"), null);
|
||||||
Assert.assertEquals("Coldcard 0F056943", keystore.getLabel());
|
Assert.assertEquals("Coldcard 0F056943", keystore.getLabel());
|
||||||
Assert.assertEquals("m/48'/1'/0'/1'", keystore.getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/1'/0'/1'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("0f056943", keystore.getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("0f056943", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("Upub5T4XUooQzDXL58NCHk8ZCw9BsRSLCtnyHeZEExAq1XdnBFXiXVrHFuvvmh3TnCR7XmKHxkwqdACv68z7QKT1vwru9L1SZSsw8B2fuBvtSa6"), keystore.getExtendedPublicKey());
|
Assert.assertEquals(ExtendedKey.fromDescriptor("Upub5T4XUooQzDXL58NCHk8ZCw9BsRSLCtnyHeZEExAq1XdnBFXiXVrHFuvvmh3TnCR7XmKHxkwqdACv68z7QKT1vwru9L1SZSsw8B2fuBvtSa6"), keystore.getExtendedPublicKey());
|
||||||
Assert.assertTrue(keystore.isValid());
|
Assert.assertTrue(keystore.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = ImportException.class)
|
@Test(expected = ImportException.class)
|
||||||
public void importKeystore1IncorrectScriptType() throws ImportException {
|
public void importKeystore1IncorrectScriptType() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WPKH, getInputStream("cc-multisig-keystore-1.json"));
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WPKH, getInputStream("cc-multisig-keystore-1.json"), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importKeystore2() throws ImportException {
|
public void importKeystore2() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH, getInputStream("cc-multisig-keystore-2.json"));
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH, getInputStream("cc-multisig-keystore-2.json"), null);
|
||||||
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
||||||
Assert.assertEquals("m/45'", keystore.getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/45'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9"), keystore.getExtendedPublicKey());
|
Assert.assertEquals(ExtendedKey.fromDescriptor("tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9"), keystore.getExtendedPublicKey());
|
||||||
Assert.assertTrue(keystore.isValid());
|
Assert.assertTrue(keystore.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importKeystore2b() throws ImportException {
|
public void importKeystore2b() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2WSH, getInputStream("cc-multisig-keystore-2.json"));
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2WSH, getInputStream("cc-multisig-keystore-2.json"), null);
|
||||||
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
||||||
Assert.assertEquals("m/48'/1'/0'/2'", keystore.getKeyDerivation().getDerivationPath());
|
Assert.assertEquals("m/48'/1'/0'/2'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("Vpub5nUnvPehg1VYQh13dGznx1P9moac3SNUrn3qhU9r85RhXabYbSSBNsNNwyR7akozAZJw1SZmRRjry1zY8PWMuw8Ga1vQZ5qzPjKyTDQwtzs"), keystore.getExtendedPublicKey());
|
Assert.assertEquals(ExtendedKey.fromDescriptor("Vpub5nUnvPehg1VYQh13dGznx1P9moac3SNUrn3qhU9r85RhXabYbSSBNsNNwyR7akozAZJw1SZmRRjry1zY8PWMuw8Ga1vQZ5qzPjKyTDQwtzs"), keystore.getExtendedPublicKey());
|
||||||
Assert.assertTrue(keystore.isValid());
|
Assert.assertTrue(keystore.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void importWallet1() throws ImportException {
|
public void importWallet1() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-1.txt"));
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-1.txt"), null);
|
||||||
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
|
@ -66,7 +66,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void importWallet2() throws ImportException {
|
public void importWallet2() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-2.txt"));
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-2.txt"), null);
|
||||||
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
@ -78,7 +78,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void importWalletMultiDeriv() throws ImportException {
|
public void importWalletMultiDeriv() throws ImportException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-multideriv.txt"));
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-multideriv.txt"), null);
|
||||||
Assert.assertEquals("el-CC-3-of-3-sb-2", wallet.getName());
|
Assert.assertEquals("el-CC-3-of-3-sb-2", wallet.getName());
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
|
@ -96,7 +96,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
public void exportWallet1() throws ImportException, ExportException, IOException {
|
public void exportWallet1() throws ImportException, ExportException, IOException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-1.txt"));
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-1.txt"));
|
||||||
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes));
|
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes), null);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ccMultisig.exportWallet(wallet, baos);
|
ccMultisig.exportWallet(wallet, baos);
|
||||||
byte[] exportedBytes = baos.toByteArray();
|
byte[] exportedBytes = baos.toByteArray();
|
||||||
|
@ -109,7 +109,7 @@ public class ColdcardMultisigTest extends IoTest {
|
||||||
public void exportWalletMultiDeriv() throws ImportException, ExportException, IOException {
|
public void exportWalletMultiDeriv() throws ImportException, ExportException, IOException {
|
||||||
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-multideriv.txt"));
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-multideriv.txt"));
|
||||||
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes));
|
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes), null);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ccMultisig.exportWallet(wallet, baos);
|
ccMultisig.exportWallet(wallet, baos);
|
||||||
byte[] exportedBytes = baos.toByteArray();
|
byte[] exportedBytes = baos.toByteArray();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -10,16 +10,24 @@ public class ColdcardSinglesigTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void testImport() throws ImportException {
|
public void testImport() throws ImportException {
|
||||||
ColdcardSinglesig ccSingleSig = new ColdcardSinglesig();
|
ColdcardSinglesig ccSingleSig = new ColdcardSinglesig();
|
||||||
Wallet wallet = ccSingleSig.importWallet(ScriptType.P2PKH, getInputStream("cc-wallet-dump.txt"));
|
Keystore keystore = ccSingleSig.getKeystore(ScriptType.P2SH_P2WPKH, getInputStream("cc-singlesig-keystore-1.json"), null);
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
|
||||||
|
|
||||||
Assert.assertEquals("Coldcard 3D88D0CF", wallet.getName());
|
Assert.assertEquals("Coldcard 0F056943", keystore.getLabel());
|
||||||
Assert.assertEquals(ScriptType.P2PKH, wallet.getScriptType());
|
Assert.assertEquals("m/49'/1'/123'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
Assert.assertEquals("0f056943", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
Assert.assertEquals("pkh(keystore1)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
Assert.assertEquals(ExtendedKey.fromDescriptor("tpubDCDqt7XXvhAdy1MpSze5nMJA9x8DrdRaKALRRPasfxyHpiqWWEAr9cbDBQ9BcX7cB3up98Pk97U2QQ3xrvQsi5dNPmRYYhdcsKY9wwEY87T"), keystore.getExtendedPublicKey());
|
||||||
Assert.assertTrue(wallet.isValid());
|
Assert.assertTrue(keystore.isValid());
|
||||||
Assert.assertEquals("3d88d0cf", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
}
|
||||||
Assert.assertEquals("m/44'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
|
||||||
Assert.assertEquals("xpub6AuabxJxEnAJbc8iBE2B5n7hxYAZC5xLjpG7oY1kyhMfz5mN13wLRaGPnCyvLo4Ec5aRSa6ZeMPHMUEABpdKxtcPymJpDG5KPEsLGTApGye", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
@Test
|
||||||
|
public void testImportWitness() throws ImportException {
|
||||||
|
ColdcardSinglesig ccSingleSig = new ColdcardSinglesig();
|
||||||
|
Keystore keystore = ccSingleSig.getKeystore(ScriptType.P2WPKH, getInputStream("cc-singlesig-keystore-1.json"), null);
|
||||||
|
|
||||||
|
Assert.assertEquals("Coldcard 0F056943", keystore.getLabel());
|
||||||
|
Assert.assertEquals("m/84'/1'/123'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("0f056943", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals(ExtendedKey.fromDescriptor("tpubDC7jGaaSE66VDB6VhEDFYQSCAyugXmfnMnrMVyHNzW9wryyTxvha7TmfAHd7GRXrr2TaAn2HXn9T8ep4gyNX1bzGiieqcTUNcu2poyntrET"), keystore.getExtendedPublicKey());
|
||||||
|
Assert.assertTrue(keystore.isValid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,14 @@ public class ECIESOutputStreamTest extends IoTest {
|
||||||
public void encrypt() throws ImportException, ExportException {
|
public void encrypt() throws ImportException, ExportException {
|
||||||
Electrum electrum = new Electrum();
|
Electrum electrum = new Electrum();
|
||||||
ECKey decryptionKey = ECKey.createKeyPbkdf2HmacSha512("pass");
|
ECKey decryptionKey = ECKey.createKeyPbkdf2HmacSha512("pass");
|
||||||
Wallet wallet = electrum.importWallet(new InflaterInputStream(new ECIESInputStream(getInputStream("electrum-encrypted"), decryptionKey)));
|
Wallet wallet = electrum.importWallet(new InflaterInputStream(new ECIESInputStream(getInputStream("electrum-encrypted"), decryptionKey)), null);
|
||||||
|
|
||||||
ECKey encyptionKey = ECKey.fromPublicOnly(decryptionKey);
|
ECKey encyptionKey = ECKey.fromPublicOnly(decryptionKey);
|
||||||
ByteArrayOutputStream dummyFileOutputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream dummyFileOutputStream = new ByteArrayOutputStream();
|
||||||
electrum.exportWallet(wallet, new DeflaterOutputStream(new ECIESOutputStream(dummyFileOutputStream, encyptionKey)));
|
electrum.exportWallet(wallet, new DeflaterOutputStream(new ECIESOutputStream(dummyFileOutputStream, encyptionKey)));
|
||||||
|
|
||||||
ByteArrayInputStream dummyFileInputStream = new ByteArrayInputStream(dummyFileOutputStream.toByteArray());
|
ByteArrayInputStream dummyFileInputStream = new ByteArrayInputStream(dummyFileOutputStream.toByteArray());
|
||||||
wallet = electrum.importWallet(new InflaterInputStream(new ECIESInputStream(dummyFileInputStream, decryptionKey)));
|
wallet = electrum.importWallet(new InflaterInputStream(new ECIESInputStream(dummyFileInputStream, decryptionKey)), null);
|
||||||
|
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class ElectrumTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void testSinglesigImport() throws ImportException {
|
public void testSinglesigImport() throws ImportException {
|
||||||
Electrum electrum = new Electrum();
|
Electrum electrum = new Electrum();
|
||||||
Wallet wallet = electrum.importWallet(getInputStream("electrum-singlesig-wallet.json"));
|
Wallet wallet = electrum.importWallet(getInputStream("electrum-singlesig-wallet.json"), null);
|
||||||
|
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
|
@ -31,11 +31,11 @@ public class ElectrumTest extends IoTest {
|
||||||
public void testSinglesigExport() throws ImportException, ExportException, IOException {
|
public void testSinglesigExport() throws ImportException, ExportException, IOException {
|
||||||
Electrum electrum = new Electrum();
|
Electrum electrum = new Electrum();
|
||||||
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-singlesig-wallet.json"));
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-singlesig-wallet.json"));
|
||||||
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes));
|
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes), null);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
electrum.exportWallet(wallet, baos);
|
electrum.exportWallet(wallet, baos);
|
||||||
|
|
||||||
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()));
|
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()), null);
|
||||||
Assert.assertTrue(wallet.isValid());
|
Assert.assertTrue(wallet.isValid());
|
||||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
|
@ -49,7 +49,7 @@ public class ElectrumTest extends IoTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMultisigImport() throws ImportException {
|
public void testMultisigImport() throws ImportException {
|
||||||
Electrum electrum = new Electrum();
|
Electrum electrum = new Electrum();
|
||||||
Wallet wallet = electrum.importWallet(getInputStream("electrum-multisig-wallet.json"));
|
Wallet wallet = electrum.importWallet(getInputStream("electrum-multisig-wallet.json"), null);
|
||||||
|
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
@ -67,11 +67,11 @@ public class ElectrumTest extends IoTest {
|
||||||
public void testMultisigExport() throws ImportException, ExportException, IOException {
|
public void testMultisigExport() throws ImportException, ExportException, IOException {
|
||||||
Electrum electrum = new Electrum();
|
Electrum electrum = new Electrum();
|
||||||
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-multisig-wallet.json"));
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-multisig-wallet.json"));
|
||||||
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes));
|
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes), null);
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
electrum.exportWallet(wallet, baos);
|
electrum.exportWallet(wallet, baos);
|
||||||
|
|
||||||
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()));
|
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()), null);
|
||||||
Assert.assertTrue(wallet.isValid());
|
Assert.assertTrue(wallet.isValid());
|
||||||
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"chain": "XTN",
|
||||||
|
"xfp": "0F056943",
|
||||||
|
"xpub": "tpubD6NzVbkrYhZ4XzL5Dhayo67Gorv1YMS7j8pRUvVMd5odC2LBPLAygka9p7748JtSq82FNGPppFEz5xxZUdasBRCqJqXvUHq6xpnsMcYJzeh",
|
||||||
|
"account": 123,
|
||||||
|
"bip44": {
|
||||||
|
"deriv": "m/44'/1'/123'",
|
||||||
|
"first": "n44vs1Rv7T8SANrg2PFGQhzVkhr5Q6jMMD",
|
||||||
|
"name": "p2pkh",
|
||||||
|
"xfp": "B7908B26",
|
||||||
|
"xpub": "tpubDCiHGUNYdRRGoSH22j8YnruUKgguCK1CC2NFQUf9PApeZh8ewAJJWGMUrhggDNK73iCTanWXv1RN5FYemUH8UrVUBjqDb8WF2VoKmDh9UTo"
|
||||||
|
},
|
||||||
|
"bip49": {
|
||||||
|
"_pub": "upub5DMRSsh6mNak9KbcVjJ7xAgHJvbE3Nx22CBTier5C35kv8j7g2q58ywxskBe6JCcAE2VH86CE2aL4MifJyKbRw8Gj9ay7SWvUBkp2DJ7y52",
|
||||||
|
"deriv": "m/49'/1'/123'",
|
||||||
|
"first": "2N87V39riUUCd4vmXfDjMWAu9gUCiBji5jB",
|
||||||
|
"name": "p2wpkh-p2sh",
|
||||||
|
"xfp": "CEE1D809",
|
||||||
|
"xpub": "tpubDCDqt7XXvhAdy1MpSze5nMJA9x8DrdRaKALRRPasfxyHpiqWWEAr9cbDBQ9BcX7cB3up98Pk97U2QQ3xrvQsi5dNPmRYYhdcsKY9wwEY87T"
|
||||||
|
},
|
||||||
|
"bip84": {
|
||||||
|
"_pub": "vpub5Y5a91QvDT45EnXQaKeuvJupVvX8f9BiywDcadSTtaeJ1VgJPPXMitnYsqd9k7GnEqh44FKJ5McJfu6KrihFXhAmvSWgm7BAVVK8Gupu4fL",
|
||||||
|
"deriv": "m/84'/1'/123'",
|
||||||
|
"first": "tb1qc58ys2dphtphg6yuugdf3d0kufmk0tye044g3l",
|
||||||
|
"name": "p2wpkh",
|
||||||
|
"xfp": "78CF94E5",
|
||||||
|
"xpub": "tpubDC7jGaaSE66VDB6VhEDFYQSCAyugXmfnMnrMVyHNzW9wryyTxvha7TmfAHd7GRXrr2TaAn2HXn9T8ep4gyNX1bzGiieqcTUNcu2poyntrET"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,97 +0,0 @@
|
||||||
# Coldcard Wallet Summary File
|
|
||||||
|
|
||||||
## Wallet operates on blockchain: Bitcoin
|
|
||||||
|
|
||||||
For BIP44, this is coin_type '0', and internally we use symbol BTC for this blockchain.
|
|
||||||
|
|
||||||
## Top-level, 'master' extended public key ('m/'):
|
|
||||||
|
|
||||||
xpub661MyMwAqRbcGQU2MzQdLtxKvfa9shyo1vUGkxETFtDNGjggQMNMd5rTZfbKR25yCXHgtpwwko4Cyq1PkzLoEGRSmNy5GnnhCkWERN1wJSy
|
|
||||||
|
|
||||||
Derived public keys, as may be needed for different systems:
|
|
||||||
|
|
||||||
|
|
||||||
## For Bitcoin Core: m/{account}'/{change}'/{idx}'
|
|
||||||
|
|
||||||
m => xpub661MyMwAqRbcGQU2MzQdLtxKvfa9shyo1vUGkxETFtDNGjggQMNMd5rTZfbKR25yCXHgtpwwko4Cyq1PkzLoEGRSmNy5GnnhCkWERN1wJSy
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/0'/0'/0' => 1AaTq7W3Mw8J4UGpKL1Sc4DwWpNQSBgeHa
|
|
||||||
m/0'/0'/1' => 1GRDRoXkjPue2SPXvL8XZz5paK2Te4tbxZ
|
|
||||||
m/0'/0'/2' => 1Gxwx9pxvsmQCTf3Yx2Yo2jfSqjeHTgqJA
|
|
||||||
m/0'/0'/3' => 13ECwnbfj99my2edurXyzVtGW8NYGHq7u1
|
|
||||||
m/0'/0'/4' => 1D8KQ8Yctm4WesGsviQ8ZWApSbh7PAnLqy
|
|
||||||
|
|
||||||
|
|
||||||
## For Bitcoin Core (Segregated Witness, P2PKH): m/{account}'/{change}'/{idx}'
|
|
||||||
|
|
||||||
m => xpub661MyMwAqRbcGQU2MzQdLtxKvfa9shyo1vUGkxETFtDNGjggQMNMd5rTZfbKR25yCXHgtpwwko4Cyq1PkzLoEGRSmNy5GnnhCkWERN1wJSy
|
|
||||||
# SLIP-132 style
|
|
||||||
m => zpub6jftahH18ngZxzrG2hysm59LGbs3kwxnr9WiKk2E1ty8NwK8ufhUsDAjc5WVQqPp1oXJPn94g7mJkQEXCPAppjneW4MvScRfkCdXCXk1zgB
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/0'/0'/0' => bc1qdyx5z3p6nlxrjfay7mhefx8t4jscqu6sueg0vu
|
|
||||||
m/0'/0'/1' => bc1q4y0ruupprurvl9umalmt0u9ztju0qxfqfrqwhw
|
|
||||||
m/0'/0'/2' => bc1q4u029f45f3xegw2z72kmd4xcfl8dgsvg58u7xn
|
|
||||||
m/0'/0'/3' => bc1qrph6zs0yzrxg5j52qzp4s9njmp3lqj88tdv7ur
|
|
||||||
m/0'/0'/4' => bc1qs5pu0x8aqjslxvng7hq4w743gysgrnspxnagtz
|
|
||||||
|
|
||||||
|
|
||||||
## For Electrum (not BIP44): m/{change}/{idx}
|
|
||||||
|
|
||||||
m => xpub661MyMwAqRbcGQU2MzQdLtxKvfa9shyo1vUGkxETFtDNGjggQMNMd5rTZfbKR25yCXHgtpwwko4Cyq1PkzLoEGRSmNy5GnnhCkWERN1wJSy
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/0/0 => 16PYSMXY2BatS8FzbzwrAqM1HrHhxPzz2A
|
|
||||||
m/0/1 => 1JccZ1v4rZ3WhU9JDSVv1z1GwgwYQpJr7m
|
|
||||||
m/0/2 => 1MJ5TicEUw169T8qp6E2QUuLkeECz2QD27
|
|
||||||
m/0/3 => 1J3f5S8v6VVHqHCfs7ECeVhvAbpV6EUKna
|
|
||||||
m/0/4 => 1C8A19VJL9NPfKNp6TiebQTJqtVNwbJ1hp
|
|
||||||
|
|
||||||
|
|
||||||
## For BIP44 / Electrum: m/44'/0'/{account}'/{change}/{idx}
|
|
||||||
|
|
||||||
m/44'/0' => xpub6AuabxJxEnAJbc8iBE2B5n7hxYAZC5xLjpG7oY1kyhMfz5mN13wLRaGPnCyvLo4Ec5aRSa6ZeMPHMUEABpdKxtcPymJpDG5KPEsLGTApGye
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/44'/0'/0'/0/0 => 1NDKGzwrhz8n7euEapPRZkktiyXBEXFyKf
|
|
||||||
m/44'/0'/0'/0/1 => 1NK9ir2VTiYfVGvSKUwftqy1HQWJPwtSrC
|
|
||||||
m/44'/0'/0'/0/2 => 1L8cB6b3WEzkCqTFGSWWyEKZMqiytP8TTX
|
|
||||||
m/44'/0'/0'/0/3 => 15grLkNbrKakMFE2eJWXa6hQNJRzswvsK4
|
|
||||||
m/44'/0'/0'/0/4 => 16714S67jGeL9zp6qQjLJd9WpsswoTVgY7
|
|
||||||
|
|
||||||
|
|
||||||
## For BIP49 (P2WPKH-nested-in-P2SH): m/49'/0'/{account}'/{change}/{idx}
|
|
||||||
|
|
||||||
m/49'/0' => xpub6ApwLnWVoU6m4aGMh1kVbwA8CACF2m31sGkJbSx15KWjifbBnE1UHjvToBJZpqDmcMD859Si6DrRPace7Q4TBMiGQwvHttjJQiwB7TL6j8H
|
|
||||||
# SLIP-132 style
|
|
||||||
m/49'/0' => ypub6VfCeTBQx9eEusTUXNY7p2FdN8LgyP2WnPGXNqqtTKtcmmQR2tB2uoabpPG9pjsh1zKvpd3GYtCyGsECq6UTybPsHHciUoYngSzpW25khLg
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/49'/0'/0'/0/0 => 3KfeHRpD4VbPnm928NVx5QBsZ4Si9L3TJH
|
|
||||||
m/49'/0'/0'/0/1 => 3Fsj1s12r12ykx7cQ6VPzXLYe2kHEHP1zk
|
|
||||||
m/49'/0'/0'/0/2 => 35Xezi189cXAx3DZ9PLUwzhVqejB22GSKc
|
|
||||||
m/49'/0'/0'/0/3 => 3BD6i8i6jYg83CCNsEo4b8hruECmFeuPNd
|
|
||||||
m/49'/0'/0'/0/4 => 3J3pVvhYt4LmGGRsTfkrnWukLg2yXd45oQ
|
|
||||||
|
|
||||||
|
|
||||||
## For BIP84 (Native Segwit P2PKH): m/84'/0'/{account}'/{change}/{idx}
|
|
||||||
|
|
||||||
m/84'/0' => xpub6BUBVXTHPtiWZuJT7ZVArTEXi5FcGNX4d4TMLTuRSCcVEQ37BASyq17BoSBxwLgaVBvyR9GbtnVeKhAAwdmqHppzrukRk55XHgc32idASq2
|
|
||||||
# SLIP-132 style
|
|
||||||
m/84'/0' => zpub6q8i6ro7hFoUGVggnH4RGdRY41YW9cW4THVnuFhCCDNFLbfZgUn758RTqr78w9zRJUAav6Tip7Ck6GPJP2brtJCCbb9GutiVq8jKoqNszsS
|
|
||||||
|
|
||||||
... first 5 receive addresses (account=0, change=0):
|
|
||||||
|
|
||||||
m/84'/0'/0'/0/0 => bc1qkwyhuqeu37f7erej85fwwtn33cmupnmra4rf2k
|
|
||||||
m/84'/0'/0'/0/1 => bc1qmng3kwg97p0emk8p8w4faym8y9w8zqeld90k2a
|
|
||||||
m/84'/0'/0'/0/2 => bc1qgaqzjdnztrle7v4qg3yvnwnu5rndpkdn3gftxm
|
|
||||||
m/84'/0'/0'/0/3 => bc1qc703cjt0jvx2adsjfhg2dcfp8k34j76xymkqdl
|
|
||||||
m/84'/0'/0'/0/4 => bc1qk3ru377gs5wj0e8psyse2jrwxn5jym3kx8ufla
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue