@@ -30,7 +30,7 @@ use std::{fmt, str};
30
30
use bitcoin;
31
31
use bitcoin:: blockdata:: script;
32
32
33
- pub use self :: context:: { BareCtx , Legacy , Segwitv0 } ;
33
+ pub use self :: context:: { BareCtx , Legacy , Segwitv0 , Tap } ;
34
34
35
35
pub mod analyzable;
36
36
pub mod astelem;
@@ -175,6 +175,46 @@ impl<Ctx: ScriptContext> Miniscript<bitcoin::PublicKey, Ctx> {
175
175
}
176
176
}
177
177
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
+
178
218
impl < Pk , Ctx > Miniscript < Pk , Ctx >
179
219
where
180
220
Pk : MiniscriptKey ,
@@ -417,8 +457,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
417
457
418
458
#[ cfg( test) ]
419
459
mod tests {
420
- use super :: Segwitv0 ;
421
460
use super :: { Miniscript , ScriptContext } ;
461
+ use super :: { Segwitv0 , Tap } ;
422
462
use hex_script;
423
463
use miniscript:: types:: { self , ExtData , Property , Type } ;
424
464
use miniscript:: Terminal ;
@@ -433,6 +473,7 @@ mod tests {
433
473
use std:: sync:: Arc ;
434
474
435
475
type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
476
+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
436
477
437
478
fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
438
479
let mut ret = Vec :: with_capacity ( n) ;
@@ -669,19 +710,19 @@ mod tests {
669
710
fn verify_parse ( ) {
670
711
let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
671
712
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( ) ) ;
673
714
674
715
let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
675
716
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( ) ) ;
677
718
678
719
let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
679
720
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( ) ) ;
681
722
682
723
let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
683
724
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( ) ) ;
685
726
}
686
727
687
728
#[ test]
@@ -907,4 +948,65 @@ mod tests {
907
948
. to_string( )
908
949
. contains( "unprintable character" ) ) ;
909
950
}
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
+ }
910
1012
}
0 commit comments