From c21dabb824ca14fb643970c54b589619d2562323 Mon Sep 17 00:00:00 2001 From: Ivan Paljak Date: Wed, 23 Sep 2020 16:07:25 +0200 Subject: [PATCH] Expose serialized data for transaction signatures --- src/blockdata/transaction.rs | 57 ++++++++++++++++++++++++------------ src/util/bip143.rs | 51 ++++++++++++++++++++------------ 2 files changed, 71 insertions(+), 37 deletions(-) diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 8cc37d84..ca81e7c7 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -307,30 +307,34 @@ impl Transaction { Wtxid::from_engine(enc) } - /// Computes a signature hash for a given input index with a given sighash flag. - /// To actually produce a scriptSig, this hash needs to be run through an - /// ECDSA signer, the SigHashType appended to the resulting sig, and a - /// script written around this, but this is the general (and hard) part. + /// Encodes the signing data from which a signature hash for a given input index with a given + /// sighash flag can be computed. To actually produce a scriptSig, this hash needs to be run + /// through an ECDSA signer, the SigHashType appended to the resulting sig, and a script + /// written around this, but this is the general (and hard) part. /// - /// *Warning* This does NOT attempt to support OP_CODESEPARATOR. In general - /// this would require evaluating `script_pubkey` to determine which separators - /// get evaluated and which don't, which we don't have the information to - /// determine. + /// *Warning* This does NOT attempt to support OP_CODESEPARATOR. In general this would require + /// evaluating `script_pubkey` to determine which separators get evaluated and which don't, + /// which we don't have the information to determine. /// - /// # Panics - /// Panics if `input_index` is greater than or equal to `self.input.len()` + /// # Panics Panics if `input_index` is greater than or equal to `self.input.len()` /// - pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> SigHash { + pub fn encode_signing_data_to( + &self, + mut writer: Write, + input_index: usize, + script_pubkey: &Script, + sighash_u32: u32 + ) { assert!(input_index < self.input.len()); // Panic on OOB let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag(); // Special-case sighash_single bug because this is easy enough. if sighash == SigHashType::Single && input_index >= self.output.len() { - return SigHash::from_slice(&[1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0]).unwrap(); + writer.write_all(&[1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0]).unwrap(); } // Build tx to sign @@ -373,10 +377,27 @@ impl Transaction { _ => unreachable!() }; // hash the result - let mut engine = SigHash::engine(); - tx.consensus_encode(&mut engine).unwrap(); + tx.consensus_encode(&mut writer).unwrap(); let sighash_arr = endian::u32_to_array_le(sighash_u32); - sighash_arr.consensus_encode(&mut engine).unwrap(); + sighash_arr.consensus_encode(&mut writer).unwrap(); + } + + /// Computes a signature hash for a given input index with a given sighash flag. + /// To actually produce a scriptSig, this hash needs to be run through an + /// ECDSA signer, the SigHashType appended to the resulting sig, and a + /// script written around this, but this is the general (and hard) part. + /// + /// *Warning* This does NOT attempt to support OP_CODESEPARATOR. In general + /// this would require evaluating `script_pubkey` to determine which separators + /// get evaluated and which don't, which we don't have the information to + /// determine. + /// + /// # Panics + /// Panics if `input_index` is greater than or equal to `self.input.len()` + /// + pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighash_u32: u32) -> SigHash { + let mut engine = SigHash::engine(); + self.encode_signing_data_to(&mut engine, input_index, script_pubkey, sighash_u32); SigHash::from_engine(engine) } diff --git a/src/util/bip143.rs b/src/util/bip143.rs index fdaaf405..688d9cef 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -25,6 +25,7 @@ use blockdata::script::Script; use blockdata::transaction::{Transaction, TxIn, SigHashType}; use consensus::encode::Encodable; +use std::io; use std::ops::{Deref, DerefMut}; /// Parts of a sighash which are common across inputs or signatures, and which are @@ -168,27 +169,32 @@ impl> SigHashCache { }) } - /// Compute the BIP143 sighash for any flag type. See SighashComponents::sighash_all simpler - /// API for the most common case - pub fn signature_hash(&mut self, input_index: usize, script_code: &Script, value: u64, sighash_type: SigHashType) -> SigHash { - + /// Encode the BIP143 signing data for any flag type into a given object implementing a + /// std::io::Write trait. + pub fn encode_signing_data_to( + &mut self, + mut writer: Write, + input_index: usize, + script_code: &Script, + value: u64, + sighash_type: SigHashType, + ) { let zero_hash = sha256d::Hash::default(); let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag(); - let mut enc = SigHash::engine(); - self.tx.version.consensus_encode(&mut enc).unwrap(); + self.tx.version.consensus_encode(&mut writer).unwrap(); if !anyone_can_pay { - self.hash_prevouts().consensus_encode(&mut enc).unwrap(); + self.hash_prevouts().consensus_encode(&mut writer).unwrap(); } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer).unwrap(); } if !anyone_can_pay && sighash != SigHashType::Single && sighash != SigHashType::None { - self.hash_sequence().consensus_encode(&mut enc).unwrap(); + self.hash_sequence().consensus_encode(&mut writer).unwrap(); } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer).unwrap(); } { @@ -196,25 +202,32 @@ impl> SigHashCache { txin .previous_output - .consensus_encode(&mut enc) + .consensus_encode(&mut writer) .unwrap(); - script_code.consensus_encode(&mut enc).unwrap(); - value.consensus_encode(&mut enc).unwrap(); - txin.sequence.consensus_encode(&mut enc).unwrap(); + script_code.consensus_encode(&mut writer).unwrap(); + value.consensus_encode(&mut writer).unwrap(); + txin.sequence.consensus_encode(&mut writer).unwrap(); } if sighash != SigHashType::Single && sighash != SigHashType::None { - self.hash_outputs().consensus_encode(&mut enc).unwrap(); + self.hash_outputs().consensus_encode(&mut writer).unwrap(); } else if sighash == SigHashType::Single && input_index < self.tx.output.len() { let mut single_enc = SigHash::engine(); self.tx.output[input_index].consensus_encode(&mut single_enc).unwrap(); - SigHash::from_engine(single_enc).consensus_encode(&mut enc).unwrap(); + SigHash::from_engine(single_enc).consensus_encode(&mut writer).unwrap(); } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer).unwrap(); } - self.tx.lock_time.consensus_encode(&mut enc).unwrap(); - sighash_type.as_u32().consensus_encode(&mut enc).unwrap(); + self.tx.lock_time.consensus_encode(&mut writer).unwrap(); + sighash_type.as_u32().consensus_encode(&mut writer).unwrap(); + } + + /// Compute the BIP143 sighash for any flag type. See SighashComponents::sighash_all simpler + /// API for the most common case + pub fn signature_hash(&mut self, input_index: usize, script_code: &Script, value: u64, sighash_type: SigHashType) -> SigHash { + let mut enc = SigHash::engine(); + self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type); SigHash::from_engine(enc) } }