wallet import and export - coldcard and electrum

This commit is contained in:
Craig Raw 2020-04-25 11:39:29 +02:00
parent 98b1aa0b1d
commit 6d202f1522
26 changed files with 948 additions and 14 deletions

2
drongo

@ -1 +1 @@
Subproject commit 282628e4558b04dfa17c3f85247378204f8c82ff Subproject commit 294649de669497283934933487d09e1dae9f3996

View file

@ -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);
} }

View 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";
}
}

View 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";
}
}

View 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;
}
}

View file

@ -0,0 +1,5 @@
package com.sparrowwallet.sparrow.external;
public interface Export {
String getName();
}

View 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);
}
}

View file

@ -0,0 +1,5 @@
package com.sparrowwallet.sparrow.external;
public interface Import {
String getName();
}

View 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);
}
}

View 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();
}

View 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;
}

View 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;
}

View 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();
}

View file

@ -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);

View 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#", ""));
}
}

View 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());
}
}

View 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());
}
}

View 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);
}
}

View 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

View 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

View 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

View 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"
}

View 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"
}

View 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

View 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}

View 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"
}