@@ -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;
@@ -175,6 +175,46 @@ impl<Ctx: ScriptContext> Miniscript<bitcoin::PublicKey, Ctx> {
175175 }
176176}
177177
178+ impl < Ctx : ScriptContext > Miniscript < bitcoin:: schnorr:: PublicKey , Ctx > {
179+ /// Attempt to parse an insane(scripts don't clear sanity checks)
180+ /// script into a Miniscript representation.
181+ /// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
182+ /// scripts without sig or scripts that can exceed resource limits.
183+ /// Some of the analysis guarantees of miniscript are lost when dealing with
184+ /// insane scripts. In general, in a multi-party setting users should only
185+ /// accept sane scripts.
186+ pub fn parse_insane (
187+ script : & script:: Script ,
188+ ) -> Result < Miniscript < bitcoin:: schnorr:: PublicKey , Ctx > , Error > {
189+ let tokens = lex ( script) ?;
190+ let mut iter = TokenIter :: new ( tokens) ;
191+
192+ let top = decode:: parse_tapscript ( & mut iter) ?;
193+ Ctx :: check_global_validity ( & top) ?;
194+ let type_check = types:: Type :: type_check ( & top. node , |_| None ) ?;
195+ if type_check. corr . base != types:: Base :: B {
196+ return Err ( Error :: NonTopLevel ( format ! ( "{:?}" , top) ) ) ;
197+ } ;
198+ if let Some ( leading) = iter. next ( ) {
199+ Err ( Error :: Trailing ( leading. to_string ( ) ) )
200+ } else {
201+ Ok ( top)
202+ }
203+ }
204+
205+ /// Attempt to parse a Script into Miniscript representation.
206+ /// This function will fail parsing for scripts that do not clear
207+ /// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
208+ /// parse such scripts.
209+ pub fn parse (
210+ script : & script:: Script ,
211+ ) -> Result < Miniscript < bitcoin:: schnorr:: PublicKey , Ctx > , Error > {
212+ let ms = Self :: parse_insane ( script) ?;
213+ ms. sanity_check ( ) ?;
214+ Ok ( ms)
215+ }
216+ }
217+
178218impl < Pk , Ctx > Miniscript < Pk , Ctx >
179219where
180220 Pk : MiniscriptKey ,
@@ -417,8 +457,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
417457
418458#[ cfg( test) ]
419459mod tests {
420- use super :: Segwitv0 ;
421460 use super :: { Miniscript , ScriptContext } ;
461+ use super :: { Segwitv0 , Tap } ;
422462 use hex_script;
423463 use miniscript:: types:: { self , ExtData , Property , Type } ;
424464 use miniscript:: Terminal ;
@@ -433,6 +473,7 @@ mod tests {
433473 use std:: sync:: Arc ;
434474
435475 type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
476+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
436477
437478 fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
438479 let mut ret = Vec :: with_capacity ( n) ;
@@ -669,19 +710,19 @@ mod tests {
669710 fn verify_parse ( ) {
670711 let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
671712 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
672- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
713+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
673714
674715 let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
675716 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
676- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
717+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
677718
678719 let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
679720 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
680- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
721+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
681722
682723 let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
683724 let ms: Segwitv0Script = Miniscript :: from_str_insane ( ms) . unwrap ( ) ;
684- assert_eq ! ( ms, Miniscript :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
725+ assert_eq ! ( ms, Segwitv0Script :: parse_insane( & ms. encode( ) ) . unwrap( ) ) ;
685726 }
686727
687728 #[ test]
@@ -907,4 +948,65 @@ mod tests {
907948 . to_string( )
908949 . contains( "unprintable character" ) ) ;
909950 }
951+
952+ #[ test]
953+ fn test_tapscript_rtt ( ) {
954+ // Test x-only invalid under segwitc0 context
955+ let ms = Segwitv0Script :: from_str_insane ( & format ! (
956+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
957+ ) ) ;
958+ assert_eq ! (
959+ ms. unwrap_err( ) . to_string( ) ,
960+ "unexpected «Key secp256k1 error: secp: malformed public key»"
961+ ) ;
962+ Tapscript :: from_str_insane ( & format ! (
963+ "pk(2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
964+ ) )
965+ . unwrap ( ) ;
966+
967+ // Now test that bitcoin::PublicKey works with Taproot context
968+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
969+ "pk(022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
970+ ) )
971+ . unwrap ( ) ;
972+
973+ // uncompressed keys should not be allowed
974+ Miniscript :: < bitcoin:: PublicKey , Tap > :: from_str_insane ( & format ! (
975+ "pk(04eed24a081bf1b1e49e3300df4bebe04208ac7e516b6f3ea8eb6e094584267c13483f89dcf194132e12238cc5a34b6b286fc7990d68ed1db86b69ebd826c63b29)"
976+ ) )
977+ . unwrap_err ( ) ;
978+
979+ //---------------- test script <-> miniscript ---------------
980+ // Test parsing from scripts: x-only fails decoding in segwitv0 ctx
981+ Segwitv0Script :: parse_insane ( & hex_script (
982+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
983+ ) )
984+ . unwrap_err ( ) ;
985+ // x-only succeeds in tap ctx
986+ Tapscript :: parse_insane ( & hex_script (
987+ "202788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
988+ ) )
989+ . unwrap ( ) ;
990+ // tapscript fails decoding with compressed
991+ Tapscript :: parse_insane ( & hex_script (
992+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
993+ ) )
994+ . unwrap_err ( ) ;
995+ // Segwitv0 succeeds decoding with tapscript.
996+ Segwitv0Script :: parse_insane ( & hex_script (
997+ "21022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99ac" ,
998+ ) )
999+ . unwrap ( ) ;
1000+
1001+ // multi not allowed in tapscript
1002+ Tapscript :: from_str_insane ( & format ! (
1003+ "multi(1,2788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
1004+ ) )
1005+ . unwrap_err ( ) ;
1006+ // but allowed in segwit
1007+ Segwitv0Script :: from_str_insane ( & format ! (
1008+ "multi(1,022788ee41e76f4f3af603da5bc8fa22997bc0344bb0f95666ba6aaff0242baa99)"
1009+ ) )
1010+ . unwrap ( ) ;
1011+ }
9101012}
0 commit comments