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