Skip to content

Commit e122255

Browse files
committed
desc keys: make 'at_derivation_index' return an error instead of panicing
This is prep work for the multipath keys, for which another ConversionError variant can occur. It's also cleaner on its own as returning a ConversionError is what we do in other places where we might fail on a conversion.
1 parent ccd7ef1 commit e122255

File tree

3 files changed

+35
-24
lines changed

3 files changed

+35
-24
lines changed

src/descriptor/key.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ impl DescriptorPublicKey {
444444

445445
#[deprecated(note = "use at_derivation_index instead")]
446446
/// Deprecated name of [`at_derivation_index`].
447-
pub fn derive(self, index: u32) -> DefiniteDescriptorKey {
447+
pub fn derive(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
448448
self.at_derivation_index(index)
449449
}
450450

@@ -457,20 +457,24 @@ impl DescriptorPublicKey {
457457
/// - If this key is an xpub but does not have a wildcard, returns `self`.
458458
/// - Otherwise, returns the xpub at derivation `index` (removing the wildcard).
459459
///
460-
/// # Panics
460+
/// # Errors
461461
///
462-
/// If `index` ≥ 2^31
463-
pub fn at_derivation_index(self, index: u32) -> DefiniteDescriptorKey {
462+
/// - If `index` is hardened.
463+
pub fn at_derivation_index(self, index: u32) -> Result<DefiniteDescriptorKey, ConversionError> {
464464
let definite = match self {
465465
DescriptorPublicKey::Single(_) => self,
466466
DescriptorPublicKey::XPub(xpub) => {
467467
let derivation_path = match xpub.wildcard {
468468
Wildcard::None => xpub.derivation_path,
469469
Wildcard::Unhardened => xpub.derivation_path.into_child(
470-
bip32::ChildNumber::from_normal_idx(index).expect("index must < 2^31"),
470+
bip32::ChildNumber::from_normal_idx(index)
471+
.ok()
472+
.ok_or(ConversionError::HardenedChild)?,
471473
),
472474
Wildcard::Hardened => xpub.derivation_path.into_child(
473-
bip32::ChildNumber::from_hardened_idx(index).expect("index must < 2^31"),
475+
bip32::ChildNumber::from_hardened_idx(index)
476+
.ok()
477+
.ok_or(ConversionError::HardenedChild)?,
474478
),
475479
};
476480
DescriptorPublicKey::XPub(DescriptorXKey {
@@ -482,8 +486,8 @@ impl DescriptorPublicKey {
482486
}
483487
};
484488

485-
DefiniteDescriptorKey::new(definite)
486-
.expect("The key should not contain any wildcards at this point")
489+
Ok(DefiniteDescriptorKey::new(definite)
490+
.expect("The key should not contain any wildcards at this point"))
487491
}
488492
}
489493

src/descriptor/mod.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -525,26 +525,30 @@ impl Descriptor<DescriptorPublicKey> {
525525
/// Replaces all wildcards (i.e. `/*`) in the descriptor with a particular derivation index,
526526
/// turning it into a *definite* descriptor.
527527
///
528-
/// # Panics
529-
///
530-
/// If index ≥ 2^31
531-
pub fn at_derivation_index(&self, index: u32) -> Descriptor<DefiniteDescriptorKey> {
528+
/// # Errors
529+
/// - If index ≥ 2^31
530+
pub fn at_derivation_index(
531+
&self,
532+
index: u32,
533+
) -> Result<Descriptor<DefiniteDescriptorKey>, ConversionError> {
532534
struct Derivator(u32);
533535

534-
impl Translator<DescriptorPublicKey, DefiniteDescriptorKey, ()> for Derivator {
535-
fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<DefiniteDescriptorKey, ()> {
536-
Ok(pk.clone().at_derivation_index(self.0))
536+
impl Translator<DescriptorPublicKey, DefiniteDescriptorKey, ConversionError> for Derivator {
537+
fn pk(
538+
&mut self,
539+
pk: &DescriptorPublicKey,
540+
) -> Result<DefiniteDescriptorKey, ConversionError> {
541+
pk.clone().at_derivation_index(self.0)
537542
}
538543

539-
translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, ());
544+
translate_hash_clone!(DescriptorPublicKey, DescriptorPublicKey, ConversionError);
540545
}
541546
self.translate_pk(&mut Derivator(index))
542-
.expect("BIP 32 key index substitution cannot fail")
543547
}
544548

545549
#[deprecated(note = "use at_derivation_index instead")]
546550
/// Deprecated name for [`at_derivation_index`].
547-
pub fn derive(&self, index: u32) -> Descriptor<DefiniteDescriptorKey> {
551+
pub fn derive(&self, index: u32) -> Result<Descriptor<DefiniteDescriptorKey>, ConversionError> {
548552
self.at_derivation_index(index)
549553
}
550554

@@ -561,8 +565,8 @@ impl Descriptor<DescriptorPublicKey> {
561565
/// .expect("Valid ranged descriptor");
562566
/// # let index = 42;
563567
/// # let secp = Secp256k1::verification_only();
564-
/// let derived_descriptor = descriptor.at_derivation_index(index).derived_descriptor(&secp);
565-
/// # assert_eq!(descriptor.derived_descriptor(&secp, index), derived_descriptor);
568+
/// let derived_descriptor = descriptor.at_derivation_index(index).unwrap().derived_descriptor(&secp).unwrap();
569+
/// # assert_eq!(descriptor.derived_descriptor(&secp, index).unwrap(), derived_descriptor);
566570
/// ```
567571
///
568572
/// and is only here really here for backwards compatbility.
@@ -579,7 +583,7 @@ impl Descriptor<DescriptorPublicKey> {
579583
secp: &secp256k1::Secp256k1<C>,
580584
index: u32,
581585
) -> Result<Descriptor<bitcoin::PublicKey>, ConversionError> {
582-
self.at_derivation_index(index).derived_descriptor(&secp)
586+
self.at_derivation_index(index)?.derived_descriptor(&secp)
583587
}
584588

585589
/// Parse a descriptor that may contain secret keys
@@ -741,7 +745,7 @@ impl Descriptor<DefiniteDescriptorKey> {
741745
/// let secp = secp256k1::Secp256k1::verification_only();
742746
/// let descriptor = Descriptor::<DescriptorPublicKey>::from_str("tr(xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ/0/*)")
743747
/// .expect("Valid ranged descriptor");
744-
/// let result = descriptor.at_derivation_index(0).derived_descriptor(&secp).expect("Non-hardened derivation");
748+
/// let result = descriptor.at_derivation_index(0).unwrap().derived_descriptor(&secp).expect("Non-hardened derivation");
745749
/// assert_eq!(result.to_string(), "tr(03cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115)#6qm9h8ym");
746750
/// ```
747751
///
@@ -1594,12 +1598,14 @@ mod tests {
15941598
// Same address
15951599
let addr_one = desc_one
15961600
.at_derivation_index(index)
1601+
.unwrap()
15971602
.derived_descriptor(&secp_ctx)
15981603
.unwrap()
15991604
.address(bitcoin::Network::Bitcoin)
16001605
.unwrap();
16011606
let addr_two = desc_two
16021607
.at_derivation_index(index)
1608+
.unwrap()
16031609
.derived_descriptor(&secp_ctx)
16041610
.unwrap()
16051611
.address(bitcoin::Network::Bitcoin)
@@ -1677,7 +1683,7 @@ pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHW
16771683
pk(03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8))";
16781684
let policy: policy::concrete::Policy<DescriptorPublicKey> = descriptor_str.parse().unwrap();
16791685
let descriptor = Descriptor::new_sh(policy.compile().unwrap()).unwrap();
1680-
let definite_descriptor = descriptor.at_derivation_index(42);
1686+
let definite_descriptor = descriptor.at_derivation_index(42).unwrap();
16811687

16821688
let res_descriptor_str = "thresh(2,\
16831689
pk([d34db33f/44'/0'/0']xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL/1/42),\

tests/test_desc.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ pub fn test_desc_satisfy(
8585

8686
let definite_desc = test_util::parse_test_desc(&descriptor, &testdata.pubdata)
8787
.map_err(|_| DescError::DescParseError)?
88-
.at_derivation_index(0);
88+
.at_derivation_index(0)
89+
.unwrap();
8990

9091
let derived_desc = definite_desc.derived_descriptor(&secp).unwrap();
9192
let desc_address = derived_desc.address(bitcoin::Network::Regtest);

0 commit comments

Comments
 (0)