1use std::borrow::Cow;
4
5use crate::error::Result;
6use crate::types::*;
7use crate::types::{ByteString, Hash};
8use crate::witness;
9use blvm_spec_lock::spec_locked;
10
11pub const TAPROOT_LEAF_VERSION_TAPSCRIPT: u8 = 0xc0;
13
14pub use crate::witness::Witness;
18
19use crate::opcodes::OP_1;
20
21pub const TAPROOT_SCRIPT_PREFIX: u8 = OP_1;
23
24#[spec_locked("11.2.1", "ValidateTaprootScript")]
26pub fn validate_taproot_script(script: &ByteString) -> Result<bool> {
27 use crate::constants::TAPROOT_SCRIPT_LENGTH;
28
29 if script.len() != TAPROOT_SCRIPT_LENGTH {
31 return Ok(false);
32 }
33
34 if script[0] != TAPROOT_SCRIPT_PREFIX {
35 return Ok(false);
36 }
37
38 Ok(true)
40}
41
42#[spec_locked("11.2.1", "ExtractTaprootOutputKey")]
44pub fn extract_taproot_output_key(script: &ByteString) -> Result<Option<[u8; 32]>> {
45 if !validate_taproot_script(script)? {
46 return Ok(None);
47 }
48
49 let mut output_key = [0u8; 32];
50 output_key.copy_from_slice(&script[1..33]);
51 Ok(Some(output_key))
52}
53
54#[spec_locked("11.2.2", "ComputeTaprootTweak")]
60pub fn compute_taproot_tweak(internal_pubkey: &[u8; 32], merkle_root: &Hash) -> Result<[u8; 32]> {
61 crate::secp256k1_backend::taproot_output_key(internal_pubkey, merkle_root)
62}
63
64#[spec_locked("11.2.2", "ValidateTaprootKeyAggregation")]
69pub fn validate_taproot_key_aggregation(
70 internal_pubkey: &[u8; 32],
71 merkle_root: &Hash,
72 output_key: &[u8; 32],
73 expected_parity: u8,
74) -> Result<bool> {
75 let (computed_key, actual_parity) =
76 crate::secp256k1_backend::taproot_output_key_with_parity(internal_pubkey, merkle_root)?;
77 Ok(computed_key == *output_key && actual_parity == expected_parity)
78}
79
80#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
82pub fn validate_taproot_script_path(
83 script: &ByteString,
84 merkle_proof: &[Hash],
85 merkle_root: &Hash,
86) -> Result<bool> {
87 validate_taproot_script_path_with_leaf_version(
88 script,
89 merkle_proof,
90 merkle_root,
91 TAPROOT_LEAF_VERSION_TAPSCRIPT,
92 )
93}
94
95#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
97pub fn validate_taproot_script_path_with_leaf_version(
98 script: &ByteString,
99 merkle_proof: &[Hash],
100 merkle_root: &Hash,
101 leaf_version: u8,
102) -> Result<bool> {
103 let computed_root = compute_script_merkle_root(script, merkle_proof, leaf_version)?;
104 Ok(computed_root == *merkle_root)
105}
106
107#[spec_locked("11.2.3", "ComputeScriptMerkleRoot")]
109pub fn compute_script_merkle_root(
110 script: &ByteString,
111 proof: &[Hash],
112 leaf_version: u8,
113) -> Result<Hash> {
114 let mut current_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, script);
115
116 for proof_hash in proof {
117 let (left, right) = if current_hash < *proof_hash {
118 (current_hash, *proof_hash)
119 } else {
120 (*proof_hash, current_hash)
121 };
122 current_hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
123 }
124
125 Ok(current_hash)
126}
127
128pub const TAPROOT_ANNEX_TAG: u8 = 0x50;
130
131#[spec_locked("11.2.4", "StripTaprootAnnex")]
138pub fn strip_taproot_annex(witness: &Witness) -> (Cow<'_, Witness>, Option<Hash>) {
139 if witness.len() >= 2
140 && witness
141 .last()
142 .and_then(|a| a.first())
143 .is_some_and(|&b| b == TAPROOT_ANNEX_TAG)
144 {
145 let annex = &witness[witness.len() - 1];
146 let annex_hash = compute_taproot_annex_sighash_hash(annex);
147 let stripped = witness[..witness.len() - 1].to_vec();
148 return (Cow::Owned(stripped), Some(annex_hash));
149 }
150 (Cow::Borrowed(witness), None)
151}
152
153fn compute_taproot_annex_sighash_hash(annex: &[u8]) -> Hash {
155 use sha2::{Digest, Sha256};
156 let mut buf = encode_varint(annex.len() as u64);
157 buf.extend_from_slice(annex);
158 let digest = Sha256::digest(&buf);
159 let mut out = [0u8; 32];
160 out.copy_from_slice(&digest);
161 out
162}
163
164#[derive(Debug)]
166pub struct TaprootControlBlock {
167 pub leaf_version: u8,
168 pub internal_pubkey: [u8; 32],
169 pub merkle_proof: Vec<Hash>,
170}
171
172pub fn parse_taproot_script_path_witness(
178 witness: &Witness,
179 output_key: &[u8; 32],
180) -> Result<Option<(ByteString, Vec<ByteString>, TaprootControlBlock)>> {
181 if witness.len() < 2 {
182 return Ok(None);
183 }
184
185 let Some(control_block) = witness.last() else {
186 return Ok(None);
187 };
188 if control_block.len() < 33 || (control_block.len() - 33) % 32 != 0 {
189 return Ok(None);
190 }
191
192 let leaf_version = control_block[0] & 0xfe;
195 let parity = control_block[0] & 0x01;
196 let mut internal_pubkey = [0u8; 32];
197 internal_pubkey.copy_from_slice(&control_block[1..33]);
198 let merkle_proof: Vec<Hash> = control_block[33..]
199 .chunks_exact(32)
200 .map(|c| {
201 let mut h = [0u8; 32];
202 h.copy_from_slice(c);
203 h
204 })
205 .collect();
206
207 let script_idx = if witness.len() >= 3 {
208 let maybe_annex = &witness[witness.len() - 2];
209 if maybe_annex.first() == Some(&0x50) {
210 witness.len() - 3
211 } else {
212 witness.len() - 2
213 }
214 } else {
215 witness.len() - 2
216 };
217
218 let tapscript = witness[script_idx].clone();
219 let stack_items: Vec<ByteString> = witness[..script_idx].to_vec();
220
221 let merkle_root = match compute_script_merkle_root(&tapscript, &merkle_proof, leaf_version) {
222 Ok(root) => root,
223 Err(_) => return Ok(None),
224 };
225 let valid = match validate_taproot_key_aggregation(
227 &internal_pubkey,
228 &merkle_root,
229 output_key,
230 parity,
231 ) {
232 Ok(v) => v,
233 Err(_) => return Ok(None),
234 };
235 if !valid {
236 return Ok(None);
237 }
238
239 Ok(Some((
240 tapscript,
241 stack_items,
242 TaprootControlBlock {
243 leaf_version, internal_pubkey,
245 merkle_proof,
246 },
247 )))
248}
249
250#[spec_locked("11.2.1", "IsTaprootOutput")]
254#[blvm_spec_lock::ensures(result == false || output.script_pubkey.len() == 34)]
255pub fn is_taproot_output(output: &TransactionOutput) -> bool {
256 validate_taproot_script(&output.script_pubkey).unwrap_or(false)
257}
258
259#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
261pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
262 for output in &tx.outputs {
264 if is_taproot_output(output) {
265 if !validate_taproot_script(&output.script_pubkey)? {
267 return Ok(false);
268 }
269 }
270 }
271
272 if let Some(w) = witness {
276 let is_script_path = w.len() >= 2;
277 if !witness::validate_taproot_witness_structure(w, is_script_path)? {
278 return Ok(false);
279 }
280 }
281
282 Ok(true)
283}
284
285fn bip341_precompute(
290 tx: &Transaction,
291 prevout_values: &[i64],
292 prevout_script_pubkeys: &[&[u8]],
293) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
294 use sha2::{Digest, Sha256};
295
296 let mut prevouts_data = Vec::new();
297 let mut amounts_data = Vec::new();
298 let mut scriptpubkeys_data = Vec::new();
299 let mut sequences_data = Vec::new();
300 for (i, input) in tx.inputs.iter().enumerate() {
301 prevouts_data.extend_from_slice(&input.prevout.hash);
302 prevouts_data.extend_from_slice(&input.prevout.index.to_le_bytes());
303 amounts_data.extend_from_slice(&(prevout_values[i] as u64).to_le_bytes());
307 let spk = prevout_script_pubkeys[i];
308 scriptpubkeys_data.extend_from_slice(&encode_varint(spk.len() as u64));
309 scriptpubkeys_data.extend_from_slice(spk);
310 sequences_data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
311 }
312 let mut outputs_data = Vec::new();
313 for output in &tx.outputs {
314 outputs_data.extend_from_slice(&(output.value as u64).to_le_bytes());
315 outputs_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
316 outputs_data.extend_from_slice(&output.script_pubkey);
317 }
318
319 (
320 Sha256::digest(&prevouts_data).into(),
321 Sha256::digest(&amounts_data).into(),
322 Sha256::digest(&scriptpubkeys_data).into(),
323 Sha256::digest(&sequences_data).into(),
324 Sha256::digest(&outputs_data).into(),
325 )
326}
327
328#[spec_locked("11.2.6", "ComputeTaprootSignatureHash")]
339pub fn compute_taproot_signature_hash(
340 tx: &Transaction,
341 input_index: usize,
342 prevout_values: &[i64],
343 prevout_script_pubkeys: &[&[u8]],
344 sighash_type: u8,
345 annex_hash: Option<&Hash>,
346) -> Result<Hash> {
347 use sha2::{Digest, Sha256};
348
349 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
352 return Err(crate::error::ConsensusError::InvalidSignature(
353 "Invalid Taproot sighash type".into(),
354 ));
355 }
356
357 if prevout_values.len() < tx.inputs.len() || prevout_script_pubkeys.len() < tx.inputs.len() {
361 return Err(crate::error::ConsensusError::InvalidSignature(
362 "prevout_values/prevout_script_pubkeys shorter than tx.inputs β cannot compute sighash"
363 .into(),
364 ));
365 }
366
367 let input_type = sighash_type & 0x80; let output_type = if sighash_type == 0x00 {
369 0x01
370 } else {
371 sighash_type & 0x03
372 }; let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
375 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
376
377 let mut sigmsg = Vec::with_capacity(180);
378
379 sigmsg.push(0x00u8);
381 sigmsg.push(sighash_type);
383 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
385 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
387
388 if input_type != 0x80 {
389 sigmsg.extend_from_slice(&sha_prevouts);
391 sigmsg.extend_from_slice(&sha_amounts);
392 sigmsg.extend_from_slice(&sha_scriptpubkeys);
393 sigmsg.extend_from_slice(&sha_sequences);
394 }
395 if output_type == 0x01 {
396 sigmsg.extend_from_slice(&sha_outputs);
398 }
399
400 let spend_type = if annex_hash.is_some() { 0x01u8 } else { 0x00u8 };
402 sigmsg.push(spend_type);
403
404 if input_type == 0x80 {
405 let input = tx.inputs.get(input_index).ok_or_else(|| {
407 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
408 })?;
409 sigmsg.extend_from_slice(&input.prevout.hash);
410 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
411 let amount = *prevout_values.get(input_index).ok_or_else(|| {
413 crate::error::ConsensusError::InvalidSignature(
414 "prevout_values index out of range for ANYONECANPAY".into(),
415 )
416 })? as u64;
417 sigmsg.extend_from_slice(&amount.to_le_bytes());
418 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
419 crate::error::ConsensusError::InvalidSignature(
420 "prevout_script_pubkeys index out of range for ANYONECANPAY".into(),
421 )
422 })?;
423 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
424 sigmsg.extend_from_slice(spk);
425 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
426 } else {
427 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
429 }
430
431 if let Some(h) = annex_hash {
432 sigmsg.extend_from_slice(h);
433 }
434
435 if output_type == 0x03 {
436 if let Some(output) = tx.outputs.get(input_index) {
438 let mut out_data = Vec::new();
439 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
440 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
441 out_data.extend_from_slice(&output.script_pubkey);
442 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
443 }
444 }
445
446 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
447}
448
449#[spec_locked("11.2.7", "ComputeTapscriptSignatureHash")]
454pub fn compute_tapscript_signature_hash(
455 tx: &Transaction,
456 input_index: usize,
457 prevout_values: &[i64],
458 prevout_script_pubkeys: &[&[u8]],
459 tapscript: &[u8],
460 leaf_version: u8,
461 codesep_pos: u32,
462 sighash_type: u8,
463 annex_hash: Option<&Hash>,
464) -> Result<Hash> {
465 use sha2::{Digest, Sha256};
466
467 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
468 return Err(crate::error::ConsensusError::InvalidSignature(
469 "Invalid Tapscript sighash type".into(),
470 ));
471 }
472
473 let input_type = sighash_type & 0x80;
474 let output_type = if sighash_type == 0x00 {
475 0x01
476 } else {
477 sighash_type & 0x03
478 };
479
480 let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
481 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
482
483 let mut sigmsg = Vec::with_capacity(250);
484
485 sigmsg.push(0x00u8);
487 sigmsg.push(sighash_type);
489 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
491 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
493
494 if input_type != 0x80 {
495 sigmsg.extend_from_slice(&sha_prevouts);
496 sigmsg.extend_from_slice(&sha_amounts);
497 sigmsg.extend_from_slice(&sha_scriptpubkeys);
498 sigmsg.extend_from_slice(&sha_sequences);
499 }
500 if output_type == 0x01 {
501 sigmsg.extend_from_slice(&sha_outputs);
502 }
503
504 let spend_type = if annex_hash.is_some() { 0x03u8 } else { 0x02u8 };
506 sigmsg.push(spend_type);
507
508 if input_type == 0x80 {
509 let input = tx.inputs.get(input_index).ok_or_else(|| {
510 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
511 })?;
512 sigmsg.extend_from_slice(&input.prevout.hash);
513 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
514 let amount = *prevout_values.get(input_index).ok_or_else(|| {
515 crate::error::ConsensusError::InvalidSignature(
516 "prevout_values index out of range for script-path ANYONECANPAY".into(),
517 )
518 })? as u64;
519 sigmsg.extend_from_slice(&amount.to_le_bytes());
520 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
521 crate::error::ConsensusError::InvalidSignature(
522 "prevout_script_pubkeys index out of range for script-path ANYONECANPAY".into(),
523 )
524 })?;
525 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
526 sigmsg.extend_from_slice(spk);
527 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
528 } else {
529 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
530 }
531
532 if let Some(h) = annex_hash {
533 sigmsg.extend_from_slice(h);
534 }
535
536 if output_type == 0x03 {
537 if let Some(output) = tx.outputs.get(input_index) {
538 let mut out_data = Vec::new();
539 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
540 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
541 out_data.extend_from_slice(&output.script_pubkey);
542 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
543 }
544 }
545
546 let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
548 sigmsg.extend_from_slice(&tapleaf_hash);
549 sigmsg.push(0x00u8); sigmsg.extend_from_slice(&codesep_pos.to_le_bytes());
551
552 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
553}
554
555fn encode_varint(value: u64) -> Vec<u8> {
557 if value < 0xfd {
558 vec![value as u8]
559 } else if value <= 0xffff {
560 let mut result = vec![0xfd];
561 result.extend_from_slice(&(value as u16).to_le_bytes());
562 result
563 } else if value <= 0xffffffff {
564 let mut result = vec![0xfe];
565 result.extend_from_slice(&(value as u32).to_le_bytes());
566 result
567 } else {
568 let mut result = vec![0xff];
569 result.extend_from_slice(&value.to_le_bytes());
570 result
571 }
572}
573
574#[cfg(test)]
575mod tests {
576 use super::*;
577
578 #[test]
579 fn test_validate_taproot_script_valid() {
580 let script = create_taproot_script(&[1u8; 32]);
581 assert!(validate_taproot_script(&script).unwrap());
582 }
583
584 #[test]
585 fn test_validate_taproot_script_invalid_length() {
586 let script = vec![0x51, 0x20]; assert!(!validate_taproot_script(&script).unwrap());
588 }
589
590 #[test]
591 fn test_validate_taproot_script_invalid_prefix() {
592 let mut script = vec![0x52]; script.extend_from_slice(&[1u8; 32]);
594 assert!(!validate_taproot_script(&script).unwrap());
595 }
596
597 #[test]
598 fn test_extract_taproot_output_key() {
599 let expected_key = [1u8; 32];
600 let script = create_taproot_script(&expected_key);
601
602 let extracted_key = extract_taproot_output_key(&script).unwrap();
603 assert_eq!(extracted_key, Some(expected_key));
604 }
605
606 #[test]
607 fn test_compute_taproot_tweak() {
608 let internal_pubkey = [
610 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
611 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
612 0x16, 0xf8, 0x17, 0x98,
613 ];
614 let merkle_root = [2u8; 32];
615
616 let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
617 assert_eq!(tweak.len(), 32);
618 }
619
620 #[test]
621 fn test_validate_taproot_key_aggregation() {
622 let internal_pubkey = [
624 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
625 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
626 0x16, 0xf8, 0x17, 0x98,
627 ];
628 let merkle_root = [2u8; 32];
629 let (output_key, parity) = crate::secp256k1_backend::taproot_output_key_with_parity(
630 &internal_pubkey,
631 &merkle_root,
632 )
633 .unwrap();
634
635 assert!(
636 validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, &output_key, parity)
637 .unwrap()
638 );
639 }
640
641 #[test]
642 fn test_validate_taproot_script_path() {
643 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
645 let merkle_root =
646 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
647 .unwrap();
648
649 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
650 }
651
652 #[test]
653 fn test_is_taproot_output() {
654 let output = TransactionOutput {
655 value: 1000,
656 script_pubkey: create_taproot_script(&[1u8; 32]),
657 };
658
659 assert!(is_taproot_output(&output));
660 }
661
662 #[test]
663 fn test_validate_taproot_transaction() {
664 let tx = Transaction {
665 version: 1,
666 inputs: vec![TransactionInput {
667 prevout: OutPoint {
668 hash: [0; 32],
669 index: 0,
670 },
671 script_sig: vec![],
672 sequence: 0xffffffff,
673 }]
674 .into(),
675 outputs: vec![TransactionOutput {
676 value: 1000,
677 script_pubkey: create_taproot_script(&[1u8; 32]),
678 }]
679 .into(),
680 lock_time: 0,
681 };
682
683 let witness = Some(vec![vec![0u8; 64]]);
685 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
686 }
687
688 #[test]
689 fn test_compute_taproot_signature_hash() {
690 let tx = Transaction {
691 version: 1,
692 inputs: vec![TransactionInput {
693 prevout: OutPoint {
694 hash: [0; 32],
695 index: 0,
696 },
697 script_sig: vec![],
698 sequence: 0xffffffff,
699 }]
700 .into(),
701 outputs: vec![TransactionOutput {
702 value: 1000,
703 script_pubkey: vec![0x51],
704 }]
705 .into(),
706 lock_time: 0,
707 };
708
709 let prevouts = [TransactionOutput {
710 value: 2000,
711 script_pubkey: create_taproot_script(&[1u8; 32]),
712 }];
713 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
714 let psp: Vec<&[u8]> = prevouts
715 .iter()
716 .map(|p| p.script_pubkey.as_slice())
717 .collect();
718 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None).unwrap();
719 assert_eq!(sig_hash.len(), 32);
720 }
721
722 #[test]
723 fn test_compute_taproot_signature_hash_invalid_input_index() {
724 let tx = Transaction {
725 version: 1,
726 inputs: vec![TransactionInput {
727 prevout: OutPoint {
728 hash: [0; 32],
729 index: 0,
730 },
731 script_sig: vec![],
732 sequence: 0xffffffff,
733 }]
734 .into(),
735 outputs: vec![TransactionOutput {
736 value: 1000,
737 script_pubkey: vec![0x51],
738 }]
739 .into(),
740 lock_time: 0,
741 };
742
743 let prevouts = [TransactionOutput {
744 value: 2000,
745 script_pubkey: create_taproot_script(&[1u8; 32]),
746 }];
747 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
748 let psp: Vec<&[u8]> = prevouts
749 .iter()
750 .map(|p| p.script_pubkey.as_slice())
751 .collect();
752 let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01, None).unwrap();
754 assert_eq!(sig_hash.len(), 32);
755 }
756
757 #[test]
758 fn test_compute_taproot_signature_hash_empty_prevouts() {
759 let tx = Transaction {
760 version: 1,
761 inputs: vec![TransactionInput {
762 prevout: OutPoint {
763 hash: [0; 32],
764 index: 0,
765 },
766 script_sig: vec![],
767 sequence: 0xffffffff,
768 }]
769 .into(),
770 outputs: vec![TransactionOutput {
771 value: 1000,
772 script_pubkey: vec![0x51],
773 }]
774 .into(),
775 lock_time: 0,
776 };
777
778 let prevouts: Vec<TransactionOutput> = vec![];
781 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
782 let psp: Vec<&[u8]> = prevouts
783 .iter()
784 .map(|p| p.script_pubkey.as_slice())
785 .collect();
786 let result = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None);
787 assert!(
788 result.is_err(),
789 "empty prevouts shorter than tx.inputs must return Err, not silently use zeros"
790 );
791 }
792
793 #[test]
794 fn test_compute_taproot_tweak_invalid_pubkey() {
795 let invalid_pubkey = [0u8; 32]; let merkle_root = [2u8; 32];
797
798 let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
799 assert!(result.is_err());
800 }
801
802 #[test]
803 fn test_validate_taproot_key_aggregation_invalid() {
804 let internal_pubkey = [
805 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
806 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
807 0x16, 0xf8, 0x17, 0x98,
808 ];
809 let merkle_root = [2u8; 32];
810 let wrong_output_key = [3u8; 32]; assert!(
813 !validate_taproot_key_aggregation(
814 &internal_pubkey,
815 &merkle_root,
816 &wrong_output_key,
817 0, )
819 .unwrap()
820 );
821 }
822
823 #[test]
824 fn test_validate_taproot_script_path_invalid() {
825 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
827 let wrong_merkle_root = [5u8; 32]; assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
830 }
831
832 #[test]
833 fn test_validate_taproot_script_path_empty_proof() {
834 let script = vec![0x51, 0x52]; let merkle_proof = vec![];
836 let merkle_root =
837 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
838 .unwrap();
839
840 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
841 }
842
843 #[test]
844 fn test_tap_leaf_hash() {
845 let script = vec![0x51, 0x52];
846 let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
847
848 assert_eq!(hash.len(), 32);
849
850 let script2 = vec![0x53, 0x54];
851 let hash2 =
852 crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
853 assert_ne!(hash, hash2);
854 }
855
856 #[test]
857 fn test_tap_branch_hash() {
858 let left = [1u8; 32];
859 let right = [2u8; 32];
860 let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
861
862 assert_eq!(hash.len(), 32);
863
864 let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
865 assert_ne!(hash, hash2);
866 }
867
868 #[test]
869 fn test_encode_varint_small() {
870 let encoded = encode_varint(0xfc);
871 assert_eq!(encoded, vec![0xfc]);
872 }
873
874 #[test]
875 fn test_encode_varint_medium() {
876 let encoded = encode_varint(0x1000);
877 assert_eq!(encoded.len(), 3);
878 assert_eq!(encoded[0], 0xfd);
879 }
880
881 #[test]
882 fn test_encode_varint_large() {
883 let encoded = encode_varint(0x1000000);
884 assert_eq!(encoded.len(), 5);
885 assert_eq!(encoded[0], 0xfe);
886 }
887
888 #[test]
889 fn test_encode_varint_huge() {
890 let encoded = encode_varint(0x1000000000000000);
891 assert_eq!(encoded.len(), 9);
892 assert_eq!(encoded[0], 0xff);
893 }
894
895 #[test]
896 fn test_extract_taproot_output_key_invalid_script() {
897 let script = vec![0x52, 0x20]; let result = extract_taproot_output_key(&script).unwrap();
899 assert!(result.is_none());
900 }
901
902 #[test]
903 fn test_is_taproot_output_false() {
904 let output = TransactionOutput {
905 value: 1000,
906 script_pubkey: vec![0x52, 0x20], };
908
909 assert!(!is_taproot_output(&output));
910 }
911
912 #[test]
913 fn test_validate_taproot_transaction_no_taproot_outputs() {
914 let tx = Transaction {
915 version: 1,
916 inputs: vec![TransactionInput {
917 prevout: OutPoint {
918 hash: [0; 32],
919 index: 0,
920 },
921 script_sig: vec![],
922 sequence: 0xffffffff,
923 }]
924 .into(),
925 outputs: vec![TransactionOutput {
926 value: 1000,
927 script_pubkey: vec![0x52], }]
929 .into(),
930 lock_time: 0,
931 };
932
933 assert!(validate_taproot_transaction(&tx, None).unwrap());
935 }
936
937 #[test]
938 fn test_validate_taproot_transaction_invalid_taproot_output() {
939 let tx = Transaction {
941 version: 1,
942 inputs: vec![TransactionInput {
943 prevout: OutPoint {
944 hash: [0; 32],
945 index: 0,
946 },
947 script_sig: vec![],
948 sequence: 0xffffffff,
949 }]
950 .into(),
951 outputs: vec![TransactionOutput {
952 value: 1000,
953 script_pubkey: create_taproot_script(&[1u8; 32]),
954 }]
955 .into(),
956 lock_time: 0,
957 };
958
959 let witness = Some(vec![vec![0u8; 64]]);
961 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
962 }
963
964 fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
966 let mut script = vec![TAPROOT_SCRIPT_PREFIX];
967 script.extend_from_slice(output_key);
968 script.push(0x00); script
970 }
971}
972
973#[cfg(test)]
974mod property_tests {
975 use super::*;
976 use proptest::prelude::*;
977
978 proptest! {
983 #[test]
984 fn prop_validate_taproot_script_deterministic(
985 script in prop::collection::vec(any::<u8>(), 0..50)
986 ) {
987 let result1 = validate_taproot_script(&script).unwrap();
988 let result2 = validate_taproot_script(&script).unwrap();
989
990 assert_eq!(result1, result2);
991 }
992 }
993
994 proptest! {
1000 #[test]
1001 fn prop_extract_taproot_output_key_correct(
1002 script in prop::collection::vec(any::<u8>(), 0..50)
1003 ) {
1004 let extracted_key = extract_taproot_output_key(&script).unwrap();
1005 let is_valid = validate_taproot_script(&script).unwrap();
1006
1007 if is_valid {
1008 assert!(extracted_key.is_some());
1009 let key = extracted_key.unwrap();
1010 assert_eq!(key.len(), 32);
1011 } else {
1012 assert!(extracted_key.is_none());
1013 }
1014 }
1015 }
1016
1017 proptest! {
1023 #[test]
1024 fn prop_taproot_key_aggregation_deterministic(
1025 internal_pubkey in create_pubkey_strategy(),
1026 merkle_root in create_hash_strategy()
1027 ) {
1028 let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1029 let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1030
1031 assert_eq!(result1.is_ok(), result2.is_ok());
1032 if result1.is_ok() && result2.is_ok() {
1033 assert_eq!(result1.unwrap(), result2.unwrap());
1034 }
1035 }
1036 }
1037
1038 proptest! {
1044 #[test]
1045 fn prop_validate_taproot_script_path_deterministic(
1046 script in prop::collection::vec(any::<u8>(), 0..20),
1047 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
1048 merkle_root in create_hash_strategy()
1049 ) {
1050 let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1051 let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1052
1053 assert_eq!(result1.is_ok(), result2.is_ok());
1054 if result1.is_ok() && result2.is_ok() {
1055 assert_eq!(result1.unwrap(), result2.unwrap());
1056 }
1057 }
1058 }
1059
1060 proptest! {
1066 #[test]
1067 fn prop_compute_taproot_signature_hash_deterministic(
1068 tx in create_transaction_strategy(),
1069 input_index in 0..10usize,
1070 prevouts in prop::collection::vec(create_output_strategy(), 0..5),
1071 sighash_type in any::<u8>()
1072 ) {
1073 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
1074 let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
1075 let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1076 let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1077
1078 assert_eq!(result1.is_ok(), result2.is_ok());
1079 if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
1080 assert_eq!(hash1, hash2);
1081 assert_eq!(hash1.len(), 32);
1082 }
1083
1084 if let Ok(ref hash) = result1 {
1086 assert_eq!(hash.len(), 32);
1087 }
1088 }
1089 }
1090
1091 proptest! {
1096 #[test]
1097 fn prop_is_taproot_output_consistent(
1098 output in create_output_strategy()
1099 ) {
1100 let is_taproot = is_taproot_output(&output);
1101 let _ = is_taproot;
1103 }
1104 }
1105
1106 proptest! {
1111 #[test]
1112 fn prop_validate_taproot_transaction_deterministic(
1113 tx in create_transaction_strategy()
1114 ) {
1115 let result1 = validate_taproot_transaction(&tx, None).unwrap();
1116 let result2 = validate_taproot_transaction(&tx, None).unwrap();
1117
1118 assert_eq!(result1, result2);
1119 }
1120 }
1121
1122 proptest! {
1124 #[test]
1125 fn prop_tap_leaf_hash_deterministic(
1126 script in prop::collection::vec(any::<u8>(), 0..20)
1127 ) {
1128 let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1129 let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1130
1131 assert_eq!(hash1, hash2);
1132 assert_eq!(hash1.len(), 32);
1133 }
1134 }
1135
1136 proptest! {
1138 #[test]
1139 fn prop_tap_branch_hash_deterministic(
1140 left in create_hash_strategy(),
1141 right in create_hash_strategy()
1142 ) {
1143 let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1144 let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1145
1146 assert_eq!(hash1, hash2);
1147 assert_eq!(hash1.len(), 32);
1148 }
1149 }
1150
1151 proptest! {
1156 #[test]
1157 fn prop_encode_varint_deterministic(
1158 value in 0..u64::MAX
1159 ) {
1160 let encoded1 = encode_varint(value);
1161 let encoded2 = encode_varint(value);
1162
1163 assert_eq!(encoded1, encoded2);
1164
1165 assert!(!encoded1.is_empty());
1167 assert!(encoded1.len() <= 9);
1168 }
1169 }
1170
1171 proptest! {
1176 #[test]
1177 fn prop_encode_varint_preserves_value(
1178 value in 0..1000000u64 ) {
1180 let encoded = encode_varint(value);
1181
1182 match encoded.len() {
1184 1 => {
1185 assert!(value < 0xfd);
1187 assert_eq!(encoded[0], value as u8);
1188 },
1189 3 => {
1190 assert!((0xfd..=0xffff).contains(&value));
1192 assert_eq!(encoded[0], 0xfd);
1193 },
1194 5 => {
1195 assert!(value > 0xffff && value <= 0xffffffff);
1197 assert_eq!(encoded[0], 0xfe);
1198 },
1199 9 => {
1200 assert!(value > 0xffffffff);
1202 assert_eq!(encoded[0], 0xff);
1203 },
1204 _ => panic!("Invalid varint encoding length"),
1205 }
1206 }
1207 }
1208
1209 proptest! {
1216 #[test]
1217 fn prop_validate_taproot_script_path_correct_proof(
1218 script in prop::collection::vec(any::<u8>(), 0..20),
1219 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
1220 ) {
1221 let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
1222 let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
1223
1224 assert!(is_valid);
1225 }
1226 }
1227
1228 fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
1230 (
1231 prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10), )
1234 .prop_map(|(input_data, output_data)| {
1235 let mut inputs = Vec::new();
1236 for (i, _) in input_data.iter().enumerate() {
1237 inputs.push(TransactionInput {
1238 prevout: OutPoint {
1239 hash: [0; 32],
1240 index: i as u32,
1241 },
1242 script_sig: vec![],
1243 sequence: 0xffffffff,
1244 });
1245 }
1246
1247 let mut outputs = Vec::new();
1248 for _ in output_data {
1249 outputs.push(TransactionOutput {
1250 value: 1000,
1251 script_pubkey: vec![0x51],
1252 });
1253 }
1254
1255 Transaction {
1256 version: 1,
1257 inputs: inputs.into(),
1258 outputs: outputs.into(),
1259 lock_time: 0,
1260 }
1261 })
1262 }
1263
1264 fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1265 (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1266 TransactionOutput {
1267 value,
1268 script_pubkey: script,
1269 }
1270 })
1271 }
1272
1273 fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1274 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1275 let mut hash = [0u8; 32];
1276 hash.copy_from_slice(&bytes);
1277 hash
1278 })
1279 }
1280
1281 fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1282 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1283 let mut pubkey = [0u8; 32];
1284 pubkey.copy_from_slice(&bytes);
1285 pubkey
1286 })
1287 }
1288}