Skip to content

Commit d44646b

Browse files
committed
Merge #382: Fix the mess around Parity
6fad20e Fix the mess around Parity (Tobin Harding) Pull request description: Recently we made a wee mess with the `Parity` opaque type. Let's fix it up by doing: - Use an enum with variants `Even` and `Odd`. - Add explicit conversion methods to/from u8 and i32 - Implement `BitXor` This PR attempts to follow the plan laid out in the issue but: - I don't get why`to_u8` is requested, I added it anyways. - `to_i32` is not mentioned but we need it if we are going to pass the parity back to FFI code after receiving it _without_ having to care about the value. And that is the whole point of this `Parity` type in the first place. - I don't get why `from_xxx` is fallible, when is an integer not even or odd? Please note: This patch is an API breaking change that does _not_ follow the deprecation guidelines. Rust does not allow deprecating `From` impl blocks AFAICT. Fixes: #370 ACKs for top commit: apoelstra: ACK 6fad20e Tree-SHA512: 810d5028f02fa608eab48721d32dca58be472080fcbac7a0ee79b5211b229112a756c48233e8597fb6c137690b2565431f410e9e97d6940ed389a4501bb015a0
2 parents da3c24c + 6fad20e commit d44646b

File tree

2 files changed

+62
-8
lines changed

2 files changed

+62
-8
lines changed

src/key.rs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#[cfg(any(test, feature = "rand"))] use rand::Rng;
1919

2020
use core::{fmt, ptr, str};
21+
use core::ops::BitXor;
2122

2223
use super::{from_hex, Secp256k1};
2324
use super::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
@@ -890,7 +891,7 @@ impl XOnlyPublicKey {
890891
return Err(Error::InvalidPublicKey);
891892
}
892893

893-
Ok(parity.into())
894+
Parity::from_i32(parity)
894895
}
895896
}
896897

@@ -918,7 +919,7 @@ impl XOnlyPublicKey {
918919
let err = ffi::secp256k1_xonly_pubkey_tweak_add_check(
919920
secp.ctx,
920921
tweaked_ser.as_c_ptr(),
921-
tweaked_parity.into(),
922+
tweaked_parity.to_i32(),
922923
&self.0,
923924
tweak.as_c_ptr(),
924925
);
@@ -928,27 +929,77 @@ impl XOnlyPublicKey {
928929
}
929930
}
930931

931-
/// Opaque type used to hold the parity passed between FFI function calls.
932+
/// Represents the parity passed between FFI function calls.
932933
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
933-
pub struct Parity(i32);
934+
pub enum Parity {
935+
/// Even parity.
936+
Even = 0,
937+
/// Odd parity.
938+
Odd = 1,
939+
}
940+
941+
impl Parity {
942+
/// Converts parity into a integer (byte) value.
943+
pub fn to_u8(self) -> u8 {
944+
self as u8
945+
}
946+
947+
/// Converts parity into a integer value.
948+
pub fn to_i32(self) -> i32 {
949+
self as i32
950+
}
951+
952+
/// Constructs a [`Parity`] from a byte.
953+
pub fn from_u8(parity: u8) -> Result<Parity, Error> {
954+
Parity::from_i32(parity as i32)
955+
}
956+
957+
/// Constructs a [`Parity`] from a signed integer.
958+
pub fn from_i32(parity: i32) -> Result<Parity, Error> {
959+
match parity {
960+
0 => Ok(Parity::Even),
961+
1 => Ok(Parity::Odd),
962+
_ => Err(Error::InvalidParityValue),
963+
}
964+
}
965+
}
934966

935967
impl From<i32> for Parity {
968+
/// Please note, this method is deprecated and will be removed in an upcoming release, it
969+
/// is not equivalent to `from_u32()`, it is better to use `Parity::from_u32`.
936970
fn from(parity: i32) -> Parity {
937-
Parity(parity)
971+
if parity % 2 == 0 {
972+
Parity::Even
973+
} else {
974+
Parity::Odd
975+
}
938976
}
939977
}
940978

941979
impl From<Parity> for i32 {
942980
fn from(parity: Parity) -> i32 {
943-
parity.0
981+
parity.to_i32()
982+
}
983+
}
984+
985+
impl BitXor for Parity {
986+
type Output = Parity;
987+
988+
fn bitxor(self, rhs: Parity) -> Self::Output {
989+
// This works because Parity has only two values (i.e. only 1 bit of information).
990+
if self == rhs {
991+
Parity::Even // 1^1==0 and 0^0==0
992+
} else {
993+
Parity::Odd // 1^0==1 and 0^1==1
994+
}
944995
}
945996
}
946997

947998
#[cfg(feature = "serde")]
948999
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
9491000
impl ::serde::Serialize for Parity {
9501001
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
951-
s.serialize_i32(self.0)
1002+
s.serialize_i32(self.to_i32())
9521003
}
9531004
}
9541005

@@ -969,7 +1020,7 @@ impl<'de> ::serde::Deserialize<'de> for Parity {
9691020
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
9701021
where E: ::serde::de::Error
9711022
{
972-
Ok(Parity::from(v))
1023+
Parity::from_i32(v).map_err(E::custom)
9731024
}
9741025
}
9751026

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ pub enum Error {
322322
NotEnoughMemory,
323323
/// Bad set of public keys
324324
InvalidPublicKeySum,
325+
/// The only valid parity values are 0 or 1.
326+
InvalidParityValue,
325327
}
326328

327329
impl Error {
@@ -336,6 +338,7 @@ impl Error {
336338
Error::InvalidTweak => "secp: bad tweak",
337339
Error::NotEnoughMemory => "secp: not enough memory allocated",
338340
Error::InvalidPublicKeySum => "secp: the sum of public keys was invalid or the input vector lengths was less than 1",
341+
Error::InvalidParityValue => "The only valid parity values are 0 or 1",
339342
}
340343
}
341344
}

0 commit comments

Comments
 (0)