1+ use crate :: error:: WasmMiniscriptError ;
12use crate :: try_into_js_value:: TryIntoJsValue ;
2- use miniscript:: bitcoin:: secp256k1:: Secp256k1 ;
3+ use miniscript:: bitcoin:: secp256k1:: { Context , Secp256k1 , Signing } ;
34use miniscript:: bitcoin:: ScriptBuf ;
45use miniscript:: descriptor:: KeyMap ;
56use miniscript:: { DefiniteDescriptorKey , Descriptor , DescriptorPublicKey } ;
67use std:: str:: FromStr ;
7- use wasm_bindgen:: prelude:: wasm_bindgen;
8- use wasm_bindgen:: { JsError , JsValue } ;
8+ use wasm_bindgen:: prelude:: * ;
99
1010pub ( crate ) enum WrapDescriptorEnum {
1111 Derivable ( Descriptor < DescriptorPublicKey > , KeyMap ) ,
@@ -18,7 +18,7 @@ pub struct WrapDescriptor(pub(crate) WrapDescriptorEnum);
1818
1919#[ wasm_bindgen]
2020impl WrapDescriptor {
21- pub fn node ( & self ) -> Result < JsValue , JsError > {
21+ pub fn node ( & self ) -> Result < JsValue , WasmMiniscriptError > {
2222 Ok ( match & self . 0 {
2323 WrapDescriptorEnum :: Derivable ( desc, _) => desc. try_to_js_value ( ) ?,
2424 WrapDescriptorEnum :: Definite ( desc) => desc. try_to_js_value ( ) ?,
@@ -45,18 +45,20 @@ impl WrapDescriptor {
4545 }
4646
4747 #[ wasm_bindgen( js_name = atDerivationIndex) ]
48- pub fn at_derivation_index ( & self , index : u32 ) -> Result < WrapDescriptor , JsError > {
48+ pub fn at_derivation_index ( & self , index : u32 ) -> Result < WrapDescriptor , WasmMiniscriptError > {
4949 match & self . 0 {
5050 WrapDescriptorEnum :: Derivable ( desc, _keys) => {
5151 let d = desc. at_derivation_index ( index) ?;
5252 Ok ( WrapDescriptor ( WrapDescriptorEnum :: Definite ( d) ) )
5353 }
54- _ => Err ( JsError :: new ( "Cannot derive from a definite descriptor" ) ) ,
54+ _ => Err ( WasmMiniscriptError :: new (
55+ "Cannot derive from a definite descriptor" ,
56+ ) ) ,
5557 }
5658 }
5759
5860 #[ wasm_bindgen( js_name = descType) ]
59- pub fn desc_type ( & self ) -> Result < JsValue , JsError > {
61+ pub fn desc_type ( & self ) -> Result < JsValue , WasmMiniscriptError > {
6062 ( match & self . 0 {
6163 WrapDescriptorEnum :: Derivable ( desc, _) => desc. desc_type ( ) ,
6264 WrapDescriptorEnum :: Definite ( desc) => desc. desc_type ( ) ,
@@ -66,34 +68,38 @@ impl WrapDescriptor {
6668 }
6769
6870 #[ wasm_bindgen( js_name = scriptPubkey) ]
69- pub fn script_pubkey ( & self ) -> Result < Vec < u8 > , JsError > {
71+ pub fn script_pubkey ( & self ) -> Result < Vec < u8 > , WasmMiniscriptError > {
7072 match & self . 0 {
7173 WrapDescriptorEnum :: Definite ( desc) => Ok ( desc. script_pubkey ( ) . to_bytes ( ) ) ,
72- _ => Err ( JsError :: new ( "Cannot derive from a non-definite descriptor" ) ) ,
74+ _ => Err ( WasmMiniscriptError :: new (
75+ "Cannot encode a derivable descriptor" ,
76+ ) ) ,
7377 }
7478 }
7579
76- fn explicit_script ( & self ) -> Result < ScriptBuf , JsError > {
80+ fn explicit_script ( & self ) -> Result < ScriptBuf , WasmMiniscriptError > {
7781 match & self . 0 {
7882 WrapDescriptorEnum :: Definite ( desc) => Ok ( desc. explicit_script ( ) ?) ,
79- WrapDescriptorEnum :: Derivable ( _, _) => {
80- Err ( JsError :: new ( "Cannot encode a derivable descriptor" ) )
81- }
82- WrapDescriptorEnum :: String ( _) => Err ( JsError :: new ( "Cannot encode a string descriptor" ) ) ,
83+ WrapDescriptorEnum :: Derivable ( _, _) => Err ( WasmMiniscriptError :: new (
84+ "Cannot encode a derivable descriptor" ,
85+ ) ) ,
86+ WrapDescriptorEnum :: String ( _) => Err ( WasmMiniscriptError :: new (
87+ "Cannot encode a string descriptor" ,
88+ ) ) ,
8389 }
8490 }
8591
86- pub fn encode ( & self ) -> Result < Vec < u8 > , JsError > {
92+ pub fn encode ( & self ) -> Result < Vec < u8 > , WasmMiniscriptError > {
8793 Ok ( self . explicit_script ( ) ?. to_bytes ( ) )
8894 }
8995
9096 #[ wasm_bindgen( js_name = toAsmString) ]
91- pub fn to_asm_string ( & self ) -> Result < String , JsError > {
97+ pub fn to_asm_string ( & self ) -> Result < String , WasmMiniscriptError > {
9298 Ok ( self . explicit_script ( ) ?. to_asm_string ( ) )
9399 }
94100
95101 #[ wasm_bindgen( js_name = maxWeightToSatisfy) ]
96- pub fn max_weight_to_satisfy ( & self ) -> Result < u32 , JsError > {
102+ pub fn max_weight_to_satisfy ( & self ) -> Result < u32 , WasmMiniscriptError > {
97103 let weight = ( match & self . 0 {
98104 WrapDescriptorEnum :: Derivable ( desc, _) => desc. max_weight_to_satisfy ( ) ,
99105 WrapDescriptorEnum :: Definite ( desc) => desc. max_weight_to_satisfy ( ) ,
@@ -102,26 +108,124 @@ impl WrapDescriptor {
102108 weight
103109 . to_wu ( )
104110 . try_into ( )
105- . map_err ( |_| JsError :: new ( "Weight exceeds u32" ) )
111+ . map_err ( |_| WasmMiniscriptError :: new ( "Weight exceeds u32" ) )
112+ }
113+
114+ fn from_string_derivable < C : Signing > (
115+ secp : & Secp256k1 < C > ,
116+ descriptor : & str ,
117+ ) -> Result < WrapDescriptor , WasmMiniscriptError > {
118+ let ( desc, keys) = Descriptor :: parse_descriptor ( & secp, descriptor) ?;
119+ Ok ( WrapDescriptor ( WrapDescriptorEnum :: Derivable ( desc, keys) ) )
120+ }
121+
122+ fn from_string_definite ( descriptor : & str ) -> Result < WrapDescriptor , WasmMiniscriptError > {
123+ let desc = Descriptor :: < DefiniteDescriptorKey > :: from_str ( descriptor) ?;
124+ Ok ( WrapDescriptor ( WrapDescriptorEnum :: Definite ( desc) ) )
106125 }
107126
127+ /// Parse a descriptor string with an explicit public key type.
128+ ///
129+ /// Note that this function permits parsing a non-derivable descriptor with a derivable key type.
130+ /// Use `from_string_detect_type` to automatically detect the key type.
131+ ///
132+ /// # Arguments
133+ /// * `descriptor` - A string containing the descriptor to parse
134+ /// * `pk_type` - The type of public key to expect:
135+ /// - "derivable": For descriptors containing derivation paths (eg. xpubs)
136+ /// - "definite": For descriptors with fully specified keys
137+ /// - "string": For descriptors with string placeholders
138+ ///
139+ /// # Returns
140+ /// * `Result<WrapDescriptor, WasmMiniscriptError>` - The parsed descriptor or an error
141+ ///
142+ /// # Example
143+ /// ```
144+ /// let desc = WrapDescriptor::from_string(
145+ /// "pk(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/*)",
146+ /// "derivable"
147+ /// );
148+ /// ```
108149 #[ wasm_bindgen( js_name = fromString, skip_typescript) ]
109- pub fn from_string ( descriptor : & str , pk_type : & str ) -> Result < WrapDescriptor , JsError > {
150+ pub fn from_string (
151+ descriptor : & str ,
152+ pk_type : & str ,
153+ ) -> Result < WrapDescriptor , WasmMiniscriptError > {
110154 match pk_type {
111- "derivable" => {
112- let secp = Secp256k1 :: new ( ) ;
113- let ( desc, keys) = Descriptor :: parse_descriptor ( & secp, descriptor) ?;
114- Ok ( WrapDescriptor ( WrapDescriptorEnum :: Derivable ( desc, keys) ) )
115- }
116- "definite" => {
117- let desc = Descriptor :: < DefiniteDescriptorKey > :: from_str ( descriptor) ?;
118- Ok ( WrapDescriptor ( WrapDescriptorEnum :: Definite ( desc) ) )
119- }
155+ "derivable" => WrapDescriptor :: from_string_derivable ( & Secp256k1 :: new ( ) , descriptor) ,
156+ "definite" => WrapDescriptor :: from_string_definite ( descriptor) ,
120157 "string" => {
121158 let desc = Descriptor :: < String > :: from_str ( descriptor) ?;
122159 Ok ( WrapDescriptor ( WrapDescriptorEnum :: String ( desc) ) )
123160 }
124- _ => Err ( JsError :: new ( "Invalid descriptor type" ) ) ,
161+ _ => Err ( WasmMiniscriptError :: new ( "Invalid descriptor type" ) ) ,
162+ }
163+ }
164+
165+ /// Parse a descriptor string, automatically detecting the appropriate public key type.
166+ /// This will check if the descriptor contains wildcards to determine if it should be
167+ /// parsed as derivable or definite.
168+ ///
169+ /// # Arguments
170+ /// * `descriptor` - A string containing the descriptor to parse
171+ ///
172+ /// # Returns
173+ /// * `Result<WrapDescriptor, WasmMiniscriptError>` - The parsed descriptor or an error
174+ ///
175+ /// # Example
176+ /// ```
177+ /// // Will be parsed as definite since it has no wildcards
178+ /// let desc = WrapDescriptor::from_string_detect_type(
179+ /// "pk(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)"
180+ /// );
181+ ///
182+ /// // Will be parsed as derivable since it contains a wildcard (*)
183+ /// let desc = WrapDescriptor::from_string_detect_type(
184+ /// "pk(xpub.../0/*)"
185+ /// );
186+ /// ```
187+ #[ wasm_bindgen( js_name = fromStringDetectType, skip_typescript) ]
188+ pub fn from_string_detect_type (
189+ descriptor : & str ,
190+ ) -> Result < WrapDescriptor , WasmMiniscriptError > {
191+ let secp = Secp256k1 :: new ( ) ;
192+ let ( descriptor, _key_map) = Descriptor :: parse_descriptor ( & secp, descriptor)
193+ . map_err ( |_| WasmMiniscriptError :: new ( "Invalid descriptor" ) ) ?;
194+ if descriptor. has_wildcard ( ) {
195+ WrapDescriptor :: from_string_derivable ( & secp, & descriptor. to_string ( ) )
196+ } else {
197+ WrapDescriptor :: from_string_definite ( & descriptor. to_string ( ) )
125198 }
126199 }
127200}
201+
202+ impl FromStr for WrapDescriptor {
203+ type Err = WasmMiniscriptError ;
204+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
205+ WrapDescriptor :: from_string_detect_type ( s)
206+ }
207+ }
208+
209+ #[ cfg( test) ]
210+ mod tests {
211+ use crate :: WrapDescriptor ;
212+
213+ #[ test]
214+ fn test_detect_type ( ) {
215+ let desc = WrapDescriptor :: from_string_detect_type (
216+ "pk(L4rK1yDtCWekvXuE6oXD9jCYfFNV2cWRpVuPLBcCU2z8TrisoyY1)" ,
217+ )
218+ . unwrap ( ) ;
219+
220+ assert_eq ! ( desc. has_wildcard( ) , false ) ;
221+ assert_eq ! (
222+ match desc {
223+ WrapDescriptor {
224+ 0 : crate :: descriptor:: WrapDescriptorEnum :: Definite ( _) ,
225+ } => true ,
226+ _ => false ,
227+ } ,
228+ true
229+ ) ;
230+ }
231+ }
0 commit comments