|
14 | 14 | //! This module contains generic utilities to work with descriptors, plus some re-exported types
|
15 | 15 | //! from [`miniscript`].
|
16 | 16 |
|
17 |
| -use std::collections::{BTreeMap, HashMap, HashSet}; |
| 17 | +use std::collections::{BTreeMap, HashSet}; |
18 | 18 | use std::ops::Deref;
|
19 | 19 |
|
20 |
| -use bitcoin::secp256k1; |
| 20 | +use bitcoin::{secp256k1, XOnlyPublicKey, PublicKey}; |
21 | 21 | use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
|
22 | 22 | use bitcoin::util::{psbt, taproot};
|
23 | 23 | use bitcoin::{Network, Script, TxOut};
|
24 | 24 |
|
25 |
| -use miniscript::descriptor::{DescriptorType, InnerXKey}; |
| 25 | +use miniscript::descriptor::{DescriptorType, InnerXKey, SinglePubKey}; |
26 | 26 | pub use miniscript::{
|
27 | 27 | descriptor::DescriptorXKey, descriptor::KeyMap, descriptor::Wildcard, Descriptor,
|
28 | 28 | DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
|
@@ -327,6 +327,7 @@ pub(crate) trait DescriptorMeta {
|
327 | 327 | tap_key_origins: &TapKeyOrigins,
|
328 | 328 | secp: &'s SecpCtx,
|
329 | 329 | ) -> Option<DerivedDescriptor<'s>>;
|
| 330 | + fn derive_from_psbt_key_origins<'s>(&self, key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>, secp: &'s SecpCtx) -> Option<DerivedDescriptor<'s>>; |
330 | 331 | fn derive_from_psbt_input<'s>(
|
331 | 332 | &self,
|
332 | 333 | psbt_input: &psbt::Input,
|
@@ -395,78 +396,101 @@ impl DescriptorMeta for ExtendedDescriptor {
|
395 | 396 | Ok(answer)
|
396 | 397 | }
|
397 | 398 |
|
398 |
| - fn derive_from_hd_keypaths<'s>( |
399 |
| - &self, |
400 |
| - hd_keypaths: &HdKeyPaths, |
401 |
| - secp: &'s SecpCtx, |
402 |
| - ) -> Option<DerivedDescriptor<'s>> { |
403 |
| - let index: HashMap<_, _> = hd_keypaths.values().map(|(a, b)| (a, b)).collect(); |
| 399 | + fn derive_from_psbt_key_origins<'s>(&self, key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>, secp: &'s SecpCtx) -> Option<DerivedDescriptor<'s>> { |
| 400 | + // Ensure that deriving `xpub` with `path` yields `expected` |
| 401 | + let verify_key = |xpub: &DescriptorXKey<ExtendedPubKey>, path: &DerivationPath, expected: &SinglePubKey| { |
| 402 | + let derived = xpub.xkey.derive_pub(secp, path).expect("The path should never contain hardened derivation steps").public_key; |
404 | 403 |
|
405 |
| - let mut path_found = None; |
406 |
| - self.for_each_key(|key| { |
407 |
| - if path_found.is_some() { |
408 |
| - // already found a matching path, we are done |
409 |
| - return true; |
| 404 | + match expected { |
| 405 | + SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true, |
| 406 | + SinglePubKey::XOnly(pk) if &XOnlyPublicKey::from(derived) == pk => true, |
| 407 | + _ => false, |
410 | 408 | }
|
| 409 | + }; |
411 | 410 |
|
| 411 | + let mut path_found = None; |
| 412 | + |
| 413 | + // using `for_any_key` should make this stop as soon as we return `true` |
| 414 | + self.for_any_key(|key| { |
412 | 415 | if let DescriptorPublicKey::XPub(xpub) = key.as_key().deref() {
|
413 |
| - // Check if the key matches one entry in our `index`. If it does, `matches()` will |
| 416 | + // Check if the key matches one entry in our `key_origins`. If it does, `matches()` will |
414 | 417 | // return the "prefix" that matched, so we remove that prefix from the full path
|
415 |
| - // found in `index` and save it in `derive_path`. We expect this to be a derivation |
| 418 | + // found in `key_origins` and save it in `derive_path`. We expect this to be a derivation |
416 | 419 | // path of length 1 if the key is `wildcard` and an empty path otherwise.
|
417 | 420 | let root_fingerprint = xpub.root_fingerprint(secp);
|
418 |
| - let derivation_path: Option<Vec<ChildNumber>> = index |
| 421 | + let derive_path = key_origins |
419 | 422 | .get_key_value(&root_fingerprint)
|
420 |
| - .and_then(|(fingerprint, path)| { |
421 |
| - xpub.matches(&(**fingerprint, (*path).clone()), secp) |
| 423 | + .and_then(|(fingerprint, (path, expected))| { |
| 424 | + xpub.matches(&(*fingerprint, (*path).clone()), secp).zip(Some((path, expected))) |
422 | 425 | })
|
423 |
| - .map(|prefix| { |
424 |
| - index |
425 |
| - .get(&xpub.root_fingerprint(secp)) |
426 |
| - .unwrap() |
427 |
| - .into_iter() |
| 426 | + .and_then(|(prefix, (full_path, expected))| { |
| 427 | + let derive_path = full_path.into_iter() |
428 | 428 | .skip(prefix.into_iter().count())
|
429 | 429 | .cloned()
|
430 |
| - .collect() |
| 430 | + .collect::<DerivationPath>(); |
| 431 | + |
| 432 | + // `derive_path` only contains the replacement index for the wildcard, if present, or |
| 433 | + // an empty path for fixed descriptors. To verify the key we also need the normal steps |
| 434 | + // that come before the wildcard, so we take them directly from `xpub` and then append |
| 435 | + // the final index |
| 436 | + if verify_key(xpub, &xpub.derivation_path.extend(derive_path.clone()), expected) { |
| 437 | + Some(derive_path) |
| 438 | + } else { |
| 439 | + log::debug!("Key `{}` derived with {} yields an unexpected key", root_fingerprint, derive_path); |
| 440 | + None |
| 441 | + } |
431 | 442 | });
|
432 | 443 |
|
433 |
| - match derivation_path { |
| 444 | + match derive_path { |
434 | 445 | Some(path) if xpub.wildcard != Wildcard::None && path.len() == 1 => {
|
435 | 446 | // Ignore hardened wildcards
|
436 | 447 | if let ChildNumber::Normal { index } = path[0] {
|
437 |
| - path_found = Some(index) |
| 448 | + path_found = Some(index); |
| 449 | + return true; |
438 | 450 | }
|
439 | 451 | }
|
440 | 452 | Some(path) if xpub.wildcard == Wildcard::None && path.is_empty() => {
|
441 |
| - path_found = Some(0) |
| 453 | + path_found = Some(0); |
| 454 | + return true; |
442 | 455 | }
|
443 | 456 | _ => {}
|
444 | 457 | }
|
445 | 458 | }
|
446 | 459 |
|
447 |
| - true |
| 460 | + false |
448 | 461 | });
|
449 | 462 |
|
450 | 463 | path_found.map(|path| self.as_derived(path, secp))
|
451 | 464 | }
|
452 | 465 |
|
| 466 | + fn derive_from_hd_keypaths<'s>( |
| 467 | + &self, |
| 468 | + hd_keypaths: &HdKeyPaths, |
| 469 | + secp: &'s SecpCtx, |
| 470 | + ) -> Option<DerivedDescriptor<'s>> { |
| 471 | + // "Convert" an hd_keypaths map to the format required by `derive_from_psbt_key_origins` |
| 472 | + let key_origins = hd_keypaths |
| 473 | + .iter() |
| 474 | + .map(|(pk, (fingerprint, path))| { |
| 475 | + (*fingerprint, (path, SinglePubKey::FullKey(PublicKey::new(*pk)))) |
| 476 | + }) |
| 477 | + .collect(); |
| 478 | + self.derive_from_psbt_key_origins(key_origins, secp) |
| 479 | + } |
| 480 | + |
453 | 481 | fn derive_from_tap_key_origins<'s>(
|
454 | 482 | &self,
|
455 | 483 | tap_key_origins: &TapKeyOrigins,
|
456 | 484 | secp: &'s SecpCtx,
|
457 | 485 | ) -> Option<DerivedDescriptor<'s>> {
|
458 |
| - // "Convert" a tap_key_origins map to an hd_keypaths map, by dropping the leaf hash vector and |
459 |
| - // converting the public key type |
460 |
| - let hd_keypaths = tap_key_origins |
| 486 | + // "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins` |
| 487 | + let key_origins = tap_key_origins |
461 | 488 | .iter()
|
462 |
| - .map(|(pk, (_, keysource))| { |
463 |
| - let mut data: Vec<u8> = vec![0x02]; |
464 |
| - data.extend(pk.serialize().iter()); |
465 |
| - let pk = secp256k1::PublicKey::from_slice(&data).expect("Invalid XOnlyPublicKey"); |
466 |
| - (pk, keysource.clone()) |
| 489 | + .map(|(pk, (_, (fingerprint, path)))| { |
| 490 | + (*fingerprint, (path, SinglePubKey::XOnly(*pk))) |
467 | 491 | })
|
468 | 492 | .collect();
|
469 |
| - self.derive_from_hd_keypaths(&hd_keypaths, secp) |
| 493 | + self.derive_from_psbt_key_origins(key_origins, secp) |
470 | 494 | }
|
471 | 495 |
|
472 | 496 | fn derive_from_psbt_input<'s>(
|
|
0 commit comments