@@ -2,15 +2,25 @@ use std::{error, fmt, str::FromStr};
2
2
3
3
use bitcoin:: {
4
4
self ,
5
- hashes:: hex:: FromHex ,
6
5
hashes:: Hash ,
6
+ hashes:: { hex:: FromHex , HashEngine } ,
7
+ schnorr:: XOnlyPublicKey ,
7
8
secp256k1,
8
9
secp256k1:: { Secp256k1 , Signing } ,
9
10
util:: bip32,
10
11
XpubIdentifier ,
11
12
} ;
12
13
13
- use MiniscriptKey ;
14
+ use { MiniscriptKey , ToPublicKey } ;
15
+
16
+ /// Single public key without any origin or range information
17
+ #[ derive( Debug , Eq , PartialEq , Clone , Ord , PartialOrd , Hash ) ]
18
+ pub enum SinglePubKey {
19
+ /// FullKey (compressed or uncompressed)
20
+ FullKey ( bitcoin:: PublicKey ) ,
21
+ /// XOnlyPublicKey
22
+ XOnly ( XOnlyPublicKey ) ,
23
+ }
14
24
15
25
/// The MiniscriptKey corresponding to Descriptors. This can
16
26
/// either be Single public key or a Xpub
@@ -28,7 +38,7 @@ pub struct DescriptorSinglePub {
28
38
/// Origin information
29
39
pub origin : Option < ( bip32:: Fingerprint , bip32:: DerivationPath ) > ,
30
40
/// The key
31
- pub key : bitcoin :: PublicKey ,
41
+ pub key : SinglePubKey ,
32
42
}
33
43
34
44
/// A Single Descriptor Secret Key with optional origin information
@@ -138,7 +148,7 @@ impl DescriptorSinglePriv {
138
148
139
149
Ok ( DescriptorSinglePub {
140
150
origin : self . origin . clone ( ) ,
141
- key : pub_key,
151
+ key : SinglePubKey :: FullKey ( pub_key) ,
142
152
} )
143
153
}
144
154
}
@@ -212,7 +222,10 @@ impl fmt::Display for DescriptorPublicKey {
212
222
match * self {
213
223
DescriptorPublicKey :: SinglePub ( ref pk) => {
214
224
maybe_fmt_master_id ( f, & pk. origin ) ?;
215
- pk. key . fmt ( f) ?;
225
+ match pk. key {
226
+ SinglePubKey :: FullKey ( full_key) => full_key. fmt ( f) ,
227
+ SinglePubKey :: XOnly ( x_only_key) => x_only_key. fmt ( f) ,
228
+ } ?;
216
229
Ok ( ( ) )
217
230
}
218
231
DescriptorPublicKey :: XPub ( ref xpub) => {
@@ -283,7 +296,7 @@ impl FromStr for DescriptorPublicKey {
283
296
284
297
fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
285
298
// A "raw" public key without any origin is the least we accept.
286
- if s. len ( ) < 66 {
299
+ if s. len ( ) < 64 {
287
300
return Err ( DescriptorKeyParseError (
288
301
"Key too short (<66 char), doesn't match any format" ,
289
302
) ) ;
@@ -302,15 +315,33 @@ impl FromStr for DescriptorPublicKey {
302
315
wildcard,
303
316
} ) )
304
317
} else {
305
- if key_part. len ( ) >= 2
306
- && !( & key_part[ 0 ..2 ] == "02" || & key_part[ 0 ..2 ] == "03" || & key_part[ 0 ..2 ] == "04" )
307
- {
308
- return Err ( DescriptorKeyParseError (
309
- "Only publickeys with prefixes 02/03/04 are allowed" ,
310
- ) ) ;
311
- }
312
- let key = bitcoin:: PublicKey :: from_str ( key_part)
313
- . map_err ( |_| DescriptorKeyParseError ( "Error while parsing simple public key" ) ) ?;
318
+ let key = match key_part. len ( ) {
319
+ 64 => {
320
+ let x_only_key = XOnlyPublicKey :: from_str ( key_part) . map_err ( |_| {
321
+ DescriptorKeyParseError ( "Error while parsing simple xonly key" )
322
+ } ) ?;
323
+ SinglePubKey :: XOnly ( x_only_key)
324
+ }
325
+ 66 | 130 => {
326
+ if !( & key_part[ 0 ..2 ] == "02"
327
+ || & key_part[ 0 ..2 ] == "03"
328
+ || & key_part[ 0 ..2 ] == "04" )
329
+ {
330
+ return Err ( DescriptorKeyParseError (
331
+ "Only publickeys with prefixes 02/03/04 are allowed" ,
332
+ ) ) ;
333
+ }
334
+ let key = bitcoin:: PublicKey :: from_str ( key_part) . map_err ( |_| {
335
+ DescriptorKeyParseError ( "Error while parsing simple public key" )
336
+ } ) ?;
337
+ SinglePubKey :: FullKey ( key)
338
+ }
339
+ _ => {
340
+ return Err ( DescriptorKeyParseError (
341
+ "Public keys must be 64/66/130 characters in size" ,
342
+ ) )
343
+ }
344
+ } ;
314
345
Ok ( DescriptorPublicKey :: SinglePub ( DescriptorSinglePub {
315
346
key,
316
347
origin,
@@ -360,10 +391,12 @@ impl DescriptorPublicKey {
360
391
fingerprint
361
392
} else {
362
393
let mut engine = XpubIdentifier :: engine ( ) ;
363
- single
364
- . key
365
- . write_into ( & mut engine)
366
- . expect ( "engines don't error" ) ;
394
+ match single. key {
395
+ SinglePubKey :: FullKey ( pk) => {
396
+ pk. write_into ( & mut engine) . expect ( "engines don't error" )
397
+ }
398
+ SinglePubKey :: XOnly ( x_only_pk) => engine. input ( & x_only_pk. serialize ( ) ) ,
399
+ } ;
367
400
bip32:: Fingerprint :: from ( & XpubIdentifier :: from_engine ( engine) [ ..4 ] )
368
401
}
369
402
}
@@ -427,7 +460,10 @@ impl DescriptorPublicKey {
427
460
self
428
461
}
429
462
430
- /// Computes the public key corresponding to this descriptor key
463
+ /// Computes the public key corresponding to this descriptor key.
464
+ /// When deriving from an XOnlyPublicKey, it adds the default 0x02 y-ordinate
465
+ /// and returns the obtained full [`bitcoin::PublicKey`]. All BIP32 derivations
466
+ /// always return a compressed key
431
467
///
432
468
/// Will return an error if the descriptor key has any hardened
433
469
/// derivation steps in its path, or if the key has any wildcards.
@@ -439,14 +475,17 @@ impl DescriptorPublicKey {
439
475
pub fn derive_public_key < C : secp256k1:: Verification > (
440
476
& self ,
441
477
secp : & Secp256k1 < C > ,
442
- ) -> Result < secp256k1 :: PublicKey , ConversionError > {
478
+ ) -> Result < bitcoin :: PublicKey , ConversionError > {
443
479
match * self {
444
- DescriptorPublicKey :: SinglePub ( ref pk) => Ok ( pk. key . key ) ,
480
+ DescriptorPublicKey :: SinglePub ( ref pk) => match pk. key {
481
+ SinglePubKey :: FullKey ( pk) => Ok ( pk) ,
482
+ SinglePubKey :: XOnly ( xpk) => Ok ( xpk. to_public_key ( ) ) ,
483
+ } ,
445
484
DescriptorPublicKey :: XPub ( ref xpk) => match xpk. wildcard {
446
485
Wildcard :: Unhardened => Err ( ConversionError :: Wildcard ) ,
447
486
Wildcard :: Hardened => Err ( ConversionError :: HardenedWildcard ) ,
448
487
Wildcard :: None => match xpk. xkey . derive_pub ( secp, & xpk. derivation_path . as_ref ( ) ) {
449
- Ok ( xpub) => Ok ( xpub. public_key ) ,
488
+ Ok ( xpub) => Ok ( bitcoin :: PublicKey :: new ( xpub. public_key ) ) ,
450
489
Err ( bip32:: Error :: CannotDeriveFromHardenedKey ) => {
451
490
Err ( ConversionError :: HardenedChild )
452
491
}
@@ -656,9 +695,20 @@ impl MiniscriptKey for DescriptorPublicKey {
656
695
657
696
fn is_uncompressed ( & self ) -> bool {
658
697
match self {
659
- DescriptorPublicKey :: SinglePub ( DescriptorSinglePub { ref key, .. } ) => {
660
- key. is_uncompressed ( )
661
- }
698
+ DescriptorPublicKey :: SinglePub ( DescriptorSinglePub {
699
+ key : SinglePubKey :: FullKey ( ref key) ,
700
+ ..
701
+ } ) => key. is_uncompressed ( ) ,
702
+ _ => false ,
703
+ }
704
+ }
705
+
706
+ fn is_x_only_key ( & self ) -> bool {
707
+ match self {
708
+ DescriptorPublicKey :: SinglePub ( DescriptorSinglePub {
709
+ key : SinglePubKey :: FullKey ( ref key) ,
710
+ ..
711
+ } ) => key. is_x_only_key ( ) ,
662
712
_ => false ,
663
713
}
664
714
}
@@ -708,7 +758,7 @@ mod test {
708
758
assert_eq ! (
709
759
DescriptorPublicKey :: from_str( desc) ,
710
760
Err ( DescriptorKeyParseError (
711
- "Error while parsing simple public key "
761
+ "Public keys must be 64/66/130 characters in size "
712
762
) )
713
763
) ;
714
764
0 commit comments