Skip to content

Commit f08276a

Browse files
committed
Add convenience methods for keys
We have a bunch of `from_<key>` methods for converting between key types. To improve the API and make it more ergonomic to use we can add methods that do the same but can be called on the initial key instead of on the resulting key's type. E.g. once applied the following are equivalent: - `let pk = PublicKey::from_keypair(kp)` - `let pk = kp.public_key()` Do this for `SecretKey`, `PublicKey`, `KeyPair`, and `XOnlyKeyPair`.
1 parent b4c7fa0 commit f08276a

File tree

2 files changed

+251
-15
lines changed

2 files changed

+251
-15
lines changed

src/key.rs

Lines changed: 244 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,31 @@ impl SecretKey {
292292
pub fn sign_ecdsa(&self, msg: Message) -> ecdsa::Signature {
293293
SECP256K1.sign_ecdsa(&msg, self)
294294
}
295+
296+
/// Returns the [`KeyPair`] for this [`SecretKey`].
297+
///
298+
/// This is equivalent to using [`KeyPair::from_secret_key`].
299+
#[inline]
300+
pub fn keypair<C: Signing>(&self, secp: &Secp256k1<C>) -> KeyPair {
301+
KeyPair::from_secret_key(secp, self)
302+
}
303+
304+
/// Returns the [`PublicKey`] for this [`SecretKey`].
305+
///
306+
/// This is equivalent to using [`PublicKey::from_secret_key`].
307+
#[inline]
308+
pub fn public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
309+
PublicKey::from_secret_key(secp, self)
310+
}
311+
312+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`SecretKey`].
313+
///
314+
/// This is equivalent to `XOnlyPublicKey::from_keypair(self.keypair(secp))`.
315+
#[inline]
316+
pub fn x_only_public_key<C: Signing>(&self, secp: &Secp256k1<C>) -> (XOnlyPublicKey, Parity) {
317+
let kp = self.keypair(secp);
318+
XOnlyPublicKey::from_keypair(&kp)
319+
}
295320
}
296321

297322
#[cfg(feature = "serde")]
@@ -418,6 +443,20 @@ impl PublicKey {
418443
}
419444
}
420445

446+
/// Creates a [`PublicKey`] using the key material from `pk` combined with the `parity`.
447+
pub fn from_x_only_public_key(pk: XOnlyPublicKey, parity: Parity) -> PublicKey {
448+
let mut buf = [0u8; 33];
449+
450+
// First byte of a compressed key should be `0x02 AND parity`.
451+
buf[0] = match parity {
452+
Parity::Even => 0x02,
453+
Parity::Odd => 0x03,
454+
};
455+
buf[1..].clone_from_slice(&pk.serialize());
456+
457+
PublicKey::from_slice(&buf).expect("we know the buffer is valid")
458+
}
459+
421460
#[inline]
422461
/// Serializes the key as a byte-encoded pair of values. In compressed form the y-coordinate is
423462
/// represented by only a single bit, as x determines it up to one bit.
@@ -589,6 +628,25 @@ impl PublicKey {
589628
}
590629
}
591630
}
631+
632+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`PublicKey`].
633+
#[inline]
634+
pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) {
635+
let mut pk_parity = 0;
636+
unsafe {
637+
let mut xonly_pk = ffi::XOnlyPublicKey::new();
638+
let ret = ffi::secp256k1_xonly_pubkey_from_pubkey(
639+
ffi::secp256k1_context_no_precomp,
640+
&mut xonly_pk,
641+
&mut pk_parity,
642+
self.as_ptr(),
643+
);
644+
debug_assert_eq!(ret, 1);
645+
let parity = Parity::from_i32(pk_parity).expect("should not panic, pk_parity is 0 or 1");
646+
647+
(XOnlyPublicKey(xonly_pk), parity)
648+
}
649+
}
592650
}
593651

594652
impl CPtr for PublicKey {
@@ -866,9 +924,27 @@ impl KeyPair {
866924
}
867925
}
868926

869-
/// Gets the [XOnlyPublicKey] for this [KeyPair].
927+
/// Returns the [`SecretKey`] for this [`KeyPair`].
928+
///
929+
/// This is equivalent to using [`SecretKey::from_keypair`].
930+
#[inline]
931+
pub fn secret_key(&self) -> SecretKey {
932+
SecretKey::from_keypair(self)
933+
}
934+
935+
/// Returns the [`PublicKey`] for this [`KeyPair`].
936+
///
937+
/// This is equivalent to using [`PublicKey::from_keypair`].
938+
#[inline]
939+
pub fn public_key(&self) -> PublicKey {
940+
PublicKey::from_keypair(self)
941+
}
942+
943+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for this [`KeyPair`].
944+
///
945+
/// This is equivalent to using [`XOnlyPublicKey::from_keypair`].
870946
#[inline]
871-
pub fn public_key(&self) -> XOnlyPublicKey {
947+
pub fn x_only_public_key(&self) -> (XOnlyPublicKey, Parity) {
872948
XOnlyPublicKey::from_keypair(self)
873949
}
874950

@@ -1014,9 +1090,9 @@ impl XOnlyPublicKey {
10141090
&mut self.0
10151091
}
10161092

1017-
/// Creates a new Schnorr public key from a Schnorr key pair.
1093+
/// Returns the [`XOnlyPublicKey`] (and it's [`Parity`]) for `keypair`.
10181094
#[inline]
1019-
pub fn from_keypair(keypair: &KeyPair) -> XOnlyPublicKey {
1095+
pub fn from_keypair(keypair: &KeyPair) -> (XOnlyPublicKey, Parity) {
10201096
let mut pk_parity = 0;
10211097
unsafe {
10221098
let mut xonly_pk = ffi::XOnlyPublicKey::new();
@@ -1027,7 +1103,9 @@ impl XOnlyPublicKey {
10271103
keypair.as_ptr(),
10281104
);
10291105
debug_assert_eq!(ret, 1);
1030-
XOnlyPublicKey(xonly_pk)
1106+
let parity = Parity::from_i32(pk_parity).expect("should not panic, pk_parity is 0 or 1");
1107+
1108+
(XOnlyPublicKey(xonly_pk), parity)
10311109
}
10321110
}
10331111

@@ -1098,7 +1176,7 @@ impl XOnlyPublicKey {
10981176
/// thread_rng().fill_bytes(&mut tweak);
10991177
///
11001178
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
1101-
/// let mut public_key = key_pair.public_key();
1179+
/// let (mut public_key, _parity) = key_pair.x_only_public_key();
11021180
/// public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
11031181
/// # }
11041182
/// ```
@@ -1163,7 +1241,7 @@ impl XOnlyPublicKey {
11631241
/// thread_rng().fill_bytes(&mut tweak);
11641242
///
11651243
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
1166-
/// let mut public_key = key_pair.public_key();
1244+
/// let (mut public_key, _) = key_pair.x_only_public_key();
11671245
/// let original = public_key;
11681246
/// let parity = public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
11691247
/// assert!(original.tweak_add_check(&secp, &public_key, parity, tweak));
@@ -1189,6 +1267,14 @@ impl XOnlyPublicKey {
11891267
err == 1
11901268
}
11911269
}
1270+
1271+
/// Returns the [`PublicKey`] for this [`XOnlyPublicKey`].
1272+
///
1273+
/// This is equivalent to using [`PublicKey::from_xonly_and_parity(self, parity)`].
1274+
#[inline]
1275+
pub fn public_key(&self, parity: Parity) -> PublicKey {
1276+
PublicKey::from_x_only_public_key(*self, parity)
1277+
}
11921278
}
11931279

11941280
/// Represents the parity passed between FFI function calls.
@@ -1978,12 +2064,15 @@ mod test {
19782064
thread_rng().fill_bytes(&mut tweak);
19792065

19802066
let mut kp = KeyPair::new(&s, &mut thread_rng());
1981-
let mut pk = kp.public_key();
2067+
let (mut pk, _parity) = kp.x_only_public_key();
19822068

19832069
let orig_pk = pk;
19842070
kp.tweak_add_assign(&s, &tweak).expect("Tweak error");
19852071
let parity = pk.tweak_add_assign(&s, &tweak).expect("Tweak error");
1986-
assert_eq!(XOnlyPublicKey::from_keypair(&kp), pk);
2072+
2073+
let (back, _) = XOnlyPublicKey::from_keypair(&kp);
2074+
2075+
assert_eq!(back, pk);
19872076
assert!(orig_pk.tweak_add_check(&s, &pk, parity, tweak));
19882077
}
19892078
}
@@ -2052,4 +2141,150 @@ mod test {
20522141
assert_tokens(&sk.readable(), &[Token::Str(SK_STR)]);
20532142
assert_tokens(&sk.readable(), &[Token::String(SK_STR)]);
20542143
}
2144+
2145+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2146+
fn keys() -> (SecretKey, PublicKey, KeyPair, XOnlyPublicKey) {
2147+
let secp = Secp256k1::new();
2148+
2149+
static SK_BYTES: [u8; 32] = [
2150+
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
2151+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2152+
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
2153+
0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
2154+
];
2155+
2156+
static PK_BYTES: [u8; 32] = [
2157+
0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f,
2158+
0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d,
2159+
0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54,
2160+
0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66
2161+
];
2162+
2163+
let mut pk_bytes = [0u8; 33];
2164+
pk_bytes[0] = 0x02; // Use positive Y co-ordinate.
2165+
pk_bytes[1..].clone_from_slice(&PK_BYTES);
2166+
2167+
let sk = SecretKey::from_slice(&SK_BYTES).expect("failed to parse sk bytes");
2168+
let pk = PublicKey::from_slice(&pk_bytes).expect("failed to create pk from iterator");
2169+
let kp = KeyPair::from_secret_key(&secp, &sk);
2170+
let xonly = XOnlyPublicKey::from_slice(&PK_BYTES).expect("failed to get xonly from slice");
2171+
2172+
(sk, pk, kp, xonly)
2173+
}
2174+
2175+
#[test]
2176+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2177+
fn convert_public_key_to_xonly_public_key() {
2178+
let (_sk, pk, _kp, want) = keys();
2179+
let (got, parity) = pk.x_only_public_key();
2180+
2181+
assert_eq!(parity, Parity::Even);
2182+
assert_eq!(got, want)
2183+
}
2184+
2185+
#[test]
2186+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2187+
fn convert_secret_key_to_public_key() {
2188+
let secp = Secp256k1::new();
2189+
2190+
let (sk, want, _kp, _xonly) = keys();
2191+
let got = sk.public_key(&secp);
2192+
2193+
assert_eq!(got, want)
2194+
}
2195+
2196+
#[test]
2197+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2198+
fn convert_secret_key_to_x_only_public_key() {
2199+
let secp = Secp256k1::new();
2200+
2201+
let (sk, _pk, _kp, want) = keys();
2202+
let (got, parity) = sk.x_only_public_key(&secp);
2203+
2204+
assert_eq!(parity, Parity::Even);
2205+
assert_eq!(got, want)
2206+
}
2207+
2208+
#[test]
2209+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2210+
fn convert_keypair_to_public_key() {
2211+
let (_sk, want, kp, _xonly) = keys();
2212+
let got = kp.public_key();
2213+
2214+
assert_eq!(got, want)
2215+
}
2216+
2217+
#[test]
2218+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2219+
fn convert_keypair_to_x_only_public_key() {
2220+
let (_sk, _pk, kp, want) = keys();
2221+
let (got, parity) = kp.x_only_public_key();
2222+
2223+
assert_eq!(parity, Parity::Even);
2224+
assert_eq!(got, want)
2225+
}
2226+
2227+
// SecretKey -> KeyPair -> SecretKey
2228+
#[test]
2229+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2230+
fn roundtrip_secret_key_via_keypair() {
2231+
let secp = Secp256k1::new();
2232+
let (sk, _pk, _kp, _xonly) = keys();
2233+
2234+
let kp = sk.keypair(&secp);
2235+
let back = kp.secret_key();
2236+
2237+
assert_eq!(back, sk)
2238+
}
2239+
2240+
// KeyPair -> SecretKey -> KeyPair
2241+
#[test]
2242+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2243+
fn roundtrip_keypair_via_secret_key() {
2244+
let secp = Secp256k1::new();
2245+
let (_sk, _pk, kp, _xonly) = keys();
2246+
2247+
let sk = kp.secret_key();
2248+
let back = sk.keypair(&secp);
2249+
2250+
assert_eq!(back, kp)
2251+
}
2252+
2253+
// XOnlyPublicKey -> PublicKey -> XOnlyPublicKey
2254+
#[test]
2255+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2256+
fn roundtrip_x_only_public_key_via_public_key() {
2257+
let (_sk, _pk, _kp, xonly) = keys();
2258+
2259+
let pk = xonly.public_key(Parity::Even);
2260+
let (back, parity) = pk.x_only_public_key();
2261+
2262+
assert_eq!(parity, Parity::Even);
2263+
assert_eq!(back, xonly)
2264+
}
2265+
2266+
// PublicKey -> XOnlyPublicKey -> PublicKey
2267+
#[test]
2268+
#[cfg(all(not(fuzzing), any(feature = "alloc", feature = "std")))]
2269+
fn roundtrip_public_key_via_x_only_public_key() {
2270+
let (_sk, pk, _kp, _xonly) = keys();
2271+
2272+
let (xonly, parity) = pk.x_only_public_key();
2273+
let back = xonly.public_key(parity);
2274+
2275+
assert_eq!(back, pk)
2276+
}
2277+
2278+
#[test]
2279+
fn public_key_from_x_only_public_key_and_odd_parity() {
2280+
let s = "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166";
2281+
let mut want = String::from("03");
2282+
want.push_str(s);
2283+
2284+
let xonly = XOnlyPublicKey::from_str(s).expect("failed to parse xonly pubkey string");
2285+
let pk = xonly.public_key(Parity::Odd);
2286+
let got = format!("{}", pk);
2287+
2288+
assert_eq!(got, want)
2289+
}
20552290
}

src/schnorr.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ impl <C: Signing> Secp256k1<C> {
268268
rng: &mut R,
269269
) -> (KeyPair, XOnlyPublicKey) {
270270
let sk = KeyPair::new(self, rng);
271-
let pubkey = XOnlyPublicKey::from_keypair(&sk);
271+
let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&sk);
272272
(sk, pubkey)
273273
}
274274
}
@@ -344,7 +344,7 @@ mod tests {
344344

345345
let mut rng = thread_rng();
346346
let kp = KeyPair::new(&secp, &mut rng);
347-
let pk = kp.public_key();
347+
let (pk, _parity) = kp.x_only_public_key();
348348

349349
let mut msg = [0u8; 32];
350350

@@ -414,7 +414,7 @@ mod tests {
414414
fn test_pubkey_serialize_roundtrip() {
415415
let secp = Secp256k1::new();
416416
let kp = KeyPair::new(&secp, &mut thread_rng());
417-
let pk = kp.public_key();
417+
let (pk, _parity) = kp.x_only_public_key();
418418

419419
let ser = pk.serialize();
420420
let pubkey2 = XOnlyPublicKey::from_slice(&ser).unwrap();
@@ -431,7 +431,7 @@ mod tests {
431431
assert_eq!(SecretKey::from_str(sk_str).unwrap(), sk);
432432
let pk = ::key::PublicKey::from_keypair(&keypair);
433433
assert_eq!(::key::PublicKey::from_secret_key(&secp, &sk), pk);
434-
let xpk = keypair.public_key();
434+
let (xpk, _parity) = keypair.x_only_public_key();
435435
assert_eq!(XOnlyPublicKey::from(pk), xpk);
436436
}
437437

@@ -478,7 +478,8 @@ mod tests {
478478

479479
// In fuzzing mode secret->public key derivation is different, so
480480
// hard-code the expected result.
481-
kp.public_key()
481+
let (pk, _parity) = kp.x_only_public_key();
482+
pk
482483
};
483484
#[cfg(fuzzing)]
484485
let pk = XOnlyPublicKey::from_slice(&[0x18, 0x84, 0x57, 0x81, 0xf6, 0x31, 0xc4, 0x8f, 0x1c, 0x97, 0x09, 0xe2, 0x30, 0x92, 0x06, 0x7d, 0x06, 0x83, 0x7f, 0x30, 0xaa, 0x0c, 0xd0, 0x54, 0x4a, 0xc8, 0x87, 0xfe, 0x91, 0xdd, 0xd1, 0x66]).expect("pk");
@@ -544,7 +545,7 @@ mod tests {
544545

545546
let secp = Secp256k1::new();
546547
let kp = KeyPair::new(&secp, &mut DumbRng(0));
547-
let pk = kp.public_key();
548+
let (pk, _parity) = kp.x_only_public_key();
548549
assert_eq!(
549550
&pk.serialize()[..],
550551
&[

0 commit comments

Comments
 (0)