Skip to content

Commit d4fb819

Browse files
Move XOnlyPublicKey to key module
1 parent 87d936a commit d4fb819

File tree

3 files changed

+248
-248
lines changed

3 files changed

+248
-248
lines changed

src/key.rs

Lines changed: 246 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
1818
#[cfg(any(test, feature = "rand"))] use rand::Rng;
1919

20-
use core::{fmt, str};
20+
use core::{fmt, ptr, str};
2121

2222
use super::{from_hex, Secp256k1};
2323
use super::Error::{self, InvalidPublicKey, InvalidPublicKeySum, InvalidSecretKey};
@@ -674,6 +674,251 @@ impl<'a> From<&'a KeyPair> for PublicKey {
674674
}
675675
}
676676

677+
/// A x-only public key, used for verification of Schnorr signatures and serialized according to BIP-340.
678+
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
679+
pub struct XOnlyPublicKey(ffi::XOnlyPublicKey);
680+
681+
impl fmt::LowerHex for XOnlyPublicKey {
682+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
683+
let ser = self.serialize();
684+
for ch in &ser[..] {
685+
write!(f, "{:02x}", *ch)?;
686+
}
687+
Ok(())
688+
}
689+
}
690+
691+
impl fmt::Display for XOnlyPublicKey {
692+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
693+
fmt::LowerHex::fmt(self, f)
694+
}
695+
}
696+
697+
impl str::FromStr for XOnlyPublicKey {
698+
type Err = Error;
699+
fn from_str(s: &str) -> Result<XOnlyPublicKey, Error> {
700+
let mut res = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE];
701+
match from_hex(s, &mut res) {
702+
Ok(constants::SCHNORRSIG_PUBLIC_KEY_SIZE) => {
703+
XOnlyPublicKey::from_slice(&res[0..constants::SCHNORRSIG_PUBLIC_KEY_SIZE])
704+
}
705+
_ => Err(Error::InvalidPublicKey),
706+
}
707+
}
708+
}
709+
710+
impl XOnlyPublicKey {
711+
/// Obtains a raw const pointer suitable for use with FFI functions
712+
#[inline]
713+
pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey {
714+
&self.0
715+
}
716+
717+
/// Obtains a raw mutable pointer suitable for use with FFI functions
718+
#[inline]
719+
pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey {
720+
&mut self.0
721+
}
722+
723+
/// Creates a new Schnorr public key from a Schnorr key pair.
724+
#[inline]
725+
pub fn from_keypair<C: Signing>(secp: &Secp256k1<C>, keypair: &KeyPair) -> XOnlyPublicKey {
726+
let mut pk_parity = 0;
727+
unsafe {
728+
let mut xonly_pk = ffi::XOnlyPublicKey::new();
729+
let ret = ffi::secp256k1_keypair_xonly_pub(
730+
secp.ctx,
731+
&mut xonly_pk,
732+
&mut pk_parity,
733+
keypair.as_ptr(),
734+
);
735+
debug_assert_eq!(ret, 1);
736+
XOnlyPublicKey(xonly_pk)
737+
}
738+
}
739+
740+
/// Creates a Schnorr public key directly from a slice
741+
///
742+
/// # Errors
743+
///
744+
/// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the
745+
/// slice does not represent a valid Secp256k1 point x coordinate
746+
#[inline]
747+
pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> {
748+
if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE {
749+
return Err(Error::InvalidPublicKey);
750+
}
751+
752+
unsafe {
753+
let mut pk = ffi::XOnlyPublicKey::new();
754+
if ffi::secp256k1_xonly_pubkey_parse(
755+
ffi::secp256k1_context_no_precomp,
756+
&mut pk,
757+
data.as_c_ptr(),
758+
) == 1
759+
{
760+
Ok(XOnlyPublicKey(pk))
761+
} else {
762+
Err(Error::InvalidPublicKey)
763+
}
764+
}
765+
}
766+
767+
#[inline]
768+
/// Serialize the key as a byte-encoded x coordinate value (32 bytes).
769+
pub fn serialize(&self) -> [u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE] {
770+
let mut ret = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE];
771+
772+
unsafe {
773+
let err = ffi::secp256k1_xonly_pubkey_serialize(
774+
ffi::secp256k1_context_no_precomp,
775+
ret.as_mut_c_ptr(),
776+
self.as_c_ptr(),
777+
);
778+
debug_assert_eq!(err, 1);
779+
}
780+
ret
781+
}
782+
783+
/// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
784+
///
785+
/// Returns a boolean representing the parity of the tweaked key, which can be provided to
786+
/// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating
787+
/// it and checking equality. Will return an error if the resulting key would be invalid or
788+
/// if the tweak was not a 32-byte length slice.
789+
pub fn tweak_add_assign<V: Verification>(
790+
&mut self,
791+
secp: &Secp256k1<V>,
792+
tweak: &[u8],
793+
) -> Result<bool, Error> {
794+
if tweak.len() != 32 {
795+
return Err(Error::InvalidTweak);
796+
}
797+
798+
unsafe {
799+
let mut pubkey = ffi::PublicKey::new();
800+
let mut err = ffi::secp256k1_xonly_pubkey_tweak_add(
801+
secp.ctx,
802+
&mut pubkey,
803+
self.as_c_ptr(),
804+
tweak.as_c_ptr(),
805+
);
806+
807+
if err != 1 {
808+
return Err(Error::InvalidTweak);
809+
}
810+
811+
let mut parity: ::secp256k1_sys::types::c_int = 0;
812+
err = ffi::secp256k1_xonly_pubkey_from_pubkey(
813+
secp.ctx,
814+
&mut self.0,
815+
&mut parity,
816+
&pubkey,
817+
);
818+
819+
if err == 0 {
820+
Err(Error::InvalidPublicKey)
821+
} else {
822+
Ok(parity != 0)
823+
}
824+
}
825+
}
826+
827+
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly
828+
///
829+
/// Should be called on the original untweaked key. Takes the tweaked key and
830+
/// output parity from `tweak_add_assign` as input.
831+
///
832+
/// Currently this is not much more efficient than just recomputing the tweak
833+
/// and checking equality. However, in future this API will support batch
834+
/// verification, which is significantly faster, so it is wise to design
835+
/// protocols with this in mind.
836+
pub fn tweak_add_check<V: Verification>(
837+
&self,
838+
secp: &Secp256k1<V>,
839+
tweaked_key: &Self,
840+
tweaked_parity: bool,
841+
tweak: [u8; 32],
842+
) -> bool {
843+
let tweaked_ser = tweaked_key.serialize();
844+
unsafe {
845+
let err = ffi::secp256k1_xonly_pubkey_tweak_add_check(
846+
secp.ctx,
847+
tweaked_ser.as_c_ptr(),
848+
if tweaked_parity { 1 } else { 0 },
849+
&self.0,
850+
tweak.as_c_ptr(),
851+
);
852+
853+
err == 1
854+
}
855+
}
856+
}
857+
858+
impl CPtr for XOnlyPublicKey {
859+
type Target = ffi::XOnlyPublicKey;
860+
fn as_c_ptr(&self) -> *const Self::Target {
861+
self.as_ptr()
862+
}
863+
864+
fn as_mut_c_ptr(&mut self) -> *mut Self::Target {
865+
self.as_mut_ptr()
866+
}
867+
}
868+
869+
/// Creates a new Schnorr public key from a FFI x-only public key
870+
impl From<ffi::XOnlyPublicKey> for XOnlyPublicKey {
871+
#[inline]
872+
fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey {
873+
XOnlyPublicKey(pk)
874+
}
875+
}
876+
877+
impl From<::key::PublicKey> for XOnlyPublicKey {
878+
fn from(src: ::key::PublicKey) -> XOnlyPublicKey {
879+
unsafe {
880+
let mut pk = ffi::XOnlyPublicKey::new();
881+
assert_eq!(
882+
1,
883+
ffi::secp256k1_xonly_pubkey_from_pubkey(
884+
ffi::secp256k1_context_no_precomp,
885+
&mut pk,
886+
ptr::null_mut(),
887+
src.as_c_ptr(),
888+
)
889+
);
890+
XOnlyPublicKey(pk)
891+
}
892+
}
893+
}
894+
895+
#[cfg(feature = "serde")]
896+
impl ::serde::Serialize for XOnlyPublicKey {
897+
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
898+
if s.is_human_readable() {
899+
s.collect_str(self)
900+
} else {
901+
s.serialize_bytes(&self.serialize())
902+
}
903+
}
904+
}
905+
906+
#[cfg(feature = "serde")]
907+
impl<'de> ::serde::Deserialize<'de> for XOnlyPublicKey {
908+
fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
909+
if d.is_human_readable() {
910+
d.deserialize_str(super::serde_util::FromStrVisitor::new(
911+
"a hex string representing 32 byte schnorr public key"
912+
))
913+
} else {
914+
d.deserialize_bytes(super::serde_util::BytesVisitor::new(
915+
"raw 32 bytes schnorr public key",
916+
XOnlyPublicKey::from_slice
917+
))
918+
}
919+
}
920+
}
921+
677922
#[cfg(test)]
678923
mod test {
679924
use Secp256k1;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ pub use key::SecretKey;
156156
pub use key::PublicKey;
157157
pub use key::ONE_KEY;
158158
pub use key::KeyPair;
159+
pub use key::XOnlyPublicKey;
159160
pub use context::*;
160161
use core::marker::PhantomData;
161162
use core::{mem, fmt, ptr, str};

0 commit comments

Comments
 (0)