Merge rust-bitcoin/rust-secp256k1#518: Make comparison functions stable

9850550734 Move AsRef impl block next to Index (Tobin C. Harding)
4d42e8e906 Derive Copy and Clone (Tobin C. Harding)
b38ae97eaf Implement stable comparison functionality (Tobin C. Harding)
630fc1fcb6 Remove len and is_empty from impl_array_newtype macros (Tobin C. Harding)
9788b6df88 Remove leading colons from impl_array_newtype methods (Tobin C. Harding)
2bb08c21e5 Remove as_[mut_]ptr from impl_array_newtype macros (Tobin C. Harding)
3e28070187 Duplicate impl_array_newtype (Tobin C. Harding)
635890322a Add newline to end of file (Tobin C. Harding)

Pull request description:

  Supersedes #515

  The primary aim of this PR is to fix the fact that we currently implement various comparison traits (`Ord`, `PartialEq`) by comparing the inner byte arrays. These bytes are meant to be opaque and are not guaranteed across versions of `libsecp256k1`.

  This PR is a bit involved because I had to detangle all the various types (across both `secp256k1` and `secp256k1-sys`) that use the `impl_array_newtype` macro.

  - Patch 1: is trivial cleanup
  - Patch 2: Step one of the PR is duplicating the macro into both crates so we can tease them apart.
  - Patch 3-5: Are cleanup of the now duplicated `impl_array_newtype` macros
  - Patch 6: Is the meat and potatoes
  - Patch 7,8: Further minor clean ups to the macro

  I had a lot of fun with this PR, I hope you enjoy it too.

  Fix: #463

ACKs for top commit:
  apoelstra:
    ACK 9850550734

Tree-SHA512: 160381e53972ff882ceb1d2d47bac56a7301a2d13bfe75d3f6ff658ab2c6fbe516ad856587c4d23f98524205918ca1a5f9b737e35c23c7a01b476c92d8d1792f
This commit is contained in:
Andrew Poelstra 2022-11-21 14:56:05 +00:00
commit d5065cc771
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
9 changed files with 433 additions and 114 deletions

View File

@ -133,6 +133,7 @@ impl SchnorrSigExtraParams {
/// Library-internal representation of a Secp256k1 public key /// Library-internal representation of a Secp256k1 public key
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct PublicKey([c_uchar; 64]); pub struct PublicKey([c_uchar; 64]);
impl_array_newtype!(PublicKey, c_uchar, 64); impl_array_newtype!(PublicKey, c_uchar, 64);
impl_raw_debug!(PublicKey); impl_raw_debug!(PublicKey);
@ -169,10 +170,64 @@ impl PublicKey {
pub fn underlying_bytes(self) -> [c_uchar; 64] { pub fn underlying_bytes(self) -> [c_uchar; 64] {
self.0 self.0
} }
/// Serializes this public key as a byte-encoded pair of values, in compressed form.
fn serialize(&self) -> [u8; 33] {
let mut buf = [0u8; 33];
let mut len = 33;
unsafe {
let ret = secp256k1_ec_pubkey_serialize(
secp256k1_context_no_precomp,
buf.as_mut_c_ptr(),
&mut len,
self,
SECP256K1_SER_COMPRESSED,
);
debug_assert_eq!(ret, 1);
debug_assert_eq!(len, 33);
};
buf
}
}
#[cfg(not(fuzzing))]
impl PartialOrd for PublicKey {
fn partial_cmp(&self, other: &PublicKey) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for PublicKey {
fn cmp(&self, other: &PublicKey) -> core::cmp::Ordering {
let ret = unsafe {
secp256k1_ec_pubkey_cmp(secp256k1_context_no_precomp, self, other)
};
ret.cmp(&0i32)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == core::cmp::Ordering::Equal
}
}
#[cfg(not(fuzzing))]
impl Eq for PublicKey {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for PublicKey {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let ser = self.serialize();
ser.hash(state);
}
} }
/// Library-internal representation of a Secp256k1 signature /// Library-internal representation of a Secp256k1 signature
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct Signature([c_uchar; 64]); pub struct Signature([c_uchar; 64]);
impl_array_newtype!(Signature, c_uchar, 64); impl_array_newtype!(Signature, c_uchar, 64);
impl_raw_debug!(Signature); impl_raw_debug!(Signature);
@ -209,9 +264,58 @@ impl Signature {
pub fn underlying_bytes(self) -> [c_uchar; 64] { pub fn underlying_bytes(self) -> [c_uchar; 64] {
self.0 self.0
} }
/// Serializes the signature in compact format.
fn serialize(&self) -> [u8; 64] {
let mut buf = [0u8; 64];
unsafe {
let ret = secp256k1_ecdsa_signature_serialize_compact(
secp256k1_context_no_precomp,
buf.as_mut_c_ptr(),
self,
);
debug_assert!(ret == 1);
}
buf
}
}
#[cfg(not(fuzzing))]
impl PartialOrd for Signature {
fn partial_cmp(&self, other: &Signature) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for Signature {
fn cmp(&self, other: &Signature) -> core::cmp::Ordering {
let this = self.serialize();
let that = other.serialize();
this.cmp(&that)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for Signature {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == core::cmp::Ordering::Equal
}
}
#[cfg(not(fuzzing))]
impl Eq for Signature {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for Signature {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let ser = self.serialize();
ser.hash(state);
}
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct XOnlyPublicKey([c_uchar; 64]); pub struct XOnlyPublicKey([c_uchar; 64]);
impl_array_newtype!(XOnlyPublicKey, c_uchar, 64); impl_array_newtype!(XOnlyPublicKey, c_uchar, 64);
impl_raw_debug!(XOnlyPublicKey); impl_raw_debug!(XOnlyPublicKey);
@ -248,9 +352,59 @@ impl XOnlyPublicKey {
pub fn underlying_bytes(self) -> [c_uchar; 64] { pub fn underlying_bytes(self) -> [c_uchar; 64] {
self.0 self.0
} }
/// Serializes this key as a byte-encoded x coordinate value (32 bytes).
fn serialize(&self) -> [u8; 32] {
let mut buf = [0u8; 32];
unsafe {
let ret = secp256k1_xonly_pubkey_serialize(
secp256k1_context_no_precomp,
buf.as_mut_c_ptr(),
self,
);
assert_eq!(ret, 1);
};
buf
}
}
#[cfg(not(fuzzing))]
impl PartialOrd for XOnlyPublicKey {
fn partial_cmp(&self, other: &XOnlyPublicKey) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for XOnlyPublicKey {
fn cmp(&self, other: &XOnlyPublicKey) -> core::cmp::Ordering {
let ret = unsafe {
secp256k1_xonly_pubkey_cmp(secp256k1_context_no_precomp, self, other)
};
ret.cmp(&0i32)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for XOnlyPublicKey {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == core::cmp::Ordering::Equal
}
}
#[cfg(not(fuzzing))]
impl Eq for XOnlyPublicKey {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for XOnlyPublicKey {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let ser = self.serialize();
ser.hash(state);
}
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct KeyPair([c_uchar; 96]); pub struct KeyPair([c_uchar; 96]);
impl_array_newtype!(KeyPair, c_uchar, 96); impl_array_newtype!(KeyPair, c_uchar, 96);
impl_raw_debug!(KeyPair); impl_raw_debug!(KeyPair);
@ -287,6 +441,58 @@ impl KeyPair {
pub fn underlying_bytes(self) -> [c_uchar; 96] { pub fn underlying_bytes(self) -> [c_uchar; 96] {
self.0 self.0
} }
/// Creates a new compressed public key from this key pair.
fn public_key(&self) -> PublicKey {
unsafe {
let mut pk = PublicKey::new();
let ret = secp256k1_keypair_pub(
secp256k1_context_no_precomp,
&mut pk,
self,
);
debug_assert_eq!(ret, 1);
pk
}
}
}
#[cfg(not(fuzzing))]
impl PartialOrd for KeyPair {
fn partial_cmp(&self, other: &KeyPair) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for KeyPair {
fn cmp(&self, other: &KeyPair) -> core::cmp::Ordering {
let this = self.public_key();
let that = other.public_key();
this.cmp(&that)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for KeyPair {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == core::cmp::Ordering::Equal
}
}
#[cfg(not(fuzzing))]
impl Eq for KeyPair {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for KeyPair {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
// To hash the key pair we just hash the serialized public key. Since any change to the
// secret key would also be a change to the public key this is a valid one way function from
// the key pair to the digest.
let pk = self.public_key();
let ser = pk.serialize();
ser.hash(state);
}
} }
extern "C" { extern "C" {

View File

@ -17,30 +17,68 @@
#[macro_export] #[macro_export]
macro_rules! impl_array_newtype { macro_rules! impl_array_newtype {
($thing:ident, $ty:ty, $len:expr) => { ($thing:ident, $ty:ty, $len:expr) => {
impl Copy for $thing {}
impl $thing { impl $thing {
/// Converts the object to a raw pointer for FFI interfacing. /// Like `cmp::Ord` but faster and with no guarantees across library versions.
#[inline] ///
pub fn as_ptr(&self) -> *const $ty { /// The inner byte array of `Self` is passed across the FFI boundry, as such there are
let &$thing(ref dat) = self; /// no guarantees on its layout and it is subject to change across library versions,
dat.as_ptr() /// even minor versions. For this reason comparison function implementations (e.g.
/// `Ord`, `PartialEq`) take measures to ensure the data will remain constant (e.g., by
/// serializing it to a guaranteed format). This means they may be slow, this function
/// provides a faster comparison if you know that your types come from the same library
/// version.
pub fn cmp_fast_unstable(&self, other: &Self) -> core::cmp::Ordering {
self[..].cmp(&other[..])
} }
/// Converts the object to a mutable raw pointer for FFI interfacing. /// Like `cmp::Eq` but faster and with no guarantees across library versions.
#[inline] ///
pub fn as_mut_ptr(&mut self) -> *mut $ty { /// The inner byte array of `Self` is passed across the FFI boundry, as such there are
let &mut $thing(ref mut dat) = self; /// no guarantees on its layout and it is subject to change across library versions,
dat.as_mut_ptr() /// even minor versions. For this reason comparison function implementations (e.g.
/// `Ord`, `PartialEq`) take measures to ensure the data will remain constant (e.g., by
/// serializing it to a guaranteed format). This means they may be slow, this function
/// provides a faster equality check if you know that your types come from the same
/// library version.
pub fn eq_fast_unstable(&self, other: &Self) -> bool {
self[..].eq(&other[..])
}
} }
/// Returns the length of the object as an array. // We cannot derive these traits because Rust 1.41.1 requires `std::array::LengthAtMost32`.
#[inline]
pub fn len(&self) -> usize { $len }
/// Returns whether the object as an array is empty. #[cfg(fuzzing)]
impl PartialEq for $thing {
#[inline] #[inline]
pub fn is_empty(&self) -> bool { false } fn eq(&self, other: &$thing) -> bool {
&self[..] == &other[..]
}
}
#[cfg(fuzzing)]
impl Eq for $thing {}
#[cfg(fuzzing)]
impl core::hash::Hash for $thing {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
(&self[..]).hash(state)
}
}
#[cfg(fuzzing)]
impl PartialOrd for $thing {
#[inline]
fn partial_cmp(&self, other: &$thing) -> Option<core::cmp::Ordering> {
self[..].partial_cmp(&other[..])
}
}
#[cfg(fuzzing)]
impl Ord for $thing {
#[inline]
fn cmp(&self, other: &$thing) -> core::cmp::Ordering {
self[..].cmp(&other[..])
}
} }
impl AsRef<[$ty; $len]> for $thing { impl AsRef<[$ty; $len]> for $thing {
@ -52,43 +90,6 @@ macro_rules! impl_array_newtype {
} }
} }
impl PartialEq for $thing {
#[inline]
fn eq(&self, other: &$thing) -> bool {
&self[..] == &other[..]
}
}
impl Eq for $thing {}
impl ::core::hash::Hash for $thing {
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
(&self[..]).hash(state)
}
}
impl PartialOrd for $thing {
#[inline]
fn partial_cmp(&self, other: &$thing) -> Option<core::cmp::Ordering> {
self[..].partial_cmp(&other[..])
}
}
impl Ord for $thing {
#[inline]
fn cmp(&self, other: &$thing) -> core::cmp::Ordering {
self[..].cmp(&other[..])
}
}
impl Clone for $thing {
#[inline]
fn clone(&self) -> $thing {
let &$thing(ref dat) = self;
$thing(dat.clone())
}
}
impl<I> core::ops::Index<I> for $thing impl<I> core::ops::Index<I> for $thing
where where
[$ty]: core::ops::Index<I>, [$ty]: core::ops::Index<I>,
@ -101,20 +102,15 @@ macro_rules! impl_array_newtype {
impl $crate::CPtr for $thing { impl $crate::CPtr for $thing {
type Target = $ty; type Target = $ty;
fn as_c_ptr(&self) -> *const Self::Target { fn as_c_ptr(&self) -> *const Self::Target {
if self.is_empty() { let &$thing(ref dat) = self;
core::ptr::null() dat.as_ptr()
} else {
self.as_ptr()
}
} }
fn as_mut_c_ptr(&mut self) -> *mut Self::Target { fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
if self.is_empty() { let &mut $thing(ref mut dat) = self;
core::ptr::null::<Self::Target>() as *mut _ dat.as_mut_ptr()
} else {
self.as_mut_ptr()
}
} }
} }
} }

View File

@ -15,18 +15,36 @@
//! # FFI of the recovery module //! # FFI of the recovery module
use crate::{Context, Signature, NonceFn, PublicKey, CPtr, impl_array_newtype}; use crate::{Context, Signature, NonceFn, PublicKey, CPtr, impl_array_newtype, secp256k1_context_no_precomp};
use crate::types::*; use crate::types::*;
use core::fmt; use core::fmt;
/// Library-internal representation of a Secp256k1 signature + recovery ID /// Library-internal representation of a Secp256k1 signature + recovery ID
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
pub struct RecoverableSignature([c_uchar; 65]); pub struct RecoverableSignature([c_uchar; 65]);
impl_array_newtype!(RecoverableSignature, c_uchar, 65); impl_array_newtype!(RecoverableSignature, c_uchar, 65);
impl RecoverableSignature { impl RecoverableSignature {
/// Create a new (zeroed) signature usable for the FFI interface /// Create a new (zeroed) signature usable for the FFI interface
pub fn new() -> RecoverableSignature { RecoverableSignature([0; 65]) } pub fn new() -> RecoverableSignature { RecoverableSignature([0; 65]) }
/// Serializes the signature in compact format.
fn serialize(&self) -> [u8; 65] {
let mut buf = [0u8; 65];
let mut recid = 0;
unsafe {
let ret = secp256k1_ecdsa_recoverable_signature_serialize_compact(
secp256k1_context_no_precomp,
buf.as_mut_c_ptr(),
&mut recid,
self,
);
debug_assert!(ret == 1);
}
buf[64] = (recid & 0xFF) as u8;
buf
}
} }
impl Default for RecoverableSignature { impl Default for RecoverableSignature {
@ -59,6 +77,40 @@ impl fmt::Debug for RecoverableSignature {
} }
} }
#[cfg(not(fuzzing))]
impl PartialOrd for RecoverableSignature {
fn partial_cmp(&self, other: &RecoverableSignature) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for RecoverableSignature {
fn cmp(&self, other: &RecoverableSignature) -> core::cmp::Ordering {
let this = self.serialize();
let that = other.serialize();
this.cmp(&that)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for RecoverableSignature {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == core::cmp::Ordering::Equal
}
}
#[cfg(not(fuzzing))]
impl Eq for RecoverableSignature {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for RecoverableSignature {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let ser = self.serialize();
ser.hash(state);
}
}
extern "C" { extern "C" {
#[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_6_1_ecdsa_recoverable_signature_parse_compact")] #[cfg_attr(not(rust_secp_no_symbol_renaming), link_name = "rustsecp256k1_v0_6_1_ecdsa_recoverable_signature_parse_compact")]
pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature, pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: *const Context, sig: *mut RecoverableSignature,

View File

@ -144,7 +144,7 @@ pub fn shared_secret_point(point: &PublicKey, scalar: &SecretKey) -> [u8; 64] {
ffi::secp256k1_context_no_precomp, ffi::secp256k1_context_no_precomp,
xy.as_mut_ptr(), xy.as_mut_ptr(),
point.as_c_ptr(), point.as_c_ptr(),
scalar.as_ptr(), scalar.as_c_ptr(),
Some(c_callback), Some(c_callback),
ptr::null_mut(), ptr::null_mut(),
) )

View File

@ -18,8 +18,9 @@ use crate::{
}; };
/// An ECDSA signature /// An ECDSA signature
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct Signature(pub(crate) ffi::Signature); pub struct Signature(pub(crate) ffi::Signature);
impl_fast_comparisons!(Signature);
impl fmt::Debug for Signature { impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }

View File

@ -24,11 +24,11 @@ use core::{fmt, ptr, str};
use serde::ser::SerializeTuple; use serde::ser::SerializeTuple;
use crate::ffi::types::c_uint; use crate::ffi::types::c_uint;
use crate::ffi::{self, impl_array_newtype, CPtr}; use crate::ffi::{self, CPtr};
#[cfg(all(feature = "global-context", feature = "rand-std"))] #[cfg(all(feature = "global-context", feature = "rand-std"))]
use crate::schnorr; use crate::schnorr;
use crate::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey}; use crate::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
use crate::{constants, from_hex, Scalar, Secp256k1, Signing, Verification}; use crate::{constants, from_hex, impl_array_newtype, Scalar, Secp256k1, Signing, Verification};
#[cfg(feature = "global-context")] #[cfg(feature = "global-context")]
use crate::{ecdsa, Message, SECP256K1}; use crate::{ecdsa, Message, SECP256K1};
#[cfg(feature = "bitcoin-hashes")] #[cfg(feature = "bitcoin-hashes")]
@ -56,6 +56,7 @@ use crate::{hashes, ThirtyTwoByteHash};
/// ``` /// ```
/// [`bincode`]: https://docs.rs/bincode /// [`bincode`]: https://docs.rs/bincode
/// [`cbor`]: https://docs.rs/cbor /// [`cbor`]: https://docs.rs/cbor
#[derive(Copy, Clone)]
pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]); pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]);
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE); impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
impl_display_secret!(SecretKey); impl_display_secret!(SecretKey);
@ -94,10 +95,10 @@ impl str::FromStr for SecretKey {
/// ``` /// ```
/// [`bincode`]: https://docs.rs/bincode /// [`bincode`]: https://docs.rs/bincode
/// [`cbor`]: https://docs.rs/cbor /// [`cbor`]: https://docs.rs/cbor
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
#[cfg_attr(fuzzing, derive(PartialOrd, Ord, PartialEq, Eq, Hash))]
#[repr(transparent)] #[repr(transparent)]
pub struct PublicKey(ffi::PublicKey); pub struct PublicKey(ffi::PublicKey);
impl_fast_comparisons!(PublicKey);
impl fmt::LowerHex for PublicKey { impl fmt::LowerHex for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -711,43 +712,6 @@ impl<'de> serde::Deserialize<'de> for PublicKey {
} }
} }
#[cfg(not(fuzzing))]
impl PartialOrd for PublicKey {
fn partial_cmp(&self, other: &PublicKey) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(fuzzing))]
impl Ord for PublicKey {
fn cmp(&self, other: &PublicKey) -> core::cmp::Ordering {
let ret = unsafe {
ffi::secp256k1_ec_pubkey_cmp(
ffi::secp256k1_context_no_precomp,
self.as_c_ptr(),
other.as_c_ptr(),
)
};
ret.cmp(&0i32)
}
}
#[cfg(not(fuzzing))]
impl PartialEq for PublicKey {
fn eq(&self, other: &Self) -> bool { self.cmp(other) == core::cmp::Ordering::Equal }
}
#[cfg(not(fuzzing))]
impl Eq for PublicKey {}
#[cfg(not(fuzzing))]
impl core::hash::Hash for PublicKey {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
let ser = self.serialize();
ser.hash(state);
}
}
/// Opaque data structure that holds a keypair consisting of a secret and a public key. /// Opaque data structure that holds a keypair consisting of a secret and a public key.
/// ///
/// # Serde support /// # Serde support
@ -772,9 +736,10 @@ impl core::hash::Hash for PublicKey {
/// ``` /// ```
/// [`bincode`]: https://docs.rs/bincode /// [`bincode`]: https://docs.rs/bincode
/// [`cbor`]: https://docs.rs/cbor /// [`cbor`]: https://docs.rs/cbor
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct KeyPair(ffi::KeyPair); pub struct KeyPair(ffi::KeyPair);
impl_display_secret!(KeyPair); impl_display_secret!(KeyPair);
impl_fast_comparisons!(KeyPair);
impl KeyPair { impl KeyPair {
/// Obtains a raw const pointer suitable for use with FFI functions. /// Obtains a raw const pointer suitable for use with FFI functions.
@ -1079,8 +1044,9 @@ impl CPtr for KeyPair {
/// ``` /// ```
/// [`bincode`]: https://docs.rs/bincode /// [`bincode`]: https://docs.rs/bincode
/// [`cbor`]: https://docs.rs/cbor /// [`cbor`]: https://docs.rs/cbor
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct XOnlyPublicKey(ffi::XOnlyPublicKey); pub struct XOnlyPublicKey(ffi::XOnlyPublicKey);
impl_fast_comparisons!(XOnlyPublicKey);
impl fmt::LowerHex for XOnlyPublicKey { impl fmt::LowerHex for XOnlyPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

View File

@ -199,7 +199,7 @@ pub use serde;
pub use crate::context::*; pub use crate::context::*;
use crate::ffi::types::AlignedType; use crate::ffi::types::AlignedType;
use crate::ffi::{impl_array_newtype, CPtr}; use crate::ffi::CPtr;
#[cfg(feature = "bitcoin-hashes")] #[cfg(feature = "bitcoin-hashes")]
use crate::hashes::Hash; use crate::hashes::Hash;
pub use crate::key::{PublicKey, SecretKey, *}; pub use crate::key::{PublicKey, SecretKey, *};
@ -232,6 +232,7 @@ impl<T: hashes::sha256t::Tag> ThirtyTwoByteHash for hashes::sha256t::Hash<T> {
} }
/// A (hashed) message input to an ECDSA signature. /// A (hashed) message input to an ECDSA signature.
#[derive(Copy, Clone)]
pub struct Message([u8; constants::MESSAGE_SIZE]); pub struct Message([u8; constants::MESSAGE_SIZE]);
impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE); impl_array_newtype!(Message, u8, constants::MESSAGE_SIZE);
impl_pretty_debug!(Message); impl_pretty_debug!(Message);

View File

@ -13,6 +13,77 @@
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. // If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
// //
/// Implement methods and traits for types that contain an inner array.
#[macro_export]
macro_rules! impl_array_newtype {
($thing:ident, $ty:ty, $len:expr) => {
// We cannot derive these traits because Rust 1.41.1 requires `std::array::LengthAtMost32`.
impl PartialEq for $thing {
#[inline]
fn eq(&self, other: &$thing) -> bool {
&self[..] == &other[..]
}
}
impl Eq for $thing {}
impl core::hash::Hash for $thing {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
(&self[..]).hash(state)
}
}
impl PartialOrd for $thing {
#[inline]
fn partial_cmp(&self, other: &$thing) -> Option<core::cmp::Ordering> {
self[..].partial_cmp(&other[..])
}
}
impl Ord for $thing {
#[inline]
fn cmp(&self, other: &$thing) -> core::cmp::Ordering {
self[..].cmp(&other[..])
}
}
impl AsRef<[$ty; $len]> for $thing {
#[inline]
/// Gets a reference to the underlying array
fn as_ref(&self) -> &[$ty; $len] {
let &$thing(ref dat) = self;
dat
}
}
impl<I> core::ops::Index<I> for $thing
where
[$ty]: core::ops::Index<I>,
{
type Output = <[$ty] as core::ops::Index<I>>::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output { &self.0[index] }
}
impl $crate::ffi::CPtr for $thing {
type Target = $ty;
fn as_c_ptr(&self) -> *const Self::Target {
let &$thing(ref dat) = self;
dat.as_ptr()
}
fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
let &mut $thing(ref mut dat) = self;
dat.as_mut_ptr()
}
}
}
}
macro_rules! impl_pretty_debug { macro_rules! impl_pretty_debug {
($thing:ident) => { ($thing:ident) => {
impl core::fmt::Debug for $thing { impl core::fmt::Debug for $thing {
@ -45,3 +116,28 @@ macro_rules! write_err {
} }
} }
} }
/// Implements fast unstable comparison methods for `$ty`.
macro_rules! impl_fast_comparisons {
($ty:ident) => {
impl $ty {
/// Like `cmp::Cmp` but faster and with no guarantees across library versions.
///
/// The `Cmp` implementation for FFI types is stable but slow because it first
/// serializes `self` and `other` before comparing them. This function provides a faster
/// comparison if you know that your types come from the same library version.
pub fn cmp_fast_unstable(&self, other: &Self) -> core::cmp::Ordering {
self.0.cmp_fast_unstable(&other.0)
}
/// Like `cmp::Eq` but faster and with no guarantees across library versions.
///
/// The `Eq` implementation for FFI types is stable but slow because it first serializes
/// `self` and `other` before comparing them. This function provides a faster equality
/// check if you know that your types come from the same library version.
pub fn eq_fast_unstable(&self, other: &Self) -> bool {
self.0.eq_fast_unstable(&other.0)
}
}
}
}

View File

@ -7,13 +7,14 @@ use core::{fmt, ptr, str};
#[cfg(feature = "rand")] #[cfg(feature = "rand")]
use rand::{CryptoRng, Rng}; use rand::{CryptoRng, Rng};
use crate::ffi::{self, impl_array_newtype, CPtr}; use crate::ffi::{self, CPtr};
use crate::key::{KeyPair, XOnlyPublicKey}; use crate::key::{KeyPair, XOnlyPublicKey};
#[cfg(feature = "global-context")] #[cfg(feature = "global-context")]
use crate::SECP256K1; use crate::SECP256K1;
use crate::{constants, from_hex, Error, Message, Secp256k1, Signing, Verification}; use crate::{constants, from_hex, impl_array_newtype, Error, Message, Secp256k1, Signing, Verification};
/// Represents a Schnorr signature. /// Represents a Schnorr signature.
#[derive(Copy, Clone)]
pub struct Signature([u8; constants::SCHNORR_SIGNATURE_SIZE]); pub struct Signature([u8; constants::SCHNORR_SIGNATURE_SIZE]);
impl_array_newtype!(Signature, u8, constants::SCHNORR_SIGNATURE_SIZE); impl_array_newtype!(Signature, u8, constants::SCHNORR_SIGNATURE_SIZE);
impl_pretty_debug!(Signature); impl_pretty_debug!(Signature);