Merge pull request #4 from paritytech/nv

Verification tweaks
This commit is contained in:
Svyatoslav Nikolsky 2018-11-26 10:14:37 +03:00 committed by GitHub
commit accb95b401
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 89 additions and 156 deletions

View File

@ -129,8 +129,8 @@ pub fn serialize_join_split(stream: &mut Stream, join_split: &Option<JoinSplit>)
};
stream.append(&d.ciphertexts);
}
stream.append(&join_split.pubkey)
.append(&join_split.sig);
stream.append(&join_split.pubkey);
stream.append(&join_split.sig);
}
}
}

View File

@ -1,7 +1,7 @@
name: pbtc
version: "0.1.0"
author: Parity Technologies <info@parity.io>
about: Parity Bitcoin client
about: Parity zcash client
args:
- testnet:
long: testnet

View File

@ -6,7 +6,7 @@ use crypto::{sha1, sha256, dhash160, dhash256, ripemd160};
use sign::Sighash;
use script::MAX_SCRIPT_ELEMENT_SIZE;
use {
script, Builder, Script, Num, VerificationFlags, Opcode, Error, SignatureChecker, Stack
script, Script, Num, VerificationFlags, Opcode, Error, SignatureChecker, Stack
};
/// Helper function.
@ -319,7 +319,6 @@ pub fn eval_script(
let mut pc = 0;
let mut op_count = 0;
let mut begincode = 0;
let mut exec_stack = Vec::<bool>::new();
let mut altstack = Stack::<Bytes>::new();
@ -891,20 +890,14 @@ pub fn eval_script(
let v = dhash256(&stack.pop()?);
stack.push(v.to_vec().into());
},
Opcode::OP_CODESEPARATOR => {
begincode = pc;
},
Opcode::OP_CHECKSIG | Opcode::OP_CHECKSIGVERIFY => {
let pubkey = stack.pop()?;
let signature = stack.pop()?;
let mut subscript = script.subscript(begincode);
let signature_script = Builder::default().push_data(&*signature).into_script();
subscript = subscript.find_and_delete(&*signature_script);
check_signature_encoding(&signature, flags)?;
check_pubkey_encoding(&pubkey, flags)?;
let success = check_signature(checker, signature.into(), pubkey.into(), &subscript);
let success = check_signature(checker, signature.into(), pubkey.into(), &script);
match opcode {
Opcode::OP_CHECKSIG => {
if success {
@ -936,12 +929,6 @@ pub fn eval_script(
let sigs_count: usize = sigs_count.into();
let sigs = (0..sigs_count).into_iter().map(|_| stack.pop()).collect::<Result<Vec<_>, _>>()?;
let mut subscript = script.subscript(begincode);
for signature in &sigs {
let signature_script = Builder::default().push_data(&*signature).into_script();
subscript = subscript.find_and_delete(&*signature_script);
}
let mut success = true;
let mut k = 0;
let mut s = 0;
@ -953,7 +940,7 @@ pub fn eval_script(
check_signature_encoding(&sig, flags)?;
check_pubkey_encoding(&key, flags)?;
let ok = check_signature(checker, sig.into(), key.into(), &subscript);
let ok = check_signature(checker, sig.into(), key.into(), &script);
if ok {
s += 1;
}
@ -2053,29 +2040,6 @@ mod tests {
assert_eq!(verify_script(&input, &output, &flags, &checker), Ok(()));
}
// https://blockchain.info/rawtx/eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb
#[test]
fn test_transaction_bip65() {
let tx: Transaction = "01000000024de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8000000006b48304502205b282fbc9b064f3bc823a23edcc0048cbb174754e7aa742e3c9f483ebe02911c022100e4b0b3a117d36cab5a67404dddbf43db7bea3c1530e0fe128ebc15621bd69a3b0121035aa98d5f77cd9a2d88710e6fc66212aff820026f0dad8f32d1f7ce87457dde50ffffffff4de8b0c4c2582db95fa6b3567a989b664484c7ad6672c85a3da413773e63fdb8010000006f004730440220276d6dad3defa37b5f81add3992d510d2f44a317fd85e04f93a1e2daea64660202200f862a0da684249322ceb8ed842fb8c859c0cb94c81e1c5308b4868157a428ee01ab51210232abdc893e7f0631364d7fd01cb33d24da45329a00357b3a7886211ab414d55a51aeffffffff02e0fd1c00000000001976a914380cb3c594de4e7e9b8e18db182987bebb5a4f7088acc0c62d000000000017142a9bc5447d664c1d0141392a842d23dba45c4f13b17500000000".into();
let signer: TransactionInputSigner = tx.into();
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(()));
let flags = VerificationFlags::default()
.verify_p2sh(true)
.verify_locktime(true);
assert_eq!(verify_script(&input, &output, &flags, &checker), Err(Error::NumberOverflow));
}
// https://blockchain.info/rawtx/54fabd73f1d20c980a0686bf0035078e07f69c58437e4d586fb29aa0bee9814f
#[test]
fn test_arithmetic_correct_arguments_order() {
@ -2127,24 +2091,6 @@ mod tests {
basic_test(&script, result, vec![vec![1].into()].into());
}
// https://webbtc.com/tx/5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f
#[test]
fn test_transaction_find_and_delete() {
let tx: Transaction = "0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000".into();
let signer: TransactionInputSigner = tx.into();
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(()));
}
#[test]
fn op_cat_disabled_by_default() {
@ -2795,7 +2741,7 @@ mod tests {
// https://github.com/bitcoincashorg/bitcoincash.org/blob/0c6f91b0b713aae3bc6c9834b46e80e247ff5fab/spec/op_checkdatasig.md
let kp = KeyPair::from_private(Private { network: Network::Mainnet, secret: 1.into(), compressed: false, }).unwrap();
let pubkey = kp.public().clone();
let message = vec![42u8; 32];
let correct_signature = kp.private().sign(&Message::from(sha256(&message))).unwrap();
@ -2877,7 +2823,7 @@ mod tests {
// https://github.com/bitcoincashorg/bitcoincash.org/blob/0c6f91b0b713aae3bc6c9834b46e80e247ff5fab/spec/op_checkdatasig.md
let kp = KeyPair::from_private(Private { network: Network::Mainnet, secret: 1.into(), compressed: false, }).unwrap();
let pubkey = kp.public().clone();
let message = vec![42u8; 32];
let correct_signature = kp.private().sign(&Message::from(sha256(&message))).unwrap();

View File

@ -194,7 +194,7 @@ pub enum Opcode {
OP_SHA256 = 0xa8,
OP_HASH160 = 0xa9,
OP_HASH256 = 0xaa,
OP_CODESEPARATOR = 0xab,
//OP_CODESEPARATOR = 0xab,
OP_CHECKSIG = 0xac,
OP_CHECKSIGVERIFY = 0xad,
OP_CHECKMULTISIG = 0xae,
@ -415,7 +415,6 @@ impl Opcode {
0xa8 => Some(OP_SHA256),
0xa9 => Some(OP_HASH160),
0xaa => Some(OP_HASH256),
0xab => Some(OP_CODESEPARATOR),
0xac => Some(OP_CHECKSIG),
0xad => Some(OP_CHECKSIGVERIFY),
0xae => Some(OP_CHECKMULTISIG),
@ -680,7 +679,6 @@ mod tests {
assert_eq!(Opcode::OP_SHA256, Opcode::from_u8(Opcode::OP_SHA256 as u8).unwrap());
assert_eq!(Opcode::OP_HASH160, Opcode::from_u8(Opcode::OP_HASH160 as u8).unwrap());
assert_eq!(Opcode::OP_HASH256, Opcode::from_u8(Opcode::OP_HASH256 as u8).unwrap());
assert_eq!(Opcode::OP_CODESEPARATOR, Opcode::from_u8(Opcode::OP_CODESEPARATOR as u8).unwrap());
assert_eq!(Opcode::OP_CHECKSIG, Opcode::from_u8(Opcode::OP_CHECKSIG as u8).unwrap());
assert_eq!(Opcode::OP_CHECKSIGVERIFY, Opcode::from_u8(Opcode::OP_CHECKSIGVERIFY as u8).unwrap());
assert_eq!(Opcode::OP_CHECKMULTISIG, Opcode::from_u8(Opcode::OP_CHECKMULTISIG as u8).unwrap());
@ -697,7 +695,7 @@ mod tests {
assert_eq!(Opcode::OP_NOP8, Opcode::from_u8(Opcode::OP_NOP8 as u8).unwrap());
assert_eq!(Opcode::OP_NOP9, Opcode::from_u8(Opcode::OP_NOP9 as u8).unwrap());
assert_eq!(Opcode::OP_NOP10, Opcode::from_u8(Opcode::OP_NOP10 as u8).unwrap());
// BCH crypto
assert_eq!(Opcode::OP_CHECKDATASIG, Opcode::from_u8(Opcode::OP_CHECKDATASIG as u8).unwrap());
assert_eq!(Opcode::OP_CHECKDATASIGVERIFY, Opcode::from_u8(Opcode::OP_CHECKDATASIGVERIFY as u8).unwrap());

View File

@ -192,29 +192,6 @@ impl Script {
self.data[from..].to_vec().into()
}
pub fn find_and_delete(&self, data: &[u8]) -> Script {
let mut result = Vec::new();
let mut current = 0;
let len = data.len();
let end = self.data.len();
if len > end || len == 0 {
return self.data.to_vec().into()
}
while current < end - len {
if &self.data[current..current + len] != data {
result.push(self.data[current]);
current += 1;
} else {
current += len;
}
}
result.extend_from_slice(&self.data[current..]);
result.into()
}
pub fn get_opcode(&self, position: usize) -> Result<Opcode, Error> {
Opcode::from_u8(self.data[position]).ok_or(Error::BadOpcode)
}
@ -267,30 +244,6 @@ impl Script {
}
}
/// Returns Script without OP_CODESEPARATOR opcodes
pub fn without_separators(&self) -> Script {
let mut pc = 0;
let mut result = Vec::new();
while pc < self.len() {
match self.get_instruction(pc) {
Ok(instruction) => {
if instruction.opcode != Opcode::OP_CODESEPARATOR {
result.extend_from_slice(&self[pc..pc + instruction.step]);
}
pc += instruction.step;
},
_ => {
result.push(self[pc]);
pc += 1;
}
}
}
result.into()
}
/// Returns true if script contains only push opcodes
pub fn is_push_only(&self) -> bool {
let mut pc = 0;
@ -456,7 +409,9 @@ impl<'a> Iterator for Instructions<'a> {
let instruction = match self.script.get_instruction(self.position) {
Ok(x) => x,
Err(e) => return Some(Err(e)),
Err(e) => {
return Some(Err(e))
}
};
self.position += instruction.step;
@ -576,13 +531,6 @@ OP_ADD
assert_eq!(script.to_string(), s.to_string());
}
#[test]
fn test_script_without_op_codeseparator() {
let script: Script = "ab00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into();
let scr_goal: Script = "00270025512102e485fdaa062387c0bbb5ab711a093b6635299ec155b7b852fce6b992d5adbfec51ae".into();
assert_eq!(script.without_separators(), scr_goal);
}
#[test]
fn test_script_is_multisig() {
let script: Script = "524104a882d414e478039cd5b52a92ffb13dd5e6bd4515497439dffd691a0f12af9575fa349b5694ed3155b136f09e63975a1700c9f4d4df849323dac06cf3bd6458cd41046ce31db9bdd543e72fe3039a1f1c047dab87037c36a669ff90e28da1848f640de68c2fe913d363a51154a0c62d7adea1b822d05035077418267b1a1379790187410411ffd36c70776538d079fbae117dc38effafb33304af83ce4894589747aee1ef992f63280567f52f5ba870678b4ab4ff6c8ea600bd217870a8b4f1f09f3a8e8353ae".into();
@ -638,12 +586,6 @@ OP_ADD
assert_eq!(script.sigops_count(false, false), 20001);
}
#[test]
fn test_script_empty_find_and_delete() {
let s: Script = vec![Opcode::OP_0 as u8].into();
let result = s.find_and_delete(&[]);
assert_eq!(s, result);
}
#[test]
fn test_extract_destinations_pub_key_compressed() {

View File

@ -5,7 +5,7 @@ use keys::KeyPair;
use crypto::dhash256;
use hash::H256;
use ser::Stream;
use chain::{Transaction, TransactionOutput, OutPoint, TransactionInput};
use chain::{Transaction, TransactionOutput, OutPoint, TransactionInput, JoinSplit};
use {Script, Builder};
#[derive(Debug, PartialEq, Clone, Copy)]
@ -104,6 +104,7 @@ pub struct TransactionInputSigner {
pub inputs: Vec<UnsignedTransactionInput>,
pub outputs: Vec<TransactionOutput>,
pub lock_time: u32,
pub join_split: Option<JoinSplit>
}
/// Used for resigning and loading test transactions
@ -114,6 +115,7 @@ impl From<Transaction> for TransactionInputSigner {
inputs: t.inputs.into_iter().map(Into::into).collect(),
outputs: t.outputs,
lock_time: t.lock_time,
join_split: t.join_split,
}
}
}
@ -133,8 +135,6 @@ impl TransactionInputSigner {
return 1u8.into();
}
let script_pubkey = script_pubkey.without_separators();
let inputs = if sighash.anyone_can_pay {
let input = &self.inputs[input_index];
vec![TransactionInput {
@ -179,7 +179,14 @@ impl TransactionInputSigner {
outputs: outputs,
version: self.version,
lock_time: self.lock_time,
..Default::default() // TODO
join_split: self.join_split.as_ref().map(|js| {
JoinSplit {
descriptions: js.descriptions.clone(),
pubkey: js.pubkey.clone(),
sig: [0u8; 64].as_ref().into(), // null signature for signing
}
}),
..Default::default()
};
let mut stream = Stream::default();
@ -204,7 +211,6 @@ impl TransactionInputSigner {
signature.push(sighash as u8);
let script_sig = Builder::default()
.push_data(&signature)
//.push_data(keypair.public())
.into_script();
let unsigned_input = &self.inputs[input_index];
@ -263,6 +269,7 @@ mod tests {
lock_time: 0,
inputs: vec![unsigned_input],
outputs: vec![output],
join_split: None,
};
let hash = input_signer.signature_hash(0, &previous_output, SighashBase::All.into());

View File

@ -63,9 +63,10 @@ impl BloomFilter {
}
/// Adds given data to current filter, so that new transactions can be accepted
pub fn update_bloom_filter(&mut self, message: types::FilterAdd) {
if let Some(ref mut bloom) = self.bloom {
bloom.lock().insert(&message.data);
pub fn update_bloom_filter(&self, message: types::FilterAdd) {
if let Some(ref bloom) = self.bloom {
let mut b = bloom.lock();
b.insert(&message.data);
}
}
@ -127,11 +128,16 @@ impl BloomFilter {
// check if match any arbitrary script data element in any scriptSig in tx
let script = Script::new(input.script_sig.clone());
for instruction in script.iter().filter_map(|i| i.ok()) {
if let Some(instruction_data) = instruction.data {
is_match = bloom.contains(&*instruction_data);
if is_match {
return true;
for instruction in script.iter() {
match instruction {
Err(_) => break,
Ok(instruction) => {
if let Some(instruction_data) = instruction.data {
is_match = bloom.contains(&*instruction_data);
if is_match {
return true;
}
}
}
}
}
@ -220,7 +226,7 @@ mod tests {
let tx1: IndexedTransaction = test_data::TransactionBuilder::with_output(10).into();
let tx2: IndexedTransaction = test_data::TransactionBuilder::with_output(20).into();
let mut filter = BloomFilter::with_filter_load(default_filterload());
let filter = BloomFilter::with_filter_load(default_filterload());
assert!(!filter.filter_transaction(&tx1));
assert!(!filter.filter_transaction(&tx2));
@ -239,7 +245,7 @@ mod tests {
let tx1_out_data: Bytes = "380cb3c594de4e7e9b8e18db182987bebb5a4f70".into();
let tx2 = IndexedTransaction::default();
let mut filter = BloomFilter::with_filter_load(default_filterload());
let filter = BloomFilter::with_filter_load(default_filterload());
assert!(!filter.filter_transaction(&tx1));
assert!(!filter.filter_transaction(&tx2));
@ -257,7 +263,7 @@ mod tests {
let tx1_previous_output: Bytes = serialize(&tx1.raw.inputs[0].previous_output);
let tx2 = IndexedTransaction::default();
let mut filter = BloomFilter::with_filter_load(default_filterload());
let filter = BloomFilter::with_filter_load(default_filterload());
assert!(!filter.filter_transaction(&tx1));
assert!(!filter.filter_transaction(&tx2));
@ -276,7 +282,7 @@ mod tests {
let tx1_input_data: Bytes = "304502205b282fbc9b064f3bc823a23edcc0048cbb174754e7aa742e3c9f483ebe02911c022100e4b0b3a117d36cab5a67404dddbf43db7bea3c1530e0fe128ebc15621bd69a3b01".into();
let tx2 = IndexedTransaction::default();
let mut filter = BloomFilter::with_filter_load(default_filterload());
let filter = BloomFilter::with_filter_load(default_filterload());
assert!(!filter.filter_transaction(&tx1));
assert!(!filter.filter_transaction(&tx2));

View File

@ -29,9 +29,9 @@ impl<'a> HeaderAcceptor<'a> {
}
pub fn check(&self) -> Result<(), Error> {
try!(self.version.check());
try!(self.work.check());
try!(self.median_timestamp.check());
self.version.check()?;
self.work.check()?;
self.median_timestamp.check()?;
Ok(())
}
}
@ -40,23 +40,21 @@ impl<'a> HeaderAcceptor<'a> {
/// https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki
pub struct HeaderVersion<'a> {
header: CanonHeader<'a>,
height: u32,
consensus_params: &'a ConsensusParams,
_height: u32,
_consensus_params: &'a ConsensusParams,
}
impl<'a> HeaderVersion<'a> {
fn new(header: CanonHeader<'a>, height: u32, consensus_params: &'a ConsensusParams) -> Self {
HeaderVersion {
header: header,
height: height,
consensus_params: consensus_params,
_height: height,
_consensus_params: consensus_params,
}
}
fn check(&self) -> Result<(), Error> {
if (self.header.raw.version < 2 && self.height >= self.consensus_params.bip34_height) ||
(self.header.raw.version < 3 && self.height >= self.consensus_params.bip66_height) ||
(self.header.raw.version < 4 && self.height >= self.consensus_params.bip65_height) {
if self.header.raw.version < 4 {
Err(Error::OldVersionBlock)
} else {
Ok(())

View File

@ -367,8 +367,8 @@ impl<'a> TransactionEval<'a> {
.verify_sigpushonly(self.verify_sigpushonly)
.verify_cleanstack(self.verify_cleanstack);
try!(verify_script(&input, &output, &flags, &checker)
.map_err(|e| TransactionError::Signature(index, e)));
verify_script(&input, &output, &flags, &checker)
.map_err(|e| TransactionError::Signature(index, e))?;
}
Ok(())
@ -424,3 +424,39 @@ impl<'a> TransactionSize<'a> {
}
}
}
#[cfg(test)]
mod tests {
use chain::Transaction;
use script::{Script, VerificationFlags, TransactionSignatureChecker, TransactionInputSigner, verify_script};
#[test]
fn join_split() {
let input_hex = "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff05021d010101ffffffff021070d90000000000232102bdbbb2eb3066bb138d31349ce32b7f05871ac08cfb382023155249b213417d82ac045c36000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a8700000000";
let output_hex = "02000000010a141a3f21ed57fa8449ceac0b11909f1b5560f06b772753ca008d49675d45310000000048473044022041aaea8391c0182bf71bd974662e99534d99849b167062f7e8372c4f1a16c2d50220291b2ca6ae7616cd1f1bfddcda5ef2f53d78c2e153d3a8db571885f9adb5f05401ffffffff0000000000011070d900000000000000000000000000d7c612c817793191a1e68652121876d6b3bde40f4fa52bc314145ce6e5cdd2597ae7c48e86173b231e84fbdcb4d8f569f28f71ebf0f9b5867f9d4c12e031a2acc0108235936d2fa2d2c968654fbea2a89fde8522ec7c227d2ff3c10bff9c1197d8a290cca91f23792df8e56aed6c142eaa322e66360b5c49132b940689fb2bc5e77f7877bba6d2c4425d9861515cbe8a5c87dfd7cf159e9d4ac9ff63c096fbcd91d2a459877b1ed40748e2f020cdc678cf576a62c63138d820aba3df4074014bb1624b703774e138c706ba394698fd33c58424bb1a8d22be0d7bc8fe58d369e89836fe673c246d8d0cb1d7e1cc94acfa5b8d76010db8d53a36a3f0e33f0ccbc0f861b5e3d0a92e1c05c6bca775ba7389f6444f0e6cbd34141953220718594664022cbbb59465c880f50d42d0d49d6422197b5f823c2b3ffdb341869b98ed2eb2fd031b271702bda61ff885788363a7cf980a134c09a24c9911dc94cbe970bd613b700b0891fe8b8b05d9d2e7e51df9d6959bdf0a3f2310164afb197a229486a0e8e3808d76c75662b568839ebac7fbf740db9d576523282e6cdd1adf8b0f9c183ae95b0301fa1146d35af869cc47c51cfd827b7efceeca3c55884f54a68e38ee7682b5d102131b9b1198ed371e7e3da9f5a8b9ad394ab5a29f67a1d9b6ca1b8449862c69a5022e5d671e6989d33c182e0a6bbbe4a9da491dbd93ca3c01490c8f74a780479c7c031fb473670cacde779713dcd8cbdad802b8d418e007335919837becf46a3b1d0e02120af9d926bed2b28ed8a2b8307b3da2a171b3ee1bc1e6196773b570407df6b43b51b52c43f834ee0854577cd3a57f8fc23b02a3845cc1f0f42410f363d862e436bf06dbc5f94eddd3b83cdf47cf0acbd7750dff5cba86ea6f1f46a5013e0dc76715d7230e44a038a527cb9033f3eeaeac661264dc6a384788a7cd8aed59589bca6205fe1bd683fa392e7a3c6cc364bba36ad75ee9babf90f7b94071953df95effc0b1c3f542913ed1eb68e15534f9ceb7777c946edf55f129df128c3f767d8d60c4aa0c5e61d00f8e495e78334e2a9feddd9302e9880cb6174d201c89a1d6bc6e83a80cbf80ab3959dcc6cdd12e3d2f6f14d226e6948954f05544941d16ed1d498532722fa39bb985c3224915dd42d70be61217fdcb4aa023251af38b5576ff9eb865a471f2cb2dbc674e401d18014e6119464768778ddcd00907f20279bdecda3880fbbb4d00bb6c5aa3e06113a2f12fcc298f34ccb6bc2c2887b0b064f3bc2e2b507d31e022e65800dd7d30f25266914646bfc07c1eafbbf1e1163c439774b47e8e844799bc8fd06db050f97f5c74ca833e81bcdcf9d864be5746f965ef41838a3535666df867ef79e07068dc7ef809fb0e08e1629bab3215fe36d0f0e0f8c6bb319f93a0f408ff4abbd88c21afaec2e7720674eaceb27efb9144f619bad6f033cbefcebfbe66cabe8286f2ff97b91f4aeef5cbd99a9b862cb904dc085d96238caaad259280ff35caa211e00324f51ff03b6a1cd159cd501faef780ef7f25a98cdcd05ef67596d58d4aea1f9f3e95aae44fd4d4ea679c5e393d4670fb35bf12d036ea731bdfad297303239251a91f9a900e06987eb8e9f5bb1fb847f5ae47e6724ddeb5a3ac01b706a02e494c5547ce338302b4906cf2c91d59a87324322763a12e13a512ace3afb897510ad9ec95aa14ca568a9962da64e5bc7fd15b3e103ab461ee7db3fc9da0a523fc403c11254cd567ca48c8dac5e5b54953e5c754e31def90fff6c56d589a5c4b9a710ccb43cd24988b2fb9336b5508aa553cfdbd1f32dfb4ff16eae066b5fb244bc9058a91898c4ae893eaf0006dae1185c7f553e6e09d12a0a2a9c181c5e4d87c8895b74b0e23a8dc87faf5d6acd5e98cb1df5585f026ae94b77db0e95c5fe22692bd2e70e8e87d07d92b98cdfcc5367e52014163a6e4511d482816259215ee7df246e493523ee51617c318e1a9825f82e73e640fbc2d25c12ce5a07875d489db6a111afdc87061047077030d32de45cd4e575c02a60c4048560bd02cf9203426f589f429b413390ace832b3ddd3dd371750d94f9c34f60a0f1b621b445525d2190a185feaab9e56a079c46236161559713d585a07e94f2316a92fffa7838f1aea39d7846638d16f9b4d1a7dc053e0ddc6620f30e3e798eba900fd25c10c5d6672c9ed7d4d2fa80c0f0137ff24933c37fcd91b19bc7cdd828f7f3f1df0e45cafca795d847e83bca8baa321006581b024306e24c4c2294c0f41b932c1e9f7602f377e8484c7eeb184fab1f747b1dff5b6e2e89f1e5c4232b5a0a41ed6a3775f8942217078b7e035747891cabd2099bfcbf6a8d4680f51265d9e7d05794514f02470e0eb003ad1222cd4fe8bcd077310c5aff274b19608c31f77453d01c9aa9c21a8d9b71de44386aee2145648f7ead471cabed297b8610bba370baa42603f21f5f4640e5bc1a0402d40394e176a0db8cedb33a9d84c48b58d3851617046511946a3700aabe8f69cdb0469ee67776480be090cad2c7adc0bf59551ef6f1ac3119e5c29ab3b82dd945dab00dc4a91d3826c4e488047a4f3ab2d57c0abe1ee7aba304784e7ad211c32c4058fca7b1db2e282132e5ccafe79fc51ab37334f03715f4ad8735b6e03f01";
// deserialize && check tx
// this is tx # 31455d67498d00ca5327776bf060551b9f90110bacce4984fa57ed213f1a140a
let coinbase_tx: Transaction = input_hex.into();
// this is tx # ec31a1b3e18533702c74a67d91c49d622717bd53d6192c5cb23b9bdf080416a5
let spending_tx: Transaction = output_hex.into();
let output_script: Script = coinbase_tx.outputs()[0].script_pubkey.clone().into();
let input_script: Script = spending_tx.inputs()[0].script_sig.clone().into();
let signer: TransactionInputSigner = spending_tx.into();
let checker = TransactionSignatureChecker {
signer: signer,
input_index: 0,
input_amount: 0,
};
let flags = VerificationFlags::default()
.verify_p2sh(true);
assert_eq!(verify_script(&input_script, &output_script, &flags, &checker), Ok(()));
}
}