Skip to content

Commit 285207e

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 26fc574 commit 285207e

File tree

2 files changed

+119
-6
lines changed

2 files changed

+119
-6
lines changed

src/policy/concrete.rs

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,14 @@ use bitcoin::hashes::hex::FromHex;
2222
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
2323
#[cfg(feature = "compiler")]
2424
use {
25-
crate::descriptor::TapTree, crate::miniscript::ScriptContext, crate::policy::compiler,
26-
crate::policy::compiler::CompilerError, crate::Descriptor, crate::Miniscript, crate::Tap,
25+
crate::descriptor::TapTree,
26+
crate::miniscript::ScriptContext,
27+
crate::policy::compiler::CompilerError,
28+
crate::policy::{compiler, Concrete, Liftable, Semantic},
29+
crate::Descriptor,
30+
crate::Miniscript,
31+
crate::Tap,
32+
std::collections::HashMap,
2733
std::sync::Arc,
2834
};
2935

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

129135
impl<Pk: MiniscriptKey> Policy<Pk> {
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+
130176
/// Single-Node compilation
131177
#[cfg(feature = "compiler")]
132178
fn compile_leaf_taptree(&self) -> Result<TapTree<Pk>, Error> {
133179
let compilation = self.compile::<Tap>().unwrap();
134180
Ok(TapTree::Leaf(Arc::new(compilation)))
135181
}
136182

137-
/// Compile a [`Policy`] into a single-leaf [`TapTree`]
183+
/// Extract the Taproot internal_key from policy tree.
184+
#[cfg(feature = "compiler")]
185+
fn extract_key(self, unspendable_key: Option<Pk>) -> Result<(Pk, Policy<Pk>), Error> {
186+
let mut internal_key: Option<Pk> = None;
187+
{
188+
let mut prob = 0.;
189+
let semantic_policy = self.lift()?;
190+
let concrete_keys = self.keys();
191+
let key_prob_map: HashMap<_, _> = self
192+
.to_tapleaf_prob_vec(1.0)
193+
.into_iter()
194+
.filter(|(_, ref pol)| match *pol {
195+
Concrete::Key(..) => true,
196+
_ => false,
197+
})
198+
.map(|(prob, key)| (key, prob))
199+
.collect();
200+
201+
for key in concrete_keys.into_iter() {
202+
if semantic_policy
203+
.clone()
204+
.satisfy_constraint(&Semantic::KeyHash(key.to_pubkeyhash()), true)
205+
== Semantic::Trivial
206+
{
207+
match key_prob_map.get(&Concrete::Key(key.clone())) {
208+
Some(val) => {
209+
if *val > prob {
210+
prob = *val;
211+
internal_key = Some(key.clone());
212+
}
213+
}
214+
None => return Err(errstr("Key should have existed in the HashMap!")),
215+
}
216+
}
217+
}
218+
}
219+
match (internal_key, unspendable_key) {
220+
(Some(ref key), _) => Ok((key.clone(), self.translate_unsatisfiable_pk(&key))),
221+
(_, Some(key)) => Ok((key, self)),
222+
_ => Err(errstr("No viable internal key found.")),
223+
}
224+
}
225+
226+
/// Compile the [`Tr`] descriptor into optimized [`TapTree`] implementation
138227
#[cfg(feature = "compiler")]
139228
pub fn compile_tr(&self, unspendable_key: Option<Pk>) -> Result<Descriptor<Pk>, Error> {
140-
let internal_key = unspendable_key.ok_or(errstr("No internal key found"))?;
141-
let tree = Descriptor::new_tr(internal_key, Some(self.compile_leaf_taptree()?))?;
229+
let (internal_key, policy) = self.clone().extract_key(unspendable_key)?;
230+
let tree = Descriptor::new_tr(internal_key, Some(policy.compile_leaf_taptree()?))?;
142231
Ok(tree)
143232
}
144233

@@ -240,6 +329,30 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
240329
}
241330
}
242331

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

src/policy/semantic.rs

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

0 commit comments

Comments
 (0)