uahf: forkid replay protection
This commit is contained in:
parent
d6b9445344
commit
5326a90243
|
@ -65,6 +65,9 @@ pub struct VerificationFlags {
|
|||
|
||||
/// Making v1-v16 witness program non-standard
|
||||
pub verify_discourage_upgradable_witness_program: bool,
|
||||
|
||||
/// Only verify transactions with SIGHASH_FORKID signatures.
|
||||
pub enforce_sighash_fork_id: bool,
|
||||
}
|
||||
|
||||
impl VerificationFlags {
|
||||
|
@ -87,5 +90,10 @@ impl VerificationFlags {
|
|||
self.verify_dersig = value;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn enforce_sighash_fork_id(mut self, value: bool) -> Self {
|
||||
self.enforce_sighash_fork_id = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,8 @@ pub fn verify_script(
|
|||
script_sig: &Script,
|
||||
script_pubkey: &Script,
|
||||
flags: &VerificationFlags,
|
||||
checker: &SignatureChecker
|
||||
checker: &SignatureChecker,
|
||||
version: SignatureVersion,
|
||||
) -> Result<(), Error> {
|
||||
if flags.verify_sigpushonly && !script_sig.is_push_only() {
|
||||
return Err(Error::SignaturePushOnly);
|
||||
|
@ -239,13 +240,13 @@ pub fn verify_script(
|
|||
let mut stack = Stack::new();
|
||||
let mut stack_copy = Stack::new();
|
||||
|
||||
try!(eval_script(&mut stack, script_sig, flags, checker, SignatureVersion::Base));
|
||||
try!(eval_script(&mut stack, script_sig, flags, checker, version));
|
||||
|
||||
if flags.verify_p2sh {
|
||||
stack_copy = stack.clone();
|
||||
}
|
||||
|
||||
let res = try!(eval_script(&mut stack, script_pubkey, flags, checker, SignatureVersion::Base));
|
||||
let res = try!(eval_script(&mut stack, script_pubkey, flags, checker, version));
|
||||
if !res {
|
||||
return Err(Error::EvalFalse);
|
||||
}
|
||||
|
@ -265,7 +266,7 @@ pub fn verify_script(
|
|||
|
||||
let pubkey2: Script = try!(stack.pop()).into();
|
||||
|
||||
let res = try!(eval_script(&mut stack, &pubkey2, flags, checker, SignatureVersion::Base));
|
||||
let res = try!(eval_script(&mut stack, &pubkey2, flags, checker, version));
|
||||
if !res {
|
||||
return Err(Error::EvalFalse);
|
||||
}
|
||||
|
@ -1842,12 +1843,13 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "47304402202cb265bf10707bf49346c3515dd3d16fc454618c58ec0a0ff448a676c54ff71302206c6624d762a1fcef4618284ead8f08678ac05b13c84235f1654e6ad168233e8201410414e301b2328f17442c0b8310d787bf3d8a404cfbd0704f135b6ad4b2d3ee751310f981926e53a6e8c39bd7d3fefd576c543cce493cbac06388f2651d1aacbfcd".into();
|
||||
let output: Script = "76a914df3bd30160e6c6145baaf2c88a8844c13a00d1d588ac".into();
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
|
||||
// https://blockchain.info/rawtx/02b082113e35d5386285094c2829e7e2963fa0b5369fb7f4b79c4c90877dcd3d
|
||||
|
@ -1858,12 +1860,13 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "00483045022100deeb1f13b5927b5e32d877f3c42a4b028e2e0ce5010fdb4e7f7b5e2921c1dcd2022068631cb285e8c1be9f061d2968a18c3163b780656f30a049effee640e80d9bff01483045022100ee80e164622c64507d243bd949217d666d8b16486e153ac6a1f8e04c351b71a502203691bef46236ca2b4f5e60a82a853a33d6712d6a1e7bf9a65e575aeb7328db8c014cc9524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353ae".into();
|
||||
let output: Script = "a9141a8b0026343166625c7475f01e48b5ede8c0252e87".into();
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
|
||||
// https://blockchain.info/en/tx/12b5633bad1f9c167d523ad1aa1947b2732a865bf5414eab2f9e5ae5d5c191ba?show_adv=true
|
||||
|
@ -1874,12 +1877,13 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "483045022052ffc1929a2d8bd365c6a2a4e3421711b4b1e1b8781698ca9075807b4227abcb0221009984107ddb9e3813782b095d0d84361ed4c76e5edaf6561d252ae162c2341cfb01".into();
|
||||
let output: Script = "410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac".into();
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
|
||||
// https://blockchain.info/rawtx/fb0a1d8d34fa5537e461ac384bac761125e1bfa7fec286fa72511240fa66864d
|
||||
|
@ -1890,12 +1894,13 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "4b3048022200002b83d59c1d23c08efd82ee0662fec23309c3adbcbd1f0b8695378db4b14e736602220000334a96676e58b1bb01784cb7c556dd8ce1c220171904da22e18fe1e7d1510db5014104d0fe07ff74c9ef5b00fed1104fad43ecf72dbab9e60733e4f56eacf24b20cf3b8cd945bcabcc73ba0158bf9ce769d43e94bd58c5c7e331a188922b3fe9ca1f5a".into();
|
||||
let output: Script = "76a9147a2a3b481ca80c4ba7939c54d9278e50189d94f988ac".into();
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
|
||||
// https://blockchain.info/rawtx/eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
|
||||
|
@ -1906,18 +1911,19 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 1,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "004730440220276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea64660202200f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee01ab51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51ae".into();
|
||||
let output: Script = "142a9bc5447d664c1d0141392a842d23dba45c4f13b175".into();
|
||||
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true)
|
||||
.verify_locktime(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Err(Error::NumberOverflow));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Err(Error::NumberOverflow));
|
||||
}
|
||||
|
||||
// https://blockchain.info/rawtx/54fabd73f1d20c980a0686bf0035078e07f69c58437e4d586fb29aa0bee9814f
|
||||
|
@ -1928,11 +1934,12 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "483045022100d92e4b61452d91a473a43cde4b469a472467c0ba0cbd5ebba0834e4f4762810402204802b76b7783db57ac1f61d2992799810e173e91055938750815b6d8a675902e014f".into();
|
||||
let output: Script = "76009f69905160a56b210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71ad6c".into();
|
||||
let flags = VerificationFlags::default();
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1978,13 +1985,14 @@ mod tests {
|
|||
let checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 1,
|
||||
input_amount: 0,
|
||||
};
|
||||
let input: Script = "00483045022015BD0139BCCCF990A6AF6EC5C1C52ED8222E03A0D51C334DF139968525D2FCD20221009F9EFE325476EB64C3958E4713E9EEFE49BF1D820ED58D2112721B134E2A1A53034930460221008431BDFA72BC67F9D41FE72E94C88FB8F359FFA30B33C72C121C5A877D922E1002210089EF5FC22DD8BFC6BF9FFDB01A9862D27687D424D1FEFBAB9E9C7176844A187A014C9052483045022015BD0139BCCCF990A6AF6EC5C1C52ED8222E03A0D51C334DF139968525D2FCD20221009F9EFE325476EB64C3958E4713E9EEFE49BF1D820ED58D2112721B134E2A1A5303210378D430274F8C5EC1321338151E9F27F4C676A008BDF8638D07C0B6BE9AB35C71210378D430274F8C5EC1321338151E9F27F4C676A008BDF8638D07C0B6BE9AB35C7153AE".into();
|
||||
let output: Script = "A914D8DACDADB7462AE15CD906F1878706D0DA8660E687".into();
|
||||
|
||||
let flags = VerificationFlags::default()
|
||||
.verify_p2sh(true);
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
|
||||
assert_eq!(verify_script(&input, &output, &flags, &checker, SignatureVersion::Base), Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ pub use self::interpreter::{eval_script, verify_script};
|
|||
pub use self::opcode::Opcode;
|
||||
pub use self::num::Num;
|
||||
pub use self::script::{Script, ScriptType, ScriptAddress};
|
||||
pub use self::sign::{TransactionInputSigner, UnsignedTransactionInput};
|
||||
pub use self::sign::{TransactionInputSigner, UnsignedTransactionInput, SignatureVersion};
|
||||
pub use self::stack::Stack;
|
||||
pub use self::verify::{SignatureChecker, NoopSignatureChecker, TransactionSignatureChecker};
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
//! Transaction signer
|
||||
|
||||
use bytes::Bytes;
|
||||
use keys::KeyPair;
|
||||
use crypto::dhash256;
|
||||
use hash::H256;
|
||||
use ser::Stream;
|
||||
use chain::{Transaction, TransactionOutput, OutPoint, TransactionInput};
|
||||
use {Script, Builder};
|
||||
use Script;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum SignatureVersion {
|
||||
Base,
|
||||
WitnessV0,
|
||||
ForkId,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
|
@ -34,67 +34,72 @@ impl From<SighashBase> for u32 {
|
|||
pub struct Sighash {
|
||||
pub base: SighashBase,
|
||||
pub anyone_can_pay: bool,
|
||||
pub fork_id: bool,
|
||||
}
|
||||
|
||||
impl From<Sighash> for u32 {
|
||||
fn from(s: Sighash) -> Self {
|
||||
let base = s.base as u32;
|
||||
if s.anyone_can_pay {
|
||||
let base = if s.anyone_can_pay {
|
||||
base | 0x80
|
||||
} else {
|
||||
base
|
||||
};
|
||||
|
||||
if s.fork_id {
|
||||
base | 0x40
|
||||
} else {
|
||||
base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Sighash {
|
||||
fn from(u: u32) -> Self {
|
||||
// use 0x9f istead of 0x1f to catch 0x80
|
||||
match u & 0x9f {
|
||||
2 => Sighash::new(SighashBase::None, false),
|
||||
3 => Sighash::new(SighashBase::Single, false),
|
||||
0x81 => Sighash::new(SighashBase::All, true),
|
||||
0x82 => Sighash::new(SighashBase::None, true),
|
||||
0x83 => Sighash::new(SighashBase::Single, true),
|
||||
x if x & 0x80 == 0x80 => Sighash::new(SighashBase::All, true),
|
||||
// 0 is handled like all...
|
||||
1 | _ => Sighash::new(SighashBase::All, false),
|
||||
}
|
||||
Sighash::new(match u & 0x1f {
|
||||
2 => SighashBase::None,
|
||||
3 => SighashBase::Single,
|
||||
1 | _ => SighashBase::All,
|
||||
},
|
||||
u & 0x80 == 0x80,
|
||||
u & 0x40 == 0x40)
|
||||
}
|
||||
}
|
||||
|
||||
impl Sighash {
|
||||
pub fn new(base: SighashBase, anyone_can_pay: bool) -> Self {
|
||||
pub fn new(base: SighashBase, anyone_can_pay: bool, fork_id: bool) -> Self {
|
||||
Sighash {
|
||||
base: base,
|
||||
anyone_can_pay: anyone_can_pay,
|
||||
fork_id: fork_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_defined(u: u32) -> bool {
|
||||
// use 0x9f istead of 0x1f to catch 0x80
|
||||
match u & 0x9f {
|
||||
// use 0xdf istead of 0x1f to catch 0x40 | 0x80
|
||||
match u & 0xdf {
|
||||
1 | 2 | 3 |
|
||||
0x81 | 0x82 | 0x83 => true,
|
||||
0x81 | 0x82 | 0x83 |
|
||||
0x41 | 0x42 | 0x43 |
|
||||
0xc1 | 0xc2 | 0xc3 => true,
|
||||
x if x & 0x80 == 0x80 => true,
|
||||
x if x & 0x40 == 0x40 => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_u32(u: u32) -> Option<Self> {
|
||||
// use 0x9f istead of 0x1f to catch 0x80
|
||||
let (base, anyone_can_pay) = match u & 0x9f {
|
||||
1 => (SighashBase::All, false),
|
||||
2 => (SighashBase::None, false),
|
||||
3 => (SighashBase::Single, false),
|
||||
0x81 => (SighashBase::All, true),
|
||||
0x82 => (SighashBase::None, true),
|
||||
0x83 => (SighashBase::Single, true),
|
||||
x if x & 0x80 == 0x80 => (SighashBase::All, true),
|
||||
let anyone_can_pay = (u & 0x80) == 0x80;
|
||||
let fork_id = (u & 0x40) == 0x40;
|
||||
let base = match u & 0x1f {
|
||||
2 => SighashBase::None,
|
||||
3 => SighashBase::Single,
|
||||
1 => SighashBase::All,
|
||||
_ if anyone_can_pay || fork_id => SighashBase::All,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Sighash::new(base, anyone_can_pay))
|
||||
Some(Sighash::new(base, anyone_can_pay, fork_id))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -133,7 +138,7 @@ impl From<Transaction> for TransactionInputSigner {
|
|||
}
|
||||
|
||||
impl TransactionInputSigner {
|
||||
pub fn signature_hash(&self, input_index: usize, script_pubkey: &Script, sighashtype: u32) -> H256 {
|
||||
pub fn signature_hash(&self, input_index: usize, input_amount: u64, script_pubkey: &Script, sigversion: SignatureVersion, sighashtype: u32) -> H256 {
|
||||
let sighash = Sighash::from(sighashtype);
|
||||
if input_index >= self.inputs.len() {
|
||||
return 1u8.into();
|
||||
|
@ -143,6 +148,14 @@ impl TransactionInputSigner {
|
|||
return 1u8.into();
|
||||
}
|
||||
|
||||
match sighash.fork_id {
|
||||
true if sigversion == SignatureVersion::ForkId => self.signature_hash_fork_id(input_index, input_amount, script_pubkey, sighashtype, sighash),
|
||||
false if sigversion == SignatureVersion::Base => self.signature_hash_original(input_index, script_pubkey, sighashtype, sighash),
|
||||
_ => 1u8.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signature_hash_original(&self, input_index: usize, script_pubkey: &Script, sighashtype: u32, sighash: Sighash) -> H256 {
|
||||
let script_pubkey = script_pubkey.without_separators();
|
||||
|
||||
let inputs = if sighash.anyone_can_pay {
|
||||
|
@ -198,30 +211,58 @@ impl TransactionInputSigner {
|
|||
dhash256(&out)
|
||||
}
|
||||
|
||||
/// input_index - index of input to sign
|
||||
/// script_pubkey - script_pubkey of input's previous_output pubkey
|
||||
pub fn signed_input(
|
||||
&self,
|
||||
keypair: &KeyPair,
|
||||
input_index: usize,
|
||||
script_pubkey: &Script,
|
||||
sighash: u32,
|
||||
) -> TransactionInput {
|
||||
let hash = self.signature_hash(input_index, script_pubkey, sighash);
|
||||
fn signature_hash_fork_id(&self, input_index: usize, input_amount: u64, script_pubkey: &Script, sighashtype: u32, sighash: Sighash) -> H256 {
|
||||
let hash_prevouts = match sighash.anyone_can_pay {
|
||||
false => {
|
||||
let mut stream = Stream::default();
|
||||
for input in &self.inputs {
|
||||
stream.append(&input.previous_output);
|
||||
}
|
||||
dhash256(&stream.out())
|
||||
},
|
||||
true => 0u8.into(),
|
||||
};
|
||||
|
||||
let mut signature: Vec<u8> = keypair.private().sign(&hash).unwrap().into();
|
||||
signature.push(sighash as u8);
|
||||
let script_sig = Builder::default()
|
||||
.push_data(&signature)
|
||||
.push_data(keypair.public())
|
||||
.into_script();
|
||||
let hash_sequence = match sighash.base {
|
||||
SighashBase::All if !sighash.anyone_can_pay => {
|
||||
let mut stream = Stream::default();
|
||||
for input in &self.inputs {
|
||||
stream.append(&input.sequence);
|
||||
}
|
||||
dhash256(&stream.out())
|
||||
},
|
||||
_ => 0u8.into(),
|
||||
};
|
||||
|
||||
let unsigned_input = &self.inputs[input_index];
|
||||
TransactionInput {
|
||||
previous_output: unsigned_input.previous_output.clone(),
|
||||
sequence: unsigned_input.sequence,
|
||||
script_sig: script_sig.to_bytes(),
|
||||
}
|
||||
let hash_outputs = match sighash.base {
|
||||
SighashBase::All => {
|
||||
let mut stream = Stream::default();
|
||||
for output in &self.outputs {
|
||||
stream.append(output);
|
||||
}
|
||||
dhash256(&stream.out())
|
||||
},
|
||||
SighashBase::Single if input_index < self.outputs.len() => {
|
||||
let mut stream = Stream::default();
|
||||
stream.append(&self.outputs[input_index]);
|
||||
dhash256(&stream.out())
|
||||
},
|
||||
_ => 0u8.into(),
|
||||
};
|
||||
|
||||
let mut stream = Stream::default();
|
||||
stream.append(&self.version);
|
||||
stream.append(&hash_prevouts);
|
||||
stream.append(&hash_sequence);
|
||||
stream.append(&self.inputs[input_index].previous_output);
|
||||
stream.append_slice(&**script_pubkey);
|
||||
stream.append(&input_amount);
|
||||
stream.append(&self.inputs[input_index].sequence);
|
||||
stream.append(&hash_outputs);
|
||||
stream.append(&self.lock_time);
|
||||
stream.append(&sighashtype); // this also includes 24-bit fork id. which is 0 for BitcoinCash
|
||||
let out = stream.out();
|
||||
dhash256(&out)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,7 +273,7 @@ mod tests {
|
|||
use keys::{KeyPair, Private, Address};
|
||||
use chain::{OutPoint, TransactionOutput, Transaction};
|
||||
use script::Script;
|
||||
use super::{UnsignedTransactionInput, TransactionInputSigner, SighashBase};
|
||||
use super::{UnsignedTransactionInput, TransactionInputSigner, SighashBase, SignatureVersion};
|
||||
|
||||
// http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html
|
||||
// https://blockchain.info/rawtx/81b4c832d70cb56ff957589752eb4125a4cab78a25a8fc52d6a09e5bd4404d48
|
||||
|
@ -274,7 +315,7 @@ mod tests {
|
|||
outputs: vec![output],
|
||||
};
|
||||
|
||||
let hash = input_signer.signature_hash(0, &previous_output, SighashBase::All.into());
|
||||
let hash = input_signer.signature_hash(0, 0, &previous_output, SignatureVersion::Base, SighashBase::All.into());
|
||||
assert_eq!(hash, expected_signature_hash);
|
||||
}
|
||||
|
||||
|
@ -290,7 +331,7 @@ mod tests {
|
|||
let script: Script = script.into();
|
||||
let expected = H256::from_reversed_str(result);
|
||||
|
||||
let hash = signer.signature_hash(input_index, &script, hash_type as u32);
|
||||
let hash = signer.signature_hash_original(input_index, &script, hash_type as u32, (hash_type as u32).into());
|
||||
assert_eq!(expected, hash);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ impl SignatureChecker for NoopSignatureChecker {
|
|||
pub struct TransactionSignatureChecker {
|
||||
pub signer: TransactionInputSigner,
|
||||
pub input_index: usize,
|
||||
pub input_amount: u64,
|
||||
}
|
||||
|
||||
impl SignatureChecker for TransactionSignatureChecker {
|
||||
|
@ -50,9 +51,9 @@ impl SignatureChecker for TransactionSignatureChecker {
|
|||
public: &Public,
|
||||
script_code: &Script,
|
||||
sighashtype: u32,
|
||||
_version: SignatureVersion
|
||||
version: SignatureVersion
|
||||
) -> bool {
|
||||
let hash = self.signer.signature_hash(self.input_index, script_code, sighashtype);
|
||||
let hash = self.signer.signature_hash(self.input_index, self.input_amount, script_code, version, sighashtype);
|
||||
public.verify(&hash, signature).unwrap_or(false)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use primitives::hash::H256;
|
|||
use primitives::bytes::Bytes;
|
||||
use db::{TransactionMetaProvider, TransactionOutputProvider, BlockHeaderProvider};
|
||||
use network::{ConsensusParams, ConsensusFork};
|
||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner};
|
||||
use script::{Script, verify_script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, SignatureVersion};
|
||||
use duplex_store::DuplexTransactionOutputProvider;
|
||||
use deployments::Deployments;
|
||||
use script::Builder;
|
||||
|
@ -284,6 +284,7 @@ pub struct TransactionEval<'a> {
|
|||
verify_locktime: bool,
|
||||
verify_checksequence: bool,
|
||||
verify_dersig: bool,
|
||||
signature_version: SignatureVersion,
|
||||
}
|
||||
|
||||
impl<'a> TransactionEval<'a> {
|
||||
|
@ -299,6 +300,10 @@ impl<'a> TransactionEval<'a> {
|
|||
let verify_p2sh = time >= params.bip16_time;
|
||||
let verify_locktime = height >= params.bip65_height;
|
||||
let verify_dersig = height >= params.bip66_height;
|
||||
let signature_version = match params.fork {
|
||||
ConsensusFork::BitcoinCash(fork_height) if height >= fork_height => SignatureVersion::ForkId,
|
||||
_ => SignatureVersion::Base,
|
||||
};
|
||||
|
||||
let verify_checksequence = deployments.csv(height, headers, params);
|
||||
|
||||
|
@ -309,6 +314,7 @@ impl<'a> TransactionEval<'a> {
|
|||
verify_locktime: verify_locktime,
|
||||
verify_checksequence: verify_checksequence,
|
||||
verify_dersig: verify_dersig,
|
||||
signature_version: signature_version,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,6 +328,7 @@ impl<'a> TransactionEval<'a> {
|
|||
let mut checker = TransactionSignatureChecker {
|
||||
signer: signer,
|
||||
input_index: 0,
|
||||
input_amount: 0,
|
||||
};
|
||||
|
||||
for (index, input) in self.transaction.raw.inputs.iter().enumerate() {
|
||||
|
@ -329,6 +336,7 @@ impl<'a> TransactionEval<'a> {
|
|||
.ok_or_else(|| TransactionError::UnknownReference(input.previous_output.hash.clone()))?;
|
||||
|
||||
checker.input_index = index;
|
||||
checker.input_amount = output.value;
|
||||
|
||||
let input: Script = input.script_sig.clone().into();
|
||||
let output: Script = output.script_pubkey.into();
|
||||
|
@ -339,7 +347,8 @@ impl<'a> TransactionEval<'a> {
|
|||
.verify_checksequence(self.verify_checksequence)
|
||||
.verify_dersig(self.verify_dersig);
|
||||
|
||||
try!(verify_script(&input, &output, &flags, &checker).map_err(|_| TransactionError::Signature(index)));
|
||||
try!(verify_script(&input, &output, &flags, &checker, self.signature_version)
|
||||
.map_err(|_| TransactionError::Signature(index)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in New Issue