mirror of
https://github.com/sparrowwallet/drongo.git
synced 2024-12-25 01:26:45 +00:00
introduce multipath derivations to output descriptors and use h for hardened indexes
This commit is contained in:
parent
ebf7128ae5
commit
8e49247832
3 changed files with 50 additions and 13 deletions
|
@ -68,13 +68,17 @@ public class KeyDerivation {
|
|||
}
|
||||
|
||||
public static String writePath(List<ChildNumber> pathList) {
|
||||
String path = "m";
|
||||
for (ChildNumber child: pathList) {
|
||||
path += "/";
|
||||
path += child.toString();
|
||||
return writePath(pathList, true);
|
||||
}
|
||||
|
||||
public static String writePath(List<ChildNumber> pathList, boolean useApostrophes) {
|
||||
StringBuilder path = new StringBuilder("m");
|
||||
for(ChildNumber child: pathList) {
|
||||
path.append("/");
|
||||
path.append(child.toString(useApostrophes));
|
||||
}
|
||||
|
||||
return path;
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
public static boolean isValid(String derivationPath) {
|
||||
|
|
|
@ -22,10 +22,11 @@ public class OutputDescriptor {
|
|||
private static final String INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ ";
|
||||
private static final String CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
private static final Pattern XPUB_PATTERN = Pattern.compile("(\\[[^\\]]+\\])?(.pub[^/\\,)]{100,112})(/[/\\d*'hH]+)?");
|
||||
private static final Pattern XPUB_PATTERN = Pattern.compile("(\\[[^\\]]+\\])?(.pub[^/\\,)]{100,112})(/[/\\d*'hH<>;]+)?");
|
||||
private static final Pattern PUBKEY_PATTERN = Pattern.compile("(\\[[^\\]]+\\])?(0[23][0-9a-fA-F]{32})");
|
||||
private static final Pattern MULTI_PATTERN = Pattern.compile("multi\\(([\\d+])");
|
||||
private static final Pattern KEY_ORIGIN_PATTERN = Pattern.compile("\\[([A-Fa-f0-9]{8})([/\\d'hH]+)\\]");
|
||||
private static final Pattern MULTIPATH_PATTERN = Pattern.compile("<([\\d*'hH;]+)>");
|
||||
private static final Pattern CHECKSUM_PATTERN = Pattern.compile("#([" + CHECKSUM_CHARSET + "]{8})$");
|
||||
|
||||
private final ScriptType scriptType;
|
||||
|
@ -258,12 +259,25 @@ public class OutputDescriptor {
|
|||
}
|
||||
|
||||
public static OutputDescriptor getOutputDescriptor(Wallet wallet, KeyPurpose keyPurpose, Integer index) {
|
||||
return getOutputDescriptor(wallet, keyPurpose == null ? null : List.of(keyPurpose), index);
|
||||
}
|
||||
|
||||
public static OutputDescriptor getOutputDescriptor(Wallet wallet, List<KeyPurpose> keyPurposes, Integer index) {
|
||||
Map<ExtendedKey, KeyDerivation> extendedKeyDerivationMap = new LinkedHashMap<>();
|
||||
Map<ExtendedKey, String> extendedKeyChildDerivationMap = new LinkedHashMap<>();
|
||||
for(Keystore keystore : wallet.getKeystores()) {
|
||||
extendedKeyDerivationMap.put(keystore.getExtendedPublicKey(), keystore.getKeyDerivation());
|
||||
if(keyPurpose != null) {
|
||||
extendedKeyChildDerivationMap.put(keystore.getExtendedPublicKey(), keyPurpose.getPathIndex().num() + "/" + (index == null ? "*" : index));
|
||||
if(keyPurposes != null) {
|
||||
String chain;
|
||||
if(keyPurposes.size() == 1) {
|
||||
chain = Integer.toString(keyPurposes.get(0).getPathIndex().num());
|
||||
} else {
|
||||
StringJoiner joiner = new StringJoiner(";");
|
||||
keyPurposes.forEach(keyPurpose -> joiner.add(Integer.toString(keyPurpose.getPathIndex().num())));
|
||||
chain = "<" + joiner + ">";
|
||||
}
|
||||
|
||||
extendedKeyChildDerivationMap.put(keystore.getExtendedPublicKey(), chain + "/" + (index == null ? "*" : index));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,7 +409,7 @@ public class OutputDescriptor {
|
|||
private static BigInteger polyMod(BigInteger c, int val)
|
||||
{
|
||||
byte c0 = c.shiftRight(35).byteValue();
|
||||
c = c.and(new BigInteger("7ffffffff", 16)).shiftLeft(5).or(BigInteger.valueOf(val));
|
||||
c = c.and(new BigInteger("7ffffffff", 16)).shiftLeft(5).xor(BigInteger.valueOf(val));
|
||||
|
||||
if((c0 & 1) > 0) {
|
||||
c = c.xor(new BigInteger("f5dee51989", 16));
|
||||
|
@ -461,10 +475,10 @@ public class OutputDescriptor {
|
|||
|
||||
Utils.LexicographicByteArrayComparator lexicographicByteArrayComparator = new Utils.LexicographicByteArrayComparator();
|
||||
sortedKeys.sort((o1, o2) -> {
|
||||
List<ChildNumber> derivation1 = KeyDerivation.parsePath(mapChildrenDerivations.get(o1));
|
||||
List<ChildNumber> derivation1 = getDerivations(mapChildrenDerivations.get(o1)).get(0);
|
||||
derivation1.add(0, o1.getKeyChildNumber());
|
||||
ECKey key1 = o1.getKey(derivation1);
|
||||
List<ChildNumber> derivation2 = KeyDerivation.parsePath(mapChildrenDerivations.get(o2));
|
||||
List<ChildNumber> derivation2 = getDerivations(mapChildrenDerivations.get(o2)).get(0);
|
||||
derivation2.add(0, o2.getKeyChildNumber());
|
||||
ECKey key2 = o2.getKey(derivation2);
|
||||
return lexicographicByteArrayComparator.compare(key1.getPubKey(), key2.getPubKey());
|
||||
|
@ -473,6 +487,21 @@ public class OutputDescriptor {
|
|||
return sortedKeys;
|
||||
}
|
||||
|
||||
private List<List<ChildNumber>> getDerivations(String childDerivation) {
|
||||
Matcher matcher = MULTIPATH_PATTERN.matcher(childDerivation);
|
||||
if(matcher.find()) {
|
||||
String multipath = matcher.group(1);
|
||||
String[] paths = multipath.split(";");
|
||||
List<List<ChildNumber>> derivations = new ArrayList<>();
|
||||
for(String path : paths) {
|
||||
derivations.add(KeyDerivation.parsePath(childDerivation.replace(matcher.group(), path)));
|
||||
}
|
||||
return derivations;
|
||||
} else {
|
||||
return List.of(KeyDerivation.parsePath(childDerivation));
|
||||
}
|
||||
}
|
||||
|
||||
private String toString(ExtendedKey pubKey, boolean addKeyOrigin) {
|
||||
StringBuilder keyBuilder = new StringBuilder();
|
||||
KeyDerivation keyDerivation = extendedPublicKeys.get(pubKey);
|
||||
|
@ -482,7 +511,7 @@ public class OutputDescriptor {
|
|||
keyBuilder.append(keyDerivation.getMasterFingerprint());
|
||||
keyBuilder.append("/");
|
||||
}
|
||||
keyBuilder.append(keyDerivation.getDerivationPath().replaceFirst("^m?/", ""));
|
||||
keyBuilder.append(keyDerivation.getDerivationPath().replaceFirst("^m?/", "").replace('\'', 'h'));
|
||||
keyBuilder.append("]");
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,11 @@ public class ChildNumber {
|
|||
public int i() { return i; }
|
||||
|
||||
public String toString() {
|
||||
return String.format(Locale.US, "%d%s", num(), isHardened() ? "'" : "");
|
||||
return toString(true);
|
||||
}
|
||||
|
||||
public String toString(boolean useApostrophes) {
|
||||
return String.format(Locale.US, "%d%s", num(), isHardened() ? (useApostrophes ? "'" : "h") : "");
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
|
|
Loading…
Reference in a new issue