@@ -29,7 +29,7 @@ use std::{fmt, str};
2929
3030use bitcoin:: blockdata:: script;
3131
32- pub use self :: context:: { BareCtx , Legacy , Segwitv0 } ;
32+ pub use self :: context:: { BareCtx , Legacy , Segwitv0 , Tap } ;
3333
3434pub mod analyzable;
3535pub mod astelem;
@@ -167,6 +167,38 @@ impl<Ctx: ScriptContext> Miniscript<Ctx::Key, Ctx> {
167167 /// This function will fail parsing for scripts that do not clear
168168 /// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
169169 /// parse such scripts.
170+ ///
171+ /// ## Decode/Parse a miniscript from script hex
172+ ///
173+ /// ```rust
174+ /// extern crate bitcoin;
175+ /// extern crate miniscript;
176+ ///
177+ /// use miniscript::Miniscript;
178+ /// use miniscript::{Segwitv0, Tap};
179+ /// type XonlyKey = bitcoin::schnorr::PublicKey;
180+ /// type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0>;
181+ /// type TapScript = Miniscript<XonlyKey, Tap>;
182+ /// use bitcoin::hashes::hex::FromHex;
183+ /// fn main() {
184+ /// // parse x-only miniscript in Taproot context
185+ /// let tapscript_ms = TapScript::parse(&bitcoin::Script::from(Vec::<u8>::from_hex(
186+ /// "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
187+ /// ).expect("Even length hex")))
188+ /// .expect("Xonly keys are valid only in taproot context");
189+ /// // tapscript fails decoding when we use them with compressed keys
190+ /// let err = TapScript::parse(&bitcoin::Script::from(Vec::<u8>::from_hex(
191+ /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
192+ /// ).expect("Even length hex")))
193+ /// .expect_err("Compressed keys cannot be used in Taproot context");
194+ /// // Segwitv0 succeeds decoding with full keys.
195+ /// Segwitv0Script::parse(&bitcoin::Script::from(Vec::<u8>::from_hex(
196+ /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
197+ /// ).expect("Even length hex")))
198+ /// .expect("Compressed keys are allowed in Segwit context");
199+ ///
200+ /// }
201+ /// ```
170202 pub fn parse ( script : & script:: Script ) -> Result < Miniscript < Ctx :: Key , Ctx > , Error > {
171203 let ms = Self :: parse_insane ( script) ?;
172204 ms. sanity_check ( ) ?;
@@ -416,8 +448,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
416448
417449#[ cfg( test) ]
418450mod tests {
419- use super :: Segwitv0 ;
420451 use super :: { Miniscript , ScriptContext } ;
452+ use super :: { Segwitv0 , Tap } ;
421453 use hex_script;
422454 use miniscript:: types:: { self , ExtData , Property , Type } ;
423455 use miniscript:: Terminal ;
@@ -432,6 +464,7 @@ mod tests {
432464 use std:: sync:: Arc ;
433465
434466 type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
467+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
435468
436469 fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
437470 let mut ret = Vec :: with_capacity ( n) ;
@@ -668,19 +701,19 @@ mod tests {
668701 fn verify_parse ( ) {
669702 let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
670703 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
671- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
704+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
672705
673706 let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
674707 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
675- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
708+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
676709
677710 let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
678711 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
679- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
712+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
680713
681714 let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
682715 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
683- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
716+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
684717 }
685718
686719 #[ test]
@@ -906,4 +939,65 @@ mod tests {
906939 . to_string( )
907940 . contains( "unprintable character" ) ) ;
908941 }
942+
943+ #[ test]
944+ fn test_tapscript_rtt ( ) {
945+ // Test x-only invalid under segwitc0 context
946+ let ms = Segwitv0Script :: from_str_insane ( & format ! (
947+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
948+ ) ) ;
949+ assert_eq ! (
950+ ms. unwrap_err( ) . to_string( ) ,
951+ "unexpected «Key secp256k1 error: secp: malformed public key»"
952+ ) ;
953+ Tapscript :: from_str_insane ( & format ! (
954+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
955+ ) )
956+ . unwrap ( ) ;
957+
958+ // Now test that bitcoin::PublicKey works with Taproot context
959+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
960+ "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
961+ ) )
962+ . unwrap ( ) ;
963+
964+ // uncompressed keys should not be allowed
965+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
966+ "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
967+ ) )
968+ . unwrap_err ( ) ;
969+
970+ //---------------- test script <-> miniscript ---------------
971+ // Test parsing from scripts: x-only fails decoding in segwitv0 ctx
972+ Segwitv0Script :: parse_insane ( & hex_script (
973+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
974+ ) )
975+ . unwrap_err ( ) ;
976+ // x-only succeeds in tap ctx
977+ Tapscript :: parse_insane ( & hex_script (
978+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
979+ ) )
980+ . unwrap ( ) ;
981+ // tapscript fails decoding with compressed
982+ Tapscript :: parse_insane ( & hex_script (
983+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
984+ ) )
985+ . unwrap_err ( ) ;
986+ // Segwitv0 succeeds decoding with tapscript.
987+ Segwitv0Script :: parse_insane ( & hex_script (
988+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
989+ ) )
990+ . unwrap ( ) ;
991+
992+ // multi not allowed in tapscript
993+ Tapscript :: from_str_insane ( & format ! (
994+ "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
995+ ) )
996+ . unwrap_err ( ) ;
997+ // but allowed in segwit
998+ Segwitv0Script :: from_str_insane ( & format ! (
999+ "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
1000+ ) )
1001+ . unwrap ( ) ;
1002+ }
9091003}
0 commit comments