use core::fmt;
#[cfg(feature = "std")]
use std::error;
use crate::prelude::*;
use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct ExtParams {
pub top_unsafe: bool,
pub resource_limitations: bool,
pub timelock_mixing: bool,
pub malleability: bool,
pub repeated_pk: bool,
pub raw_pkh: bool,
}
impl ExtParams {
pub fn new() -> ExtParams {
ExtParams {
top_unsafe: false,
resource_limitations: false,
timelock_mixing: false,
malleability: false,
repeated_pk: false,
raw_pkh: false,
}
}
pub fn sane() -> ExtParams {
ExtParams::new()
}
pub fn insane() -> ExtParams {
ExtParams {
top_unsafe: true,
resource_limitations: true,
timelock_mixing: true,
malleability: true,
repeated_pk: true,
raw_pkh: false,
}
}
pub fn allow_all() -> ExtParams {
ExtParams {
top_unsafe: true,
resource_limitations: true,
timelock_mixing: true,
malleability: true,
repeated_pk: true,
raw_pkh: true,
}
}
pub fn top_unsafe(mut self) -> ExtParams {
self.top_unsafe = true;
self
}
pub fn exceed_resource_limitations(mut self) -> ExtParams {
self.resource_limitations = true;
self
}
pub fn timelock_mixing(mut self) -> ExtParams {
self.timelock_mixing = true;
self
}
pub fn malleability(mut self) -> ExtParams {
self.malleability = true;
self
}
pub fn repeated_pk(mut self) -> ExtParams {
self.repeated_pk = true;
self
}
pub fn raw_pkh(mut self) -> ExtParams {
self.raw_pkh = true;
self
}
}
#[derive(Debug, PartialEq)]
pub enum AnalysisError {
SiglessBranch,
RepeatedPubkeys,
BranchExceedResouceLimits,
HeightTimelockCombination,
Malleable,
ContainsRawPkh,
}
impl fmt::Display for AnalysisError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AnalysisError::SiglessBranch => {
f.write_str("All spend paths must require a signature")
}
AnalysisError::RepeatedPubkeys => {
f.write_str("Miniscript contains repeated pubkeys or pubkeyhashes")
}
AnalysisError::BranchExceedResouceLimits => {
f.write_str("At least one spend path exceeds the resource limits(stack depth/satisfaction size..)")
}
AnalysisError::HeightTimelockCombination => {
f.write_str("Contains a combination of heightlock and timelock")
}
AnalysisError::Malleable => f.write_str("Miniscript is malleable"),
AnalysisError::ContainsRawPkh => f.write_str("Miniscript contains raw pkh"),
}
}
}
#[cfg(feature = "std")]
impl error::Error for AnalysisError {
fn cause(&self) -> Option<&dyn error::Error> {
use self::AnalysisError::*;
match self {
SiglessBranch
| RepeatedPubkeys
| BranchExceedResouceLimits
| HeightTimelockCombination
| Malleable
| ContainsRawPkh => None,
}
}
}
impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
pub fn requires_sig(&self) -> bool {
self.ty.mall.safe
}
pub fn is_non_malleable(&self) -> bool {
self.ty.mall.non_malleable
}
pub fn within_resource_limits(&self) -> bool {
Ctx::check_local_validity(self).is_ok()
}
pub fn has_mixed_timelocks(&self) -> bool {
self.ext.timelock_info.contains_unspendable_path()
}
pub fn has_repeated_keys(&self) -> bool {
let all_pkhs_len = self.iter_pk().count();
let unique_pkhs_len = self.iter_pk().collect::<HashSet<_>>().len();
unique_pkhs_len != all_pkhs_len
}
pub fn contains_raw_pkh(&self) -> bool {
self.iter().any(|ms| match ms.node {
Terminal::RawPkH(_) => true,
_ => false,
})
}
pub fn sanity_check(&self) -> Result<(), AnalysisError> {
if !self.requires_sig() {
Err(AnalysisError::SiglessBranch)
} else if !self.is_non_malleable() {
Err(AnalysisError::Malleable)
} else if !self.within_resource_limits() {
Err(AnalysisError::BranchExceedResouceLimits)
} else if self.has_repeated_keys() {
Err(AnalysisError::RepeatedPubkeys)
} else if self.has_mixed_timelocks() {
Err(AnalysisError::HeightTimelockCombination)
} else {
Ok(())
}
}
pub fn ext_check(&self, ext: &ExtParams) -> Result<(), AnalysisError> {
if !ext.top_unsafe && !self.requires_sig() {
Err(AnalysisError::SiglessBranch)
} else if !ext.malleability && !self.is_non_malleable() {
Err(AnalysisError::Malleable)
} else if !ext.resource_limitations && !self.within_resource_limits() {
Err(AnalysisError::BranchExceedResouceLimits)
} else if !ext.repeated_pk && self.has_repeated_keys() {
Err(AnalysisError::RepeatedPubkeys)
} else if !ext.timelock_mixing && self.has_mixed_timelocks() {
Err(AnalysisError::HeightTimelockCombination)
} else if !ext.raw_pkh && self.contains_raw_pkh() {
Err(AnalysisError::ContainsRawPkh)
} else {
Ok(())
}
}
}