support better script descriptors in wallet policy

This commit is contained in:
Craig Raw 2020-09-07 09:30:40 +02:00
parent 10ebfe463d
commit 3115669c46
3 changed files with 112 additions and 14 deletions

View file

@ -37,19 +37,19 @@ public class Policy {
public static Policy getPolicy(PolicyType policyType, ScriptType scriptType, List<Keystore> keystores, Integer threshold) { public static Policy getPolicy(PolicyType policyType, ScriptType scriptType, List<Keystore> keystores, Integer threshold) {
if(SINGLE.equals(policyType)) { if(SINGLE.equals(policyType)) {
if(P2PK.equals(scriptType)) { return new Policy(new Miniscript(scriptType.getDescriptor() + keystores.get(0).getScriptName() + scriptType.getCloseDescriptor()));
return new Policy(new Miniscript("pk(" + keystores.get(0).getScriptName() + ")"));
}
return new Policy(new Miniscript("pkh(" + keystores.get(0).getScriptName() + ")"));
} }
if(MULTI.equals(policyType)) { if(MULTI.equals(policyType)) {
StringBuilder builder = new StringBuilder("multi("); StringBuilder builder = new StringBuilder();
builder.append(scriptType.getDescriptor());
builder.append(MULTISIG.getDescriptor());
builder.append(threshold); builder.append(threshold);
for(Keystore keystore : keystores) { for(Keystore keystore : keystores) {
builder.append(",").append(keystore.getScriptName()); builder.append(",").append(keystore.getScriptName());
} }
builder.append(")"); builder.append(MULTISIG.getCloseDescriptor());
builder.append(scriptType.getCloseDescriptor());
return new Policy(new Miniscript(builder.toString())); return new Policy(new Miniscript(builder.toString()));
} }

View file

@ -58,7 +58,7 @@ public enum ScriptType {
@Override @Override
public String getOutputDescriptor(ECKey key) { public String getOutputDescriptor(ECKey key) {
return "pk(" + Utils.bytesToHex(key.getPubKey()) + ")"; return getDescriptor() + Utils.bytesToHex(key.getPubKey()) + getCloseDescriptor();
} }
@Override @Override
@ -66,6 +66,11 @@ public enum ScriptType {
throw new ProtocolException("No script derived output descriptor for non pay to script type"); throw new ProtocolException("No script derived output descriptor for non pay to script type");
} }
@Override
public String getDescriptor() {
return "pk(";
}
@Override @Override
public boolean isScriptType(Script script) { public boolean isScriptType(Script script) {
List<ScriptChunk> chunks = script.chunks; List<ScriptChunk> chunks = script.chunks;
@ -165,7 +170,7 @@ public enum ScriptType {
@Override @Override
public String getOutputDescriptor(ECKey key) { public String getOutputDescriptor(ECKey key) {
return "pkh(" + Utils.bytesToHex(key.getPubKey()) + ")"; return getDescriptor() + Utils.bytesToHex(key.getPubKey()) + getCloseDescriptor();
} }
@Override @Override
@ -173,6 +178,11 @@ public enum ScriptType {
throw new ProtocolException("No script derived output descriptor for non pay to script type"); throw new ProtocolException("No script derived output descriptor for non pay to script type");
} }
@Override
public String getDescriptor() {
return "pkh(";
}
@Override @Override
public boolean isScriptType(Script script) { public boolean isScriptType(Script script) {
List<ScriptChunk> chunks = script.chunks; List<ScriptChunk> chunks = script.chunks;
@ -320,7 +330,12 @@ public enum ScriptType {
joiner.add(Utils.bytesToHex(pubKey)); joiner.add(Utils.bytesToHex(pubKey));
} }
return "multi(" + threshold + "," + joiner.toString() + ")"; return getDescriptor() + threshold + "," + joiner.toString() + getCloseDescriptor();
}
@Override
public String getDescriptor() {
return "multi(";
} }
@Override @Override
@ -461,7 +476,12 @@ public enum ScriptType {
throw new IllegalArgumentException("Can only create output descriptor from multisig script"); throw new IllegalArgumentException("Can only create output descriptor from multisig script");
} }
return "sh(" + MULTISIG.getOutputDescriptor(script) + ")"; return getDescriptor() + MULTISIG.getOutputDescriptor(script) + getCloseDescriptor();
}
@Override
public String getDescriptor() {
return "sh(";
} }
@Override @Override
@ -577,7 +597,7 @@ public enum ScriptType {
@Override @Override
public String getOutputDescriptor(ECKey key) { public String getOutputDescriptor(ECKey key) {
return "sh(wpkh(" + Utils.bytesToHex(key.getPubKey()) + "))"; return getDescriptor() + Utils.bytesToHex(key.getPubKey()) + getCloseDescriptor();
} }
@Override @Override
@ -585,6 +605,11 @@ public enum ScriptType {
throw new ProtocolException("No script derived output descriptor for non pay to script type"); throw new ProtocolException("No script derived output descriptor for non pay to script type");
} }
@Override
public String getDescriptor() {
return "sh(wpkh(";
}
@Override @Override
public boolean isScriptType(Script script) { public boolean isScriptType(Script script) {
return P2SH.isScriptType(script); return P2SH.isScriptType(script);
@ -676,7 +701,12 @@ public enum ScriptType {
throw new IllegalArgumentException("Can only create output descriptor from multisig script"); throw new IllegalArgumentException("Can only create output descriptor from multisig script");
} }
return "sh(wsh(" + MULTISIG.getOutputDescriptor(script) + "))"; return getDescriptor() + MULTISIG.getOutputDescriptor(script) + getCloseDescriptor();
}
@Override
public String getDescriptor() {
return "sh(wsh(";
} }
@Override @Override
@ -765,7 +795,7 @@ public enum ScriptType {
@Override @Override
public String getOutputDescriptor(ECKey key) { public String getOutputDescriptor(ECKey key) {
return "wpkh(" + Utils.bytesToHex(key.getPubKey()) + ")"; return getDescriptor() + Utils.bytesToHex(key.getPubKey()) + getCloseDescriptor();
} }
@Override @Override
@ -773,6 +803,11 @@ public enum ScriptType {
throw new ProtocolException("No script derived output descriptor for non pay to script type"); throw new ProtocolException("No script derived output descriptor for non pay to script type");
} }
@Override
public String getDescriptor() {
return "wpkh(";
}
@Override @Override
public boolean isScriptType(Script script) { public boolean isScriptType(Script script) {
List<ScriptChunk> chunks = script.chunks; List<ScriptChunk> chunks = script.chunks;
@ -874,7 +909,12 @@ public enum ScriptType {
throw new IllegalArgumentException("Can only create output descriptor from multisig script"); throw new IllegalArgumentException("Can only create output descriptor from multisig script");
} }
return "wsh(" + MULTISIG.getOutputDescriptor(script) + ")"; return getDescriptor() + MULTISIG.getOutputDescriptor(script) + getCloseDescriptor();
}
@Override
public String getDescriptor() {
return "wsh(";
} }
@Override @Override
@ -1003,6 +1043,12 @@ public enum ScriptType {
public abstract String getOutputDescriptor(Script script); public abstract String getOutputDescriptor(Script script);
public abstract String getDescriptor();
public String getCloseDescriptor() {
return getDescriptor().chars().filter(ch -> ch == '(').boxed().map(n -> ")").collect(Collectors.joining());
}
public abstract boolean isScriptType(Script script); public abstract boolean isScriptType(Script script);
public abstract byte[] getHashFromScript(Script script); public abstract byte[] getHashFromScript(Script script);
@ -1055,6 +1101,16 @@ public enum ScriptType {
return null; return null;
} }
public static ScriptType fromDescriptor(String descriptor) {
for(ScriptType type : values()) {
if(type.getDescriptor().equals(descriptor.toLowerCase())) {
return type;
}
}
return null;
}
/** /**
* Determines the dust threshold for the given output for this script type. * Determines the dust threshold for the given output for this script type.
* *

View file

@ -0,0 +1,42 @@
package com.sparrowwallet.drongo.wallet;
import com.sparrowwallet.drongo.policy.Policy;
import com.sparrowwallet.drongo.policy.PolicyType;
import com.sparrowwallet.drongo.protocol.ScriptType;
import org.junit.Assert;
import org.junit.Test;
import java.util.List;
public class PolicyTest {
@Test
public void testMiniscriptParsing() {
Keystore keystore1 = new Keystore("Keystore 1");
Keystore keystore2 = new Keystore("Keystore 2");
Keystore keystore3 = new Keystore("Keystore 3");
Policy policy = Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2PKH, List.of(keystore1), 1);
Assert.assertEquals("pkh(keystore1)", policy.getMiniscript().toString());
Assert.assertEquals(1, policy.getNumSignaturesRequired());
Policy policy2 = Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2SH_P2WPKH, List.of(keystore1), 1);
Assert.assertEquals("sh(wpkh(keystore1))", policy2.getMiniscript().toString());
Assert.assertEquals(1, policy2.getNumSignaturesRequired());
Policy policy3 = Policy.getPolicy(PolicyType.SINGLE, ScriptType.P2WPKH, List.of(keystore1), 1);
Assert.assertEquals("wpkh(keystore1)", policy3.getMiniscript().toString());
Assert.assertEquals(1, policy3.getNumSignaturesRequired());
Policy policy4 = Policy.getPolicy(PolicyType.MULTI, ScriptType.P2SH, List.of(keystore1, keystore2, keystore3), 2);
Assert.assertEquals("sh(multi(2,keystore1,keystore2,keystore3))", policy4.getMiniscript().toString());
Assert.assertEquals(2, policy4.getNumSignaturesRequired());
Policy policy5 = Policy.getPolicy(PolicyType.MULTI, ScriptType.P2SH_P2WSH, List.of(keystore1, keystore2, keystore3), 2);
Assert.assertEquals("sh(wsh(multi(2,keystore1,keystore2,keystore3)))", policy5.getMiniscript().toString());
Assert.assertEquals(2, policy5.getNumSignaturesRequired());
Policy policy6 = Policy.getPolicy(PolicyType.MULTI, ScriptType.P2WSH, List.of(keystore1, keystore2, keystore3), 2);
Assert.assertEquals("wsh(multi(2,keystore1,keystore2,keystore3))", policy6.getMiniscript().toString());
Assert.assertEquals(2, policy6.getNumSignaturesRequired());
}
}