Expose serialized data for transaction signatures

This commit is contained in:
Ivan Paljak 2020-09-23 16:07:25 +02:00 committed by Ivan Paljak
parent c1ae3b7955
commit c21dabb824
2 changed files with 71 additions and 37 deletions

View File

@ -307,27 +307,31 @@ impl Transaction {
Wtxid::from_engine(enc) Wtxid::from_engine(enc)
} }
/// Computes a signature hash for a given input index with a given sighash flag. /// Encodes the signing data from which a signature hash for a given input index with a given
/// To actually produce a scriptSig, this hash needs to be run through an /// sighash flag can be computed. To actually produce a scriptSig, this hash needs to be run
/// ECDSA signer, the SigHashType appended to the resulting sig, and a /// through an ECDSA signer, the SigHashType appended to the resulting sig, and a script
/// script written around this, but this is the general (and hard) part. /// written around this, but this is the general (and hard) part.
/// ///
/// *Warning* This does NOT attempt to support OP_CODESEPARATOR. In general /// *Warning* This does NOT attempt to support OP_CODESEPARATOR. In general this would require
/// this would require evaluating `script_pubkey` to determine which separators /// evaluating `script_pubkey` to determine which separators get evaluated and which don't,
/// get evaluated and which don't, which we don't have the information to /// which we don't have the information to determine.
/// determine.
/// ///
/// # Panics /// # Panics Panics if `input_index` is greater than or equal to `self.input.len()`
/// 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<Write: io::Write>(
&self,
mut writer: Write,
input_index: usize,
script_pubkey: &Script,
sighash_u32: u32
) {
assert!(input_index < self.input.len()); // Panic on OOB assert!(input_index < self.input.len()); // Panic on OOB
let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag(); let (sighash, anyone_can_pay) = SigHashType::from_u32(sighash_u32).split_anyonecanpay_flag();
// Special-case sighash_single bug because this is easy enough. // Special-case sighash_single bug because this is easy enough.
if sighash == SigHashType::Single && input_index >= self.output.len() { if sighash == SigHashType::Single && input_index >= self.output.len() {
return SigHash::from_slice(&[1, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0]).unwrap(); 0, 0, 0, 0, 0, 0, 0, 0]).unwrap();
@ -373,10 +377,27 @@ impl Transaction {
_ => unreachable!() _ => unreachable!()
}; };
// hash the result // hash the result
let mut engine = SigHash::engine(); tx.consensus_encode(&mut writer).unwrap();
tx.consensus_encode(&mut engine).unwrap();
let sighash_arr = endian::u32_to_array_le(sighash_u32); 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) SigHash::from_engine(engine)
} }

View File

@ -25,6 +25,7 @@ use blockdata::script::Script;
use blockdata::transaction::{Transaction, TxIn, SigHashType}; use blockdata::transaction::{Transaction, TxIn, SigHashType};
use consensus::encode::Encodable; use consensus::encode::Encodable;
use std::io;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
/// Parts of a sighash which are common across inputs or signatures, and which are /// Parts of a sighash which are common across inputs or signatures, and which are
@ -168,27 +169,32 @@ impl<R: Deref<Target=Transaction>> SigHashCache<R> {
}) })
} }
/// Compute the BIP143 sighash for any flag type. See SighashComponents::sighash_all simpler /// Encode the BIP143 signing data for any flag type into a given object implementing a
/// API for the most common case /// std::io::Write trait.
pub fn signature_hash(&mut self, input_index: usize, script_code: &Script, value: u64, sighash_type: SigHashType) -> SigHash { pub fn encode_signing_data_to<Write: io::Write>(
&mut self,
mut writer: Write,
input_index: usize,
script_code: &Script,
value: u64,
sighash_type: SigHashType,
) {
let zero_hash = sha256d::Hash::default(); let zero_hash = sha256d::Hash::default();
let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag(); let (sighash, anyone_can_pay) = sighash_type.split_anyonecanpay_flag();
let mut enc = SigHash::engine(); self.tx.version.consensus_encode(&mut writer).unwrap();
self.tx.version.consensus_encode(&mut enc).unwrap();
if !anyone_can_pay { if !anyone_can_pay {
self.hash_prevouts().consensus_encode(&mut enc).unwrap(); self.hash_prevouts().consensus_encode(&mut writer).unwrap();
} else { } 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 { 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 { } else {
zero_hash.consensus_encode(&mut enc).unwrap(); zero_hash.consensus_encode(&mut writer).unwrap();
} }
{ {
@ -196,25 +202,32 @@ impl<R: Deref<Target=Transaction>> SigHashCache<R> {
txin txin
.previous_output .previous_output
.consensus_encode(&mut enc) .consensus_encode(&mut writer)
.unwrap(); .unwrap();
script_code.consensus_encode(&mut enc).unwrap(); script_code.consensus_encode(&mut writer).unwrap();
value.consensus_encode(&mut enc).unwrap(); value.consensus_encode(&mut writer).unwrap();
txin.sequence.consensus_encode(&mut enc).unwrap(); txin.sequence.consensus_encode(&mut writer).unwrap();
} }
if sighash != SigHashType::Single && sighash != SigHashType::None { 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() { } else if sighash == SigHashType::Single && input_index < self.tx.output.len() {
let mut single_enc = SigHash::engine(); let mut single_enc = SigHash::engine();
self.tx.output[input_index].consensus_encode(&mut single_enc).unwrap(); 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 { } else {
zero_hash.consensus_encode(&mut enc).unwrap(); zero_hash.consensus_encode(&mut writer).unwrap();
} }
self.tx.lock_time.consensus_encode(&mut enc).unwrap(); self.tx.lock_time.consensus_encode(&mut writer).unwrap();
sighash_type.as_u32().consensus_encode(&mut enc).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) SigHash::from_engine(enc)
} }
} }