Skip to content

Commit e837ad9

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 62acab4 commit e837ad9

File tree

2 files changed

+112
-9
lines changed

2 files changed

+112
-9
lines changed

src/policy/concrete.rs

Lines changed: 111 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,46 @@ 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 of tuple `(leaf script, leaf probability)`
137+
/// with leaf probabilities corresponding to odds for sub-branch in the policy.
138+
/// We calculate the probability of selecting the sub-branch at every level and calculate the
139+
/// leaf probabilities as the probability of traversing through required branches to reach the
140+
/// leaf node, i.e. multiplication of the respective probabilities.
141+
///
142+
/// For example, the policy tree: OR
143+
/// / \
144+
/// 2 1 odds
145+
/// / \
146+
/// A OR
147+
/// / \
148+
/// 3 1 odds
149+
/// / \
150+
/// B C
151+
///
152+
/// gives the vector [(2/3, A), (1/3 * 3/4, B), (1/3 * 1/4, C)].
153+
#[cfg(feature = "compiler")]
154+
fn to_tapleaf_prob_vec(&self, prob: f64) -> Vec<(f64, Policy<Pk>)> {
155+
match *self {
156+
Policy::Or(ref subs) => {
157+
let total_odds: usize = subs.iter().map(|(ref k, _)| k).sum();
158+
subs.iter()
159+
.map(|(k, ref policy)| {
160+
policy.to_tapleaf_prob_vec(prob * *k as f64 / total_odds as f64)
161+
})
162+
.flatten()
163+
.collect::<Vec<_>>()
164+
}
165+
Policy::Threshold(k, ref subs) if k == 1 => {
166+
let total_odds = subs.len();
167+
subs.iter()
168+
.map(|policy| policy.to_tapleaf_prob_vec(prob / total_odds as f64))
169+
.flatten()
170+
.collect::<Vec<_>>()
171+
}
172+
ref x => vec![(prob, x.clone())],
173+
}
174+
}
175+
131176
#[cfg(feature = "compiler")]
132177
fn compile_leaf_taptree(&self) -> Result<TapTree<Pk>, Error> {
133178
let compilation = self.compile::<Tap>().unwrap();
@@ -136,17 +181,51 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
136181

137182
/// Extract the Taproot internal_key from policy tree.
138183
#[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")),
184+
fn extract_key(self, unspendable_key: Option<Pk>) -> Result<(Pk, Policy<Pk>), Error> {
185+
let mut internal_key: Option<Pk> = None;
186+
{
187+
let mut prob = 0.;
188+
let semantic_policy = self.lift()?;
189+
let concrete_keys = self.keys();
190+
let key_prob_map: HashMap<_, _> = self
191+
.to_tapleaf_prob_vec(1.0)
192+
.into_iter()
193+
.filter(|(_, ref pol)| match *pol {
194+
Concrete::Key(..) => true,
195+
_ => false,
196+
})
197+
.map(|(prob, key)| (key, prob))
198+
.collect();
199+
200+
for key in concrete_keys.into_iter() {
201+
if semantic_policy
202+
.clone()
203+
.satisfy_constraint(&Semantic::KeyHash(key.to_pubkeyhash()), true)
204+
== Semantic::Trivial
205+
{
206+
match key_prob_map.get(&Concrete::Key(key.clone())) {
207+
Some(val) => {
208+
if *val > prob {
209+
prob = *val;
210+
internal_key = Some(key.clone());
211+
}
212+
}
213+
None => return Err(errstr("Key should have existed in the HashMap!")),
214+
}
215+
}
216+
}
217+
}
218+
match (internal_key, unspendable_key) {
219+
(Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(&key))),
220+
(_, Some(key)) => Ok((key, self)),
221+
_ => Err(errstr("No viable internal key found.")),
143222
}
144223
}
145224

146225
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
147226
#[cfg(feature = "compiler")]
148227
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, Error> {
149-
let (internal_key, policy) = self.extract_key(unspendable_key)?;
228+
let (internal_key, policy) = self.clone().extract_key(unspendable_key)?;
150229
let tree = Descriptor::new_tr(internal_key, Some(policy.compile_leaf_taptree()?))?;
151230
Ok(tree)
152231
}
@@ -249,6 +328,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
249328
}
250329
}
251330

331+
/// Translate `Concrete::Key(key)` to `Concrete::Unsatisfiable` when extracting TapKey
332+
pub fn translate_unsatisfiable_pk(self, key: &Pk) -> Policy<Pk> {
333+
match self {
334+
Policy::Key(ref k) if k.clone() == *key => Policy::Unsatisfiable,
335+
Policy::And(subs) => Policy::And(
336+
subs.into_iter()
337+
.map(|sub| sub.translate_unsatisfiable_pk(key))
338+
.collect::<Vec<_>>(),
339+
),
340+
Policy::Or(subs) => Policy::Or(
341+
subs.into_iter()
342+
.map(|(k, sub)| (k, sub.translate_unsatisfiable_pk(key)))
343+
.collect::<Vec<_>>(),
344+
),
345+
Policy::Threshold(k, subs) => Policy::Threshold(
346+
k,
347+
subs.into_iter()
348+
.map(|sub| sub.translate_unsatisfiable_pk(key))
349+
.collect::<Vec<_>>(),
350+
),
351+
x => x,
352+
}
353+
}
354+
252355
/// Get all keys in the policy
253356
pub fn keys(&self) -> Vec<&Pk> {
254357
match *self {

src/policy/semantic.rs

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

0 commit comments

Comments
 (0)