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