Skip to content

Commit b36c116

Browse files
Policy to single-leaf TapTree compilation done
1 parent 6d920ed commit b36c116

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

src/policy/compiler.rs

+31-12
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use std::convert::From;
2222
use std::marker::PhantomData;
2323
use std::{cmp, error, f64, fmt, mem};
2424

25+
use miniscript::context::SigType;
2526
use miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
2627
use miniscript::types::{self, ErrorKind, ExtData, Property, Type};
2728
use miniscript::ScriptContext;
@@ -994,18 +995,25 @@ where
994995
})
995996
.collect();
996997

997-
if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG {
998-
insert_wrap!(AstElemExt::terminal(Terminal::Multi(k, key_vec)));
999-
}
1000-
// Not a threshold, it's always more optimal to translate it to and()s as we save the
1001-
// resulting threshold check (N EQUAL) in any case.
1002-
else if k == subs.len() {
1003-
let mut policy = subs.first().expect("No sub policy in thresh() ?").clone();
1004-
for sub in &subs[1..] {
1005-
policy = Concrete::And(vec![sub.clone(), policy]);
998+
match Ctx::sig_type() {
999+
SigType::Schnorr => {
1000+
insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, key_vec)))
1001+
}
1002+
_ => {
1003+
if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG {
1004+
insert_wrap!(AstElemExt::terminal(Terminal::Multi(k, key_vec)));
1005+
}
1006+
// Not a threshold, it's always more optimal to translate it to and()s as we save the
1007+
// resulting threshold check (N EQUAL) in any case.
1008+
else if k == subs.len() {
1009+
let mut policy = subs.first().expect("No sub policy in thresh() ?").clone();
1010+
for sub in &subs[1..] {
1011+
policy = Concrete::And(vec![sub.clone(), policy]);
1012+
}
1013+
1014+
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
1015+
}
10061016
}
1007-
1008-
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
10091017
}
10101018

10111019
// FIXME: Should we also optimize thresh(1, subs) ?
@@ -1168,7 +1176,7 @@ mod tests {
11681176

11691177
use miniscript::{satisfy, Legacy, Segwitv0};
11701178
use policy::Liftable;
1171-
use script_num_size;
1179+
use {script_num_size, Tap};
11721180

11731181
type SPolicy = Concrete<String>;
11741182
type BPolicy = Concrete<bitcoin::PublicKey>;
@@ -1554,6 +1562,17 @@ mod tests {
15541562
))
15551563
);
15561564
}
1565+
1566+
#[test]
1567+
fn compile_tr_thresh() {
1568+
for k in 1..4 {
1569+
let small_thresh: Concrete<String> =
1570+
policy_str!("{}", &format!("thresh({},pk(B),pk(C),pk(D))", k));
1571+
let small_thresh_ms: Miniscript<String, Tap> = small_thresh.compile().unwrap();
1572+
let small_thresh_ms_expected: Miniscript<String, Tap> = ms_str!("multi_a({},B,C,D)", k);
1573+
assert_eq!(small_thresh_ms, small_thresh_ms_expected);
1574+
}
1575+
}
15571576
}
15581577

15591578
#[cfg(all(test, feature = "unstable"))]

src/policy/concrete.rs

+29-7
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,12 @@ use expression::{self, FromTree};
2626
use miniscript::limits::{HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG};
2727
use miniscript::types::extra_props::TimeLockInfo;
2828
#[cfg(feature = "compiler")]
29-
use miniscript::ScriptContext;
30-
#[cfg(feature = "compiler")]
31-
use policy::compiler;
32-
#[cfg(feature = "compiler")]
33-
use policy::compiler::CompilerError;
34-
#[cfg(feature = "compiler")]
35-
use Miniscript;
29+
use {
30+
descriptor::TapTree, miniscript::ScriptContext, policy::compiler,
31+
policy::compiler::CompilerError, std::sync::Arc, Descriptor, Miniscript, Tap,
32+
};
3633
use {Error, ForEach, ForEachKey, MiniscriptKey};
34+
3735
/// Concrete policy which corresponds directly to a Miniscript structure,
3836
/// and whose disjunctions are annotated with satisfaction probabilities
3937
/// to assist the compiler
@@ -128,6 +126,30 @@ impl fmt::Display for PolicyError {
128126
}
129127

130128
impl<Pk: MiniscriptKey> Policy<Pk> {
129+
/// Single-Node compilation
130+
#[cfg(feature = "compiler")]
131+
fn compile_leaf_taptree(&self) -> Result<TapTree<Pk>, Error> {
132+
let compilation = self.compile::<Tap>().unwrap();
133+
Ok(TapTree::Leaf(Arc::new(compilation)))
134+
}
135+
136+
/// Extract the Taproot internal_key from policy tree.
137+
#[cfg(feature = "compiler")]
138+
fn extract_key(&self, unspendable_key: Option<Pk>) -> Result<(Pk, &Policy<Pk>), Error> {
139+
match unspendable_key {
140+
Some(key) => Ok((key, self)),
141+
None => Err(errstr("No internal key found")),
142+
}
143+
}
144+
145+
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
146+
#[cfg(feature = "compiler")]
147+
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, Error> {
148+
let (internal_key, policy) = self.extract_key(unspendable_key)?;
149+
let tree = Descriptor::new_tr(internal_key, Some(policy.compile_leaf_taptree()?))?;
150+
Ok(tree)
151+
}
152+
131153
/// Compile the descriptor into an optimized `Miniscript` representation
132154
#[cfg(feature = "compiler")]
133155
pub fn compile<Ctx: ScriptContext>(&self) -> Result<Miniscript<Pk, Ctx>, CompilerError> {

src/policy/mod.rs

+22
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,14 @@ mod tests {
233233
Concrete, Liftable, Semantic,
234234
};
235235
use bitcoin;
236+
#[cfg(feature = "compiler")]
237+
use descriptor::TapTree;
236238
use std::str::FromStr;
239+
#[cfg(feature = "compiler")]
240+
use std::sync::Arc;
237241
use DummyKey;
242+
#[cfg(feature = "compiler")]
243+
use {Descriptor, Tap};
238244

239245
type ConcretePol = Concrete<DummyKey>;
240246
type SemanticPol = Semantic<DummyKey>;
@@ -364,4 +370,20 @@ mod tests {
364370
ms_str.lift().unwrap()
365371
);
366372
}
373+
374+
#[test]
375+
#[cfg(feature = "compiler")]
376+
fn single_leaf_tr_compile() {
377+
for k in 1..5 {
378+
let unspendable_key: String = "z".to_string();
379+
let policy: Concrete<String> = policy_str!("thresh({},pk(A),pk(B),pk(C),pk(D))", k);
380+
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
381+
382+
let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a({},A,B,C,D)", k);
383+
let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
384+
let expected_descriptor = Descriptor::new_tr(unspendable_key, Some(tree)).unwrap();
385+
386+
assert_eq!(descriptor, expected_descriptor);
387+
}
388+
}
367389
}

0 commit comments

Comments
 (0)