Skip to content

Commit 9f1290e

Browse files
committed
Merge #377: Re-name as_public -> to_public
be5e042 Re-factor DescriptorSecretKey::to_public (Tobin C. Harding) d179959 Re-factor DescriptorXKey to_public method (Tobin C. Harding) 5c0ecd7 Remove unused error return (Tobin C. Harding) ab6e1d8 Re-name as_public -> to_public (Tobin C. Harding) 5c1614a Use 'master' instead of 'root' (Tobin C. Harding) 6c96ddc Refactor DescriptorPublicKey::derive method (Tobin C. Harding) Pull request description: As we have been doing in `rust-bitcoin` attempt to use idiomatic names for our conversion functions. Re-name the key conversion functions `as_public` to be `to_public` because they take a borrowed type and return an owned, non-Copy type. While we are it at do a bunch of refactoring to the individual functions. Each done as a separate patch, can drop any not wanted. ACKs for top commit: apoelstra: ACK be5e042 Tree-SHA512: 3169122f4d783808314ae486c210fc1886d7340ab6089f16ee3615ef38e1babe33abf809572899b0b32a15f77d3eabb4423c32aae8f163114b469da80121b396
2 parents 0c8d12d + be5e042 commit 9f1290e

File tree

2 files changed

+68
-60
lines changed

2 files changed

+68
-60
lines changed

src/descriptor/key.rs

Lines changed: 67 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,14 @@ pub enum Wildcard {
137137
}
138138

139139
impl SinglePriv {
140-
/// Returns the public key of this key
141-
fn as_public<C: Signing>(
142-
&self,
143-
secp: &Secp256k1<C>,
144-
) -> Result<SinglePub, DescriptorKeyParseError> {
140+
/// Returns the public key of this key.
141+
fn to_public<C: Signing>(&self, secp: &Secp256k1<C>) -> SinglePub {
145142
let pub_key = self.key.public_key(secp);
146143

147-
Ok(SinglePub {
144+
SinglePub {
148145
origin: self.origin.clone(),
149146
key: SinglePubKey::FullKey(pub_key),
150-
})
147+
}
151148
}
152149
}
153150

@@ -156,47 +153,50 @@ impl DescriptorXKey<bip32::ExtendedPrivKey> {
156153
/// private key before turning it into a public key.
157154
///
158155
/// If the key already has an origin, the derivation steps applied will be appended to the path
159-
/// already present, otherwise this key will be treated as "root" key and an origin will be
156+
/// already present, otherwise this key will be treated as a master key and an origin will be
160157
/// added with this key's fingerprint and the derivation steps applied.
161-
fn as_public<C: Signing>(
158+
fn to_public<C: Signing>(
162159
&self,
163160
secp: &Secp256k1<C>,
164161
) -> Result<DescriptorXKey<bip32::ExtendedPubKey>, DescriptorKeyParseError> {
165-
let path_len = (&self.derivation_path).as_ref().len();
166-
let public_suffix_len = (&self.derivation_path)
162+
let unhardened = self
163+
.derivation_path
167164
.into_iter()
168165
.rev()
169166
.take_while(|c| c.is_normal())
170167
.count();
168+
let last_hardened_idx = self.derivation_path.len() - unhardened;
171169

172-
let derivation_path = &self.derivation_path[(path_len - public_suffix_len)..];
173-
let deriv_on_hardened = &self.derivation_path[..(path_len - public_suffix_len)];
170+
let hardened_path = &self.derivation_path[..last_hardened_idx];
171+
let unhardened_path = &self.derivation_path[last_hardened_idx..];
174172

175-
let derived_xprv = self
173+
let xprv = self
176174
.xkey
177-
.derive_priv(&secp, &deriv_on_hardened)
175+
.derive_priv(&secp, &hardened_path)
178176
.map_err(|_| DescriptorKeyParseError("Unable to derive the hardened steps"))?;
179-
let xpub = bip32::ExtendedPubKey::from_priv(&secp, &derived_xprv);
177+
let xpub = bip32::ExtendedPubKey::from_priv(&secp, &xprv);
180178

181179
let origin = match &self.origin {
182-
&Some((fingerprint, ref origin_path)) => Some((
183-
fingerprint,
184-
origin_path
185-
.into_iter()
186-
.chain(deriv_on_hardened.into_iter())
180+
Some((fingerprint, path)) => Some((
181+
*fingerprint,
182+
path.into_iter()
183+
.chain(hardened_path.into_iter())
187184
.cloned()
188185
.collect(),
189186
)),
190-
&None if !deriv_on_hardened.as_ref().is_empty() => {
191-
Some((self.xkey.fingerprint(&secp), deriv_on_hardened.into()))
187+
None => {
188+
if hardened_path.is_empty() {
189+
None
190+
} else {
191+
Some((self.xkey.fingerprint(&secp), hardened_path.into()))
192+
}
192193
}
193-
_ => self.origin.clone(),
194194
};
195195

196196
Ok(DescriptorXKey {
197197
origin,
198198
xkey: xpub,
199-
derivation_path: derivation_path.into(),
199+
derivation_path: unhardened_path.into(),
200200
wildcard: self.wildcard,
201201
})
202202
}
@@ -243,24 +243,22 @@ impl fmt::Display for DescriptorPublicKey {
243243

244244
impl DescriptorSecretKey {
245245
/// Return the public version of this key, by applying either
246-
/// [`SinglePriv::as_public`] or [`DescriptorXKey<bip32::ExtendedPrivKey>::as_public`]
246+
/// [`SinglePriv::to_public`] or [`DescriptorXKey<bip32::ExtendedPrivKey>::to_public`]
247247
/// depending on the type of key.
248248
///
249249
/// If the key is an "XPrv", the hardened derivation steps will be applied before converting it
250-
/// to a public key. See the documentation of [`DescriptorXKey<bip32::ExtendedPrivKey>::as_public`]
250+
/// to a public key. See the documentation of [`DescriptorXKey<bip32::ExtendedPrivKey>::to_public`]
251251
/// for more details.
252-
pub fn as_public<C: Signing>(
252+
pub fn to_public<C: Signing>(
253253
&self,
254254
secp: &Secp256k1<C>,
255255
) -> Result<DescriptorPublicKey, DescriptorKeyParseError> {
256-
Ok(match self {
257-
&DescriptorSecretKey::Single(ref sk) => {
258-
DescriptorPublicKey::Single(sk.as_public(secp)?)
259-
}
260-
&DescriptorSecretKey::XPrv(ref xprv) => {
261-
DescriptorPublicKey::XPub(xprv.as_public(secp)?)
262-
}
263-
})
256+
let pk = match self {
257+
DescriptorSecretKey::Single(prv) => DescriptorPublicKey::Single(prv.to_public(secp)),
258+
DescriptorSecretKey::XPrv(xprv) => DescriptorPublicKey::XPub(xprv.to_public(secp)?),
259+
};
260+
261+
Ok(pk)
264262
}
265263
}
266264

@@ -431,28 +429,38 @@ impl DescriptorPublicKey {
431429
}
432430
}
433431

434-
/// If this public key has a wildcard, replace it by the given index
432+
/// Derives the [`DescriptorPublicKey`] at `index` if this key is an xpub and has a wildcard.
433+
///
434+
/// # Returns
435+
///
436+
/// - If this key is not an xpub, returns `self`.
437+
/// - If this key is an xpub but does not have a wildcard, returns `self`.
438+
/// - Otherwise, returns the derived xpub at `index` (removing the wildcard).
435439
///
436-
/// Panics if given an index ≥ 2^31
437-
pub fn derive(mut self, index: u32) -> DescriptorPublicKey {
438-
if let DescriptorPublicKey::XPub(mut xpub) = self {
439-
match xpub.wildcard {
440-
Wildcard::None => {}
441-
Wildcard::Unhardened => {
442-
xpub.derivation_path = xpub
440+
/// # Panics
441+
///
442+
/// If `index` ≥ 2^31
443+
pub fn derive(self, index: u32) -> DescriptorPublicKey {
444+
match self {
445+
DescriptorPublicKey::Single(_) => self,
446+
DescriptorPublicKey::XPub(xpub) => {
447+
let derivation_path = match xpub.wildcard {
448+
Wildcard::None => xpub.derivation_path,
449+
Wildcard::Unhardened => xpub
443450
.derivation_path
444-
.into_child(bip32::ChildNumber::from_normal_idx(index).unwrap())
445-
}
446-
Wildcard::Hardened => {
447-
xpub.derivation_path = xpub
451+
.into_child(bip32::ChildNumber::from_normal_idx(index).unwrap()),
452+
Wildcard::Hardened => xpub
448453
.derivation_path
449-
.into_child(bip32::ChildNumber::from_hardened_idx(index).unwrap())
450-
}
454+
.into_child(bip32::ChildNumber::from_hardened_idx(index).unwrap()),
455+
};
456+
DescriptorPublicKey::XPub(DescriptorXKey {
457+
origin: xpub.origin,
458+
xkey: xpub.xkey,
459+
derivation_path: derivation_path,
460+
wildcard: Wildcard::None,
461+
})
451462
}
452-
xpub.wildcard = Wildcard::None;
453-
self = DescriptorPublicKey::XPub(xpub);
454463
}
455-
self
456464
}
457465

458466
/// Computes the public key corresponding to this descriptor key.
@@ -465,7 +473,7 @@ impl DescriptorPublicKey {
465473
///
466474
/// To ensure there are no wildcards, call `.derive(0)` or similar;
467475
/// to avoid hardened derivation steps, start from a `DescriptorSecretKey`
468-
/// and call `as_public`, or call `TranslatePk2::translate_pk2` with
476+
/// and call `to_public`, or call `TranslatePk2::translate_pk2` with
469477
/// some function which has access to secret key data.
470478
pub fn derive_public_key<C: secp256k1::Verification>(
471479
&self,
@@ -836,32 +844,32 @@ mod test {
836844
let secp = secp256k1::Secp256k1::signing_only();
837845

838846
let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2").unwrap();
839-
let public_key = secret_key.as_public(&secp).unwrap();
847+
let public_key = secret_key.to_public(&secp).unwrap();
840848
assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2");
841849
assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
842850
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'/2");
843851
assert_eq!(public_key.is_deriveable(), false);
844852

845853
let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2'").unwrap();
846-
let public_key = secret_key.as_public(&secp).unwrap();
854+
let public_key = secret_key.to_public(&secp).unwrap();
847855
assert_eq!(public_key.to_string(), "[2cbe2a6d/0'/1'/2']tpubDDPuH46rv4dbFtmF6FrEtJEy1CvLZonyBoVxF6xsesHdYDdTBrq2mHhm8AbsPh39sUwL2nZyxd6vo4uWNTU9v4t893CwxjqPnwMoUACLvMV");
848856
assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
849857
assert_eq!(public_key.full_derivation_path().to_string(), "m/0'/1'/2'");
850858

851859
let secret_key = DescriptorSecretKey::from_str("tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap();
852-
let public_key = secret_key.as_public(&secp).unwrap();
860+
let public_key = secret_key.to_public(&secp).unwrap();
853861
assert_eq!(public_key.to_string(), "tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2");
854862
assert_eq!(public_key.master_fingerprint().to_string(), "2cbe2a6d");
855863
assert_eq!(public_key.full_derivation_path().to_string(), "m/0/1/2");
856864

857865
let secret_key = DescriptorSecretKey::from_str("[aabbccdd]tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0/1/2").unwrap();
858-
let public_key = secret_key.as_public(&secp).unwrap();
866+
let public_key = secret_key.to_public(&secp).unwrap();
859867
assert_eq!(public_key.to_string(), "[aabbccdd]tpubD6NzVbkrYhZ4WQdzxL7NmJN7b85ePo4p6RSj9QQHF7te2RR9iUeVSGgnGkoUsB9LBRosgvNbjRv9bcsJgzgBd7QKuxDm23ZewkTRzNSLEDr/0/1/2");
860868
assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd");
861869
assert_eq!(public_key.full_derivation_path().to_string(), "m/0/1/2");
862870

863871
let secret_key = DescriptorSecretKey::from_str("[aabbccdd/90']tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/0'/1'/2").unwrap();
864-
let public_key = secret_key.as_public(&secp).unwrap();
872+
let public_key = secret_key.to_public(&secp).unwrap();
865873
assert_eq!(public_key.to_string(), "[aabbccdd/90'/0'/1']tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi/2");
866874
assert_eq!(public_key.master_fingerprint().to_string(), "aabbccdd");
867875
assert_eq!(

src/descriptor/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ impl Descriptor<DescriptorPublicKey> {
712712
key_map: &mut KeyMap|
713713
-> Result<DescriptorPublicKey, DescriptorKeyParseError> {
714714
let (public_key, secret_key) = match DescriptorSecretKey::from_str(s) {
715-
Ok(sk) => (sk.as_public(&secp)?, Some(sk)),
715+
Ok(sk) => (sk.to_public(&secp)?, Some(sk)),
716716
Err(_) => (DescriptorPublicKey::from_str(s)?, None),
717717
};
718718

0 commit comments

Comments
 (0)