1use core::marker::PhantomData;
17use core::{fmt, hash, str};
18
19use qtum::script;
20use qtum::taproot::{LeafVersion, TapLeafHash};
21
22use self::analyzable::ExtParams;
23pub use self::context::{BareCtx, Legacy, Segwitv0, Tap};
24use crate::prelude::*;
25use crate::TranslateErr;
26
27pub mod analyzable;
28pub mod astelem;
29pub(crate) mod context;
30pub mod decode;
31pub mod iter;
32pub mod lex;
33pub mod limits;
34pub mod satisfy;
35pub mod types;
36
37use core::cmp;
38
39use sync::Arc;
40
41use self::lex::{lex, TokenIter};
42use self::types::Property;
43pub use crate::miniscript::context::ScriptContext;
44use crate::miniscript::decode::Terminal;
45use crate::miniscript::types::extra_props::ExtData;
46use crate::miniscript::types::Type;
47use crate::{expression, Error, ForEachKey, MiniscriptKey, ToPublicKey, TranslatePk, Translator};
48#[cfg(test)]
49mod ms_tests;
50
51#[derive(Clone)]
53pub struct Miniscript<Pk: MiniscriptKey, Ctx: ScriptContext> {
54 pub node: Terminal<Pk, Ctx>,
56 pub ty: types::Type,
58 pub ext: types::extra_props::ExtData,
60 phantom: PhantomData<Ctx>,
62}
63
64impl<Pk: MiniscriptKey, Ctx: ScriptContext> PartialOrd for Miniscript<Pk, Ctx> {
68 fn partial_cmp(&self, other: &Miniscript<Pk, Ctx>) -> Option<cmp::Ordering> {
69 Some(self.node.cmp(&other.node))
70 }
71}
72
73impl<Pk: MiniscriptKey, Ctx: ScriptContext> Ord for Miniscript<Pk, Ctx> {
77 fn cmp(&self, other: &Miniscript<Pk, Ctx>) -> cmp::Ordering {
78 self.node.cmp(&other.node)
79 }
80}
81
82impl<Pk: MiniscriptKey, Ctx: ScriptContext> PartialEq for Miniscript<Pk, Ctx> {
86 fn eq(&self, other: &Miniscript<Pk, Ctx>) -> bool {
87 self.node.eq(&other.node)
88 }
89}
90
91impl<Pk: MiniscriptKey, Ctx: ScriptContext> Eq for Miniscript<Pk, Ctx> {}
95
96impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Debug for Miniscript<Pk, Ctx> {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 write!(f, "{:?}", self.node)
99 }
100}
101
102impl<Pk: MiniscriptKey, Ctx: ScriptContext> hash::Hash for Miniscript<Pk, Ctx> {
103 fn hash<H: hash::Hasher>(&self, state: &mut H) {
104 self.node.hash(state);
105 }
106}
107
108impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
109 pub fn from_ast(t: Terminal<Pk, Ctx>) -> Result<Miniscript<Pk, Ctx>, Error> {
113 let res = Miniscript {
114 ty: Type::type_check(&t, |_| None)?,
115 ext: ExtData::type_check(&t, |_| None)?,
116 node: t,
117 phantom: PhantomData,
118 };
119 Ctx::check_global_consensus_validity(&res)?;
120 Ok(res)
121 }
122
123 pub fn from_components_unchecked(
129 node: Terminal<Pk, Ctx>,
130 ty: types::Type,
131 ext: types::extra_props::ExtData,
132 ) -> Miniscript<Pk, Ctx> {
133 Miniscript {
134 node,
135 ty,
136 ext,
137 phantom: PhantomData,
138 }
139 }
140}
141
142impl<Pk: MiniscriptKey, Ctx: ScriptContext> fmt::Display for Miniscript<Pk, Ctx> {
143 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 write!(f, "{}", self.node)
145 }
146}
147
148impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
149 pub fn into_inner(self) -> Terminal<Pk, Ctx> {
151 self.node
152 }
153
154 pub fn as_inner(&self) -> &Terminal<Pk, Ctx> {
156 &self.node
157 }
158}
159
160impl<Ctx: ScriptContext> Miniscript<Ctx::Key, Ctx> {
161 pub fn parse_insane(script: &script::Script) -> Result<Miniscript<Ctx::Key, Ctx>, Error> {
169 Miniscript::parse_with_ext(script, &ExtParams::insane())
170 }
171
172 pub fn parse_with_ext(
180 script: &script::Script,
181 ext: &ExtParams,
182 ) -> Result<Miniscript<Ctx::Key, Ctx>, Error> {
183 let tokens = lex(script)?;
184 let mut iter = TokenIter::new(tokens);
185
186 let top = decode::parse(&mut iter)?;
187 Ctx::check_global_validity(&top)?;
188 let type_check = types::Type::type_check(&top.node, |_| None)?;
189 if type_check.corr.base != types::Base::B {
190 return Err(Error::NonTopLevel(format!("{:?}", top)));
191 };
192 if let Some(leading) = iter.next() {
193 Err(Error::Trailing(leading.to_string()))
194 } else {
195 top.ext_check(ext)?;
196 Ok(top)
197 }
198 }
199
200 pub fn parse(script: &script::Script) -> Result<Miniscript<Ctx::Key, Ctx>, Error> {
234 let ms = Self::parse_with_ext(script, &ExtParams::sane())?;
235 Ok(ms)
236 }
237}
238
239impl<Pk, Ctx> Miniscript<Pk, Ctx>
240where
241 Pk: MiniscriptKey,
242 Ctx: ScriptContext,
243{
244 pub fn encode(&self) -> script::ScriptBuf
246 where
247 Pk: ToPublicKey,
248 {
249 self.node.encode(script::Builder::new()).into_script()
250 }
251
252 pub fn script_size(&self) -> usize {
260 self.node.script_size()
261 }
262}
263
264impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
265 pub fn max_satisfaction_witness_elements(&self) -> Result<usize, Error> {
273 self.ext
274 .stack_elem_count_sat
275 .map(|x| x + 1)
276 .ok_or(Error::ImpossibleSatisfaction)
277 }
278
279 pub fn max_satisfaction_size(&self) -> Result<usize, Error> {
292 Ctx::max_satisfaction_size(self).ok_or(Error::ImpossibleSatisfaction)
293 }
294}
295
296impl<Pk: MiniscriptKey, Ctx: ScriptContext> ForEachKey<Pk> for Miniscript<Pk, Ctx> {
297 fn for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, mut pred: F) -> bool {
298 self.real_for_each_key(&mut pred)
299 }
300}
301
302impl<Pk, Q, Ctx> TranslatePk<Pk, Q> for Miniscript<Pk, Ctx>
303where
304 Pk: MiniscriptKey,
305 Q: MiniscriptKey,
306 Ctx: ScriptContext,
307{
308 type Output = Miniscript<Q, Ctx>;
309
310 fn translate_pk<T, E>(&self, translate: &mut T) -> Result<Self::Output, TranslateErr<E>>
313 where
314 T: Translator<Pk, Q, E>,
315 {
316 self.real_translate_pk(translate)
317 }
318}
319
320impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
321 fn real_for_each_key<'a, F: FnMut(&'a Pk) -> bool>(&'a self, pred: &mut F) -> bool {
322 self.node.real_for_each_key(pred)
323 }
324
325 pub(super) fn real_translate_pk<Q, CtxQ, T, FuncError>(
326 &self,
327 t: &mut T,
328 ) -> Result<Miniscript<Q, CtxQ>, TranslateErr<FuncError>>
329 where
330 Q: MiniscriptKey,
331 CtxQ: ScriptContext,
332 T: Translator<Pk, Q, FuncError>,
333 {
334 let inner = self.node.real_translate_pk(t)?;
335 Miniscript::from_ast(inner).map_err(TranslateErr::OuterError)
336 }
337}
338
339impl_block_str!(
340 ;Ctx; ScriptContext,
341 Miniscript<Pk, Ctx>,
342 pub fn from_str_insane(s: &str,) -> Result<Miniscript<Pk, Ctx>, Error>
350 {
351 Miniscript::from_str_ext(s, &ExtParams::insane())
352 }
353);
354
355impl_block_str!(
356 ;Ctx; ScriptContext,
357 Miniscript<Pk, Ctx>,
358 pub fn from_str_ext(s: &str, ext: &ExtParams,) -> Result<Miniscript<Pk, Ctx>, Error>
364 {
365 let top = expression::Tree::from_str(s)?;
367 let ms: Miniscript<Pk, Ctx> = expression::FromTree::from_tree(&top)?;
368 ms.ext_check(ext)?;
369
370 if ms.ty.corr.base != types::Base::B {
371 Err(Error::NonTopLevel(format!("{:?}", ms)))
372 } else {
373 Ok(ms)
374 }
375 }
376);
377
378impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
379 pub fn satisfy<S: satisfy::Satisfier<Pk>>(&self, satisfier: S) -> Result<Vec<Vec<u8>>, Error>
382 where
383 Pk: ToPublicKey,
384 {
385 let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
387 match satisfy::Satisfaction::satisfy(&self.node, &satisfier, self.ty.mall.safe, &leaf_hash)
388 .stack
389 {
390 satisfy::Witness::Stack(stack) => {
391 Ctx::check_witness::<Pk>(&stack)?;
392 Ok(stack)
393 }
394 satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
395 Err(Error::CouldNotSatisfy)
396 }
397 }
398 }
399
400 pub fn satisfy_malleable<S: satisfy::Satisfier<Pk>>(
403 &self,
404 satisfier: S,
405 ) -> Result<Vec<Vec<u8>>, Error>
406 where
407 Pk: ToPublicKey,
408 {
409 let leaf_hash = TapLeafHash::from_script(&self.encode(), LeafVersion::TapScript);
410 match satisfy::Satisfaction::satisfy_mall(
411 &self.node,
412 &satisfier,
413 self.ty.mall.safe,
414 &leaf_hash,
415 )
416 .stack
417 {
418 satisfy::Witness::Stack(stack) => {
419 Ctx::check_witness::<Pk>(&stack)?;
420 Ok(stack)
421 }
422 satisfy::Witness::Unavailable | satisfy::Witness::Impossible => {
423 Err(Error::CouldNotSatisfy)
424 }
425 }
426 }
427}
428
429impl_from_tree!(
430 ;Ctx; ScriptContext,
431 Arc<Miniscript<Pk, Ctx>>,
432 fn from_tree(top: &expression::Tree) -> Result<Arc<Miniscript<Pk, Ctx>>, Error> {
433 Ok(Arc::new(expression::FromTree::from_tree(top)?))
434 }
435);
436
437impl_from_tree!(
438 ;Ctx; ScriptContext,
439 Miniscript<Pk, Ctx>,
440 fn from_tree(top: &expression::Tree) -> Result<Miniscript<Pk, Ctx>, Error> {
443 let inner: Terminal<Pk, Ctx> = expression::FromTree::from_tree(top)?;
444 Miniscript::from_ast(inner)
445 }
446);
447
448impl_from_str!(
449 ;Ctx; ScriptContext,
450 Miniscript<Pk, Ctx>,
451 type Err = Error;,
452 fn from_str(s: &str) -> Result<Miniscript<Pk, Ctx>, Error> {
456 let ms = Self::from_str_ext(s, &ExtParams::sane())?;
457 Ok(ms)
458 }
459);
460
461serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
462
463pub mod hash256 {
465 use qtum::hashes::{hash_newtype, sha256d};
466
467 hash_newtype! {
468 #[hash_newtype(forward)]
470 pub struct Hash(sha256d::Hash);
471 }
472}
473
474#[cfg(test)]
475mod tests {
476
477 use core::marker::PhantomData;
478 use core::str;
479 use core::str::FromStr;
480
481 use qtum::hashes::{hash160, sha256, Hash};
482 use qtum::secp256k1::XOnlyPublicKey;
483 use qtum::taproot::TapLeafHash;
484 use qtum::{self, secp256k1, Sequence};
485 use sync::Arc;
486
487 use super::{Miniscript, ScriptContext, Segwitv0, Tap};
488 use crate::miniscript::types::{self, ExtData, Property, Type};
489 use crate::miniscript::Terminal;
490 use crate::policy::Liftable;
491 use crate::prelude::*;
492 use crate::test_utils::{StrKeyTranslator, StrXOnlyKeyTranslator};
493 use crate::{hex_script, ExtParams, Satisfier, ToPublicKey, TranslatePk};
494
495 type Segwitv0Script = Miniscript<qtum::PublicKey, Segwitv0>;
496 type Tapscript = Miniscript<qtum::secp256k1::XOnlyPublicKey, Tap>;
497
498 fn pubkeys(n: usize) -> Vec<qtum::PublicKey> {
499 let mut ret = Vec::with_capacity(n);
500 let secp = secp256k1::Secp256k1::new();
501 let mut sk = [0; 32];
502 for i in 1..n + 1 {
503 sk[0] = i as u8;
504 sk[1] = (i >> 8) as u8;
505 sk[2] = (i >> 16) as u8;
506
507 let pk = qtum::PublicKey {
508 inner: secp256k1::PublicKey::from_secret_key(
509 &secp,
510 &secp256k1::SecretKey::from_slice(&sk[..]).expect("secret key"),
511 ),
512 compressed: true,
513 };
514 ret.push(pk);
515 }
516 ret
517 }
518
519 fn string_rtt<Ctx: ScriptContext>(
520 script: Miniscript<qtum::PublicKey, Ctx>,
521 expected_debug: &str,
522 expected_display: &str,
523 ) {
524 assert_eq!(script.ty.corr.base, types::Base::B);
525 let debug = format!("{:?}", script);
526 let display = format!("{}", script);
527 if let Some(expected) = expected_debug.into() {
528 assert_eq!(debug, expected);
529 }
530 if let Some(expected) = expected_display.into() {
531 assert_eq!(display, expected);
532 }
533 let roundtrip = Miniscript::from_str(&display).expect("parse string serialization");
534 assert_eq!(roundtrip, script);
535 }
536
537 fn string_display_debug_test<Ctx: ScriptContext>(
538 script: Miniscript<qtum::PublicKey, Ctx>,
539 expected_debug: &str,
540 expected_display: &str,
541 ) {
542 assert_eq!(script.ty.corr.base, types::Base::B);
543 let debug = format!("{:?}", script);
544 let display = format!("{}", script);
545 if let Some(expected) = expected_debug.into() {
546 assert_eq!(debug, expected);
547 }
548 if let Some(expected) = expected_display.into() {
549 assert_eq!(display, expected);
550 }
551 }
552
553 fn dummy_string_rtt<Ctx: ScriptContext>(
554 script: Miniscript<String, Ctx>,
555 expected_debug: &str,
556 expected_display: &str,
557 ) {
558 assert_eq!(script.ty.corr.base, types::Base::B);
559 let debug = format!("{:?}", script);
560 let display = format!("{}", script);
561 if let Some(expected) = expected_debug.into() {
562 assert_eq!(debug, expected);
563 }
564 if let Some(expected) = expected_display.into() {
565 assert_eq!(display, expected);
566 }
567 let roundtrip = Miniscript::from_str(&display).expect("parse string serialization");
568 assert_eq!(roundtrip, script);
569 }
570
571 fn script_rtt<Str1: Into<Option<&'static str>>>(script: Segwitv0Script, expected_hex: Str1) {
572 assert_eq!(script.ty.corr.base, types::Base::B);
573 let bitcoin_script = script.encode();
574 assert_eq!(bitcoin_script.len(), script.script_size());
575 if let Some(expected) = expected_hex.into() {
576 assert_eq!(format!("{:x}", bitcoin_script), expected);
577 }
578 let roundtrip = Segwitv0Script::parse_with_ext(&bitcoin_script, &ExtParams::allow_all())
580 .expect("parse string serialization");
581 assert_eq!(roundtrip, script);
582 }
583
584 fn roundtrip(tree: &Segwitv0Script, s: &str) {
585 assert_eq!(tree.ty.corr.base, types::Base::B);
586 let ser = tree.encode();
587 assert_eq!(ser.len(), tree.script_size());
588 assert_eq!(ser.to_string(), s);
589 let deser = Segwitv0Script::parse_insane(&ser).expect("deserialize result of serialize");
590 assert_eq!(*tree, deser);
591 }
592
593 fn ms_attributes_test(
594 ms: &str,
595 expected_hex: &str,
596 valid: bool,
597 non_mal: bool,
598 need_sig: bool,
599 ops: usize,
600 _stack: usize,
601 ) {
602 let ms: Result<Segwitv0Script, _> = Miniscript::from_str_insane(ms);
603 match (ms, valid) {
604 (Ok(ms), true) => {
605 assert_eq!(format!("{:x}", ms.encode()), expected_hex);
606 assert_eq!(ms.ty.mall.non_malleable, non_mal);
607 assert_eq!(ms.ty.mall.safe, need_sig);
608 assert_eq!(ms.ext.ops.op_count().unwrap(), ops);
609 }
610 (Err(_), false) => {}
611 _ => unreachable!(),
612 }
613 }
614
615 #[test]
616 fn all_attribute_tests() {
617 ms_attributes_test(
618 "lltvln:after(1231488000)",
619 "6300676300676300670400046749b1926869516868",
620 true,
621 true,
622 false,
623 12,
624 3,
625 );
626 ms_attributes_test("uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))", "6363829263522103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a21025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc52af0400046749b168670068670068", true, true, true, 14, 5);
627 ms_attributes_test("or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))", "63522103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee872921024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae926700686b63006760b2686c9b", true, false, false, 14, 5);
628 ms_attributes_test(
629 "j:and_v(vdv:after(1567547623),older(2016))",
630 "829263766304e7e06e5db169686902e007b268",
631 true,
632 true,
633 false,
634 11,
635 1,
636 );
637 ms_attributes_test("t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))", "6382012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876700686982012088a820ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc58851", true, true, false, 12, 3);
638 ms_attributes_test("t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))", "532102d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e2103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a14602975562102e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1353ae6482012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2886703010040b2696851", true, true, false, 13, 5);
639 ms_attributes_test("or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))", "512102f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f951ae73645321022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a0121032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f2103d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a53ae7c630320a107b16700689b68", true, true, false, 15, 7);
640 ms_attributes_test("or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))", "82012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68773646304ff64cd1db19267006864006703010040b26868", true, false, false, 16, 1);
641 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);
642 ms_attributes_test("j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))", "82926352210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c9752ae7c6351b26703e2e440b2689a68", true, false, true, 14, 4);
643 ms_attributes_test("and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))", "60b27c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87736404e7e06e5db192689a", true, false, false, 12, 1);
644 ms_attributes_test("j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))", "82926382012088a91420195b5a3d650c17f0f29f91c33f8f6335193d078882012088a82096de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c4787736460b26868", true, false, false, 16, 2);
645 ms_attributes_test("and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))", "82012088aa2032ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac876b82012088aa20131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b876b51b26c9a6c9a", true, true, false, 15, 2);
646 ms_attributes_test("thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))", "522103a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c721036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0052ae6b5121036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a0051ae6c936b21022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01ac6c935287", true, true, true, 13, 6);
647 ms_attributes_test("and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))", "82012088a820d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68876400676303e2e440b26967029000b269685168", true, false, false, 14, 2);
648 ms_attributes_test("or_d(nd:and_v(v:older(4252898),v:older(4252898)),sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6))", "766303e2e440b26903e2e440b2696892736482012088a82038df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b68768", true, false, false, 15, 2);
649 ms_attributes_test("c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764512102c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db51af682103acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbeac", true, false, true, 9, 2);
650 ms_attributes_test("c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))", "5221036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a002102352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d552ae6482012088a6141b0f3c404d12075c68c938f9f60ebea4f74941a088682103fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556ac", true, true, true, 10, 5);
651 ms_attributes_test("and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))", "82012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b2587640350c300b2696782012088aa20939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735886804ff64cd1db1", true, false, false, 14, 2);
652 ms_attributes_test("andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))", "82012088aa205f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040876482012088a61444d90e2d3714c8663b632fcf0f9d5f22192cc4c8876782926382012088a9143a2bff0da9d96868e66abc4427bea4691cf61ccd8803010040b26868", true, false, false, 20, 2);
653 ms_attributes_test("or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))", "630320a107b1692102c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5ac6782012088a820d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f9468768", true, true, false, 10, 2);
654 ms_attributes_test("thresh(2,c:pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))", "76a91420d637c1a6404d2227f3561fdbaff5a680dba64888ac7c82012088a820e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f87936b82012088a914dd69735817e0e3f6f826a9238dc2e291184f0131876c935287", true, false, false, 18, 4);
655 ms_attributes_test("and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))", "82012088a8209267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed28764006763029000b2692103fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ceac67006868", true, false, true, 13, 3);
656 ms_attributes_test("and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))", "2103daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729ac64006763006703e2e440b2686b60b26c9a68", true, true, true, 12, 2);
657 ms_attributes_test("c:or_i(and_v(v:older(16),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5))", "6360b26976a91420d637c1a6404d2227f3561fdbaff5a680dba648886776a9148f9dff39a81ee4abcbad2ad8bafff090415a2be88868ac", true, true, true, 12, 3);
658 ms_attributes_test("or_d(c:pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),andor(c:pk_k(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),older(2016),after(1567547623)))", "76a9148f9dff39a81ee4abcbad2ad8bafff090415a2be888ac736421024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97ac6404e7e06e5db16702e007b26868", true, true, false, 13, 3);
659 ms_attributes_test("c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5)))", "82012088a6146ad07d21fd5dfc646f0b30577045ce201616b9ba876482012088aa208a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b258876a9148f9dff39a81ee4abcbad2ad8bafff090415a2be8886776a91420d637c1a6404d2227f3561fdbaff5a680dba6488868ac", true, false, true, 18, 3);
660 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);
661 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);
662 }
663
664 #[test]
665 fn basic() {
666 let pk = qtum::PublicKey::from_str(
667 "\
668 020202020202020202020202020202020202020202020202020202020202020202\
669 ",
670 )
671 .unwrap();
672 let hash = hash160::Hash::from_byte_array([17; 20]);
673
674 let pk_node = Terminal::Check(Arc::new(Miniscript {
675 node: Terminal::PkK(String::from("")),
676 ty: Type::from_pk_k::<Segwitv0>(),
677 ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
678 phantom: PhantomData,
679 }));
680 let pkk_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pk_node).unwrap();
681 dummy_string_rtt(pkk_ms, "[B/onduesm]c:[K/onduesm]pk_k(\"\")", "pk()");
682
683 let pkh_node = Terminal::Check(Arc::new(Miniscript {
684 node: Terminal::PkH(String::from("")),
685 ty: Type::from_pk_h::<Segwitv0>(),
686 ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
687 phantom: PhantomData,
688 }));
689 let pkh_ms: Miniscript<String, Segwitv0> = Miniscript::from_ast(pkh_node).unwrap();
690
691 let expected_debug = "[B/nduesm]c:[K/nduesm]pk_h(\"\")";
692 let expected_display = "pkh()";
693
694 assert_eq!(pkh_ms.ty.corr.base, types::Base::B);
695 let debug = format!("{:?}", pkh_ms);
696 let display = format!("{}", pkh_ms);
697 if let Some(expected) = expected_debug.into() {
698 assert_eq!(debug, expected);
699 }
700 if let Some(expected) = expected_display.into() {
701 assert_eq!(display, expected);
702 }
703
704 let pkk_node = Terminal::Check(Arc::new(Miniscript {
705 node: Terminal::PkK(pk),
706 ty: Type::from_pk_k::<Segwitv0>(),
707 ext: types::extra_props::ExtData::from_pk_k::<Segwitv0>(),
708 phantom: PhantomData,
709 }));
710 let pkk_ms: Segwitv0Script = Miniscript::from_ast(pkk_node).unwrap();
711
712 script_rtt(
713 pkk_ms,
714 "21020202020202020202020202020202020202020202020202020202020\
715 202020202ac",
716 );
717
718 let pkh_ms: Segwitv0Script = Miniscript {
719 node: Terminal::Check(Arc::new(Miniscript {
720 node: Terminal::RawPkH(hash),
721 ty: Type::from_pk_h::<Segwitv0>(),
722 ext: types::extra_props::ExtData::from_pk_h::<Segwitv0>(),
723 phantom: PhantomData,
724 })),
725 ty: Type::cast_check(Type::from_pk_h::<Segwitv0>()).unwrap(),
726 ext: ExtData::cast_check(ExtData::from_pk_h::<Segwitv0>()).unwrap(),
727 phantom: PhantomData,
728 };
729
730 script_rtt(pkh_ms, "76a914111111111111111111111111111111111111111188ac");
731 }
732
733 #[test]
734 fn true_false() {
735 roundtrip(&ms_str!("1"), "OP_PUSHNUM_1");
736 roundtrip(&ms_str!("tv:1"), "OP_PUSHNUM_1 OP_VERIFY OP_PUSHNUM_1");
737 roundtrip(&ms_str!("0"), "OP_0");
738 roundtrip(
739 &ms_str!("andor(0,1,0)"),
740 "OP_0 OP_NOTIF OP_0 OP_ELSE OP_PUSHNUM_1 OP_ENDIF",
741 );
742
743 assert!(Segwitv0Script::from_str("1()").is_err());
744 assert!(Segwitv0Script::from_str("tv:1()").is_err());
745 }
746
747 #[test]
748 fn verify_parse() {
749 let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
750 let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
751 assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
752
753 let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
754 let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
755 assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
756
757 let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
758 let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
759 assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
760
761 let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))";
762 let ms: Segwitv0Script = Miniscript::from_str_insane(ms).unwrap();
763 assert_eq!(ms, Segwitv0Script::parse_insane(&ms.encode()).unwrap());
764 }
765
766 #[test]
767 fn pk_alias() {
768 let pubkey = pubkeys(1)[0];
769
770 let script: Segwitv0Script = ms_str!("c:pk_k({})", pubkey.to_string());
771
772 string_rtt(
773 script,
774 "[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
775 "pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
776 );
777
778 let script: Segwitv0Script = ms_str!("pk({})", pubkey.to_string());
779
780 string_rtt(
781 script,
782 "[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
783 "pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
784 );
785
786 let script: Segwitv0Script = ms_str!("tv:pk({})", pubkey.to_string());
787
788 string_rtt(
789 script,
790 "[B/onufsm]t[V/onfsm]v[B/onduesm]c:[K/onduesm]pk_k(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
791 "tv:pk(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)"
792 );
793
794 let script: Segwitv0Script = ms_str!("c:pk_h({})", pubkey.to_string());
795
796 string_display_debug_test(
797 script,
798 "[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
799 "pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
800 );
801
802 let script: Segwitv0Script = ms_str!("pkh({})", pubkey.to_string());
803
804 string_display_debug_test(
805 script,
806 "[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
807 "pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
808 );
809
810 let script: Segwitv0Script = ms_str!("tv:pkh({})", pubkey.to_string());
811
812 string_display_debug_test(
813 script,
814 "[B/nufsm]t[V/nfsm]v[B/nduesm]c:[K/nduesm]pk_h(PublicKey { compressed: true, inner: PublicKey(aa4c32e50fb34a95a372940ae3654b692ea35294748c3dd2c08b29f87ba9288c8294efcb73dc719e45b91c45f084e77aebc07c1ff3ed8f37935130a36304a340) })",
815 "tv:pkh(028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa)",
816 );
817 }
818
819 #[test]
820 fn serialize() {
821 let keys = pubkeys(6);
822
823 let tree: &Segwitv0Script = &ms_str!("c:pk_h({})", keys[5]);
824 assert_eq!(tree.ty.corr.base, types::Base::B);
825 let ser = tree.encode();
826 let s = "\
827 OP_DUP OP_HASH160 OP_PUSHBYTES_20 \
828 7e5a2a6a7610ca4ea78bd65a087bd75b1870e319 \
829 OP_EQUALVERIFY OP_CHECKSIG\
830 ";
831 assert_eq!(ser.len(), tree.script_size());
832 assert_eq!(ser.to_string(), s);
833
834 roundtrip(
835 &ms_str!("pk({})", keys[0]),
836 "OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa OP_CHECKSIG"
837 );
838 roundtrip(
839 &ms_str!("multi(3,{},{},{},{},{})", keys[0], keys[1], keys[2], keys[3], keys[4]),
840 "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"
841 );
842
843 roundtrip(
845 &ms_str!("or_d(multi(2,{},{}),and_v(v:multi(2,{},{}),older(10000)))",
846 keys[0].to_string(),
847 keys[1].to_string(),
848 keys[3].to_string(),
849 keys[4].to_string()),
850 "OP_PUSHNUM_2 OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa \
851 OP_PUSHBYTES_33 03ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2 \
852 OP_PUSHNUM_2 OP_CHECKMULTISIG \
853 OP_IFDUP OP_NOTIF \
854 OP_PUSHNUM_2 OP_PUSHBYTES_33 032564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa \
855 OP_PUSHBYTES_33 0289637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff \
856 OP_PUSHNUM_2 OP_CHECKMULTISIGVERIFY \
857 OP_PUSHBYTES_2 1027 OP_CSV \
858 OP_ENDIF"
859 );
860
861 let miniscript: Segwitv0Script = ms_str!(
862 "or_d(multi(3,{},{},{}),and_v(v:multi(2,{},{}),older(10000)))",
863 keys[0].to_string(),
864 keys[1].to_string(),
865 keys[2].to_string(),
866 keys[3].to_string(),
867 keys[4].to_string(),
868 );
869
870 let mut abs = miniscript.lift().unwrap();
871 assert_eq!(abs.n_keys(), 5);
872 assert_eq!(abs.minimum_n_keys(), Some(2));
873 abs = abs.at_age(Sequence::from_height(10000));
874 assert_eq!(abs.n_keys(), 5);
875 assert_eq!(abs.minimum_n_keys(), Some(2));
876 abs = abs.at_age(Sequence::from_height(9999));
877 assert_eq!(abs.n_keys(), 3);
878 assert_eq!(abs.minimum_n_keys(), Some(3));
879 abs = abs.at_age(Sequence::ZERO);
880 assert_eq!(abs.n_keys(), 3);
881 assert_eq!(abs.minimum_n_keys(), Some(3));
882
883 roundtrip(&ms_str!("older(921)"), "OP_PUSHBYTES_2 9903 OP_CSV");
884
885 roundtrip(
886 &ms_str!("sha256({})",sha256::Hash::hash(&[])),
887 "OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 OP_PUSHBYTES_32 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 OP_EQUAL"
888 );
889
890 roundtrip(
891 &ms_str!(
892 "multi(3,{},{},{},{},{})",
893 keys[0],
894 keys[1],
895 keys[2],
896 keys[3],
897 keys[4]
898 ),
899 "OP_PUSHNUM_3 \
900 OP_PUSHBYTES_33 028c28a97bf8298bc0d23d8c749452a32e694b65e30a9472a3954ab30fe5324caa \
901 OP_PUSHBYTES_33 03ab1ac1872a38a2f196bed5a6047f0da2c8130fe8de49fc4d5dfb201f7611d8e2 \
902 OP_PUSHBYTES_33 039729247032c0dfcf45b4841fcd72f6e9a2422631fc3466cf863e87154754dd40 \
903 OP_PUSHBYTES_33 032564fe9b5beef82d3703a607253f31ef8ea1b365772df434226aee642651b3fa \
904 OP_PUSHBYTES_33 0289637f97580a796e050791ad5a2f27af1803645d95df021a3c2d82eb8c2ca7ff \
905 OP_PUSHNUM_5 OP_CHECKMULTISIG",
906 );
907
908 roundtrip(
909 &ms_str!(
910 "t:and_v(\
911 vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),\
912 v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5)\
913 )"),
914 "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"
915 );
916 roundtrip(
917 &ms_str!("and_n(pk(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))"),
918 "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"
919 );
920 roundtrip(
921 &ms_str!(
922 "t:andor(multi(\
923 3,\
924 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,\
925 03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,\
926 02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13\
927 ),\
928 v:older(4194305),\
929 v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2)\
930 )"),
931 "OP_PUSHNUM_3 OP_PUSHBYTES_33 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e \
932 OP_PUSHBYTES_33 03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556 \
933 OP_PUSHBYTES_33 02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13 \
934 OP_PUSHNUM_3 OP_CHECKMULTISIG OP_NOTIF OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 \
935 OP_PUSHBYTES_32 9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2 OP_EQUALVERIFY \
936 OP_ELSE OP_PUSHBYTES_3 010040 OP_CSV OP_VERIFY OP_ENDIF OP_PUSHNUM_1"
937 );
938 roundtrip(
939 &ms_str!(
940 "t:and_v(\
941 vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),\
942 v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5)\
943 )"),
944 "\
945 OP_IF OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_HASH256 OP_PUSHBYTES_32 131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b OP_EQUAL \
946 OP_ELSE OP_0 OP_ENDIF OP_VERIFY OP_SIZE OP_PUSHBYTES_1 20 OP_EQUALVERIFY OP_SHA256 OP_PUSHBYTES_32 ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5 OP_EQUALVERIFY \
947 OP_PUSHNUM_1\
948 "
949 );
950
951 roundtrip(
953 &ms_str!("tv:thresh(1,pk(02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e))", ),
954 "OP_PUSHBYTES_33 02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e OP_CHECKSIG OP_PUSHNUM_1 OP_EQUALVERIFY OP_PUSHNUM_1",
955 );
956 }
957
958 #[test]
959 fn deserialize() {
960 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());
974 assert!(Segwitv0Script::parse_insane(&hex_script("04009a2970af00")).is_err()); assert!(Segwitv0Script::parse_insane(&hex_script(
976 "2102ffffffffffffffefefefefefefefefefefef394c0fe5b711179e124008584753ac6900"
977 ))
978 .is_err());
979 }
980
981 #[test]
982 fn non_ascii() {
983 assert!(Segwitv0Script::from_str_insane("🌏")
984 .unwrap_err()
985 .to_string()
986 .contains("unprintable character"));
987 }
988
989 #[test]
990 fn test_tapscript_rtt() {
991 Segwitv0Script::from_str_insane(
993 "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
994 )
995 .unwrap_err();
996 Tapscript::from_str_insane(
997 "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
998 )
999 .unwrap();
1000
1001 Miniscript::<qtum::PublicKey, Tap>::from_str_insane(
1003 "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1004 )
1005 .unwrap();
1006
1007 Miniscript::<qtum::PublicKey, Tap>::from_str_insane(
1009 "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
1010 )
1011 .unwrap_err();
1012
1013 Segwitv0Script::parse_insane(&hex_script(
1016 "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1017 ))
1018 .unwrap_err();
1019 Tapscript::parse_insane(&hex_script(
1021 "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1022 ))
1023 .unwrap();
1024 Tapscript::parse_insane(&hex_script(
1026 "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1027 ))
1028 .unwrap_err();
1029 Segwitv0Script::parse_insane(&hex_script(
1031 "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
1032 ))
1033 .unwrap();
1034
1035 Tapscript::from_str_insane(
1037 "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1038 )
1039 .unwrap_err();
1040 Segwitv0Script::from_str_insane(
1042 "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)",
1043 )
1044 .unwrap();
1045 }
1046
1047 #[test]
1048 fn multi_a_tests() {
1049 type Segwitv0Ms = Miniscript<String, Segwitv0>;
1051 type TapMs = Miniscript<String, Tap>;
1052 let segwit_multi_a_ms = Segwitv0Ms::from_str_insane("multi_a(1,A,B,C)");
1053 assert_eq!(
1054 segwit_multi_a_ms.unwrap_err().to_string(),
1055 "Multi a(CHECKSIGADD) only allowed post tapscript"
1056 );
1057 let tap_multi_a_ms = TapMs::from_str_insane("multi_a(1,A,B,C)").unwrap();
1058 assert_eq!(tap_multi_a_ms.to_string(), "multi_a(1,A,B,C)");
1059
1060 let tap_ms = tap_multi_a_ms
1062 .translate_pk(&mut StrXOnlyKeyTranslator::new())
1063 .unwrap();
1064 assert_eq!(
1066 Miniscript::<XOnlyPublicKey, Tap>::parse_insane(&tap_ms.encode()).unwrap(),
1067 tap_ms
1068 );
1069 assert_eq!(tap_ms.script_size(), 104);
1070 assert_eq!(tap_ms.encode().len(), tap_ms.script_size());
1071
1072 struct SimpleSatisfier(secp256k1::schnorr::Signature);
1074
1075 impl<Pk: ToPublicKey> Satisfier<Pk> for SimpleSatisfier {
1077 fn lookup_tap_leaf_script_sig(
1078 &self,
1079 _pk: &Pk,
1080 _h: &TapLeafHash,
1081 ) -> Option<qtum::taproot::Signature> {
1082 Some(qtum::taproot::Signature {
1083 sig: self.0,
1084 hash_ty: qtum::sighash::TapSighashType::Default,
1085 })
1086 }
1087 }
1088
1089 let schnorr_sig = secp256k1::schnorr::Signature::from_str("84526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f0784526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07").unwrap();
1090 let s = SimpleSatisfier(schnorr_sig);
1091
1092 let wit = tap_ms.satisfy(s).unwrap();
1093 assert_eq!(wit, vec![schnorr_sig.as_ref().to_vec(), vec![], vec![]]);
1094 }
1095
1096 #[test]
1097 fn decode_bug_cpp_review() {
1098 let ms = Miniscript::<String, Segwitv0>::from_str_insane(
1099 "and_b(1,s:and_v(v:older(9),c:pk_k(A)))",
1100 )
1101 .unwrap();
1102 let ms_trans = ms.translate_pk(&mut StrKeyTranslator::new()).unwrap();
1103 let enc = ms_trans.encode();
1104 let ms = Miniscript::<qtum::PublicKey, Segwitv0>::parse_insane(&enc).unwrap();
1105 assert_eq!(ms_trans.encode(), ms.encode());
1106 }
1107
1108 #[test]
1109 fn expr_features() {
1110 let hash160_str = "e9f171df53e04b270fa6271b42f66b0f4a99c5a2";
1112 let ms_str = &format!("c:expr_raw_pkh({})", hash160_str);
1113 type SegwitMs = Miniscript<qtum::PublicKey, Segwitv0>;
1114
1115 SegwitMs::from_str(ms_str).unwrap_err();
1117 SegwitMs::from_str_insane(ms_str).unwrap_err();
1118 let ms = SegwitMs::from_str_ext(ms_str, &ExtParams::allow_all()).unwrap();
1119
1120 let script = ms.encode();
1121 SegwitMs::parse(&script).unwrap_err();
1123 SegwitMs::parse_insane(&script).unwrap_err();
1124 SegwitMs::parse_with_ext(&script, &ExtParams::allow_all()).unwrap();
1125 }
1126
1127 #[test]
1128 fn tr_multi_a_j_wrapper() {
1129 type TapMs = Miniscript<String, Tap>;
1133 let ms_str = TapMs::from_str_insane("j:multi_a(1,A,B,C)");
1134 assert!(ms_str.is_err());
1135 }
1136
1137 #[test]
1138 fn translate_tests() {
1139 let ms = Miniscript::<String, Segwitv0>::from_str("pk(A)").unwrap();
1140 let mut t = StrKeyTranslator::new();
1141 let uncompressed = qtum::PublicKey::from_str("0400232a2acfc9b43fa89f1b4f608fde335d330d7114f70ea42bfb4a41db368a3e3be6934a4097dd25728438ef73debb1f2ffdb07fec0f18049df13bdc5285dc5b").unwrap();
1142 t.pk_map.insert(String::from("A"), uncompressed);
1143 ms.translate_pk(&mut t).unwrap_err();
1144 }
1145}