pub mod correctness;
pub mod extra_props;
pub mod malleability;
use std::{error, fmt};
pub use self::correctness::{Base, Correctness, Input};
pub use self::extra_props::ExtData;
pub use self::malleability::{Dissat, Malleability};
use super::ScriptContext;
use MiniscriptKey;
use Terminal;
fn return_none<T>(_: usize) -> Option<T> {
None
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum ErrorKind {
ZeroTime,
NonZeroDupIf,
ZeroThreshold,
OverThreshold(usize, usize),
NoStrongChild,
LeftNotDissatisfiable,
RightNotDissatisfiable,
SwapNonOne,
NonZeroZero,
LeftNotUnit,
ChildBase1(Base),
ChildBase2(Base, Base),
ChildBase3(Base, Base, Base),
ThresholdBase(usize, Base),
ThresholdDissat(usize),
ThresholdNonUnit(usize),
ThresholdNotStrong {
k: usize,
n: usize,
n_strong: usize,
},
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Error<Pk: MiniscriptKey, Ctx: ScriptContext> {
pub fragment: Terminal<Pk, Ctx>,
pub error: ErrorKind,
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> error::Error for Error<Pk, Ctx> {
fn cause(&self) -> Option<&error::Error> {
None
}
fn description(&self) -> &str {
"description() is deprecated; use Display"
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Error<Pk, Ctx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.error {
ErrorKind::ZeroTime => write!(
f,
"fragment «{}» represents a 0-valued timelock (use `1` instead)",
self.fragment,
),
ErrorKind::NonZeroDupIf => write!(
f,
"fragment «{}» represents needs to be `z`, needs to consume zero elements from the stack",
self.fragment,
),
ErrorKind::ZeroThreshold => write!(
f,
"fragment «{}» has a threshold value of 0",
self.fragment,
),
ErrorKind::OverThreshold(k, n) => write!(
f,
"fragment «{}» is a {}-of-{} threshold, which does not
make sense",
self.fragment, k, n,
),
ErrorKind::NoStrongChild => write!(
f,
"fragment «{}» requires at least one strong child \
(a 3rd party cannot create a witness without having \
seen one before) to prevent malleability",
self.fragment,
),
ErrorKind::LeftNotDissatisfiable => write!(
f,
"fragment «{}» requires its left child be dissatisfiable",
self.fragment,
),
ErrorKind::RightNotDissatisfiable => write!(
f,
"fragment «{}» requires its right child be dissatisfiable",
self.fragment,
),
ErrorKind::SwapNonOne => write!(
f,
"fragment «{}» attempts to use `SWAP` to prefix something
which does not take exactly one input",
self.fragment,
),
ErrorKind::NonZeroZero => write!(
f,
"fragment «{}» attempts to use use the `j:` wrapper around a
fragment which might be satisfied by an input of size zero",
self.fragment,
),
ErrorKind::LeftNotUnit => write!(
f,
"fragment «{}» requires its left child be a unit (outputs
exactly 1 given a satisfying input)",
self.fragment,
),
ErrorKind::ChildBase1(base) => write!(
f,
"fragment «{}» cannot wrap a fragment of type {:?}",
self.fragment, base,
),
ErrorKind::ChildBase2(base1, base2) => write!(
f,
"fragment «{}» cannot accept children of types {:?} and {:?}",
self.fragment, base1, base2,
),
ErrorKind::ChildBase3(base1, base2, base3) => write!(
f,
"fragment «{}» cannot accept children of types {:?}, {:?} and {:?}",
self.fragment, base1, base2, base3,
),
ErrorKind::ThresholdBase(idx, base) => write!(
f,
"fragment «{}» sub-fragment {} has type {:?} rather than {:?}",
self.fragment,
idx,
base,
if idx == 0 { Base::B } else { Base::W },
),
ErrorKind::ThresholdDissat(idx) => write!(
f,
"fragment «{}» sub-fragment {} can not be dissatisfied \
and cannot be used in a threshold",
self.fragment, idx,
),
ErrorKind::ThresholdNonUnit(idx) => write!(
f,
"fragment «{}» sub-fragment {} is not a unit (does not put \
exactly 1 on the stack given a satisfying input)",
self.fragment, idx,
),
ErrorKind::ThresholdNotStrong { k, n, n_strong } => write!(
f,
"fragment «{}» is a {}-of-{} threshold, and needs {} of \
its children to be strong to prevent malleability; however \
only {} children were strong.",
self.fragment,
k,
n,
n - k,
n_strong,
),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Type {
pub corr: Correctness,
pub mall: Malleability,
}
impl Type {
pub fn is_subtype(&self, other: Self) -> bool {
if self.corr.is_subtype(other.corr) && self.mall.is_subtype(other.mall) {
return true;
}
return false;
}
}
pub trait Property: Sized {
fn sanity_checks(&self) {
}
fn from_true() -> Self;
fn from_false() -> Self;
fn from_pk_k() -> Self;
fn from_pk_h() -> Self;
fn from_multi(k: usize, n: usize) -> Self;
fn from_hash() -> Self;
fn from_sha256() -> Self {
Self::from_hash()
}
fn from_txtemplate() -> Self;
fn from_hash256() -> Self {
Self::from_hash()
}
fn from_ripemd160() -> Self {
Self::from_hash()
}
fn from_hash160() -> Self {
Self::from_hash()
}
fn from_time(t: u32) -> Self;
fn from_after(t: u32) -> Self {
Self::from_time(t)
}
fn from_older(t: u32) -> Self {
Self::from_time(t)
}
fn cast_alt(self) -> Result<Self, ErrorKind>;
fn cast_swap(self) -> Result<Self, ErrorKind>;
fn cast_check(self) -> Result<Self, ErrorKind>;
fn cast_dupif(self) -> Result<Self, ErrorKind>;
fn cast_verify(self) -> Result<Self, ErrorKind>;
fn cast_nonzero(self) -> Result<Self, ErrorKind>;
fn cast_zeronotequal(self) -> Result<Self, ErrorKind>;
fn cast_true(self) -> Result<Self, ErrorKind>;
fn cast_or_i_false(self) -> Result<Self, ErrorKind>;
fn cast_unlikely(self) -> Result<Self, ErrorKind> {
self.cast_or_i_false()
}
fn cast_likely(self) -> Result<Self, ErrorKind> {
self.cast_or_i_false()
}
fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn and_n(left: Self, right: Self) -> Result<Self, ErrorKind> {
Self::and_or(left, right, Self::from_false())
}
fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind>;
fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind>;
fn threshold<S>(k: usize, n: usize, sub_ck: S) -> Result<Self, ErrorKind>
where
S: FnMut(usize) -> Result<Self, ErrorKind>;
fn type_check<Pk, Ctx, C>(
fragment: &Terminal<Pk, Ctx>,
mut child: C,
) -> Result<Self, Error<Pk, Ctx>>
where
C: FnMut(usize) -> Option<Self>,
Pk: MiniscriptKey,
Ctx: ScriptContext,
{
let mut get_child = |sub, n| {
child(n)
.map(Ok)
.unwrap_or_else(|| Self::type_check(sub, return_none))
};
let wrap_err = |result: Result<Self, ErrorKind>| {
result.map_err(|kind| Error {
fragment: fragment.clone(),
error: kind,
})
};
let ret = match *fragment {
Terminal::True => Ok(Self::from_true()),
Terminal::False => Ok(Self::from_false()),
Terminal::PkK(..) => Ok(Self::from_pk_k()),
Terminal::PkH(..) => Ok(Self::from_pk_h()),
Terminal::Multi(k, ref pks) => {
if k == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroThreshold,
});
}
if k > pks.len() {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::OverThreshold(k, pks.len()),
});
}
Ok(Self::from_multi(k, pks.len()))
}
Terminal::After(t) => {
if t == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroTime,
});
}
Ok(Self::from_after(t))
}
Terminal::Older(t) => {
if t == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroTime,
});
}
Ok(Self::from_older(t))
}
Terminal::Sha256(..) => Ok(Self::from_sha256()),
Terminal::Hash256(..) => Ok(Self::from_hash256()),
Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
Terminal::Hash160(..) => Ok(Self::from_hash160()),
Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(get_child(&sub.node, 0)?)),
Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(get_child(&sub.node, 0)?)),
Terminal::Check(ref sub) => wrap_err(Self::cast_check(get_child(&sub.node, 0)?)),
Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(get_child(&sub.node, 0)?)),
Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(get_child(&sub.node, 0)?)),
Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(get_child(&sub.node, 0)?)),
Terminal::ZeroNotEqual(ref sub) => {
wrap_err(Self::cast_zeronotequal(get_child(&sub.node, 0)?))
}
Terminal::AndB(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::and_b(ltype, rtype))
}
Terminal::AndV(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::and_v(ltype, rtype))
}
Terminal::OrB(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::or_b(ltype, rtype))
}
Terminal::OrD(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::or_d(ltype, rtype))
}
Terminal::OrC(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::or_c(ltype, rtype))
}
Terminal::OrI(ref l, ref r) => {
let ltype = get_child(&l.node, 0)?;
let rtype = get_child(&r.node, 1)?;
wrap_err(Self::or_i(ltype, rtype))
}
Terminal::AndOr(ref a, ref b, ref c) => {
let atype = get_child(&a.node, 0)?;
let btype = get_child(&b.node, 1)?;
let ctype = get_child(&c.node, 2)?;
wrap_err(Self::and_or(atype, btype, ctype))
}
Terminal::Thresh(k, ref subs) => {
if k == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroThreshold,
});
}
if k > subs.len() {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::OverThreshold(k, subs.len()),
});
}
let mut last_err_frag = None;
let res = Self::threshold(k, subs.len(), |n| match get_child(&subs[n].node, n) {
Ok(x) => Ok(x),
Err(e) => {
last_err_frag = Some(e.fragment);
Err(e.error)
}
});
res.map_err(|kind| Error {
fragment: last_err_frag.unwrap_or(fragment.clone()),
error: kind,
})
}
Terminal::TxTemplate(..) => Ok(Self::from_txtemplate()),
};
if let Ok(ref ret) = ret {
ret.sanity_checks()
}
ret
}
}
impl Property for Type {
fn sanity_checks(&self) {
debug_assert!(!self.corr.dissatisfiable || self.mall.dissat != Dissat::None);
debug_assert!(self.mall.dissat == Dissat::None || self.corr.base != Base::V);
debug_assert!(self.mall.safe || self.corr.base != Base::K);
debug_assert!(self.mall.non_malleable || self.corr.input != Input::Zero);
}
fn from_true() -> Self {
Type {
corr: Property::from_true(),
mall: Property::from_true(),
}
}
fn from_false() -> Self {
Type {
corr: Property::from_false(),
mall: Property::from_false(),
}
}
fn from_pk_k() -> Self {
Type {
corr: Property::from_pk_k(),
mall: Property::from_pk_k(),
}
}
fn from_pk_h() -> Self {
Type {
corr: Property::from_pk_h(),
mall: Property::from_pk_h(),
}
}
fn from_multi(k: usize, n: usize) -> Self {
Type {
corr: Property::from_multi(k, n),
mall: Property::from_multi(k, n),
}
}
fn from_hash() -> Self {
Type {
corr: Property::from_hash(),
mall: Property::from_hash(),
}
}
fn from_sha256() -> Self {
Type {
corr: Property::from_sha256(),
mall: Property::from_sha256(),
}
}
fn from_txtemplate() -> Self {
Type {
corr: Property::from_txtemplate(),
mall: Property::from_txtemplate(),
}
}
fn from_hash256() -> Self {
Type {
corr: Property::from_hash256(),
mall: Property::from_hash256(),
}
}
fn from_ripemd160() -> Self {
Type {
corr: Property::from_ripemd160(),
mall: Property::from_ripemd160(),
}
}
fn from_hash160() -> Self {
Type {
corr: Property::from_hash160(),
mall: Property::from_hash160(),
}
}
fn from_time(t: u32) -> Self {
Type {
corr: Property::from_time(t),
mall: Property::from_time(t),
}
}
fn from_after(t: u32) -> Self {
Type {
corr: Property::from_after(t),
mall: Property::from_after(t),
}
}
fn from_older(t: u32) -> Self {
Type {
corr: Property::from_older(t),
mall: Property::from_older(t),
}
}
fn cast_alt(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_alt(self.corr)?,
mall: Property::cast_alt(self.mall)?,
})
}
fn cast_swap(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_swap(self.corr)?,
mall: Property::cast_swap(self.mall)?,
})
}
fn cast_check(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_check(self.corr)?,
mall: Property::cast_check(self.mall)?,
})
}
fn cast_dupif(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_dupif(self.corr)?,
mall: Property::cast_dupif(self.mall)?,
})
}
fn cast_verify(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_verify(self.corr)?,
mall: Property::cast_verify(self.mall)?,
})
}
fn cast_nonzero(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_nonzero(self.corr)?,
mall: Property::cast_nonzero(self.mall)?,
})
}
fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_zeronotequal(self.corr)?,
mall: Property::cast_zeronotequal(self.mall)?,
})
}
fn cast_true(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_true(self.corr)?,
mall: Property::cast_true(self.mall)?,
})
}
fn cast_or_i_false(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_or_i_false(self.corr)?,
mall: Property::cast_or_i_false(self.mall)?,
})
}
fn cast_unlikely(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_unlikely(self.corr)?,
mall: Property::cast_unlikely(self.mall)?,
})
}
fn cast_likely(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::cast_likely(self.corr)?,
mall: Property::cast_likely(self.mall)?,
})
}
fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::and_b(left.corr, right.corr)?,
mall: Property::and_b(left.mall, right.mall)?,
})
}
fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::and_v(left.corr, right.corr)?,
mall: Property::and_v(left.mall, right.mall)?,
})
}
fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::or_b(left.corr, right.corr)?,
mall: Property::or_b(left.mall, right.mall)?,
})
}
fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::or_d(left.corr, right.corr)?,
mall: Property::or_d(left.mall, right.mall)?,
})
}
fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::or_c(left.corr, right.corr)?,
mall: Property::or_c(left.mall, right.mall)?,
})
}
fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::or_i(left.corr, right.corr)?,
mall: Property::or_i(left.mall, right.mall)?,
})
}
fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: Property::and_or(a.corr, b.corr, c.corr)?,
mall: Property::and_or(a.mall, b.mall, c.mall)?,
})
}
fn threshold<S>(k: usize, n: usize, mut sub_ck: S) -> Result<Self, ErrorKind>
where
S: FnMut(usize) -> Result<Self, ErrorKind>,
{
Ok(Type {
corr: Property::threshold(k, n, |n| Ok(sub_ck(n)?.corr))?,
mall: Property::threshold(k, n, |n| Ok(sub_ck(n)?.mall))?,
})
}
fn type_check<Pk, Ctx, C>(
fragment: &Terminal<Pk, Ctx>,
_child: C,
) -> Result<Self, Error<Pk, Ctx>>
where
C: FnMut(usize) -> Option<Self>,
Pk: MiniscriptKey,
Ctx: ScriptContext,
{
let wrap_err = |result: Result<Self, ErrorKind>| {
result.map_err(|kind| Error {
fragment: fragment.clone(),
error: kind,
})
};
let ret = match *fragment {
Terminal::True => Ok(Self::from_true()),
Terminal::False => Ok(Self::from_false()),
Terminal::PkK(..) => Ok(Self::from_pk_k()),
Terminal::PkH(..) => Ok(Self::from_pk_h()),
Terminal::Multi(k, ref pks) => {
if k == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroThreshold,
});
}
if k > pks.len() {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::OverThreshold(k, pks.len()),
});
}
Ok(Self::from_multi(k, pks.len()))
}
Terminal::After(t) => {
if t == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroTime,
});
}
Ok(Self::from_after(t))
}
Terminal::Older(t) => {
if t == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroTime,
});
}
Ok(Self::from_older(t))
}
Terminal::Sha256(..) => Ok(Self::from_sha256()),
Terminal::Hash256(..) => Ok(Self::from_hash256()),
Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
Terminal::Hash160(..) => Ok(Self::from_hash160()),
Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ty.clone())),
Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ty.clone())),
Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ty.clone())),
Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(sub.ty.clone())),
Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(sub.ty.clone())),
Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(sub.ty.clone())),
Terminal::ZeroNotEqual(ref sub) => wrap_err(Self::cast_zeronotequal(sub.ty.clone())),
Terminal::AndB(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::and_b(ltype, rtype))
}
Terminal::AndV(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::and_v(ltype, rtype))
}
Terminal::OrB(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::or_b(ltype, rtype))
}
Terminal::OrD(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::or_d(ltype, rtype))
}
Terminal::OrC(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::or_c(ltype, rtype))
}
Terminal::OrI(ref l, ref r) => {
let ltype = l.ty.clone();
let rtype = r.ty.clone();
wrap_err(Self::or_i(ltype, rtype))
}
Terminal::AndOr(ref a, ref b, ref c) => {
let atype = a.ty.clone();
let btype = b.ty.clone();
let ctype = c.ty.clone();
wrap_err(Self::and_or(atype, btype, ctype))
}
Terminal::Thresh(k, ref subs) => {
if k == 0 {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::ZeroThreshold,
});
}
if k > subs.len() {
return Err(Error {
fragment: fragment.clone(),
error: ErrorKind::OverThreshold(k, subs.len()),
});
}
let res = Self::threshold(k, subs.len(), |n| Ok(subs[n].ty.clone()));
res.map_err(|kind| Error {
fragment: fragment.clone(),
error: kind,
})
}
Terminal::TxTemplate(..) => Ok(Self::from_txtemplate()),
};
if let Ok(ref ret) = ret {
ret.sanity_checks()
}
ret
}
}