diff --git a/liana/src/descriptors/analysis.rs b/liana/src/descriptors/analysis.rs index 48d142c6a..884761102 100644 --- a/liana/src/descriptors/analysis.rs +++ b/liana/src/descriptors/analysis.rs @@ -464,10 +464,14 @@ pub struct LianaPolicy { impl LianaPolicy { /// Create a new Liana policy from a given configuration. + /// + /// `compile` controls whether to check the policy compiles + /// to miniscript before returning. fn _new( primary_path: PathInfo, recovery_paths: BTreeMap, is_taproot: bool, + compile: bool, ) -> Result { if recovery_paths.is_empty() { return Err(LianaPolicyError::MissingRecoveryPath); @@ -523,7 +527,9 @@ impl LianaPolicy { recovery_paths, is_taproot, }; - policy.clone().into_multipath_descriptor_fallible()?; + if compile { + policy.clone().into_multipath_descriptor_fallible()?; + } Ok(policy) } @@ -532,7 +538,12 @@ impl LianaPolicy { primary_path: PathInfo, recovery_paths: BTreeMap, ) -> Result { - Self::_new(primary_path, recovery_paths, /* is_taproot = */ true) + Self::_new( + primary_path, + recovery_paths, + /* is_taproot = */ true, + /* compile = */ true, + ) } /// Create a new Liana policy for use under a P2WSH context. @@ -540,7 +551,12 @@ impl LianaPolicy { primary_path: PathInfo, recovery_paths: BTreeMap, ) -> Result { - Self::_new(primary_path, recovery_paths, /* is_taproot = */ false) + Self::_new( + primary_path, + recovery_paths, + /* is_taproot = */ false, + /* compile = */ true, + ) } /// Create a Liana policy from a descriptor. This will check the descriptor is correctly formed @@ -631,7 +647,15 @@ impl LianaPolicy { // Use the constructor for sanity checking the keys and the Miniscript policy. Note this // makes sure the recovery paths mapping isn't empty, too. let prim_path = primary_path.ok_or(LianaPolicyError::IncompatibleDesc)?; - LianaPolicy::_new(prim_path, recovery_paths, is_taproot) + // We don't compile the policy as we assume it compiles given we started with a descriptor. + // This will still perform all other checks to make sure the descriptor conforms to + // a Liana policy. + LianaPolicy::_new( + prim_path, + recovery_paths, + is_taproot, + /* compile = */ false, + ) } pub fn primary_path(&self) -> &PathInfo { diff --git a/liana/src/descriptors/mod.rs b/liana/src/descriptors/mod.rs index dadf38fb7..e628d92a2 100644 --- a/liana/src/descriptors/mod.rs +++ b/liana/src/descriptors/mod.rs @@ -115,7 +115,10 @@ impl str::FromStr for LianaDescriptor { fn from_str(s: &str) -> Result { // Parse a descriptor and check it is a multipath descriptor corresponding to a valid Liana // spending policy. + // Sanity checks are not always performed when calling `Descriptor::from_str`, so we perform + // them explicitly. See https://github.com/rust-bitcoin/rust-miniscript/issues/734. let desc = descriptor::Descriptor::::from_str(s) + .and_then(|desc| desc.sanity_check().map(|_| desc)) .map_err(LianaDescError::Miniscript)?; LianaPolicy::from_multipath_descriptor(&desc)?;