mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
add bitkey support
This commit is contained in:
parent
2c27112dad
commit
ddb4b53ef5
8 changed files with 262 additions and 3 deletions
|
|
@ -49,8 +49,6 @@ import javafx.geometry.Side;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.image.ImageView;
|
|
||||||
import javafx.scene.input.*;
|
import javafx.scene.input.*;
|
||||||
import javafx.scene.layout.*;
|
import javafx.scene.layout.*;
|
||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
@ -1230,6 +1228,7 @@ public class AppController implements Initializable {
|
||||||
new CoboVaultSinglesig(), new CoboVaultMultisig(),
|
new CoboVaultSinglesig(), new CoboVaultMultisig(),
|
||||||
new PassportSinglesig(),
|
new PassportSinglesig(),
|
||||||
new KeystoneSinglesig(), new KeystoneMultisig(),
|
new KeystoneSinglesig(), new KeystoneMultisig(),
|
||||||
|
new BitkeyMultisig(),
|
||||||
new CaravanMultisig());
|
new CaravanMultisig());
|
||||||
for(WalletImport importer : walletImporters) {
|
for(WalletImport importer : walletImporters) {
|
||||||
if(importer.isDeprecated() && !Config.get().isShowDeprecatedImportExport()) {
|
if(importer.isDeprecated() && !Config.get().isShowDeprecatedImportExport()) {
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ public class WalletImportDialog extends Dialog<Wallet> {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WalletImport> walletImporters = new ArrayList<>(List.of(new Bip129(), new CaravanMultisig(), new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(),
|
List<WalletImport> walletImporters = new ArrayList<>(List.of(new Bip129(), new CaravanMultisig(), new ColdcardMultisig(), new CoboVaultMultisig(), new Electrum(),
|
||||||
new KeystoneMultisig(), new Descriptor(), new SpecterDesktop(), new BlueWalletMultisig(), new Sparrow(), new JadeMultisig(), new PassportMultisig(), new SpecterDIYMultisig()));
|
new KeystoneMultisig(), new Descriptor(), new SpecterDesktop(), new BlueWalletMultisig(), new Sparrow(), new JadeMultisig(), new PassportMultisig(), new SpecterDIYMultisig(), new BitkeyMultisig()));
|
||||||
if(!selectedWalletForms.isEmpty()) {
|
if(!selectedWalletForms.isEmpty()) {
|
||||||
walletImporters.add(new WalletLabels(selectedWalletForms));
|
walletImporters.add(new WalletLabels(selectedWalletForms));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
171
src/main/java/com/sparrowwallet/sparrow/io/BitkeyMultisig.java
Normal file
171
src/main/java/com/sparrowwallet/sparrow/io/BitkeyMultisig.java
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
|
import com.sparrowwallet.drongo.KeyDerivation;
|
||||||
|
import com.sparrowwallet.drongo.policy.Policy;
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BitkeyMultisig implements WalletImport, KeystoreFileImport {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(BitkeyMultisig.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Bitkey Multisig";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WalletModel getWalletModel() {
|
||||||
|
return WalletModel.BITKEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
||||||
|
throw new ImportException("Bitkey export format is for full wallets, not individual keystores in this context.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getKeystoreImportDescription(int account) {
|
||||||
|
return "Import the XPUB file exported from your Bitkey app. This file contains all co-signer xpubs for a multisig wallet.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Wallet importWallet(InputStream inputStream, String password) throws ImportException {
|
||||||
|
Wallet wallet = new Wallet();
|
||||||
|
wallet.setPolicyType(PolicyType.MULTI);
|
||||||
|
wallet.setName(getName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<String> lines = CharStreams.readLines(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
||||||
|
String externalDescriptorLine = null;
|
||||||
|
|
||||||
|
for (String line : lines) {
|
||||||
|
if (line.startsWith("External: ")) {
|
||||||
|
externalDescriptorLine = line.substring("External: ".length()).trim();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (externalDescriptorLine == null) {
|
||||||
|
throw new ImportException("External descriptor line not found in Bitkey export file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String descriptorContent;
|
||||||
|
if (externalDescriptorLine.startsWith("wsh(")) {
|
||||||
|
wallet.setScriptType(ScriptType.P2WSH);
|
||||||
|
descriptorContent = externalDescriptorLine.substring("wsh(".length(), externalDescriptorLine.length() - 1); // remove wsh() wrapper
|
||||||
|
} else if (externalDescriptorLine.startsWith("sh(wsh(")) {
|
||||||
|
wallet.setScriptType(ScriptType.P2SH_P2WSH);
|
||||||
|
descriptorContent = externalDescriptorLine.substring("sh(wsh(".length(), externalDescriptorLine.length() - 2); // remove sh(wsh()) wrapper
|
||||||
|
} else {
|
||||||
|
throw new ImportException("Unsupported script type in Bitkey descriptor: " + externalDescriptorLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!descriptorContent.startsWith("sortedmulti(")) {
|
||||||
|
throw new ImportException("Could not find sortedmulti in descriptor: " + descriptorContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
String sortedMultiContent = descriptorContent.substring("sortedmulti(".length(), descriptorContent.length() - 1);
|
||||||
|
|
||||||
|
int firstComma = sortedMultiContent.indexOf(',');
|
||||||
|
if (firstComma == -1) {
|
||||||
|
throw new ImportException("Could not parse threshold from sortedmulti: " + sortedMultiContent);
|
||||||
|
}
|
||||||
|
int threshold = Integer.parseInt(sortedMultiContent.substring(0, firstComma));
|
||||||
|
|
||||||
|
String xpubsData = sortedMultiContent.substring(firstComma + 1);
|
||||||
|
String[] xpubEntries = xpubsData.split(",");
|
||||||
|
|
||||||
|
List<Keystore> keystores = new ArrayList<>();
|
||||||
|
for (String entry : xpubEntries) {
|
||||||
|
entry = entry.trim();
|
||||||
|
if (!entry.startsWith("[") || !entry.contains("]")) {
|
||||||
|
throw new ImportException("Invalid xpub entry format: " + entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
String fullDerivation = entry.substring(1, entry.indexOf(']'));
|
||||||
|
String xpubWithBip32Path = entry.substring(entry.indexOf(']') + 1);
|
||||||
|
|
||||||
|
int slashIndex = fullDerivation.indexOf('/');
|
||||||
|
if (slashIndex == -1) {
|
||||||
|
throw new ImportException("Invalid full derivation format (missing fingerprint/path separator '/'): " + fullDerivation);
|
||||||
|
}
|
||||||
|
String fingerprint = fullDerivation.substring(0, slashIndex);
|
||||||
|
String derivationPathSuffix = fullDerivation.substring(slashIndex + 1);
|
||||||
|
// The KeyDerivation class expects 'm/' prefix if it's not already there implicitly by being a full path.
|
||||||
|
// Since we have a suffix after the fingerprint, we prepend 'm/'.
|
||||||
|
String derivationPath = "m/" + derivationPathSuffix;
|
||||||
|
|
||||||
|
// The xpub from descriptor might include /0/* or /1/*, remove it for the base xpub used in Keystore.
|
||||||
|
String baseXpub = xpubWithBip32Path;
|
||||||
|
if (baseXpub.endsWith("/0/*")) {
|
||||||
|
baseXpub = baseXpub.substring(0, baseXpub.length() - "/0/*".length());
|
||||||
|
} else if (baseXpub.endsWith("/1/*")) {
|
||||||
|
baseXpub = baseXpub.substring(0, baseXpub.length() - "/1/*".length());
|
||||||
|
}
|
||||||
|
|
||||||
|
Keystore keystore = new Keystore();
|
||||||
|
keystore.setLabel(WalletModel.BITKEY.toDisplayString() + " " + fingerprint.substring(0, Math.min(fingerprint.length(), 4)));
|
||||||
|
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
||||||
|
keystore.setWalletModel(WalletModel.BITKEY);
|
||||||
|
keystore.setKeyDerivation(new KeyDerivation(fingerprint, derivationPath));
|
||||||
|
|
||||||
|
log.debug("Attempting to create ExtendedKey from baseXpub: [{}] for fingerprint: [{}] and path: [{}]", baseXpub, fingerprint, derivationPath);
|
||||||
|
try {
|
||||||
|
keystore.setExtendedPublicKey(ExtendedKey.fromDescriptor(baseXpub));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to create ExtendedKey from baseXpub: {}", baseXpub, e);
|
||||||
|
throw new ImportException("Failed to parse xpub: " + baseXpub, e);
|
||||||
|
}
|
||||||
|
wallet.makeLabelsUnique(keystore);
|
||||||
|
keystores.add(keystore);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keystores.isEmpty()) {
|
||||||
|
throw new ImportException("No xpubs found in Bitkey descriptor: " + externalDescriptorLine);
|
||||||
|
}
|
||||||
|
wallet.getKeystores().addAll(keystores);
|
||||||
|
|
||||||
|
Policy policy = Policy.getPolicy(PolicyType.MULTI, wallet.getScriptType(), wallet.getKeystores(), threshold);
|
||||||
|
wallet.setDefaultPolicy(policy);
|
||||||
|
|
||||||
|
log.debug("Wallet assembled. Policy: {} ScriptType: {} Keystores: {} Threshold: {}", policy, wallet.getScriptType(), keystores.size(), threshold);
|
||||||
|
|
||||||
|
return wallet;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error importing Bitkey Multisig wallet", e);
|
||||||
|
throw new ImportException("Error importing Bitkey Multisig wallet: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWalletImportDescription() {
|
||||||
|
return "Import the .txt file exported from your Bitkey companion app. This file describes a multisig wallet setup.";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted(File file) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWalletImportScannable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isKeystoreImportScannable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/main/resources/image/walletmodel/bitkey-invert.svg
Normal file
10
src/main/resources/image/walletmodel/bitkey-invert.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_3323_17112)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6399 7.17173C25.2437 6.94276 24.7554 6.94276 24.3591 7.17173L17.1591 11.3322C16.7633 11.561 16.5195 11.9834 16.5195 12.4405V20.7634C16.5195 21.2206 16.7633 21.643 17.1591 21.8718L22.0415 24.693C22.4373 24.9218 22.6811 25.3442 22.6811 25.8013V41.7198C22.6811 42.4267 23.2542 42.9998 23.9611 42.9998H26.038C26.7449 42.9998 27.318 42.4267 27.318 41.7198V25.8013C27.318 25.3442 27.5618 24.9218 27.9575 24.693L32.8399 21.8718C33.2358 21.643 33.4795 21.2206 33.4795 20.7634V12.4405C33.4795 11.9834 33.2358 11.561 32.8399 11.3322L25.6399 7.17173ZM30.2859 34.0713C29.6459 33.6981 28.8423 34.1598 28.8423 34.9006V35.469V39.9481V40.5166C28.8423 41.2574 29.6459 41.719 30.2859 41.3458L32.2065 40.2258C32.5015 40.0538 32.683 39.7381 32.683 39.3966V36.0206C32.683 35.679 32.5015 35.3633 32.2065 35.1912L30.2859 34.0713ZM25.4851 12.4493C25.1863 12.275 24.8168 12.275 24.518 12.4493L21.6373 14.129C21.3423 14.301 21.1608 14.6168 21.1608 14.9583V18.3344C21.1608 18.6758 21.3423 18.9917 21.6373 19.1637L24.518 20.8434C24.8168 21.0177 25.1863 21.0177 25.4851 20.8434L28.3658 19.1637C28.6608 18.9917 28.8423 18.6758 28.8423 18.3344V14.9583C28.8423 14.6168 28.6608 14.301 28.3658 14.129L25.4851 12.4493Z" fill="#D6D2D2"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_3323_17112">
|
||||||
|
<rect width="16.96" height="36" fill="white" transform="translate(16.5195 7)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
10
src/main/resources/image/walletmodel/bitkey.svg
Normal file
10
src/main/resources/image/walletmodel/bitkey.svg
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_3323_17111)">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.6409 7.17173C25.2447 6.94276 24.7563 6.94276 24.3601 7.17173L17.1601 11.3322C16.7643 11.561 16.5205 11.9834 16.5205 12.4405V20.7634C16.5205 21.2206 16.7643 21.643 17.1601 21.8718L22.0425 24.693C22.4383 24.9218 22.6821 25.3442 22.6821 25.8013V41.7198C22.6821 42.4267 23.2551 42.9998 23.9621 42.9998H26.039C26.7459 42.9998 27.319 42.4267 27.319 41.7198V25.8013C27.319 25.3442 27.5627 24.9218 27.9585 24.693L32.8409 21.8718C33.2367 21.643 33.4805 21.2206 33.4805 20.7634V12.4405C33.4805 11.9834 33.2367 11.561 32.8409 11.3322L25.6409 7.17173ZM30.2868 34.0713C29.6468 33.6981 28.8432 34.1598 28.8432 34.9006V35.469V39.9481V40.5166C28.8432 41.2574 29.6468 41.719 30.2868 41.3458L32.2075 40.2258C32.5025 40.0538 32.6839 39.7381 32.6839 39.3966V36.0206C32.6839 35.679 32.5025 35.3633 32.2075 35.1912L30.2868 34.0713ZM25.4861 12.4493C25.1872 12.275 24.8178 12.275 24.5189 12.4493L21.6383 14.129C21.3432 14.301 21.1618 14.6168 21.1618 14.9583V18.3344C21.1618 18.6758 21.3432 18.9917 21.6383 19.1637L24.5189 20.8434C24.8178 21.0177 25.1872 21.0177 25.4861 20.8434L28.3667 19.1637C28.6618 18.9917 28.8432 18.6758 28.8432 18.3344V14.9583C28.8432 14.6168 28.6618 14.301 28.3667 14.129L25.4861 12.4493Z" fill="#242424"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_3323_17111">
|
||||||
|
<rect width="16.96" height="36" fill="white" transform="translate(16.5205 7)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,65 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.ExtendedKey;
|
||||||
|
import com.sparrowwallet.drongo.Network;
|
||||||
|
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class BitkeyMultisigTest extends IoTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
Network.set(Network.MAINNET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImport() throws ImportException {
|
||||||
|
BitkeyMultisig bitkeyMultisig = new BitkeyMultisig();
|
||||||
|
Wallet wallet = bitkeyMultisig.importWallet(getInputStream("bitkey-multisig-export.txt"), null);
|
||||||
|
|
||||||
|
Assertions.assertNotNull(wallet);
|
||||||
|
Assertions.assertEquals("Bitkey Multisig", wallet.getName());
|
||||||
|
Assertions.assertEquals(PolicyType.MULTI, wallet.getPolicyType());
|
||||||
|
Assertions.assertEquals(ScriptType.P2WSH, wallet.getScriptType());
|
||||||
|
Assertions.assertEquals(3, wallet.getKeystores().size());
|
||||||
|
Assertions.assertEquals(2, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||||
|
|
||||||
|
// Keystore 1
|
||||||
|
Assertions.assertEquals("c11d36d2", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assertions.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||||
|
Assertions.assertEquals(ExtendedKey.fromDescriptor("xpub6D4Sz2PtJCj14ZmnCTzmGgMRttx2iU3MXbyCzEGetB2D4DsRz2Rk69Hp2M94Nhkn6QBZ5JTa1EW82CtjHeTe1wqQXMKYY6Pq7qVaz3uXdZ2"), wallet.getKeystores().get(0).getExtendedPublicKey());
|
||||||
|
Assertions.assertEquals(WalletModel.BITKEY, wallet.getKeystores().get(0).getWalletModel());
|
||||||
|
|
||||||
|
// Keystore 2
|
||||||
|
Assertions.assertEquals("647b89ea", wallet.getKeystores().get(1).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assertions.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(1).getKeyDerivation().getDerivationPath());
|
||||||
|
Assertions.assertEquals(ExtendedKey.fromDescriptor("xpub6CbVC3uhwPsQfAMC6ZVqCZsEx4BtNESnCLe3N8WqxAp46J2kjptausZRr11dCVh2WHamnEPrC8haz4iLR4sveUcqQx2iUawK41AY5RgyK8z"), wallet.getKeystores().get(1).getExtendedPublicKey());
|
||||||
|
Assertions.assertEquals(WalletModel.BITKEY, wallet.getKeystores().get(1).getWalletModel());
|
||||||
|
|
||||||
|
// Keystore 3
|
||||||
|
Assertions.assertEquals("02bfb691", wallet.getKeystores().get(2).getKeyDerivation().getMasterFingerprint());
|
||||||
|
Assertions.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(2).getKeyDerivation().getDerivationPath());
|
||||||
|
Assertions.assertEquals(ExtendedKey.fromDescriptor("xpub6DAGAFL3RnuYbzs9zbbDeGG1uEMj8tTpp8EEy4DMiPXHNaX5jVdeixoZN5jpDGbPa6a62LMqH4FCi9JDs4GF9qx89bRmVoHeznSfx7svqYN"), wallet.getKeystores().get(2).getExtendedPublicKey());
|
||||||
|
Assertions.assertEquals(WalletModel.BITKEY, wallet.getKeystores().get(2).getWalletModel());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidFormat() throws ImportException {
|
||||||
|
BitkeyMultisig bitkeyMultisig = new BitkeyMultisig();
|
||||||
|
Assertions.assertThrows(ImportException.class, () ->
|
||||||
|
bitkeyMultisig.importWallet(getInputStream("bitkey-invalid-format.txt"), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown() {
|
||||||
|
Network.set(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
This is not a valid Bitkey export format.
|
||||||
|
It should contain External: and Internal: lines with descriptors.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
External: wsh(sortedmulti(2,[c11d36d2/84'/0'/0']xpub6D4Sz2PtJCj14ZmnCTzmGgMRttx2iU3MXbyCzEGetB2D4DsRz2Rk69Hp2M94Nhkn6QBZ5JTa1EW82CtjHeTe1wqQXMKYY6Pq7qVaz3uXdZ2/0/*,[647b89ea/84'/0'/0']xpub6CbVC3uhwPsQfAMC6ZVqCZsEx4BtNESnCLe3N8WqxAp46J2kjptausZRr11dCVh2WHamnEPrC8haz4iLR4sveUcqQx2iUawK41AY5RgyK8z/0/*,[02bfb691/84'/0'/0']xpub6DAGAFL3RnuYbzs9zbbDeGG1uEMj8tTpp8EEy4DMiPXHNaX5jVdeixoZN5jpDGbPa6a62LMqH4FCi9JDs4GF9qx89bRmVoHeznSfx7svqYN/0/*))
|
||||||
|
Internal: wsh(sortedmulti(2,[c11d36d2/84'/0'/0']xpub6D4Sz2PtJCj14ZmnCTzmGgMRttx2iU3MXbyCzEGetB2D4DsRz2Rk69Hp2M94Nhkn6QBZ5JTa1EW82CtjHeTe1wqQXMKYY6Pq7qVaz3uXdZ2/1/*,[647b89ea/84'/0'/0']xpub6CbVC3uhwPsQfAMC6ZVqCZsEx4BtNESnCLe3N8WqxAp46J2kjptausZRr11dCVh2WHamnEPrC8haz4iLR4sveUcqQx2iUawK41AY5RgyK8z/1/*,[02bfb691/84'/0'/0']xpub6DAGAFL3RnuYbzs9zbbDeGG1uEMj8tTpp8EEy4DMiPXHNaX5jVdeixoZN5jpDGbPa6a62LMqH4FCi9JDs4GF9qx89bRmVoHeznSfx7svqYN/1/*))
|
||||||
Loading…
Reference in a new issue