diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 4c68fb79..a5979318 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -302,30 +302,35 @@ 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 + ) -> Result<(), encode::Error> { 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])?; + return Ok(()); } // Build tx to sign @@ -368,11 +373,34 @@ impl Transaction { _ => unreachable!() }; // hash the result - let mut engine = SigHash::engine(); - tx.consensus_encode(&mut engine).unwrap(); + tx.consensus_encode(&mut writer)?; let sighash_arr = endian::u32_to_array_le(sighash_u32); - sighash_arr.consensus_encode(&mut engine).unwrap(); - SigHash::from_engine(engine) + sighash_arr.consensus_encode(&mut writer)?; + Ok(()) + } + + /// 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 + ) -> Result { + let mut engine = SigHash::engine(); + self.encode_signing_data_to(&mut engine, input_index, script_pubkey, sighash_u32)?; + Ok(SigHash::from_engine(engine)) } /// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty @@ -869,7 +897,7 @@ mod tests { raw_expected.reverse(); let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap(); - let actual_result = tx.signature_hash(input_index, &script, hash_type as u32); + let actual_result = tx.signature_hash(input_index, &script, hash_type as u32).unwrap(); assert_eq!(actual_result, expected_result); } diff --git a/src/util/bip143.rs b/src/util/bip143.rs index fdaaf405..f5cb5c1c 100644 --- a/src/util/bip143.rs +++ b/src/util/bip143.rs @@ -23,8 +23,9 @@ use hashes::{Hash, sha256d}; use hash_types::SigHash; use blockdata::script::Script; use blockdata::transaction::{Transaction, TxIn, SigHashType}; -use consensus::encode::Encodable; +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, + ) -> Result<(), encode::Error> { 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)?; if !anyone_can_pay { - self.hash_prevouts().consensus_encode(&mut enc).unwrap(); + self.hash_prevouts().consensus_encode(&mut writer)?; } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer)?; } 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)?; } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer)?; } { @@ -196,26 +202,39 @@ impl> SigHashCache { txin .previous_output - .consensus_encode(&mut enc) - .unwrap(); - script_code.consensus_encode(&mut enc).unwrap(); - value.consensus_encode(&mut enc).unwrap(); - txin.sequence.consensus_encode(&mut enc).unwrap(); + .consensus_encode(&mut writer)?; + script_code.consensus_encode(&mut writer)?; + value.consensus_encode(&mut writer)?; + txin.sequence.consensus_encode(&mut writer)?; } if sighash != SigHashType::Single && sighash != SigHashType::None { - self.hash_outputs().consensus_encode(&mut enc).unwrap(); + self.hash_outputs().consensus_encode(&mut writer)?; } 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(); + self.tx.output[input_index].consensus_encode(&mut single_enc)?; + SigHash::from_engine(single_enc).consensus_encode(&mut writer)?; } else { - zero_hash.consensus_encode(&mut enc).unwrap(); + zero_hash.consensus_encode(&mut writer)?; } - self.tx.lock_time.consensus_encode(&mut enc).unwrap(); - sighash_type.as_u32().consensus_encode(&mut enc).unwrap(); - SigHash::from_engine(enc) + self.tx.lock_time.consensus_encode(&mut writer)?; + sighash_type.as_u32().consensus_encode(&mut writer)?; + Ok(()) + } + + /// 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 + ) -> Result { + let mut enc = SigHash::engine(); + self.encode_signing_data_to(&mut enc, input_index, script_code, value, sighash_type)?; + Ok(SigHash::from_engine(enc)) } } @@ -273,7 +292,7 @@ mod tests { let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap(); let mut cache = SigHashCache::new(&tx); let sighash_type = SigHashType::from_u32(hash_type); - let actual_result = cache.signature_hash(input_index, &script, value, sighash_type); + let actual_result = cache.signature_hash(input_index, &script, value, sighash_type).unwrap(); assert_eq!(actual_result, expected_result); }