diff --git a/src/main/java/com/sparrowwallet/drongo/ExtendedPublicKey.java b/src/main/java/com/sparrowwallet/drongo/ExtendedPublicKey.java index f7f7a13..3f20950 100644 --- a/src/main/java/com/sparrowwallet/drongo/ExtendedPublicKey.java +++ b/src/main/java/com/sparrowwallet/drongo/ExtendedPublicKey.java @@ -16,16 +16,13 @@ public class ExtendedPublicKey { private static final int bip32HeaderP2WHSHPub = 0x2AA7ED3; // The 4 byte header that serializes in base58 to "Zpub" private static final int bip32HeaderTestnetPub = 0x43587CF; // The 4 byte header that serializes in base58 to "tpub" - private KeyDerivation keyDerivation; - private byte[] parentFingerprint; - private DeterministicKey pubKey; - private String childDerivationPath; - private ChildNumber pubKeyChildNumber; + private final byte[] parentFingerprint; + private final DeterministicKey pubKey; + private final String childDerivationPath; + private final ChildNumber pubKeyChildNumber; + private final DeterministicHierarchy hierarchy; - private DeterministicHierarchy hierarchy; - - public ExtendedPublicKey(String masterFingerprint, byte[] parentFingerprint, String keyDerivationPath, DeterministicKey pubKey, String childDerivationPath, ChildNumber pubKeyChildNumber) { - this.keyDerivation = new KeyDerivation(masterFingerprint, keyDerivationPath); + public ExtendedPublicKey(DeterministicKey pubKey, byte[] parentFingerprint, String childDerivationPath, ChildNumber pubKeyChildNumber) { this.parentFingerprint = parentFingerprint; this.pubKey = pubKey; this.childDerivationPath = childDerivationPath; @@ -34,10 +31,6 @@ public class ExtendedPublicKey { this.hierarchy = new DeterministicHierarchy(pubKey); } - public String getMasterFingerprint() { - return keyDerivation.getMasterFingerprint(); - } - public byte[] getParentFingerprint() { return parentFingerprint; } @@ -46,14 +39,6 @@ public class ExtendedPublicKey { return pubKey.getFingerprint(); } - public String getKeyDerivationPath() { - return keyDerivation.getDerivationPath(); - } - - public List getKeyDerivation() { - return keyDerivation.getParsedDerivationPath(); - } - public DeterministicKey getPubKey() { return pubKey; } @@ -139,7 +124,7 @@ public class ExtendedPublicKey { return buffer.array(); } - public static ExtendedPublicKey fromDescriptor(String masterFingerprint, String keyDerivationPath, String extPubKey, String childDerivationPath) { + public static ExtendedPublicKey fromDescriptor(String extPubKey, String childDerivationPath) { byte[] serializedKey = Base58.decodeChecked(extPubKey); ByteBuffer buffer = ByteBuffer.wrap(serializedKey); int header = buffer.getInt(); @@ -177,7 +162,17 @@ public class ExtendedPublicKey { } DeterministicKey pubKey = new DeterministicKey(path, chainCode, new LazyECPoint(ECKey.CURVE.getCurve(), data), depth, parentFingerprint); - return new ExtendedPublicKey(masterFingerprint, parentFingerprint, keyDerivationPath, pubKey, childDerivationPath, childNumber); + return new ExtendedPublicKey(pubKey, parentFingerprint, childDerivationPath, childNumber); + } + + public static boolean isValid(String extPubKey) { + try { + ExtendedPublicKey.fromDescriptor(extPubKey, null); + } catch (Exception e) { + return false; + } + + return true; } @Override diff --git a/src/main/java/com/sparrowwallet/drongo/KeyDerivation.java b/src/main/java/com/sparrowwallet/drongo/KeyDerivation.java index 8a7022b..9d76c0a 100644 --- a/src/main/java/com/sparrowwallet/drongo/KeyDerivation.java +++ b/src/main/java/com/sparrowwallet/drongo/KeyDerivation.java @@ -3,15 +3,18 @@ package com.sparrowwallet.drongo; import com.sparrowwallet.drongo.crypto.ChildNumber; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class KeyDerivation { - private String masterFingerprint; - private String derivationPath; + private final String masterFingerprint; + private final String derivationPath; + private final List derivation; public KeyDerivation(String masterFingerprint, String derivationPath) { this.masterFingerprint = masterFingerprint; this.derivationPath = derivationPath; + this.derivation = parsePath(derivationPath); } public String getMasterFingerprint() { @@ -22,8 +25,8 @@ public class KeyDerivation { return derivationPath; } - public List getParsedDerivationPath() { - return parsePath(derivationPath); + public List getDerivation() { + return Collections.unmodifiableList(derivation); } public static List parsePath(String path) { @@ -31,9 +34,12 @@ public class KeyDerivation { } public static List parsePath(String path, int wildcardReplacement) { - String[] parsedNodes = path.replace("M", "").replace("m", "").split("/"); List nodes = new ArrayList<>(); + if(path == null) { + return nodes; + } + String[] parsedNodes = path.replace("M", "").replace("m", "").split("/"); for (String n : parsedNodes) { n = n.replaceAll(" ", ""); if (n.length() == 0) continue; @@ -57,6 +63,16 @@ public class KeyDerivation { return path; } + public static boolean isValid(String derivationPath) { + try { + parsePath(derivationPath); + } catch (Exception e) { + return false; + } + + return true; + } + public String toString() { return masterFingerprint + (derivationPath != null ? derivationPath.replace("m", "") : ""); } diff --git a/src/main/java/com/sparrowwallet/drongo/OutputDescriptor.java b/src/main/java/com/sparrowwallet/drongo/OutputDescriptor.java index c949262..01007ad 100644 --- a/src/main/java/com/sparrowwallet/drongo/OutputDescriptor.java +++ b/src/main/java/com/sparrowwallet/drongo/OutputDescriptor.java @@ -8,10 +8,7 @@ import com.sparrowwallet.drongo.protocol.ScriptChunk; import com.sparrowwallet.drongo.protocol.ScriptOpCodes; import com.sparrowwallet.drongo.protocol.ScriptType; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.StringJoiner; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,26 +17,30 @@ public class OutputDescriptor { private static final Pattern MULTI_PATTERN = Pattern.compile("multi\\(([\\d+])"); private static final Pattern KEY_ORIGIN_PATTERN = Pattern.compile("\\[([a-f0-9]+)([/\\d']+)\\]"); - private String script; - private int multisigThreshold; - private List extendedPublicKeys; + private final String script; + private final int multisigThreshold; + private final Map extendedPublicKeys; - public OutputDescriptor(String script, ExtendedPublicKey extendedPublicKey) { - this(script, Collections.singletonList(extendedPublicKey)); + public OutputDescriptor(String script, ExtendedPublicKey extendedPublicKey, KeyDerivation keyDerivation) { + this(script, Collections.singletonMap(extendedPublicKey, keyDerivation)); } - public OutputDescriptor(String script, List extendedPublicKeys) { + public OutputDescriptor(String script, Map extendedPublicKeys) { this(script, 0, extendedPublicKeys); } - public OutputDescriptor(String script, int multisigThreshold, List extendedPublicKeys) { + public OutputDescriptor(String script, int multisigThreshold, Map extendedPublicKeys) { this.script = script; this.multisigThreshold = multisigThreshold; this.extendedPublicKeys = extendedPublicKeys; } - public List getExtendedPublicKeys() { - return extendedPublicKeys; + public Set getExtendedPublicKeys() { + return Collections.unmodifiableSet(extendedPublicKeys.keySet()); + } + + public KeyDerivation getKeyDerivation(ExtendedPublicKey extendedPublicKey) { + return extendedPublicKeys.get(extendedPublicKey); } public boolean isMultisig() { @@ -51,7 +52,7 @@ public class OutputDescriptor { throw new IllegalStateException("Output descriptor contains multiple public keys but singleton requested"); } - return extendedPublicKeys.get(0); + return extendedPublicKeys.keySet().iterator().next(); } public String getScript() { @@ -59,7 +60,7 @@ public class OutputDescriptor { } public boolean describesMultipleAddresses() { - for(ExtendedPublicKey pubKey : extendedPublicKeys) { + for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) { if(!pubKey.describesMultipleAddresses()) { return false; } @@ -70,7 +71,7 @@ public class OutputDescriptor { public List getChildDerivation() { List lastDerivation = null; - for(ExtendedPublicKey pubKey : extendedPublicKeys) { + for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) { List derivation = pubKey.getChildDerivation(); 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"); @@ -146,7 +147,7 @@ public class OutputDescriptor { List chunks = new ArrayList<>(); chunks.add(new ScriptChunk(Script.encodeToOpN(multisigThreshold), null)); - for(ExtendedPublicKey pubKey : extendedPublicKeys) { + for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) { List keyPath = null; if(path.get(0).num() == 0) { keyPath = pubKey.getReceivingDerivation(path.get(1).num()); @@ -193,8 +194,8 @@ public class OutputDescriptor { } } - private static List getExtendedPublicKeys(String descriptor) { - List keys = new ArrayList<>(); + private static Map getExtendedPublicKeys(String descriptor) { + Map keys = new LinkedHashMap<>(); Matcher matcher = XPUB_PATTERN.matcher(descriptor); while(matcher.find()) { String masterFingerprint = null; @@ -216,8 +217,9 @@ public class OutputDescriptor { childDerivationPath = matcher.group(3); } - ExtendedPublicKey extendedPublicKey = ExtendedPublicKey.fromDescriptor(masterFingerprint, keyDerivationPath, extPubKey, childDerivationPath); - keys.add(extendedPublicKey); + KeyDerivation keyDerivation = new KeyDerivation(masterFingerprint, keyDerivationPath); + ExtendedPublicKey extendedPublicKey = ExtendedPublicKey.fromDescriptor(extPubKey, childDerivationPath); + keys.put(extendedPublicKey, keyDerivation); } return keys; @@ -231,7 +233,7 @@ public class OutputDescriptor { if(isMultisig()) { StringJoiner joiner = new StringJoiner(","); joiner.add(Integer.toString(multisigThreshold)); - for(ExtendedPublicKey pubKey : extendedPublicKeys) { + for(ExtendedPublicKey pubKey : extendedPublicKeys.keySet()) { joiner.add(pubKey.toString()); } builder.append(joiner.toString()); diff --git a/src/main/java/com/sparrowwallet/drongo/policy/Miniscript.java b/src/main/java/com/sparrowwallet/drongo/policy/Miniscript.java new file mode 100644 index 0000000..ff46930 --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/policy/Miniscript.java @@ -0,0 +1,38 @@ +package com.sparrowwallet.drongo.policy; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Miniscript { + private static final Pattern SINGLE_PATTERN = Pattern.compile("pkh?\\("); + private static final Pattern MULTI_PATTERN = Pattern.compile("multi\\(([\\d+])"); + + private String script; + + public Miniscript(String script) { + this.script = script; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + public int getNumSignaturesRequired() { + Matcher singleMatcher = SINGLE_PATTERN.matcher(script); + if(singleMatcher.find()) { + return 1; + } + + Matcher multiMatcher = MULTI_PATTERN.matcher(script); + if(multiMatcher.find()) { + String threshold = multiMatcher.group(1); + return Integer.parseInt(threshold); + } else { + throw new IllegalArgumentException("Could not find multisig threshold in " + this); + } + } +} diff --git a/src/main/java/com/sparrowwallet/drongo/policy/Policy.java b/src/main/java/com/sparrowwallet/drongo/policy/Policy.java index a60007d..350100c 100644 --- a/src/main/java/com/sparrowwallet/drongo/policy/Policy.java +++ b/src/main/java/com/sparrowwallet/drongo/policy/Policy.java @@ -1,35 +1,56 @@ package com.sparrowwallet.drongo.policy; import com.sparrowwallet.drongo.protocol.ScriptType; +import com.sparrowwallet.drongo.wallet.Keystore; + +import java.util.List; import static com.sparrowwallet.drongo.protocol.ScriptType.*; import static com.sparrowwallet.drongo.policy.PolicyType.*; public class Policy { - private String policy; + private static final String DEFAULT_NAME = "Default"; - public Policy(String policy) { - this.policy = policy; + private String name; + private Miniscript miniscript; + + public Policy(Miniscript miniscript) { + this(DEFAULT_NAME, miniscript); } - public String getPolicy() { - return policy; + public Policy(String name, Miniscript miniscript) { + this.name = name; + this.miniscript = miniscript; } - public void setPolicy(String policy) { - this.policy = policy; + public Miniscript getMiniscript() { + return miniscript; } - public static Policy getPolicy(PolicyType policyType, ScriptType scriptType, Integer threshold, Integer numCosigners) { + public void setMiniscript(Miniscript miniscript) { + this.miniscript = miniscript; + } + + public int getNumSignaturesRequired() { + return getMiniscript().getNumSignaturesRequired(); + } + + public static Policy getPolicy(PolicyType policyType, ScriptType scriptType, List keystores, Integer threshold) { if(SINGLE.equals(policyType)) { if(P2PK.equals(scriptType)) { - return new Policy("pk()"); + return new Policy(new Miniscript("pk(" + keystores.get(0).getScriptName() + ")")); } - return new Policy("pkh()"); + return new Policy(new Miniscript("pkh(" + keystores.get(0).getScriptName() + ")")); } if(MULTI.equals(policyType)) { - return new Policy("multi(,,)"); + StringBuilder builder = new StringBuilder("multi("); + builder.append(threshold); + for(Keystore keystore : keystores) { + builder.append(",").append(keystore.getScriptName()); + } + builder.append(")"); + return new Policy(new Miniscript(builder.toString())); } throw new PolicyException("No standard policy for " + policyType + " policy with script type " + scriptType); diff --git a/src/main/java/com/sparrowwallet/drongo/protocol/ScriptType.java b/src/main/java/com/sparrowwallet/drongo/protocol/ScriptType.java index 99bcf9b..d0bcc25 100644 --- a/src/main/java/com/sparrowwallet/drongo/protocol/ScriptType.java +++ b/src/main/java/com/sparrowwallet/drongo/protocol/ScriptType.java @@ -6,14 +6,16 @@ import com.sparrowwallet.drongo.policy.PolicyType; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import static com.sparrowwallet.drongo.policy.PolicyType.*; import static com.sparrowwallet.drongo.protocol.Script.decodeFromOpN; import static com.sparrowwallet.drongo.protocol.ScriptOpCodes.*; public enum ScriptType { - P2PK("P2PK", new PolicyType[]{SINGLE}) { + P2PK("P2PK") { @Override public Address getAddress(byte[] pubKey) { return new P2PKAddress(pubKey); @@ -59,8 +61,13 @@ public enum ScriptType { public ECKey getPublicKeyFromScript(Script script) { return ECKey.fromPublicOnly(script.chunks.get(0).data); } + + @Override + public List getAllowedPolicyTypes() { + return List.of(SINGLE); + } }, - P2PKH("P2PKH", new PolicyType[]{SINGLE}) { + P2PKH("P2PKH") { @Override public Address getAddress(byte[] pubKeyHash) { return new P2PKHAddress(pubKeyHash); @@ -103,8 +110,13 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return script.chunks.get(2).data; } + + @Override + public List getAllowedPolicyTypes() { + return List.of(SINGLE); + } }, - MULTISIG("Bare Multisig", new PolicyType[]{MULTI}) { + MULTISIG("Bare Multisig") { @Override public Address getAddress(byte[] bytes) { throw new ProtocolException("No single address for multisig script type"); @@ -178,8 +190,13 @@ public enum ScriptType { public int getThreshold(Script script) { return decodeFromOpN(script.chunks.get(0).opcode); } + + @Override + public List getAllowedPolicyTypes() { + return List.of(MULTI); + } }, - P2SH("P2SH", new PolicyType[]{MULTI}) { + P2SH("P2SH") { @Override public Address getAddress(byte[] bytes) { return new P2SHAddress(bytes); @@ -224,8 +241,13 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return script.chunks.get(1).data; } + + @Override + public List getAllowedPolicyTypes() { + return List.of(MULTI); + } }, - P2SH_P2WPKH("P2SH-P2WPKH", new PolicyType[]{SINGLE}) { + P2SH_P2WPKH("P2SH-P2WPKH") { @Override public Address getAddress(byte[] bytes) { return P2SH.getAddress(bytes); @@ -245,8 +267,13 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return P2SH.getHashFromScript(script); } + + @Override + public List getAllowedPolicyTypes() { + return List.of(SINGLE); + } }, - P2SH_P2WSH("P2SH-P2WSH", new PolicyType[]{MULTI, CUSTOM}) { + P2SH_P2WSH("P2SH-P2WSH") { @Override public Address getAddress(byte[] bytes) { return P2SH.getAddress(bytes); @@ -266,8 +293,13 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return P2SH.getHashFromScript(script); } + + @Override + public List getAllowedPolicyTypes() { + return List.of(MULTI, CUSTOM); + } }, - P2WPKH("P2WPKH", new PolicyType[]{SINGLE}) { + P2WPKH("P2WPKH") { @Override public Address getAddress(byte[] bytes) { return new P2WPKHAddress(bytes); @@ -301,8 +333,13 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return script.chunks.get(1).data; } + + @Override + public List getAllowedPolicyTypes() { + return List.of(SINGLE); + } }, - P2WSH("P2WSH", new PolicyType[]{MULTI, CUSTOM}) { + P2WSH("P2WSH") { @Override public Address getAddress(byte[] bytes) { return new P2WSHAddress(bytes); @@ -336,22 +373,27 @@ public enum ScriptType { public byte[] getHashFromScript(Script script) { return script.chunks.get(1).data; } + + @Override + public List getAllowedPolicyTypes() { + return List.of(MULTI, CUSTOM); + } }; private final String name; - private final PolicyType[] allowedPolicyTypes; - ScriptType(String name, PolicyType[] allowedPolicyTypes) { + ScriptType(String name) { this.name = name; - this.allowedPolicyTypes = allowedPolicyTypes; } public String getName() { return name; } - public PolicyType[] getAllowedPolicyTypes() { - return allowedPolicyTypes; + public abstract List getAllowedPolicyTypes(); + + public boolean isAllowed(PolicyType policyType) { + return getAllowedPolicyTypes().contains(policyType); } public abstract Address getAddress(byte[] bytes); @@ -380,6 +422,10 @@ public enum ScriptType { public static final ScriptType[] SINGLE_HASH_TYPES = {P2PKH, P2SH, P2SH_P2WPKH, P2SH_P2WSH, P2WPKH, P2WSH}; + public static List getScriptTypesForPolicyType(PolicyType policyType) { + return Arrays.stream(values()).filter(scriptType -> scriptType.isAllowed(policyType)).collect(Collectors.toList()); + } + @Override public String toString() { return name; diff --git a/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java b/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java index ad8a737..ac302b2 100644 --- a/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java +++ b/src/main/java/com/sparrowwallet/drongo/psbt/PSBT.java @@ -221,9 +221,9 @@ public class PSBT { case PSBT_GLOBAL_BIP32_PUBKEY: entry.checkOneBytePlusXpubKey(); KeyDerivation keyDerivation = parseKeyDerivation(entry.getData()); - ExtendedPublicKey pubKey = ExtendedPublicKey.fromDescriptor(keyDerivation.getMasterFingerprint(), keyDerivation.getDerivationPath(), Base58.encodeChecked(entry.getKeyData()), null); + ExtendedPublicKey pubKey = ExtendedPublicKey.fromDescriptor(Base58.encodeChecked(entry.getKeyData()), null); this.extendedPublicKeys.put(pubKey, keyDerivation); - log.debug("Pubkey with master fingerprint " + pubKey.getMasterFingerprint() + " at path " + pubKey.getKeyDerivationPath() + ": " + pubKey.getExtendedPublicKey()); + log.debug("Pubkey with master fingerprint " + keyDerivation.getMasterFingerprint() + " at path " + keyDerivation.getDerivationPath() + ": " + pubKey.getExtendedPublicKey()); break; case PSBT_GLOBAL_VERSION: entry.checkOneByteKey(); diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java index c73ea35..e1eb9f6 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Keystore.java @@ -4,6 +4,39 @@ import com.sparrowwallet.drongo.ExtendedPublicKey; import com.sparrowwallet.drongo.KeyDerivation; public class Keystore { + private String label; private KeyDerivation keyDerivation; private ExtendedPublicKey extendedPublicKey; + + public Keystore(String label) { + this.label = label; + } + + public String getLabel() { + return label; + } + + public String getScriptName() { + return label.replace(" ", "").toLowerCase(); + } + + public void setLabel(String label) { + this.label = label; + } + + public KeyDerivation getKeyDerivation() { + return keyDerivation; + } + + public void setKeyDerivation(KeyDerivation keyDerivation) { + this.keyDerivation = keyDerivation; + } + + public ExtendedPublicKey getExtendedPublicKey() { + return extendedPublicKey; + } + + public void setExtendedPublicKey(ExtendedPublicKey extendedPublicKey) { + this.extendedPublicKey = extendedPublicKey; + } } diff --git a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java index 27e7908..c777dbf 100644 --- a/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java +++ b/src/main/java/com/sparrowwallet/drongo/wallet/Wallet.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.wallet; +import com.sparrowwallet.drongo.policy.Policy; import com.sparrowwallet.drongo.policy.PolicyType; import com.sparrowwallet.drongo.protocol.ScriptType; @@ -9,7 +10,7 @@ import java.util.List; public class Wallet { private PolicyType policyType; private ScriptType scriptType; - private String policy; + private Policy defaultPolicy; private List keystores = new ArrayList<>(); public PolicyType getPolicyType() { @@ -28,12 +29,12 @@ public class Wallet { this.scriptType = scriptType; } - public String getPolicy() { - return policy; + public Policy getDefaultPolicy() { + return defaultPolicy; } - public void setPolicy(String policy) { - this.policy = policy; + public void setDefaultPolicy(Policy defaultPolicy) { + this.defaultPolicy = defaultPolicy; } public List getKeystores() { diff --git a/src/test/java/com/sparrowwallet/drongo/OutputDescriptorTest.java b/src/test/java/com/sparrowwallet/drongo/OutputDescriptorTest.java index f5b8610..a0da296 100644 --- a/src/test/java/com/sparrowwallet/drongo/OutputDescriptorTest.java +++ b/src/test/java/com/sparrowwallet/drongo/OutputDescriptorTest.java @@ -45,7 +45,9 @@ public class OutputDescriptorTest { public void masterP2PKH() { OutputDescriptor descriptor = OutputDescriptor.getOutputDescriptor("pkh([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/*)"); Assert.assertEquals("pkh(xpub6CY2xt3vG5BhUS7krcphJprmHCh3jHYB1A8bxtJocU8NyQttKUCLp5izorV1wdXbp7XSSEcaFiKzUroEAL5tD1de8iAUeHP76byTWZu79SD/1/*)", descriptor.toString()); - Assert.assertEquals("d34db33f", descriptor.getSingletonExtendedPublicKey().getMasterFingerprint()); - Assert.assertEquals("m/44'/0'/0'", descriptor.getSingletonExtendedPublicKey().getKeyDerivationPath()); + ExtendedPublicKey extendedPublicKey = descriptor.getSingletonExtendedPublicKey(); + KeyDerivation derivation = descriptor.getKeyDerivation(extendedPublicKey); + Assert.assertEquals("d34db33f", derivation.getMasterFingerprint()); + Assert.assertEquals("m/44'/0'/0'", derivation.getDerivationPath()); } } diff --git a/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java b/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java index 05fbe9a..0e520d5 100644 --- a/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java +++ b/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java @@ -1,5 +1,7 @@ package com.sparrowwallet.drongo.psbt; +import com.sparrowwallet.drongo.ExtendedPublicKey; +import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.protocol.NonStandardScriptException; import com.sparrowwallet.drongo.protocol.Transaction; import org.bouncycastle.util.encoders.Hex; @@ -214,8 +216,10 @@ public class PSBTTest { String psbt = "cHNidP8BAJ0BAAAAAnEOp2q0XFy2Q45gflnMA3YmmBgFrp4N/ZCJASq7C+U1AQAAAAD/////GQmU1qizyMgsy8+y+6QQaqBmObhyqNRHRlwNQliNbWcAAAAAAP////8CAOH1BQAAAAAZdqkUtrwsDuVlWoQ9ea/t0MzD991kNAmIrGBa9AUAAAAAFgAUEYjvjkzgRJ6qyPsUHL9aEXbmoIgAAAAATwEEiLIeA55TDKyAAAAAPbyKXJdp8DGxfnf+oVGGAyIaGP0Y8rmlTGyMGsdcvDUC8jBYSxVdHH8c1FEgplPEjWULQxtnxbLBPyfXFCA3wWkQJ1acUDEAAIAAAACAAAAAgAABAR8A4fUFAAAAABYAFDO5gvkbKPFgySC0q5XljOUN2jpKIgIDMJaA8zx9446mpHzU7NZvH1pJdHxv+4gI7QkDkkPjrVxHMEQCIC1wTO2DDFapCTRL10K2hS3M0QPpY7rpLTjnUlTSu0JFAiAthsQ3GV30bAztoITyopHD2i1kBw92v5uQsZXn7yj3cgEiBgMwloDzPH3jjqakfNTs1m8fWkl0fG/7iAjtCQOSQ+OtXBgnVpxQMQAAgAAAAIAAAACAAAAAAAEAAAAAAQEfAOH1BQAAAAAWABQ4j7lEMH63fvRRl9CwskXgefAR3iICAsd3Fh9z0LfHK57nveZQKT0T8JW8dlatH1Jdpf0uELEQRzBEAiBMsftfhpyULg4mEAV2ElQ5F5rojcqKncO6CPeVOYj6pgIgUh9JynkcJ9cOJzybFGFphZCTYeJb4nTqIA1+CIJ+UU0BIgYCx3cWH3PQt8crnue95lApPRPwlbx2Vq0fUl2l/S4QsRAYJ1acUDEAAIAAAACAAAAAgAAAAAAAAAAAAAAiAgLSDKUC7iiWhtIYFb1DqAY3sGmOH7zb5MrtRF9sGgqQ7xgnVpxQMQAAgAAAAIAAAACAAAAAAAQAAAAA"; PSBT psbt1 = PSBT.fromString(psbt); - Assert.assertEquals("27569c50", psbt1.getExtendedPublicKeys().get(0).getMasterFingerprint()); - Assert.assertEquals("m/49'/0'/0'", psbt1.getExtendedPublicKeys().get(0).getKeyDerivationPath()); + ExtendedPublicKey extendedPublicKey = psbt1.getExtendedPublicKeys().get(0); + KeyDerivation keyDerivation = psbt1.getKeyDerivation(extendedPublicKey); + Assert.assertEquals("27569c50", keyDerivation.getMasterFingerprint()); + Assert.assertEquals("m/49'/0'/0'", keyDerivation.getDerivationPath()); Assert.assertEquals(2, psbt1.getPsbtInputs().size()); }