@@ -25,24 +25,28 @@ use std::fmt;
2525use elements:: secp256k1_zkp;
2626
2727use crate :: descriptor:: checksum:: { desc_checksum, verify_checksum} ;
28+ use crate :: descriptor:: DescriptorSecretKey ;
2829use crate :: expression:: FromTree ;
2930use crate :: extensions:: { CovExtArgs , CovenantExt , Extension , ParseableExt } ;
3031use crate :: { expression, Error , MiniscriptKey , ToPublicKey } ;
3132
3233/// A description of a blinding key
33- #[ derive( Copy , Clone , PartialEq , Eq , PartialOrd , Ord , Debug , Hash ) ]
34+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
3435pub enum Key < Pk : MiniscriptKey > {
3536 /// Blinding key is computed using SLIP77 with the given master key
3637 Slip77 ( slip77:: MasterBlindingKey ) ,
3738 /// Blinding key is given directly
3839 Bare ( Pk ) ,
40+ /// Blinding key is given directly, as a secret key
41+ View ( DescriptorSecretKey ) ,
3942}
4043
4144impl < Pk : MiniscriptKey > fmt:: Display for Key < Pk > {
4245 fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
4346 match self {
4447 Key :: Slip77 ( data) => write ! ( f, "slip77({})" , data) ,
4548 Key :: Bare ( pk) => fmt:: Display :: fmt ( pk, f) ,
49+ Key :: View ( sk) => fmt:: Display :: fmt ( sk, f) ,
4650 }
4751 }
4852}
@@ -56,12 +60,13 @@ impl<Pk: MiniscriptKey + ToPublicKey> Key<Pk> {
5660 match * self {
5761 Key :: Slip77 ( ref mbk) => mbk. blinding_key ( secp, spk) ,
5862 Key :: Bare ( ref pk) => bare:: tweak_key ( secp, spk, pk) ,
63+ Key :: View ( ref sk) => bare:: tweak_key ( secp, spk, & sk. to_public ( secp) . expect ( "view keys cannot be multipath keys" ) . at_derivation_index ( 0 ) . expect ( "FIXME deal with derivation paths properly" ) ) ,
5964 }
6065 }
6166}
6267
6368/// A confidential descriptor
64- #[ derive( Clone , PartialEq , Eq , PartialOrd , Ord , Debug ) ]
69+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
6570pub struct Descriptor < Pk : MiniscriptKey , T : Extension = CovenantExt < CovExtArgs > > {
6671 /// The blinding key
6772 pub key : Key < Pk > ,
@@ -132,7 +137,8 @@ impl_from_str!(
132137 ( "slip77" , _) => return Err ( Error :: BadDescriptor (
133138 "slip77() must have exactly one argument" . to_owned( )
134139 ) ) ,
135- _ => Key :: Bare ( expression:: terminal( keyexpr, Pk :: from_str) ?) ,
140+ _ => expression:: terminal( keyexpr, Pk :: from_str) . map( Key :: Bare )
141+ . or_else( |_| expression:: terminal( keyexpr, DescriptorSecretKey :: from_str) . map( Key :: View ) ) ?,
136142 } ,
137143 descriptor: crate :: Descriptor :: from_tree( & top. args[ 1 ] ) ?,
138144 } )
@@ -207,7 +213,8 @@ mod tests {
207213 index, self . descriptor_str
208214 ) ;
209215 match self . key {
210- Key :: Bare ( ref pk) => println ! ( "** Blinding key: <code>{}</code>" , pk) ,
216+ Key :: Bare ( ref pk) => println ! ( "** Blinding public key: <code>{}</code>" , pk) ,
217+ Key :: View ( ref sk) => println ! ( "** Blinding private key: <code>{}</code>" , sk) ,
211218 Key :: Slip77 ( mbk) => println ! ( "** SLIP77 master blinding key: <code>{}</code>" , mbk) ,
212219 }
213220 println ! ( "** Confidential address: <code>{}</code>" , self . conf_addr) ;
@@ -355,4 +362,38 @@ mod tests {
355362 assert_eq ! ( bad_str. 1 , err. to_string( ) ) ;
356363 }
357364 }
365+
366+ #[ test]
367+ fn view_descriptor ( ) {
368+ let secp = secp256k1_zkp:: Secp256k1 :: new ( ) ;
369+
370+ let view_key = DescriptorSecretKey :: from_str (
371+ "xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb" ,
372+ ) . unwrap ( ) ;
373+ let ct_key = view_key. to_public ( & secp) . unwrap ( ) . at_derivation_index ( 0 ) . unwrap ( ) ; // FIXME figure out derivation
374+ let spk_key = DefiniteDescriptorKey :: from_str (
375+ "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH" ,
376+ )
377+ . unwrap ( ) ;
378+
379+ // View key, P2PKH
380+ let test = ConfidentialTest {
381+ key : Key :: View ( view_key. clone ( ) ) ,
382+ descriptor : crate :: Descriptor :: new_wpkh ( spk_key. clone ( ) ) . unwrap ( ) ,
383+ descriptor_str : format ! ( "ct({},elwpkh({}))#j95xktq7" , view_key, spk_key) ,
384+ conf_addr : "el1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvq78z9wukacu0" ,
385+ unconf_addr : "ert1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyk32h3ur" ,
386+ } ;
387+ test. check ( & secp) ;
388+
389+ // View key converted to Bare (note that addresses are the same)
390+ let test = ConfidentialTest {
391+ key : Key :: Bare ( ct_key. clone ( ) ) ,
392+ descriptor : crate :: Descriptor :: new_wpkh ( spk_key. clone ( ) ) . unwrap ( ) ,
393+ descriptor_str : format ! ( "ct({},elwpkh({}))#elmfpmp9" , ct_key, spk_key) ,
394+ conf_addr : "el1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvq78z9wukacu0" ,
395+ unconf_addr : "ert1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyk32h3ur" ,
396+ } ;
397+ test. check ( & secp) ;
398+ }
358399}
0 commit comments