1
2use bitcoin::absolute::LockTime;
3use bitcoin::hashes::sha256;
4use bitcoin::key::constants::SCHNORR_SIGNATURE_SIZE;
5use bitcoin::secp256k1::schnorr;
6use bitcoin::taproot::{self, ControlBlock};
7use bitcoin::{Sequence, VarInt, Witness};
8use bitcoin::{secp256k1::PublicKey, ScriptBuf};
9
10use bitcoin_ext::{BlockDelta, BlockHeight};
11
12use crate::lightning::{PaymentHash, Preimage};
13use crate::vtxo::policy::Policy;
14use crate::Vtxo;
15use crate::scripts;
16
17pub trait TapScriptClause: Sized + Clone {
22 type WitnessData;
24
25 fn tapscript(&self) -> ScriptBuf;
27
28 fn control_block<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> ControlBlock {
30 vtxo.output_taproot()
31 .control_block(&(self.tapscript(), taproot::LeafVersion::TapScript))
32 .expect("clause is not in taproot tree")
33 }
34
35 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize;
46
47 fn witness(
49 &self,
50 data: &Self::WitnessData,
51 control_block: &ControlBlock,
52 ) -> Witness;
53}
54
55#[derive(Debug, Clone)]
58pub struct DelayedSignClause {
59 pub pubkey: PublicKey,
60 pub block_delta: BlockDelta,
61}
62
63impl DelayedSignClause {
64 pub fn sequence(&self) -> Sequence {
66 Sequence::from_height(self.block_delta)
67 }
68}
69
70impl TapScriptClause for DelayedSignClause {
71 type WitnessData = schnorr::Signature;
72
73 fn tapscript(&self) -> ScriptBuf {
74 scripts::delayed_sign(self.block_delta, self.pubkey.x_only_public_key().0)
75 }
76
77 fn witness(
78 &self,
79 signature: &Self::WitnessData,
80 control_block: &ControlBlock,
81 ) -> Witness {
82 Witness::from_slice(&[
83 &signature[..],
84 self.tapscript().as_bytes(),
85 &control_block.serialize()[..],
86 ])
87 }
88
89
90 #[allow(clippy::arithmetic_side_effects)]
92 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
93 let cb_size = self.control_block(vtxo).size();
94 let tapscript_size = self.tapscript().as_bytes().len();
95
96 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
104}
105
106impl Into<VtxoClause> for DelayedSignClause {
107 fn into(self) -> VtxoClause {
108 VtxoClause::DelayedSign(self)
109 }
110}
111
112#[derive(Debug, Clone)]
115pub struct TimelockSignClause {
116 pub pubkey: PublicKey,
117 pub timelock_height: BlockHeight,
118}
119
120impl TimelockSignClause {
121 pub fn locktime(&self) -> LockTime {
123 LockTime::from_height(self.timelock_height).expect("timelock height is valid")
124 }
125}
126
127impl TapScriptClause for TimelockSignClause {
128 type WitnessData = schnorr::Signature;
129
130 fn tapscript(&self) -> ScriptBuf {
131 scripts::timelock_sign(self.timelock_height, self.pubkey.x_only_public_key().0)
132 }
133
134 fn witness(
135 &self,
136 signature: &Self::WitnessData,
137 control_block: &ControlBlock,
138 ) -> Witness {
139 Witness::from_slice(&[
140 &signature[..],
141 self.tapscript().as_bytes(),
142 &control_block.serialize()[..],
143 ])
144 }
145
146 #[allow(clippy::arithmetic_side_effects)]
148 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
149 let cb_size = self.control_block(vtxo).size();
150 let tapscript_size = self.tapscript().as_bytes().len();
151
152 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
160}
161
162impl Into<VtxoClause> for TimelockSignClause {
163 fn into(self) -> VtxoClause {
164 VtxoClause::TimelockSign(self)
165 }
166}
167
168#[derive(Debug, Clone)]
171pub struct DelayedTimelockSignClause {
172 pub pubkey: PublicKey,
173 pub timelock_height: BlockHeight,
174 pub block_delta: BlockDelta,
175}
176
177impl DelayedTimelockSignClause {
178 pub fn sequence(&self) -> Sequence {
180 Sequence::from_height(self.block_delta)
181 }
182
183 pub fn locktime(&self) -> LockTime {
185 LockTime::from_height(self.timelock_height).expect("timelock height is valid")
186 }
187}
188
189impl TapScriptClause for DelayedTimelockSignClause {
190 type WitnessData = schnorr::Signature;
191
192 fn tapscript(&self) -> ScriptBuf {
193 scripts::delay_timelock_sign(
194 self.block_delta,
195 self.timelock_height,
196 self.pubkey.x_only_public_key().0,
197 )
198 }
199
200 fn witness(
201 &self,
202 signature: &Self::WitnessData,
203 control_block: &ControlBlock,
204 ) -> Witness {
205 Witness::from_slice(&[
206 &signature[..],
207 self.tapscript().as_bytes(),
208 &control_block.serialize()[..],
209 ])
210 }
211
212 #[allow(clippy::arithmetic_side_effects)]
214 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
215 let cb_size = self.control_block(vtxo).size();
216 let tapscript_size = self.tapscript().as_bytes().len();
217
218 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
226}
227
228impl Into<VtxoClause> for DelayedTimelockSignClause {
229 fn into(self) -> VtxoClause {
230 VtxoClause::DelayedTimelockSign(self)
231 }
232}
233
234#[derive(Debug, Clone)]
237pub struct HashDelaySignClause {
238 pub pubkey: PublicKey,
239 pub hash: sha256::Hash,
240 pub block_delta: BlockDelta,
241}
242
243impl HashDelaySignClause {
244 pub fn sequence(&self) -> Sequence {
246 Sequence::from_height(self.block_delta)
247 }
248
249 pub fn extract_preimage_from_witness(
254 witness: &Witness,
255 payment_hash: PaymentHash,
256 ) -> Option<Preimage> {
257 if witness.len() != 4 {
258 return None;
259 }
260
261 let bytes = witness.nth(1)?;
262 let bytes: [u8; 32] = bytes.try_into().ok()?;
263
264 let preimage = Preimage::from(bytes);
265 if preimage.compute_payment_hash() != payment_hash {
266 return None;
267 }
268
269 Some(preimage)
270 }
271}
272
273impl TapScriptClause for HashDelaySignClause {
274 type WitnessData = (schnorr::Signature, [u8; 32]);
275
276 fn tapscript(&self) -> ScriptBuf {
277 scripts::hash_delay_sign(
278 self.hash,
279 self.block_delta,
280 self.pubkey.x_only_public_key().0,
281 )
282 }
283
284 fn witness(
285 &self,
286 data: &Self::WitnessData,
287 control_block: &ControlBlock,
288 ) -> Witness {
289 let (signature, preimage) = data;
290 Witness::from_slice(&[
291 &signature[..],
292 &preimage[..],
293 self.tapscript().as_bytes(),
294 &control_block.serialize()[..],
295 ])
296 }
297
298 #[allow(clippy::arithmetic_side_effects)]
300 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
301 let cb_size = self.control_block(vtxo).size();
302 let tapscript_size = self.tapscript().as_bytes().len();
303
304 1 + 1 + SCHNORR_SIGNATURE_SIZE + 1 + 32 + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
314}
315
316impl Into<VtxoClause> for HashDelaySignClause {
317 fn into(self) -> VtxoClause {
318 VtxoClause::HashDelaySign(self)
319 }
320}
321
322#[derive(Debug, Clone)]
325#[allow(non_camel_case_types)]
326pub struct HashDelaySignClause_v0 {
327 pub pubkey: PublicKey,
328 pub hash: sha256::Hash,
329 pub block_delta: BlockDelta,
330}
331
332impl HashDelaySignClause_v0 {
333 pub fn sequence(&self) -> Sequence {
335 Sequence::from_height(self.block_delta)
336 }
337
338 pub fn extract_preimage_from_witness(
343 witness: &Witness,
344 payment_hash: PaymentHash,
345 ) -> Option<Preimage> {
346 if witness.len() != 4 {
347 return None;
348 }
349
350 let bytes = witness.nth(1)?;
351 let bytes: [u8; 32] = bytes.try_into().ok()?;
352
353 let preimage = Preimage::from(bytes);
354 if preimage.compute_payment_hash() != payment_hash {
355 return None;
356 }
357
358 Some(preimage)
359 }
360}
361
362impl TapScriptClause for HashDelaySignClause_v0 {
363 type WitnessData = (schnorr::Signature, [u8; 32]);
364
365 fn tapscript(&self) -> ScriptBuf {
366 scripts::hash_delay_sign_v0(
367 self.hash,
368 self.block_delta,
369 self.pubkey.x_only_public_key().0,
370 )
371 }
372
373 fn witness(
374 &self,
375 data: &Self::WitnessData,
376 control_block: &ControlBlock,
377 ) -> Witness {
378 let (signature, preimage) = data;
379 Witness::from_slice(&[
380 &signature[..],
381 &preimage[..],
382 self.tapscript().as_bytes(),
383 &control_block.serialize()[..],
384 ])
385 }
386
387 #[allow(clippy::arithmetic_side_effects)]
389 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
390 let cb_size = self.control_block(vtxo).size();
391 let tapscript_size = self.tapscript().as_bytes().len();
392
393 1 + 1 + SCHNORR_SIGNATURE_SIZE + 1 + 32 + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
403}
404
405impl Into<VtxoClause> for HashDelaySignClause_v0 {
406 fn into(self) -> VtxoClause {
407 VtxoClause::HashDelaySign_v0(self)
408 }
409}
410
411#[derive(Debug, Clone)]
416pub struct HashSignClause {
417 pub pubkey: PublicKey,
418 pub hash: sha256::Hash,
419}
420
421impl TapScriptClause for HashSignClause {
422 type WitnessData = (schnorr::Signature, [u8; 32]);
423
424 fn tapscript(&self) -> ScriptBuf {
425 scripts::hash_and_sign(self.hash, self.pubkey.x_only_public_key().0)
426 }
427
428 fn witness(
429 &self,
430 data: &Self::WitnessData,
431 control_block: &ControlBlock,
432 ) -> Witness {
433 let (signature, preimage) = data;
434 Witness::from_slice(&[
435 &signature[..],
436 &preimage[..],
437 self.tapscript().as_bytes(),
438 &control_block.serialize()[..],
439 ])
440 }
441
442 #[allow(clippy::arithmetic_side_effects)]
444 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
445 let cb_size = self.control_block(vtxo).size();
446 let tapscript_size = self.tapscript().as_bytes().len();
447
448 1 + 1 + SCHNORR_SIGNATURE_SIZE + 1 + 32 + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
458}
459
460impl Into<VtxoClause> for HashSignClause {
461 fn into(self) -> VtxoClause {
462 VtxoClause::HashSign(self)
463 }
464}
465
466#[derive(Debug, Clone)]
471#[allow(non_camel_case_types)]
472pub struct HashSignClause_v0 {
473 pub pubkey: PublicKey,
474 pub hash: sha256::Hash,
475}
476
477impl TapScriptClause for HashSignClause_v0 {
478 type WitnessData = (schnorr::Signature, [u8; 32]);
479
480 fn tapscript(&self) -> ScriptBuf {
481 scripts::hash_and_sign_v0(self.hash, self.pubkey.x_only_public_key().0)
482 }
483
484 fn witness(
485 &self,
486 data: &Self::WitnessData,
487 control_block: &ControlBlock,
488 ) -> Witness {
489 let (signature, preimage) = data;
490 Witness::from_slice(&[
491 &signature[..],
492 &preimage[..],
493 self.tapscript().as_bytes(),
494 &control_block.serialize()[..],
495 ])
496 }
497
498 #[allow(clippy::arithmetic_side_effects)]
500 fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
501 let cb_size = self.control_block(vtxo).size();
502 let tapscript_size = 57;
503
504 debug_assert_eq!(tapscript_size, self.tapscript().as_bytes().len());
505
506 1 + 1 + SCHNORR_SIGNATURE_SIZE + 1 + 32 + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
516}
517
518impl Into<VtxoClause> for HashSignClause_v0 {
519 fn into(self) -> VtxoClause {
520 VtxoClause::HashSign_v0(self)
521 }
522}
523
524#[derive(Debug, Clone)]
525pub enum VtxoClause {
526 DelayedSign(DelayedSignClause),
527 TimelockSign(TimelockSignClause),
528 DelayedTimelockSign(DelayedTimelockSignClause),
529 HashDelaySign(HashDelaySignClause),
530 HashSign(HashSignClause),
531 #[allow(non_camel_case_types)]
532 HashDelaySign_v0(HashDelaySignClause_v0),
533 #[allow(non_camel_case_types)]
534 HashSign_v0(HashSignClause_v0),
535}
536
537impl VtxoClause {
538 pub fn pubkey(&self) -> PublicKey {
540 match self {
541 Self::DelayedSign(c) => c.pubkey,
542 Self::TimelockSign(c) => c.pubkey,
543 Self::DelayedTimelockSign(c) => c.pubkey,
544 Self::HashDelaySign(c) => c.pubkey,
545 Self::HashSign(c) => c.pubkey,
546 Self::HashDelaySign_v0(c) => c.pubkey,
547 Self::HashSign_v0(c) => c.pubkey,
548 }
549 }
550
551
552 pub fn tapscript(&self) -> ScriptBuf {
554 match self {
555 Self::DelayedSign(c) => c.tapscript(),
556 Self::TimelockSign(c) => c.tapscript(),
557 Self::DelayedTimelockSign(c) => c.tapscript(),
558 Self::HashDelaySign(c) => c.tapscript(),
559 Self::HashSign(c) => c.tapscript(),
560 Self::HashDelaySign_v0(c) => c.tapscript(),
561 Self::HashSign_v0(c) => c.tapscript(),
562 }
563 }
564
565 pub fn sequence(&self) -> Option<Sequence> {
567 match self {
568 Self::DelayedSign(c) => Some(c.sequence()),
569 Self::TimelockSign(_) => None,
570 Self::DelayedTimelockSign(c) => Some(c.sequence()),
571 Self::HashDelaySign(c) => Some(c.sequence()),
572 Self::HashSign(_) => None,
573 Self::HashDelaySign_v0(c) => Some(c.sequence()),
574 Self::HashSign_v0(_) => None,
575 }
576 }
577
578 pub fn control_block<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> ControlBlock {
580 match self {
581 Self::DelayedSign(c) => c.control_block(vtxo),
582 Self::TimelockSign(c) => c.control_block(vtxo),
583 Self::DelayedTimelockSign(c) => c.control_block(vtxo),
584 Self::HashDelaySign(c) => c.control_block(vtxo),
585 Self::HashSign(c) => c.control_block(vtxo),
586 Self::HashDelaySign_v0(c) => c.control_block(vtxo),
587 Self::HashSign_v0(c) => c.control_block(vtxo),
588 }
589 }
590
591 pub fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
593 match self {
594 Self::DelayedSign(c) => c.witness_size(vtxo),
595 Self::TimelockSign(c) => c.witness_size(vtxo),
596 Self::DelayedTimelockSign(c) => c.witness_size(vtxo),
597 Self::HashDelaySign(c) => c.witness_size(vtxo),
598 Self::HashSign(c) => c.witness_size(vtxo),
599 Self::HashDelaySign_v0(c) => c.witness_size(vtxo),
600 Self::HashSign_v0(c) => c.witness_size(vtxo),
601 }
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use std::str::FromStr;
608
609 use bitcoin::taproot::TaprootSpendInfo;
610 use bitcoin::{Amount, OutPoint, Transaction, TxIn, TxOut, Txid, sighash};
611 use bitcoin::hashes::Hash;
612 use bitcoin::key::Keypair;
613 use bitcoin_ext::{TaprootSpendInfoExt, fee};
614
615 use crate::{SECP, musig};
616 use crate::test_util::verify_tx;
617
618 use super::*;
619
620 lazy_static! {
621 static ref USER_KEYPAIR: Keypair = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
622 static ref SERVER_KEYPAIR: Keypair = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
623 }
624
625 #[allow(unused)]
626 fn all_clause_tested(clause: VtxoClause) -> bool {
627 match clause {
629 VtxoClause::DelayedSign(_) => true,
630 VtxoClause::TimelockSign(_) => true,
631 VtxoClause::DelayedTimelockSign(_) => true,
632 VtxoClause::HashDelaySign(_) => true,
633 VtxoClause::HashSign(_) => true,
634 VtxoClause::HashDelaySign_v0(_) => true,
635 VtxoClause::HashSign_v0(_) => true,
636 }
637 }
638
639 fn transaction() -> Transaction {
640 let address = bitcoin::Address::from_str("tb1q00h5delzqxl7xae8ufmsegghcl4jwfvdnd8530")
641 .unwrap().assume_checked();
642
643 Transaction {
644 version: bitcoin::transaction::Version(3),
645 lock_time: bitcoin::absolute::LockTime::ZERO,
646 input: vec![],
647 output: vec![TxOut {
648 script_pubkey: address.script_pubkey(),
649 value: Amount::from_sat(900_000),
650 }, fee::fee_anchor()]
651 }
652 }
653
654 fn taproot_material(clause_spk: ScriptBuf) -> (TaprootSpendInfo, ControlBlock) {
655 let user_pubkey = USER_KEYPAIR.public_key();
656 let server_pubkey = SERVER_KEYPAIR.public_key();
657
658 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey])
659 .x_only_public_key().0;
660 let taproot = taproot::TaprootBuilder::new()
661 .add_leaf(0, clause_spk.clone()).unwrap()
662 .finalize(&SECP, combined_pk).unwrap();
663
664 let cb = taproot
665 .control_block(&(clause_spk.clone(), taproot::LeafVersion::TapScript))
666 .expect("script is in taproot");
667
668 (taproot, cb)
669 }
670
671 fn signature(tx: &Transaction, input: &TxOut, clause_spk: ScriptBuf) -> schnorr::Signature {
672 let leaf_hash = taproot::TapLeafHash::from_script(
673 &clause_spk,
674 taproot::LeafVersion::TapScript,
675 );
676
677 let mut shc = sighash::SighashCache::new(tx);
678 let sighash = shc.taproot_script_spend_signature_hash(
679 0, &sighash::Prevouts::All(&[input.clone()]), leaf_hash, sighash::TapSighashType::Default,
680 ).expect("all prevouts provided");
681
682 SECP.sign_schnorr(&sighash.into(), &*USER_KEYPAIR)
683 }
684
685 #[test]
686 fn test_delayed_sign_clause() {
687 let clause = DelayedSignClause {
688 pubkey: USER_KEYPAIR.public_key(),
689 block_delta: 100,
690 };
691
692 let (taproot, cb) = taproot_material(clause.tapscript());
694 let tx_in = TxOut {
695 script_pubkey: taproot.script_pubkey(),
696 value: Amount::from_sat(1_000_000),
697 };
698
699 let mut tx = transaction();
701 tx.input.push(TxIn {
702 previous_output: OutPoint::new(Txid::all_zeros(), 0),
703 script_sig: ScriptBuf::default(),
704 sequence: clause.sequence(),
705 witness: Witness::new(),
706 });
707
708 let signature = signature(&tx, &tx_in, clause.tapscript());
710 tx.input[0].witness = clause.witness(&signature, &cb);
711
712 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
714 }
715
716 #[test]
717 fn test_timelock_sign_clause() {
718 let clause = TimelockSignClause {
719 pubkey: USER_KEYPAIR.public_key(),
720 timelock_height: 100,
721 };
722
723 let (taproot, cb) = taproot_material(clause.tapscript());
725 let tx_in = TxOut {
726 script_pubkey: taproot.script_pubkey(),
727 value: Amount::from_sat(1_000_000),
728 };
729
730 let mut tx = transaction();
732 tx.lock_time = clause.locktime();
733 tx.input.push(TxIn {
734 previous_output: OutPoint::new(Txid::all_zeros(), 0),
735 script_sig: ScriptBuf::default(),
736 sequence: Sequence::ZERO,
737 witness: Witness::new(),
738 });
739
740 let signature = signature(&tx, &tx_in, clause.tapscript());
742 tx.input[0].witness = clause.witness(&signature, &cb);
743
744 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
746 }
747
748 #[test]
749 fn test_delayed_timelock_clause() {
750 let clause = DelayedTimelockSignClause {
751 pubkey: USER_KEYPAIR.public_key(),
752 timelock_height: 100,
753 block_delta: 24,
754 };
755
756 let (taproot, cb) = taproot_material(clause.tapscript());
758 let tx_in = TxOut {
759 script_pubkey: taproot.script_pubkey(),
760 value: Amount::from_sat(1_000_000),
761 };
762
763 let mut tx = transaction();
765 tx.lock_time = clause.locktime();
766 tx.input.push(TxIn {
767 previous_output: OutPoint::new(Txid::all_zeros(), 0),
768 script_sig: ScriptBuf::default(),
769 sequence: clause.sequence(),
770 witness: Witness::new(),
771 });
772
773 let signature = signature(&tx, &tx_in, clause.tapscript());
775 tx.input[0].witness = clause.witness(&signature, &cb);
776
777 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
779 }
780
781 #[test]
782 fn test_hash_delay_clause() {
783 let preimage = [0; 32];
784
785 let clause = HashDelaySignClause_v0 {
786 pubkey: USER_KEYPAIR.public_key(),
787 hash: sha256::Hash::hash(&preimage),
788 block_delta: 24,
789 };
790
791 let (taproot, cb) = taproot_material(clause.tapscript());
793 let tx_in = TxOut {
794 script_pubkey: taproot.script_pubkey(),
795 value: Amount::from_sat(1_000_000),
796 };
797
798 let mut tx = transaction();
800 tx.input.push(TxIn {
801 previous_output: OutPoint::new(Txid::all_zeros(), 0),
802 script_sig: ScriptBuf::default(),
803 sequence: clause.sequence(),
804 witness: Witness::new(),
805 });
806
807 let signature = signature(&tx, &tx_in, clause.tapscript());
809 tx.input[0].witness = clause.witness(&(signature, preimage), &cb);
810
811 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
813 }
814
815 #[test]
816 fn test_extract_preimage_from_witness() {
817 let preimage_bytes = [42u8; 32];
818 let payment_hash = sha256::Hash::hash(&preimage_bytes);
819
820 let clause = HashDelaySignClause_v0 {
821 pubkey: USER_KEYPAIR.public_key(),
822 hash: payment_hash,
823 block_delta: 24,
824 };
825
826 let (taproot, cb) = taproot_material(clause.tapscript());
828 let tx_in = TxOut {
829 script_pubkey: taproot.script_pubkey(),
830 value: Amount::from_sat(1_000_000),
831 };
832
833 let mut tx = transaction();
834 tx.input.push(TxIn {
835 previous_output: OutPoint::new(Txid::all_zeros(), 0),
836 script_sig: ScriptBuf::default(),
837 sequence: clause.sequence(),
838 witness: Witness::new(),
839 });
840
841 let sig = signature(&tx, &tx_in, clause.tapscript());
842 let witness = clause.witness(&(sig, preimage_bytes), &cb);
843
844 let extracted = HashDelaySignClause_v0::extract_preimage_from_witness(
846 &witness,
847 payment_hash.into(),
848 );
849 assert!(extracted.is_some());
850 assert_eq!(extracted.unwrap().as_ref(), &preimage_bytes);
851
852 let wrong_hash = sha256::Hash::hash(&[0u8; 32]);
854 let extracted = HashDelaySignClause_v0::extract_preimage_from_witness(
855 &witness,
856 wrong_hash.into(),
857 );
858 assert!(extracted.is_none());
859
860 let short_witness = Witness::from_slice(&[&sig[..], &preimage_bytes[..]]);
862 let extracted = HashDelaySignClause_v0::extract_preimage_from_witness(
863 &short_witness,
864 payment_hash.into(),
865 );
866 assert!(extracted.is_none());
867 }
868
869 #[test]
870 fn test_hash_sign_clause() {
871 let preimage = [0u8; 32];
872 let hash = sha256::Hash::hash(&preimage);
873
874 let agg_pk = musig::combine_keys([USER_KEYPAIR.public_key(), SERVER_KEYPAIR.public_key()]);
876
877 let clause = HashSignClause_v0 {
878 pubkey: agg_pk,
879 hash,
880 };
881
882 let (taproot, cb) = taproot_material(clause.tapscript());
884 let tx_in = TxOut {
885 script_pubkey: taproot.script_pubkey(),
886 value: Amount::from_sat(1_000_000),
887 };
888
889 let mut tx = transaction();
891 tx.input.push(TxIn {
892 previous_output: OutPoint::new(Txid::all_zeros(), 0),
893 script_sig: ScriptBuf::default(),
894 sequence: Sequence::ZERO, witness: Witness::new(),
896 });
897
898 let leaf_hash = taproot::TapLeafHash::from_script(
900 &clause.tapscript(),
901 taproot::LeafVersion::TapScript,
902 );
903
904 let mut shc = sighash::SighashCache::new(&tx);
905 let sighash = shc.taproot_script_spend_signature_hash(
906 0, &sighash::Prevouts::All(&[tx_in.clone()]), leaf_hash, sighash::TapSighashType::Default,
907 ).expect("all prevouts provided");
908
909 let (user_sec_nonce, user_pub_nonce) = musig::nonce_pair(&*USER_KEYPAIR);
911 let (server_pub_nonce, server_part_sig) = musig::deterministic_partial_sign(
912 &*SERVER_KEYPAIR,
913 [USER_KEYPAIR.public_key()],
914 &[&user_pub_nonce],
915 sighash.to_byte_array(),
916 None,
917 );
918 let agg_nonce = musig::nonce_agg(&[&user_pub_nonce, &server_pub_nonce]);
919
920 let (_user_part_sig, final_sig) = musig::partial_sign(
921 [USER_KEYPAIR.public_key(), SERVER_KEYPAIR.public_key()],
922 agg_nonce,
923 &*USER_KEYPAIR,
924 user_sec_nonce,
925 sighash.to_byte_array(),
926 None,
927 Some(&[&server_part_sig]),
928 );
929 let final_sig = final_sig.expect("should have final signature");
930
931 tx.input[0].witness = clause.witness(&(final_sig, preimage), &cb);
932
933 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
935 }
936}