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, TapNodeHash, TapSighashType, TapTweakHash, Transaction, TxOut, Weight,
48 Witness, XOnlyPublicKey,
49 consensus::deserialize,
50 hashes::Hash,
51 key::Secp256k1,
52 secp256k1,
53 secp256k1::{Keypair, Message, PublicKey, Scalar, SecretKey, constants::CURVE_ORDER},
54 sighash::{Annex, Prevouts, SighashCache},
55 taproot::{ControlBlock, Signature},
56};
57#[cfg(feature = "bitcoinkernel")]
58use bitcoinkernel::{KernelError, verify};
59use hmac::{Hmac, Mac};
60use num_bigint::BigUint;
61use sha2::Sha512;
62use std::fmt;
63
64pub const TAPROOT_ANNEX_DATA_CARRYING_TAG: u8 = 0;
66
67#[derive(Debug)]
69pub enum Error {
70 VerificationFailed(String),
72 Secp256k1(secp256k1::Error),
74 NotScriptPathSpend,
76 InvalidControlBlock,
78 InvalidAmount(bitcoin_units::amount::OutOfRangeError),
80 DeserializationFailed(bitcoin::consensus::encode::Error),
82 InvalidSighash,
84 InputIndexOutOfBounds,
86 UnexpectedInput,
88 ExceedsMaxWeight,
90}
91
92pub trait Verifier {
95 fn verify(
108 &self,
109 script_pubkey: &[u8],
110 amount: Option<i64>,
111 tx_to: &[u8],
112 input_index: u32,
113 spent_outputs: &[TxOut],
114 tx_weight: Weight,
115 ) -> Result<(), Error>;
116}
117
118#[cfg(feature = "bitcoinkernel")]
120pub struct DefaultVerifier;
121
122#[cfg(feature = "bitcoinkernel")]
123impl Verifier for DefaultVerifier {
124 fn verify(
125 &self,
126 script_pubkey: &[u8],
127 amount: Option<i64>,
128 tx_to: &[u8],
129 input_index: u32,
130 spent_outputs: &[TxOut],
131 tx_weight: Weight,
132 ) -> Result<(), Error> {
133 if tx_weight > Weight::MAX_BLOCK {
134 return Err(Error::ExceedsMaxWeight);
135 }
136
137 let mut outputs = Vec::new();
138 for txout in spent_outputs {
139 let amount = txout.value.to_signed()?.to_sat();
140 let script = bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes())?;
141 outputs.push(bitcoinkernel::TxOut::new(&script, amount));
142 }
143
144 verify(
145 &bitcoinkernel::ScriptPubkey::try_from(script_pubkey)?,
146 amount,
147 &bitcoinkernel::Transaction::try_from(tx_to)?,
148 input_index,
149 None,
150 &outputs,
151 )?;
152
153 Ok(())
154 }
155}
156
157pub fn verify_and_sign<V: Verifier>(
181 verifier: &V,
182 input_index: u32,
183 emulated_tx_to: &[u8],
184 actual_spent_outputs: &[TxOut],
185 aux_rand: &[u8; 32],
186 parent_key: SecretKey,
187 backup_merkle_root: Option<TapNodeHash>,
188) -> Result<Transaction, Error> {
189 let mut tx: Transaction = deserialize(emulated_tx_to)?;
191
192 if input_index as usize >= tx.input.len() {
194 return Err(Error::InputIndexOutOfBounds);
195 }
196
197 let amount = actual_spent_outputs[input_index as usize]
199 .value
200 .to_signed()?
201 .to_sat();
202
203 let input = tx.input[input_index as usize].clone();
205 let (Some(control_block), Some(tapleaf)) = (
206 input.witness.taproot_control_block(),
207 input.witness.taproot_leaf_script(),
208 ) else {
209 return Err(Error::NotScriptPathSpend);
210 };
211 let Ok(control_block) = ControlBlock::decode(control_block) else {
212 return Err(Error::NotScriptPathSpend);
213 };
214
215 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 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 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 let actual_address = Address::p2tr(&secp, internal_key, backup_merkle_root, Network::Bitcoin);
241 if actual_spent_outputs[input_index as usize].script_pubkey != actual_address.script_pubkey() {
242 return Err(Error::UnexpectedInput);
243 }
244
245 verifier.verify(
247 emulated_address.script_pubkey().as_bytes(),
248 Some(amount),
249 emulated_tx_to,
250 input_index,
251 actual_spent_outputs,
252 tx.weight(),
253 )?;
254
255 let annex = input
257 .witness
258 .taproot_annex()
259 .filter(|bytes| bytes.len() > 1 && bytes[1] == TAPROOT_ANNEX_DATA_CARRYING_TAG)
260 .and_then(|bytes| Annex::new(bytes).ok());
261
262 let mut sighash_cache = SighashCache::new(&tx);
264 let sighash_bytes = sighash_cache
265 .taproot_signature_hash(
266 input_index as usize,
267 &Prevouts::All(actual_spent_outputs),
268 annex.clone(),
269 None,
270 TapSighashType::Default,
271 )
272 .map_err(|_| Error::InvalidSighash)?;
273 let mut sighash = [0u8; 32];
274 sighash.copy_from_slice(sighash_bytes.as_byte_array());
275
276 let tweak = TapTweakHash::from_key_and_tweak(internal_key, backup_merkle_root);
278 let tweaked_secret_key = child_key_for_tweak.add_tweak(&tweak.to_scalar())?;
279 let tweaked_keypair = Keypair::from_secret_key(&secp, &tweaked_secret_key);
280
281 let message = Message::from_digest(sighash);
283 let signature = secp.sign_schnorr_with_aux_rand(&message, &tweaked_keypair, aux_rand);
284
285 let tap_signature = Signature {
287 signature,
288 sighash_type: TapSighashType::Default,
289 };
290
291 let mut witness = Witness::new();
293 witness.push(tap_signature.to_vec());
294 if let Some(annex) = annex {
295 witness.push(annex.as_bytes());
296 }
297 tx.input[input_index as usize].witness = witness;
298
299 Ok(tx)
300}
301
302pub fn generate_address(
314 parent_key: PublicKey,
315 emulated_merkle_root: TapNodeHash,
316 backup_merkle_root: Option<TapNodeHash>,
317 network: Network,
318) -> Result<Address, secp256k1::Error> {
319 let secp = Secp256k1::new();
320 let child_key = derive_child_public_key(parent_key, emulated_merkle_root.to_byte_array())?;
321 let internal_key = XOnlyPublicKey::from(child_key);
322 let address = Address::p2tr(&secp, internal_key, backup_merkle_root, network);
323
324 Ok(address)
325}
326
327fn derive_child_secret_key(
330 parent_key: SecretKey,
331 emulated_merkle_root: [u8; 32],
332) -> Result<SecretKey, secp256k1::Error> {
333 let secp = Secp256k1::new();
334
335 let parent_public = parent_key.public_key(&secp);
337
338 let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
340 .expect("PublicKey serialization should always be non-empty");
341 mac.update(&emulated_merkle_root);
342 let hmac_result = mac.finalize().into_bytes();
343
344 let mut key_material = [0u8; 32];
346 key_material.copy_from_slice(&hmac_result[..32]);
347 let scalar = reduce_mod_order(&key_material);
348
349 parent_key.add_tweak(&scalar)
351}
352
353fn derive_child_public_key(
356 parent_public: PublicKey,
357 emulated_merkle_root: [u8; 32],
358) -> Result<PublicKey, secp256k1::Error> {
359 let secp = Secp256k1::new();
360
361 let mut mac = Hmac::<Sha512>::new_from_slice(&parent_public.serialize())
363 .expect("PublicKey serialization should always be non-empty");
364 mac.update(&emulated_merkle_root);
365 let hmac_result = mac.finalize().into_bytes();
366
367 let mut key_material = [0u8; 32];
369 key_material.copy_from_slice(&hmac_result[..32]);
370 let scalar = reduce_mod_order(&key_material);
371
372 parent_public.add_exp_tweak(&secp, &scalar)
374}
375
376fn reduce_mod_order(bytes: &[u8; 32]) -> Scalar {
378 let mut attempt = *bytes;
381 loop {
382 match Scalar::from_be_bytes(attempt) {
383 Ok(scalar) => return scalar,
384 Err(_) => {
385 attempt = subtract_curve_order(&attempt);
388 }
389 }
390 }
391}
392
393fn subtract_curve_order(bytes: &[u8; 32]) -> [u8; 32] {
395 let value = BigUint::from_bytes_be(bytes);
396 let order = BigUint::from_bytes_be(&CURVE_ORDER);
397 let reduced = value % order;
398
399 let mut result = [0u8; 32];
400 let reduced_bytes = reduced.to_bytes_be();
401 let offset = 32 - reduced_bytes.len();
402 result[offset..].copy_from_slice(&reduced_bytes);
403 result
404}
405
406impl fmt::Display for Error {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 match self {
409 Error::VerificationFailed(e) => {
410 write!(f, "Verification failed: {e}")
411 }
412 Error::Secp256k1(e) => {
413 write!(f, "Secp256k1 cryptographic operation failed: {e}")
414 }
415 Error::NotScriptPathSpend => {
416 write!(
417 f,
418 "Input is not a script path spend (missing taproot control block)"
419 )
420 }
421 Error::InvalidAmount(e) => {
422 write!(f, "Invalid amount: {e}")
423 }
424 Error::InvalidControlBlock => {
425 write!(f, "Input has invalid control block")
426 }
427 Error::DeserializationFailed(e) => {
428 write!(f, "Failed to deserialize: {e}")
429 }
430 Error::InvalidSighash => {
431 write!(f, "Unable to calculate sighash for input")
432 }
433 Error::InputIndexOutOfBounds => {
434 write!(f, "Input index out of bounds")
435 }
436 Error::UnexpectedInput => {
437 write!(f, "Unexpected input scriptPubKey")
438 }
439 Error::ExceedsMaxWeight => {
440 write!(f, "Exceeds maximum allowed transaction weight")
441 }
442 }
443 }
444}
445
446#[cfg(feature = "bitcoinkernel")]
447impl From<KernelError> for Error {
448 fn from(error: KernelError) -> Self {
449 Error::VerificationFailed(error.to_string())
450 }
451}
452
453impl From<secp256k1::Error> for Error {
454 fn from(error: secp256k1::Error) -> Self {
455 Error::Secp256k1(error)
456 }
457}
458
459impl From<bitcoin::consensus::encode::Error> for Error {
460 fn from(error: bitcoin::consensus::encode::Error) -> Self {
461 Error::DeserializationFailed(error)
462 }
463}
464
465impl From<bitcoin_units::amount::OutOfRangeError> for Error {
466 fn from(error: bitcoin_units::amount::OutOfRangeError) -> Self {
467 Error::InvalidAmount(error)
468 }
469}
470
471#[cfg(test)]
472#[cfg(feature = "bitcoinkernel")]
473mod kernel_tests {
474 use super::*;
475 use bitcoin::{
476 Address, Amount, Network, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut, Txid,
477 Witness,
478 consensus::encode::serialize,
479 hashes::Hash,
480 key::UntweakedPublicKey,
481 taproot::{LeafVersion, TaprootBuilder},
482 };
483
484 fn create_test_transaction_single_input() -> Transaction {
485 Transaction {
486 version: bitcoin::transaction::Version::TWO,
487 lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
488 input: vec![TxIn {
489 previous_output: OutPoint::null(),
490 script_sig: ScriptBuf::new(),
491 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
492 witness: Witness::new(),
493 }],
494 output: vec![TxOut {
495 value: Amount::from_sat(100000),
496 script_pubkey: ScriptBuf::new_op_return([]),
497 }],
498 }
499 }
500
501 fn create_test_transaction_multi_input() -> Transaction {
502 Transaction {
503 version: bitcoin::transaction::Version::TWO,
504 lock_time: bitcoin::locktime::absolute::LockTime::ZERO,
505 input: vec![
506 TxIn {
507 previous_output: OutPoint::null(),
508 script_sig: ScriptBuf::new(),
509 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
510 witness: Witness::new(),
511 },
512 TxIn {
513 previous_output: OutPoint::new(Txid::all_zeros(), 1),
514 script_sig: ScriptBuf::new(),
515 sequence: bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
516 witness: Witness::new(),
517 },
518 ],
519 output: vec![TxOut {
520 value: Amount::from_sat(100000),
521 script_pubkey: ScriptBuf::new_op_return([]),
522 }],
523 }
524 }
525
526 #[test]
527 fn test_unable_to_deserialize_tx() {
528 let result = verify_and_sign(
529 &DefaultVerifier,
530 0,
531 &[],
532 &[],
533 &[1u8; 32],
534 SecretKey::from_slice(&[1u8; 32]).unwrap(),
535 None,
536 );
537
538 assert!(matches!(result, Err(Error::DeserializationFailed(_))));
539 }
540
541 #[test]
542 fn test_input_index_out_of_bounds() {
543 let txout = TxOut {
544 value: Amount::from_sat(100000),
545 script_pubkey: ScriptBuf::new_op_return([]),
546 };
547 let result = verify_and_sign(
548 &DefaultVerifier,
549 1,
550 &serialize(&create_test_transaction_single_input()),
551 std::slice::from_ref(&txout),
552 &[1u8; 32],
553 SecretKey::from_slice(&[1u8; 32]).unwrap(),
554 None,
555 );
556
557 assert!(matches!(result, Err(Error::InputIndexOutOfBounds)));
558 }
559
560 #[test]
561 fn test_not_script_path_spend() {
562 let txout = TxOut {
563 value: Amount::from_sat(100000),
564 script_pubkey: ScriptBuf::new_op_return([]),
565 };
566 let result = verify_and_sign(
567 &DefaultVerifier,
568 0,
569 &serialize(&create_test_transaction_single_input()),
570 std::slice::from_ref(&txout),
571 &[1u8; 32],
572 SecretKey::from_slice(&[1u8; 32]).unwrap(),
573 None,
574 );
575
576 assert!(matches!(result, Err(Error::NotScriptPathSpend)));
577 }
578
579 #[test]
580 fn test_unexpected_input_script_pubkey() {
581 let secp = Secp256k1::new();
582
583 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
585 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
586
587 let op_true_script = Script::builder()
589 .push_opcode(bitcoin::opcodes::OP_TRUE)
590 .into_script();
591
592 let taproot_builder = TaprootBuilder::new()
594 .add_leaf(0, op_true_script.clone())
595 .unwrap();
596 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
597
598 let control_block = taproot_spend_info
600 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
601 .unwrap();
602
603 let mut witness = Witness::new();
605 witness.push(op_true_script.as_bytes());
606 witness.push(control_block.serialize());
607
608 let mut emulated_tx = create_test_transaction_single_input();
610 emulated_tx.input[0].witness = witness;
611
612 let txout = TxOut {
614 value: Amount::from_sat(100000),
615 script_pubkey: ScriptBuf::new_op_return([]),
616 };
617
618 let result = verify_and_sign(
619 &DefaultVerifier,
620 0,
621 &serialize(&emulated_tx),
622 std::slice::from_ref(&txout),
623 &[1u8; 32],
624 SecretKey::from_slice(&[1u8; 32]).unwrap(),
625 None,
626 );
627
628 assert!(matches!(result, Err(Error::UnexpectedInput)));
629 }
630
631 #[test]
632 fn test_verify_and_sign_single_input_single_leaf() {
633 let secp = Secp256k1::new();
634
635 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
637 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
638
639 let op_true_script = Script::builder()
641 .push_opcode(bitcoin::opcodes::OP_TRUE)
642 .into_script();
643
644 let taproot_builder = TaprootBuilder::new()
646 .add_leaf(0, op_true_script.clone())
647 .unwrap();
648 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
649
650 let control_block = taproot_spend_info
652 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
653 .unwrap();
654
655 let mut witness = Witness::new();
657 witness.push(op_true_script.as_bytes());
658 witness.push(control_block.serialize());
659
660 let mut emulated_tx = create_test_transaction_single_input();
662 emulated_tx.input[0].witness = witness;
663
664 let aux_rand = [1u8; 32];
666 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
667 let child_secret = derive_child_secret_key(
668 parent_secret,
669 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
670 )
671 .unwrap();
672
673 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
675 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
676 let actual_spent_outputs = [TxOut {
677 value: Amount::from_sat(100_000),
678 script_pubkey: actual_address.script_pubkey(),
679 }];
680
681 let actual_tx = verify_and_sign(
683 &DefaultVerifier,
684 0,
685 &serialize(&emulated_tx),
686 &actual_spent_outputs,
687 &aux_rand,
688 parent_secret,
689 None,
690 )
691 .unwrap();
692
693 let mut actual_outputs = Vec::new();
694 for txout in actual_spent_outputs {
695 let amount = txout.value.to_signed().unwrap().to_sat();
696 let script =
697 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
698 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
699 }
700
701 let verify_result = bitcoinkernel::verify(
703 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
704 .unwrap(),
705 Some(100_000),
706 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
707 0,
708 None,
709 &actual_outputs,
710 );
711
712 assert!(verify_result.is_ok());
713 assert_eq!(actual_tx.input[0].witness.len(), 1);
714 }
715
716 #[test]
717 fn test_verify_and_sign_single_input_multiple_leaves() {
718 let secp = Secp256k1::new();
719
720 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
722 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
723
724 let op_true_script = Script::builder()
726 .push_opcode(bitcoin::opcodes::OP_TRUE)
727 .into_script();
728 let op_false_script = Script::builder()
729 .push_opcode(bitcoin::opcodes::OP_FALSE)
730 .into_script();
731
732 let taproot_builder = TaprootBuilder::new()
734 .add_leaf(1, op_true_script.clone())
735 .unwrap()
736 .add_leaf(1, op_false_script.clone())
737 .unwrap();
738 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
739
740 let control_block = taproot_spend_info
742 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
743 .unwrap();
744
745 let mut witness = Witness::new();
747 witness.push(op_true_script.as_bytes());
748 witness.push(control_block.serialize());
749
750 let mut emulated_tx = create_test_transaction_single_input();
752 emulated_tx.input[0].witness = witness;
753
754 let aux_rand = [1u8; 32];
756 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
757 let child_secret = derive_child_secret_key(
758 parent_secret,
759 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
760 )
761 .unwrap();
762
763 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
765 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
766 let actual_spent_outputs = [TxOut {
767 value: Amount::from_sat(100_000),
768 script_pubkey: actual_address.script_pubkey(),
769 }];
770
771 let actual_tx = verify_and_sign(
773 &DefaultVerifier,
774 0,
775 &serialize(&emulated_tx),
776 &actual_spent_outputs,
777 &aux_rand,
778 parent_secret,
779 None,
780 )
781 .unwrap();
782
783 let mut actual_outputs = Vec::new();
784 for txout in actual_spent_outputs {
785 let amount = txout.value.to_signed().unwrap().to_sat();
786 let script =
787 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
788 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
789 }
790
791 let verify_result = bitcoinkernel::verify(
793 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
794 .unwrap(),
795 Some(100_000),
796 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
797 0,
798 None,
799 &actual_outputs,
800 );
801
802 assert!(verify_result.is_ok());
803 assert_eq!(actual_tx.input[0].witness.len(), 1);
804 }
805
806 #[test]
807 fn test_verify_and_sign_single_input_with_backup() {
808 let secp = Secp256k1::new();
809
810 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
812 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
813
814 let op_true_script = Script::builder()
816 .push_opcode(bitcoin::opcodes::OP_TRUE)
817 .into_script();
818
819 let taproot_builder = TaprootBuilder::new()
821 .add_leaf(0, op_true_script.clone())
822 .unwrap();
823 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
824
825 let control_block = taproot_spend_info
827 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
828 .unwrap();
829
830 let mut witness = Witness::new();
832 witness.push(op_true_script.as_bytes());
833 witness.push(control_block.serialize());
834
835 let mut emulated_tx = create_test_transaction_single_input();
837 emulated_tx.input[0].witness = witness;
838
839 let aux_rand = [1u8; 32];
841 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
842 let child_secret = derive_child_secret_key(
843 parent_secret,
844 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
845 )
846 .unwrap();
847
848 let actual_backup_merkle_root = taproot_spend_info.merkle_root();
850 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
851 let actual_address = Address::p2tr(
852 &secp,
853 actual_internal_key,
854 actual_backup_merkle_root,
855 Network::Bitcoin,
856 );
857 let actual_spent_outputs = [TxOut {
858 value: Amount::from_sat(100_000),
859 script_pubkey: actual_address.script_pubkey(),
860 }];
861
862 let actual_tx = verify_and_sign(
864 &DefaultVerifier,
865 0,
866 &serialize(&emulated_tx),
867 &actual_spent_outputs,
868 &aux_rand,
869 parent_secret,
870 actual_backup_merkle_root,
871 )
872 .unwrap();
873
874 let mut actual_outputs = Vec::new();
875 for txout in actual_spent_outputs {
876 let amount = txout.value.to_signed().unwrap().to_sat();
877 let script =
878 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
879 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
880 }
881
882 let verify_result = bitcoinkernel::verify(
884 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
885 .unwrap(),
886 Some(100_000),
887 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
888 0,
889 None,
890 &actual_outputs,
891 );
892
893 assert!(verify_result.is_ok());
894 assert_eq!(actual_tx.input[0].witness.len(), 1);
895 }
896
897 #[test]
898 fn test_verify_and_sign_single_input_single_leaf_with_data_carrying_annex() {
899 let secp = Secp256k1::new();
900
901 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
903 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
904
905 let op_true_script = Script::builder()
907 .push_opcode(bitcoin::opcodes::OP_TRUE)
908 .into_script();
909
910 let taproot_builder = TaprootBuilder::new()
912 .add_leaf(0, op_true_script.clone())
913 .unwrap();
914 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
915
916 let control_block = taproot_spend_info
918 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
919 .unwrap();
920
921 let annex: &[u8] = &[0x50, TAPROOT_ANNEX_DATA_CARRYING_TAG, 0x01, 0x02, 0x03];
923
924 let mut witness = Witness::new();
926 witness.push(op_true_script.as_bytes());
927 witness.push(control_block.serialize());
928 witness.push(annex);
929
930 let mut emulated_tx = create_test_transaction_single_input();
932 emulated_tx.input[0].witness = witness;
933
934 let aux_rand = [1u8; 32];
936 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
937 let child_secret = derive_child_secret_key(
938 parent_secret,
939 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
940 )
941 .unwrap();
942
943 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
945 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
946 let actual_spent_outputs = [TxOut {
947 value: Amount::from_sat(100_000),
948 script_pubkey: actual_address.script_pubkey(),
949 }];
950
951 let actual_tx = verify_and_sign(
953 &DefaultVerifier,
954 0,
955 &serialize(&emulated_tx),
956 &actual_spent_outputs,
957 &aux_rand,
958 parent_secret,
959 None,
960 )
961 .unwrap();
962
963 let mut actual_outputs = Vec::new();
964 for txout in actual_spent_outputs {
965 let amount = txout.value.to_signed().unwrap().to_sat();
966 let script =
967 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
968 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
969 }
970
971 let verify_result = bitcoinkernel::verify(
973 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
974 .unwrap(),
975 Some(100_000),
976 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
977 0,
978 None,
979 &actual_outputs,
980 );
981
982 assert!(verify_result.is_ok());
983 assert_eq!(actual_tx.input[0].witness.len(), 2);
984 assert_eq!(&actual_tx.input[0].witness[1], annex);
985 }
986
987 #[test]
988 fn test_verify_and_sign_single_input_single_leaf_with_non_data_carrying_annex() {
989 let secp = Secp256k1::new();
990
991 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
993 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
994
995 let op_true_script = Script::builder()
997 .push_opcode(bitcoin::opcodes::OP_TRUE)
998 .into_script();
999
1000 let taproot_builder = TaprootBuilder::new()
1002 .add_leaf(0, op_true_script.clone())
1003 .unwrap();
1004 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1005
1006 let control_block = taproot_spend_info
1008 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1009 .unwrap();
1010
1011 let annex: &[u8] = &[0x50, 0x01, 0x02, 0x03];
1013
1014 let mut witness = Witness::new();
1016 witness.push(op_true_script.as_bytes());
1017 witness.push(control_block.serialize());
1018 witness.push(annex);
1019
1020 let mut emulated_tx = create_test_transaction_single_input();
1022 emulated_tx.input[0].witness = witness;
1023
1024 let aux_rand = [1u8; 32];
1026 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1027 let child_secret = derive_child_secret_key(
1028 parent_secret,
1029 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1030 )
1031 .unwrap();
1032
1033 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1035 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1036 let actual_spent_outputs = [TxOut {
1037 value: Amount::from_sat(100_000),
1038 script_pubkey: actual_address.script_pubkey(),
1039 }];
1040
1041 let actual_tx = verify_and_sign(
1043 &DefaultVerifier,
1044 0,
1045 &serialize(&emulated_tx),
1046 &actual_spent_outputs,
1047 &aux_rand,
1048 parent_secret,
1049 None,
1050 )
1051 .unwrap();
1052
1053 let mut actual_outputs = Vec::new();
1054 for txout in actual_spent_outputs {
1055 let amount = txout.value.to_signed().unwrap().to_sat();
1056 let script =
1057 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
1058 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
1059 }
1060
1061 let verify_result = bitcoinkernel::verify(
1063 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
1064 .unwrap(),
1065 Some(100_000),
1066 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
1067 0,
1068 None,
1069 &actual_outputs,
1070 );
1071
1072 assert!(verify_result.is_ok());
1073 assert_eq!(actual_tx.input[0].witness.len(), 1);
1074 }
1075
1076 #[test]
1077 fn test_verify_and_sign_multi_input_tx() {
1078 let secp = Secp256k1::new();
1079
1080 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1082 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
1083
1084 let op_true_script = Script::builder()
1086 .push_opcode(bitcoin::opcodes::OP_TRUE)
1087 .into_script();
1088
1089 let taproot_builder = TaprootBuilder::new()
1091 .add_leaf(0, op_true_script.clone())
1092 .unwrap();
1093 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1094
1095 let control_block = taproot_spend_info
1097 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1098 .unwrap();
1099
1100 let mut witness = Witness::new();
1102 witness.push(op_true_script.as_bytes());
1103 witness.push(control_block.serialize());
1104
1105 let mut emulated_tx = create_test_transaction_multi_input();
1107 emulated_tx.input[1].witness = witness;
1108
1109 let aux_rand = [1u8; 32];
1111 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1112 let child_secret = derive_child_secret_key(
1113 parent_secret,
1114 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1115 )
1116 .unwrap();
1117
1118 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1120 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1121 let actual_spent_outputs = [
1122 TxOut {
1123 value: Amount::from_sat(200_000),
1124 script_pubkey: actual_address.script_pubkey(),
1125 },
1126 TxOut {
1127 value: Amount::from_sat(100_000),
1128 script_pubkey: actual_address.script_pubkey(),
1129 },
1130 ];
1131
1132 let actual_tx = verify_and_sign(
1134 &DefaultVerifier,
1135 1,
1136 &serialize(&emulated_tx),
1137 &actual_spent_outputs,
1138 &aux_rand,
1139 parent_secret,
1140 None,
1141 )
1142 .unwrap();
1143
1144 let mut actual_outputs = Vec::new();
1145 for txout in actual_spent_outputs {
1146 let amount = txout.value.to_signed().unwrap().to_sat();
1147 let script =
1148 bitcoinkernel::ScriptPubkey::try_from(txout.script_pubkey.as_bytes()).unwrap();
1149 actual_outputs.push(bitcoinkernel::TxOut::new(&script, amount));
1150 }
1151
1152 let verify_result = bitcoinkernel::verify(
1154 &bitcoinkernel::ScriptPubkey::try_from(actual_address.script_pubkey().as_bytes())
1155 .unwrap(),
1156 Some(100_000),
1157 &bitcoinkernel::Transaction::try_from(serialize(&actual_tx).as_slice()).unwrap(),
1158 1,
1159 None,
1160 &actual_outputs,
1161 );
1162
1163 assert!(verify_result.is_ok());
1164 assert_eq!(actual_tx.input[1].witness.len(), 1);
1165 }
1166
1167 #[test]
1168 fn test_exceeds_max_weight() {
1169 let secp = Secp256k1::new();
1170
1171 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1173 let internal_key = UntweakedPublicKey::from(internal_secret.public_key(&secp));
1174
1175 let op_true_script = Script::builder()
1177 .push_opcode(bitcoin::opcodes::OP_TRUE)
1178 .into_script();
1179
1180 let taproot_builder = TaprootBuilder::new()
1182 .add_leaf(0, op_true_script.clone())
1183 .unwrap();
1184 let taproot_spend_info = taproot_builder.finalize(&secp, internal_key).unwrap();
1185
1186 let control_block = taproot_spend_info
1188 .control_block(&(op_true_script.clone(), LeafVersion::TapScript))
1189 .unwrap();
1190
1191 let mut witness = Witness::new();
1193 witness.push(op_true_script.as_bytes());
1194 witness.push(control_block.serialize());
1195
1196 let mut emulated_tx = create_test_transaction_single_input();
1198 emulated_tx.output = vec![
1199 TxOut {
1200 value: Amount::from_sat(1),
1201 script_pubkey: ScriptBuf::new(),
1202 };
1203 120_000
1204 ];
1205 emulated_tx.input[0].witness = witness;
1206
1207 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1209 let child_secret = derive_child_secret_key(
1210 parent_secret,
1211 taproot_spend_info.merkle_root().unwrap().to_byte_array(),
1212 )
1213 .unwrap();
1214
1215 let actual_internal_key = XOnlyPublicKey::from(child_secret.public_key(&secp));
1217 let actual_address = Address::p2tr(&secp, actual_internal_key, None, Network::Bitcoin);
1218 let actual_spent_outputs = [TxOut {
1219 value: Amount::from_sat(100_000),
1220 script_pubkey: actual_address.script_pubkey(),
1221 }];
1222
1223 let result = verify_and_sign(
1225 &DefaultVerifier,
1226 0,
1227 &serialize(&emulated_tx),
1228 &actual_spent_outputs,
1229 &[1u8; 32],
1230 parent_secret,
1231 None,
1232 );
1233
1234 assert!(matches!(result, Err(Error::ExceedsMaxWeight)));
1235 }
1236}
1237
1238#[cfg(test)]
1239mod non_kernel_tests {
1240 use super::*;
1241 use bitcoin::{
1242 Script,
1243 key::{Secp256k1, UntweakedPublicKey},
1244 taproot::TaprootBuilder,
1245 };
1246
1247 #[test]
1248 fn test_generate_address() {
1249 let secp = Secp256k1::new();
1250
1251 let emulated_script = Script::builder()
1253 .push_opcode(bitcoin::opcodes::OP_TRUE)
1254 .into_script();
1255
1256 let taproot_builder = TaprootBuilder::new()
1258 .add_leaf(0, emulated_script.clone())
1259 .unwrap();
1260 let dummy_internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1261 let dummy_internal_key = UntweakedPublicKey::from(dummy_internal_secret.public_key(&secp));
1262 let taproot_spend_info = taproot_builder.finalize(&secp, dummy_internal_key).unwrap();
1263 let emulated_merkle_root = taproot_spend_info.merkle_root().unwrap();
1264
1265 let backup_merkle_root = emulated_merkle_root;
1267
1268 let internal_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1270 let master_public_key: PublicKey = internal_secret.public_key(&secp);
1271 let onchain_address = generate_address(
1272 master_public_key,
1273 emulated_merkle_root,
1274 Some(backup_merkle_root),
1275 Network::Bitcoin,
1276 );
1277
1278 assert!(onchain_address.is_ok());
1279 }
1280
1281 #[test]
1282 fn test_public_private_key_derivation_consistency() {
1283 let parent_secret = SecretKey::from_slice(&[1u8; 32]).unwrap();
1284 let parent_public = parent_secret.public_key(&Secp256k1::new());
1285 let merkle_root = [42u8; 32];
1286
1287 let child_secret = derive_child_secret_key(parent_secret, merkle_root).unwrap();
1288 let child_public_from_secret = child_secret.public_key(&Secp256k1::new());
1289 let child_public_direct = derive_child_public_key(parent_public, merkle_root).unwrap();
1290
1291 assert_eq!(child_public_from_secret, child_public_direct);
1292 }
1293
1294 #[test]
1295 fn test_curve_order_reduction() {
1296 let max_bytes = [0xFF; 32];
1297 let reduced = reduce_mod_order(&max_bytes);
1298 #[allow(clippy::useless_conversion)]
1300 let _ = Scalar::from(reduced);
1301 }
1302}