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