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")]
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")]
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")]
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")]
64pub fn validate_taproot_key_aggregation(
65 internal_pubkey: &[u8; 32],
66 merkle_root: &Hash,
67 output_key: &[u8; 32],
68) -> Result<bool> {
69 let expected_output_key = compute_taproot_tweak(internal_pubkey, merkle_root)?;
70 Ok(expected_output_key == *output_key)
71}
72
73#[spec_locked("11.2.3")]
75pub fn validate_taproot_script_path(
76 script: &ByteString,
77 merkle_proof: &[Hash],
78 merkle_root: &Hash,
79) -> Result<bool> {
80 validate_taproot_script_path_with_leaf_version(
81 script,
82 merkle_proof,
83 merkle_root,
84 TAPROOT_LEAF_VERSION_TAPSCRIPT,
85 )
86}
87
88#[spec_locked("11.2.3")]
90pub fn validate_taproot_script_path_with_leaf_version(
91 script: &ByteString,
92 merkle_proof: &[Hash],
93 merkle_root: &Hash,
94 leaf_version: u8,
95) -> Result<bool> {
96 let computed_root = compute_script_merkle_root(script, merkle_proof, leaf_version)?;
97 Ok(computed_root == *merkle_root)
98}
99
100#[spec_locked("11.2.3")]
102pub fn compute_script_merkle_root(
103 script: &ByteString,
104 proof: &[Hash],
105 leaf_version: u8,
106) -> Result<Hash> {
107 let mut current_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, script);
108
109 for proof_hash in proof {
110 let (left, right) = if current_hash < *proof_hash {
111 (current_hash, *proof_hash)
112 } else {
113 (*proof_hash, current_hash)
114 };
115 current_hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
116 }
117
118 Ok(current_hash)
119}
120
121#[derive(Debug)]
123pub struct TaprootControlBlock {
124 pub leaf_version: u8,
125 pub internal_pubkey: [u8; 32],
126 pub merkle_proof: Vec<Hash>,
127}
128
129#[spec_locked("11.2.4")]
135pub fn parse_taproot_script_path_witness(
136 witness: &Witness,
137 output_key: &[u8; 32],
138) -> Result<Option<(ByteString, Vec<ByteString>, TaprootControlBlock)>> {
139 if witness.len() < 2 {
140 return Ok(None);
141 }
142
143 let control_block = witness.last().expect("len >= 2");
144 if control_block.len() < 33 || (control_block.len() - 33) % 32 != 0 {
145 return Ok(None);
146 }
147
148 let leaf_version = control_block[0];
149 let mut internal_pubkey = [0u8; 32];
150 internal_pubkey.copy_from_slice(&control_block[1..33]);
151 let merkle_proof: Vec<Hash> = control_block[33..]
152 .chunks_exact(32)
153 .map(|c| {
154 let mut h = [0u8; 32];
155 h.copy_from_slice(c);
156 h
157 })
158 .collect();
159
160 let script_idx = if witness.len() >= 3 {
161 let maybe_annex = &witness[witness.len() - 2];
162 if maybe_annex.first() == Some(&0x50) {
163 witness.len() - 3
164 } else {
165 witness.len() - 2
166 }
167 } else {
168 witness.len() - 2
169 };
170
171 let tapscript = witness[script_idx].clone();
172 let stack_items: Vec<ByteString> = witness[..script_idx].to_vec();
173
174 let merkle_root = compute_script_merkle_root(&tapscript, &merkle_proof, leaf_version)?;
175 if !validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, output_key)? {
176 return Ok(None);
177 }
178
179 Ok(Some((
180 tapscript,
181 stack_items,
182 TaprootControlBlock {
183 leaf_version,
184 internal_pubkey,
185 merkle_proof,
186 },
187 )))
188}
189
190#[spec_locked("11.2.1")]
192pub fn is_taproot_output(output: &TransactionOutput) -> bool {
193 validate_taproot_script(&output.script_pubkey).unwrap_or(false)
194}
195
196#[spec_locked("11.2.5")]
198pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
199 for output in &tx.outputs {
201 if is_taproot_output(output) {
202 if !validate_taproot_script(&output.script_pubkey)? {
204 return Ok(false);
205 }
206 }
207 }
208
209 if let Some(w) = witness {
213 let is_script_path = w.len() >= 2;
214 if !witness::validate_taproot_witness_structure(w, is_script_path)? {
215 return Ok(false);
216 }
217 }
218
219 Ok(true)
220}
221
222#[spec_locked("11.2.6")]
225pub fn compute_taproot_signature_hash(
226 tx: &Transaction,
227 input_index: usize,
228 prevout_values: &[i64],
229 prevout_script_pubkeys: &[&[u8]],
230 sighash_type: u8,
231) -> Result<Hash> {
232 let mut sigmsg = Vec::new();
233
234 sigmsg.extend((tx.version as u32).to_le_bytes());
235 sigmsg.extend(encode_varint(tx.inputs.len() as u64));
236 for input in &tx.inputs {
237 sigmsg.extend(input.prevout.hash);
238 sigmsg.extend(input.prevout.index.to_le_bytes());
239 sigmsg.push(0);
240 sigmsg.extend((input.sequence as u32).to_le_bytes());
241 }
242 sigmsg.extend(encode_varint(tx.outputs.len() as u64));
243 for output in &tx.outputs {
244 sigmsg.extend((output.value as u64).to_le_bytes());
245 sigmsg.extend(encode_varint(output.script_pubkey.len() as u64));
246 sigmsg.extend(&output.script_pubkey);
247 }
248 sigmsg.extend((tx.lock_time as u32).to_le_bytes());
249 sigmsg.extend((sighash_type as u32).to_le_bytes());
250 sigmsg.extend((input_index as u32).to_le_bytes());
251 if input_index < prevout_values.len() {
252 sigmsg.extend((prevout_values[input_index] as u64).to_le_bytes());
253 } else {
254 sigmsg.extend([0u8; 8]);
255 }
256 if input_index < prevout_script_pubkeys.len() {
257 sigmsg.extend(encode_varint(
258 prevout_script_pubkeys[input_index].len() as u64
259 ));
260 sigmsg.extend(prevout_script_pubkeys[input_index]);
261 } else {
262 sigmsg.push(0);
263 }
264
265 let mut tagged_input = Vec::with_capacity(1 + sigmsg.len());
266 tagged_input.push(0x00);
267 tagged_input.extend(sigmsg);
268 Ok(crate::secp256k1_backend::tap_sighash_hash(&tagged_input))
269}
270
271#[spec_locked("11.2.7")]
274pub fn compute_tapscript_signature_hash(
275 tx: &Transaction,
276 input_index: usize,
277 prevout_values: &[i64],
278 prevout_script_pubkeys: &[&[u8]],
279 tapscript: &[u8],
280 leaf_version: u8,
281 codesep_pos: u32,
282 sighash_type: u8,
283) -> Result<Hash> {
284 let mut sigmsg = Vec::new();
285 sigmsg.extend((tx.version as u32).to_le_bytes());
286 sigmsg.extend(encode_varint(tx.inputs.len() as u64));
287 for input in &tx.inputs {
288 sigmsg.extend(input.prevout.hash);
289 sigmsg.extend(input.prevout.index.to_le_bytes());
290 sigmsg.push(0);
291 sigmsg.extend((input.sequence as u32).to_le_bytes());
292 }
293 sigmsg.extend(encode_varint(tx.outputs.len() as u64));
294 for output in &tx.outputs {
295 sigmsg.extend((output.value as u64).to_le_bytes());
296 sigmsg.extend(encode_varint(output.script_pubkey.len() as u64));
297 sigmsg.extend(&output.script_pubkey);
298 }
299 sigmsg.extend((tx.lock_time as u32).to_le_bytes());
300 sigmsg.extend((sighash_type as u32).to_le_bytes());
301 sigmsg.extend((input_index as u32).to_le_bytes());
302 if input_index < prevout_values.len() {
303 sigmsg.extend((prevout_values[input_index] as u64).to_le_bytes());
304 } else {
305 sigmsg.extend([0u8; 8]);
306 }
307 if input_index < prevout_script_pubkeys.len() {
308 sigmsg.extend(encode_varint(
309 prevout_script_pubkeys[input_index].len() as u64
310 ));
311 sigmsg.extend(prevout_script_pubkeys[input_index]);
312 } else {
313 sigmsg.push(0);
314 }
315 let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
316 sigmsg.extend(codesep_pos.to_le_bytes());
317 sigmsg.push(0x00);
318 sigmsg.extend(tapleaf_hash);
319 let mut tagged_input = Vec::with_capacity(1 + sigmsg.len());
320 tagged_input.push(0x00);
321 tagged_input.extend(sigmsg);
322 Ok(crate::secp256k1_backend::tap_sighash_hash(&tagged_input))
323}
324
325fn encode_varint(value: u64) -> Vec<u8> {
327 if value < 0xfd {
328 vec![value as u8]
329 } else if value <= 0xffff {
330 let mut result = vec![0xfd];
331 result.extend_from_slice(&(value as u16).to_le_bytes());
332 result
333 } else if value <= 0xffffffff {
334 let mut result = vec![0xfe];
335 result.extend_from_slice(&(value as u32).to_le_bytes());
336 result
337 } else {
338 let mut result = vec![0xff];
339 result.extend_from_slice(&value.to_le_bytes());
340 result
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347
348 #[test]
349 fn test_validate_taproot_script_valid() {
350 let script = create_taproot_script(&[1u8; 32]);
351 assert!(validate_taproot_script(&script).unwrap());
352 }
353
354 #[test]
355 fn test_validate_taproot_script_invalid_length() {
356 let script = vec![0x51, 0x20]; assert!(!validate_taproot_script(&script).unwrap());
358 }
359
360 #[test]
361 fn test_validate_taproot_script_invalid_prefix() {
362 let mut script = vec![0x52]; script.extend_from_slice(&[1u8; 32]);
364 assert!(!validate_taproot_script(&script).unwrap());
365 }
366
367 #[test]
368 fn test_extract_taproot_output_key() {
369 let expected_key = [1u8; 32];
370 let script = create_taproot_script(&expected_key);
371
372 let extracted_key = extract_taproot_output_key(&script).unwrap();
373 assert_eq!(extracted_key, Some(expected_key));
374 }
375
376 #[test]
377 fn test_compute_taproot_tweak() {
378 let internal_pubkey = [
380 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
381 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
382 0x16, 0xf8, 0x17, 0x98,
383 ];
384 let merkle_root = [2u8; 32];
385
386 let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
387 assert_eq!(tweak.len(), 32);
388 }
389
390 #[test]
391 fn test_validate_taproot_key_aggregation() {
392 let internal_pubkey = [
394 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
395 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
396 0x16, 0xf8, 0x17, 0x98,
397 ];
398 let merkle_root = [2u8; 32];
399 let output_key = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
400
401 assert!(
402 validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, &output_key).unwrap()
403 );
404 }
405
406 #[test]
407 fn test_validate_taproot_script_path() {
408 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
410 let merkle_root =
411 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
412 .unwrap();
413
414 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
415 }
416
417 #[test]
418 fn test_is_taproot_output() {
419 let output = TransactionOutput {
420 value: 1000,
421 script_pubkey: create_taproot_script(&[1u8; 32]),
422 };
423
424 assert!(is_taproot_output(&output));
425 }
426
427 #[test]
428 fn test_validate_taproot_transaction() {
429 let tx = Transaction {
430 version: 1,
431 inputs: vec![TransactionInput {
432 prevout: OutPoint {
433 hash: [0; 32].into(),
434 index: 0,
435 },
436 script_sig: vec![],
437 sequence: 0xffffffff,
438 }]
439 .into(),
440 outputs: vec![TransactionOutput {
441 value: 1000,
442 script_pubkey: create_taproot_script(&[1u8; 32].into()),
443 }]
444 .into(),
445 lock_time: 0,
446 };
447
448 let witness = Some(vec![vec![0u8; 64]]);
450 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
451 }
452
453 #[test]
454 fn test_compute_taproot_signature_hash() {
455 let tx = Transaction {
456 version: 1,
457 inputs: vec![TransactionInput {
458 prevout: OutPoint {
459 hash: [0; 32].into(),
460 index: 0,
461 },
462 script_sig: vec![],
463 sequence: 0xffffffff,
464 }]
465 .into(),
466 outputs: vec![TransactionOutput {
467 value: 1000,
468 script_pubkey: vec![0x51].into(),
469 }]
470 .into(),
471 lock_time: 0,
472 };
473
474 let prevouts = vec![TransactionOutput {
475 value: 2000,
476 script_pubkey: create_taproot_script(&[1u8; 32]),
477 }];
478 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
479 let psp: Vec<&[u8]> = prevouts
480 .iter()
481 .map(|p| p.script_pubkey.as_slice())
482 .collect();
483 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01).unwrap();
484 assert_eq!(sig_hash.len(), 32);
485 }
486
487 #[test]
488 fn test_compute_taproot_signature_hash_invalid_input_index() {
489 let tx = Transaction {
490 version: 1,
491 inputs: vec![TransactionInput {
492 prevout: OutPoint {
493 hash: [0; 32].into(),
494 index: 0,
495 },
496 script_sig: vec![],
497 sequence: 0xffffffff,
498 }]
499 .into(),
500 outputs: vec![TransactionOutput {
501 value: 1000,
502 script_pubkey: vec![0x51].into(),
503 }]
504 .into(),
505 lock_time: 0,
506 };
507
508 let prevouts = vec![TransactionOutput {
509 value: 2000,
510 script_pubkey: create_taproot_script(&[1u8; 32]),
511 }];
512 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
513 let psp: Vec<&[u8]> = prevouts
514 .iter()
515 .map(|p| p.script_pubkey.as_slice())
516 .collect();
517 let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01).unwrap();
519 assert_eq!(sig_hash.len(), 32);
520 }
521
522 #[test]
523 fn test_compute_taproot_signature_hash_empty_prevouts() {
524 let tx = Transaction {
525 version: 1,
526 inputs: vec![TransactionInput {
527 prevout: OutPoint {
528 hash: [0; 32].into(),
529 index: 0,
530 },
531 script_sig: vec![],
532 sequence: 0xffffffff,
533 }]
534 .into(),
535 outputs: vec![TransactionOutput {
536 value: 1000,
537 script_pubkey: vec![0x51].into(),
538 }]
539 .into(),
540 lock_time: 0,
541 };
542
543 let prevouts: Vec<TransactionOutput> = vec![];
544 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
545 let psp: Vec<&[u8]> = prevouts
546 .iter()
547 .map(|p| p.script_pubkey.as_slice())
548 .collect();
549 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01).unwrap();
550 assert_eq!(sig_hash.len(), 32);
551 }
552
553 #[test]
554 fn test_compute_taproot_tweak_invalid_pubkey() {
555 let invalid_pubkey = [0u8; 32]; let merkle_root = [2u8; 32];
557
558 let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
559 assert!(result.is_err());
560 }
561
562 #[test]
563 fn test_validate_taproot_key_aggregation_invalid() {
564 let internal_pubkey = [
565 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
566 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
567 0x16, 0xf8, 0x17, 0x98,
568 ];
569 let merkle_root = [2u8; 32];
570 let wrong_output_key = [3u8; 32]; assert!(!validate_taproot_key_aggregation(
573 &internal_pubkey,
574 &merkle_root,
575 &wrong_output_key
576 )
577 .unwrap());
578 }
579
580 #[test]
581 fn test_validate_taproot_script_path_invalid() {
582 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
584 let wrong_merkle_root = [5u8; 32]; assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
587 }
588
589 #[test]
590 fn test_validate_taproot_script_path_empty_proof() {
591 let script = vec![0x51, 0x52]; let merkle_proof = vec![];
593 let merkle_root =
594 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
595 .unwrap();
596
597 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
598 }
599
600 #[test]
601 fn test_tap_leaf_hash() {
602 let script = vec![0x51, 0x52];
603 let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
604
605 assert_eq!(hash.len(), 32);
606
607 let script2 = vec![0x53, 0x54];
608 let hash2 =
609 crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
610 assert_ne!(hash, hash2);
611 }
612
613 #[test]
614 fn test_tap_branch_hash() {
615 let left = [1u8; 32];
616 let right = [2u8; 32];
617 let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
618
619 assert_eq!(hash.len(), 32);
620
621 let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
622 assert_ne!(hash, hash2);
623 }
624
625 #[test]
626 fn test_encode_varint_small() {
627 let encoded = encode_varint(0xfc);
628 assert_eq!(encoded, vec![0xfc]);
629 }
630
631 #[test]
632 fn test_encode_varint_medium() {
633 let encoded = encode_varint(0x1000);
634 assert_eq!(encoded.len(), 3);
635 assert_eq!(encoded[0], 0xfd);
636 }
637
638 #[test]
639 fn test_encode_varint_large() {
640 let encoded = encode_varint(0x1000000);
641 assert_eq!(encoded.len(), 5);
642 assert_eq!(encoded[0], 0xfe);
643 }
644
645 #[test]
646 fn test_encode_varint_huge() {
647 let encoded = encode_varint(0x1000000000000000);
648 assert_eq!(encoded.len(), 9);
649 assert_eq!(encoded[0], 0xff);
650 }
651
652 #[test]
653 fn test_extract_taproot_output_key_invalid_script() {
654 let script = vec![0x52, 0x20]; let result = extract_taproot_output_key(&script).unwrap();
656 assert!(result.is_none());
657 }
658
659 #[test]
660 fn test_is_taproot_output_false() {
661 let output = TransactionOutput {
662 value: 1000,
663 script_pubkey: vec![0x52, 0x20], };
665
666 assert!(!is_taproot_output(&output));
667 }
668
669 #[test]
670 fn test_validate_taproot_transaction_no_taproot_outputs() {
671 let tx = Transaction {
672 version: 1,
673 inputs: vec![TransactionInput {
674 prevout: OutPoint {
675 hash: [0; 32].into(),
676 index: 0,
677 },
678 script_sig: vec![],
679 sequence: 0xffffffff,
680 }]
681 .into(),
682 outputs: vec![TransactionOutput {
683 value: 1000,
684 script_pubkey: vec![0x52].into(), }]
686 .into(),
687 lock_time: 0,
688 };
689
690 assert!(validate_taproot_transaction(&tx, None).unwrap());
692 }
693
694 #[test]
695 fn test_validate_taproot_transaction_invalid_taproot_output() {
696 let tx = Transaction {
698 version: 1,
699 inputs: vec![TransactionInput {
700 prevout: OutPoint {
701 hash: [0; 32].into(),
702 index: 0,
703 },
704 script_sig: vec![],
705 sequence: 0xffffffff,
706 }]
707 .into(),
708 outputs: vec![TransactionOutput {
709 value: 1000,
710 script_pubkey: create_taproot_script(&[1u8; 32].into()),
711 }]
712 .into(),
713 lock_time: 0,
714 };
715
716 let witness = Some(vec![vec![0u8; 64]]);
718 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
719 }
720
721 fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
723 let mut script = vec![TAPROOT_SCRIPT_PREFIX];
724 script.extend_from_slice(output_key);
725 script.push(0x00); script
727 }
728}
729
730#[cfg(test)]
731mod property_tests {
732 use super::*;
733 use proptest::prelude::*;
734
735 proptest! {
740 #[test]
741 fn prop_validate_taproot_script_deterministic(
742 script in prop::collection::vec(any::<u8>(), 0..50)
743 ) {
744 let result1 = validate_taproot_script(&script).unwrap();
745 let result2 = validate_taproot_script(&script).unwrap();
746
747 assert_eq!(result1, result2);
748 }
749 }
750
751 proptest! {
757 #[test]
758 fn prop_extract_taproot_output_key_correct(
759 script in prop::collection::vec(any::<u8>(), 0..50)
760 ) {
761 let extracted_key = extract_taproot_output_key(&script).unwrap();
762 let is_valid = validate_taproot_script(&script).unwrap();
763
764 if is_valid {
765 assert!(extracted_key.is_some());
766 let key = extracted_key.unwrap();
767 assert_eq!(key.len(), 32);
768 } else {
769 assert!(extracted_key.is_none());
770 }
771 }
772 }
773
774 proptest! {
780 #[test]
781 fn prop_taproot_key_aggregation_deterministic(
782 internal_pubkey in create_pubkey_strategy(),
783 merkle_root in create_hash_strategy()
784 ) {
785 let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
786 let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
787
788 assert_eq!(result1.is_ok(), result2.is_ok());
789 if result1.is_ok() && result2.is_ok() {
790 assert_eq!(result1.unwrap(), result2.unwrap());
791 }
792 }
793 }
794
795 proptest! {
801 #[test]
802 fn prop_validate_taproot_script_path_deterministic(
803 script in prop::collection::vec(any::<u8>(), 0..20),
804 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
805 merkle_root in create_hash_strategy()
806 ) {
807 let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
808 let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
809
810 assert_eq!(result1.is_ok(), result2.is_ok());
811 if result1.is_ok() && result2.is_ok() {
812 assert_eq!(result1.unwrap(), result2.unwrap());
813 }
814 }
815 }
816
817 proptest! {
823 #[test]
824 fn prop_compute_taproot_signature_hash_deterministic(
825 tx in create_transaction_strategy(),
826 input_index in 0..10usize,
827 prevouts in prop::collection::vec(create_output_strategy(), 0..5),
828 sighash_type in any::<u8>()
829 ) {
830 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
831 let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
832 let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
833 let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type);
834
835 assert_eq!(result1.is_ok(), result2.is_ok());
836 if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
837 assert_eq!(hash1, hash2);
838 assert_eq!(hash1.len(), 32);
839 }
840
841 if let Ok(ref hash) = result1 {
843 assert_eq!(hash.len(), 32);
844 }
845 }
846 }
847
848 proptest! {
853 #[test]
854 fn prop_is_taproot_output_consistent(
855 output in create_output_strategy()
856 ) {
857 let is_taproot = is_taproot_output(&output);
858 let _ = is_taproot;
860 }
861 }
862
863 proptest! {
868 #[test]
869 fn prop_validate_taproot_transaction_deterministic(
870 tx in create_transaction_strategy()
871 ) {
872 let result1 = validate_taproot_transaction(&tx, None).unwrap();
873 let result2 = validate_taproot_transaction(&tx, None).unwrap();
874
875 assert_eq!(result1, result2);
876 }
877 }
878
879 proptest! {
881 #[test]
882 fn prop_tap_leaf_hash_deterministic(
883 script in prop::collection::vec(any::<u8>(), 0..20)
884 ) {
885 let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
886 let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
887
888 assert_eq!(hash1, hash2);
889 assert_eq!(hash1.len(), 32);
890 }
891 }
892
893 proptest! {
895 #[test]
896 fn prop_tap_branch_hash_deterministic(
897 left in create_hash_strategy(),
898 right in create_hash_strategy()
899 ) {
900 let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
901 let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
902
903 assert_eq!(hash1, hash2);
904 assert_eq!(hash1.len(), 32);
905 }
906 }
907
908 proptest! {
913 #[test]
914 fn prop_encode_varint_deterministic(
915 value in 0..u64::MAX
916 ) {
917 let encoded1 = encode_varint(value);
918 let encoded2 = encode_varint(value);
919
920 assert_eq!(encoded1, encoded2);
921
922 assert!(!encoded1.is_empty());
924 assert!(encoded1.len() <= 9);
925 }
926 }
927
928 proptest! {
933 #[test]
934 fn prop_encode_varint_preserves_value(
935 value in 0..1000000u64 ) {
937 let encoded = encode_varint(value);
938
939 match encoded.len() {
941 1 => {
942 assert!(value < 0xfd);
944 assert_eq!(encoded[0], value as u8);
945 },
946 3 => {
947 assert!((0xfd..=0xffff).contains(&value));
949 assert_eq!(encoded[0], 0xfd);
950 },
951 5 => {
952 assert!(value > 0xffff && value <= 0xffffffff);
954 assert_eq!(encoded[0], 0xfe);
955 },
956 9 => {
957 assert!(value > 0xffffffff);
959 assert_eq!(encoded[0], 0xff);
960 },
961 _ => panic!("Invalid varint encoding length"),
962 }
963 }
964 }
965
966 proptest! {
973 #[test]
974 fn prop_validate_taproot_script_path_correct_proof(
975 script in prop::collection::vec(any::<u8>(), 0..20),
976 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
977 ) {
978 let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
979 let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
980
981 assert!(is_valid);
982 }
983 }
984
985 fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
987 (
988 prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10), )
991 .prop_map(|(input_data, output_data)| {
992 let mut inputs = Vec::new();
993 for (i, _) in input_data.iter().enumerate() {
994 inputs.push(TransactionInput {
995 prevout: OutPoint {
996 hash: [0; 32],
997 index: i as u32,
998 },
999 script_sig: vec![],
1000 sequence: 0xffffffff,
1001 });
1002 }
1003
1004 let mut outputs = Vec::new();
1005 for _ in output_data {
1006 outputs.push(TransactionOutput {
1007 value: 1000,
1008 script_pubkey: vec![0x51],
1009 });
1010 }
1011
1012 Transaction {
1013 version: 1,
1014 inputs: inputs.into(),
1015 outputs: outputs.into(),
1016 lock_time: 0,
1017 }
1018 })
1019 }
1020
1021 fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1022 (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1023 TransactionOutput {
1024 value,
1025 script_pubkey: script,
1026 }
1027 })
1028 }
1029
1030 fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1031 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1032 let mut hash = [0u8; 32];
1033 hash.copy_from_slice(&bytes);
1034 hash
1035 })
1036 }
1037
1038 fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1039 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1040 let mut pubkey = [0u8; 32];
1041 pubkey.copy_from_slice(&bytes);
1042 pubkey
1043 })
1044 }
1045}