@@ -32,7 +32,8 @@ use std::str::{self, FromStr};
3232
3333use expression;
3434use miniscript;
35- use miniscript:: Miniscript ;
35+ use miniscript:: context:: ScriptContextError ;
36+ use miniscript:: { Legacy , Miniscript , Segwitv0 } ;
3637use Error ;
3738use MiniscriptKey ;
3839use Satisfier ;
@@ -50,8 +51,8 @@ pub use self::satisfied_constraints::Stack;
5051/// Script descriptor
5152#[ derive( Clone , PartialEq , Eq , PartialOrd , Ord ) ]
5253pub enum Descriptor < Pk : MiniscriptKey > {
53- /// A raw scriptpubkey (including pay-to-pubkey)
54- Bare ( Miniscript < Pk > ) ,
54+ /// A raw scriptpubkey (including pay-to-pubkey) under Legacy context
55+ Bare ( Miniscript < Pk , Legacy > ) ,
5556 /// Pay-to-Pubkey
5657 Pk ( Pk ) ,
5758 /// Pay-to-PubKey-Hash
@@ -60,16 +61,19 @@ pub enum Descriptor<Pk: MiniscriptKey> {
6061 Wpkh ( Pk ) ,
6162 /// Pay-to-Witness-PubKey-Hash inside P2SH
6263 ShWpkh ( Pk ) ,
63- /// Pay-to-ScriptHash
64- Sh ( Miniscript < Pk > ) ,
65- /// Pay-to-Witness-ScriptHash
66- Wsh ( Miniscript < Pk > ) ,
67- /// P2SH-P2WSH
68- ShWsh ( Miniscript < Pk > ) ,
64+ /// Pay-to-ScriptHash with Legacy context
65+ Sh ( Miniscript < Pk , Legacy > ) ,
66+ /// Pay-to-Witness-ScriptHash with Segwitv0 context
67+ Wsh ( Miniscript < Pk , Segwitv0 > ) ,
68+ /// P2SH-P2WSH with Segwitv0 context
69+ ShWsh ( Miniscript < Pk , Segwitv0 > ) ,
6970}
7071
7172impl < Pk : MiniscriptKey > Descriptor < Pk > {
7273 /// Convert a descriptor using abstract keys to one using specific keys
74+ /// This will panic if translatefpk returns an uncompressed key when
75+ /// converting to a Segwit descriptor. To prevent this panic, ensure
76+ /// translatefpk returns an error in this case instead.
7377 pub fn translate_pk < Fpk , Fpkh , Q , E > (
7478 & self ,
7579 mut translatefpk : Fpk ,
@@ -86,8 +90,18 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
8690 ) ) ,
8791 Descriptor :: Pk ( ref pk) => translatefpk ( pk) . map ( Descriptor :: Pk ) ,
8892 Descriptor :: Pkh ( ref pk) => translatefpk ( pk) . map ( Descriptor :: Pkh ) ,
89- Descriptor :: Wpkh ( ref pk) => translatefpk ( pk) . map ( Descriptor :: Wpkh ) ,
90- Descriptor :: ShWpkh ( ref pk) => translatefpk ( pk) . map ( Descriptor :: ShWpkh ) ,
93+ Descriptor :: Wpkh ( ref pk) => {
94+ if pk. is_uncompressed ( ) {
95+ panic ! ( "Uncompressed pubkeys are not allowed in segwit v0 scripts" ) ;
96+ }
97+ translatefpk ( pk) . map ( Descriptor :: Wpkh )
98+ }
99+ Descriptor :: ShWpkh ( ref pk) => {
100+ if pk. is_uncompressed ( ) {
101+ panic ! ( "Uncompressed pubkeys are not allowed in segwit v0 scripts" ) ;
102+ }
103+ translatefpk ( pk) . map ( Descriptor :: ShWpkh )
104+ }
91105 Descriptor :: Sh ( ref ms) => Ok ( Descriptor :: Sh (
92106 ms. translate_pk ( & mut translatefpk, & mut translatefpkh) ?,
93107 ) ) ,
@@ -201,7 +215,8 @@ impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
201215 let addr = bitcoin:: Address :: p2wpkh ( & pk. to_public_key ( ) , bitcoin:: Network :: Bitcoin ) ;
202216 addr. script_pubkey ( )
203217 }
204- Descriptor :: Sh ( ref d) | Descriptor :: Wsh ( ref d) | Descriptor :: ShWsh ( ref d) => d. encode ( ) ,
218+ Descriptor :: Sh ( ref d) => d. encode ( ) ,
219+ Descriptor :: Wsh ( ref d) | Descriptor :: ShWsh ( ref d) => d. encode ( ) ,
205220 }
206221 }
207222
@@ -396,7 +411,12 @@ where
396411 expression:: terminal ( & top. args [ 0 ] , |pk| Pk :: from_str ( pk) . map ( Descriptor :: Pkh ) )
397412 }
398413 ( "wpkh" , 1 ) => {
399- expression:: terminal ( & top. args [ 0 ] , |pk| Pk :: from_str ( pk) . map ( Descriptor :: Wpkh ) )
414+ let wpkh = expression:: terminal ( & top. args [ 0 ] , |pk| Pk :: from_str ( pk) ) ?;
415+ if wpkh. is_uncompressed ( ) {
416+ Err ( Error :: ContextError ( ScriptContextError :: CompressedOnly ) )
417+ } else {
418+ Ok ( Descriptor :: Wpkh ( wpkh) )
419+ }
400420 }
401421 ( "sh" , 1 ) => {
402422 let newtop = & top. args [ 0 ] ;
@@ -409,9 +429,14 @@ where
409429 Ok ( Descriptor :: ShWsh ( sub) )
410430 }
411431 }
412- ( "wpkh" , 1 ) => expression:: terminal ( & newtop. args [ 0 ] , |pk| {
413- Pk :: from_str ( pk) . map ( Descriptor :: ShWpkh )
414- } ) ,
432+ ( "wpkh" , 1 ) => {
433+ let wpkh = expression:: terminal ( & newtop. args [ 0 ] , |pk| Pk :: from_str ( pk) ) ?;
434+ if wpkh. is_uncompressed ( ) {
435+ Err ( Error :: ContextError ( ScriptContextError :: CompressedOnly ) )
436+ } else {
437+ Ok ( Descriptor :: ShWpkh ( wpkh) )
438+ }
439+ }
415440 _ => {
416441 let sub = Miniscript :: from_tree ( & top. args [ 0 ] ) ?;
417442 if sub. ty . corr . base != miniscript:: types:: Base :: B {
@@ -586,6 +611,18 @@ mod tests {
586611 StdDescriptor :: from_str ( "nl:0" ) . unwrap_err ( ) ; //issue 63
587612
588613 StdDescriptor :: from_str ( TEST_PK ) . unwrap ( ) ;
614+
615+ let uncompressed_pk =
616+ "0414fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267be5645686309c6e6736dbd93940707cc9143d3cf29f1b877ff340e2cb2d259cf" ;
617+
618+ // Context tests
619+ StdDescriptor :: from_str ( & format ! ( "pk({})" , uncompressed_pk) ) . unwrap ( ) ;
620+ StdDescriptor :: from_str ( & format ! ( "pkh({})" , uncompressed_pk) ) . unwrap ( ) ;
621+ StdDescriptor :: from_str ( & format ! ( "sh(pk({}))" , uncompressed_pk) ) . unwrap ( ) ;
622+ StdDescriptor :: from_str ( & format ! ( "wpkh({})" , uncompressed_pk) ) . unwrap_err ( ) ;
623+ StdDescriptor :: from_str ( & format ! ( "sh(wpkh({}))" , uncompressed_pk) ) . unwrap_err ( ) ;
624+ StdDescriptor :: from_str ( & format ! ( "wsh(pk{})" , uncompressed_pk) ) . unwrap_err ( ) ;
625+ StdDescriptor :: from_str ( & format ! ( "sh(wsh(pk{}))" , uncompressed_pk) ) . unwrap_err ( ) ;
589626 }
590627
591628 #[ test]
@@ -875,6 +912,8 @@ mod tests {
875912 ) ;
876913 assert_eq ! ( sh. unsigned_script_sig( ) , bitcoin:: Script :: new( ) ) ;
877914
915+ let ms = ms_str ! ( "c:pk_k({})" , pk) ;
916+
878917 let wsh = Descriptor :: Wsh ( ms. clone ( ) ) ;
879918 wsh. satisfy ( & mut txin, & satisfier) . expect ( "satisfaction" ) ;
880919 assert_eq ! (
0 commit comments