Merge rust-bitcoin/rust-secp256k1#392: Add global context API
eb453b8227
Add global context API (Tobin Harding)3ecb5e41b3
Refactor from_secret_key definition (Tobin Harding)e2d47a29e2
Remove unnecessary import statement (Tobin Harding)d79989bc95
Remove erroneous duplicate feature (Tobin Harding) Pull request description: Our API often involves a `Secp256k1` parameter, when users enable the `global-context` feature they must then pass `SECP256K1` into these functions. This is kind of clunky since the global is by definition available everywhere. Make the API more ergonomic for `global-context` builds by adding various API functions/methods that use the global context implicitly. The first 3 patches are clean up. Resolves: #330 ACKs for top commit: apoelstra: ACKeb453b8227
Tree-SHA512: 21d89a6688c24a7920d48ea92d923889bec2bbe9dc5ed5e33639405be45a50f50022a28dc1f235b8bea850ac39013c7dd24b5aed086ed40f5b259dd44c06433d
This commit is contained in:
commit
4f0f542c99
|
@ -12,6 +12,9 @@ mod recovery;
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "recovery")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "recovery")))]
|
||||||
pub use self::recovery::{RecoveryId, RecoverableSignature};
|
pub use self::recovery::{RecoveryId, RecoverableSignature};
|
||||||
|
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
use SECP256K1;
|
||||||
|
|
||||||
/// An ECDSA signature
|
/// An ECDSA signature
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct Signature(pub(crate) ffi::Signature);
|
pub struct Signature(pub(crate) ffi::Signature);
|
||||||
|
@ -269,6 +272,14 @@ impl Signature {
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verifies an ECDSA signature for `msg` using `pk` and the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
|
pub fn verify(&self, msg: &Message, pk: &PublicKey) -> Result<(), Error> {
|
||||||
|
SECP256K1.verify_ecdsa(msg, self, pk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CPtr for Signature {
|
impl CPtr for Signature {
|
||||||
|
|
|
@ -121,6 +121,15 @@ impl RecoverableSignature {
|
||||||
Signature(ret)
|
Signature(ret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determines the public key for which this [`Signature`] is valid for `msg`. Requires a
|
||||||
|
/// verify-capable context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
|
pub fn recover(&self, msg: &Message) -> Result<key::PublicKey, Error> {
|
||||||
|
SECP256K1.recover_ecdsa(msg, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
53
src/key.rs
53
src/key.rs
|
@ -28,6 +28,11 @@ use Verification;
|
||||||
use constants;
|
use constants;
|
||||||
use ffi::{self, CPtr};
|
use ffi::{self, CPtr};
|
||||||
|
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
use {Message, ecdsa, SECP256K1};
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand-std"))]
|
||||||
|
use schnorr;
|
||||||
|
|
||||||
/// Secret 256-bit key used as `x` in an ECDSA signature.
|
/// Secret 256-bit key used as `x` in an ECDSA signature.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -279,6 +284,14 @@ impl SecretKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs an ECDSA signature for `msg` using the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
|
pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature {
|
||||||
|
SECP256K1.sign_ecdsa(&msg, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
@ -338,9 +351,7 @@ impl PublicKey {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_secret_key<C: Signing>(secp: &Secp256k1<C>,
|
pub fn from_secret_key<C: Signing>(secp: &Secp256k1<C>,sk: &SecretKey) -> PublicKey {
|
||||||
sk: &SecretKey)
|
|
||||||
-> PublicKey {
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut pk = ffi::PublicKey::new();
|
let mut pk = ffi::PublicKey::new();
|
||||||
// We can assume the return value because it's not possible to construct
|
// We can assume the return value because it's not possible to construct
|
||||||
|
@ -351,6 +362,14 @@ impl PublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new public key from a [`SecretKey`] and the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
|
pub fn from_secret_key_global(sk: &SecretKey) -> PublicKey {
|
||||||
|
PublicKey::from_secret_key(&SECP256K1, sk)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a public key directly from a slice.
|
/// Creates a public key directly from a slice.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
|
pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
|
||||||
|
@ -740,6 +759,18 @@ impl KeyPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a Schnorr [`KeyPair`] directly from a secret key string and the global [`SECP256K1`] context.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// [`Error::InvalidSecretKey`] if corresponding public key for the provided secret key is not even.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(feature = "global-context")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
|
pub fn from_seckey_str_global(s: &str) -> Result<KeyPair, Error> {
|
||||||
|
KeyPair::from_seckey_str(SECP256K1, s)
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a new random secret key.
|
/// Generates a new random secret key.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -770,6 +801,14 @@ impl KeyPair {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a new random secret key using the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand"))))]
|
||||||
|
pub fn new_global<R: ::rand::Rng + ?Sized>(rng: &mut R) -> KeyPair {
|
||||||
|
KeyPair::new(SECP256K1, rng)
|
||||||
|
}
|
||||||
|
|
||||||
/// Serializes the key pair as a secret key byte value.
|
/// Serializes the key pair as a secret key byte value.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
|
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
|
||||||
|
@ -832,6 +871,14 @@ impl KeyPair {
|
||||||
pub fn public_key(&self) -> XOnlyPublicKey {
|
pub fn public_key(&self) -> XOnlyPublicKey {
|
||||||
XOnlyPublicKey::from_keypair(self)
|
XOnlyPublicKey::from_keypair(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs an schnorr signature for `msg` using the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand-std"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand-std"))))]
|
||||||
|
pub fn sign_schnorr(&self, msg: Message) -> schnorr::Signature {
|
||||||
|
SECP256K1.sign_schnorr(&msg, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<KeyPair> for SecretKey {
|
impl From<KeyPair> for SecretKey {
|
||||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -57,6 +57,22 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! If the "global-context" feature is enabled you have access to an alternate API.
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! # #[cfg(all(feature="global-context", feature = "std", feature="rand-std", features = "bitcoin_hashes"))] {
|
||||||
|
//! use secp256k1::rand::thread_rng;
|
||||||
|
//! use secp256k1::{generate_keypair, Message};
|
||||||
|
//! use secp256k1::hashes::sha256;
|
||||||
|
//!
|
||||||
|
//! let (secret_key, public_key) = generate_keypair(&mut thread_rng());
|
||||||
|
//! let message = Message::from_hashed_data::<sha256::Hash>("Hello World!".as_bytes());
|
||||||
|
//!
|
||||||
|
//! let sig = secret_key.sign_ecdsa(&message, &secret_key);
|
||||||
|
//! assert!(sig.verify(&message, &public_key).is_ok());
|
||||||
|
//! # }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! The above code requires `rust-secp256k1` to be compiled with the `rand-std` and `bitcoin_hashes`
|
//! The above code requires `rust-secp256k1` to be compiled with the `rand-std` and `bitcoin_hashes`
|
||||||
//! feature enabled, to get access to [`generate_keypair`](struct.Secp256k1.html#method.generate_keypair)
|
//! feature enabled, to get access to [`generate_keypair`](struct.Secp256k1.html#method.generate_keypair)
|
||||||
//! Alternately, keys and messages can be parsed from slices, like
|
//! Alternately, keys and messages can be parsed from slices, like
|
||||||
|
@ -198,7 +214,7 @@ use core::{mem, fmt, str};
|
||||||
use ffi::{CPtr, types::AlignedType};
|
use ffi::{CPtr, types::AlignedType};
|
||||||
|
|
||||||
#[cfg(feature = "global-context")]
|
#[cfg(feature = "global-context")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(any(feature = "global-context", feature = "global-context"))))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "global-context")))]
|
||||||
pub use context::global::SECP256K1;
|
pub use context::global::SECP256K1;
|
||||||
|
|
||||||
#[cfg(feature = "bitcoin_hashes")]
|
#[cfg(feature = "bitcoin_hashes")]
|
||||||
|
@ -458,6 +474,14 @@ impl<C: Signing> Secp256k1<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a random keypair using the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand"))))]
|
||||||
|
pub fn generate_keypair<R: Rng + ?Sized>(rng: &mut R) -> (key::SecretKey, key::PublicKey) {
|
||||||
|
SECP256K1.generate_keypair(rng)
|
||||||
|
}
|
||||||
|
|
||||||
/// Utility function used to parse hex into a target u8 buffer. Returns
|
/// Utility function used to parse hex into a target u8 buffer. Returns
|
||||||
/// the number of bytes converted or an error if it encounters an invalid
|
/// the number of bytes converted or an error if it encounters an invalid
|
||||||
/// character or unexpected end of string.
|
/// character or unexpected end of string.
|
||||||
|
@ -960,8 +984,6 @@ mod tests {
|
||||||
#[cfg(feature = "global-context")]
|
#[cfg(feature = "global-context")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_global_context() {
|
fn test_global_context() {
|
||||||
use super::SECP256K1;
|
|
||||||
|
|
||||||
let sk_data = hex!("e6dd32f8761625f105c39a39f19370b3521d845a12456d60ce44debd0a362641");
|
let sk_data = hex!("e6dd32f8761625f105c39a39f19370b3521d845a12456d60ce44debd0a362641");
|
||||||
let sk = SecretKey::from_slice(&sk_data).unwrap();
|
let sk = SecretKey::from_slice(&sk_data).unwrap();
|
||||||
let msg_data = hex!("a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d");
|
let msg_data = hex!("a4965ca63b7d8562736ceec36dfa5a11bf426eb65be8ea3f7a49ae363032da0d");
|
||||||
|
|
|
@ -13,6 +13,9 @@ use ffi::{self, CPtr};
|
||||||
use {constants, Secp256k1};
|
use {constants, Secp256k1};
|
||||||
use {Message, Signing, Verification, KeyPair, XOnlyPublicKey};
|
use {Message, Signing, Verification, KeyPair, XOnlyPublicKey};
|
||||||
|
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand-std"))]
|
||||||
|
use SECP256K1;
|
||||||
|
|
||||||
/// Represents a Schnorr signature.
|
/// Represents a Schnorr signature.
|
||||||
pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]);
|
pub struct Signature([u8; constants::SCHNORRSIG_SIGNATURE_SIZE]);
|
||||||
impl_array_newtype!(Signature, u8, constants::SCHNORRSIG_SIGNATURE_SIZE);
|
impl_array_newtype!(Signature, u8, constants::SCHNORRSIG_SIGNATURE_SIZE);
|
||||||
|
@ -88,6 +91,14 @@ impl Signature {
|
||||||
_ => Err(Error::InvalidSignature),
|
_ => Err(Error::InvalidSignature),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verifies a schnorr signature for `msg` using `pk` and the global [`SECP256K1`] context.
|
||||||
|
#[inline]
|
||||||
|
#[cfg(all(feature = "global-context", feature = "rand-std"))]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(all(feature = "global-context", feature = "rand-std"))))]
|
||||||
|
pub fn verify(&self, msg: &Message, pk: &XOnlyPublicKey) -> Result<(), Error> {
|
||||||
|
SECP256K1.verify_schnorr(self, msg, pk)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Signing> Secp256k1<C> {
|
impl<C: Signing> Secp256k1<C> {
|
||||||
|
|
Loading…
Reference in New Issue