1use 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#[derive(Clone)]
52pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext, Ext: Extension = NoExt> {
53 pub node: Terminal<Pk, Ctx, Ext>,
55 pub ty: types::Type,
57 pub ext: types::extra_props::ExtData,
59 pub(crate) phantom: PhantomData<Ctx>,
61}
62
63impl<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
74impl<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
83impl<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
100impl<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 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 pub fn into_inner(self) -> Terminal<Pk, Ctx, Ext> {
138 self.node
139 }
140
141 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 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 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 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 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 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 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 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 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 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 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 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 pub fn from_str_ext(s: &str, ext: &ExtParams,) -> Result<Miniscript<Pk, Ctx, Ext>, Error>
416 {
417 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 pub fn satisfy<S: satisfy::Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error> {
434 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 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 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 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
518pub mod hash256 {
520 use bitcoin::hashes::{hash_newtype, sha256d};
521
522 hash_newtype! {
523 #[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 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 assert!(MsStr::from_str("pk(slip7(k))").is_err());
726 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 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 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 assert!(Segwitv0Script::parse_insane(&hex_script("")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("00")).is_ok()); assert!(Segwitv0Script::parse_insane(&hex_script("51")).is_ok()); assert!(Segwitv0Script::parse_insane(&hex_script("69")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("0000")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("1001")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("03990300b2")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("8559b2")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("4c0169b2")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("0000af0000ae85")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script("0000000000af")).is_err());
1060 assert!(Segwitv0Script::parse_insane(&hex_script("04009a2970af00")).is_err()); 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 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 Miniscript::<bitcoin::PublicKey, Tap>::from_str_insane(
1100 "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1101 )
1102 .unwrap();
1103
1104 Miniscript::<bitcoin::PublicKey, Tap>::from_str_insane(
1106 "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
1107 )
1108 .unwrap_err();
1109
1110 Segwitv0Script::parse_insane(&hex_script(
1113 "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1114 ))
1115 .unwrap_err();
1116 Tapscript::parse_insane(&hex_script(
1118 "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1119 ))
1120 .unwrap();
1121 Tapscript::parse_insane(&hex_script(
1123 "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1124 ))
1125 .unwrap_err();
1126 Segwitv0Script::parse_insane(&hex_script(
1128 "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1129 ))
1130 .unwrap();
1131
1132 Tapscript::from_str_insane(
1134 "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1135 )
1136 .unwrap_err();
1137 Segwitv0Script::from_str_insane(
1139 "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1140 )
1141 .unwrap();
1142 }
1143
1144 #[test]
1145 fn multi_a_tests() {
1146 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 let tap_ms = tap_multi_a_ms
1159 .translate_pk(&mut StrXOnlyKeyTranslator::new())
1160 .unwrap();
1161 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 struct SimpleSatisfier(secp256k1_zkp::schnorr::Signature);
1172
1173 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 let hash160_str = "e9f171df53e04b270fa6271b42f66b0f4a99c5a2";
1210 let ms_str = &format!("c:expr_raw_pkh({})", hash160_str);
1211 type SegwitMs = Miniscript<bitcoin::PublicKey, Segwitv0>;
1212
1213 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 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 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}