1use std::borrow::Cow;
4
5use crate::error::Result;
6use crate::types::*;
7use crate::types::{ByteString, Hash};
8use crate::witness;
9use blvm_spec_lock::spec_locked;
10
11pub const TAPROOT_LEAF_VERSION_TAPSCRIPT: u8 = 0xc0;
13
14pub use crate::witness::Witness;
18
19use crate::opcodes::{OP_1, PUSH_32_BYTES};
20
21pub const TAPROOT_SCRIPT_PREFIX: u8 = OP_1;
23
24#[spec_locked("11.2.1", "ValidateTaprootScript")]
26pub fn validate_taproot_script(script: &ByteString) -> Result<bool> {
27 use crate::constants::TAPROOT_SCRIPT_LENGTH;
28
29 if script.len() != TAPROOT_SCRIPT_LENGTH {
31 return Ok(false);
32 }
33
34 if script[0] != TAPROOT_SCRIPT_PREFIX {
35 return Ok(false);
36 }
37
38 if script[1] != PUSH_32_BYTES {
40 return Ok(false);
41 }
42
43 Ok(true)
44}
45
46#[spec_locked("11.2.1", "ExtractTaprootOutputKey")]
48pub fn extract_taproot_output_key(script: &ByteString) -> Result<Option<[u8; 32]>> {
49 if !validate_taproot_script(script)? {
50 return Ok(None);
51 }
52
53 let mut output_key = [0u8; 32];
54 output_key.copy_from_slice(&script[2..34]);
55 Ok(Some(output_key))
56}
57
58#[spec_locked("11.2.2", "ComputeTaprootTweak")]
64pub fn compute_taproot_tweak(internal_pubkey: &[u8; 32], merkle_root: &Hash) -> Result<[u8; 32]> {
65 crate::secp256k1_backend::taproot_output_key(internal_pubkey, merkle_root)
66}
67
68#[spec_locked("11.2.2", "ValidateTaprootKeyAggregation")]
73pub fn validate_taproot_key_aggregation(
74 internal_pubkey: &[u8; 32],
75 merkle_root: &Hash,
76 output_key: &[u8; 32],
77 expected_parity: u8,
78) -> Result<bool> {
79 let (computed_key, actual_parity) =
80 crate::secp256k1_backend::taproot_output_key_with_parity(internal_pubkey, merkle_root)?;
81 Ok(computed_key == *output_key && actual_parity == expected_parity)
82}
83
84#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
86pub fn validate_taproot_script_path(
87 script: &ByteString,
88 merkle_proof: &[Hash],
89 merkle_root: &Hash,
90) -> Result<bool> {
91 validate_taproot_script_path_with_leaf_version(
92 script,
93 merkle_proof,
94 merkle_root,
95 TAPROOT_LEAF_VERSION_TAPSCRIPT,
96 )
97}
98
99#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
101pub fn validate_taproot_script_path_with_leaf_version(
102 script: &ByteString,
103 merkle_proof: &[Hash],
104 merkle_root: &Hash,
105 leaf_version: u8,
106) -> Result<bool> {
107 let computed_root = compute_script_merkle_root(script, merkle_proof, leaf_version)?;
108 Ok(computed_root == *merkle_root)
109}
110
111#[spec_locked("11.2.3", "ComputeScriptMerkleRoot")]
113pub fn compute_script_merkle_root(
114 script: &ByteString,
115 proof: &[Hash],
116 leaf_version: u8,
117) -> Result<Hash> {
118 let mut current_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, script);
119
120 for proof_hash in proof {
121 let (left, right) = if current_hash < *proof_hash {
122 (current_hash, *proof_hash)
123 } else {
124 (*proof_hash, current_hash)
125 };
126 current_hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
127 }
128
129 Ok(current_hash)
130}
131
132pub const TAPROOT_ANNEX_TAG: u8 = 0x50;
134
135#[spec_locked("11.2.4", "StripTaprootAnnex")]
142pub fn strip_taproot_annex(witness: &Witness) -> (Cow<'_, Witness>, Option<Hash>) {
143 if witness.len() >= 2
144 && witness
145 .last()
146 .and_then(|a| a.first())
147 .is_some_and(|&b| b == TAPROOT_ANNEX_TAG)
148 {
149 let annex = &witness[witness.len() - 1];
150 let annex_hash = compute_taproot_annex_sighash_hash(annex);
151 let stripped = witness[..witness.len() - 1].to_vec();
152 return (Cow::Owned(stripped), Some(annex_hash));
153 }
154 (Cow::Borrowed(witness), None)
155}
156
157fn compute_taproot_annex_sighash_hash(annex: &[u8]) -> Hash {
159 use sha2::{Digest, Sha256};
160 let mut buf = encode_varint(annex.len() as u64);
161 buf.extend_from_slice(annex);
162 let digest = Sha256::digest(&buf);
163 let mut out = [0u8; 32];
164 out.copy_from_slice(&digest);
165 out
166}
167
168#[derive(Debug)]
170pub struct TaprootControlBlock {
171 pub leaf_version: u8,
172 pub internal_pubkey: [u8; 32],
173 pub merkle_proof: Vec<Hash>,
174}
175
176#[spec_locked("11.2.4", "ParseTaprootScriptPathWitness")]
182pub fn parse_taproot_script_path_witness(
183 witness: &Witness,
184 output_key: &[u8; 32],
185) -> Result<Option<(ByteString, Vec<ByteString>, TaprootControlBlock)>> {
186 if witness.len() < 2 {
187 return Ok(None);
188 }
189
190 let Some(control_block) = witness.last() else {
191 return Ok(None);
192 };
193 if control_block.len() < 33 || (control_block.len() - 33) % 32 != 0 {
194 return Ok(None);
195 }
196
197 let leaf_version = control_block[0] & 0xfe;
200 let parity = control_block[0] & 0x01;
201 let mut internal_pubkey = [0u8; 32];
202 internal_pubkey.copy_from_slice(&control_block[1..33]);
203 let merkle_proof: Vec<Hash> = control_block[33..]
204 .chunks_exact(32)
205 .map(|c| {
206 let mut h = [0u8; 32];
207 h.copy_from_slice(c);
208 h
209 })
210 .collect();
211
212 let script_idx = if witness.len() >= 3 {
213 let maybe_annex = &witness[witness.len() - 2];
214 if maybe_annex.first() == Some(&0x50) {
215 witness.len() - 3
216 } else {
217 witness.len() - 2
218 }
219 } else {
220 witness.len() - 2
221 };
222
223 let tapscript = witness[script_idx].clone();
224 let stack_items: Vec<ByteString> = witness[..script_idx].to_vec();
225
226 let merkle_root = match compute_script_merkle_root(&tapscript, &merkle_proof, leaf_version) {
227 Ok(root) => root,
228 Err(_) => return Ok(None),
229 };
230 let valid = match validate_taproot_key_aggregation(
232 &internal_pubkey,
233 &merkle_root,
234 output_key,
235 parity,
236 ) {
237 Ok(v) => v,
238 Err(_) => return Ok(None),
239 };
240 if !valid {
241 return Ok(None);
242 }
243
244 Ok(Some((
245 tapscript,
246 stack_items,
247 TaprootControlBlock {
248 leaf_version, internal_pubkey,
250 merkle_proof,
251 },
252 )))
253}
254
255#[spec_locked("11.2.1", "IsTaprootOutput")]
259#[blvm_spec_lock::ensures(result == false || output.script_pubkey.len() == 34)]
260pub fn is_taproot_output(output: &TransactionOutput) -> bool {
261 validate_taproot_script(&output.script_pubkey).unwrap_or(false)
262}
263
264#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
266pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
267 for output in &tx.outputs {
269 if is_taproot_output(output) {
270 if !validate_taproot_script(&output.script_pubkey)? {
272 return Ok(false);
273 }
274 }
275 }
276
277 if let Some(w) = witness {
280 let (body, _) = strip_taproot_annex(w);
281 let is_script_path = body.len() >= 2;
282 if !witness::validate_taproot_witness_structure(&body, is_script_path)? {
283 return Ok(false);
284 }
285 }
286
287 Ok(true)
288}
289
290fn bip341_precompute(
295 tx: &Transaction,
296 prevout_values: &[i64],
297 prevout_script_pubkeys: &[&[u8]],
298) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
299 use sha2::{Digest, Sha256};
300
301 let mut prevouts_data = Vec::new();
302 let mut amounts_data = Vec::new();
303 let mut scriptpubkeys_data = Vec::new();
304 let mut sequences_data = Vec::new();
305 for (i, input) in tx.inputs.iter().enumerate() {
306 prevouts_data.extend_from_slice(&input.prevout.hash);
307 prevouts_data.extend_from_slice(&input.prevout.index.to_le_bytes());
308 amounts_data.extend_from_slice(&(prevout_values[i] as u64).to_le_bytes());
312 let spk = prevout_script_pubkeys[i];
313 scriptpubkeys_data.extend_from_slice(&encode_varint(spk.len() as u64));
314 scriptpubkeys_data.extend_from_slice(spk);
315 sequences_data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
316 }
317 let mut outputs_data = Vec::new();
318 for output in &tx.outputs {
319 outputs_data.extend_from_slice(&(output.value as u64).to_le_bytes());
320 outputs_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
321 outputs_data.extend_from_slice(&output.script_pubkey);
322 }
323
324 (
325 Sha256::digest(&prevouts_data).into(),
326 Sha256::digest(&amounts_data).into(),
327 Sha256::digest(&scriptpubkeys_data).into(),
328 Sha256::digest(&sequences_data).into(),
329 Sha256::digest(&outputs_data).into(),
330 )
331}
332
333#[spec_locked("11.2.6", "ComputeTaprootSignatureHash")]
344pub fn compute_taproot_signature_hash(
345 tx: &Transaction,
346 input_index: usize,
347 prevout_values: &[i64],
348 prevout_script_pubkeys: &[&[u8]],
349 sighash_type: u8,
350 annex_hash: Option<&Hash>,
351) -> Result<Hash> {
352 use sha2::{Digest, Sha256};
353
354 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
357 return Err(crate::error::ConsensusError::InvalidSignature(
358 "Invalid Taproot sighash type".into(),
359 ));
360 }
361
362 if prevout_values.len() < tx.inputs.len() || prevout_script_pubkeys.len() < tx.inputs.len() {
366 return Err(crate::error::ConsensusError::InvalidSignature(
367 "prevout_values/prevout_script_pubkeys shorter than tx.inputs β cannot compute sighash"
368 .into(),
369 ));
370 }
371
372 let input_type = sighash_type & 0x80; let output_type = if sighash_type == 0x00 {
374 0x01
375 } else {
376 sighash_type & 0x03
377 }; let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
380 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
381
382 let mut sigmsg = Vec::with_capacity(180);
383
384 sigmsg.push(0x00u8);
386 sigmsg.push(sighash_type);
388 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
390 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
392
393 if input_type != 0x80 {
394 sigmsg.extend_from_slice(&sha_prevouts);
396 sigmsg.extend_from_slice(&sha_amounts);
397 sigmsg.extend_from_slice(&sha_scriptpubkeys);
398 sigmsg.extend_from_slice(&sha_sequences);
399 }
400 if output_type == 0x01 {
401 sigmsg.extend_from_slice(&sha_outputs);
403 }
404
405 let spend_type = if annex_hash.is_some() { 0x01u8 } else { 0x00u8 };
407 sigmsg.push(spend_type);
408
409 if input_type == 0x80 {
410 let input = tx.inputs.get(input_index).ok_or_else(|| {
412 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
413 })?;
414 sigmsg.extend_from_slice(&input.prevout.hash);
415 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
416 let amount = *prevout_values.get(input_index).ok_or_else(|| {
418 crate::error::ConsensusError::InvalidSignature(
419 "prevout_values index out of range for ANYONECANPAY".into(),
420 )
421 })? as u64;
422 sigmsg.extend_from_slice(&amount.to_le_bytes());
423 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
424 crate::error::ConsensusError::InvalidSignature(
425 "prevout_script_pubkeys index out of range for ANYONECANPAY".into(),
426 )
427 })?;
428 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
429 sigmsg.extend_from_slice(spk);
430 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
431 } else {
432 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
434 }
435
436 if let Some(h) = annex_hash {
437 sigmsg.extend_from_slice(h);
438 }
439
440 if output_type == 0x03 {
441 if let Some(output) = tx.outputs.get(input_index) {
443 let mut out_data = Vec::new();
444 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
445 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
446 out_data.extend_from_slice(&output.script_pubkey);
447 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
448 }
449 }
450
451 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
452}
453
454#[spec_locked("11.2.7", "ComputeTapscriptSignatureHash")]
459pub fn compute_tapscript_signature_hash(
460 tx: &Transaction,
461 input_index: usize,
462 prevout_values: &[i64],
463 prevout_script_pubkeys: &[&[u8]],
464 tapscript: &[u8],
465 leaf_version: u8,
466 codesep_pos: u32,
467 sighash_type: u8,
468 annex_hash: Option<&Hash>,
469) -> Result<Hash> {
470 use sha2::{Digest, Sha256};
471
472 if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
473 return Err(crate::error::ConsensusError::InvalidSignature(
474 "Invalid Tapscript sighash type".into(),
475 ));
476 }
477
478 let input_type = sighash_type & 0x80;
479 let output_type = if sighash_type == 0x00 {
480 0x01
481 } else {
482 sighash_type & 0x03
483 };
484
485 let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
486 bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
487
488 let mut sigmsg = Vec::with_capacity(250);
489
490 sigmsg.push(0x00u8);
492 sigmsg.push(sighash_type);
494 sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
496 sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
498
499 if input_type != 0x80 {
500 sigmsg.extend_from_slice(&sha_prevouts);
501 sigmsg.extend_from_slice(&sha_amounts);
502 sigmsg.extend_from_slice(&sha_scriptpubkeys);
503 sigmsg.extend_from_slice(&sha_sequences);
504 }
505 if output_type == 0x01 {
506 sigmsg.extend_from_slice(&sha_outputs);
507 }
508
509 let spend_type = if annex_hash.is_some() { 0x03u8 } else { 0x02u8 };
511 sigmsg.push(spend_type);
512
513 if input_type == 0x80 {
514 let input = tx.inputs.get(input_index).ok_or_else(|| {
515 crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
516 })?;
517 sigmsg.extend_from_slice(&input.prevout.hash);
518 sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
519 let amount = *prevout_values.get(input_index).ok_or_else(|| {
520 crate::error::ConsensusError::InvalidSignature(
521 "prevout_values index out of range for script-path ANYONECANPAY".into(),
522 )
523 })? as u64;
524 sigmsg.extend_from_slice(&amount.to_le_bytes());
525 let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
526 crate::error::ConsensusError::InvalidSignature(
527 "prevout_script_pubkeys index out of range for script-path ANYONECANPAY".into(),
528 )
529 })?;
530 sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
531 sigmsg.extend_from_slice(spk);
532 sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
533 } else {
534 sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
535 }
536
537 if let Some(h) = annex_hash {
538 sigmsg.extend_from_slice(h);
539 }
540
541 if output_type == 0x03 {
542 if let Some(output) = tx.outputs.get(input_index) {
543 let mut out_data = Vec::new();
544 out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
545 out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
546 out_data.extend_from_slice(&output.script_pubkey);
547 sigmsg.extend_from_slice(&Sha256::digest(&out_data));
548 }
549 }
550
551 let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
553 sigmsg.extend_from_slice(&tapleaf_hash);
554 sigmsg.push(0x00u8); sigmsg.extend_from_slice(&codesep_pos.to_le_bytes());
556
557 Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
558}
559
560fn encode_varint(value: u64) -> Vec<u8> {
562 if value < 0xfd {
563 vec![value as u8]
564 } else if value <= 0xffff {
565 let mut result = vec![0xfd];
566 result.extend_from_slice(&(value as u16).to_le_bytes());
567 result
568 } else if value <= 0xffffffff {
569 let mut result = vec![0xfe];
570 result.extend_from_slice(&(value as u32).to_le_bytes());
571 result
572 } else {
573 let mut result = vec![0xff];
574 result.extend_from_slice(&value.to_le_bytes());
575 result
576 }
577}
578
579#[cfg(test)]
580mod tests {
581 use super::*;
582
583 #[test]
584 fn test_validate_taproot_script_valid() {
585 let script = create_taproot_script(&[1u8; 32]);
586 assert!(validate_taproot_script(&script).unwrap());
587 }
588
589 #[test]
590 fn test_validate_taproot_script_invalid_length() {
591 let script = vec![0x51, 0x20]; assert!(!validate_taproot_script(&script).unwrap());
593 }
594
595 #[test]
596 fn test_validate_taproot_script_invalid_prefix() {
597 let mut script = vec![0x52]; script.extend_from_slice(&[1u8; 32]);
599 assert!(!validate_taproot_script(&script).unwrap());
600 }
601
602 #[test]
603 fn test_extract_taproot_output_key() {
604 let expected_key = [1u8; 32];
605 let script = create_taproot_script(&expected_key);
606
607 let extracted_key = extract_taproot_output_key(&script).unwrap();
608 assert_eq!(extracted_key, Some(expected_key));
609 }
610
611 #[test]
612 fn test_compute_taproot_tweak() {
613 let internal_pubkey = [
615 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
616 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
617 0x16, 0xf8, 0x17, 0x98,
618 ];
619 let merkle_root = [2u8; 32];
620
621 let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
622 assert_eq!(tweak.len(), 32);
623 }
624
625 #[test]
626 fn test_validate_taproot_key_aggregation() {
627 let internal_pubkey = [
629 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
630 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
631 0x16, 0xf8, 0x17, 0x98,
632 ];
633 let merkle_root = [2u8; 32];
634 let (output_key, parity) = crate::secp256k1_backend::taproot_output_key_with_parity(
635 &internal_pubkey,
636 &merkle_root,
637 )
638 .unwrap();
639
640 assert!(
641 validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, &output_key, parity)
642 .unwrap()
643 );
644 }
645
646 #[test]
647 fn test_validate_taproot_script_path() {
648 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
650 let merkle_root =
651 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
652 .unwrap();
653
654 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
655 }
656
657 #[test]
658 fn test_validate_taproot_script_requires_push_opcode() {
659 let mut bad = vec![TAPROOT_SCRIPT_PREFIX];
661 bad.extend_from_slice(&[0u8; 33]);
662 assert!(!validate_taproot_script(&bad).unwrap());
663
664 assert!(validate_taproot_script(&create_taproot_script(&[1u8; 32])).unwrap());
665 }
666
667 #[test]
668 fn test_is_taproot_output() {
669 let output = TransactionOutput {
670 value: 1000,
671 script_pubkey: create_taproot_script(&[1u8; 32]),
672 };
673
674 assert!(is_taproot_output(&output));
675 }
676
677 #[test]
678 fn test_validate_taproot_transaction() {
679 let tx = Transaction {
680 version: 1,
681 inputs: vec![TransactionInput {
682 prevout: OutPoint {
683 hash: [0; 32],
684 index: 0,
685 },
686 script_sig: vec![],
687 sequence: 0xffffffff,
688 }]
689 .into(),
690 outputs: vec![TransactionOutput {
691 value: 1000,
692 script_pubkey: create_taproot_script(&[1u8; 32]),
693 }]
694 .into(),
695 lock_time: 0,
696 };
697
698 let witness = Some(vec![vec![0u8; 64]]);
700 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
701 }
702
703 #[test]
704 fn test_compute_taproot_signature_hash() {
705 let tx = Transaction {
706 version: 1,
707 inputs: vec![TransactionInput {
708 prevout: OutPoint {
709 hash: [0; 32],
710 index: 0,
711 },
712 script_sig: vec![],
713 sequence: 0xffffffff,
714 }]
715 .into(),
716 outputs: vec![TransactionOutput {
717 value: 1000,
718 script_pubkey: vec![0x51],
719 }]
720 .into(),
721 lock_time: 0,
722 };
723
724 let prevouts = [TransactionOutput {
725 value: 2000,
726 script_pubkey: create_taproot_script(&[1u8; 32]),
727 }];
728 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
729 let psp: Vec<&[u8]> = prevouts
730 .iter()
731 .map(|p| p.script_pubkey.as_slice())
732 .collect();
733 let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None).unwrap();
734 assert_eq!(sig_hash.len(), 32);
735 }
736
737 #[test]
738 fn test_compute_taproot_signature_hash_invalid_input_index() {
739 let tx = Transaction {
740 version: 1,
741 inputs: vec![TransactionInput {
742 prevout: OutPoint {
743 hash: [0; 32],
744 index: 0,
745 },
746 script_sig: vec![],
747 sequence: 0xffffffff,
748 }]
749 .into(),
750 outputs: vec![TransactionOutput {
751 value: 1000,
752 script_pubkey: vec![0x51],
753 }]
754 .into(),
755 lock_time: 0,
756 };
757
758 let prevouts = [TransactionOutput {
759 value: 2000,
760 script_pubkey: create_taproot_script(&[1u8; 32]),
761 }];
762 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
763 let psp: Vec<&[u8]> = prevouts
764 .iter()
765 .map(|p| p.script_pubkey.as_slice())
766 .collect();
767 let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01, None).unwrap();
769 assert_eq!(sig_hash.len(), 32);
770 }
771
772 #[test]
773 fn test_compute_taproot_signature_hash_empty_prevouts() {
774 let tx = Transaction {
775 version: 1,
776 inputs: vec![TransactionInput {
777 prevout: OutPoint {
778 hash: [0; 32],
779 index: 0,
780 },
781 script_sig: vec![],
782 sequence: 0xffffffff,
783 }]
784 .into(),
785 outputs: vec![TransactionOutput {
786 value: 1000,
787 script_pubkey: vec![0x51],
788 }]
789 .into(),
790 lock_time: 0,
791 };
792
793 let prevouts: Vec<TransactionOutput> = vec![];
796 let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
797 let psp: Vec<&[u8]> = prevouts
798 .iter()
799 .map(|p| p.script_pubkey.as_slice())
800 .collect();
801 let result = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None);
802 assert!(
803 result.is_err(),
804 "empty prevouts shorter than tx.inputs must return Err, not silently use zeros"
805 );
806 }
807
808 #[test]
809 fn test_compute_taproot_tweak_invalid_pubkey() {
810 let invalid_pubkey = [0u8; 32]; let merkle_root = [2u8; 32];
812
813 let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
814 assert!(result.is_err());
815 }
816
817 #[test]
818 fn test_validate_taproot_key_aggregation_invalid() {
819 let internal_pubkey = [
820 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
821 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
822 0x16, 0xf8, 0x17, 0x98,
823 ];
824 let merkle_root = [2u8; 32];
825 let wrong_output_key = [3u8; 32]; assert!(
828 !validate_taproot_key_aggregation(
829 &internal_pubkey,
830 &merkle_root,
831 &wrong_output_key,
832 0, )
834 .unwrap()
835 );
836 }
837
838 #[test]
839 fn test_validate_taproot_script_path_invalid() {
840 let script = vec![0x51, 0x52]; let merkle_proof = vec![[3u8; 32], [4u8; 32]];
842 let wrong_merkle_root = [5u8; 32]; assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
845 }
846
847 #[test]
848 fn test_validate_taproot_script_path_empty_proof() {
849 let script = vec![0x51, 0x52]; let merkle_proof = vec![];
851 let merkle_root =
852 compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
853 .unwrap();
854
855 assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
856 }
857
858 #[test]
859 fn test_tap_leaf_hash() {
860 let script = vec![0x51, 0x52];
861 let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
862
863 assert_eq!(hash.len(), 32);
864
865 let script2 = vec![0x53, 0x54];
866 let hash2 =
867 crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
868 assert_ne!(hash, hash2);
869 }
870
871 #[test]
872 fn test_tap_branch_hash() {
873 let left = [1u8; 32];
874 let right = [2u8; 32];
875 let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
876
877 assert_eq!(hash.len(), 32);
878
879 let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
880 assert_ne!(hash, hash2);
881 }
882
883 #[test]
884 fn test_encode_varint_small() {
885 let encoded = encode_varint(0xfc);
886 assert_eq!(encoded, vec![0xfc]);
887 }
888
889 #[test]
890 fn test_encode_varint_medium() {
891 let encoded = encode_varint(0x1000);
892 assert_eq!(encoded.len(), 3);
893 assert_eq!(encoded[0], 0xfd);
894 }
895
896 #[test]
897 fn test_encode_varint_large() {
898 let encoded = encode_varint(0x1000000);
899 assert_eq!(encoded.len(), 5);
900 assert_eq!(encoded[0], 0xfe);
901 }
902
903 #[test]
904 fn test_encode_varint_huge() {
905 let encoded = encode_varint(0x1000000000000000);
906 assert_eq!(encoded.len(), 9);
907 assert_eq!(encoded[0], 0xff);
908 }
909
910 #[test]
911 fn test_extract_taproot_output_key_invalid_script() {
912 let script = vec![0x52, 0x20]; let result = extract_taproot_output_key(&script).unwrap();
914 assert!(result.is_none());
915 }
916
917 #[test]
918 fn test_is_taproot_output_false() {
919 let output = TransactionOutput {
920 value: 1000,
921 script_pubkey: vec![0x52, 0x20], };
923
924 assert!(!is_taproot_output(&output));
925 }
926
927 #[test]
928 fn test_validate_taproot_transaction_no_taproot_outputs() {
929 let tx = Transaction {
930 version: 1,
931 inputs: vec![TransactionInput {
932 prevout: OutPoint {
933 hash: [0; 32],
934 index: 0,
935 },
936 script_sig: vec![],
937 sequence: 0xffffffff,
938 }]
939 .into(),
940 outputs: vec![TransactionOutput {
941 value: 1000,
942 script_pubkey: vec![0x52], }]
944 .into(),
945 lock_time: 0,
946 };
947
948 assert!(validate_taproot_transaction(&tx, None).unwrap());
950 }
951
952 #[test]
953 fn test_validate_taproot_transaction_invalid_taproot_output() {
954 let tx = Transaction {
956 version: 1,
957 inputs: vec![TransactionInput {
958 prevout: OutPoint {
959 hash: [0; 32],
960 index: 0,
961 },
962 script_sig: vec![],
963 sequence: 0xffffffff,
964 }]
965 .into(),
966 outputs: vec![TransactionOutput {
967 value: 1000,
968 script_pubkey: create_taproot_script(&[1u8; 32]),
969 }]
970 .into(),
971 lock_time: 0,
972 };
973
974 let witness = Some(vec![vec![0u8; 64]]);
976 assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
977 }
978
979 fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
981 let mut script = vec![TAPROOT_SCRIPT_PREFIX, PUSH_32_BYTES];
982 script.extend_from_slice(output_key);
983 script
984 }
985}
986
987#[cfg(test)]
988mod property_tests {
989 use super::*;
990 use proptest::prelude::*;
991
992 proptest! {
997 #[test]
998 fn prop_validate_taproot_script_deterministic(
999 script in prop::collection::vec(any::<u8>(), 0..50)
1000 ) {
1001 let result1 = validate_taproot_script(&script).unwrap();
1002 let result2 = validate_taproot_script(&script).unwrap();
1003
1004 assert_eq!(result1, result2);
1005 }
1006 }
1007
1008 proptest! {
1014 #[test]
1015 fn prop_extract_taproot_output_key_correct(
1016 script in prop::collection::vec(any::<u8>(), 0..50)
1017 ) {
1018 let extracted_key = extract_taproot_output_key(&script).unwrap();
1019 let is_valid = validate_taproot_script(&script).unwrap();
1020
1021 if is_valid {
1022 assert!(extracted_key.is_some());
1023 let key = extracted_key.unwrap();
1024 assert_eq!(key.len(), 32);
1025 } else {
1026 assert!(extracted_key.is_none());
1027 }
1028 }
1029 }
1030
1031 proptest! {
1037 #[test]
1038 fn prop_taproot_key_aggregation_deterministic(
1039 internal_pubkey in create_pubkey_strategy(),
1040 merkle_root in create_hash_strategy()
1041 ) {
1042 let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1043 let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1044
1045 assert_eq!(result1.is_ok(), result2.is_ok());
1046 if result1.is_ok() && result2.is_ok() {
1047 assert_eq!(result1.unwrap(), result2.unwrap());
1048 }
1049 }
1050 }
1051
1052 proptest! {
1058 #[test]
1059 fn prop_validate_taproot_script_path_deterministic(
1060 script in prop::collection::vec(any::<u8>(), 0..20),
1061 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
1062 merkle_root in create_hash_strategy()
1063 ) {
1064 let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1065 let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1066
1067 assert_eq!(result1.is_ok(), result2.is_ok());
1068 if result1.is_ok() && result2.is_ok() {
1069 assert_eq!(result1.unwrap(), result2.unwrap());
1070 }
1071 }
1072 }
1073
1074 proptest! {
1080 #[test]
1081 fn prop_compute_taproot_signature_hash_deterministic(
1082 tx in create_transaction_strategy(),
1083 input_index in 0..10usize,
1084 prevouts in prop::collection::vec(create_output_strategy(), 0..5),
1085 sighash_type in any::<u8>()
1086 ) {
1087 let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
1088 let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
1089 let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1090 let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1091
1092 assert_eq!(result1.is_ok(), result2.is_ok());
1093 if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
1094 assert_eq!(hash1, hash2);
1095 assert_eq!(hash1.len(), 32);
1096 }
1097
1098 if let Ok(ref hash) = result1 {
1100 assert_eq!(hash.len(), 32);
1101 }
1102 }
1103 }
1104
1105 proptest! {
1110 #[test]
1111 fn prop_is_taproot_output_consistent(
1112 output in create_output_strategy()
1113 ) {
1114 let is_taproot = is_taproot_output(&output);
1115 let _ = is_taproot;
1117 }
1118 }
1119
1120 proptest! {
1125 #[test]
1126 fn prop_validate_taproot_transaction_deterministic(
1127 tx in create_transaction_strategy()
1128 ) {
1129 let result1 = validate_taproot_transaction(&tx, None).unwrap();
1130 let result2 = validate_taproot_transaction(&tx, None).unwrap();
1131
1132 assert_eq!(result1, result2);
1133 }
1134 }
1135
1136 proptest! {
1138 #[test]
1139 fn prop_tap_leaf_hash_deterministic(
1140 script in prop::collection::vec(any::<u8>(), 0..20)
1141 ) {
1142 let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1143 let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1144
1145 assert_eq!(hash1, hash2);
1146 assert_eq!(hash1.len(), 32);
1147 }
1148 }
1149
1150 proptest! {
1152 #[test]
1153 fn prop_tap_branch_hash_deterministic(
1154 left in create_hash_strategy(),
1155 right in create_hash_strategy()
1156 ) {
1157 let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1158 let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1159
1160 assert_eq!(hash1, hash2);
1161 assert_eq!(hash1.len(), 32);
1162 }
1163 }
1164
1165 proptest! {
1170 #[test]
1171 fn prop_encode_varint_deterministic(
1172 value in 0..u64::MAX
1173 ) {
1174 let encoded1 = encode_varint(value);
1175 let encoded2 = encode_varint(value);
1176
1177 assert_eq!(encoded1, encoded2);
1178
1179 assert!(!encoded1.is_empty());
1181 assert!(encoded1.len() <= 9);
1182 }
1183 }
1184
1185 proptest! {
1190 #[test]
1191 fn prop_encode_varint_preserves_value(
1192 value in 0..1000000u64 ) {
1194 let encoded = encode_varint(value);
1195
1196 match encoded.len() {
1198 1 => {
1199 assert!(value < 0xfd);
1201 assert_eq!(encoded[0], value as u8);
1202 },
1203 3 => {
1204 assert!((0xfd..=0xffff).contains(&value));
1206 assert_eq!(encoded[0], 0xfd);
1207 },
1208 5 => {
1209 assert!(value > 0xffff && value <= 0xffffffff);
1211 assert_eq!(encoded[0], 0xfe);
1212 },
1213 9 => {
1214 assert!(value > 0xffffffff);
1216 assert_eq!(encoded[0], 0xff);
1217 },
1218 _ => panic!("Invalid varint encoding length"),
1219 }
1220 }
1221 }
1222
1223 proptest! {
1230 #[test]
1231 fn prop_validate_taproot_script_path_correct_proof(
1232 script in prop::collection::vec(any::<u8>(), 0..20),
1233 merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
1234 ) {
1235 let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
1236 let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
1237
1238 assert!(is_valid);
1239 }
1240 }
1241
1242 fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
1244 (
1245 prop::collection::vec(any::<u8>(), 0..10), prop::collection::vec(any::<u8>(), 0..10), )
1248 .prop_map(|(input_data, output_data)| {
1249 let mut inputs = Vec::new();
1250 for (i, _) in input_data.iter().enumerate() {
1251 inputs.push(TransactionInput {
1252 prevout: OutPoint {
1253 hash: [0; 32],
1254 index: i as u32,
1255 },
1256 script_sig: vec![],
1257 sequence: 0xffffffff,
1258 });
1259 }
1260
1261 let mut outputs = Vec::new();
1262 for _ in output_data {
1263 outputs.push(TransactionOutput {
1264 value: 1000,
1265 script_pubkey: vec![0x51],
1266 });
1267 }
1268
1269 Transaction {
1270 version: 1,
1271 inputs: inputs.into(),
1272 outputs: outputs.into(),
1273 lock_time: 0,
1274 }
1275 })
1276 }
1277
1278 fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1279 (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1280 TransactionOutput {
1281 value,
1282 script_pubkey: script,
1283 }
1284 })
1285 }
1286
1287 fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1288 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1289 let mut hash = [0u8; 32];
1290 hash.copy_from_slice(&bytes);
1291 hash
1292 })
1293 }
1294
1295 fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1296 prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1297 let mut pubkey = [0u8; 32];
1298 pubkey.copy_from_slice(&bytes);
1299 pubkey
1300 })
1301 }
1302}