mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-24 12:46:45 +00:00
wallet import and export - coldcard and electrum
This commit is contained in:
parent
98b1aa0b1d
commit
6d202f1522
26 changed files with 948 additions and 14 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 282628e4558b04dfa17c3f85247378204f8c82ff
|
Subproject commit 294649de669497283934933487d09e1dae9f3996
|
|
@ -210,7 +210,7 @@ public class AppController implements Initializable {
|
||||||
Optional<String> walletName = dlg.showAndWait();
|
Optional<String> walletName = dlg.showAndWait();
|
||||||
if(walletName.isPresent()) {
|
if(walletName.isPresent()) {
|
||||||
File walletFile = Storage.getStorage().getWalletFile(walletName.get());
|
File walletFile = Storage.getStorage().getWalletFile(walletName.get());
|
||||||
Wallet wallet = new Wallet(PolicyType.SINGLE, ScriptType.P2WPKH);
|
Wallet wallet = new Wallet(walletName.get(), PolicyType.SINGLE, ScriptType.P2WPKH);
|
||||||
Tab tab = addWalletTab(walletFile, null, wallet);
|
Tab tab = addWalletTab(walletFile, null, wallet);
|
||||||
tabs.getSelectionModel().select(tab);
|
tabs.getSelectionModel().select(tab);
|
||||||
}
|
}
|
||||||
|
|
186
src/main/java/com/sparrowwallet/sparrow/external/ColdcardMultisig.java
vendored
Normal file
186
src/main/java/com/sparrowwallet/sparrow/external/ColdcardMultisig.java
vendored
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
||||||
|
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.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.sparrow.storage.Storage;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ColdcardMultisig implements MultisigWalletImport, KeystoreImport, WalletExport {
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Coldcard (Multisig)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PolicyType getPolicyType() {
|
||||||
|
return PolicyType.MULTI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream) throws ImportException {
|
||||||
|
InputStreamReader reader = new InputStreamReader(inputStream);
|
||||||
|
ColdcardKeystore cck = Storage.getStorage().getGson().fromJson(reader, ColdcardKeystore.class);
|
||||||
|
|
||||||
|
Keystore keystore = new Keystore("Coldcard " + cck.xfp);
|
||||||
|
|
||||||
|
if(scriptType.equals(ScriptType.P2SH)) {
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2sh_deriv));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2sh));
|
||||||
|
} else if(scriptType.equals(ScriptType.P2SH_P2WSH)) {
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_p2sh_deriv));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2wsh_p2sh));
|
||||||
|
} else if(scriptType.equals(ScriptType.P2WSH)) {
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(cck.xfp, cck.p2wsh_deriv));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(cck.p2wsh));
|
||||||
|
} else {
|
||||||
|
throw new ImportException("Correct derivation not found for script type: " + scriptType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return keystore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ColdcardKeystore {
|
||||||
|
public String p2sh_deriv;
|
||||||
|
public String p2sh;
|
||||||
|
public String p2wsh_p2sh_deriv;
|
||||||
|
public String p2wsh_p2sh;
|
||||||
|
public String p2wsh_deriv;
|
||||||
|
public String p2wsh;
|
||||||
|
public String xfp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeystoreImportDescription() {
|
||||||
|
return "Import file created by using the Settings > Multisig Wallets > Export XPUB feature on your Coldcard";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wallet importWallet(InputStream inputStream) throws ImportException {
|
||||||
|
Wallet wallet = new Wallet();
|
||||||
|
wallet.setPolicyType(PolicyType.MULTI);
|
||||||
|
|
||||||
|
int threshold = 2;
|
||||||
|
ScriptType scriptType = null;
|
||||||
|
String derivation = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> lines = CharStreams.readLines(new InputStreamReader(inputStream));
|
||||||
|
for (String line : lines) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] keyValue = line.split(":");
|
||||||
|
if (keyValue.length == 2) {
|
||||||
|
String key = keyValue[0].trim();
|
||||||
|
String value = keyValue[1].trim();
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "Name":
|
||||||
|
wallet.setName(value.trim());
|
||||||
|
break;
|
||||||
|
case "Policy":
|
||||||
|
threshold = Integer.parseInt(value.split(" ")[0]);
|
||||||
|
break;
|
||||||
|
case "Derivation":
|
||||||
|
case "# derivation":
|
||||||
|
derivation = value;
|
||||||
|
break;
|
||||||
|
case "Format":
|
||||||
|
scriptType = ScriptType.valueOf(value.replace("P2WSH-P2SH", "P2SH_P2WSH"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (key.length() == 8 && Utils.isHex(key)) {
|
||||||
|
Keystore keystore = new Keystore("Coldcard " + key);
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(key, derivation));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(value));
|
||||||
|
wallet.getKeystores().add(keystore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Policy policy = Policy.getPolicy(PolicyType.MULTI, scriptType, wallet.getKeystores(), threshold);
|
||||||
|
wallet.setDefaultPolicy(policy);
|
||||||
|
wallet.setScriptType(scriptType);
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ImportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWalletImportDescription() {
|
||||||
|
return "Import file created by using the Settings > Multisig Wallets > [Wallet Detail] > Coldcard Export feature on your Coldcard";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException {
|
||||||
|
if(!wallet.isValid()) {
|
||||||
|
throw new ExportException("Cannot export an incomplete wallet");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!wallet.getPolicyType().equals(PolicyType.MULTI)) {
|
||||||
|
throw new ExportException("Coldcard multisig import requires a multisig wallet");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean multipleDerivations = false;
|
||||||
|
Set<String> derivationSet = new HashSet<>();
|
||||||
|
for(Keystore keystore : wallet.getKeystores()) {
|
||||||
|
derivationSet.add(keystore.getKeyDerivation().getDerivationPath());
|
||||||
|
}
|
||||||
|
if(derivationSet.size() > 1) {
|
||||||
|
multipleDerivations = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
|
||||||
|
writer.append("# Coldcard Multisig 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");
|
||||||
|
if(!multipleDerivations) {
|
||||||
|
writer.append("Derivation: ").append(wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath()).append("\n");
|
||||||
|
}
|
||||||
|
writer.append("Format: ").append(wallet.getScriptType().toString().replace("P2SH-P2WSH", "P2WSH-P2SH")).append("\n");
|
||||||
|
writer.append("\n");
|
||||||
|
|
||||||
|
for(Keystore keystore : wallet.getKeystores()) {
|
||||||
|
if(multipleDerivations) {
|
||||||
|
writer.append("# derivation: ").append(keystore.getKeyDerivation().getDerivationPath()).append("\n");
|
||||||
|
}
|
||||||
|
writer.append(keystore.getKeyDerivation().getMasterFingerprint().toUpperCase()).append(": ").append(keystore.getExtendedPublicKey().toString()).append("\n");
|
||||||
|
if(multipleDerivations) {
|
||||||
|
writer.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
} catch(Exception e) {
|
||||||
|
throw new ExportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWalletExportDescription() {
|
||||||
|
return "Export file that can be read by your Coldcard using the Settings > Multisig Wallets > Import from SD feature";
|
||||||
|
}
|
||||||
|
}
|
81
src/main/java/com/sparrowwallet/sparrow/external/ColdcardSinglesig.java
vendored
Normal file
81
src/main/java/com/sparrowwallet/sparrow/external/ColdcardSinglesig.java
vendored
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
||||||
|
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.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.sparrowwallet.drongo.protocol.ScriptType.*;
|
||||||
|
|
||||||
|
public class ColdcardSinglesig implements SinglesigWalletImport {
|
||||||
|
public static final List<ScriptType> ALLOWED_SCRIPT_TYPES = List.of(P2PKH, P2SH_P2WPKH, P2WPKH);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Coldcard";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wallet importWallet(InputStream inputStream, ScriptType scriptType) 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.getDefaultDerivation().startsWith(key)) {
|
||||||
|
ExtendedPublicKey extPubKey = ExtendedPublicKey.fromDescriptor(value);
|
||||||
|
Keystore keystore = new Keystore();
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
164
src/main/java/com/sparrowwallet/sparrow/external/Electrum.java
vendored
Normal file
164
src/main/java/com/sparrowwallet/sparrow/external/Electrum.java
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
||||||
|
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.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Electrum implements SinglesigWalletImport, MultisigWalletImport, WalletExport {
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Electrum";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wallet importWallet(InputStream inputStream) throws ImportException {
|
||||||
|
InputStreamReader reader = new InputStreamReader(inputStream);
|
||||||
|
try {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
Type stringStringMap = new TypeToken<Map<String, JsonElement>>(){}.getType();
|
||||||
|
Map<String,JsonElement> map = gson.fromJson(reader, stringStringMap);
|
||||||
|
|
||||||
|
ElectrumJsonWallet ew = new ElectrumJsonWallet();
|
||||||
|
ew.wallet_type = map.get("wallet_type").getAsString();
|
||||||
|
|
||||||
|
for(String key : map.keySet()) {
|
||||||
|
if(key.startsWith("x") || key.equals("keystore")) {
|
||||||
|
ElectrumKeystore ek = gson.fromJson(map.get(key), ElectrumKeystore.class);
|
||||||
|
if(ek.root_fingerprint == null && ek.ckcc_xfp != null) {
|
||||||
|
byte[] le = new byte[4];
|
||||||
|
Utils.uint32ToByteArrayLE(Long.parseLong(ek.ckcc_xfp), le, 0);
|
||||||
|
ek.root_fingerprint = Utils.bytesToHex(le).toUpperCase();
|
||||||
|
}
|
||||||
|
ew.keystores.put(key, ek);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Wallet wallet = new Wallet();
|
||||||
|
ScriptType scriptType = null;
|
||||||
|
|
||||||
|
for(ElectrumKeystore ek : ew.keystores.values()) {
|
||||||
|
Keystore keystore = new Keystore(ek.label);
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(ek.root_fingerprint, ek.derivation));
|
||||||
|
keystore.setExtendedPublicKey(ExtendedPublicKey.fromDescriptor(ek.xpub));
|
||||||
|
wallet.getKeystores().add(keystore);
|
||||||
|
|
||||||
|
ExtendedPublicKey.XpubHeader xpubHeader = ExtendedPublicKey.XpubHeader.fromXpub(ek.xpub);
|
||||||
|
scriptType = xpubHeader.getDefaultScriptType();
|
||||||
|
}
|
||||||
|
|
||||||
|
wallet.setScriptType(scriptType);
|
||||||
|
|
||||||
|
if(ew.wallet_type.equals("standard")) {
|
||||||
|
wallet.setPolicyType(PolicyType.SINGLE);
|
||||||
|
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.SINGLE, scriptType, wallet.getKeystores(), 1));
|
||||||
|
} else if(ew.wallet_type.contains("of")) {
|
||||||
|
wallet.setPolicyType(PolicyType.MULTI);
|
||||||
|
String[] mOfn = ew.wallet_type.split("of");
|
||||||
|
int threshold = Integer.parseInt(mOfn[0]);
|
||||||
|
wallet.setDefaultPolicy(Policy.getPolicy(PolicyType.MULTI, scriptType, wallet.getKeystores(), threshold));
|
||||||
|
} else {
|
||||||
|
throw new ImportException("Unknown Electrum wallet type of " + ew.wallet_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!wallet.isValid()) {
|
||||||
|
throw new IllegalStateException("Electrum wallet is in an inconsistent state");
|
||||||
|
}
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ImportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWalletImportDescription() {
|
||||||
|
return "Import an Electrum wallet";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wallet importWallet(InputStream inputStream, ScriptType scriptType) throws ImportException {
|
||||||
|
Wallet wallet = importWallet(inputStream);
|
||||||
|
wallet.setScriptType(scriptType);
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException {
|
||||||
|
try {
|
||||||
|
ElectrumJsonWallet ew = new ElectrumJsonWallet();
|
||||||
|
if(wallet.getPolicyType().equals(PolicyType.SINGLE)) {
|
||||||
|
ew.wallet_type = "standard";
|
||||||
|
} else if(wallet.getPolicyType().equals(PolicyType.MULTI)) {
|
||||||
|
ew.wallet_type = wallet.getDefaultPolicy().getNumSignaturesRequired() + "of" + wallet.getKeystores().size();
|
||||||
|
} else {
|
||||||
|
throw new ExportException("Could not export a wallet with a " + wallet.getPolicyType() + " policy");
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedPublicKey.XpubHeader xpubHeader = ExtendedPublicKey.XpubHeader.fromScriptType(wallet.getScriptType());
|
||||||
|
|
||||||
|
int index = 1;
|
||||||
|
for(Keystore keystore : wallet.getKeystores()) {
|
||||||
|
ElectrumKeystore ek = new ElectrumKeystore();
|
||||||
|
ek.xpub = keystore.getExtendedPublicKey().toString(xpubHeader);
|
||||||
|
ek.derivation = keystore.getKeyDerivation().getDerivationPath();
|
||||||
|
ek.root_fingerprint = keystore.getKeyDerivation().getMasterFingerprint();
|
||||||
|
ek.label = keystore.getLabel();
|
||||||
|
|
||||||
|
if(wallet.getPolicyType().equals(PolicyType.SINGLE)) {
|
||||||
|
ew.keystores.put("keystore", ek);
|
||||||
|
} else if(wallet.getPolicyType().equals(PolicyType.MULTI)) {
|
||||||
|
ew.keystores.put("x" + index + "/", ek);
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonObject eJson = gson.toJsonTree(ew.keystores).getAsJsonObject();
|
||||||
|
eJson.addProperty("wallet_type", ew.wallet_type);
|
||||||
|
|
||||||
|
gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||||
|
String json = gson.toJson(eJson);
|
||||||
|
outputStream.write(json.getBytes(StandardCharsets.UTF_8));
|
||||||
|
outputStream.flush();
|
||||||
|
outputStream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ExportException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWalletExportDescription() {
|
||||||
|
return "Export this wallet as an Electrum wallet file";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ElectrumJsonWallet {
|
||||||
|
public Map<String, ElectrumKeystore> keystores = new LinkedHashMap<>();
|
||||||
|
public String wallet_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ElectrumKeystore {
|
||||||
|
public String xpub;
|
||||||
|
public String hw_type;
|
||||||
|
public String ckcc_xfp;
|
||||||
|
public String root_fingerprint;
|
||||||
|
public String label;
|
||||||
|
public String soft_device_id;
|
||||||
|
public String type;
|
||||||
|
public String derivation;
|
||||||
|
}
|
||||||
|
}
|
5
src/main/java/com/sparrowwallet/sparrow/external/Export.java
vendored
Normal file
5
src/main/java/com/sparrowwallet/sparrow/external/Export.java
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
public interface Export {
|
||||||
|
String getName();
|
||||||
|
}
|
19
src/main/java/com/sparrowwallet/sparrow/external/ExportException.java
vendored
Normal file
19
src/main/java/com/sparrowwallet/sparrow/external/ExportException.java
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
public class ExportException extends Throwable {
|
||||||
|
public ExportException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExportException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExportException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExportException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
5
src/main/java/com/sparrowwallet/sparrow/external/Import.java
vendored
Normal file
5
src/main/java/com/sparrowwallet/sparrow/external/Import.java
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
public interface Import {
|
||||||
|
String getName();
|
||||||
|
}
|
19
src/main/java/com/sparrowwallet/sparrow/external/ImportException.java
vendored
Normal file
19
src/main/java/com/sparrowwallet/sparrow/external/ImportException.java
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
public class ImportException extends Exception {
|
||||||
|
public ImportException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImportException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImportException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImportException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/com/sparrowwallet/sparrow/external/KeystoreImport.java
vendored
Normal file
13
src/main/java/com/sparrowwallet/sparrow/external/KeystoreImport.java
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface KeystoreImport extends Import {
|
||||||
|
PolicyType getPolicyType();
|
||||||
|
Keystore getKeystore(ScriptType scriptType, InputStream inputStream) throws ImportException;
|
||||||
|
String getKeystoreImportDescription();
|
||||||
|
}
|
10
src/main/java/com/sparrowwallet/sparrow/external/MultisigWalletImport.java
vendored
Normal file
10
src/main/java/com/sparrowwallet/sparrow/external/MultisigWalletImport.java
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface MultisigWalletImport extends Import {
|
||||||
|
String getWalletImportDescription();
|
||||||
|
Wallet importWallet(InputStream inputStream) throws ImportException;
|
||||||
|
}
|
11
src/main/java/com/sparrowwallet/sparrow/external/SinglesigWalletImport.java
vendored
Normal file
11
src/main/java/com/sparrowwallet/sparrow/external/SinglesigWalletImport.java
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public interface SinglesigWalletImport extends Import {
|
||||||
|
String getWalletImportDescription();
|
||||||
|
Wallet importWallet(InputStream inputStream, ScriptType scriptType) throws ImportException;
|
||||||
|
}
|
10
src/main/java/com/sparrowwallet/sparrow/external/WalletExport.java
vendored
Normal file
10
src/main/java/com/sparrowwallet/sparrow/external/WalletExport.java
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
public interface WalletExport extends Export {
|
||||||
|
void exportWallet(Wallet wallet, OutputStream outputStream) throws ExportException;
|
||||||
|
String getWalletExportDescription();
|
||||||
|
}
|
|
@ -36,6 +36,10 @@ public class Storage {
|
||||||
return SINGLETON;
|
return SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Gson getGson() {
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
public Wallet loadWallet(File file) throws IOException {
|
public Wallet loadWallet(File file) throws IOException {
|
||||||
Reader reader = new FileReader(file);
|
Reader reader = new FileReader(file);
|
||||||
Wallet wallet = gson.fromJson(reader, Wallet.class);
|
Wallet wallet = gson.fromJson(reader, Wallet.class);
|
||||||
|
@ -44,18 +48,6 @@ public class Storage {
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final void main(String[] args) throws Exception {
|
|
||||||
File file = new File("/Users/scy/.electrum-latest/wallets/scyone");
|
|
||||||
ECKey pubKey = ECKey.createKeyPbkdf2HmacSha512("***REMOVED***");
|
|
||||||
|
|
||||||
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
|
||||||
byte[] encrypted = ByteStreams.toByteArray(inputStream);
|
|
||||||
byte[] decrypted = pubKey.decryptEcies(encrypted, getEncryptionMagic());
|
|
||||||
String jsonWallet = inflate(decrypted);
|
|
||||||
|
|
||||||
System.out.println(jsonWallet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Wallet loadWallet(File file, ECKey encryptionKey) throws IOException {
|
public Wallet loadWallet(File file, ECKey encryptionKey) throws IOException {
|
||||||
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||||
byte[] encrypted = ByteStreams.toByteArray(inputStream);
|
byte[] encrypted = ByteStreams.toByteArray(inputStream);
|
||||||
|
|
120
src/test/java/com/sparrowwallet/sparrow/external/ColdcardMultisigTest.java
vendored
Normal file
120
src/test/java/com/sparrowwallet/sparrow/external/ColdcardMultisigTest.java
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedPublicKey;
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class ColdcardMultisigTest extends ImportExportTest {
|
||||||
|
@Test
|
||||||
|
public void importKeystore1() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WSH, getInputStream("cc-multisig-keystore-1.json"));
|
||||||
|
Assert.assertEquals("Coldcard 0F056943", keystore.getLabel());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/1'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("0f056943", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("Upub5T4XUooQzDXL58NCHk8ZCw9BsRSLCtnyHeZEExAq1XdnBFXiXVrHFuvvmh3TnCR7XmKHxkwqdACv68z7QKT1vwru9L1SZSsw8B2fuBvtSa6"), keystore.getExtendedPublicKey());
|
||||||
|
Assert.assertTrue(keystore.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ImportException.class)
|
||||||
|
public void importKeystore1IncorrectScriptType() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH_P2WPKH, getInputStream("cc-multisig-keystore-1.json"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importKeystore2() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2SH, getInputStream("cc-multisig-keystore-2.json"));
|
||||||
|
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
||||||
|
Assert.assertEquals("m/45'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9"), keystore.getExtendedPublicKey());
|
||||||
|
Assert.assertTrue(keystore.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importKeystore2b() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Keystore keystore = ccMultisig.getKeystore(ScriptType.P2WSH, getInputStream("cc-multisig-keystore-2.json"));
|
||||||
|
Assert.assertEquals("Coldcard 6BA6CFD0", keystore.getLabel());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/2'", keystore.getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("6ba6cfd0", keystore.getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals(ExtendedPublicKey.fromDescriptor("Vpub5nUnvPehg1VYQh13dGznx1P9moac3SNUrn3qhU9r85RhXabYbSSBNsNNwyR7akozAZJw1SZmRRjry1zY8PWMuw8Ga1vQZ5qzPjKyTDQwtzs"), keystore.getExtendedPublicKey());
|
||||||
|
Assert.assertTrue(keystore.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importWallet1() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-1.txt"));
|
||||||
|
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
||||||
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("multi(2,coldcard0f056943,coldcard6ba6cfd0,coldcard747b698e,coldcard7bb026be)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importWallet2() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-2.txt"));
|
||||||
|
Assert.assertEquals("CC-2-of-4", wallet.getName());
|
||||||
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("multi(2,coldcard0f056943,coldcard6ba6cfd0,coldcard747b698e,coldcard7bb026be)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void importWalletMultiDeriv() throws ImportException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
Wallet wallet = ccMultisig.importWallet(getInputStream("cc-multisig-export-multideriv.txt"));
|
||||||
|
Assert.assertEquals("el-CC-3-of-3-sb-2", wallet.getName());
|
||||||
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(3, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("multi(3,coldcard06b57041,coldcard4b569672,coldcardca9a2b19)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertEquals("06b57041", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/0'/0'/2'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
Assert.assertEquals("ca9a2b19", wallet.getKeystores().get(2).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/0'/0'/1'", wallet.getKeystores().get(2).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exportWallet1() throws ImportException, ExportException, IOException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-1.txt"));
|
||||||
|
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes));
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ccMultisig.exportWallet(wallet, baos);
|
||||||
|
byte[] exportedBytes = baos.toByteArray();
|
||||||
|
String original = new String(walletBytes);
|
||||||
|
String exported = new String(exportedBytes);
|
||||||
|
Assert.assertEquals(original.replaceAll("created on [0-9A-F]+", ""), exported.replace("created by Sparrow", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void exportWalletMultiDeriv() throws ImportException, ExportException, IOException {
|
||||||
|
ColdcardMultisig ccMultisig = new ColdcardMultisig();
|
||||||
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("cc-multisig-export-multideriv.txt"));
|
||||||
|
Wallet wallet = ccMultisig.importWallet(new ByteArrayInputStream(walletBytes));
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ccMultisig.exportWallet(wallet, baos);
|
||||||
|
byte[] exportedBytes = baos.toByteArray();
|
||||||
|
String original = new String(walletBytes);
|
||||||
|
String exported = new String(exportedBytes);
|
||||||
|
Assert.assertEquals(original.replaceAll("Exported from Electrum", ""), exported.replace("Coldcard Multisig setup file (created by Sparrow)\n#", ""));
|
||||||
|
}
|
||||||
|
}
|
25
src/test/java/com/sparrowwallet/sparrow/external/ColdcardSinglesigTest.java
vendored
Normal file
25
src/test/java/com/sparrowwallet/sparrow/external/ColdcardSinglesigTest.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ColdcardSinglesigTest extends ImportExportTest {
|
||||||
|
@Test
|
||||||
|
public void testImport() throws ImportException {
|
||||||
|
ColdcardSinglesig ccSingleSig = new ColdcardSinglesig();
|
||||||
|
Wallet wallet = ccSingleSig.importWallet(getInputStream("cc-wallet-dump.txt"), ScriptType.P2PKH);
|
||||||
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
|
|
||||||
|
Assert.assertEquals("Coldcard 3D88D0CF", wallet.getName());
|
||||||
|
Assert.assertEquals(ScriptType.P2PKH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("pkh(keystore1)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertTrue(wallet.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());
|
||||||
|
}
|
||||||
|
}
|
86
src/test/java/com/sparrowwallet/sparrow/external/ElectrumTest.java
vendored
Normal file
86
src/test/java/com/sparrowwallet/sparrow/external/ElectrumTest.java
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class ElectrumTest extends ImportExportTest {
|
||||||
|
@Test
|
||||||
|
public void testSinglesigImport() throws ImportException {
|
||||||
|
Electrum electrum = new Electrum();
|
||||||
|
Wallet wallet = electrum.importWallet(getInputStream("electrum-singlesig-wallet.json"));
|
||||||
|
|
||||||
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("pkh(trezortest)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSinglesigExport() throws ImportException, ExportException, IOException {
|
||||||
|
Electrum electrum = new Electrum();
|
||||||
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-singlesig-wallet.json"));
|
||||||
|
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes));
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
electrum.exportWallet(wallet, baos);
|
||||||
|
|
||||||
|
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2SH_P2WPKH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("pkh(trezortest)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertEquals("ab543c67", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultisigImport() throws ImportException {
|
||||||
|
Electrum electrum = new Electrum();
|
||||||
|
Wallet wallet = electrum.importWallet(getInputStream("electrum-multisig-wallet.json"));
|
||||||
|
|
||||||
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("multi(2,coldcard6ba6cfd0,coldcard747b698e,coldcard7bb026be,coldcard0f056943)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
Assert.assertEquals("7bb026be", wallet.getKeystores().get(2).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(2).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultisigExport() throws ImportException, ExportException, IOException {
|
||||||
|
Electrum electrum = new Electrum();
|
||||||
|
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-multisig-wallet.json"));
|
||||||
|
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes));
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
electrum.exportWallet(wallet, baos);
|
||||||
|
|
||||||
|
wallet = electrum.importWallet(new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
Assert.assertTrue(wallet.isValid());
|
||||||
|
Assert.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assert.assertEquals(ScriptType.P2SH_P2WSH, wallet.getScriptType());
|
||||||
|
Assert.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
Assert.assertEquals("multi(2,coldcard6ba6cfd0,coldcard747b698e,coldcard7bb026be,coldcard0f056943)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||||
|
Assert.assertEquals("6ba6cfd0", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assert.assertEquals("xpub6FFEQVG6QR28chQzgSJ7Gjx5j5BGLkCMgZ9bc41YJCXfwYiCKUQdcwm4Fe1stvzRjosz5udMedYZFRL56AeZXCsiVmnVUysio4jkAKTukmN", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||||
|
Assert.assertEquals("7bb026be", wallet.getKeystores().get(2).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assert.assertEquals("m/48'/1'/0'/1'", wallet.getKeystores().get(2).getKeyDerivation().getDerivationPath());
|
||||||
|
}
|
||||||
|
}
|
10
src/test/java/com/sparrowwallet/sparrow/external/ImportExportTest.java
vendored
Normal file
10
src/test/java/com/sparrowwallet/sparrow/external/ImportExportTest.java
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package com.sparrowwallet.sparrow.external;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class ImportExportTest {
|
||||||
|
|
||||||
|
protected InputStream getInputStream(String filename) {
|
||||||
|
return this.getClass().getResourceAsStream("/com/sparrowwallet/sparrow/external/" + filename);
|
||||||
|
}
|
||||||
|
}
|
11
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-1.txt
vendored
Normal file
11
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-1.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Coldcard Multisig setup file (created on 0F056943)
|
||||||
|
#
|
||||||
|
Name: CC-2-of-4
|
||||||
|
Policy: 2 of 4
|
||||||
|
Derivation: m/48'/1'/0'/2'
|
||||||
|
Format: P2WSH
|
||||||
|
|
||||||
|
0F056943: xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z
|
||||||
|
6BA6CFD0: xpub6FFEQVG6QR28giDuML74Y7EMPwqEiKftNjScLzg5WKM41bf6LMP2XspjBgNp28tvkNUZdokmTY4TcRbuGZBSMvNoUECrKW1y3TBPeQJVmAg
|
||||||
|
747B698E: xpub6Eb6Z1xtmWRiWKgRpHf6dHiEagGd6FLiBXrnma1nFK4PGRYqSVqVyJaxna5Mb8etSP4ATKVAvKnXG1a9HZauoAawuSDJT5RgH2HqEVHZVHY
|
||||||
|
7BB026BE: xpub6FMGmJccz6dqLo9TMmXYPpZ7HUDm71RHHSTXqTUgkyP9TZmF2uexoB7qttEBtHaotopPwfAVfKfwmdEjCGabVpND1m7ix2AW2LxPuNfLZhi
|
11
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-2.txt
vendored
Normal file
11
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-2.txt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Coldcard Multisig setup file (created on 0F056943)
|
||||||
|
#
|
||||||
|
Name: CC-2-of-4
|
||||||
|
Policy: 2 of 4
|
||||||
|
Derivation: m/48'/1'/0'/1'
|
||||||
|
Format: P2WSH-P2SH
|
||||||
|
|
||||||
|
0F056943: tpubDF2rnouQaaYrUEy2JM1YD3RFzew4onawGM4X2Re67gguTf5CbHonBRiFGe3Xjz7DK88dxBFGf2i7K1hef3PM4cFKyUjcbJXddaY9F5tJBoP
|
||||||
|
6BA6CFD0: tpubDFcrvj5n7gyatVbr8dHCUfHT4CGvL8hREBjtxc4ge7HZgqNuPhFimPRtVg6fRRwfXiQthV9EBjNbwbpgV2VoQeL1ZNXoAWXxP2L9vMtRjax
|
||||||
|
747B698E: tpubDExj5FnaUnPAjjgzELoSiNRkuXJG8Cm1pbdiA4Hc5vkAZHphibeVcUp6mqH5LuNVKbtLVZxVSzyja5X26Cfmx6pzRH6gXBUJAH7MiqwNyuM
|
||||||
|
7BB026BE: tpubDFiuHYSJhNbHaGtB5skiuDLg12tRboh2uVZ6KGXxr8WVr28pLcS7F3gv8SsHFa2tm1jtx3VAuw56YfgRkdo6DXyfp51oygTKY3nJFT5jBMt
|
14
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-multideriv.txt
vendored
Normal file
14
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-export-multideriv.txt
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# Exported from Electrum
|
||||||
|
Name: el-CC-3-of-3-sb-2
|
||||||
|
Policy: 3 of 3
|
||||||
|
Format: P2WSH
|
||||||
|
|
||||||
|
# derivation: m/48'/0'/0'/2'
|
||||||
|
06B57041: xpub6EfEGa5isJbQFSswM5Uptw5BSq2Td1ZDJr3QUNUcMySpC7itZ3ccypVHtLPnvMzKQ2qxrAgH49vhVxRcaQLFbixAVRR8RACrYTp88Uv9h8Z
|
||||||
|
|
||||||
|
# derivation: m/48'/0'/0'/2'
|
||||||
|
4B569672: xpub6FFEQVG6QR28giDuML74Y7EMPwqEiKftNjScLzg5WKM41bf6LMP2XspjBgNp28tvkNUZdokmTY4TcRbuGZBSMvNoUECrKW1y3TBPeQJVmAg
|
||||||
|
|
||||||
|
# derivation: m/48'/0'/0'/1'
|
||||||
|
CA9A2B19: xpub6Eb6Z1xtmWRiWKgRpHf6dHiEagGd6FLiBXrnma1nFK4PGRYqSVqVyJaxna5Mb8etSP4ATKVAvKnXG1a9HZauoAawuSDJT5RgH2HqEVHZVHY
|
||||||
|
|
9
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-keystore-1.json
vendored
Normal file
9
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-keystore-1.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"p2sh_deriv": "m/45'",
|
||||||
|
"p2sh": "tpubD8NXmKsmWp3a3DXhbihAYbYLGaRNVdTnr6JoSxxfXYQcmwVtW2hv8QoDwng6JtEonmJoL3cNEwfd2cLXMpGezwZ2vL2dQ7259bueNKj9C8n",
|
||||||
|
"p2wsh_p2sh_deriv": "m/48'/1'/0'/1'",
|
||||||
|
"p2wsh_p2sh": "Upub5T4XUooQzDXL58NCHk8ZCw9BsRSLCtnyHeZEExAq1XdnBFXiXVrHFuvvmh3TnCR7XmKHxkwqdACv68z7QKT1vwru9L1SZSsw8B2fuBvtSa6",
|
||||||
|
"p2wsh_deriv": "m/48'/1'/0'/2'",
|
||||||
|
"p2wsh": "Vpub5mtnnUUL8u4oyRf5d2NZJqDypgmpx8FontedpqxNyjXTi6fLp8fmpp2wedS6UyuNpDgLDoVH23c6rYpFSEfB9jhdbD8gek2stjxhwJeE1Eq",
|
||||||
|
"xfp": "0F056943"
|
||||||
|
}
|
9
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-keystore-2.json
vendored
Normal file
9
src/test/resources/com/sparrowwallet/sparrow/external/cc-multisig-keystore-2.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"p2sh_deriv": "m/45'",
|
||||||
|
"p2sh": "tpubD9429UXFGCTKJ9NdiNK4rC5ygqSUkginycYHccqSg5gkmyQ7PZRHNjk99M6a6Y3NY8ctEUUJvCu6iCCui8Ju3xrHRu3Ez1CKB4ZFoRZDdP9",
|
||||||
|
"p2wsh_p2sh_deriv": "m/48'/1'/0'/1'",
|
||||||
|
"p2wsh_p2sh": "Upub5TeXciynXKx4VP1282QDUZ1NvxnBjEuTFVEcB8bRXxESQRqRKuJDqseZzj6bTeFZkMbYi4qo9rsQij79EJZUGywajDod8etFscpgaTSShLd",
|
||||||
|
"p2wsh_deriv": "m/48'/1'/0'/2'",
|
||||||
|
"p2wsh": "Vpub5nUnvPehg1VYQh13dGznx1P9moac3SNUrn3qhU9r85RhXabYbSSBNsNNwyR7akozAZJw1SZmRRjry1zY8PWMuw8Ga1vQZ5qzPjKyTDQwtzs",
|
||||||
|
"xfp": "6BA6CFD0"
|
||||||
|
}
|
97
src/test/resources/com/sparrowwallet/sparrow/external/cc-wallet-dump.txt
vendored
Normal file
97
src/test/resources/com/sparrowwallet/sparrow/external/cc-wallet-dump.txt
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
2
src/test/resources/com/sparrowwallet/sparrow/external/electrum-multisig-wallet.json
vendored
Normal file
2
src/test/resources/com/sparrowwallet/sparrow/external/electrum-multisig-wallet.json
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{"x2/": {"xpub": "Upub5TeXciynXKx4VP1282QDUZ1NvxnBjEuTFVEcB8bRXxESQRqRKuJDqseZzj6bTeFZkMbYi4qo9rsQij79EJZUGywajDod8etFscpgaTSShLd", "hw_type": "coldcard", "ckcc_xfp": 3503269483, "label": "Coldcard 6BA6CFD0", "derivation": "m/48'/1'/0'/1'", "type": "hardware"}, "x3/": {"xpub": "Upub5SzPmFgatRMeLd6ADjvTiG9gnHoXXJy3qu8RNapLymh3GtHDeogzgy2nGtH1P7gPYF4zW9f4R8UYMCoUqUjSpSSZb8NWVKpbesbtP21e6Z4", "hw_type": "coldcard", "ckcc_xfp": 2389277556, "label": "Coldcard 747B698E", "derivation": "m/48'/1'/0'/1'", "type": "hardware"}, "x4/": {"xpub": "Upub5TkZyYLK71ZmBAHM5Gsju74bsoPgzuu4vo3oXo4hjyTNZcbLGpUcKXubdVsDHnLnyevYxdBjt4ZuKnxtVurm5sbEyvHdwpod2eGpubnG9yQ", "hw_type": "coldcard", "ckcc_xfp": 3190206587, "label": "Coldcard 7BB026BE", "derivation": "m/48'/1'/0'/1'", "type": "hardware"}, "x1/": {"xpub": "Upub5T4XUooQzDXL58NCHk8ZCw9BsRSLCtnyHeZEExAq1XdnBFXiXVrHFuvvmh3TnCR7XmKHxkwqdACv68z7QKT1vwru9L1SZSsw8B2fuBvtSa6", "hw_type": "coldcard", "ckcc_xfp": 1130956047, "label": "Coldcard 0F056943", "derivation": "m/48'/1'/0'/1'", "type": "hardware"}, "wallet_type": "2of4", "use_encryption": false, "seed_version": 17}
|
||||||
|
|
25
src/test/resources/com/sparrowwallet/sparrow/external/electrum-singlesig-wallet.json
vendored
Normal file
25
src/test/resources/com/sparrowwallet/sparrow/external/electrum-singlesig-wallet.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"fiat_value": {},
|
||||||
|
"invoices": {},
|
||||||
|
"keystore": {
|
||||||
|
"derivation": "m/84'/0'/0'",
|
||||||
|
"hw_type": "trezor",
|
||||||
|
"label": "trezortest",
|
||||||
|
"root_fingerprint": "ab543c67",
|
||||||
|
"soft_device_id": "65465645",
|
||||||
|
"type": "hardware",
|
||||||
|
"xpub": "ypub6a5Vi9w1Z6ZcTzc7Wo5jUq3au3KiHNBrbffpPSuRgCuYzeXRa8aCF1RCGqyTtqeM9SznqPDv7Hu78hwdos4aKSZKN7Uv4thD4noPYpzrfN9"
|
||||||
|
},
|
||||||
|
"labels": {},
|
||||||
|
"payment_requests": {},
|
||||||
|
"prevouts_by_scripthash": {},
|
||||||
|
"seed_version": 28,
|
||||||
|
"spent_outpoints": {},
|
||||||
|
"transactions": {},
|
||||||
|
"tx_fees": {},
|
||||||
|
"txi": {},
|
||||||
|
"txo": {},
|
||||||
|
"use_encryption": false,
|
||||||
|
"verified_tx3": {},
|
||||||
|
"wallet_type": "standard"
|
||||||
|
}
|
Loading…
Reference in a new issue