confidential_script_lib/
lib.rs

1// Written in 2025 by Joshua Doman <joshsdoman@gmail.com>
2// SPDX-License-Identifier: CC0-1.0
3
4//! # Confidential Script Library
5//!
6//! Emulate Bitcoin script by converting valid script-path spends to key-path spends. Intended for use within a Trusted Execution Environment (TEE), the library validates unlocking conditions and then authorizes the transaction using a deterministically derived private key.
7//!
8//! This approach enables confidential execution of complex script, including opcodes not yet supported by the Bitcoin protocol. The actual on-chain footprint is a minimal key-path spend, preserving privacy and efficiency.
9//!
10//! ## Overview
11//!
12//! The library operates on a two-step process: emulation and signing.
13//!
14//! 1.  **Emulation**: A transaction is constructed using an input spending a *real* `previous_outpoint` with a witness that is a script-path spend from an *emulated* P2TR `script_pubkey`. The library validates this emulated witness using a `Verifier`, which matches the API of `rust-bitcoinkernel`. If compiled with the `bitcoinkernel` feature, users can use the actual kernel as the default verifier, or they can provide an alternative verifier that enforces a different set of rules (ex: a fork of `bitcoinkernel` that supports Simplicity).
15//!
16//! 2.  **Signing**: If the transaction is valid, the library uses the provided parent private key and the merkle root of the *emulated* script path spend to derive a child private key, which corresponds to the internal public key of the *actual* UTXO being spent. The library then updates the transaction with a key-path spend signed with this child key.
17//!
18//! To facilitate offline generation of the real `script_pubkey`, the child key is derived from the parent key using a non-hardened HMAC-SHA512 derivation scheme. This lets users generate addresses using the parent _public_ key, while the parent private key is secured elsewhere.
19//!
20//! This library is intended to be run within a TEE, which is securely provisioned with the parent private key. This decouples script execution from on-chain settlement, keeping execution private and enabling new functionality with minimal trust assumptions.
21//!
22//! ## Failsafe Mechanism: Backup Script Path
23//!
24//! To prevent funds from being irrecoverably locked if the TEE becomes unavailable, the library allows for the inclusion of an optional `backup_merkle_root` when creating the actual on-chain address. This backup merkle root defines the alternative spending paths that are available independently of the TEE.
25//!
26//! A common use case for this feature is to include a timelocked recovery script (e.g., using `OP_CHECKSEQUENCEVERIFY`). If the primary TEE-based execution path becomes unavailable for any reason, the owner can wait for the timelock to expire and then recover the funds using a pre-defined backup script. This provides a crucial failsafe, ensuring that users retain ultimate control over their assets.
27//!
28//! ## Extensibility for Proposed Soft Forks
29//!
30//! This library can be used to emulate proposed upgrades, such as new opcodes like `OP_CAT` or `OP_CTV` or new scripting languages like Simplicity. It accepts any verifier that adheres to the `rust-bitcoinkernel` API, allowing developers to experiment with new functionality by forking the kernel, without waiting for a soft fork to gain adoption on mainnet.
31//!
32
33// Coding conventions
34#![deny(unsafe_code)]
35#![deny(non_upper_case_globals)]
36#![deny(non_camel_case_types)]
37#![deny(non_snake_case)]
38#![deny(unused_mut)]
39#![deny(dead_code)]
40#![deny(unused_imports)]
41#![deny(missing_docs)]
42
43#[cfg(not(any(feature = "std")))]
44compile_error!("`std` must be enabled");
45
46use bitcoin::{
47    Address, Network, TapNodeHash, TapSighashType, TapTweakHash, Transaction, TxOut, Weight,
48    Witness, XOnlyPublicKey,
49    consensus::deserialize,
50    hashes::Hash,
51    key::Secp256k1,
52    secp256k1,
53    secp256k1::{Keypair, Message, PublicKey, Scalar, SecretKey, constants::CURVE_ORDER},
54    sighash::{Annex, Prevouts, SighashCache},
55    taproot::{ControlBlock, Signature},
56};
57#[cfg(feature = "bitcoinkernel")]
58use bitcoinkernel::{KernelError, verify};
59use hmac::{Hmac, Mac};
60use num_bigint::BigUint;
61use sha2::Sha512;
62use std::fmt;
63
64/// The initial byte in a data-carrying taproot annex
65pub const TAPROOT_ANNEX_DATA_CARRYING_TAG: u8 = 0;
66
67/// Comprehensive error type for verify_and_sign operations
68#[derive(Debug)]
69pub enum Error {
70    /// Verification failed
71    VerificationFailed(String),
72    /// Wrapped secp256k1 errors from cryptographic operations
73    Secp256k1(secp256k1::Error),
74    /// Input is not a script path spend (missing taproot control block)
75    NotScriptPathSpend,
76    /// Invalid control block format or size
77    InvalidControlBlock,
78    /// Invalid amount
79    InvalidAmount(bitcoin_units::amount::OutOfRangeError),
80    /// Deserialization failed
81    DeserializationFailed(bitcoin::consensus::encode::Error),
82    /// Unable to calculate sighash
83    InvalidSighash,
84    /// Input index out of bounds
85    InputIndexOutOfBounds,
86    /// Unexpected input scriptPubKey
87    UnexpectedInput,
88    /// Exceeds maximum allowed weight
89    ExceedsMaxWeight,
90}
91
92/// Trait to abstract the behavior of the bitcoin script verifier, allowing
93/// users to provide their own verifier.
94pub trait Verifier {
95    /// Verify a bitcoin script, mirroring the API of `bitcoinkernel::verify`.
96    ///
97    /// # Arguments
98    /// * `script_pubkey` - The script public key to verify.
99    /// * `amount` - The amount of the input being spent.
100    /// * `tx_to` - The transaction containing the script.
101    /// * `input_index` - The index of the input to verify.
102    /// * `spent_outputs` - The outputs being spent by the transaction.
103    /// * `tx_weight` - The weight of the transaction.
104    ///
105    /// # Errors
106    /// Returns `Error` if verification fails.
107    fn verify(
108        &self,
109        script_pubkey: &[u8],
110        amount: Option<i64>,
111        tx_to: &[u8],
112        input_index: u32,
113        spent_outputs: &[TxOut],
114        tx_weight: Weight,
115    ) -> Result<(), Error>;
116}
117
118/// The default `Verifier` implementation that uses `bitcoinkernel`.
119#[cfg(feature = "bitcoinkernel")]
120pub struct DefaultVerifier;
121
122#[cfg(feature = "bitcoinkernel")]
123impl Verifier for DefaultVerifier {
124    fn verify(
125        &self,
126        script_pubkey: &[u8],
127        amount: Option<i64>,
128        tx_to: &[u8],
129        input_index: u32,
130        spent_outputs: &[TxOut],
131        tx_weight: Weight,
132    ) -> Result<(), Error> {
133        if tx_weight > Weight::MAX_BLOCK {
134            return Err(Error::ExceedsMaxWeight);
135        }
136
137        let mut outputs = Vec::new();
138        for txout in spent_outputs {
139            let amount = txout.value.to_signed()?.to_sat();
140            let script = bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes())?;
141            outputs.push(bitcoinkernel::TxOut::new(&script, amount));
142        }
143
144        verify(
145            &bitcoinkernel::ScriptPubkey::try_from(script_pubkey)?,
146            amount,
147            &bitcoinkernel::Transaction::try_from(tx_to)?,
148            input_index,
149            None,
150            &outputs,
151        )?;
152
153        Ok(())
154    }
155}
156
157/// Verifies an emulated Bitcoin script and signs the corresponding transaction.
158///
159/// This function performs script verification using bitcoinkernel, verifying an
160/// emulated P2TR input. If successful, it derives an XOnlyPublicKey from the
161/// parent key and the emulated merkle root, which is then tweaked with an optional
162/// backup merkle root to derive the actual spent UTXO, which is then key-path signed
163/// with `SIGHASH_DEFAULT`.
164///
165/// If the emulated script-path spend includes a data-carrying annex (begins with 0x50
166/// followed by 0x00), the annex is included in the key-path spend. Otherwise, the annex
167/// is dropped.
168///
169/// # Arguments
170/// * `verifier` - The verifier to use for script validation
171/// * `input_index` - Index of the input to verify and sign (0-based)
172/// * `emulated_tx_to` - Serialized transaction to verify and sign
173/// * `actual_spent_outputs` - Actual outputs being spent
174/// * `aux_rand` - Auxiliary random data for signing
175/// * `parent_key` - Parent secret key used to derive child key for signing
176/// * `backup_merkle_root` - Optional merkle root for backup script path spending
177///
178/// # Errors
179/// Returns error if verification fails, key derivation fails, or signing fails
180pub fn verify_and_sign<V: Verifier>(
181    verifier: &V,
182    input_index: u32,
183    emulated_tx_to: &[u8],
184    actual_spent_outputs: &[TxOut],
185    aux_rand: &[u8; 32],
186    parent_key: SecretKey,
187    backup_merkle_root: Option<TapNodeHash>,
188) -> Result<Transaction, Error> {
189    // Must be able to deserialize transaction
190    let mut tx: Transaction = deserialize(emulated_tx_to)?;
191
192    // Input index must be in bounds
193    if input_index as usize >= tx.input.len() {
194        return Err(Error::InputIndexOutOfBounds);
195    }
196
197    // Get the input amount
198    let amount = actual_spent_outputs[input_index as usize]
199        .value
200        .to_signed()?
201        .to_sat();
202
203    // Must be script path spend
204    let input = tx.input[input_index as usize].clone();
205    let (Some(control_block), Some(tapleaf)) = (
206        input.witness.taproot_control_block(),
207        input.witness.taproot_leaf_script(),
208    ) else {
209        return Err(Error::NotScriptPathSpend);
210    };
211    let Ok(control_block) = ControlBlock::decode(control_block) else {
212        return Err(Error::NotScriptPathSpend);
213    };
214
215    // Calculate merkle root
216    let mut merkle_root = TapNodeHash::from_script(tapleaf.script, tapleaf.version);
217    for elem in &control_block.merkle_branch {
218        merkle_root = TapNodeHash::from_node_hashes(merkle_root, *elem);
219    }
220
221    // Create emulated script pubkey
222    let secp = Secp256k1::new();
223    let emulated_address = Address::p2tr(
224        &secp,
225        control_block.internal_key,
226        Some(merkle_root),
227        Network::Bitcoin,
228    );
229
230    // Get actual internal key and child key to be tweaked for signing
231    let child_key = derive_child_secret_key(parent_key, merkle_root.to_byte_array())?;
232    let (internal_key, parity) = child_key.public_key(&secp).x_only_public_key();
233    let child_key_for_tweak = if parity == secp256k1::Parity::Odd {
234        child_key.negate()
235    } else {
236        child_key
237    };
238
239    // Actual input scriptPubKey must match expected actual scriptPubKey
240    let actual_address = Address::p2tr(&secp, internal_key, backup_merkle_root, Network::Bitcoin);
241    if actual_spent_outputs[input_index as usize].script_pubkey != actual_address.script_pubkey() {
242        return Err(Error::UnexpectedInput);
243    }
244
245    // Must satisfy verifier
246    verifier.verify(
247        emulated_address.script_pubkey().as_bytes(),
248        Some(amount),
249        emulated_tx_to,
250        input_index,
251        actual_spent_outputs,
252        tx.weight(),
253    )?;
254
255    // Get annex if it is data-carrying (leading byte is 0x00)
256    let annex = input
257        .witness
258        .taproot_annex()
259        .filter(|bytes| bytes.len() > 1 && bytes[1] == TAPROOT_ANNEX_DATA_CARRYING_TAG)
260        .and_then(|bytes| Annex::new(bytes).ok());
261
262    // Create sighash for the input
263    let mut sighash_cache = SighashCache::new(&tx);
264    let sighash_bytes = sighash_cache
265        .taproot_signature_hash(
266            input_index as usize,
267            &Prevouts::All(actual_spent_outputs),
268            annex.clone(),
269            None,
270            TapSighashType::Default,
271        )
272        .map_err(|_| Error::InvalidSighash)?;
273    let mut sighash = [0u8; 32];
274    sighash.copy_from_slice(sighash_bytes.as_byte_array());
275
276    // Calculate the taproot tweaked private key for keypath spending
277    let tweak = TapTweakHash::from_key_and_tweak(internal_key, backup_merkle_root);
278    let tweaked_secret_key = child_key_for_tweak.add_tweak(&tweak.to_scalar())?;
279    let tweaked_keypair = Keypair::from_secret_key(&secp, &tweaked_secret_key);
280
281    // Sign the sighash
282    let message = Message::from_digest(sighash);
283    let signature = secp.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand);
284
285    // Create taproot signature (schnorr signature + sighash type)
286    let tap_signature = Signature {
287        signature,
288        sighash_type: TapSighashType::Default,
289    };
290
291    // Create witness for keypath spend (include annex if data-carrying annex is present)
292    let mut witness = Witness::new();
293    witness.push(tap_signature.to_vec());
294    if let Some(annex) = annex {
295        witness.push(annex.as_bytes());
296    }
297    tx.input[input_index as usize].witness = witness;
298
299    Ok(tx)
300}
301
302/// Generates P2TR address from a parent public key and the emulated merkle root,
303/// with an optional backup merkle root.
304///
305/// # Arguments
306/// * `parent_key` - The parent public key
307/// * `emulated_merkle_root` - The merkle root of the emulated input
308/// * `backup_merkle_root` - Optional merkle root for backup script path spending
309/// * `network` - The network to generate the address for
310///
311/// # Errors
312/// Returns an error if key derivation fails
313pub fn generate_address(
314    parent_key: PublicKey,
315    emulated_merkle_root: TapNodeHash,
316    backup_merkle_root: Option<TapNodeHash>,
317    network: Network,
318) -> Result<Address, secp256k1::Error> {
319    let secp = Secp256k1::new();
320    let child_key = derive_child_public_key(parent_key, emulated_merkle_root.to_byte_array())?;
321    let internal_key = XOnlyPublicKey::from(child_key);
322    let address = Address::p2tr(&secp, internal_key, backup_merkle_root, network);
323
324    Ok(address)
325}
326
327/// Derives a child secret key from a parent secret key and emulated merkle root
328/// using HMAC-SHA512 based key derivation (non-hardened derivation).
329fn derive_child_secret_key(
330    parent_key: SecretKey,
331    emulated_merkle_root: [u8; 32],
332) -> Result<SecretKey, secp256k1::Error> {
333    let secp = Secp256k1::new();
334
335    // Derive parent public key from parent secret
336    let parent_public = parent_key.public_key(&secp);
337
338    // Create HMAC-SHA512 with parent public key and merkle root
339    let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
340        .expect("PublicKey serialization should always be non-empty");
341    mac.update(&emulated_merkle_root);
342    let hmac_result = mac.finalize().into_bytes();
343
344    // Use first 32 bytes for key material
345    let mut key_material = [0u8; 32];
346    key_material.copy_from_slice(&hmac_result[..32]);
347    let scalar = reduce_mod_order(&key_material);
348
349    // Add the key material to parent private key
350    parent_key.add_tweak(&scalar)
351}
352
353/// Derives a child public key from a parent public key and emulated merkle root
354/// This allows public key derivation without access to private keys.
355fn derive_child_public_key(
356    parent_public: PublicKey,
357    emulated_merkle_root: [u8; 32],
358) -> Result<PublicKey, secp256k1::Error> {
359    let secp = Secp256k1::new();
360
361    // Create HMAC-SHA512 with parent public key as key
362    let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
363        .expect("PublicKey serialization should always be non-empty");
364    mac.update(&emulated_merkle_root);
365    let hmac_result = mac.finalize().into_bytes();
366
367    // Use first 32 bytes as scalar for point multiplication
368    let mut key_material = [0u8; 32];
369    key_material.copy_from_slice(&hmac_result[..32]);
370    let scalar = reduce_mod_order(&key_material);
371
372    // Add scalar * G to parent public key
373    parent_public.add_exp_tweak(&secp, &scalar)
374}
375
376/// Safely reduces a 32-byte array modulo the secp256k1 curve order
377fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
378    // Keep trying to create a scalar until we get a valid one
379    // In practice, this loop will almost always execute only once
380    let mut attempt = *bytes;
381    loop {
382        match Scalar::from_be_bytes(attempt) {
383            Ok(scalar) => return scalar,
384            Err(_) => {
385                // If the value is too large, subtract the curve order
386                // This is equivalent to modular reduction
387                attempt = subtract_curve_order(&attempt);
388            }
389        }
390    }
391}
392
393/// Subtract the secp256k1 curve order from a 32-byte big-endian number
394fn subtract_curve_order(bytes: &[u8; 32]) -> [u8; 32] {
395    let value = BigUint::from_bytes_be(bytes);
396    let order = BigUint::from_bytes_be(&CURVE_ORDER);
397    let reduced = value % order;
398
399    let mut result = [0u8; 32];
400    let reduced_bytes = reduced.to_bytes_be();
401    let offset = 32 - reduced_bytes.len();
402    result[offset..].copy_from_slice(&reduced_bytes);
403    result
404}
405
406impl fmt::Display for Error {
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        match self {
409            Error::VerificationFailed(e) => {
410                write!(f, "Verification failed: {e}")
411            }
412            Error::Secp256k1(e) => {
413                write!(f, "Secp256k1 cryptographic operation failed: {e}")
414            }
415            Error::NotScriptPathSpend => {
416                write!(
417                    f,
418                    "Input is not a script path spend (missing taproot control block)"
419                )
420            }
421            Error::InvalidAmount(e) => {
422                write!(f, "Invalid amount: {e}")
423            }
424            Error::InvalidControlBlock => {
425                write!(f, "Input has invalid control block")
426            }
427            Error::DeserializationFailed(e) => {
428                write!(f, "Failed to deserialize: {e}")
429            }
430            Error::InvalidSighash => {
431                write!(f, "Unable to calculate sighash for input")
432            }
433            Error::InputIndexOutOfBounds => {
434                write!(f, "Input index out of bounds")
435            }
436            Error::UnexpectedInput => {
437                write!(f, "Unexpected input scriptPubKey")
438            }
439            Error::ExceedsMaxWeight => {
440                write!(f, "Exceeds maximum allowed transaction weight")
441            }
442        }
443    }
444}
445
446#[cfg(feature = "bitcoinkernel")]
447impl From<KernelError> for Error {
448    fn from(error: KernelError) -> Self {
449        Error::VerificationFailed(error.to_string())
450    }
451}
452
453impl From<secp256k1::Error> for Error {
454    fn from(error: secp256k1::Error) -> Self {
455        Error::Secp256k1(error)
456    }
457}
458
459impl From<bitcoin::consensus::encode::Error> for Error {
460    fn from(error: bitcoin::consensus::encode::Error) -> Self {
461        Error::DeserializationFailed(error)
462    }
463}
464
465impl From<bitcoin_units::amount::OutOfRangeError> for Error {
466    fn from(error: bitcoin_units::amount::OutOfRangeError) -> Self {
467        Error::InvalidAmount(error)
468    }
469}
470
471#[cfg(test)]
472#[cfg(feature = "bitcoinkernel")]
473mod kernel_tests {
474    use super::*;
475    use bitcoin::{
476        Address, Amount, Network, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut, Txid,
477        Witness,
478        consensus::encode::serialize,
479        hashes::Hash,
480        key::UntweakedPublicKey,
481        taproot::{LeafVersion, TaprootBuilder},
482    };
483
484    fn create_test_transaction_single_input() -> Transaction {
485        Transaction {
486            version: bitcoin::transaction::Version::TWO,
487            lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
488            input: vec![TxIn {
489                previous_output: OutPoint::null(),
490                script_sig: ScriptBuf::new(),
491                sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
492                witness: Witness::new(),
493            }],
494            output: vec![TxOut {
495                value: Amount::from_sat(100000),
496                script_pubkey: ScriptBuf::new_op_return([]),
497            }],
498        }
499    }
500
501    fn create_test_transaction_multi_input() -> Transaction {
502        Transaction {
503            version: bitcoin::transaction::Version::TWO,
504            lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
505            input: vec![
506                TxIn {
507                    previous_output: OutPoint::null(),
508                    script_sig: ScriptBuf::new(),
509                    sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
510                    witness: Witness::new(),
511                },
512                TxIn {
513                    previous_output: OutPoint::new(Txid::all_zeros(), 1),
514                    script_sig: ScriptBuf::new(),
515                    sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
516                    witness: Witness::new(),
517                },
518            ],
519            output: vec![TxOut {
520                value: Amount::from_sat(100000),
521                script_pubkey: ScriptBuf::new_op_return([]),
522            }],
523        }
524    }
525
526    #[test]
527    fn test_unable_to_deserialize_tx() {
528        let result = verify_and_sign(
529            &DefaultVerifier,
530            0,
531            &[],
532            &[],
533            &[1u8; 32],
534            SecretKey::from_slice(&[1u8; 32]).unwrap(),
535            None,
536        );
537
538        assert!(matches!(result, Err(Error::DeserializationFailed(_))));
539    }
540
541    #[test]
542    fn test_input_index_out_of_bounds() {
543        let txout = TxOut {
544            value: Amount::from_sat(100000),
545            script_pubkey: ScriptBuf::new_op_return([]),
546        };
547        let result = verify_and_sign(
548            &DefaultVerifier,
549            1,
550            &serialize(&create_test_transaction_single_input()),
551            std::slice::from_ref(&txout),
552            &[1u8; 32],
553            SecretKey::from_slice(&[1u8; 32]).unwrap(),
554            None,
555        );
556
557        assert!(matches!(result, Err(Error::InputIndexOutOfBounds)));
558    }
559
560    #[test]
561    fn test_not_script_path_spend() {
562        let txout = TxOut {
563            value: Amount::from_sat(100000),
564            script_pubkey: ScriptBuf::new_op_return([]),
565        };
566        let result = verify_and_sign(
567            &DefaultVerifier,
568            0,
569            &serialize(&create_test_transaction_single_input()),
570            std::slice::from_ref(&txout),
571            &[1u8; 32],
572            SecretKey::from_slice(&[1u8; 32]).unwrap(),
573            None,
574        );
575
576        assert!(matches!(result, Err(Error::NotScriptPathSpend)));
577    }
578
579    #[test]
580    fn test_unexpected_input_script_pubkey() {
581        let secp = Secp256k1::new();
582
583        // 1. Create a dummy internal key
584        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
585        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
586
587        // 2. Create OP_TRUE script leaf
588        let op_true_script = Script::builder()
589            .push_opcode(bitcoin::opcodes::OP_TRUE)
590            .into_script();
591
592        // 3. Build the taproot tree with single OP_TRUE leaf
593        let taproot_builder = TaprootBuilder::new()
594            .add_leaf(0, op_true_script.clone())
595            .unwrap();
596        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
597
598        // 4. Get the control block for our OP_TRUE leaf
599        let control_block = taproot_spend_info
600            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
601            .unwrap();
602
603        // 5. Create the witness stack for script path spending
604        let mut witness = Witness::new();
605        witness.push(op_true_script.as_bytes());
606        witness.push(control_block.serialize());
607
608        // 6. Create emulated transaction
609        let mut emulated_tx = create_test_transaction_single_input();
610        emulated_tx.input[0].witness = witness;
611
612        // 7. Create input UTXO with unexpected scriptPubKey
613        let txout = TxOut {
614            value: Amount::from_sat(100000),
615            script_pubkey: ScriptBuf::new_op_return([]),
616        };
617
618        let result = verify_and_sign(
619            &DefaultVerifier,
620            0,
621            &serialize(&emulated_tx),
622            std::slice::from_ref(&txout),
623            &[1u8; 32],
624            SecretKey::from_slice(&[1u8; 32]).unwrap(),
625            None,
626        );
627
628        assert!(matches!(result, Err(Error::UnexpectedInput)));
629    }
630
631    #[test]
632    fn test_verify_and_sign_single_input_single_leaf() {
633        let secp = Secp256k1::new();
634
635        // 1. Create a dummy internal key
636        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
637        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
638
639        // 2. Create OP_TRUE script leaf
640        let op_true_script = Script::builder()
641            .push_opcode(bitcoin::opcodes::OP_TRUE)
642            .into_script();
643
644        // 3. Build the taproot tree with single OP_TRUE leaf
645        let taproot_builder = TaprootBuilder::new()
646            .add_leaf(0, op_true_script.clone())
647            .unwrap();
648        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
649
650        // 4. Get the control block for our OP_TRUE leaf
651        let control_block = taproot_spend_info
652            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
653            .unwrap();
654
655        // 5. Create the witness stack for script path spending
656        let mut witness = Witness::new();
657        witness.push(op_true_script.as_bytes());
658        witness.push(control_block.serialize());
659
660        // 6. Create emulated transaction
661        let mut emulated_tx = create_test_transaction_single_input();
662        emulated_tx.input[0].witness = witness;
663
664        // 7. Create actual child secret
665        let aux_rand = [1u8; 32];
666        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
667        let child_secret = derive_child_secret_key(
668            parent_secret,
669            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
670        )
671        .unwrap();
672
673        // 8. Create actual P2TR outputs
674        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
675        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
676        let actual_spent_outputs = [TxOut {
677            value: Amount::from_sat(100_000),
678            script_pubkey: actual_address.script_pubkey(),
679        }];
680
681        // 9. Verify and sign actual transaction
682        let actual_tx = verify_and_sign(
683            &DefaultVerifier,
684            0,
685            &serialize(&emulated_tx),
686            &actual_spent_outputs,
687            &aux_rand,
688            parent_secret,
689            None,
690        )
691        .unwrap();
692
693        let mut actual_outputs = Vec::new();
694        for txout in actual_spent_outputs {
695            let amount = txout.value.to_signed().unwrap().to_sat();
696            let script =
697                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
698            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
699        }
700
701        // 10. Verify the actual transaction was properly signed
702        let verify_result = bitcoinkernel::verify(
703            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
704                .unwrap(),
705            Some(100_000),
706            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
707            0,
708            None,
709            &actual_outputs,
710        );
711
712        assert!(verify_result.is_ok());
713        assert_eq!(actual_tx.input[0].witness.len(), 1);
714    }
715
716    #[test]
717    fn test_verify_and_sign_single_input_multiple_leaves() {
718        let secp = Secp256k1::new();
719
720        // 1. Create a dummy internal key
721        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
722        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
723
724        // 2. Create script leaves
725        let op_true_script = Script::builder()
726            .push_opcode(bitcoin::opcodes::OP_TRUE)
727            .into_script();
728        let op_false_script = Script::builder()
729            .push_opcode(bitcoin::opcodes::OP_FALSE)
730            .into_script();
731
732        // 3. Build the taproot tree with two leaves
733        let taproot_builder = TaprootBuilder::new()
734            .add_leaf(1, op_true_script.clone())
735            .unwrap()
736            .add_leaf(1, op_false_script.clone())
737            .unwrap();
738        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
739
740        // 4. Get the control block for our OP_TRUE leaf
741        let control_block = taproot_spend_info
742            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
743            .unwrap();
744
745        // 5. Create the witness stack for script path spending
746        let mut witness = Witness::new();
747        witness.push(op_true_script.as_bytes());
748        witness.push(control_block.serialize());
749
750        // 6. Create emulated transaction
751        let mut emulated_tx = create_test_transaction_single_input();
752        emulated_tx.input[0].witness = witness;
753
754        // 7. Create actual child secret
755        let aux_rand = [1u8; 32];
756        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
757        let child_secret = derive_child_secret_key(
758            parent_secret,
759            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
760        )
761        .unwrap();
762
763        // 8. Create actual P2TR outputs
764        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
765        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
766        let actual_spent_outputs = [TxOut {
767            value: Amount::from_sat(100_000),
768            script_pubkey: actual_address.script_pubkey(),
769        }];
770
771        // 9. Verify and sign actual transaction
772        let actual_tx = verify_and_sign(
773            &DefaultVerifier,
774            0,
775            &serialize(&emulated_tx),
776            &actual_spent_outputs,
777            &aux_rand,
778            parent_secret,
779            None,
780        )
781        .unwrap();
782
783        let mut actual_outputs = Vec::new();
784        for txout in actual_spent_outputs {
785            let amount = txout.value.to_signed().unwrap().to_sat();
786            let script =
787                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
788            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
789        }
790
791        // 10. Verify the actual transaction was properly signed
792        let verify_result = bitcoinkernel::verify(
793            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
794                .unwrap(),
795            Some(100_000),
796            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
797            0,
798            None,
799            &actual_outputs,
800        );
801
802        assert!(verify_result.is_ok());
803        assert_eq!(actual_tx.input[0].witness.len(), 1);
804    }
805
806    #[test]
807    fn test_verify_and_sign_single_input_with_backup() {
808        let secp = Secp256k1::new();
809
810        // 1. Create a dummy internal key
811        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
812        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
813
814        // 2. Create OP_TRUE script leaf
815        let op_true_script = Script::builder()
816            .push_opcode(bitcoin::opcodes::OP_TRUE)
817            .into_script();
818
819        // 3. Build the taproot tree with single OP_TRUE leaf
820        let taproot_builder = TaprootBuilder::new()
821            .add_leaf(0, op_true_script.clone())
822            .unwrap();
823        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
824
825        // 4. Get the control block for our OP_TRUE leaf
826        let control_block = taproot_spend_info
827            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
828            .unwrap();
829
830        // 5. Create the witness stack for script path spending
831        let mut witness = Witness::new();
832        witness.push(op_true_script.as_bytes());
833        witness.push(control_block.serialize());
834
835        // 6. Create emulated transaction
836        let mut emulated_tx = create_test_transaction_single_input();
837        emulated_tx.input[0].witness = witness;
838
839        // 7. Create actual child secret
840        let aux_rand = [1u8; 32];
841        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
842        let child_secret = derive_child_secret_key(
843            parent_secret,
844            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
845        )
846        .unwrap();
847
848        // 8. Create actual P2TR outputs
849        let actual_backup_merkle_root = taproot_spend_info.merkle_root();
850        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
851        let actual_address = Address::p2tr(
852            &secp,
853            actual_internal_key,
854            actual_backup_merkle_root,
855            Network::Bitcoin,
856        );
857        let actual_spent_outputs = [TxOut {
858            value: Amount::from_sat(100_000),
859            script_pubkey: actual_address.script_pubkey(),
860        }];
861
862        // 9. Verify and sign actual transaction
863        let actual_tx = verify_and_sign(
864            &DefaultVerifier,
865            0,
866            &serialize(&emulated_tx),
867            &actual_spent_outputs,
868            &aux_rand,
869            parent_secret,
870            actual_backup_merkle_root,
871        )
872        .unwrap();
873
874        let mut actual_outputs = Vec::new();
875        for txout in actual_spent_outputs {
876            let amount = txout.value.to_signed().unwrap().to_sat();
877            let script =
878                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
879            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
880        }
881
882        // 10. Verify the actual transaction was properly signed
883        let verify_result = bitcoinkernel::verify(
884            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
885                .unwrap(),
886            Some(100_000),
887            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
888            0,
889            None,
890            &actual_outputs,
891        );
892
893        assert!(verify_result.is_ok());
894        assert_eq!(actual_tx.input[0].witness.len(), 1);
895    }
896
897    #[test]
898    fn test_verify_and_sign_single_input_single_leaf_with_data_carrying_annex() {
899        let secp = Secp256k1::new();
900
901        // 1. Create a dummy internal key
902        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
903        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
904
905        // 2. Create OP_TRUE script leaf
906        let op_true_script = Script::builder()
907            .push_opcode(bitcoin::opcodes::OP_TRUE)
908            .into_script();
909
910        // 3. Build the taproot tree with single OP_TRUE leaf
911        let taproot_builder = TaprootBuilder::new()
912            .add_leaf(0, op_true_script.clone())
913            .unwrap();
914        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
915
916        // 4. Get the control block for our OP_TRUE leaf
917        let control_block = taproot_spend_info
918            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
919            .unwrap();
920
921        // 5. Create data-carrying annex
922        let annex: &[u8] = &[0x50, TAPROOT_ANNEX_DATA_CARRYING_TAG, 0x01, 0x02, 0x03];
923
924        // 6. Create the witness stack for script path spending
925        let mut witness = Witness::new();
926        witness.push(op_true_script.as_bytes());
927        witness.push(control_block.serialize());
928        witness.push(annex);
929
930        // 7. Create emulated transaction
931        let mut emulated_tx = create_test_transaction_single_input();
932        emulated_tx.input[0].witness = witness;
933
934        // 8. Create actual child secret
935        let aux_rand = [1u8; 32];
936        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
937        let child_secret = derive_child_secret_key(
938            parent_secret,
939            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
940        )
941        .unwrap();
942
943        // 9. Create actual P2TR outputs
944        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
945        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
946        let actual_spent_outputs = [TxOut {
947            value: Amount::from_sat(100_000),
948            script_pubkey: actual_address.script_pubkey(),
949        }];
950
951        // 10. Verify and sign actual transaction
952        let actual_tx = verify_and_sign(
953            &DefaultVerifier,
954            0,
955            &serialize(&emulated_tx),
956            &actual_spent_outputs,
957            &aux_rand,
958            parent_secret,
959            None,
960        )
961        .unwrap();
962
963        let mut actual_outputs = Vec::new();
964        for txout in actual_spent_outputs {
965            let amount = txout.value.to_signed().unwrap().to_sat();
966            let script =
967                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
968            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
969        }
970
971        // 11. Verify the actual transaction was properly signed
972        let verify_result = bitcoinkernel::verify(
973            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
974                .unwrap(),
975            Some(100_000),
976            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
977            0,
978            None,
979            &actual_outputs,
980        );
981
982        assert!(verify_result.is_ok());
983        assert_eq!(actual_tx.input[0].witness.len(), 2);
984        assert_eq!(&actual_tx.input[0].witness[1], annex);
985    }
986
987    #[test]
988    fn test_verify_and_sign_single_input_single_leaf_with_non_data_carrying_annex() {
989        let secp = Secp256k1::new();
990
991        // 1. Create a dummy internal key
992        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
993        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
994
995        // 2. Create OP_TRUE script leaf
996        let op_true_script = Script::builder()
997            .push_opcode(bitcoin::opcodes::OP_TRUE)
998            .into_script();
999
1000        // 3. Build the taproot tree with single OP_TRUE leaf
1001        let taproot_builder = TaprootBuilder::new()
1002            .add_leaf(0, op_true_script.clone())
1003            .unwrap();
1004        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1005
1006        // 4. Get the control block for our OP_TRUE leaf
1007        let control_block = taproot_spend_info
1008            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1009            .unwrap();
1010
1011        // 5. Create non-data-carrying annex
1012        let annex: &[u8] = &[0x50, 0x01, 0x02, 0x03];
1013
1014        // 6. Create the witness stack for script path spending
1015        let mut witness = Witness::new();
1016        witness.push(op_true_script.as_bytes());
1017        witness.push(control_block.serialize());
1018        witness.push(annex);
1019
1020        // 7. Create emulated transaction
1021        let mut emulated_tx = create_test_transaction_single_input();
1022        emulated_tx.input[0].witness = witness;
1023
1024        // 8. Create actual child secret
1025        let aux_rand = [1u8; 32];
1026        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1027        let child_secret = derive_child_secret_key(
1028            parent_secret,
1029            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1030        )
1031        .unwrap();
1032
1033        // 9. Create actual P2TR outputs
1034        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1035        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1036        let actual_spent_outputs = [TxOut {
1037            value: Amount::from_sat(100_000),
1038            script_pubkey: actual_address.script_pubkey(),
1039        }];
1040
1041        // 10. Verify and sign actual transaction
1042        let actual_tx = verify_and_sign(
1043            &DefaultVerifier,
1044            0,
1045            &serialize(&emulated_tx),
1046            &actual_spent_outputs,
1047            &aux_rand,
1048            parent_secret,
1049            None,
1050        )
1051        .unwrap();
1052
1053        let mut actual_outputs = Vec::new();
1054        for txout in actual_spent_outputs {
1055            let amount = txout.value.to_signed().unwrap().to_sat();
1056            let script =
1057                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
1058            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
1059        }
1060
1061        // 11. Verify the actual transaction was properly signed
1062        let verify_result = bitcoinkernel::verify(
1063            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
1064                .unwrap(),
1065            Some(100_000),
1066            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
1067            0,
1068            None,
1069            &actual_outputs,
1070        );
1071
1072        assert!(verify_result.is_ok());
1073        assert_eq!(actual_tx.input[0].witness.len(), 1);
1074    }
1075
1076    #[test]
1077    fn test_verify_and_sign_multi_input_tx() {
1078        let secp = Secp256k1::new();
1079
1080        // 1. Create a dummy internal key
1081        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1082        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
1083
1084        // 2. Create OP_TRUE script leaf
1085        let op_true_script = Script::builder()
1086            .push_opcode(bitcoin::opcodes::OP_TRUE)
1087            .into_script();
1088
1089        // 3. Build the taproot tree with single OP_TRUE leaf
1090        let taproot_builder = TaprootBuilder::new()
1091            .add_leaf(0, op_true_script.clone())
1092            .unwrap();
1093        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1094
1095        // 4. Get the control block for our OP_TRUE leaf
1096        let control_block = taproot_spend_info
1097            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1098            .unwrap();
1099
1100        // 5. Create the witness stack for script path spending
1101        let mut witness = Witness::new();
1102        witness.push(op_true_script.as_bytes());
1103        witness.push(control_block.serialize());
1104
1105        // 6. Create emulated transaction
1106        let mut emulated_tx = create_test_transaction_multi_input();
1107        emulated_tx.input[1].witness = witness;
1108
1109        // 7. Create actual child secret
1110        let aux_rand = [1u8; 32];
1111        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1112        let child_secret = derive_child_secret_key(
1113            parent_secret,
1114            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1115        )
1116        .unwrap();
1117
1118        // 8. Create actual P2TR outputs
1119        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1120        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1121        let actual_spent_outputs = [
1122            TxOut {
1123                value: Amount::from_sat(200_000),
1124                script_pubkey: actual_address.script_pubkey(),
1125            },
1126            TxOut {
1127                value: Amount::from_sat(100_000),
1128                script_pubkey: actual_address.script_pubkey(),
1129            },
1130        ];
1131
1132        // 9. Verify and sign actual transaction
1133        let actual_tx = verify_and_sign(
1134            &DefaultVerifier,
1135            1,
1136            &serialize(&emulated_tx),
1137            &actual_spent_outputs,
1138            &aux_rand,
1139            parent_secret,
1140            None,
1141        )
1142        .unwrap();
1143
1144        let mut actual_outputs = Vec::new();
1145        for txout in actual_spent_outputs {
1146            let amount = txout.value.to_signed().unwrap().to_sat();
1147            let script =
1148                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
1149            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
1150        }
1151
1152        // 10. Verify the actual transaction was properly signed
1153        let verify_result = bitcoinkernel::verify(
1154            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
1155                .unwrap(),
1156            Some(100_000),
1157            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
1158            1,
1159            None,
1160            &actual_outputs,
1161        );
1162
1163        assert!(verify_result.is_ok());
1164        assert_eq!(actual_tx.input[1].witness.len(), 1);
1165    }
1166
1167    #[test]
1168    fn test_exceeds_max_weight() {
1169        let secp = Secp256k1::new();
1170
1171        // 1. Create a dummy internal key
1172        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1173        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
1174
1175        // 2. Create OP_TRUE script leaf
1176        let op_true_script = Script::builder()
1177            .push_opcode(bitcoin::opcodes::OP_TRUE)
1178            .into_script();
1179
1180        // 3. Build the taproot tree with single OP_TRUE leaf
1181        let taproot_builder = TaprootBuilder::new()
1182            .add_leaf(0, op_true_script.clone())
1183            .unwrap();
1184        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1185
1186        // 4. Get the control block for our OP_TRUE leaf
1187        let control_block = taproot_spend_info
1188            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1189            .unwrap();
1190
1191        // 5. Create the witness stack for script path spending
1192        let mut witness = Witness::new();
1193        witness.push(op_true_script.as_bytes());
1194        witness.push(control_block.serialize());
1195
1196        // 6. Create an excessively large emulated transaction
1197        let mut emulated_tx = create_test_transaction_single_input();
1198        emulated_tx.output = vec![
1199            TxOut {
1200                value: Amount::from_sat(1),
1201                script_pubkey: ScriptBuf::new(),
1202            };
1203            120_000
1204        ];
1205        emulated_tx.input[0].witness = witness;
1206
1207        // 7. Create actual child secret
1208        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1209        let child_secret = derive_child_secret_key(
1210            parent_secret,
1211            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1212        )
1213        .unwrap();
1214
1215        // 8. Create actual P2TR output
1216        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1217        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1218        let actual_spent_outputs = [TxOut {
1219            value: Amount::from_sat(100_000),
1220            script_pubkey: actual_address.script_pubkey(),
1221        }];
1222
1223        // 9. Verify and sign, expecting an error
1224        let result = verify_and_sign(
1225            &DefaultVerifier,
1226            0,
1227            &serialize(&emulated_tx),
1228            &actual_spent_outputs,
1229            &[1u8; 32],
1230            parent_secret,
1231            None,
1232        );
1233
1234        assert!(matches!(result, Err(Error::ExceedsMaxWeight)));
1235    }
1236}
1237
1238#[cfg(test)]
1239mod non_kernel_tests {
1240    use super::*;
1241    use bitcoin::{
1242        Script,
1243        key::{Secp256k1, UntweakedPublicKey},
1244        taproot::TaprootBuilder,
1245    };
1246
1247    #[test]
1248    fn test_generate_address() {
1249        let secp = Secp256k1::new();
1250
1251        // 1. Create emulated script
1252        let emulated_script = Script::builder()
1253            .push_opcode(bitcoin::opcodes::OP_TRUE)
1254            .into_script();
1255
1256        // 2. Build the taproot tree and create emulated merkle root
1257        let taproot_builder = TaprootBuilder::new()
1258            .add_leaf(0, emulated_script.clone())
1259            .unwrap();
1260        let dummy_internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1261        let dummy_internal_key = UntweakedPublicKey::from(dummy_internal_secret.public_key(&secp));
1262        let taproot_spend_info = taproot_builder.finalize(&secp, dummy_internal_key).unwrap();
1263        let emulated_merkle_root = taproot_spend_info.merkle_root().unwrap();
1264
1265        // 3. Create backup merkle root
1266        let backup_merkle_root = emulated_merkle_root;
1267
1268        // 4. Generate an on-chain address
1269        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1270        let master_public_key: PublicKey = internal_secret.public_key(&secp);
1271        let onchain_address = generate_address(
1272            master_public_key,
1273            emulated_merkle_root,
1274            Some(backup_merkle_root),
1275            Network::Bitcoin,
1276        );
1277
1278        assert!(onchain_address.is_ok());
1279    }
1280
1281    #[test]
1282    fn test_public_private_key_derivation_consistency() {
1283        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1284        let parent_public = parent_secret.public_key(&Secp256k1::new());
1285        let merkle_root = [42u8; 32];
1286
1287        let child_secret = derive_child_secret_key(parent_secret, merkle_root).unwrap();
1288        let child_public_from_secret = child_secret.public_key(&Secp256k1::new());
1289        let child_public_direct = derive_child_public_key(parent_public, merkle_root).unwrap();
1290
1291        assert_eq!(child_public_from_secret, child_public_direct);
1292    }
1293
1294    #[test]
1295    fn test_curve_order_reduction() {
1296        let max_bytes = [0xFF; 32];
1297        let reduced = reduce_mod_order(&max_bytes);
1298        // Should not panic and should be valid scalar
1299        #[allow(clippy::useless_conversion)]
1300        let _ = Scalar::from(reduced);
1301    }
1302}