What changed, and why it matters
This commit is a large but straightforward internal code reorganization. It moves Bitcoin key types (public keys, private keys, keypairs, and related errors) from the main `bitcoin` crate into a new `crypto` sub-crate, then re-exports them so existing users of the `bitcoin` crate see the same public API. There is no indication of a security bug being fixed or introduced; it is a refactoring to improve the project's modular structure.
No security action required. Treat as a normal refactoring commit. Reviewers may optionally verify that the re-export surface matches the original API and that feature forwarding (especially `rand` and `serde`) is complete.
Security signals we found
Large-scale code move with no functional change
Re-exports preserve existing public API
No new input parsing or cryptographic operations introduced
No memory-safety or secret-handling changes observed
No vendor disclosure or advisory references present
Evidence from the diff
The patch relocates the bulk of bitcoin/src/crypto/key.rs to a new file crypto/src/key.rs, adds the necessary dependencies to crypto/Cargo.toml (base58, hashes, network, serde_test dev-dep), and updates bitcoin/Cargo.toml to forward the rand feature to crypto/rand. The bitcoin crate keeps a thin wrapper that re-exports the moved types and provides a few bitcoin-specific extension traits (FullPublicKeyExt, LegacyPublicKeyExt, PrivateKeyExt, TapTweak) and the legacy SerializedLegacyPublicKey type. The public API of bitcoin::key is preserved through re-exports. No cryptographic logic changes are visible in the diff.
Changed components
bitcoin/src/crypto/key.rscrypto/src/key.rscrypto/Cargo.tomlbitcoin/Cargo.tomlInspect captured patch +2433 / −2379
diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock
index e7326fd8..7383947b 100644
--- a/Cargo-minimal.lock
+++ b/Cargo-minimal.lock
@@ -92,12 +92,16 @@ name = "bitcoin-crypto"
version = "0.0.0"
dependencies = [
"arbitrary",
+ "base58ck",
"bitcoin-internals",
"bitcoin-io",
+ "bitcoin-network-kind",
+ "bitcoin_hashes",
"hex-conservative 0.3.2",
"hex-conservative 1.0.0",
"secp256k1",
"serde",
+ "serde_test",
]
[[package]]
diff --git a/Cargo-recent.lock b/Cargo-recent.lock
index f7c7ade9..cba702e9 100644
--- a/Cargo-recent.lock
+++ b/Cargo-recent.lock
@@ -91,12 +91,16 @@ name = "bitcoin-crypto"
version = "0.0.0"
dependencies = [
"arbitrary",
+ "base58ck",
"bitcoin-internals",
"bitcoin-io",
+ "bitcoin-network-kind",
+ "bitcoin_hashes",
"hex-conservative 0.3.2",
"hex-conservative 1.0.0",
"secp256k1",
"serde",
+ "serde_test",
]
[[package]]
diff --git a/bitcoin/Cargo.toml b/bitcoin/Cargo.toml
index 3b2522fb..9bd5631d 100644
--- a/bitcoin/Cargo.toml
+++ b/bitcoin/Cargo.toml
@@ -18,7 +18,7 @@ exclude = ["tests", "contrib"]
[features]
default = [ "std", "secp-recovery" ]
std = ["base58/std", "bech32/std", "crypto/std", "encoding/std", "hashes/std", "hex-stable/std", "hex-unstable/std", "internals/std", "io/std", "network/std", "primitives/std", "secp256k1/std", "units/std", "base64?/std", "bitcoinconsensus?/std"]
-rand = ["secp256k1/rand"]
+rand = ["secp256k1/rand", "crypto/rand"]
serde = ["base64", "crypto/serde", "dep:serde", "hashes/serde", "internals/serde", "network/serde", "primitives/serde", "secp256k1/serde", "units/serde"]
secp-global-context = ["secp256k1/global-context"]
secp-lowmemory = ["secp256k1/lowmemory"]
diff --git a/bitcoin/src/crypto/key.rs b/bitcoin/src/crypto/key.rs
index 0cf3a58d..a6af6021 100644
--- a/bitcoin/src/crypto/key.rs
+++ b/bitcoin/src/crypto/key.rs
@@ -6,284 +6,32 @@
//! (de)serialized.
use core::borrow::Borrow;
-use core::fmt;
-use core::str::FromStr;
-#[cfg(feature = "arbitrary")]
-use arbitrary::{Arbitrary, Unstructured};
-use hashes::hash160;
-use internals::array::ArrayExt;
-use internals::array_vec::ArrayVec;
-use internals::impl_to_hex_from_lower_hex;
-use io::{Read, Write};
-
-use crate::crypto::ecdsa;
-use crate::hex::{self, DecodeFixedLengthBytesError};
-use crate::internal_macros::{define_extension_trait, impl_asref_push_bytes};
-use crate::network::NetworkKind;
-use crate::prelude::{DisplayHex, String, Vec};
+use crate::internal_macros::define_extension_trait;
use crate::script::{self, PushBytes, WitnessScriptBuf};
-#[cfg(feature = "serde")]
-use crate::serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "secp-recovery")]
use crate::sign_message::MessageSignature;
use crate::taproot::{TapNodeHash, TapTweakHash};
#[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1::{constants, Parity, Verification};
-pub use serialized_legacy_public_key::SerializedLegacyPublicKey;
-pub use encapsulate::{
- FullPublicKey, Keypair, LegacyPublicKey, PrivateKey, SerializedXOnlyPublicKey, TweakedKeypair,
- TweakedPublicKey, XOnlyPublicKey,
-};
-#[cfg(feature = "rand")]
-#[cfg(feature = "std")]
-pub use secp256k1::rand;
-
#[doc(no_inline)]
-pub use self::error::{
+pub use crypto::key::{
FromSliceError, FromWifError, InvalidAddressVersionError, InvalidBase58PayloadLengthError,
- InvalidWifCompressionFlagError, ParseFullPublicKeyError, ParseKeypairError,
- ParsePublicKeyError, ParseXOnlyPublicKeyError, TweakXOnlyPublicKeyError,
- UncompressedPublicKeyError,
+ InvalidWifCompressionFlagError, ParseFullPublicKeyError, ParseXOnlyPublicKeyError,
+ TweakXOnlyPublicKeyError, UncompressedPublicKeyError,
};
-
-/// Encapsulation module to provide a clear barrier for construction/destruction of types.
-mod encapsulate {
- use secp256k1::Parity;
-
- /// A Bitcoin Schnorr X-only public key used for BIP-0340 signatures.
- ///
- /// This type also holds the parity of the full public key.
- #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- pub struct XOnlyPublicKey {
- inner: secp256k1::XOnlyPublicKey,
- parity: Parity,
- }
-
- impl XOnlyPublicKey {
- /// Constructs a new x-only public key from the provided secp256k1 x-only public key.
- pub fn from_secp(key: impl Into<secp256k1::XOnlyPublicKey>, parity: Parity) -> Self {
- Self { inner: key.into(), parity }
- }
-
- /// Sets the parity of this [`XOnlyPublicKey`].
- ///
- /// This returns a new `XOnlyPublicKey` with the same inner value, but the given parity.
- pub fn with_parity(self, parity: Parity) -> Self { Self { parity, ..self } }
-
- /// Returns the parity of this x-only public key.
- pub fn parity(&self) -> Parity { self.parity }
-
- /// Returns a reference to the inner secp256k1 x-only public key.
- #[inline]
- pub fn as_inner(&self) -> &secp256k1::XOnlyPublicKey { &self.inner }
-
- /// Returns the inner secp256k1 x-only public key.
- #[inline]
- pub fn to_inner(self) -> secp256k1::XOnlyPublicKey { self.inner }
-
- /// Returns the inner secp256k1 x-only public key.
- #[inline]
- #[deprecated(since = "TBD", note = "use `to_inner()` instead")]
- pub fn into_inner(self) -> secp256k1::XOnlyPublicKey { self.to_inner() }
- }
-
- /// A Bitcoin secret and public key pair.
- #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
- pub struct Keypair(secp256k1::Keypair);
-
- impl Keypair {
- /// Constructs a keypair from a provided secp256k1 keypair.
- #[inline]
- pub fn from_secp(keypair: impl Into<secp256k1::Keypair>) -> Self { Self(keypair.into()) }
-
- /// Returns a reference to the inner [`secp256k1::Keypair`].
- #[inline]
- pub(super) fn as_inner(&self) -> &secp256k1::Keypair { &self.0 }
- }
-
- /// A Bitcoin ECDSA public key.
- #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- pub struct LegacyPublicKey {
- /// Whether this public key should be serialized as compressed.
- compressed: bool,
- /// The actual ECDSA key.
- inner: secp256k1::PublicKey,
- }
-
- impl LegacyPublicKey {
- /// Constructs a new compressed ECDSA public key from the provided secp256k1 public key.
- pub fn from_secp(key: impl Into<secp256k1::PublicKey>) -> Self {
- Self { compressed: true, inner: key.into() }
- }
-
- /// Constructs a new uncompressed (legacy) ECDSA public key from the provided secp256k1 public
- /// key.
- pub fn from_secp_uncompressed(key: impl Into<secp256k1::PublicKey>) -> Self {
- Self { compressed: false, inner: key.into() }
- }
-
- /// Returns the inner secp256k1 public key.
- #[inline]
- pub fn to_inner(self) -> secp256k1::PublicKey { self.inner }
-
- /// Returns whether this public key should be serialized as compressed.
- #[inline]
- pub fn compressed(&self) -> bool { self.compressed }
- }
-
- impl Drop for Keypair {
- fn drop(&mut self) { self.0.non_secure_erase(); }
- }
-
- /// An always-compressed Bitcoin ECDSA public key.
- #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- pub struct FullPublicKey(secp256k1::PublicKey);
-
- impl FullPublicKey {
- /// Constructs a new compressed public key from the provided secp public key.
- #[inline]
- pub fn from_secp(inner: secp256k1::PublicKey) -> Self { Self(inner) }
-
- /// Returns the inner [`secp256k1::PublicKey`].
- #[inline]
- pub fn to_inner(self) -> secp256k1::PublicKey { self.0 }
- }
-
- /// A Bitcoin ECDSA private key.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct PrivateKey {
- /// Whether this private key should be serialized as compressed.
- compressed: bool,
- /// The actual ECDSA key.
- inner: secp256k1::SecretKey,
- }
-
- impl PrivateKey {
- /// Constructs a new compressed ECDSA private key from the provided secp256k1 private key.
- pub fn from_secp(key: secp256k1::SecretKey) -> Self {
- Self { compressed: true, inner: key }
- }
-
- /// Constructs a new uncompressed (legacy) ECDSA private key from the provided secp256k1
- /// private key.
- pub fn from_secp_uncompressed(key: secp256k1::SecretKey) -> Self {
- Self { compressed: false, inner: key }
- }
-
- /// Returns a reference to the inner secp256k1 secret key.
- #[inline]
- pub(super) fn as_inner(&self) -> &secp256k1::SecretKey { &self.inner }
-
- /// Returns whether this private key should be serialized as compressed.
- #[inline]
- pub fn compressed(&self) -> bool { self.compressed }
- }
-
- impl Drop for PrivateKey {
- fn drop(&mut self) { self.inner.non_secure_erase(); }
- }
-
- /// Tweaked BIP-0340 X-coord-only public key.
- #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
- #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
- #[cfg_attr(feature = "serde", serde(transparent))]
- pub struct TweakedPublicKey(XOnlyPublicKey);
-
- impl TweakedPublicKey {
- /// Returns the [`TweakedPublicKey`] for `keypair`.
- #[inline]
- pub fn from_keypair(keypair: &TweakedKeypair) -> Self {
- Self(keypair.as_keypair().to_x_only_public_key())
- }
-
- /// Constructs a new [`TweakedPublicKey`] from a [`XOnlyPublicKey`]. No tweak is applied, consider
- /// calling `tap_tweak` on an [`UntweakedPublicKey`] instead of using this constructor.
- ///
- /// 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.
- ///
- /// [`UntweakedPublicKey`]: super::UntweakedPublicKey
- #[inline]
- pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> Self { Self(key) }
-
- /// Returns the underlying x-only public key.
- #[inline]
- pub fn to_x_only_public_key(self) -> XOnlyPublicKey { self.0 }
-
- /// Returns a reference to the underlying x-only public key.
- #[inline]
- pub fn as_x_only_public_key(&self) -> &XOnlyPublicKey { &self.0 }
- }
-
- /// Tweaked BIP-0340 key pair.
- ///
- /// # Examples
- ///
- /// ```
- /// # #[cfg(feature = "rand")]
- /// # #[cfg(feature = "std")]
- /// # {
- /// # use bitcoin::key::{Keypair, TweakedKeypair, TweakedPublicKey};
- /// # let keypair = TweakedKeypair::dangerous_assume_tweaked(Keypair::generate());
- /// // There are various conversion methods available to get a tweaked pubkey from a tweaked keypair.
- /// let (_pk, _parity) = keypair.public_parts();
- /// let _pk = TweakedPublicKey::from_keypair(&keypair);
- /// let _pk = TweakedPublicKey::from(&keypair);
- /// # }
- /// ```
- #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
- #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
- #[cfg_attr(feature = "serde", serde(transparent))]
- pub struct TweakedKeypair(Keypair);
-
- impl TweakedKeypair {
- /// Constructs a new [`TweakedKeypair`] from a [`Keypair`]. No tweak is applied, consider
- /// calling `tap_tweak` on an [`UntweakedKeypair`](super::UntweakedKeypair) instead of using this constructor.
- ///
- /// 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) -> Self { Self(pair) }
-
- /// Returns the underlying key pair.
- #[inline]
- pub fn into_keypair(self) -> Keypair { self.0 }
-
- /// Returns a reference to the underlying key pair.
- #[inline]
- pub fn as_keypair(&self) -> &Keypair { &self.0 }
- }
-
- transparent_newtype! {
- /// An array of bytes that's semantically an x-only public but was **not** validated.
- ///
- /// This can be useful when validation is not desired but semantics of the bytes should be
- /// preserved. The validation can still happen using `to_validated()` method.
- #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
- pub struct SerializedXOnlyPublicKey([u8; 32]);
-
- impl SerializedXOnlyPublicKey {
- pub(crate) fn from_bytes_ref(bytes: &_) -> Self;
- }
- }
-
- impl SerializedXOnlyPublicKey {
- /// Marks the supplied bytes as a serialized x-only public key.
- pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self(bytes) }
-
- /// Returns the raw bytes.
- pub const fn to_byte_array(self) -> [u8; 32] { self.0 }
-
- /// Returns a reference to the raw bytes.
- pub const fn as_byte_array(&self) -> &[u8; 32] { &self.0 }
- }
-}
+#[doc(inline)]
+pub use crypto::key::{
+ FullPublicKey, Keypair, LegacyPublicKey, PrivateKey, PubkeyHash, SerializedXOnlyPublicKey,
+ TweakedKeypair, TweakedPublicKey, UntweakedKeypair, UntweakedPublicKey, WPubkeyHash, WifKey,
+ XOnlyPublicKey,
+};
+pub use serialized_legacy_public_key::SerializedLegacyPublicKey;
mod serialized_legacy_public_key {
use internals::array_vec::ArrayVec;
+
use crate::script::PushBytes;
/// A serialized form of `LegacyPublicKey`.
@@ -340,1712 +88,173 @@ impl Borrow<[u8]> for SerializedLegacyPublicKey {
}
}
-impl XOnlyPublicKey {
- /// Constructs an x-only public key from a keypair.
- ///
- /// Returns the x-only public key, with the relevant parity set from the full public key.
- #[inline]
- pub fn from_keypair(keypair: &Keypair) -> Self {
- let (xonly, parity) = secp256k1::XOnlyPublicKey::from_keypair(keypair.as_inner());
- Self::from_secp(xonly, parity)
- }
-
- /// Constructs an x-only public key from a 32-byte x-coordinate.
- ///
- /// # Errors
- ///
- /// Errors if the provided bytes don't represent a valid secp256k1 point x-coordinate.
- #[inline]
- pub fn from_byte_array(
- data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE],
- ) -> Result<Self, ParseXOnlyPublicKeyError> {
- secp256k1::XOnlyPublicKey::from_byte_array(*data)
- .map(|key| Self::from_secp(key, Parity::Even))
- .map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
- }
+#[deprecated(since = "TBD", note = "use `LegacyPublicKey` instead")]
+#[doc(hidden)]
+pub type PublicKey = LegacyPublicKey;
- /// Serializes the x-only public key as a byte-encoded x coordinate value (32 bytes).
- #[inline]
- pub fn serialize(&self) -> ([u8; constants::SCHNORR_PUBLIC_KEY_SIZE], Parity) {
- (self.as_inner().serialize(), self.parity())
- }
+#[deprecated(since = "TBD", note = "use `FullPublicKey` instead")]
+#[doc(hidden)]
+pub type CompressedPublicKey = FullPublicKey;
- /// Converts this x-only public key to a full public key.
- ///
- /// The [`LegacyPublicKey`] is constructed using the parity in this x-only public key.
- #[inline]
- pub fn to_public_key(self) -> LegacyPublicKey {
- self.as_inner().public_key(self.parity()).into()
+define_extension_trait! {
+ /// Extension functionality for the [`FullPublicKey`] type.
+ pub trait FullPublicKeyExt impl for FullPublicKey {
+ /// Returns the script code used to spend a P2WPKH input.
+ ///
+ /// While the type returned is [`WitnessScriptBuf`], this is **not** a witness script and
+ /// should not be used as one. It is a special template defined in BIP 143 which is used
+ /// in place of a witness script for purposes of sighash computation.
+ fn p2wpkh_script_code(&self) -> WitnessScriptBuf {
+ script::p2wpkh_script_code(self.wpubkey_hash())
+ }
}
+}
- /// Verifies that a tweak produced by [`XOnlyPublicKey::add_tweak`] was computed correctly.
- ///
- /// Should be called on the original untweaked key. Takes the tweaked key with its output parity from
- /// [`XOnlyPublicKey::add_tweak`] as input.
- #[inline]
- pub fn tweak_add_check(&self, tweaked_key: &Self, tweak: secp256k1::Scalar) -> bool {
- self.as_inner().tweak_add_check(tweaked_key.as_inner(), tweaked_key.parity(), tweak)
- }
+define_extension_trait! {
+ /// Extension functionality for the [`LegacyPublicKey`] type.
+ pub trait LegacyPublicKeyExt impl for LegacyPublicKey {
+ /// Returns the script code used to spend a P2WPKH input.
+ ///
+ /// While the type returned is [`WitnessScriptBuf`], this is **not** a witness script and
+ /// should not be used as one. It is a special template defined in BIP 143 which is used
+ /// in place of a witness script for purposes of sighash computation.
+ ///
+ /// # Errors
+ ///
+ /// Errors if this key is not compressed.
+ fn p2wpkh_script_code(&self) -> Result<WitnessScriptBuf, UncompressedPublicKeyError> {
+ let key = FullPublicKey::try_from(*self)?;
+ Ok(key.p2wpkh_script_code())
+ }
- /// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the given tweak to it.
- ///
- /// # Returns
- ///
- /// The newly tweaked key. This key has its parity set according to the parity following the
- /// tweak. This key should be provided to `tweak_add_check` which can be used to verify a tweak
- /// more efficiently than regenerating it and checking equality.
- ///
- /// # Errors
- ///
- /// If the resulting key would be invalid.
- #[inline]
- pub fn add_tweak(&self, tweak: &secp256k1::Scalar) -> Result<Self, TweakXOnlyPublicKeyError> {
- match self.as_inner().add_tweak(tweak) {
- Ok((xonly, parity)) => Ok(Self::from_secp(xonly, parity)),
- Err(secp256k1::Error::InvalidTweak) => Err(TweakXOnlyPublicKeyError::BadTweak),
- Err(secp256k1::Error::InvalidParityValue(_)) =>
- Err(TweakXOnlyPublicKeyError::ParityError),
- Err(_) => Err(TweakXOnlyPublicKeyError::ResultKeyInvalid),
+ /// Serializes the public key to bytes.
+ fn to_bytes(self) -> SerializedLegacyPublicKey {
+ if self.compressed() {
+ SerializedLegacyPublicKey::new_compressed(&self.serialize_compressed())
+ } else {
+ SerializedLegacyPublicKey::new_uncompressed(&self.serialize_uncompressed())
+ }
}
}
}
-impl FromStr for XOnlyPublicKey {
- type Err = ParseXOnlyPublicKeyError;
- fn from_str(s: &str) -> Result<Self, ParseXOnlyPublicKeyError> {
- secp256k1::XOnlyPublicKey::from_str(s)
- .map(Self::from)
- .map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
+#[cfg(feature = "secp-recovery")]
+define_extension_trait! {
+ /// Extension functionality for the [`PrivateKey`] type.
+ pub trait PrivateKeyExt impl for PrivateKey {
+ /// ECDSA signs a [`Message`] with this private key.
+ ///
+ /// This produces an ECDSA signature with a recovery ID for pubkey recovery.
+ /// See [`RecoverableSignature::sign_ecdsa_recoverable`] for details.
+ ///
+ /// [`Message`]: secp256k1::Message
+ /// [`RecoverableSignature::sign_ecdsa_recoverable`]: secp256k1::ecdsa::RecoverableSignature::sign_ecdsa_recoverable
+ #[inline]
+ fn raw_ecdsa_sign_recoverable(
+ &self,
+ msg: impl Into<secp256k1::Message>,
+ ) -> MessageSignature {
+ MessageSignature::new(
+ secp256k1::ecdsa::RecoverableSignature::sign_ecdsa_recoverable(msg, self.as_inner()),
+ self.compressed(),
+ )
+ }
}
}
-impl From<secp256k1::XOnlyPublicKey> for XOnlyPublicKey {
- fn from(pk: secp256k1::XOnlyPublicKey) -> Self { Self::from_secp(pk, Parity::Even) }
-}
-
-impl From<secp256k1::PublicKey> for XOnlyPublicKey {
- fn from(pk: secp256k1::PublicKey) -> Self {
- let (xonly, parity) = pk.x_only_public_key();
- Self::from_secp(xonly, parity)
- }
+mod sealed {
+ pub trait Sealed {}
+ impl Sealed for super::FullPublicKey {}
+ impl Sealed for super::LegacyPublicKey {}
+ impl Sealed for super::PrivateKey {}
}
-impl fmt::LowerHex for XOnlyPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_inner(), f) }
-}
-// Allocate for serialized size
-impl_to_hex_from_lower_hex!(XOnlyPublicKey, |_| constants::SCHNORR_PUBLIC_KEY_SIZE * 2);
+/// A trait for tweaking BIP-0340 key types (x-only public keys and key pairs).
+pub trait TapTweak {
+ /// Tweaked key type with optional auxiliary information.
+ type TweakedAux;
+ /// Tweaked key type.
+ type TweakedKey;
-impl fmt::Display for XOnlyPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_inner(), f) }
-}
+ /// Tweaks an untweaked key with corresponding public key value and optional script tree Merkle
+ /// root. For the [`Keypair`] type this also tweaks the private key in the pair.
+ ///
+ /// This is done by using the equation Q = P + H(P|c)G, where
+ /// * Q is the tweaked public key
+ /// * P is the internal public key
+ /// * H is the hash function
+ /// * c is the commitment data
+ /// * G is the generator point
+ ///
+ /// # Returns
+ ///
+ /// The tweaked key, with the required parity.
+ fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> Self::TweakedAux;
-// XOnlyPublicKey should serialize/deserialize identically to the inner type.
-#[cfg(feature = "serde")]
-impl Serialize for XOnlyPublicKey {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- <secp256k1::XOnlyPublicKey as Serialize>::serialize(self.as_inner(), serializer)
- }
+ /// Directly converts an [`UntweakedPublicKey`] to a [`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.
+ fn dangerous_assume_tweaked(self) -> Self::TweakedKey;
}
-#[cfg(feature = "serde")]
-impl<'de> Deserialize<'de> for XOnlyPublicKey {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- Ok(Self::from_secp(secp256k1::XOnlyPublicKey::deserialize(deserializer)?, Parity::Even))
- }
-}
+impl TapTweak for UntweakedPublicKey {
+ type TweakedAux = TweakedPublicKey;
+ type TweakedKey = TweakedPublicKey;
-impl Keypair {
- /// Generates a new random key pair.
+ /// Tweaks an untweaked public key with corresponding public key value and optional script tree
+ /// Merkle root.
///
- /// # Examples
+ /// This is done by using the equation Q = P + H(P|c)G, where
+ /// * Q is the tweaked public key
+ /// * P is the internal public key
+ /// * H is the hash function
+ /// * c is the commitment data
+ /// * G is the generator point
///
- /// ```
- /// # #[cfg(feature = "rand")]
- /// # #[cfg(feature = "std")]
- /// # {
- /// use bitcoin::Keypair;
+ /// # Returns
///
- /// let keypair = Keypair::generate();
- /// # }
- /// ```
- #[inline]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- pub fn generate() -> Self {
- let kp = secp256k1::Keypair::new(&mut rand::rng());
- Self::from_secp(kp)
- }
+ /// The tweaked key and its parity.
+ fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> TweakedPublicKey {
+ let tweak = TapTweakHash::from_key_and_merkle_root(self, merkle_root).to_scalar();
+ let output_key = self.add_tweak(&tweak).expect("Tap tweak failed");
- /// Constructs a [`Keypair`] from a [`PrivateKey`].
- #[inline]
- pub fn from_private_key(pk: &PrivateKey) -> Self {
- Self::from(secp256k1::Keypair::from_secret_key(pk.as_inner()))
+ debug_assert!(self.tweak_add_check(&output_key, tweak));
+ TweakedPublicKey::dangerous_assume_tweaked(output_key)
}
- /// Returns a compressed [`PrivateKey`] for this [`Keypair`].
- #[inline]
- pub fn to_private_key(&self) -> PrivateKey {
- PrivateKey::from_secp(secp256k1::SecretKey::from_keypair(self.as_inner()))
+ fn dangerous_assume_tweaked(self) -> TweakedPublicKey {
+ TweakedPublicKey::dangerous_assume_tweaked(self)
}
+}
- /// Returns the secret bytes for this [`Keypair`].
- #[inline]
- pub fn to_secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] {
- self.as_inner().to_secret_bytes()
- }
+impl TapTweak for UntweakedKeypair {
+ type TweakedAux = TweakedKeypair;
+ type TweakedKey = TweakedKeypair;
- /// Returns the [`LegacyPublicKey`] for this [`Keypair`].
+ /// Applies a Taproot tweak to both keys within the keypair.
///
- /// This is equivalent to using [`LegacyPublicKey::from_keypair`].
- #[inline]
- pub fn to_public_key(&self) -> LegacyPublicKey { LegacyPublicKey::from_keypair(self) }
-
- /// Returns the [`XOnlyPublicKey`] for this [`Keypair`].
+ /// If `merkle_root` is provided, produces a Taproot key that can be spent by any
+ /// of the script paths committed to by the root. If it is not provided, produces
+ /// a Taproot key which can [provably only be spent via
+ /// keyspend](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23).
///
- /// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
- #[inline]
- pub fn to_x_only_public_key(&self) -> XOnlyPublicKey { XOnlyPublicKey::from_keypair(self) }
-
- /// Schnorr sign a message slice with this keypair.
- ///
- /// If the `rand` and `std` features are enabled, this function will randomly seed auxiliary
- /// data. Otherwise, this will use no auxiliary data.
- #[inline]
- pub fn raw_bip340_sign(&self, msg: &[u8]) -> secp256k1::schnorr::Signature {
- #[cfg(not(all(feature = "rand", feature = "std")))]
- {
- secp256k1::schnorr::sign_no_aux_rand(msg, self.as_inner())
- }
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- {
- secp256k1::schnorr::sign(msg, self.as_inner())
- }
- }
-
- /// Schnorr sign a message slice with this keypair, using provided auxiliary random data.
- #[inline]
- pub fn raw_bip340_sign_with_aux_randomness(
- &self,
- msg: &[u8],
- aux_rand: &[u8; 32],
- ) -> secp256k1::schnorr::Signature {
- secp256k1::schnorr::sign_with_aux_rand(msg, self.as_inner(), aux_rand)
- }
-}
-
-impl FromStr for Keypair {
- type Err = ParseKeypairError;
- fn from_str(s: &str) -> Result<Self, ParseKeypairError> {
- secp256k1::Keypair::from_str(s).map(Self::from).map_err(ParseKeypairError)
- }
-}
-
-impl From<secp256k1::Keypair> for Keypair {
- fn from(pk: secp256k1::Keypair) -> Self { Self::from_secp(pk) }
-}
-
-impl From<Keypair> for secp256k1::PublicKey {
- fn from(kp: Keypair) -> Self { kp.to_public_key().to_inner() }
-}
-
-impl From<PrivateKey> for Keypair {
- fn from(pk: PrivateKey) -> Self { Self::from(&pk) }
-}
-
-impl From<&PrivateKey> for Keypair {
- fn from(pk: &PrivateKey) -> Self { Self::from_private_key(pk) }
-}
-
-#[deprecated(since = "TBD", note = "use `LegacyPublicKey` instead")]
-#[doc(hidden)]
-pub type PublicKey = LegacyPublicKey;
-
-impl LegacyPublicKey {
- /// Constructs a new compressed ECDSA public key from the provided generic secp256k1 public key.
- #[deprecated(since = "TBD", note = "use `from_secp` instead")]
- pub fn new(key: impl Into<secp256k1::PublicKey>) -> Self { Self::from_secp(key) }
-
- /// Constructs a new uncompressed (legacy) ECDSA public key from the provided generic secp256k1
- /// public key.
- #[deprecated(since = "TBD", note = "use `from_secp_uncompressed` instead")]
- pub fn new_uncompressed(key: impl Into<secp256k1::PublicKey>) -> Self {
- Self::from_secp_uncompressed(key)
- }
-
- /// Serializes the key as a byte-encoded pair of values.
- ///
- /// This will call the provided function with the key as a byte slice in either
- /// compressed or uncompressed form.
- ///
- /// See [`LegacyPublicKey::serialize_compressed`] and [`LegacyPublicKey::serialize_uncompressed`]
- /// for more information on the byte formats for the key.
- ///
- /// # Examples
- ///
- /// ```
- /// use bitcoin::LegacyPublicKey;
- /// use bitcoin::hashes::hash160;
- ///
- /// let key = "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
- /// .parse::<LegacyPublicKey>()
- /// .unwrap();
- /// assert!(key.compressed());
- /// let vec_out = key.with_serialized(<[_]>::to_vec);
- /// assert_eq!(vec_out.len(), 33);
- ///
- /// let hash = key.with_serialized(hash160::Hash::hash).to_string();
- /// assert_eq!(hash, "dabedb4de2bd2bfec5d38475b9c64af13999a043");
- /// ```
- pub fn with_serialized<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
- if self.compressed() {
- f(&self.serialize_compressed())
- } else {
- f(&self.serialize_uncompressed())
- }
- }
-
- /// Serializes the key as a byte-encoded pair of values.
- ///
- /// This function serializes the key in compressed form, where the y-coordinate is
- /// represented by only a single bit, as x determines it up to one bit.
- ///
- /// If you want to serialize while considering the compressedness of this key,
- /// use [`with_serialized`] instead.
- ///
- /// [`with_serialized`]: LegacyPublicKey::with_serialized
- pub fn serialize_compressed(&self) -> [u8; 33] { self.to_inner().serialize() }
-
- /// Serializes the key as a byte-encoded pair of values, in uncompressed form.
- ///
- /// If you want to serialize while considering the compressedness of this key,
- /// use [`with_serialized`] instead.
- ///
- /// [`with_serialized`]: LegacyPublicKey::with_serialized
- pub fn serialize_uncompressed(&self) -> [u8; 65] { self.to_inner().serialize_uncompressed() }
-
- /// Returns bitcoin 160-bit hash of the public key.
- pub fn pubkey_hash(&self) -> PubkeyHash {
- PubkeyHash(self.with_serialized(hash160::Hash::hash))
- }
-
- /// Returns bitcoin 160-bit hash of the public key for witness program
- ///
- /// # Errors
- ///
- /// Errors if this key is not compressed.
- pub fn wpubkey_hash(&self) -> Result<WPubkeyHash, UncompressedPublicKeyError> {
- if self.compressed() {
- Ok(WPubkeyHash::from_byte_array(
- hash160::Hash::hash(&self.to_inner().serialize()).to_byte_array(),
- ))
- } else {
- Err(UncompressedPublicKeyError)
- }
- }
-
- /// Converts this [`LegacyPublicKey`] into a [`FullPublicKey`] infallibly.
- ///
- /// Unlike the `TryFrom` implementation, this function will discard compressedness
- /// information on the [`LegacyPublicKey`].
- pub fn force_compressed(self) -> FullPublicKey { FullPublicKey::from_secp(self.to_inner()) }
-
- /// Writes the public key into a writer.
- ///
- /// # Errors
- ///
- /// Errors if the bytes fail to write to the provided writer.
- pub fn write_into<W: Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
- self.with_serialized(|bytes| writer.write_all(bytes))
- }
-
- /// Reads the public key from a reader.
- ///
- /// This internally reads the first byte before reading the rest, so
- /// use of a `BufReader` is recommended.
- ///
- /// # Errors
- ///
- /// Errors if the reader fails to read, or the read bytes are not a valid public key.
- pub fn read_from<R: Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error> {
- 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 no-std io
- #[cfg(feature = "std")]
- let reason = e;
- #[cfg(not(feature = "std"))]
- let reason = match e {
- FromSliceError::Secp256k1(_) => "secp256k1 error",
- FromSliceError::InvalidKeyPrefix(_) => "invalid key prefix",
- FromSliceError::InvalidLength(_) => "invalid length",
- };
- io::Error::new(io::ErrorKind::InvalidData, reason)
- })
- }
-
- /// Serializes the public key to bytes.
- #[allow(clippy::missing_panics_doc)]
- pub fn to_vec(self) -> Vec<u8> {
- let mut buf = Vec::new();
- self.write_into(&mut buf).expect("vecs don't error");
- buf
- }
-
- /// Serializes the public key into a `SortKey`.
- ///
- /// `SortKey` is not too useful by itself, but it can be used to sort a
- /// `[LegacyPublicKey]` slice using `sort_unstable_by_key`, `sort_by_cached_key`,
- /// `sort_by_key`, or any of the other `*_by_key` methods on slice.
- /// Pass the method into the sort method directly. (ie. `LegacyPublicKey::to_sort_key`)
- ///
- /// This method of sorting is in line with Bitcoin Core's implementation of
- /// sorting keys for output descriptors such as `sortedmulti()`.
- ///
- /// If every `LegacyPublicKey` in the slice is `compressed == true` then this will sort
- /// the keys in a
- /// [BIP-0067](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki)
- /// compliant way.
- ///
- /// # Example: Using with `sort_unstable_by_key`
- ///
- /// ```rust
- /// use bitcoin::LegacyPublicKey;
- ///
- /// let pk = |s: &str| s.parse::<LegacyPublicKey>().unwrap();
- ///
- /// let mut unsorted = [
- /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
- /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
- /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
- /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
- /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
- /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
- /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
- /// ];
- /// let sorted = [
- /// // These first 4 keys are in a BIP-0067 compatible sorted order
- /// // (since they are compressed)
- /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
- /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
- /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
- /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
- /// // Uncompressed keys are not BIP-0067 compliant, but are sorted
- /// // after compressed keys in Bitcoin Core using `sortedmulti()`
- /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
- /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
- /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
- /// ];
- ///
- /// unsorted.sort_unstable_by_key(|k| LegacyPublicKey::to_sort_key(*k));
- ///
- /// assert_eq!(unsorted, sorted);
- /// ```
- pub fn to_sort_key(self) -> SortKey {
- let buf = self.with_serialized(ArrayVec::from_slice);
- SortKey(buf)
- }
-
- /// Deserializes a public key from a slice.
- ///
- /// # Errors
- ///
- /// * [`FromSliceError::InvalidLength`] if the slice has an invalid number of bytes.
- /// * [`FromSliceError::InvalidKeyPrefix`] if the key prefix is invalid.
- /// * [`FromSliceError::Secp256k1`] if the provided bytes do not form a valid public key.
- pub fn from_slice(data: &[u8]) -> Result<Self, FromSliceError> {
- let compressed = match data.len() {
- 33 => true,
- 65 => false,
- len => {
- return Err(FromSliceError::InvalidLength(len));
- }
- };
-
- // Compressed keys must have a prefix byte of 2 or 3. Uncompressed must be 4
- match (compressed, data[0]) {
- (true, 0x02) => (),
- (true, 0x03) => (),
- (false, 0x04) => (),
- (_, byte) => return Err(FromSliceError::InvalidKeyPrefix(byte)),
- }
-
- Ok(match compressed {
- true => Self::from_secp(secp256k1::PublicKey::from_slice(data)?),
- false => Self::from_secp_uncompressed(secp256k1::PublicKey::from_slice(data)?),
- })
- }
-
- /// Computes the public key as supposed to be used with this secret.
- pub fn from_private_key(sk: &PrivateKey) -> Self { sk.to_public_key() }
-
- /// Extracts the public key from a Keypair
- pub fn from_keypair(pair: &Keypair) -> Self { FullPublicKey::from_keypair(pair).into() }
-
- /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key.
- ///
- /// # Errors
- ///
- /// [`secp256k1::Error::InvalidSignature`] if the signature is not valid for the given
- /// [`Message`].
- ///
- /// [`Message`]: secp256k1::Message
- pub fn verify(
- &self,
- msg: secp256k1::Message,
- sig: ecdsa::Signature,
- ) -> Result<(), secp256k1::Error> {
- secp256k1::ecdsa::verify(&sig.signature, msg, &self.to_inner())
- }
-}
-
-define_extension_trait! {
- /// Extension functionality for the [`LegacyPublicKey`] type.
- pub trait LegacyPublicKeyExt impl for LegacyPublicKey {
- /// Returns the script code used to spend a P2WPKH input.
- ///
- /// While the type returned is [`WitnessScriptBuf`], this is **not** a witness script and
- /// should not be used as one. It is a special template defined in BIP 143 which is used
- /// in place of a witness script for purposes of sighash computation.
- ///
- /// # Errors
- ///
- /// Errors if this key is not compressed.
- fn p2wpkh_script_code(&self) -> Result<WitnessScriptBuf, UncompressedPublicKeyError> {
- let key = FullPublicKey::try_from(*self)?;
- Ok(key.p2wpkh_script_code())
- }
-
- /// Serializes the public key to bytes.
- fn to_bytes(self) -> SerializedLegacyPublicKey {
- if self.compressed() {
- SerializedLegacyPublicKey::new_compressed(&self.serialize_compressed())
- } else {
- SerializedLegacyPublicKey::new_uncompressed(&self.serialize_uncompressed())
- }
- }
- }
-}
-
-impl From<secp256k1::PublicKey> for LegacyPublicKey {
- fn from(pk: secp256k1::PublicKey) -> Self { Self::from_secp(pk) }
-}
-
-impl From<LegacyPublicKey> for XOnlyPublicKey {
- fn from(pk: LegacyPublicKey) -> Self {
- let (xonly, parity) = pk.to_inner().x_only_public_key();
- Self::from_secp(xonly, parity)
- }
-}
-
-/// An opaque return type for [`LegacyPublicKey::to_sort_key`].
-#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
-pub struct SortKey(ArrayVec<u8, 65>);
-
-impl fmt::Display for LegacyPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.with_serialized(|bytes| fmt::Display::fmt(&bytes.as_hex(), f))
- }
-}
-
-impl FromStr for LegacyPublicKey {
- type Err = ParsePublicKeyError;
- fn from_str(s: &str) -> Result<Self, ParsePublicKeyError> {
- match s.len() {
- 66 => {
- let bytes = hex::decode_to_array::<33>(s).map_err(|e| match e {
- DecodeFixedLengthBytesError::InvalidChar(e) =>
- ParsePublicKeyError::InvalidChar(e),
- DecodeFixedLengthBytesError::InvalidLength(_) =>
- unreachable!("length checked already"),
- })?;
- Ok(Self::from_slice(&bytes)?)
- }
- 130 => {
- let bytes = hex::decode_to_array::<65>(s).map_err(|e| match e {
- DecodeFixedLengthBytesError::InvalidChar(e) =>
- ParsePublicKeyError::InvalidChar(e),
- DecodeFixedLengthBytesError::InvalidLength(_) =>
- unreachable!("length checked already"),
- })?;
- Ok(Self::from_slice(&bytes)?)
- }
- len => Err(ParsePublicKeyError::InvalidHexLength(len)),
- }
- }
-}
-
-hashes::hash_newtype! {
- /// A hash of a public key.
- pub struct PubkeyHash(hash160::Hash);
- /// SegWit version of a public key hash.
- pub struct WPubkeyHash(hash160::Hash);
-}
-
-hashes::impl_hex_for_newtype!(PubkeyHash, WPubkeyHash);
-#[cfg(feature = "serde")]
-hashes::impl_serde_for_newtype!(PubkeyHash, WPubkeyHash);
-
-impl_asref_push_bytes!(PubkeyHash, WPubkeyHash);
-
-impl From<LegacyPublicKey> for PubkeyHash {
- fn from(key: LegacyPublicKey) -> Self { key.pubkey_hash() }
-}
-
-impl From<&LegacyPublicKey> for PubkeyHash {
- fn from(key: &LegacyPublicKey) -> Self { key.pubkey_hash() }
-}
-
-#[deprecated(since = "TBD", note = "use `FullPublicKey` instead")]
-#[doc(hidden)]
-pub type CompressedPublicKey = FullPublicKey;
-
-impl FullPublicKey {
- /// Returns bitcoin 160-bit hash of the public key.
- pub fn pubkey_hash(&self) -> PubkeyHash { PubkeyHash(hash160::Hash::hash(&self.to_bytes())) }
-
- /// Returns bitcoin 160-bit hash of the public key for witness program.
- pub fn wpubkey_hash(&self) -> WPubkeyHash {
- WPubkeyHash::from_byte_array(hash160::Hash::hash(&self.to_bytes()).to_byte_array())
- }
-
- /// Writes the public key into a writer.
- ///
- /// # Errors
- ///
- /// Errors if the bytes fail to write to the provided writer.
- pub fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
- writer.write_all(&self.to_bytes())
- }
-
- /// Reads the public key from a reader.
- ///
- /// This internally reads the first byte before reading the rest, so
- /// use of a `BufReader` is recommended.
- ///
- /// # Errors
- ///
- /// Errors if the reader fails to read, or the read bytes are not a valid public key.
- pub fn read_from<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error> {
- let mut bytes = [0; 33];
-
- reader.read_exact(&mut bytes)?;
- #[allow(unused_variables)] // e when std not enabled
- Self::from_bytes(bytes).map_err(|e| {
- // Need a static string for no-std io
- #[cfg(feature = "std")]
- let reason = e;
- #[cfg(not(feature = "std"))]
- let reason = "secp256k1 error";
- io::Error::new(io::ErrorKind::InvalidData, reason)
- })
- }
-
- /// Serializes the public key.
- ///
- /// As the type name suggests, the key is serialized in compressed format.
- ///
- /// Note that this can be used as a sort key to get BIP-0067-compliant sorting.
- /// That's why this type doesn't have the `to_sort_key` method - it would duplicate this one.
- pub fn to_bytes(self) -> [u8; 33] { self.to_inner().serialize() }
-
- /// Deserializes a public key from a slice.
- ///
- /// # Errors
- ///
- /// See [`secp256k1::PublicKey::from_slice`].
- #[deprecated(
- since = "TBD",
- note = "use `from_bytes` instead; if you only have a slice, use `<&[u8; 33]>::try_from` first"
- )]
- pub fn from_slice(data: &[u8]) -> Result<Self, secp256k1::Error> {
- let bytes_arr = data.try_into().map_err(|_| secp256k1::Error::InvalidPublicKey)?;
- Self::from_bytes(bytes_arr)
- }
-
- /// Deserializes a public key from compressed pubkey bytes.
- ///
- /// # Errors
- ///
- /// See [`secp256k1::PublicKey::from_byte_array_compressed`].
- pub fn from_bytes(data: [u8; 33]) -> Result<Self, secp256k1::Error> {
- secp256k1::PublicKey::from_byte_array_compressed(data).map(Self::from_secp)
- }
-
- /// Computes the public key as supposed to be used with this secret.
- ///
- /// # Errors
- ///
- /// Errors if the private key is not compressed.
- pub fn from_private_key(sk: &PrivateKey) -> Result<Self, UncompressedPublicKeyError> {
- sk.to_public_key().try_into()
- }
-
- /// Extracts the public key from a Keypair
- pub fn from_keypair(pair: &Keypair) -> Self {
- Self::from_secp(secp256k1::PublicKey::from_keypair(pair.as_inner()))
- }
-
- /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key.
- ///
- /// # Errors
- ///
- /// See [`LegacyPublicKey::verify`].
- pub fn verify(
- &self,
- msg: secp256k1::Message,
- sig: ecdsa::Signature,
- ) -> Result<(), secp256k1::Error> {
- Ok(secp256k1::ecdsa::verify(&sig.signature, msg, &self.to_inner())?)
- }
-}
-
-define_extension_trait! {
- /// Extension functionality for the [`FullPublicKey`] type.
- pub trait FullPublicKeyExt impl for FullPublicKey {
- /// Returns the script code used to spend a P2WPKH input.
- ///
- /// While the type returned is [`WitnessScriptBuf`], this is **not** a witness script and
- /// should not be used as one. It is a special template defined in BIP 143 which is used
- /// in place of a witness script for purposes of sighash computation.
- fn p2wpkh_script_code(&self) -> WitnessScriptBuf {
- script::p2wpkh_script_code(self.wpubkey_hash())
- }
- }
-}
-
-impl fmt::Display for FullPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.to_bytes().as_hex(), f)
- }
-}
-
-impl fmt::Debug for FullPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_fmt(format_args!("FullPublicKey({})", self))
- }
-}
-
-impl FromStr for FullPublicKey {
- type Err = ParseFullPublicKeyError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- Self::from_bytes(hex::decode_to_array::<33>(s)?).map_err(Into::into)
- }
-}
-
-impl TryFrom<LegacyPublicKey> for FullPublicKey {
- type Error = UncompressedPublicKeyError;
-
- fn try_from(value: LegacyPublicKey) -> Result<Self, Self::Error> {
- if value.compressed() {
- Ok(Self::from_secp(value.to_inner()))
- } else {
- Err(UncompressedPublicKeyError)
- }
- }
-}
-
-impl From<secp256k1::PublicKey> for FullPublicKey {
- fn from(pk: secp256k1::PublicKey) -> Self { Self::from_secp(pk) }
-}
-
-impl From<FullPublicKey> for LegacyPublicKey {
- fn from(value: FullPublicKey) -> Self { Self::from_secp(value.to_inner()) }
-}
-
-impl From<FullPublicKey> for XOnlyPublicKey {
- fn from(pk: FullPublicKey) -> Self { pk.to_inner().into() }
-}
-
-impl From<FullPublicKey> for PubkeyHash {
- fn from(key: FullPublicKey) -> Self { key.pubkey_hash() }
-}
-
-impl From<&FullPublicKey> for PubkeyHash {
- fn from(key: &FullPublicKey) -> Self { key.pubkey_hash() }
-}
-
-impl From<FullPublicKey> for WPubkeyHash {
- fn from(key: FullPublicKey) -> Self { key.wpubkey_hash() }
-}
-
-impl From<&FullPublicKey> for WPubkeyHash {
- fn from(key: &FullPublicKey) -> Self { key.wpubkey_hash() }
-}
-
-mod sealed {
- pub trait Sealed {}
- impl Sealed for super::FullPublicKey {}
- impl Sealed for super::LegacyPublicKey {}
- impl Sealed for super::PrivateKey {}
-}
-
-impl PrivateKey {
- /// Constructs a new compressed ECDSA private key using the secp256k1 algorithm and
- /// a secure random number generator.
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- pub fn generate() -> Self {
- let secret_key = secp256k1::SecretKey::new(&mut rand::rng());
- Self::from_secp(secret_key)
- }
-
- /// Constructs a new public key from this private key.
- pub fn to_public_key(&self) -> LegacyPublicKey {
- match self.compressed() {
- true =>
- LegacyPublicKey::from_secp(secp256k1::PublicKey::from_secret_key(self.as_inner())),
- false => LegacyPublicKey::from_secp_uncompressed(
- secp256k1::PublicKey::from_secret_key(self.as_inner()),
- ),
- }
- }
-
- /// Constructs a new public key from this private key.
- #[deprecated(since = "TBD", note = "use `to_public_key` instead")]
- pub fn public_key(&self) -> LegacyPublicKey { self.to_public_key() }
-
- /// Serializes the private key to bytes.
- #[deprecated(since = "TBD", note = "use to_secret_vec instead")]
- pub fn to_bytes(&self) -> Vec<u8> { self.to_secret_vec() }
-
- /// Serializes the private key to bytes.
- pub fn to_secret_vec(&self) -> Vec<u8> { self.to_secret_bytes().to_vec() }
-
- /// Serializes the private key to bytes.
- pub fn to_secret_bytes(&self) -> [u8; 32] { self.as_inner().to_secret_bytes() }
-
- /// Deserializes a private key from a byte array.
- ///
- /// # Errors
- ///
- /// Errors when the secret key is invalid: when it is all-zeros or would exceed
- /// the curve order when interpreted as a big-endian unsigned integer.
- pub fn from_secret_bytes(data: &[u8; 32]) -> Result<Self, secp256k1::Error> {
- Ok(Self::from_secp(secp256k1::SecretKey::from_secret_bytes(*data)?))
- }
-
- /// Deserializes a private key from a slice.
- ///
- /// # Errors
- ///
- /// [`secp256k1::Error::InvalidSecretKey`] if the slice is not 32 bytes long.
- /// See [`from_secret_bytes`] for other errors.
- ///
- /// [`from_secret_bytes`]: PrivateKey::from_secret_bytes
- #[deprecated(since = "TBD", note = "use from_secret_bytes instead")]
- pub fn from_slice(
- data: &[u8],
- _network: impl Into<NetworkKind>,
- ) -> Result<Self, secp256k1::Error> {
- let array = data.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)?;
- Self::from_secret_bytes(array)
- }
-
- /// Returns a new private key with the negated secret value.
- ///
- /// The resulting key corresponds to the same x-only public key (identical x-coordinate)
- /// but with the opposite y-coordinate parity. This is useful for ensuring compatibility
- /// with specific public key formats and BIP-0340 requirements.
- #[inline]
- pub fn negate(&self) -> Self {
- match self.compressed() {
- true => Self::from_secp(self.as_inner().negate()),
- false => Self::from_secp_uncompressed(self.as_inner().negate()),
- }
- }
-
- /// ECDSA signs a [`Message`] with this private key.
- ///
- /// This functions grinds the nonce to produce a signature less than 71 bytes and compatible
- /// with the low r signature implementation of bitcoin core.
- ///
- /// See [`secp256k1::ecdsa::sign_low_r`] for details.
- ///
- /// [`Message`]: secp256k1::Message
- #[inline]
- pub fn raw_ecdsa_sign(
- &self,
- msg: impl Into<secp256k1::Message>,
- ) -> secp256k1::ecdsa::Signature {
- secp256k1::ecdsa::sign_low_r(msg, self.as_inner())
- }
-}
-
-#[cfg(feature = "secp-recovery")]
-define_extension_trait! {
- /// Extension functionality for the [`PrivateKey`] type.
- pub trait PrivateKeyExt impl for PrivateKey {
- /// ECDSA signs a [`Message`] with this private key.
- ///
- /// This produces an ECDSA signature with a recovery ID for pubkey recovery.
- /// See [`RecoverableSignature::sign_ecdsa_recoverable`] for details.
- ///
- /// [`Message`]: secp256k1::Message
- /// [`RecoverableSignature::sign_ecdsa_recoverable`]: secp256k1::ecdsa::RecoverableSignature::sign_ecdsa_recoverable
- #[inline]
- fn raw_ecdsa_sign_recoverable(
- &self,
- msg: impl Into<secp256k1::Message>,
- ) -> MessageSignature {
- MessageSignature::new(
- secp256k1::ecdsa::RecoverableSignature::sign_ecdsa_recoverable(msg, self.as_inner()),
- self.compressed(),
- )
- }
- }
-}
-
-/// A Bitcoin ECDSA private key with known network for WIF.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct WifKey {
- /// The actual key
- pub private_key: PrivateKey,
- /// The network kind on which this key should be used.
- pub network_kind: NetworkKind,
-}
-
-impl WifKey {
- /// Constructs a new WIF private key from the provided [`PrivateKey`] and the
- /// specified network.
- pub fn new(key: PrivateKey, network: impl Into<NetworkKind>) -> Self {
- Self { network_kind: network.into(), private_key: key }
- }
-
- /// Formats the private key to WIF format.
- ///
- /// # Errors
- ///
- /// Errors if `fmt` cannot be written to.
- #[rustfmt::skip]
- pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
- let mut ret = [0; 34];
- ret[0] = if self.network_kind.is_mainnet() { 128 } else { 239 };
-
- ret[1..33].copy_from_slice(&self.private_key.as_inner()[..]);
- let privkey = if self.private_key.compressed() {
- ret[33] = 1;
- base58::encode_check(&ret[..])
- } else {
- base58::encode_check(&ret[..33])
- };
- fmt.write_str(&privkey)
- }
-
- /// Gets the WIF encoding of this private key.
- pub fn to_wif(&self) -> String {
- let mut buf = String::new();
- let _ = self.fmt_wif(&mut buf);
- buf.shrink_to_fit();
- buf
- }
-
- /// Parses the WIF encoded private key.
- ///
- /// # Errors
- ///
- /// * [`FromWifError::Base58`] if the string is not a valid base58 encoded string.
- /// * [`FromWifError::InvalidBase58PayloadLength`] if the decoded base58 data is not 33 or 34
- /// bytes long.
- /// * [`FromWifError::InvalidWifCompressionFlag`] if the compression flag is not 1 for a 34 byte
- /// data string.
- /// * [`FromWifError::InvalidAddressVersion`] if the network version byte is not main or testnet.
- /// * [`FromWifError::Secp256k1`] if the bytes are not representative of a valid private key.
- pub fn from_wif(wif: &str) -> Result<Self, FromWifError> {
- let data = base58::decode_check(wif)?;
-
- let (compressed, data) = if let Ok(data) = <&[u8; 33]>::try_from(&*data) {
- (false, data)
- } else if let Ok(data) = <&[u8; 34]>::try_from(&*data) {
- let (compressed_flag, data) = data.split_last::<33>();
- if *compressed_flag != 1 {
- return Err(InvalidWifCompressionFlagError { invalid: *compressed_flag }.into());
- }
- (true, data)
- } else {
- return Err(InvalidBase58PayloadLengthError { length: data.len() }.into());
- };
-
- let (network, key) = data.split_first();
- let network = match *network {
- 128 => NetworkKind::Main,
- 239 => NetworkKind::Test,
- invalid => {
- return Err(InvalidAddressVersionError { invalid }.into());
- }
- };
-
- let sec_key = secp256k1::SecretKey::from_secret_bytes(*key)?;
- let priv_key = match compressed {
- true => PrivateKey::from_secp(sec_key),
- false => PrivateKey::from_secp_uncompressed(sec_key),
- };
- Ok(Self::new(priv_key, network))
- }
-}
-
-// [`WifKey`] intentionally has a `FromStr` without a reciprocal `Display`.
-// Parsing from a WIF string should be convenient, printing secret data should not.
-impl FromStr for WifKey {
- type Err = FromWifError;
- fn from_str(s: &str) -> Result<Self, FromWifError> { Self::from_wif(s) }
-}
-
-#[cfg(feature = "serde")]
-impl serde::Serialize for WifKey {
- fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
- s.serialize_str(&self.to_wif())
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> serde::Deserialize<'de> for WifKey {
- fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
- struct WifVisitor;
-
- impl serde::de::Visitor<'_> for WifVisitor {
- type Value = WifKey;
-
- fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
- formatter.write_str("an ASCII WIF string")
- }
-
- fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- if let Ok(s) = core::str::from_utf8(v) {
- s.parse::<WifKey>().map_err(E::custom)
- } else {
- Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
- }
- }
-
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- v.parse::<WifKey>().map_err(E::custom)
- }
- }
-
- d.deserialize_str(WifVisitor)
- }
-}
-
-#[cfg(feature = "serde")]
-#[allow(clippy::collapsible_else_if)] // Aids readability.
-impl serde::Serialize for LegacyPublicKey {
- fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
- if s.is_human_readable() {
- s.collect_str(self)
- } else {
- self.with_serialized(|bytes| s.serialize_bytes(bytes))
- }
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> serde::Deserialize<'de> for LegacyPublicKey {
- fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
- if d.is_human_readable() {
- struct HexVisitor;
-
- impl serde::de::Visitor<'_> for HexVisitor {
- type Value = LegacyPublicKey;
-
- fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
- formatter.write_str("an ASCII hex string")
- }
-
- fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- if let Ok(hex) = core::str::from_utf8(v) {
- hex.parse::<LegacyPublicKey>().map_err(E::custom)
- } else {
- Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
- }
- }
-
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- v.parse::<LegacyPublicKey>().map_err(E::custom)
- }
- }
- d.deserialize_str(HexVisitor)
- } else {
- struct BytesVisitor;
-
- impl serde::de::Visitor<'_> for BytesVisitor {
- type Value = LegacyPublicKey;
-
- fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
- formatter.write_str("a bytestring")
- }
-
- fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- LegacyPublicKey::from_slice(v).map_err(E::custom)
- }
- }
-
- d.deserialize_bytes(BytesVisitor)
- }
- }
-}
-
-#[cfg(feature = "serde")]
-impl serde::Serialize for FullPublicKey {
- fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
- if s.is_human_readable() {
- s.collect_str(self)
- } else {
- s.serialize_bytes(&self.to_bytes())
- }
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> serde::Deserialize<'de> for FullPublicKey {
- fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
- if d.is_human_readable() {
- struct HexVisitor;
-
- impl serde::de::Visitor<'_> for HexVisitor {
- type Value = FullPublicKey;
-
- fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
- formatter.write_str("a 66 digits long ASCII hex string")
- }
-
- fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- if let Ok(hex) = core::str::from_utf8(v) {
- hex.parse::<FullPublicKey>().map_err(E::custom)
- } else {
- Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
- }
- }
-
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- v.parse::<FullPublicKey>().map_err(E::custom)
- }
- }
- d.deserialize_str(HexVisitor)
- } else {
- struct BytesVisitor;
-
- impl serde::de::Visitor<'_> for BytesVisitor {
- type Value = FullPublicKey;
-
- fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
- formatter.write_str("a bytestring")
- }
-
- fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
- where
- E: serde::de::Error,
- {
- let arr = v.try_into().map_err(E::custom)?;
- FullPublicKey::from_bytes(arr).map_err(E::custom)
- }
- }
-
- d.deserialize_bytes(BytesVisitor)
- }
- }
-}
-/// Untweaked BIP-0340 X-coord-only public key.
-pub type UntweakedPublicKey = XOnlyPublicKey;
-
-impl fmt::LowerHex for TweakedPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::LowerHex::fmt(self.as_x_only_public_key(), f)
- }
-}
-// Allocate for serialized size
-impl_to_hex_from_lower_hex!(TweakedPublicKey, |_| constants::SCHNORR_PUBLIC_KEY_SIZE * 2);
-
-impl fmt::Display for TweakedPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(self.as_x_only_public_key(), f)
- }
-}
-
-/// Untweaked BIP-0340 key pair.
-pub type UntweakedKeypair = Keypair;
-
-/// A trait for tweaking BIP-0340 key types (x-only public keys and key pairs).
-pub trait TapTweak {
- /// Tweaked key type with optional auxiliary information.
- type TweakedAux;
- /// Tweaked key type.
- type TweakedKey;
-
- /// Tweaks an untweaked key with corresponding public key value and optional script tree Merkle
- /// root. For the [`Keypair`] type this also tweaks the private key in the pair.
- ///
- /// This is done by using the equation Q = P + H(P|c)G, where
- /// * Q is the tweaked public key
- /// * P is the internal public key
- /// * H is the hash function
- /// * c is the commitment data
- /// * G is the generator point
- ///
- /// # Returns
- ///
- /// The tweaked key, with the required parity.
- fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> Self::TweakedAux;
-
- /// Directly converts an [`UntweakedPublicKey`] to a [`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.
- fn dangerous_assume_tweaked(self) -> Self::TweakedKey;
-}
-
-impl TapTweak for UntweakedPublicKey {
- type TweakedAux = TweakedPublicKey;
- type TweakedKey = TweakedPublicKey;
-
- /// Tweaks an untweaked public key with corresponding public key value and optional script tree
- /// Merkle root.
- ///
- /// This is done by using the equation Q = P + H(P|c)G, where
- /// * Q is the tweaked public key
- /// * P is the internal public key
- /// * H is the hash function
- /// * c is the commitment data
- /// * G is the generator point
- ///
- /// # Returns
- ///
- /// The tweaked key and its parity.
- fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> TweakedPublicKey {
- let tweak = TapTweakHash::from_key_and_merkle_root(self, merkle_root).to_scalar();
- let output_key = self.add_tweak(&tweak).expect("Tap tweak failed");
-
- debug_assert!(self.tweak_add_check(&output_key, tweak));
- TweakedPublicKey::dangerous_assume_tweaked(output_key)
- }
-
- fn dangerous_assume_tweaked(self) -> TweakedPublicKey {
- TweakedPublicKey::dangerous_assume_tweaked(self)
- }
-}
-
-impl TapTweak for UntweakedKeypair {
- type TweakedAux = TweakedKeypair;
- type TweakedKey = TweakedKeypair;
-
- /// Applies a Taproot tweak to both keys within the keypair.
- ///
- /// If `merkle_root` is provided, produces a Taproot key that can be spent by any
- /// of the script paths committed to by the root. If it is not provided, produces
- /// a Taproot key which can [provably only be spent via
- /// keyspend](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#cite_note-23).
- ///
- /// # Returns
- ///
- /// The tweaked keypair.
- fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> TweakedKeypair {
- let pubkey = XOnlyPublicKey::from_keypair(&self);
- let tweak = TapTweakHash::from_key_and_merkle_root(pubkey, merkle_root).to_scalar();
- let tweaked = self.as_inner().add_xonly_tweak(&tweak).expect("Tap tweak failed");
- TweakedKeypair::dangerous_assume_tweaked(Self::from(tweaked))
- }
-
- fn dangerous_assume_tweaked(self) -> TweakedKeypair {
- TweakedKeypair::dangerous_assume_tweaked(self)
- }
-}
-
-impl TweakedPublicKey {
- /// Returns the underlying public key.
- #[inline]
- #[doc(hidden)]
- #[deprecated(since = "0.32.6", note = "use to_x_only_public_key() instead")]
- pub fn to_inner(self) -> XOnlyPublicKey { self.to_x_only_public_key() }
-
- /// Serializes the key as a byte-encoded x coordinate value (32 bytes).
- #[inline]
- pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
- self.as_x_only_public_key().serialize().0
- }
-}
-
-impl TweakedKeypair {
- /// Returns the underlying key pair.
- #[inline]
- #[doc(hidden)]
- #[deprecated(since = "0.32.6", note = "use into_keypair() instead")]
- pub fn to_inner(self) -> Keypair { self.into_keypair() }
-
- /// Returns the [`TweakedPublicKey`] and its [`Parity`] for this [`TweakedKeypair`].
- #[inline]
- pub fn public_parts(&self) -> (TweakedPublicKey, Parity) {
- let xonly = self.as_keypair().to_x_only_public_key();
- (TweakedPublicKey::dangerous_assume_tweaked(xonly), xonly.parity())
- }
-}
-
-impl From<TweakedPublicKey> for XOnlyPublicKey {
- #[inline]
- fn from(pair: TweakedPublicKey) -> Self { pair.to_x_only_public_key() }
-}
-
-impl From<TweakedKeypair> for Keypair {
- #[inline]
- fn from(pair: TweakedKeypair) -> Self { pair.into_keypair() }
-}
-
-impl<'a> From<&'a TweakedKeypair> for &'a Keypair {
- #[inline]
- fn from(pair: &'a TweakedKeypair) -> Self { pair.as_keypair() }
-}
-
-impl From<TweakedKeypair> for TweakedPublicKey {
- #[inline]
- fn from(pair: TweakedKeypair) -> Self { Self::from(&pair) }
-}
-
-impl From<&TweakedKeypair> for TweakedPublicKey {
- #[inline]
- fn from(pair: &TweakedKeypair) -> Self { Self::from_keypair(pair) }
-}
-
-impl SerializedXOnlyPublicKey {
- /// Returns `XOnlyPublicKey` if the bytes are valid.
- ///
- /// # Errors
- ///
- /// [`ParseXOnlyPublicKeyError::InvalidXCoordinate`] if the provided bytes don't represent
- /// a valid secp256k1 point x-coordinate.
- pub fn to_validated(self) -> Result<XOnlyPublicKey, ParseXOnlyPublicKeyError> {
- XOnlyPublicKey::from_byte_array(self.as_byte_array())
- }
-}
-
-impl AsRef<[u8; 32]> for SerializedXOnlyPublicKey {
- fn as_ref(&self) -> &[u8; 32] { self.as_byte_array() }
-}
-
-impl From<&Self> for SerializedXOnlyPublicKey {
- fn from(borrowed: &Self) -> Self { *borrowed }
-}
-
-impl fmt::Debug for SerializedXOnlyPublicKey {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Debug::fmt(&self.as_byte_array().as_hex(), f)
- }
-}
-
-/// Error types for bitcoin keys.
-pub mod error {
- use core::convert::Infallible;
- use core::fmt;
-
- use internals::write_err;
-
- /// Error returned while generating key from slice.
- #[derive(Debug, Clone, PartialEq, Eq)]
- #[non_exhaustive]
- pub enum FromSliceError {
- /// Invalid key prefix error.
- InvalidKeyPrefix(u8),
- /// A secp256k1 error.
- Secp256k1(secp256k1::Error),
- /// Invalid Length of the slice.
- InvalidLength(usize),
- }
-
- impl From<Infallible> for FromSliceError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for FromSliceError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Secp256k1(e) => write_err!(f, "secp256k1"; e),
- Self::InvalidKeyPrefix(b) => write!(f, "key prefix invalid: {}", b),
- Self::InvalidLength(got) =>
- write!(f, "slice length should be 33 or 65 bytes, got: {}", got),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for FromSliceError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Secp256k1(ref e) => Some(e),
- Self::InvalidKeyPrefix(_) | Self::InvalidLength(_) => None,
- }
- }
- }
-
- impl From<secp256k1::Error> for FromSliceError {
- fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
- }
-
- /// Error generated from WIF key format.
- #[derive(Debug, Clone, PartialEq, Eq)]
- #[non_exhaustive]
- pub enum FromWifError {
- /// A base58 decoding error.
- Base58(base58::Error),
- /// Base58 decoded data was an invalid length.
- InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
- /// Base58 decoded data contained an invalid address version byte.
- InvalidAddressVersion(InvalidAddressVersionError),
- /// A secp256k1 error.
- Secp256k1(secp256k1::Error),
- /// Invalid WIF compression flag.
- InvalidWifCompressionFlag(InvalidWifCompressionFlagError),
- }
-
- impl From<Infallible> for FromWifError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for FromWifError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Base58(ref e) => write_err!(f, "invalid base58"; e),
- Self::InvalidBase58PayloadLength(ref e) =>
- write_err!(f, "decoded base58 data was an invalid length"; e),
- Self::InvalidAddressVersion(ref e) =>
- write_err!(f, "decoded base58 data contained an invalid address version byte"; e),
- Self::Secp256k1(ref e) => write_err!(f, "private key validation failed"; e),
- Self::InvalidWifCompressionFlag(ref e) =>
- write_err!(f, "invalid WIF compression flag"; e),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for FromWifError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Base58(ref e) => Some(e),
- Self::InvalidBase58PayloadLength(ref e) => Some(e),
- Self::InvalidAddressVersion(ref e) => Some(e),
- Self::Secp256k1(ref e) => Some(e),
- Self::InvalidWifCompressionFlag(ref e) => Some(e),
- }
- }
- }
-
- impl From<base58::Error> for FromWifError {
- fn from(e: base58::Error) -> Self { Self::Base58(e) }
- }
-
- impl From<secp256k1::Error> for FromWifError {
- fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
- }
-
- impl From<InvalidBase58PayloadLengthError> for FromWifError {
- fn from(e: InvalidBase58PayloadLengthError) -> Self { Self::InvalidBase58PayloadLength(e) }
- }
-
- impl From<InvalidAddressVersionError> for FromWifError {
- fn from(e: InvalidAddressVersionError) -> Self { Self::InvalidAddressVersion(e) }
- }
-
- impl From<InvalidWifCompressionFlagError> for FromWifError {
- fn from(e: InvalidWifCompressionFlagError) -> Self { Self::InvalidWifCompressionFlag(e) }
- }
-
- /// Error returned while constructing a [`Keypair`] from string.
- ///
- /// [`Keypair`]: super::Keypair
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct ParseKeypairError(pub(super) secp256k1::Error);
-
- impl From<Infallible> for ParseKeypairError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for ParseKeypairError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write_err!(f, "parse keypair failed"; self.0)
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for ParseKeypairError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
- }
-
- /// Error returned while constructing public key from string.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub enum ParsePublicKeyError {
- /// Error originated while parsing string.
- Encoding(FromSliceError),
- /// Hex decoding error.
- InvalidChar(hex::error::InvalidCharError),
- /// `LegacyPublicKey` hex should be 66 or 130 digits long.
- InvalidHexLength(usize),
- }
-
- impl From<Infallible> for ParsePublicKeyError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for ParsePublicKeyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Encoding(ref e) => write_err!(f, "string error"; e),
- Self::InvalidChar(ref e) => write_err!(f, "hex decoding"; e),
- Self::InvalidHexLength(got) =>
- write!(f, "pubkey string should be 66 or 130 digits long, got: {}", got),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for ParsePublicKeyError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Encoding(ref e) => Some(e),
- Self::InvalidChar(ref e) => Some(e),
- Self::InvalidHexLength(_) => None,
- }
- }
- }
-
- impl From<FromSliceError> for ParsePublicKeyError {
- fn from(e: FromSliceError) -> Self { Self::Encoding(e) }
- }
-
- /// Error returned when parsing a [`FullPublicKey`] from a string.
- ///
- /// [`FullPublicKey`]: super::FullPublicKey
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub enum ParseFullPublicKeyError {
- /// secp256k1 Error.
- Secp256k1(secp256k1::Error),
- /// hex to array conversion error.
- Hex(hex::DecodeFixedLengthBytesError),
- }
-
- impl From<Infallible> for ParseFullPublicKeyError {
- fn from(never: Infallible) -> Self { match never {} }
- }
-
- impl fmt::Display for ParseFullPublicKeyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Secp256k1(e) => write_err!(f, "secp256k1 error"; e),
- Self::Hex(e) => write_err!(f, "invalid hex"; e),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for ParseFullPublicKeyError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::Secp256k1(e) => Some(e),
- Self::Hex(e) => Some(e),
- }
- }
- }
-
- impl From<secp256k1::Error> for ParseFullPublicKeyError {
- fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
- }
-
- impl From<hex::DecodeFixedLengthBytesError> for ParseFullPublicKeyError {
- fn from(e: hex::DecodeFixedLengthBytesError) -> Self { Self::Hex(e) }
- }
-
- /// SegWit public keys must always be compressed.
- #[derive(Debug, Clone, PartialEq, Eq)]
- #[non_exhaustive]
- pub struct UncompressedPublicKeyError;
-
- impl fmt::Display for UncompressedPublicKeyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("SegWit public keys must always be compressed")
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for UncompressedPublicKeyError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
- }
-
- /// Decoded base58 data was an invalid length.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct InvalidBase58PayloadLengthError {
- /// The base58 payload length we got after decoding WIF string.
- pub(crate) length: usize,
- }
-
- impl InvalidBase58PayloadLengthError {
- /// Returns the invalid payload length.
- pub fn invalid_base58_payload_length(&self) -> usize { self.length }
- }
-
- impl fmt::Display for InvalidBase58PayloadLengthError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "decoded base58 data was an invalid length: {} (expected 33 or 34)",
- self.length
- )
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for InvalidBase58PayloadLengthError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
- }
-
- /// Invalid address version in decoded base58 data.
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct InvalidAddressVersionError {
- /// The invalid version.
- pub(crate) invalid: u8,
- }
-
- impl InvalidAddressVersionError {
- /// Returns the invalid version.
- pub fn invalid_address_version(&self) -> u8 { self.invalid }
- }
-
- impl fmt::Display for InvalidAddressVersionError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "invalid address version in decoded base58 data {}", self.invalid)
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for InvalidAddressVersionError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
- }
-
- /// Invalid compression flag for a WIF key
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct InvalidWifCompressionFlagError {
- /// The invalid compression flag.
- pub(crate) invalid: u8,
- }
-
- impl InvalidWifCompressionFlagError {
- /// Returns the invalid compression flag.
- pub fn invalid_compression_flag(&self) -> u8 { self.invalid }
- }
-
- impl fmt::Display for InvalidWifCompressionFlagError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "invalid WIF compression flag. Expected a 0x01 byte at the end of the key but found: {}", self.invalid)
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for InvalidWifCompressionFlagError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
- }
-
- /// Error that can occur when parsing an [`XOnlyPublicKey`] from bytes.
- ///
- /// [`XOnlyPublicKey`]: super::XOnlyPublicKey
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub enum ParseXOnlyPublicKeyError {
- /// The provided bytes do not represent a valid secp256k1 point x-coordinate.
- InvalidXCoordinate,
- }
-
- impl fmt::Display for ParseXOnlyPublicKeyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::InvalidXCoordinate => write!(f, "Invalid X coordinate for secp256k1 point"),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for ParseXOnlyPublicKeyError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::InvalidXCoordinate => None,
- }
- }
- }
-
- /// Error that can occur when tweaking an [`XOnlyPublicKey`].
+ /// # Returns
///
- /// [`XOnlyPublicKey`]: super::XOnlyPublicKey
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub enum TweakXOnlyPublicKeyError {
- /// The tweak value was invalid.
- BadTweak,
- /// The resulting public key would be invalid.
- ResultKeyInvalid,
- /// Invalid parity value encountered during the operation.
- ParityError,
- }
-
- impl fmt::Display for TweakXOnlyPublicKeyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::BadTweak => write!(f, "Invalid tweak value"),
- Self::ResultKeyInvalid => write!(f, "Resulting public key would be invalid"),
- Self::ParityError => write!(f, "Invalid parity value encountered"),
- }
- }
- }
-
- #[cfg(feature = "std")]
- impl std::error::Error for TweakXOnlyPublicKeyError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- Self::BadTweak => None,
- Self::ResultKeyInvalid => None,
- Self::ParityError => None,
- }
- }
+ /// The tweaked keypair.
+ fn tap_tweak(self, merkle_root: Option<TapNodeHash>) -> TweakedKeypair {
+ let pubkey = XOnlyPublicKey::from_keypair(&self);
+ let tweak = TapTweakHash::from_key_and_merkle_root(pubkey, merkle_root).to_scalar();
+ let tweaked = self.as_inner().add_xonly_tweak(&tweak).expect("Tap tweak failed");
+ TweakedKeypair::dangerous_assume_tweaked(Self::from(tweaked))
}
-}
-#[cfg(feature = "arbitrary")]
-impl<'a> Arbitrary<'a> for LegacyPublicKey {
- fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
- Ok(Self::from_secp(secp256k1::PublicKey::arbitrary(u)?))
+ fn dangerous_assume_tweaked(self) -> TweakedKeypair {
+ TweakedKeypair::dangerous_assume_tweaked(self)
}
}
-#[cfg(feature = "arbitrary")]
-impl<'a> Arbitrary<'a> for XOnlyPublicKey {
- fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
- Ok(Self::from_secp(secp256k1::XOnlyPublicKey::arbitrary(u)?, u.arbitrary()?))
- }
-}
+crate::internal_macros::impl_asref_push_bytes!(PubkeyHash, WPubkeyHash);
#[cfg(test)]
mod tests {
@@ -2053,17 +262,13 @@ mod tests {
use super::*;
use crate::address::Address;
+ use crate::network::NetworkKind;
#[test]
fn key_derivation() {
// mainnet compressed WIF with invalid compression flag.
let sk = WifKey::from_wif("L2x4uC2YgfFWZm9tF4pjDnVR6nJkheizFhEr2KvDNnTEmEqVzPJY");
- assert!(matches!(
- sk,
- Err(FromWifError::InvalidWifCompressionFlag(InvalidWifCompressionFlagError {
- invalid: 49
- }))
- ));
+ assert!(matches!(sk, Err(FromWifError::InvalidWifCompressionFlag(_))));
// testnet compressed
let sk = WifKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
@@ -2106,441 +311,6 @@ mod tests {
);
}
- #[test]
- fn pubkey_hash() {
- let pk = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
- .parse::<LegacyPublicKey>()
- .unwrap();
- let upk = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"
- .parse::<LegacyPublicKey>().unwrap();
- assert_eq!(pk.pubkey_hash().to_string(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
- assert_eq!(upk.pubkey_hash().to_string(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a");
- }
-
- #[test]
- fn wpubkey_hash() {
- let pk = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
- .parse::<LegacyPublicKey>()
- .unwrap();
- let upk = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133".parse::<LegacyPublicKey>().unwrap();
- assert_eq!(
- pk.wpubkey_hash().unwrap().to_string(),
- "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"
- );
- assert!(upk.wpubkey_hash().is_err());
- }
-
- #[test]
- #[cfg(feature = "serde")]
- fn skey_serde() {
- use serde_test::{assert_tokens, Configure, Token};
-
- static KEY_WIF: &str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
- static PK_STR: &str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef";
- static PK_STR_U: &str = "\
- 04\
- 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\
- 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\
- ";
- #[rustfmt::skip]
- 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,
- ];
- #[rustfmt::skip]
- 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 wk = KEY_WIF.parse::<WifKey>().unwrap();
- let pk = LegacyPublicKey::from_private_key(&wk.private_key);
- let pk_u = LegacyPublicKey::from_secp_uncompressed(pk.to_inner());
-
- assert_tokens(&wk, &[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) -> LegacyPublicKey {
- 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) = LegacyPublicKey::from_slice(&data[..]) {
- return key;
- }
- } else {
- data[0] = 2 + (data[0] >> 7);
- if let Ok(key) = LegacyPublicKey::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 reader = v.as_slice();
- let mut dec_keys = vec![];
- for _ in 0..N_KEYS {
- dec_keys.push(LegacyPublicKey::read_from(&mut reader).expect("reading from vec"));
- }
- assert_eq!(keys, dec_keys);
- assert!(LegacyPublicKey::read_from(&mut reader).is_err());
-
- // sanity checks
- let mut empty: &[u8] = &[];
- assert!(LegacyPublicKey::read_from(&mut empty).is_err());
- assert!(LegacyPublicKey::read_from(&mut &[0; 33][..]).is_err());
- assert!(LegacyPublicKey::read_from(&mut &[2; 32][..]).is_err());
- assert!(LegacyPublicKey::read_from(&mut &[0; 65][..]).is_err());
- assert!(LegacyPublicKey::read_from(&mut &[4; 64][..]).is_err());
- }
-
- #[test]
- fn pubkey_to_sort_key() {
- let key1 = "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
- .parse::<LegacyPublicKey>()
- .unwrap();
- let key2 = LegacyPublicKey::from_secp_uncompressed(key1.to_inner());
- let arrayvec1 = ArrayVec::from_slice(
- &hex::decode_to_array::<33>(
- "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
- )
- .unwrap(),
- );
- let expected1 = SortKey(arrayvec1);
- let arrayvec2 = ArrayVec::from_slice(&hex::decode_to_array::<65>(
- "04ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f81794e7f3d5e420641a3bc690067df5541470c966cbca8c694bf39aa16d836918",
- ).unwrap());
- let expected2 = SortKey(arrayvec2);
- assert_eq!(key1.to_sort_key(), expected1);
- assert_eq!(key2.to_sort_key(), expected2);
- }
-
- #[test]
- fn pubkey_sort() {
- struct Vector {
- input: Vec<LegacyPublicKey>,
- expect: Vec<LegacyPublicKey>,
- }
- let fmt = |v: Vec<_>| {
- v.into_iter().map(|s: &str| s.parse::<LegacyPublicKey>().unwrap()).collect::<Vec<_>>()
- };
- let vectors = vec![
- // Start BIP-0067 vectors
- // Vector 1
- Vector {
- input: fmt(vec![
- "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
- "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
- ]),
- expect: fmt(vec![
- "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
- "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
- ]),
- },
- // Vector 2 (Already sorted, no action required)
- Vector {
- input: fmt(vec![
- "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
- "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
- "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
- ]),
- expect: fmt(vec![
- "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
- "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
- "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
- ]),
- },
- // Vector 3
- Vector {
- input: fmt(vec![
- "030000000000000000000000000000000000004141414141414141414141414141",
- "020000000000000000000000000000000000004141414141414141414141414141",
- "020000000000000000000000000000000000004141414141414141414141414140",
- "030000000000000000000000000000000000004141414141414141414141414140",
- ]),
- expect: fmt(vec![
- "020000000000000000000000000000000000004141414141414141414141414140",
- "020000000000000000000000000000000000004141414141414141414141414141",
- "030000000000000000000000000000000000004141414141414141414141414140",
- "030000000000000000000000000000000000004141414141414141414141414141",
- ]),
- },
- // Vector 4: (from bitcore)
- Vector {
- input: fmt(vec![
- "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
- "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
- "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
- ]),
- expect: fmt(vec![
- "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
- "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
- "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
- ]),
- },
- // Non-BIP67 vectors
- Vector {
- input: fmt(vec![
- "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
- "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
- "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
- "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
- "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
- "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
- "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
- "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
- "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
- "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
- "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
- "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
- "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
- "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
- "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
- "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
- ]),
- expect: fmt(vec![
- "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
- "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
- "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
- "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
- "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
- "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
- "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
- "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
- "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
- "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
- "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
- "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
- // These two pubkeys are mirrored. This helps verify the sort past the x value.
- "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
- "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
- "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
- "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
- ]),
- },
- ];
- for mut vector in vectors {
- vector.input.sort_by_cached_key(|k| LegacyPublicKey::to_sort_key(*k));
- assert_eq!(vector.input, vector.expect);
- }
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn public_key_constructors() {
- let kp = Keypair::generate();
-
- let _ = LegacyPublicKey::from_secp(kp.clone());
- let _ = LegacyPublicKey::from_secp_uncompressed(kp);
- }
-
- #[test]
- fn public_key_from_str_wrong_length() {
- // Sanity checks, we accept string length 130 digits.
- let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
- assert_eq!(s.len(), 130);
- assert!(s.parse::<LegacyPublicKey>().is_ok());
- // And 66 digits.
- let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af";
- assert_eq!(s.len(), 66);
- assert!(s.parse::<LegacyPublicKey>().is_ok());
-
- let s = "aoeusthb";
- assert_eq!(s.len(), 8);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- assert_eq!(res.unwrap_err(), ParsePublicKeyError::InvalidHexLength(8));
- }
-
- #[test]
- fn public_key_from_str_invalid_str() {
- // Ensuring test cases fail when LegacyPublicKey::from_str is used on invalid keys
- let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b142";
- assert_eq!(s.len(), 130);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- assert_eq!(
- res.unwrap_err(),
- ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
- secp256k1::Error::InvalidPublicKey
- ))
- );
-
- let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd169";
- assert_eq!(s.len(), 66);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- assert_eq!(
- res.unwrap_err(),
- ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
- secp256k1::Error::InvalidPublicKey
- ))
- );
-
- let s = "062e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
- assert_eq!(s.len(), 130);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- assert_eq!(
- res.unwrap_err(),
- ParsePublicKeyError::Encoding(FromSliceError::InvalidKeyPrefix(6))
- );
-
- let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b13g";
- assert_eq!(s.len(), 130);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- if let Err(ParsePublicKeyError::InvalidChar(err)) = res {
- assert_eq!(err.pos(), 129);
- } else {
- panic!("expected ParsePublicKeyError::InvalidChar");
- }
-
- let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1ag";
- assert_eq!(s.len(), 66);
- let res = s.parse::<LegacyPublicKey>();
- assert!(res.is_err());
- if let Err(ParsePublicKeyError::InvalidChar(err)) = res {
- assert_eq!(err.pos(), 65);
- } else {
- panic!("expected ParsePublicKeyError::InvalidChar");
- }
- }
-
- #[test]
- #[allow(deprecated)] // tests the deprecated function
- #[allow(deprecated_in_future)]
- fn invalid_private_key_len() {
- use crate::Network;
- assert!(PrivateKey::from_slice(&[1u8; 31], Network::Regtest).is_err());
- assert!(PrivateKey::from_slice(&[1u8; 33], Network::Regtest).is_err());
- }
-
- #[test]
- fn xonly_pubkey_from_bytes() {
- let key_bytes = &hex::decode_to_array::<32>(
- "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d",
- )
- .expect("Failed to convert hex string to byte array");
- let xonly_pub_key = XOnlyPublicKey::from_byte_array(key_bytes)
- .expect("Failed to create an XOnlyPublicKey from a byte array");
- // Confirm that the public key from bytes serializes back to the same bytes
- assert_eq!(&xonly_pub_key.serialize().0, key_bytes);
- }
-
- #[test]
- fn xonly_pubkey_to_inner() {
- let key_bytes = &hex::decode_to_array::<32>(
- "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d",
- )
- .expect("Failed to convert hex string to byte array");
- let inner_key = secp256k1::XOnlyPublicKey::from_byte_array(*key_bytes)
- .expect("Failed to create a secp256k1 x-only public key from a byte array");
- let btc_pubkey = XOnlyPublicKey::from(inner_key);
- // Confirm that the to_inner() returns the same data that was initially wrapped
- assert_eq!(inner_key, btc_pubkey.to_inner());
- }
-
- #[test]
- fn keypair_from_str_roundtrip() {
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- let keypair = Keypair::generate();
- #[cfg(not(all(feature = "rand", feature = "std")))]
- let keypair = {
- let bytes = hex::decode_to_array::<32>(
- "1ede31b0e7e47c2afc65ffd158b1b1b9d3b752bba8fd117dc8b9e944a390e8d9",
- )
- .unwrap();
- let sk = PrivateKey::from_secret_bytes(&bytes).unwrap();
- Keypair::from_private_key(&sk)
- };
-
- // Use secp256k1::DisplaySecret, since no key type implements Display
- let encoded = format!("{}", keypair.as_inner().display_secret());
- let decoded = encoded.parse::<Keypair>().unwrap();
- assert_eq!(decoded, keypair);
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn keypair_secp_roundtrip() {
- let bitcoin_key = Keypair::generate();
- let secp_key =
- secp256k1::Keypair::from_seckey_byte_array(bitcoin_key.to_secret_bytes()).unwrap();
- assert_eq!(Keypair::from_secp(secp_key), bitcoin_key);
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn public_key_secp_roundtrip() {
- let bitcoin_key = Keypair::generate().to_public_key();
- let secp_key =
- secp256k1::PublicKey::from_byte_array_compressed(bitcoin_key.serialize_compressed())
- .unwrap();
- assert_eq!(LegacyPublicKey::from_secp(secp_key), bitcoin_key);
- // Also assert that generating a secp from compressed or uncompressed yields the same value
- assert_eq!(
- secp256k1::PublicKey::from_byte_array_uncompressed(
- bitcoin_key.serialize_uncompressed()
- )
- .unwrap(),
- secp_key,
- );
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn xonly_secp_roundtrip() {
- let bitcoin_key = Keypair::generate().to_x_only_public_key();
- let secp_key =
- secp256k1::XOnlyPublicKey::from_byte_array(bitcoin_key.serialize().0).unwrap();
- assert_eq!(bitcoin_key, XOnlyPublicKey::from_secp(secp_key, bitcoin_key.parity()),);
- }
-
- #[test]
- #[cfg(feature = "rand")]
- #[cfg(feature = "std")]
- fn private_key_secp_roundtrip() {
- let bitcoin_key = PrivateKey::generate();
- let secp_key =
- secp256k1::SecretKey::from_secret_bytes(bitcoin_key.to_secret_bytes()).unwrap();
- assert_eq!(PrivateKey::from_secp(secp_key), bitcoin_key);
- }
-
#[test]
#[cfg(feature = "rand")]
#[cfg(feature = "std")]
diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml
index 88c8580d..b21e4d24 100644
--- a/crypto/Cargo.toml
+++ b/crypto/Cargo.toml
@@ -15,22 +15,27 @@ exclude = ["tests", "contrib"]
[features]
default = ["std"]
-std = ["alloc", "hex-stable/std", "hex-unstable/std", "internals/std", "io/std", "secp256k1/std", "serde?/std"]
-alloc = ["hex-stable/alloc", "hex-unstable/alloc", "internals/alloc", "io/alloc", "secp256k1/alloc", "serde?/alloc"]
-serde = ["dep:serde", "internals/serde", "secp256k1/serde"]
+std = ["alloc", "base58/std", "hashes/std", "hex-stable/std", "hex-unstable/std", "internals/std", "io/std", "network/std", "secp256k1/std", "serde?/std"]
+rand = ["secp256k1/rand"]
+alloc = ["base58/alloc", "hashes/alloc", "hex-stable/alloc", "hex-unstable/alloc", "internals/alloc", "io/alloc", "network/alloc", "secp256k1/alloc", "serde?/alloc"]
+serde = ["dep:serde", "hashes/serde", "internals/serde", "secp256k1/serde"]
arbitrary = ["dep:arbitrary", "secp256k1/arbitrary"]
[dependencies]
+base58 = { package = "base58ck", path = "../base58", version = "0.4.0", default-features = false }
+hashes = { package = "bitcoin_hashes", path = "../hashes", version = "0.20.0", default-features = false, features = ["hex"] }
hex-unstable = { package = "hex-conservative", version = "0.3.2", default-features = false }
hex-stable = { package = "hex-conservative", version = "1.0.0", default-features = false }
internals = { package = "bitcoin-internals", path = "../internals", version = "0.5.0", features = ["hex"] }
io = { package = "bitcoin-io", path = "../io", version = "0.5.0", default-features = false, features = ["hashes"] }
+network = { package = "bitcoin-network-kind", path = "../network", version = "0.1.0", default-features = false }
secp256k1 = { version = "0.32.0-beta.2", default-features = false }
arbitrary = { version = "1.4.1", optional = true }
serde = { version = "1.0.195", default-features = false, features = ["derive"], optional = true }
[dev-dependencies]
+serde_test = "1.0.19"
[package.metadata.docs.rs]
all-features = true
diff --git a/crypto/include b/crypto/include
new file mode 120000
index 00000000..f5030fe8
--- /dev/null
+++ b/crypto/include
@@ -0,0 +1 @@
+../include
\ No newline at end of file
diff --git a/crypto/src/key.rs b/crypto/src/key.rs
new file mode 100644
index 00000000..5be6c7d9
--- /dev/null
+++ b/crypto/src/key.rs
@@ -0,0 +1,2265 @@
+// SPDX-License-Identifier: CC0-1.0
+
+//! Bitcoin keys.
+//!
+//! This module provides keys used in Bitcoin that can be roundtrip
+//! (de)serialized.
+
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::fmt;
+use core::str::FromStr;
+
+#[cfg(feature = "arbitrary")]
+use arbitrary::{Arbitrary, Unstructured};
+use hashes::hash160;
+use hex_unstable::DisplayHex;
+use internals::array::ArrayExt;
+use internals::array_vec::ArrayVec;
+use internals::impl_to_hex_from_lower_hex;
+use io::{Read, Write};
+use network::NetworkKind;
+#[cfg(feature = "rand")]
+#[cfg(feature = "std")]
+pub use secp256k1::rand;
+#[cfg(feature = "serde")]
+use serde::{Deserialize, Deserializer, Serialize, Serializer};
+
+use crate::ecdsa;
+use crate::hex::{self, DecodeFixedLengthBytesError};
+
+#[rustfmt::skip] // Keep public re-exports separate.
+pub use secp256k1::{constants, Parity, Verification};
+pub use encapsulate::{
+ FullPublicKey, Keypair, LegacyPublicKey, PrivateKey, SerializedXOnlyPublicKey, TweakedKeypair,
+ TweakedPublicKey, XOnlyPublicKey,
+};
+
+#[doc(no_inline)]
+pub use self::error::{
+ FromSliceError, FromWifError, InvalidAddressVersionError, InvalidBase58PayloadLengthError,
+ InvalidWifCompressionFlagError, ParseFullPublicKeyError, ParseKeypairError,
+ ParsePublicKeyError, ParseXOnlyPublicKeyError, TweakXOnlyPublicKeyError,
+ UncompressedPublicKeyError,
+};
+
+/// Encapsulation module to provide a clear barrier for construction/destruction of types.
+mod encapsulate {
+ use secp256k1::Parity;
+ #[cfg(feature = "serde")]
+ use serde::{Deserialize, Serialize};
+
+ /// A Bitcoin Schnorr X-only public key used for BIP-0340 signatures.
+ ///
+ /// This type also holds the parity of the full public key.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct XOnlyPublicKey {
+ inner: secp256k1::XOnlyPublicKey,
+ parity: Parity,
+ }
+
+ impl XOnlyPublicKey {
+ /// Constructs a new x-only public key from the provided secp256k1 x-only public key.
+ pub fn from_secp(key: impl Into<secp256k1::XOnlyPublicKey>, parity: Parity) -> Self {
+ Self { inner: key.into(), parity }
+ }
+
+ /// Sets the parity of this [`XOnlyPublicKey`].
+ ///
+ /// This returns a new `XOnlyPublicKey` with the same inner value, but the given parity.
+ #[must_use]
+ pub fn with_parity(self, parity: Parity) -> Self { Self { parity, ..self } }
+
+ /// Returns the parity of this x-only public key.
+ pub fn parity(&self) -> Parity { self.parity }
+
+ /// Returns a reference to the inner secp256k1 x-only public key.
+ #[inline]
+ pub fn as_inner(&self) -> &secp256k1::XOnlyPublicKey { &self.inner }
+
+ /// Returns the inner secp256k1 x-only public key.
+ #[inline]
+ pub fn to_inner(self) -> secp256k1::XOnlyPublicKey { self.inner }
+
+ /// Returns the inner secp256k1 x-only public key.
+ #[inline]
+ #[deprecated(since = "TBD", note = "use `to_inner()` instead")]
+ pub fn into_inner(self) -> secp256k1::XOnlyPublicKey { self.to_inner() }
+ }
+
+ /// A Bitcoin secret and public key pair.
+ #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+ pub struct Keypair(secp256k1::Keypair);
+
+ impl Keypair {
+ /// Constructs a keypair from a provided secp256k1 keypair.
+ #[inline]
+ pub fn from_secp(keypair: impl Into<secp256k1::Keypair>) -> Self { Self(keypair.into()) }
+
+ /// Returns a reference to the inner [`secp256k1::Keypair`].
+ #[inline]
+ pub fn as_inner(&self) -> &secp256k1::Keypair { &self.0 }
+ }
+
+ /// A Bitcoin ECDSA public key.
+ #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct LegacyPublicKey {
+ /// Whether this public key should be serialized as compressed.
+ compressed: bool,
+ /// The actual ECDSA key.
+ inner: secp256k1::PublicKey,
+ }
+
+ impl LegacyPublicKey {
+ /// Constructs a new compressed ECDSA public key from the provided secp256k1 public key.
+ pub fn from_secp(key: impl Into<secp256k1::PublicKey>) -> Self {
+ Self { compressed: true, inner: key.into() }
+ }
+
+ /// Constructs a new uncompressed (legacy) ECDSA public key from the provided secp256k1 public
+ /// key.
+ pub fn from_secp_uncompressed(key: impl Into<secp256k1::PublicKey>) -> Self {
+ Self { compressed: false, inner: key.into() }
+ }
+
+ /// Returns the inner secp256k1 public key.
+ #[inline]
+ pub fn to_inner(self) -> secp256k1::PublicKey { self.inner }
+
+ /// Returns whether this public key should be serialized as compressed.
+ #[inline]
+ pub fn compressed(&self) -> bool { self.compressed }
+ }
+
+ impl Drop for Keypair {
+ fn drop(&mut self) { self.0.non_secure_erase(); }
+ }
+
+ /// An always-compressed Bitcoin ECDSA public key.
+ #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ pub struct FullPublicKey(secp256k1::PublicKey);
+
+ impl FullPublicKey {
+ /// Constructs a new compressed public key from the provided secp public key.
+ #[inline]
+ pub fn from_secp(inner: secp256k1::PublicKey) -> Self { Self(inner) }
+
+ /// Returns the inner [`secp256k1::PublicKey`].
+ #[inline]
+ pub fn to_inner(self) -> secp256k1::PublicKey { self.0 }
+ }
+
+ /// A Bitcoin ECDSA private key.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct PrivateKey {
+ /// Whether this private key should be serialized as compressed.
+ compressed: bool,
+ /// The actual ECDSA key.
+ inner: secp256k1::SecretKey,
+ }
+
+ impl PrivateKey {
+ /// Constructs a new compressed ECDSA private key from the provided secp256k1 private key.
+ pub fn from_secp(key: secp256k1::SecretKey) -> Self {
+ Self { compressed: true, inner: key }
+ }
+
+ /// Constructs a new uncompressed (legacy) ECDSA private key from the provided secp256k1
+ /// private key.
+ pub fn from_secp_uncompressed(key: secp256k1::SecretKey) -> Self {
+ Self { compressed: false, inner: key }
+ }
+
+ /// Returns a reference to the inner secp256k1 secret key.
+ #[inline]
+ pub fn as_inner(&self) -> &secp256k1::SecretKey { &self.inner }
+
+ /// Returns whether this private key should be serialized as compressed.
+ #[inline]
+ pub fn compressed(&self) -> bool { self.compressed }
+ }
+
+ impl Drop for PrivateKey {
+ fn drop(&mut self) { self.inner.non_secure_erase(); }
+ }
+
+ /// Tweaked BIP-0340 X-coord-only public key.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+ #[cfg_attr(feature = "serde", serde(transparent))]
+ pub struct TweakedPublicKey(XOnlyPublicKey);
+
+ impl TweakedPublicKey {
+ /// Returns the [`TweakedPublicKey`] for `keypair`.
+ #[inline]
+ pub fn from_keypair(keypair: &TweakedKeypair) -> Self {
+ Self(keypair.as_keypair().to_x_only_public_key())
+ }
+
+ /// Constructs a new [`TweakedPublicKey`] from a [`XOnlyPublicKey`]. No tweak is applied, consider
+ /// calling `tap_tweak` on an [`UntweakedPublicKey`] instead of using this constructor.
+ ///
+ /// 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.
+ ///
+ /// [`UntweakedPublicKey`]: super::UntweakedPublicKey
+ #[inline]
+ pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> Self { Self(key) }
+
+ /// Returns the underlying x-only public key.
+ #[inline]
+ pub fn to_x_only_public_key(self) -> XOnlyPublicKey { self.0 }
+
+ /// Returns a reference to the underlying x-only public key.
+ #[inline]
+ pub fn as_x_only_public_key(&self) -> &XOnlyPublicKey { &self.0 }
+ }
+
+ /// Tweaked BIP-0340 key pair.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #[cfg(feature = "rand")]
+ /// # #[cfg(feature = "std")]
+ /// # {
+ /// # use bitcoin_crypto::key::{Keypair, TweakedKeypair, TweakedPublicKey};
+ /// # let keypair = TweakedKeypair::dangerous_assume_tweaked(Keypair::generate());
+ /// // There are various conversion methods available to get a tweaked pubkey from a tweaked keypair.
+ /// let (_pk, _parity) = keypair.public_parts();
+ /// let _pk = TweakedPublicKey::from_keypair(&keypair);
+ /// let _pk = TweakedPublicKey::from(&keypair);
+ /// # }
+ /// ```
+ #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+ #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+ #[cfg_attr(feature = "serde", serde(transparent))]
+ pub struct TweakedKeypair(Keypair);
+
+ impl TweakedKeypair {
+ /// Constructs a new [`TweakedKeypair`] from a [`Keypair`]. No tweak is applied, consider
+ /// calling `tap_tweak` on an [`UntweakedKeypair`](super::UntweakedKeypair) instead of using this constructor.
+ ///
+ /// 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) -> Self { Self(pair) }
+
+ /// Returns the underlying key pair.
+ #[inline]
+ pub fn into_keypair(self) -> Keypair { self.0 }
+
+ /// Returns a reference to the underlying key pair.
+ #[inline]
+ pub fn as_keypair(&self) -> &Keypair { &self.0 }
+ }
+
+ crate::transparent_newtype! {
+ /// An array of bytes that's semantically an x-only public but was **not** validated.
+ ///
+ /// This can be useful when validation is not desired but semantics of the bytes should be
+ /// preserved. The validation can still happen using `to_validated()` method.
+ #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+ pub struct SerializedXOnlyPublicKey([u8; 32]);
+
+ impl SerializedXOnlyPublicKey {
+ /// Constructs a [`SerializedXOnlyPublicKey`] from a reference to similar bytes.
+ pub fn from_bytes_ref(bytes: &_) -> Self;
+ }
+ }
+
+ impl SerializedXOnlyPublicKey {
+ /// Marks the supplied bytes as a serialized x-only public key.
+ pub const fn from_byte_array(bytes: [u8; 32]) -> Self { Self(bytes) }
+
+ /// Returns the raw bytes.
+ pub const fn to_byte_array(self) -> [u8; 32] { self.0 }
+
+ /// Returns a reference to the raw bytes.
+ pub const fn as_byte_array(&self) -> &[u8; 32] { &self.0 }
+ }
+}
+
+impl XOnlyPublicKey {
+ /// Constructs an x-only public key from a keypair.
+ ///
+ /// Returns the x-only public key, with the relevant parity set from the full public key.
+ #[inline]
+ pub fn from_keypair(keypair: &Keypair) -> Self {
+ let (xonly, parity) = secp256k1::XOnlyPublicKey::from_keypair(keypair.as_inner());
+ Self::from_secp(xonly, parity)
+ }
+
+ /// Constructs an x-only public key from a 32-byte x-coordinate.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the provided bytes don't represent a valid secp256k1 point x-coordinate.
+ #[inline]
+ pub fn from_byte_array(
+ data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE],
+ ) -> Result<Self, ParseXOnlyPublicKeyError> {
+ secp256k1::XOnlyPublicKey::from_byte_array(*data)
+ .map(|key| Self::from_secp(key, Parity::Even))
+ .map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
+ }
+
+ /// Serializes the x-only public key as a byte-encoded x coordinate value (32 bytes).
+ #[inline]
+ pub fn serialize(&self) -> ([u8; constants::SCHNORR_PUBLIC_KEY_SIZE], Parity) {
+ (self.as_inner().serialize(), self.parity())
+ }
+
+ /// Converts this x-only public key to a full public key.
+ ///
+ /// The [`LegacyPublicKey`] is constructed using the parity in this x-only public key.
+ #[inline]
+ pub fn to_public_key(self) -> LegacyPublicKey {
+ self.as_inner().public_key(self.parity()).into()
+ }
+
+ /// Verifies that a tweak produced by [`XOnlyPublicKey::add_tweak`] was computed correctly.
+ ///
+ /// Should be called on the original untweaked key. Takes the tweaked key with its output parity from
+ /// [`XOnlyPublicKey::add_tweak`] as input.
+ #[inline]
+ pub fn tweak_add_check(&self, tweaked_key: &Self, tweak: secp256k1::Scalar) -> bool {
+ self.as_inner().tweak_add_check(tweaked_key.as_inner(), tweaked_key.parity(), tweak)
+ }
+
+ /// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the given tweak to it.
+ ///
+ /// # Returns
+ ///
+ /// The newly tweaked key. This key has its parity set according to the parity following the
+ /// tweak. This key should be provided to `tweak_add_check` which can be used to verify a tweak
+ /// more efficiently than regenerating it and checking equality.
+ ///
+ /// # Errors
+ ///
+ /// If the resulting key would be invalid.
+ #[inline]
+ pub fn add_tweak(&self, tweak: &secp256k1::Scalar) -> Result<Self, TweakXOnlyPublicKeyError> {
+ match self.as_inner().add_tweak(tweak) {
+ Ok((xonly, parity)) => Ok(Self::from_secp(xonly, parity)),
+ Err(secp256k1::Error::InvalidTweak) => Err(TweakXOnlyPublicKeyError::BadTweak),
+ Err(secp256k1::Error::InvalidParityValue(_)) =>
+ Err(TweakXOnlyPublicKeyError::ParityError),
+ Err(_) => Err(TweakXOnlyPublicKeyError::ResultKeyInvalid),
+ }
+ }
+}
+
+impl FromStr for XOnlyPublicKey {
+ type Err = ParseXOnlyPublicKeyError;
+ fn from_str(s: &str) -> Result<Self, ParseXOnlyPublicKeyError> {
+ secp256k1::XOnlyPublicKey::from_str(s)
+ .map(Self::from)
+ .map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
+ }
+}
+
+impl From<secp256k1::XOnlyPublicKey> for XOnlyPublicKey {
+ fn from(pk: secp256k1::XOnlyPublicKey) -> Self { Self::from_secp(pk, Parity::Even) }
+}
+
+impl From<secp256k1::PublicKey> for XOnlyPublicKey {
+ fn from(pk: secp256k1::PublicKey) -> Self {
+ let (xonly, parity) = pk.x_only_public_key();
+ Self::from_secp(xonly, parity)
+ }
+}
+
+impl fmt::LowerHex for XOnlyPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self.as_inner(), f) }
+}
+// Allocate for serialized size
+impl_to_hex_from_lower_hex!(XOnlyPublicKey, |_| constants::SCHNORR_PUBLIC_KEY_SIZE * 2);
+
+impl fmt::Display for XOnlyPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self.as_inner(), f) }
+}
+
+// XOnlyPublicKey should serialize/deserialize identically to the inner type.
+#[cfg(feature = "serde")]
+impl Serialize for XOnlyPublicKey {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ <secp256k1::XOnlyPublicKey as Serialize>::serialize(self.as_inner(), serializer)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> Deserialize<'de> for XOnlyPublicKey {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ Ok(Self::from_secp(secp256k1::XOnlyPublicKey::deserialize(deserializer)?, Parity::Even))
+ }
+}
+
+impl Keypair {
+ /// Generates a new random key pair.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #[cfg(feature = "rand")]
+ /// # #[cfg(feature = "std")]
+ /// # {
+ /// use bitcoin_crypto::key::Keypair;
+ ///
+ /// let keypair = Keypair::generate();
+ /// # }
+ /// ```
+ #[inline]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ pub fn generate() -> Self {
+ let kp = secp256k1::Keypair::new(&mut rand::rng());
+ Self::from_secp(kp)
+ }
+
+ /// Constructs a [`Keypair`] from a [`PrivateKey`].
+ #[inline]
+ pub fn from_private_key(pk: &PrivateKey) -> Self {
+ Self::from(secp256k1::Keypair::from_secret_key(pk.as_inner()))
+ }
+
+ /// Returns a compressed [`PrivateKey`] for this [`Keypair`].
+ #[inline]
+ pub fn to_private_key(&self) -> PrivateKey {
+ PrivateKey::from_secp(secp256k1::SecretKey::from_keypair(self.as_inner()))
+ }
+
+ /// Returns the secret bytes for this [`Keypair`].
+ #[inline]
+ pub fn to_secret_bytes(&self) -> [u8; constants::SECRET_KEY_SIZE] {
+ self.as_inner().to_secret_bytes()
+ }
+
+ /// Returns the [`LegacyPublicKey`] for this [`Keypair`].
+ ///
+ /// This is equivalent to using [`LegacyPublicKey::from_keypair`].
+ #[inline]
+ pub fn to_public_key(&self) -> LegacyPublicKey { LegacyPublicKey::from_keypair(self) }
+
+ /// Returns the [`XOnlyPublicKey`] for this [`Keypair`].
+ ///
+ /// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
+ #[inline]
+ pub fn to_x_only_public_key(&self) -> XOnlyPublicKey { XOnlyPublicKey::from_keypair(self) }
+
+ /// Schnorr sign a message slice with this keypair.
+ ///
+ /// If the `rand` and `std` features are enabled, this function will randomly seed auxiliary
+ /// data. Otherwise, this will use no auxiliary data.
+ #[inline]
+ pub fn raw_bip340_sign(&self, msg: &[u8]) -> secp256k1::schnorr::Signature {
+ #[cfg(not(all(feature = "rand", feature = "std")))]
+ {
+ secp256k1::schnorr::sign_no_aux_rand(msg, self.as_inner())
+ }
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ {
+ secp256k1::schnorr::sign(msg, self.as_inner())
+ }
+ }
+
+ /// Schnorr sign a message slice with this keypair, using provided auxiliary random data.
+ #[inline]
+ pub fn raw_bip340_sign_with_aux_randomness(
+ &self,
+ msg: &[u8],
+ aux_rand: &[u8; 32],
+ ) -> secp256k1::schnorr::Signature {
+ secp256k1::schnorr::sign_with_aux_rand(msg, self.as_inner(), aux_rand)
+ }
+}
+
+impl FromStr for Keypair {
+ type Err = ParseKeypairError;
+ fn from_str(s: &str) -> Result<Self, ParseKeypairError> {
+ secp256k1::Keypair::from_str(s).map(Self::from).map_err(ParseKeypairError)
+ }
+}
+
+impl From<secp256k1::Keypair> for Keypair {
+ fn from(pk: secp256k1::Keypair) -> Self { Self::from_secp(pk) }
+}
+
+impl From<Keypair> for secp256k1::PublicKey {
+ fn from(kp: Keypair) -> Self { kp.to_public_key().to_inner() }
+}
+
+impl From<PrivateKey> for Keypair {
+ fn from(pk: PrivateKey) -> Self { Self::from(&pk) }
+}
+
+impl From<&PrivateKey> for Keypair {
+ fn from(pk: &PrivateKey) -> Self { Self::from_private_key(pk) }
+}
+
+impl LegacyPublicKey {
+ /// Constructs a new compressed ECDSA public key from the provided generic secp256k1 public key.
+ #[deprecated(since = "TBD", note = "use `from_secp` instead")]
+ pub fn new(key: impl Into<secp256k1::PublicKey>) -> Self { Self::from_secp(key) }
+
+ /// Constructs a new uncompressed (legacy) ECDSA public key from the provided generic secp256k1
+ /// public key.
+ #[deprecated(since = "TBD", note = "use `from_secp_uncompressed` instead")]
+ pub fn new_uncompressed(key: impl Into<secp256k1::PublicKey>) -> Self {
+ Self::from_secp_uncompressed(key)
+ }
+
+ /// Serializes the key as a byte-encoded pair of values.
+ ///
+ /// This will call the provided function with the key as a byte slice in either
+ /// compressed or uncompressed form.
+ ///
+ /// See [`LegacyPublicKey::serialize_compressed`] and [`LegacyPublicKey::serialize_uncompressed`]
+ /// for more information on the byte formats for the key.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use bitcoin_crypto::key::LegacyPublicKey;
+ /// use hashes::hash160;
+ ///
+ /// let key = "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
+ /// .parse::<LegacyPublicKey>()
+ /// .unwrap();
+ /// assert!(key.compressed());
+ /// let vec_out = key.with_serialized(<[_]>::to_vec);
+ /// assert_eq!(vec_out.len(), 33);
+ ///
+ /// let hash = key.with_serialized(hash160::Hash::hash).to_string();
+ /// assert_eq!(hash, "dabedb4de2bd2bfec5d38475b9c64af13999a043");
+ /// ```
+ pub fn with_serialized<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
+ if self.compressed() {
+ f(&self.serialize_compressed())
+ } else {
+ f(&self.serialize_uncompressed())
+ }
+ }
+
+ /// Serializes the key as a byte-encoded pair of values.
+ ///
+ /// This function serializes the key in compressed form, where the y-coordinate is
+ /// represented by only a single bit, as x determines it up to one bit.
+ ///
+ /// If you want to serialize while considering the compressedness of this key,
+ /// use [`with_serialized`] instead.
+ ///
+ /// [`with_serialized`]: LegacyPublicKey::with_serialized
+ pub fn serialize_compressed(&self) -> [u8; 33] { self.to_inner().serialize() }
+
+ /// Serializes the key as a byte-encoded pair of values, in uncompressed form.
+ ///
+ /// If you want to serialize while considering the compressedness of this key,
+ /// use [`with_serialized`] instead.
+ ///
+ /// [`with_serialized`]: LegacyPublicKey::with_serialized
+ pub fn serialize_uncompressed(&self) -> [u8; 65] { self.to_inner().serialize_uncompressed() }
+
+ /// Returns bitcoin 160-bit hash of the public key.
+ pub fn pubkey_hash(&self) -> PubkeyHash {
+ PubkeyHash(self.with_serialized(hash160::Hash::hash))
+ }
+
+ /// Returns bitcoin 160-bit hash of the public key for witness program
+ ///
+ /// # Errors
+ ///
+ /// Errors if this key is not compressed.
+ pub fn wpubkey_hash(&self) -> Result<WPubkeyHash, UncompressedPublicKeyError> {
+ if self.compressed() {
+ Ok(WPubkeyHash::from_byte_array(
+ hash160::Hash::hash(&self.to_inner().serialize()).to_byte_array(),
+ ))
+ } else {
+ Err(UncompressedPublicKeyError)
+ }
+ }
+
+ /// Converts this [`LegacyPublicKey`] into a [`FullPublicKey`] infallibly.
+ ///
+ /// Unlike the `TryFrom` implementation, this function will discard compressedness
+ /// information on the [`LegacyPublicKey`].
+ pub fn force_compressed(self) -> FullPublicKey { FullPublicKey::from_secp(self.to_inner()) }
+
+ /// Writes the public key into a writer.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the bytes fail to write to the provided writer.
+ pub fn write_into<W: Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
+ self.with_serialized(|bytes| writer.write_all(bytes))
+ }
+
+ /// Reads the public key from a reader.
+ ///
+ /// This internally reads the first byte before reading the rest, so
+ /// use of a `BufReader` is recommended.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the reader fails to read, or the read bytes are not a valid public key.
+ pub fn read_from<R: Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error> {
+ 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 no-std io
+ #[cfg(feature = "std")]
+ let reason = e;
+ #[cfg(not(feature = "std"))]
+ let reason = match e {
+ FromSliceError::Secp256k1(_) => "secp256k1 error",
+ FromSliceError::InvalidKeyPrefix(_) => "invalid key prefix",
+ FromSliceError::InvalidLength(_) => "invalid length",
+ };
+ io::Error::new(io::ErrorKind::InvalidData, reason)
+ })
+ }
+
+ /// Serializes the public key to bytes.
+ #[allow(clippy::missing_panics_doc)]
+ pub fn to_vec(self) -> Vec<u8> {
+ let mut buf = Vec::new();
+ self.write_into(&mut buf).expect("vecs don't error");
+ buf
+ }
+
+ /// Serializes the public key into a `SortKey`.
+ ///
+ /// `SortKey` is not too useful by itself, but it can be used to sort a
+ /// `[LegacyPublicKey]` slice using `sort_unstable_by_key`, `sort_by_cached_key`,
+ /// `sort_by_key`, or any of the other `*_by_key` methods on slice.
+ /// Pass the method into the sort method directly. (ie. `LegacyPublicKey::to_sort_key`)
+ ///
+ /// This method of sorting is in line with Bitcoin Core's implementation of
+ /// sorting keys for output descriptors such as `sortedmulti()`.
+ ///
+ /// If every `LegacyPublicKey` in the slice is `compressed == true` then this will sort
+ /// the keys in a
+ /// [BIP-0067](https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki)
+ /// compliant way.
+ ///
+ /// # Example: Using with `sort_unstable_by_key`
+ ///
+ /// ```rust
+ /// use bitcoin_crypto::key::LegacyPublicKey;
+ ///
+ /// let pk = |s: &str| s.parse::<LegacyPublicKey>().unwrap();
+ ///
+ /// let mut unsorted = [
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
+ /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
+ /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
+ /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
+ /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
+ /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
+ /// ];
+ /// let sorted = [
+ /// // These first 4 keys are in a BIP-0067 compatible sorted order
+ /// // (since they are compressed)
+ /// pk("0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68"),
+ /// pk("028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa"),
+ /// pk("032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b"),
+ /// pk("038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354"),
+ /// // Uncompressed keys are not BIP-0067 compliant, but are sorted
+ /// // after compressed keys in Bitcoin Core using `sortedmulti()`
+ /// pk("045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8"),
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa"),
+ /// pk("04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35"),
+ /// ];
+ ///
+ /// unsorted.sort_unstable_by_key(|k| LegacyPublicKey::to_sort_key(*k));
+ ///
+ /// assert_eq!(unsorted, sorted);
+ /// ```
+ pub fn to_sort_key(self) -> SortKey {
+ let buf = self.with_serialized(ArrayVec::from_slice);
+ SortKey(buf)
+ }
+
+ /// Deserializes a public key from a slice.
+ ///
+ /// # Errors
+ ///
+ /// * [`FromSliceError::InvalidLength`] if the slice has an invalid number of bytes.
+ /// * [`FromSliceError::InvalidKeyPrefix`] if the key prefix is invalid.
+ /// * [`FromSliceError::Secp256k1`] if the provided bytes do not form a valid public key.
+ pub fn from_slice(data: &[u8]) -> Result<Self, FromSliceError> {
+ let compressed = match data.len() {
+ 33 => true,
+ 65 => false,
+ len => {
+ return Err(FromSliceError::InvalidLength(len));
+ }
+ };
+
+ // Compressed keys must have a prefix byte of 2 or 3. Uncompressed must be 4
+ match (compressed, data[0]) {
+ (true, 0x02) => (),
+ (true, 0x03) => (),
+ (false, 0x04) => (),
+ (_, byte) => return Err(FromSliceError::InvalidKeyPrefix(byte)),
+ }
+
+ Ok(match compressed {
+ true => Self::from_secp(secp256k1::PublicKey::from_slice(data)?),
+ false => Self::from_secp_uncompressed(secp256k1::PublicKey::from_slice(data)?),
+ })
+ }
+
+ /// Computes the public key as supposed to be used with this secret.
+ pub fn from_private_key(sk: &PrivateKey) -> Self { sk.to_public_key() }
+
+ /// Extracts the public key from a Keypair
+ pub fn from_keypair(pair: &Keypair) -> Self { FullPublicKey::from_keypair(pair).into() }
+
+ /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key.
+ ///
+ /// # Errors
+ ///
+ /// [`secp256k1::Error::InvalidSignature`] if the signature is not valid for the given
+ /// [`Message`].
+ ///
+ /// [`Message`]: secp256k1::Message
+ pub fn verify(
+ &self,
+ msg: secp256k1::Message,
+ sig: ecdsa::Signature,
+ ) -> Result<(), secp256k1::Error> {
+ secp256k1::ecdsa::verify(&sig.signature, msg, &self.to_inner())
+ }
+}
+
+impl From<secp256k1::PublicKey> for LegacyPublicKey {
+ fn from(pk: secp256k1::PublicKey) -> Self { Self::from_secp(pk) }
+}
+
+impl From<LegacyPublicKey> for XOnlyPublicKey {
+ fn from(pk: LegacyPublicKey) -> Self {
+ let (xonly, parity) = pk.to_inner().x_only_public_key();
+ Self::from_secp(xonly, parity)
+ }
+}
+
+/// An opaque return type for [`LegacyPublicKey::to_sort_key`].
+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
+pub struct SortKey(ArrayVec<u8, 65>);
+
+impl fmt::Display for LegacyPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.with_serialized(|bytes| fmt::Display::fmt(&bytes.as_hex(), f))
+ }
+}
+
+impl FromStr for LegacyPublicKey {
+ type Err = ParsePublicKeyError;
+ fn from_str(s: &str) -> Result<Self, ParsePublicKeyError> {
+ match s.len() {
+ 66 => {
+ let bytes = hex::decode_to_array::<33>(s).map_err(|e| match e {
+ DecodeFixedLengthBytesError::InvalidChar(e) =>
+ ParsePublicKeyError::InvalidChar(e),
+ DecodeFixedLengthBytesError::InvalidLength(_) =>
+ unreachable!("length checked already"),
+ })?;
+ Ok(Self::from_slice(&bytes)?)
+ }
+ 130 => {
+ let bytes = hex::decode_to_array::<65>(s).map_err(|e| match e {
+ DecodeFixedLengthBytesError::InvalidChar(e) =>
+ ParsePublicKeyError::InvalidChar(e),
+ DecodeFixedLengthBytesError::InvalidLength(_) =>
+ unreachable!("length checked already"),
+ })?;
+ Ok(Self::from_slice(&bytes)?)
+ }
+ len => Err(ParsePublicKeyError::InvalidHexLength(len)),
+ }
+ }
+}
+
+hashes::hash_newtype! {
+ /// A hash of a public key.
+ pub struct PubkeyHash(hash160::Hash);
+ /// SegWit version of a public key hash.
+ pub struct WPubkeyHash(hash160::Hash);
+}
+
+hashes::impl_hex_for_newtype!(PubkeyHash, WPubkeyHash);
+#[cfg(feature = "serde")]
+hashes::impl_serde_for_newtype!(PubkeyHash, WPubkeyHash);
+
+impl From<LegacyPublicKey> for PubkeyHash {
+ fn from(key: LegacyPublicKey) -> Self { key.pubkey_hash() }
+}
+
+impl From<&LegacyPublicKey> for PubkeyHash {
+ fn from(key: &LegacyPublicKey) -> Self { key.pubkey_hash() }
+}
+
+impl FullPublicKey {
+ /// Returns bitcoin 160-bit hash of the public key.
+ pub fn pubkey_hash(&self) -> PubkeyHash { PubkeyHash(hash160::Hash::hash(&self.to_bytes())) }
+
+ /// Returns bitcoin 160-bit hash of the public key for witness program.
+ pub fn wpubkey_hash(&self) -> WPubkeyHash {
+ WPubkeyHash::from_byte_array(hash160::Hash::hash(&self.to_bytes()).to_byte_array())
+ }
+
+ /// Writes the public key into a writer.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the bytes fail to write to the provided writer.
+ pub fn write_into<W: io::Write + ?Sized>(&self, writer: &mut W) -> Result<(), io::Error> {
+ writer.write_all(&self.to_bytes())
+ }
+
+ /// Reads the public key from a reader.
+ ///
+ /// This internally reads the first byte before reading the rest, so
+ /// use of a `BufReader` is recommended.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the reader fails to read, or the read bytes are not a valid public key.
+ pub fn read_from<R: io::Read + ?Sized>(reader: &mut R) -> Result<Self, io::Error> {
+ let mut bytes = [0; 33];
+
+ reader.read_exact(&mut bytes)?;
+ #[allow(unused_variables)] // e when std not enabled
+ Self::from_bytes(bytes).map_err(|e| {
+ // Need a static string for no-std io
+ #[cfg(feature = "std")]
+ let reason = e;
+ #[cfg(not(feature = "std"))]
+ let reason = "secp256k1 error";
+ io::Error::new(io::ErrorKind::InvalidData, reason)
+ })
+ }
+
+ /// Serializes the public key.
+ ///
+ /// As the type name suggests, the key is serialized in compressed format.
+ ///
+ /// Note that this can be used as a sort key to get BIP-0067-compliant sorting.
+ /// That's why this type doesn't have the `to_sort_key` method - it would duplicate this one.
+ pub fn to_bytes(self) -> [u8; 33] { self.to_inner().serialize() }
+
+ /// Deserializes a public key from a slice.
+ ///
+ /// # Errors
+ ///
+ /// See [`secp256k1::PublicKey::from_slice`].
+ #[deprecated(
+ since = "TBD",
+ note = "use `from_bytes` instead; if you only have a slice, use `<&[u8; 33]>::try_from` first"
+ )]
+ pub fn from_slice(data: &[u8]) -> Result<Self, secp256k1::Error> {
+ let bytes_arr = data.try_into().map_err(|_| secp256k1::Error::InvalidPublicKey)?;
+ Self::from_bytes(bytes_arr)
+ }
+
+ /// Deserializes a public key from compressed pubkey bytes.
+ ///
+ /// # Errors
+ ///
+ /// See [`secp256k1::PublicKey::from_byte_array_compressed`].
+ pub fn from_bytes(data: [u8; 33]) -> Result<Self, secp256k1::Error> {
+ secp256k1::PublicKey::from_byte_array_compressed(data).map(Self::from_secp)
+ }
+
+ /// Computes the public key as supposed to be used with this secret.
+ ///
+ /// # Errors
+ ///
+ /// Errors if the private key is not compressed.
+ pub fn from_private_key(sk: &PrivateKey) -> Result<Self, UncompressedPublicKeyError> {
+ sk.to_public_key().try_into()
+ }
+
+ /// Extracts the public key from a Keypair
+ pub fn from_keypair(pair: &Keypair) -> Self {
+ Self::from_secp(secp256k1::PublicKey::from_keypair(pair.as_inner()))
+ }
+
+ /// Checks that `sig` is a valid ECDSA signature for `msg` using this public key.
+ ///
+ /// # Errors
+ ///
+ /// See [`LegacyPublicKey::verify`].
+ pub fn verify(
+ &self,
+ msg: secp256k1::Message,
+ sig: ecdsa::Signature,
+ ) -> Result<(), secp256k1::Error> {
+ Ok(secp256k1::ecdsa::verify(&sig.signature, msg, &self.to_inner())?)
+ }
+}
+
+impl fmt::Display for FullPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.to_bytes().as_hex(), f)
+ }
+}
+
+impl fmt::Debug for FullPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_fmt(format_args!("FullPublicKey({})", self))
+ }
+}
+
+impl FromStr for FullPublicKey {
+ type Err = ParseFullPublicKeyError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Self::from_bytes(hex::decode_to_array::<33>(s)?).map_err(Into::into)
+ }
+}
+
+impl TryFrom<LegacyPublicKey> for FullPublicKey {
+ type Error = UncompressedPublicKeyError;
+
+ fn try_from(value: LegacyPublicKey) -> Result<Self, Self::Error> {
+ if value.compressed() {
+ Ok(Self::from_secp(value.to_inner()))
+ } else {
+ Err(UncompressedPublicKeyError)
+ }
+ }
+}
+
+impl From<secp256k1::PublicKey> for FullPublicKey {
+ fn from(pk: secp256k1::PublicKey) -> Self { Self::from_secp(pk) }
+}
+
+impl From<FullPublicKey> for LegacyPublicKey {
+ fn from(value: FullPublicKey) -> Self { Self::from_secp(value.to_inner()) }
+}
+
+impl From<FullPublicKey> for XOnlyPublicKey {
+ fn from(pk: FullPublicKey) -> Self { pk.to_inner().into() }
+}
+
+impl From<FullPublicKey> for PubkeyHash {
+ fn from(key: FullPublicKey) -> Self { key.pubkey_hash() }
+}
+
+impl From<&FullPublicKey> for PubkeyHash {
+ fn from(key: &FullPublicKey) -> Self { key.pubkey_hash() }
+}
+
+impl From<FullPublicKey> for WPubkeyHash {
+ fn from(key: FullPublicKey) -> Self { key.wpubkey_hash() }
+}
+
+impl From<&FullPublicKey> for WPubkeyHash {
+ fn from(key: &FullPublicKey) -> Self { key.wpubkey_hash() }
+}
+
+impl PrivateKey {
+ /// Constructs a new compressed ECDSA private key using the secp256k1 algorithm and
+ /// a secure random number generator.
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ pub fn generate() -> Self {
+ let secret_key = secp256k1::SecretKey::new(&mut rand::rng());
+ Self::from_secp(secret_key)
+ }
+
+ /// Constructs a new public key from this private key.
+ pub fn to_public_key(&self) -> LegacyPublicKey {
+ match self.compressed() {
+ true =>
+ LegacyPublicKey::from_secp(secp256k1::PublicKey::from_secret_key(self.as_inner())),
+ false => LegacyPublicKey::from_secp_uncompressed(
+ secp256k1::PublicKey::from_secret_key(self.as_inner()),
+ ),
+ }
+ }
+
+ /// Constructs a new public key from this private key.
+ #[deprecated(since = "TBD", note = "use `to_public_key` instead")]
+ pub fn public_key(&self) -> LegacyPublicKey { self.to_public_key() }
+
+ /// Serializes the private key to bytes.
+ #[deprecated(since = "TBD", note = "use to_secret_vec instead")]
+ pub fn to_bytes(&self) -> Vec<u8> { self.to_secret_vec() }
+
+ /// Serializes the private key to bytes.
+ pub fn to_secret_vec(&self) -> Vec<u8> { self.to_secret_bytes().to_vec() }
+
+ /// Serializes the private key to bytes.
+ pub fn to_secret_bytes(&self) -> [u8; 32] { self.as_inner().to_secret_bytes() }
+
+ /// Deserializes a private key from a byte array.
+ ///
+ /// # Errors
+ ///
+ /// Errors when the secret key is invalid: when it is all-zeros or would exceed
+ /// the curve order when interpreted as a big-endian unsigned integer.
+ pub fn from_secret_bytes(data: &[u8; 32]) -> Result<Self, secp256k1::Error> {
+ Ok(Self::from_secp(secp256k1::SecretKey::from_secret_bytes(*data)?))
+ }
+
+ /// Deserializes a private key from a slice.
+ ///
+ /// # Errors
+ ///
+ /// [`secp256k1::Error::InvalidSecretKey`] if the slice is not 32 bytes long.
+ /// See [`from_secret_bytes`] for other errors.
+ ///
+ /// [`from_secret_bytes`]: PrivateKey::from_secret_bytes
+ #[deprecated(since = "TBD", note = "use from_secret_bytes instead")]
+ pub fn from_slice(
+ data: &[u8],
+ _network: impl Into<NetworkKind>,
+ ) -> Result<Self, secp256k1::Error> {
+ let array = data.try_into().map_err(|_| secp256k1::Error::InvalidSecretKey)?;
+ Self::from_secret_bytes(array)
+ }
+
+ /// Returns a new private key with the negated secret value.
+ ///
+ /// The resulting key corresponds to the same x-only public key (identical x-coordinate)
+ /// but with the opposite y-coordinate parity. This is useful for ensuring compatibility
+ /// with specific public key formats and BIP-0340 requirements.
+ #[inline]
+ #[must_use]
+ pub fn negate(&self) -> Self {
+ match self.compressed() {
+ true => Self::from_secp(self.as_inner().negate()),
+ false => Self::from_secp_uncompressed(self.as_inner().negate()),
+ }
+ }
+
+ /// ECDSA signs a [`Message`] with this private key.
+ ///
+ /// This functions grinds the nonce to produce a signature less than 71 bytes and compatible
+ /// with the low r signature implementation of bitcoin core.
+ ///
+ /// See [`secp256k1::ecdsa::sign_low_r`] for details.
+ ///
+ /// [`Message`]: secp256k1::Message
+ #[inline]
+ pub fn raw_ecdsa_sign(
+ &self,
+ msg: impl Into<secp256k1::Message>,
+ ) -> secp256k1::ecdsa::Signature {
+ secp256k1::ecdsa::sign_low_r(msg, self.as_inner())
+ }
+}
+
+/// A Bitcoin ECDSA private key with known network for WIF.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct WifKey {
+ /// The actual key
+ pub private_key: PrivateKey,
+ /// The network kind on which this key should be used.
+ pub network_kind: NetworkKind,
+}
+
+impl WifKey {
+ /// Constructs a new WIF private key from the provided [`PrivateKey`] and the
+ /// specified network.
+ pub fn new(key: PrivateKey, network: impl Into<NetworkKind>) -> Self {
+ Self { network_kind: network.into(), private_key: key }
+ }
+
+ /// Formats the private key to WIF format.
+ ///
+ /// # Errors
+ ///
+ /// Errors if `fmt` cannot be written to.
+ #[rustfmt::skip]
+ pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
+ let mut ret = [0; 34];
+ ret[0] = if self.network_kind.is_mainnet() { 128 } else { 239 };
+
+ ret[1..33].copy_from_slice(&self.private_key.as_inner()[..]);
+ let privkey = if self.private_key.compressed() {
+ ret[33] = 1;
+ base58::encode_check(&ret[..])
+ } else {
+ base58::encode_check(&ret[..33])
+ };
+ fmt.write_str(&privkey)
+ }
+
+ /// Gets the WIF encoding of this private key.
+ pub fn to_wif(&self) -> String {
+ let mut buf = String::new();
+ let _ = self.fmt_wif(&mut buf);
+ buf.shrink_to_fit();
+ buf
+ }
+
+ /// Parses the WIF encoded private key.
+ ///
+ /// # Errors
+ ///
+ /// * [`FromWifError::Base58`] if the string is not a valid base58 encoded string.
+ /// * [`FromWifError::InvalidBase58PayloadLength`] if the decoded base58 data is not 33 or 34
+ /// bytes long.
+ /// * [`FromWifError::InvalidWifCompressionFlag`] if the compression flag is not 1 for a 34 byte
+ /// data string.
+ /// * [`FromWifError::InvalidAddressVersion`] if the network version byte is not main or testnet.
+ /// * [`FromWifError::Secp256k1`] if the bytes are not representative of a valid private key.
+ pub fn from_wif(wif: &str) -> Result<Self, FromWifError> {
+ let data = base58::decode_check(wif)?;
+
+ let (compressed, data) = if let Ok(data) = <&[u8; 33]>::try_from(&*data) {
+ (false, data)
+ } else if let Ok(data) = <&[u8; 34]>::try_from(&*data) {
+ let (compressed_flag, data) = data.split_last::<33>();
+ if *compressed_flag != 1 {
+ return Err(InvalidWifCompressionFlagError { invalid: *compressed_flag }.into());
+ }
+ (true, data)
+ } else {
+ return Err(InvalidBase58PayloadLengthError { length: data.len() }.into());
+ };
+
+ let (network, key) = data.split_first();
+ let network = match *network {
+ 128 => NetworkKind::Main,
+ 239 => NetworkKind::Test,
+ invalid => {
+ return Err(InvalidAddressVersionError { invalid }.into());
+ }
+ };
+
+ let sec_key = secp256k1::SecretKey::from_secret_bytes(*key)?;
+ let priv_key = match compressed {
+ true => PrivateKey::from_secp(sec_key),
+ false => PrivateKey::from_secp_uncompressed(sec_key),
+ };
+ Ok(Self::new(priv_key, network))
+ }
+}
+
+// [`WifKey`] intentionally has a `FromStr` without a reciprocal `Display`.
+// Parsing from a WIF string should be convenient, printing secret data should not.
+impl FromStr for WifKey {
+ type Err = FromWifError;
+ fn from_str(s: &str) -> Result<Self, FromWifError> { Self::from_wif(s) }
+}
+
+#[cfg(feature = "serde")]
+impl serde::Serialize for WifKey {
+ fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
+ s.serialize_str(&self.to_wif())
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> serde::Deserialize<'de> for WifKey {
+ fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+ struct WifVisitor;
+
+ impl serde::de::Visitor<'_> for WifVisitor {
+ type Value = WifKey;
+
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ formatter.write_str("an ASCII WIF string")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if let Ok(s) = core::str::from_utf8(v) {
+ s.parse::<WifKey>().map_err(E::custom)
+ } else {
+ Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
+ }
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ v.parse::<WifKey>().map_err(E::custom)
+ }
+ }
+
+ d.deserialize_str(WifVisitor)
+ }
+}
+
+#[cfg(feature = "serde")]
+#[allow(clippy::collapsible_else_if)] // Aids readability.
+impl serde::Serialize for LegacyPublicKey {
+ fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
+ if s.is_human_readable() {
+ s.collect_str(self)
+ } else {
+ self.with_serialized(|bytes| s.serialize_bytes(bytes))
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> serde::Deserialize<'de> for LegacyPublicKey {
+ fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+ if d.is_human_readable() {
+ struct HexVisitor;
+
+ impl serde::de::Visitor<'_> for HexVisitor {
+ type Value = LegacyPublicKey;
+
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ formatter.write_str("an ASCII hex string")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if let Ok(hex) = core::str::from_utf8(v) {
+ hex.parse::<LegacyPublicKey>().map_err(E::custom)
+ } else {
+ Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
+ }
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ v.parse::<LegacyPublicKey>().map_err(E::custom)
+ }
+ }
+ d.deserialize_str(HexVisitor)
+ } else {
+ struct BytesVisitor;
+
+ impl serde::de::Visitor<'_> for BytesVisitor {
+ type Value = LegacyPublicKey;
+
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ formatter.write_str("a bytestring")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ LegacyPublicKey::from_slice(v).map_err(E::custom)
+ }
+ }
+
+ d.deserialize_bytes(BytesVisitor)
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+impl serde::Serialize for FullPublicKey {
+ fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
+ if s.is_human_readable() {
+ s.collect_str(self)
+ } else {
+ s.serialize_bytes(&self.to_bytes())
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> serde::Deserialize<'de> for FullPublicKey {
+ fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
+ if d.is_human_readable() {
+ struct HexVisitor;
+
+ impl serde::de::Visitor<'_> for HexVisitor {
+ type Value = FullPublicKey;
+
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ formatter.write_str("a 66 digits long ASCII hex string")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if let Ok(hex) = core::str::from_utf8(v) {
+ hex.parse::<FullPublicKey>().map_err(E::custom)
+ } else {
+ Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
+ }
+ }
+
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ v.parse::<FullPublicKey>().map_err(E::custom)
+ }
+ }
+ d.deserialize_str(HexVisitor)
+ } else {
+ struct BytesVisitor;
+
+ impl serde::de::Visitor<'_> for BytesVisitor {
+ type Value = FullPublicKey;
+
+ fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
+ formatter.write_str("a bytestring")
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ let arr = v.try_into().map_err(E::custom)?;
+ FullPublicKey::from_bytes(arr).map_err(E::custom)
+ }
+ }
+
+ d.deserialize_bytes(BytesVisitor)
+ }
+ }
+}
+/// Untweaked BIP-0340 X-coord-only public key.
+pub type UntweakedPublicKey = XOnlyPublicKey;
+
+impl fmt::LowerHex for TweakedPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::LowerHex::fmt(self.as_x_only_public_key(), f)
+ }
+}
+// Allocate for serialized size
+impl_to_hex_from_lower_hex!(TweakedPublicKey, |_| constants::SCHNORR_PUBLIC_KEY_SIZE * 2);
+
+impl fmt::Display for TweakedPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self.as_x_only_public_key(), f)
+ }
+}
+
+/// Untweaked BIP-0340 key pair.
+pub type UntweakedKeypair = Keypair;
+
+impl TweakedPublicKey {
+ /// Returns the underlying public key.
+ #[inline]
+ #[doc(hidden)]
+ #[deprecated(since = "0.32.6", note = "use to_x_only_public_key() instead")]
+ pub fn to_inner(self) -> XOnlyPublicKey { self.to_x_only_public_key() }
+
+ /// Serializes the key as a byte-encoded x coordinate value (32 bytes).
+ #[inline]
+ pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
+ self.as_x_only_public_key().serialize().0
+ }
+}
+
+impl TweakedKeypair {
+ /// Returns the underlying key pair.
+ #[inline]
+ #[doc(hidden)]
+ #[deprecated(since = "0.32.6", note = "use into_keypair() instead")]
+ pub fn to_inner(self) -> Keypair { self.into_keypair() }
+
+ /// Returns the [`TweakedPublicKey`] and its [`Parity`] for this [`TweakedKeypair`].
+ #[inline]
+ pub fn public_parts(&self) -> (TweakedPublicKey, Parity) {
+ let xonly = self.as_keypair().to_x_only_public_key();
+ (TweakedPublicKey::dangerous_assume_tweaked(xonly), xonly.parity())
+ }
+}
+
+impl From<TweakedPublicKey> for XOnlyPublicKey {
+ #[inline]
+ fn from(pair: TweakedPublicKey) -> Self { pair.to_x_only_public_key() }
+}
+
+impl From<TweakedKeypair> for Keypair {
+ #[inline]
+ fn from(pair: TweakedKeypair) -> Self { pair.into_keypair() }
+}
+
+impl<'a> From<&'a TweakedKeypair> for &'a Keypair {
+ #[inline]
+ fn from(pair: &'a TweakedKeypair) -> Self { pair.as_keypair() }
+}
+
+impl From<TweakedKeypair> for TweakedPublicKey {
+ #[inline]
+ fn from(pair: TweakedKeypair) -> Self { Self::from(&pair) }
+}
+
+impl From<&TweakedKeypair> for TweakedPublicKey {
+ #[inline]
+ fn from(pair: &TweakedKeypair) -> Self { Self::from_keypair(pair) }
+}
+
+impl SerializedXOnlyPublicKey {
+ /// Returns `XOnlyPublicKey` if the bytes are valid.
+ ///
+ /// # Errors
+ ///
+ /// [`ParseXOnlyPublicKeyError::InvalidXCoordinate`] if the provided bytes don't represent
+ /// a valid secp256k1 point x-coordinate.
+ pub fn to_validated(self) -> Result<XOnlyPublicKey, ParseXOnlyPublicKeyError> {
+ XOnlyPublicKey::from_byte_array(self.as_byte_array())
+ }
+}
+
+impl AsRef<[u8; 32]> for SerializedXOnlyPublicKey {
+ fn as_ref(&self) -> &[u8; 32] { self.as_byte_array() }
+}
+
+impl From<&Self> for SerializedXOnlyPublicKey {
+ fn from(borrowed: &Self) -> Self { *borrowed }
+}
+
+impl fmt::Debug for SerializedXOnlyPublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.as_byte_array().as_hex(), f)
+ }
+}
+
+/// Error types for bitcoin keys.
+pub mod error {
+ use core::convert::Infallible;
+ use core::fmt;
+
+ use internals::write_err;
+
+ /// Error returned while generating key from slice.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ #[non_exhaustive]
+ pub enum FromSliceError {
+ /// Invalid key prefix error.
+ InvalidKeyPrefix(u8),
+ /// A secp256k1 error.
+ Secp256k1(secp256k1::Error),
+ /// Invalid Length of the slice.
+ InvalidLength(usize),
+ }
+
+ impl From<Infallible> for FromSliceError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for FromSliceError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Secp256k1(e) => write_err!(f, "secp256k1"; e),
+ Self::InvalidKeyPrefix(b) => write!(f, "key prefix invalid: {}", b),
+ Self::InvalidLength(got) =>
+ write!(f, "slice length should be 33 or 65 bytes, got: {}", got),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for FromSliceError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Secp256k1(ref e) => Some(e),
+ Self::InvalidKeyPrefix(_) | Self::InvalidLength(_) => None,
+ }
+ }
+ }
+
+ impl From<secp256k1::Error> for FromSliceError {
+ fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
+ }
+
+ /// Error generated from WIF key format.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ #[non_exhaustive]
+ pub enum FromWifError {
+ /// A base58 decoding error.
+ Base58(base58::Error),
+ /// Base58 decoded data was an invalid length.
+ InvalidBase58PayloadLength(InvalidBase58PayloadLengthError),
+ /// Base58 decoded data contained an invalid address version byte.
+ InvalidAddressVersion(InvalidAddressVersionError),
+ /// A secp256k1 error.
+ Secp256k1(secp256k1::Error),
+ /// Invalid WIF compression flag.
+ InvalidWifCompressionFlag(InvalidWifCompressionFlagError),
+ }
+
+ impl From<Infallible> for FromWifError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for FromWifError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Base58(ref e) => write_err!(f, "invalid base58"; e),
+ Self::InvalidBase58PayloadLength(ref e) =>
+ write_err!(f, "decoded base58 data was an invalid length"; e),
+ Self::InvalidAddressVersion(ref e) =>
+ write_err!(f, "decoded base58 data contained an invalid address version byte"; e),
+ Self::Secp256k1(ref e) => write_err!(f, "private key validation failed"; e),
+ Self::InvalidWifCompressionFlag(ref e) =>
+ write_err!(f, "invalid WIF compression flag"; e),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for FromWifError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Base58(ref e) => Some(e),
+ Self::InvalidBase58PayloadLength(ref e) => Some(e),
+ Self::InvalidAddressVersion(ref e) => Some(e),
+ Self::Secp256k1(ref e) => Some(e),
+ Self::InvalidWifCompressionFlag(ref e) => Some(e),
+ }
+ }
+ }
+
+ impl From<base58::Error> for FromWifError {
+ fn from(e: base58::Error) -> Self { Self::Base58(e) }
+ }
+
+ impl From<secp256k1::Error> for FromWifError {
+ fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
+ }
+
+ impl From<InvalidBase58PayloadLengthError> for FromWifError {
+ fn from(e: InvalidBase58PayloadLengthError) -> Self { Self::InvalidBase58PayloadLength(e) }
+ }
+
+ impl From<InvalidAddressVersionError> for FromWifError {
+ fn from(e: InvalidAddressVersionError) -> Self { Self::InvalidAddressVersion(e) }
+ }
+
+ impl From<InvalidWifCompressionFlagError> for FromWifError {
+ fn from(e: InvalidWifCompressionFlagError) -> Self { Self::InvalidWifCompressionFlag(e) }
+ }
+
+ /// Error returned while constructing a [`Keypair`] from string.
+ ///
+ /// [`Keypair`]: super::Keypair
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct ParseKeypairError(pub(super) secp256k1::Error);
+
+ impl From<Infallible> for ParseKeypairError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for ParseKeypairError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write_err!(f, "parse keypair failed"; self.0)
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for ParseKeypairError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.0) }
+ }
+
+ /// Error returned while constructing public key from string.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub enum ParsePublicKeyError {
+ /// Error originated while parsing string.
+ Encoding(FromSliceError),
+ /// Hex decoding error.
+ InvalidChar(hex::error::InvalidCharError),
+ /// `LegacyPublicKey` hex should be 66 or 130 digits long.
+ InvalidHexLength(usize),
+ }
+
+ impl From<Infallible> for ParsePublicKeyError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for ParsePublicKeyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Encoding(ref e) => write_err!(f, "string error"; e),
+ Self::InvalidChar(ref e) => write_err!(f, "hex decoding"; e),
+ Self::InvalidHexLength(got) =>
+ write!(f, "pubkey string should be 66 or 130 digits long, got: {}", got),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for ParsePublicKeyError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Encoding(ref e) => Some(e),
+ Self::InvalidChar(ref e) => Some(e),
+ Self::InvalidHexLength(_) => None,
+ }
+ }
+ }
+
+ impl From<FromSliceError> for ParsePublicKeyError {
+ fn from(e: FromSliceError) -> Self { Self::Encoding(e) }
+ }
+
+ /// Error returned when parsing a [`FullPublicKey`] from a string.
+ ///
+ /// [`FullPublicKey`]: super::FullPublicKey
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub enum ParseFullPublicKeyError {
+ /// secp256k1 Error.
+ Secp256k1(secp256k1::Error),
+ /// hex to array conversion error.
+ Hex(hex::DecodeFixedLengthBytesError),
+ }
+
+ impl From<Infallible> for ParseFullPublicKeyError {
+ fn from(never: Infallible) -> Self { match never {} }
+ }
+
+ impl fmt::Display for ParseFullPublicKeyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::Secp256k1(e) => write_err!(f, "secp256k1 error"; e),
+ Self::Hex(e) => write_err!(f, "invalid hex"; e),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for ParseFullPublicKeyError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::Secp256k1(e) => Some(e),
+ Self::Hex(e) => Some(e),
+ }
+ }
+ }
+
+ impl From<secp256k1::Error> for ParseFullPublicKeyError {
+ fn from(e: secp256k1::Error) -> Self { Self::Secp256k1(e) }
+ }
+
+ impl From<hex::DecodeFixedLengthBytesError> for ParseFullPublicKeyError {
+ fn from(e: hex::DecodeFixedLengthBytesError) -> Self { Self::Hex(e) }
+ }
+
+ /// SegWit public keys must always be compressed.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ #[non_exhaustive]
+ pub struct UncompressedPublicKeyError;
+
+ impl fmt::Display for UncompressedPublicKeyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("SegWit public keys must always be compressed")
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for UncompressedPublicKeyError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+ }
+
+ /// Decoded base58 data was an invalid length.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct InvalidBase58PayloadLengthError {
+ /// The base58 payload length we got after decoding WIF string.
+ pub(crate) length: usize,
+ }
+
+ impl InvalidBase58PayloadLengthError {
+ /// Returns the invalid payload length.
+ pub fn invalid_base58_payload_length(&self) -> usize { self.length }
+ }
+
+ impl fmt::Display for InvalidBase58PayloadLengthError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "decoded base58 data was an invalid length: {} (expected 33 or 34)",
+ self.length
+ )
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for InvalidBase58PayloadLengthError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+ }
+
+ /// Invalid address version in decoded base58 data.
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct InvalidAddressVersionError {
+ /// The invalid version.
+ pub(crate) invalid: u8,
+ }
+
+ impl InvalidAddressVersionError {
+ /// Returns the invalid version.
+ pub fn invalid_address_version(&self) -> u8 { self.invalid }
+ }
+
+ impl fmt::Display for InvalidAddressVersionError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "invalid address version in decoded base58 data {}", self.invalid)
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for InvalidAddressVersionError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+ }
+
+ /// Invalid compression flag for a WIF key
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct InvalidWifCompressionFlagError {
+ /// The invalid compression flag.
+ pub(crate) invalid: u8,
+ }
+
+ impl InvalidWifCompressionFlagError {
+ /// Returns the invalid compression flag.
+ pub fn invalid_compression_flag(&self) -> u8 { self.invalid }
+ }
+
+ impl fmt::Display for InvalidWifCompressionFlagError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "invalid WIF compression flag. Expected a 0x01 byte at the end of the key but found: {}", self.invalid)
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for InvalidWifCompressionFlagError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None }
+ }
+
+ /// Error that can occur when parsing an [`XOnlyPublicKey`] from bytes.
+ ///
+ /// [`XOnlyPublicKey`]: super::XOnlyPublicKey
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub enum ParseXOnlyPublicKeyError {
+ /// The provided bytes do not represent a valid secp256k1 point x-coordinate.
+ InvalidXCoordinate,
+ }
+
+ impl fmt::Display for ParseXOnlyPublicKeyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::InvalidXCoordinate => write!(f, "Invalid X coordinate for secp256k1 point"),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for ParseXOnlyPublicKeyError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::InvalidXCoordinate => None,
+ }
+ }
+ }
+
+ /// Error that can occur when tweaking an [`XOnlyPublicKey`].
+ ///
+ /// [`XOnlyPublicKey`]: super::XOnlyPublicKey
+ #[derive(Debug, Clone, PartialEq, Eq)]
+ pub enum TweakXOnlyPublicKeyError {
+ /// The tweak value was invalid.
+ BadTweak,
+ /// The resulting public key would be invalid.
+ ResultKeyInvalid,
+ /// Invalid parity value encountered during the operation.
+ ParityError,
+ }
+
+ impl fmt::Display for TweakXOnlyPublicKeyError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Self::BadTweak => write!(f, "Invalid tweak value"),
+ Self::ResultKeyInvalid => write!(f, "Resulting public key would be invalid"),
+ Self::ParityError => write!(f, "Invalid parity value encountered"),
+ }
+ }
+ }
+
+ #[cfg(feature = "std")]
+ impl std::error::Error for TweakXOnlyPublicKeyError {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ match self {
+ Self::BadTweak => None,
+ Self::ResultKeyInvalid => None,
+ Self::ParityError => None,
+ }
+ }
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for LegacyPublicKey {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_secp(secp256k1::PublicKey::arbitrary(u)?))
+ }
+}
+
+#[cfg(feature = "arbitrary")]
+impl<'a> Arbitrary<'a> for XOnlyPublicKey {
+ fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
+ Ok(Self::from_secp(secp256k1::XOnlyPublicKey::arbitrary(u)?, u.arbitrary()?))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use alloc::string::ToString;
+ use alloc::{format, vec};
+
+ use super::*;
+
+ #[test]
+ fn pubkey_hash() {
+ let pk = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
+ .parse::<LegacyPublicKey>()
+ .unwrap();
+ let upk = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"
+ .parse::<LegacyPublicKey>().unwrap();
+ assert_eq!(pk.pubkey_hash().to_string(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
+ assert_eq!(upk.pubkey_hash().to_string(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a");
+ }
+
+ #[test]
+ fn wpubkey_hash() {
+ let pk = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af"
+ .parse::<LegacyPublicKey>()
+ .unwrap();
+ let upk = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133".parse::<LegacyPublicKey>().unwrap();
+ assert_eq!(
+ pk.wpubkey_hash().unwrap().to_string(),
+ "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4"
+ );
+ assert!(upk.wpubkey_hash().is_err());
+ }
+
+ #[test]
+ #[cfg(feature = "serde")]
+ fn skey_serde() {
+ use serde_test::{assert_tokens, Configure, Token};
+
+ static KEY_WIF: &str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
+ static PK_STR: &str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef";
+ static PK_STR_U: &str = "\
+ 04\
+ 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\
+ 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\
+ ";
+ #[rustfmt::skip]
+ 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,
+ ];
+ #[rustfmt::skip]
+ 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 wk = KEY_WIF.parse::<WifKey>().unwrap();
+ let pk = LegacyPublicKey::from_private_key(&wk.private_key);
+ let pk_u = LegacyPublicKey::from_secp_uncompressed(pk.to_inner());
+
+ assert_tokens(&wk, &[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) -> LegacyPublicKey {
+ 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) = LegacyPublicKey::from_slice(&data[..]) {
+ return key;
+ }
+ } else {
+ data[0] = 2 + (data[0] >> 7);
+ if let Ok(key) = LegacyPublicKey::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 reader = v.as_slice();
+ let mut dec_keys = vec![];
+ for _ in 0..N_KEYS {
+ dec_keys.push(LegacyPublicKey::read_from(&mut reader).expect("reading from vec"));
+ }
+ assert_eq!(keys, dec_keys);
+ assert!(LegacyPublicKey::read_from(&mut reader).is_err());
+
+ // sanity checks
+ let mut empty: &[u8] = &[];
+ assert!(LegacyPublicKey::read_from(&mut empty).is_err());
+ assert!(LegacyPublicKey::read_from(&mut &[0; 33][..]).is_err());
+ assert!(LegacyPublicKey::read_from(&mut &[2; 32][..]).is_err());
+ assert!(LegacyPublicKey::read_from(&mut &[0; 65][..]).is_err());
+ assert!(LegacyPublicKey::read_from(&mut &[4; 64][..]).is_err());
+ }
+
+ #[test]
+ fn pubkey_to_sort_key() {
+ let key1 = "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8"
+ .parse::<LegacyPublicKey>()
+ .unwrap();
+ let key2 = LegacyPublicKey::from_secp_uncompressed(key1.to_inner());
+ let arrayvec1 = ArrayVec::from_slice(
+ &hex::decode_to_array::<33>(
+ "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
+ )
+ .unwrap(),
+ );
+ let expected1 = SortKey(arrayvec1);
+ let arrayvec2 = ArrayVec::from_slice(&hex::decode_to_array::<65>(
+ "04ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f81794e7f3d5e420641a3bc690067df5541470c966cbca8c694bf39aa16d836918",
+ ).unwrap());
+ let expected2 = SortKey(arrayvec2);
+ assert_eq!(key1.to_sort_key(), expected1);
+ assert_eq!(key2.to_sort_key(), expected2);
+ }
+
+ #[test]
+ fn pubkey_sort() {
+ struct Vector {
+ input: Vec<LegacyPublicKey>,
+ expect: Vec<LegacyPublicKey>,
+ }
+ let fmt = |v: Vec<_>| {
+ v.into_iter().map(|s: &str| s.parse::<LegacyPublicKey>().unwrap()).collect::<Vec<_>>()
+ };
+ let vectors = vec![
+ // Start BIP-0067 vectors
+ // Vector 1
+ Vector {
+ input: fmt(vec![
+ "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
+ "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
+ ]),
+ expect: fmt(vec![
+ "02fe6f0a5a297eb38c391581c4413e084773ea23954d93f7753db7dc0adc188b2f",
+ "02ff12471208c14bd580709cb2358d98975247d8765f92bc25eab3b2763ed605f8",
+ ]),
+ },
+ // Vector 2 (Already sorted, no action required)
+ Vector {
+ input: fmt(vec![
+ "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
+ "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
+ "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
+ ]),
+ expect: fmt(vec![
+ "02632b12f4ac5b1d1b72b2a3b508c19172de44f6f46bcee50ba33f3f9291e47ed0",
+ "027735a29bae7780a9755fae7a1c4374c656ac6a69ea9f3697fda61bb99a4f3e77",
+ "02e2cc6bd5f45edd43bebe7cb9b675f0ce9ed3efe613b177588290ad188d11b404",
+ ]),
+ },
+ // Vector 3
+ Vector {
+ input: fmt(vec![
+ "030000000000000000000000000000000000004141414141414141414141414141",
+ "020000000000000000000000000000000000004141414141414141414141414141",
+ "020000000000000000000000000000000000004141414141414141414141414140",
+ "030000000000000000000000000000000000004141414141414141414141414140",
+ ]),
+ expect: fmt(vec![
+ "020000000000000000000000000000000000004141414141414141414141414140",
+ "020000000000000000000000000000000000004141414141414141414141414141",
+ "030000000000000000000000000000000000004141414141414141414141414140",
+ "030000000000000000000000000000000000004141414141414141414141414141",
+ ]),
+ },
+ // Vector 4: (from bitcore)
+ Vector {
+ input: fmt(vec![
+ "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
+ "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
+ "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
+ ]),
+ expect: fmt(vec![
+ "021f2f6e1e50cb6a953935c3601284925decd3fd21bc445712576873fb8c6ebc18",
+ "022df8750480ad5b26950b25c7ba79d3e37d75f640f8e5d9bcd5b150a0f85014da",
+ "03e3818b65bcc73a7d64064106a859cc1a5a728c4345ff0b641209fba0d90de6e9",
+ ]),
+ },
+ // Non-BIP67 vectors
+ Vector {
+ input: fmt(vec![
+ "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
+ "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
+ "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
+ "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
+ "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
+ "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
+ "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
+ "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
+ "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
+ "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
+ "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
+ "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
+ "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
+ "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
+ ]),
+ expect: fmt(vec![
+ "0234dd69c56c36a41230d573d68adeae0030c9bc0bf26f24d3e1b64c604d293c68",
+ "028bde91b10013e08949a318018fedbd896534a549a278e220169ee2a36517c7aa",
+ "028d3a2d9f1b1c5c75845944f93bc183ba23aecde53f1978b8aa1b77661be6114f",
+ "028e1c947c8c0b8ed021088b8e981491ac7af2b8fabebea1abdb448424c8ed75b7",
+ "02c690d642c1310f3a1ababad94e3930e4023c930ea472e7f37f660fe485263b88",
+ "03004a8a3d242d7957c0b60fb7208d386fa6a0193aabd1f3f095ffd0ac097e447b",
+ "032b8324c93575034047a52e9bca05a46d8347046b91a032eff07d5de8d3f2730b",
+ "038f47dcd43ba6d97fc9ed2e3bba09b175a45fac55f0683e8cf771e8ced4572354",
+ "03e1a1cfa9eaff604ae237b7af31ffe4c01be22eb96f3da0e62c5850dd4b4386c1",
+ "041a181bd0e79974bd7ca552e09fc42ba9c3d5dbb3753741d6f0ab3015dbfd9a22d6b001a32f5f51ac6f2c0f35e73a6a62f59e848fa854d3d21f3f231594eeaa46",
+ "04516cde23e14f2319423b7a4a7ae48b1dadceb5e9c123198d417d10895684c42eb05e210f90ccbc72448803a22312e3f122ff2939956ccef4f7316f836295ddd5",
+ "045d753414fa292ea5b8f56e39cfb6a0287b2546231a5cb05c4b14ab4b463d171f5128148985b23eccb1e2905374873b1f09b9487f47afa6b1f2b0083ac8b4f7e8",
+ // These two pubkeys are mirrored. This helps verify the sort past the x value.
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc3816753d96001fd7cba3ce5372f5c9a0d63708183033538d07b1e532fc43aaacfa",
+ "04c4b0bbb339aa236bff38dbe6a451e111972a7909a126bc424013cba2ec33bc38e98ac269ffe028345c31ac8d0a365f29c8f7e7cfccac72f84e1acd02bc554f35",
+ "04c6bec3b07586a4b085a78cbb97e9bab6f1d3c9ebf299b65dec85213c5eacd44487de86017183120bb7ea3b6c6660c5037615fe1add2a73f800cbeeae22c60438",
+ "04eb0db2d71ccbb0edd8fb35092cbcae2f7fa1f06d4c170804bf52007924b569a8d2d6f6bc8fd2b3caa3253fa1bb674443743bf7fb9f94f9c0b0831a252894cfa8",
+ ]),
+ },
+ ];
+ for mut vector in vectors {
+ vector.input.sort_by_cached_key(|k| LegacyPublicKey::to_sort_key(*k));
+ assert_eq!(vector.input, vector.expect);
+ }
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ fn public_key_constructors() {
+ let kp = Keypair::generate();
+
+ let _ = LegacyPublicKey::from_secp(kp.clone());
+ let _ = LegacyPublicKey::from_secp_uncompressed(kp);
+ }
+
+ #[test]
+ fn public_key_from_str_wrong_length() {
+ // Sanity checks, we accept string length 130 digits.
+ let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
+ assert_eq!(s.len(), 130);
+ assert!(s.parse::<LegacyPublicKey>().is_ok());
+ // And 66 digits.
+ let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af";
+ assert_eq!(s.len(), 66);
+ assert!(s.parse::<LegacyPublicKey>().is_ok());
+
+ let s = "aoeusthb";
+ assert_eq!(s.len(), 8);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ assert_eq!(res.unwrap_err(), ParsePublicKeyError::InvalidHexLength(8));
+ }
+
+ #[test]
+ fn public_key_from_str_invalid_str() {
+ // Ensuring test cases fail when LegacyPublicKey::from_str is used on invalid keys
+ let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b142";
+ assert_eq!(s.len(), 130);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err(),
+ ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
+ secp256k1::Error::InvalidPublicKey
+ ))
+ );
+
+ let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd169";
+ assert_eq!(s.len(), 66);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err(),
+ ParsePublicKeyError::Encoding(FromSliceError::Secp256k1(
+ secp256k1::Error::InvalidPublicKey
+ ))
+ );
+
+ let s = "062e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133";
+ assert_eq!(s.len(), 130);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ assert_eq!(
+ res.unwrap_err(),
+ ParsePublicKeyError::Encoding(FromSliceError::InvalidKeyPrefix(6))
+ );
+
+ let s = "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b13g";
+ assert_eq!(s.len(), 130);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ if let Err(ParsePublicKeyError::InvalidChar(err)) = res {
+ assert_eq!(err.pos(), 129);
+ } else {
+ panic!("expected ParsePublicKeyError::InvalidChar");
+ }
+
+ let s = "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1ag";
+ assert_eq!(s.len(), 66);
+ let res = s.parse::<LegacyPublicKey>();
+ assert!(res.is_err());
+ if let Err(ParsePublicKeyError::InvalidChar(err)) = res {
+ assert_eq!(err.pos(), 65);
+ } else {
+ panic!("expected ParsePublicKeyError::InvalidChar");
+ }
+ }
+
+ #[test]
+ #[allow(deprecated)] // tests the deprecated function
+ #[allow(deprecated_in_future)]
+ fn invalid_private_key_len() {
+ use network::Network;
+ assert!(PrivateKey::from_slice(&[1u8; 31], Network::Regtest).is_err());
+ assert!(PrivateKey::from_slice(&[1u8; 33], Network::Regtest).is_err());
+ }
+
+ #[test]
+ fn xonly_pubkey_from_bytes() {
+ let key_bytes = &hex::decode_to_array::<32>(
+ "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d",
+ )
+ .expect("Failed to convert hex string to byte array");
+ let xonly_pub_key = XOnlyPublicKey::from_byte_array(key_bytes)
+ .expect("Failed to create an XOnlyPublicKey from a byte array");
+ // Confirm that the public key from bytes serializes back to the same bytes
+ assert_eq!(&xonly_pub_key.serialize().0, key_bytes);
+ }
+
+ #[test]
+ fn xonly_pubkey_to_inner() {
+ let key_bytes = &hex::decode_to_array::<32>(
+ "5b1e57ec453cd33fdc7cfc901450a3931fd315422558f2fb7fefb064e6e7d60d",
+ )
+ .expect("Failed to convert hex string to byte array");
+ let inner_key = secp256k1::XOnlyPublicKey::from_byte_array(*key_bytes)
+ .expect("Failed to create a secp256k1 x-only public key from a byte array");
+ let btc_pubkey = XOnlyPublicKey::from(inner_key);
+ // Confirm that the to_inner() returns the same data that was initially wrapped
+ assert_eq!(inner_key, btc_pubkey.to_inner());
+ }
+
+ #[test]
+ fn keypair_from_str_roundtrip() {
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ let keypair = Keypair::generate();
+ #[cfg(not(all(feature = "rand", feature = "std")))]
+ let keypair = {
+ let bytes = hex::decode_to_array::<32>(
+ "1ede31b0e7e47c2afc65ffd158b1b1b9d3b752bba8fd117dc8b9e944a390e8d9",
+ )
+ .unwrap();
+ let sk = PrivateKey::from_secret_bytes(&bytes).unwrap();
+ Keypair::from_private_key(&sk)
+ };
+
+ // Use secp256k1::DisplaySecret, since no key type implements Display
+ let encoded = format!("{}", keypair.as_inner().display_secret());
+ let decoded = encoded.parse::<Keypair>().unwrap();
+ assert_eq!(decoded, keypair);
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ fn keypair_secp_roundtrip() {
+ let bitcoin_key = Keypair::generate();
+ let secp_key =
+ secp256k1::Keypair::from_seckey_byte_array(bitcoin_key.to_secret_bytes()).unwrap();
+ assert_eq!(Keypair::from_secp(secp_key), bitcoin_key);
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ fn public_key_secp_roundtrip() {
+ let bitcoin_key = Keypair::generate().to_public_key();
+ let secp_key =
+ secp256k1::PublicKey::from_byte_array_compressed(bitcoin_key.serialize_compressed())
+ .unwrap();
+ assert_eq!(LegacyPublicKey::from_secp(secp_key), bitcoin_key);
+ // Also assert that generating a secp from compressed or uncompressed yields the same value
+ assert_eq!(
+ secp256k1::PublicKey::from_byte_array_uncompressed(
+ bitcoin_key.serialize_uncompressed()
+ )
+ .unwrap(),
+ secp_key,
+ );
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ fn xonly_secp_roundtrip() {
+ let bitcoin_key = Keypair::generate().to_x_only_public_key();
+ let secp_key =
+ secp256k1::XOnlyPublicKey::from_byte_array(bitcoin_key.serialize().0).unwrap();
+ assert_eq!(bitcoin_key, XOnlyPublicKey::from_secp(secp_key, bitcoin_key.parity()),);
+ }
+
+ #[test]
+ #[cfg(feature = "rand")]
+ #[cfg(feature = "std")]
+ fn private_key_secp_roundtrip() {
+ let bitcoin_key = PrivateKey::generate();
+ let secp_key =
+ secp256k1::SecretKey::from_secret_bytes(bitcoin_key.to_secret_bytes()).unwrap();
+ assert_eq!(PrivateKey::from_secp(secp_key), bitcoin_key);
+ }
+}
diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs
index 65ce5da7..1574571c 100644
--- a/crypto/src/lib.rs
+++ b/crypto/src/lib.rs
@@ -21,4 +21,9 @@ pub extern crate hex_stable as hex;
#[cfg(feature = "alloc")]
pub mod ecdsa;
#[cfg(feature = "alloc")]
+pub mod key;
+#[cfg(feature = "alloc")]
pub mod sighash;
+
+#[cfg(feature = "alloc")]
+include!("../include/newtype.rs"); // Explained in `REPO_DIR/docs/README.md`.
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.