sapio_miniscript/miniscript/
mod.rs

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