Skip to content

Commit f097077

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

File tree

4 files changed

+204
-41
lines changed

4 files changed

+204
-41
lines changed

src/policy/compiler.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 154 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,15 @@ 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::Concrete,
34+
policy::{compiler, Liftable, Semantic},
35+
std::cmp::Reverse,
36+
std::collections::{BinaryHeap, HashMap},
37+
std::sync::Arc,
38+
Descriptor, Miniscript, Tap,
3239
};
3340
use {Error, ForEach, ForEachKey, MiniscriptKey};
3441

@@ -126,27 +133,137 @@ impl fmt::Display for PolicyError {
126133
}
127134

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

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

145255
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
256+
// TODO: We might require other compile errors for Taproot. Will discuss and update.
146257
#[cfg(feature = "compiler")]
147258
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()?))?;
259+
let (internal_key, policy) = self.clone().extract_key(unspendable_key)?;
260+
let tree = Descriptor::new_tr(
261+
internal_key,
262+
match policy {
263+
Policy::Trivial => None,
264+
policy => Some(policy.compile_tr_policy()?),
265+
},
266+
)?;
150267
Ok(tree)
151268
}
152269

@@ -250,6 +367,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
250367
}
251368
}
252369

370+
/// Translate `Semantic::Key(key)` to `Semantic::Unsatisfiable` when extracting TapKey
371+
pub fn translate_unsatisfiable_pk(self, key: &Pk) -> Policy<Pk> {
372+
match self {
373+
Policy::Key(ref k) if k.clone() == *key => Policy::Unsatisfiable,
374+
Policy::And(subs) => Policy::And(
375+
subs.into_iter()
376+
.map(|sub| sub.translate_unsatisfiable_pk(key))
377+
.collect::<Vec<_>>(),
378+
),
379+
Policy::Or(subs) => Policy::Or(
380+
subs.into_iter()
381+
.map(|(k, sub)| (k, sub.translate_unsatisfiable_pk(key)))
382+
.collect::<Vec<_>>(),
383+
),
384+
Policy::Threshold(k, subs) => Policy::Threshold(
385+
k,
386+
subs.into_iter()
387+
.map(|sub| sub.translate_unsatisfiable_pk(key))
388+
.collect::<Vec<_>>(),
389+
),
390+
x => x,
391+
}
392+
}
393+
253394
/// Get all keys in the policy
254395
pub fn keys(&self) -> Vec<&Pk> {
255396
match *self {

src/policy/mod.rs

Lines changed: 48 additions & 26 deletions
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

Lines changed: 1 addition & 1 deletion
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)