mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-11-02 20:36:44 +00:00
passphrase not stored
This commit is contained in:
parent
1bf8c85a65
commit
6b09dc0293
15 changed files with 93 additions and 111 deletions
2
drongo
2
drongo
|
@ -1 +1 @@
|
|||
Subproject commit 9e5a7d0e8d31eb4e21d543081c23f7f0aaef9795
|
||||
Subproject commit 312143cb611fefce4e75654266f90cfd3b37b09e
|
|
@ -4,14 +4,13 @@ import com.google.common.base.Charsets;
|
|||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.common.io.ByteSource;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.crypto.ECIESKeyCrypter;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.crypto.InvalidPasswordException;
|
||||
import com.sparrowwallet.drongo.crypto.*;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.protocol.Transaction;
|
||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||
import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import com.sparrowwallet.sparrow.control.*;
|
||||
import com.sparrowwallet.sparrow.event.*;
|
||||
|
@ -235,24 +234,65 @@ public class AppController implements Initializable {
|
|||
if(file != null) {
|
||||
try {
|
||||
Wallet wallet;
|
||||
String password = null;
|
||||
ECKey encryptionPubKey = WalletForm.NO_PASSWORD_KEY;
|
||||
FileType fileType = IOUtils.getFileType(file);
|
||||
if(FileType.JSON.equals(fileType)) {
|
||||
wallet = Storage.getStorage().loadWallet(file);
|
||||
} else if(FileType.BINARY.equals(fileType)) {
|
||||
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<String> password = dlg.showAndWait();
|
||||
if(!password.isPresent()) {
|
||||
Optional<String> optionalPassword = dlg.showAndWait();
|
||||
if(!optionalPassword.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ECKey encryptionFullKey = ECIESKeyCrypter.deriveECKey(password.get());
|
||||
password = optionalPassword.get();
|
||||
ECKey encryptionFullKey = ECIESKeyCrypter.deriveECKey(password);
|
||||
wallet = Storage.getStorage().loadWallet(file, encryptionFullKey);
|
||||
encryptionPubKey = ECKey.fromPublicOnly(encryptionFullKey);
|
||||
} else {
|
||||
throw new IOException("Unsupported file type");
|
||||
}
|
||||
|
||||
if(wallet.containsSeeds()) {
|
||||
//Derive xpub and master fingerprint from seed, potentially with passphrase
|
||||
Wallet copy = wallet.copy();
|
||||
if(wallet.isEncrypted()) {
|
||||
if(password == null) {
|
||||
throw new IllegalStateException("Wallet seeds are encrypted but wallet is not");
|
||||
}
|
||||
|
||||
copy.decrypt(password);
|
||||
}
|
||||
|
||||
for(Keystore copyKeystore : copy.getKeystores()) {
|
||||
if(copyKeystore.hasSeed()) {
|
||||
if(copyKeystore.getSeed().needsPassphrase()) {
|
||||
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(copyKeystore);
|
||||
Optional<String> optionalPassphrase = passphraseDialog.showAndWait();
|
||||
if(optionalPassphrase.isPresent()) {
|
||||
copyKeystore.getSeed().setPassphrase(optionalPassphrase.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
copyKeystore.getSeed().setPassphrase("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < wallet.getKeystores().size(); i++) {
|
||||
Keystore keystore = wallet.getKeystores().get(i);
|
||||
if(keystore.hasSeed()) {
|
||||
Keystore copyKeystore = copy.getKeystores().get(i);
|
||||
Keystore derivedKeystore = Keystore.fromSeed(copyKeystore.getSeed(), copyKeystore.getKeyDerivation().getDerivation());
|
||||
keystore.setKeyDerivation(derivedKeystore.getKeyDerivation());
|
||||
keystore.setExtendedPublicKey(derivedKeystore.getExtendedPublicKey());
|
||||
keystore.getSeed().setPassphrase(copyKeystore.getSeed().getPassphrase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tab tab = addWalletTab(file, encryptionPubKey, wallet);
|
||||
tabs.getSelectionModel().select(tab);
|
||||
} catch (InvalidPasswordException e) {
|
||||
|
|
|
@ -55,19 +55,7 @@ public class FileWalletExportPane extends TitledDescriptionPane {
|
|||
WalletPasswordDialog dlg = new WalletPasswordDialog(WalletPasswordDialog.PasswordRequirement.LOAD);
|
||||
Optional<String> password = dlg.showAndWait();
|
||||
if(password.isPresent()) {
|
||||
copy.decrypt(password.get(), "");
|
||||
|
||||
for(Keystore keystore : copy.getKeystores()) {
|
||||
if(keystore.hasSeed() && keystore.getSeed().usesPassphrase()) {
|
||||
KeystorePassphraseDialog passphraseDialog = new KeystorePassphraseDialog(keystore);
|
||||
Optional<String> passphrase = passphraseDialog.showAndWait();
|
||||
if(passphrase.isPresent()) {
|
||||
keystore.getSeed().setPassphrase(passphrase.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
copy.decrypt(password.get());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class Bip39 implements KeystoreMnemonicImport {
|
|||
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
|
||||
try {
|
||||
Bip39MnemonicCode.INSTANCE.check(mnemonicWords);
|
||||
DeterministicSeed seed = new DeterministicSeed(mnemonicWords, null, passphrase, System.currentTimeMillis(), DeterministicSeed.Type.BIP39);
|
||||
DeterministicSeed seed = new DeterministicSeed(mnemonicWords, passphrase, System.currentTimeMillis(), DeterministicSeed.Type.BIP39);
|
||||
return Keystore.fromSeed(seed, derivation);
|
||||
} catch (Exception e) {
|
||||
throw new ImportException(e);
|
||||
|
|
|
@ -34,7 +34,7 @@ public class ColdcardMultisig implements WalletImport, KeystoreFileImport, Walle
|
|||
@Override
|
||||
public Keystore getKeystore(ScriptType scriptType, InputStream inputStream, String password) throws ImportException {
|
||||
InputStreamReader reader = new InputStreamReader(inputStream);
|
||||
ColdcardKeystore cck = Storage.getStorage().getGson().fromJson(reader, ColdcardKeystore.class);
|
||||
ColdcardKeystore cck = Storage.getGson().fromJson(reader, ColdcardKeystore.class);
|
||||
|
||||
Keystore keystore = new Keystore("Coldcard " + cck.xfp);
|
||||
keystore.setSource(KeystoreSource.HW_AIRGAPPED);
|
||||
|
|
|
@ -43,7 +43,7 @@ public class ECIESInputStream extends FilterInputStream {
|
|||
byte[] encryptedBytes = ByteStreams.toByteArray(in);
|
||||
in.close();
|
||||
ECIESKeyCrypter keyCrypter = new ECIESKeyCrypter();
|
||||
byte[] decryptedBytes = keyCrypter.decrypt(new EncryptedData(encryptionMagic, encryptedBytes), decryptionKey);
|
||||
byte[] decryptedBytes = keyCrypter.decrypt(new EncryptedData(encryptionMagic, encryptedBytes, null), decryptionKey);
|
||||
in = new ByteArrayInputStream(decryptedBytes);
|
||||
decrypted = true;
|
||||
}
|
||||
|
|
|
@ -112,17 +112,15 @@ public class Electrum implements KeystoreFileImport, WalletImport, WalletExport
|
|||
throw new ImportException("Electrum does not support exporting BIP39 derived seeds.");
|
||||
} else if(ek.seed != null) {
|
||||
keystore.setSource(KeystoreSource.SW_SEED);
|
||||
String seed = ek.seed;
|
||||
String mnemonic = ek.seed;
|
||||
String passphrase = ek.passphrase;
|
||||
if(password != null) {
|
||||
seed = decrypt(seed, password);
|
||||
mnemonic = decrypt(mnemonic, password);
|
||||
passphrase = decrypt(passphrase, password);
|
||||
}
|
||||
|
||||
keystore.setSeed(new DeterministicSeed(seed, null, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
|
||||
keystore.getSeed().setPassphrase(passphrase);
|
||||
|
||||
if(derivationPath == "m/0") {
|
||||
keystore.setSeed(new DeterministicSeed(mnemonic, passphrase, 0, DeterministicSeed.Type.ELECTRUM));
|
||||
if(derivationPath.equals("m/0")) {
|
||||
derivationPath = "m/0'";
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.gson.*;
|
|||
import com.sparrowwallet.drongo.ExtendedKey;
|
||||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
|
||||
import java.io.*;
|
||||
|
@ -20,12 +21,7 @@ public class Storage {
|
|||
private final Gson gson;
|
||||
|
||||
private Storage() {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
|
||||
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
|
||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
|
||||
gson = gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
|
||||
gson = getGson();
|
||||
}
|
||||
|
||||
public static Storage getStorage() {
|
||||
|
@ -36,8 +32,21 @@ public class Storage {
|
|||
return SINGLETON;
|
||||
}
|
||||
|
||||
public Gson getGson() {
|
||||
return gson;
|
||||
public static Gson getGson() {
|
||||
return getGson(true);
|
||||
}
|
||||
|
||||
private static Gson getGson(boolean includeKeystoreSerializer) {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeySerializer());
|
||||
gsonBuilder.registerTypeAdapter(ExtendedKey.class, new ExtendedPublicKeyDeserializer());
|
||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArraySerializer());
|
||||
gsonBuilder.registerTypeAdapter(byte[].class, new ByteArrayDeserializer());
|
||||
if(includeKeystoreSerializer) {
|
||||
gsonBuilder.registerTypeAdapter(Keystore.class, new KeystoreSerializer());
|
||||
}
|
||||
|
||||
return gsonBuilder.setPrettyPrinting().disableHtmlEscaping().create();
|
||||
}
|
||||
|
||||
public Wallet loadWallet(File file) throws IOException {
|
||||
|
@ -150,4 +159,18 @@ public class Storage {
|
|||
return Utils.hexToBytes(json.getAsJsonPrimitive().getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class KeystoreSerializer implements JsonSerializer<Keystore> {
|
||||
@Override
|
||||
public JsonElement serialize(Keystore keystore, Type typeOfSrc, JsonSerializationContext context) {
|
||||
|
||||
JsonObject jsonObject = (JsonObject)getGson(false).toJsonTree(keystore);
|
||||
if(keystore.hasSeed()) {
|
||||
jsonObject.remove("extendedPublicKey");
|
||||
jsonObject.getAsJsonObject("keyDerivation").remove("masterFingerprint");
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,7 +152,6 @@ public class KeystoreController extends WalletFormController implements Initiali
|
|||
type.setText(getTypeLabel(keystore));
|
||||
|
||||
boolean editable = (keystore.getSource() == KeystoreSource.SW_WATCH);
|
||||
label.setEditable(editable);
|
||||
fingerprint.setEditable(editable);
|
||||
derivation.setEditable(editable);
|
||||
xpub.setEditable(editable);
|
||||
|
|
|
@ -88,7 +88,7 @@ public class ColdcardMultisigTest extends IoTest {
|
|||
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.assertEquals("m/47'/0'/0'/1'", wallet.getKeystores().get(2).getKeyDerivation().getDerivationPath());
|
||||
Assert.assertTrue(wallet.isValid());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.crypto.ECIESKeyCrypter;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
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.util.zip.InflaterInputStream;
|
||||
|
||||
public class ECIESInputStreamTest extends IoTest {
|
||||
@Test
|
||||
public void decrypt() throws ImportException {
|
||||
Electrum electrum = new Electrum();
|
||||
ECKey decryptionKey = ECIESKeyCrypter.deriveECKey("pass");
|
||||
Wallet wallet = electrum.importWallet(new InflaterInputStream(new ECIESInputStream(getInputStream("electrum-encrypted"), decryptionKey)), null);
|
||||
|
||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||
Assert.assertEquals("pkh(electrum05aba071)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||
Assert.assertEquals("05aba071", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||
Assert.assertEquals("xpub67vv394epQsLhdjNGx7dfgURicP7XwBMuHPTVAMdXcXhDuC9VP8SqVvh2cYqKWm9xoUd6YynWK8JzRcXpmeuZFRH7i1kt8fR9GXoJSiHk1E", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||
Assert.assertTrue(wallet.isValid());
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package com.sparrowwallet.sparrow.io;
|
||||
|
||||
import com.sparrowwallet.drongo.crypto.ECIESKeyCrypter;
|
||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
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.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class ECIESOutputStreamTest extends IoTest {
|
||||
@Test
|
||||
public void encrypt() throws ImportException, ExportException {
|
||||
Electrum electrum = new Electrum();
|
||||
ECKey decryptionKey = ECIESKeyCrypter.deriveECKey("pass");
|
||||
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)), null);
|
||||
|
||||
Assert.assertEquals(PolicyType.SINGLE, wallet.getPolicyType());
|
||||
Assert.assertEquals(ScriptType.P2WPKH, wallet.getScriptType());
|
||||
Assert.assertEquals(1, wallet.getDefaultPolicy().getNumSignaturesRequired());
|
||||
Assert.assertEquals("pkh(electrum05aba071)", wallet.getDefaultPolicy().getMiniscript().getScript());
|
||||
Assert.assertEquals("05aba071", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||
Assert.assertEquals("m/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||
Assert.assertEquals("xpub67vv394epQsLhdjNGx7dfgURicP7XwBMuHPTVAMdXcXhDuC9VP8SqVvh2cYqKWm9xoUd6YynWK8JzRcXpmeuZFRH7i1kt8fR9GXoJSiHk1E", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||
Assert.assertTrue(wallet.isValid());
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import com.google.common.io.ByteStreams;
|
|||
import com.sparrowwallet.drongo.Utils;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.MnemonicException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -102,7 +103,7 @@ public class ElectrumTest extends IoTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSinglesigSeedExport() throws ImportException, ExportException, IOException {
|
||||
public void testSinglesigSeedExport() throws ImportException, ExportException, IOException, MnemonicException {
|
||||
Electrum electrum = new Electrum();
|
||||
byte[] walletBytes = ByteStreams.toByteArray(getInputStream("electrum-singlesig-seed-wallet.json"));
|
||||
Wallet wallet = electrum.importWallet(new ByteArrayInputStream(walletBytes), null);
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.sparrowwallet.drongo.crypto.ECIESKeyCrypter;
|
|||
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||
import com.sparrowwallet.drongo.policy.PolicyType;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
import com.sparrowwallet.drongo.wallet.MnemonicException;
|
||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -20,7 +21,7 @@ public class StorageTest extends IoTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void loadSeedWallet() throws IOException {
|
||||
public void loadSeedWallet() throws IOException, MnemonicException {
|
||||
ECKey decryptionKey = ECIESKeyCrypter.deriveECKey("pass");
|
||||
|
||||
Wallet wallet = Storage.getStorage().loadWallet(getFile("sparrow-single-seed-wallet"), decryptionKey);
|
||||
|
@ -34,7 +35,7 @@ public class StorageTest extends IoTest {
|
|||
Assert.assertEquals("60bcd3a7", wallet.getKeystores().get(0).getKeyDerivation().getMasterFingerprint());
|
||||
Assert.assertEquals("m/84'/0'/0'", wallet.getKeystores().get(0).getKeyDerivation().getDerivationPath());
|
||||
Assert.assertEquals("xpub6BrhGFTWPd3DQaGP7p5zTQkE5nqVbaRs23HNae8jAoNJYS2NGa9Sgpeqv1dS5ygwD4sQfwqLCk5qXRK45FTgnqHRcrPnts3Qgh78BZrnoMn", wallet.getKeystores().get(0).getExtendedPublicKey().toString());
|
||||
Assert.assertEquals("b0e161bff5f589e74b20d9cd260702a6a1e6e1ab3ba4ce764f388dd8f360a1ccdb21099a2f22757ca72f9bde3a34b97a31fb513fb8931c821b0d25798e450b6a57dc106973849ca586b50b2db2840adc", Utils.bytesToHex(wallet.getKeystores().get(0).getSeed().getEncryptedSeedData().getEncryptedBytes()));
|
||||
Assert.assertEquals("a48767d6b58732a0cad17ed93e23022ec603a177e75461f2aed994713fbbe532b61f6c0758a8aedcf9b2b8102c01c6f3e3e212ca06f13644d4ac8dad66556e164b7eaf79d0b42eadecee8b735e97fc0a", Utils.bytesToHex(wallet.getKeystores().get(0).getSeed().getEncryptedData().getEncryptedBytes()));
|
||||
Assert.assertNull(wallet.getKeystores().get(0).getSeed().getSeedBytes());
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,6 @@ Format: P2WSH
|
|||
# derivation: m/48'/0'/0'/2'
|
||||
4B569672: xpub6FFEQVG6QR28giDuML74Y7EMPwqEiKftNjScLzg5WKM41bf6LMP2XspjBgNp28tvkNUZdokmTY4TcRbuGZBSMvNoUECrKW1y3TBPeQJVmAg
|
||||
|
||||
# derivation: m/48'/0'/0'/1'
|
||||
# derivation: m/47'/0'/0'/1'
|
||||
CA9A2B19: xpub6Eb6Z1xtmWRiWKgRpHf6dHiEagGd6FLiBXrnma1nFK4PGRYqSVqVyJaxna5Mb8etSP4ATKVAvKnXG1a9HZauoAawuSDJT5RgH2HqEVHZVHY
|
||||
|
||||
|
|
Loading…
Reference in a new issue