Merge pull request #485 from ipaljak-tbtl/expose-tx-signature-data
Expose serialized data for transaction signatures
This commit is contained in:
commit
3618d7a41d
|
@ -302,30 +302,35 @@ 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
|
||||||
|
) -> Result<(), encode::Error> {
|
||||||
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])?;
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build tx to sign
|
// Build tx to sign
|
||||||
|
@ -368,11 +373,34 @@ impl Transaction {
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
// hash the result
|
// hash the result
|
||||||
let mut engine = SigHash::engine();
|
tx.consensus_encode(&mut writer)?;
|
||||||
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)?;
|
||||||
SigHash::from_engine(engine)
|
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<SigHash, encode::Error> {
|
||||||
|
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
|
/// Gets the "weight" of this transaction, as defined by BIP141. For transactions with an empty
|
||||||
|
@ -869,7 +897,7 @@ mod tests {
|
||||||
raw_expected.reverse();
|
raw_expected.reverse();
|
||||||
let expected_result = SigHash::from_slice(&raw_expected[..]).unwrap();
|
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);
|
assert_eq!(actual_result, expected_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,9 @@ use hashes::{Hash, sha256d};
|
||||||
use hash_types::SigHash;
|
use hash_types::SigHash;
|
||||||
use blockdata::script::Script;
|
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,
|
||||||
|
) -> Result<(), encode::Error> {
|
||||||
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)?;
|
||||||
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)?;
|
||||||
} else {
|
} else {
|
||||||
zero_hash.consensus_encode(&mut enc).unwrap();
|
zero_hash.consensus_encode(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)?;
|
||||||
} else {
|
} else {
|
||||||
zero_hash.consensus_encode(&mut enc).unwrap();
|
zero_hash.consensus_encode(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -196,26 +202,39 @@ impl<R: Deref<Target=Transaction>> SigHashCache<R> {
|
||||||
|
|
||||||
txin
|
txin
|
||||||
.previous_output
|
.previous_output
|
||||||
.consensus_encode(&mut enc)
|
.consensus_encode(&mut writer)?;
|
||||||
.unwrap();
|
script_code.consensus_encode(&mut writer)?;
|
||||||
script_code.consensus_encode(&mut enc).unwrap();
|
value.consensus_encode(&mut writer)?;
|
||||||
value.consensus_encode(&mut enc).unwrap();
|
txin.sequence.consensus_encode(&mut writer)?;
|
||||||
txin.sequence.consensus_encode(&mut enc).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)?;
|
||||||
} 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)?;
|
||||||
SigHash::from_engine(single_enc).consensus_encode(&mut enc).unwrap();
|
SigHash::from_engine(single_enc).consensus_encode(&mut writer)?;
|
||||||
} else {
|
} else {
|
||||||
zero_hash.consensus_encode(&mut enc).unwrap();
|
zero_hash.consensus_encode(&mut writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tx.lock_time.consensus_encode(&mut enc).unwrap();
|
self.tx.lock_time.consensus_encode(&mut writer)?;
|
||||||
sighash_type.as_u32().consensus_encode(&mut enc).unwrap();
|
sighash_type.as_u32().consensus_encode(&mut writer)?;
|
||||||
SigHash::from_engine(enc)
|
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<SigHash, encode::Error> {
|
||||||
|
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 expected_result = SigHash::from_slice(&raw_expected[..]).unwrap();
|
||||||
let mut cache = SigHashCache::new(&tx);
|
let mut cache = SigHashCache::new(&tx);
|
||||||
let sighash_type = SigHashType::from_u32(hash_type);
|
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);
|
assert_eq!(actual_result, expected_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue