1use crate::error::Result;
4use crate::types::*;
5use crate::types::{ByteString, Hash};
6use crate::witness;
7use blvm_spec_lock::spec_locked;
8
9pub const TAPROOT_LEAF_VERSION_TAPSCRIPT: u8 = 0xc0;
11
12pub use crate::witness::Witness;
16
17use crate::opcodes::OP_1;
18
19pub const TAPROOT_SCRIPT_PREFIX: u8 = OP_1;
21
22#[spec_locked("11.2.1", "ValidateTaprootScript")]
24pub fn validate_taproot_script(script: &ByteString) -> Result<bool> {
25 use crate::constants::TAPROOT_SCRIPT_LENGTH;
26
27 if script.len() != TAPROOT_SCRIPT_LENGTH {
29 return Ok(false);
30 }
31
32 if script[0] != TAPROOT_SCRIPT_PREFIX {
33 return Ok(false);
34 }
35
36 Ok(true)
38}
39
40#[spec_locked("11.2.1", "ExtractTaprootOutputKey")]
42pub fn extract_taproot_output_key(script: &ByteString) -> Result<Option<[u8; 32]>> {
43 if !validate_taproot_script(script)? {
44 return Ok(None);
45 }
46
47 let mut output_key = [0u8; 32];
48 output_key.copy_from_slice(&script[1..33]);
49 Ok(Some(output_key))
50}
51
52#[spec_locked("11.2.2", "ComputeTaprootTweak")]
58pub fn compute_taproot_tweak(internal_pubkey: &[u8; 32], merkle_root: &Hash) -> Result<[u8; 32]> {
59 crate::secp256k1_backend::taproot_output_key(internal_pubkey, merkle_root)
60}
61
62#[spec_locked("11.2.2", "ValidateTaprootKeyAggregation")]
67pub fn validate_taproot_key_aggregation(
68 internal_pubkey: &[u8; 32],
69 merkle_root: &Hash,
70 output_key: &[u8; 32],
71 expected_parity: u8,
72) -> Result<bool> {
73 let (computed_key, actual_parity) =
74 crate::secp256k1_backend::taproot_output_key_with_parity(internal_pubkey, merkle_root)?;
75 Ok(computed_key == *output_key && actual_parity == expected_parity)
76}
77
78#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
80pub fn validate_taproot_script_path(
81 script: &ByteString,
82 merkle_proof: &[Hash],
83 merkle_root: &Hash,
84) -> Result<bool> {
85 validate_taproot_script_path_with_leaf_version(
86 script,
87 merkle_proof,
88 merkle_root,
89 TAPROOT_LEAF_VERSION_TAPSCRIPT,
90 )
91}
92
93#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
95pub fn validate_taproot_script_path_with_leaf_version(
96 script: &ByteString,
97 merkle_proof: &[Hash],
98 merkle_root: &Hash,
99 leaf_version: u8,
100) -> Result<bool> {
101 let computed_root = compute_script_merkle_root(script, merkle_proof, leaf_version)?;
102 Ok(computed_root == *merkle_root)
103}
104
105#[spec_locked("11.2.3", "ComputeScriptMerkleRoot")]
107pub fn compute_script_merkle_root(
108 script: &ByteString,
109 proof: &[Hash],
110 leaf_version: u8,
111) -> Result<Hash> {
112 let mut current_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, script);
113
114 for proof_hash in proof {
115 let (left, right) = if current_hash < *proof_hash {
116 (current_hash, *proof_hash)
117 } else {
118 (*proof_hash, current_hash)
119 };
120 current_hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
121 }
122
123 Ok(current_hash)
124}
125
126#[derive(Debug)]
128pub struct TaprootControlBlock {
129 pub leaf_version: u8,
130 pub internal_pubkey: [u8; 32],
131 pub merkle_proof: Vec<Hash>,
132}
133
134pub fn parse_taproot_script_path_witness(
140 witness: &Witness,
141 output_key: &[u8; 32],
142) -> Result<Option<(ByteString, Vec<ByteString>, TaprootControlBlock)>> {
143 if witness.len() < 2 {
144 return Ok(None);
145 }
146
147 let Some(control_block) = witness.last() else {
148 return Ok(None);
149 };
150 if control_block.len() < 33 || (control_block.len() - 33) % 32 != 0 {
151 return Ok(None);
152 }
153
154 let leaf_version = control_block[0] & 0xfe;
157 let parity = control_block[0] & 0x01;
158 let mut internal_pubkey = [0u8; 32];
159 internal_pubkey.copy_from_slice(&control_block[1..33]);
160 let merkle_proof: Vec<Hash> = control_block[33..]
161 .chunks_exact(32)
162 .map(|c| {
163 let mut h = [0u8; 32];
164 h.copy_from_slice(c);
165 h
166 })
167 .collect();
168
169 let script_idx = if witness.len() >= 3 {
170 let maybe_annex = &witness[witness.len() - 2];
171 if maybe_annex.first() == Some(&0x50) {
172 witness.len() - 3
173 } else {
174 witness.len() - 2
175 }
176 } else {
177 witness.len() - 2
178 };
179
180 let tapscript = witness[script_idx].clone();
181 let stack_items: Vec<ByteString> = witness[..script_idx].to_vec();
182
183 let merkle_root = compute_script_merkle_root(&tapscript, &merkle_proof, leaf_version)?;
184 if !validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, output_key, parity)? {
186 return Ok(None);
187 }
188
189 Ok(Some((
190 tapscript,
191 stack_items,
192 TaprootControlBlock {
193 leaf_version, internal_pubkey,
195 merkle_proof,
196 },
197 )))
198}
199
200#[spec_locked("11.2.1", "IsTaprootOutput")]
202pub fn is_taproot_output(output: &TransactionOutput) -> bool {
203 validate_taproot_script(&output.script_pubkey).unwrap_or(false)
204}
205
206#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
208pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
209 for output in &tx.outputs {
211 if is_taproot_output(output) {
212 if !validate_taproot_script(&output.script_pubkey)? {
214 return Ok(false);
215 }
216 }
217 }
218
219 if let Some(w) = witness {
223 let is_script_path = w.len() >= 2;
224 if !witness::validate_taproot_witness_structure(w, is_script_path)? {
225 return Ok(false);
226 }
227 }
228
229 Ok(true)
230}
231
232fn bip341_precompute(
237 tx: &Transaction,
238 prevout_values: &[i64],
239 prevout_script_pubkeys: &[&[u8]],
240) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
241 use sha2::{Digest, Sha256};
242
243 let mut prevouts_data = Vec::new();
244 let mut amounts_data = Vec::new();
245 let mut scriptpubkeys_data = Vec::new();
246 let mut sequences_data = Vec::new();
247 for (i, input) in tx.inputs.iter().enumerate() {
248 prevouts_data.extend_from_slice(&input.prevout.hash);
249 prevouts_data.extend_from_slice(&input.prevout.index.to_le_bytes());
250 amounts_data
251 .extend_from_slice(&(prevout_values.get(i).copied().unwrap_or(0) as u64).to_le_bytes());
252 let spk = prevout_script_pubkeys.get(i).copied().unwrap_or(&[]);
253 scriptpubkeys_data.extend_from_slice(&encode_varint(spk.len() as u64));
254 scriptpubkeys_data.extend_from_slice(spk);
255 sequences_data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
256 }
257 let mut outputs_data = Vec::new();
258 for output in &tx.outputs {
259 outputs_data.extend_from_slice(&(output.value as u64).to_le_bytes());
260 outputs_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
261 outputs_data.extend_from_slice(&output.script_pubkey);
262 }
263
264 (
265 Sha256::digest(&prevouts_data).into(),
266 Sha256::digest(&amounts_data).into(),
267 Sha256::digest(&scriptpubkeys_data).into(),
268 Sha256::digest(&sequences_data).into(),
269 Sha256::digest(&outputs_data).into(),
270 )
271}
272
273#[spec_locked("11.2.6", "ComputeTaprootSignatureHash")]
284pub fn compute_taproot_signature_hash(
285 tx: &Transaction,
286 input_index: usize,
287 prevout_values: &[i64],
288 prevout_script_pubkeys: &[&[u8]],
289 sighash_type: u8,
290) -> Result<Hash> {
291 use sha2::{Digest, Sha256};
292
293 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
296 return Err(crate::error::ConsensusError::InvalidSignature(
297 "Invalid Taproot sighash type".into(),
298 ));
299 }
300
301 let input_type = sighash_type & 0x80; let output_type = if sighash_type == 0x00 {
303 0x01
304 } else {
305 sighash_type & 0x03
306 }; let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
309 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
310
311 let mut sigmsg = Vec::with_capacity(180);
312
313 sigmsg.push(0x00u8);
315 sigmsg.push(sighash_type);
317 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
319 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
321
322 if input_type != 0x80 {
323 sigmsg.extend_from_slice(&sha_prevouts);
325 sigmsg.extend_from_slice(&sha_amounts);
326 sigmsg.extend_from_slice(&sha_scriptpubkeys);
327 sigmsg.extend_from_slice(&sha_sequences);
328 }
329 if output_type == 0x01 {
330 sigmsg.extend_from_slice(&sha_outputs);
332 }
333
334 sigmsg.push(0x00u8);
336
337 if input_type == 0x80 {
338 let input = tx.inputs.get(input_index).ok_or_else(|| {
340 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
341 })?;
342 sigmsg.extend_from_slice(&input.prevout.hash);
343 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
344 let amount = prevout_values.get(input_index).copied().unwrap_or(0) as u64;
345 sigmsg.extend_from_slice(&amount.to_le_bytes());
346 let spk = prevout_script_pubkeys
347 .get(input_index)
348 .copied()
349 .unwrap_or(&[]);
350 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
351 sigmsg.extend_from_slice(spk);
352 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
353 } else {
354 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
356 }
357
358 if output_type == 0x03 {
359 if let Some(output) = tx.outputs.get(input_index) {
361 let mut out_data = Vec::new();
362 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
363 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
364 out_data.extend_from_slice(&output.script_pubkey);
365 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
366 }
367 }
368
369 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
370}
371
372#[spec_locked("11.2.7", "ComputeTapscriptSignatureHash")]
377pub fn compute_tapscript_signature_hash(
378 tx: &Transaction,
379 input_index: usize,
380 prevout_values: &[i64],
381 prevout_script_pubkeys: &[&[u8]],
382 tapscript: &[u8],
383 leaf_version: u8,
384 codesep_pos: u32,
385 sighash_type: u8,
386) -> Result<Hash> {
387 use sha2::{Digest, Sha256};
388
389 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
390 return Err(crate::error::ConsensusError::InvalidSignature(
391 "Invalid Tapscript sighash type".into(),
392 ));
393 }
394
395 let input_type = sighash_type & 0x80;
396 let output_type = if sighash_type == 0x00 {
397 0x01
398 } else {
399 sighash_type & 0x03
400 };
401
402 let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
403 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
404
405 let mut sigmsg = Vec::with_capacity(250);
406
407 sigmsg.push(0x00u8);
409 sigmsg.push(sighash_type);
411 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
413 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
415
416 if input_type != 0x80 {
417 sigmsg.extend_from_slice(&sha_prevouts);
418 sigmsg.extend_from_slice(&sha_amounts);
419 sigmsg.extend_from_slice(&sha_scriptpubkeys);
420 sigmsg.extend_from_slice(&sha_sequences);
421 }
422 if output_type == 0x01 {
423 sigmsg.extend_from_slice(&sha_outputs);
424 }
425
426 sigmsg.push(0x02u8);
428
429 if input_type == 0x80 {
430 let input = tx.inputs.get(input_index).ok_or_else(|| {
431 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
432 })?;
433 sigmsg.extend_from_slice(&input.prevout.hash);
434 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
435 let amount = prevout_values.get(input_index).copied().unwrap_or(0) as u64;
436 sigmsg.extend_from_slice(&amount.to_le_bytes());
437 let spk = prevout_script_pubkeys
438 .get(input_index)
439 .copied()
440 .unwrap_or(&[]);
441 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
442 sigmsg.extend_from_slice(spk);
443 let input = tx.inputs.get(input_index).unwrap();
444 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
445 } else {
446 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
447 }
448
449 if output_type == 0x03 {
450 if let Some(output) = tx.outputs.get(input_index) {
451 let mut out_data = Vec::new();
452 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
453 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
454 out_data.extend_from_slice(&output.script_pubkey);
455 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
456 }
457 }
458
459 let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
461 sigmsg.extend_from_slice(&tapleaf_hash);
462 sigmsg.push(0x00u8); sigmsg.extend_from_slice(&codesep_pos.to_le_bytes());
464
465 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
466}
467
468fn encode_varint(value: u64) -> Vec<u8> {
470 if value < 0xfd {
471 vec![value as u8]
472 } else if value <= 0xffff {
473 let mut result = vec![0xfd];
474 result.extend_from_slice(&(value as u16).to_le_bytes());
475 result
476 } else if value <= 0xffffffff {
477 let mut result = vec![0xfe];
478 result.extend_from_slice(&(value as u32).to_le_bytes());
479 result
480 } else {
481 let mut result = vec![0xff];
482 result.extend_from_slice(&value.to_le_bytes());
483 result
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 #[test]
492 fn test_validate_taproot_script_valid() {
493 let script = create_taproot_script(&[1u8; 32]);
494 assert!(validate_taproot_script(&script).unwrap());
495 }
496
497 #[test]
498 fn test_validate_taproot_script_invalid_length() {
499 let script = vec![0x51, 0x20]; assert!(!validate_taproot_script(&script).unwrap());
501 }
502
503 #[test]
504 fn test_validate_taproot_script_invalid_prefix() {
505 let mut script = vec![0x52]; script.extend_from_slice(&[1u8; 32]);
507 assert!(!validate_taproot_script(&script).unwrap());
508 }
509
510 #[test]
511 fn test_extract_taproot_output_key() {
512 let expected_key = [1u8; 32];
513 let script = create_taproot_script(&expected_key);
514
515 let extracted_key = extract_taproot_output_key(&script).unwrap();
516 assert_eq!(extracted_key, Some(expected_key));
517 }
518
519 #[test]
520 fn test_compute_taproot_tweak() {
521 let internal_pubkey = [
523 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
524 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
525 0x16, 0xf8, 0x17, 0x98,
526 ];
527 let merkle_root = [2u8; 32];
528
529 let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
530 assert_eq!(tweak.len(), 32);
531 }
532
533 #[test]
534 fn test_validate_taproot_key_aggregation() {
535 let internal_pubkey = [
537 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
538 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
539 0x16, 0xf8, 0x17, 0x98,
540 ];
541 let merkle_root = [2u8; 32];
542 let (output_key, parity) = crate::secp256k1_backend::taproot_output_key_with_parity(
543 &internal_pubkey,
544 &merkle_root,
545 )
546 .unwrap();
547
548 assert!(validate_taproot_key_aggregation(
549 &internal_pubkey,
550 &merkle_root,
551 &output_key,
552 parity
553 )
554 .unwrap());
555 }
556
557 #[test]
558 fn test_validate_taproot_script_path() {
559 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
561 let merkle_root =
562 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
563 .unwrap();
564
565 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
566 }
567
568 #[test]
569 fn test_is_taproot_output() {
570 let output = TransactionOutput {
571 value: 1000,
572 script_pubkey: create_taproot_script(&[1u8; 32]),
573 };
574
575 assert!(is_taproot_output(&output));
576 }
577
578 #[test]
579 fn test_validate_taproot_transaction() {
580 let tx = Transaction {
581 version: 1,
582 inputs: vec![TransactionInput {
583 prevout: OutPoint {
584 hash: [0; 32].into(),
585 index: 0,
586 },
587 script_sig: vec![],
588 sequence: 0xffffffff,
589 }]
590 .into(),
591 outputs: vec![TransactionOutput {
592 value: 1000,
593 script_pubkey: create_taproot_script(&[1u8; 32].into()),
594 }]
595 .into(),
596 lock_time: 0,
597 };
598
599 let witness = Some(vec![vec![0u8; 64]]);
601 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
602 }
603
604 #[test]
605 fn test_compute_taproot_signature_hash() {
606 let tx = Transaction {
607 version: 1,
608 inputs: vec![TransactionInput {
609 prevout: OutPoint {
610 hash: [0; 32].into(),
611 index: 0,
612 },
613 script_sig: vec![],
614 sequence: 0xffffffff,
615 }]
616 .into(),
617 outputs: vec![TransactionOutput {
618 value: 1000,
619 script_pubkey: vec![0x51].into(),
620 }]
621 .into(),
622 lock_time: 0,
623 };
624
625 let prevouts = vec![TransactionOutput {
626 value: 2000,
627 script_pubkey: create_taproot_script(&[1u8; 32]),
628 }];
629 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
630 let psp: Vec<&[u8]> = prevouts
631 .iter()
632 .map(|p| p.script_pubkey.as_slice())
633 .collect();
634 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01).unwrap();
635 assert_eq!(sig_hash.len(), 32);
636 }
637
638 #[test]
639 fn test_compute_taproot_signature_hash_invalid_input_index() {
640 let tx = Transaction {
641 version: 1,
642 inputs: vec![TransactionInput {
643 prevout: OutPoint {
644 hash: [0; 32].into(),
645 index: 0,
646 },
647 script_sig: vec![],
648 sequence: 0xffffffff,
649 }]
650 .into(),
651 outputs: vec![TransactionOutput {
652 value: 1000,
653 script_pubkey: vec![0x51].into(),
654 }]
655 .into(),
656 lock_time: 0,
657 };
658
659 let prevouts = vec![TransactionOutput {
660 value: 2000,
661 script_pubkey: create_taproot_script(&[1u8; 32]),
662 }];
663 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
664 let psp: Vec<&[u8]> = prevouts
665 .iter()
666 .map(|p| p.script_pubkey.as_slice())
667 .collect();
668 let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01).unwrap();
670 assert_eq!(sig_hash.len(), 32);
671 }
672
673 #[test]
674 fn test_compute_taproot_signature_hash_empty_prevouts() {
675 let tx = Transaction {
676 version: 1,
677 inputs: vec![TransactionInput {
678 prevout: OutPoint {
679 hash: [0; 32].into(),
680 index: 0,
681 },
682 script_sig: vec![],
683 sequence: 0xffffffff,
684 }]
685 .into(),
686 outputs: vec![TransactionOutput {
687 value: 1000,
688 script_pubkey: vec![0x51].into(),
689 }]
690 .into(),
691 lock_time: 0,
692 };
693
694 let prevouts: Vec<TransactionOutput> = vec![];
695 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
696 let psp: Vec<&[u8]> = prevouts
697 .iter()
698 .map(|p| p.script_pubkey.as_slice())
699 .collect();
700 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01).unwrap();
701 assert_eq!(sig_hash.len(), 32);
702 }
703
704 #[test]
705 fn test_compute_taproot_tweak_invalid_pubkey() {
706 let invalid_pubkey = [0u8; 32]; let merkle_root = [2u8; 32];
708
709 let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
710 assert!(result.is_err());
711 }
712
713 #[test]
714 fn test_validate_taproot_key_aggregation_invalid() {
715 let internal_pubkey = [
716 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
717 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
718 0x16, 0xf8, 0x17, 0x98,
719 ];
720 let merkle_root = [2u8; 32];
721 let wrong_output_key = [3u8; 32]; assert!(!validate_taproot_key_aggregation(
724 &internal_pubkey,
725 &merkle_root,
726 &wrong_output_key,
727 0, )
729 .unwrap());
730 }
731
732 #[test]
733 fn test_validate_taproot_script_path_invalid() {
734 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
736 let wrong_merkle_root = [5u8; 32]; assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
739 }
740
741 #[test]
742 fn test_validate_taproot_script_path_empty_proof() {
743 let script = vec![0x51, 0x52]; let merkle_proof = vec![];
745 let merkle_root =
746 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
747 .unwrap();
748
749 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
750 }
751
752 #[test]
753 fn test_tap_leaf_hash() {
754 let script = vec![0x51, 0x52];
755 let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
756
757 assert_eq!(hash.len(), 32);
758
759 let script2 = vec![0x53, 0x54];
760 let hash2 =
761 crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
762 assert_ne!(hash, hash2);
763 }
764
765 #[test]
766 fn test_tap_branch_hash() {
767 let left = [1u8; 32];
768 let right = [2u8; 32];
769 let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
770
771 assert_eq!(hash.len(), 32);
772
773 let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
774 assert_ne!(hash, hash2);
775 }
776
777 #[test]
778 fn test_encode_varint_small() {
779 let encoded = encode_varint(0xfc);
780 assert_eq!(encoded, vec![0xfc]);
781 }
782
783 #[test]
784 fn test_encode_varint_medium() {
785 let encoded = encode_varint(0x1000);
786 assert_eq!(encoded.len(), 3);
787 assert_eq!(encoded[0], 0xfd);
788 }
789
790 #[test]
791 fn test_encode_varint_large() {
792 let encoded = encode_varint(0x1000000);
793 assert_eq!(encoded.len(), 5);
794 assert_eq!(encoded[0], 0xfe);
795 }
796
797 #[test]
798 fn test_encode_varint_huge() {
799 let encoded = encode_varint(0x1000000000000000);
800 assert_eq!(encoded.len(), 9);
801 assert_eq!(encoded[0], 0xff);
802 }
803
804 #[test]
805 fn test_extract_taproot_output_key_invalid_script() {
806 let script = vec![0x52, 0x20]; let result = extract_taproot_output_key(&script).unwrap();
808 assert!(result.is_none());
809 }
810
811 #[test]
812 fn test_is_taproot_output_false() {
813 let output = TransactionOutput {
814 value: 1000,
815 script_pubkey: vec![0x52, 0x20], };
817
818 assert!(!is_taproot_output(&output));
819 }
820
821 #[test]
822 fn test_validate_taproot_transaction_no_taproot_outputs() {
823 let tx = Transaction {
824 version: 1,
825 inputs: vec![TransactionInput {
826 prevout: OutPoint {
827 hash: [0; 32].into(),
828 index: 0,
829 },
830 script_sig: vec![],
831 sequence: 0xffffffff,
832 }]
833 .into(),
834 outputs: vec![TransactionOutput {
835 value: 1000,
836 script_pubkey: vec![0x52].into(), }]
838 .into(),
839 lock_time: 0,
840 };
841
842 assert!(validate_taproot_transaction(&tx, None).unwrap());
844 }
845
846 #[test]
847 fn test_validate_taproot_transaction_invalid_taproot_output() {
848 let tx = Transaction {
850 version: 1,
851 inputs: vec![TransactionInput {
852 prevout: OutPoint {
853 hash: [0; 32].into(),
854 index: 0,
855 },
856 script_sig: vec![],
857 sequence: 0xffffffff,
858 }]
859 .into(),
860 outputs: vec![TransactionOutput {
861 value: 1000,
862 script_pubkey: create_taproot_script(&[1u8; 32].into()),
863 }]
864 .into(),
865 lock_time: 0,
866 };
867
868 let witness = Some(vec![vec![0u8; 64]]);
870 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
871 }
872
873 fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
875 let mut script = vec![TAPROOT_SCRIPT_PREFIX];
876 script.extend_from_slice(output_key);
877 script.push(0x00); script
879 }
880}
881
882#[cfg(test)]
883mod property_tests {
884 use super::*;
885 use proptest::prelude::*;
886
887 proptest! {
892 #[test]
893 fn prop_validate_taproot_script_deterministic(
894 script in prop::collection::vec(any::<u8>(), 0..50)
895 ) {
896 let result1 = validate_taproot_script(&script).unwrap();
897 let result2 = validate_taproot_script(&script).unwrap();
898
899 assert_eq!(result1, result2);
900 }
901 }
902
903 proptest! {
909 #[test]
910 fn prop_extract_taproot_output_key_correct(
911 script in prop::collection::vec(any::<u8>(), 0..50)
912 ) {
913 let extracted_key = extract_taproot_output_key(&script).unwrap();
914 let is_valid = validate_taproot_script(&script).unwrap();
915
916 if is_valid {
917 assert!(extracted_key.is_some());
918 let key = extracted_key.unwrap();
919 assert_eq!(key.len(), 32);
920 } else {
921 assert!(extracted_key.is_none());
922 }
923 }
924 }
925
926 proptest! {
932 #[test]
933 fn prop_taproot_key_aggregation_deterministic(
934 internal_pubkey in create_pubkey_strategy(),
935 merkle_root in create_hash_strategy()
936 ) {
937 let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
938 let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
939
940 assert_eq!(result1.is_ok(), result2.is_ok());
941 if result1.is_ok() && result2.is_ok() {
942 assert_eq!(result1.unwrap(), result2.unwrap());
943 }
944 }
945 }
946
947 proptest! {
953 #[test]
954 fn prop_validate_taproot_script_path_deterministic(
955 script in prop::collection::vec(any::<u8>(), 0..20),
956 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
957 merkle_root in create_hash_strategy()
958 ) {
959 let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
960 let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
961
962 assert_eq!(result1.is_ok(), result2.is_ok());
963 if result1.is_ok() && result2.is_ok() {
964 assert_eq!(result1.unwrap(), result2.unwrap());
965 }
966 }
967 }
968
969 proptest! {
975 #[test]
976 fn prop_compute_taproot_signature_hash_deterministic(
977 tx in create_transaction_strategy(),
978 input_index in 0..10usize,
979 prevouts in prop::collection::vec(create_output_strategy(), 0..5),
980 sighash_type in any::<u8>()
981 ) {
982 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
983 let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
984 let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
985 let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
986
987 assert_eq!(result1.is_ok(), result2.is_ok());
988 if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
989 assert_eq!(hash1, hash2);
990 assert_eq!(hash1.len(), 32);
991 }
992
993 if let Ok(ref hash) = result1 {
995 assert_eq!(hash.len(), 32);
996 }
997 }
998 }
999
1000 proptest! {
1005 #[test]
1006 fn prop_is_taproot_output_consistent(
1007 output in create_output_strategy()
1008 ) {
1009 let is_taproot = is_taproot_output(&output);
1010 let _ = is_taproot;
1012 }
1013 }
1014
1015 proptest! {
1020 #[test]
1021 fn prop_validate_taproot_transaction_deterministic(
1022 tx in create_transaction_strategy()
1023 ) {
1024 let result1 = validate_taproot_transaction(&tx, None).unwrap();
1025 let result2 = validate_taproot_transaction(&tx, None).unwrap();
1026
1027 assert_eq!(result1, result2);
1028 }
1029 }
1030
1031 proptest! {
1033 #[test]
1034 fn prop_tap_leaf_hash_deterministic(
1035 script in prop::collection::vec(any::<u8>(), 0..20)
1036 ) {
1037 let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1038 let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1039
1040 assert_eq!(hash1, hash2);
1041 assert_eq!(hash1.len(), 32);
1042 }
1043 }
1044
1045 proptest! {
1047 #[test]
1048 fn prop_tap_branch_hash_deterministic(
1049 left in create_hash_strategy(),
1050 right in create_hash_strategy()
1051 ) {
1052 let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1053 let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1054
1055 assert_eq!(hash1, hash2);
1056 assert_eq!(hash1.len(), 32);
1057 }
1058 }
1059
1060 proptest! {
1065 #[test]
1066 fn prop_encode_varint_deterministic(
1067 value in 0..u64::MAX
1068 ) {
1069 let encoded1 = encode_varint(value);
1070 let encoded2 = encode_varint(value);
1071
1072 assert_eq!(encoded1, encoded2);
1073
1074 assert!(!encoded1.is_empty());
1076 assert!(encoded1.len() <= 9);
1077 }
1078 }
1079
1080 proptest! {
1085 #[test]
1086 fn prop_encode_varint_preserves_value(
1087 value in 0..1000000u64 ) {
1089 let encoded = encode_varint(value);
1090
1091 match encoded.len() {
1093 1 => {
1094 assert!(value < 0xfd);
1096 assert_eq!(encoded[0], value as u8);
1097 },
1098 3 => {
1099 assert!((0xfd..=0xffff).contains(&value));
1101 assert_eq!(encoded[0], 0xfd);
1102 },
1103 5 => {
1104 assert!(value > 0xffff && value <= 0xffffffff);
1106 assert_eq!(encoded[0], 0xfe);
1107 },
1108 9 => {
1109 assert!(value > 0xffffffff);
1111 assert_eq!(encoded[0], 0xff);
1112 },
1113 _ => panic!("Invalid varint encoding length"),
1114 }
1115 }
1116 }
1117
1118 proptest! {
1125 #[test]
1126 fn prop_validate_taproot_script_path_correct_proof(
1127 script in prop::collection::vec(any::<u8>(), 0..20),
1128 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
1129 ) {
1130 let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
1131 let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
1132
1133 assert!(is_valid);
1134 }
1135 }
1136
1137 fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
1139 (
1140 prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10), )
1143 .prop_map(|(input_data, output_data)| {
1144 let mut inputs = Vec::new();
1145 for (i, _) in input_data.iter().enumerate() {
1146 inputs.push(TransactionInput {
1147 prevout: OutPoint {
1148 hash: [0; 32],
1149 index: i as u32,
1150 },
1151 script_sig: vec![],
1152 sequence: 0xffffffff,
1153 });
1154 }
1155
1156 let mut outputs = Vec::new();
1157 for _ in output_data {
1158 outputs.push(TransactionOutput {
1159 value: 1000,
1160 script_pubkey: vec![0x51],
1161 });
1162 }
1163
1164 Transaction {
1165 version: 1,
1166 inputs: inputs.into(),
1167 outputs: outputs.into(),
1168 lock_time: 0,
1169 }
1170 })
1171 }
1172
1173 fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1174 (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1175 TransactionOutput {
1176 value,
1177 script_pubkey: script,
1178 }
1179 })
1180 }
1181
1182 fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1183 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1184 let mut hash = [0u8; 32];
1185 hash.copy_from_slice(&bytes);
1186 hash
1187 })
1188 }
1189
1190 fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1191 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1192 let mut pubkey = [0u8; 32];
1193 pubkey.copy_from_slice(&bytes);
1194 pubkey
1195 })
1196 }
1197}