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