1#![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, ScriptBuf, TapNodeHash, TapSighashType, TapTweakHash, Transaction, TxIn,
48    TxOut, Witness, XOnlyPublicKey,
49    consensus::deserialize,
50    hashes::Hash,
51    key::Secp256k1,
52    secp256k1,
53    secp256k1::{Keypair, Message, PublicKey, Scalar, SecretKey, constants::CURVE_ORDER},
54    sighash::{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#[derive(Debug)]
66pub enum Error {
67    VerificationFailed(String),
69    Secp256k1(secp256k1::Error),
71    NotScriptPathSpend,
73    InvalidControlBlock,
75    InvalidAmount(bitcoin_units::amount::OutOfRangeError),
77    DeserializationFailed(bitcoin::consensus::encode::Error),
79    InvalidSighash,
81    InputIndexOutOfBounds,
83}
84
85pub trait Verifier {
88    fn verify(
101        &self,
102        script_pubkey: &[u8],
103        amount: Option<i64>,
104        tx_to: &[u8],
105        input_index: u32,
106        flags: Option<u32>,
107        spent_outputs: &[TxOut],
108    ) -> Result<(), Error>;
109}
110
111#[cfg(feature = "bitcoinkernel")]
113pub struct DefaultVerifier;
114
115#[cfg(feature = "bitcoinkernel")]
116impl Verifier for DefaultVerifier {
117    fn verify(
118        &self,
119        script_pubkey: &[u8],
120        amount: Option<i64>,
121        tx_to: &[u8],
122        input_index: u32,
123        flags: Option<u32>,
124        spent_outputs: &[TxOut],
125    ) -> Result<(), Error> {
126        let mut outputs = Vec::new();
127        for txout in spent_outputs {
128            let amount = txout.value.to_signed()?.to_sat();
129            let script = bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes())?;
130            outputs.push(bitcoinkernel::TxOut::new(&script, amount));
131        }
132
133        verify(
134            &bitcoinkernel::ScriptPubkey::try_from(script_pubkey)?,
135            amount,
136            &bitcoinkernel::Transaction::try_from(tx_to)?,
137            input_index,
138            flags,
139            &outputs,
140        )?;
141
142        Ok(())
143    }
144}
145
146pub fn verify_and_sign<V: Verifier>(
166    verifier: &V,
167    input_index: u32,
168    emulated_tx_to: &[u8],
169    actual_spent_outputs: &[TxOut],
170    aux_rand: &[u8; 32],
171    parent_key: SecretKey,
172    backup_merkle_root: Option<TapNodeHash>,
173) -> Result<Transaction, Error> {
174    let mut tx: Transaction = deserialize(emulated_tx_to)?;
176
177    if input_index as usize >= tx.input.len() {
179        return Err(Error::InputIndexOutOfBounds);
180    }
181
182    let amount = actual_spent_outputs[input_index as usize]
184        .value
185        .to_signed()?
186        .to_sat();
187
188    let input = tx.input[input_index as usize].clone();
190    let (Some(control_block), Some(tapleaf)) = (
191        input.witness.taproot_control_block(),
192        input.witness.taproot_leaf_script(),
193    ) else {
194        return Err(Error::NotScriptPathSpend);
195    };
196    let Ok(control_block) = ControlBlock::decode(control_block) else {
197        return Err(Error::NotScriptPathSpend);
198    };
199
200    let mut merkle_root = TapNodeHash::from_script(tapleaf.script, tapleaf.version);
202    for elem in &control_block.merkle_branch {
203        merkle_root = TapNodeHash::from_node_hashes(merkle_root, *elem);
204    }
205
206    let secp = Secp256k1::new();
208    let address = Address::p2tr(
209        &secp,
210        control_block.internal_key,
211        Some(merkle_root),
212        Network::Bitcoin,
213    );
214
215    verifier.verify(
217        address.script_pubkey().as_bytes(),
218        Some(amount),
219        emulated_tx_to,
220        input_index,
221        None,
222        actual_spent_outputs,
223    )?;
224
225    let child_key = derive_child_secret_key(parent_key, merkle_root.to_byte_array())?;
227    let (internal_key, parity) = child_key.public_key(&secp).x_only_public_key();
228    let child_key_for_tweak = if parity == secp256k1::Parity::Odd {
229        child_key.negate()
230    } else {
231        child_key
232    };
233
234    tx.input[input_index as usize] = TxIn {
236        previous_output: input.previous_output,
237        script_sig: ScriptBuf::new(),
238        sequence: input.sequence, witness: Witness::new(),
240    };
241
242    let mut sighash_cache = SighashCache::new(&tx);
244    let sighash_bytes = sighash_cache
245        .taproot_key_spend_signature_hash(
246            input_index as usize,
247            &Prevouts::All(actual_spent_outputs),
248            TapSighashType::Default,
249        )
250        .map_err(|_| Error::InvalidSighash)?;
251    let mut sighash = [0u8; 32];
252    sighash.copy_from_slice(sighash_bytes.as_byte_array());
253
254    let tweak = TapTweakHash::from_key_and_tweak(internal_key, backup_merkle_root);
256    let tweaked_secret_key = child_key_for_tweak.add_tweak(&tweak.to_scalar())?;
257    let tweaked_keypair = Keypair::from_secret_key(&secp, &tweaked_secret_key);
258
259    let message = Message::from_digest(sighash);
261    let signature = secp.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand);
262
263    let tap_signature = Signature {
265        signature,
266        sighash_type: TapSighashType::Default,
267    };
268
269    let mut witness = Witness::new();
271    witness.push(tap_signature.to_vec());
272    tx.input[input_index as usize].witness = witness;
273
274    Ok(tx)
275}
276
277pub fn generate_address(
289    parent_key: PublicKey,
290    emulated_merkle_root: TapNodeHash,
291    backup_merkle_root: Option<TapNodeHash>,
292    network: Network,
293) -> Result<Address, secp256k1::Error> {
294    let secp = Secp256k1::new();
295    let child_key = derive_child_public_key(parent_key, emulated_merkle_root.to_byte_array())?;
296    let internal_key = XOnlyPublicKey::from(child_key);
297    let address = Address::p2tr(&secp, internal_key, backup_merkle_root, network);
298
299    Ok(address)
300}
301
302fn derive_child_secret_key(
305    parent_key: SecretKey,
306    emulated_merkle_root: [u8; 32],
307) -> Result<SecretKey, secp256k1::Error> {
308    let secp = Secp256k1::new();
309
310    let parent_public = parent_key.public_key(&secp);
312
313    let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
315        .expect("PublicKey serialization should always be non-empty");
316    mac.update(&emulated_merkle_root);
317    let hmac_result = mac.finalize().into_bytes();
318
319    let mut key_material = [0u8; 32];
321    key_material.copy_from_slice(&hmac_result[..32]);
322    let scalar = reduce_mod_order(&key_material);
323
324    parent_key.add_tweak(&scalar)
326}
327
328fn derive_child_public_key(
331    parent_public: PublicKey,
332    emulated_merkle_root: [u8; 32],
333) -> Result<PublicKey, secp256k1::Error> {
334    let secp = Secp256k1::new();
335
336    let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
338        .expect("PublicKey serialization should always be non-empty");
339    mac.update(&emulated_merkle_root);
340    let hmac_result = mac.finalize().into_bytes();
341
342    let mut key_material = [0u8; 32];
344    key_material.copy_from_slice(&hmac_result[..32]);
345    let scalar = reduce_mod_order(&key_material);
346
347    parent_public.add_exp_tweak(&secp, &scalar)
349}
350
351fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
353    let mut attempt = *bytes;
356    loop {
357        match Scalar::from_be_bytes(attempt) {
358            Ok(scalar) => return scalar,
359            Err(_) => {
360                attempt = subtract_curve_order(&attempt);
363            }
364        }
365    }
366}
367
368fn subtract_curve_order(bytes: &[u8; 32]) -> [u8; 32] {
370    let value = BigUint::from_bytes_be(bytes);
371    let order = BigUint::from_bytes_be(&CURVE_ORDER);
372    let reduced = value % order;
373
374    let mut result = [0u8; 32];
375    let reduced_bytes = reduced.to_bytes_be();
376    let offset = 32 - reduced_bytes.len();
377    result[offset..].copy_from_slice(&reduced_bytes);
378    result
379}
380
381impl fmt::Display for Error {
382    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
383        match self {
384            Error::VerificationFailed(e) => {
385                write!(f, "Verification failed: {e}")
386            }
387            Error::Secp256k1(e) => {
388                write!(f, "Secp256k1 cryptographic operation failed: {e}")
389            }
390            Error::NotScriptPathSpend => {
391                write!(
392                    f,
393                    "Input is not a script path spend (missing taproot control block)"
394                )
395            }
396            Error::InvalidAmount(e) => {
397                write!(f, "Invalid amount: {e}")
398            }
399            Error::InvalidControlBlock => {
400                write!(f, "Input has invalid control block")
401            }
402            Error::DeserializationFailed(e) => {
403                write!(f, "Failed to deserialize: {e}")
404            }
405            Error::InvalidSighash => {
406                write!(f, "Unable to calculate sighash for input")
407            }
408            Error::InputIndexOutOfBounds => {
409                write!(f, "Input index out of bounds")
410            }
411        }
412    }
413}
414
415#[cfg(feature = "bitcoinkernel")]
416impl From<KernelError> for Error {
417    fn from(error: KernelError) -> Self {
418        Error::VerificationFailed(error.to_string())
419    }
420}
421
422impl From<secp256k1::Error> for Error {
423    fn from(error: secp256k1::Error) -> Self {
424        Error::Secp256k1(error)
425    }
426}
427
428impl From<bitcoin::consensus::encode::Error> for Error {
429    fn from(error: bitcoin::consensus::encode::Error) -> Self {
430        Error::DeserializationFailed(error)
431    }
432}
433
434impl From<bitcoin_units::amount::OutOfRangeError> for Error {
435    fn from(error: bitcoin_units::amount::OutOfRangeError) -> Self {
436        Error::InvalidAmount(error)
437    }
438}
439
440#[cfg(test)]
441#[cfg(feature = "bitcoinkernel")]
442mod kernel_tests {
443    use super::*;
444    use bitcoin::{
445        Address, Amount, Network, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut, Txid,
446        Witness,
447        consensus::encode::serialize,
448        hashes::Hash,
449        key::UntweakedPublicKey,
450        taproot::{LeafVersion, TaprootBuilder},
451    };
452
453    fn create_test_transaction_single_input() -> Transaction {
454        Transaction {
455            version: bitcoin::transaction::Version::TWO,
456            lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
457            input: vec![TxIn {
458                previous_output: OutPoint::null(),
459                script_sig: ScriptBuf::new(),
460                sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
461                witness: Witness::new(),
462            }],
463            output: vec![TxOut {
464                value: Amount::from_sat(100000),
465                script_pubkey: ScriptBuf::new_op_return([]),
466            }],
467        }
468    }
469
470    fn create_test_transaction_multi_input() -> Transaction {
471        Transaction {
472            version: bitcoin::transaction::Version::TWO,
473            lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
474            input: vec![
475                TxIn {
476                    previous_output: OutPoint::null(),
477                    script_sig: ScriptBuf::new(),
478                    sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
479                    witness: Witness::new(),
480                },
481                TxIn {
482                    previous_output: OutPoint::new(Txid::all_zeros(), 1),
483                    script_sig: ScriptBuf::new(),
484                    sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
485                    witness: Witness::new(),
486                },
487            ],
488            output: vec![TxOut {
489                value: Amount::from_sat(100000),
490                script_pubkey: ScriptBuf::new_op_return([]),
491            }],
492        }
493    }
494
495    #[test]
496    fn test_unable_to_deserialize_tx() {
497        let result = verify_and_sign(
498            &DefaultVerifier,
499            0,
500            &[],
501            &[],
502            &[1u8; 32],
503            SecretKey::from_slice(&[1u8; 32]).unwrap(),
504            None,
505        );
506
507        assert!(matches!(result, Err(Error::DeserializationFailed(_))));
508    }
509
510    #[test]
511    fn test_input_index_out_of_bounds() {
512        let txout = TxOut {
513            value: Amount::from_sat(100000),
514            script_pubkey: ScriptBuf::new_op_return([]),
515        };
516        let result = verify_and_sign(
517            &DefaultVerifier,
518            1,
519            &serialize(&create_test_transaction_single_input()),
520            std::slice::from_ref(&txout),
521            &[1u8; 32],
522            SecretKey::from_slice(&[1u8; 32]).unwrap(),
523            None,
524        );
525
526        assert!(matches!(result, Err(Error::InputIndexOutOfBounds)));
527    }
528
529    #[test]
530    fn test_verify_and_sign_single_input_single_leaf() {
531        let secp = Secp256k1::new();
532
533        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
535        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
536
537        let op_true_script = Script::builder()
539            .push_opcode(bitcoin::opcodes::OP_TRUE)
540            .into_script();
541
542        let taproot_builder = TaprootBuilder::new()
544            .add_leaf(0, op_true_script.clone())
545            .unwrap();
546        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
547
548        let control_block = taproot_spend_info
550            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
551            .unwrap();
552
553        let mut witness = Witness::new();
555        witness.push(op_true_script.as_bytes());
556        witness.push(control_block.serialize());
557
558        let mut emulated_tx = create_test_transaction_single_input();
560        emulated_tx.input[0].witness = witness;
561
562        let aux_rand = [1u8; 32];
564        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
565        let child_secret = derive_child_secret_key(
566            parent_secret,
567            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
568        )
569        .unwrap();
570
571        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
573        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
574        let actual_spent_outputs = [TxOut {
575            value: Amount::from_sat(100_000),
576            script_pubkey: actual_address.script_pubkey(),
577        }];
578
579        let actual_tx = verify_and_sign(
581            &DefaultVerifier,
582            0,
583            &serialize(&emulated_tx),
584            &actual_spent_outputs,
585            &aux_rand,
586            parent_secret,
587            None,
588        )
589        .unwrap();
590
591        let mut actual_outputs = Vec::new();
592        for txout in actual_spent_outputs {
593            let amount = txout.value.to_signed().unwrap().to_sat();
594            let script =
595                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
596            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
597        }
598
599        let verify_result = bitcoinkernel::verify(
601            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
602                .unwrap(),
603            Some(100_000),
604            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
605            0,
606            None,
607            &actual_outputs,
608        );
609
610        assert!(verify_result.is_ok());
611        assert_eq!(actual_tx.input[0].witness.len(), 1)
612    }
613
614    #[test]
615    fn test_verify_and_sign_single_input_multiple_leaves() {
616        let secp = Secp256k1::new();
617
618        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
620        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
621
622        let op_true_script = Script::builder()
624            .push_opcode(bitcoin::opcodes::OP_TRUE)
625            .into_script();
626        let op_false_script = Script::builder()
627            .push_opcode(bitcoin::opcodes::OP_FALSE)
628            .into_script();
629
630        let taproot_builder = TaprootBuilder::new()
632            .add_leaf(1, op_true_script.clone())
633            .unwrap()
634            .add_leaf(1, op_false_script.clone())
635            .unwrap();
636        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
637
638        let control_block = taproot_spend_info
640            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
641            .unwrap();
642
643        let mut witness = Witness::new();
645        witness.push(op_true_script.as_bytes());
646        witness.push(control_block.serialize());
647
648        let mut emulated_tx = create_test_transaction_single_input();
650        emulated_tx.input[0].witness = witness;
651
652        let aux_rand = [1u8; 32];
654        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
655        let child_secret = derive_child_secret_key(
656            parent_secret,
657            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
658        )
659        .unwrap();
660
661        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
663        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
664        let actual_spent_outputs = [TxOut {
665            value: Amount::from_sat(100_000),
666            script_pubkey: actual_address.script_pubkey(),
667        }];
668
669        let actual_tx = verify_and_sign(
671            &DefaultVerifier,
672            0,
673            &serialize(&emulated_tx),
674            &actual_spent_outputs,
675            &aux_rand,
676            parent_secret,
677            None,
678        )
679        .unwrap();
680
681        let mut actual_outputs = Vec::new();
682        for txout in actual_spent_outputs {
683            let amount = txout.value.to_signed().unwrap().to_sat();
684            let script =
685                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
686            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
687        }
688
689        let verify_result = bitcoinkernel::verify(
691            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
692                .unwrap(),
693            Some(100_000),
694            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
695            0,
696            None,
697            &actual_outputs,
698        );
699
700        assert!(verify_result.is_ok());
701        assert_eq!(actual_tx.input[0].witness.len(), 1)
702    }
703
704    #[test]
705    fn test_verify_and_sign_single_input_with_backup() {
706        let secp = Secp256k1::new();
707
708        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
710        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
711
712        let op_true_script = Script::builder()
714            .push_opcode(bitcoin::opcodes::OP_TRUE)
715            .into_script();
716
717        let taproot_builder = TaprootBuilder::new()
719            .add_leaf(0, op_true_script.clone())
720            .unwrap();
721        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
722
723        let control_block = taproot_spend_info
725            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
726            .unwrap();
727
728        let mut witness = Witness::new();
730        witness.push(op_true_script.as_bytes());
731        witness.push(control_block.serialize());
732
733        let mut emulated_tx = create_test_transaction_single_input();
735        emulated_tx.input[0].witness = witness;
736
737        let aux_rand = [1u8; 32];
739        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
740        let child_secret = derive_child_secret_key(
741            parent_secret,
742            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
743        )
744        .unwrap();
745
746        let actual_backup_merkle_root = taproot_spend_info.merkle_root();
748        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
749        let actual_address = Address::p2tr(
750            &secp,
751            actual_internal_key,
752            actual_backup_merkle_root,
753            Network::Bitcoin,
754        );
755        let actual_spent_outputs = [TxOut {
756            value: Amount::from_sat(100_000),
757            script_pubkey: actual_address.script_pubkey(),
758        }];
759
760        let actual_tx = verify_and_sign(
762            &DefaultVerifier,
763            0,
764            &serialize(&emulated_tx),
765            &actual_spent_outputs,
766            &aux_rand,
767            parent_secret,
768            actual_backup_merkle_root,
769        )
770        .unwrap();
771
772        let mut actual_outputs = Vec::new();
773        for txout in actual_spent_outputs {
774            let amount = txout.value.to_signed().unwrap().to_sat();
775            let script =
776                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
777            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
778        }
779
780        let verify_result = bitcoinkernel::verify(
782            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
783                .unwrap(),
784            Some(100_000),
785            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
786            0,
787            None,
788            &actual_outputs,
789        );
790
791        assert!(verify_result.is_ok());
792        assert_eq!(actual_tx.input[0].witness.len(), 1)
793    }
794
795    #[test]
796    fn test_verify_and_sign_multi_input_tx() {
797        let secp = Secp256k1::new();
798
799        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
801        let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
802
803        let op_true_script = Script::builder()
805            .push_opcode(bitcoin::opcodes::OP_TRUE)
806            .into_script();
807
808        let taproot_builder = TaprootBuilder::new()
810            .add_leaf(0, op_true_script.clone())
811            .unwrap();
812        let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
813
814        let control_block = taproot_spend_info
816            .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
817            .unwrap();
818
819        let mut witness = Witness::new();
821        witness.push(op_true_script.as_bytes());
822        witness.push(control_block.serialize());
823
824        let mut emulated_tx = create_test_transaction_multi_input();
826        emulated_tx.input[1].witness = witness;
827
828        let aux_rand = [1u8; 32];
830        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
831        let child_secret = derive_child_secret_key(
832            parent_secret,
833            taproot_spend_info.merkle_root().unwrap().to_byte_array(),
834        )
835        .unwrap();
836
837        let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
839        let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
840        let actual_spent_outputs = [
841            TxOut {
842                value: Amount::from_sat(200_000),
843                script_pubkey: actual_address.script_pubkey(),
844            },
845            TxOut {
846                value: Amount::from_sat(100_000),
847                script_pubkey: actual_address.script_pubkey(),
848            },
849        ];
850
851        let actual_tx = verify_and_sign(
853            &DefaultVerifier,
854            1,
855            &serialize(&emulated_tx),
856            &actual_spent_outputs,
857            &aux_rand,
858            parent_secret,
859            None,
860        )
861        .unwrap();
862
863        let mut actual_outputs = Vec::new();
864        for txout in actual_spent_outputs {
865            let amount = txout.value.to_signed().unwrap().to_sat();
866            let script =
867                bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
868            actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
869        }
870
871        let verify_result = bitcoinkernel::verify(
873            &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
874                .unwrap(),
875            Some(100_000),
876            &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
877            1,
878            None,
879            &actual_outputs,
880        );
881
882        assert!(verify_result.is_ok());
883        assert_eq!(actual_tx.input[1].witness.len(), 1)
884    }
885}
886
887#[cfg(test)]
888mod non_kernel_tests {
889    use super::*;
890    use bitcoin::{
891        Script,
892        key::{Secp256k1, UntweakedPublicKey},
893        taproot::TaprootBuilder,
894    };
895
896    #[test]
897    fn test_generate_address() {
898        let secp = Secp256k1::new();
899
900        let emulated_script = Script::builder()
902            .push_opcode(bitcoin::opcodes::OP_TRUE)
903            .into_script();
904
905        let taproot_builder = TaprootBuilder::new()
907            .add_leaf(0, emulated_script.clone())
908            .unwrap();
909        let dummy_internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
910        let dummy_internal_key = UntweakedPublicKey::from(dummy_internal_secret.public_key(&secp));
911        let taproot_spend_info = taproot_builder.finalize(&secp, dummy_internal_key).unwrap();
912        let emulated_merkle_root = taproot_spend_info.merkle_root().unwrap();
913
914        let backup_merkle_root = emulated_merkle_root;
916
917        let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
919        let master_public_key: PublicKey = internal_secret.public_key(&secp);
920        let onchain_address = generate_address(
921            master_public_key,
922            emulated_merkle_root,
923            Some(backup_merkle_root),
924            Network::Bitcoin,
925        );
926
927        assert!(onchain_address.is_ok());
928    }
929
930    #[test]
931    fn test_public_private_key_derivation_consistency() {
932        let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
933        let parent_public = parent_secret.public_key(&Secp256k1::new());
934        let merkle_root = [42u8; 32];
935
936        let child_secret = derive_child_secret_key(parent_secret, merkle_root).unwrap();
937        let child_public_from_secret = child_secret.public_key(&Secp256k1::new());
938        let child_public_direct = derive_child_public_key(parent_public, merkle_root).unwrap();
939
940        assert_eq!(child_public_from_secret, child_public_direct);
941    }
942
943    #[test]
944    fn test_curve_order_reduction() {
945        let max_bytes = [0xFF; 32];
946        let reduced = reduce_mod_order(&max_bytes);
947        #[allow(clippy::useless_conversion)]
949        let _ = Scalar::from(reduced);
950    }
951}