Skip to main content

blvm_consensus/
taproot.rs

1//! Taproot functions from Orange Paper Section 11.2
2
3use 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
11/// BIP 341 default tapscript leaf version.
12pub const TAPROOT_LEAF_VERSION_TAPSCRIPT: u8 = 0xc0;
13
14/// Witness Data: 𝒲 = π•Š* (stack of witness elements)
15///
16/// Uses unified witness type from witness module for consistency with SegWit
17pub use crate::witness::Witness;
18
19use crate::opcodes::OP_1;
20
21/// Taproot output script: OP_1 <32-byte-hash>
22pub const TAPROOT_SCRIPT_PREFIX: u8 = OP_1;
23
24/// Validate Taproot output script
25#[spec_locked("11.2.1", "ValidateTaprootScript")]
26pub fn validate_taproot_script(script: &ByteString) -> Result<bool> {
27    use crate::constants::TAPROOT_SCRIPT_LENGTH;
28
29    // Check if script is P2TR: OP_1 <32-byte-program>
30    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    // The remaining 33 bytes should be the Taproot output key
39    Ok(true)
40}
41
42/// Extract Taproot output key from script
43#[spec_locked("11.2.1", "ExtractTaprootOutputKey")]
44pub fn extract_taproot_output_key(script: &ByteString) -> Result<Option<[u8; 32]>> {
45    if !validate_taproot_script(script)? {
46        return Ok(None);
47    }
48
49    let mut output_key = [0u8; 32];
50    output_key.copy_from_slice(&script[1..33]);
51    Ok(Some(output_key))
52}
53
54/// Compute Taproot tweak using proper cryptographic operations
55/// OutputKey = InternalPubKey + TaprootTweak(MerkleRoot) Γ— G
56///
57/// With `blvm-secp256k1` feature: uses BIP 341 tagged hash (correct).
58/// Without: uses libsecp256k1 with plain SHA256 (legacy, non-BIP341).
59#[spec_locked("11.2.2", "ComputeTaprootTweak")]
60pub fn compute_taproot_tweak(internal_pubkey: &[u8; 32], merkle_root: &Hash) -> Result<[u8; 32]> {
61    crate::secp256k1_backend::taproot_output_key(internal_pubkey, merkle_root)
62}
63
64/// Validate Taproot key aggregation, including the parity commitment from the control block.
65///
66/// Per BIP341: verifies that `output_key.x == tweaked_internal_key.x` AND that
67/// `tweaked_internal_key.y_parity == expected_parity` (from `control_block[0] & 0x01`).
68#[spec_locked("11.2.2", "ValidateTaprootKeyAggregation")]
69pub fn validate_taproot_key_aggregation(
70    internal_pubkey: &[u8; 32],
71    merkle_root: &Hash,
72    output_key: &[u8; 32],
73    expected_parity: u8,
74) -> Result<bool> {
75    let (computed_key, actual_parity) =
76        crate::secp256k1_backend::taproot_output_key_with_parity(internal_pubkey, merkle_root)?;
77    Ok(computed_key == *output_key && actual_parity == expected_parity)
78}
79
80/// Validate Taproot script path spending
81#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
82pub fn validate_taproot_script_path(
83    script: &ByteString,
84    merkle_proof: &[Hash],
85    merkle_root: &Hash,
86) -> Result<bool> {
87    validate_taproot_script_path_with_leaf_version(
88        script,
89        merkle_proof,
90        merkle_root,
91        TAPROOT_LEAF_VERSION_TAPSCRIPT,
92    )
93}
94
95/// Validate Taproot script path spending with explicit leaf version.
96#[spec_locked("11.2.3", "ValidateTaprootScriptPath")]
97pub fn validate_taproot_script_path_with_leaf_version(
98    script: &ByteString,
99    merkle_proof: &[Hash],
100    merkle_root: &Hash,
101    leaf_version: u8,
102) -> Result<bool> {
103    let computed_root = compute_script_merkle_root(script, merkle_proof, leaf_version)?;
104    Ok(computed_root == *merkle_root)
105}
106
107/// Compute merkle root for script path using BIP 341 TapLeaf/TapBranch tagged hashes.
108#[spec_locked("11.2.3", "ComputeScriptMerkleRoot")]
109pub fn compute_script_merkle_root(
110    script: &ByteString,
111    proof: &[Hash],
112    leaf_version: u8,
113) -> Result<Hash> {
114    let mut current_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, script);
115
116    for proof_hash in proof {
117        let (left, right) = if current_hash < *proof_hash {
118            (current_hash, *proof_hash)
119        } else {
120            (*proof_hash, current_hash)
121        };
122        current_hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
123    }
124
125    Ok(current_hash)
126}
127
128/// BIP341 annex tag (first byte of optional annex witness element).
129pub const TAPROOT_ANNEX_TAG: u8 = 0x50;
130
131/// Strip optional BIP341 annex from the end of a Taproot witness stack.
132///
133/// Core `VerifyWitnessProgram` (v1): if there are at least two stack elements and the
134/// last begins with `0x50`, it is an annex and removed before key/script path dispatch.
135/// Returns the stripped witness and the annex sighash hash when present.
136/// Borrows the stack when no annex is present (avoids cloning the full witness).
137#[spec_locked("11.2.4", "StripTaprootAnnex")]
138pub fn strip_taproot_annex(witness: &Witness) -> (Cow<'_, Witness>, Option<Hash>) {
139    if witness.len() >= 2
140        && witness
141            .last()
142            .and_then(|a| a.first())
143            .is_some_and(|&b| b == TAPROOT_ANNEX_TAG)
144    {
145        let annex = &witness[witness.len() - 1];
146        let annex_hash = compute_taproot_annex_sighash_hash(annex);
147        let stripped = witness[..witness.len() - 1].to_vec();
148        return (Cow::Owned(stripped), Some(annex_hash));
149    }
150    (Cow::Borrowed(witness), None)
151}
152
153/// SHA256 of the annex element for Taproot sighash (matches Core `HashWriter << annex`).
154fn compute_taproot_annex_sighash_hash(annex: &[u8]) -> Hash {
155    use sha2::{Digest, Sha256};
156    let mut buf = encode_varint(annex.len() as u64);
157    buf.extend_from_slice(annex);
158    let digest = Sha256::digest(&buf);
159    let mut out = [0u8; 32];
160    out.copy_from_slice(&digest);
161    out
162}
163
164/// Parsed control block from Taproot script-path witness.
165#[derive(Debug)]
166pub struct TaprootControlBlock {
167    pub leaf_version: u8,
168    pub internal_pubkey: [u8; 32],
169    pub merkle_proof: Vec<Hash>,
170}
171
172/// Parse and validate Taproot script-path witness.
173/// Returns (tapscript, stack_items) if valid, Err otherwise.
174/// Witness format: [stack_items..., script, annex?, control_block]
175/// Annex: optional, last element before control block, must start with 0x50.
176/// Control block: leaf_version (1) + internal_pubkey (32) + merkle_proof (32*n).
177pub fn parse_taproot_script_path_witness(
178    witness: &Witness,
179    output_key: &[u8; 32],
180) -> Result<Option<(ByteString, Vec<ByteString>, TaprootControlBlock)>> {
181    if witness.len() < 2 {
182        return Ok(None);
183    }
184
185    let Some(control_block) = witness.last() else {
186        return Ok(None);
187    };
188    if control_block.len() < 33 || (control_block.len() - 33) % 32 != 0 {
189        return Ok(None);
190    }
191
192    // BIP341: leaf_version = control_block[0] & 0xfe (strip parity bit).
193    // parity = control_block[0] & 0x01 (which y-coordinate the tweaked key uses).
194    let leaf_version = control_block[0] & 0xfe;
195    let parity = control_block[0] & 0x01;
196    let mut internal_pubkey = [0u8; 32];
197    internal_pubkey.copy_from_slice(&control_block[1..33]);
198    let merkle_proof: Vec<Hash> = control_block[33..]
199        .chunks_exact(32)
200        .map(|c| {
201            let mut h = [0u8; 32];
202            h.copy_from_slice(c);
203            h
204        })
205        .collect();
206
207    let script_idx = if witness.len() >= 3 {
208        let maybe_annex = &witness[witness.len() - 2];
209        if maybe_annex.first() == Some(&0x50) {
210            witness.len() - 3
211        } else {
212            witness.len() - 2
213        }
214    } else {
215        witness.len() - 2
216    };
217
218    let tapscript = witness[script_idx].clone();
219    let stack_items: Vec<ByteString> = witness[..script_idx].to_vec();
220
221    let merkle_root = match compute_script_merkle_root(&tapscript, &merkle_proof, leaf_version) {
222        Ok(root) => root,
223        Err(_) => return Ok(None),
224    };
225    // Invalid tweak / aggregation mismatch β†’ script invalid (Ok(false)), not consensus Err.
226    let valid = match validate_taproot_key_aggregation(
227        &internal_pubkey,
228        &merkle_root,
229        output_key,
230        parity,
231    ) {
232        Ok(v) => v,
233        Err(_) => return Ok(None),
234    };
235    if !valid {
236        return Ok(None);
237    }
238
239    Ok(Some((
240        tapscript,
241        stack_items,
242        TaprootControlBlock {
243            leaf_version, // already masked (0xfe) β€” parity bit stripped
244            internal_pubkey,
245            merkle_proof,
246        },
247    )))
248}
249
250/// Check if transaction output is Taproot
251/// BIP341 invariant: a Taproot output has a P2TR scriptPubKey of exactly 34 bytes
252/// (OP_1 + 0x20 push + 32-byte x-only pubkey).  If this returns true, the length is 34.
253#[spec_locked("11.2.1", "IsTaprootOutput")]
254#[blvm_spec_lock::ensures(result == false || output.script_pubkey.len() == 34)]
255pub fn is_taproot_output(output: &TransactionOutput) -> bool {
256    validate_taproot_script(&output.script_pubkey).unwrap_or(false)
257}
258
259/// Validate Taproot transaction
260#[spec_locked("11.2.5", "ValidateTaprootTransaction")]
261pub fn validate_taproot_transaction(tx: &Transaction, witness: Option<&Witness>) -> Result<bool> {
262    // Check if any output is Taproot
263    for output in &tx.outputs {
264        if is_taproot_output(output) {
265            // Validate Taproot output
266            if !validate_taproot_script(&output.script_pubkey)? {
267                return Ok(false);
268            }
269        }
270    }
271
272    // Validate Taproot witness structure using unified framework
273    // Determine if this is a script path spend based on witness structure
274    // Script path has at least 2 elements (script + control block), key path has 1 element (signature)
275    if let Some(w) = witness {
276        let is_script_path = w.len() >= 2;
277        if !witness::validate_taproot_witness_structure(w, is_script_path)? {
278            return Ok(false);
279        }
280    }
281
282    Ok(true)
283}
284
285/// Compute BIP341 SigMsg hash commitments from all inputs and outputs.
286///
287/// Returns (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs).
288/// Each is SHA256 of the concatenation of the respective field across all inputs/outputs.
289fn bip341_precompute(
290    tx: &Transaction,
291    prevout_values: &[i64],
292    prevout_script_pubkeys: &[&[u8]],
293) -> ([u8; 32], [u8; 32], [u8; 32], [u8; 32], [u8; 32]) {
294    use sha2::{Digest, Sha256};
295
296    let mut prevouts_data = Vec::new();
297    let mut amounts_data = Vec::new();
298    let mut scriptpubkeys_data = Vec::new();
299    let mut sequences_data = Vec::new();
300    for (i, input) in tx.inputs.iter().enumerate() {
301        prevouts_data.extend_from_slice(&input.prevout.hash);
302        prevouts_data.extend_from_slice(&input.prevout.index.to_le_bytes());
303        // Both callers validate that prevout slices are at least as long as tx.inputs before
304        // calling this function.  Direct indexing here makes the invariant explicit; a panic
305        // would signal a programming error, not a consensus-invalid input.
306        amounts_data.extend_from_slice(&(prevout_values[i] as u64).to_le_bytes());
307        let spk = prevout_script_pubkeys[i];
308        scriptpubkeys_data.extend_from_slice(&encode_varint(spk.len() as u64));
309        scriptpubkeys_data.extend_from_slice(spk);
310        sequences_data.extend_from_slice(&(input.sequence as u32).to_le_bytes());
311    }
312    let mut outputs_data = Vec::new();
313    for output in &tx.outputs {
314        outputs_data.extend_from_slice(&(output.value as u64).to_le_bytes());
315        outputs_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
316        outputs_data.extend_from_slice(&output.script_pubkey);
317    }
318
319    (
320        Sha256::digest(&prevouts_data).into(),
321        Sha256::digest(&amounts_data).into(),
322        Sha256::digest(&scriptpubkeys_data).into(),
323        Sha256::digest(&sequences_data).into(),
324        Sha256::digest(&outputs_data).into(),
325    )
326}
327
328/// Compute Taproot key-path signature hash following BIP341.
329///
330/// SigMsg = epoch(1) || hash_type(1) || nVersion(4) || nLockTime(4)
331///        || sha_prevouts(32) || sha_amounts(32) || sha_scriptpubkeys(32) || sha_sequences(32)
332///        || sha_outputs(32)   [if SIGHASH_ALL]
333///        || spend_type(1)     [0x00 = key-path, no annex]
334///        || input_index(4)
335///
336/// Only SIGHASH_DEFAULT (0x00 = treated as ALL) and SIGHASH_ALL (0x01) are handled here;
337/// ANYONECANPAY and SIGHASH_SINGLE paths use the same base but are not yet exercised.
338#[spec_locked("11.2.6", "ComputeTaprootSignatureHash")]
339pub fn compute_taproot_signature_hash(
340    tx: &Transaction,
341    input_index: usize,
342    prevout_values: &[i64],
343    prevout_script_pubkeys: &[&[u8]],
344    sighash_type: u8,
345    annex_hash: Option<&Hash>,
346) -> Result<Hash> {
347    use sha2::{Digest, Sha256};
348
349    // Reject invalid sighash types (BIP341 Β§Common signature message)
350    // Valid: 0x00 (DEFAULT/ALL), 0x01–0x03, 0x81–0x83.
351    if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
352        return Err(crate::error::ConsensusError::InvalidSignature(
353            "Invalid Taproot sighash type".into(),
354        ));
355    }
356
357    // Validate prevout slices: one entry per input is required for a correct sighash.
358    // A short slice would silently produce wrong amounts/scriptpubkeys (zeroed),
359    // which is a consensus error.
360    if prevout_values.len() < tx.inputs.len() || prevout_script_pubkeys.len() < tx.inputs.len() {
361        return Err(crate::error::ConsensusError::InvalidSignature(
362            "prevout_values/prevout_script_pubkeys shorter than tx.inputs β€” cannot compute sighash"
363                .into(),
364        ));
365    }
366
367    let input_type = sighash_type & 0x80; // ANYONECANPAY bit
368    let output_type = if sighash_type == 0x00 {
369        0x01
370    } else {
371        sighash_type & 0x03
372    }; // DEFAULT β†’ ALL
373
374    let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
375        bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
376
377    let mut sigmsg = Vec::with_capacity(180);
378
379    // epoch
380    sigmsg.push(0x00u8);
381    // hash_type
382    sigmsg.push(sighash_type);
383    // nVersion
384    sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
385    // nLockTime
386    sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
387
388    if input_type != 0x80 {
389        // not ANYONECANPAY: include all-input hash commitments
390        sigmsg.extend_from_slice(&sha_prevouts);
391        sigmsg.extend_from_slice(&sha_amounts);
392        sigmsg.extend_from_slice(&sha_scriptpubkeys);
393        sigmsg.extend_from_slice(&sha_sequences);
394    }
395    if output_type == 0x01 {
396        // SIGHASH_ALL: include all-output hash
397        sigmsg.extend_from_slice(&sha_outputs);
398    }
399
400    // spend_type: ext_flag=0 (key path), annex_present in low bit (BIP341)
401    let spend_type = if annex_hash.is_some() { 0x01u8 } else { 0x00u8 };
402    sigmsg.push(spend_type);
403
404    if input_type == 0x80 {
405        // ANYONECANPAY: include this input's outpoint + amount + scriptpubkey + sequence
406        let input = tx.inputs.get(input_index).ok_or_else(|| {
407            crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
408        })?;
409        sigmsg.extend_from_slice(&input.prevout.hash);
410        sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
411        // prevout slice length was validated at function entry; indexing is safe here.
412        let amount = *prevout_values.get(input_index).ok_or_else(|| {
413            crate::error::ConsensusError::InvalidSignature(
414                "prevout_values index out of range for ANYONECANPAY".into(),
415            )
416        })? as u64;
417        sigmsg.extend_from_slice(&amount.to_le_bytes());
418        let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
419            crate::error::ConsensusError::InvalidSignature(
420                "prevout_script_pubkeys index out of range for ANYONECANPAY".into(),
421            )
422        })?;
423        sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
424        sigmsg.extend_from_slice(spk);
425        sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
426    } else {
427        // include input_index
428        sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
429    }
430
431    if let Some(h) = annex_hash {
432        sigmsg.extend_from_slice(h);
433    }
434
435    if output_type == 0x03 {
436        // SIGHASH_SINGLE: hash of this input's corresponding output
437        if let Some(output) = tx.outputs.get(input_index) {
438            let mut out_data = Vec::new();
439            out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
440            out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
441            out_data.extend_from_slice(&output.script_pubkey);
442            sigmsg.extend_from_slice(&Sha256::digest(&out_data));
443        }
444    }
445
446    Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
447}
448
449/// Compute Tapscript (BIP342) signature hash.
450///
451/// Same base SigMsg as key-path (spend_type bit 0 set = 0x01 or 0x03 depending on annex),
452/// with extension: tapleaf_hash(32) || key_version(1=0x00) || codesep_pos(4).
453#[spec_locked("11.2.7", "ComputeTapscriptSignatureHash")]
454pub fn compute_tapscript_signature_hash(
455    tx: &Transaction,
456    input_index: usize,
457    prevout_values: &[i64],
458    prevout_script_pubkeys: &[&[u8]],
459    tapscript: &[u8],
460    leaf_version: u8,
461    codesep_pos: u32,
462    sighash_type: u8,
463    annex_hash: Option<&Hash>,
464) -> Result<Hash> {
465    use sha2::{Digest, Sha256};
466
467    if !(sighash_type <= 0x03 || (0x81..=0x83).contains(&sighash_type)) {
468        return Err(crate::error::ConsensusError::InvalidSignature(
469            "Invalid Tapscript sighash type".into(),
470        ));
471    }
472
473    let input_type = sighash_type & 0x80;
474    let output_type = if sighash_type == 0x00 {
475        0x01
476    } else {
477        sighash_type & 0x03
478    };
479
480    let (sha_prevouts, sha_amounts, sha_scriptpubkeys, sha_sequences, sha_outputs) =
481        bip341_precompute(tx, prevout_values, prevout_script_pubkeys);
482
483    let mut sigmsg = Vec::with_capacity(250);
484
485    // epoch
486    sigmsg.push(0x00u8);
487    // hash_type
488    sigmsg.push(sighash_type);
489    // nVersion
490    sigmsg.extend_from_slice(&(tx.version as u32).to_le_bytes());
491    // nLockTime
492    sigmsg.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
493
494    if input_type != 0x80 {
495        sigmsg.extend_from_slice(&sha_prevouts);
496        sigmsg.extend_from_slice(&sha_amounts);
497        sigmsg.extend_from_slice(&sha_scriptpubkeys);
498        sigmsg.extend_from_slice(&sha_sequences);
499    }
500    if output_type == 0x01 {
501        sigmsg.extend_from_slice(&sha_outputs);
502    }
503
504    // spend_type: ext_flag=1 (script path); annex_present in low bit (BIP341)
505    let spend_type = if annex_hash.is_some() { 0x03u8 } else { 0x02u8 };
506    sigmsg.push(spend_type);
507
508    if input_type == 0x80 {
509        let input = tx.inputs.get(input_index).ok_or_else(|| {
510            crate::error::ConsensusError::InvalidSignature("input_index out of range".into())
511        })?;
512        sigmsg.extend_from_slice(&input.prevout.hash);
513        sigmsg.extend_from_slice(&input.prevout.index.to_le_bytes());
514        let amount = *prevout_values.get(input_index).ok_or_else(|| {
515            crate::error::ConsensusError::InvalidSignature(
516                "prevout_values index out of range for script-path ANYONECANPAY".into(),
517            )
518        })? as u64;
519        sigmsg.extend_from_slice(&amount.to_le_bytes());
520        let spk = *prevout_script_pubkeys.get(input_index).ok_or_else(|| {
521            crate::error::ConsensusError::InvalidSignature(
522                "prevout_script_pubkeys index out of range for script-path ANYONECANPAY".into(),
523            )
524        })?;
525        sigmsg.extend_from_slice(&encode_varint(spk.len() as u64));
526        sigmsg.extend_from_slice(spk);
527        sigmsg.extend_from_slice(&(input.sequence as u32).to_le_bytes());
528    } else {
529        sigmsg.extend_from_slice(&(input_index as u32).to_le_bytes());
530    }
531
532    if let Some(h) = annex_hash {
533        sigmsg.extend_from_slice(h);
534    }
535
536    if output_type == 0x03 {
537        if let Some(output) = tx.outputs.get(input_index) {
538            let mut out_data = Vec::new();
539            out_data.extend_from_slice(&(output.value as u64).to_le_bytes());
540            out_data.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
541            out_data.extend_from_slice(&output.script_pubkey);
542            sigmsg.extend_from_slice(&Sha256::digest(&out_data));
543        }
544    }
545
546    // BIP342 extension: tapleaf_hash || key_version(0x00) || codesep_pos
547    let tapleaf_hash = crate::secp256k1_backend::tap_leaf_hash(leaf_version, tapscript);
548    sigmsg.extend_from_slice(&tapleaf_hash);
549    sigmsg.push(0x00u8); // key_version
550    sigmsg.extend_from_slice(&codesep_pos.to_le_bytes());
551
552    Ok(crate::secp256k1_backend::tap_sighash_hash(&sigmsg))
553}
554
555/// Encode a number as a Bitcoin varint
556fn encode_varint(value: u64) -> Vec<u8> {
557    if value < 0xfd {
558        vec![value as u8]
559    } else if value <= 0xffff {
560        let mut result = vec![0xfd];
561        result.extend_from_slice(&(value as u16).to_le_bytes());
562        result
563    } else if value <= 0xffffffff {
564        let mut result = vec![0xfe];
565        result.extend_from_slice(&(value as u32).to_le_bytes());
566        result
567    } else {
568        let mut result = vec![0xff];
569        result.extend_from_slice(&value.to_le_bytes());
570        result
571    }
572}
573
574#[cfg(test)]
575mod tests {
576    use super::*;
577
578    #[test]
579    fn test_validate_taproot_script_valid() {
580        let script = create_taproot_script(&[1u8; 32]);
581        assert!(validate_taproot_script(&script).unwrap());
582    }
583
584    #[test]
585    fn test_validate_taproot_script_invalid_length() {
586        let script = vec![0x51, 0x20]; // Too short
587        assert!(!validate_taproot_script(&script).unwrap());
588    }
589
590    #[test]
591    fn test_validate_taproot_script_invalid_prefix() {
592        let mut script = vec![0x52]; // Wrong prefix (OP_2 instead of OP_1)
593        script.extend_from_slice(&[1u8; 32]);
594        assert!(!validate_taproot_script(&script).unwrap());
595    }
596
597    #[test]
598    fn test_extract_taproot_output_key() {
599        let expected_key = [1u8; 32];
600        let script = create_taproot_script(&expected_key);
601
602        let extracted_key = extract_taproot_output_key(&script).unwrap();
603        assert_eq!(extracted_key, Some(expected_key));
604    }
605
606    #[test]
607    fn test_compute_taproot_tweak() {
608        // Use a valid secp256k1 public key (x-coordinate only for Taproot)
609        let internal_pubkey = [
610            0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
611            0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
612            0x16, 0xf8, 0x17, 0x98,
613        ];
614        let merkle_root = [2u8; 32];
615
616        let tweak = compute_taproot_tweak(&internal_pubkey, &merkle_root).unwrap();
617        assert_eq!(tweak.len(), 32);
618    }
619
620    #[test]
621    fn test_validate_taproot_key_aggregation() {
622        // Use a valid secp256k1 public key (x-coordinate only for Taproot)
623        let internal_pubkey = [
624            0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
625            0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
626            0x16, 0xf8, 0x17, 0x98,
627        ];
628        let merkle_root = [2u8; 32];
629        let (output_key, parity) = crate::secp256k1_backend::taproot_output_key_with_parity(
630            &internal_pubkey,
631            &merkle_root,
632        )
633        .unwrap();
634
635        assert!(
636            validate_taproot_key_aggregation(&internal_pubkey, &merkle_root, &output_key, parity)
637                .unwrap()
638        );
639    }
640
641    #[test]
642    fn test_validate_taproot_script_path() {
643        let script = vec![0x51, 0x52]; // OP_1, OP_2
644        let merkle_proof = vec![[3u8; 32], [4u8; 32]];
645        let merkle_root =
646            compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
647                .unwrap();
648
649        assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
650    }
651
652    #[test]
653    fn test_is_taproot_output() {
654        let output = TransactionOutput {
655            value: 1000,
656            script_pubkey: create_taproot_script(&[1u8; 32]),
657        };
658
659        assert!(is_taproot_output(&output));
660    }
661
662    #[test]
663    fn test_validate_taproot_transaction() {
664        let tx = Transaction {
665            version: 1,
666            inputs: vec![TransactionInput {
667                prevout: OutPoint {
668                    hash: [0; 32],
669                    index: 0,
670                },
671                script_sig: vec![],
672                sequence: 0xffffffff,
673            }]
674            .into(),
675            outputs: vec![TransactionOutput {
676                value: 1000,
677                script_pubkey: create_taproot_script(&[1u8; 32]),
678            }]
679            .into(),
680            lock_time: 0,
681        };
682
683        // Key path spend: single signature
684        let witness = Some(vec![vec![0u8; 64]]);
685        assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
686    }
687
688    #[test]
689    fn test_compute_taproot_signature_hash() {
690        let tx = Transaction {
691            version: 1,
692            inputs: vec![TransactionInput {
693                prevout: OutPoint {
694                    hash: [0; 32],
695                    index: 0,
696                },
697                script_sig: vec![],
698                sequence: 0xffffffff,
699            }]
700            .into(),
701            outputs: vec![TransactionOutput {
702                value: 1000,
703                script_pubkey: vec![0x51],
704            }]
705            .into(),
706            lock_time: 0,
707        };
708
709        let prevouts = [TransactionOutput {
710            value: 2000,
711            script_pubkey: create_taproot_script(&[1u8; 32]),
712        }];
713        let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
714        let psp: Vec<&[u8]> = prevouts
715            .iter()
716            .map(|p| p.script_pubkey.as_slice())
717            .collect();
718        let sig_hash = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None).unwrap();
719        assert_eq!(sig_hash.len(), 32);
720    }
721
722    #[test]
723    fn test_compute_taproot_signature_hash_invalid_input_index() {
724        let tx = Transaction {
725            version: 1,
726            inputs: vec![TransactionInput {
727                prevout: OutPoint {
728                    hash: [0; 32],
729                    index: 0,
730                },
731                script_sig: vec![],
732                sequence: 0xffffffff,
733            }]
734            .into(),
735            outputs: vec![TransactionOutput {
736                value: 1000,
737                script_pubkey: vec![0x51],
738            }]
739            .into(),
740            lock_time: 0,
741        };
742
743        let prevouts = [TransactionOutput {
744            value: 2000,
745            script_pubkey: create_taproot_script(&[1u8; 32]),
746        }];
747        let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
748        let psp: Vec<&[u8]> = prevouts
749            .iter()
750            .map(|p| p.script_pubkey.as_slice())
751            .collect();
752        // Use invalid input index (out of bounds)
753        let sig_hash = compute_taproot_signature_hash(&tx, 1, &pv, &psp, 0x01, None).unwrap();
754        assert_eq!(sig_hash.len(), 32);
755    }
756
757    #[test]
758    fn test_compute_taproot_signature_hash_empty_prevouts() {
759        let tx = Transaction {
760            version: 1,
761            inputs: vec![TransactionInput {
762                prevout: OutPoint {
763                    hash: [0; 32],
764                    index: 0,
765                },
766                script_sig: vec![],
767                sequence: 0xffffffff,
768            }]
769            .into(),
770            outputs: vec![TransactionOutput {
771                value: 1000,
772                script_pubkey: vec![0x51],
773            }]
774            .into(),
775            lock_time: 0,
776        };
777
778        // Empty prevout slices with a non-empty input list must return Err (fail-closed).
779        // Previously this silently used zeros; the audit fix made it explicit.
780        let prevouts: Vec<TransactionOutput> = vec![];
781        let pv: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
782        let psp: Vec<&[u8]> = prevouts
783            .iter()
784            .map(|p| p.script_pubkey.as_slice())
785            .collect();
786        let result = compute_taproot_signature_hash(&tx, 0, &pv, &psp, 0x01, None);
787        assert!(
788            result.is_err(),
789            "empty prevouts shorter than tx.inputs must return Err, not silently use zeros"
790        );
791    }
792
793    #[test]
794    fn test_compute_taproot_tweak_invalid_pubkey() {
795        let invalid_pubkey = [0u8; 32]; // Invalid public key
796        let merkle_root = [2u8; 32];
797
798        let result = compute_taproot_tweak(&invalid_pubkey, &merkle_root);
799        assert!(result.is_err());
800    }
801
802    #[test]
803    fn test_validate_taproot_key_aggregation_invalid() {
804        let internal_pubkey = [
805            0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
806            0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b,
807            0x16, 0xf8, 0x17, 0x98,
808        ];
809        let merkle_root = [2u8; 32];
810        let wrong_output_key = [3u8; 32]; // Wrong output key
811
812        assert!(
813            !validate_taproot_key_aggregation(
814                &internal_pubkey,
815                &merkle_root,
816                &wrong_output_key,
817                0, // parity β€” irrelevant since key mismatch will fail first
818            )
819            .unwrap()
820        );
821    }
822
823    #[test]
824    fn test_validate_taproot_script_path_invalid() {
825        let script = vec![0x51, 0x52]; // OP_1, OP_2
826        let merkle_proof = vec![[3u8; 32], [4u8; 32]];
827        let wrong_merkle_root = [5u8; 32]; // Wrong merkle root
828
829        assert!(!validate_taproot_script_path(&script, &merkle_proof, &wrong_merkle_root).unwrap());
830    }
831
832    #[test]
833    fn test_validate_taproot_script_path_empty_proof() {
834        let script = vec![0x51, 0x52]; // OP_1, OP_2
835        let merkle_proof = vec![];
836        let merkle_root =
837            compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT)
838                .unwrap();
839
840        assert!(validate_taproot_script_path(&script, &merkle_proof, &merkle_root).unwrap());
841    }
842
843    #[test]
844    fn test_tap_leaf_hash() {
845        let script = vec![0x51, 0x52];
846        let hash = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
847
848        assert_eq!(hash.len(), 32);
849
850        let script2 = vec![0x53, 0x54];
851        let hash2 =
852            crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script2);
853        assert_ne!(hash, hash2);
854    }
855
856    #[test]
857    fn test_tap_branch_hash() {
858        let left = [1u8; 32];
859        let right = [2u8; 32];
860        let hash = crate::secp256k1_backend::tap_branch_hash(&left, &right);
861
862        assert_eq!(hash.len(), 32);
863
864        let hash2 = crate::secp256k1_backend::tap_branch_hash(&right, &left);
865        assert_ne!(hash, hash2);
866    }
867
868    #[test]
869    fn test_encode_varint_small() {
870        let encoded = encode_varint(0xfc);
871        assert_eq!(encoded, vec![0xfc]);
872    }
873
874    #[test]
875    fn test_encode_varint_medium() {
876        let encoded = encode_varint(0x1000);
877        assert_eq!(encoded.len(), 3);
878        assert_eq!(encoded[0], 0xfd);
879    }
880
881    #[test]
882    fn test_encode_varint_large() {
883        let encoded = encode_varint(0x1000000);
884        assert_eq!(encoded.len(), 5);
885        assert_eq!(encoded[0], 0xfe);
886    }
887
888    #[test]
889    fn test_encode_varint_huge() {
890        let encoded = encode_varint(0x1000000000000000);
891        assert_eq!(encoded.len(), 9);
892        assert_eq!(encoded[0], 0xff);
893    }
894
895    #[test]
896    fn test_extract_taproot_output_key_invalid_script() {
897        let script = vec![0x52, 0x20]; // Invalid script
898        let result = extract_taproot_output_key(&script).unwrap();
899        assert!(result.is_none());
900    }
901
902    #[test]
903    fn test_is_taproot_output_false() {
904        let output = TransactionOutput {
905            value: 1000,
906            script_pubkey: vec![0x52, 0x20], // Not a Taproot script
907        };
908
909        assert!(!is_taproot_output(&output));
910    }
911
912    #[test]
913    fn test_validate_taproot_transaction_no_taproot_outputs() {
914        let tx = Transaction {
915            version: 1,
916            inputs: vec![TransactionInput {
917                prevout: OutPoint {
918                    hash: [0; 32],
919                    index: 0,
920                },
921                script_sig: vec![],
922                sequence: 0xffffffff,
923            }]
924            .into(),
925            outputs: vec![TransactionOutput {
926                value: 1000,
927                script_pubkey: vec![0x52], // Not Taproot
928            }]
929            .into(),
930            lock_time: 0,
931        };
932
933        // No witness needed for non-Taproot transaction
934        assert!(validate_taproot_transaction(&tx, None).unwrap());
935    }
936
937    #[test]
938    fn test_validate_taproot_transaction_invalid_taproot_output() {
939        // Create a transaction with a valid Taproot script
940        let tx = Transaction {
941            version: 1,
942            inputs: vec![TransactionInput {
943                prevout: OutPoint {
944                    hash: [0; 32],
945                    index: 0,
946                },
947                script_sig: vec![],
948                sequence: 0xffffffff,
949            }]
950            .into(),
951            outputs: vec![TransactionOutput {
952                value: 1000,
953                script_pubkey: create_taproot_script(&[1u8; 32]),
954            }]
955            .into(),
956            lock_time: 0,
957        };
958
959        // Key path spend: single signature
960        let witness = Some(vec![vec![0u8; 64]]);
961        assert!(validate_taproot_transaction(&tx, witness.as_ref()).unwrap());
962    }
963
964    // Helper function
965    fn create_taproot_script(output_key: &[u8; 32]) -> ByteString {
966        let mut script = vec![TAPROOT_SCRIPT_PREFIX];
967        script.extend_from_slice(output_key);
968        script.push(0x00); // Add extra byte to make it 34 bytes total
969        script
970    }
971}
972
973#[cfg(test)]
974mod property_tests {
975    use super::*;
976    use proptest::prelude::*;
977
978    /// Property test: Taproot script validation is deterministic
979    ///
980    /// Mathematical specification:
981    /// βˆ€ script ∈ ByteString: validate_taproot_script(script) is deterministic
982    proptest! {
983        #[test]
984        fn prop_validate_taproot_script_deterministic(
985            script in prop::collection::vec(any::<u8>(), 0..50)
986        ) {
987            let result1 = validate_taproot_script(&script).unwrap();
988            let result2 = validate_taproot_script(&script).unwrap();
989
990            assert_eq!(result1, result2);
991        }
992    }
993
994    /// Property test: Taproot output key extraction is correct
995    ///
996    /// Mathematical specification:
997    /// βˆ€ script ∈ ByteString: if validate_taproot_script(script) = true
998    /// then extract_taproot_output_key(script) returns Some(key)
999    proptest! {
1000        #[test]
1001        fn prop_extract_taproot_output_key_correct(
1002            script in prop::collection::vec(any::<u8>(), 0..50)
1003        ) {
1004            let extracted_key = extract_taproot_output_key(&script).unwrap();
1005            let is_valid = validate_taproot_script(&script).unwrap();
1006
1007            if is_valid {
1008                assert!(extracted_key.is_some());
1009                let key = extracted_key.unwrap();
1010                assert_eq!(key.len(), 32);
1011            } else {
1012                assert!(extracted_key.is_none());
1013            }
1014        }
1015    }
1016
1017    /// Property test: Taproot key aggregation is deterministic
1018    ///
1019    /// Mathematical specification:
1020    /// βˆ€ internal_pubkey ∈ [u8; 32], merkle_root ∈ Hash:
1021    /// compute_taproot_tweak(internal_pubkey, merkle_root) is deterministic
1022    proptest! {
1023        #[test]
1024        fn prop_taproot_key_aggregation_deterministic(
1025            internal_pubkey in create_pubkey_strategy(),
1026            merkle_root in create_hash_strategy()
1027        ) {
1028            let result1 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1029            let result2 = compute_taproot_tweak(&internal_pubkey, &merkle_root);
1030
1031            assert_eq!(result1.is_ok(), result2.is_ok());
1032            if result1.is_ok() && result2.is_ok() {
1033                assert_eq!(result1.unwrap(), result2.unwrap());
1034            }
1035        }
1036    }
1037
1038    /// Property test: Taproot script path validation is deterministic
1039    ///
1040    /// Mathematical specification:
1041    /// βˆ€ script ∈ ByteString, merkle_proof ∈ [Hash], merkle_root ∈ Hash:
1042    /// validate_taproot_script_path(script, merkle_proof, merkle_root) is deterministic
1043    proptest! {
1044        #[test]
1045        fn prop_validate_taproot_script_path_deterministic(
1046            script in prop::collection::vec(any::<u8>(), 0..20),
1047            merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5),
1048            merkle_root in create_hash_strategy()
1049        ) {
1050            let result1 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1051            let result2 = validate_taproot_script_path(&script, &merkle_proof, &merkle_root);
1052
1053            assert_eq!(result1.is_ok(), result2.is_ok());
1054            if result1.is_ok() && result2.is_ok() {
1055                assert_eq!(result1.unwrap(), result2.unwrap());
1056            }
1057        }
1058    }
1059
1060    /// Property test: Taproot signature hash computation is deterministic
1061    ///
1062    /// Mathematical specification:
1063    /// βˆ€ tx ∈ Transaction, input_index ∈ β„•, prevouts ∈ [TransactionOutput], sighash_type ∈ β„•:
1064    /// compute_taproot_signature_hash(tx, input_index, prevouts, sighash_type) is deterministic
1065    proptest! {
1066        #[test]
1067        fn prop_compute_taproot_signature_hash_deterministic(
1068            tx in create_transaction_strategy(),
1069            input_index in 0..10usize,
1070            prevouts in prop::collection::vec(create_output_strategy(), 0..5),
1071            sighash_type in any::<u8>()
1072        ) {
1073            let prevout_values: Vec<i64> = prevouts.iter().map(|p| p.value).collect();
1074            let prevout_script_pubkeys: Vec<&[u8]> = prevouts.iter().map(|p| p.script_pubkey.as_slice()).collect();
1075            let result1 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1076            let result2 = compute_taproot_signature_hash(&tx, input_index, &prevout_values, &prevout_script_pubkeys, sighash_type, None);
1077
1078            assert_eq!(result1.is_ok(), result2.is_ok());
1079            if let (Ok(hash1), Ok(hash2)) = (&result1, &result2) {
1080                assert_eq!(hash1, hash2);
1081                assert_eq!(hash1.len(), 32);
1082            }
1083
1084            // Hash should be 32 bytes if result is Ok
1085            if let Ok(ref hash) = result1 {
1086                assert_eq!(hash.len(), 32);
1087            }
1088        }
1089    }
1090
1091    /// Property test: Taproot output detection is consistent
1092    ///
1093    /// Mathematical specification:
1094    /// βˆ€ output ∈ TransactionOutput: is_taproot_output(output) ∈ {true, false}
1095    proptest! {
1096        #[test]
1097        fn prop_is_taproot_output_consistent(
1098            output in create_output_strategy()
1099        ) {
1100            let is_taproot = is_taproot_output(&output);
1101            // Just test it returns a boolean (is_taproot is either true or false)
1102            let _ = is_taproot;
1103        }
1104    }
1105
1106    /// Property test: Taproot transaction validation is deterministic
1107    ///
1108    /// Mathematical specification:
1109    /// βˆ€ tx ∈ Transaction: validate_taproot_transaction(tx) is deterministic
1110    proptest! {
1111        #[test]
1112        fn prop_validate_taproot_transaction_deterministic(
1113            tx in create_transaction_strategy()
1114        ) {
1115            let result1 = validate_taproot_transaction(&tx, None).unwrap();
1116            let result2 = validate_taproot_transaction(&tx, None).unwrap();
1117
1118            assert_eq!(result1, result2);
1119        }
1120    }
1121
1122    /// Property test: TapLeaf hashing is deterministic
1123    proptest! {
1124        #[test]
1125        fn prop_tap_leaf_hash_deterministic(
1126            script in prop::collection::vec(any::<u8>(), 0..20)
1127        ) {
1128            let hash1 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1129            let hash2 = crate::secp256k1_backend::tap_leaf_hash(TAPROOT_LEAF_VERSION_TAPSCRIPT, &script);
1130
1131            assert_eq!(hash1, hash2);
1132            assert_eq!(hash1.len(), 32);
1133        }
1134    }
1135
1136    /// Property test: TapBranch hashing is deterministic
1137    proptest! {
1138        #[test]
1139        fn prop_tap_branch_hash_deterministic(
1140            left in create_hash_strategy(),
1141            right in create_hash_strategy()
1142        ) {
1143            let hash1 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1144            let hash2 = crate::secp256k1_backend::tap_branch_hash(&left, &right);
1145
1146            assert_eq!(hash1, hash2);
1147            assert_eq!(hash1.len(), 32);
1148        }
1149    }
1150
1151    /// Property test: Varint encoding is deterministic
1152    ///
1153    /// Mathematical specification:
1154    /// βˆ€ value ∈ β„•: encode_varint(value) is deterministic
1155    proptest! {
1156        #[test]
1157        fn prop_encode_varint_deterministic(
1158            value in 0..u64::MAX
1159        ) {
1160            let encoded1 = encode_varint(value);
1161            let encoded2 = encode_varint(value);
1162
1163            assert_eq!(encoded1, encoded2);
1164
1165            // Encoded length should be reasonable
1166            assert!(!encoded1.is_empty());
1167            assert!(encoded1.len() <= 9);
1168        }
1169    }
1170
1171    /// Property test: Varint encoding preserves value
1172    ///
1173    /// Mathematical specification:
1174    /// βˆ€ value ∈ β„•: decode_varint(encode_varint(value)) = value
1175    proptest! {
1176        #[test]
1177        fn prop_encode_varint_preserves_value(
1178            value in 0..1000000u64  // Smaller range for tractability
1179        ) {
1180            let encoded = encode_varint(value);
1181
1182            // Basic validation of encoding format
1183            match encoded.len() {
1184                1 => {
1185                    // Single byte encoding
1186                    assert!(value < 0xfd);
1187                    assert_eq!(encoded[0], value as u8);
1188                },
1189                3 => {
1190                    // 2-byte encoding
1191                    assert!((0xfd..=0xffff).contains(&value));
1192                    assert_eq!(encoded[0], 0xfd);
1193                },
1194                5 => {
1195                    // 4-byte encoding
1196                    assert!(value > 0xffff && value <= 0xffffffff);
1197                    assert_eq!(encoded[0], 0xfe);
1198                },
1199                9 => {
1200                    // 8-byte encoding
1201                    assert!(value > 0xffffffff);
1202                    assert_eq!(encoded[0], 0xff);
1203                },
1204                _ => panic!("Invalid varint encoding length"),
1205            }
1206        }
1207    }
1208
1209    /// Property test: Taproot script path validation with correct proof
1210    ///
1211    /// Mathematical specification:
1212    /// βˆ€ script ∈ ByteString, merkle_proof ∈ [Hash]:
1213    /// If computed_root = compute_script_merkle_root(script, merkle_proof)
1214    /// then validate_taproot_script_path(script, merkle_proof, computed_root) = true
1215    proptest! {
1216        #[test]
1217        fn prop_validate_taproot_script_path_correct_proof(
1218            script in prop::collection::vec(any::<u8>(), 0..20),
1219            merkle_proof in prop::collection::vec(create_hash_strategy(), 0..5)
1220        ) {
1221            let computed_root = compute_script_merkle_root(&script, &merkle_proof, TAPROOT_LEAF_VERSION_TAPSCRIPT).unwrap();
1222            let is_valid = validate_taproot_script_path(&script, &merkle_proof, &computed_root).unwrap();
1223
1224            assert!(is_valid);
1225        }
1226    }
1227
1228    // Property test strategies
1229    fn create_transaction_strategy() -> impl Strategy<Value = Transaction> {
1230        (
1231            prop::collection::vec(any::<u8>(), 0..10), // inputs
1232            prop::collection::vec(any::<u8>(), 0..10), // outputs
1233        )
1234            .prop_map(|(input_data, output_data)| {
1235                let mut inputs = Vec::new();
1236                for (i, _) in input_data.iter().enumerate() {
1237                    inputs.push(TransactionInput {
1238                        prevout: OutPoint {
1239                            hash: [0; 32],
1240                            index: i as u32,
1241                        },
1242                        script_sig: vec![],
1243                        sequence: 0xffffffff,
1244                    });
1245                }
1246
1247                let mut outputs = Vec::new();
1248                for _ in output_data {
1249                    outputs.push(TransactionOutput {
1250                        value: 1000,
1251                        script_pubkey: vec![0x51],
1252                    });
1253                }
1254
1255                Transaction {
1256                    version: 1,
1257                    inputs: inputs.into(),
1258                    outputs: outputs.into(),
1259                    lock_time: 0,
1260                }
1261            })
1262    }
1263
1264    fn create_output_strategy() -> impl Strategy<Value = TransactionOutput> {
1265        (any::<i64>(), prop::collection::vec(any::<u8>(), 0..50)).prop_map(|(value, script)| {
1266            TransactionOutput {
1267                value,
1268                script_pubkey: script,
1269            }
1270        })
1271    }
1272
1273    fn create_hash_strategy() -> impl Strategy<Value = Hash> {
1274        prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1275            let mut hash = [0u8; 32];
1276            hash.copy_from_slice(&bytes);
1277            hash
1278        })
1279    }
1280
1281    fn create_pubkey_strategy() -> impl Strategy<Value = [u8; 32]> {
1282        prop::collection::vec(any::<u8>(), 32..=32).prop_map(|bytes| {
1283            let mut pubkey = [0u8; 32];
1284            pubkey.copy_from_slice(&bytes);
1285            pubkey
1286        })
1287    }
1288}