1use crate::crypto::OptimizedSha256;
10use crate::error::Result;
11use crate::types::*;
12use blvm_spec_lock::spec_locked;
13
14#[inline]
16fn write_varint_to_vec(vec: &mut Vec<u8>, value: u64) {
17 if value < 0xfd {
18 vec.push(value as u8);
19 } else if value <= 0xffff {
20 vec.push(0xfd);
21 vec.extend_from_slice(&(value as u16).to_le_bytes());
22 } else if value <= 0xffffffff {
23 vec.push(0xfe);
24 vec.extend_from_slice(&(value as u32).to_le_bytes());
25 } else {
26 vec.push(0xff);
27 vec.extend_from_slice(&value.to_le_bytes());
28 }
29}
30
31#[spec_locked("5.1.1", "SerializeScriptCode")]
36pub fn serialize_script_code_for_legacy_sighash(script: &[u8]) -> Vec<u8> {
37 let mut out = Vec::with_capacity(script.len());
38 append_stripped_codeseparators(&mut out, script);
39 out
40}
41
42#[spec_locked("11.1.9.1", "DeriveWitnessScriptCode")]
44pub fn derive_bip143_script_code_p2wpkh(pubkey_hash: &[u8; 20]) -> [u8; 25] {
45 use crate::opcodes::{OP_CHECKSIG, OP_DUP, OP_EQUALVERIFY, OP_HASH160, PUSH_20_BYTES};
46 let mut script_code = [0u8; 25];
47 script_code[0] = OP_DUP;
48 script_code[1] = OP_HASH160;
49 script_code[2] = PUSH_20_BYTES;
50 script_code[3..23].copy_from_slice(pubkey_hash);
51 script_code[23] = OP_EQUALVERIFY;
52 script_code[24] = OP_CHECKSIG;
53 script_code
54}
55
56#[spec_locked("11.1.9.1", "DeriveWitnessScriptCode")]
61pub fn derive_bip143_script_code(
62 witness_program: &[u8],
63 witness_script: Option<&[u8]>,
64) -> Option<Vec<u8>> {
65 use crate::opcodes::{OP_0, PUSH_20_BYTES, PUSH_32_BYTES};
66 if witness_program.len() == 22
67 && witness_program[0] == OP_0
68 && witness_program[1] == PUSH_20_BYTES
69 {
70 let mut hash = [0u8; 20];
71 hash.copy_from_slice(&witness_program[2..22]);
72 return Some(derive_bip143_script_code_p2wpkh(&hash).to_vec());
73 }
74 if witness_program.len() == 34
75 && witness_program[0] == OP_0
76 && witness_program[1] == PUSH_32_BYTES
77 {
78 return witness_script.map(|s| s.to_vec());
79 }
80 None
81}
82
83#[inline]
90fn write_script_code_for_sighash(preimage: &mut Vec<u8>, code: &[u8]) {
91 let n_codeseps = count_opcode_codeseparators(code);
92 write_varint_to_vec(preimage, (code.len() - n_codeseps) as u64);
93 if n_codeseps == 0 {
94 preimage.extend_from_slice(code);
95 } else {
96 append_stripped_codeseparators(preimage, code);
97 }
98}
99
100#[inline]
102fn count_opcode_codeseparators(script: &[u8]) -> usize {
103 let mut count = 0usize;
104 let mut i = 0usize;
105 while i < script.len() {
106 let op = script[i];
107 let advance = if (0x01..=0x4b).contains(&op) {
108 1 + op as usize
109 } else if op == 0x4c {
110 if i + 1 >= script.len() {
111 break;
112 }
113 2 + script[i + 1] as usize
114 } else if op == 0x4d {
115 if i + 2 >= script.len() {
116 break;
117 }
118 3 + u16::from_le_bytes([script[i + 1], script[i + 2]]) as usize
119 } else if op == 0x4e {
120 if i + 4 >= script.len() {
121 break;
122 }
123 5 + u32::from_le_bytes([script[i + 1], script[i + 2], script[i + 3], script[i + 4]])
124 as usize
125 } else {
126 if op == 0xab {
127 count += 1;
128 }
129 1
130 };
131 i += advance.min(script.len() - i);
132 }
133 count
134}
135
136fn append_stripped_codeseparators(out: &mut Vec<u8>, code: &[u8]) {
138 let mut i = 0usize;
139 while i < code.len() {
140 let op = code[i];
141 if (0x01..=0x4b).contains(&op) {
142 let end = (i + 1 + op as usize).min(code.len());
143 out.extend_from_slice(&code[i..end]);
144 i = end;
145 } else if op == 0x4c {
146 if i + 1 >= code.len() {
147 out.push(op);
148 break;
149 }
150 let len = code[i + 1] as usize;
151 let end = (i + 2 + len).min(code.len());
152 out.extend_from_slice(&code[i..end]);
153 i = end;
154 } else if op == 0x4d {
155 if i + 2 >= code.len() {
156 out.extend_from_slice(&code[i..]);
157 break;
158 }
159 let len = u16::from_le_bytes([code[i + 1], code[i + 2]]) as usize;
160 let end = (i + 3 + len).min(code.len());
161 out.extend_from_slice(&code[i..end]);
162 i = end;
163 } else if op == 0x4e {
164 if i + 4 >= code.len() {
165 out.extend_from_slice(&code[i..]);
166 break;
167 }
168 let len =
169 u32::from_le_bytes([code[i + 1], code[i + 2], code[i + 3], code[i + 4]]) as usize;
170 let end = (i + 5 + len).min(code.len());
171 out.extend_from_slice(&code[i..end]);
172 i = end;
173 } else if op == 0xab {
174 i += 1; } else {
176 out.push(op);
177 i += 1;
178 }
179 }
180}
181
182#[cfg(feature = "production")]
183use lru::LruCache;
184#[cfg(feature = "production")]
185use rustc_hash::{FxHashMap, FxHasher};
186#[cfg(feature = "production")]
187use std::cell::RefCell;
188#[cfg(feature = "production")]
189use std::hash::{Hash as StdHash, Hasher};
190
191#[cfg(feature = "production")]
194#[derive(Clone, Copy, PartialEq, Eq, Debug)]
195pub struct SighashCacheKey {
196 prevout: crate::types::OutPoint,
197 code_hash: u64,
198 sighash_byte: u8,
199}
200
201#[cfg(feature = "production")]
202impl std::hash::Hash for SighashCacheKey {
203 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
204 state.write_u64(self.code_hash);
205 }
206}
207
208#[cfg(feature = "production")]
209pub type SighashMidstateCache =
210 std::sync::Arc<std::sync::Mutex<FxHashMap<SighashCacheKey, [u8; 32]>>>;
211
212#[cfg(feature = "production")]
216thread_local! {
217 static SIGHASH_MIDSTATE_CACHE: RefCell<LruCache<SighashCacheKey, [u8; 32]>> = RefCell::new({
218 let cap = std::env::var("BLVM_SIGHASH_MIDSTATE_CACHE_SIZE")
219 .ok()
220 .and_then(|s| s.parse().ok())
221 .unwrap_or(262_144)
222 .clamp(65_536, 2_097_152);
223 LruCache::new(std::num::NonZeroUsize::new(cap).unwrap())
224 });
225}
226
227#[cfg(feature = "production")]
228fn insert_midstate_cache(
229 sighash_cache: Option<&SighashMidstateCache>,
230 prevout: crate::types::OutPoint,
231 code: &[u8],
232 sighash_byte: u8,
233 hash: [u8; 32],
234) {
235 let key_hash = sighash_cache_hash(&prevout, code, sighash_byte);
236 let key = SighashCacheKey {
237 prevout,
238 code_hash: key_hash,
239 sighash_byte,
240 };
241 if let Some(c) = sighash_cache {
242 let _ = c.lock().map(|mut g| g.insert(key, hash));
243 } else {
244 SIGHASH_MIDSTATE_CACHE.with(|cell| {
245 cell.borrow_mut().put(key, hash);
246 });
247 }
248}
249
250#[cfg(feature = "production")]
252#[inline]
253fn sighash_cache_hash(prevout: &crate::types::OutPoint, code: &[u8], sighash_byte: u8) -> u64 {
254 let mut hasher = FxHasher::default();
255 prevout.hash(&mut hasher);
256 code.hash(&mut hasher);
257 sighash_byte.hash(&mut hasher);
258 hasher.finish()
259}
260
261#[cfg(feature = "production")]
266thread_local! {
267 static SIGHASH_CACHE: RefCell<LruCache<[u8; 32], [u8; 32]>> = RefCell::new({
268 let cap = std::env::var("BLVM_SIGHASH_CACHE_SIZE")
269 .ok()
270 .and_then(|s| s.parse().ok())
271 .unwrap_or(262_144)
272 .clamp(65_536, 2_097_152);
273 LruCache::new(std::num::NonZeroUsize::new(cap).unwrap())
274 });
275}
276
277#[cfg(feature = "production")]
279thread_local! {
280 static SIGHASH_PREIMAGE_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(4096));
281}
282
283#[cfg(feature = "production")]
286thread_local! {
287 static BIP143_SERIALIZE_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(131_072)); }
289
290#[cfg(feature = "production")]
292thread_local! {
293 static BIP143_PREIMAGE_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(1024));
294}
295
296#[cfg(feature = "production")]
298thread_local! {
299 static BIP143_SINGLE_OUTPUT_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(256));
300}
301
302#[cfg(feature = "production")]
305thread_local! {
306 static LEGACY_BATCH_PREIMAGES: std::cell::RefCell<Vec<Vec<u8>>> =
307 const { std::cell::RefCell::new(Vec::new()) };
308}
309
310#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
319pub struct SighashType(pub u8);
320
321impl SighashType {
322 pub const ALL_LEGACY: Self = SighashType(0x00);
324 pub const ALL: Self = SighashType(0x01);
325 pub const NONE: Self = SighashType(0x02);
326 pub const SINGLE: Self = SighashType(0x03);
327 pub const ALL_ANYONECANPAY: Self = SighashType(0x81);
328 pub const NONE_ANYONECANPAY: Self = SighashType(0x82);
329 pub const SINGLE_ANYONECANPAY: Self = SighashType(0x83);
330
331 pub fn from_byte(byte: u8) -> Self {
334 SighashType(byte)
335 }
336
337 pub fn as_u32(&self) -> u32 {
339 self.0 as u32
340 }
341
342 pub fn base_type(&self) -> u8 {
344 self.0 & 0x1f
345 }
346
347 pub fn is_anyonecanpay(&self) -> bool {
349 self.0 & 0x80 != 0
350 }
351
352 pub fn is_all(&self) -> bool {
354 let base = self.base_type();
355 base != 0x02 && base != 0x03
356 }
357
358 pub fn is_none(&self) -> bool {
360 self.base_type() == 0x02
361 }
362
363 pub fn is_single(&self) -> bool {
365 self.base_type() == 0x03
366 }
367}
368
369#[cfg(feature = "production")]
371#[allow(dead_code)]
372#[inline]
373fn is_cacheable_sighash_pattern(
374 tx: &Transaction,
375 input_index: usize,
376 sighash_type: SighashType,
377) -> bool {
378 if sighash_type.is_anyonecanpay() {
379 return false;
380 }
381 let base = sighash_type.base_type();
383 if base == 0x01 || base == 0x00 {
384 let ni = tx.inputs.len();
385 let no = tx.outputs.len();
386 (ni == 1 && (1..=4).contains(&no))
387 || ((1..=4).contains(&ni) && no == 1)
388 || (ni == 2 && no == 2)
389 || (ni == 1 && no == 1)
390 } else if base == 0x02 {
391 !tx.inputs.is_empty() && tx.inputs.len() <= 4
393 } else if base == 0x03 {
394 input_index < tx.outputs.len() && tx.inputs.len() <= 4
396 } else {
397 false
398 }
399}
400
401#[cfg(feature = "production")]
406fn sighash_with_cache(preimage: &[u8]) -> Hash {
407 let hasher = OptimizedSha256::new();
408 let first_hash: [u8; 32] = hasher.hash(preimage);
409 SIGHASH_CACHE.with(|cell| {
410 let mut cache = cell.borrow_mut();
411 if let Some(cached) = cache.get(&first_hash) {
412 return *cached;
413 }
414 let second_hash = hasher.hash(&first_hash);
415 let mut result = [0u8; 32];
416 result.copy_from_slice(&second_hash);
417 cache.put(first_hash, result);
418 result
419 })
420}
421
422#[cfg(feature = "production")]
426#[spec_locked("5.1.1", "CalculateSighash")]
427#[inline]
428pub fn compute_legacy_sighash_nocache(
429 tx: &Transaction,
430 input_index: usize,
431 script_code: &[u8],
432 sighash_byte: u8,
433) -> [u8; 32] {
434 use sha2::{Digest, Sha256};
435
436 let sighash_u32 = sighash_byte as u32;
437 let base_type = sighash_u32 & 0x1f;
438 let anyone_can_pay = (sighash_u32 & 0x80) != 0;
439 let hash_none = base_type == 0x02;
440 let hash_single = base_type == 0x03;
441
442 if hash_single && input_index >= tx.outputs.len() {
443 let mut result = [0u8; 32];
444 result[0] = 1;
445 return result;
446 }
447
448 let mut h = Sha256::new();
449 h.update((tx.version as u32).to_le_bytes());
450
451 let n_inputs = if anyone_can_pay { 1 } else { tx.inputs.len() };
452 update_varint(&mut h, n_inputs as u64);
453
454 for i in 0..n_inputs {
455 let actual_i = if anyone_can_pay { input_index } else { i };
456 let input = &tx.inputs[actual_i];
457 h.update(input.prevout.hash);
458 h.update(input.prevout.index.to_le_bytes());
459
460 if actual_i == input_index {
461 update_varint(&mut h, script_code.len() as u64);
462 h.update(script_code);
463 } else {
464 h.update([0u8]);
465 }
466
467 if actual_i != input_index && (hash_single || hash_none) {
468 h.update(0u32.to_le_bytes());
469 } else {
470 h.update((input.sequence as u32).to_le_bytes());
471 }
472 }
473
474 let n_outputs = if hash_none {
475 0
476 } else if hash_single {
477 input_index + 1
478 } else {
479 tx.outputs.len()
480 };
481 update_varint(&mut h, n_outputs as u64);
482
483 for i in 0..n_outputs {
484 if hash_single && i != input_index {
485 h.update((-1i64).to_le_bytes());
486 h.update([0u8]);
487 } else {
488 let output = &tx.outputs[i];
489 h.update(output.value.to_le_bytes());
490 update_varint(&mut h, output.script_pubkey.len() as u64);
491 h.update(output.script_pubkey.as_slice());
492 }
493 }
494
495 h.update((tx.lock_time as u32).to_le_bytes());
496 h.update(sighash_u32.to_le_bytes());
497
498 let first_hash = h.finalize();
499 let second_hash = Sha256::digest(first_hash);
500 let mut result = [0u8; 32];
501 result.copy_from_slice(&second_hash);
502 result
503}
504
505#[cfg(feature = "production")]
507#[inline]
508fn update_varint(hasher: &mut sha2::Sha256, value: u64) {
509 use sha2::Digest;
510 if value < 0xfd {
511 hasher.update([value as u8]);
512 } else if value <= 0xffff {
513 hasher.update([0xfd]);
514 hasher.update((value as u16).to_le_bytes());
515 } else if value <= 0xffffffff {
516 hasher.update([0xfe]);
517 hasher.update((value as u32).to_le_bytes());
518 } else {
519 hasher.update([0xff]);
520 hasher.update(value.to_le_bytes());
521 }
522}
523
524#[cfg(feature = "production")]
526#[inline]
527fn push_varint(buf: &mut Vec<u8>, value: u64) {
528 if value < 0xfd {
529 buf.push(value as u8);
530 } else if value <= 0xffff {
531 buf.push(0xfd);
532 buf.extend_from_slice(&(value as u16).to_le_bytes());
533 } else if value <= 0xffffffff {
534 buf.push(0xfe);
535 buf.extend_from_slice(&(value as u32).to_le_bytes());
536 } else {
537 buf.push(0xff);
538 buf.extend_from_slice(&value.to_le_bytes());
539 }
540}
541
542#[cfg(feature = "production")]
545#[spec_locked("5.1.1", "CalculateSighash")]
546#[inline]
547pub fn compute_legacy_sighash_buffered(
548 tx: &Transaction,
549 input_index: usize,
550 script_code: &[u8],
551 sighash_byte: u8,
552) -> [u8; 32] {
553 use sha2::{Digest, Sha256};
554
555 let sighash_u32 = sighash_byte as u32;
556 let base_type = sighash_u32 & 0x1f;
557 let anyone_can_pay = (sighash_u32 & 0x80) != 0;
558 let hash_none = base_type == 0x02;
559 let hash_single = base_type == 0x03;
560
561 if hash_single && input_index >= tx.outputs.len() {
562 let mut result = [0u8; 32];
563 result[0] = 1;
564 return result;
565 }
566
567 thread_local! {
568 static BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(4096));
569 }
570
571 BUF.with(|cell| {
572 let mut buf = cell.borrow_mut();
573 buf.clear();
574
575 buf.extend_from_slice(&(tx.version as u32).to_le_bytes());
576
577 let n_inputs = if anyone_can_pay { 1 } else { tx.inputs.len() };
578 push_varint(&mut buf, n_inputs as u64);
579
580 for i in 0..n_inputs {
581 let actual_i = if anyone_can_pay { input_index } else { i };
582 let input = &tx.inputs[actual_i];
583 buf.extend_from_slice(&input.prevout.hash);
584 buf.extend_from_slice(&input.prevout.index.to_le_bytes());
585
586 if actual_i == input_index {
587 push_varint(&mut buf, script_code.len() as u64);
588 buf.extend_from_slice(script_code);
589 } else {
590 buf.push(0u8);
591 }
592
593 if actual_i != input_index && (hash_single || hash_none) {
594 buf.extend_from_slice(&0u32.to_le_bytes());
595 } else {
596 buf.extend_from_slice(&(input.sequence as u32).to_le_bytes());
597 }
598 }
599
600 let n_outputs = if hash_none {
601 0
602 } else if hash_single {
603 input_index + 1
604 } else {
605 tx.outputs.len()
606 };
607 push_varint(&mut buf, n_outputs as u64);
608
609 for i in 0..n_outputs {
610 if hash_single && i != input_index {
611 buf.extend_from_slice(&(-1i64).to_le_bytes());
612 buf.push(0u8);
613 } else {
614 let output = &tx.outputs[i];
615 buf.extend_from_slice(&output.value.to_le_bytes());
616 push_varint(&mut buf, output.script_pubkey.len() as u64);
617 buf.extend_from_slice(&output.script_pubkey);
618 }
619 }
620
621 buf.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
622 buf.extend_from_slice(&sighash_u32.to_le_bytes());
623
624 let first_hash = Sha256::digest(buf.as_slice());
625 let second_hash = Sha256::digest(first_hash);
626 let mut result = [0u8; 32];
627 result.copy_from_slice(&second_hash);
628 result
629 })
630}
631
632#[cfg(feature = "production")]
639#[spec_locked("5.1.1", "CalculateSighash")]
640pub fn compute_sighashes_batch(
641 tx: &Transaction,
642 script_codes: &[&[u8]],
643 sighash_bytes: &[u8],
644) -> Vec<[u8; 32]> {
645 use sha2::{Digest, Sha256};
646 let n = tx.inputs.len();
647 debug_assert_eq!(script_codes.len(), n);
648 debug_assert_eq!(sighash_bytes.len(), n);
649
650 let mut results = Vec::with_capacity(n);
651
652 let all_sighash_all = sighash_bytes.iter().all(|&b| {
653 let base = (b as u32) & 0x1f;
654 let acp = (b as u32) & 0x80;
655 base == 0x01 && acp == 0
656 });
657
658 if !all_sighash_all || n <= 1 {
659 for i in 0..n {
660 results.push(compute_legacy_sighash_nocache(
661 tx,
662 i,
663 script_codes[i],
664 sighash_bytes[i],
665 ));
666 }
667 return results;
668 }
669
670 let mut outputs_buf: Vec<u8> = Vec::with_capacity(tx.outputs.len() * 40 + 16);
672 write_varint_to_vec(&mut outputs_buf, tx.outputs.len() as u64);
673 for output in tx.outputs.iter() {
674 outputs_buf.extend_from_slice(&output.value.to_le_bytes());
675 write_varint_to_vec(&mut outputs_buf, output.script_pubkey.len() as u64);
676 outputs_buf.extend_from_slice(&output.script_pubkey);
677 }
678 outputs_buf.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
679
680 let mut running = Sha256::new();
682 running.update((tx.version as u32).to_le_bytes());
683 update_varint(&mut running, n as u64);
684
685 let mut midstates: Vec<Sha256> = Vec::with_capacity(n);
686 for j in 0..n {
687 midstates.push(running.clone());
688 running.update(tx.inputs[j].prevout.hash);
689 running.update(tx.inputs[j].prevout.index.to_le_bytes());
690 running.update([0u8]);
691 running.update((tx.inputs[j].sequence as u32).to_le_bytes());
692 }
693
694 let sighash_u32_le = 0x01u32.to_le_bytes();
695
696 for i in 0..n {
697 let mut h = midstates[i].clone();
698
699 h.update(tx.inputs[i].prevout.hash);
701 h.update(tx.inputs[i].prevout.index.to_le_bytes());
702 update_varint(&mut h, script_codes[i].len() as u64);
703 h.update(script_codes[i]);
704 h.update((tx.inputs[i].sequence as u32).to_le_bytes());
705
706 for j in (i + 1)..n {
708 h.update(tx.inputs[j].prevout.hash);
709 h.update(tx.inputs[j].prevout.index.to_le_bytes());
710 h.update([0u8]);
711 h.update((tx.inputs[j].sequence as u32).to_le_bytes());
712 }
713
714 h.update(outputs_buf.as_slice());
716 h.update(sighash_u32_le);
717
718 let first_hash = h.finalize();
719 let second_hash = Sha256::digest(first_hash);
720 let mut result = [0u8; 32];
721 result.copy_from_slice(&second_hash);
722 results.push(result);
723 }
724
725 results
726}
727
728#[spec_locked("5.1.1", "CalculateSighash")]
746pub fn calculate_transaction_sighash(
747 tx: &Transaction,
748 input_index: usize,
749 prevouts: &[TransactionOutput],
750 sighash_type: SighashType,
751) -> Result<Hash> {
752 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
754 let prevout_script_pubkeys: Vec<&[u8]> =
755 prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
756 if prevout_values.len() != tx.inputs.len() || prevout_script_pubkeys.len() != tx.inputs.len() {
758 return Err(crate::error::ConsensusError::InvalidPrevoutsCount(
759 prevout_values.len(),
760 tx.inputs.len(),
761 ));
762 }
763 calculate_transaction_sighash_with_script_code(
764 tx,
765 input_index,
766 &prevout_values,
767 &prevout_script_pubkeys,
768 sighash_type,
769 None,
770 #[cfg(feature = "production")]
771 None,
772 )
773}
774
775#[spec_locked("5.1.1", "CalculateSighash")]
780pub fn calculate_transaction_sighash_single_input(
781 tx: &Transaction,
782 input_index: usize,
783 script_for_signing: &[u8],
784 prevout_value: i64,
785 sighash_type: SighashType,
786 #[cfg(feature = "production")] sighash_cache: Option<&SighashMidstateCache>,
787) -> Result<Hash> {
788 if input_index >= tx.inputs.len() {
789 return Err(crate::error::ConsensusError::InvalidInputIndex(input_index));
790 }
791 #[cfg(feature = "production")]
795 return calculate_transaction_sighash_with_script_code(
796 tx,
797 input_index,
798 &[],
799 &[],
800 sighash_type,
801 Some(script_for_signing),
802 sighash_cache,
803 );
804 #[cfg(not(feature = "production"))]
805 {
806 let mut prevout_values = vec![0i64; tx.inputs.len()];
807 prevout_values[input_index] = prevout_value;
808 let prevout_script_pubkeys: Vec<&[u8]> = (0..tx.inputs.len())
809 .map(|i| {
810 if i == input_index {
811 script_for_signing
812 } else {
813 &[]
814 }
815 })
816 .collect();
817 calculate_transaction_sighash_with_script_code(
818 tx,
819 input_index,
820 &prevout_values,
821 &prevout_script_pubkeys,
822 sighash_type,
823 Some(script_for_signing),
824 )
825 }
826}
827
828#[spec_locked("5.1.1", "CalculateSighash")]
834pub fn calculate_transaction_sighash_with_script_code(
835 tx: &Transaction,
836 input_index: usize,
837 prevout_values: &[i64],
838 prevout_script_pubkeys: &[&[u8]],
839 sighash_type: SighashType,
840 script_code: Option<&[u8]>,
841 #[cfg(feature = "production")] sighash_cache: Option<&SighashMidstateCache>,
842) -> Result<Hash> {
843 #[cfg(all(feature = "production", feature = "profile"))]
844 let _t0 = std::time::Instant::now();
845
846 if input_index >= tx.inputs.len() {
848 return Err(crate::error::ConsensusError::InvalidInputIndex(input_index));
849 }
850
851 if script_code.is_none()
855 && (prevout_values.len() != tx.inputs.len()
856 || prevout_script_pubkeys.len() != tx.inputs.len())
857 {
858 return Err(crate::error::ConsensusError::InvalidPrevoutsCount(
859 prevout_values.len(),
860 tx.inputs.len(),
861 ));
862 }
863
864 let sighash_byte = sighash_type.as_u32();
865 let base_type = sighash_byte & 0x1f;
866 let anyone_can_pay = (sighash_byte & 0x80) != 0;
867 let hash_none = base_type == 0x02; let hash_single = base_type == 0x03; if hash_single && input_index >= tx.outputs.len() {
873 let mut result = [0u8; 32];
874 result[0] = 1; return Ok(result);
876 }
877
878 #[cfg(feature = "production")]
881 {
882 let prevout = &tx.inputs[input_index].prevout;
883 let code = script_code.unwrap_or_else(|| prevout_script_pubkeys[input_index]);
884 let sighash_byte_u8 = sighash_byte as u8;
885 let hash = sighash_cache_hash(prevout, code, sighash_byte_u8);
886 let lookup_key = SighashCacheKey {
887 prevout: *prevout,
888 code_hash: hash,
889 sighash_byte: sighash_byte_u8,
890 };
891 let cached = if let Some(cache) = sighash_cache {
892 cache
893 .lock()
894 .ok()
895 .and_then(|guard| guard.get(&lookup_key).copied())
896 } else {
897 SIGHASH_MIDSTATE_CACHE.with(|cell| cell.borrow_mut().get(&lookup_key).copied())
898 };
899 if let Some(cached) = cached {
900 return Ok(cached);
901 }
902 }
903
904 #[cfg(feature = "production")]
906 if tx.inputs.len() == 1 && input_index == 0 && !anyone_can_pay && !hash_none && !hash_single {
907 let base_type = sighash_byte & 0x1f;
908 if base_type == 0x01 || base_type == 0x00 {
909 let n_out = tx.outputs.len();
910 if n_out == 1 {
911 if let Ok(h) = build_preimage_1in1out_sighash_all(
912 tx,
913 prevout_values,
914 prevout_script_pubkeys,
915 script_code,
916 sighash_byte,
917 ) {
918 #[cfg(all(feature = "production", feature = "profile"))]
919 crate::script_profile::add_sighash_ns(_t0.elapsed().as_nanos() as u64);
920 #[cfg(feature = "production")]
921 insert_midstate_cache(
922 sighash_cache,
923 tx.inputs[0].prevout,
924 script_code.unwrap_or_else(|| prevout_script_pubkeys[0]),
925 sighash_byte as u8,
926 h,
927 );
928 return Ok(h);
929 }
930 } else if (2..=16).contains(&n_out) {
931 if let Ok(h) = build_preimage_1in_nout_sighash_all(
932 tx,
933 prevout_script_pubkeys,
934 script_code,
935 sighash_byte,
936 ) {
937 #[cfg(all(feature = "production", feature = "profile"))]
938 crate::script_profile::add_sighash_ns(_t0.elapsed().as_nanos() as u64);
939 #[cfg(feature = "production")]
940 insert_midstate_cache(
941 sighash_cache,
942 tx.inputs[0].prevout,
943 script_code.unwrap_or_else(|| prevout_script_pubkeys[0]),
944 sighash_byte as u8,
945 h,
946 );
947 return Ok(h);
948 }
949 }
950 }
951 }
952
953 #[cfg(feature = "production")]
955 if tx.inputs.len() == 2 && input_index < 2 && !anyone_can_pay && !hash_none && !hash_single {
956 let base_type = sighash_byte & 0x1f;
957 if base_type == 0x01 || base_type == 0x00 {
958 let n_out = tx.outputs.len();
959 if n_out == 1 {
960 if let Ok(h) = build_preimage_2in1out_sighash_all(
961 tx,
962 input_index,
963 prevout_values,
964 prevout_script_pubkeys,
965 script_code,
966 sighash_byte,
967 ) {
968 #[cfg(all(feature = "production", feature = "profile"))]
969 crate::script_profile::add_sighash_ns(_t0.elapsed().as_nanos() as u64);
970 #[cfg(feature = "production")]
971 insert_midstate_cache(
972 sighash_cache,
973 tx.inputs[input_index].prevout,
974 script_code.unwrap_or_else(|| prevout_script_pubkeys[input_index]),
975 sighash_byte as u8,
976 h,
977 );
978 return Ok(h);
979 }
980 } else if n_out == 2 {
981 if let Ok(h) = build_preimage_2in2out_sighash_all(
982 tx,
983 input_index,
984 prevout_script_pubkeys,
985 script_code,
986 sighash_byte,
987 ) {
988 #[cfg(all(feature = "production", feature = "profile"))]
989 crate::script_profile::add_sighash_ns(_t0.elapsed().as_nanos() as u64);
990 #[cfg(feature = "production")]
991 insert_midstate_cache(
992 sighash_cache,
993 tx.inputs[input_index].prevout,
994 script_code.unwrap_or_else(|| prevout_script_pubkeys[input_index]),
995 sighash_byte as u8,
996 h,
997 );
998 return Ok(h);
999 }
1000 }
1001 }
1002 }
1003
1004 let estimated_size = 4 + 2 + (tx.inputs.len() * 50) + 2 + (tx.outputs.len() * 30) + 4 + 4;
1006 let capacity = estimated_size.min(4096);
1007
1008 #[cfg(feature = "production")]
1009 let (result, preimage_vec) = SIGHASH_PREIMAGE_BUF.with(|buf_cell| {
1010 let mut preimage = buf_cell.borrow_mut();
1011 preimage.clear();
1012 if preimage.capacity() < capacity {
1013 preimage.reserve(capacity);
1014 }
1015 build_preimage_and_hash(
1016 tx,
1017 input_index,
1018 prevout_values,
1019 prevout_script_pubkeys,
1020 script_code,
1021 sighash_byte,
1022 anyone_can_pay,
1023 hash_none,
1024 hash_single,
1025 &mut preimage,
1026 )
1027 });
1028
1029 #[cfg(not(feature = "production"))]
1030 let (result, preimage_vec) = {
1031 let mut preimage = Vec::with_capacity(capacity);
1032 build_preimage_and_hash(
1033 tx,
1034 input_index,
1035 prevout_values,
1036 prevout_script_pubkeys,
1037 script_code,
1038 sighash_byte,
1039 anyone_can_pay,
1040 hash_none,
1041 hash_single,
1042 &mut preimage,
1043 )
1044 };
1045
1046 #[cfg(all(feature = "production", feature = "profile"))]
1047 crate::script_profile::add_sighash_ns(_t0.elapsed().as_nanos() as u64);
1048
1049 #[cfg(feature = "production")]
1050 if let Ok(ref h) = result {
1051 insert_midstate_cache(
1052 sighash_cache,
1053 tx.inputs[input_index].prevout,
1054 script_code.unwrap_or_else(|| prevout_script_pubkeys[input_index]),
1055 sighash_byte as u8,
1056 *h,
1057 );
1058 }
1059 result
1060}
1061
1062#[cfg(feature = "production")]
1064#[inline]
1065fn build_preimage_1in1out_sighash_all(
1066 tx: &crate::types::Transaction,
1067 prevout_values: &[i64],
1068 prevout_script_pubkeys: &[&[u8]],
1069 script_code: Option<&[u8]>,
1070 sighash_byte: u32,
1071) -> Result<Hash> {
1072 let input = &tx.inputs[0];
1073 let output = &tx.outputs[0];
1074 let code = script_code.unwrap_or_else(|| prevout_script_pubkeys[0]);
1075
1076 let capacity = 4 + 2 + 36 + 2 + code.len() + 4 + 2 + 8 + 2 + output.script_pubkey.len() + 4 + 4;
1077 let (result, _) = SIGHASH_PREIMAGE_BUF.with(|buf_cell| {
1078 let mut preimage = buf_cell.borrow_mut();
1079 preimage.clear();
1080 if preimage.capacity() < capacity {
1081 preimage.reserve(capacity);
1082 }
1083 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1084 preimage.push(1); preimage.extend_from_slice(&input.prevout.hash);
1086 preimage.extend_from_slice(&input.prevout.index.to_le_bytes());
1087 write_script_code_for_sighash(&mut preimage, code);
1088 preimage.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1089 preimage.push(1); preimage.extend_from_slice(&output.value.to_le_bytes());
1091 write_varint_to_vec(&mut preimage, output.script_pubkey.len() as u64);
1092 preimage.extend_from_slice(&output.script_pubkey);
1093 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1094 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1095 let r = sighash_with_cache(&preimage);
1096 (Ok(r), ())
1097 });
1098 result
1099}
1100
1101#[cfg(feature = "production")]
1103#[inline]
1104fn build_preimage_1in_nout_sighash_all(
1105 tx: &crate::types::Transaction,
1106 prevout_script_pubkeys: &[&[u8]],
1107 script_code: Option<&[u8]>,
1108 sighash_byte: u32,
1109) -> Result<Hash> {
1110 let input = &tx.inputs[0];
1111 let code = script_code.unwrap_or_else(|| prevout_script_pubkeys[0]);
1112 let mut capacity = 4 + 2 + 36 + 2 + code.len() + 4 + 2; for out in &tx.outputs {
1114 capacity += 8 + 2 + out.script_pubkey.len();
1115 }
1116 capacity += 4 + 4; let (result, _) = SIGHASH_PREIMAGE_BUF.with(|buf_cell| {
1119 let mut preimage = buf_cell.borrow_mut();
1120 preimage.clear();
1121 if preimage.capacity() < capacity {
1122 preimage.reserve(capacity);
1123 }
1124 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1125 preimage.push(1); preimage.extend_from_slice(&input.prevout.hash);
1127 preimage.extend_from_slice(&input.prevout.index.to_le_bytes());
1128 write_script_code_for_sighash(&mut preimage, code);
1129 preimage.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1130 write_varint_to_vec(&mut preimage, tx.outputs.len() as u64);
1131 for output in &tx.outputs {
1132 preimage.extend_from_slice(&output.value.to_le_bytes());
1133 write_varint_to_vec(&mut preimage, output.script_pubkey.len() as u64);
1134 preimage.extend_from_slice(&output.script_pubkey);
1135 }
1136 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1137 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1138 let r = sighash_with_cache(&preimage);
1139 (Ok(r), ())
1140 });
1141 result
1142}
1143
1144#[cfg(feature = "production")]
1147#[inline]
1148fn build_preimage_2in1out_sighash_all(
1149 tx: &crate::types::Transaction,
1150 input_index: usize,
1151 _prevout_values: &[i64],
1152 prevout_script_pubkeys: &[&[u8]],
1153 script_code: Option<&[u8]>,
1154 sighash_byte: u32,
1155) -> Result<Hash> {
1156 let output = &tx.outputs[0];
1157 let code_len = script_code
1158 .map(|s| s.len())
1159 .unwrap_or_else(|| prevout_script_pubkeys[input_index].len());
1160 let capacity =
1161 4 + 2 + 36 + 2 + code_len + 4 + 36 + 2 + 4 + 8 + 2 + output.script_pubkey.len() + 4 + 4;
1162
1163 let (result, _) = SIGHASH_PREIMAGE_BUF.with(|buf_cell| {
1164 let mut preimage = buf_cell.borrow_mut();
1165 preimage.clear();
1166 if preimage.capacity() < capacity {
1167 preimage.reserve(capacity);
1168 }
1169 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1170 preimage.push(2);
1171 for (i, inp) in tx.inputs.iter().enumerate().take(2) {
1172 let (script_len, script_slice): (usize, &[u8]) = if i == input_index {
1173 let c = script_code.unwrap_or_else(|| prevout_script_pubkeys[i]);
1174 (c.len(), c)
1175 } else {
1176 (0, &[][..]) };
1178 preimage.extend_from_slice(&inp.prevout.hash);
1179 preimage.extend_from_slice(&inp.prevout.index.to_le_bytes());
1180 write_varint_to_vec(&mut preimage, script_len as u64);
1181 preimage.extend_from_slice(script_slice);
1182 preimage.extend_from_slice(&(inp.sequence as u32).to_le_bytes());
1183 }
1184 preimage.push(1);
1185 preimage.extend_from_slice(&output.value.to_le_bytes());
1186 write_varint_to_vec(&mut preimage, output.script_pubkey.len() as u64);
1187 preimage.extend_from_slice(&output.script_pubkey);
1188 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1189 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1190 let r = sighash_with_cache(&preimage);
1191 (Ok(r), ())
1192 });
1193 result
1194}
1195
1196#[cfg(feature = "production")]
1199#[inline]
1200fn build_preimage_2in2out_sighash_all(
1201 tx: &crate::types::Transaction,
1202 input_index: usize,
1203 prevout_script_pubkeys: &[&[u8]],
1204 script_code: Option<&[u8]>,
1205 sighash_byte: u32,
1206) -> Result<Hash> {
1207 let code_len = script_code
1208 .map(|s| s.len())
1209 .unwrap_or_else(|| prevout_script_pubkeys[input_index].len());
1210 let mut capacity = 4 + 2 + 36 + 2 + code_len + 4 + 36 + 2 + 4;
1211 for out in &tx.outputs {
1212 capacity += 8 + 2 + out.script_pubkey.len();
1213 }
1214 capacity += 4 + 4;
1215
1216 let (result, _) = SIGHASH_PREIMAGE_BUF.with(|buf_cell| {
1217 let mut preimage = buf_cell.borrow_mut();
1218 preimage.clear();
1219 if preimage.capacity() < capacity {
1220 preimage.reserve(capacity);
1221 }
1222 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1223 preimage.push(2);
1224 for (i, inp) in tx.inputs.iter().enumerate().take(2) {
1225 let (script_len, script_slice): (usize, &[u8]) = if i == input_index {
1226 let c = script_code.unwrap_or_else(|| prevout_script_pubkeys[i]);
1227 (c.len(), c)
1228 } else {
1229 (0, &[][..])
1230 };
1231 preimage.extend_from_slice(&inp.prevout.hash);
1232 preimage.extend_from_slice(&inp.prevout.index.to_le_bytes());
1233 write_varint_to_vec(&mut preimage, script_len as u64);
1234 preimage.extend_from_slice(script_slice);
1235 preimage.extend_from_slice(&(inp.sequence as u32).to_le_bytes());
1236 }
1237 write_varint_to_vec(&mut preimage, 2);
1238 for output in &tx.outputs {
1239 preimage.extend_from_slice(&output.value.to_le_bytes());
1240 write_varint_to_vec(&mut preimage, output.script_pubkey.len() as u64);
1241 preimage.extend_from_slice(&output.script_pubkey);
1242 }
1243 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1244 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1245 let r = sighash_with_cache(&preimage);
1246 (Ok(r), ())
1247 });
1248 result
1249}
1250
1251#[inline]
1252fn build_preimage_and_hash(
1253 tx: &crate::types::Transaction,
1254 input_index: usize,
1255 prevout_values: &[i64],
1256 prevout_script_pubkeys: &[&[u8]],
1257 script_code: Option<&[u8]>,
1258 sighash_byte: u32,
1259 anyone_can_pay: bool,
1260 hash_none: bool,
1261 hash_single: bool,
1262 preimage: &mut Vec<u8>,
1263) -> (Result<Hash>, ()) {
1264 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1266
1267 let n_inputs = if anyone_can_pay { 1 } else { tx.inputs.len() };
1269 write_varint_to_vec(preimage, n_inputs as u64);
1270
1271 for i in 0..n_inputs {
1273 let actual_i = if anyone_can_pay { input_index } else { i };
1275 let input = &tx.inputs[actual_i];
1276
1277 preimage.extend_from_slice(&input.prevout.hash);
1279 preimage.extend_from_slice(&input.prevout.index.to_le_bytes());
1280
1281 if actual_i == input_index {
1283 let code = match script_code {
1284 Some(s) => s,
1285 None => prevout_script_pubkeys[actual_i],
1286 };
1287 write_script_code_for_sighash(preimage, code);
1288 } else {
1289 preimage.push(0); }
1291
1292 if actual_i != input_index && (hash_single || hash_none) {
1294 preimage.extend_from_slice(&0u32.to_le_bytes());
1295 } else {
1296 preimage.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1297 }
1298 }
1299
1300 let n_outputs = if hash_none {
1302 0
1303 } else if hash_single {
1304 input_index + 1
1305 } else {
1306 tx.outputs.len()
1307 };
1308 write_varint_to_vec(preimage, n_outputs as u64);
1309
1310 for i in 0..n_outputs {
1312 if hash_single && i != input_index {
1313 preimage.extend_from_slice(&(-1i64).to_le_bytes()); preimage.push(0); } else {
1317 let output = &tx.outputs[i];
1318 preimage.extend_from_slice(&output.value.to_le_bytes());
1319 write_varint_to_vec(preimage, output.script_pubkey.len() as u64);
1320 preimage.extend_from_slice(&output.script_pubkey);
1321 }
1322 }
1323
1324 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1326
1327 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1329
1330 #[cfg(feature = "production")]
1332 let result = sighash_with_cache(preimage);
1333 #[cfg(not(feature = "production"))]
1334 let result = {
1335 let hasher = OptimizedSha256::new();
1336 let first_hash = hasher.hash(&preimage);
1337 let second_hash = hasher.hash(&first_hash);
1338 let mut r = [0u8; 32];
1339 r.copy_from_slice(&second_hash);
1340 r
1341 };
1342 (Ok(result), ())
1343}
1344
1345#[spec_locked("5.1.1", "CalculateSighash")]
1359pub fn batch_compute_sighashes(
1360 tx: &Transaction,
1361 prevouts: &[TransactionOutput],
1362 sighash_type: SighashType,
1363) -> Result<Vec<Hash>> {
1364 if prevouts.len() != tx.inputs.len() {
1366 return Err(crate::error::ConsensusError::InvalidPrevoutsCount(
1367 prevouts.len(),
1368 tx.inputs.len(),
1369 ));
1370 }
1371
1372 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
1374 let prevout_script_pubkeys: Vec<&[u8]> =
1375 prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
1376
1377 #[cfg(feature = "production")]
1378 {
1379 let sighash_byte = sighash_type.as_u32() as u8;
1382 let specs: Vec<(usize, u8, &[u8])> = (0..tx.inputs.len())
1383 .map(|i| (i, sighash_byte, prevout_script_pubkeys[i]))
1384 .collect();
1385 let hashes =
1386 batch_compute_legacy_sighashes(tx, &prevout_values, &prevout_script_pubkeys, &specs)?;
1387 Ok(hashes)
1388 }
1389
1390 #[cfg(not(feature = "production"))]
1391 {
1392 let mut results = Vec::with_capacity(tx.inputs.len());
1394 for i in 0..tx.inputs.len() {
1395 results.push(calculate_transaction_sighash_with_script_code(
1396 tx,
1397 i,
1398 &prevout_values,
1399 &prevout_script_pubkeys,
1400 sighash_type,
1401 None,
1402 )?);
1403 }
1404 Ok(results)
1405 }
1406}
1407
1408#[cfg(feature = "production")]
1410fn build_legacy_sighash_preimage_into(
1411 preimage: &mut Vec<u8>,
1412 tx: &Transaction,
1413 input_index: usize,
1414 prevout_values: &[i64],
1415 prevout_script_pubkeys: &[&[u8]],
1416 script_code: &[u8],
1417 sighash_byte: u32,
1418) {
1419 let anyone_can_pay = (sighash_byte & 0x80) != 0;
1420 let hash_none = (sighash_byte & 0x1f) == 0x02;
1421 let hash_single = (sighash_byte & 0x1f) == 0x03;
1422 let n_inputs = if anyone_can_pay { 1 } else { tx.inputs.len() };
1423 let n_outputs = if hash_none {
1424 0
1425 } else if hash_single {
1426 input_index + 1
1427 } else {
1428 tx.outputs.len()
1429 };
1430 preimage.clear();
1431 preimage.reserve(512);
1432 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1433 write_varint_to_vec(preimage, n_inputs as u64);
1434 for i in 0..n_inputs {
1435 let actual_i = if anyone_can_pay { input_index } else { i };
1436 let input = &tx.inputs[actual_i];
1437 preimage.extend_from_slice(&input.prevout.hash);
1438 preimage.extend_from_slice(&input.prevout.index.to_le_bytes());
1439 if actual_i == input_index {
1440 write_script_code_for_sighash(preimage, script_code);
1441 } else {
1442 preimage.push(0);
1443 }
1444 if actual_i != input_index && (hash_single || hash_none) {
1445 preimage.extend_from_slice(&0u32.to_le_bytes());
1446 } else {
1447 preimage.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1448 }
1449 }
1450 write_varint_to_vec(preimage, n_outputs as u64);
1451 for i in 0..n_outputs {
1452 let use_missing_output =
1454 hash_single && (i != input_index || input_index >= tx.outputs.len());
1455 if use_missing_output {
1456 preimage.extend_from_slice(&(-1i64).to_le_bytes());
1457 preimage.push(0);
1458 } else {
1459 let output = &tx.outputs[i];
1460 preimage.extend_from_slice(&output.value.to_le_bytes());
1461 write_varint_to_vec(preimage, output.script_pubkey.len() as u64);
1462 preimage.extend_from_slice(&output.script_pubkey);
1463 }
1464 }
1465 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1466 preimage.extend_from_slice(&sighash_byte.to_le_bytes());
1467}
1468
1469#[cfg(feature = "production")]
1474#[spec_locked("5.1.1", "CalculateSighash")]
1475pub fn batch_compute_legacy_sighashes(
1476 tx: &Transaction,
1477 prevout_values: &[i64],
1478 prevout_script_pubkeys: &[&[u8]],
1479 specs: &[(usize, u8, &[u8])],
1480) -> Result<Vec<[u8; 32]>> {
1481 if prevout_values.len() != tx.inputs.len() || prevout_script_pubkeys.len() != tx.inputs.len() {
1482 return Err(crate::error::ConsensusError::InvalidPrevoutsCount(
1483 prevout_values.len(),
1484 tx.inputs.len(),
1485 ));
1486 }
1487 const SIGHASH_SINGLE_INVALID: [u8; 32] = {
1489 let mut h = [0u8; 32];
1490 h[0] = 1;
1491 h
1492 };
1493
1494 LEGACY_BATCH_PREIMAGES.with(|cell| {
1495 let mut storage = cell.borrow_mut();
1496 storage.resize_with(specs.len(), || Vec::with_capacity(512));
1497 let mut fixed_indices: Vec<usize> = Vec::new();
1498 for (i, &(input_index, sighash_byte, script_code)) in specs.iter().enumerate() {
1499 let hash_single = (sighash_byte & 0x1f) == 0x03;
1500 if hash_single && input_index >= tx.outputs.len() {
1501 fixed_indices.push(i);
1502 continue;
1503 }
1504 build_legacy_sighash_preimage_into(
1505 &mut storage[i],
1506 tx,
1507 input_index,
1508 prevout_values,
1509 prevout_script_pubkeys,
1510 script_code,
1511 sighash_byte as u32,
1512 );
1513 }
1514 let preimage_refs: Vec<&[u8]> = storage
1516 .iter()
1517 .enumerate()
1518 .filter(|(i, _)| !fixed_indices.contains(i))
1519 .map(|(_, v)| v.as_slice())
1520 .collect();
1521 let batch_hashes =
1522 crate::optimizations::simd_vectorization::batch_double_sha256(&preimage_refs);
1523 let mut result = vec![[0u8; 32]; specs.len()];
1525 let mut batch_idx = 0;
1526 for (i, slot) in result.iter_mut().enumerate() {
1527 if fixed_indices.contains(&i) {
1528 *slot = SIGHASH_SINGLE_INVALID;
1529 } else {
1530 *slot = batch_hashes[batch_idx];
1531 batch_idx += 1;
1532 }
1533 }
1534 Ok(result)
1535 })
1536}
1537
1538#[cfg(all(feature = "production", feature = "benchmarking"))]
1541pub fn clear_sighash_templates() {
1542 SIGHASH_CACHE.with(|cell| {
1543 cell.borrow_mut().clear();
1544 });
1545}
1546
1547fn encode_varint(value: u64) -> Vec<u8> {
1548 if value < 0xfd {
1549 vec![value as u8]
1550 } else if value <= 0xffff {
1551 let mut result = vec![0xfd];
1552 result.extend_from_slice(&(value as u16).to_le_bytes());
1553 result
1554 } else if value <= 0xffffffff {
1555 let mut result = vec![0xfe];
1556 result.extend_from_slice(&(value as u32).to_le_bytes());
1557 result
1558 } else {
1559 let mut result = vec![0xff];
1560 result.extend_from_slice(&value.to_le_bytes());
1561 result
1562 }
1563}
1564
1565#[derive(Clone, Debug)]
1578pub struct Bip143PrecomputedHashes {
1579 pub hash_prevouts: [u8; 32],
1581 pub hash_sequence: [u8; 32],
1583 pub hash_outputs: [u8; 32],
1585}
1586
1587impl Bip143PrecomputedHashes {
1588 #[inline]
1592 pub fn compute(
1593 tx: &Transaction,
1594 _prevout_values: &[i64],
1595 _prevout_script_pubkeys: &[&[u8]],
1596 ) -> Self {
1597 #[cfg(feature = "production")]
1598 {
1599 let hash_prevouts = BIP143_SERIALIZE_BUF.with(|cell| {
1600 let mut data = cell.borrow_mut();
1601 data.clear();
1602 data.reserve(tx.inputs.len() * 36);
1603 for input in tx.inputs.iter() {
1604 data.extend_from_slice(&input.prevout.hash);
1605 data.extend_from_slice(&input.prevout.index.to_le_bytes());
1606 }
1607 double_sha256(&data)
1608 });
1609
1610 let hash_sequence = BIP143_SERIALIZE_BUF.with(|cell| {
1611 let mut data = cell.borrow_mut();
1612 data.clear();
1613 data.reserve(tx.inputs.len() * 4);
1614 for input in tx.inputs.iter() {
1615 data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1616 }
1617 double_sha256(&data)
1618 });
1619
1620 let hash_outputs = BIP143_SERIALIZE_BUF.with(|cell| {
1621 let mut data = cell.borrow_mut();
1622 data.clear();
1623 let cap = tx
1624 .outputs
1625 .iter()
1626 .map(|o| 8 + 5 + o.script_pubkey.len())
1627 .sum::<usize>();
1628 data.reserve(cap);
1629 for output in tx.outputs.iter() {
1630 data.extend_from_slice(&output.value.to_le_bytes());
1631 write_varint_to_vec(&mut data, output.script_pubkey.len() as u64);
1632 data.extend_from_slice(&output.script_pubkey);
1633 }
1634 double_sha256(&data)
1635 });
1636
1637 Self {
1638 hash_prevouts,
1639 hash_sequence,
1640 hash_outputs,
1641 }
1642 }
1643
1644 #[cfg(not(feature = "production"))]
1645 {
1646 let hash_prevouts = {
1648 let mut data = Vec::with_capacity(tx.inputs.len() * 36);
1649 for input in tx.inputs.iter() {
1650 data.extend_from_slice(&input.prevout.hash);
1651 data.extend_from_slice(&input.prevout.index.to_le_bytes());
1652 }
1653 double_sha256(&data)
1654 };
1655
1656 let hash_sequence = {
1657 let mut data = Vec::with_capacity(tx.inputs.len() * 4);
1658 for input in tx.inputs.iter() {
1659 data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1660 }
1661 double_sha256(&data)
1662 };
1663
1664 let hash_outputs = {
1665 let mut data = Vec::with_capacity(tx.outputs.len() * 34);
1666 for output in tx.outputs.iter() {
1667 data.extend_from_slice(&output.value.to_le_bytes());
1668 write_varint_to_vec(&mut data, output.script_pubkey.len() as u64);
1669 data.extend_from_slice(&output.script_pubkey);
1670 }
1671 double_sha256(&data)
1672 };
1673
1674 Self {
1675 hash_prevouts,
1676 hash_sequence,
1677 hash_outputs,
1678 }
1679 }
1680 }
1681}
1682
1683#[inline(always)]
1685fn double_sha256(data: &[u8]) -> [u8; 32] {
1686 let hasher = OptimizedSha256::new();
1687 hasher.hash256(data)
1688}
1689
1690#[spec_locked("11.1.9", "ComputeWitnessSignatureHash")]
1707pub fn calculate_bip143_sighash(
1708 tx: &Transaction,
1709 input_index: usize,
1710 script_code: &[u8],
1711 amount: i64,
1712 sighash_type: u8,
1713 precomputed: Option<&Bip143PrecomputedHashes>,
1714) -> Result<Hash> {
1715 if input_index >= tx.inputs.len() {
1716 return Err(crate::error::ConsensusError::InvalidInputIndex(input_index));
1717 }
1718
1719 let anyone_can_pay = (sighash_type & 0x80) != 0;
1721 let base_type = sighash_type & 0x1f;
1722 let is_none = base_type == 0x02;
1723 let is_single = base_type == 0x03;
1724
1725 let computed;
1727 let hashes = match precomputed {
1728 Some(h) => h,
1729 None => {
1730 computed = Bip143PrecomputedHashes::compute(tx, &[], &[]);
1731 &computed
1732 }
1733 };
1734
1735 #[cfg(feature = "production")]
1738 let preimage_result = BIP143_PREIMAGE_BUF.with(|buf_cell| {
1739 let mut preimage = buf_cell.borrow_mut();
1740 preimage.clear();
1741 let cap = 160 + script_code.len();
1742 if preimage.capacity() < cap {
1743 preimage.reserve(cap);
1744 }
1745 build_bip143_preimage(
1746 tx,
1747 input_index,
1748 script_code,
1749 amount,
1750 sighash_type,
1751 anyone_can_pay,
1752 is_none,
1753 is_single,
1754 hashes,
1755 &mut preimage,
1756 )
1757 });
1758 #[cfg(not(feature = "production"))]
1759 let preimage_result = {
1760 let mut preimage = Vec::with_capacity(160 + script_code.len());
1761 build_bip143_preimage(
1762 tx,
1763 input_index,
1764 script_code,
1765 amount,
1766 sighash_type,
1767 anyone_can_pay,
1768 is_none,
1769 is_single,
1770 hashes,
1771 &mut preimage,
1772 )
1773 };
1774 preimage_result
1775}
1776
1777#[inline]
1778fn build_bip143_preimage(
1779 tx: &Transaction,
1780 input_index: usize,
1781 script_code: &[u8],
1782 amount: i64,
1783 sighash_type: u8,
1784 anyone_can_pay: bool,
1785 is_none: bool,
1786 is_single: bool,
1787 hashes: &Bip143PrecomputedHashes,
1788 preimage: &mut Vec<u8>,
1789) -> Result<Hash> {
1790 preimage.extend_from_slice(&(tx.version as u32).to_le_bytes());
1792
1793 if anyone_can_pay {
1795 preimage.extend_from_slice(&[0u8; 32]);
1796 } else {
1797 preimage.extend_from_slice(&hashes.hash_prevouts);
1798 }
1799
1800 if anyone_can_pay || is_none || is_single {
1802 preimage.extend_from_slice(&[0u8; 32]);
1803 } else {
1804 preimage.extend_from_slice(&hashes.hash_sequence);
1805 }
1806
1807 let input = &tx.inputs[input_index];
1809 preimage.extend_from_slice(&input.prevout.hash);
1810 preimage.extend_from_slice(&input.prevout.index.to_le_bytes());
1811
1812 write_varint_to_vec(preimage, script_code.len() as u64);
1814 preimage.extend_from_slice(script_code);
1815
1816 preimage.extend_from_slice(&amount.to_le_bytes());
1818
1819 preimage.extend_from_slice(&(input.sequence as u32).to_le_bytes());
1821
1822 if is_none {
1824 preimage.extend_from_slice(&[0u8; 32]);
1825 } else if is_single {
1826 if input_index < tx.outputs.len() {
1827 let output = &tx.outputs[input_index];
1829 #[cfg(feature = "production")]
1830 let hash_outputs = BIP143_SINGLE_OUTPUT_BUF.with(|buf_cell| {
1831 let mut output_data = buf_cell.borrow_mut();
1832 output_data.clear();
1833 let cap = 8 + 9 + output.script_pubkey.len(); if output_data.capacity() < cap {
1835 output_data.reserve(cap);
1836 }
1837 output_data.extend_from_slice(&output.value.to_le_bytes());
1838 write_varint_to_vec(&mut output_data, output.script_pubkey.len() as u64);
1839 output_data.extend_from_slice(&output.script_pubkey);
1840 double_sha256(&output_data)
1841 });
1842 #[cfg(not(feature = "production"))]
1843 let hash_outputs = {
1844 let mut output_data = Vec::with_capacity(8 + 9 + output.script_pubkey.len());
1845 output_data.extend_from_slice(&output.value.to_le_bytes());
1846 write_varint_to_vec(&mut output_data, output.script_pubkey.len() as u64);
1847 output_data.extend_from_slice(&output.script_pubkey);
1848 double_sha256(&output_data)
1849 };
1850 preimage.extend_from_slice(&hash_outputs);
1851 } else {
1852 preimage.extend_from_slice(&[0u8; 32]);
1854 }
1855 } else {
1856 preimage.extend_from_slice(&hashes.hash_outputs);
1857 }
1858
1859 preimage.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
1861
1862 preimage.extend_from_slice(&(sighash_type as u32).to_le_bytes());
1864
1865 Ok(double_sha256(preimage))
1867}
1868
1869#[spec_locked("11.1.9", "ComputeWitnessSignatureHash")]
1873pub fn batch_compute_bip143_sighashes(
1874 tx: &Transaction,
1875 prevout_values: &[i64],
1876 prevout_script_pubkeys: &[&[u8]],
1877 script_codes: &[&[u8]],
1878 sighash_type: u8,
1879) -> Result<Vec<Hash>> {
1880 if prevout_values.len() != tx.inputs.len()
1881 || prevout_script_pubkeys.len() != tx.inputs.len()
1882 || script_codes.len() != tx.inputs.len()
1883 {
1884 return Err(crate::error::ConsensusError::InvalidPrevoutsCount(
1885 prevout_values.len(),
1886 tx.inputs.len(),
1887 ));
1888 }
1889
1890 let precomputed = Bip143PrecomputedHashes::compute(tx, prevout_values, prevout_script_pubkeys);
1892
1893 let mut results = Vec::with_capacity(tx.inputs.len());
1895 for (i, (value, script_code)) in prevout_values.iter().zip(script_codes.iter()).enumerate() {
1896 let sighash =
1897 calculate_bip143_sighash(tx, i, script_code, *value, sighash_type, Some(&precomputed))?;
1898 results.push(sighash);
1899 }
1900 Ok(results)
1901}
1902
1903#[cfg(test)]
1904mod tests {
1905 use super::*;
1906 use crate::opcodes::*;
1907
1908 #[test]
1909 fn test_sighash_type_parsing() {
1910 assert_eq!(SighashType::from_byte(0x01), SighashType::ALL);
1912 assert_eq!(SighashType::from_byte(0x02), SighashType::NONE);
1913 assert_eq!(SighashType::from_byte(0x03), SighashType::SINGLE);
1914 assert_eq!(SighashType::from_byte(0x00), SighashType::ALL_LEGACY);
1915 assert_eq!(SighashType::from_byte(0x81), SighashType::ALL_ANYONECANPAY);
1916 assert_eq!(SighashType::from_byte(0x82), SighashType::NONE_ANYONECANPAY);
1917 assert_eq!(
1918 SighashType::from_byte(0x83),
1919 SighashType::SINGLE_ANYONECANPAY
1920 );
1921 assert_eq!(SighashType::ALL_ANYONECANPAY.as_u32(), 0x81);
1923 assert_eq!(SighashType::NONE_ANYONECANPAY.as_u32(), 0x82);
1924 assert_eq!(SighashType::SINGLE_ANYONECANPAY.as_u32(), 0x83);
1925 let st = SighashType::from_byte(0x04);
1927 assert!(st.is_all()); assert_eq!(st.as_u32(), 0x04); let st84 = SighashType::from_byte(0x84);
1930 assert!(st84.is_all());
1931 assert!(st84.is_anyonecanpay());
1932 assert_eq!(st84.as_u32(), 0x84);
1933 }
1934
1935 #[test]
1936 fn test_varint_encoding() {
1937 assert_eq!(encode_varint(0), vec![0]);
1938 assert_eq!(encode_varint(252), vec![252]);
1939 assert_eq!(encode_varint(253), vec![0xfd, 253, 0]);
1940 assert_eq!(encode_varint(65535), vec![0xfd, 255, 255]);
1941 assert_eq!(encode_varint(65536), vec![0xfe, 0, 0, 1, 0]);
1942 }
1943
1944 #[test]
1945 fn test_sighash_calculation() {
1946 let tx = Transaction {
1948 version: 1,
1949 inputs: vec![TransactionInput {
1950 prevout: OutPoint {
1951 hash: [1u8; 32],
1952 index: 0,
1953 },
1954 script_sig: vec![OP_1],
1955 sequence: 0xffffffff,
1956 }]
1957 .into(),
1958 outputs: vec![TransactionOutput {
1959 value: 5000000000,
1960 script_pubkey: vec![
1961 OP_DUP,
1962 OP_HASH160,
1963 PUSH_20_BYTES,
1964 0x89,
1965 0xab,
1966 0xcd,
1967 0xef,
1968 0x12,
1969 0x34,
1970 0x56,
1971 0x78,
1972 0x9a,
1973 0xbc,
1974 0xde,
1975 0xf0,
1976 0x12,
1977 0x34,
1978 0x56,
1979 0x78,
1980 0x9a,
1981 OP_EQUALVERIFY,
1982 OP_CHECKSIG,
1983 ], }]
1985 .into(),
1986 lock_time: 0,
1987 };
1988
1989 let prevouts = vec![TransactionOutput {
1990 value: 10000000000,
1991 script_pubkey: vec![
1992 OP_DUP,
1993 OP_HASH160,
1994 PUSH_20_BYTES,
1995 0x89,
1996 0xab,
1997 0xcd,
1998 0xef,
1999 0x12,
2000 0x34,
2001 0x56,
2002 0x78,
2003 0x9a,
2004 0xbc,
2005 0xde,
2006 0xf0,
2007 0x12,
2008 0x34,
2009 0x56,
2010 0x78,
2011 0x9a,
2012 OP_EQUALVERIFY,
2013 OP_CHECKSIG,
2014 ],
2015 }];
2016
2017 let sighash = calculate_transaction_sighash(&tx, 0, &prevouts, SighashType::ALL).unwrap();
2019 assert_eq!(sighash.len(), 32);
2020
2021 let sighash_none =
2023 calculate_transaction_sighash(&tx, 0, &prevouts, SighashType::NONE).unwrap();
2024 assert_ne!(sighash, sighash_none);
2025
2026 let sighash_single =
2028 calculate_transaction_sighash(&tx, 0, &prevouts, SighashType::SINGLE).unwrap();
2029 assert_ne!(sighash, sighash_single);
2030 }
2031
2032 #[test]
2033 fn test_sighash_invalid_input_index() {
2034 let tx = Transaction {
2035 version: 1,
2036 inputs: vec![].into(),
2037 outputs: vec![].into(),
2038 lock_time: 0,
2039 };
2040
2041 let result = calculate_transaction_sighash(&tx, 0, &[], SighashType::ALL);
2042 assert!(result.is_err());
2043 }
2044
2045 #[test]
2046 fn test_bip143_sighash() {
2047 let tx = Transaction {
2049 version: 1,
2050 inputs: vec![
2051 TransactionInput {
2052 prevout: OutPoint {
2053 hash: [1u8; 32],
2054 index: 0,
2055 },
2056 script_sig: vec![], sequence: 0xffffffff,
2058 },
2059 TransactionInput {
2060 prevout: OutPoint {
2061 hash: [2u8; 32],
2062 index: 1,
2063 },
2064 script_sig: vec![],
2065 sequence: 0xfffffffe,
2066 },
2067 ]
2068 .into(),
2069 outputs: vec![TransactionOutput {
2070 value: 5000000000,
2071 script_pubkey: vec![
2072 OP_0,
2073 PUSH_20_BYTES,
2074 0x89,
2075 0xab,
2076 0xcd,
2077 0xef,
2078 0x12,
2079 0x34,
2080 0x56,
2081 0x78,
2082 0x9a,
2083 0xbc,
2084 0xde,
2085 0xf0,
2086 0x12,
2087 0x34,
2088 0x56,
2089 0x78,
2090 0x9a,
2091 0xbc,
2092 0xde,
2093 0xf0,
2094 ],
2095 }]
2096 .into(),
2097 lock_time: 0,
2098 };
2099
2100 let prevouts = [
2101 TransactionOutput {
2102 value: 10000000000,
2103 script_pubkey: vec![
2104 OP_0,
2105 PUSH_20_BYTES,
2106 0x11,
2107 0x22,
2108 0x33,
2109 0x44,
2110 0x55,
2111 0x66,
2112 0x77,
2113 0x88,
2114 0x99,
2115 0xaa,
2116 0xbb,
2117 0xcc,
2118 0xdd,
2119 0xee,
2120 0xff,
2121 0x00,
2122 0x11,
2123 0x22,
2124 0x33,
2125 0x44,
2126 ],
2127 },
2128 TransactionOutput {
2129 value: 8000000000,
2130 script_pubkey: vec![
2131 OP_0,
2132 PUSH_20_BYTES,
2133 0xaa,
2134 0xbb,
2135 0xcc,
2136 0xdd,
2137 0xee,
2138 0xff,
2139 0x00,
2140 0x11,
2141 0x22,
2142 0x33,
2143 0x44,
2144 0x55,
2145 0x66,
2146 0x77,
2147 0x88,
2148 0x99,
2149 0xaa,
2150 0xbb,
2151 0xcc,
2152 0xdd,
2153 ],
2154 },
2155 ];
2156
2157 let script_code = vec![
2159 OP_DUP,
2160 OP_HASH160,
2161 PUSH_20_BYTES,
2162 0x11,
2163 0x22,
2164 0x33,
2165 0x44,
2166 0x55,
2167 0x66,
2168 0x77,
2169 0x88,
2170 0x99,
2171 0xaa,
2172 0xbb,
2173 0xcc,
2174 0xdd,
2175 0xee,
2176 0xff,
2177 0x00,
2178 0x11,
2179 0x22,
2180 0x33,
2181 OP_EQUALVERIFY,
2182 OP_CHECKSIG,
2183 ];
2184
2185 let sighash0 =
2187 calculate_bip143_sighash(&tx, 0, &script_code, prevouts[0].value, 0x01, None).unwrap();
2188 assert_eq!(sighash0.len(), 32);
2189
2190 let sighash1 =
2192 calculate_bip143_sighash(&tx, 1, &script_code, prevouts[1].value, 0x01, None).unwrap();
2193 assert_ne!(sighash0, sighash1);
2194
2195 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
2197 let prevout_script_pubkeys: Vec<&[u8]> =
2198 prevouts.iter().map(|p| p.script_pubkey.as_ref()).collect();
2199 let precomputed =
2200 Bip143PrecomputedHashes::compute(&tx, &prevout_values, &prevout_script_pubkeys);
2201 let sighash0_precomputed = calculate_bip143_sighash(
2202 &tx,
2203 0,
2204 &script_code,
2205 prevout_values[0],
2206 0x01,
2207 Some(&precomputed),
2208 )
2209 .unwrap();
2210 assert_eq!(sighash0, sighash0_precomputed);
2211 }
2212
2213 #[test]
2214 fn test_bip143_anyonecanpay() {
2215 let tx = Transaction {
2216 version: 1,
2217 inputs: vec![TransactionInput {
2218 prevout: OutPoint {
2219 hash: [1u8; 32],
2220 index: 0,
2221 },
2222 script_sig: vec![],
2223 sequence: 0xffffffff,
2224 }]
2225 .into(),
2226 outputs: vec![TransactionOutput {
2227 value: 5000000000,
2228 script_pubkey: vec![OP_0, PUSH_20_BYTES],
2229 }]
2230 .into(),
2231 lock_time: 0,
2232 };
2233
2234 let script_code = {
2235 let mut s = vec![OP_DUP, OP_HASH160, PUSH_20_BYTES];
2236 s.extend_from_slice(&[0u8; 20]); s.push(OP_EQUALVERIFY);
2238 s.push(OP_CHECKSIG);
2239 s };
2241 let amount = 10000000000i64;
2242
2243 let sighash_all =
2245 calculate_bip143_sighash(&tx, 0, &script_code, amount, 0x01, None).unwrap();
2246
2247 let sighash_anyonecanpay =
2249 calculate_bip143_sighash(&tx, 0, &script_code, amount, 0x81, None).unwrap();
2250
2251 assert_ne!(sighash_all, sighash_anyonecanpay);
2253 }
2254
2255 #[test]
2259 fn test_2input_legacy_sighash_non_signing_empty_script() {
2260 let script_a = vec![
2262 PUSH_33_BYTES,
2263 0x02,
2264 0x00,
2265 0x00,
2266 0x00,
2267 0x00,
2268 0x00,
2269 0x00,
2270 0x00,
2271 0x00,
2272 0x00,
2273 0x00,
2274 0x00,
2275 0x00,
2276 0x00,
2277 0x00,
2278 0x00,
2279 0x00,
2280 0x00,
2281 0x00,
2282 0x00,
2283 0x00,
2284 0x00,
2285 0x00,
2286 0x00,
2287 0x00,
2288 0x00,
2289 0x00,
2290 0x00,
2291 0x00,
2292 0x00,
2293 0x00,
2294 OP_CHECKSIG,
2295 ]; let script_b = vec![
2297 PUSH_33_BYTES,
2298 0x03,
2299 0x11,
2300 0x11,
2301 0x11,
2302 0x11,
2303 0x11,
2304 0x11,
2305 0x11,
2306 0x11,
2307 0x11,
2308 0x11,
2309 0x11,
2310 0x11,
2311 0x11,
2312 0x11,
2313 0x11,
2314 0x11,
2315 0x11,
2316 0x11,
2317 0x11,
2318 0x11,
2319 0x11,
2320 0x11,
2321 0x11,
2322 0x11,
2323 0x11,
2324 0x11,
2325 0x11,
2326 0x11,
2327 0x11,
2328 0x11,
2329 OP_CHECKSIG,
2330 ]; let tx = Transaction {
2332 version: 1,
2333 inputs: vec![
2334 TransactionInput {
2335 prevout: OutPoint {
2336 hash: [1u8; 32],
2337 index: 0,
2338 },
2339 script_sig: vec![],
2340 sequence: 0xffffffff,
2341 },
2342 TransactionInput {
2343 prevout: OutPoint {
2344 hash: [2u8; 32],
2345 index: 1,
2346 },
2347 script_sig: vec![],
2348 sequence: 0xffffffff,
2349 },
2350 ]
2351 .into(),
2352 outputs: vec![TransactionOutput {
2353 value: 5000000000,
2354 script_pubkey: vec![OP_DUP, OP_HASH160, PUSH_20_BYTES],
2355 }]
2356 .into(),
2357 lock_time: 0,
2358 };
2359 let pv: Vec<i64> = vec![10_000_000_000, 8_000_000_000];
2360 let psp_ab: Vec<&[u8]> = vec![script_a.as_slice(), script_b.as_slice()];
2361 let psp_aa: Vec<&[u8]> = vec![script_a.as_slice(), script_a.as_slice()];
2362
2363 let sighash_ab = calculate_transaction_sighash_with_script_code(
2366 &tx,
2367 0,
2368 &pv,
2369 &psp_ab,
2370 SighashType::ALL,
2371 None,
2372 #[cfg(feature = "production")]
2373 None,
2374 )
2375 .unwrap();
2376 let sighash_aa = calculate_transaction_sighash_with_script_code(
2377 &tx,
2378 0,
2379 &pv,
2380 &psp_aa,
2381 SighashType::ALL,
2382 None,
2383 #[cfg(feature = "production")]
2384 None,
2385 )
2386 .unwrap();
2387 assert_eq!(sighash_ab, sighash_aa,
2388 "2-input legacy: signing input 0 — input 1 script must be empty; changing input 1 scriptPubKey must not change sighash");
2389 }
2390
2391 #[cfg(feature = "production")]
2392 #[test]
2393 fn test_batch_sighash_single_input_index_ge_outputs() {
2394 let tx = Transaction {
2397 version: 1,
2398 inputs: vec![
2399 TransactionInput {
2400 prevout: OutPoint {
2401 hash: [1u8; 32],
2402 index: 0,
2403 },
2404 script_sig: vec![],
2405 sequence: 0xffffffff,
2406 },
2407 TransactionInput {
2408 prevout: OutPoint {
2409 hash: [2u8; 32],
2410 index: 1,
2411 },
2412 script_sig: vec![],
2413 sequence: 0xffffffff,
2414 },
2415 ]
2416 .into(),
2417 outputs: vec![TransactionOutput {
2418 value: 5000000000,
2419 script_pubkey: vec![OP_DUP, OP_HASH160, PUSH_20_BYTES],
2420 }]
2421 .into(),
2422 lock_time: 0,
2423 };
2424 let prevout_values = vec![10_000_000_000i64, 8_000_000_000i64];
2425 let script = vec![OP_DUP, OP_HASH160, PUSH_20_BYTES];
2426 let prevout_script_pubkeys: Vec<&[u8]> = vec![script.as_slice(), script.as_slice()];
2427 let specs = vec![(1usize, 0x03u8, script.as_slice() as &[u8])]; let hashes = super::batch_compute_legacy_sighashes(
2429 &tx,
2430 &prevout_values,
2431 &prevout_script_pubkeys,
2432 &specs,
2433 )
2434 .unwrap();
2435 assert_eq!(hashes.len(), 1);
2436 let mut expected = [0u8; 32];
2437 expected[0] = 1;
2438 assert_eq!(
2439 hashes[0], expected,
2440 "SIGHASH_SINGLE with input_index>=outputs.len() must return 0x0000...0001"
2441 );
2442 }
2443}