1pub 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#[derive(Clone, PartialEq, Eq, Debug)]
40pub enum Key {
41 Slip77(slip77::MasterBlindingKey),
43 Bare(DescriptorPublicKey),
45 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 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#[derive(Clone, PartialEq, Eq, Debug)]
109pub struct Descriptor<Pk: MiniscriptKey, T: Extension = CovenantExt<CovExtArgs>> {
110 pub key: Key,
112 pub descriptor: crate::Descriptor<Pk, T>,
114}
115
116impl<Pk: MiniscriptKey, T: Extension> Descriptor<Pk, T> {
117 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 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 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 pub fn unconfidential_address(
172 &self,
173 params: &'static elements::AddressParams,
174 ) -> Result<elements::Address, Error> {
175 self.descriptor.address(params)
176 }
177
178 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 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 let ct_key = DescriptorPublicKey::from_str(
324 "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
325 )
326 .unwrap();
327 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 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 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 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 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 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 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 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 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 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 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 }
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 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 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 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 let view_key = "ab16855a17319477d4283fe5c29cc7d047f81e8ffb199e20d9be1bc31a751c4c";
543 let _public_key = DescriptorPublicKey::from_str(view_key).unwrap();
545 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 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}