Skip to content

Commit f47ba52

Browse files
trivial P2Tr compiler done
Taproot internal-key extraction from policy done
1 parent d1dbe3a commit f47ba52

File tree

4 files changed

+184
-41
lines changed

4 files changed

+184
-41
lines changed

src/policy/compiler.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type PolicyCache<Pk, Ctx> =
3838

3939
///Ordered f64 for comparison
4040
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
41-
struct OrdF64(f64);
41+
pub(crate) struct OrdF64(pub f64);
4242

4343
impl Eq for OrdF64 {}
4444
impl Ord for OrdF64 {

src/policy/concrete.rs

+134-13
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,14 @@ use miniscript::limits::{HEIGHT_TIME_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG};
2727
use miniscript::types::extra_props::TimeLockInfo;
2828
#[cfg(feature = "compiler")]
2929
use {
30-
descriptor::TapTree, miniscript::ScriptContext, policy::compiler,
31-
policy::compiler::CompilerError, std::sync::Arc, Descriptor, Miniscript, Tap,
30+
descriptor::TapTree,
31+
miniscript::ScriptContext,
32+
policy::compiler::{CompilerError, OrdF64},
33+
policy::{compiler, Liftable, Semantic},
34+
std::cmp::Reverse,
35+
std::collections::BinaryHeap,
36+
std::sync::Arc,
37+
Descriptor, Miniscript, Tap,
3238
};
3339
use {Error, ForEach, ForEachKey, MiniscriptKey};
3440

@@ -126,27 +132,118 @@ impl fmt::Display for PolicyError {
126132
}
127133

128134
impl<Pk: MiniscriptKey> Policy<Pk> {
129-
/// Single-Node compilation
135+
/// Create a Huffman Tree from compiled [Miniscript] nodes
130136
#[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)))
137+
fn with_huffman_tree<T>(
138+
ms: Vec<(OrdF64, Miniscript<Pk, Tap>)>,
139+
f: T,
140+
) -> Result<TapTree<Pk>, Error>
141+
where
142+
T: Fn(OrdF64) -> OrdF64,
143+
{
144+
// Pattern match terminal Or/ Terminal (with equal odds)
145+
let mut node_weights = BinaryHeap::<(Reverse<OrdF64>, TapTree<Pk>)>::new();
146+
for (prob, script) in ms {
147+
node_weights.push((Reverse(f(prob)), TapTree::Leaf(Arc::new(script))));
148+
}
149+
if node_weights.is_empty() {
150+
return Err(errstr("Empty Miniscript compilation"));
151+
}
152+
while node_weights.len() > 1 {
153+
let (p1, s1) = node_weights.pop().expect("len must atleast be two");
154+
let (p2, s2) = node_weights.pop().expect("len must atleast be two");
155+
156+
let p = (p1.0).0 + (p2.0).0;
157+
node_weights.push((
158+
Reverse(OrdF64(p)),
159+
TapTree::Tree(Arc::from(s1), Arc::from(s2)),
160+
));
161+
}
162+
163+
debug_assert!(node_weights.len() == 1);
164+
let node = node_weights
165+
.pop()
166+
.expect("huffman tree algorithm is broken")
167+
.1;
168+
Ok(node)
134169
}
135170

136-
/// Extract the Taproot internal_key from policy tree.
171+
/// Flatten the [`Policy`] tree structure into a Vector with corresponding leaf probability
172+
// TODO: 1. Can try to push the maximum of scaling factors and accordingly update later for
173+
// TODO: 1. integer metric. (Accordingly change metrics everywhere)
137174
#[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")),
175+
fn to_tapleaf_prob_vec(&self, prob: f64) -> Vec<(f64, Policy<Pk>)> {
176+
match *self {
177+
Policy::Or(ref subs) => {
178+
let total_odds: usize = subs.iter().map(|(ref k, _)| k).sum();
179+
subs.iter()
180+
.map(|(k, ref policy)| {
181+
policy.to_tapleaf_prob_vec(prob * *k as f64 / total_odds as f64)
182+
})
183+
.flatten()
184+
.collect::<Vec<_>>()
185+
}
186+
Policy::Threshold(k, ref subs) if k == 1 => {
187+
let total_odds = subs.len();
188+
subs.iter()
189+
.map(|policy| policy.to_tapleaf_prob_vec(prob / total_odds as f64))
190+
.flatten()
191+
.collect::<Vec<_>>()
192+
}
193+
ref x => vec![(prob, x.clone())],
194+
}
195+
}
196+
197+
/// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
198+
#[cfg(feature = "compiler")]
199+
fn compile_tr_policy(&self) -> Result<TapTree<Pk>, Error> {
200+
let leaf_compilations: Vec<_> = self
201+
.to_tapleaf_prob_vec(1.0)
202+
.into_iter()
203+
.map(|(prob, ref policy)| (OrdF64(prob), policy.compile::<Tap>().unwrap()))
204+
.collect();
205+
let taptree = Self::with_huffman_tree(leaf_compilations, |x| x).unwrap();
206+
Ok(taptree)
207+
}
208+
209+
/// Extract the internal_key from policy tree.
210+
#[cfg(feature = "compiler")]
211+
fn extract_key(self, unspendable_key: Option<Pk>) -> Result<(Pk, Policy<Pk>), Error> {
212+
// Making sure the borrow ends before you move the value.
213+
let mut internal_key: Option<Pk> = None;
214+
{
215+
let semantic_policy = (&self).lift()?;
216+
let concrete_keys = (&self).keys().into_iter().collect::<HashSet<_>>();
217+
for key in concrete_keys.iter() {
218+
if semantic_policy
219+
.clone()
220+
.satisfy_constraint(&Semantic::KeyHash(key.to_pubkeyhash()), true)
221+
== Semantic::Trivial
222+
{
223+
internal_key = Some((*key).clone());
224+
break;
225+
}
226+
}
227+
}
228+
match (internal_key, unspendable_key) {
229+
(Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(&key))),
230+
(_, Some(key)) => Ok((key, self)),
231+
_ => Err(errstr("No viable internal key found.")),
142232
}
143233
}
144234

145235
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
236+
// TODO: We might require other compile errors for Taproot. Will discuss and update.
146237
#[cfg(feature = "compiler")]
147238
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()?))?;
239+
let (internal_key, policy) = self.clone().extract_key(unspendable_key)?;
240+
let tree = Descriptor::new_tr(
241+
internal_key,
242+
match policy {
243+
Policy::Trivial => None,
244+
policy => Some(policy.compile_tr_policy()?),
245+
},
246+
)?;
150247
Ok(tree)
151248
}
152249

@@ -250,6 +347,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
250347
}
251348
}
252349

350+
/// Translate `Semantic::Key(key)` to `Semantic::Unsatisfiable` when extracting TapKey
351+
pub fn translate_unsatisfiable_pk(self, key: &Pk) -> Policy<Pk> {
352+
match self {
353+
Policy::Key(ref k) if k.clone() == *key => Policy::Unsatisfiable,
354+
Policy::And(subs) => Policy::And(
355+
subs.into_iter()
356+
.map(|sub| sub.translate_unsatisfiable_pk(key))
357+
.collect::<Vec<_>>(),
358+
),
359+
Policy::Or(subs) => Policy::Or(
360+
subs.into_iter()
361+
.map(|(k, sub)| (k, sub.translate_unsatisfiable_pk(key)))
362+
.collect::<Vec<_>>(),
363+
),
364+
Policy::Threshold(k, subs) => Policy::Threshold(
365+
k,
366+
subs.into_iter()
367+
.map(|sub| sub.translate_unsatisfiable_pk(key))
368+
.collect::<Vec<_>>(),
369+
),
370+
x => x,
371+
}
372+
}
373+
253374
/// Get all keys in the policy
254375
pub fn keys(&self) -> Vec<&Pk> {
255376
match *self {

src/policy/mod.rs

+48-26
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,26 @@
2121
//! The format represents EC public keys abstractly to allow wallets to replace
2222
//! these with BIP32 paths, pay-to-contract instructions, etc.
2323
//!
24-
use {error, fmt};
25-
26-
#[cfg(feature = "compiler")]
27-
pub mod compiler;
28-
pub mod concrete;
29-
pub mod semantic;
30-
3124
use descriptor::Descriptor;
3225
use miniscript::{Miniscript, ScriptContext};
26+
use Error;
27+
use MiniscriptKey;
3328
use Terminal;
29+
use {error, fmt};
3430

3531
pub use self::concrete::Policy as Concrete;
3632
/// Semantic policies are "abstract" policies elsewhere; but we
3733
/// avoid this word because it is a reserved keyword in Rust
3834
pub use self::semantic::Policy as Semantic;
39-
use Error;
40-
use MiniscriptKey;
35+
36+
#[cfg(feature = "compiler")]
37+
pub mod compiler;
38+
pub mod concrete;
39+
pub mod semantic;
4140

4241
/// Policy entailment algorithm maximum number of terminals allowed
4342
const ENTAILMENT_MAX_TERMINALS: usize = 20;
43+
4444
/// Trait describing script representations which can be lifted into
4545
/// an abstract policy, by discarding information.
4646
/// After Lifting all policies are converted into `KeyHash(Pk::HasH)` to
@@ -228,20 +228,23 @@ impl<Pk: MiniscriptKey> Liftable<Pk> for Concrete<Pk> {
228228

229229
#[cfg(test)]
230230
mod tests {
231-
use super::{
232-
super::miniscript::{context::Segwitv0, Miniscript},
233-
Concrete, Liftable, Semantic,
234-
};
235-
use bitcoin;
236-
#[cfg(feature = "compiler")]
237-
use descriptor::TapTree;
238231
use std::str::FromStr;
239232
#[cfg(feature = "compiler")]
240233
use std::sync::Arc;
234+
235+
use bitcoin;
236+
237+
#[cfg(feature = "compiler")]
238+
use descriptor::TapTree;
241239
use DummyKey;
242240
#[cfg(feature = "compiler")]
243241
use {Descriptor, Tap};
244242

243+
use super::{
244+
super::miniscript::{context::Segwitv0, Miniscript},
245+
Concrete, Liftable, Semantic,
246+
};
247+
245248
type ConcretePol = Concrete<DummyKey>;
246249
type SemanticPol = Semantic<DummyKey>;
247250

@@ -272,6 +275,7 @@ mod tests {
272275
// thresh with k = 2
273276
assert!(ConcretePol::from_str("thresh(2,after(1000000000),after(100),pk())").is_err());
274277
}
278+
275279
#[test]
276280
fn policy_rtt_tests() {
277281
concrete_policy_rtt("pk()");
@@ -361,28 +365,46 @@ mod tests {
361365
2,
362366
vec![
363367
Semantic::KeyHash(key_a.pubkey_hash().as_hash()),
364-
Semantic::Older(42)
365-
]
368+
Semantic::Older(42),
369+
],
366370
),
367-
Semantic::KeyHash(key_b.pubkey_hash().as_hash())
368-
]
371+
Semantic::KeyHash(key_b.pubkey_hash().as_hash()),
372+
],
369373
),
370374
ms_str.lift().unwrap()
371375
);
372376
}
373377

374378
#[test]
375379
#[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+
fn taproot_compile() {
381+
// Trivial single-node compilation
382+
let unspendable_key: String = "UNSPENDABLE".to_string();
383+
{
384+
let policy: Concrete<String> = policy_str!("thresh(2,pk(A),pk(B),pk(C),pk(D))");
380385
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
381386

382-
let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a({},A,B,C,D)", k);
387+
let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a(2,A,B,C,D)");
383388
let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
384-
let expected_descriptor = Descriptor::new_tr(unspendable_key, Some(tree)).unwrap();
389+
let expected_descriptor =
390+
Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
391+
assert_eq!(descriptor, expected_descriptor);
392+
}
393+
394+
// Trivial multi-node compilation
395+
{
396+
let policy: Concrete<String> = policy_str!("or(and(pk(A),pk(B)),and(pk(C),pk(D)))");
397+
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
385398

399+
let left_ms_compilation: Arc<Miniscript<String, Tap>> =
400+
Arc::new(ms_str!("and_v(v:pk(C),pk(D))"));
401+
let right_ms_compilation: Arc<Miniscript<String, Tap>> =
402+
Arc::new(ms_str!("and_v(v:pk(A),pk(B))"));
403+
let left_node: Arc<TapTree<String>> = Arc::from(TapTree::Leaf(left_ms_compilation));
404+
let right_node: Arc<TapTree<String>> = Arc::from(TapTree::Leaf(right_ms_compilation));
405+
let tree: TapTree<String> = TapTree::Tree(left_node, right_node);
406+
let expected_descriptor =
407+
Descriptor::new_tr(unspendable_key.clone(), Some(tree)).unwrap();
386408
assert_eq!(descriptor, expected_descriptor);
387409
}
388410
}

src/policy/semantic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
189189
// policy.
190190
// Witness is currently encoded as policy. Only accepts leaf fragment and
191191
// a normalized policy
192-
fn satisfy_constraint(self, witness: &Policy<Pk>, available: bool) -> Policy<Pk> {
192+
pub(crate) fn satisfy_constraint(self, witness: &Policy<Pk>, available: bool) -> Policy<Pk> {
193193
debug_assert!(self.clone().normalized() == self.clone());
194194
match *witness {
195195
// only for internal purposes, safe to use unreachable!

0 commit comments

Comments
 (0)