Skip to content

Commit 6fb1b29

Browse files
Policy to single-leaf TapTree compilation done
A new method `compile_tr` is introduced in policy for creating a Tr descriptor with TapTree containing single script child-node. An internal-key extraction method from the policy has also been implemented for the same.
1 parent de74dc0 commit 6fb1b29

File tree

3 files changed

+76
-18
lines changed

3 files changed

+76
-18
lines changed

src/policy/compiler.rs

Lines changed: 27 additions & 10 deletions
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 crate::miniscript::context::SigType;
2526
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
2627
use crate::miniscript::types::{self, ErrorKind, ExtData, Property, Type};
2728
use crate::miniscript::ScriptContext;
@@ -993,18 +994,23 @@ where
993994
})
994995
.collect();
995996

996-
if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG {
997-
insert_wrap!(AstElemExt::terminal(Terminal::Multi(k, key_vec)));
998-
}
999-
// Not a threshold, it's always more optimal to translate it to and()s as we save the
1000-
// resulting threshold check (N EQUAL) in any case.
1001-
else if k == subs.len() {
1002-
let mut policy = subs.first().expect("No sub policy in thresh() ?").clone();
1003-
for sub in &subs[1..] {
1004-
policy = Concrete::And(vec![sub.clone(), policy]);
997+
match Ctx::sig_type() {
998+
SigType::Schnorr if key_vec.len() == subs.len() => {
999+
insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, key_vec)))
1000+
}
1001+
SigType::Ecdsa
1002+
if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG =>
1003+
{
1004+
insert_wrap!(AstElemExt::terminal(Terminal::Multi(k, key_vec)))
10051005
}
1006+
_ if k == subs.len() => {
1007+
let mut it = subs.iter();
1008+
let mut policy = it.next().expect("No sub policy in thresh() ?").clone();
1009+
policy = it.fold(policy, |acc, pol| Concrete::And(vec![acc, pol.clone()]));
10061010

1007-
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
1011+
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
1012+
}
1013+
_ => {}
10081014
}
10091015

10101016
// FIXME: Should we also optimize thresh(1, subs) ?
@@ -1555,6 +1561,17 @@ mod tests {
15551561
))
15561562
);
15571563
}
1564+
1565+
#[test]
1566+
fn compile_tr_thresh() {
1567+
for k in 1..4 {
1568+
let small_thresh: Concrete<String> =
1569+
policy_str!("{}", &format!("thresh({},pk(B),pk(C),pk(D))", k));
1570+
let small_thresh_ms: Miniscript<String, Tap> = small_thresh.compile().unwrap();
1571+
let small_thresh_ms_expected: Miniscript<String, Tap> = ms_str!("multi_a({},B,C,D)", k);
1572+
assert_eq!(small_thresh_ms, small_thresh_ms_expected);
1573+
}
1574+
}
15581575
}
15591576

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

src/policy/concrete.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@ use crate::errstr;
2525
use crate::expression::{self, FromTree};
2626
use crate::miniscript::limits::{HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG};
2727
use crate::miniscript::types::extra_props::TimeLockInfo;
28-
#[cfg(feature = "compiler")]
29-
use crate::miniscript::ScriptContext;
30-
#[cfg(feature = "compiler")]
31-
use crate::policy::compiler;
32-
#[cfg(feature = "compiler")]
33-
use crate::policy::compiler::CompilerError;
34-
#[cfg(feature = "compiler")]
35-
use crate::Miniscript;
3628
use crate::{Error, ForEach, ForEachKey, MiniscriptKey};
29+
#[cfg(feature = "compiler")]
30+
use {
31+
crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler,
32+
crate::policy::compiler::CompilerError, crate::Descriptor, crate::Miniscript, crate::Tap,
33+
std::sync::Arc,
34+
};
35+
3736
/// Concrete policy which corresponds directly to a Miniscript structure,
3837
/// and whose disjunctions are annotated with satisfaction probabilities
3938
/// to assist the compiler
@@ -128,6 +127,30 @@ impl fmt::Display for PolicyError {
128127
}
129128

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

src/policy/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,12 @@ mod tests {
233233
Concrete, Liftable, Semantic,
234234
};
235235
use crate::DummyKey;
236+
#[cfg(feature = "compiler")]
237+
use crate::{descriptor::TapTree, Descriptor, Tap};
236238
use bitcoin;
237239
use std::str::FromStr;
240+
#[cfg(feature = "compiler")]
241+
use std::sync::Arc;
238242

239243
type ConcretePol = Concrete<DummyKey>;
240244
type SemanticPol = Semantic<DummyKey>;
@@ -364,4 +368,18 @@ mod tests {
364368
ms_str.lift().unwrap()
365369
);
366370
}
371+
372+
#[test]
373+
#[cfg(feature = "compiler")]
374+
fn single_leaf_tr_compile() {
375+
let unspendable_key: String = "z".to_string();
376+
let policy: Concrete<String> = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))");
377+
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
378+
379+
let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a(2,A,B,C,D)");
380+
let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
381+
let expected_descriptor = Descriptor::new_tr(unspendable_key, Some(tree)).unwrap();
382+
383+
assert_eq!(descriptor, expected_descriptor);
384+
}
367385
}

0 commit comments

Comments
 (0)