20
20
pub mod hash_to_private;
21
21
pub mod slip77;
22
22
23
- use crate :: { Error , MiniscriptKey , ToPublicKey } ;
23
+ use crate :: descriptor:: checksum:: verify_checksum;
24
+ use crate :: { expression, Error , MiniscriptKey , ToPublicKey } ;
25
+ use crate :: expression:: FromTree ;
24
26
use elements:: hashes:: hex;
25
27
use elements:: secp256k1_zkp;
26
28
use crate :: extensions:: { CovenantExt , CovExtArgs , Extension , ParseableExt } ;
@@ -39,7 +41,7 @@ pub enum Key<Pk: MiniscriptKey> {
39
41
Bare ( Pk ) ,
40
42
}
41
43
42
- impl < Pk : MiniscriptKey + fmt :: Display > fmt:: Display for Key < Pk > {
44
+ impl < Pk : MiniscriptKey > fmt:: Display for Key < Pk > {
43
45
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
44
46
match * self {
45
47
Key :: HashToPrivate ( ref keys) => {
@@ -115,6 +117,48 @@ impl<Pk: MiniscriptKey + ToPublicKey, T: Extension + ParseableExt> Descriptor<Pk
115
117
}
116
118
}
117
119
120
+ impl < Pk : MiniscriptKey , T : Extension > fmt:: Display for Descriptor < Pk , T > {
121
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
122
+ write ! ( f, "ct({},{})" , self . key, self . descriptor)
123
+ }
124
+ }
125
+
126
+ impl_from_str ! (
127
+ ; T ; Extension ,
128
+ Descriptor <Pk , T >,
129
+ type Err = Error ; ,
130
+ fn from_str( s: & str ) -> Result <Descriptor <Pk , T >, Error > {
131
+ let desc_str = verify_checksum( s) ?;
132
+ let top = expression:: Tree :: from_str( desc_str) ?;
133
+
134
+ if top. name != "ct" {
135
+ return Err ( Error :: BadDescriptor ( String :: from(
136
+ "Not a CT Descriptor" ,
137
+ ) ) ) ;
138
+ }
139
+ if top. args. len( ) != 2 {
140
+ return Err ( Error :: BadDescriptor (
141
+ format!( "CT descriptor had {} arguments rather than 2" , top. args. len( ) )
142
+ ) ) ;
143
+ }
144
+
145
+ let keyexpr = & top. args[ 0 ] ;
146
+ Ok ( Descriptor {
147
+ key: match ( keyexpr. name, keyexpr. args. len( ) ) {
148
+ ( "slip77" , 1 ) => Key :: Slip77 ( expression:: terminal( & keyexpr. args[ 0 ] , hex:: FromHex :: from_hex) ?) ,
149
+ ( "hash_to_private" , 0 ) => return Err ( Error :: BadDescriptor (
150
+ format!( "hash_to_private must have a nonzero number of keys" )
151
+ ) ) ,
152
+ ( "hash_to_private" , _) => Key :: HashToPrivate (
153
+ keyexpr. args. iter( ) . map( |arg| expression:: terminal( arg, Pk :: from_str) ) . collect:: <Result <Vec <_>, _>>( ) ?
154
+ ) ,
155
+ _ => Key :: Bare ( expression:: terminal( & keyexpr, Pk :: from_str) ?) ,
156
+ } ,
157
+ descriptor: crate :: Descriptor :: from_tree( & top. args[ 1 ] ) ?,
158
+ } )
159
+ }
160
+ ) ;
161
+
118
162
#[ cfg( test) ]
119
163
mod tests {
120
164
use super :: * ;
@@ -196,12 +240,24 @@ mod tests {
196
240
desc. address( & secp, & elements:: AddressParams :: ELEMENTS ) . unwrap( ) . to_string( ) ,
197
241
"el1qq2rr0dcmn6mfvcf7x486z3djs7j283arxspjj6adzgjsjxsa5r0v2rmqvvk2ce5ksnxcs9ecgtnryt7xg34068lggvfp79zus" ,
198
242
) ;
243
+ assert_eq ! (
244
+ desc. to_string( ) ,
245
+ "ct(hash_to_private(02fb7a8c3b2fc3e42095b51d8c28e37f15ec3da8f84ec7d51cbbc83abd39eec2d6,03d5614e0f8983f8d1bed90299e8cb40b1f0fd61d36d7e2958682cca625d14d744,03cb8d2ef4aaf7be6a9bb80afe798e6cf72fd102b46c44f81c31593ae62a3c7de8,032ea1c87f4023d48ad3a30acf1ac63b6d8a876b880c0ddc9a4163ab6a25f28a2b,03d38adcd566db859f59ba28cc6cba54e9c9ae5399b875034e6038f1e22fe3f2ff,02729d8336d93c59ab20edb4cf7589ff5dbc159d2e239e80d0db01ddd22b994f0b,02fde5e64596d934e0d560afff13d7f9732b3751555f836c5093ee69d4fa3d836f,029f00e038dbc4ecd875d5170feff8f56ae7e0c0a90467454d78a4212e1964194d,0247c0c61ad7b6671c4ea9b277bffb0b27412ae2d2795cccadb405b152a67ff250,03e8485a732bced45ad81ac259d9889459a0c6128a16b6782cc68edfa43fea2783,02412e2ba495c3f52f80a6db41524079f293ae2d47f6a239550879b0430b3c0e0e,023569dac5cefeac572481ad6eb1205c59a774236352c2a34a6f04c9597ea5218f,02bb22f63397b818ac12f43a47446333cbc3da79427469257561801f0bdd0d8342,03fbff1f52bb55262c83a66873433e8408eec5f06a24f33eb2f2eab146e47c7d2a,02253adb8820f73fdeec7a3472baddde79795d7792f4fb6d748b8b33bda9d30449,03517fb63ed0136223d9b2239ce2594a78b7ad4adf3194dbfe94cd30047d2d868e,02be9a86606e90a1cc3a97393a295ee9661e943e7585519266d230b31cf8b1812c,034e93eb5054ab504205855ddafb4bfe1e0deacc51929c784c0b2f9b11123ec2e2,0212d3048099d52ee4b8153f75623fc7153788f659e8c1df21ac2e6ff21561cb10),elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)#pmkay5r6)" ,
246
+ ) ;
199
247
// Same address with sorted blinding keys (should be the same)
200
248
desc. key = Key :: HashToPrivate ( sorted_keys) ;
201
249
assert_eq ! (
202
250
desc. address( & secp, & elements:: AddressParams :: ELEMENTS ) . unwrap( ) . to_string( ) ,
203
251
"el1qq2rr0dcmn6mfvcf7x486z3djs7j283arxspjj6adzgjsjxsa5r0v2rmqvvk2ce5ksnxcs9ecgtnryt7xg34068lggvfp79zus" ,
204
252
) ;
253
+ // but notice that when serializing as a string, the key order has changed
254
+ // FIXME: is this the correct behavior? (i vote yes, so string round-tripping works)
255
+ // FIXME: should equality use the sorted order? (i vote no because it's hard to do)
256
+ // FIXME: should the answer to either of these be in the spec? (yeah, probably)
257
+ assert_eq ! (
258
+ desc. to_string( ) ,
259
+ "ct(hash_to_private(0212d3048099d52ee4b8153f75623fc7153788f659e8c1df21ac2e6ff21561cb10,02253adb8820f73fdeec7a3472baddde79795d7792f4fb6d748b8b33bda9d30449,023569dac5cefeac572481ad6eb1205c59a774236352c2a34a6f04c9597ea5218f,02412e2ba495c3f52f80a6db41524079f293ae2d47f6a239550879b0430b3c0e0e,0247c0c61ad7b6671c4ea9b277bffb0b27412ae2d2795cccadb405b152a67ff250,02729d8336d93c59ab20edb4cf7589ff5dbc159d2e239e80d0db01ddd22b994f0b,029f00e038dbc4ecd875d5170feff8f56ae7e0c0a90467454d78a4212e1964194d,02bb22f63397b818ac12f43a47446333cbc3da79427469257561801f0bdd0d8342,02be9a86606e90a1cc3a97393a295ee9661e943e7585519266d230b31cf8b1812c,02fb7a8c3b2fc3e42095b51d8c28e37f15ec3da8f84ec7d51cbbc83abd39eec2d6,02fde5e64596d934e0d560afff13d7f9732b3751555f836c5093ee69d4fa3d836f,032ea1c87f4023d48ad3a30acf1ac63b6d8a876b880c0ddc9a4163ab6a25f28a2b,034e93eb5054ab504205855ddafb4bfe1e0deacc51929c784c0b2f9b11123ec2e2,03517fb63ed0136223d9b2239ce2594a78b7ad4adf3194dbfe94cd30047d2d868e,03cb8d2ef4aaf7be6a9bb80afe798e6cf72fd102b46c44f81c31593ae62a3c7de8,03d38adcd566db859f59ba28cc6cba54e9c9ae5399b875034e6038f1e22fe3f2ff,03d5614e0f8983f8d1bed90299e8cb40b1f0fd61d36d7e2958682cca625d14d744,03e8485a732bced45ad81ac259d9889459a0c6128a16b6782cc68edfa43fea2783,03fbff1f52bb55262c83a66873433e8408eec5f06a24f33eb2f2eab146e47c7d2a),elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)#pmkay5r6)" ,
260
+ ) ;
205
261
206
262
// P2PKH address
207
263
desc. descriptor = crate :: Descriptor :: new_pkh ( spk_key. clone ( ) ) ;
0 commit comments