1use crate::crypto;
7use crate::error::SignerError;
8use sha2::{Digest, Sha256};
9
10use super::tapscript::SighashType;
11use super::transaction::{Transaction, TxOut};
12
13pub struct PrevOut {
17 pub script_code: Vec<u8>,
19 pub value: u64,
21}
22
23pub fn segwit_v0_sighash(
33 tx: &Transaction,
34 input_idx: usize,
35 prev_out: &PrevOut,
36 sighash_type: SighashType,
37) -> Result<[u8; 32], SignerError> {
38 if input_idx >= tx.inputs.len() {
39 return Err(SignerError::SigningFailed(format!(
40 "input index {} out of range ({})",
41 input_idx,
42 tx.inputs.len()
43 )));
44 }
45
46 let sighash_u32 = sighash_type.to_byte() as u32;
47 let anyone_can_pay = sighash_u32 & 0x80 != 0;
48 let base_type = sighash_u32 & 0x1f;
49
50 let hash_prevouts = if !anyone_can_pay {
52 let mut buf = Vec::new();
53 for input in &tx.inputs {
54 buf.extend_from_slice(&input.previous_output.txid);
55 buf.extend_from_slice(&input.previous_output.vout.to_le_bytes());
56 }
57 crypto::double_sha256(&buf)
58 } else {
59 [0u8; 32]
60 };
61
62 let hash_sequence = if !anyone_can_pay && base_type != 0x02 && base_type != 0x03 {
64 let mut buf = Vec::new();
65 for input in &tx.inputs {
66 buf.extend_from_slice(&input.sequence.to_le_bytes());
67 }
68 crypto::double_sha256(&buf)
69 } else {
70 [0u8; 32]
71 };
72
73 let hash_outputs = if base_type != 0x02 && base_type != 0x03 {
75 let mut buf = Vec::new();
77 for output in &tx.outputs {
78 buf.extend_from_slice(&output.value.to_le_bytes());
79 crate::encoding::encode_compact_size(&mut buf, output.script_pubkey.len() as u64);
80 buf.extend_from_slice(&output.script_pubkey);
81 }
82 crypto::double_sha256(&buf)
83 } else if base_type == 0x03 && input_idx < tx.outputs.len() {
84 let mut buf = Vec::new();
86 let output = &tx.outputs[input_idx];
87 buf.extend_from_slice(&output.value.to_le_bytes());
88 crate::encoding::encode_compact_size(&mut buf, output.script_pubkey.len() as u64);
89 buf.extend_from_slice(&output.script_pubkey);
90 crypto::double_sha256(&buf)
91 } else {
92 [0u8; 32]
93 };
94
95 let input = &tx.inputs[input_idx];
97 let mut preimage = Vec::with_capacity(256);
98 preimage.extend_from_slice(&tx.version.to_le_bytes());
99 preimage.extend_from_slice(&hash_prevouts);
100 preimage.extend_from_slice(&hash_sequence);
101 preimage.extend_from_slice(&input.previous_output.txid);
103 preimage.extend_from_slice(&input.previous_output.vout.to_le_bytes());
104 crate::encoding::encode_compact_size(&mut preimage, prev_out.script_code.len() as u64);
106 preimage.extend_from_slice(&prev_out.script_code);
107 preimage.extend_from_slice(&prev_out.value.to_le_bytes());
109 preimage.extend_from_slice(&input.sequence.to_le_bytes());
111 preimage.extend_from_slice(&hash_outputs);
112 preimage.extend_from_slice(&tx.locktime.to_le_bytes());
113 preimage.extend_from_slice(&sighash_u32.to_le_bytes());
114
115 Ok(crypto::double_sha256(&preimage))
116}
117
118pub fn taproot_key_path_sighash(
128 tx: &Transaction,
129 input_idx: usize,
130 prevouts: &[TxOut],
131 sighash_type: SighashType,
132) -> Result<[u8; 32], SignerError> {
133 if input_idx >= tx.inputs.len() {
134 return Err(SignerError::SigningFailed(format!(
135 "input index {} out of range ({})",
136 input_idx,
137 tx.inputs.len()
138 )));
139 }
140 if prevouts.len() != tx.inputs.len() {
141 return Err(SignerError::SigningFailed(format!(
142 "prevouts length {} != inputs length {}",
143 prevouts.len(),
144 tx.inputs.len()
145 )));
146 }
147
148 let sighash_byte = sighash_type.to_byte();
149 let anyone_can_pay = sighash_byte & 0x80 != 0;
150 let base_type = sighash_byte & 0x03;
151 let effective_base = if sighash_byte == 0x00 {
153 0x01
154 } else {
155 base_type
156 };
157
158 let mut sig_msg = Vec::with_capacity(256);
160 sig_msg.push(0x00); sig_msg.push(sighash_byte);
164
165 sig_msg.extend_from_slice(&tx.version.to_le_bytes());
167 sig_msg.extend_from_slice(&tx.locktime.to_le_bytes());
169
170 if !anyone_can_pay {
172 let mut h = Sha256::new();
174 for input in &tx.inputs {
175 h.update(input.previous_output.txid);
176 h.update(input.previous_output.vout.to_le_bytes());
177 }
178 sig_msg.extend_from_slice(&h.finalize());
179
180 let mut h = Sha256::new();
182 for p in prevouts {
183 h.update(p.value.to_le_bytes());
184 }
185 sig_msg.extend_from_slice(&h.finalize());
186
187 let mut h = Sha256::new();
189 for p in prevouts {
190 let mut tmp = Vec::new();
192 crate::encoding::encode_compact_size(&mut tmp, p.script_pubkey.len() as u64);
193 h.update(&tmp);
194 h.update(&p.script_pubkey);
195 }
196 sig_msg.extend_from_slice(&h.finalize());
197
198 let mut h = Sha256::new();
200 for input in &tx.inputs {
201 h.update(input.sequence.to_le_bytes());
202 }
203 sig_msg.extend_from_slice(&h.finalize());
204 }
205
206 if effective_base == 0x01 {
208 let mut h = Sha256::new();
209 for output in &tx.outputs {
210 h.update(output.value.to_le_bytes());
211 let mut tmp = Vec::new();
212 crate::encoding::encode_compact_size(&mut tmp, output.script_pubkey.len() as u64);
213 h.update(&tmp);
214 h.update(&output.script_pubkey);
215 }
216 sig_msg.extend_from_slice(&h.finalize());
217 }
218
219 sig_msg.push(0x00); if anyone_can_pay {
224 let input = &tx.inputs[input_idx];
226 sig_msg.extend_from_slice(&input.previous_output.txid);
227 sig_msg.extend_from_slice(&input.previous_output.vout.to_le_bytes());
228 sig_msg.extend_from_slice(&prevouts[input_idx].value.to_le_bytes());
230 let mut tmp = Vec::new();
232 crate::encoding::encode_compact_size(
233 &mut tmp,
234 prevouts[input_idx].script_pubkey.len() as u64,
235 );
236 sig_msg.extend_from_slice(&tmp);
237 sig_msg.extend_from_slice(&prevouts[input_idx].script_pubkey);
238 sig_msg.extend_from_slice(&tx.inputs[input_idx].sequence.to_le_bytes());
240 } else {
241 sig_msg.extend_from_slice(&(input_idx as u32).to_le_bytes());
243 }
244
245 if effective_base == 0x03 && input_idx < tx.outputs.len() {
247 let mut h = Sha256::new();
248 let output = &tx.outputs[input_idx];
249 h.update(output.value.to_le_bytes());
250 let mut tmp = Vec::new();
251 crate::encoding::encode_compact_size(&mut tmp, output.script_pubkey.len() as u64);
252 h.update(&tmp);
253 h.update(&output.script_pubkey);
254 sig_msg.extend_from_slice(&h.finalize());
255 }
256
257 Ok(crypto::tagged_hash(b"TapSighash", &sig_msg))
259}
260
261#[must_use]
267pub fn p2wpkh_script_code(pubkey_hash: &[u8; 20]) -> Vec<u8> {
268 let mut script = Vec::with_capacity(25);
269 script.push(0x76); script.push(0xa9); script.push(0x14); script.extend_from_slice(pubkey_hash);
273 script.push(0x88); script.push(0xac); script
276}
277
278pub fn taproot_script_path_sighash(
296 tx: &Transaction,
297 input_idx: usize,
298 prevouts: &[TxOut],
299 sighash_type: SighashType,
300 tapleaf_hash: &[u8; 32],
301 codesep_pos: u32,
302) -> Result<[u8; 32], SignerError> {
303 if input_idx >= tx.inputs.len() {
304 return Err(SignerError::SigningFailed(format!(
305 "input index {} out of range ({})",
306 input_idx,
307 tx.inputs.len()
308 )));
309 }
310 if prevouts.len() != tx.inputs.len() {
311 return Err(SignerError::SigningFailed(format!(
312 "prevouts length {} != inputs length {}",
313 prevouts.len(),
314 tx.inputs.len()
315 )));
316 }
317
318 let sighash_byte = sighash_type.to_byte();
319 let anyone_can_pay = sighash_byte & 0x80 != 0;
320 let base_type = sighash_byte & 0x03;
321 let effective_base = if sighash_byte == 0x00 {
322 0x01
323 } else {
324 base_type
325 };
326
327 let mut sig_msg = Vec::with_capacity(300);
328 sig_msg.push(0x00); sig_msg.push(sighash_byte);
330 sig_msg.extend_from_slice(&tx.version.to_le_bytes());
331 sig_msg.extend_from_slice(&tx.locktime.to_le_bytes());
332
333 if !anyone_can_pay {
334 let mut h = Sha256::new();
335 for input in &tx.inputs {
336 h.update(input.previous_output.txid);
337 h.update(input.previous_output.vout.to_le_bytes());
338 }
339 sig_msg.extend_from_slice(&h.finalize());
340
341 let mut h = Sha256::new();
342 for p in prevouts {
343 h.update(p.value.to_le_bytes());
344 }
345 sig_msg.extend_from_slice(&h.finalize());
346
347 let mut h = Sha256::new();
348 for p in prevouts {
349 let mut tmp = Vec::new();
350 crate::encoding::encode_compact_size(&mut tmp, p.script_pubkey.len() as u64);
351 h.update(&tmp);
352 h.update(&p.script_pubkey);
353 }
354 sig_msg.extend_from_slice(&h.finalize());
355
356 let mut h = Sha256::new();
357 for input in &tx.inputs {
358 h.update(input.sequence.to_le_bytes());
359 }
360 sig_msg.extend_from_slice(&h.finalize());
361 }
362
363 if effective_base == 0x01 {
364 let mut h = Sha256::new();
365 for output in &tx.outputs {
366 h.update(output.value.to_le_bytes());
367 let mut tmp = Vec::new();
368 crate::encoding::encode_compact_size(&mut tmp, output.script_pubkey.len() as u64);
369 h.update(&tmp);
370 h.update(&output.script_pubkey);
371 }
372 sig_msg.extend_from_slice(&h.finalize());
373 }
374
375 sig_msg.push(0x02); if anyone_can_pay {
379 let input = &tx.inputs[input_idx];
380 sig_msg.extend_from_slice(&input.previous_output.txid);
381 sig_msg.extend_from_slice(&input.previous_output.vout.to_le_bytes());
382 sig_msg.extend_from_slice(&prevouts[input_idx].value.to_le_bytes());
383 let mut tmp = Vec::new();
384 crate::encoding::encode_compact_size(
385 &mut tmp,
386 prevouts[input_idx].script_pubkey.len() as u64,
387 );
388 sig_msg.extend_from_slice(&tmp);
389 sig_msg.extend_from_slice(&prevouts[input_idx].script_pubkey);
390 sig_msg.extend_from_slice(&tx.inputs[input_idx].sequence.to_le_bytes());
391 } else {
392 sig_msg.extend_from_slice(&(input_idx as u32).to_le_bytes());
393 }
394
395 if effective_base == 0x03 && input_idx < tx.outputs.len() {
396 let mut h = Sha256::new();
397 let output = &tx.outputs[input_idx];
398 h.update(output.value.to_le_bytes());
399 let mut tmp = Vec::new();
400 crate::encoding::encode_compact_size(&mut tmp, output.script_pubkey.len() as u64);
401 h.update(&tmp);
402 h.update(&output.script_pubkey);
403 sig_msg.extend_from_slice(&h.finalize());
404 }
405
406 sig_msg.extend_from_slice(tapleaf_hash);
408 sig_msg.push(0x00); sig_msg.extend_from_slice(&codesep_pos.to_le_bytes());
410
411 Ok(crypto::tagged_hash(b"TapSighash", &sig_msg))
412}
413
414#[cfg(test)]
417#[allow(clippy::unwrap_used, clippy::expect_used)]
418mod tests {
419 use super::super::transaction::*;
420 use super::*;
421
422 fn sample_segwit_tx() -> Transaction {
423 let mut tx = Transaction::new(2);
424 tx.inputs.push(TxIn {
425 previous_output: OutPoint {
426 txid: [0x01; 32],
427 vout: 0,
428 },
429 script_sig: vec![],
430 sequence: 0xFFFFFFFF,
431 });
432 tx.outputs.push(TxOut {
433 value: 49_000,
434 script_pubkey: {
435 let mut spk = vec![0x00, 0x14];
436 spk.extend_from_slice(&[0xAA; 20]);
437 spk
438 },
439 });
440 tx
441 }
442
443 #[test]
444 fn test_segwit_sighash_deterministic() {
445 let tx = sample_segwit_tx();
446 let prev = PrevOut {
447 script_code: p2wpkh_script_code(&[0xBB; 20]),
448 value: 50_000,
449 };
450 let h1 = segwit_v0_sighash(&tx, 0, &prev, SighashType::All).unwrap();
451 let h2 = segwit_v0_sighash(&tx, 0, &prev, SighashType::All).unwrap();
452 assert_eq!(h1, h2);
453 }
454
455 #[test]
456 fn test_segwit_sighash_different_types() {
457 let tx = sample_segwit_tx();
458 let prev = PrevOut {
459 script_code: p2wpkh_script_code(&[0xBB; 20]),
460 value: 50_000,
461 };
462 let h_all = segwit_v0_sighash(&tx, 0, &prev, SighashType::All).unwrap();
463 let h_none = segwit_v0_sighash(&tx, 0, &prev, SighashType::None).unwrap();
464 assert_ne!(h_all, h_none);
465 }
466
467 #[test]
468 fn test_segwit_sighash_out_of_range() {
469 let tx = sample_segwit_tx();
470 let prev = PrevOut {
471 script_code: p2wpkh_script_code(&[0xBB; 20]),
472 value: 50_000,
473 };
474 assert!(segwit_v0_sighash(&tx, 5, &prev, SighashType::All).is_err());
475 }
476
477 #[test]
478 fn test_taproot_sighash_deterministic() {
479 let tx = sample_segwit_tx();
480 let prevouts = vec![TxOut {
481 value: 50_000,
482 script_pubkey: {
483 let mut spk = vec![0x51, 0x20];
484 spk.extend_from_slice(&[0xCC; 32]);
485 spk
486 },
487 }];
488 let h1 = taproot_key_path_sighash(&tx, 0, &prevouts, SighashType::Default).unwrap();
489 let h2 = taproot_key_path_sighash(&tx, 0, &prevouts, SighashType::Default).unwrap();
490 assert_eq!(h1, h2);
491 }
492
493 #[test]
494 fn test_taproot_sighash_different_from_segwit() {
495 let tx = sample_segwit_tx();
496 let prev = PrevOut {
497 script_code: p2wpkh_script_code(&[0xBB; 20]),
498 value: 50_000,
499 };
500 let prevouts = vec![TxOut {
501 value: 50_000,
502 script_pubkey: {
503 let mut spk = vec![0x51, 0x20];
504 spk.extend_from_slice(&[0xCC; 32]);
505 spk
506 },
507 }];
508 let h_segwit = segwit_v0_sighash(&tx, 0, &prev, SighashType::All).unwrap();
509 let h_taproot = taproot_key_path_sighash(&tx, 0, &prevouts, SighashType::Default).unwrap();
510 assert_ne!(h_segwit, h_taproot);
511 }
512
513 #[test]
514 fn test_taproot_sighash_mismatched_prevouts() {
515 let tx = sample_segwit_tx();
516 assert!(taproot_key_path_sighash(&tx, 0, &[], SighashType::Default).is_err());
518 }
519
520 #[test]
521 fn test_p2wpkh_script_code_structure() {
522 let hash = [0xAA; 20];
523 let code = p2wpkh_script_code(&hash);
524 assert_eq!(code.len(), 25);
525 assert_eq!(code[0], 0x76); assert_eq!(code[1], 0xa9); assert_eq!(code[2], 0x14); assert_eq!(&code[3..23], &hash);
529 assert_eq!(code[23], 0x88); assert_eq!(code[24], 0xac); }
532
533 #[test]
536 fn test_script_path_sighash_deterministic() {
537 let tx = sample_segwit_tx();
538 let prevouts = vec![TxOut {
539 value: 50_000,
540 script_pubkey: {
541 let mut spk = vec![0x51, 0x20];
542 spk.extend_from_slice(&[0xCC; 32]);
543 spk
544 },
545 }];
546 let leaf_hash = [0xDD; 32];
547 let h1 = taproot_script_path_sighash(
548 &tx,
549 0,
550 &prevouts,
551 SighashType::Default,
552 &leaf_hash,
553 0xFFFFFFFF,
554 )
555 .unwrap();
556 let h2 = taproot_script_path_sighash(
557 &tx,
558 0,
559 &prevouts,
560 SighashType::Default,
561 &leaf_hash,
562 0xFFFFFFFF,
563 )
564 .unwrap();
565 assert_eq!(h1, h2);
566 }
567
568 #[test]
569 fn test_script_path_different_from_key_path() {
570 let tx = sample_segwit_tx();
571 let prevouts = vec![TxOut {
572 value: 50_000,
573 script_pubkey: {
574 let mut spk = vec![0x51, 0x20];
575 spk.extend_from_slice(&[0xCC; 32]);
576 spk
577 },
578 }];
579 let leaf_hash = [0xDD; 32];
580 let h_key = taproot_key_path_sighash(&tx, 0, &prevouts, SighashType::Default).unwrap();
581 let h_script = taproot_script_path_sighash(
582 &tx,
583 0,
584 &prevouts,
585 SighashType::Default,
586 &leaf_hash,
587 0xFFFFFFFF,
588 )
589 .unwrap();
590 assert_ne!(h_key, h_script);
591 }
592
593 #[test]
594 fn test_script_path_different_leaf_hashes() {
595 let tx = sample_segwit_tx();
596 let prevouts = vec![TxOut {
597 value: 50_000,
598 script_pubkey: {
599 let mut spk = vec![0x51, 0x20];
600 spk.extend_from_slice(&[0xCC; 32]);
601 spk
602 },
603 }];
604 let h1 = taproot_script_path_sighash(
605 &tx,
606 0,
607 &prevouts,
608 SighashType::Default,
609 &[0xAA; 32],
610 0xFFFFFFFF,
611 )
612 .unwrap();
613 let h2 = taproot_script_path_sighash(
614 &tx,
615 0,
616 &prevouts,
617 SighashType::Default,
618 &[0xBB; 32],
619 0xFFFFFFFF,
620 )
621 .unwrap();
622 assert_ne!(h1, h2);
623 }
624
625 #[test]
626 fn test_script_path_codesep_affects_hash() {
627 let tx = sample_segwit_tx();
628 let prevouts = vec![TxOut {
629 value: 50_000,
630 script_pubkey: {
631 let mut spk = vec![0x51, 0x20];
632 spk.extend_from_slice(&[0xCC; 32]);
633 spk
634 },
635 }];
636 let leaf = [0xDD; 32];
637 let h1 =
638 taproot_script_path_sighash(&tx, 0, &prevouts, SighashType::Default, &leaf, 0xFFFFFFFF)
639 .unwrap();
640 let h2 =
641 taproot_script_path_sighash(&tx, 0, &prevouts, SighashType::Default, &leaf, 5).unwrap();
642 assert_ne!(h1, h2, "codesep_pos should affect the sighash");
643 }
644}