elements_miniscript/confidential/
mod.rs

1// Miniscript
2// Written in 2022 by
3//     Andrew Poelstra <apoelstra@wpsoftware.net>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the CC0 Public Domain Dedication
11// along with this software.
12// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13//
14
15//! Confidential Descriptors
16//!
17//! Implements ELIP ????, described at `URL`
18//!
19
20pub mod bare;
21pub mod elip151;
22pub mod slip77;
23
24use std::fmt;
25
26use bitcoin::bip32;
27use elements::secp256k1_zkp;
28
29use crate::descriptor::checksum::{self, verify_checksum};
30use crate::descriptor::{
31    ConversionError, DefiniteDescriptorKey, DescriptorSecretKey, DescriptorPublicKey,
32    DescriptorXKey, Wildcard
33};
34use crate::expression::FromTree;
35use crate::extensions::{CovExtArgs, CovenantExt, Extension, ParseableExt};
36use crate::{expression, Error, MiniscriptKey, ToPublicKey};
37
38/// A description of a blinding key
39#[derive(Clone, PartialEq, Eq, Debug)]
40pub enum Key {
41    /// Blinding key is computed using SLIP77 with the given master key
42    Slip77(slip77::MasterBlindingKey),
43    /// Blinding key is given directly
44    Bare(DescriptorPublicKey),
45    /// Blinding key is given directly, as a secret key
46    View(DescriptorSecretKey),
47}
48
49impl fmt::Display for Key {
50    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51        match self {
52            Key::Slip77(data) => write!(f, "slip77({})", data),
53            Key::Bare(pk) => fmt::Display::fmt(pk, f),
54            Key::View(sk) => {
55                if let DescriptorSecretKey::Single(sk) = sk {
56                    crate::descriptor::maybe_fmt_master_id(f, &sk.origin)?;
57                    for byte in &sk.key.inner.secret_bytes() {
58                        write!(f, "{:02x}", byte)?;
59                    }
60                    Ok(())
61                } else {
62                    fmt::Display::fmt(sk, f)
63                }
64            }
65        }
66    }
67}
68
69impl Key {
70    fn to_public_key<C: secp256k1_zkp::Signing + secp256k1_zkp::Verification>(
71        &self,
72        secp: &secp256k1_zkp::Secp256k1<C>,
73        spk: &elements::Script,
74    ) -> Result<secp256k1_zkp::PublicKey, Error> {
75        match *self {
76            Key::Slip77(ref mbk) => Ok(mbk.blinding_key(secp, spk)),
77            Key::Bare(ref pk) => {
78                if pk.is_multipath() {
79                    Err(Error::Unexpected("multipath blinding key".into()))
80                } else if pk.has_wildcard() {
81                    Err(Error::Unexpected("wildcard blinding key".into()))
82                } else {
83                    // Convert into a DefiniteDescriptorKey, note that we are deriving the xpub
84                    // since there is not wildcard.
85                    // Consider adding DescriptorPublicKey::to_definite_descriptor
86                    let pk = pk.clone().at_derivation_index(0).expect("single or xpub without wildcards");
87                    Ok(bare::tweak_key(secp, spk, &pk))
88                }
89            },
90            Key::View(ref sk) => {
91                if sk.is_multipath() {
92                    Err(Error::Unexpected("multipath blinding key".into()))
93                } else {
94                    let pk = sk.to_public(secp).expect("single or xprv");
95                    if pk.has_wildcard() {
96                        Err(Error::Unexpected("wildcard blinding key".into()))
97                    } else {
98                        let pk = pk.at_derivation_index(0).expect("single or xprv without wildcards");
99                        Ok(bare::tweak_key(secp, spk, &pk))
100                    }
101                }
102            },
103        }
104    }
105}
106
107/// A confidential descriptor
108#[derive(Clone, PartialEq, Eq, Debug)]
109pub struct Descriptor<Pk: MiniscriptKey, T: Extension = CovenantExt<CovExtArgs>> {
110    /// The blinding key
111    pub key: Key,
112    /// The script descriptor
113    pub descriptor: crate::Descriptor<Pk, T>,
114}
115
116impl<Pk: MiniscriptKey, T: Extension> Descriptor<Pk, T> {
117    /// Sanity checks for the underlying descriptor.
118    pub fn sanity_check(&self) -> Result<(), Error> {
119        self.descriptor.sanity_check()?;
120        Ok(())
121    }
122}
123
124impl<T: Extension + ParseableExt> Descriptor<DescriptorPublicKey, T> {
125    /// Replaces all wildcards (i.e. `/*`) in the descriptor and the descriptor blinding key
126    /// with a particular derivation index, turning it into a *definite* descriptor.
127    ///
128    /// # Errors
129    /// - If index ≥ 2^31
130    pub fn at_derivation_index(&self, index: u32) -> Result<Descriptor<DefiniteDescriptorKey, T>, ConversionError> {
131        let definite_key = match self.key.clone() {
132            Key::Slip77(k) => Key::Slip77(k),
133            Key::Bare(k) => Key::Bare(k.at_derivation_index(index)?.into_descriptor_public_key()),
134            Key::View(k) => Key::View(match k {
135                // Consider implementing DescriptorSecretKey::at_derivation_index
136                DescriptorSecretKey::Single(_) => k,
137                DescriptorSecretKey::XPrv(xprv) => {
138                    let derivation_path = match xprv.wildcard {
139                        Wildcard::None => xprv.derivation_path,
140                        Wildcard::Unhardened => xprv.derivation_path.into_child(
141                            bip32::ChildNumber::from_normal_idx(index)
142                                .ok()
143                                .ok_or(ConversionError::HardenedChild)?,
144                        ),
145                        Wildcard::Hardened => xprv.derivation_path.into_child(
146                            bip32::ChildNumber::from_hardened_idx(index)
147                                .ok()
148                                .ok_or(ConversionError::HardenedChild)?,
149                        ),
150                    };
151                    DescriptorSecretKey::XPrv(DescriptorXKey {
152                        origin: xprv.origin,
153                        xkey: xprv.xkey,
154                        derivation_path,
155                        wildcard: Wildcard::None,
156                    })
157                },
158                DescriptorSecretKey::MultiXPrv(_) => return Err(ConversionError::MultiKey),
159            }),
160        };
161        let definite_descriptor = self.descriptor.at_derivation_index(index)?;
162        Ok(Descriptor{
163            key: definite_key,
164            descriptor: definite_descriptor,
165        })
166    }
167}
168
169impl<Pk: MiniscriptKey + ToPublicKey, T: Extension + ParseableExt> Descriptor<Pk, T> {
170    /// Obtains the unblinded address for this descriptor.
171    pub fn unconfidential_address(
172        &self,
173        params: &'static elements::AddressParams,
174    ) -> Result<elements::Address, Error> {
175        self.descriptor.address(params)
176    }
177
178    /// Obtains the blinded address for this descriptor.
179    pub fn address<C: secp256k1_zkp::Signing + secp256k1_zkp::Verification>(
180        &self,
181        secp: &secp256k1_zkp::Secp256k1<C>,
182        params: &'static elements::AddressParams,
183    ) -> Result<elements::Address, Error> {
184        let spk = self.descriptor.script_pubkey();
185        self.descriptor
186            .blinded_address(self.key.to_public_key(secp, &spk)?, params)
187    }
188}
189
190impl<Pk: MiniscriptKey, T: Extension> fmt::Display for Descriptor<Pk, T> {
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        use fmt::Write;
193        let mut wrapped_f = checksum::Formatter::new(f);
194        write!(wrapped_f, "ct({},{:#})", self.key, self.descriptor)?;
195        wrapped_f.write_checksum_if_not_alt()
196    }
197}
198
199impl_from_str!(
200    ;T; Extension,
201    Descriptor<Pk, T>,
202    type Err = Error;,
203    fn from_str(s: &str) -> Result<Descriptor<Pk, T>, Error> {
204        let desc_str = verify_checksum(s)?;
205        let top = expression::Tree::from_str(desc_str)?;
206
207        if top.name != "ct" {
208            return Err(Error::BadDescriptor(String::from(
209                "Not a CT Descriptor",
210            )));
211        }
212        if top.args.len() != 2 {
213            return Err(Error::BadDescriptor(
214                format!("CT descriptor had {} arguments rather than 2", top.args.len())
215            ));
216        }
217
218        let keyexpr = &top.args[0];
219        Ok(Descriptor {
220            key: match (keyexpr.name, keyexpr.args.len()) {
221                ("elip151", 0) => {
222                    let d = crate::Descriptor::<DescriptorPublicKey>::from_tree(&top.args[1])?;
223                    Key::from_elip151(&d)?
224                }
225                ("slip77", 1) => Key::Slip77(expression::terminal(&keyexpr.args[0], slip77::MasterBlindingKey::from_str)?),
226                ("slip77", _) => return Err(Error::BadDescriptor(
227                    "slip77() must have exactly one argument".to_owned()
228                )),
229                _ => expression::terminal(keyexpr, |s: &str| DescriptorSecretKey::from_str_inner(s, true)).map(Key::View)
230                .or_else(|_| expression::terminal(keyexpr, DescriptorPublicKey::from_str).map(Key::Bare))?,
231            },
232            descriptor: crate::Descriptor::from_tree(&top.args[1])?,
233        })
234    }
235);
236
237#[cfg(test)]
238mod tests {
239    use std::str::FromStr;
240
241    use elements::Address;
242
243    use super::*;
244    use crate::{DefiniteDescriptorKey, NoExt};
245
246    #[test]
247    fn bare_addr_to_confidential() {
248        let secp = secp256k1_zkp::Secp256k1::new();
249
250        // taken from libwally src/test/test_confidential_addr.py
251        let mut addr = Address::from_str("Q7qcjTLsYGoMA7TjUp97R6E6AM5VKqBik6").unwrap();
252        let key = Key::Bare(
253            DescriptorPublicKey::from_str(
254                "02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623",
255            )
256            .unwrap(),
257        );
258        addr.blinding_pubkey = Some(key.to_public_key(&secp, &addr.script_pubkey()).unwrap());
259        assert_eq!(
260            addr.to_string(),
261            "VTpt7krqRQPJwqe3XQXPg2cVdEKYVFbuprTr7es7pNRMe8mndnq2iYWddxJWYowhLAwoDF8QrZ1v2EXv"
262        );
263    }
264
265    struct ConfidentialTest {
266        key: Key,
267        descriptor: crate::Descriptor<DefiniteDescriptorKey, NoExt>,
268        descriptor_str: String,
269        conf_addr: &'static str,
270        unconf_addr: &'static str,
271    }
272
273    impl ConfidentialTest {
274        fn check<C: secp256k1_zkp::Signing + secp256k1_zkp::Verification>(
275            &self,
276            secp: &secp256k1_zkp::Secp256k1<C>,
277        ) {
278            let desc: Descriptor<DefiniteDescriptorKey, NoExt> = Descriptor {
279                key: self.key.clone(),
280                descriptor: self.descriptor.clone(),
281            };
282            assert_eq!(self.descriptor_str, desc.to_string());
283            assert_eq!(desc, Descriptor::from_str(&desc.to_string()).unwrap());
284            assert_eq!(
285                self.conf_addr,
286                desc.address(secp, &elements::AddressParams::LIQUID)
287                    .unwrap()
288                    .to_string(),
289            );
290            assert_eq!(
291                self.unconf_addr,
292                desc.unconfidential_address(&elements::AddressParams::LIQUID)
293                    .unwrap()
294                    .to_string(),
295            );
296        }
297
298        #[allow(dead_code)]
299        fn output_elip_test_vector(&self, index: usize) {
300            println!(
301                "* Valid Descriptor {}: <code>{}</code>",
302                index, self.descriptor_str
303            );
304            match self.key {
305                Key::Bare(ref pk) => println!("** Blinding public key: <code>{}</code>", pk),
306                Key::View(ref sk) => println!("** Blinding private key: <code>{}</code>", sk),
307                Key::Slip77(mbk) => println!("** SLIP77 master blinding key: <code>{}</code>", mbk),
308            }
309            println!("** Confidential address: <code>{}</code>", self.conf_addr);
310            println!(
311                "** Unconfidential address: <code>{}</code>",
312                self.unconf_addr
313            );
314            println!();
315        }
316    }
317
318    #[test]
319    fn confidential_descriptor() {
320        let secp = secp256k1_zkp::Secp256k1::new();
321
322        // CT key used for bare keys
323        let ct_key = DescriptorPublicKey::from_str(
324            "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
325        )
326        .unwrap();
327        // Auxiliary key to create scriptpubkeys from
328        let spk_key = DefiniteDescriptorKey::from_str(
329            "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
330        )
331        .unwrap();
332
333
334        let single_ct_key = DescriptorPublicKey::from_str(
335            "02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623",
336        )
337        .unwrap();
338        let single_spk_key = DefiniteDescriptorKey::from_str(
339            "03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4",
340        )
341        .unwrap();
342
343        let tests = vec![
344            // Bare key, P2PKH
345            ConfidentialTest {
346                key: Key::Bare(ct_key.clone()),
347                descriptor: crate::Descriptor::new_pkh(spk_key.clone()),
348                descriptor_str: format!("ct({},elpkh({}))#y0lg3d5y", ct_key, spk_key),
349                conf_addr: "VTpvZZYdbhbyVF3Wa99eMjgXhfvu4LS26dR2FwMfNXq7FDX73HZEsZr3VvgH9EDgQnYK7sP6ACKSuMGw",
350                unconf_addr: "Q5WHLVd78iAspUNvzuULvi2F8u693pzAqe",
351            },
352            // Bare key, P2WPKH
353            ConfidentialTest {
354                key: Key::Bare(ct_key.clone()),
355                descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
356                descriptor_str: format!("ct({},elwpkh({}))#kt4e25qt", ct_key, spk_key),
357                conf_addr: "lq1qqg5s7xj7upzl7h4q2k2wj4vq63nvaktn0egqu09nqcr6d44p4evaqknpl78t02k2xqgdh9ltmfmpy9ssk7qfvghdsfr4mvr9c",
358                unconf_addr: "ex1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyktcafre",
359            },
360            // Bare key, P2SH-WPKH
361            ConfidentialTest {
362                key: Key::Bare(ct_key.clone()),
363                descriptor: crate::Descriptor::new_sh_wpkh(spk_key.clone()).unwrap(),
364                descriptor_str: format!("ct({},elsh(wpkh({})))#xg9r4jej", ct_key, spk_key),
365                conf_addr: "VJL8znN4XjXEUKzDaYsqdzRASGLY2KHxC4N6g5b5QvrNjXfeKp83Ci9AW2a8QzbZjpEffoy4PEywpLAZ",
366                unconf_addr: "Gq6kpy2HiNgsyQVpBsuBKAPRFiir23qKro",
367            },
368            // Bare key, P2TR
369            ConfidentialTest {
370                key: Key::Bare(ct_key.clone()),
371                descriptor: crate::Descriptor::new_tr(spk_key.clone(), None).unwrap(),
372                descriptor_str: format!("ct({},eltr({}))#c0pjjxyw", ct_key, spk_key),
373                conf_addr: "lq1pq0nsl8du3gsuk7r90sgm78259mmv6mt9d4yvj30zr3u052ufs5meuc2tuvwx7k7g9kvhhpux07vqpm3qjj8uwdj94650265ustv0xy8zrdxdfgp8g9pl",
374                unconf_addr: "ex1pv997x8r0t0yzmxtms7r8lxqqacsffr78xez6a284d2wg9k8nzr3qxa9kvf",
375            },
376            // SLIP77, P2PKH
377            ConfidentialTest {
378                key: Key::Slip77(slip77::MasterBlindingKey::from_seed(b"abcd")),
379                descriptor: crate::Descriptor::new_pkh(spk_key.clone()),
380                descriptor_str: format!("ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),elpkh({}))#hw2glz99", spk_key),
381                conf_addr: "VTq585ahVjWarEwg2nKQ9yYirmYs5F5j74CeYYA9cq1EZD9obm7hwpx6xqq3J1AY9YRaSavEMzYfr6t7",
382                unconf_addr: "Q5WHLVd78iAspUNvzuULvi2F8u693pzAqe",
383            },
384            // SLIP77, P2WPKH
385            ConfidentialTest {
386                key: Key::Slip77(slip77::MasterBlindingKey::from_seed(b"abcd")),
387                descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
388                descriptor_str: format!("ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),elwpkh({}))#545pl285", spk_key),
389                conf_addr: "lq1qqdx5wnttttzulcs6ujlg9pfts6mp3r4sdwg5ekdej566n5wxzk88vknpl78t02k2xqgdh9ltmfmpy9ssk7qfvr33xa22hpw23",
390                unconf_addr: "ex1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyktcafre",
391            },
392            // SLIP77, P2SH
393            ConfidentialTest {
394                key: Key::Slip77(slip77::MasterBlindingKey::from_seed(b"abcd")),
395                descriptor: crate::Descriptor::new_sh_wpkh(spk_key.clone()).unwrap(),
396                descriptor_str: format!("ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),elsh(wpkh({})))#m30vswxr", spk_key),
397                conf_addr: "VJLFGQ17aGa3WSVEVyxzDktD9SFixJjfSmqVq8xaWmR9X6gFbiF95KFwKA41PBhu3jNTxJFKTUphHL8J",
398                unconf_addr: "Gq6kpy2HiNgsyQVpBsuBKAPRFiir23qKro",
399            },
400            // SLIP77, P2TR
401            ConfidentialTest {
402                key: Key::Slip77(slip77::MasterBlindingKey::from_seed(b"abcd")),
403                descriptor: crate::Descriptor::new_tr(spk_key.clone(), None).unwrap(),
404                descriptor_str: format!("ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),eltr({}))#n3v4t5cs", spk_key),
405                conf_addr: "lq1pq26fndnz8ef6umlz6e2755sm6j5jwxv3tdt2295mr4mx6ux0uf8vcc2tuvwx7k7g9kvhhpux07vqpm3qjj8uwdj94650265ustv0xy8z8wfacw9e5a5t",
406                unconf_addr: "ex1pv997x8r0t0yzmxtms7r8lxqqacsffr78xez6a284d2wg9k8nzr3qxa9kvf",
407            },
408            // Bare, P2WPKH
409            ConfidentialTest {
410                key: Key::Bare(single_ct_key.clone()),
411                descriptor: crate::Descriptor::new_wpkh(single_spk_key).unwrap(),
412                descriptor_str: "ct(02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623,elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#h5e0p6m9".to_string(),
413                conf_addr: "lq1qq0r6pegudzm0tzpszelc34qjln4fdxawgwmgnza63wwpzdy6jrm0grmqvvk2ce5ksnxcs9ecgtnryt7xg3406y5ccl0k2glns",
414                unconf_addr: "ex1qpasxxt9vv6tgfnvgzuuy9e3j9lryg6hawrval4",
415            },
416            // Bare, P2WPKH xpub
417            ConfidentialTest {
418                key: Key::Bare(single_ct_key),
419                descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
420                descriptor_str: format!("ct(02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623,elwpkh({}))#x6sc2de2", spk_key),
421                conf_addr: "lq1qqwkeuelr466ue5u8e0lz3a27q4yk93qnupry5h3q4h9pjpf8vrrzvknpl78t02k2xqgdh9ltmfmpy9ssk7qfvwt93dvuvssha",
422                unconf_addr: "ex1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyktcafre",
423            },
424        ];
425
426        for test in &tests {
427            test.check(&secp);
428        }
429        // Uncomment to regenerate test vectors; to see the output, run
430        // cargo test confidential::tests::confidential_descriptor -- --nocapture
431        /*
432        for (n, test) in tests.iter().enumerate() {
433            test.output_elip_test_vector(n + 1);
434        }
435        */
436    }
437
438    #[test]
439    fn confidential_descriptor_invalid() {
440        let bad_strs = vec![
441            (
442                "ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),elsh(wpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)))#xxxxxxxx",
443                "Invalid descriptor: Invalid checksum 'xxxxxxxx', expected 'qgjmm4as'",
444            ),
445            (
446                "ct(slip77(b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04,b2396b3ee20509cdb64fe24180a14a72dbd671728eaa49bac69d2bdecb5f5a04),elsh(wpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)))#qs64ccxw",
447                "Invalid descriptor: slip77() must have exactly one argument",
448            ),
449            (
450                "ct(slip77,elsh(wpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)))#8p3zmumf",
451                "Invalid descriptor: slip77() must have exactly one argument",
452            ),
453            (
454                "ct(elsh(wpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4)))#u9cwz9f3",
455                "Invalid descriptor: CT descriptor had 1 arguments rather than 2",
456            ),
457            (
458                "ct(02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623,02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623,elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#cnsp2qsc",
459                "Invalid descriptor: CT descriptor had 3 arguments rather than 2",
460            ),
461            (
462                "ct(pk(02dce16018bbbb8e36de7b394df5b5166e9adb7498be7d881a85a09aeecf76b623),elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#nvax6rau",
463                "unexpected «pk»",
464            ),
465            (
466                "ct(L3jXxwef3fpB7hcrFozcWgHeJCPSAFiZ1Ji2YJMPxceaGvy3PC1q,elwpkh(03774eec7a3d550d18e9f89414152025b3b0ad6a342b19481f702d843cff06dfc4))#gcy6hcfz",
467                "unexpected «Key too short (<66 char), doesn't match any format»",
468            ),
469        ];
470
471        /*
472        for (n, bad_str) in bad_strs.iter().enumerate() {
473            println!("* Invalid Descriptor {}", n + 1);
474            println!("** <code>{}</code>", bad_str.0);
475            println!("** Reason:");
476        }
477        */
478
479        for bad_str in bad_strs {
480            let err = Descriptor::<DefiniteDescriptorKey>::from_str(bad_str.0).unwrap_err();
481            assert_eq!(bad_str.1, err.to_string());
482        }
483    }
484
485    #[test]
486    fn view_descriptor() {
487        let secp = secp256k1_zkp::Secp256k1::new();
488
489        let view_key = DescriptorSecretKey::from_str(
490            "xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb",
491        ).unwrap();
492        let ct_key = view_key.to_public(&secp).unwrap();
493        let spk_key = DefiniteDescriptorKey::from_str(
494            "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
495        )
496        .unwrap();
497
498        // View key, P2PKH
499        let test = ConfidentialTest {
500            key: Key::View(view_key.clone()),
501            descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
502            descriptor_str: format!("ct({},elwpkh({}))#j95xktq7", view_key, spk_key),
503            conf_addr: "lq1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvtk83xqzx62q4",
504            unconf_addr: "ex1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyktcafre",
505        };
506        test.check(&secp);
507
508        // View key converted to Bare (note that addresses are the same)
509        let test = ConfidentialTest {
510            key: Key::Bare(ct_key.clone()),
511            descriptor: crate::Descriptor::new_wpkh(spk_key.clone()).unwrap(),
512            descriptor_str: format!("ct({},elwpkh({}))#elmfpmp9", ct_key, spk_key),
513            conf_addr: "lq1qq2r0pdvcknjpwev96qu9975alzqs78cvsut5ju82t7tv8d645dgmwknpl78t02k2xqgdh9ltmfmpy9ssk7qfvtk83xqzx62q4",
514            unconf_addr: "ex1qtfsllr4h4t9rqyxmjl4a5asjzcgt0qyktcafre",
515        };
516        test.check(&secp);
517    }
518
519    #[test]
520    fn view_single_key_descriptor() {
521        let secp = secp256k1_zkp::Secp256k1::new();
522        let view_key = "c25deb86fa11e49d651d7eae27c220ef930fbd86ea023eebfa73e54875647963";
523        let ct_key = "0286fc9a38e765d955e9b0bcc18fa9ae81b0c893e2dd1ef5542a9c73780a086b90";
524        let pk = "021a8fb6bd5a653b021b98a2a785725b8ddacfe3687bc043aa7f4d25d3a48d40b5";
525        let addr_conf = "lq1qq265u4g3k3m3qpyxjwpdrtnm293wuxgvs9xzmzcs2ck0mv5rx23w4d7xfsednsmmxrszfe7s9rs0c6cvf3dfytxax3utlmm46";
526        let addr_unconf = "ex1qklrycvkecdanpcpyulgz3c8udvxyck5jvsv4j5";
527
528        for desc_str in [
529            format!("ct({view_key},elwpkh({pk}))#c2kx9zll"),
530            format!("ct({ct_key},elwpkh({pk}))#m5mvyh29"),
531        ] {
532            let desc = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_str).unwrap();
533            assert_eq!(desc.to_string(), desc_str);
534            assert_eq!(addr_conf, &desc.address(&secp, &elements::AddressParams::LIQUID).unwrap().to_string());
535            assert_eq!(addr_unconf, &desc.unconfidential_address(&elements::AddressParams::LIQUID).unwrap().to_string());
536        }
537    }
538
539    #[test]
540    fn view_xonly_pubkey_descriptor() {
541        // View keys are 64 hex chars, but also x-only public keys are 64 hex chars
542        let view_key = "ab16855a17319477d4283fe5c29cc7d047f81e8ffb199e20d9be1bc31a751c4c";
543        // This view key can also be interpreted as a public key
544        let _public_key = DescriptorPublicKey::from_str(view_key).unwrap();
545        // But since compressed public keys are disallowed, it must be interpreted as a view key
546        let pk = "021a8fb6bd5a653b021b98a2a785725b8ddacfe3687bc043aa7f4d25d3a48d40b5";
547        let desc_str = format!("ct({view_key},elwpkh({pk}))#n9uc7tzt");
548        let desc = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_str).unwrap();
549        assert!(matches!(desc.key, Key::View(_)));
550    }
551
552    #[test]
553    fn descriptor_wildcard() {
554        let secp = secp256k1_zkp::Secp256k1::new();
555        let params = &elements::AddressParams::LIQUID;
556
557        let xprv = "xprv9s21ZrQH143K28NgQ7bHCF61hy9VzwquBZvpzTwXLsbmQLRJ6iV9k2hUBRt5qzmBaSpeMj5LdcsHaXJvM7iFEivPryRcL8irN7Na9p65UUb";
558        let xpub = "xpub661MyMwAqRbcEcT9W98HZP2kFzyzQQZkYnrRnrM8uD8kH8kSeFoQHq1x2iihLgC6PXGy5LrjCL66uSNhJ8pwjfx2rMUTLWuRMns2EG9xnjs";
559        let desc_view_str = format!("ct({}/*,elwpkh({}/*))#wk8ltq6h", xprv, xpub);
560        let desc_bare_str = format!("ct({}/*,elwpkh({}/*))#zzac2dpf", xpub, xpub);
561        let index = 1;
562        let conf_addr = "lq1qqf6690fpw2y00hv5a84zsydjgztg2089d5xnll4k4cstzn63uvgudd907qpvlvvwd5ym9gx7j0v46elf23kfxhmutc58z4k24";
563        let unconf_addr = "ex1qkjhlqqk0kx8x6zdj5r0f8k2avl54gmynyjcw4v";
564
565        let desc_view = Descriptor::<DescriptorPublicKey>::from_str(&desc_view_str).unwrap();
566        let desc_bare = Descriptor::<DescriptorPublicKey>::from_str(&desc_bare_str).unwrap();
567        let definite_desc_view = desc_view.at_derivation_index(index).unwrap();
568        let definite_desc_bare = desc_bare.at_derivation_index(index).unwrap();
569        assert_eq!(definite_desc_view.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
570        assert_eq!(definite_desc_bare.address(&secp, params).unwrap().to_string(), conf_addr.to_string());
571        assert_eq!(definite_desc_view.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
572        assert_eq!(definite_desc_bare.unconfidential_address(params).unwrap().to_string(), unconf_addr.to_string());
573
574        // It's not possible to get an address if the blinding key has a wildcard,
575        // because the descriptor blinding key is not *definite*,
576        // but we can't enforce this with the Descriptor generic.
577        let desc_view_str = format!("ct({}/*,elwpkh({}))#ls6mx2ac", xprv, xpub);
578        let desc_view = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_view_str).unwrap();
579        assert_eq!(desc_view.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
580
581        let desc_bare_str = format!("ct({}/*,elwpkh({}))#czkz0hwn", xpub, xpub);
582        let desc_bare = Descriptor::<DefiniteDescriptorKey>::from_str(&desc_bare_str).unwrap();
583        assert_eq!(desc_bare.address(&secp, params).unwrap_err(), Error::Unexpected("wildcard blinding key".into()));
584    }
585}