bdk/src/descriptor/policy.rs

716 lines
23 KiB
Rust
Raw Normal View History

2020-02-17 14:22:53 +01:00
use std::cmp::max;
use std::collections::{BTreeMap, HashSet, VecDeque};
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
2020-02-07 23:22:28 +01:00
use bitcoin::hashes::*;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32::Fingerprint;
use bitcoin::PublicKey;
use miniscript::{Descriptor, Miniscript, Terminal};
2020-02-17 14:22:53 +01:00
#[allow(unused_imports)]
use log::{debug, error, info, trace};
2020-02-07 23:22:28 +01:00
use super::checksum::get_checksum;
2020-02-17 14:22:53 +01:00
use super::error::Error;
use crate::descriptor::{Key, MiniscriptExtractPolicy};
use crate::psbt::PSBTSatisfier;
#[derive(Debug, Clone, Default, Serialize)]
2020-02-07 23:22:28 +01:00
pub struct PKOrF {
#[serde(skip_serializing_if = "Option::is_none")]
pubkey: Option<PublicKey>,
#[serde(skip_serializing_if = "Option::is_none")]
2020-02-17 14:22:53 +01:00
pubkey_hash: Option<hash160::Hash>,
#[serde(skip_serializing_if = "Option::is_none")]
2020-02-07 23:22:28 +01:00
fingerprint: Option<Fingerprint>,
}
impl PKOrF {
fn from_key(k: &Box<dyn Key>) -> Self {
let secp = Secp256k1::gen_new();
2020-02-15 21:27:51 +01:00
let pubkey = k.as_public_key(&secp, None).unwrap();
2020-02-07 23:22:28 +01:00
if let Some(fing) = k.fingerprint(&secp) {
PKOrF {
fingerprint: Some(fing),
2020-02-17 14:22:53 +01:00
..Default::default()
2020-02-07 23:22:28 +01:00
}
} else {
PKOrF {
2020-02-15 21:27:51 +01:00
pubkey: Some(pubkey),
2020-02-17 14:22:53 +01:00
..Default::default()
2020-02-07 23:22:28 +01:00
}
}
}
}
2020-02-17 14:22:53 +01:00
#[derive(Debug, Clone, Serialize)]
2020-02-07 23:22:28 +01:00
#[serde(tag = "type", rename_all = "UPPERCASE")]
pub enum SatisfiableItem {
// Leaves
Signature(PKOrF),
2020-02-17 14:22:53 +01:00
SignatureKey(PKOrF),
2020-02-07 23:22:28 +01:00
SHA256Preimage {
hash: sha256::Hash,
},
HASH256Preimage {
hash: sha256d::Hash,
},
RIPEMD160Preimage {
hash: ripemd160::Hash,
},
HASH160Preimage {
hash: hash160::Hash,
},
AbsoluteTimelock {
2020-02-15 21:27:51 +01:00
value: u32,
2020-02-07 23:22:28 +01:00
},
RelativeTimelock {
2020-02-15 21:27:51 +01:00
value: u32,
2020-02-07 23:22:28 +01:00
},
// Complex item
Thresh {
items: Vec<Policy>,
threshold: usize,
},
Multisig {
keys: Vec<PKOrF>,
threshold: usize,
},
}
impl SatisfiableItem {
pub fn is_leaf(&self) -> bool {
match self {
SatisfiableItem::Thresh {
items: _,
threshold: _,
} => false,
_ => true,
}
}
pub fn id(&self) -> String {
get_checksum(&serde_json::to_string(self).expect("Failed to serialize a SatisfiableItem"))
.expect("Failed to compute a SatisfiableItem id")
}
2020-02-17 14:22:53 +01:00
}
2020-02-15 21:27:51 +01:00
2020-02-17 14:22:53 +01:00
fn combinations(vec: &Vec<usize>, size: usize) -> Vec<Vec<usize>> {
assert!(vec.len() >= size);
let mut answer = Vec::new();
let mut queue = VecDeque::new();
for (index, val) in vec.iter().enumerate() {
let mut new_vec = Vec::with_capacity(size);
new_vec.push(*val);
queue.push_back((index, new_vec));
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
while let Some((index, vals)) = queue.pop_front() {
if vals.len() >= size {
answer.push(vals);
} else {
for (new_index, val) in vec.iter().skip(index + 1).enumerate() {
let mut cloned = vals.clone();
cloned.push(*val);
queue.push_front((new_index, cloned));
}
}
}
answer
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
fn mix<T: Clone>(vec: Vec<Vec<T>>) -> Vec<Vec<T>> {
if vec.is_empty() || vec.iter().any(Vec::is_empty) {
return vec![];
}
let mut answer = Vec::new();
let size = vec.len();
let mut queue = VecDeque::new();
for i in &vec[0] {
let mut new_vec = Vec::with_capacity(size);
new_vec.push(i.clone());
queue.push_back(new_vec);
}
while let Some(vals) = queue.pop_front() {
if vals.len() >= size {
answer.push(vals);
} else {
let level = vals.len();
for i in &vec[level] {
let mut cloned = vals.clone();
cloned.push(i.clone());
queue.push_front(cloned);
}
}
}
answer
}
pub type ConditionMap = BTreeMap<usize, HashSet<Condition>>;
pub type FoldedConditionMap = BTreeMap<Vec<usize>, HashSet<Condition>>;
fn serialize_folded_cond_map<S>(
input_map: &FoldedConditionMap,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(input_map.len()))?;
for (k, v) in input_map {
let k_string = format!("{:?}", k);
map.serialize_entry(&k_string, v)?;
}
map.end()
}
#[derive(Debug, Clone, Serialize)]
2020-02-15 21:27:51 +01:00
#[serde(tag = "type", rename_all = "UPPERCASE")]
pub enum Satisfaction {
Partial {
2020-02-17 14:22:53 +01:00
n: usize,
2020-02-15 21:27:51 +01:00
m: usize,
2020-02-17 14:22:53 +01:00
items: Vec<usize>,
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
conditions: ConditionMap,
},
PartialComplete {
2020-02-15 21:27:51 +01:00
n: usize,
2020-02-17 14:22:53 +01:00
m: usize,
items: Vec<usize>,
#[serde(
serialize_with = "serialize_folded_cond_map",
skip_serializing_if = "BTreeMap::is_empty"
)]
conditions: FoldedConditionMap,
},
Complete {
condition: Condition,
2020-02-15 21:27:51 +01:00
},
None,
}
impl Satisfaction {
2020-02-17 14:22:53 +01:00
pub fn is_leaf(&self) -> bool {
match self {
Satisfaction::None | Satisfaction::Complete { .. } => true,
Satisfaction::PartialComplete { .. } | Satisfaction::Partial { .. } => false,
2020-02-15 21:27:51 +01:00
}
}
2020-02-17 14:22:53 +01:00
// add `inner` as one of self's partial items. this only makes sense on partials
fn add(&mut self, inner: &Satisfaction, inner_index: usize) -> Result<(), PolicyError> {
match self {
Satisfaction::None | Satisfaction::Complete { .. } => Err(PolicyError::AddOnLeaf),
Satisfaction::PartialComplete { .. } => Err(PolicyError::AddOnPartialComplete),
Satisfaction::Partial {
n,
ref mut conditions,
ref mut items,
..
} => {
if inner_index >= *n || items.contains(&inner_index) {
return Err(PolicyError::IndexOutOfRange(inner_index));
}
match inner {
// not relevant if not completed yet
Satisfaction::None | Satisfaction::Partial { .. } => return Ok(()),
Satisfaction::Complete { condition } => {
items.push(inner_index);
conditions.insert(inner_index, vec![*condition].into_iter().collect());
}
Satisfaction::PartialComplete {
conditions: other_conditions,
..
} => {
items.push(inner_index);
let conditions_set = other_conditions
.values()
.fold(HashSet::new(), |set, i| set.union(&i).cloned().collect());
conditions.insert(inner_index, conditions_set);
}
}
2020-02-15 21:27:51 +01:00
2020-02-17 14:22:53 +01:00
Ok(())
}
}
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
fn finalize(&mut self) -> Result<(), PolicyError> {
// if partial try to bump it to a partialcomplete
if let Satisfaction::Partial {
n,
m,
items,
conditions,
} = self
{
if items.len() >= *m {
let mut map = BTreeMap::new();
let indexes = combinations(items, *m);
// `indexes` at this point is a Vec<Vec<usize>>, with the "n choose k" of items (m of n)
indexes
.into_iter()
// .inspect(|x| println!("--- orig --- {:?}", x))
// we map each of the combinations of elements into a tuple of ([choosen items], [conditions]). unfortunately, those items have potentially more than one
// condition (think about ORs), so we also use `mix` to expand those, i.e. [[0], [1, 2]] becomes [[0, 1], [0, 2]]. This is necessary to make sure that we
// consider every possibile options and check whether or not they are compatible.
.map(|i_vec| {
mix(i_vec
.iter()
.map(|i| {
conditions
.get(i)
.and_then(|set| Some(set.clone().into_iter().collect()))
.unwrap_or(vec![])
})
.collect())
.into_iter()
.map(|x| (i_vec.clone(), x))
.collect::<Vec<(Vec<usize>, Vec<Condition>)>>()
})
// .inspect(|x: &Vec<(Vec<usize>, Vec<Condition>)>| println!("fetch {:?}", x))
// since the previous step can turn one item of the iterator into multiple ones, we call flatten to expand them out
.flatten()
// .inspect(|x| println!("flat {:?}", x))
// try to fold all the conditions for this specific combination of indexes/options. if they are not compatibile, try_fold will be Err
.map(|(key, val)| {
(
key,
val.into_iter()
.try_fold(Condition::default(), |acc, v| acc.merge(&v)),
)
})
// .inspect(|x| println!("try_fold {:?}", x))
// filter out all the incompatible combinations
.filter(|(_, val)| val.is_ok())
// .inspect(|x| println!("filter {:?}", x))
// push them into the map
.for_each(|(key, val)| {
map.entry(key)
.or_insert_with(HashSet::new)
.insert(val.unwrap());
});
// TODO: if the map is empty, the conditions are not compatible, return an error?
*self = Satisfaction::PartialComplete {
n: *n,
m: *m,
items: items.clone(),
conditions: map,
};
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
}
Ok(())
}
}
impl From<bool> for Satisfaction {
fn from(other: bool) -> Self {
if other {
Satisfaction::Complete {
condition: Default::default(),
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
} else {
Satisfaction::None
2020-02-15 21:27:51 +01:00
}
}
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
#[derive(Debug, Clone, Serialize)]
2020-02-07 23:22:28 +01:00
pub struct Policy {
id: String,
2020-02-07 23:22:28 +01:00
#[serde(flatten)]
item: SatisfiableItem,
2020-02-15 21:27:51 +01:00
satisfaction: Satisfaction,
contribution: Satisfaction,
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize)]
pub struct Condition {
2020-02-15 21:27:51 +01:00
#[serde(skip_serializing_if = "Option::is_none")]
2020-02-07 23:22:28 +01:00
pub csv: Option<u32>,
2020-02-15 21:27:51 +01:00
#[serde(skip_serializing_if = "Option::is_none")]
2020-02-07 23:22:28 +01:00
pub timelock: Option<u32>,
}
2020-02-17 14:22:53 +01:00
impl Condition {
fn merge_timelock(a: u32, b: u32) -> Result<u32, PolicyError> {
const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000;
if (a < BLOCKS_TIMELOCK_THRESHOLD) != (b < BLOCKS_TIMELOCK_THRESHOLD) {
Err(PolicyError::MixedTimelockUnits)
} else {
Ok(max(a, b))
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
}
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
fn merge(mut self, other: &Condition) -> Result<Self, PolicyError> {
2020-02-07 23:22:28 +01:00
match (self.csv, other.csv) {
2020-02-17 14:22:53 +01:00
(Some(a), Some(b)) => self.csv = Some(Self::merge_timelock(a, b)?),
(None, any) => self.csv = any,
_ => {}
}
2020-02-07 23:22:28 +01:00
match (self.timelock, other.timelock) {
2020-02-17 14:22:53 +01:00
(Some(a), Some(b)) => self.timelock = Some(Self::merge_timelock(a, b)?),
(None, any) => self.timelock = any,
_ => {}
}
2020-02-07 23:22:28 +01:00
2020-02-17 14:22:53 +01:00
Ok(self)
2020-02-07 23:22:28 +01:00
}
pub fn is_null(&self) -> bool {
self.csv.is_none() && self.timelock.is_none()
}
}
#[derive(Debug)]
pub enum PolicyError {
NotEnoughItemsSelected(String),
TooManyItemsSelected(String),
2020-02-17 14:22:53 +01:00
IndexOutOfRange(usize),
AddOnLeaf,
AddOnPartialComplete,
MixedTimelockUnits,
IncompatibleConditions,
2020-02-07 23:22:28 +01:00
}
impl Policy {
pub fn new(item: SatisfiableItem) -> Self {
Policy {
id: item.id(),
2020-02-07 23:22:28 +01:00
item,
2020-02-15 21:27:51 +01:00
satisfaction: Satisfaction::None,
contribution: Satisfaction::None,
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_and(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
2020-02-07 23:22:28 +01:00
match (a, b) {
2020-02-17 14:22:53 +01:00
(None, None) => Ok(None),
(Some(x), None) | (None, Some(x)) => Ok(Some(x)),
2020-02-15 21:27:51 +01:00
(Some(a), Some(b)) => Self::make_thresh(vec![a, b], 2),
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_or(a: Option<Policy>, b: Option<Policy>) -> Result<Option<Policy>, PolicyError> {
2020-02-07 23:22:28 +01:00
match (a, b) {
2020-02-17 14:22:53 +01:00
(None, None) => Ok(None),
(Some(x), None) | (None, Some(x)) => Ok(Some(x)),
2020-02-15 21:27:51 +01:00
(Some(a), Some(b)) => Self::make_thresh(vec![a, b], 1),
2020-02-07 23:22:28 +01:00
}
}
2020-02-17 14:22:53 +01:00
pub fn make_thresh(
items: Vec<Policy>,
threshold: usize,
) -> Result<Option<Policy>, PolicyError> {
2020-02-07 23:22:28 +01:00
if threshold == 0 {
2020-02-17 14:22:53 +01:00
return Ok(None);
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
let mut contribution = Satisfaction::Partial {
n: items.len(),
m: threshold,
items: vec![],
conditions: Default::default(),
};
for (index, item) in items.iter().enumerate() {
contribution.add(&item.contribution, index)?;
}
contribution.finalize()?;
2020-02-15 21:27:51 +01:00
let mut policy: Policy = SatisfiableItem::Thresh { items, threshold }.into();
policy.contribution = contribution;
2020-02-17 14:22:53 +01:00
Ok(Some(policy))
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
fn make_multisig(
keys: Vec<Option<&Box<dyn Key>>>,
threshold: usize,
) -> Result<Option<Policy>, PolicyError> {
if threshold == 0 {
return Ok(None);
}
2020-02-15 21:27:51 +01:00
let parsed_keys = keys.iter().map(|k| PKOrF::from_key(k.unwrap())).collect();
2020-02-17 14:22:53 +01:00
let mut contribution = Satisfaction::Partial {
n: keys.len(),
m: threshold,
items: vec![],
conditions: Default::default(),
};
for (index, key) in keys.iter().enumerate() {
let val = if key.is_some() && key.unwrap().has_secret() {
Satisfaction::Complete {
condition: Default::default(),
}
} else {
Satisfaction::None
};
contribution.add(&val, index)?;
}
contribution.finalize()?;
2020-02-15 21:27:51 +01:00
let mut policy: Policy = SatisfiableItem::Multisig {
keys: parsed_keys,
threshold,
}
.into();
2020-02-17 14:22:53 +01:00
policy.contribution = contribution;
Ok(Some(policy))
2020-02-15 21:27:51 +01:00
}
2020-02-17 14:22:53 +01:00
pub fn satisfy(&mut self, _satisfier: &PSBTSatisfier, _desc_node: &Terminal<PublicKey>) {
//self.satisfaction = self.item.satisfy(satisfier, desc_node);
//self.contribution += &self.satisfaction;
2020-02-07 23:22:28 +01:00
}
pub fn requires_path(&self) -> bool {
self.get_requirements(&BTreeMap::new()).is_err()
2020-02-07 23:22:28 +01:00
}
pub fn get_requirements(
2020-02-07 23:22:28 +01:00
&self,
path: &BTreeMap<String, Vec<usize>>,
2020-02-17 14:22:53 +01:00
) -> Result<Condition, PolicyError> {
2020-02-07 23:22:28 +01:00
// if items.len() == threshold, selected can be omitted and we take all of them by default
let default = match &self.item {
SatisfiableItem::Thresh { items, threshold } if items.len() == *threshold => {
(0..*threshold).into_iter().collect()
}
_ => vec![],
};
let selected = match path.get(&self.id) {
2020-02-07 23:22:28 +01:00
_ if !default.is_empty() => &default,
Some(arr) => arr,
_ => &default,
};
match &self.item {
SatisfiableItem::Thresh { items, threshold } => {
let mapped_req = items
.iter()
.map(|i| i.get_requirements(path))
2020-02-07 23:22:28 +01:00
.collect::<Result<Vec<_>, _>>()?;
// if all the requirements are null we don't care about `selected` because there
// are no requirements
2020-02-17 14:22:53 +01:00
if mapped_req.iter().all(Condition::is_null) {
return Ok(Condition::default());
2020-02-07 23:22:28 +01:00
}
// if we have something, make sure we have enough items. note that the user can set
// an empty value for this step in case of n-of-n, because `selected` is set to all
// the elements above
if selected.len() < *threshold {
return Err(PolicyError::NotEnoughItemsSelected(self.id.clone()));
} else if selected.len() > *threshold {
return Err(PolicyError::TooManyItemsSelected(self.id.clone()));
2020-02-07 23:22:28 +01:00
}
// check the selected items, see if there are conflicting requirements
2020-02-17 14:22:53 +01:00
let mut requirements = Condition::default();
2020-02-07 23:22:28 +01:00
for item_index in selected {
2020-02-17 14:22:53 +01:00
requirements = requirements.merge(
2020-02-07 23:22:28 +01:00
mapped_req
.get(*item_index)
2020-02-17 14:22:53 +01:00
.ok_or(PolicyError::IndexOutOfRange(*item_index))?,
2020-02-07 23:22:28 +01:00
)?;
}
Ok(requirements)
}
_ if !selected.is_empty() => Err(PolicyError::TooManyItemsSelected(self.id.clone())),
2020-02-17 14:22:53 +01:00
SatisfiableItem::AbsoluteTimelock { value } => Ok(Condition {
2020-02-07 23:22:28 +01:00
csv: None,
2020-02-15 21:27:51 +01:00
timelock: Some(*value),
2020-02-07 23:22:28 +01:00
}),
2020-02-17 14:22:53 +01:00
SatisfiableItem::RelativeTimelock { value } => Ok(Condition {
2020-02-15 21:27:51 +01:00
csv: Some(*value),
2020-02-07 23:22:28 +01:00
timelock: None,
}),
2020-02-17 14:22:53 +01:00
_ => Ok(Condition::default()),
2020-02-07 23:22:28 +01:00
}
}
}
impl From<SatisfiableItem> for Policy {
fn from(other: SatisfiableItem) -> Self {
Self::new(other)
}
}
fn signature_from_string(key: Option<&Box<dyn Key>>) -> Option<Policy> {
2020-02-15 21:27:51 +01:00
key.map(|k| {
let mut policy: Policy = SatisfiableItem::Signature(PKOrF::from_key(k)).into();
policy.contribution = if k.has_secret() {
Satisfaction::Complete {
condition: Default::default(),
}
} else {
Satisfaction::None
};
policy
})
2020-02-07 23:22:28 +01:00
}
fn signature_key_from_string(key: Option<&Box<dyn Key>>) -> Option<Policy> {
let secp = Secp256k1::gen_new();
key.map(|k| {
2020-02-15 21:27:51 +01:00
let pubkey = k.as_public_key(&secp, None).unwrap();
let mut policy: Policy = if let Some(fing) = k.fingerprint(&secp) {
2020-02-17 14:22:53 +01:00
SatisfiableItem::SignatureKey(PKOrF {
2020-02-07 23:22:28 +01:00
fingerprint: Some(fing),
2020-02-17 14:22:53 +01:00
..Default::default()
})
2020-02-07 23:22:28 +01:00
} else {
2020-02-17 14:22:53 +01:00
SatisfiableItem::SignatureKey(PKOrF {
2020-02-15 21:27:51 +01:00
pubkey_hash: Some(hash160::Hash::hash(&pubkey.to_bytes())),
2020-02-17 14:22:53 +01:00
..Default::default()
})
2020-02-07 23:22:28 +01:00
}
2020-02-15 21:27:51 +01:00
.into();
policy.contribution = if k.has_secret() {
Satisfaction::Complete {
condition: Default::default(),
}
} else {
Satisfaction::None
};
policy
2020-02-07 23:22:28 +01:00
})
}
impl MiniscriptExtractPolicy for Miniscript<String> {
2020-02-17 14:22:53 +01:00
fn extract_policy(
&self,
lookup_map: &BTreeMap<String, Box<dyn Key>>,
) -> Result<Option<Policy>, Error> {
Ok(match &self.node {
2020-02-07 23:22:28 +01:00
// Leaves
Terminal::True | Terminal::False => None,
Terminal::Pk(pubkey) => signature_from_string(lookup_map.get(pubkey)),
Terminal::PkH(pubkey_hash) => signature_key_from_string(lookup_map.get(pubkey_hash)),
2020-02-15 21:27:51 +01:00
Terminal::After(value) => {
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
policy.contribution = Satisfaction::Complete {
2020-02-17 14:22:53 +01:00
condition: Condition {
2020-02-15 21:27:51 +01:00
timelock: Some(*value),
2020-02-17 14:22:53 +01:00
csv: None,
2020-02-15 21:27:51 +01:00
},
};
Some(policy)
2020-02-07 23:22:28 +01:00
}
2020-02-15 21:27:51 +01:00
Terminal::Older(value) => {
let mut policy: Policy = SatisfiableItem::RelativeTimelock { value: *value }.into();
policy.contribution = Satisfaction::Complete {
2020-02-17 14:22:53 +01:00
condition: Condition {
2020-02-15 21:27:51 +01:00
timelock: None,
2020-02-17 14:22:53 +01:00
csv: Some(*value),
2020-02-15 21:27:51 +01:00
},
};
Some(policy)
2020-02-07 23:22:28 +01:00
}
Terminal::Sha256(hash) => Some(SatisfiableItem::SHA256Preimage { hash: *hash }.into()),
Terminal::Hash256(hash) => {
Some(SatisfiableItem::HASH256Preimage { hash: *hash }.into())
}
Terminal::Ripemd160(hash) => {
Some(SatisfiableItem::RIPEMD160Preimage { hash: *hash }.into())
}
Terminal::Hash160(hash) => {
Some(SatisfiableItem::HASH160Preimage { hash: *hash }.into())
}
2020-02-15 21:27:51 +01:00
Terminal::ThreshM(k, pks) => {
2020-02-17 14:22:53 +01:00
Policy::make_multisig(pks.iter().map(|s| lookup_map.get(s)).collect(), *k)?
2020-02-15 21:27:51 +01:00
}
2020-02-07 23:22:28 +01:00
// Identities
Terminal::Alt(inner)
| Terminal::Swap(inner)
| Terminal::Check(inner)
| Terminal::DupIf(inner)
| Terminal::Verify(inner)
| Terminal::NonZero(inner)
2020-02-17 14:22:53 +01:00
| Terminal::ZeroNotEqual(inner) => inner.extract_policy(lookup_map)?,
2020-02-07 23:22:28 +01:00
// Complex policies
Terminal::AndV(a, b) | Terminal::AndB(a, b) => {
2020-02-17 14:22:53 +01:00
Policy::make_and(a.extract_policy(lookup_map)?, b.extract_policy(lookup_map)?)?
2020-02-07 23:22:28 +01:00
}
Terminal::AndOr(x, y, z) => Policy::make_or(
2020-02-17 14:22:53 +01:00
Policy::make_and(x.extract_policy(lookup_map)?, y.extract_policy(lookup_map)?)?,
z.extract_policy(lookup_map)?,
)?,
2020-02-07 23:22:28 +01:00
Terminal::OrB(a, b)
| Terminal::OrD(a, b)
| Terminal::OrC(a, b)
| Terminal::OrI(a, b) => {
2020-02-17 14:22:53 +01:00
Policy::make_or(a.extract_policy(lookup_map)?, b.extract_policy(lookup_map)?)?
2020-02-07 23:22:28 +01:00
}
Terminal::Thresh(k, nodes) => {
let mut threshold = *k;
let mapped: Vec<_> = nodes
.iter()
2020-02-17 14:22:53 +01:00
.map(|n| n.extract_policy(lookup_map))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.filter_map(|x| x)
2020-02-07 23:22:28 +01:00
.collect();
if mapped.len() < nodes.len() {
threshold = match threshold.checked_sub(nodes.len() - mapped.len()) {
2020-02-17 14:22:53 +01:00
None => return Ok(None),
2020-02-07 23:22:28 +01:00
Some(x) => x,
};
}
2020-02-17 14:22:53 +01:00
Policy::make_thresh(mapped, threshold)?
2020-02-07 23:22:28 +01:00
}
2020-02-17 14:22:53 +01:00
})
2020-02-07 23:22:28 +01:00
}
}
impl MiniscriptExtractPolicy for Descriptor<String> {
2020-02-17 14:22:53 +01:00
fn extract_policy(
&self,
lookup_map: &BTreeMap<String, Box<dyn Key>>,
) -> Result<Option<Policy>, Error> {
2020-02-07 23:22:28 +01:00
match self {
Descriptor::Pk(pubkey)
| Descriptor::Pkh(pubkey)
| Descriptor::Wpkh(pubkey)
2020-02-17 14:22:53 +01:00
| Descriptor::ShWpkh(pubkey) => Ok(signature_from_string(lookup_map.get(pubkey))),
2020-02-07 23:22:28 +01:00
Descriptor::Bare(inner)
| Descriptor::Sh(inner)
| Descriptor::Wsh(inner)
2020-02-17 14:22:53 +01:00
| Descriptor::ShWsh(inner) => Ok(inner.extract_policy(lookup_map)?),
2020-02-07 23:22:28 +01:00
}
}
}