@@ -30,7 +30,7 @@ use std::{fmt, str};
3030use bitcoin;
3131use bitcoin:: blockdata:: script;
3232
33- pub use self :: context:: { BareCtx , Legacy , Segwitv0 } ;
33+ pub use self :: context:: { BareCtx , Legacy , Segwitv0 , Tap } ;
3434
3535pub mod analyzable;
3636pub mod astelem;
@@ -42,6 +42,7 @@ pub mod limits;
4242pub mod satisfy;
4343pub mod types;
4444
45+ use self :: decode:: ParseableKey ;
4546use self :: lex:: { lex, TokenIter } ;
4647use self :: types:: Property ;
4748pub use miniscript:: context:: ScriptContext ;
@@ -137,17 +138,19 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
137138 }
138139}
139140
140- impl < Ctx : ScriptContext > Miniscript < bitcoin:: PublicKey , Ctx > {
141+ impl < Pk , Ctx : ScriptContext > Miniscript < Pk , Ctx >
142+ where
143+ Pk : ParseableKey + MiniscriptKey < Hash = bitcoin:: hashes:: hash160:: Hash > ,
144+ {
141145 /// Attempt to parse an insane(scripts don't clear sanity checks)
142146 /// script into a Miniscript representation.
143147 /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
144148 /// scripts without sig or scripts that can exceed resource limits.
145149 /// Some of the analysis guarantees of miniscript are lost when dealing with
146150 /// insane scripts. In general, in a multi-party setting users should only
147151 /// accept sane scripts.
148- pub fn parse_insane (
149- script : & script:: Script ,
150- ) -> Result < Miniscript < bitcoin:: PublicKey , Ctx > , Error > {
152+ /// This function can be used any key that follow the `[decode::ParseableKey]` trait
153+ pub fn parse_insane ( script : & script:: Script ) -> Result < Miniscript < Pk , Ctx > , Error > {
151154 let tokens = lex ( script) ?;
152155 let mut iter = TokenIter :: new ( tokens) ;
153156
@@ -168,7 +171,38 @@ impl<Ctx: ScriptContext> Miniscript<bitcoin::PublicKey, Ctx> {
168171 /// This function will fail parsing for scripts that do not clear
169172 /// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
170173 /// parse such scripts.
171- pub fn parse ( script : & script:: Script ) -> Result < Miniscript < bitcoin:: PublicKey , Ctx > , Error > {
174+ /// ## Decode/Parse a miniscript from script hex
175+ ///
176+ /// ```rust
177+ /// extern crate bitcoin;
178+ /// extern crate miniscript;
179+ ///
180+ /// use miniscript::Miniscript;
181+ /// use miniscript::{Segwitv0, Tap};
182+ /// type XonlyKey = bitcoin::schnorr::PublicKey;
183+ /// type Segwitv0Script = Miniscript<bitcoin::PublicKey, Segwitv0>;
184+ /// type TapScript = Miniscript<XonlyKey, Tap>;
185+ /// use bitcoin::hashes::hex::FromHex;
186+ /// fn main() {
187+ /// // parse x-only miniscript in Taproot context
188+ /// let tapscript_ms = TapScript::parse_insane(&bitcoin::Script::from(Vec::<u8>::from_hex(
189+ /// "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
190+ /// ).expect("Even length hex")))
191+ /// .expect("Xonly keys are valid only in taproot context");
192+ /// // tapscript fails decoding when we use them with compressed keys
193+ /// let err = TapScript::parse_insane(&bitcoin::Script::from(Vec::<u8>::from_hex(
194+ /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
195+ /// ).expect("Even length hex")))
196+ /// .expect_err("Compressed keys cannot be used in Taproot context");
197+ /// // Segwitv0 succeeds decoding with full keys.
198+ /// Segwitv0Script::parse_insane(&bitcoin::Script::from(Vec::<u8>::from_hex(
199+ /// "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac",
200+ /// ).expect("Even length hex")))
201+ /// .expect("Compressed keys are allowed in Segwit context");
202+ ///
203+ /// }
204+ /// ```
205+ pub fn parse ( script : & script:: Script ) -> Result < Miniscript < Pk , Ctx > , Error > {
172206 let ms = Self :: parse_insane ( script) ?;
173207 ms. sanity_check ( ) ?;
174208 Ok ( ms)
@@ -417,8 +451,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
417451
418452#[ cfg( test) ]
419453mod tests {
420- use super :: Segwitv0 ;
421454 use super :: { Miniscript , ScriptContext } ;
455+ use super :: { Segwitv0 , Tap } ;
422456 use hex_script;
423457 use miniscript:: types:: { self , ExtData , Property , Type } ;
424458 use miniscript:: Terminal ;
@@ -433,6 +467,7 @@ mod tests {
433467 use std:: sync:: Arc ;
434468
435469 type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
470+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
436471
437472 fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
438473 let mut ret = Vec :: with_capacity ( n) ;
@@ -669,19 +704,19 @@ mod tests {
669704 fn verify_parse ( ) {
670705 let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
671706 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
672- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
707+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
673708
674709 let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
675710 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
676- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
711+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
677712
678713 let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
679714 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
680- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
715+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
681716
682717 let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
683718 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
684- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
719+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
685720 }
686721
687722 #[ test]
@@ -907,4 +942,65 @@ mod tests {
907942 . to_string( )
908943 . contains( "unprintable character" ) ) ;
909944 }
945+
946+ #[ test]
947+ fn test_tapscript_rtt ( ) {
948+ // Test x-only invalid under segwitc0 context
949+ let ms = Segwitv0Script :: from_str_insane ( & format ! (
950+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
951+ ) ) ;
952+ assert_eq ! (
953+ ms. unwrap_err( ) . to_string( ) ,
954+ "unexpected «Key secp256k1 error: secp: malformed public key»"
955+ ) ;
956+ Tapscript :: from_str_insane ( & format ! (
957+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
958+ ) )
959+ . unwrap ( ) ;
960+
961+ // Now test that bitcoin::PublicKey works with Taproot context
962+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
963+ "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
964+ ) )
965+ . unwrap ( ) ;
966+
967+ // uncompressed keys should not be allowed
968+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
969+ "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
970+ ) )
971+ . unwrap_err ( ) ;
972+
973+ //---------------- test script <-> miniscript ---------------
974+ // Test parsing from scripts: x-only fails decoding in segwitv0 ctx
975+ Segwitv0Script :: parse_insane ( & hex_script (
976+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
977+ ) )
978+ . unwrap_err ( ) ;
979+ // x-only succeeds in tap ctx
980+ Tapscript :: parse_insane ( & hex_script (
981+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
982+ ) )
983+ . unwrap ( ) ;
984+ // tapscript fails decoding with compressed
985+ Tapscript :: parse_insane ( & hex_script (
986+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
987+ ) )
988+ . unwrap_err ( ) ;
989+ // Segwitv0 succeeds decoding with tapscript.
990+ Segwitv0Script :: parse_insane ( & hex_script (
991+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
992+ ) )
993+ . unwrap ( ) ;
994+
995+ // multi not allowed in tapscript
996+ Tapscript :: from_str_insane ( & format ! (
997+ "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
998+ ) )
999+ . unwrap_err ( ) ;
1000+ // but allowed in segwit
1001+ Segwitv0Script :: from_str_insane ( & format ! (
1002+ "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
1003+ ) )
1004+ . unwrap ( ) ;
1005+ }
9101006}
0 commit comments