Skip to content

Commit 397614f

Browse files
committed
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.
1 parent c8bc9af commit 397614f

File tree

1 file changed

+29
-0
lines changed

1 file changed

+29
-0
lines changed

src/blockdata/witness.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
//! This module contains the [`Witness`] struct and related methods to operate on it
44
//!
55
6+
use blockdata::transaction::EcdsaSighashType;
67
use consensus::encode::{Error, MAX_VEC_SIZE};
78
use consensus::{Decodable, Encodable, WriteExt};
89
use io::{self, Read, Write};
910
use prelude::*;
11+
use secp256k1::ecdsa;
1012
use VarInt;
1113

1214
#[cfg(feature = "serde")]
@@ -211,6 +213,17 @@ impl Witness {
211213
self.content[end_varint..].copy_from_slice(new_element);
212214
}
213215

216+
/// Pushes a DER-encoded ECDSA signature with a signature hash type as a new element on the
217+
/// witness, requires an allocation.
218+
pub fn push_bitcoin_signature(&mut self, signature: &ecdsa::SerializedSignature, hash_type: EcdsaSighashType) {
219+
// Note that a maximal length ECDSA signature is 72 bytes, plus the sighash type makes 73
220+
let mut sig = [0; 73];
221+
sig[..signature.len()].copy_from_slice(&signature);
222+
sig[signature.len()] = hash_type as u8;
223+
self.push(&sig[..signature.len() + 1]);
224+
}
225+
226+
214227
fn element_at(&self, index: usize) -> Option<&[u8]> {
215228
let varint = VarInt::consensus_decode(&self.content[index..]).ok()?;
216229
let start = index + varint.len();
@@ -289,10 +302,12 @@ impl<'de> serde::Deserialize<'de> for Witness {
289302

290303
#[cfg(test)]
291304
mod test {
305+
use blockdata::transaction::EcdsaSighashType;
292306
use blockdata::witness::Witness;
293307
use consensus::{deserialize, serialize};
294308
use hashes::hex::{FromHex, ToHex};
295309
use Transaction;
310+
use secp256k1::ecdsa;
296311

297312
#[test]
298313
fn test_push() {
@@ -321,6 +336,20 @@ mod test {
321336
assert_eq!(witness.second_to_last(), Some(&[0u8][..]));
322337
}
323338

339+
#[test]
340+
fn test_push_ecdsa_sig() {
341+
// The very first signature in block 734,958
342+
let sig_bytes =
343+
Vec::from_hex("304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b541");
344+
let sig = ecdsa::Signature::from_der(&sig_bytes.unwrap()).unwrap();
345+
let mut witness = Witness::default();
346+
witness.push_bitcoin_signature(&sig.serialize_der(), EcdsaSighashType::All);
347+
let expected_witness = vec![Vec::from_hex(
348+
"304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b54101")
349+
.unwrap()];
350+
assert_eq!(witness.to_vec(), expected_witness);
351+
}
352+
324353
#[test]
325354
fn test_witness() {
326355
let w0 =

0 commit comments

Comments
 (0)