Update how x coordinates are handled in bitcoin-serai

This commit is contained in:
Luke Parker
2025-08-18 13:02:35 -04:00
parent ceede14f5c
commit 95c30720d2
3 changed files with 19 additions and 13 deletions

View File

@@ -7,15 +7,18 @@ use k256::{
use bitcoin::key::XOnlyPublicKey; use bitcoin::key::XOnlyPublicKey;
/// Get the x coordinate of a non-infinity, even point. Panics on invalid input. /// Get the x coordinate of a non-infinity point.
pub fn x(key: &ProjectivePoint) -> [u8; 32] { ///
/// Panics on invalid input.
fn x(key: &ProjectivePoint) -> [u8; 32] {
let encoded = key.to_encoded_point(true); let encoded = key.to_encoded_point(true);
assert_eq!(encoded.tag(), Tag::CompressedEvenY, "x coordinate of odd key");
(*encoded.x().expect("point at infinity")).into() (*encoded.x().expect("point at infinity")).into()
} }
/// Convert a non-infinity even point to a XOnlyPublicKey. Panics on invalid input. /// Convert a non-infinity point to a XOnlyPublicKey (dropping its sign).
pub fn x_only(key: &ProjectivePoint) -> XOnlyPublicKey { ///
/// Panics on invalid input.
pub(crate) fn x_only(key: &ProjectivePoint) -> XOnlyPublicKey {
XOnlyPublicKey::from_slice(&x(key)).expect("x_only was passed a point which was infinity or odd") XOnlyPublicKey::from_slice(&x(key)).expect("x_only was passed a point which was infinity or odd")
} }
@@ -46,9 +49,9 @@ mod frost_crypto {
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm. /// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
/// ///
/// If passed an odd nonce, it will have the generator added until it is even. /// If passed an odd nonce, the challenge will be negated.
/// ///
/// If the key is odd, this will panic. /// If either `R` or `A` is the point at infinity, this will panic.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct Hram; pub struct Hram;
#[allow(non_snake_case)] #[allow(non_snake_case)]
@@ -72,9 +75,12 @@ mod frost_crypto {
/// BIP-340 Schnorr signature algorithm. /// BIP-340 Schnorr signature algorithm.
/// ///
/// This must be used with a ThresholdKeys whose group key is even. If it is odd, this may panic. /// This may panic if called with nonces/a group key which are the point at infinity (which have
/// a negligible probability for a well-reasoned caller, even with malicious participants
/// present).
/// ///
/// `verify`, `verify_share` must be called after `sign_share` is called. /// `verify`, `verify_share` MUST be called after `sign_share` is called. Otherwise, this library
/// MAY panic.
#[derive(Clone)] #[derive(Clone)]
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>); pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
impl Schnorr { impl Schnorr {

View File

@@ -39,9 +39,9 @@ pub use send::*;
/// from being spent via a script. To have keys which have spendable script paths, further offsets /// from being spent via a script. To have keys which have spendable script paths, further offsets
/// from this position must be used. /// from this position must be used.
/// ///
/// After adding an unspendable script path, the key is incremented until its even. This means the /// After adding an unspendable script path, the key is negated if odd.
/// existence of the unspendable script path may not provable, without an understanding of the ///
/// algorithm used here. /// This has a neligible probability of returning keys whose group key is the point at infinity.
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> { pub fn tweak_keys(keys: ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
// Adds the unspendable script path per // Adds the unspendable script path per

View File

@@ -288,7 +288,7 @@ impl SignableTransaction {
/// A FROST signing machine to produce a Bitcoin transaction. /// A FROST signing machine to produce a Bitcoin transaction.
/// ///
/// This does not support caching its preprocess. When sign is called, the message must be empty. /// This does not support caching its preprocess. When sign is called, the message must be empty.
/// This will panic if either `cache` is called or the message isn't empty. /// This will panic if either `cache`, `from_cache` is called or the message isn't empty.
pub struct TransactionMachine { pub struct TransactionMachine {
tx: SignableTransaction, tx: SignableTransaction,
sigs: Vec<AlgorithmMachine<Secp256k1, Schnorr>>, sigs: Vec<AlgorithmMachine<Secp256k1, Schnorr>>,