Skip to main content

ark/vtxo/policy/
clause.rs

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
17/// A trait describing a VTXO policy clause.
18///
19/// It can be used when creating the VTXO, specifying the script pubkey,
20/// and check the satisfaction weight when spending it.
21pub trait TapScriptClause: Sized + Clone {
22	/// The type of witness data required to sign the clause.
23	type WitnessData;
24
25	/// Returns the tapscript for the clause.
26	fn tapscript(&self) -> ScriptBuf;
27
28	/// Construct the taproot control block for spending the VTXO using this clause
29	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	/// Computes the total witness size in bytes for spending via this clause.
36	///
37	/// Implementations sum bounded components and so cannot overflow `usize`
38	/// on any supported platform:
39	///   tapscript_size: <= MAX_STANDARD_TAPSCRIPT_SIZE (10_000 bytes)
40	///   cb_size: <= TAPROOT_CONTROL_MAX_SIZE (33 + 32*128 = 4_129 bytes)
41	///   VarInt sizes: <= 9 bytes each
42	///   constants: <= 1 + 1 + 64 (+ 1 + 32 for hash variants)
43	/// Sum is on the order of ~14 KB, well below u32::MAX, so the
44	/// usize addition cannot overflow even when usize is 32 bits.
45	fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize;
46
47	/// Constructs the witness for the clause.
48	fn witness(
49		&self,
50		data: &Self::WitnessData,
51		control_block: &ControlBlock,
52	) -> Witness;
53}
54
55/// A clause that allows to sign and spend the UTXO after a relative
56/// timelock.
57#[derive(Debug, Clone)]
58pub struct DelayedSignClause {
59	pub pubkey: PublicKey,
60	pub block_delta: BlockDelta,
61}
62
63impl DelayedSignClause {
64	/// Returns the input sequence value for this clause.
65	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	// See `TapScriptClause::witness_size` for the overflow analysis.
91	#[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 // byte for the number of witness elements
97		+ 1  // schnorr signature size byte
98		+ SCHNORR_SIGNATURE_SIZE // schnorr sig bytes
99		+ VarInt::from(tapscript_size).size()  // tapscript size bytes
100		+ tapscript_size // tapscript bytes
101		+ VarInt::from(cb_size).size()  // control block size bytes
102		+ cb_size // control block bytes
103	}
104}
105
106impl Into<VtxoClause> for DelayedSignClause {
107	fn into(self) -> VtxoClause {
108		VtxoClause::DelayedSign(self)
109	}
110}
111
112/// A clause that allows to sign and spend the UTXO after an absolute
113/// timelock.
114#[derive(Debug, Clone)]
115pub struct TimelockSignClause {
116	pub pubkey: PublicKey,
117	pub timelock_height: BlockHeight,
118}
119
120impl TimelockSignClause {
121	/// Returns the absolute locktime for this clause.
122	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	// See `TapScriptClause::witness_size` for the overflow analysis.
147	#[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 // byte for the number of witness elements
153		+ 1  // schnorr signature size byte
154		+ SCHNORR_SIGNATURE_SIZE // schnorr sig bytes
155		+ VarInt::from(tapscript_size).size()  // tapscript size bytes
156		+ tapscript_size // tapscript bytes
157		+ VarInt::from(cb_size).size()  // control block size bytes
158		+ cb_size // control block bytes
159	}
160}
161
162impl Into<VtxoClause> for TimelockSignClause {
163	fn into(self) -> VtxoClause {
164		VtxoClause::TimelockSign(self)
165	}
166}
167
168/// A clause that allows to sign and spend the UTXO after a relative
169/// timelock, with an additional absolute one.
170#[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	/// Returns the input sequence for this clause.
179	pub fn sequence(&self) -> Sequence {
180		Sequence::from_height(self.block_delta)
181	}
182
183	/// Returns the absolute locktime for this clause.
184	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	// See `TapScriptClause::witness_size` for the overflow analysis.
213	#[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 // byte for the number of witness elements
219		+ 1  // schnorr signature size byte
220		+ SCHNORR_SIGNATURE_SIZE // schnorr sig bytes
221		+ VarInt::from(tapscript_size).size()  // tapscript size bytes
222		+ tapscript_size // tapscript bytes
223		+ VarInt::from(cb_size).size()  // control block size bytes
224		+ cb_size // control block bytes
225	}
226}
227
228impl Into<VtxoClause> for DelayedTimelockSignClause {
229	fn into(self) -> VtxoClause {
230		VtxoClause::DelayedTimelockSign(self)
231	}
232}
233
234/// A clause that allows to sign and spend the UTXO after a relative
235/// timelock, if preimage matching the hash is provided.
236#[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	/// Returns the input sequence for this clause.
245	pub fn sequence(&self) -> Sequence {
246		Sequence::from_height(self.block_delta)
247	}
248
249	/// Try to extract the preimage from a witness that spends this clause.
250	///
251	/// Witness layout: `[signature, preimage, tapscript, control_block]`.
252	/// Returns the preimage if it is 32 bytes and hashes to the given payment hash.
253	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	// See `TapScriptClause::witness_size` for the overflow analysis.
299	#[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 // byte for the number of witness elements
305		+ 1  // schnorr signature size byte
306		+ SCHNORR_SIGNATURE_SIZE // schnorr sig bytes
307		+ 1  // preimage size byte
308		+ 32 // preimage bytes
309		+ VarInt::from(tapscript_size).size()  // tapscript size bytes
310		+ tapscript_size // tapscript bytes
311		+ VarInt::from(cb_size).size()  // control block size bytes
312		+ cb_size // control block bytes
313	}
314}
315
316impl Into<VtxoClause> for HashDelaySignClause {
317	fn into(self) -> VtxoClause {
318		VtxoClause::HashDelaySign(self)
319	}
320}
321
322/// A clause that allows spending by revealing a preimage and providing a signature.
323///
324/// This is used for the unlock clause in hArk leaf outputs, where the aggregate
325/// pubkey of user+server must sign, and a preimage must be revealed.
326#[derive(Debug, Clone)]
327pub struct HashSignClause {
328	pub pubkey: PublicKey,
329	pub hash: sha256::Hash,
330}
331
332impl TapScriptClause for HashSignClause {
333	type WitnessData = (schnorr::Signature, [u8; 32]);
334
335	fn tapscript(&self) -> ScriptBuf {
336		scripts::hash_and_sign(self.hash, self.pubkey.x_only_public_key().0)
337	}
338
339	fn witness(
340		&self,
341		data: &Self::WitnessData,
342		control_block: &ControlBlock,
343	) -> Witness {
344		let (signature, preimage) = data;
345		Witness::from_slice(&[
346			&signature[..],
347			&preimage[..],
348			self.tapscript().as_bytes(),
349			&control_block.serialize()[..],
350		])
351	}
352
353	// See `TapScriptClause::witness_size` for the overflow analysis.
354	#[allow(clippy::arithmetic_side_effects)]
355	fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
356		let cb_size = self.control_block(vtxo).size();
357		let tapscript_size = 57;
358
359		debug_assert_eq!(tapscript_size, self.tapscript().as_bytes().len());
360
361		1 // byte for the number of witness elements
362		+ 1  // schnorr signature size byte
363		+ SCHNORR_SIGNATURE_SIZE // schnorr sig bytes
364		+ 1  // preimage size byte
365		+ 32 // preimage bytes
366		+ VarInt::from(tapscript_size).size()  // tapscript size bytes
367		+ tapscript_size // tapscript bytes
368		+ VarInt::from(cb_size).size()  // control block size bytes
369		+ cb_size // control block bytes
370	}
371}
372
373impl Into<VtxoClause> for HashSignClause {
374	fn into(self) -> VtxoClause {
375		VtxoClause::HashSign(self)
376	}
377}
378
379#[derive(Debug, Clone)]
380pub enum VtxoClause {
381	DelayedSign(DelayedSignClause),
382	TimelockSign(TimelockSignClause),
383	DelayedTimelockSign(DelayedTimelockSignClause),
384	HashDelaySign(HashDelaySignClause),
385	HashSign(HashSignClause),
386}
387
388impl VtxoClause {
389	/// Returns the public key associated with this clause.
390	pub fn pubkey(&self) -> PublicKey {
391		match self {
392			Self::DelayedSign(c) => c.pubkey,
393			Self::TimelockSign(c) => c.pubkey,
394			Self::DelayedTimelockSign(c) => c.pubkey,
395			Self::HashDelaySign(c) => c.pubkey,
396			Self::HashSign(c) => c.pubkey,
397		}
398	}
399
400
401	/// Returns the tapscript for this clause.
402	pub fn tapscript(&self) -> ScriptBuf {
403		match self {
404			Self::DelayedSign(c) => c.tapscript(),
405			Self::TimelockSign(c) => c.tapscript(),
406			Self::DelayedTimelockSign(c) => c.tapscript(),
407			Self::HashDelaySign(c) => c.tapscript(),
408			Self::HashSign(c) => c.tapscript(),
409		}
410	}
411
412	/// Returns the input sequence for this clause, if applicable.
413	pub fn sequence(&self) -> Option<Sequence> {
414		match self {
415			Self::DelayedSign(c) => Some(c.sequence()),
416			Self::TimelockSign(_) => None,
417			Self::DelayedTimelockSign(c) => Some(c.sequence()),
418			Self::HashDelaySign(c) => Some(c.sequence()),
419			Self::HashSign(_) => None,
420		}
421	}
422
423	/// Computes the total witness size in bytes for spending the VTXO via this clause.
424	pub fn control_block<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> ControlBlock {
425		match self {
426			Self::DelayedSign(c) => c.control_block(vtxo),
427			Self::TimelockSign(c) => c.control_block(vtxo),
428			Self::DelayedTimelockSign(c) => c.control_block(vtxo),
429			Self::HashDelaySign(c) => c.control_block(vtxo),
430			Self::HashSign(c) => c.control_block(vtxo),
431		}
432	}
433
434	/// Computes the total witness size in bytes for spending the VTXO via this clause.
435	pub fn witness_size<G, P: Policy>(&self, vtxo: &Vtxo<G, P>) -> usize {
436		match self {
437			Self::DelayedSign(c) => c.witness_size(vtxo),
438			Self::TimelockSign(c) => c.witness_size(vtxo),
439			Self::DelayedTimelockSign(c) => c.witness_size(vtxo),
440			Self::HashDelaySign(c) => c.witness_size(vtxo),
441			Self::HashSign(c) => c.witness_size(vtxo),
442		}
443	}
444}
445
446#[cfg(test)]
447mod tests {
448	use std::str::FromStr;
449
450	use bitcoin::taproot::TaprootSpendInfo;
451	use bitcoin::{Amount, OutPoint, Transaction, TxIn, TxOut, Txid, sighash};
452	use bitcoin::hashes::Hash;
453	use bitcoin::key::Keypair;
454	use bitcoin_ext::{TaprootSpendInfoExt, fee};
455
456	use crate::{SECP, musig};
457	use crate::test_util::verify_tx;
458
459	use super::*;
460
461	lazy_static! {
462		static ref USER_KEYPAIR: Keypair = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
463		static ref SERVER_KEYPAIR: Keypair = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
464	}
465
466	#[allow(unused)]
467	fn all_clause_tested(clause: VtxoClause) -> bool {
468		// NB: matcher to ensure all clauses are tested
469		match clause {
470			VtxoClause::DelayedSign(_) => true,
471			VtxoClause::TimelockSign(_) => true,
472			VtxoClause::DelayedTimelockSign(_) => true,
473			VtxoClause::HashDelaySign(_) => true,
474			VtxoClause::HashSign(_) => true,
475		}
476	}
477
478	fn transaction() -> Transaction {
479		let address = bitcoin::Address::from_str("tb1q00h5delzqxl7xae8ufmsegghcl4jwfvdnd8530")
480			.unwrap().assume_checked();
481
482		Transaction {
483			version: bitcoin::transaction::Version(3),
484			lock_time: bitcoin::absolute::LockTime::ZERO,
485			input: vec![],
486			output: vec![TxOut {
487				script_pubkey: address.script_pubkey(),
488				value: Amount::from_sat(900_000),
489			}, fee::fee_anchor()]
490		}
491	}
492
493	fn taproot_material(clause_spk: ScriptBuf) -> (TaprootSpendInfo, ControlBlock) {
494		let user_pubkey = USER_KEYPAIR.public_key();
495		let server_pubkey = SERVER_KEYPAIR.public_key();
496
497		let combined_pk = musig::combine_keys([user_pubkey, server_pubkey])
498			.x_only_public_key().0;
499		let taproot = taproot::TaprootBuilder::new()
500			.add_leaf(0, clause_spk.clone()).unwrap()
501			.finalize(&SECP, combined_pk).unwrap();
502
503		let cb = taproot
504			.control_block(&(clause_spk.clone(), taproot::LeafVersion::TapScript))
505			.expect("script is in taproot");
506
507		(taproot, cb)
508	}
509
510	fn signature(tx: &Transaction, input: &TxOut, clause_spk: ScriptBuf) -> schnorr::Signature {
511		let leaf_hash = taproot::TapLeafHash::from_script(
512			&clause_spk,
513			taproot::LeafVersion::TapScript,
514		);
515
516		let mut shc = sighash::SighashCache::new(tx);
517		let sighash = shc.taproot_script_spend_signature_hash(
518			0, &sighash::Prevouts::All(&[input.clone()]), leaf_hash, sighash::TapSighashType::Default,
519		).expect("all prevouts provided");
520
521		SECP.sign_schnorr(&sighash.into(), &*USER_KEYPAIR)
522	}
523
524	#[test]
525	fn test_delayed_sign_clause() {
526		let clause = DelayedSignClause {
527			pubkey: USER_KEYPAIR.public_key(),
528			block_delta: 100,
529		};
530
531		// We compute taproot material for the clause
532		let (taproot, cb) = taproot_material(clause.tapscript());
533		let tx_in = TxOut {
534			script_pubkey: taproot.script_pubkey(),
535			value: Amount::from_sat(1_000_000),
536		};
537
538		// We build transaction spending input containing clause
539		let mut tx = transaction();
540		tx.input.push(TxIn {
541			previous_output: OutPoint::new(Txid::all_zeros(), 0),
542			script_sig: ScriptBuf::default(),
543			sequence: clause.sequence(),
544			witness: Witness::new(),
545		});
546
547		// We compute the signature for the transaction
548		let signature = signature(&tx, &tx_in, clause.tapscript());
549		tx.input[0].witness = clause.witness(&signature, &cb);
550
551		// We verify the transaction
552		verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
553	}
554
555	#[test]
556	fn test_timelock_sign_clause() {
557		let clause = TimelockSignClause {
558			pubkey: USER_KEYPAIR.public_key(),
559			timelock_height: 100,
560		};
561
562		// We compute taproot material for the clause
563		let (taproot, cb) = taproot_material(clause.tapscript());
564		let tx_in = TxOut {
565			script_pubkey: taproot.script_pubkey(),
566			value: Amount::from_sat(1_000_000),
567		};
568
569		// We build transaction spending input containing clause
570		let mut tx = transaction();
571		tx.lock_time = clause.locktime();
572		tx.input.push(TxIn {
573			previous_output: OutPoint::new(Txid::all_zeros(), 0),
574			script_sig: ScriptBuf::default(),
575			sequence: Sequence::ZERO,
576			witness: Witness::new(),
577		});
578
579		// We compute the signature for the transaction
580		let signature = signature(&tx, &tx_in, clause.tapscript());
581		tx.input[0].witness = clause.witness(&signature, &cb);
582
583		// We verify the transaction
584		verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
585	}
586
587	#[test]
588	fn test_delayed_timelock_clause() {
589		let clause = DelayedTimelockSignClause {
590			pubkey: USER_KEYPAIR.public_key(),
591			timelock_height: 100,
592			block_delta: 24,
593		};
594
595		// We compute taproot material for the clause
596		let (taproot, cb) = taproot_material(clause.tapscript());
597		let tx_in = TxOut {
598			script_pubkey: taproot.script_pubkey(),
599			value: Amount::from_sat(1_000_000),
600		};
601
602		// We build transaction spending input containing clause
603		let mut tx = transaction();
604		tx.lock_time = clause.locktime();
605		tx.input.push(TxIn {
606			previous_output: OutPoint::new(Txid::all_zeros(), 0),
607			script_sig: ScriptBuf::default(),
608			sequence: clause.sequence(),
609			witness: Witness::new(),
610		});
611
612		// We compute the signature for the transaction
613		let signature = signature(&tx, &tx_in, clause.tapscript());
614		tx.input[0].witness = clause.witness(&signature, &cb);
615
616		// We verify the transaction
617		verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
618	}
619
620	#[test]
621	fn test_hash_delay_clause() {
622		let preimage = [0; 32];
623
624		let clause = HashDelaySignClause {
625			pubkey: USER_KEYPAIR.public_key(),
626			hash: sha256::Hash::hash(&preimage),
627			block_delta: 24,
628		};
629
630		// We compute taproot material for the clause
631		let (taproot, cb) = taproot_material(clause.tapscript());
632		let tx_in = TxOut {
633			script_pubkey: taproot.script_pubkey(),
634			value: Amount::from_sat(1_000_000),
635		};
636
637		// We build transaction spending input containing clause
638		let mut tx = transaction();
639		tx.input.push(TxIn {
640			previous_output: OutPoint::new(Txid::all_zeros(), 0),
641			script_sig: ScriptBuf::default(),
642			sequence: clause.sequence(),
643			witness: Witness::new(),
644		});
645
646		// We compute the signature for the transaction
647		let signature = signature(&tx, &tx_in, clause.tapscript());
648		tx.input[0].witness = clause.witness(&(signature, preimage), &cb);
649
650		// We verify the transaction
651		verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
652	}
653
654	#[test]
655	fn test_extract_preimage_from_witness() {
656		let preimage_bytes = [42u8; 32];
657		let payment_hash = sha256::Hash::hash(&preimage_bytes);
658
659		let clause = HashDelaySignClause {
660			pubkey: USER_KEYPAIR.public_key(),
661			hash: payment_hash,
662			block_delta: 24,
663		};
664
665		// Build a valid witness via the clause
666		let (taproot, cb) = taproot_material(clause.tapscript());
667		let tx_in = TxOut {
668			script_pubkey: taproot.script_pubkey(),
669			value: Amount::from_sat(1_000_000),
670		};
671
672		let mut tx = transaction();
673		tx.input.push(TxIn {
674			previous_output: OutPoint::new(Txid::all_zeros(), 0),
675			script_sig: ScriptBuf::default(),
676			sequence: clause.sequence(),
677			witness: Witness::new(),
678		});
679
680		let sig = signature(&tx, &tx_in, clause.tapscript());
681		let witness = clause.witness(&(sig, preimage_bytes), &cb);
682
683		// Extract should succeed with correct payment hash
684		let extracted = HashDelaySignClause::extract_preimage_from_witness(
685			&witness,
686			payment_hash.into(),
687		);
688		assert!(extracted.is_some());
689		assert_eq!(extracted.unwrap().as_ref(), &preimage_bytes);
690
691		// Extract should fail with wrong payment hash
692		let wrong_hash = sha256::Hash::hash(&[0u8; 32]);
693		let extracted = HashDelaySignClause::extract_preimage_from_witness(
694			&witness,
695			wrong_hash.into(),
696		);
697		assert!(extracted.is_none());
698
699		// Extract should fail with wrong witness length
700		let short_witness = Witness::from_slice(&[&sig[..], &preimage_bytes[..]]);
701		let extracted = HashDelaySignClause::extract_preimage_from_witness(
702			&short_witness,
703			payment_hash.into(),
704		);
705		assert!(extracted.is_none());
706	}
707
708	#[test]
709	fn test_hash_sign_clause() {
710		let preimage = [0u8; 32];
711		let hash = sha256::Hash::hash(&preimage);
712
713		// HashSignClause uses an x-only aggregate public key
714		let agg_pk = musig::combine_keys([USER_KEYPAIR.public_key(), SERVER_KEYPAIR.public_key()]);
715
716		let clause = HashSignClause {
717			pubkey: agg_pk,
718			hash,
719		};
720
721		// We compute taproot material for the clause
722		let (taproot, cb) = taproot_material(clause.tapscript());
723		let tx_in = TxOut {
724			script_pubkey: taproot.script_pubkey(),
725			value: Amount::from_sat(1_000_000),
726		};
727
728		// We build transaction spending input containing clause
729		let mut tx = transaction();
730		tx.input.push(TxIn {
731			previous_output: OutPoint::new(Txid::all_zeros(), 0),
732			script_sig: ScriptBuf::default(),
733			sequence: Sequence::ZERO, // HashSignClause has no relative timelock
734			witness: Witness::new(),
735		});
736
737		// For HashSignClause, we need a MuSig signature from both parties
738		let leaf_hash = taproot::TapLeafHash::from_script(
739			&clause.tapscript(),
740			taproot::LeafVersion::TapScript,
741		);
742
743		let mut shc = sighash::SighashCache::new(&tx);
744		let sighash = shc.taproot_script_spend_signature_hash(
745			0, &sighash::Prevouts::All(&[tx_in.clone()]), leaf_hash, sighash::TapSighashType::Default,
746		).expect("all prevouts provided");
747
748		// Create MuSig signature
749		let (user_sec_nonce, user_pub_nonce) = musig::nonce_pair(&*USER_KEYPAIR);
750		let (server_pub_nonce, server_part_sig) = musig::deterministic_partial_sign(
751			&*SERVER_KEYPAIR,
752			[USER_KEYPAIR.public_key()],
753			&[&user_pub_nonce],
754			sighash.to_byte_array(),
755			None,
756		);
757		let agg_nonce = musig::nonce_agg(&[&user_pub_nonce, &server_pub_nonce]);
758
759		let (_user_part_sig, final_sig) = musig::partial_sign(
760			[USER_KEYPAIR.public_key(), SERVER_KEYPAIR.public_key()],
761			agg_nonce,
762			&*USER_KEYPAIR,
763			user_sec_nonce,
764			sighash.to_byte_array(),
765			None,
766			Some(&[&server_part_sig]),
767		);
768		let final_sig = final_sig.expect("should have final signature");
769
770		tx.input[0].witness = clause.witness(&(final_sig, preimage), &cb);
771
772		// We verify the transaction
773		verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
774	}
775}