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