diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs index d4b50199..579d1f27 100644 --- a/bitcoin/src/crypto/ecdsa.rs +++ b/bitcoin/src/crypto/ecdsa.rs @@ -9,12 +9,15 @@ use core::str::FromStr; use core::{fmt, iter}; use bitcoin_internals::write_err; +use bitcoin_internals::hex::display::DisplayHex; use secp256k1; use crate::prelude::*; use crate::hashes::hex::{self, FromHex}; use crate::sighash::{EcdsaSighashType, NonStandardSighashType}; +const MAX_SIG_LEN: usize = 73; + /// An ECDSA signature with the corresponding hash type. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -47,6 +50,23 @@ impl Signature { } /// Serializes an ECDSA signature (inner secp256k1 signature in DER format). + /// + /// This does **not** perform extra heap allocation. + pub fn serialize(&self) -> SerializedSignature { + let mut buf = [0u8; MAX_SIG_LEN]; + let signature = self.sig.serialize_der(); + buf[..signature.len()].copy_from_slice(&signature); + buf[signature.len()] = self.hash_ty as u8; + SerializedSignature { + data: buf, + len: signature.len() + 1, + } + } + + /// Serializes an ECDSA signature (inner secp256k1 signature in DER format) into `Vec`. + /// + /// Note: this performs an extra heap allocation, you might prefer the + /// [`serialize`](Self::serialize) method instead. pub fn to_vec(self) -> Vec { // TODO: add support to serialize to a writer to SerializedSig self.sig.serialize_der() @@ -77,6 +97,116 @@ impl FromStr for Signature { } } +/// Holds signature serialized in-line (not in `Vec`). +/// +/// This avoids allocation and allows proving maximum size of the signature (73 bytes). +/// The type can be used largely as a byte slice. It implements all standard traits one would +/// expect and has familiar methods. +#[derive(Copy, Clone)] +pub struct SerializedSignature { + data: [u8; MAX_SIG_LEN], + len: usize, +} + +impl SerializedSignature { + /// Returns an iterator over bytes of the signature. + #[inline] + pub fn iter(&self) -> core::slice::Iter<'_, u8> { + self.into_iter() + } +} + +impl core::ops::Deref for SerializedSignature { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.data[..self.len] + } +} + +impl core::ops::DerefMut for SerializedSignature { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data[..self.len] + } +} + +impl AsRef<[u8]> for SerializedSignature { + #[inline] + fn as_ref(&self) -> &[u8] { + self + } +} + +impl AsMut<[u8]> for SerializedSignature { + #[inline] + fn as_mut(&mut self) -> &mut [u8] { + self + } +} + +impl core::borrow::Borrow<[u8]> for SerializedSignature { + #[inline] + fn borrow(&self) -> &[u8] { + self + } +} + +impl core::borrow::BorrowMut<[u8]> for SerializedSignature { + #[inline] + fn borrow_mut(&mut self) -> &mut [u8] { + self + } +} + +impl fmt::Debug for SerializedSignature { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } +} + +impl fmt::Display for SerializedSignature { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) } +} + +impl fmt::LowerHex for SerializedSignature { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(&(**self).as_hex(), f) + } +} + +impl fmt::UpperHex for SerializedSignature { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::UpperHex::fmt(&(**self).as_hex(), f) + } +} + +impl PartialEq for SerializedSignature { + #[inline] + fn eq(&self, other: &SerializedSignature) -> bool { **self == **other } +} + +impl Eq for SerializedSignature {} + +impl core::hash::Hash for SerializedSignature { + fn hash(&self, state: &mut H) { + core::hash::Hash::hash(&**self, state) + } +} + +impl<'a> IntoIterator for &'a SerializedSignature { + type IntoIter = core::slice::Iter<'a, u8>; + type Item = &'a u8; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + (*self).iter() + } +} + /// A key-related error. #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[non_exhaustive]