Skip to content

Commit d84745f

Browse files
committed
Merge rust-bitcoin#4623: Improve Xpriv::derive_xpriv and Xpub::derive_xpub ergonomics slightly
3527122 psbt: Use new `derive_xpriv` flexibility in GetKey (Daniel Roberts) bd3f4b6 psbt: Add test for GetKey bip32 (Daniel Roberts) c7bdec1 Fix clippy lint and formatting for `Xpriv::derive_xpriv` and `Xpriv::derive_xpub` calls (Daniel Roberts) 9d4381c Improve `Xpriv::derive_xpriv` and `Xpub::derive_xpub` ergonomics (Daniel Roberts) Pull request description: This enables a couple more things to be passed to the bip32 derive methods, with (afaict) no downside (all existing call sites remain valid and work as before). Given ``` let secp = Secp256k1::new(); let path: DerivationPath = "42'/350'/0".parse().unwrap(); let xpriv: Xpriv = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi".parse().unwrap(); ``` The following *new* ways to call derive are enabled: ``` /// Derive using only part of the path xpriv.derive_xpriv(&secp, &path[1..]) ``` ``` /// Derive moving into the method xpriv.derive_xpriv(&secp, path) ``` The second case is probably of questionable usefulness, but I've ended up writing something like ``` let path: DerivationPath = path.into_iter().skip(n).cloned().collect() ``` enough that the first case would be welcome, and can be done with a minimal change. I'm reasonably confident this doesn't break anything (and it indeed doesn't break any tests that I can see) but this is definitely on the edge of my comfort with the rust type system, which is why I've created this as a draft. ACKs for top commit: tcharding: ACK 3527122 apoelstra: ACK 3527122; successfully ran local tests; nice! Tree-SHA512: 0babdd9589d3312d4df54d77202a71dbb021e216167514c1a87f1f992f254f2b298b874411f8c7a3d56d7f730b22d104c3a78f9145715ea8df6bedad35c1ffd5
2 parents 732a83c + 3527122 commit d84745f

File tree

7 files changed

+40
-23
lines changed

7 files changed

+40
-23
lines changed

bitcoin/examples/bip32.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn main() {
4646
// generate first receiving address at m/0/0
4747
// manually creating indexes this time
4848
let zero = ChildNumber::ZERO_NORMAL;
49-
let public_key = xpub.derive_xpub(&secp, &[zero, zero]).unwrap().public_key;
49+
let public_key = xpub.derive_xpub(&secp, [zero, zero]).unwrap().public_key;
5050
let address = Address::p2wpkh(CompressedPublicKey(public_key), KnownHrp::Mainnet);
5151
println!("First receiving address: {address}");
5252
}

bitcoin/examples/ecdsa-psbt-simple.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ fn get_external_address_xpriv<C: Signing>(
6767
let external_index = ChildNumber::ZERO_NORMAL;
6868
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
6969

70-
child_xpriv.derive_xpriv(secp, &[external_index, idx]).expect("only deriving two more steps")
70+
child_xpriv.derive_xpriv(secp, [external_index, idx]).expect("only deriving two more steps")
7171
}
7272

7373
// Derive the internal address xpriv.
@@ -83,7 +83,7 @@ fn get_internal_address_xpriv<C: Signing>(
8383
let internal_index = ChildNumber::ONE_NORMAL;
8484
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
8585

86-
child_xpriv.derive_xpriv(secp, &[internal_index, idx]).expect("only deriving two more steps")
86+
child_xpriv.derive_xpriv(secp, [internal_index, idx]).expect("only deriving two more steps")
8787
}
8888

8989
// The address to send to.

bitcoin/examples/ecdsa-psbt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ impl WatchOnly {
260260
secp: &Secp256k1<C>,
261261
) -> Result<(CompressedPublicKey, Address, DerivationPath)> {
262262
let path = [ChildNumber::ONE_NORMAL, ChildNumber::ZERO_NORMAL];
263-
let derived = self.account_0_xpub.derive_xpub(secp, &path)?;
263+
let derived = self.account_0_xpub.derive_xpub(secp, path)?;
264264

265265
let pk = derived.to_public_key();
266266
let addr = Address::p2wpkh(pk, NETWORK);

bitcoin/examples/taproot-psbt-simple.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ fn get_external_address_xpriv<C: Signing>(
6565
let external_index = ChildNumber::ZERO_NORMAL;
6666
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
6767

68-
child_xpriv.derive_xpriv(secp, &[external_index, idx]).expect("only deriving two more steps")
68+
child_xpriv.derive_xpriv(secp, [external_index, idx]).expect("only deriving two more steps")
6969
}
7070

7171
// Derive the internal address xpriv.
@@ -81,7 +81,7 @@ fn get_internal_address_xpriv<C: Signing>(
8181
let internal_index = ChildNumber::ONE_NORMAL;
8282
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
8383

84-
child_xpriv.derive_xpriv(secp, &[internal_index, idx]).expect("only deriving two more steps")
84+
child_xpriv.derive_xpriv(secp, [internal_index, idx]).expect("only deriving two more steps")
8585
}
8686

8787
// Get the Taproot Key Origin.

bitcoin/examples/taproot-psbt.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ fn generate_bip86_key_spend_tx(
298298
.ok_or("missing Taproot key origin")?;
299299

300300
let secret_key =
301-
master_xpriv.derive_xpriv(secp, &derivation_path)?.to_private_key().inner;
301+
master_xpriv.derive_xpriv(secp, derivation_path)?.to_private_key().inner;
302302
sign_psbt_taproot(
303303
secret_key,
304304
input.tap_internal_key.unwrap(),
@@ -540,7 +540,7 @@ impl BenefactorWallet {
540540
.ok_or("missing Taproot key origin")?;
541541
let secret_key = self
542542
.master_xpriv
543-
.derive_xpriv(&self.secp, &derivation_path)
543+
.derive_xpriv(&self.secp, derivation_path)
544544
.expect("derivation path is short")
545545
.to_private_key()
546546
.inner;
@@ -664,11 +664,8 @@ impl BeneficiaryWallet {
664664
for (x_only_pubkey, (leaf_hashes, (_, derivation_path))) in
665665
&psbt.inputs[0].tap_key_origins.clone()
666666
{
667-
let secret_key = self
668-
.master_xpriv
669-
.derive_xpriv(&self.secp, &derivation_path)?
670-
.to_private_key()
671-
.inner;
667+
let secret_key =
668+
self.master_xpriv.derive_xpriv(&self.secp, derivation_path)?.to_private_key().inner;
672669
for lh in leaf_hashes {
673670
let sighash_type = TapSighashType::All;
674671
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(

bitcoin/src/bip32.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ impl Xpriv {
756756
pub fn derive_priv<C: secp256k1::Signing, P: AsRef<[ChildNumber]>>(
757757
&self,
758758
secp: &Secp256k1<C>,
759-
path: &P,
759+
path: P,
760760
) -> Result<Xpriv, DerivationError> {
761761
self.derive_xpriv(secp, path)
762762
}
@@ -767,7 +767,7 @@ impl Xpriv {
767767
pub fn derive_xpriv<C: secp256k1::Signing, P: AsRef<[ChildNumber]>>(
768768
&self,
769769
secp: &Secp256k1<C>,
770-
path: &P,
770+
path: P,
771771
) -> Result<Xpriv, DerivationError> {
772772
let mut sk: Xpriv = *self;
773773
for cnum in path.as_ref() {
@@ -910,7 +910,7 @@ impl Xpub {
910910
pub fn derive_pub<C: secp256k1::Verification, P: AsRef<[ChildNumber]>>(
911911
&self,
912912
secp: &Secp256k1<C>,
913-
path: &P,
913+
path: P,
914914
) -> Result<Xpub, DerivationError> {
915915
self.derive_xpub(secp, path)
916916
}
@@ -921,7 +921,7 @@ impl Xpub {
921921
pub fn derive_xpub<C: secp256k1::Verification, P: AsRef<[ChildNumber]>>(
922922
&self,
923923
secp: &Secp256k1<C>,
924-
path: &P,
924+
path: P,
925925
) -> Result<Xpub, DerivationError> {
926926
let mut pk: Xpub = *self;
927927
for cnum in path.as_ref() {

bitcoin/src/psbt/mod.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::collections::{HashMap, HashSet};
2121
use internals::write_err;
2222
use secp256k1::{Keypair, Message, Secp256k1, Signing, Verification};
2323

24-
use crate::bip32::{self, DerivationPath, KeySource, Xpriv, Xpub};
24+
use crate::bip32::{self, KeySource, Xpriv, Xpub};
2525
use crate::crypto::key::{PrivateKey, PublicKey};
2626
use crate::crypto::{ecdsa, taproot};
2727
use crate::key::{TapTweak, XOnlyPublicKey};
@@ -814,14 +814,13 @@ impl GetKey for Xpriv {
814814
KeyRequest::XOnlyPubkey(_) => Err(GetKeyError::NotSupported),
815815
KeyRequest::Bip32((fingerprint, path)) => {
816816
let key = if self.fingerprint(secp) == *fingerprint {
817-
let k = self.derive_xpriv(secp, &path).map_err(GetKeyError::Bip32)?;
817+
let k = self.derive_xpriv(secp, path).map_err(GetKeyError::Bip32)?;
818818
Some(k.to_private_key())
819819
} else if self.parent_fingerprint == *fingerprint
820820
&& !path.is_empty()
821821
&& path[0] == self.child_number
822822
{
823-
let path = DerivationPath::from_iter(path.into_iter().skip(1).copied());
824-
let k = self.derive_xpriv(secp, &path).map_err(GetKeyError::Bip32)?;
823+
let k = self.derive_xpriv(secp, &path[1..]).map_err(GetKeyError::Bip32)?;
825824
Some(k.to_private_key())
826825
} else {
827826
None
@@ -1330,7 +1329,7 @@ mod tests {
13301329
#[cfg(feature = "rand-std")]
13311330
use {
13321331
crate::address::script_pubkey::ScriptBufExt as _,
1333-
crate::bip32::{DerivationPath, Fingerprint},
1332+
crate::bip32::Fingerprint,
13341333
crate::locktime,
13351334
crate::witness_version::WitnessVersion,
13361335
crate::WitnessProgram,
@@ -1339,7 +1338,7 @@ mod tests {
13391338

13401339
use super::*;
13411340
use crate::address::script_pubkey::ScriptExt as _;
1342-
use crate::bip32::ChildNumber;
1341+
use crate::bip32::{ChildNumber, DerivationPath};
13431342
use crate::locktime::absolute;
13441343
use crate::network::NetworkKind;
13451344
use crate::psbt::serialize::{Deserialize, Serialize};
@@ -2397,6 +2396,27 @@ mod tests {
23972396
);
23982397
}
23992398

2399+
#[test]
2400+
fn get_key_xpriv_bip32_parent() {
2401+
let secp = Secp256k1::new();
2402+
2403+
let seed = hex!("000102030405060708090a0b0c0d0e0f");
2404+
let parent_xpriv: Xpriv = Xpriv::new_master(NetworkKind::Main, &seed);
2405+
let path: DerivationPath = "m/1/2/3".parse().unwrap();
2406+
let path_prefix: DerivationPath = "m/1".parse().unwrap();
2407+
2408+
let expected_private_key =
2409+
parent_xpriv.derive_xpriv(&secp, &path).unwrap().to_private_key();
2410+
2411+
let derived_xpriv = parent_xpriv.derive_xpriv(&secp, &path_prefix).unwrap();
2412+
2413+
let derived_key = derived_xpriv
2414+
.get_key(&KeyRequest::Bip32((parent_xpriv.fingerprint(&secp), path.clone())), &secp)
2415+
.unwrap();
2416+
2417+
assert_eq!(derived_key, Some(expected_private_key));
2418+
}
2419+
24002420
#[test]
24012421
fn fee() {
24022422
let output_0_val = Amount::from_sat_u32(99_999_699);

0 commit comments

Comments
 (0)