diff --git a/src/lib.rs b/src/lib.rs index aad5e612..8502c6b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,8 +135,8 @@ pub use util::amount::SignedAmount; pub use util::merkleblock::MerkleBlock; pub use util::sighash::SchnorrSigHashType; -pub use util::ecdsa; -pub use util::schnorr; +pub use util::ecdsa::{self, EcdsaSig, EcdsaSigError}; +pub use util::schnorr::{self, SchnorrSig, SchnorrSigError}; #[deprecated(since = "0.26.1", note = "Please use `ecdsa::PrivateKey` instead")] pub use util::ecdsa::PrivateKey; #[deprecated(since = "0.26.1", note = "Please use `ecdsa::PublicKey` instead")] diff --git a/src/util/schnorr.rs b/src/util/schnorr.rs index 2a2bc19f..10455524 100644 --- a/src/util/schnorr.rs +++ b/src/util/schnorr.rs @@ -17,11 +17,14 @@ //! Schnorr key types. //! +use core::fmt; +use prelude::*; + pub use secp256k1::schnorrsig::{PublicKey, KeyPair}; -use secp256k1::{Secp256k1, Verification, constants}; +use secp256k1::{self, Secp256k1, Verification, constants}; use hashes::Hash; use util::taproot::{TapBranchHash, TapTweakHash}; -use core::fmt; +use SchnorrSigHashType; /// Untweaked Schnorr public key pub type UntweakedPublicKey = PublicKey; @@ -104,3 +107,87 @@ impl TweakedPublicKey { self.0.serialize() } } + +/// A BIP340-341 serialized schnorr signature with the corresponding hash type. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SchnorrSig { + /// The underlying schnorr signature + pub sig: secp256k1::schnorrsig::Signature, + /// The corresponding hash type + pub hash_ty: SchnorrSigHashType, +} + +impl SchnorrSig { + + /// Deserialize from slice + pub fn from_slice(sl: &[u8]) -> Result { + match sl.len() { + 64 => { + // default type + let sig = secp256k1::schnorrsig::Signature::from_slice(sl) + .map_err(SchnorrSigError::Secp256k1)?; + return Ok( SchnorrSig { sig, hash_ty : SchnorrSigHashType::Default }); + }, + 65 => { + let (hash_ty, sig) = sl.split_last().expect("Slice len checked == 65"); + let hash_ty = SchnorrSigHashType::from_u8(*hash_ty) + .map_err(|_| SchnorrSigError::InvalidSighashType(*hash_ty))?; + let sig = secp256k1::schnorrsig::Signature::from_slice(sig) + .map_err(SchnorrSigError::Secp256k1)?; + Ok(SchnorrSig { sig, hash_ty }) + } + len => { + Err(SchnorrSigError::InvalidSchnorrSigSize(len)) + } + } + } + + /// Serialize SchnorrSig + pub fn to_vec(&self) -> Vec { + // TODO: add support to serialize to a writer to SerializedSig + let mut ser_sig = self.sig.as_ref().to_vec(); + if self.hash_ty == SchnorrSigHashType::Default { + // default sighash type, don't add extra sighash byte + } else { + ser_sig.push(self.hash_ty as u8); + } + ser_sig + } + +} + +/// A schnorr sig related error. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum SchnorrSigError { + /// Base58 encoding error + InvalidSighashType(u8), + /// Signature has valid size but does not parse correctly + Secp256k1(secp256k1::Error), + /// Invalid schnorr signature size + InvalidSchnorrSigSize(usize), +} + + +impl fmt::Display for SchnorrSigError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + SchnorrSigError::InvalidSighashType(hash_ty) => + write!(f, "Invalid signature hash type {}", hash_ty), + SchnorrSigError::Secp256k1(ref e) => + write!(f, "Schnorr Signature has correct len, but is malformed : {}", e), + SchnorrSigError::InvalidSchnorrSigSize(sz) => + write!(f, "Invalid Schnorr signature size: {}", sz), + } + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl ::std::error::Error for SchnorrSigError {} + +impl From for SchnorrSigError { + + fn from(e: secp256k1::Error) -> SchnorrSigError { + SchnorrSigError::Secp256k1(e) + } +}