Skip to content

Commit 872e06b

Browse files
Internal-key extraction done
The internal-key extraction method now selects the most-likely key (in the policy) as suggested by BIP341. Also corresponding changes have been made to the tests.
1 parent 682b25a commit 872e06b

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
@@ -27,8 +27,14 @@ 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,
33+
policy::Concrete,
34+
policy::{compiler, Liftable, Semantic},
35+
std::collections::HashMap,
36+
std::sync::Arc,
37+
Descriptor, Miniscript, Tap,
3238
};
3339
use {Error, ForEach, ForEachKey, MiniscriptKey};
3440

@@ -126,7 +132,30 @@ impl fmt::Display for PolicyError {
126132
}
127133

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

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

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

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