use error;
use miniscript::iter::PkPkh;
use std::collections::HashSet;
use std::fmt;
use {Miniscript, MiniscriptKey, ScriptContext};
#[derive(Debug)]
pub enum AnalysisError {
SiglessBranch,
RepeatedPubkeys,
BranchExceedResouceLimits,
HeightTimeLockCombination,
Malleable,
}
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")
}
}
}
impl error::Error for AnalysisError {}
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 {
match Ctx::check_local_validity(&self) {
Ok(_) => true,
Err(_) => false,
}
}
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_pkh().count();
let unique_pkhs_len = self
.iter_pk_pkh()
.map(|pk_pkh| match pk_pkh {
PkPkh::PlainPubkey(pk) => pk.to_pubkeyhash(),
PkPkh::HashedPubkey(h) => h,
})
.collect::<HashSet<_>>()
.len();
unique_pkhs_len != all_pkhs_len
}
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(())
}
}
}