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