Skip to main content

blvm_consensus/
taproot.rs

1//! Taproot functions from Orange Paper Section 11.2
2
3use crate::error::Result;
4use crate::types::*;
5use crate::types::{ByteString, Hash};
6use crate::witness;
7use blvm_spec_lock::spec_locked;
8
9/// BIP 341 default tapscript leaf version.
10pub const TAPROOT_LEAF_VERSION_TAPSCRIPT: u8 = 0xc0;
11
12/// Witness Data: 𝒲 = 𝕊* (stack of witness elements)
13///
14/// Uses unified witness type from witness module for consistency with SegWit
15pub use crate::witness::Witness;
16
17use crate::opcodes::OP_1;
18
19/// Taproot output script: OP_1 <32-byte-hash>
20pub const TAPROOT_SCRIPT_PREFIX: u8 = OP_1;
21
22/// Validate Taproot output script
23#[spec_locked("11.2.1", "ValidateTaprootScript")]
24pub fn validate_taproot_script(script: &ByteString) -> Result<bool> {
25    use crate::constants::TAPROOT_SCRIPT_LENGTH;
26
27    // Check if script is P2TR: OP_1 <32-byte-program>
28    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    // The remaining 33 bytes should be the Taproot output key
37    Ok(true)
38}
39
40/// Extract Taproot output key from script
41#[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/// Compute Taproot tweak using proper cryptographic operations
53/// OutputKey = InternalPubKey + TaprootTweak(MerkleRoot) × G
54///
55/// With `blvm-secp256k1` feature: uses BIP 341 tagged hash (correct).
56/// Without: uses libsecp256k1 with plain SHA256 (legacy, non-BIP341).
57#[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/// Validate Taproot key aggregation
63#[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/// Validate Taproot script path spending
74#[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/// Validate Taproot script path spending with explicit leaf version.
89#[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/// Compute merkle root for script path using BIP 341 TapLeaf/TapBranch tagged hashes.
101#[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/// Parsed control block from Taproot script-path witness.
122#[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/// Parse and validate Taproot script-path witness.
130/// Returns (tapscript, stack_items) if valid, Err otherwise.
131/// Witness format: [stack_items..., script, annex?, control_block]
132/// Annex: optional, last element before control block, must start with 0x50.
133/// Control block: leaf_version (1) + internal_pubkey (32) + merkle_proof (32*n).
134pub 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/// Check if transaction output is Taproot
192#[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/// Validate Taproot transaction
198#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
199pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
200    // Check if any output is Taproot
201    for output in &tx.outputs {
202        if is_taproot_output(output) {
203            // Validate Taproot output
204            if !validate_taproot_script(&output.script_pubkey)? {
205                return Ok(false);
206            }
207        }
208    }
209
210    // Validate Taproot witness structure using unified framework
211    // Determine if this is a script path spend based on witness structure
212    // Script path has at least 2 elements (script + control block), key path has 1 element (signature)
213    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/// Compute Taproot signature hash following BIP 341 specification.
224/// Uses TaggedHash("TapSighash", 0x00 || SigMsg(...)) per BIP 341.
225#[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/// Compute Tapscript signature hash per BIP 342.
273/// Same base SigMsg as key-path, with ext = codesep_pos (4) || key_version (1) || tapleaf_hash (32).
274#[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
326/// Encode a number as a Bitcoin varint
327fn 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]; // Too short
358        assert!(!validate_taproot_script(&script).unwrap());
359    }
360
361    #[test]
362    fn test_validate_taproot_script_invalid_prefix() {
363        let mut script = vec![0x52]; // Wrong prefix (OP_2 instead of OP_1)
364        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        // Use a valid secp256k1 public key (x-coordinate only for Taproot)
380        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        // Use a valid secp256k1 public key (x-coordinate only for Taproot)
394        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]; // OP_1, OP_2
410        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        // Key path spend: single signature
450        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        // Use invalid input index (out of bounds)
519        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]; // Invalid public key
557        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]; // Wrong output key
572
573        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]; // OP_1, OP_2
584        let merkle_proof = vec![[3u8; 32], [4u8; 32]];
585        let wrong_merkle_root = [5u8; 32]; // Wrong merkle root
586
587        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]; // OP_1, OP_2
593        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]; // Invalid script
656        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], // Not a Taproot script
665        };
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(), // Not Taproot
686            }]
687            .into(),
688            lock_time: 0,
689        };
690
691        // No witness needed for non-Taproot transaction
692        assert!(validate_taproot_transaction(&tx, None).unwrap());
693    }
694
695    #[test]
696    fn test_validate_taproot_transaction_invalid_taproot_output() {
697        // Create a transaction with a valid Taproot script
698        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        // Key path spend: single signature
718        let witness = Some(vec![vec![0u8; 64]]);
719        assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
720    }
721
722    // Helper function
723    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); // Add extra byte to make it 34 bytes total
727        script
728    }
729}
730
731#[cfg(test)]
732mod property_tests {
733    use super::*;
734    use proptest::prelude::*;
735
736    /// Property test: Taproot script validation is deterministic
737    ///
738    /// Mathematical specification:
739    /// ∀ script ∈ ByteString: validate_taproot_script(script) is deterministic
740    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    /// Property test: Taproot output key extraction is correct
753    ///
754    /// Mathematical specification:
755    /// ∀ script ∈ ByteString: if validate_taproot_script(script) = true
756    /// then extract_taproot_output_key(script) returns Some(key)
757    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    /// Property test: Taproot key aggregation is deterministic
776    ///
777    /// Mathematical specification:
778    /// ∀ internal_pubkey ∈ [u8; 32], merkle_root ∈ Hash:
779    /// compute_taproot_tweak(internal_pubkey, merkle_root) is deterministic
780    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    /// Property test: Taproot script path validation is deterministic
797    ///
798    /// Mathematical specification:
799    /// ∀ script ∈ ByteString, merkle_proof ∈ [Hash], merkle_root ∈ Hash:
800    /// validate_taproot_script_path(script, merkle_proof, merkle_root) is deterministic
801    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    /// Property test: Taproot signature hash computation is deterministic
819    ///
820    /// Mathematical specification:
821    /// ∀ tx ∈ Transaction, input_index ∈ ℕ, prevouts ∈ [TransactionOutput], sighash_type ∈ ℕ:
822    /// compute_taproot_signature_hash(tx, input_index, prevouts, sighash_type) is deterministic
823    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            // Hash should be 32 bytes if result is Ok
843            if let Ok(ref hash) = result1 {
844                assert_eq!(hash.len(), 32);
845            }
846        }
847    }
848
849    /// Property test: Taproot output detection is consistent
850    ///
851    /// Mathematical specification:
852    /// ∀ output ∈ TransactionOutput: is_taproot_output(output) ∈ {true, false}
853    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            // Just test it returns a boolean (is_taproot is either true or false)
860            let _ = is_taproot;
861        }
862    }
863
864    /// Property test: Taproot transaction validation is deterministic
865    ///
866    /// Mathematical specification:
867    /// ∀ tx ∈ Transaction: validate_taproot_transaction(tx) is deterministic
868    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    /// Property test: TapLeaf hashing is deterministic
881    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    /// Property test: TapBranch hashing is deterministic
895    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    /// Property test: Varint encoding is deterministic
910    ///
911    /// Mathematical specification:
912    /// ∀ value ∈ ℕ: encode_varint(value) is deterministic
913    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            // Encoded length should be reasonable
924            assert!(!encoded1.is_empty());
925            assert!(encoded1.len() <= 9);
926        }
927    }
928
929    /// Property test: Varint encoding preserves value
930    ///
931    /// Mathematical specification:
932    /// ∀ value ∈ ℕ: decode_varint(encode_varint(value)) = value
933    proptest! {
934        #[test]
935        fn prop_encode_varint_preserves_value(
936            value in 0..1000000u64  // Smaller range for tractability
937        ) {
938            let encoded = encode_varint(value);
939
940            // Basic validation of encoding format
941            match encoded.len() {
942                1 => {
943                    // Single byte encoding
944                    assert!(value < 0xfd);
945                    assert_eq!(encoded[0], value as u8);
946                },
947                3 => {
948                    // 2-byte encoding
949                    assert!((0xfd..=0xffff).contains(&value));
950                    assert_eq!(encoded[0], 0xfd);
951                },
952                5 => {
953                    // 4-byte encoding
954                    assert!(value > 0xffff && value <= 0xffffffff);
955                    assert_eq!(encoded[0], 0xfe);
956                },
957                9 => {
958                    // 8-byte encoding
959                    assert!(value > 0xffffffff);
960                    assert_eq!(encoded[0], 0xff);
961                },
962                _ => panic!("Invalid varint encoding length"),
963            }
964        }
965    }
966
967    /// Property test: Taproot script path validation with correct proof
968    ///
969    /// Mathematical specification:
970    /// ∀ script ∈ ByteString, merkle_proof ∈ [Hash]:
971    /// If computed_root = compute_script_merkle_root(script, merkle_proof)
972    /// then validate_taproot_script_path(script, merkle_proof, computed_root) = true
973    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    // Property test strategies
987    fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
988        (
989            prop::collection::vec(any::<u8>(), 0..10), // inputs
990            prop::collection::vec(any::<u8>(), 0..10), // outputs
991        )
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}