diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs index d38bcea2..88406702 100644 --- a/src/blockdata/script.rs +++ b/src/blockdata/script.rs @@ -39,7 +39,7 @@ use policy::DUST_RELAY_TX_FEE; #[cfg(feature="bitcoinconsensus")] use core::convert::From; #[cfg(feature="bitcoinconsensus")] use OutPoint; -use util::ecdsa::PublicKey; +use util::key::PublicKey; use util::address::WitnessVersion; use util::taproot::{LeafVersion, TapBranchHash, TapLeafHash}; use secp256k1::{Secp256k1, Verification}; @@ -1031,7 +1031,7 @@ mod test { use hashes::hex::{FromHex, ToHex}; use consensus::encode::{deserialize, serialize}; use blockdata::opcodes; - use util::ecdsa::PublicKey; + use util::key::PublicKey; use util::psbt::serialize::Serialize; #[test] diff --git a/src/lib.rs b/src/lib.rs index ef326038..5005744c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,10 +138,7 @@ pub use util::sighash::SchnorrSigHashType; pub use util::ecdsa::{self, EcdsaSig, EcdsaSigError}; pub use util::schnorr::{self, SchnorrSig, SchnorrSigError}; -#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PrivateKey` instead")] -pub use util::ecdsa::PrivateKey; -#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PublicKey` instead")] -pub use util::ecdsa::PublicKey; +pub use util::key::{PrivateKey, PublicKey, XOnlyPublicKey, KeyPair}; #[allow(deprecated)] pub use blockdata::transaction::SigHashType; diff --git a/src/util/address.rs b/src/util/address.rs index def02a4f..20d275b3 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -20,13 +20,13 @@ //! ```rust //! use bitcoin::network::constants::Network; //! use bitcoin::util::address::Address; -//! use bitcoin::util::ecdsa; +//! use bitcoin::PublicKey; //! use bitcoin::secp256k1::Secp256k1; //! use bitcoin::secp256k1::rand::thread_rng; //! //! // Generate random key pair. //! let s = Secp256k1::new(); -//! let public_key = ecdsa::PublicKey::new(s.generate_keypair(&mut thread_rng()).1); +//! let public_key = PublicKey::new(s.generate_keypair(&mut thread_rng()).1); //! //! // Generate pay-to-pubkey-hash address. //! let address = Address::p2pkh(&public_key, Network::Bitcoin); @@ -47,8 +47,8 @@ use blockdata::{script, opcodes}; use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST, MAX_SCRIPT_ELEMENT_SIZE}; use network::constants::Network; use util::base58; -use util::ecdsa; use util::taproot::TapBranchHash; +use util::key::PublicKey; use blockdata::script::Instruction; use util::schnorr::{TapTweak, UntweakedPublicKey, TweakedPublicKey}; @@ -408,7 +408,7 @@ impl Payload { /// Creates a pay to (compressed) public key hash payload from a public key #[inline] - pub fn p2pkh(pk: &ecdsa::PublicKey) -> Payload { + pub fn p2pkh(pk: &PublicKey) -> Payload { Payload::PubkeyHash(pk.pubkey_hash()) } @@ -422,7 +422,7 @@ impl Payload { } /// Create a witness pay to public key payload from a public key - pub fn p2wpkh(pk: &ecdsa::PublicKey) -> Result { + pub fn p2wpkh(pk: &PublicKey) -> Result { Ok(Payload::WitnessProgram { version: WitnessVersion::V0, program: pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.to_vec(), @@ -430,7 +430,7 @@ impl Payload { } /// Create a pay to script payload that embeds a witness pay to public key - pub fn p2shwpkh(pk: &ecdsa::PublicKey) -> Result { + pub fn p2shwpkh(pk: &PublicKey) -> Result { let builder = script::Builder::new() .push_int(0) .push_slice(&pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?); @@ -543,7 +543,7 @@ impl Address { /// /// This is the preferred non-witness type address. #[inline] - pub fn p2pkh(pk: &ecdsa::PublicKey, network: Network) -> Address { + pub fn p2pkh(pk: &PublicKey, network: Network) -> Address { Address { network, payload: Payload::p2pkh(pk), @@ -568,7 +568,7 @@ impl Address { /// /// # Errors /// Will only return an error if an uncompressed public key is provided. - pub fn p2wpkh(pk: &ecdsa::PublicKey, network: Network) -> Result { + pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result { Ok(Address { network, payload: Payload::p2wpkh(pk)?, @@ -581,7 +581,7 @@ impl Address { /// /// # Errors /// Will only return an Error if an uncompressed public key is provided. - pub fn p2shwpkh(pk: &ecdsa::PublicKey, network: Network) -> Result { + pub fn p2shwpkh(pk: &PublicKey, network: Network) -> Result { Ok(Address { network, payload: Payload::p2shwpkh(pk)?, @@ -878,7 +878,7 @@ mod tests { use blockdata::script::Script; use network::constants::Network::{Bitcoin, Testnet}; - use util::ecdsa::PublicKey; + use util::key::PublicKey; use secp256k1::XOnlyPublicKey; use super::*; diff --git a/src/util/bip143.rs b/src/util/bip143.rs index 17ff8e67..b9024ff1 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -194,7 +194,7 @@ mod tests { use consensus::encode::deserialize; use network::constants::Network; use util::address::Address; - use util::ecdsa::PublicKey; + use util::key::PublicKey; use hashes::hex::FromHex; use super::*; diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 71658726..9e13a9d0 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -19,6 +19,7 @@ use prelude::*; +use io::Write; use core::{fmt, str::FromStr, default::Default}; #[cfg(feature = "std")] use std::error; #[cfg(feature = "serde")] use serde; @@ -28,9 +29,8 @@ use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine}; use secp256k1::{self, Secp256k1, XOnlyPublicKey}; use network::constants::Network; -use util::{base58, endian}; -use util::{key, ecdsa, schnorr}; -use io::Write; +use util::{base58, endian, key}; +use util::key::{PublicKey, PrivateKey, KeyPair}; /// A chain code #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -528,8 +528,8 @@ impl ExtendedPrivKey { } /// Constructs ECDSA compressed private key matching internal secret key representation. - pub fn to_priv(&self) -> ecdsa::PrivateKey { - ecdsa::PrivateKey { + pub fn to_priv(&self) -> PrivateKey { + PrivateKey { compressed: true, network: self.network, inner: self.private_key @@ -538,8 +538,8 @@ impl ExtendedPrivKey { /// 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") + pub fn to_keypair(&self, secp: &Secp256k1) -> KeyPair { + 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. @@ -660,8 +660,8 @@ impl ExtendedPubKey { } /// Constructs ECDSA compressed public key matching internal public key representation. - pub fn to_pub(&self) -> ecdsa::PublicKey { - ecdsa::PublicKey { + pub fn to_pub(&self) -> PublicKey { + PublicKey { compressed: true, inner: self.public_key } diff --git a/src/util/ecdsa.rs b/src/util/ecdsa.rs index c939d419..52a2fe73 100644 --- a/src/util/ecdsa.rs +++ b/src/util/ecdsa.rs @@ -1,6 +1,7 @@ // Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra +// // To the extent possible under law, the author(s) have dedicated all // copyright and related and neighboring rights to this software to // the public domain worldwide. This software is distributed without @@ -11,412 +12,17 @@ // If not, see . // -//! ECDSA Bitcoin keys. -//! -//! This module provides ECDSA keys used in Bitcoin that can be roundtrip -//! (de)serialized. +//! ECDSA Bitcoin signatures. //! +//! This module provides ECDSA signatures used Bitcoin that can be roundtrip (de)serialized. use prelude::*; - -use core::{ops, str::FromStr}; -use core::fmt::{self, Write as _fmtWrite}; -use io; - -use secp256k1::{self, Secp256k1}; -use network::constants::Network; -use hashes::{Hash, hash160, hex}; -use hashes::hex::FromHex; -use hash_types::{PubkeyHash, WPubkeyHash}; -use util::base58; -use util::key::Error; -use blockdata::transaction::{EcdsaSigHashType, NonStandardSigHashType}; - - -/// A Bitcoin ECDSA public key -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PublicKey { - /// Whether this public key should be serialized as compressed - pub compressed: bool, - /// The actual ECDSA key - pub inner: secp256k1::PublicKey, -} - -impl PublicKey { - /// Constructs compressed ECDSA public key from the provided generic Secp256k1 public key - pub fn new(key: secp256k1::PublicKey) -> PublicKey { - PublicKey { - compressed: true, - inner: key, - } - } - - /// Constructs uncompressed (legacy) ECDSA public key from the provided generic Secp256k1 - /// public key - pub fn new_uncompressed(key: secp256k1::PublicKey) -> PublicKey { - PublicKey { - compressed: false, - inner: key, - } - } - - /// Returns bitcoin 160-bit hash of the public key - pub fn pubkey_hash(&self) -> PubkeyHash { - if self.compressed { - PubkeyHash::hash(&self.inner.serialize()) - } else { - PubkeyHash::hash(&self.inner.serialize_uncompressed()) - } - } - - /// Returns bitcoin 160-bit hash of the public key for witness program - pub fn wpubkey_hash(&self) -> Option { - if self.compressed { - Some(WPubkeyHash::from_inner( - hash160::Hash::hash(&self.inner.serialize()).into_inner() - )) - } else { - // We can't create witness pubkey hashes for an uncompressed - // public keys - None - } - } - - /// Write the public key into a writer - pub fn write_into(&self, mut writer: W) -> Result<(), io::Error> { - if self.compressed { - writer.write_all(&self.inner.serialize()) - } else { - writer.write_all(&self.inner.serialize_uncompressed()) - } - } - - /// Read the public key from a reader - /// - /// This internally reads the first byte before reading the rest, so - /// use of a `BufReader` is recommended. - pub fn read_from(mut reader: R) -> Result { - let mut bytes = [0; 65]; - - reader.read_exact(&mut bytes[0..1])?; - let bytes = if bytes[0] < 4 { - &mut bytes[..33] - } else { - &mut bytes[..65] - }; - - reader.read_exact(&mut bytes[1..])?; - Self::from_slice(bytes).map_err(|e|{ - // Need a static string for core2 - #[cfg(feature = "std")] - let reason = e; - #[cfg(not(feature = "std"))] - let reason = match e { - Error::Base58(_) => "base58 error", - Error::Secp256k1(_) => "secp256k1 error", - }; - io::Error::new(io::ErrorKind::InvalidData, reason) - }) - } - - /// Serialize the public key to bytes - pub fn to_bytes(&self) -> Vec { - let mut buf = Vec::new(); - self.write_into(&mut buf).expect("vecs don't error"); - buf - } - - /// Deserialize a public key from a slice - pub fn from_slice(data: &[u8]) -> Result { - let compressed: bool = match data.len() { - 33 => true, - 65 => false, - len => { return Err(base58::Error::InvalidLength(len).into()); }, - }; - - Ok(PublicKey { - compressed, - inner: secp256k1::PublicKey::from_slice(data)?, - }) - } - - /// Computes the public key as supposed to be used with this secret - pub fn from_private_key(secp: &Secp256k1, sk: &PrivateKey) -> PublicKey { - sk.public_key(secp) - } -} - -impl fmt::Display for PublicKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.compressed { - for ch in &self.inner.serialize()[..] { - write!(f, "{:02x}", ch)?; - } - } else { - for ch in &self.inner.serialize_uncompressed()[..] { - write!(f, "{:02x}", ch)?; - } - } - Ok(()) - } -} - -impl FromStr for PublicKey { - type Err = Error; - fn from_str(s: &str) -> Result { - let key = secp256k1::PublicKey::from_str(s)?; - Ok(PublicKey { - inner: key, - compressed: s.len() == 66 - }) - } -} - -/// A Bitcoin ECDSA private key -#[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct PrivateKey { - /// Whether this private key should be serialized as compressed - pub compressed: bool, - /// The network on which this key should be used - pub network: Network, - /// The actual ECDSA key - pub inner: secp256k1::SecretKey, -} - -impl PrivateKey { - /// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key - /// and the specified network - pub fn new(key: secp256k1::SecretKey, network: Network) -> PrivateKey { - PrivateKey { - compressed: true, - network, - inner: key, - } - } - - /// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1 - /// private key and the specified network - pub fn new_uncompressed(key: secp256k1::SecretKey, network: Network) -> PrivateKey { - PrivateKey { - compressed: false, - network, - inner: key, - } - } - - /// Creates a public key from this private key - pub fn public_key(&self, secp: &Secp256k1) -> PublicKey { - PublicKey { - compressed: self.compressed, - inner: secp256k1::PublicKey::from_secret_key(secp, &self.inner) - } - } - - /// Serialize the private key to bytes - pub fn to_bytes(&self) -> Vec { - self.inner[..].to_vec() - } - - /// Deserialize a private key from a slice - pub fn from_slice(data: &[u8], network: Network) -> Result { - Ok(PrivateKey::new( - secp256k1::SecretKey::from_slice(data)?, - network, - )) - } - - /// Format the private key to WIF format. - pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result { - let mut ret = [0; 34]; - ret[0] = match self.network { - Network::Bitcoin => 128, - Network::Testnet | Network::Signet | Network::Regtest => 239, - }; - ret[1..33].copy_from_slice(&self.inner[..]); - let privkey = if self.compressed { - ret[33] = 1; - base58::check_encode_slice(&ret[..]) - } else { - base58::check_encode_slice(&ret[..33]) - }; - fmt.write_str(&privkey) - } - - /// Get WIF encoding of this private key. - pub fn to_wif(&self) -> String { - let mut buf = String::new(); - buf.write_fmt(format_args!("{}", self)).unwrap(); - buf.shrink_to_fit(); - buf - } - - /// Parse WIF encoded private key. - pub fn from_wif(wif: &str) -> Result { - let data = base58::from_check(wif)?; - - let compressed = match data.len() { - 33 => false, - 34 => true, - _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); } - }; - - let network = match data[0] { - 128 => Network::Bitcoin, - 239 => Network::Testnet, - x => { return Err(Error::Base58(base58::Error::InvalidAddressVersion(x))); } - }; - - Ok(PrivateKey { - compressed, - network, - inner: secp256k1::SecretKey::from_slice(&data[1..33])?, - }) - } -} - -impl fmt::Display for PrivateKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.fmt_wif(f) - } -} - -#[cfg(not(feature = "std"))] -impl fmt::Debug for PrivateKey { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[private key data]") - } -} - -impl FromStr for PrivateKey { - type Err = Error; - fn from_str(s: &str) -> Result { - PrivateKey::from_wif(s) - } -} - -impl ops::Index for PrivateKey { - type Output = [u8]; - fn index(&self, _: ops::RangeFull) -> &[u8] { - &self.inner[..] - } -} - -#[cfg(feature = "serde")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -impl ::serde::Serialize for PrivateKey { - fn serialize(&self, s: S) -> Result { - s.collect_str(self) - } -} - -#[cfg(feature = "serde")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -impl<'de> ::serde::Deserialize<'de> for PrivateKey { - fn deserialize>(d: D) -> Result { - struct WifVisitor; - - impl<'de> ::serde::de::Visitor<'de> for WifVisitor { - type Value = PrivateKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("an ASCII WIF string") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - if let Ok(s) = ::core::str::from_utf8(v) { - PrivateKey::from_str(s).map_err(E::custom) - } else { - Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: ::serde::de::Error, - { - PrivateKey::from_str(v).map_err(E::custom) - } - } - - d.deserialize_str(WifVisitor) - } -} - -#[cfg(feature = "serde")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -impl ::serde::Serialize for PublicKey { - fn serialize(&self, s: S) -> Result { - if s.is_human_readable() { - s.collect_str(self) - } else { - if self.compressed { - s.serialize_bytes(&self.inner.serialize()[..]) - } else { - s.serialize_bytes(&self.inner.serialize_uncompressed()[..]) - } - } - } -} - -#[cfg(feature = "serde")] -#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -impl<'de> ::serde::Deserialize<'de> for PublicKey { - fn deserialize>(d: D) -> Result { - if d.is_human_readable() { - struct HexVisitor; - - impl<'de> ::serde::de::Visitor<'de> for HexVisitor { - type Value = PublicKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("an ASCII hex string") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - if let Ok(hex) = ::core::str::from_utf8(v) { - PublicKey::from_str(hex).map_err(E::custom) - } else { - Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) - } - } - - fn visit_str(self, v: &str) -> Result - where - E: ::serde::de::Error, - { - PublicKey::from_str(v).map_err(E::custom) - } - } - d.deserialize_str(HexVisitor) - } else { - struct BytesVisitor; - - impl<'de> ::serde::de::Visitor<'de> for BytesVisitor { - type Value = PublicKey; - - fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - formatter.write_str("a bytestring") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: ::serde::de::Error, - { - PublicKey::from_slice(v).map_err(E::custom) - } - } - - d.deserialize_bytes(BytesVisitor) - } - } -} +use core::str::FromStr; +use core::fmt; +use hashes::hex::{self, FromHex}; +use blockdata::transaction::NonStandardSigHashType; +use secp256k1; +use EcdsaSigHashType; /// An ECDSA signature with the corresponding hash type. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -527,162 +133,3 @@ impl From for EcdsaSigError { EcdsaSigError::HexEncoding(err) } } - -#[cfg(test)] -mod tests { - use io; - use super::{PrivateKey, PublicKey}; - use secp256k1::Secp256k1; - use std::str::FromStr; - use hashes::hex::ToHex; - use network::constants::Network::Testnet; - use network::constants::Network::Bitcoin; - use util::address::Address; - - #[test] - fn test_key_derivation() { - // testnet compressed - let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); - assert_eq!(sk.network, Testnet); - assert_eq!(sk.compressed, true); - assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); - - let secp = Secp256k1::new(); - let pk = Address::p2pkh(&sk.public_key(&secp), sk.network); - assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); - - // test string conversion - assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); - let sk_str = - PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); - assert_eq!(&sk.to_wif(), &sk_str.to_wif()); - - // mainnet uncompressed - let sk = PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap(); - assert_eq!(sk.network, Bitcoin); - assert_eq!(sk.compressed, false); - assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); - - let secp = Secp256k1::new(); - let mut pk = sk.public_key(&secp); - assert_eq!(pk.compressed, false); - assert_eq!(&pk.to_string(), "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"); - assert_eq!(pk, PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap()); - let addr = Address::p2pkh(&pk, sk.network); - assert_eq!(&addr.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); - pk.compressed = true; - assert_eq!(&pk.to_string(), "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"); - assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap()); - } - - #[test] - fn test_pubkey_hash() { - let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); - let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); - assert_eq!(pk.pubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); - assert_eq!(upk.pubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a"); - } - - #[test] - fn test_wpubkey_hash() { - let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); - let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); - assert_eq!(pk.wpubkey_hash().unwrap().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); - assert_eq!(upk.wpubkey_hash(), None); - } - - #[cfg(feature = "serde")] - #[test] - fn test_key_serde() { - use serde_test::{Configure, Token, assert_tokens}; - - static KEY_WIF: &'static str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"; - static PK_STR: &'static str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef"; - static PK_STR_U: &'static str = "\ - 04\ - 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\ - 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\ - "; - static PK_BYTES: [u8; 33] = [ - 0x03, - 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec, - 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c, - 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e, - 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef, - ]; - static PK_BYTES_U: [u8; 65] = [ - 0x04, - 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec, - 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c, - 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e, - 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef, - 0x87, 0x28, 0x8e, 0xd7, 0x3c, 0xe4, 0x7f, 0xc4, - 0xf5, 0xc7, 0x9d, 0x19, 0xeb, 0xfa, 0x57, 0xda, - 0x7c, 0xff, 0x3a, 0xff, 0x6e, 0x81, 0x9e, 0x4e, - 0xe9, 0x71, 0xd8, 0x6b, 0x5e, 0x61, 0x87, 0x5d, - ]; - - let s = Secp256k1::new(); - let sk = PrivateKey::from_str(&KEY_WIF).unwrap(); - let pk = PublicKey::from_private_key(&s, &sk); - let pk_u = PublicKey { - inner: pk.inner, - compressed: false, - }; - - assert_tokens(&sk, &[Token::BorrowedStr(KEY_WIF)]); - assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]); - assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); - assert_tokens(&pk_u.compact(), &[Token::BorrowedBytes(&PK_BYTES_U[..])]); - assert_tokens(&pk_u.readable(), &[Token::BorrowedStr(PK_STR_U)]); - } - - fn random_key(mut seed: u8) -> PublicKey { - loop { - let mut data = [0; 65]; - for byte in &mut data[..] { - *byte = seed; - // totally a rng - seed = seed.wrapping_mul(41).wrapping_add(43); - } - if data[0] % 2 == 0 { - data[0] = 4; - if let Ok(key) = PublicKey::from_slice(&data[..]) { - return key; - } - } else { - data[0] = 2 + (data[0] >> 7); - if let Ok(key) = PublicKey::from_slice(&data[..33]) { - return key; - } - } - } - } - - #[test] - fn pubkey_read_write() { - const N_KEYS: usize = 20; - let keys: Vec<_> = (0..N_KEYS).map(|i| random_key(i as u8)).collect(); - - let mut v = vec![]; - for k in &keys { - k.write_into(&mut v).expect("writing into vec"); - } - - let mut dec_keys = vec![]; - let mut cursor = io::Cursor::new(&v); - for _ in 0..N_KEYS { - dec_keys.push(PublicKey::read_from(&mut cursor).expect("reading from vec")); - } - - assert_eq!(keys, dec_keys); - - // sanity checks - assert!(PublicKey::read_from(&mut cursor).is_err()); - assert!(PublicKey::read_from(io::Cursor::new(&[])).is_err()); - assert!(PublicKey::read_from(io::Cursor::new(&[0; 33][..])).is_err()); - assert!(PublicKey::read_from(io::Cursor::new(&[2; 32][..])).is_err()); - assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err()); - assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err()); - } -} diff --git a/src/util/key.rs b/src/util/key.rs index 9cd44e73..a4f50eba 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -13,18 +13,25 @@ //! Bitcoin keys. //! -//! This module provides keys used in Bitcoin that can be roundtrip (de)serialized. -//! +//! This module provides keys used in Bitcoin that can be roundtrip +//! (de)serialized. -#[deprecated(since = "0.26.1", note = "Please use `util::ecdsa` instead")] -pub use util::ecdsa::{PrivateKey, PublicKey}; +pub use secp256k1::{XOnlyPublicKey, KeyPair}; -use core::fmt; +use prelude::*; + +use core::{ops, str::FromStr}; +use core::fmt::{self, Write as _fmtWrite}; +use io; #[cfg(feature = "std")] use std::error; -use secp256k1; +use secp256k1::{self, Secp256k1}; +use network::constants::Network; +use hashes::{Hash, hash160}; +use hash_types::{PubkeyHash, WPubkeyHash}; use util::base58; + /// A key-related error. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub enum Error { @@ -68,3 +75,548 @@ impl From for Error { Error::Secp256k1(e) } } + + +/// A Bitcoin ECDSA public key +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PublicKey { + /// Whether this public key should be serialized as compressed + pub compressed: bool, + /// The actual ECDSA key + pub inner: secp256k1::PublicKey, +} + +impl PublicKey { + /// Constructs compressed ECDSA public key from the provided generic Secp256k1 public key + pub fn new(key: secp256k1::PublicKey) -> PublicKey { + PublicKey { + compressed: true, + inner: key, + } + } + + /// Constructs uncompressed (legacy) ECDSA public key from the provided generic Secp256k1 + /// public key + pub fn new_uncompressed(key: secp256k1::PublicKey) -> PublicKey { + PublicKey { + compressed: false, + inner: key, + } + } + + /// Returns bitcoin 160-bit hash of the public key + pub fn pubkey_hash(&self) -> PubkeyHash { + if self.compressed { + PubkeyHash::hash(&self.inner.serialize()) + } else { + PubkeyHash::hash(&self.inner.serialize_uncompressed()) + } + } + + /// Returns bitcoin 160-bit hash of the public key for witness program + pub fn wpubkey_hash(&self) -> Option { + if self.compressed { + Some(WPubkeyHash::from_inner( + hash160::Hash::hash(&self.inner.serialize()).into_inner() + )) + } else { + // We can't create witness pubkey hashes for an uncompressed + // public keys + None + } + } + + /// Write the public key into a writer + pub fn write_into(&self, mut writer: W) -> Result<(), io::Error> { + if self.compressed { + writer.write_all(&self.inner.serialize()) + } else { + writer.write_all(&self.inner.serialize_uncompressed()) + } + } + + /// Read the public key from a reader + /// + /// This internally reads the first byte before reading the rest, so + /// use of a `BufReader` is recommended. + pub fn read_from(mut reader: R) -> Result { + let mut bytes = [0; 65]; + + reader.read_exact(&mut bytes[0..1])?; + let bytes = if bytes[0] < 4 { + &mut bytes[..33] + } else { + &mut bytes[..65] + }; + + reader.read_exact(&mut bytes[1..])?; + Self::from_slice(bytes).map_err(|e|{ + // Need a static string for core2 + #[cfg(feature = "std")] + let reason = e; + #[cfg(not(feature = "std"))] + let reason = match e { + Error::Base58(_) => "base58 error", + Error::Secp256k1(_) => "secp256k1 error", + }; + io::Error::new(io::ErrorKind::InvalidData, reason) + }) + } + + /// Serialize the public key to bytes + pub fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + self.write_into(&mut buf).expect("vecs don't error"); + buf + } + + /// Deserialize a public key from a slice + pub fn from_slice(data: &[u8]) -> Result { + let compressed: bool = match data.len() { + 33 => true, + 65 => false, + len => { return Err(base58::Error::InvalidLength(len).into()); }, + }; + + Ok(PublicKey { + compressed, + inner: secp256k1::PublicKey::from_slice(data)?, + }) + } + + /// Computes the public key as supposed to be used with this secret + pub fn from_private_key(secp: &Secp256k1, sk: &PrivateKey) -> PublicKey { + sk.public_key(secp) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.compressed { + for ch in &self.inner.serialize()[..] { + write!(f, "{:02x}", ch)?; + } + } else { + for ch in &self.inner.serialize_uncompressed()[..] { + write!(f, "{:02x}", ch)?; + } + } + Ok(()) + } +} + +impl FromStr for PublicKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let key = secp256k1::PublicKey::from_str(s)?; + Ok(PublicKey { + inner: key, + compressed: s.len() == 66 + }) + } +} + +/// A Bitcoin ECDSA private key +#[derive(Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct PrivateKey { + /// Whether this private key should be serialized as compressed + pub compressed: bool, + /// The network on which this key should be used + pub network: Network, + /// The actual ECDSA key + pub inner: secp256k1::SecretKey, +} + +impl PrivateKey { + /// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key + /// and the specified network + pub fn new(key: secp256k1::SecretKey, network: Network) -> PrivateKey { + PrivateKey { + compressed: true, + network, + inner: key, + } + } + + /// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1 + /// private key and the specified network + pub fn new_uncompressed(key: secp256k1::SecretKey, network: Network) -> PrivateKey { + PrivateKey { + compressed: false, + network, + inner: key, + } + } + + /// Creates a public key from this private key + pub fn public_key(&self, secp: &Secp256k1) -> PublicKey { + PublicKey { + compressed: self.compressed, + inner: secp256k1::PublicKey::from_secret_key(secp, &self.inner) + } + } + + /// Serialize the private key to bytes + pub fn to_bytes(&self) -> Vec { + self.inner[..].to_vec() + } + + /// Deserialize a private key from a slice + pub fn from_slice(data: &[u8], network: Network) -> Result { + Ok(PrivateKey::new( + secp256k1::SecretKey::from_slice(data)?, + network, + )) + } + + /// Format the private key to WIF format. + pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result { + let mut ret = [0; 34]; + ret[0] = match self.network { + Network::Bitcoin => 128, + Network::Testnet | Network::Signet | Network::Regtest => 239, + }; + ret[1..33].copy_from_slice(&self.inner[..]); + let privkey = if self.compressed { + ret[33] = 1; + base58::check_encode_slice(&ret[..]) + } else { + base58::check_encode_slice(&ret[..33]) + }; + fmt.write_str(&privkey) + } + + /// Get WIF encoding of this private key. + pub fn to_wif(&self) -> String { + let mut buf = String::new(); + buf.write_fmt(format_args!("{}", self)).unwrap(); + buf.shrink_to_fit(); + buf + } + + /// Parse WIF encoded private key. + pub fn from_wif(wif: &str) -> Result { + let data = base58::from_check(wif)?; + + let compressed = match data.len() { + 33 => false, + 34 => true, + _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); } + }; + + let network = match data[0] { + 128 => Network::Bitcoin, + 239 => Network::Testnet, + x => { return Err(Error::Base58(base58::Error::InvalidAddressVersion(x))); } + }; + + Ok(PrivateKey { + compressed, + network, + inner: secp256k1::SecretKey::from_slice(&data[1..33])?, + }) + } +} + +impl fmt::Display for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.fmt_wif(f) + } +} + +#[cfg(not(feature = "std"))] +impl fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "[private key data]") + } +} + +impl FromStr for PrivateKey { + type Err = Error; + fn from_str(s: &str) -> Result { + PrivateKey::from_wif(s) + } +} + +impl ops::Index for PrivateKey { + type Output = [u8]; + fn index(&self, _: ops::RangeFull) -> &[u8] { + &self.inner[..] + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl ::serde::Serialize for PrivateKey { + fn serialize(&self, s: S) -> Result { + s.collect_str(self) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl<'de> ::serde::Deserialize<'de> for PrivateKey { + fn deserialize>(d: D) -> Result { + struct WifVisitor; + + impl<'de> ::serde::de::Visitor<'de> for WifVisitor { + type Value = PrivateKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("an ASCII WIF string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if let Ok(s) = ::core::str::from_utf8(v) { + PrivateKey::from_str(s).map_err(E::custom) + } else { + Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) + } + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error, + { + PrivateKey::from_str(v).map_err(E::custom) + } + } + + d.deserialize_str(WifVisitor) + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl ::serde::Serialize for PublicKey { + fn serialize(&self, s: S) -> Result { + if s.is_human_readable() { + s.collect_str(self) + } else { + if self.compressed { + s.serialize_bytes(&self.inner.serialize()[..]) + } else { + s.serialize_bytes(&self.inner.serialize_uncompressed()[..]) + } + } + } +} + +#[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] +impl<'de> ::serde::Deserialize<'de> for PublicKey { + fn deserialize>(d: D) -> Result { + if d.is_human_readable() { + struct HexVisitor; + + impl<'de> ::serde::de::Visitor<'de> for HexVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("an ASCII hex string") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + if let Ok(hex) = ::core::str::from_utf8(v) { + PublicKey::from_str(hex).map_err(E::custom) + } else { + Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) + } + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error, + { + PublicKey::from_str(v).map_err(E::custom) + } + } + d.deserialize_str(HexVisitor) + } else { + struct BytesVisitor; + + impl<'de> ::serde::de::Visitor<'de> for BytesVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + formatter.write_str("a bytestring") + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error, + { + PublicKey::from_slice(v).map_err(E::custom) + } + } + + d.deserialize_bytes(BytesVisitor) + } + } +} + +#[cfg(test)] +mod tests { + use io; + use super::{PrivateKey, PublicKey}; + use secp256k1::Secp256k1; + use std::str::FromStr; + use hashes::hex::ToHex; + use network::constants::Network::Testnet; + use network::constants::Network::Bitcoin; + use util::address::Address; + + #[test] + fn test_key_derivation() { + // testnet compressed + let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); + assert_eq!(sk.network, Testnet); + assert_eq!(sk.compressed, true); + assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); + + let secp = Secp256k1::new(); + let pk = Address::p2pkh(&sk.public_key(&secp), sk.network); + assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); + + // test string conversion + assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); + let sk_str = + PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); + assert_eq!(&sk.to_wif(), &sk_str.to_wif()); + + // mainnet uncompressed + let sk = PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap(); + assert_eq!(sk.network, Bitcoin); + assert_eq!(sk.compressed, false); + assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); + + let secp = Secp256k1::new(); + let mut pk = sk.public_key(&secp); + assert_eq!(pk.compressed, false); + assert_eq!(&pk.to_string(), "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"); + assert_eq!(pk, PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap()); + let addr = Address::p2pkh(&pk, sk.network); + assert_eq!(&addr.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); + pk.compressed = true; + assert_eq!(&pk.to_string(), "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"); + assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap()); + } + + #[test] + fn test_pubkey_hash() { + let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); + let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); + assert_eq!(pk.pubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); + assert_eq!(upk.pubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a"); + } + + #[test] + fn test_wpubkey_hash() { + let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap(); + let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap(); + assert_eq!(pk.wpubkey_hash().unwrap().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"); + assert_eq!(upk.wpubkey_hash(), None); + } + + #[cfg(feature = "serde")] + #[test] + fn test_key_serde() { + use serde_test::{Configure, Token, assert_tokens}; + + static KEY_WIF: &'static str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"; + static PK_STR: &'static str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef"; + static PK_STR_U: &'static str = "\ + 04\ + 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\ + 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\ + "; + static PK_BYTES: [u8; 33] = [ + 0x03, + 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec, + 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c, + 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e, + 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef, + ]; + static PK_BYTES_U: [u8; 65] = [ + 0x04, + 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec, + 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c, + 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e, + 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef, + 0x87, 0x28, 0x8e, 0xd7, 0x3c, 0xe4, 0x7f, 0xc4, + 0xf5, 0xc7, 0x9d, 0x19, 0xeb, 0xfa, 0x57, 0xda, + 0x7c, 0xff, 0x3a, 0xff, 0x6e, 0x81, 0x9e, 0x4e, + 0xe9, 0x71, 0xd8, 0x6b, 0x5e, 0x61, 0x87, 0x5d, + ]; + + let s = Secp256k1::new(); + let sk = PrivateKey::from_str(&KEY_WIF).unwrap(); + let pk = PublicKey::from_private_key(&s, &sk); + let pk_u = PublicKey { + inner: pk.inner, + compressed: false, + }; + + assert_tokens(&sk, &[Token::BorrowedStr(KEY_WIF)]); + assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]); + assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); + assert_tokens(&pk_u.compact(), &[Token::BorrowedBytes(&PK_BYTES_U[..])]); + assert_tokens(&pk_u.readable(), &[Token::BorrowedStr(PK_STR_U)]); + } + + fn random_key(mut seed: u8) -> PublicKey { + loop { + let mut data = [0; 65]; + for byte in &mut data[..] { + *byte = seed; + // totally a rng + seed = seed.wrapping_mul(41).wrapping_add(43); + } + if data[0] % 2 == 0 { + data[0] = 4; + if let Ok(key) = PublicKey::from_slice(&data[..]) { + return key; + } + } else { + data[0] = 2 + (data[0] >> 7); + if let Ok(key) = PublicKey::from_slice(&data[..33]) { + return key; + } + } + } + } + + #[test] + fn pubkey_read_write() { + const N_KEYS: usize = 20; + let keys: Vec<_> = (0..N_KEYS).map(|i| random_key(i as u8)).collect(); + + let mut v = vec![]; + for k in &keys { + k.write_into(&mut v).expect("writing into vec"); + } + + let mut dec_keys = vec![]; + let mut cursor = io::Cursor::new(&v); + for _ in 0..N_KEYS { + dec_keys.push(PublicKey::read_from(&mut cursor).expect("reading from vec")); + } + + assert_eq!(keys, dec_keys); + + // sanity checks + assert!(PublicKey::read_from(&mut cursor).is_err()); + assert!(PublicKey::read_from(io::Cursor::new(&[])).is_err()); + assert!(PublicKey::read_from(io::Cursor::new(&[0; 33][..])).is_err()); + assert!(PublicKey::read_from(io::Cursor::new(&[2; 32][..])).is_err()); + assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err()); + assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err()); + } +} diff --git a/src/util/misc.rs b/src/util/misc.rs index 0c3ef086..e51c4a19 100644 --- a/src/util/misc.rs +++ b/src/util/misc.rs @@ -42,7 +42,7 @@ mod message_signing { use secp256k1; use secp256k1::ecdsa::{RecoveryId, RecoverableSignature}; - use util::ecdsa::PublicKey; + use util::key::PublicKey; use util::address::{Address, AddressType}; /// An error used for dealing with Bitcoin Signed Messages. diff --git a/src/util/mod.rs b/src/util/mod.rs index e111f495..ba02fd70 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -17,8 +17,8 @@ //! Functions needed by all parts of the Bitcoin library. //! -pub mod ecdsa; pub mod key; +pub mod ecdsa; pub mod schnorr; pub mod address; pub mod amount; diff --git a/src/util/schnorr.rs b/src/util/schnorr.rs index 6a0c9fa1..c2c5daad 100644 --- a/src/util/schnorr.rs +++ b/src/util/schnorr.rs @@ -20,18 +20,27 @@ use core::fmt; use prelude::*; -pub use secp256k1::{XOnlyPublicKey, KeyPair}; +use secp256k1::{XOnlyPublicKey as _XOnlyPublicKey, KeyPair as _KeyPair}; + use secp256k1::{self, Secp256k1, Verification, constants}; use hashes::Hash; use util::taproot::{TapBranchHash, TapTweakHash}; use SchnorrSigHashType; +/// Deprecated re-export of [`secp256k1::XOnlyPublicKey`] +#[deprecated(since = "0.28.0", note = "Please use `util::key::XOnlyPublicKey` instead")] +pub type XOnlyPublicKey = _XOnlyPublicKey; + +/// Deprecated re-export of [`secp256k1::KeyPair`] +#[deprecated(since = "0.28.0", note = "Please use `util::key::KeyPair` instead")] +pub type KeyPair = _KeyPair; + /// Untweaked BIP-340 X-coord-only public key -pub type UntweakedPublicKey = XOnlyPublicKey; +pub type UntweakedPublicKey = ::XOnlyPublicKey; /// Tweaked BIP-340 X-coord-only public key #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TweakedPublicKey(XOnlyPublicKey); +pub struct TweakedPublicKey(::XOnlyPublicKey); impl fmt::LowerHex for TweakedPublicKey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -46,13 +55,13 @@ impl fmt::Display for TweakedPublicKey { } /// Untweaked BIP-340 key pair -pub type UntweakedKeyPair = KeyPair; +pub type UntweakedKeyPair = ::KeyPair; /// Tweaked BIP-340 key pair #[derive(Clone)] #[cfg_attr(feature = "std", derive(Debug))] // TODO: Add other derives once secp256k1 v0.21.3 released -pub struct TweakedKeyPair(KeyPair); +pub struct TweakedKeyPair(::KeyPair); /// A trait for tweaking BIP340 key types (x-only public keys and key pairs). pub trait TapTweak { @@ -129,7 +138,7 @@ impl TapTweak for UntweakedKeyPair { /// # Returns /// The tweaked key and its parity. fn tap_tweak(mut self, secp: &Secp256k1, merkle_root: Option) -> TweakedKeyPair { - let pubkey = XOnlyPublicKey::from_keypair(&self); + let pubkey = ::XOnlyPublicKey::from_keypair(&self); let tweak_value = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).into_inner(); self.tweak_add_assign(&secp, &tweak_value).expect("Tap tweak failed"); TweakedKeyPair(self) @@ -147,17 +156,17 @@ impl TweakedPublicKey { /// This method is dangerous and can lead to loss of funds if used incorrectly. /// Specifically, in multi-party protocols a peer can provide a value that allows them to steal. #[inline] - pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> TweakedPublicKey { + pub fn dangerous_assume_tweaked(key: ::XOnlyPublicKey) -> TweakedPublicKey { TweakedPublicKey(key) } /// Returns the underlying public key. - pub fn into_inner(self) -> XOnlyPublicKey { + pub fn into_inner(self) -> ::XOnlyPublicKey { self.0 } /// Returns a reference to underlying public key. - pub fn as_inner(&self) -> &XOnlyPublicKey { + pub fn as_inner(&self) -> &::XOnlyPublicKey { &self.0 } @@ -177,25 +186,25 @@ impl TweakedKeyPair { /// This method is dangerous and can lead to loss of funds if used incorrectly. /// Specifically, in multi-party protocols a peer can provide a value that allows them to steal. #[inline] - pub fn dangerous_assume_tweaked(pair: KeyPair) -> TweakedKeyPair { + pub fn dangerous_assume_tweaked(pair: ::KeyPair) -> TweakedKeyPair { TweakedKeyPair(pair) } /// Returns the underlying key pair #[inline] - pub fn into_inner(self) -> KeyPair { + pub fn into_inner(self) -> ::KeyPair { self.0 } } -impl From for XOnlyPublicKey { +impl From for ::XOnlyPublicKey { #[inline] fn from(pair: TweakedPublicKey) -> Self { pair.0 } } -impl From for KeyPair { +impl From for ::KeyPair { #[inline] fn from(pair: TweakedKeyPair) -> Self { pair.0