@@ -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;
@@ -42,6 +42,7 @@ pub mod limits;
42
42
pub mod satisfy;
43
43
pub mod types;
44
44
45
+ use self :: decode:: ParseableKey ;
45
46
use self :: lex:: { lex, TokenIter } ;
46
47
use self :: types:: Property ;
47
48
pub use miniscript:: context:: ScriptContext ;
@@ -137,17 +138,19 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
137
138
}
138
139
}
139
140
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
+ {
141
145
/// Attempt to parse an insane(scripts don't clear sanity checks)
142
146
/// script into a Miniscript representation.
143
147
/// Use this to parse scripts with repeated pubkeys, timelock mixing, malleable
144
148
/// scripts without sig or scripts that can exceed resource limits.
145
149
/// Some of the analysis guarantees of miniscript are lost when dealing with
146
150
/// insane scripts. In general, in a multi-party setting users should only
147
151
/// 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 > {
151
154
let tokens = lex ( script) ?;
152
155
let mut iter = TokenIter :: new ( tokens) ;
153
156
@@ -168,7 +171,38 @@ impl<Ctx: ScriptContext> Miniscript<bitcoin::PublicKey, Ctx> {
168
171
/// This function will fail parsing for scripts that do not clear
169
172
/// the [Miniscript::sanity_check] checks. Use [Miniscript::parse_insane] to
170
173
/// 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 > {
172
206
let ms = Self :: parse_insane ( script) ?;
173
207
ms. sanity_check ( ) ?;
174
208
Ok ( ms)
@@ -417,8 +451,8 @@ serde_string_impl_pk!(Miniscript, "a miniscript", Ctx; ScriptContext);
417
451
418
452
#[ cfg( test) ]
419
453
mod tests {
420
- use super :: Segwitv0 ;
421
454
use super :: { Miniscript , ScriptContext } ;
455
+ use super :: { Segwitv0 , Tap } ;
422
456
use hex_script;
423
457
use miniscript:: types:: { self , ExtData , Property , Type } ;
424
458
use miniscript:: Terminal ;
@@ -433,6 +467,7 @@ mod tests {
433
467
use std:: sync:: Arc ;
434
468
435
469
type Segwitv0Script = Miniscript < bitcoin:: PublicKey , Segwitv0 > ;
470
+ type Tapscript = Miniscript < bitcoin:: schnorr:: PublicKey , Tap > ;
436
471
437
472
fn pubkeys ( n : usize ) -> Vec < bitcoin:: PublicKey > {
438
473
let mut ret = Vec :: with_capacity ( n) ;
@@ -669,19 +704,19 @@ mod tests {
669
704
fn verify_parse ( ) {
670
705
let ms = "and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
671
706
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( ) ) ;
673
708
674
709
let ms = "and_v(v:sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
675
710
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( ) ) ;
677
712
678
713
let ms = "and_v(v:ripemd160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
679
714
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( ) ) ;
681
716
682
717
let ms = "and_v(v:hash256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))" ;
683
718
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( ) ) ;
685
720
}
686
721
687
722
#[ test]
@@ -907,4 +942,65 @@ mod tests {
907
942
. to_string( )
908
943
. contains( "unprintable character" ) ) ;
909
944
}
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
+ }
910
1006
}
0 commit comments