mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-26 01:56:44 +00:00
move child derivation paths into outputdescriptor
This commit is contained in:
parent
075707f1ad
commit
f7f15cebc7
4 changed files with 101 additions and 94 deletions
|
@ -18,16 +18,13 @@ public class ExtendedPublicKey {
|
|||
|
||||
private final byte[] parentFingerprint;
|
||||
private final DeterministicKey pubKey;
|
||||
private final String childDerivationPath;
|
||||
private final ChildNumber pubKeyChildNumber;
|
||||
private final DeterministicHierarchy hierarchy;
|
||||
|
||||
public ExtendedPublicKey(DeterministicKey pubKey, byte[] parentFingerprint, String childDerivationPath, ChildNumber pubKeyChildNumber) {
|
||||
public ExtendedPublicKey(DeterministicKey pubKey, byte[] parentFingerprint, ChildNumber pubKeyChildNumber) {
|
||||
this.parentFingerprint = parentFingerprint;
|
||||
this.pubKey = pubKey;
|
||||
this.childDerivationPath = childDerivationPath;
|
||||
this.pubKeyChildNumber = pubKeyChildNumber;
|
||||
|
||||
this.hierarchy = new DeterministicHierarchy(pubKey);
|
||||
}
|
||||
|
||||
|
@ -35,62 +32,10 @@ public class ExtendedPublicKey {
|
|||
return parentFingerprint;
|
||||
}
|
||||
|
||||
public byte[] getFingerprint() {
|
||||
return pubKey.getFingerprint();
|
||||
}
|
||||
|
||||
public DeterministicKey getPubKey() {
|
||||
return pubKey;
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChildDerivation() {
|
||||
return getChildDerivation(0);
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChildDerivation(int wildCardReplacement) {
|
||||
return getChildDerivation(getPubKey().getChildNumber(), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
|
||||
public boolean describesMultipleAddresses() {
|
||||
return childDerivationPath.endsWith("/*");
|
||||
}
|
||||
|
||||
public List<ChildNumber> getReceivingDerivation(int wildCardReplacement) {
|
||||
if(describesMultipleAddresses()) {
|
||||
if(childDerivationPath.endsWith("0/*")) {
|
||||
return getChildDerivation(getPubKey().getChildNumber(), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
|
||||
if(pubKeyChildNumber.num() == 0 && childDerivationPath.endsWith("/*")) {
|
||||
return getChildDerivation(new ChildNumber(0, getPubKey().getChildNumber().isHardened()), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot derive receiving address from output descriptor " + this.toString());
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChangeDerivation(int wildCardReplacement) {
|
||||
if(describesMultipleAddresses()) {
|
||||
if(childDerivationPath.endsWith("0/*")) {
|
||||
return getChildDerivation(getPubKey().getChildNumber(), childDerivationPath.replace("0/*", "1/*"), wildCardReplacement);
|
||||
}
|
||||
|
||||
if(pubKeyChildNumber.num() == 1 && childDerivationPath.endsWith("/*")) {
|
||||
return getChildDerivation(new ChildNumber(1, getPubKey().getChildNumber().isHardened()), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot derive change address from output descriptor " + this.toString());
|
||||
}
|
||||
|
||||
private List<ChildNumber> getChildDerivation(ChildNumber firstChild, String derivationPath, int wildCardReplacement) {
|
||||
List<ChildNumber> path = new ArrayList<>();
|
||||
path.add(firstChild);
|
||||
path.addAll(parsePath(derivationPath, wildCardReplacement));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public DeterministicKey getKey(List<ChildNumber> path) {
|
||||
return hierarchy.get(path);
|
||||
}
|
||||
|
@ -98,7 +43,6 @@ public class ExtendedPublicKey {
|
|||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(getExtendedPublicKey());
|
||||
builder.append(childDerivationPath);
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
@ -106,25 +50,23 @@ public class ExtendedPublicKey {
|
|||
return Base58.encodeChecked(getExtendedPublicKeyBytes());
|
||||
}
|
||||
|
||||
public ChildNumber getPubKeyChildNumber() {
|
||||
return pubKeyChildNumber;
|
||||
}
|
||||
|
||||
public byte[] getExtendedPublicKeyBytes() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(78);
|
||||
buffer.putInt(bip32HeaderP2PKHXPub);
|
||||
|
||||
List<ChildNumber> childPath = parsePath(childDerivationPath);
|
||||
int depth = 5 - childPath.size();
|
||||
buffer.put((byte)depth);
|
||||
|
||||
buffer.put((byte)pubKey.getDepth());
|
||||
buffer.put(parentFingerprint);
|
||||
|
||||
buffer.putInt(pubKeyChildNumber.i());
|
||||
|
||||
buffer.put(pubKey.getChainCode());
|
||||
buffer.put(pubKey.getPubKey());
|
||||
|
||||
return buffer.array();
|
||||
}
|
||||
|
||||
public static ExtendedPublicKey fromDescriptor(String extPubKey, String childDerivationPath) {
|
||||
public static ExtendedPublicKey fromDescriptor(String extPubKey) {
|
||||
byte[] serializedKey = Base58.decodeChecked(extPubKey);
|
||||
ByteBuffer buffer = ByteBuffer.wrap(serializedKey);
|
||||
int header = buffer.getInt();
|
||||
|
@ -147,7 +89,7 @@ public class ExtendedPublicKey {
|
|||
} else {
|
||||
childNumber = new ChildNumber(i, false);
|
||||
}
|
||||
path = Collections.unmodifiableList(new ArrayList<>(Arrays.asList(childNumber)));
|
||||
path = List.of(childNumber);
|
||||
|
||||
byte[] chainCode = new byte[32];
|
||||
buffer.get(chainCode);
|
||||
|
@ -157,17 +99,13 @@ public class ExtendedPublicKey {
|
|||
throw new IllegalArgumentException("Found unexpected data in key");
|
||||
}
|
||||
|
||||
if(childDerivationPath == null) {
|
||||
childDerivationPath = writePath(Collections.singletonList(childNumber));
|
||||
}
|
||||
|
||||
DeterministicKey pubKey = new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), depth, parentFingerprint);
|
||||
return new ExtendedPublicKey(pubKey, parentFingerprint, childDerivationPath, childNumber);
|
||||
return new ExtendedPublicKey(pubKey, parentFingerprint, childNumber);
|
||||
}
|
||||
|
||||
public static boolean isValid(String extPubKey) {
|
||||
try {
|
||||
ExtendedPublicKey.fromDescriptor(extPubKey, null);
|
||||
ExtendedPublicKey.fromDescriptor(extPubKey);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@ import com.sparrowwallet.drongo.protocol.ScriptChunk;
|
|||
import com.sparrowwallet.drongo.protocol.ScriptOpCodes;
|
||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.sparrowwallet.drongo.KeyDerivation.parsePath;
|
||||
|
||||
public class OutputDescriptor {
|
||||
private static final Pattern XPUB_PATTERN = Pattern.compile("(\\[[^\\]]+\\])?(.pub[^/\\)]+)(/[/\\d*']+)?");
|
||||
private static final Pattern MULTI_PATTERN = Pattern.compile("multi\\(([\\d+])");
|
||||
|
@ -20,6 +23,7 @@ public class OutputDescriptor {
|
|||
private final String script;
|
||||
private final int multisigThreshold;
|
||||
private final Map<ExtendedPublicKey, KeyDerivation> extendedPublicKeys;
|
||||
private final Map<ExtendedPublicKey, String> mapChildrenDerivations;
|
||||
|
||||
public OutputDescriptor(String script, ExtendedPublicKey extendedPublicKey, KeyDerivation keyDerivation) {
|
||||
this(script, Collections.singletonMap(extendedPublicKey, keyDerivation));
|
||||
|
@ -30,9 +34,14 @@ public class OutputDescriptor {
|
|||
}
|
||||
|
||||
public OutputDescriptor(String script, int multisigThreshold, Map<ExtendedPublicKey, KeyDerivation> extendedPublicKeys) {
|
||||
this(script, multisigThreshold, extendedPublicKeys, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public OutputDescriptor(String script, int multisigThreshold, Map<ExtendedPublicKey, KeyDerivation> extendedPublicKeys, Map<ExtendedPublicKey, String> mapChildrenDerivations) {
|
||||
this.script = script;
|
||||
this.multisigThreshold = multisigThreshold;
|
||||
this.extendedPublicKeys = extendedPublicKeys;
|
||||
this.mapChildrenDerivations = mapChildrenDerivations;
|
||||
}
|
||||
|
||||
public Set<ExtendedPublicKey> getExtendedPublicKeys() {
|
||||
|
@ -43,6 +52,61 @@ public class OutputDescriptor {
|
|||
return extendedPublicKeys.get(extendedPublicKey);
|
||||
}
|
||||
|
||||
public String getChildDerivationPath(ExtendedPublicKey extendedPublicKey) {
|
||||
return mapChildrenDerivations.get(extendedPublicKey);
|
||||
}
|
||||
|
||||
public boolean describesMultipleAddresses(ExtendedPublicKey extendedPublicKey) {
|
||||
return getChildDerivationPath(extendedPublicKey).endsWith("/*");
|
||||
}
|
||||
|
||||
public List<ChildNumber> getReceivingDerivation(ExtendedPublicKey extendedPublicKey, int wildCardReplacement) {
|
||||
String childDerivationPath = getChildDerivationPath(extendedPublicKey);
|
||||
if(describesMultipleAddresses(extendedPublicKey)) {
|
||||
if(childDerivationPath.endsWith("0/*")) {
|
||||
return getChildDerivation(extendedPublicKey.getPubKey().getChildNumber(), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
|
||||
if(extendedPublicKey.getPubKeyChildNumber().num() == 0 && childDerivationPath.endsWith("/*")) {
|
||||
return getChildDerivation(new ChildNumber(0, extendedPublicKey.getPubKey().getChildNumber().isHardened()), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot derive receiving address from output descriptor " + this.toString());
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChangeDerivation(ExtendedPublicKey extendedPublicKey, int wildCardReplacement) {
|
||||
String childDerivationPath = getChildDerivationPath(extendedPublicKey);
|
||||
if(describesMultipleAddresses(extendedPublicKey)) {
|
||||
if(childDerivationPath.endsWith("0/*")) {
|
||||
return getChildDerivation(extendedPublicKey.getPubKey().getChildNumber(), childDerivationPath.replace("0/*", "1/*"), wildCardReplacement);
|
||||
}
|
||||
|
||||
if(extendedPublicKey.getPubKeyChildNumber().num() == 1 && childDerivationPath.endsWith("/*")) {
|
||||
return getChildDerivation(new ChildNumber(1, extendedPublicKey.getPubKey().getChildNumber().isHardened()), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Cannot derive change address from output descriptor " + this.toString());
|
||||
}
|
||||
|
||||
private List<ChildNumber> getChildDerivation(ChildNumber firstChild, String derivationPath, int wildCardReplacement) {
|
||||
List<ChildNumber> path = new ArrayList<>();
|
||||
path.add(firstChild);
|
||||
path.addAll(parsePath(derivationPath, wildCardReplacement));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChildDerivation(ExtendedPublicKey extendedPublicKey) {
|
||||
return getChildDerivation(extendedPublicKey, 0);
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChildDerivation(ExtendedPublicKey extendedPublicKey, int wildCardReplacement) {
|
||||
String childDerivationPath = getChildDerivationPath(extendedPublicKey);
|
||||
return getChildDerivation(extendedPublicKey.getPubKey().getChildNumber(), childDerivationPath, wildCardReplacement);
|
||||
}
|
||||
|
||||
public boolean isMultisig() {
|
||||
return extendedPublicKeys.size() > 1;
|
||||
}
|
||||
|
@ -61,7 +125,7 @@ public class OutputDescriptor {
|
|||
|
||||
public boolean describesMultipleAddresses() {
|
||||
for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) {
|
||||
if(!pubKey.describesMultipleAddresses()) {
|
||||
if(describesMultipleAddresses(pubKey)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +136,7 @@ public class OutputDescriptor {
|
|||
public List<ChildNumber> getChildDerivation() {
|
||||
List<ChildNumber> lastDerivation = null;
|
||||
for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) {
|
||||
List<ChildNumber> derivation = pubKey.getChildDerivation();
|
||||
List<ChildNumber> derivation = getChildDerivation(pubKey);
|
||||
if(lastDerivation != null && !lastDerivation.subList(1, lastDerivation.size()).equals(derivation.subList(1, derivation.size()))) {
|
||||
throw new IllegalStateException("Cannot determine multisig derivation: constituent derivations do not match");
|
||||
}
|
||||
|
@ -90,7 +154,7 @@ public class OutputDescriptor {
|
|||
return path;
|
||||
}
|
||||
|
||||
return getSingletonExtendedPublicKey().getReceivingDerivation(wildCardReplacement);
|
||||
return getReceivingDerivation(getSingletonExtendedPublicKey(), wildCardReplacement);
|
||||
}
|
||||
|
||||
public List<ChildNumber> getChangeDerivation(int wildCardReplacement) {
|
||||
|
@ -101,7 +165,7 @@ public class OutputDescriptor {
|
|||
return path;
|
||||
}
|
||||
|
||||
return getSingletonExtendedPublicKey().getChangeDerivation(wildCardReplacement);
|
||||
return getChangeDerivation(getSingletonExtendedPublicKey(), wildCardReplacement);
|
||||
}
|
||||
|
||||
public Address getAddress(List<ChildNumber> path) {
|
||||
|
@ -150,11 +214,11 @@ public class OutputDescriptor {
|
|||
for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) {
|
||||
List<ChildNumber> keyPath = null;
|
||||
if(path.get(0).num() == 0) {
|
||||
keyPath = pubKey.getReceivingDerivation(path.get(1).num());
|
||||
keyPath = getReceivingDerivation(pubKey, path.get(1).num());
|
||||
} else if(path.get(0).num() == 1) {
|
||||
keyPath = pubKey.getChangeDerivation(path.get(1).num());
|
||||
keyPath = getChangeDerivation(pubKey, path.get(1).num());
|
||||
} else {
|
||||
keyPath = pubKey.getChildDerivation(path.get(1).num());
|
||||
keyPath = getChildDerivation(pubKey, path.get(1).num());
|
||||
}
|
||||
|
||||
byte[] pubKeyBytes = pubKey.getKey(keyPath).getPubKey();
|
||||
|
@ -170,15 +234,15 @@ public class OutputDescriptor {
|
|||
// See https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
|
||||
public static OutputDescriptor getOutputDescriptor(String descriptor) {
|
||||
if(descriptor.startsWith("pkh") || descriptor.startsWith("xpub")) {
|
||||
return new OutputDescriptor("pkh", getExtendedPublicKeys(descriptor));
|
||||
return getOutputDescriptorImpl("pkh", 0, descriptor);
|
||||
} else if(descriptor.startsWith("wpkh") || descriptor.startsWith("zpub")) {
|
||||
return new OutputDescriptor("wpkh", getExtendedPublicKeys(descriptor));
|
||||
return getOutputDescriptorImpl("wpkh", 0, descriptor);
|
||||
} else if(descriptor.startsWith("sh(wpkh") || descriptor.startsWith("ypub")) {
|
||||
return new OutputDescriptor("sh(wpkh", getExtendedPublicKeys(descriptor));
|
||||
return getOutputDescriptorImpl("sh(wpkh", 0, descriptor);
|
||||
} else if(descriptor.startsWith("sh(multi") || descriptor.startsWith("Ypub")) {
|
||||
return new OutputDescriptor("sh(multi", getMultsigThreshold(descriptor), getExtendedPublicKeys(descriptor));
|
||||
return getOutputDescriptorImpl("sh(multi", getMultsigThreshold(descriptor), descriptor);
|
||||
} else if(descriptor.startsWith("wsh(multi") || descriptor.startsWith("Zpub")) {
|
||||
return new OutputDescriptor("wsh(multi", getMultsigThreshold(descriptor), getExtendedPublicKeys(descriptor));
|
||||
return getOutputDescriptorImpl("wsh(multi", getMultsigThreshold(descriptor), descriptor);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not parse output descriptor:" + descriptor);
|
||||
}
|
||||
|
@ -194,8 +258,9 @@ public class OutputDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
private static Map<ExtendedPublicKey, KeyDerivation> getExtendedPublicKeys(String descriptor) {
|
||||
Map<ExtendedPublicKey, KeyDerivation> keys = new LinkedHashMap<>();
|
||||
private static OutputDescriptor getOutputDescriptorImpl(String script, int multisigThreshold, String descriptor) {
|
||||
Map<ExtendedPublicKey, KeyDerivation> keyDerivationMap = new LinkedHashMap<>();
|
||||
Map<ExtendedPublicKey, String> keyChildDerivationMap = new LinkedHashMap<>();
|
||||
Matcher matcher = XPUB_PATTERN.matcher(descriptor);
|
||||
while(matcher.find()) {
|
||||
String masterFingerprint = null;
|
||||
|
@ -218,11 +283,12 @@ public class OutputDescriptor {
|
|||
}
|
||||
|
||||
KeyDerivation keyDerivation = new KeyDerivation(masterFingerprint, keyDerivationPath);
|
||||
ExtendedPublicKey extendedPublicKey = ExtendedPublicKey.fromDescriptor(extPubKey, childDerivationPath);
|
||||
keys.put(extendedPublicKey, keyDerivation);
|
||||
ExtendedPublicKey extendedPublicKey = ExtendedPublicKey.fromDescriptor(extPubKey);
|
||||
keyDerivationMap.put(extendedPublicKey, keyDerivation);
|
||||
keyChildDerivationMap.put(extendedPublicKey, childDerivationPath);
|
||||
}
|
||||
|
||||
return keys;
|
||||
return new OutputDescriptor(script, multisigThreshold, keyDerivationMap, keyChildDerivationMap);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
|
@ -235,10 +301,13 @@ public class OutputDescriptor {
|
|||
joiner.add(Integer.toString(multisigThreshold));
|
||||
for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) {
|
||||
joiner.add(pubKey.toString());
|
||||
joiner.add(mapChildrenDerivations.get(pubKey));
|
||||
}
|
||||
builder.append(joiner.toString());
|
||||
} else {
|
||||
builder.append(getSingletonExtendedPublicKey());
|
||||
ExtendedPublicKey extendedPublicKey = getSingletonExtendedPublicKey();
|
||||
builder.append(extendedPublicKey);
|
||||
builder.append(mapChildrenDerivations.get(extendedPublicKey));
|
||||
}
|
||||
|
||||
builder.append(")");
|
||||
|
|
|
@ -221,7 +221,7 @@ public class PSBT {
|
|||
case PSBT_GLOBAL_BIP32_PUBKEY:
|
||||
entry.checkOneBytePlusXpubKey();
|
||||
KeyDerivation keyDerivation = parseKeyDerivation(entry.getData());
|
||||
ExtendedPublicKey pubKey = ExtendedPublicKey.fromDescriptor(Base58.encodeChecked(entry.getKeyData()), null);
|
||||
ExtendedPublicKey pubKey = ExtendedPublicKey.fromDescriptor(Base58.encodeChecked(entry.getKeyData()));
|
||||
this.extendedPublicKeys.put(pubKey, keyDerivation);
|
||||
log.debug("Pubkey with master fingerprint " + keyDerivation.getMasterFingerprint() + " at path " + keyDerivation.getDerivationPath() + ": " + pubKey.getExtendedPublicKey());
|
||||
break;
|
||||
|
|
|
@ -8,7 +8,7 @@ public class OutputDescriptorTest {
|
|||
@Test
|
||||
public void electrumP2PKH() {
|
||||
OutputDescriptor descriptor = OutputDescriptor.getOutputDescriptor("xpub661MyMwAqRbcFT5HwyRoP5hebbeRDvy2RGDTH2uxFyDPaf5FLtu4njuishddViQxTABZKzoWKuwpy6MsgfPvTw9pKnRGDL5eBxDej9kF54Z");
|
||||
Assert.assertEquals("pkh(xpub6BemYiVEULcbpkxh3wp6KUzfzGPFL7JNcxbfQcXxGnJ6sPugTkR69neX8RT9iXdMHFV1FCge72a21WpoHjgoeBTcZju3JKyFf9DztGT2FhE/0/*)", descriptor.toString());
|
||||
Assert.assertEquals("pkh(xpub661MyMwAqRbcFT5HwyRoP5hebbeRDvy2RGDTH2uxFyDPaf5FLtu4njuishddViQxTABZKzoWKuwpy6MsgfPvTw9pKnRGDL5eBxDej9kF54Z/0/*)", descriptor.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -20,7 +20,7 @@ public class OutputDescriptorTest {
|
|||
@Test
|
||||
public void electrumP2WPKH() {
|
||||
OutputDescriptor descriptor = OutputDescriptor.getOutputDescriptor("zpub6njbcfTHEfK4U96Z8dBaTULdb1LGWMtj73yYZ76kfmE9nuf3KhNSsXfzDefz5KV6TreWjnQbgvnSmSttudzTugesV2HFunYu7gWYJUD4eoR");
|
||||
Assert.assertEquals("wpkh(xpub6CqLiu9VMua6V5yFXtXrfZgJqWsG2a8dQdBuk34KFdCCYXvCtx41CmWugPJVZNzBXyHCWy8uHgVUMpePCxh2S3VXueYG8dWLDh49dQ9MJGu/0/*)", descriptor.toString());
|
||||
Assert.assertEquals("wpkh(xpub69551L7SwJE6mYiKTucL3J9dF53Nd7ujGpw6zKJyukUPgi2apP3KdQMiBEkp5WBFeaQuEqDUmc5LzsfmUFASKDHfkLtQjxuvaEPFXNDF4Kg/0/*)", descriptor.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -44,7 +44,7 @@ public class OutputDescriptorTest {
|
|||
@Test
|
||||
public void masterP2PKH() {
|
||||
OutputDescriptor descriptor = OutputDescriptor.getOutputDescriptor("pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)");
|
||||
Assert.assertEquals("pkh(xpub6CY2xt3vG5BhUS7krcphJprmHCh3jHYB1A8bxtJocU8NyQttKUCLp5izorV1wdXbp7XSSEcaFiKzUroEAL5tD1de8iAUeHP76byTWZu79SD/1/*)", descriptor.toString());
|
||||
Assert.assertEquals("pkh(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)", descriptor.toString());
|
||||
ExtendedPublicKey extendedPublicKey = descriptor.getSingletonExtendedPublicKey();
|
||||
KeyDerivation derivation = descriptor.getKeyDerivation(extendedPublicKey);
|
||||
Assert.assertEquals("d34db33f", derivation.getMasterFingerprint());
|
||||
|
|
Loading…
Reference in a new issue