From e6a3d603c9a3e062e38d4eb03bbc8effd64b6319 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Sun, 1 Aug 2021 21:03:58 +0200 Subject: [PATCH] BIP32 extended key `to_ecdsa()` and `to_schnorr()` methods --- examples/bip32.rs | 2 +- src/util/bip32.rs | 47 ++++++++++++++++++++++++++++++++++++++------ src/util/psbt/mod.rs | 2 +- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/examples/bip32.rs b/examples/bip32.rs index 0ae3bce3..ea4b8762 100644 --- a/examples/bip32.rs +++ b/examples/bip32.rs @@ -49,7 +49,7 @@ fn main() { let path = DerivationPath::from_str("m/84h/0h/0h").unwrap(); let child = root.derive_priv(&secp, &path).unwrap(); println!("Child at {}: {}", path, child); - let xpub = ExtendedPubKey::from_private(&secp, &child); + let xpub = ExtendedPubKey::from_priv(&secp, &child); println!("Public key at {}: {}", path, xpub); // generate first receiving address at m/0/0 diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 77b7de28..248dc476 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -25,11 +25,11 @@ use core::{fmt, str::FromStr, default::Default}; use hash_types::XpubIdentifier; use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine}; -use secp256k1::{self, Secp256k1}; +use secp256k1::{self, Secp256k1, XOnlyPublicKey}; use network::constants::Network; use util::{base58, endian}; -use util::key; +use util::{key, ecdsa, schnorr}; use io::Write; /// A chain code @@ -527,6 +527,21 @@ impl ExtendedPrivKey { }) } + /// Constructs ECDSA compressed private key matching internal secret key representation. + pub fn to_priv(&self) -> ecdsa::PrivateKey { + ecdsa::PrivateKey { + compressed: true, + network: self.network, + key: self.private_key + } + } + + /// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal + /// secret key representation. + pub fn to_keypair(&self, secp: &Secp256k1) -> schnorr::KeyPair { + schnorr::KeyPair::from_seckey_slice(secp, &self.private_key[..]).expect("BIP32 internal private key representation is broken") + } + /// Attempts to derive an extended private key from a path. /// /// The `path` argument can be both of type `DerivationPath` or `Vec`. @@ -616,7 +631,7 @@ impl ExtendedPrivKey { /// Returns the HASH160 of the public key belonging to the xpriv pub fn identifier(&self, secp: &Secp256k1) -> XpubIdentifier { - ExtendedPubKey::from_private(secp, self).identifier() + ExtendedPubKey::from_priv(secp, self).identifier() } /// Returns the first four bytes of the identifier @@ -627,7 +642,13 @@ impl ExtendedPrivKey { impl ExtendedPubKey { /// Derives a public key from a private key + #[deprecated(since = "0.28.0", note = "use ExtendedPubKey::from_priv")] pub fn from_private(secp: &Secp256k1, sk: &ExtendedPrivKey) -> ExtendedPubKey { + ExtendedPubKey::from_priv(secp, sk) + } + + /// Derives a public key from a private key + pub fn from_priv(secp: &Secp256k1, sk: &ExtendedPrivKey) -> ExtendedPubKey { ExtendedPubKey { network: sk.network, depth: sk.depth, @@ -638,6 +659,20 @@ impl ExtendedPubKey { } } + /// Constructs ECDSA compressed public key matching internal public key representation. + pub fn to_pub(&self) -> ecdsa::PublicKey { + ecdsa::PublicKey { + compressed: true, + key: self.public_key + } + } + + /// Constructs BIP340 x-only public key for BIP-340 signatures and Taproot use matching + /// the internal public key representation. + pub fn to_x_only_pub(&self) -> XOnlyPublicKey { + XOnlyPublicKey::from(self.public_key) + } + /// Attempts to derive an extended public key from a path. /// /// The `path` argument can be both of type `DerivationPath` or `Vec`. @@ -869,7 +904,7 @@ mod tests { expected_pk: &str) { let mut sk = ExtendedPrivKey::new_master(network, seed).unwrap(); - let mut pk = ExtendedPubKey::from_private(secp, &sk); + let mut pk = ExtendedPubKey::from_priv(secp, &sk); // Check derivation convenience method for ExtendedPrivKey assert_eq!( @@ -897,7 +932,7 @@ mod tests { match num { Normal {..} => { let pk2 = pk.ckd_pub(secp, num).unwrap(); - pk = ExtendedPubKey::from_private(secp, &sk); + pk = ExtendedPubKey::from_priv(secp, &sk); assert_eq!(pk, pk2); } Hardened {..} => { @@ -905,7 +940,7 @@ mod tests { pk.ckd_pub(secp, num), Err(Error::CannotDeriveFromHardenedKey) ); - pk = ExtendedPubKey::from_private(secp, &sk); + pk = ExtendedPubKey::from_priv(secp, &sk); } } } diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs index 401929b5..c6c4e46c 100644 --- a/src/util/psbt/mod.rs +++ b/src/util/psbt/mod.rs @@ -310,7 +310,7 @@ mod tests { sk = sk.derive_priv(secp, &dpath).unwrap(); - let pk: ExtendedPubKey = ExtendedPubKey::from_private(&secp, &sk); + let pk: ExtendedPubKey = ExtendedPubKey::from_priv(&secp, &sk); hd_keypaths.insert(pk.public_key, (fprint, dpath.into()));