From 64483f36b0c16336a1fa737b215c34523b3384e8 Mon Sep 17 00:00:00 2001 From: Leonardo Lima Date: Fri, 31 Oct 2025 12:06:49 -0300 Subject: [PATCH] fix(get_key): for `Xprv` with key origin info - fixes the implementation of `GetKey` for `Xprv` with `KeyRequest::Bip32` and a key_origin information. - adds a new test for a scenario where no wildcard is used, and an specific derivation index is used. --- src/descriptor/key_map.rs | 64 ++++++++++++++++++++++++++++++++------- src/policy/concrete.rs | 1 + 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/descriptor/key_map.rs b/src/descriptor/key_map.rs index 3e193ec87..792217608 100644 --- a/src/descriptor/key_map.rs +++ b/src/descriptor/key_map.rs @@ -136,18 +136,28 @@ impl GetKey for DescriptorSecretKey { return Ok(Some(key)); } - if let Some(matched_path) = descriptor_xkey.matches(key_source, secp) { + if descriptor_xkey.matches(key_source, secp).is_some() { let (_, full_path) = key_source; - let derivation_path = &full_path[matched_path.len()..]; - - return Ok(Some( - descriptor_xkey - .xkey - .derive_priv(secp, &derivation_path) - .map_err(GetKeyError::Bip32)? - .to_priv(), - )); + match &descriptor_xkey.origin { + Some((_, origin_path)) => { + let derivation_path = &full_path[origin_path.len()..]; + return Ok(Some( + descriptor_xkey + .xkey + .derive_priv(secp, &derivation_path)? + .to_priv(), + )); + } + None => { + return Ok(Some( + descriptor_xkey + .xkey + .derive_priv(secp, &full_path)? + .to_priv(), + )) + } + }; } Ok(None) @@ -314,7 +324,7 @@ mod tests { } #[test] - fn get_key_xpriv_with_key_origin() { + fn get_key_xpriv_with_key_origin_and_wildcard() { let secp = Secp256k1::new(); let descriptor_str = "wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/*)"; @@ -345,6 +355,38 @@ mod tests { assert_eq!(pk, expected_pk); } + #[test] + fn get_key_xpriv_with_key_origin_and_no_wildcard() { + let secp = Secp256k1::new(); + + let descriptor_str = "wpkh([d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/0)"; + let (_descriptor_pk, keymap) = Descriptor::parse_descriptor(&secp, descriptor_str).unwrap(); + + let descriptor_sk = DescriptorSecretKey::from_str("[d34db33f/84h/1h/0h]tprv8ZgxMBicQKsPd3EupYiPRhaMooHKUHJxNsTfYuScep13go8QFfHdtkG9nRkFGb7busX4isf6X9dURGCoKgitaApQ6MupRhZMcELAxTBRJgS/0").unwrap(); + let xpriv = match descriptor_sk { + DescriptorSecretKey::XPrv(descriptor_xkey) => descriptor_xkey, + _ => unreachable!(), + }; + + let expected_deriv_path: DerivationPath = (&[ChildNumber::Normal { index: 0 }][..]).into(); + let expected_pk = xpriv + .xkey + .derive_priv(&secp, &expected_deriv_path) + .unwrap() + .to_priv(); + + let derivation_path = DerivationPath::from_str("84'/1'/0'/0").unwrap(); + let (fp, _) = xpriv.origin.unwrap(); + let key_request = KeyRequest::Bip32((fp, derivation_path)); + + let pk = keymap + .get_key(key_request, &secp) + .expect("get_key should not fail") + .expect("get_key should return a `PrivateKey`"); + + assert_eq!(pk, expected_pk); + } + #[test] fn get_key_keymap_no_match() { let secp = Secp256k1::new(); diff --git a/src/policy/concrete.rs b/src/policy/concrete.rs index 59932e652..6c4cdd0a2 100644 --- a/src/policy/concrete.rs +++ b/src/policy/concrete.rs @@ -347,6 +347,7 @@ impl Policy { /// It is **not recommended** to use policy as a stable identifier for a miniscript. You should /// use the policy compiler once, and then use the miniscript output as a stable identifier. See /// the compiler document in [`doc/compiler.md`] for more details. + #[allow(rustdoc::broken_intra_doc_links)] #[cfg(feature = "compiler")] pub fn compile_to_descriptor( &self,