pub mod correctness;
pub mod extra_props;
pub mod malleability;
#[cfg(all(not(feature = "std"), not(test)))]
use alloc::string::{String, ToString};
use core::fmt;
#[cfg(feature = "std")]
use std::error;
pub use self::correctness::{Base, Correctness, Input};
pub use self::extra_props::ExtData;
pub use self::malleability::{Dissat, Malleability};
use super::ScriptContext;
use crate::{MiniscriptKey, Terminal};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum ErrorKind {
NonZeroDupIf,
LeftNotDissatisfiable,
RightNotDissatisfiable,
SwapNonOne,
NonZeroZero,
LeftNotUnit,
ChildBase1(Base),
ChildBase2(Base, Base),
ChildBase3(Base, Base, Base),
ThresholdBase(usize, Base),
ThresholdDissat(usize),
ThresholdNonUnit(usize),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Error {
pub fragment_string: String,
pub error: ErrorKind,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.error {
ErrorKind::NonZeroDupIf => write!(
f,
"fragment «{}» represents needs to be `z`, needs to consume zero elements from the stack",
self.fragment_string,
),
ErrorKind::LeftNotDissatisfiable => write!(
f,
"fragment «{}» requires its left child be dissatisfiable",
self.fragment_string,
),
ErrorKind::RightNotDissatisfiable => write!(
f,
"fragment «{}» requires its right child be dissatisfiable",
self.fragment_string,
),
ErrorKind::SwapNonOne => write!(
f,
"fragment «{}» attempts to use `SWAP` to prefix something
which does not take exactly one input",
self.fragment_string,
),
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_string,
),
ErrorKind::LeftNotUnit => write!(
f,
"fragment «{}» requires its left child be a unit (outputs
exactly 1 given a satisfying input)",
self.fragment_string,
),
ErrorKind::ChildBase1(base) => write!(
f,
"fragment «{}» cannot wrap a fragment of type {:?}",
self.fragment_string, base,
),
ErrorKind::ChildBase2(base1, base2) => write!(
f,
"fragment «{}» cannot accept children of types {:?} and {:?}",
self.fragment_string, base1, base2,
),
ErrorKind::ChildBase3(base1, base2, base3) => write!(
f,
"fragment «{}» cannot accept children of types {:?}, {:?} and {:?}",
self.fragment_string, base1, base2, base3,
),
ErrorKind::ThresholdBase(idx, base) => write!(
f,
"fragment «{}» sub-fragment {} has type {:?} rather than {:?}",
self.fragment_string,
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_string, 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_string, idx,
),
}
}
}
#[cfg(feature = "std")]
impl error::Error for Error {
fn cause(&self) -> Option<&dyn error::Error> { None }
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Type {
pub corr: Correctness,
pub mall: Malleability,
}
impl Type {
pub const TRUE: Self = Type { corr: Correctness::TRUE, mall: Malleability::TRUE };
pub const FALSE: Self = Type { corr: Correctness::FALSE, mall: Malleability::FALSE };
pub const fn is_subtype(&self, other: Self) -> bool {
self.corr.is_subtype(other.corr) && self.mall.is_subtype(other.mall)
}
pub 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);
}
pub const fn pk_k() -> Self { Type { corr: Correctness::pk_k(), mall: Malleability::pk_k() } }
pub const fn pk_h() -> Self { Type { corr: Correctness::pk_h(), mall: Malleability::pk_h() } }
pub const fn multi() -> Self {
Type { corr: Correctness::multi(), mall: Malleability::multi() }
}
pub const fn multi_a() -> Self {
Type { corr: Correctness::multi_a(), mall: Malleability::multi_a() }
}
pub const fn hash() -> Self { Type { corr: Correctness::hash(), mall: Malleability::hash() } }
pub const fn time() -> Self { Type { corr: Correctness::time(), mall: Malleability::time() } }
pub const fn cast_alt(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_alt(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_alt(self.mall),
})
}
pub const fn cast_swap(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_swap(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_swap(self.mall),
})
}
pub const fn cast_check(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_check(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_check(self.mall),
})
}
pub const fn cast_dupif(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_dupif(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_dupif(self.mall),
})
}
pub const fn cast_verify(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_verify(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_verify(self.mall),
})
}
pub const fn cast_nonzero(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_nonzero(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_nonzero(self.mall),
})
}
pub const fn cast_zeronotequal(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_zeronotequal(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_zeronotequal(self.mall),
})
}
pub const fn cast_true(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_true(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_true(self.mall),
})
}
pub const fn cast_unlikely(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_or_i_false(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_or_i_false(self.mall),
})
}
pub const fn cast_likely(self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::cast_or_i_false(self.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::cast_or_i_false(self.mall),
})
}
pub const fn and_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::and_b(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::and_b(left.mall, right.mall),
})
}
pub const fn and_v(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::and_v(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::and_v(left.mall, right.mall),
})
}
pub const fn or_b(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::or_b(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::or_b(left.mall, right.mall),
})
}
pub const fn or_d(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::or_d(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::or_d(left.mall, right.mall),
})
}
pub const fn or_c(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::or_c(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::or_c(left.mall, right.mall),
})
}
pub const fn or_i(left: Self, right: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::or_i(left.corr, right.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::or_i(left.mall, right.mall),
})
}
pub const fn and_or(a: Self, b: Self, c: Self) -> Result<Self, ErrorKind> {
Ok(Type {
corr: match Correctness::and_or(a.corr, b.corr, c.corr) {
Ok(x) => x,
Err(e) => return Err(e),
},
mall: Malleability::and_or(a.mall, b.mall, c.mall),
})
}
pub fn threshold<'a, I>(k: usize, subs: I) -> Result<Self, ErrorKind>
where
I: Clone + ExactSizeIterator<Item = &'a Self>,
{
Ok(Type {
corr: Correctness::threshold(k, subs.clone().map(|s| &s.corr))?,
mall: Malleability::threshold(k, subs.map(|s| &s.mall)),
})
}
}
impl Type {
pub fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
where
Pk: MiniscriptKey,
Ctx: ScriptContext,
{
let wrap_err = |result: Result<Self, ErrorKind>| {
result.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
};
let ret = match *fragment {
Terminal::True => Ok(Self::TRUE),
Terminal::False => Ok(Self::FALSE),
Terminal::PkK(..) => Ok(Self::pk_k()),
Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::pk_h()),
Terminal::Multi(..) => Ok(Self::multi()),
Terminal::MultiA(..) => Ok(Self::multi_a()),
Terminal::After(_) => Ok(Self::time()),
Terminal::Older(_) => Ok(Self::time()),
Terminal::Sha256(..) => Ok(Self::hash()),
Terminal::Hash256(..) => Ok(Self::hash()),
Terminal::Ripemd160(..) => Ok(Self::hash()),
Terminal::Hash160(..) => Ok(Self::hash()),
Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(sub.ty)),
Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(sub.ty)),
Terminal::Check(ref sub) => wrap_err(Self::cast_check(sub.ty)),
Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(sub.ty)),
Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(sub.ty)),
Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(sub.ty)),
Terminal::ZeroNotEqual(ref sub) => wrap_err(Self::cast_zeronotequal(sub.ty)),
Terminal::AndB(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::and_b(ltype, rtype))
}
Terminal::AndV(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::and_v(ltype, rtype))
}
Terminal::OrB(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::or_b(ltype, rtype))
}
Terminal::OrD(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::or_d(ltype, rtype))
}
Terminal::OrC(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::or_c(ltype, rtype))
}
Terminal::OrI(ref l, ref r) => {
let ltype = l.ty;
let rtype = r.ty;
wrap_err(Self::or_i(ltype, rtype))
}
Terminal::AndOr(ref a, ref b, ref c) => {
let atype = a.ty;
let btype = b.ty;
let ctype = c.ty;
wrap_err(Self::and_or(atype, btype, ctype))
}
Terminal::Thresh(ref thresh) => {
let res = Self::threshold(thresh.k(), thresh.iter().map(|ms| &ms.ty));
res.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
}
};
if let Ok(ref ret) = ret {
ret.sanity_checks()
}
ret
}
}