Skip to content

Commit 81e2c7a

Browse files
Internal-key extraction done
The internal-key extraction method now selects the most-likely key (in the policy) as suggested by BIP341.
1 parent 6fb1b29 commit 81e2c7a

File tree

2 files changed

+96
-9
lines changed

2 files changed

+96
-9
lines changed

src/policy/concrete.rs

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,14 @@ use crate::miniscript::types::extra_props::TimeLockInfo;
2828
use crate::{Error, ForEach, ForEachKey, MiniscriptKey};
2929
#[cfg(feature = "compiler")]
3030
use {
31-
crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler,
32-
crate::policy::compiler::CompilerError, crate::Descriptor, crate::Miniscript, crate::Tap,
31+
crate::descriptor::TapTree,
32+
crate::miniscript::ScriptContext,
33+
crate::policy::compiler::CompilerError,
34+
crate::policy::{compiler, Concrete, Liftable, Semantic},
35+
crate::Descriptor,
36+
crate::Miniscript,
37+
crate::Tap,
38+
std::collections::HashMap,
3339
std::sync::Arc,
3440
};
3541

@@ -127,7 +133,30 @@ impl fmt::Display for PolicyError {
127133
}
128134

129135
impl<Pk: MiniscriptKey> Policy<Pk> {
130-
/// Single-Node compilation
136+
/// Flatten the [`Policy`] tree structure into a Vector with corresponding leaf probability
137+
#[cfg(feature = "compiler")]
138+
fn to_tapleaf_prob_vec(&self, prob: f64) -> Vec<(f64, Policy<Pk>)> {
139+
match *self {
140+
Policy::Or(ref subs) => {
141+
let total_odds: usize = subs.iter().map(|(ref k, _)| k).sum();
142+
subs.iter()
143+
.map(|(k, ref policy)| {
144+
policy.to_tapleaf_prob_vec(prob * *k as f64 / total_odds as f64)
145+
})
146+
.flatten()
147+
.collect::<Vec<_>>()
148+
}
149+
Policy::Threshold(k, ref subs) if k == 1 => {
150+
let total_odds = subs.len();
151+
subs.iter()
152+
.map(|policy| policy.to_tapleaf_prob_vec(prob / total_odds as f64))
153+
.flatten()
154+
.collect::<Vec<_>>()
155+
}
156+
ref x => vec![(prob, x.clone())],
157+
}
158+
}
159+
131160
#[cfg(feature = "compiler")]
132161
fn compile_leaf_taptree(&self) -> Result<TapTree<Pk>, Error> {
133162
let compilation = self.compile::<Tap>().unwrap();
@@ -136,17 +165,51 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
136165

137166
/// Extract the Taproot internal_key from policy tree.
138167
#[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")),
168+
fn extract_key(self, unspendable_key: Option<Pk>) -> Result<(Pk, Policy<Pk>), Error> {
169+
let mut internal_key: Option<Pk> = None;
170+
{
171+
let mut prob = 0.;
172+
let semantic_policy = self.lift()?;
173+
let concrete_keys = self.keys();
174+
let key_prob_map: HashMap<_, _> = self
175+
.to_tapleaf_prob_vec(1.0)
176+
.into_iter()
177+
.filter(|(_, ref pol)| match *pol {
178+
Concrete::Key(..) => true,
179+
_ => false,
180+
})
181+
.map(|(prob, key)| (key, prob))
182+
.collect();
183+
184+
for key in concrete_keys.into_iter() {
185+
if semantic_policy
186+
.clone()
187+
.satisfy_constraint(&Semantic::KeyHash(key.to_pubkeyhash()), true)
188+
== Semantic::Trivial
189+
{
190+
match key_prob_map.get(&Concrete::Key(key.clone())) {
191+
Some(val) => {
192+
if *val > prob {
193+
prob = *val;
194+
internal_key = Some(key.clone());
195+
}
196+
}
197+
None => return Err(errstr("Key should have existed in the HashMap!")),
198+
}
199+
}
200+
}
201+
}
202+
match (internal_key, unspendable_key) {
203+
(Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(&key))),
204+
(_, Some(key)) => Ok((key, self)),
205+
_ => Err(errstr("No viable internal key found.")),
143206
}
144207
}
145208

146209
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
147210
#[cfg(feature = "compiler")]
148211
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, Error> {
149-
let (internal_key, policy) = self.extract_key(unspendable_key)?;
212+
let (internal_key, policy) = self.clone().extract_key(unspendable_key)?;
150213
let tree = Descriptor::new_tr(internal_key, Some(policy.compile_leaf_taptree()?))?;
151214
Ok(tree)
152215
}
@@ -251,6 +314,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
251314
}
252315
}
253316

317+
/// Translate `Semantic::Key(key)` to `Semantic::Unsatisfiable` when extracting TapKey
318+
pub fn translate_unsatisfiable_pk(self, key: &Pk) -> Policy<Pk> {
319+
match self {
320+
Policy::Key(ref k) if k.clone() == *key => Policy::Unsatisfiable,
321+
Policy::And(subs) => Policy::And(
322+
subs.into_iter()
323+
.map(|sub| sub.translate_unsatisfiable_pk(key))
324+
.collect::<Vec<_>>(),
325+
),
326+
Policy::Or(subs) => Policy::Or(
327+
subs.into_iter()
328+
.map(|(k, sub)| (k, sub.translate_unsatisfiable_pk(key)))
329+
.collect::<Vec<_>>(),
330+
),
331+
Policy::Threshold(k, subs) => Policy::Threshold(
332+
k,
333+
subs.into_iter()
334+
.map(|sub| sub.translate_unsatisfiable_pk(key))
335+
.collect::<Vec<_>>(),
336+
),
337+
x => x,
338+
}
339+
}
340+
254341
/// Get all keys in the policy
255342
pub fn keys(&self) -> Vec<&Pk> {
256343
match *self {

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)