Skip to content

Commit 4833b97

Browse files
committed
Merge #344: Improve handling of parity integer
ede114f Improve docs on tweak_add_check method (Tobin Harding) fbc64c7 Add opaque parity type (Tobin Harding) 1b768b2 Make tweak_add_assign return statements uniform (Tobin Harding) edafb88 Move key unit tests to key module (Tobin Harding) e3d21a3 Clean up test imports with key module (Tobin Harding) Pull request description: Two functions in the FFI secp code return and accept a parity integer. Currently we are manually converting this to a bool. Doing so forces readers of the code to think what the bool means even though understanding this value is not needed since in is just passed back down to the FFI code. We initially tried to solve this issue by adding an enum, discussion below refers to that version. Instead of an enum we can solve this issue by adding an opaque type that holds the parity value returned by the FFI function call and then just pass it back down to FFI code without devs needing to know what the value should be. This fully abstracts the value away and removes the boolean conversion code which must otherwise be read by each dev. - Patch 1 and 2 improve unit tests that test the code path modified by this PR - Patch 3 trivially changes code to be uniform between two similar methods (`tweak_add_assign`) - Patch 4 is the meat and potatoes (main part of PR :) - Patch 5 is docs improvements to code in the area of this PR ACKs for top commit: apoelstra: ACK ede114f Tree-SHA512: 37843e066d9006c5daa30dece9f7eb7a802864b85606e43ed2651c6d55938c4f884cc4abab81eccb69685f6eda918a9b9ba57bf1a4efec41e89239b99ae2b726
2 parents f531be3 + ede114f commit 4833b97

File tree

2 files changed

+76
-57
lines changed

2 files changed

+76
-57
lines changed

src/key.rs

Lines changed: 76 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -636,12 +636,11 @@ impl KeyPair {
636636
&mut self.0,
637637
tweak.as_c_ptr(),
638638
);
639-
640-
if err == 1 {
641-
Ok(())
642-
} else {
643-
Err(Error::InvalidTweak)
639+
if err != 1 {
640+
return Err(Error::InvalidTweak);
644641
}
642+
643+
Ok(())
645644
}
646645
}
647646
}
@@ -825,15 +824,18 @@ impl XOnlyPublicKey {
825824

826825
/// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
827826
///
828-
/// Returns a boolean representing the parity of the tweaked key, which can be provided to
827+
/// # Return
828+
/// An opaque type representing the parity of the tweaked key, this should be provided to
829829
/// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating
830-
/// it and checking equality. Will return an error if the resulting key would be invalid or
831-
/// if the tweak was not a 32-byte length slice.
830+
/// it and checking equality.
831+
///
832+
/// # Error
833+
/// If the resulting key would be invalid or if the tweak was not a 32-byte length slice.
832834
pub fn tweak_add_assign<V: Verification>(
833835
&mut self,
834836
secp: &Secp256k1<V>,
835837
tweak: &[u8],
836-
) -> Result<bool, Error> {
838+
) -> Result<Parity, Error> {
837839
if tweak.len() != 32 {
838840
return Err(Error::InvalidTweak);
839841
}
@@ -846,7 +848,6 @@ impl XOnlyPublicKey {
846848
self.as_c_ptr(),
847849
tweak.as_c_ptr(),
848850
);
849-
850851
if err != 1 {
851852
return Err(Error::InvalidTweak);
852853
}
@@ -858,16 +859,15 @@ impl XOnlyPublicKey {
858859
&mut parity,
859860
&pubkey,
860861
);
861-
862862
if err == 0 {
863-
Err(Error::InvalidPublicKey)
864-
} else {
865-
Ok(parity != 0)
863+
return Err(Error::InvalidPublicKey);
866864
}
865+
866+
Ok(parity.into())
867867
}
868868
}
869869

870-
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly
870+
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly.
871871
///
872872
/// Should be called on the original untweaked key. Takes the tweaked key and
873873
/// output parity from `tweak_add_assign` as input.
@@ -876,19 +876,22 @@ impl XOnlyPublicKey {
876876
/// and checking equality. However, in future this API will support batch
877877
/// verification, which is significantly faster, so it is wise to design
878878
/// protocols with this in mind.
879+
///
880+
/// # Return
881+
/// True if tweak and check is successful, false otherwise.
879882
pub fn tweak_add_check<V: Verification>(
880883
&self,
881884
secp: &Secp256k1<V>,
882885
tweaked_key: &Self,
883-
tweaked_parity: bool,
886+
tweaked_parity: Parity,
884887
tweak: [u8; 32],
885888
) -> bool {
886889
let tweaked_ser = tweaked_key.serialize();
887890
unsafe {
888891
let err = ffi::secp256k1_xonly_pubkey_tweak_add_check(
889892
secp.ctx,
890893
tweaked_ser.as_c_ptr(),
891-
if tweaked_parity { 1 } else { 0 },
894+
tweaked_parity.into(),
892895
&self.0,
893896
tweak.as_c_ptr(),
894897
);
@@ -898,6 +901,21 @@ impl XOnlyPublicKey {
898901
}
899902
}
900903

904+
/// Opaque type used to hold the parity passed between FFI function calls.
905+
pub struct Parity(i32);
906+
907+
impl From<i32> for Parity {
908+
fn from(parity: i32) -> Parity {
909+
Parity(parity)
910+
}
911+
}
912+
913+
impl From<Parity> for i32 {
914+
fn from(parity: Parity) -> i32 {
915+
parity.0
916+
}
917+
}
918+
901919
impl CPtr for XOnlyPublicKey {
902920
type Target = ffi::XOnlyPublicKey;
903921
fn as_c_ptr(&self) -> *const Self::Target {
@@ -964,17 +982,17 @@ impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey {
964982

965983
#[cfg(test)]
966984
mod test {
967-
use Secp256k1;
968-
use {from_hex, to_hex};
969-
use super::super::Error::{InvalidPublicKey, InvalidSecretKey};
970-
use super::{PublicKey, SecretKey};
971-
use super::super::constants;
985+
use super::*;
972986

973-
use rand::{Error, ErrorKind, RngCore, thread_rng};
974-
use rand_core::impls;
975987
use std::iter;
976988
use std::str::FromStr;
977989

990+
use rand::{Error, ErrorKind, RngCore, thread_rng};
991+
use rand_core::impls;
992+
993+
use {to_hex, constants};
994+
use Error::{InvalidPublicKey, InvalidSecretKey};
995+
978996
#[cfg(target_arch = "wasm32")]
979997
use wasm_bindgen_test::wasm_bindgen_test as test;
980998

@@ -1476,4 +1494,38 @@ mod test {
14761494
assert_tokens(&pk.readable(), &[Token::String(PK_STR)]);
14771495

14781496
}
1497+
1498+
#[test]
1499+
fn test_tweak_add_assign_then_tweak_add_check() {
1500+
let s = Secp256k1::new();
1501+
1502+
for _ in 0..10 {
1503+
let mut tweak = [0u8; 32];
1504+
thread_rng().fill_bytes(&mut tweak);
1505+
let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng());
1506+
let orig_pk = pk;
1507+
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
1508+
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
1509+
assert_eq!(XOnlyPublicKey::from_keypair(&kp), pk);
1510+
assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak));
1511+
}
1512+
}
1513+
1514+
#[test]
1515+
fn test_from_key_pubkey() {
1516+
let kpk1 = PublicKey::from_str(
1517+
"02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443",
1518+
)
1519+
.unwrap();
1520+
let kpk2 = PublicKey::from_str(
1521+
"0384526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07",
1522+
)
1523+
.unwrap();
1524+
1525+
let pk1 = XOnlyPublicKey::from(kpk1);
1526+
let pk2 = XOnlyPublicKey::from(kpk2);
1527+
1528+
assert_eq!(pk1.serialize()[..], kpk1.serialize()[1..]);
1529+
assert_eq!(pk2.serialize()[..], kpk2.serialize()[1..]);
1530+
}
14791531
}

src/schnorr.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -565,37 +565,4 @@ mod tests {
565565
assert_tokens(&pk.readable(), &[Token::Str(PK_STR)]);
566566
assert_tokens(&pk.readable(), &[Token::String(PK_STR)]);
567567
}
568-
#[test]
569-
fn test_addition() {
570-
let s = Secp256k1::new();
571-
572-
for _ in 0..10 {
573-
let mut tweak = [0u8; 32];
574-
thread_rng().fill_bytes(&mut tweak);
575-
let (mut kp, mut pk) = s.generate_schnorrsig_keypair(&mut thread_rng());
576-
let orig_pk = pk;
577-
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
578-
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
579-
assert_eq!(XOnlyPublicKey::from_keypair(&kp), pk);
580-
assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak));
581-
}
582-
}
583-
584-
#[test]
585-
fn test_from_key_pubkey() {
586-
let kpk1 = ::key::PublicKey::from_str(
587-
"02e6642fd69bd211f93f7f1f36ca51a26a5290eb2dd1b0d8279a87bb0d480c8443",
588-
)
589-
.unwrap();
590-
let kpk2 = ::key::PublicKey::from_str(
591-
"0384526253c27c7aef56c7b71a5cd25bebb66dddda437826defc5b2568bde81f07",
592-
)
593-
.unwrap();
594-
595-
let pk1 = XOnlyPublicKey::from(kpk1);
596-
let pk2 = XOnlyPublicKey::from(kpk2);
597-
598-
assert_eq!(pk1.serialize()[..], kpk1.serialize()[1..]);
599-
assert_eq!(pk2.serialize()[..], kpk2.serialize()[1..]);
600-
}
601568
}

0 commit comments

Comments
 (0)