Craig Raw
5 years ago
26 changed files with 948 additions and 14 deletions
@ -1 +1 @@ |
|||
Subproject commit 282628e4558b04dfa17c3f85247378204f8c82ff |
|||
Subproject commit 294649de669497283934933487d09e1dae9f3996 |
@ -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"; |
|||
} |
|||
} |
@ -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"; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
package com.sparrowwallet.sparrow.external; |
|||
|
|||
public interface Export { |
|||
String getName(); |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
package com.sparrowwallet.sparrow.external; |
|||
|
|||
public interface Import { |
|||
String getName(); |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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(); |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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(); |
|||
} |
@ -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#", "")); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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()); |
|||
} |
|||
} |
@ -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); |
|||
} |
|||
} |
@ -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 |
@ -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 |
@ -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 |
|||
|
@ -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" |
|||
} |
@ -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" |
|||
} |
@ -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 |
|||
|
|||
|
@ -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} |
|||
|
@ -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 new issue