Skip to content

Commit 5a7fe06

Browse files
committed
feat: implement byte array conversions for XOnlyPublicKey
Adds `to_byte_array`, `from_byte_array`, and `try_from_byte_array` to XOnlyPublicKey. The implementation wraps `secp256k1_xonly_pubkey_parse` to ensure the provided x-coordinate represents a valid point on the curve. The `from_byte_array` method uses a `debug_assert` to satisfy the `bytes_rtt_test` macro requirements, while `try_from_byte_array` provides a fallible interface for production use.
1 parent 0b3d9ff commit 5a7fe06

4 files changed

Lines changed: 30 additions & 10 deletions

File tree

src/key/mod.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ impl str::FromStr for XOnlyPublicKey {
845845
fn from_str(s: &str) -> Result<XOnlyPublicKey, Error> {
846846
let mut res = [0u8; constants::SCHNORR_PUBLIC_KEY_SIZE];
847847
match from_hex(s, &mut res) {
848-
Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => XOnlyPublicKey::from_byte_array(res),
848+
Ok(constants::SCHNORR_PUBLIC_KEY_SIZE) => XOnlyPublicKey::try_from_byte_array(res),
849849
_ => Err(Error::InvalidPublicKey),
850850
}
851851
}
@@ -882,19 +882,34 @@ impl XOnlyPublicKey {
882882
#[inline]
883883
pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> {
884884
match <[u8; constants::SCHNORR_PUBLIC_KEY_SIZE]>::try_from(data) {
885-
Ok(data) => Self::from_byte_array(data),
885+
Ok(data) => Self::try_from_byte_array(data),
886886
Err(_) => Err(InvalidPublicKey),
887887
}
888888
}
889889

890+
/// Creates a schnorr public key directly from a byte array.
891+
#[inline]
892+
pub fn from_byte_array(data: [u8; constants::SCHNORR_PUBLIC_KEY_SIZE]) -> XOnlyPublicKey {
893+
unsafe {
894+
let mut pk = ffi::XOnlyPublicKey::new();
895+
let res = ffi::secp256k1_xonly_pubkey_parse(
896+
ffi::secp256k1_context_no_precomp,
897+
&mut pk,
898+
data.as_c_ptr(),
899+
);
900+
debug_assert_eq!(res, 1);
901+
XOnlyPublicKey(pk)
902+
}
903+
}
904+
890905
/// Creates a schnorr public key directly from a byte array.
891906
///
892907
/// # Errors
893908
///
894909
/// Returns [`Error::InvalidPublicKey`] if the array does not represent a valid Secp256k1 point
895910
/// x coordinate.
896911
#[inline]
897-
pub fn from_byte_array(
912+
pub fn try_from_byte_array(
898913
data: [u8; constants::SCHNORR_PUBLIC_KEY_SIZE],
899914
) -> Result<XOnlyPublicKey, Error> {
900915
unsafe {
@@ -912,6 +927,10 @@ impl XOnlyPublicKey {
912927
}
913928
}
914929

930+
/// Returns the byte array of an `XOnlyPublicKey`
931+
#[inline]
932+
pub fn to_byte_array(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] { self.serialize() }
933+
915934
#[inline]
916935
/// Serializes the key as a byte-encoded x coordinate value (32 bytes).
917936
pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] {
@@ -1255,7 +1274,7 @@ impl<'de> serde::Deserialize<'de> for XOnlyPublicKey {
12551274
} else {
12561275
let visitor = super::serde_util::Tuple32Visitor::new(
12571276
"raw 32 bytes schnorr public key",
1258-
XOnlyPublicKey::from_byte_array,
1277+
XOnlyPublicKey::try_from_byte_array,
12591278
);
12601279
d.deserialize_tuple(constants::SCHNORR_PUBLIC_KEY_SIZE, visitor)
12611280
}
@@ -1348,7 +1367,7 @@ impl<'a> Arbitrary<'a> for XOnlyPublicKey {
13481367
}
13491368

13501369
u.fill_buffer(&mut bytes[..])?;
1351-
if let Ok(pk) = XOnlyPublicKey::from_byte_array(bytes) {
1370+
if let Ok(pk) = XOnlyPublicKey::try_from_byte_array(bytes) {
13521371
return Ok(pk);
13531372
}
13541373
}
@@ -1998,7 +2017,7 @@ mod test {
19982017
let pk = PublicKey::from_slice(&pk_bytes).expect("failed to create pk from iterator");
19992018
let kp = Keypair::from_secret_key(&sk);
20002019
let xonly =
2001-
XOnlyPublicKey::from_byte_array(PK_BYTES).expect("failed to get xonly from slice");
2020+
XOnlyPublicKey::try_from_byte_array(PK_BYTES).expect("failed to get xonly from slice");
20022021

20032022
(sk, pk, kp, xonly)
20042023
}

src/schnorr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ mod tests {
388388
let (pk, _parity) = kp.x_only_public_key();
389389

390390
let ser = pk.serialize();
391-
let pubkey2 = XOnlyPublicKey::from_byte_array(ser).unwrap();
391+
let pubkey2 = XOnlyPublicKey::try_from_byte_array(ser).unwrap();
392392
assert_eq!(pk, pubkey2);
393393
}
394394

@@ -514,7 +514,7 @@ mod tests {
514514
170, 12, 208, 84, 74, 200, 135, 254, 145, 221, 209, 102,
515515
];
516516
static PK_STR: &str = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
517-
let pk = XOnlyPublicKey::from_byte_array(PK_BYTES).unwrap();
517+
let pk = XOnlyPublicKey::try_from_byte_array(PK_BYTES).unwrap();
518518

519519
assert_tokens(&sig.compact(), &[Token::BorrowedBytes(&SIG_BYTES[..])]);
520520
assert_tokens(&sig.compact(), &[Token::Bytes(&SIG_BYTES[..])]);
@@ -733,7 +733,7 @@ mod tests {
733733
assert_eq!(sig.to_byte_array(), signature);
734734
}
735735
let sig = Signature::from_byte_array(signature);
736-
let is_verified = if let Ok(pubkey) = XOnlyPublicKey::from_byte_array(public_key) {
736+
let is_verified = if let Ok(pubkey) = XOnlyPublicKey::try_from_byte_array(public_key) {
737737
verify(&sig, &message, &pubkey).is_ok()
738738
} else {
739739
false

tests/api.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ macro_rules! bytes_rtt_test {
5959
// FIXME ElligatorSwift should pass this
6060
// Scalar has to_be_bytes and to_le_bytes (and corresponding froms)
6161
bytes_rtt_test!(rtt_i, schnorr::Signature);
62+
bytes_rtt_test!(rtt_c, XOnlyPublicKey);
6263

6364
macro_rules! secret_bytes_rtt_test {
6465
($name: ident, $ty:ty) => {

tests/serde.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ fn bincode_keypair() {
105105

106106
#[test]
107107
fn bincode_x_only_public_key() {
108-
let pk = XOnlyPublicKey::from_byte_array(XONLY_PK_BYTES)
108+
let pk = XOnlyPublicKey::try_from_byte_array(XONLY_PK_BYTES)
109109
.expect("failed to create xonly pk from slice");
110110
let ser = bincode::serialize(&pk).unwrap();
111111

0 commit comments

Comments
 (0)