Skip to content

Commit b10cb7a

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

File tree

4 files changed

+141
-38
lines changed

4 files changed

+141
-38
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 struct OrdF64(pub f64);
4242

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

src/policy/concrete.rs

+127-18
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
use bitcoin::hashes::hex::FromHex;
1919
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2020
use std::collections::HashSet;
21-
#[cfg(feature = "compiler")]
22-
use std::sync::Arc;
2321
use std::{error, fmt, str};
2422

23+
#[cfg(feature = "compiler")]
24+
use super::Liftable;
2525
use super::ENTAILMENT_MAX_TERMINALS;
2626
#[cfg(feature = "compiler")]
2727
use descriptor::TapTree;
@@ -32,9 +32,15 @@ use miniscript::types::extra_props::TimeLockInfo;
3232
#[cfg(feature = "compiler")]
3333
use miniscript::ScriptContext;
3434
#[cfg(feature = "compiler")]
35-
use policy::compiler;
35+
use policy::compiler::{CompilerError, OrdF64};
36+
#[cfg(feature = "compiler")]
37+
use policy::{compiler, Semantic};
38+
#[cfg(feature = "compiler")]
39+
use std::cmp::Reverse;
3640
#[cfg(feature = "compiler")]
37-
use policy::compiler::CompilerError;
41+
use std::collections::BinaryHeap;
42+
#[cfg(feature = "compiler")]
43+
use std::sync::Arc;
3844
#[cfg(feature = "compiler")]
3945
use Descriptor;
4046
#[cfg(feature = "compiler")]
@@ -137,30 +143,109 @@ impl fmt::Display for PolicyError {
137143
}
138144

139145
impl<Pk: MiniscriptKey> Policy<Pk> {
140-
/// Single-Node compilation
146+
/// Create a Huffman Tree from compiled [Miniscript] nodes
147+
#[cfg(feature = "compiler")]
148+
fn with_huffman_tree<T>(
149+
ms: Vec<(OrdF64, Miniscript<Pk, Tap>)>,
150+
f: T,
151+
) -> Result<TapTree<Pk>, Error>
152+
where
153+
T: Fn(OrdF64) -> OrdF64,
154+
{
155+
// Pattern match terminal Or/ Terminal (with equal odds)
156+
let mut node_weights = BinaryHeap::<(Reverse<OrdF64>, Arc<TapTree<Pk>>)>::new();
157+
for (prob, script) in ms {
158+
node_weights.push((Reverse(f(prob)), Arc::from(TapTree::Leaf(Arc::new(script)))));
159+
}
160+
if node_weights.is_empty() {
161+
return Err(errstr("Empty Miniscript compilation"));
162+
}
163+
while node_weights.len() > 1 {
164+
let (p1, s1) = node_weights.pop().expect("len must atleast be two");
165+
let (p2, s2) = node_weights.pop().expect("len must atleast be two");
166+
167+
let p = (p1.0).0 + (p2.0).0;
168+
node_weights.push((Reverse(OrdF64(p)), Arc::from(TapTree::Tree(s1, s2))));
169+
}
170+
171+
debug_assert!(node_weights.len() == 1);
172+
let node = node_weights
173+
.pop()
174+
.expect("huffman tree algorithm is broken")
175+
.1;
176+
Ok((*node).clone())
177+
}
178+
179+
/// Flatten the [`Policy`] tree structure into a Vector with corresponding leaf probability
180+
// TODO: 1. Can try to push the maximum of scaling factors and accordingly update later for
181+
// TODO: 1. integer metric. (Accordingly change metrics everywhere)
182+
#[cfg(feature = "compiler")]
183+
fn flatten_policy(&self, prob: f64) -> Vec<(f64, Policy<Pk>)> {
184+
match *self {
185+
Policy::Or(ref subs) => {
186+
let total_odds: usize = subs.iter().map(|(ref k, _)| k).sum();
187+
subs.iter()
188+
.map(|(k, ref policy)| {
189+
policy.flatten_policy(prob * *k as f64 / total_odds as f64)
190+
})
191+
.flatten()
192+
.collect::<Vec<_>>()
193+
}
194+
Policy::Threshold(k, ref subs) if k == 1 => {
195+
let total_odds = subs.len();
196+
subs.iter()
197+
.map(|policy| policy.flatten_policy(prob / total_odds as f64))
198+
.flatten()
199+
.collect::<Vec<_>>()
200+
}
201+
ref x => vec![(prob, x.clone())],
202+
}
203+
}
204+
205+
/// Compile [`Policy::Or`] and [`Policy::Threshold`] according to odds
141206
#[cfg(feature = "compiler")]
142-
fn compile_huffman_taptree(policy: &Self) -> Result<TapTree<Pk>, Error> {
143-
let compilation = policy.compile::<Tap>().unwrap();
144-
Ok(TapTree::Leaf(Arc::new(compilation)))
207+
fn compile_tr_policy(&self) -> Result<TapTree<Pk>, Error> {
208+
let leaf_compilations: Vec<_> = self
209+
.flatten_policy(1.0)
210+
.into_iter()
211+
.map(|(prob, ref policy)| (OrdF64(prob), policy.compile::<Tap>().unwrap()))
212+
.collect();
213+
let taptree = Self::with_huffman_tree(leaf_compilations, |x| x).unwrap();
214+
Ok(taptree)
145215
}
146-
/// Extract the Taproot internal_key from policy tree.
216+
217+
/// Extract the internal_key from policy tree.
147218
#[cfg(feature = "compiler")]
148-
fn extract_key(policy: &Self, unspendable_key: Option<Pk>) -> Result<(Pk, &Policy<Pk>), Error> {
149-
match unspendable_key {
150-
Some(key) => Ok((key, policy)),
151-
None => Err(errstr("No internal key found")),
219+
fn extract_key(policy: &Self, unspendable_key: Option<Pk>) -> Result<(Pk, Policy<Pk>), Error> {
220+
let semantic_policy = policy.lift()?;
221+
let concrete_keys = policy.keys().into_iter().collect::<HashSet<_>>();
222+
let mut internal_key: Option<Pk> = None;
223+
224+
for key in concrete_keys.iter() {
225+
if semantic_policy
226+
.clone()
227+
.satisfy_constraint(&Semantic::KeyHash(key.to_pubkeyhash()), true)
228+
== Semantic::Trivial
229+
{
230+
internal_key = Some((*key).clone());
231+
break;
232+
}
233+
}
234+
235+
match (internal_key, unspendable_key) {
236+
(Some(key), _) => Ok((key.clone(), policy.translate_unsatisfiable_pk(&key))),
237+
(_, Some(key)) => Ok((key, policy.clone())),
238+
_ => Err(errstr("No viable internal key found.")),
152239
}
153240
}
154241

155242
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
243+
// TODO: We might require other compile errors for Taproot. Will discuss and update.
156244
#[cfg(feature = "compiler")]
157245
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, Error> {
158246
let (internal_key, policy) = Self::extract_key(self, unspendable_key).unwrap();
159-
let tree = Descriptor::new_tr(
160-
internal_key,
161-
Some(Self::compile_huffman_taptree(policy).unwrap()),
162-
)
163-
.unwrap();
247+
let tree =
248+
Descriptor::new_tr(internal_key, Some(policy.compile_tr_policy().unwrap())).unwrap();
164249
Ok(tree)
165250
}
166251

@@ -264,6 +349,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
264349
}
265350
}
266351

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

src/policy/mod.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -374,16 +374,13 @@ mod tests {
374374
#[test]
375375
#[cfg(feature = "compiler")]
376376
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();
377+
let unspendable_key: String = "UNSPENDABLE".to_string();
378+
let policy: Concrete<String> = policy_str!("thresh(2,pk(A),pk(A),pk(C),pk(D))");
379+
let descriptor = policy.compile_tr(Some(unspendable_key.clone())).unwrap();
381380

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-
}
381+
let ms_compilation: Miniscript<String, Tap> = ms_str!("multi_a(2,C,D)");
382+
let tree: TapTree<String> = TapTree::Leaf(Arc::new(ms_compilation));
383+
let expected_descriptor = Descriptor::new_tr("A".to_string(), Some(tree)).unwrap();
384+
assert_eq!(descriptor, expected_descriptor);
388385
}
389386
}

src/policy/semantic.rs

+6-9
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!
@@ -679,10 +679,7 @@ mod tests {
679679
let policy = StringPolicy::from_str("or(pkh(),older(1000))").unwrap();
680680
assert_eq!(
681681
policy,
682-
Policy::Threshold(
683-
1,
684-
vec![Policy::KeyHash("".to_owned()), Policy::Older(1000),]
685-
)
682+
Policy::Threshold(1, vec![Policy::KeyHash("".to_owned()), Policy::Older(1000)])
686683
);
687684
assert_eq!(policy.relative_timelocks(), vec![1000]);
688685
assert_eq!(policy.absolute_timelocks(), vec![]);
@@ -698,7 +695,7 @@ mod tests {
698695
policy,
699696
Policy::Threshold(
700697
1,
701-
vec![Policy::KeyHash("".to_owned()), Policy::Unsatisfiable,]
698+
vec![Policy::KeyHash("".to_owned()), Policy::Unsatisfiable],
702699
)
703700
);
704701
assert_eq!(policy.relative_timelocks(), vec![]);
@@ -711,7 +708,7 @@ mod tests {
711708
policy,
712709
Policy::Threshold(
713710
2,
714-
vec![Policy::KeyHash("".to_owned()), Policy::Unsatisfiable,]
711+
vec![Policy::KeyHash("".to_owned()), Policy::Unsatisfiable],
715712
)
716713
);
717714
assert_eq!(policy.relative_timelocks(), vec![]);
@@ -735,7 +732,7 @@ mod tests {
735732
Policy::Older(1000),
736733
Policy::Older(2000),
737734
Policy::Older(2000),
738-
]
735+
],
739736
)
740737
);
741738
assert_eq!(
@@ -759,7 +756,7 @@ mod tests {
759756
Policy::Older(1000),
760757
Policy::Unsatisfiable,
761758
Policy::Unsatisfiable,
762-
]
759+
],
763760
)
764761
);
765762
assert_eq!(

0 commit comments

Comments
 (0)