@@ -29,7 +29,7 @@ use std::{fmt, str};
29
29
30
30
use bitcoin:: blockdata:: script;
31
31
32
- pub use self :: context:: { BareCtx , Legacy , Segwitv0 } ;
32
+ pub use self :: context:: { BareCtx , Legacy , Segwitv0 , Tap } ;
33
33
34
34
pub mod analyzable;
35
35
pub mod astelem;
@@ -167,6 +167,38 @@ impl<Ctx: ScriptContext> Miniscript<Ctx::Key, Ctx> {
167
167
/// This function will fail parsing for scripts that do not clear
168
168
/// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
169
169
/// 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
+ /// ```
170
202
pub fn parse ( script : & script:: Script ) -> Result < Miniscript < Ctx :: Key , Ctx > , Error > {
171
203
let ms = Self :: parse_insane ( script) ?;
172
204
ms. sanity_check ( ) ?;
@@ -416,8 +448,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
416
448
417
449
#[ cfg( test) ]
418
450
mod tests {
419
- use super :: Segwitv0 ;
420
451
use super :: { Miniscript , ScriptContext } ;
452
+ use super :: { Segwitv0 , Tap } ;
421
453
use hex_script;
422
454
use miniscript:: types:: { self , ExtData , Property , Type } ;
423
455
use miniscript:: Terminal ;
@@ -432,6 +464,7 @@ mod tests {
432
464
use std:: sync:: Arc ;
433
465
434
466
type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
467
+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
435
468
436
469
fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
437
470
let mut ret = Vec :: with_capacity ( n) ;
@@ -668,19 +701,19 @@ mod tests {
668
701
fn verify_parse ( ) {
669
702
let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
670
703
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( ) ) ;
672
705
673
706
let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
674
707
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( ) ) ;
676
709
677
710
let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
678
711
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( ) ) ;
680
713
681
714
let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
682
715
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( ) ) ;
684
717
}
685
718
686
719
#[ test]
@@ -906,4 +939,65 @@ mod tests {
906
939
. to_string( )
907
940
. contains( "unprintable character" ) ) ;
908
941
}
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
+ }
909
1003
}
0 commit comments