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")]
204#[blvm_spec_lock::ensures(result == false || output.script_pubkey.len() == 34)]
205pub fn is_taproot_output(output: &TransactionOutput) -> bool {
206 validate_taproot_script(&output.script_pubkey).unwrap_or(false)
207}
208
209#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
211pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
212 for output in &tx.outputs {
214 if is_taproot_output(output) {
215 if !validate_taproot_script(&output.script_pubkey)? {
217 return Ok(false);
218 }
219 }
220 }
221
222 if let Some(w) = witness {
226 let is_script_path = w.len() >= 2;
227 if !witness::validate_taproot_witness_structure(w, is_script_path)? {
228 return Ok(false);
229 }
230 }
231
232 Ok(true)
233}
234
235fn bip341_precompute(
240 tx: &Transaction,
241 prevout_values: &[i64],
242 prevout_script_pubkeys: &[&[u8]],
243) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
244 use sha2::{Digest, Sha256};
245
246 let mut prevouts_data = Vec::new();
247 let mut amounts_data = Vec::new();
248 let mut scriptpubkeys_data = Vec::new();
249 let mut sequences_data = Vec::new();
250 for (i, input) in tx.inputs.iter().enumerate() {
251 prevouts_data.extend_from_slice(&input.prevout.hash);
252 prevouts_data.extend_from_slice(&input.prevout.index.to_le_bytes());
253 amounts_data.extend_from_slice(&(prevout_values[i] as u64).to_le_bytes());
257 let spk = prevout_script_pubkeys[i];
258 scriptpubkeys_data.extend_from_slice(&encode_varint(spk.len() as u64));
259 scriptpubkeys_data.extend_from_slice(spk);
260 sequences_data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
261 }
262 let mut outputs_data = Vec::new();
263 for output in &tx.outputs {
264 outputs_data.extend_from_slice(&(output.value as u64).to_le_bytes());
265 outputs_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
266 outputs_data.extend_from_slice(&output.script_pubkey);
267 }
268
269 (
270 Sha256::digest(&prevouts_data).into(),
271 Sha256::digest(&amounts_data).into(),
272 Sha256::digest(&scriptpubkeys_data).into(),
273 Sha256::digest(&sequences_data).into(),
274 Sha256::digest(&outputs_data).into(),
275 )
276}
277
278#[spec_locked("11.2.6", "ComputeTaprootSignatureHash")]
289pub fn compute_taproot_signature_hash(
290 tx: &Transaction,
291 input_index: usize,
292 prevout_values: &[i64],
293 prevout_script_pubkeys: &[&[u8]],
294 sighash_type: u8,
295) -> Result<Hash> {
296 use sha2::{Digest, Sha256};
297
298 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
301 return Err(crate::error::ConsensusError::InvalidSignature(
302 "Invalid Taproot sighash type".into(),
303 ));
304 }
305
306 if prevout_values.len() < tx.inputs.len() || prevout_script_pubkeys.len() < tx.inputs.len() {
310 return Err(crate::error::ConsensusError::InvalidSignature(
311 "prevout_values/prevout_script_pubkeys shorter than tx.inputs β cannot compute sighash"
312 .into(),
313 ));
314 }
315
316 let input_type = sighash_type & 0x80; let output_type = if sighash_type == 0x00 {
318 0x01
319 } else {
320 sighash_type & 0x03
321 }; let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
324 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
325
326 let mut sigmsg = Vec::with_capacity(180);
327
328 sigmsg.push(0x00u8);
330 sigmsg.push(sighash_type);
332 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
334 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
336
337 if input_type != 0x80 {
338 sigmsg.extend_from_slice(&sha_prevouts);
340 sigmsg.extend_from_slice(&sha_amounts);
341 sigmsg.extend_from_slice(&sha_scriptpubkeys);
342 sigmsg.extend_from_slice(&sha_sequences);
343 }
344 if output_type == 0x01 {
345 sigmsg.extend_from_slice(&sha_outputs);
347 }
348
349 sigmsg.push(0x00u8);
351
352 if input_type == 0x80 {
353 let input = tx.inputs.get(input_index).ok_or_else(|| {
355 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
356 })?;
357 sigmsg.extend_from_slice(&input.prevout.hash);
358 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
359 let amount = *prevout_values.get(input_index).ok_or_else(|| {
361 crate::error::ConsensusError::InvalidSignature(
362 "prevout_values index out of range for ANYONECANPAY".into(),
363 )
364 })? as u64;
365 sigmsg.extend_from_slice(&amount.to_le_bytes());
366 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
367 crate::error::ConsensusError::InvalidSignature(
368 "prevout_script_pubkeys index out of range for ANYONECANPAY".into(),
369 )
370 })?;
371 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
372 sigmsg.extend_from_slice(spk);
373 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
374 } else {
375 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
377 }
378
379 if output_type == 0x03 {
380 if let Some(output) = tx.outputs.get(input_index) {
382 let mut out_data = Vec::new();
383 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
384 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
385 out_data.extend_from_slice(&output.script_pubkey);
386 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
387 }
388 }
389
390 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
391}
392
393#[spec_locked("11.2.7", "ComputeTapscriptSignatureHash")]
398pub fn compute_tapscript_signature_hash(
399 tx: &Transaction,
400 input_index: usize,
401 prevout_values: &[i64],
402 prevout_script_pubkeys: &[&[u8]],
403 tapscript: &[u8],
404 leaf_version: u8,
405 codesep_pos: u32,
406 sighash_type: u8,
407) -> Result<Hash> {
408 use sha2::{Digest, Sha256};
409
410 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
411 return Err(crate::error::ConsensusError::InvalidSignature(
412 "Invalid Tapscript sighash type".into(),
413 ));
414 }
415
416 let input_type = sighash_type & 0x80;
417 let output_type = if sighash_type == 0x00 {
418 0x01
419 } else {
420 sighash_type & 0x03
421 };
422
423 let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
424 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
425
426 let mut sigmsg = Vec::with_capacity(250);
427
428 sigmsg.push(0x00u8);
430 sigmsg.push(sighash_type);
432 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
434 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
436
437 if input_type != 0x80 {
438 sigmsg.extend_from_slice(&sha_prevouts);
439 sigmsg.extend_from_slice(&sha_amounts);
440 sigmsg.extend_from_slice(&sha_scriptpubkeys);
441 sigmsg.extend_from_slice(&sha_sequences);
442 }
443 if output_type == 0x01 {
444 sigmsg.extend_from_slice(&sha_outputs);
445 }
446
447 sigmsg.push(0x02u8);
449
450 if input_type == 0x80 {
451 let input = tx.inputs.get(input_index).ok_or_else(|| {
452 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
453 })?;
454 sigmsg.extend_from_slice(&input.prevout.hash);
455 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
456 let amount = *prevout_values.get(input_index).ok_or_else(|| {
457 crate::error::ConsensusError::InvalidSignature(
458 "prevout_values index out of range for script-path ANYONECANPAY".into(),
459 )
460 })? as u64;
461 sigmsg.extend_from_slice(&amount.to_le_bytes());
462 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
463 crate::error::ConsensusError::InvalidSignature(
464 "prevout_script_pubkeys index out of range for script-path ANYONECANPAY".into(),
465 )
466 })?;
467 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
468 sigmsg.extend_from_slice(spk);
469 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
470 } else {
471 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
472 }
473
474 if output_type == 0x03 {
475 if let Some(output) = tx.outputs.get(input_index) {
476 let mut out_data = Vec::new();
477 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
478 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
479 out_data.extend_from_slice(&output.script_pubkey);
480 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
481 }
482 }
483
484 let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
486 sigmsg.extend_from_slice(&tapleaf_hash);
487 sigmsg.push(0x00u8); sigmsg.extend_from_slice(&codesep_pos.to_le_bytes());
489
490 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
491}
492
493fn encode_varint(value: u64) -> Vec<u8> {
495 if value < 0xfd {
496 vec![value as u8]
497 } else if value <= 0xffff {
498 let mut result = vec![0xfd];
499 result.extend_from_slice(&(value as u16).to_le_bytes());
500 result
501 } else if value <= 0xffffffff {
502 let mut result = vec![0xfe];
503 result.extend_from_slice(&(value as u32).to_le_bytes());
504 result
505 } else {
506 let mut result = vec![0xff];
507 result.extend_from_slice(&value.to_le_bytes());
508 result
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use super::*;
515
516 #[test]
517 fn test_validate_taproot_script_valid() {
518 let script = create_taproot_script(&[1u8; 32]);
519 assert!(validate_taproot_script(&script).unwrap());
520 }
521
522 #[test]
523 fn test_validate_taproot_script_invalid_length() {
524 let script = vec![0x51, 0x20]; assert!(!validate_taproot_script(&script).unwrap());
526 }
527
528 #[test]
529 fn test_validate_taproot_script_invalid_prefix() {
530 let mut script = vec![0x52]; script.extend_from_slice(&[1u8; 32]);
532 assert!(!validate_taproot_script(&script).unwrap());
533 }
534
535 #[test]
536 fn test_extract_taproot_output_key() {
537 let expected_key = [1u8; 32];
538 let script = create_taproot_script(&expected_key);
539
540 let extracted_key = extract_taproot_output_key(&script).unwrap();
541 assert_eq!(extracted_key, Some(expected_key));
542 }
543
544 #[test]
545 fn test_compute_taproot_tweak() {
546 let internal_pubkey = [
548 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
549 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
550 0x16, 0xf8, 0x17, 0x98,
551 ];
552 let merkle_root = [2u8; 32];
553
554 let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
555 assert_eq!(tweak.len(), 32);
556 }
557
558 #[test]
559 fn test_validate_taproot_key_aggregation() {
560 let internal_pubkey = [
562 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
563 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
564 0x16, 0xf8, 0x17, 0x98,
565 ];
566 let merkle_root = [2u8; 32];
567 let (output_key, parity) = crate::secp256k1_backend::taproot_output_key_with_parity(
568 &internal_pubkey,
569 &merkle_root,
570 )
571 .unwrap();
572
573 assert!(validate_taproot_key_aggregation(
574 &internal_pubkey,
575 &merkle_root,
576 &output_key,
577 parity
578 )
579 .unwrap());
580 }
581
582 #[test]
583 fn test_validate_taproot_script_path() {
584 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
586 let merkle_root =
587 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
588 .unwrap();
589
590 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
591 }
592
593 #[test]
594 fn test_is_taproot_output() {
595 let output = TransactionOutput {
596 value: 1000,
597 script_pubkey: create_taproot_script(&[1u8; 32]),
598 };
599
600 assert!(is_taproot_output(&output));
601 }
602
603 #[test]
604 fn test_validate_taproot_transaction() {
605 let tx = Transaction {
606 version: 1,
607 inputs: vec![TransactionInput {
608 prevout: OutPoint {
609 hash: [0; 32],
610 index: 0,
611 },
612 script_sig: vec![],
613 sequence: 0xffffffff,
614 }]
615 .into(),
616 outputs: vec![TransactionOutput {
617 value: 1000,
618 script_pubkey: create_taproot_script(&[1u8; 32]),
619 }]
620 .into(),
621 lock_time: 0,
622 };
623
624 let witness = Some(vec![vec![0u8; 64]]);
626 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
627 }
628
629 #[test]
630 fn test_compute_taproot_signature_hash() {
631 let tx = Transaction {
632 version: 1,
633 inputs: vec![TransactionInput {
634 prevout: OutPoint {
635 hash: [0; 32],
636 index: 0,
637 },
638 script_sig: vec![],
639 sequence: 0xffffffff,
640 }]
641 .into(),
642 outputs: vec![TransactionOutput {
643 value: 1000,
644 script_pubkey: vec![0x51],
645 }]
646 .into(),
647 lock_time: 0,
648 };
649
650 let prevouts = [TransactionOutput {
651 value: 2000,
652 script_pubkey: create_taproot_script(&[1u8; 32]),
653 }];
654 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
655 let psp: Vec<&[u8]> = prevouts
656 .iter()
657 .map(|p| p.script_pubkey.as_slice())
658 .collect();
659 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01).unwrap();
660 assert_eq!(sig_hash.len(), 32);
661 }
662
663 #[test]
664 fn test_compute_taproot_signature_hash_invalid_input_index() {
665 let tx = Transaction {
666 version: 1,
667 inputs: vec![TransactionInput {
668 prevout: OutPoint {
669 hash: [0; 32],
670 index: 0,
671 },
672 script_sig: vec![],
673 sequence: 0xffffffff,
674 }]
675 .into(),
676 outputs: vec![TransactionOutput {
677 value: 1000,
678 script_pubkey: vec![0x51],
679 }]
680 .into(),
681 lock_time: 0,
682 };
683
684 let prevouts = [TransactionOutput {
685 value: 2000,
686 script_pubkey: create_taproot_script(&[1u8; 32]),
687 }];
688 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
689 let psp: Vec<&[u8]> = prevouts
690 .iter()
691 .map(|p| p.script_pubkey.as_slice())
692 .collect();
693 let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01).unwrap();
695 assert_eq!(sig_hash.len(), 32);
696 }
697
698 #[test]
699 fn test_compute_taproot_signature_hash_empty_prevouts() {
700 let tx = Transaction {
701 version: 1,
702 inputs: vec![TransactionInput {
703 prevout: OutPoint {
704 hash: [0; 32],
705 index: 0,
706 },
707 script_sig: vec![],
708 sequence: 0xffffffff,
709 }]
710 .into(),
711 outputs: vec![TransactionOutput {
712 value: 1000,
713 script_pubkey: vec![0x51],
714 }]
715 .into(),
716 lock_time: 0,
717 };
718
719 let prevouts: Vec<TransactionOutput> = vec![];
722 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
723 let psp: Vec<&[u8]> = prevouts
724 .iter()
725 .map(|p| p.script_pubkey.as_slice())
726 .collect();
727 let result = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01);
728 assert!(
729 result.is_err(),
730 "empty prevouts shorter than tx.inputs must return Err, not silently use zeros"
731 );
732 }
733
734 #[test]
735 fn test_compute_taproot_tweak_invalid_pubkey() {
736 let invalid_pubkey = [0u8; 32]; let merkle_root = [2u8; 32];
738
739 let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
740 assert!(result.is_err());
741 }
742
743 #[test]
744 fn test_validate_taproot_key_aggregation_invalid() {
745 let internal_pubkey = [
746 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
747 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
748 0x16, 0xf8, 0x17, 0x98,
749 ];
750 let merkle_root = [2u8; 32];
751 let wrong_output_key = [3u8; 32]; assert!(!validate_taproot_key_aggregation(
754 &internal_pubkey,
755 &merkle_root,
756 &wrong_output_key,
757 0, )
759 .unwrap());
760 }
761
762 #[test]
763 fn test_validate_taproot_script_path_invalid() {
764 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
766 let wrong_merkle_root = [5u8; 32]; assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
769 }
770
771 #[test]
772 fn test_validate_taproot_script_path_empty_proof() {
773 let script = vec![0x51, 0x52]; let merkle_proof = vec![];
775 let merkle_root =
776 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
777 .unwrap();
778
779 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
780 }
781
782 #[test]
783 fn test_tap_leaf_hash() {
784 let script = vec![0x51, 0x52];
785 let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
786
787 assert_eq!(hash.len(), 32);
788
789 let script2 = vec![0x53, 0x54];
790 let hash2 =
791 crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
792 assert_ne!(hash, hash2);
793 }
794
795 #[test]
796 fn test_tap_branch_hash() {
797 let left = [1u8; 32];
798 let right = [2u8; 32];
799 let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
800
801 assert_eq!(hash.len(), 32);
802
803 let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
804 assert_ne!(hash, hash2);
805 }
806
807 #[test]
808 fn test_encode_varint_small() {
809 let encoded = encode_varint(0xfc);
810 assert_eq!(encoded, vec![0xfc]);
811 }
812
813 #[test]
814 fn test_encode_varint_medium() {
815 let encoded = encode_varint(0x1000);
816 assert_eq!(encoded.len(), 3);
817 assert_eq!(encoded[0], 0xfd);
818 }
819
820 #[test]
821 fn test_encode_varint_large() {
822 let encoded = encode_varint(0x1000000);
823 assert_eq!(encoded.len(), 5);
824 assert_eq!(encoded[0], 0xfe);
825 }
826
827 #[test]
828 fn test_encode_varint_huge() {
829 let encoded = encode_varint(0x1000000000000000);
830 assert_eq!(encoded.len(), 9);
831 assert_eq!(encoded[0], 0xff);
832 }
833
834 #[test]
835 fn test_extract_taproot_output_key_invalid_script() {
836 let script = vec![0x52, 0x20]; let result = extract_taproot_output_key(&script).unwrap();
838 assert!(result.is_none());
839 }
840
841 #[test]
842 fn test_is_taproot_output_false() {
843 let output = TransactionOutput {
844 value: 1000,
845 script_pubkey: vec![0x52, 0x20], };
847
848 assert!(!is_taproot_output(&output));
849 }
850
851 #[test]
852 fn test_validate_taproot_transaction_no_taproot_outputs() {
853 let tx = Transaction {
854 version: 1,
855 inputs: vec![TransactionInput {
856 prevout: OutPoint {
857 hash: [0; 32],
858 index: 0,
859 },
860 script_sig: vec![],
861 sequence: 0xffffffff,
862 }]
863 .into(),
864 outputs: vec![TransactionOutput {
865 value: 1000,
866 script_pubkey: vec![0x52], }]
868 .into(),
869 lock_time: 0,
870 };
871
872 assert!(validate_taproot_transaction(&tx, None).unwrap());
874 }
875
876 #[test]
877 fn test_validate_taproot_transaction_invalid_taproot_output() {
878 let tx = Transaction {
880 version: 1,
881 inputs: vec![TransactionInput {
882 prevout: OutPoint {
883 hash: [0; 32],
884 index: 0,
885 },
886 script_sig: vec![],
887 sequence: 0xffffffff,
888 }]
889 .into(),
890 outputs: vec![TransactionOutput {
891 value: 1000,
892 script_pubkey: create_taproot_script(&[1u8; 32]),
893 }]
894 .into(),
895 lock_time: 0,
896 };
897
898 let witness = Some(vec![vec![0u8; 64]]);
900 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
901 }
902
903 fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
905 let mut script = vec![TAPROOT_SCRIPT_PREFIX];
906 script.extend_from_slice(output_key);
907 script.push(0x00); script
909 }
910}
911
912#[cfg(test)]
913mod property_tests {
914 use super::*;
915 use proptest::prelude::*;
916
917 proptest! {
922 #[test]
923 fn prop_validate_taproot_script_deterministic(
924 script in prop::collection::vec(any::<u8>(), 0..50)
925 ) {
926 let result1 = validate_taproot_script(&script).unwrap();
927 let result2 = validate_taproot_script(&script).unwrap();
928
929 assert_eq!(result1, result2);
930 }
931 }
932
933 proptest! {
939 #[test]
940 fn prop_extract_taproot_output_key_correct(
941 script in prop::collection::vec(any::<u8>(), 0..50)
942 ) {
943 let extracted_key = extract_taproot_output_key(&script).unwrap();
944 let is_valid = validate_taproot_script(&script).unwrap();
945
946 if is_valid {
947 assert!(extracted_key.is_some());
948 let key = extracted_key.unwrap();
949 assert_eq!(key.len(), 32);
950 } else {
951 assert!(extracted_key.is_none());
952 }
953 }
954 }
955
956 proptest! {
962 #[test]
963 fn prop_taproot_key_aggregation_deterministic(
964 internal_pubkey in create_pubkey_strategy(),
965 merkle_root in create_hash_strategy()
966 ) {
967 let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
968 let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
969
970 assert_eq!(result1.is_ok(), result2.is_ok());
971 if result1.is_ok() && result2.is_ok() {
972 assert_eq!(result1.unwrap(), result2.unwrap());
973 }
974 }
975 }
976
977 proptest! {
983 #[test]
984 fn prop_validate_taproot_script_path_deterministic(
985 script in prop::collection::vec(any::<u8>(), 0..20),
986 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
987 merkle_root in create_hash_strategy()
988 ) {
989 let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
990 let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
991
992 assert_eq!(result1.is_ok(), result2.is_ok());
993 if result1.is_ok() && result2.is_ok() {
994 assert_eq!(result1.unwrap(), result2.unwrap());
995 }
996 }
997 }
998
999 proptest! {
1005 #[test]
1006 fn prop_compute_taproot_signature_hash_deterministic(
1007 tx in create_transaction_strategy(),
1008 input_index in 0..10usize,
1009 prevouts in prop::collection::vec(create_output_strategy(), 0..5),
1010 sighash_type in any::<u8>()
1011 ) {
1012 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
1013 let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
1014 let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
1015 let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
1016
1017 assert_eq!(result1.is_ok(), result2.is_ok());
1018 if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
1019 assert_eq!(hash1, hash2);
1020 assert_eq!(hash1.len(), 32);
1021 }
1022
1023 if let Ok(ref hash) = result1 {
1025 assert_eq!(hash.len(), 32);
1026 }
1027 }
1028 }
1029
1030 proptest! {
1035 #[test]
1036 fn prop_is_taproot_output_consistent(
1037 output in create_output_strategy()
1038 ) {
1039 let is_taproot = is_taproot_output(&output);
1040 let _ = is_taproot;
1042 }
1043 }
1044
1045 proptest! {
1050 #[test]
1051 fn prop_validate_taproot_transaction_deterministic(
1052 tx in create_transaction_strategy()
1053 ) {
1054 let result1 = validate_taproot_transaction(&tx, None).unwrap();
1055 let result2 = validate_taproot_transaction(&tx, None).unwrap();
1056
1057 assert_eq!(result1, result2);
1058 }
1059 }
1060
1061 proptest! {
1063 #[test]
1064 fn prop_tap_leaf_hash_deterministic(
1065 script in prop::collection::vec(any::<u8>(), 0..20)
1066 ) {
1067 let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1068 let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1069
1070 assert_eq!(hash1, hash2);
1071 assert_eq!(hash1.len(), 32);
1072 }
1073 }
1074
1075 proptest! {
1077 #[test]
1078 fn prop_tap_branch_hash_deterministic(
1079 left in create_hash_strategy(),
1080 right in create_hash_strategy()
1081 ) {
1082 let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1083 let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1084
1085 assert_eq!(hash1, hash2);
1086 assert_eq!(hash1.len(), 32);
1087 }
1088 }
1089
1090 proptest! {
1095 #[test]
1096 fn prop_encode_varint_deterministic(
1097 value in 0..u64::MAX
1098 ) {
1099 let encoded1 = encode_varint(value);
1100 let encoded2 = encode_varint(value);
1101
1102 assert_eq!(encoded1, encoded2);
1103
1104 assert!(!encoded1.is_empty());
1106 assert!(encoded1.len() <= 9);
1107 }
1108 }
1109
1110 proptest! {
1115 #[test]
1116 fn prop_encode_varint_preserves_value(
1117 value in 0..1000000u64 ) {
1119 let encoded = encode_varint(value);
1120
1121 match encoded.len() {
1123 1 => {
1124 assert!(value < 0xfd);
1126 assert_eq!(encoded[0], value as u8);
1127 },
1128 3 => {
1129 assert!((0xfd..=0xffff).contains(&value));
1131 assert_eq!(encoded[0], 0xfd);
1132 },
1133 5 => {
1134 assert!(value > 0xffff && value <= 0xffffffff);
1136 assert_eq!(encoded[0], 0xfe);
1137 },
1138 9 => {
1139 assert!(value > 0xffffffff);
1141 assert_eq!(encoded[0], 0xff);
1142 },
1143 _ => panic!("Invalid varint encoding length"),
1144 }
1145 }
1146 }
1147
1148 proptest! {
1155 #[test]
1156 fn prop_validate_taproot_script_path_correct_proof(
1157 script in prop::collection::vec(any::<u8>(), 0..20),
1158 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
1159 ) {
1160 let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
1161 let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
1162
1163 assert!(is_valid);
1164 }
1165 }
1166
1167 fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
1169 (
1170 prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10), )
1173 .prop_map(|(input_data, output_data)| {
1174 let mut inputs = Vec::new();
1175 for (i, _) in input_data.iter().enumerate() {
1176 inputs.push(TransactionInput {
1177 prevout: OutPoint {
1178 hash: [0; 32],
1179 index: i as u32,
1180 },
1181 script_sig: vec![],
1182 sequence: 0xffffffff,
1183 });
1184 }
1185
1186 let mut outputs = Vec::new();
1187 for _ in output_data {
1188 outputs.push(TransactionOutput {
1189 value: 1000,
1190 script_pubkey: vec![0x51],
1191 });
1192 }
1193
1194 Transaction {
1195 version: 1,
1196 inputs: inputs.into(),
1197 outputs: outputs.into(),
1198 lock_time: 0,
1199 }
1200 })
1201 }
1202
1203 fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1204 (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1205 TransactionOutput {
1206 value,
1207 script_pubkey: script,
1208 }
1209 })
1210 }
1211
1212 fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1213 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1214 let mut hash = [0u8; 32];
1215 hash.copy_from_slice(&bytes);
1216 hash
1217 })
1218 }
1219
1220 fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1221 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1222 let mut pubkey = [0u8; 32];
1223 pubkey.copy_from_slice(&bytes);
1224 pubkey
1225 })
1226 }
1227}