From 0ab5eeac811b74e606a2c7a57af3c1e458a151f4 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 5 May 2022 03:27:28 +0000 Subject: [PATCH] Add method to push an ECDSA sig + sighash type byte on a witness We do this all over the place in rust-lightning, and its probably the most common thing to do with a `Witness` so I figured I'd upstream the util method to do this. It also avoids an allocation compared to the naive approach of `SerializedSignature.to_vec()` with two pushes, which is nice. --- src/blockdata/witness.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/blockdata/witness.rs b/src/blockdata/witness.rs index 3c700007..246a3a69 100644 --- a/src/blockdata/witness.rs +++ b/src/blockdata/witness.rs @@ -3,10 +3,12 @@ //! This module contains the [`Witness`] struct and related methods to operate on it //! +use blockdata::transaction::EcdsaSighashType; use consensus::encode::{Error, MAX_VEC_SIZE}; use consensus::{Decodable, Encodable, WriteExt}; use io::{self, Read, Write}; use prelude::*; +use secp256k1::ecdsa; use VarInt; #[cfg(feature = "serde")] @@ -211,6 +213,17 @@ impl Witness { self.content[end_varint..].copy_from_slice(new_element); } + /// Pushes a DER-encoded ECDSA signature with a signature hash type as a new element on the + /// witness, requires an allocation. + pub fn push_bitcoin_signature(&mut self, signature: &ecdsa::SerializedSignature, hash_type: EcdsaSighashType) { + // Note that a maximal length ECDSA signature is 72 bytes, plus the sighash type makes 73 + let mut sig = [0; 73]; + sig[..signature.len()].copy_from_slice(&signature); + sig[signature.len()] = hash_type as u8; + self.push(&sig[..signature.len() + 1]); + } + + fn element_at(&self, index: usize) -> Option<&[u8]> { let varint = VarInt::consensus_decode(&self.content[index..]).ok()?; let start = index + varint.len(); @@ -289,10 +302,12 @@ impl<'de> serde::Deserialize<'de> for Witness { #[cfg(test)] mod test { + use blockdata::transaction::EcdsaSighashType; use blockdata::witness::Witness; use consensus::{deserialize, serialize}; use hashes::hex::{FromHex, ToHex}; use Transaction; + use secp256k1::ecdsa; #[test] fn test_push() { @@ -321,6 +336,20 @@ mod test { assert_eq!(witness.second_to_last(), Some(&[0u8][..])); } + #[test] + fn test_push_ecdsa_sig() { + // The very first signature in block 734,958 + let sig_bytes = + Vec::from_hex("304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b541"); + let sig = ecdsa::Signature::from_der(&sig_bytes.unwrap()).unwrap(); + let mut witness = Witness::default(); + witness.push_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All); + let expected_witness = vec![Vec::from_hex( + "304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b54101") + .unwrap()]; + assert_eq!(witness.to_vec(), expected_witness); + } + #[test] fn test_witness() { let w0 =