1
2use bitcoin::absolute::LockTime;
3use bitcoin::key::constants::SCHNORR_SIGNATURE_SIZE;
4use bitcoin::secp256k1::schnorr;
5use bitcoin::taproot::{self, ControlBlock};
6use bitcoin::{Sequence, VarInt, Witness};
7use bitcoin::{secp256k1::PublicKey, ScriptBuf};
8
9use bitcoin_ext::{BlockDelta, BlockHeight};
10
11use crate::lightning::{PREIMAGE_SIZE, PaymentHash, Preimage};
12use crate::{Vtxo, scripts};
13
14pub trait TapScriptClause: Sized + Clone {
19 type WitnessData;
21
22 fn tapscript(&self) -> ScriptBuf;
24
25 fn control_block(&self, vtxo: &Vtxo) -> ControlBlock {
27 vtxo.output_taproot()
28 .control_block(&(self.tapscript(), taproot::LeafVersion::TapScript))
29 .expect("clause is not in taproot tree")
30 }
31
32 fn witness_size(&self, vtxo: &Vtxo) -> usize;
34
35 fn witness(
37 &self,
38 data: &Self::WitnessData,
39 control_block: &ControlBlock,
40 ) -> Witness;
41}
42
43#[derive(Debug, Clone)]
46pub struct DelayedSignClause {
47 pub pubkey: PublicKey,
48 pub block_delta: BlockDelta,
49}
50
51impl DelayedSignClause {
52 pub fn sequence(&self) -> Sequence {
54 Sequence::from_height(self.block_delta)
55 }
56}
57
58impl TapScriptClause for DelayedSignClause {
59 type WitnessData = schnorr::Signature;
60
61 fn tapscript(&self) -> ScriptBuf {
62 assert_ne!(self.block_delta, 0, "block delta must be non-zero");
63 scripts::delayed_sign(self.block_delta, self.pubkey.x_only_public_key().0)
64 }
65
66 fn witness(
67 &self,
68 signature: &Self::WitnessData,
69 control_block: &ControlBlock,
70 ) -> Witness {
71 Witness::from_slice(&[
72 &signature[..],
73 self.tapscript().as_bytes(),
74 &control_block.serialize()[..],
75 ])
76 }
77
78
79 fn witness_size(&self, vtxo: &Vtxo) -> usize {
80 let cb_size = self.control_block(vtxo).size();
81 let tapscript_size = self.tapscript().as_bytes().len();
82
83 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
91}
92
93impl Into<VtxoClause> for DelayedSignClause {
94 fn into(self) -> VtxoClause {
95 VtxoClause::DelayedSign(self)
96 }
97}
98
99#[derive(Debug, Clone)]
102pub struct TimelockSignClause {
103 pub pubkey: PublicKey,
104 pub timelock_height: BlockHeight,
105}
106
107impl TimelockSignClause {
108 pub fn locktime(&self) -> LockTime {
110 LockTime::from_height(self.timelock_height).expect("timelock height is valid")
111 }
112}
113
114impl TapScriptClause for TimelockSignClause {
115 type WitnessData = schnorr::Signature;
116
117 fn tapscript(&self) -> ScriptBuf {
118 scripts::timelock_sign(self.timelock_height, self.pubkey.x_only_public_key().0)
119 }
120
121 fn witness(
122 &self,
123 signature: &Self::WitnessData,
124 control_block: &ControlBlock,
125 ) -> Witness {
126 Witness::from_slice(&[
127 &signature[..],
128 self.tapscript().as_bytes(),
129 &control_block.serialize()[..],
130 ])
131 }
132
133 fn witness_size(&self, vtxo: &Vtxo) -> usize {
134 let cb_size = self.control_block(vtxo).size();
135 let tapscript_size = self.tapscript().as_bytes().len();
136
137 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
145}
146
147impl Into<VtxoClause> for TimelockSignClause {
148 fn into(self) -> VtxoClause {
149 VtxoClause::TimelockSign(self)
150 }
151}
152
153#[derive(Debug, Clone)]
156pub struct DelayedTimelockSignClause {
157 pub pubkey: PublicKey,
158 pub timelock_height: BlockHeight,
159 pub block_delta: BlockDelta,
160}
161
162impl DelayedTimelockSignClause {
163 pub fn sequence(&self) -> Sequence {
165 Sequence::from_height(self.block_delta)
166 }
167
168 pub fn locktime(&self) -> LockTime {
170 LockTime::from_height(self.timelock_height).expect("timelock height is valid")
171 }
172}
173
174impl TapScriptClause for DelayedTimelockSignClause {
175 type WitnessData = schnorr::Signature;
176
177 fn tapscript(&self) -> ScriptBuf {
178 assert_ne!(self.block_delta, 0, "block delta must be non-zero");
179 scripts::delay_timelock_sign(
180 self.block_delta,
181 self.timelock_height,
182 self.pubkey.x_only_public_key().0,
183 )
184 }
185
186 fn witness(
187 &self,
188 signature: &Self::WitnessData,
189 control_block: &ControlBlock,
190 ) -> Witness {
191 Witness::from_slice(&[
192 &signature[..],
193 self.tapscript().as_bytes(),
194 &control_block.serialize()[..],
195 ])
196 }
197
198 fn witness_size(&self, vtxo: &Vtxo) -> usize {
199 let cb_size = self.control_block(vtxo).size();
200 let tapscript_size = self.tapscript().as_bytes().len();
201
202 1 + 1 + SCHNORR_SIGNATURE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
210}
211
212impl Into<VtxoClause> for DelayedTimelockSignClause {
213 fn into(self) -> VtxoClause {
214 VtxoClause::DelayedTimelockSign(self)
215 }
216}
217
218#[derive(Debug, Clone)]
221pub struct HashDelaySignClause {
222 pub pubkey: PublicKey,
223 pub payment_hash: PaymentHash,
224 pub block_delta: BlockDelta,
225}
226
227impl HashDelaySignClause {
228 pub fn sequence(&self) -> Sequence {
230 assert_ne!(self.block_delta, 0, "block delta must be non-zero");
231 Sequence::from_height(self.block_delta)
232 }
233}
234
235impl TapScriptClause for HashDelaySignClause {
236 type WitnessData = (schnorr::Signature, Preimage);
237
238 fn tapscript(&self) -> ScriptBuf {
239 assert_ne!(self.block_delta, 0, "block delta must be non-zero");
240 scripts::hash_delay_sign(
241 self.payment_hash.to_sha256_hash(),
242 self.block_delta,
243 self.pubkey.x_only_public_key().0,
244 )
245 }
246
247 fn witness(
248 &self,
249 data: &Self::WitnessData,
250 control_block: &ControlBlock,
251 ) -> Witness {
252 let (signature, preimage) = data;
253 Witness::from_slice(&[
254 &signature[..],
255 &preimage.as_ref()[..],
256 self.tapscript().as_bytes(),
257 &control_block.serialize()[..],
258 ])
259 }
260
261 fn witness_size(&self, vtxo: &Vtxo) -> usize {
262 let cb_size = self.control_block(vtxo).size();
263 let tapscript_size = self.tapscript().as_bytes().len();
264
265 1 + 1 + SCHNORR_SIGNATURE_SIZE + 1 + PREIMAGE_SIZE + VarInt::from(tapscript_size).size() + tapscript_size + VarInt::from(cb_size).size() + cb_size }
275}
276
277impl Into<VtxoClause> for HashDelaySignClause {
278 fn into(self) -> VtxoClause {
279 VtxoClause::HashDelaySign(self)
280 }
281}
282
283#[derive(Debug, Clone)]
284pub enum VtxoClause {
285 DelayedSign(DelayedSignClause),
286 TimelockSign(TimelockSignClause),
287 DelayedTimelockSign(DelayedTimelockSignClause),
288 HashDelaySign(HashDelaySignClause),
289}
290
291impl VtxoClause {
292 pub fn pubkey(&self) -> PublicKey {
294 match self {
295 Self::DelayedSign(c) => c.pubkey,
296 Self::TimelockSign(c) => c.pubkey,
297 Self::DelayedTimelockSign(c) => c.pubkey,
298 Self::HashDelaySign(c) => c.pubkey,
299 }
300 }
301
302 pub fn tapscript(&self) -> ScriptBuf {
304 match self {
305 Self::DelayedSign(c) => c.tapscript(),
306 Self::TimelockSign(c) => c.tapscript(),
307 Self::DelayedTimelockSign(c) => c.tapscript(),
308 Self::HashDelaySign(c) => c.tapscript(),
309 }
310 }
311
312 pub fn sequence(&self) -> Option<Sequence> {
314 match self {
315 Self::DelayedSign(c) => Some(c.sequence()),
316 Self::TimelockSign(_) => None,
317 Self::DelayedTimelockSign(c) => Some(c.sequence()),
318 Self::HashDelaySign(c) => Some(c.sequence()),
319 }
320 }
321
322 pub fn control_block(&self, vtxo: &Vtxo) -> ControlBlock {
324 match self {
325 Self::DelayedSign(c) => c.control_block(vtxo),
326 Self::TimelockSign(c) => c.control_block(vtxo),
327 Self::DelayedTimelockSign(c) => c.control_block(vtxo),
328 Self::HashDelaySign(c) => c.control_block(vtxo),
329 }
330 }
331
332 pub fn witness_size(&self, vtxo: &Vtxo) -> usize {
334 match self {
335 Self::DelayedSign(c) => c.witness_size(vtxo),
336 Self::TimelockSign(c) => c.witness_size(vtxo),
337 Self::DelayedTimelockSign(c) => c.witness_size(vtxo),
338 Self::HashDelaySign(c) => c.witness_size(vtxo),
339 }
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use std::str::FromStr;
346
347 use bitcoin::taproot::TaprootSpendInfo;
348 use bitcoin::{Amount, OutPoint, Transaction, TxIn, TxOut, Txid, sighash};
349 use bitcoin::hashes::Hash;
350 use bitcoin::key::Keypair;
351 use bitcoin_ext::{TaprootSpendInfoExt, fee};
352
353 use crate::{SECP, musig};
354 use crate::test::verify_tx;
355
356 use super::*;
357
358 lazy_static! {
359 static ref USER_KEYPAIR: Keypair = Keypair::from_str("5255d132d6ec7d4fc2a41c8f0018bb14343489ddd0344025cc60c7aa2b3fda6a").unwrap();
360 static ref SERVER_KEYPAIR: Keypair = Keypair::from_str("1fb316e653eec61de11c6b794636d230379509389215df1ceb520b65313e5426").unwrap();
361 }
362
363 #[allow(unused)]
364 fn all_clause_tested(clause: VtxoClause) -> bool {
365 match clause {
367 VtxoClause::DelayedSign(_) => true,
368 VtxoClause::TimelockSign(_) => true,
369 VtxoClause::DelayedTimelockSign(_) => true,
370 VtxoClause::HashDelaySign(_) => true,
371 }
372 }
373
374 fn transaction() -> Transaction {
375 let address = bitcoin::Address::from_str("tb1q00h5delzqxl7xae8ufmsegghcl4jwfvdnd8530")
376 .unwrap().assume_checked();
377
378 Transaction {
379 version: bitcoin::transaction::Version(3),
380 lock_time: bitcoin::absolute::LockTime::ZERO,
381 input: vec![],
382 output: vec![TxOut {
383 script_pubkey: address.script_pubkey(),
384 value: Amount::from_sat(900_000),
385 }, fee::fee_anchor()]
386 }
387 }
388
389 fn taproot_material(clause_spk: ScriptBuf) -> (TaprootSpendInfo, ControlBlock) {
390 let user_pubkey = USER_KEYPAIR.public_key();
391 let server_pubkey = SERVER_KEYPAIR.public_key();
392
393 let combined_pk = musig::combine_keys([user_pubkey, server_pubkey]);
394 let taproot = taproot::TaprootBuilder::new()
395 .add_leaf(0, clause_spk.clone()).unwrap()
396 .finalize(&SECP, combined_pk).unwrap();
397
398 let cb = taproot
399 .control_block(&(clause_spk.clone(), taproot::LeafVersion::TapScript))
400 .expect("script is in taproot");
401
402 (taproot, cb)
403 }
404
405 fn signature(tx: &Transaction, input: &TxOut, clause_spk: ScriptBuf) -> schnorr::Signature {
406 let leaf_hash = taproot::TapLeafHash::from_script(
407 &clause_spk,
408 taproot::LeafVersion::TapScript,
409 );
410
411 let mut shc = sighash::SighashCache::new(tx);
412 let sighash = shc.taproot_script_spend_signature_hash(
413 0, &sighash::Prevouts::All(&[input.clone()]), leaf_hash, sighash::TapSighashType::Default,
414 ).expect("all prevouts provided");
415
416 SECP.sign_schnorr(&sighash.into(), &*USER_KEYPAIR)
417 }
418
419 #[test]
420 fn test_delayed_sign_clause() {
421 let clause = DelayedSignClause {
422 pubkey: USER_KEYPAIR.public_key(),
423 block_delta: 100,
424 };
425
426 let (taproot, cb) = taproot_material(clause.tapscript());
428 let tx_in = TxOut {
429 script_pubkey: taproot.script_pubkey(),
430 value: Amount::from_sat(1_000_000),
431 };
432
433 let mut tx = transaction();
435 tx.input.push(TxIn {
436 previous_output: OutPoint::new(Txid::all_zeros(), 0),
437 script_sig: ScriptBuf::default(),
438 sequence: clause.sequence(),
439 witness: Witness::new(),
440 });
441
442 let signature = signature(&tx, &tx_in, clause.tapscript());
444 tx.input[0].witness = clause.witness(&signature, &cb);
445
446 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
448 }
449
450 #[test]
451 fn test_timelock_sign_clause() {
452 let clause = TimelockSignClause {
453 pubkey: USER_KEYPAIR.public_key(),
454 timelock_height: 100,
455 };
456
457 let (taproot, cb) = taproot_material(clause.tapscript());
459 let tx_in = TxOut {
460 script_pubkey: taproot.script_pubkey(),
461 value: Amount::from_sat(1_000_000),
462 };
463
464 let mut tx = transaction();
466 tx.lock_time = clause.locktime();
467 tx.input.push(TxIn {
468 previous_output: OutPoint::new(Txid::all_zeros(), 0),
469 script_sig: ScriptBuf::default(),
470 sequence: Sequence::ZERO,
471 witness: Witness::new(),
472 });
473
474 let signature = signature(&tx, &tx_in, clause.tapscript());
476 tx.input[0].witness = clause.witness(&signature, &cb);
477
478 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
480 }
481
482 #[test]
483 fn test_delayed_timelock_clause() {
484 let clause = DelayedTimelockSignClause {
485 pubkey: USER_KEYPAIR.public_key(),
486 timelock_height: 100,
487 block_delta: 24,
488 };
489
490 let (taproot, cb) = taproot_material(clause.tapscript());
492 let tx_in = TxOut {
493 script_pubkey: taproot.script_pubkey(),
494 value: Amount::from_sat(1_000_000),
495 };
496
497 let mut tx = transaction();
499 tx.lock_time = clause.locktime();
500 tx.input.push(TxIn {
501 previous_output: OutPoint::new(Txid::all_zeros(), 0),
502 script_sig: ScriptBuf::default(),
503 sequence: clause.sequence(),
504 witness: Witness::new(),
505 });
506
507 let signature = signature(&tx, &tx_in, clause.tapscript());
509 tx.input[0].witness = clause.witness(&signature, &cb);
510
511 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
513 }
514
515 #[test]
516 fn test_hash_delay_clause() {
517 let preimage = Preimage::from_slice(&[0; 32]).unwrap();
518
519 let clause = HashDelaySignClause {
520 pubkey: USER_KEYPAIR.public_key(),
521 payment_hash: preimage.compute_payment_hash(),
522 block_delta: 24,
523 };
524
525 let (taproot, cb) = taproot_material(clause.tapscript());
527 let tx_in = TxOut {
528 script_pubkey: taproot.script_pubkey(),
529 value: Amount::from_sat(1_000_000),
530 };
531
532 let mut tx = transaction();
534 tx.input.push(TxIn {
535 previous_output: OutPoint::new(Txid::all_zeros(), 0),
536 script_sig: ScriptBuf::default(),
537 sequence: clause.sequence(),
538 witness: Witness::new(),
539 });
540
541 let signature = signature(&tx, &tx_in, clause.tapscript());
543 tx.input[0].witness = clause.witness(&(signature, preimage), &cb);
544
545 verify_tx(&[tx_in], 0, &tx).expect("transaction is invalid");
547 }
548}