use core::str::FromStr;
use core::{fmt, str};
use bitcoin::{absolute, relative};
use super::concrete::PolicyError;
use super::ENTAILMENT_MAX_TERMINALS;
use crate::iter::{Tree, TreeLike};
use crate::prelude::*;
use crate::sync::Arc;
use crate::{
errstr, expression, AbsLockTime, Error, ForEachKey, FromStrKey, MiniscriptKey, RelLockTime,
Threshold, Translator,
};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Policy<Pk: MiniscriptKey> {
Unsatisfiable,
Trivial,
Key(Pk),
After(AbsLockTime),
Older(RelLockTime),
Sha256(Pk::Sha256),
Hash256(Pk::Hash256),
Ripemd160(Pk::Ripemd160),
Hash160(Pk::Hash160),
Thresh(Threshold<Arc<Policy<Pk>>, 0>),
}
impl<Pk: MiniscriptKey> ForEachKey<Pk> for Policy<Pk> {
fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool {
self.pre_order_iter().all(|policy| match policy {
Policy::Key(ref pk) => pred(pk),
_ => true,
})
}
}
impl<Pk: MiniscriptKey> Policy<Pk> {
pub fn translate_pk<Q, E, T>(&self, t: &mut T) -> Result<Policy<Q>, E>
where
T: Translator<Pk, Q, E>,
Q: MiniscriptKey,
{
use Policy::*;
let mut translated = vec![];
for data in self.post_order_iter() {
let new_policy = match data.node {
Unsatisfiable => Unsatisfiable,
Trivial => Trivial,
Key(ref pk) => t.pk(pk).map(Key)?,
Sha256(ref h) => t.sha256(h).map(Sha256)?,
Hash256(ref h) => t.hash256(h).map(Hash256)?,
Ripemd160(ref h) => t.ripemd160(h).map(Ripemd160)?,
Hash160(ref h) => t.hash160(h).map(Hash160)?,
Older(ref n) => Older(*n),
After(ref n) => After(*n),
Thresh(ref thresh) => {
Thresh(thresh.map_from_post_order_iter(&data.child_indices, &translated))
}
};
translated.push(Arc::new(new_policy));
}
let root_node = translated.pop().unwrap();
Ok(Arc::try_unwrap(root_node).unwrap())
}
pub fn entails(self, other: Policy<Pk>) -> Result<bool, PolicyError> {
if self.n_terminals() > ENTAILMENT_MAX_TERMINALS {
return Err(PolicyError::EntailmentMaxTerminals);
}
match (self, other) {
(Policy::Unsatisfiable, _) => Ok(true),
(Policy::Trivial, Policy::Trivial) => Ok(true),
(Policy::Trivial, _) => Ok(false),
(_, Policy::Unsatisfiable) => Ok(false),
(a, b) => {
let (a_norm, b_norm) = (a.normalized(), b.normalized());
let first_constraint = a_norm.first_constraint();
let (a1, b1) = (
a_norm.clone().satisfy_constraint(&first_constraint, true),
b_norm.clone().satisfy_constraint(&first_constraint, true),
);
let (a2, b2) = (
a_norm.satisfy_constraint(&first_constraint, false),
b_norm.satisfy_constraint(&first_constraint, false),
);
Ok(Policy::entails(a1, b1)? && Policy::entails(a2, b2)?)
}
}
}
fn n_terminals(&self) -> usize {
use Policy::*;
let mut n_terminals = vec![];
for data in self.post_order_iter() {
let n_terminals_for_child_n = |n| n_terminals[data.child_indices[n]];
let num = match data.node {
Thresh(thresh) => (0..thresh.n()).map(n_terminals_for_child_n).sum(),
Trivial | Unsatisfiable => 0,
_leaf => 1,
};
n_terminals.push(num);
}
n_terminals.pop().unwrap()
}
fn first_constraint(&self) -> Policy<Pk> {
debug_assert!(self.clone().normalized() == self.clone());
match self {
Policy::Thresh(ref thresh) => thresh.data()[0].first_constraint(),
first => first.clone(),
}
}
pub(crate) fn satisfy_constraint(self, witness: &Policy<Pk>, available: bool) -> Policy<Pk> {
debug_assert!(self.clone().normalized() == self);
if let Policy::Thresh { .. } = *witness {
panic!("should be unreachable")
}
let ret =
match self {
Policy::Thresh(thresh) => Policy::Thresh(thresh.map(|sub| {
Arc::new(sub.as_ref().clone().satisfy_constraint(witness, available))
})),
ref leaf if leaf == witness => {
if available {
Policy::Trivial
} else {
Policy::Unsatisfiable
}
}
x => x,
};
ret.normalized()
}
}
impl<Pk: MiniscriptKey> fmt::Debug for Policy<Pk> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Policy::Unsatisfiable => f.write_str("UNSATISFIABLE()"),
Policy::Trivial => f.write_str("TRIVIAL()"),
Policy::Key(ref pkh) => write!(f, "pk({:?})", pkh),
Policy::After(n) => write!(f, "after({})", n),
Policy::Older(n) => write!(f, "older({})", n),
Policy::Sha256(ref h) => write!(f, "sha256({})", h),
Policy::Hash256(ref h) => write!(f, "hash256({})", h),
Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
Policy::Hash160(ref h) => write!(f, "hash160({})", h),
Policy::Thresh(ref thresh) => {
if thresh.k() == thresh.n() {
thresh.debug("and", false).fmt(f)
} else if thresh.k() == 1 {
thresh.debug("or", false).fmt(f)
} else {
thresh.debug("thresh", true).fmt(f)
}
}
}
}
}
impl<Pk: MiniscriptKey> fmt::Display for Policy<Pk> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Policy::Unsatisfiable => f.write_str("UNSATISFIABLE"),
Policy::Trivial => f.write_str("TRIVIAL"),
Policy::Key(ref pkh) => write!(f, "pk({})", pkh),
Policy::After(n) => write!(f, "after({})", n),
Policy::Older(n) => write!(f, "older({})", n),
Policy::Sha256(ref h) => write!(f, "sha256({})", h),
Policy::Hash256(ref h) => write!(f, "hash256({})", h),
Policy::Ripemd160(ref h) => write!(f, "ripemd160({})", h),
Policy::Hash160(ref h) => write!(f, "hash160({})", h),
Policy::Thresh(ref thresh) => {
if thresh.k() == thresh.n() {
thresh.display("and", false).fmt(f)
} else if thresh.k() == 1 {
thresh.display("or", false).fmt(f)
} else {
thresh.display("thresh", true).fmt(f)
}
}
}
}
}
impl<Pk: FromStrKey> str::FromStr for Policy<Pk> {
type Err = Error;
fn from_str(s: &str) -> Result<Policy<Pk>, Error> {
expression::check_valid_chars(s)?;
let tree = expression::Tree::from_str(s)?;
expression::FromTree::from_tree(&tree)
}
}
serde_string_impl_pk!(Policy, "a miniscript semantic policy");
impl<Pk: FromStrKey> expression::FromTree for Policy<Pk> {
fn from_tree(top: &expression::Tree) -> Result<Policy<Pk>, Error> {
match (top.name, top.args.len()) {
("UNSATISFIABLE", 0) => Ok(Policy::Unsatisfiable),
("TRIVIAL", 0) => Ok(Policy::Trivial),
("pk", 1) => expression::terminal(&top.args[0], |pk| Pk::from_str(pk).map(Policy::Key)),
("after", 1) => expression::terminal(&top.args[0], |x| {
expression::parse_num(x)
.and_then(|x| AbsLockTime::from_consensus(x).map_err(Error::AbsoluteLockTime))
.map(Policy::After)
}),
("older", 1) => expression::terminal(&top.args[0], |x| {
expression::parse_num(x)
.and_then(|x| RelLockTime::from_consensus(x).map_err(Error::RelativeLockTime))
.map(Policy::Older)
}),
("sha256", 1) => {
expression::terminal(&top.args[0], |x| Pk::Sha256::from_str(x).map(Policy::Sha256))
}
("hash256", 1) => expression::terminal(&top.args[0], |x| {
Pk::Hash256::from_str(x).map(Policy::Hash256)
}),
("ripemd160", 1) => expression::terminal(&top.args[0], |x| {
Pk::Ripemd160::from_str(x).map(Policy::Ripemd160)
}),
("hash160", 1) => expression::terminal(&top.args[0], |x| {
Pk::Hash160::from_str(x).map(Policy::Hash160)
}),
("and", nsubs) => {
if nsubs < 2 {
return Err(Error::PolicyError(PolicyError::InsufficientArgsforAnd));
}
let mut subs = Vec::with_capacity(nsubs);
for arg in &top.args {
subs.push(Arc::new(Policy::from_tree(arg)?));
}
Ok(Policy::Thresh(Threshold::new(nsubs, subs).map_err(Error::Threshold)?))
}
("or", nsubs) => {
if nsubs < 2 {
return Err(Error::PolicyError(PolicyError::InsufficientArgsforOr));
}
let mut subs = Vec::with_capacity(nsubs);
for arg in &top.args {
subs.push(Arc::new(Policy::from_tree(arg)?));
}
Ok(Policy::Thresh(Threshold::new(1, subs).map_err(Error::Threshold)?))
}
("thresh", _) => {
let thresh = top.to_null_threshold().map_err(Error::ParseThreshold)?;
if thresh.is_or() || thresh.is_and() {
return Err(errstr(
"Semantic Policy thresh cannot have k = 1 or k = n, use `and`/`or` instead",
));
}
thresh
.translate_by_index(|i| Policy::from_tree(&top.args[1 + i]).map(Arc::new))
.map(Policy::Thresh)
}
_ => Err(errstr(top.name)),
}
}
}
impl<Pk: MiniscriptKey> Policy<Pk> {
pub fn normalized(self) -> Policy<Pk> {
match self {
Policy::Thresh(thresh) => {
let mut ret_subs = Vec::with_capacity(thresh.n());
let subs: Vec<_> = thresh
.iter()
.map(|sub| Arc::new(sub.as_ref().clone().normalized()))
.collect();
let trivial_count = subs
.iter()
.filter(|&pol| *pol.as_ref() == Policy::Trivial)
.count();
let unsatisfied_count = subs
.iter()
.filter(|&pol| *pol.as_ref() == Policy::Unsatisfiable)
.count();
let n = subs.len() - unsatisfied_count - trivial_count; let m = thresh.k().saturating_sub(trivial_count); let is_and = m == n;
let is_or = m == 1;
for sub in subs {
match sub.as_ref() {
Policy::Trivial | Policy::Unsatisfiable => {}
Policy::Thresh(ref subthresh) => {
match (is_and, is_or) {
(true, true) => {
ret_subs.push(Arc::new(Policy::Thresh(subthresh.clone())));
}
(true, false) if subthresh.k() == subthresh.n() => {
ret_subs.extend(subthresh.iter().cloned())
} (false, true) if subthresh.k() == 1 => {
ret_subs.extend(subthresh.iter().cloned())
} _ => ret_subs.push(Arc::new(Policy::Thresh(subthresh.clone()))),
}
}
x => ret_subs.push(Arc::new(x.clone())),
}
}
if m == 0 {
Policy::Trivial
} else if m > ret_subs.len() {
Policy::Unsatisfiable
} else if ret_subs.len() == 1 {
let policy = ret_subs.pop().unwrap();
Arc::try_unwrap(policy).unwrap()
} else if is_and {
Policy::Thresh(Threshold::new(ret_subs.len(), ret_subs).unwrap())
} else if is_or {
Policy::Thresh(Threshold::new(1, ret_subs).unwrap())
} else {
Policy::Thresh(Threshold::new(m, ret_subs).unwrap())
}
}
x => x,
}
}
pub fn is_trivial(&self) -> bool { matches!(*self, Policy::Trivial) }
pub fn is_unsatisfiable(&self) -> bool { matches!(*self, Policy::Unsatisfiable) }
fn real_relative_timelocks(&self) -> Vec<u32> {
self.pre_order_iter()
.filter_map(|policy| match policy {
Policy::Older(t) => Some(t.to_consensus_u32()),
_ => None,
})
.collect()
}
pub fn relative_timelocks(&self) -> Vec<u32> {
let mut ret = self.real_relative_timelocks();
ret.sort_unstable();
ret.dedup();
ret
}
fn real_absolute_timelocks(&self) -> Vec<u32> {
self.pre_order_iter()
.filter_map(|policy| match policy {
Policy::After(t) => Some(t.to_consensus_u32()),
_ => None,
})
.collect()
}
pub fn absolute_timelocks(&self) -> Vec<u32> {
let mut ret = self.real_absolute_timelocks();
ret.sort_unstable();
ret.dedup();
ret
}
pub fn at_age(self, age: relative::LockTime) -> Policy<Pk> {
use Policy::*;
let mut at_age = vec![];
for data in Arc::new(self).post_order_iter() {
let new_policy = match data.node.as_ref() {
Older(ref t) => {
if relative::LockTime::from(*t).is_implied_by(age) {
Some(Older(*t))
} else {
Some(Unsatisfiable)
}
}
Thresh(ref thresh) => {
Some(Thresh(thresh.map_from_post_order_iter(&data.child_indices, &at_age)))
}
_ => None,
};
match new_policy {
Some(new_policy) => at_age.push(Arc::new(new_policy)),
None => at_age.push(Arc::clone(&data.node)),
}
}
let root_node = at_age.pop().unwrap();
let policy = Arc::try_unwrap(root_node).unwrap();
policy.normalized()
}
pub fn at_lock_time(self, n: absolute::LockTime) -> Policy<Pk> {
use Policy::*;
let mut at_age = vec![];
for data in Arc::new(self).post_order_iter() {
let new_policy = match data.node.as_ref() {
After(t) => {
if absolute::LockTime::from(*t).is_implied_by(n) {
Some(After(*t))
} else {
Some(Unsatisfiable)
}
}
Thresh(ref thresh) => {
Some(Thresh(thresh.map_from_post_order_iter(&data.child_indices, &at_age)))
}
_ => None,
};
match new_policy {
Some(new_policy) => at_age.push(Arc::new(new_policy)),
None => at_age.push(Arc::clone(&data.node)),
}
}
let root_node = at_age.pop().unwrap();
let policy = Arc::try_unwrap(root_node).unwrap();
policy.normalized()
}
pub fn n_keys(&self) -> usize {
self.pre_order_iter()
.filter(|policy| matches!(policy, Policy::Key(..)))
.count()
}
pub fn minimum_n_keys(&self) -> Option<usize> {
use Policy::*;
let mut minimum_n_keys = vec![];
for data in Arc::new(self).post_order_iter() {
let minimum_n_keys_for_child_n = |n| minimum_n_keys[data.child_indices[n]];
let minimum_n_key = match data.node {
Unsatisfiable => None,
Trivial | After(..) | Older(..) | Sha256(..) | Hash256(..) | Ripemd160(..)
| Hash160(..) => Some(0),
Key(..) => Some(1),
Thresh(ref thresh) => {
let mut sublens = (0..thresh.n())
.filter_map(minimum_n_keys_for_child_n)
.collect::<Vec<usize>>();
if sublens.len() < thresh.k() {
None
} else {
sublens.sort_unstable();
Some(sublens[0..thresh.k()].iter().cloned().sum::<usize>())
}
}
};
minimum_n_keys.push(minimum_n_key);
}
minimum_n_keys.pop().unwrap()
}
}
impl<Pk: MiniscriptKey> Policy<Pk> {
pub fn sorted(self) -> Policy<Pk> {
use Policy::*;
let mut sorted = vec![];
for data in Arc::new(self).post_order_iter() {
let new_policy = match data.node.as_ref() {
Thresh(ref thresh) => {
let mut new_thresh =
thresh.map_from_post_order_iter(&data.child_indices, &sorted);
new_thresh.data_mut().sort();
Some(Thresh(new_thresh))
}
_ => None,
};
match new_policy {
Some(new_policy) => sorted.push(Arc::new(new_policy)),
None => sorted.push(Arc::clone(&data.node)),
}
}
let root_node = sorted.pop().unwrap();
Arc::try_unwrap(root_node).unwrap()
}
}
impl<'a, Pk: MiniscriptKey> TreeLike for &'a Policy<Pk> {
fn as_node(&self) -> Tree<Self> {
use Policy::*;
match *self {
Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
| Ripemd160(_) | Hash160(_) => Tree::Nullary,
Thresh(ref thresh) => Tree::Nary(thresh.iter().map(Arc::as_ref).collect()),
}
}
}
impl<Pk: MiniscriptKey> TreeLike for Arc<Policy<Pk>> {
fn as_node(&self) -> Tree<Self> {
use Policy::*;
match self.as_ref() {
Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
| Ripemd160(_) | Hash160(_) => Tree::Nullary,
Thresh(ref thresh) => Tree::Nary(thresh.iter().map(Arc::clone).collect()),
}
}
}
#[cfg(test)]
mod tests {
use bitcoin::PublicKey;
use super::*;
type StringPolicy = Policy<String>;
#[test]
fn parse_policy_err() {
assert!(StringPolicy::from_str("(").is_err());
assert!(StringPolicy::from_str("(x()").is_err());
assert!(StringPolicy::from_str("(\u{7f}()3").is_err());
assert!(StringPolicy::from_str("pk()").is_ok());
assert!(StringPolicy::from_str("or(or)").is_err());
assert!(Policy::<PublicKey>::from_str("pk()").is_err());
assert!(Policy::<PublicKey>::from_str(
"pk(\
0200000000000000000000000000000000000002\
)"
)
.is_err());
assert!(Policy::<PublicKey>::from_str(
"pk(\
02c79ef3ede6d14f72a00d0e49b4becfb152197b64c0707425c4f231df29500ee7\
)"
)
.is_ok());
}
#[test]
fn semantic_analysis() {
let policy = StringPolicy::from_str("pk()").unwrap();
assert_eq!(policy, Policy::Key("".to_owned()));
assert_eq!(policy.relative_timelocks(), vec![]);
assert_eq!(policy.absolute_timelocks(), vec![]);
assert_eq!(policy.clone().at_age(RelLockTime::ZERO.into()), policy);
assert_eq!(
policy
.clone()
.at_age(RelLockTime::from_height(10000).into()),
policy
);
assert_eq!(policy.n_keys(), 1);
assert_eq!(policy.minimum_n_keys(), Some(1));
let policy = StringPolicy::from_str("older(1000)").unwrap();
assert_eq!(policy, Policy::Older(RelLockTime::from_height(1000)));
assert_eq!(policy.absolute_timelocks(), vec![]);
assert_eq!(policy.relative_timelocks(), vec![1000]);
assert_eq!(policy.clone().at_age(RelLockTime::ZERO.into()), Policy::Unsatisfiable);
assert_eq!(
policy.clone().at_age(RelLockTime::from_height(999).into()),
Policy::Unsatisfiable
);
assert_eq!(policy.clone().at_age(RelLockTime::from_height(1000).into()), policy);
assert_eq!(
policy
.clone()
.at_age(RelLockTime::from_height(10000).into()),
policy
);
assert_eq!(policy.n_keys(), 0);
assert_eq!(policy.minimum_n_keys(), Some(0));
let policy = StringPolicy::from_str("or(pk(),older(1000))").unwrap();
assert_eq!(
policy,
Policy::Thresh(Threshold::or(
Policy::Key("".to_owned()).into(),
Policy::Older(RelLockTime::from_height(1000)).into(),
))
);
assert_eq!(policy.relative_timelocks(), vec![1000]);
assert_eq!(policy.absolute_timelocks(), vec![]);
assert_eq!(policy.clone().at_age(RelLockTime::ZERO.into()), Policy::Key("".to_owned()));
assert_eq!(
policy.clone().at_age(RelLockTime::from_height(999).into()),
Policy::Key("".to_owned())
);
assert_eq!(
policy.clone().at_age(RelLockTime::from_height(1000).into()),
policy.clone().normalized()
);
assert_eq!(
policy
.clone()
.at_age(RelLockTime::from_height(10000).into()),
policy.clone().normalized()
);
assert_eq!(policy.n_keys(), 1);
assert_eq!(policy.minimum_n_keys(), Some(0));
let policy = StringPolicy::from_str("or(pk(),UNSATISFIABLE)").unwrap();
assert_eq!(
policy,
Policy::Thresh(Threshold::or(
Policy::Key("".to_owned()).into(),
Policy::Unsatisfiable.into()
))
);
assert_eq!(policy.relative_timelocks(), vec![]);
assert_eq!(policy.absolute_timelocks(), vec![]);
assert_eq!(policy.n_keys(), 1);
assert_eq!(policy.minimum_n_keys(), Some(1));
let policy = StringPolicy::from_str("and(pk(),UNSATISFIABLE)").unwrap();
assert_eq!(
policy,
Policy::Thresh(Threshold::and(
Policy::Key("".to_owned()).into(),
Policy::Unsatisfiable.into()
))
);
assert_eq!(policy.relative_timelocks(), vec![]);
assert_eq!(policy.absolute_timelocks(), vec![]);
assert_eq!(policy.n_keys(), 1);
assert_eq!(policy.minimum_n_keys(), None);
let policy = StringPolicy::from_str(
"thresh(\
2,older(1000),older(10000),older(1000),older(2000),older(2000)\
)",
)
.unwrap();
assert_eq!(
policy,
Policy::Thresh(
Threshold::new(
2,
vec![
Policy::Older(RelLockTime::from_height(1000)).into(),
Policy::Older(RelLockTime::from_height(10000)).into(),
Policy::Older(RelLockTime::from_height(1000)).into(),
Policy::Older(RelLockTime::from_height(2000)).into(),
Policy::Older(RelLockTime::from_height(2000)).into(),
]
)
.unwrap()
)
);
assert_eq!(
policy.relative_timelocks(),
vec![1000, 2000, 10000] );
let policy = StringPolicy::from_str(
"thresh(\
2,older(1000),older(10000),older(1000),UNSATISFIABLE,UNSATISFIABLE\
)",
)
.unwrap();
assert_eq!(
policy,
Policy::Thresh(
Threshold::new(
2,
vec![
Policy::Older(RelLockTime::from_height(1000)).into(),
Policy::Older(RelLockTime::from_height(10000)).into(),
Policy::Older(RelLockTime::from_height(1000)).into(),
Policy::Unsatisfiable.into(),
Policy::Unsatisfiable.into(),
]
)
.unwrap()
)
);
assert_eq!(
policy.relative_timelocks(),
vec![1000, 10000] );
assert_eq!(policy.n_keys(), 0);
assert_eq!(policy.minimum_n_keys(), Some(0));
let policy = StringPolicy::from_str("after(1000)").unwrap();
assert_eq!(policy, Policy::After(AbsLockTime::from_consensus(1000).unwrap()));
assert_eq!(policy.absolute_timelocks(), vec![1000]);
assert_eq!(policy.relative_timelocks(), vec![]);
assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")),
policy
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")),
policy
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")),
Policy::Unsatisfiable
);
assert_eq!(policy.n_keys(), 0);
assert_eq!(policy.minimum_n_keys(), Some(0));
let policy = StringPolicy::from_str("after(500000010)").unwrap();
assert_eq!(policy, Policy::After(AbsLockTime::from_consensus(500_000_010).unwrap()));
assert_eq!(policy.absolute_timelocks(), vec![500_000_010]);
assert_eq!(policy.relative_timelocks(), vec![]);
assert_eq!(policy.clone().at_lock_time(absolute::LockTime::ZERO), Policy::Unsatisfiable);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(999).expect("valid block height")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(1000).expect("valid block height")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_height(10000).expect("valid block height")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_time(500_000_000).expect("valid timestamp")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_time(500_000_001).expect("valid timestamp")),
Policy::Unsatisfiable
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_time(500_000_010).expect("valid timestamp")),
policy
);
assert_eq!(
policy
.clone()
.at_lock_time(absolute::LockTime::from_time(500_000_012).expect("valid timestamp")),
policy
);
assert_eq!(policy.n_keys(), 0);
assert_eq!(policy.minimum_n_keys(), Some(0));
}
#[test]
fn entailment_liquid_test() {
let liquid_pol = StringPolicy::from_str(
"or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap();
let master_key = StringPolicy::from_str("and(older(50000000),pk(master))").unwrap();
let new_liquid_pol =
Policy::Thresh(Threshold::or(liquid_pol.clone().into(), master_key.into()));
assert!(liquid_pol.clone().entails(new_liquid_pol.clone()).unwrap());
assert!(!new_liquid_pol.entails(liquid_pol.clone()).unwrap());
let backup_policy = StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap();
assert!(!backup_policy
.entails(
liquid_pol
.clone()
.at_age(RelLockTime::from_height(4095).into())
)
.unwrap());
let fed_pol = StringPolicy::from_str("thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14))").unwrap();
let backup_policy_after_expiry =
StringPolicy::from_str("and(older(4096),thresh(2,pk(A),pk(B),pk(C)))").unwrap();
assert!(fed_pol.entails(liquid_pol.clone()).unwrap());
assert!(backup_policy_after_expiry.entails(liquid_pol).unwrap());
}
#[test]
fn entailment_escrow() {
let escrow_pol = StringPolicy::from_str("thresh(2,pk(Alice),pk(Bob),pk(Judge))").unwrap();
let auth_alice = StringPolicy::from_str("and(pk(Alice),pk(Judge))").unwrap();
let control_alice = StringPolicy::from_str("or(pk(Alice),and(pk(Judge),pk(Bob)))").unwrap();
assert!(auth_alice.entails(escrow_pol.clone()).unwrap());
assert!(escrow_pol.entails(control_alice).unwrap());
let h = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
let htlc_pol = StringPolicy::from_str(&format!(
"or(and(pk(Alice),older(100)),and(pk(Bob),sha256({})))",
h
))
.unwrap();
let auth_alice = StringPolicy::from_str("and(pk(Alice),older(100))").unwrap();
let control_alice =
StringPolicy::from_str(&format!("or(pk(Alice),sha256({}))", h)).unwrap();
assert!(auth_alice.entails(htlc_pol.clone()).unwrap());
assert!(htlc_pol.entails(control_alice).unwrap());
}
#[test]
fn for_each_key() {
let liquid_pol = StringPolicy::from_str(
"or(and(older(4096),thresh(2,pk(A),pk(B),pk(C))),thresh(11,pk(F1),pk(F2),pk(F3),pk(F4),pk(F5),pk(F6),pk(F7),pk(F8),pk(F9),pk(F10),pk(F11),pk(F12),pk(F13),pk(F14)))").unwrap();
let mut count = 0;
assert!(liquid_pol.for_each_key(|_| {
count += 1;
true
}));
assert_eq!(count, 17);
}
}