From 3cfd746bbc4aa961a1a3f2a624ffb810c70df493 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 23 Jan 2024 19:10:58 +1100 Subject: [PATCH] Add functionality to serialize signatures to a writer Serializing the ecdsa and taproot `Signature` straight to a writer is a useful thing to be able to do. To both ECDSA and Taproot types: - Add `SerializedSignature::to_writer` - Add `Signature::serialize_to_writer` Remove TODO comments from code. --- bitcoin/src/crypto/ecdsa.rs | 34 ++++++++++++++++++++- bitcoin/src/crypto/taproot.rs | 9 +++++- bitcoin/src/taproot/serialized_signature.rs | 7 +++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/crypto/ecdsa.rs b/bitcoin/src/crypto/ecdsa.rs index 2ce06002..24940b02 100644 --- a/bitcoin/src/crypto/ecdsa.rs +++ b/bitcoin/src/crypto/ecdsa.rs @@ -9,6 +9,7 @@ use core::{fmt, iter}; use hex::FromHex; use internals::write_err; +use io::Write; use secp256k1; use crate::prelude::*; @@ -58,7 +59,6 @@ impl Signature { /// 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.signature .serialize_der() .iter() @@ -66,6 +66,13 @@ impl Signature { .chain(iter::once(self.sighash_type as u8)) .collect() } + + /// Serializes an ECDSA signature (inner secp256k1 signature in DER format) to a `writer`. + #[inline] + pub fn serialize_to_writer(&self, writer: &mut W) -> Result<(), io::Error> { + let sig = self.serialize(); + sig.write_to(writer) + } } impl fmt::Display for Signature { @@ -105,6 +112,12 @@ impl SerializedSignature { /// Returns an iterator over bytes of the signature. #[inline] pub fn iter(&self) -> core::slice::Iter<'_, u8> { self.into_iter() } + + /// Writes this serialized signature to a `writer`. + #[inline] + pub fn write_to(&self, writer: &mut W) -> Result<(), io::Error> { + writer.write_all(self) + } } impl core::ops::Deref for SerializedSignature { @@ -239,3 +252,22 @@ impl From for Error { impl From for Error { fn from(err: hex::HexToBytesError) -> Self { Error::Hex(err) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn write_serialized_signature() { + let hex = "3046022100839c1fbc5304de944f697c9f4b1d01d1faeba32d751c0f7acb21ac8a0f436a72022100e89bd46bb3a5a62adc679f659b7ce876d83ee297c7a5587b2011c4fcc72eab45"; + let sig = Signature { + signature: secp256k1::ecdsa::Signature::from_str(hex).unwrap(), + sighash_type: EcdsaSighashType::All, + }; + + let mut buf = vec![]; + sig.serialize_to_writer(&mut buf).expect("write failed"); + + assert_eq!(sig.to_vec(), buf) + } +} diff --git a/bitcoin/src/crypto/taproot.rs b/bitcoin/src/crypto/taproot.rs index c0a1a2d7..b64a8e4d 100644 --- a/bitcoin/src/crypto/taproot.rs +++ b/bitcoin/src/crypto/taproot.rs @@ -8,6 +8,7 @@ use core::fmt; use internals::write_err; +use io::Write; use crate::prelude::*; use crate::sighash::{InvalidSighashTypeError, TapSighashType}; @@ -47,7 +48,6 @@ impl Signature { /// /// Note: this allocates on the heap, prefer [`serialize`](Self::serialize) if vec is not needed. pub fn to_vec(self) -> Vec { - // TODO: add support to serialize to a writer to SerializedSig let mut ser_sig = self.signature.as_ref().to_vec(); if self.sighash_type == TapSighashType::Default { // default sighash type, don't add extra sighash byte @@ -57,6 +57,13 @@ impl Signature { ser_sig } + /// Serializes the signature to `writer`. + #[inline] + pub fn serialize_to_writer(&self, writer: &mut W) -> Result<(), io::Error> { + let sig = self.serialize(); + sig.write_to(writer) + } + /// Serializes the signature (without heap allocation) /// /// This returns a type with an API very similar to that of `Box<[u8]>`. diff --git a/bitcoin/src/taproot/serialized_signature.rs b/bitcoin/src/taproot/serialized_signature.rs index ebf9ccf4..581c139b 100644 --- a/bitcoin/src/taproot/serialized_signature.rs +++ b/bitcoin/src/taproot/serialized_signature.rs @@ -10,6 +10,7 @@ use core::convert::TryFrom; use core::{fmt, ops}; pub use into_iter::IntoIter; +use io::Write; use super::{SigFromSliceError, Signature}; @@ -166,6 +167,12 @@ impl SerializedSignature { /// (this serializes it) #[inline] pub fn from_signature(sig: &Signature) -> SerializedSignature { sig.serialize() } + + /// Writes this serialized signature to a `writer`. + #[inline] + pub fn write_to(&self, writer: &mut W) -> Result<(), io::Error> { + writer.write_all(self) + } } /// Separate mod to prevent outside code from accidentally breaking invariants.