Skip to content

Commit d5065cc

Browse files
committed
Merge #518: Make comparison functions stable
9850550 Move AsRef impl block next to Index (Tobin C. Harding) 4d42e8e Derive Copy and Clone (Tobin C. Harding) b38ae97 Implement stable comparison functionality (Tobin C. Harding) 630fc1f Remove len and is_empty from impl_array_newtype macros (Tobin C. Harding) 9788b6d Remove leading colons from impl_array_newtype methods (Tobin C. Harding) 2bb08c2 Remove as_[mut_]ptr from impl_array_newtype macros (Tobin C. Harding) 3e28070 Duplicate impl_array_newtype (Tobin C. Harding) 6358903 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 9850550 Tree-SHA512: 160381e53972ff882ceb1d2d47bac56a7301a2d13bfe75d3f6ff658ab2c6fbe516ad856587c4d23f98524205918ca1a5f9b737e35c23c7a01b476c92d8d1792f
2 parents 29c0549 + 9850550 commit d5065cc

File tree

9 files changed

+412
-93
lines changed

9 files changed

+412
-93
lines changed

secp256k1-sys/src/lib.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ impl SchnorrSigExtraParams {
133133

134134
/// Library-internal representation of a Secp256k1 public key
135135
#[repr(C)]
136+
#[derive(Copy, Clone)]
136137
pub struct PublicKey([c_uchar; 64]);
137138
impl_array_newtype!(PublicKey, c_uchar, 64);
138139
impl_raw_debug!(PublicKey);
@@ -169,10 +170,64 @@ impl PublicKey {
169170
pub fn underlying_bytes(self) -> [c_uchar; 64] {
170171
self.0
171172
}
173+
174+
/// Serializes this public key as a byte-encoded pair of values, in compressed form.
175+
fn serialize(&self) -> [u8; 33] {
176+
let mut buf = [0u8; 33];
177+
let mut len = 33;
178+
unsafe {
179+
let ret = secp256k1_ec_pubkey_serialize(
180+
secp256k1_context_no_precomp,
181+
buf.as_mut_c_ptr(),
182+
&mut len,
183+
self,
184+
SECP256K1_SER_COMPRESSED,
185+
);
186+
debug_assert_eq!(ret, 1);
187+
debug_assert_eq!(len, 33);
188+
};
189+
buf
190+
}
191+
}
192+
193+
#[cfg(not(fuzzing))]
194+
impl PartialOrd for PublicKey {
195+
fn partial_cmp(&self, other: &PublicKey) -> Option<core::cmp::Ordering> {
196+
Some(self.cmp(other))
197+
}
198+
}
199+
200+
#[cfg(not(fuzzing))]
201+
impl Ord for PublicKey {
202+
fn cmp(&self, other: &PublicKey) -> core::cmp::Ordering {
203+
let ret = unsafe {
204+
secp256k1_ec_pubkey_cmp(secp256k1_context_no_precomp, self, other)
205+
};
206+
ret.cmp(&0i32)
207+
}
208+
}
209+
210+
#[cfg(not(fuzzing))]
211+
impl PartialEq for PublicKey {
212+
fn eq(&self, other: &Self) -> bool {
213+
self.cmp(other) == core::cmp::Ordering::Equal
214+
}
215+
}
216+
217+
#[cfg(not(fuzzing))]
218+
impl Eq for PublicKey {}
219+
220+
#[cfg(not(fuzzing))]
221+
impl core::hash::Hash for PublicKey {
222+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
223+
let ser = self.serialize();
224+
ser.hash(state);
225+
}
172226
}
173227

174228
/// Library-internal representation of a Secp256k1 signature
175229
#[repr(C)]
230+
#[derive(Copy, Clone)]
176231
pub struct Signature([c_uchar; 64]);
177232
impl_array_newtype!(Signature, c_uchar, 64);
178233
impl_raw_debug!(Signature);
@@ -209,9 +264,58 @@ impl Signature {
209264
pub fn underlying_bytes(self) -> [c_uchar; 64] {
210265
self.0
211266
}
267+
268+
/// Serializes the signature in compact format.
269+
fn serialize(&self) -> [u8; 64] {
270+
let mut buf = [0u8; 64];
271+
unsafe {
272+
let ret = secp256k1_ecdsa_signature_serialize_compact(
273+
secp256k1_context_no_precomp,
274+
buf.as_mut_c_ptr(),
275+
self,
276+
);
277+
debug_assert!(ret == 1);
278+
}
279+
buf
280+
}
281+
}
282+
283+
#[cfg(not(fuzzing))]
284+
impl PartialOrd for Signature {
285+
fn partial_cmp(&self, other: &Signature) -> Option<core::cmp::Ordering> {
286+
Some(self.cmp(other))
287+
}
288+
}
289+
290+
#[cfg(not(fuzzing))]
291+
impl Ord for Signature {
292+
fn cmp(&self, other: &Signature) -> core::cmp::Ordering {
293+
let this = self.serialize();
294+
let that = other.serialize();
295+
this.cmp(&that)
296+
}
297+
}
298+
299+
#[cfg(not(fuzzing))]
300+
impl PartialEq for Signature {
301+
fn eq(&self, other: &Self) -> bool {
302+
self.cmp(other) == core::cmp::Ordering::Equal
303+
}
304+
}
305+
306+
#[cfg(not(fuzzing))]
307+
impl Eq for Signature {}
308+
309+
#[cfg(not(fuzzing))]
310+
impl core::hash::Hash for Signature {
311+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
312+
let ser = self.serialize();
313+
ser.hash(state);
314+
}
212315
}
213316

214317
#[repr(C)]
318+
#[derive(Copy, Clone)]
215319
pub struct XOnlyPublicKey([c_uchar; 64]);
216320
impl_array_newtype!(XOnlyPublicKey, c_uchar, 64);
217321
impl_raw_debug!(XOnlyPublicKey);
@@ -248,9 +352,59 @@ impl XOnlyPublicKey {
248352
pub fn underlying_bytes(self) -> [c_uchar; 64] {
249353
self.0
250354
}
355+
356+
/// Serializes this key as a byte-encoded x coordinate value (32 bytes).
357+
fn serialize(&self) -> [u8; 32] {
358+
let mut buf = [0u8; 32];
359+
unsafe {
360+
let ret = secp256k1_xonly_pubkey_serialize(
361+
secp256k1_context_no_precomp,
362+
buf.as_mut_c_ptr(),
363+
self,
364+
);
365+
assert_eq!(ret, 1);
366+
};
367+
buf
368+
}
369+
}
370+
371+
#[cfg(not(fuzzing))]
372+
impl PartialOrd for XOnlyPublicKey {
373+
fn partial_cmp(&self, other: &XOnlyPublicKey) -> Option<core::cmp::Ordering> {
374+
Some(self.cmp(other))
375+
}
376+
}
377+
378+
#[cfg(not(fuzzing))]
379+
impl Ord for XOnlyPublicKey {
380+
fn cmp(&self, other: &XOnlyPublicKey) -> core::cmp::Ordering {
381+
let ret = unsafe {
382+
secp256k1_xonly_pubkey_cmp(secp256k1_context_no_precomp, self, other)
383+
};
384+
ret.cmp(&0i32)
385+
}
386+
}
387+
388+
#[cfg(not(fuzzing))]
389+
impl PartialEq for XOnlyPublicKey {
390+
fn eq(&self, other: &Self) -> bool {
391+
self.cmp(other) == core::cmp::Ordering::Equal
392+
}
393+
}
394+
395+
#[cfg(not(fuzzing))]
396+
impl Eq for XOnlyPublicKey {}
397+
398+
#[cfg(not(fuzzing))]
399+
impl core::hash::Hash for XOnlyPublicKey {
400+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
401+
let ser = self.serialize();
402+
ser.hash(state);
403+
}
251404
}
252405

253406
#[repr(C)]
407+
#[derive(Copy, Clone)]
254408
pub struct KeyPair([c_uchar; 96]);
255409
impl_array_newtype!(KeyPair, c_uchar, 96);
256410
impl_raw_debug!(KeyPair);
@@ -287,6 +441,58 @@ impl KeyPair {
287441
pub fn underlying_bytes(self) -> [c_uchar; 96] {
288442
self.0
289443
}
444+
445+
/// Creates a new compressed public key from this key pair.
446+
fn public_key(&self) -> PublicKey {
447+
unsafe {
448+
let mut pk = PublicKey::new();
449+
let ret = secp256k1_keypair_pub(
450+
secp256k1_context_no_precomp,
451+
&mut pk,
452+
self,
453+
);
454+
debug_assert_eq!(ret, 1);
455+
pk
456+
}
457+
}
458+
}
459+
460+
#[cfg(not(fuzzing))]
461+
impl PartialOrd for KeyPair {
462+
fn partial_cmp(&self, other: &KeyPair) -> Option<core::cmp::Ordering> {
463+
Some(self.cmp(other))
464+
}
465+
}
466+
467+
#[cfg(not(fuzzing))]
468+
impl Ord for KeyPair {
469+
fn cmp(&self, other: &KeyPair) -> core::cmp::Ordering {
470+
let this = self.public_key();
471+
let that = other.public_key();
472+
this.cmp(&that)
473+
}
474+
}
475+
476+
#[cfg(not(fuzzing))]
477+
impl PartialEq for KeyPair {
478+
fn eq(&self, other: &Self) -> bool {
479+
self.cmp(other) == core::cmp::Ordering::Equal
480+
}
481+
}
482+
483+
#[cfg(not(fuzzing))]
484+
impl Eq for KeyPair {}
485+
486+
#[cfg(not(fuzzing))]
487+
impl core::hash::Hash for KeyPair {
488+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
489+
// To hash the key pair we just hash the serialized public key. Since any change to the
490+
// secret key would also be a change to the public key this is a valid one way function from
491+
// the key pair to the digest.
492+
let pk = self.public_key();
493+
let ser = pk.serialize();
494+
ser.hash(state);
495+
}
290496
}
291497

292498
extern "C" {

secp256k1-sys/src/macros.rs

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,75 +17,76 @@
1717
#[macro_export]
1818
macro_rules! impl_array_newtype {
1919
($thing:ident, $ty:ty, $len:expr) => {
20-
impl Copy for $thing {}
21-
2220
impl $thing {
23-
/// Converts the object to a raw pointer for FFI interfacing.
24-
#[inline]
25-
pub fn as_ptr(&self) -> *const $ty {
26-
let &$thing(ref dat) = self;
27-
dat.as_ptr()
21+
/// Like `cmp::Ord` but faster and with no guarantees across library versions.
22+
///
23+
/// The inner byte array of `Self` is passed across the FFI boundry, as such there are
24+
/// no guarantees on its layout and it is subject to change across library versions,
25+
/// even minor versions. For this reason comparison function implementations (e.g.
26+
/// `Ord`, `PartialEq`) take measures to ensure the data will remain constant (e.g., by
27+
/// serializing it to a guaranteed format). This means they may be slow, this function
28+
/// provides a faster comparison if you know that your types come from the same library
29+
/// version.
30+
pub fn cmp_fast_unstable(&self, other: &Self) -> core::cmp::Ordering {
31+
self[..].cmp(&other[..])
2832
}
2933

30-
/// Converts the object to a mutable raw pointer for FFI interfacing.
31-
#[inline]
32-
pub fn as_mut_ptr(&mut self) -> *mut $ty {
33-
let &mut $thing(ref mut dat) = self;
34-
dat.as_mut_ptr()
34+
/// Like `cmp::Eq` but faster and with no guarantees across library versions.
35+
///
36+
/// The inner byte array of `Self` is passed across the FFI boundry, as such there are
37+
/// no guarantees on its layout and it is subject to change across library versions,
38+
/// even minor versions. For this reason comparison function implementations (e.g.
39+
/// `Ord`, `PartialEq`) take measures to ensure the data will remain constant (e.g., by
40+
/// serializing it to a guaranteed format). This means they may be slow, this function
41+
/// provides a faster equality check if you know that your types come from the same
42+
/// library version.
43+
pub fn eq_fast_unstable(&self, other: &Self) -> bool {
44+
self[..].eq(&other[..])
3545
}
36-
37-
/// Returns the length of the object as an array.
38-
#[inline]
39-
pub fn len(&self) -> usize { $len }
40-
41-
/// Returns whether the object as an array is empty.
42-
#[inline]
43-
pub fn is_empty(&self) -> bool { false }
4446
}
4547

46-
impl AsRef<[$ty; $len]> for $thing {
47-
#[inline]
48-
/// Gets a reference to the underlying array
49-
fn as_ref(&self) -> &[$ty; $len] {
50-
let &$thing(ref dat) = self;
51-
dat
52-
}
53-
}
48+
// We cannot derive these traits because Rust 1.41.1 requires `std::array::LengthAtMost32`.
5449

50+
#[cfg(fuzzing)]
5551
impl PartialEq for $thing {
5652
#[inline]
5753
fn eq(&self, other: &$thing) -> bool {
5854
&self[..] == &other[..]
5955
}
6056
}
6157

58+
#[cfg(fuzzing)]
6259
impl Eq for $thing {}
6360

64-
impl ::core::hash::Hash for $thing {
65-
fn hash<H: ::core::hash::Hasher>(&self, state: &mut H) {
61+
#[cfg(fuzzing)]
62+
impl core::hash::Hash for $thing {
63+
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
6664
(&self[..]).hash(state)
6765
}
6866
}
6967

68+
#[cfg(fuzzing)]
7069
impl PartialOrd for $thing {
7170
#[inline]
7271
fn partial_cmp(&self, other: &$thing) -> Option<core::cmp::Ordering> {
7372
self[..].partial_cmp(&other[..])
7473
}
7574
}
7675

76+
#[cfg(fuzzing)]
7777
impl Ord for $thing {
7878
#[inline]
7979
fn cmp(&self, other: &$thing) -> core::cmp::Ordering {
8080
self[..].cmp(&other[..])
8181
}
8282
}
8383

84-
impl Clone for $thing {
84+
impl AsRef<[$ty; $len]> for $thing {
8585
#[inline]
86-
fn clone(&self) -> $thing {
86+
/// Gets a reference to the underlying array
87+
fn as_ref(&self) -> &[$ty; $len] {
8788
let &$thing(ref dat) = self;
88-
$thing(dat.clone())
89+
dat
8990
}
9091
}
9192

@@ -101,20 +102,15 @@ macro_rules! impl_array_newtype {
101102

102103
impl $crate::CPtr for $thing {
103104
type Target = $ty;
105+
104106
fn as_c_ptr(&self) -> *const Self::Target {
105-
if self.is_empty() {
106-
core::ptr::null()
107-
} else {
108-
self.as_ptr()
109-
}
107+
let &$thing(ref dat) = self;
108+
dat.as_ptr()
110109
}
111110

112111
fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
113-
if self.is_empty() {
114-
core::ptr::null::<Self::Target>() as *mut _
115-
} else {
116-
self.as_mut_ptr()
117-
}
112+
let &mut $thing(ref mut dat) = self;
113+
dat.as_mut_ptr()
118114
}
119115
}
120116
}

0 commit comments

Comments
 (0)