elements_miniscript/miniscript/
mod.rs

1// Written in 2019 by Andrew Poelstra <apoelstra@wpsoftware.net>
2// SPDX-License-Identifier: CC0-1.0
3
4//! # Abstract Syntax Tree
5//!
6//! Defines a variety of data structures for describing Miniscript, a subset of
7//! Bitcoin Script which can be efficiently parsed and serialized from Script,
8//! and from which it is easy to extract data needed to construct witnesses.
9//!
10//! Users of the library in general will only need to use the structures exposed
11//! from the top level of this module; however for people wanting to do advanced
12//! things, the submodules are public as well which provide visibility into the
13//! components of the AST.
14//!
15
16use std::marker::PhantomData;
17use std::{fmt, str};
18
19use elements::script;
20use elements::taproot::{LeafVersion, TapLeafHash};
21
22use self::analyzable::ExtParams;
23pub use self::context::{BareCtx, Legacy, Segwitv0, Tap};
24
25pub mod analyzable;
26pub mod astelem;
27pub(crate) mod context;
28pub mod decode;
29pub mod iter;
30pub mod lex;
31pub mod limits;
32pub mod satisfy;
33pub mod types;
34
35use std::cmp;
36use std::sync::Arc;
37
38use self::lex::{lex, TokenIter};
39use crate::extensions::ParseableExt;
40pub use crate::miniscript::context::ScriptContext;
41use crate::miniscript::decode::Terminal;
42use crate::miniscript::types::extra_props::ExtData;
43use crate::miniscript::types::Type;
44use crate::{
45    expression, Error, ExtTranslator, Extension, ForEachKey, MiniscriptKey, NoExt, ToPublicKey,
46    TranslateExt, TranslatePk, Translator,
47};
48#[cfg(test)]
49mod ms_tests;
50/// Top-level script AST type
51#[derive(Clone)]
52pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension = NoExt> {
53    ///A node in the Abstract Syntax Tree(
54    pub node: Terminal<Pk, Ctx, Ext>,
55    ///The correctness and malleability type information for the AST node
56    pub ty: types::Type,
57    ///Additional information helpful for extra analysis.
58    pub ext: types::extra_props::ExtData,
59    /// Context PhantomData. Only accessible inside this crate
60    pub(crate) phantom: PhantomData<Ctx>,
61}
62
63/// `PartialOrd` of `Miniscript` must depend only on node and not the type information.
64/// The type information and extra_properties can be deterministically determined
65/// by the ast.
66impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> PartialOrd
67    for Miniscript<Pk, Ctx, Ext>
68{
69    fn partial_cmp(&self, other: &Miniscript<Pk, Ctx, Ext>) -> Option<cmp::Ordering> {
70        Some(self.node.cmp(&other.node))
71    }
72}
73
74/// `Ord` of `Miniscript` must depend only on node and not the type information.
75/// The type information and extra_properties can be deterministically determined
76/// by the ast.
77impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Ord for Miniscript<Pk, Ctx, Ext> {
78    fn cmp(&self, other: &Miniscript<Pk, Ctx, Ext>) -> cmp::Ordering {
79        self.node.cmp(&other.node)
80    }
81}
82
83/// `PartialEq` of `Miniscript` must depend only on node and not the type information.
84/// The type information and extra_properties can be deterministically determined
85/// by the ast.
86impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> PartialEq for Miniscript<Pk, Ctx, Ext> {
87    fn eq(&self, other: &Miniscript<Pk, Ctx, Ext>) -> bool {
88        self.node.eq(&other.node)
89    }
90}
91
92impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> ::std::hash::Hash
93    for Miniscript<Pk, Ctx, Ext>
94{
95    fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
96        self.node.hash(state);
97    }
98}
99
100/// `Eq` of `Miniscript` must depend only on node and not the type information.
101/// The type information and extra_properties can be deterministically determined
102/// by the ast.
103impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Eq for Miniscript<Pk, Ctx, Ext> {}
104
105impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> fmt::Debug
106    for Miniscript<Pk, Ctx, Ext>
107{
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        write!(f, "{:?}", self.node)
110    }
111}
112
113impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Miniscript<Pk, Ctx, Ext> {
114    /// Add type information(Type and Extdata) to Miniscript based on
115    /// `AstElem` fragment. Dependent on display and clone because of Error
116    /// Display code of type_check.
117    pub fn from_ast(t: Terminal<Pk, Ctx, Ext>) -> Result<Miniscript<Pk, Ctx, Ext>, Error> {
118        Ok(Miniscript {
119            ty: Type::type_check(&t)?,
120            ext: ExtData::type_check(&t)?,
121            node: t,
122            phantom: PhantomData,
123        })
124    }
125}
126
127impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> fmt::Display
128    for Miniscript<Pk, Ctx, Ext>
129{
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        write!(f, "{}", self.node)
132    }
133}
134
135impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Miniscript<Pk, Ctx, Ext> {
136    /// Extracts the `AstElem` representing the root of the miniscript
137    pub fn into_inner(self) -> Terminal<Pk, Ctx, Ext> {
138        self.node
139    }
140
141    /// Get a reference to the inner `AstElem` representing the root of miniscript
142    pub fn as_inner(&self) -> &Terminal<Pk, Ctx, Ext> {
143        &self.node
144    }
145}
146
147impl<Ctx, Ext> Miniscript<Ctx::Key, Ctx, Ext>
148where
149    Ctx: ScriptContext,
150    Ext: ParseableExt,
151{
152    /// Attempt to parse an insane(scripts don't clear sanity checks)
153    /// script into a Miniscript representation.
154    /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
155    /// scripts without sig or scripts that can exceed resource limits.
156    /// Some of the analysis guarantees of miniscript are lost when dealing with
157    /// insane scripts. In general, in a multi-party setting users should only
158    /// accept sane scripts.
159    pub fn parse_insane(script: &script::Script) -> Result<Miniscript<Ctx::Key, Ctx, Ext>, Error> {
160        Miniscript::parse_with_ext(script, &ExtParams::insane())
161    }
162
163    /// Attempt to parse an miniscript with extra features that not yet specified in the spec.
164    /// Users should not use this function unless they scripts can/will change in the future.
165    /// Currently, this function supports the following features:
166    ///     - Parsing all insane scripts
167    ///     - Parsing miniscripts with raw pubkey hashes
168    ///
169    /// Allowed extra features can be specified by the ext [`ExtParams`] argument.
170    pub fn parse_with_ext(
171        script: &script::Script,
172        ext: &ExtParams,
173    ) -> Result<Miniscript<Ctx::Key, Ctx, Ext>, Error> {
174        let tokens = lex(script)?;
175        let mut iter = TokenIter::new(tokens);
176
177        let top = decode::parse(&mut iter)?;
178        Ctx::check_global_validity(&top)?;
179        let type_check = types::Type::type_check(&top.node)?;
180        if type_check.corr.base != types::Base::B {
181            return Err(Error::NonTopLevel(format!("{:?}", top)));
182        };
183        if let Some(leading) = iter.next() {
184            Err(Error::Trailing(leading.to_string()))
185        } else {
186            top.ext_check(ext)?;
187            Ok(top)
188        }
189    }
190
191    /// Attempt to parse a Script into Miniscript representation.
192    /// This function will fail parsing for scripts that do not clear
193    /// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
194    /// parse such scripts.
195    ///
196    /// ## Decode/Parse a miniscript from script hex
197    ///
198    /// ```rust
199    ///
200    /// use elements_miniscript::Miniscript;
201    /// use elements_miniscript::{Segwitv0, Tap};
202    /// use elements_miniscript::bitcoin::secp256k1::XOnlyPublicKey;
203    /// type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0>;
204    /// type TapScript = Miniscript<XOnlyPublicKey, Tap>;
205    /// use elements::hex::FromHex;
206    ///
207    /// // parse x-only miniscript in Taproot context
208    /// let tapscript_ms = TapScript::parse(&elements::Script::from(Vec::<u8>::from_hex(
209    ///     "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
210    /// ).expect("Even length hex")))
211    /// .expect("Xonly keys are valid only in taproot context");
212    /// // tapscript fails decoding when we use them with compressed keys
213    /// let err = TapScript::parse(&elements::Script::from(Vec::<u8>::from_hex(
214    ///     "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
215    /// ).expect("Even length hex")))
216    /// .expect_err("Compressed keys cannot be used in Taproot context");
217    /// // Segwitv0 succeeds decoding with full keys.
218    /// Segwitv0Script::parse(&elements::Script::from(Vec::<u8>::from_hex(
219    ///     "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
220    /// ).expect("Even length hex")))
221    /// .expect("Compressed keys are allowed in Segwit context");
222    /// ```
223    pub fn parse(script: &script::Script) -> Result<Miniscript<Ctx::Key, Ctx, Ext>, Error> {
224        let ms = Self::parse_with_ext(script, &ExtParams::sane())?;
225        ms.sanity_check()?;
226        Ok(ms)
227    }
228}
229
230impl<Pk, Ctx, Ext> Miniscript<Pk, Ctx, Ext>
231where
232    Pk: MiniscriptKey,
233    Ctx: ScriptContext,
234    Ext: Extension,
235{
236    /// Encode as a Bitcoin script
237    pub fn encode(&self) -> script::Script
238    where
239        Pk: ToPublicKey,
240        Ext: ParseableExt,
241    {
242        self.node.encode(script::Builder::new()).into_script()
243    }
244
245    /// Size, in bytes of the script-pubkey. If this Miniscript is used outside
246    /// of segwit (e.g. in a bare or P2SH descriptor), this quantity should be
247    /// multiplied by 4 to compute the weight.
248    ///
249    /// In general, it is not recommended to use this function directly, but
250    /// to instead call the corresponding function on a `Descriptor`, which
251    /// will handle the segwit/non-segwit technicalities for you.
252    pub fn script_size(&self) -> usize {
253        self.node.script_size()
254    }
255}
256
257impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Miniscript<Pk, Ctx, Ext> {
258    /// Maximum number of witness elements used to satisfy the Miniscript
259    /// fragment, including the witness script itself. Used to estimate
260    /// the weight of the `VarInt` that specifies this number in a serialized
261    /// transaction.
262    ///
263    /// This function may returns Error when the Miniscript is
264    /// impossible to satisfy
265    pub fn max_satisfaction_witness_elements(&self) -> Result<usize, Error> {
266        self.ext
267            .stack_elem_count_sat
268            .map(|x| x + 1)
269            .ok_or(Error::ImpossibleSatisfaction)
270    }
271
272    /// Maximum size, in bytes, of a satisfying witness. For Segwit outputs
273    /// `one_cost` should be set to 2, since the number `1` requires two
274    /// bytes to encode. For non-segwit outputs `one_cost` should be set to
275    /// 1, since `OP_1` is available in scriptSigs.
276    ///
277    /// In general, it is not recommended to use this function directly, but
278    /// to instead call the corresponding function on a `Descriptor`, which
279    /// will handle the segwit/non-segwit technicalities for you.
280    ///
281    /// All signatures are assumed to be 73 bytes in size, including the
282    /// length prefix (segwit) or push opcode (pre-segwit) and sighash
283    /// postfix.
284    pub fn max_satisfaction_size(&self) -> Result<usize, Error> {
285        Ctx::max_satisfaction_size::<Pk, Ext>(self).ok_or(Error::ImpossibleSatisfaction)
286    }
287}
288
289impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> ForEachKey<Pk>
290    for Miniscript<Pk, Ctx, Ext>
291{
292    fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool
293    where
294        Pk: 'a,
295    {
296        self.real_for_each_key(&mut pred)
297    }
298}
299
300impl<Pk, Q, Ctx, Ext> TranslatePk<Pk, Q> for Miniscript<Pk, Ctx, Ext>
301where
302    Pk: MiniscriptKey,
303    Q: MiniscriptKey,
304    Ctx: ScriptContext,
305    Ext: Extension,
306{
307    type Output = Miniscript<Q, Ctx, Ext>;
308
309    /// Translates a struct from one generic to another where the translation
310    /// for Pk is provided by [`Translator`]
311    fn translate_pk<T, E>(&self, translate: &mut T) -> Result<Self::Output, E>
312    where
313        T: Translator<Pk, Q, E>,
314    {
315        self.real_translate_pk(translate)
316    }
317}
318
319impl<Pk, Ctx, Ext, ExtQ> TranslateExt<Ext, ExtQ> for Miniscript<Pk, Ctx, Ext>
320where
321    Pk: MiniscriptKey,
322    Ctx: ScriptContext,
323    Ext: Extension + TranslateExt<Ext, ExtQ, Output = ExtQ>,
324    ExtQ: Extension,
325{
326    type Output = Miniscript<Pk, Ctx, ExtQ>;
327
328    fn translate_ext<T, E>(&self, t: &mut T) -> Result<Self::Output, E>
329    where
330        T: ExtTranslator<Ext, ExtQ, E>,
331    {
332        self.real_translate_ext(t)
333    }
334}
335
336impl<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension> Miniscript<Pk, Ctx, Ext> {
337    fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool
338    where
339        Pk: 'a,
340    {
341        self.node.real_for_each_key(pred)
342    }
343
344    pub(super) fn real_translate_pk<Q, CtxQ, T, FuncError>(
345        &self,
346        t: &mut T,
347    ) -> Result<Miniscript<Q, CtxQ, Ext>, FuncError>
348    where
349        Q: MiniscriptKey,
350        CtxQ: ScriptContext,
351        T: Translator<Pk, Q, FuncError>,
352        Ctx: ScriptContext,
353        Ext: Extension,
354    {
355        let inner = self.node.real_translate_pk(t)?;
356        let ms = Miniscript {
357            //directly copying the type and ext is safe because translating public
358            //key should not change any properties
359            ty: self.ty,
360            ext: self.ext,
361            node: inner,
362            phantom: PhantomData,
363        };
364        Ok(ms)
365    }
366
367    pub(super) fn real_translate_ext<T, FuncError, ExtQ>(
368        &self,
369        t: &mut T,
370    ) -> Result<Miniscript<Pk, Ctx, ExtQ>, FuncError>
371    where
372        ExtQ: Extension,
373        T: ExtTranslator<Ext, ExtQ, FuncError>,
374        Ext: TranslateExt<Ext, ExtQ, Output = ExtQ>,
375    {
376        let inner = self.node.real_translate_ext(t)?;
377        let ms = Miniscript {
378            //directly copying the type and ext is safe because translating public
379            //key should not change any properties
380            ty: self.ty,
381            ext: self.ext,
382            node: inner,
383            phantom: PhantomData,
384        };
385        Ok(ms)
386    }
387}
388
389impl_block_str!(
390    ;Ctx; ScriptContext,
391    Miniscript<Pk, Ctx, Ext>,
392    => Ext ; Extension,
393    /// Attempt to parse an insane(scripts don't clear sanity checks)
394    /// from string into a Miniscript representation.
395    /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
396    /// scripts without sig or scripts that can exceed resource limits.
397    /// Some of the analysis guarantees of miniscript are lost when dealing with
398    /// insane scripts. In general, in a multi-party setting users should only
399    /// accept sane scripts.
400    pub fn from_str_insane(s: &str, ) -> Result<Miniscript<Pk, Ctx, Ext>, Error>
401    {
402        Miniscript::from_str_ext(s, &ExtParams::insane())
403    }
404);
405
406impl_block_str!(
407    ;Ctx; ScriptContext,
408    Miniscript<Pk, Ctx, Ext>,
409    => Ext ; Extension,
410    /// Attempt to parse an Miniscripts that don't follow the spec.
411    /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
412    /// scripts, raw pubkey hashes without sig or scripts that can exceed resource limits.
413    ///
414    /// Use [`ExtParams`] builder to specify the types of non-sane rules to allow while parsing.
415    pub fn from_str_ext(s: &str, ext: &ExtParams,) -> Result<Miniscript<Pk, Ctx, Ext>, Error>
416    {
417        // This checks for invalid ASCII chars
418        let top = expression::Tree::from_str(s)?;
419        let ms: Miniscript<Pk, Ctx, Ext> = expression::FromTree::from_tree(&top)?;
420        ms.ext_check(ext)?;
421
422        if ms.ty.corr.base != types::Base::B {
423            Err(Error::NonTopLevel(format!("{:?}", ms)))
424        } else {
425            Ok(ms)
426        }
427    }
428);
429
430impl<Pk: ToPublicKey, Ctx: ScriptContext, Ext: ParseableExt> Miniscript<Pk, Ctx, Ext> {
431    /// Attempt to produce non-malleable satisfying witness for the
432    /// witness script represented by the parse tree
433    pub fn satisfy<S: satisfy::Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error> {
434        // Only satisfactions for default versions (0xc0) are allowed.
435        let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::default());
436        match satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, &leaf_hash)
437            .stack
438        {
439            satisfy::Witness::Stack(stack) => {
440                Ctx::check_witness::<Pk, Ext>(&stack)?;
441                Ok(stack)
442            }
443            satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
444                Err(Error::CouldNotSatisfy)
445            }
446        }
447    }
448
449    /// Attempt to produce a malleable satisfying witness for the
450    /// witness script represented by the parse tree
451    pub fn satisfy_malleable<S: satisfy::Satisfier<Pk>>(
452        &self,
453        satisfier: S,
454    ) -> Result<Vec<Vec<u8>>, Error> {
455        let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::default());
456        match satisfy::Satisfaction::satisfy_mall(
457            &self.node,
458            &satisfier,
459            self.ty.mall.safe,
460            &leaf_hash,
461        )
462        .stack
463        {
464            satisfy::Witness::Stack(stack) => {
465                Ctx::check_witness::<Pk, Ext>(&stack)?;
466                Ok(stack)
467            }
468            satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
469                Err(Error::CouldNotSatisfy)
470            }
471        }
472    }
473}
474
475impl_from_tree!(
476    ;Ctx; ScriptContext,
477    Arc<Miniscript<Pk, Ctx, Ext>>,
478    => Ext ; Extension,
479    fn from_tree(top: &expression::Tree<'_>) -> Result<Arc<Miniscript<Pk, Ctx, Ext>>, Error> {
480        Ok(Arc::new(expression::FromTree::from_tree(top)?))
481    }
482);
483
484impl_from_tree!(
485    ;Ctx; ScriptContext,
486    Miniscript<Pk, Ctx, Ext>,
487    => Ext ; Extension,
488    /// Parse an expression tree into a Miniscript. As a general rule, this
489    /// should not be called directly; rather go through the descriptor API.
490    fn from_tree(top: &expression::Tree<'_>) -> Result<Miniscript<Pk, Ctx, Ext>, Error> {
491        let inner: Terminal<Pk, Ctx, Ext> = expression::FromTree::from_tree(top)?;
492        Ok(Miniscript {
493            ty: Type::type_check(&inner)?,
494            ext: ExtData::type_check(&inner)?,
495            node: inner,
496            phantom: PhantomData,
497        })
498    }
499);
500
501impl_from_str!(
502    ;Ctx; ScriptContext,
503    Miniscript<Pk, Ctx, Ext>,
504    => Ext ; Extension,
505    type Err = Error;,
506    /// Parse a Miniscript from string and perform sanity checks
507    /// See [Miniscript::from_str_insane] to parse scripts from string that
508    /// do not clear the [Miniscript::sanity_check] checks.
509    fn from_str(s: &str) -> Result<Miniscript<Pk, Ctx, Ext>, Error> {
510        let ms = Self::from_str_ext(s, &ExtParams::sane())?;
511        ms.sanity_check()?;
512        Ok(ms)
513    }
514);
515
516serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext => Ext2 ; Extension);
517
518/// Provides a Double SHA256 `Hash` type that displays forwards.
519pub mod hash256 {
520    use bitcoin::hashes::{hash_newtype, sha256d};
521
522    hash_newtype! {
523        /// A hash256 of preimage.
524        #[hash_newtype(forward)]
525        pub struct Hash(sha256d::Hash);
526    }
527}
528
529#[cfg(test)]
530mod tests {
531
532    use std::marker::PhantomData;
533    use std::str;
534    use std::str::FromStr;
535    use std::sync::Arc;
536
537    use bitcoin::key::XOnlyPublicKey;
538    use bitcoin::{self};
539    use elements::hashes::{hash160, sha256, Hash};
540    use elements::taproot::TapLeafHash;
541    use elements::{self, secp256k1_zkp, Sequence};
542
543    use super::{Miniscript, ScriptContext, Segwitv0, Tap};
544    use crate::extensions::CovExtArgs;
545    use crate::miniscript::types::{self, ExtData, Property, Type};
546    use crate::miniscript::Terminal;
547    use crate::policy::Liftable;
548    use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator};
549    use crate::{hex_script, CovenantExt, ExtParams, NoExt, Satisfier, ToPublicKey, TranslatePk};
550
551    type Tapscript = Miniscript<XOnlyPublicKey, Tap, NoExt>;
552    type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0, CovenantExt<CovExtArgs>>;
553
554    fn pubkeys(n: usize) -> Vec<bitcoin::PublicKey> {
555        let mut ret = Vec::with_capacity(n);
556        let secp = secp256k1_zkp::Secp256k1::new();
557        let mut sk = [0; 32];
558        for i in 1..n + 1 {
559            sk[0] = i as u8;
560            sk[1] = (i >> 8) as u8;
561            sk[2] = (i >> 16) as u8;
562
563            let pk = bitcoin::PublicKey {
564                inner: secp256k1_zkp::PublicKey::from_secret_key(
565                    &secp,
566                    &secp256k1_zkp::SecretKey::from_slice(&sk[..]).expect("secret key"),
567                ),
568                compressed: true,
569            };
570            ret.push(pk);
571        }
572        ret
573    }
574
575    fn string_rtt(script: Segwitv0Script, expected_debug: &str, expected_display: &str) {
576        assert_eq!(script.ty.corr.base, types::Base::B);
577        let debug = format!("{:?}", script);
578        let display = format!("{}", script);
579        if let Some(expected) = expected_debug.into() {
580            assert_eq!(debug, expected);
581        }
582        if let Some(expected) = expected_display.into() {
583            assert_eq!(display, expected);
584        }
585        let roundtrip = Miniscript::from_str(&display).expect("parse string serialization");
586        assert_eq!(roundtrip, script);
587    }
588
589    fn string_display_debug_test<Ctx: ScriptContext>(
590        script: Miniscript<bitcoin::PublicKey, Ctx, CovenantExt<CovExtArgs>>,
591        expected_debug: &str,
592        expected_display: &str,
593    ) {
594        assert_eq!(script.ty.corr.base, types::Base::B);
595        let debug = format!("{:?}", script);
596        let display = format!("{}", script);
597        if let Some(expected) = expected_debug.into() {
598            assert_eq!(debug, expected);
599        }
600        if let Some(expected) = expected_display.into() {
601            assert_eq!(display, expected);
602        }
603    }
604
605    fn dummy_string_rtt<Ctx: ScriptContext>(
606        script: Miniscript<String, Ctx, NoExt>,
607        expected_debug: &str,
608        expected_display: &str,
609    ) {
610        assert_eq!(script.ty.corr.base, types::Base::B);
611        let debug = format!("{:?}", script);
612        let display = format!("{}", script);
613        if let Some(expected) = expected_debug.into() {
614            assert_eq!(debug, expected);
615        }
616        if let Some(expected) = expected_display.into() {
617            assert_eq!(display, expected);
618        }
619        let roundtrip = Miniscript::from_str(&display).expect("parse string serialization");
620        assert_eq!(roundtrip, script);
621    }
622
623    fn script_rtt<Str1: Into<Option<&'static str>>>(script: Segwitv0Script, expected_hex: Str1) {
624        assert_eq!(script.ty.corr.base, types::Base::B);
625        let bitcoin_script = script.encode();
626        assert_eq!(bitcoin_script.len(), script.script_size());
627        if let Some(expected) = expected_hex.into() {
628            assert_eq!(format!("{:x}", bitcoin_script), expected);
629        }
630        // Parse scripts with all extensions
631        let roundtrip = Segwitv0Script::parse_with_ext(&bitcoin_script, &ExtParams::allow_all())
632            .expect("parse string serialization");
633        assert_eq!(roundtrip, script);
634    }
635
636    fn roundtrip(tree: &Segwitv0Script, s: &str) {
637        assert_eq!(tree.ty.corr.base, types::Base::B);
638        let ser = tree.encode();
639        assert_eq!(ser.len(), tree.script_size());
640        assert_eq!(ser.to_string(), s);
641        let deser = Segwitv0Script::parse_insane(&ser).expect("deserialize result of serialize");
642        assert_eq!(*tree, deser);
643    }
644
645    fn ms_attributes_test(
646        ms: &str,
647        expected_hex: &str,
648        valid: bool,
649        non_mal: bool,
650        need_sig: bool,
651        ops: usize,
652        _stack: usize,
653    ) {
654        let ms: Result<Segwitv0Script, _> = Miniscript::from_str_insane(ms);
655        match (ms, valid) {
656            (Ok(ms), true) => {
657                assert_eq!(format!("{:x}", ms.encode()), expected_hex);
658                assert_eq!(ms.ty.mall.non_malleable, non_mal);
659                assert_eq!(ms.ty.mall.safe, need_sig);
660                assert_eq!(ms.ext.ops.op_count().unwrap(), ops);
661            }
662            (Err(_), false) => {}
663            _ => unreachable!(),
664        }
665    }
666
667    #[test]
668    fn all_attribute_tests() {
669        ms_attributes_test(
670            "lltvln:after(1231488000)",
671            "6300676300676300670400046749b1926869516868",
672            true,
673            true,
674            false,
675            12,
676            3,
677        );
678        ms_attributes_test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", true, true, true, 14, 5);
679        ms_attributes_test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", true, false, false, 14, 5);
680        ms_attributes_test(
681            "j:and_v(vdv:after(1567547623),older(2016))",
682            "829263766304e7e06e5db169686902e007b268",
683            true,
684            true,
685            false,
686            11,
687            1,
688        );
689        ms_attributes_test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", true, true, false, 12, 3);
690        ms_attributes_test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", true, true, false, 13, 5);
691        ms_attributes_test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", true, true, false, 15, 7);
692        ms_attributes_test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", true, false, false, 16, 1);
693        ms_attributes_test("and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))", "63522102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee52103774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb52af67522103e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a21025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc52af6882012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c6887", true, true, true, 11, 5);
694        ms_attributes_test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", true, false, true, 14, 4);
695        ms_attributes_test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", true, false, false, 12, 1);
696        ms_attributes_test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", true, false, false, 16, 2);
697        ms_attributes_test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", true, true, false, 15, 2);
698        ms_attributes_test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", true, true, true, 13, 6);
699        ms_attributes_test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", true, false, false, 14, 2);
700        ms_attributes_test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", true, false, false, 15, 2);
701        ms_attributes_test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", true, false, true, 9, 2);
702        ms_attributes_test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", true, true, true, 10, 5);
703        ms_attributes_test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", true, false, false, 14, 2);
704        ms_attributes_test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", true, false, false, 20, 2);
705        ms_attributes_test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", true, true, false, 10, 2);
706        ms_attributes_test("thresh(2,c:pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a91420d637c1a6404d2227f3561fdbaff5a680dba64888ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", true, false, false, 18, 4);
707        ms_attributes_test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", true, false, true, 13, 3);
708        ms_attributes_test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", true, true, true, 12, 2);
709        ms_attributes_test("c:or_i(and_v(v:older(16),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5))", "6360b26976a91420d637c1a6404d2227f3561fdbaff5a680dba648886776a9148f9dff39a81ee4abcbad2ad8bafff090415a2be88868ac", true, true, true, 12, 3);
710        ms_attributes_test("or_d(c:pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a9148f9dff39a81ee4abcbad2ad8bafff090415a2be888ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", true, true, false, 13, 3);
711        ms_attributes_test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a9148f9dff39a81ee4abcbad2ad8bafff090415a2be8886776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", true, false, true, 18, 3);
712        ms_attributes_test("c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5)))", "6382012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba87670068646376a914385defb0ed10fe95817943ed37b4984f8f4255d6886776a9148f9dff39a81ee4abcbad2ad8bafff090415a2be888686776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", true, false, true, 23, 4);
713        ms_attributes_test("c:or_i(andor(c:pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),pk_h(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "6376a9148f9dff39a81ee4abcbad2ad8bafff090415a2be888ac6476a91420d637c1a6404d2227f3561fdbaff5a680dba648886776a914385defb0ed10fe95817943ed37b4984f8f4255d68868672103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a146029755668ac", true, true, true, 17, 5);
714    }
715
716    #[test]
717    fn recursive_key_parsing() {
718        type MsStr = Miniscript<String, Segwitv0, NoExt>;
719        assert!(MsStr::from_str("pk(musig(a))").is_ok());
720        assert!(MsStr::from_str("pk(musig(a,b))").is_ok());
721        assert!(MsStr::from_str("pk(musig(a,musig(b,c,d)))").is_ok());
722        assert!(MsStr::from_str("pk(musig(a,musig(b,c,musig(d,e,f,musig(g,h,i)))))").is_ok());
723        assert!(MsStr::from_str("pk(musig(musig(a,b),musig(c,d)))").is_ok());
724        // should err on slip7
725        assert!(MsStr::from_str("pk(slip7(k))").is_err());
726        // should err on multi args to pk
727        assert!(MsStr::from_str("pk(musig(a,b),musig(c,d))").is_err());
728        assert!(MsStr::from_str("musig").is_err());
729        assert!(MsStr::from_str("slip77").is_err());
730    }
731    #[test]
732    fn basic() {
733        let pk = bitcoin::PublicKey::from_str(
734            "\
735             020202020202020202020202020202020202020202020202020202020202020202\
736             ",
737        )
738        .unwrap();
739        let hash = hash160::Hash::from_byte_array([17; 20]);
740
741        let pkk_ms: Miniscript<String, Segwitv0> = Miniscript {
742            node: Terminal::Check(Arc::new(Miniscript {
743                node: Terminal::PkK(String::from("")),
744                ty: Type::from_pk_k::<Segwitv0>(),
745                ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
746                phantom: PhantomData,
747            })),
748            ty: Type::cast_check(Type::from_pk_k::<Segwitv0>()).unwrap(),
749            ext: ExtData::cast_check(ExtData::from_pk_k::<Segwitv0>()).unwrap(),
750            phantom: PhantomData,
751        };
752        dummy_string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(\"\")", "pk()");
753
754        let pkh_ms: Miniscript<String, Segwitv0> = Miniscript {
755            node: Terminal::Check(Arc::new(Miniscript {
756                node: Terminal::PkH(String::from("")),
757                ty: Type::from_pk_h::<Segwitv0>(),
758                ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
759                phantom: PhantomData,
760            })),
761            ty: Type::cast_check(Type::from_pk_h::<Segwitv0>()).unwrap(),
762            ext: ExtData::cast_check(ExtData::from_pk_h::<Segwitv0>()).unwrap(),
763            phantom: PhantomData,
764        };
765
766        let expected_debug = "[B/nduesm]c:[K/nduesm]pk_h(\"\")";
767        let expected_display = "pkh()";
768
769        assert_eq!(pkh_ms.ty.corr.base, types::Base::B);
770        let debug = format!("{:?}", pkh_ms);
771        let display = format!("{}", pkh_ms);
772        if let Some(expected) = expected_debug.into() {
773            assert_eq!(debug, expected);
774        }
775        if let Some(expected) = expected_display.into() {
776            assert_eq!(display, expected);
777        }
778
779        let pkk_ms: Segwitv0Script = Miniscript {
780            node: Terminal::Check(Arc::new(Miniscript {
781                node: Terminal::PkK(pk),
782                ty: Type::from_pk_k::<Segwitv0>(),
783                ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
784                phantom: PhantomData,
785            })),
786            ty: Type::cast_check(Type::from_pk_k::<Segwitv0>()).unwrap(),
787            ext: ExtData::cast_check(ExtData::from_pk_k::<Segwitv0>()).unwrap(),
788            phantom: PhantomData,
789        };
790
791        script_rtt(
792            pkk_ms,
793            "21020202020202020202020202020202020202020202020202020202020\
794             202020202ac",
795        );
796
797        let pkh_ms: Segwitv0Script = Miniscript {
798            node: Terminal::Check(Arc::new(Miniscript {
799                node: Terminal::RawPkH(hash),
800                ty: Type::from_pk_h::<Segwitv0>(),
801                ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
802                phantom: PhantomData,
803            })),
804            ty: Type::cast_check(Type::from_pk_h::<Segwitv0>()).unwrap(),
805            ext: ExtData::cast_check(ExtData::from_pk_h::<Segwitv0>()).unwrap(),
806            phantom: PhantomData,
807        };
808
809        script_rtt(pkh_ms, "76a914111111111111111111111111111111111111111188ac");
810    }
811
812    #[test]
813    fn true_false() {
814        roundtrip(&ms_str!("1"), "Script(OP_PUSHNUM_1)");
815        roundtrip(
816            &ms_str!("tv:1"),
817            "Script(OP_PUSHNUM_1 OP_VERIFY OP_PUSHNUM_1)",
818        );
819        roundtrip(
820            &ms_str!("tv:thresh(1,pk(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", ),
821            "Script(OP_PUSHBYTES_33 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e OP_CHECKSIG OP_PUSHNUM_1 OP_EQUALVERIFY OP_PUSHNUM_1)",
822        );
823        roundtrip(&ms_str!("0"), "Script(OP_0)");
824        roundtrip(
825            &ms_str!("andor(0,1,0)"),
826            "Script(OP_0 OP_NOTIF OP_0 OP_ELSE OP_PUSHNUM_1 OP_ENDIF)",
827        );
828
829        assert!(Segwitv0Script::from_str("1()").is_err());
830        assert!(Segwitv0Script::from_str("tv:1()").is_err());
831    }
832
833    #[test]
834    fn verify_parse() {
835        let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
836        let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
837        assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
838
839        let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
840        let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
841        assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
842
843        let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
844        let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
845        assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
846
847        let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
848        let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
849        assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
850    }
851
852    #[test]
853    fn pk_alias() {
854        let pubkey = pubkeys(1)[0];
855
856        let script: Segwitv0Script = ms_str!("c:pk_k({})", pubkey.to_string());
857
858        string_rtt(
859            script,
860            "[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
861            "pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
862        );
863
864        let script: Segwitv0Script = ms_str!("pk({})", pubkey.to_string());
865
866        string_rtt(
867            script,
868            "[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
869            "pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
870        );
871
872        let script: Segwitv0Script = ms_str!("tv:pk({})", pubkey.to_string());
873
874        string_rtt(
875            script,
876            "[B/onufsm]t[V/onfsm]v[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
877            "tv:pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
878        );
879
880        let script: Segwitv0Script = ms_str!("c:pk_h({})", pubkey.to_string());
881
882        string_display_debug_test(
883            script,
884            "[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
885            "pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
886        );
887
888        let script: Segwitv0Script = ms_str!("pkh({})", pubkey.to_string());
889
890        string_display_debug_test(
891            script,
892            "[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
893            "pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
894        );
895
896        let script: Segwitv0Script = ms_str!("tv:pkh({})", pubkey.to_string());
897
898        string_display_debug_test(
899            script,
900            "[B/nufsm]t[V/nfsm]v[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
901            "tv:pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
902        );
903    }
904
905    #[test]
906    fn serialize() {
907        let keys = pubkeys(6);
908
909        let tree: &Segwitv0Script = &ms_str!("c:pk_h({})", keys[5]);
910        assert_eq!(tree.ty.corr.base, types::Base::B);
911        let ser = tree.encode();
912        let s = "\
913             Script(OP_DUP OP_HASH160 OP_PUSHBYTES_20 \
914             7e5a2a6a7610ca4ea78bd65a087bd75b1870e319 \
915             OP_EQUALVERIFY OP_CHECKSIG)\
916             ";
917        assert_eq!(ser.len(), tree.script_size());
918        assert_eq!(ser.to_string(), s);
919
920        roundtrip(
921            &ms_str!("pk({})", keys[0]),
922            "Script(OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa OP_CHECKSIG)"
923        );
924        roundtrip(
925            &ms_str!("multi(3,{},{},{},{},{})", keys[0], keys[1], keys[2], keys[3], keys[4]),
926            "Script(OP_PUSHNUM_3 OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa OP_PUSHBYTES_33 03ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2 OP_PUSHBYTES_33 039729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40 OP_PUSHBYTES_33 032564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa OP_PUSHBYTES_33 0289637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff OP_PUSHNUM_5 OP_CHECKMULTISIG)"
927        );
928
929        // Liquid policy
930        roundtrip(
931            &ms_str!("or_d(multi(2,{},{}),and_v(v:multi(2,{},{}),older(10000)))",
932                      keys[0].to_string(),
933                      keys[1].to_string(),
934                      keys[3].to_string(),
935                      keys[4].to_string()),
936            "Script(OP_PUSHNUM_2 OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa \
937                                  OP_PUSHBYTES_33 03ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2 \
938                                  OP_PUSHNUM_2 OP_CHECKMULTISIG \
939                     OP_IFDUP OP_NOTIF \
940                         OP_PUSHNUM_2 OP_PUSHBYTES_33 032564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa \
941                                      OP_PUSHBYTES_33 0289637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff \
942                                      OP_PUSHNUM_2 OP_CHECKMULTISIGVERIFY \
943                         OP_PUSHBYTES_2 1027 OP_CSV \
944                     OP_ENDIF)"
945        );
946
947        let miniscript: Segwitv0Script = ms_str!(
948            "or_d(multi(3,{},{},{}),and_v(v:multi(2,{},{}),older(10000)))",
949            keys[0].to_string(),
950            keys[1].to_string(),
951            keys[2].to_string(),
952            keys[3].to_string(),
953            keys[4].to_string(),
954        );
955
956        let mut abs = miniscript.lift().unwrap();
957        assert_eq!(abs.n_keys(), 5);
958        assert_eq!(abs.minimum_n_keys(), Some(2));
959        abs = abs.at_age(Sequence::from_height(10000));
960        assert_eq!(abs.n_keys(), 5);
961        assert_eq!(abs.minimum_n_keys(), Some(2));
962        abs = abs.at_age(Sequence::from_height(9999));
963        assert_eq!(abs.n_keys(), 3);
964        assert_eq!(abs.minimum_n_keys(), Some(3));
965        abs = abs.at_age(Sequence::ZERO);
966        assert_eq!(abs.n_keys(), 3);
967        assert_eq!(abs.minimum_n_keys(), Some(3));
968
969        roundtrip(&ms_str!("older(921)"), "Script(OP_PUSHBYTES_2 9903 OP_CSV)");
970
971        roundtrip(
972            &ms_str!("sha256({})",sha256::Hash::hash(&[])),
973            "Script(OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 OP_PUSHBYTES_32 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 OP_EQUAL)"
974        );
975
976        roundtrip(
977            &ms_str!(
978                "multi(3,{},{},{},{},{})",
979                keys[0],
980                keys[1],
981                keys[2],
982                keys[3],
983                keys[4]
984            ),
985            "Script(OP_PUSHNUM_3 \
986             OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa \
987             OP_PUSHBYTES_33 03ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2 \
988             OP_PUSHBYTES_33 039729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40 \
989             OP_PUSHBYTES_33 032564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa \
990             OP_PUSHBYTES_33 0289637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff \
991             OP_PUSHNUM_5 OP_CHECKMULTISIG)",
992        );
993
994        roundtrip(
995            &ms_str!(
996                "t:and_v(\
997                     vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),\
998                     v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5)\
999                 )"),
1000            "Script(OP_IF OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_HASH256 OP_PUSHBYTES_32 131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b OP_EQUAL OP_ELSE OP_0 OP_ENDIF OP_VERIFY OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 OP_PUSHBYTES_32 ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 OP_EQUALVERIFY OP_PUSHNUM_1)"
1001        );
1002        roundtrip(
1003            &ms_str!("and_n(pk(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))"),
1004            "Script(OP_PUSHBYTES_33 03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729 OP_CHECKSIG OP_NOTIF OP_0 OP_ELSE OP_IF OP_0 OP_ELSE OP_PUSHBYTES_3 e2e440 OP_CSV OP_ENDIF OP_TOALTSTACK OP_PUSHNUM_16 OP_CSV OP_FROMALTSTACK OP_BOOLAND OP_ENDIF)"
1005        );
1006        roundtrip(
1007            &ms_str!(
1008                "t:andor(multi(\
1009                    3,\
1010                    02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,\
1011                    03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,\
1012                    02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13\
1013                 ),\
1014                 v:older(4194305),\
1015                 v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2)\
1016                 )"),
1017            "Script(OP_PUSHNUM_3 OP_PUSHBYTES_33 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e \
1018             OP_PUSHBYTES_33 03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556 \
1019             OP_PUSHBYTES_33 02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13 \
1020             OP_PUSHNUM_3 OP_CHECKMULTISIG OP_NOTIF OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 \
1021             OP_PUSHBYTES_32 9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2 OP_EQUALVERIFY \
1022             OP_ELSE OP_PUSHBYTES_3 010040 OP_CSV OP_VERIFY OP_ENDIF OP_PUSHNUM_1)"
1023        );
1024        roundtrip(
1025            &ms_str!(
1026                "t:and_v(\
1027                    vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),\
1028                    v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5)\
1029                 )"),
1030            "Script(\
1031             OP_IF OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_HASH256 OP_PUSHBYTES_32 131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b OP_EQUAL \
1032             OP_ELSE OP_0 OP_ENDIF OP_VERIFY OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 OP_PUSHBYTES_32 ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 OP_EQUALVERIFY \
1033             OP_PUSHNUM_1\
1034             )"
1035        );
1036
1037        // Thresh bug with equal verify roundtrip
1038        roundtrip(
1039            &ms_str!("tv:thresh(1,pk(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", ),
1040               "Script(OP_PUSHBYTES_33 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e OP_CHECKSIG OP_PUSHNUM_1 OP_EQUALVERIFY OP_PUSHNUM_1)",
1041        );
1042    }
1043
1044    #[test]
1045    fn deserialize() {
1046        // Most of these came from fuzzing, hence the increasing lengths
1047        assert!(Segwitv0Script::parse_insane(&hex_script("")).is_err()); // empty
1048        assert!(Segwitv0Script::parse_insane(&hex_script("00")).is_ok()); // FALSE
1049        assert!(Segwitv0Script::parse_insane(&hex_script("51")).is_ok()); // TRUE
1050        assert!(Segwitv0Script::parse_insane(&hex_script("69")).is_err()); // VERIFY
1051        assert!(Segwitv0Script::parse_insane(&hex_script("0000")).is_err()); //and_v(FALSE,FALSE)
1052        assert!(Segwitv0Script::parse_insane(&hex_script("1001")).is_err()); // incomplete push
1053        assert!(Segwitv0Script::parse_insane(&hex_script("03990300b2")).is_err()); // non-minimal #
1054        assert!(Segwitv0Script::parse_insane(&hex_script("8559b2")).is_err()); // leading bytes
1055        assert!(Segwitv0Script::parse_insane(&hex_script("4c0169b2")).is_err()); // non-minimal push
1056        assert!(Segwitv0Script::parse_insane(&hex_script("0000af0000ae85")).is_err()); // OR not BOOLOR
1057
1058        // misc fuzzer problems
1059        assert!(Segwitv0Script::parse_insane(&hex_script("0000000000af")).is_err());
1060        assert!(Segwitv0Script::parse_insane(&hex_script("04009a2970af00")).is_err()); // giant CMS key num
1061        assert!(Segwitv0Script::parse_insane(&hex_script(
1062            "2102ffffffffffffffefefefefefefefefefefef394c0fe5b711179e124008584753ac6900"
1063        ))
1064        .is_err());
1065    }
1066
1067    #[test]
1068    fn cov_script_rtt() {
1069        roundtrip(
1070            &ms_str!("ver_eq(4)"),
1071            "Script(OP_DEPTH OP_PUSHNUM_12 OP_SUB OP_PICK OP_PUSHBYTES_4 04000000 OP_EQUAL)",
1072        );
1073    }
1074
1075    #[test]
1076    fn non_ascii() {
1077        assert!(Segwitv0Script::from_str_insane("🌏")
1078            .unwrap_err()
1079            .to_string()
1080            .contains("unprintable character"));
1081    }
1082
1083    #[test]
1084    fn test_tapscript_rtt() {
1085        // Test x-only invalid under segwitc0 context
1086        let ms = Segwitv0Script::from_str_insane(
1087            "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1088        );
1089        assert_eq!(
1090            ms.unwrap_err().to_string(),
1091            "unexpected «pubkey string should be 66 or 130 digits long, got: 64»",
1092        );
1093        Tapscript::from_str_insane(
1094            "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1095        )
1096        .unwrap();
1097
1098        // Now test that bitcoin::PublicKey works with Taproot context
1099        Miniscript::<bitcoin::PublicKey, Tap>::from_str_insane(
1100            "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1101        )
1102        .unwrap();
1103
1104        // uncompressed keys should not be allowed
1105        Miniscript::<bitcoin::PublicKey, Tap>::from_str_insane(
1106            "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
1107        )
1108        .unwrap_err();
1109
1110        //---------------- test script <-> miniscript ---------------
1111        // Test parsing from scripts: x-only fails decoding in segwitv0 ctx
1112        Segwitv0Script::parse_insane(&hex_script(
1113            "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1114        ))
1115        .unwrap_err();
1116        // x-only succeeds in tap ctx
1117        Tapscript::parse_insane(&hex_script(
1118            "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1119        ))
1120        .unwrap();
1121        // tapscript fails decoding with compressed
1122        Tapscript::parse_insane(&hex_script(
1123            "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1124        ))
1125        .unwrap_err();
1126        // Segwitv0 succeeds decoding with tapscript.
1127        Segwitv0Script::parse_insane(&hex_script(
1128            "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1129        ))
1130        .unwrap();
1131
1132        // multi not allowed in tapscript
1133        Tapscript::from_str_insane(
1134            "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1135        )
1136        .unwrap_err();
1137        // but allowed in segwit
1138        Segwitv0Script::from_str_insane(
1139            "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1140        )
1141        .unwrap();
1142    }
1143
1144    #[test]
1145    fn multi_a_tests() {
1146        // Test from string tests
1147        type Segwitv0Ms = Miniscript<String, Segwitv0>;
1148        type TapMs = Miniscript<String, Tap>;
1149        let segwit_multi_a_ms = Segwitv0Ms::from_str_insane("multi_a(1,A,B,C)");
1150        assert_eq!(
1151            segwit_multi_a_ms.unwrap_err().to_string(),
1152            "Multi a(CHECKSIGADD) only allowed post tapscript"
1153        );
1154        let tap_multi_a_ms = TapMs::from_str_insane("multi_a(1,A,B,C)").unwrap();
1155        assert_eq!(tap_multi_a_ms.to_string(), "multi_a(1,A,B,C)");
1156
1157        // Test encode/decode and translation tests
1158        let tap_ms = tap_multi_a_ms
1159            .translate_pk(&mut StrXOnlyKeyTranslator::new())
1160            .unwrap();
1161        // script rtt test
1162        assert_eq!(
1163            Miniscript::<bitcoin::key::XOnlyPublicKey, Tap>::parse_insane(&tap_ms.encode())
1164                .unwrap(),
1165            tap_ms
1166        );
1167        assert_eq!(tap_ms.script_size(), 104);
1168        assert_eq!(tap_ms.encode().len(), tap_ms.script_size());
1169
1170        // Test satisfaction code
1171        struct SimpleSatisfier(secp256k1_zkp::schnorr::Signature);
1172
1173        // a simple satisfier that always outputs the same signature
1174        impl<Pk: ToPublicKey> Satisfier<Pk> for SimpleSatisfier {
1175            fn lookup_tap_leaf_script_sig(
1176                &self,
1177                _pk: &Pk,
1178                _h: &TapLeafHash,
1179            ) -> Option<elements::SchnorrSig> {
1180                Some(elements::SchnorrSig {
1181                    sig: self.0,
1182                    hash_ty: elements::SchnorrSighashType::Default,
1183                })
1184            }
1185        }
1186
1187        let schnorr_sig = secp256k1_zkp::schnorr::Signature::from_str("84526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f0784526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07").unwrap();
1188        let s = SimpleSatisfier(schnorr_sig);
1189
1190        let wit = tap_ms.satisfy(s).unwrap();
1191        assert_eq!(wit, vec![schnorr_sig.as_ref().to_vec(), vec![], vec![]]);
1192    }
1193
1194    #[test]
1195    fn decode_bug_cpp_review() {
1196        let ms = Miniscript::<String, Segwitv0>::from_str_insane(
1197            "and_b(1,s:and_v(v:older(9),c:pk_k(A)))",
1198        )
1199        .unwrap();
1200        let ms_trans = ms.translate_pk(&mut StrKeyTranslator::new()).unwrap();
1201        let enc = ms_trans.encode();
1202        let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::parse_insane(&enc).unwrap();
1203        assert_eq!(ms_trans.encode(), ms.encode());
1204    }
1205
1206    #[test]
1207    fn expr_features() {
1208        // test that parsing raw hash160 does not work with
1209        let hash160_str = "e9f171df53e04b270fa6271b42f66b0f4a99c5a2";
1210        let ms_str = &format!("c:expr_raw_pkh({})", hash160_str);
1211        type SegwitMs = Miniscript<bitcoin::PublicKey, Segwitv0>;
1212
1213        // Test that parsing raw hash160 from string does not work without extra features
1214        SegwitMs::from_str(ms_str).unwrap_err();
1215        SegwitMs::from_str_insane(ms_str).unwrap_err();
1216        let ms = SegwitMs::from_str_ext(ms_str, &ExtParams::allow_all()).unwrap();
1217
1218        let script = ms.encode();
1219        // The same test, but parsing from script
1220        SegwitMs::parse(&script).unwrap_err();
1221        SegwitMs::parse_insane(&script).unwrap_err();
1222        SegwitMs::parse_with_ext(&script, &ExtParams::allow_all()).unwrap();
1223    }
1224
1225    #[test]
1226    fn tr_multi_a_j_wrapper() {
1227        // Reported by darosior
1228        // `multi_a` fragment may require the top stack element to be the empty vector.
1229        // Previous version had incorrectly copied this code from multi.
1230        type TapMs = Miniscript<String, Tap>;
1231        let ms_str = TapMs::from_str_insane("j:multi_a(1,A,B,C)");
1232        assert!(ms_str.is_err());
1233    }
1234}