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 NotTaprootSpend,
73 NotScriptPathSpend,
75 InvalidControlBlock,
77 InvalidAmount(bitcoin_units::amount::OutOfRangeError),
79 DeserializationFailed(bitcoin::consensus::encode::Error),
81 InvalidSighash,
83 SpentOutputsMismatch,
85 InputIndexOutOfBounds,
87}
88
89pub trait Verifier {
92 fn verify(
105 &self,
106 script_pubkey: &[u8],
107 amount: Option<i64>,
108 tx_to: &[u8],
109 input_index: u32,
110 flags: Option<u32>,
111 spent_outputs: &[TxOut],
112 ) -> Result<(), Error>;
113}
114
115#[cfg(feature = "bitcoinkernel")]
117pub struct DefaultVerifier;
118
119#[cfg(feature = "bitcoinkernel")]
120impl Verifier for DefaultVerifier {
121 fn verify(
122 &self,
123 script_pubkey: &[u8],
124 amount: Option<i64>,
125 tx_to: &[u8],
126 input_index: u32,
127 flags: Option<u32>,
128 spent_outputs: &[TxOut],
129 ) -> Result<(), Error> {
130 let mut outputs = Vec::new();
131 for txout in spent_outputs {
132 let amount = txout.value.to_signed()?.to_sat();
133 let script = bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes())?;
134 outputs.push(bitcoinkernel::TxOut::new(&script, amount));
135 }
136
137 verify(
138 &bitcoinkernel::ScriptPubkey::try_from(script_pubkey)?,
139 amount,
140 &bitcoinkernel::Transaction::try_from(tx_to)?,
141 input_index,
142 flags,
143 &outputs,
144 )?;
145
146 Ok(())
147 }
148}
149
150#[allow(clippy::too_many_arguments)]
173pub fn verify_and_sign<V: Verifier>(
174 verifier: &V,
175 emulated_script_pubkey: &[u8],
176 amount: i64,
177 emulated_tx_to: &[u8],
178 input_index: u32,
179 emulated_spent_outputs: &[TxOut],
180 actual_spent_outputs: &[TxOut],
181 aux_rand: &[u8; 32],
182 parent_key: SecretKey,
183 backup_merkle_root: Option<TapNodeHash>,
184) -> Result<Transaction, Error> {
185 if !ScriptBuf::from(emulated_script_pubkey.to_vec()).is_p2tr() {
187 return Err(Error::NotTaprootSpend);
188 }
189
190 let mut tx: Transaction = deserialize(emulated_tx_to)?;
192
193 if tx.input.len() != emulated_spent_outputs.len()
195 || tx.input.len() != actual_spent_outputs.len()
196 || emulated_spent_outputs
197 .iter()
198 .zip(actual_spent_outputs.iter())
199 .any(|(e, a)| e.value != a.value)
200 {
201 return Err(Error::SpentOutputsMismatch);
202 }
203
204 if input_index as usize >= tx.input.len() {
206 return Err(Error::InputIndexOutOfBounds);
207 }
208
209 let input = tx.input[input_index as usize].clone();
211 let (Some(control_block), Some(tapleaf)) = (
212 input.witness.taproot_control_block(),
213 input.witness.taproot_leaf_script(),
214 ) else {
215 return Err(Error::NotScriptPathSpend);
216 };
217 let Ok(control_block) = ControlBlock::decode(control_block) else {
218 return Err(Error::NotScriptPathSpend);
219 };
220
221 verifier.verify(
223 emulated_script_pubkey,
224 Some(amount),
225 emulated_tx_to,
226 input_index,
227 None,
228 emulated_spent_outputs,
229 )?;
230
231 let mut curr_hash = TapNodeHash::from_script(tapleaf.script, tapleaf.version);
233 for elem in &control_block.merkle_branch {
234 curr_hash = TapNodeHash::from_node_hashes(curr_hash, *elem);
235 }
236 let merkle_root = curr_hash.to_byte_array();
237
238 let secp = Secp256k1::new();
240 let child_key = derive_child_secret_key(parent_key, merkle_root)?;
241 let (internal_key, parity) = child_key.public_key(&secp).x_only_public_key();
242 let child_key_for_tweak = if parity == secp256k1::Parity::Odd {
243 child_key.negate()
244 } else {
245 child_key
246 };
247
248 tx.input[input_index as usize] = TxIn {
250 previous_output: input.previous_output,
251 script_sig: ScriptBuf::new(),
252 sequence: input.sequence, witness: Witness::new(),
254 };
255
256 let mut sighash_cache = SighashCache::new(&tx);
258 let sighash_bytes = sighash_cache
259 .taproot_key_spend_signature_hash(
260 input_index as usize,
261 &Prevouts::All(actual_spent_outputs),
262 TapSighashType::Default,
263 )
264 .map_err(|_| Error::InvalidSighash)?;
265 let mut sighash = [0u8; 32];
266 sighash.copy_from_slice(sighash_bytes.as_byte_array());
267
268 let tweak = TapTweakHash::from_key_and_tweak(internal_key, backup_merkle_root);
270 let tweaked_secret_key = child_key_for_tweak.add_tweak(&tweak.to_scalar())?;
271 let tweaked_keypair = Keypair::from_secret_key(&secp, &tweaked_secret_key);
272
273 let message = Message::from_digest(sighash);
275 let signature = secp.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand);
276
277 let tap_signature = Signature {
279 signature,
280 sighash_type: TapSighashType::Default,
281 };
282
283 let mut witness = Witness::new();
285 witness.push(tap_signature.to_vec());
286 tx.input[input_index as usize].witness = witness;
287
288 Ok(tx)
289}
290
291pub fn generate_address(
303 parent_key: PublicKey,
304 emulated_merkle_root: TapNodeHash,
305 backup_merkle_root: Option<TapNodeHash>,
306 network: Network,
307) -> Result<Address, secp256k1::Error> {
308 let secp = Secp256k1::new();
309 let child_key = derive_child_public_key(parent_key, emulated_merkle_root.to_byte_array())?;
310 let internal_key = XOnlyPublicKey::from(child_key);
311 let address = Address::p2tr(&secp, internal_key, backup_merkle_root, network);
312
313 Ok(address)
314}
315
316fn derive_child_secret_key(
319 parent_key: SecretKey,
320 emulated_merkle_root: [u8; 32],
321) -> Result<SecretKey, secp256k1::Error> {
322 let secp = Secp256k1::new();
323
324 let parent_public = parent_key.public_key(&secp);
326
327 let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
329 .expect("PublicKey serialization should always be non-empty");
330 mac.update(&emulated_merkle_root);
331 let hmac_result = mac.finalize().into_bytes();
332
333 let mut key_material = [0u8; 32];
335 key_material.copy_from_slice(&hmac_result[..32]);
336 let scalar = reduce_mod_order(&key_material);
337
338 parent_key.add_tweak(&scalar)
340}
341
342fn derive_child_public_key(
345 parent_public: PublicKey,
346 emulated_merkle_root: [u8; 32],
347) -> Result<PublicKey, secp256k1::Error> {
348 let secp = Secp256k1::new();
349
350 let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
352 .expect("PublicKey serialization should always be non-empty");
353 mac.update(&emulated_merkle_root);
354 let hmac_result = mac.finalize().into_bytes();
355
356 let mut key_material = [0u8; 32];
358 key_material.copy_from_slice(&hmac_result[..32]);
359 let scalar = reduce_mod_order(&key_material);
360
361 parent_public.add_exp_tweak(&secp, &scalar)
363}
364
365fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
367 let mut attempt = *bytes;
370 loop {
371 match Scalar::from_be_bytes(attempt) {
372 Ok(scalar) => return scalar,
373 Err(_) => {
374 attempt = subtract_curve_order(&attempt);
377 }
378 }
379 }
380}
381
382fn subtract_curve_order(bytes: &[u8; 32]) -> [u8; 32] {
384 let value = BigUint::from_bytes_be(bytes);
385 let order = BigUint::from_bytes_be(&CURVE_ORDER);
386 let reduced = value % order;
387
388 let mut result = [0u8; 32];
389 let reduced_bytes = reduced.to_bytes_be();
390 let offset = 32 - reduced_bytes.len();
391 result[offset..].copy_from_slice(&reduced_bytes);
392 result
393}
394
395impl fmt::Display for Error {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 match self {
398 Error::VerificationFailed(e) => {
399 write!(f, "Verification failed: {e}")
400 }
401 Error::Secp256k1(e) => {
402 write!(f, "Secp256k1 cryptographic operation failed: {e}")
403 }
404 Error::NotTaprootSpend => {
405 write!(f, "Input is not a taproot spend")
406 }
407 Error::NotScriptPathSpend => {
408 write!(
409 f,
410 "Input is not a script path spend (missing taproot control block)"
411 )
412 }
413 Error::InvalidAmount(e) => {
414 write!(f, "Invalid amount: {e}")
415 }
416 Error::InvalidControlBlock => {
417 write!(f, "Input has invalid control block")
418 }
419 Error::DeserializationFailed(e) => {
420 write!(f, "Failed to deserialize: {e}")
421 }
422 Error::InvalidSighash => {
423 write!(f, "Unable to calculate sighash for input")
424 }
425 Error::SpentOutputsMismatch => {
426 write!(
427 f,
428 "Mismatch between number of emulated and actual spent outputs"
429 )
430 }
431 Error::InputIndexOutOfBounds => {
432 write!(f, "Input index out of bounds")
433 }
434 }
435 }
436}
437
438#[cfg(feature = "bitcoinkernel")]
439impl From<KernelError> for Error {
440 fn from(error: KernelError) -> Self {
441 Error::VerificationFailed(error.to_string())
442 }
443}
444
445impl From<secp256k1::Error> for Error {
446 fn from(error: secp256k1::Error) -> Self {
447 Error::Secp256k1(error)
448 }
449}
450
451impl From<bitcoin::consensus::encode::Error> for Error {
452 fn from(error: bitcoin::consensus::encode::Error) -> Self {
453 Error::DeserializationFailed(error)
454 }
455}
456
457impl From<bitcoin_units::amount::OutOfRangeError> for Error {
458 fn from(error: bitcoin_units::amount::OutOfRangeError) -> Self {
459 Error::InvalidAmount(error)
460 }
461}
462
463#[cfg(test)]
464#[cfg(feature = "bitcoinkernel")]
465mod kernel_tests {
466 use super::*;
467 use bitcoin::{
468 Address, Amount, Network, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut, Txid,
469 Witness,
470 consensus::encode::serialize,
471 hashes::Hash,
472 key::UntweakedPublicKey,
473 taproot::{LeafVersion, TaprootBuilder},
474 };
475
476 fn create_test_transaction_single_input() -> Transaction {
477 Transaction {
478 version: bitcoin::transaction::Version::TWO,
479 lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
480 input: vec![TxIn {
481 previous_output: OutPoint::null(),
482 script_sig: ScriptBuf::new(),
483 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
484 witness: Witness::new(),
485 }],
486 output: vec![TxOut {
487 value: Amount::from_sat(100000),
488 script_pubkey: ScriptBuf::new_op_return([]),
489 }],
490 }
491 }
492
493 fn create_test_transaction_multi_input() -> Transaction {
494 Transaction {
495 version: bitcoin::transaction::Version::TWO,
496 lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
497 input: vec![
498 TxIn {
499 previous_output: OutPoint::null(),
500 script_sig: ScriptBuf::new(),
501 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
502 witness: Witness::new(),
503 },
504 TxIn {
505 previous_output: OutPoint::new(Txid::all_zeros(), 1),
506 script_sig: ScriptBuf::new(),
507 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
508 witness: Witness::new(),
509 },
510 ],
511 output: vec![TxOut {
512 value: Amount::from_sat(100000),
513 script_pubkey: ScriptBuf::new_op_return([]),
514 }],
515 }
516 }
517
518 #[test]
519 fn test_not_taproot_input() {
520 let result = verify_and_sign(
521 &DefaultVerifier,
522 ScriptBuf::new_op_return([]).as_bytes(),
523 0,
524 &[],
525 0,
526 &[],
527 &[],
528 &[1u8; 32],
529 SecretKey::from_slice(&[1u8; 32]).unwrap(),
530 None,
531 );
532
533 assert!(matches!(result, Err(Error::NotTaprootSpend)));
534 }
535
536 #[test]
537 fn test_unable_to_deserialize_tx() {
538 let secp = Secp256k1::new();
539 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
540 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
541 let address = Address::p2tr(&secp, internal_key, None, Network::Bitcoin);
542
543 let result = verify_and_sign(
544 &DefaultVerifier,
545 address.script_pubkey().as_bytes(),
546 0,
547 &[],
548 0,
549 &[],
550 &[],
551 &[1u8; 32],
552 SecretKey::from_slice(&[1u8; 32]).unwrap(),
553 None,
554 );
555
556 assert!(matches!(result, Err(Error::DeserializationFailed(_))));
557 }
558
559 #[test]
560 fn test_spent_output_mismatch() {
561 let secp = Secp256k1::new();
562 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
563 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
564 let address = Address::p2tr(&secp, internal_key, None, Network::Bitcoin);
565
566 let result = verify_and_sign(
567 &DefaultVerifier,
568 address.script_pubkey().as_bytes(),
569 0,
570 &serialize(&create_test_transaction_single_input()),
571 0,
572 &[],
573 &[],
574 &[1u8; 32],
575 SecretKey::from_slice(&[1u8; 32]).unwrap(),
576 None,
577 );
578
579 assert!(matches!(result, Err(Error::SpentOutputsMismatch)));
580
581 let txout = TxOut {
582 value: Amount::from_sat(100000),
583 script_pubkey: ScriptBuf::new_op_return([]),
584 };
585 let result = verify_and_sign(
586 &DefaultVerifier,
587 address.script_pubkey().as_bytes(),
588 0,
589 &serialize(&create_test_transaction_single_input()),
590 0,
591 std::slice::from_ref(&txout),
592 &[],
593 &[1u8; 32],
594 SecretKey::from_slice(&[1u8; 32]).unwrap(),
595 None,
596 );
597
598 assert!(matches!(result, Err(Error::SpentOutputsMismatch)));
599
600 let result = verify_and_sign(
601 &DefaultVerifier,
602 address.script_pubkey().as_bytes(),
603 0,
604 &serialize(&create_test_transaction_single_input()),
605 0,
606 &[txout.clone(), txout.clone()],
607 &[txout.clone(), txout.clone()],
608 &[1u8; 32],
609 SecretKey::from_slice(&[1u8; 32]).unwrap(),
610 None,
611 );
612
613 assert!(matches!(result, Err(Error::SpentOutputsMismatch)));
614
615 let result = verify_and_sign(
616 &DefaultVerifier,
617 address.script_pubkey().as_bytes(),
618 0,
619 &serialize(&create_test_transaction_single_input()),
620 0,
621 std::slice::from_ref(&txout),
622 &[TxOut {
623 value: Amount::from_sat(200000),
624 script_pubkey: ScriptBuf::new_op_return([]),
625 }],
626 &[1u8; 32],
627 SecretKey::from_slice(&[1u8; 32]).unwrap(),
628 None,
629 );
630
631 assert!(matches!(result, Err(Error::SpentOutputsMismatch)));
632 }
633
634 #[test]
635 fn test_input_index_out_of_bounds() {
636 let secp = Secp256k1::new();
637 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
638 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
639 let address = Address::p2tr(&secp, internal_key, None, Network::Bitcoin);
640
641 let txout = TxOut {
642 value: Amount::from_sat(100000),
643 script_pubkey: ScriptBuf::new_op_return([]),
644 };
645 let result = verify_and_sign(
646 &DefaultVerifier,
647 address.script_pubkey().as_bytes(),
648 0,
649 &serialize(&create_test_transaction_single_input()),
650 1,
651 std::slice::from_ref(&txout),
652 std::slice::from_ref(&txout),
653 &[1u8; 32],
654 SecretKey::from_slice(&[1u8; 32]).unwrap(),
655 None,
656 );
657
658 assert!(matches!(result, Err(Error::InputIndexOutOfBounds)));
659 }
660
661 #[test]
662 fn test_verify_and_sign_single_input_single_leaf() {
663 let secp = Secp256k1::new();
664
665 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
667 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
668
669 let op_true_script = Script::builder()
671 .push_opcode(bitcoin::opcodes::OP_TRUE)
672 .into_script();
673
674 let taproot_builder = TaprootBuilder::new()
676 .add_leaf(0, op_true_script.clone())
677 .unwrap();
678 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
679
680 let emulated_address = Address::p2tr(
682 &secp,
683 internal_key,
684 taproot_spend_info.merkle_root(),
685 Network::Bitcoin,
686 );
687 let emulated_spent_outputs = [TxOut {
688 value: Amount::from_sat(100_000),
689 script_pubkey: emulated_address.script_pubkey(),
690 }];
691
692 let control_block = taproot_spend_info
694 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
695 .unwrap();
696
697 let mut witness = Witness::new();
699 witness.push(op_true_script.as_bytes());
700 witness.push(control_block.serialize());
701
702 let mut emulated_tx = create_test_transaction_single_input();
704 emulated_tx.input[0].witness = witness;
705
706 let aux_rand = [1u8; 32];
708 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
709 let child_secret = derive_child_secret_key(
710 parent_secret,
711 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
712 )
713 .unwrap();
714
715 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
717 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
718 let mut actual_spent_outputs = emulated_spent_outputs.clone();
719 actual_spent_outputs[0].script_pubkey = actual_address.script_pubkey();
720
721 let actual_tx = verify_and_sign(
723 &DefaultVerifier,
724 emulated_address.script_pubkey().as_bytes(),
725 100_000,
726 &serialize(&emulated_tx),
727 0,
728 &emulated_spent_outputs,
729 &actual_spent_outputs,
730 &aux_rand,
731 parent_secret,
732 None,
733 )
734 .unwrap();
735
736 let mut actual_outputs = Vec::new();
737 for txout in actual_spent_outputs {
738 let amount = txout.value.to_signed().unwrap().to_sat();
739 let script =
740 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
741 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
742 }
743
744 let verify_result = bitcoinkernel::verify(
746 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
747 .unwrap(),
748 Some(100_000),
749 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
750 0,
751 None,
752 &actual_outputs,
753 );
754
755 assert!(verify_result.is_ok());
756 assert_eq!(actual_tx.input[0].witness.len(), 1)
757 }
758
759 #[test]
760 fn test_verify_and_sign_single_input_multiple_leaves() {
761 let secp = Secp256k1::new();
762
763 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
765 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
766
767 let op_true_script = Script::builder()
769 .push_opcode(bitcoin::opcodes::OP_TRUE)
770 .into_script();
771 let op_false_script = Script::builder()
772 .push_opcode(bitcoin::opcodes::OP_FALSE)
773 .into_script();
774
775 let taproot_builder = TaprootBuilder::new()
777 .add_leaf(1, op_true_script.clone())
778 .unwrap()
779 .add_leaf(1, op_false_script.clone())
780 .unwrap();
781 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
782
783 let emulated_address = Address::p2tr(
785 &secp,
786 internal_key,
787 taproot_spend_info.merkle_root(),
788 Network::Bitcoin,
789 );
790 let emulated_spent_outputs = [TxOut {
791 value: Amount::from_sat(100_000),
792 script_pubkey: emulated_address.script_pubkey(),
793 }];
794
795 let control_block = taproot_spend_info
797 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
798 .unwrap();
799
800 let mut witness = Witness::new();
802 witness.push(op_true_script.as_bytes());
803 witness.push(control_block.serialize());
804
805 let mut emulated_tx = create_test_transaction_single_input();
807 emulated_tx.input[0].witness = witness;
808
809 let aux_rand = [1u8; 32];
811 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
812 let child_secret = derive_child_secret_key(
813 parent_secret,
814 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
815 )
816 .unwrap();
817
818 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
820 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
821 let mut actual_spent_outputs = emulated_spent_outputs.clone();
822 actual_spent_outputs[0].script_pubkey = actual_address.script_pubkey();
823
824 let actual_tx = verify_and_sign(
826 &DefaultVerifier,
827 emulated_address.script_pubkey().as_bytes(),
828 100_000,
829 &serialize(&emulated_tx),
830 0,
831 &emulated_spent_outputs,
832 &actual_spent_outputs,
833 &aux_rand,
834 parent_secret,
835 None,
836 )
837 .unwrap();
838
839 let mut actual_outputs = Vec::new();
840 for txout in actual_spent_outputs {
841 let amount = txout.value.to_signed().unwrap().to_sat();
842 let script =
843 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
844 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
845 }
846
847 let verify_result = bitcoinkernel::verify(
849 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
850 .unwrap(),
851 Some(100_000),
852 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
853 0,
854 None,
855 &actual_outputs,
856 );
857
858 assert!(verify_result.is_ok());
859 assert_eq!(actual_tx.input[0].witness.len(), 1)
860 }
861
862 #[test]
863 fn test_verify_and_sign_single_input_with_backup() {
864 let secp = Secp256k1::new();
865
866 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
868 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
869
870 let op_true_script = Script::builder()
872 .push_opcode(bitcoin::opcodes::OP_TRUE)
873 .into_script();
874
875 let taproot_builder = TaprootBuilder::new()
877 .add_leaf(0, op_true_script.clone())
878 .unwrap();
879 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
880
881 let emulated_address = Address::p2tr(
883 &secp,
884 internal_key,
885 taproot_spend_info.merkle_root(),
886 Network::Bitcoin,
887 );
888 let emulated_spent_outputs = [TxOut {
889 value: Amount::from_sat(100_000),
890 script_pubkey: emulated_address.script_pubkey(),
891 }];
892
893 let control_block = taproot_spend_info
895 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
896 .unwrap();
897
898 let mut witness = Witness::new();
900 witness.push(op_true_script.as_bytes());
901 witness.push(control_block.serialize());
902
903 let mut emulated_tx = create_test_transaction_single_input();
905 emulated_tx.input[0].witness = witness;
906
907 let aux_rand = [1u8; 32];
909 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
910 let child_secret = derive_child_secret_key(
911 parent_secret,
912 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
913 )
914 .unwrap();
915
916 let actual_backup_merkle_root = taproot_spend_info.merkle_root();
918 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
919 let actual_address = Address::p2tr(
920 &secp,
921 actual_internal_key,
922 actual_backup_merkle_root,
923 Network::Bitcoin,
924 );
925 let mut actual_spent_outputs = emulated_spent_outputs.clone();
926 actual_spent_outputs[0].script_pubkey = actual_address.script_pubkey();
927
928 let actual_tx = verify_and_sign(
930 &DefaultVerifier,
931 emulated_address.script_pubkey().as_bytes(),
932 100_000,
933 &serialize(&emulated_tx),
934 0,
935 &emulated_spent_outputs,
936 &actual_spent_outputs,
937 &aux_rand,
938 parent_secret,
939 actual_backup_merkle_root,
940 )
941 .unwrap();
942
943 let mut actual_outputs = Vec::new();
944 for txout in actual_spent_outputs {
945 let amount = txout.value.to_signed().unwrap().to_sat();
946 let script =
947 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
948 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
949 }
950
951 let verify_result = bitcoinkernel::verify(
953 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
954 .unwrap(),
955 Some(100_000),
956 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
957 0,
958 None,
959 &actual_outputs,
960 );
961
962 assert!(verify_result.is_ok());
963 assert_eq!(actual_tx.input[0].witness.len(), 1)
964 }
965
966 #[test]
967 fn test_verify_and_sign_multi_input_tx() {
968 let secp = Secp256k1::new();
969
970 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
972 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
973
974 let op_true_script = Script::builder()
976 .push_opcode(bitcoin::opcodes::OP_TRUE)
977 .into_script();
978
979 let taproot_builder = TaprootBuilder::new()
981 .add_leaf(0, op_true_script.clone())
982 .unwrap();
983 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
984
985 let emulated_address = Address::p2tr(
987 &secp,
988 internal_key,
989 taproot_spend_info.merkle_root(),
990 Network::Bitcoin,
991 );
992 let emulated_spent_outputs = [
993 TxOut {
994 value: Amount::from_sat(200_000),
995 script_pubkey: emulated_address.script_pubkey(),
996 },
997 TxOut {
998 value: Amount::from_sat(100_000),
999 script_pubkey: emulated_address.script_pubkey(),
1000 },
1001 ];
1002
1003 let control_block = taproot_spend_info
1005 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1006 .unwrap();
1007
1008 let mut witness = Witness::new();
1010 witness.push(op_true_script.as_bytes());
1011 witness.push(control_block.serialize());
1012
1013 let mut emulated_tx = create_test_transaction_multi_input();
1015 emulated_tx.input[1].witness = witness;
1016
1017 let aux_rand = [1u8; 32];
1019 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1020 let child_secret = derive_child_secret_key(
1021 parent_secret,
1022 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1023 )
1024 .unwrap();
1025
1026 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1028 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1029 let mut actual_spent_outputs = emulated_spent_outputs.clone();
1030 actual_spent_outputs[1].script_pubkey = actual_address.script_pubkey();
1031
1032 let actual_tx = verify_and_sign(
1034 &DefaultVerifier,
1035 emulated_address.script_pubkey().as_bytes(),
1036 100_000,
1037 &serialize(&emulated_tx),
1038 1,
1039 &emulated_spent_outputs,
1040 &actual_spent_outputs,
1041 &aux_rand,
1042 parent_secret,
1043 None,
1044 )
1045 .unwrap();
1046
1047 let mut actual_outputs = Vec::new();
1048 for txout in actual_spent_outputs {
1049 let amount = txout.value.to_signed().unwrap().to_sat();
1050 let script =
1051 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
1052 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
1053 }
1054
1055 let verify_result = bitcoinkernel::verify(
1057 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
1058 .unwrap(),
1059 Some(100_000),
1060 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
1061 1,
1062 None,
1063 &actual_outputs,
1064 );
1065
1066 assert!(verify_result.is_ok());
1067 assert_eq!(actual_tx.input[1].witness.len(), 1)
1068 }
1069}
1070
1071#[cfg(test)]
1072mod non_kernel_tests {
1073 use super::*;
1074 use bitcoin::{
1075 Script,
1076 key::{Secp256k1, UntweakedPublicKey},
1077 taproot::TaprootBuilder,
1078 };
1079
1080 #[test]
1081 fn test_generate_address() {
1082 let secp = Secp256k1::new();
1083
1084 let emulated_script = Script::builder()
1086 .push_opcode(bitcoin::opcodes::OP_TRUE)
1087 .into_script();
1088
1089 let taproot_builder = TaprootBuilder::new()
1091 .add_leaf(0, emulated_script.clone())
1092 .unwrap();
1093 let dummy_internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1094 let dummy_internal_key = UntweakedPublicKey::from(dummy_internal_secret.public_key(&secp));
1095 let taproot_spend_info = taproot_builder.finalize(&secp, dummy_internal_key).unwrap();
1096 let emulated_merkle_root = taproot_spend_info.merkle_root().unwrap();
1097
1098 let backup_merkle_root = emulated_merkle_root;
1100
1101 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1103 let master_public_key: PublicKey = internal_secret.public_key(&secp);
1104 let onchain_address = generate_address(
1105 master_public_key,
1106 emulated_merkle_root,
1107 Some(backup_merkle_root),
1108 Network::Bitcoin,
1109 );
1110
1111 assert!(onchain_address.is_ok());
1112 }
1113
1114 #[test]
1115 fn test_public_private_key_derivation_consistency() {
1116 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1117 let parent_public = parent_secret.public_key(&Secp256k1::new());
1118 let merkle_root = [42u8; 32];
1119
1120 let child_secret = derive_child_secret_key(parent_secret, merkle_root).unwrap();
1121 let child_public_from_secret = child_secret.public_key(&Secp256k1::new());
1122 let child_public_direct = derive_child_public_key(parent_public, merkle_root).unwrap();
1123
1124 assert_eq!(child_public_from_secret, child_public_direct);
1125 }
1126
1127 #[test]
1128 fn test_curve_order_reduction() {
1129 let max_bytes = [0xFF; 32];
1130 let reduced = reduce_mod_order(&max_bytes);
1131 #[allow(clippy::useless_conversion)]
1133 let _ = Scalar::from(reduced);
1134 }
1135}