1
2pub mod clause;
3pub mod signing;
4
5use std::fmt;
6use std::str::FromStr;
7
8use bitcoin::{Amount, ScriptBuf, TxOut, taproot};
9use bitcoin::secp256k1::PublicKey;
10
11use bitcoin_ext::{BlockDelta, BlockHeight, TaprootSpendInfoExt};
12
13use crate::{SECP, musig };
14use crate::lightning::PaymentHash;
15use crate::vtxo::TapScriptClause;
16use crate::vtxo::policy::clause::{
17 DelayedSignClause, DelayedTimelockSignClause, HashDelaySignClause, TimelockSignClause,
18 VtxoClause,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub enum VtxoPolicyKind {
24 Pubkey,
26 Checkpoint,
29 ServerHtlcSend,
31 ServerHtlcRecv,
33}
34
35impl fmt::Display for VtxoPolicyKind {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match self {
38 Self::Pubkey => f.write_str("pubkey"),
39 Self::Checkpoint => f.write_str("checkpoint"),
40 Self::ServerHtlcSend => f.write_str("server-htlc-send"),
41 Self::ServerHtlcRecv => f.write_str("server-htlc-receive"),
42 }
43 }
44}
45
46impl FromStr for VtxoPolicyKind {
47 type Err = String;
48 fn from_str(s: &str) -> Result<Self, Self::Err> {
49 Ok(match s {
50 "pubkey" => Self::Pubkey,
51 "checkpoint" => Self::Checkpoint,
52 "server-htlc-send" => Self::ServerHtlcSend,
53 "server-htlc-receive" => Self::ServerHtlcRecv,
54 _ => return Err(format!("unknown VtxoPolicyKind: {}", s)),
55 })
56 }
57}
58
59impl serde::Serialize for VtxoPolicyKind {
60 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
61 s.collect_str(self)
62 }
63}
64
65impl<'de> serde::Deserialize<'de> for VtxoPolicyKind {
66 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
67 struct Visitor;
68 impl<'de> serde::de::Visitor<'de> for Visitor {
69 type Value = VtxoPolicyKind;
70 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "a VtxoPolicyKind")
72 }
73 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
74 VtxoPolicyKind::from_str(v).map_err(serde::de::Error::custom)
75 }
76 }
77 d.deserialize_str(Visitor)
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
90pub struct PubkeyVtxoPolicy {
91 pub user_pubkey: PublicKey,
92}
93
94impl From<PubkeyVtxoPolicy> for VtxoPolicy {
95 fn from(policy: PubkeyVtxoPolicy) -> Self {
96 Self::Pubkey(policy)
97 }
98}
99
100impl PubkeyVtxoPolicy {
101 pub fn user_pubkey_claim_clause(&self, exit_delta: BlockDelta) -> DelayedSignClause {
103 DelayedSignClause { pubkey: self.user_pubkey, block_delta: exit_delta }
104 }
105
106 pub fn clauses(&self, exit_delta: BlockDelta) -> Vec<VtxoClause> {
107 vec![self.user_pubkey_claim_clause(exit_delta).into()]
108 }
109
110 pub fn taproot(
111 &self,
112 server_pubkey: PublicKey,
113 exit_delta: BlockDelta,
114 ) -> taproot::TaprootSpendInfo {
115 let combined_pk = musig::combine_keys([self.user_pubkey, server_pubkey]);
116
117 let user_pubkey_claim_clause = self.user_pubkey_claim_clause(exit_delta);
118 taproot::TaprootBuilder::new()
119 .add_leaf(0, user_pubkey_claim_clause.tapscript()).unwrap()
120 .finalize(&SECP, combined_pk).unwrap()
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
133pub struct CheckpointVtxoPolicy {
134 pub user_pubkey: PublicKey,
135}
136
137impl From<CheckpointVtxoPolicy> for VtxoPolicy {
138 fn from(policy: CheckpointVtxoPolicy) -> Self {
139 Self::Checkpoint(policy)
140 }
141}
142
143impl CheckpointVtxoPolicy {
144 pub fn server_sweeping_clause(
146 &self,
147 expiry_height: BlockHeight,
148 server_pubkey: PublicKey,
149 ) -> TimelockSignClause {
150 TimelockSignClause { pubkey: server_pubkey, timelock_height: expiry_height }
151 }
152
153 pub fn clauses(
154 &self,
155 expiry_height: BlockHeight,
156 server_pubkey: PublicKey,
157 ) -> Vec<VtxoClause> {
158 vec![self.server_sweeping_clause(expiry_height, server_pubkey).into()]
159 }
160
161 pub fn taproot(
162 &self,
163 server_pubkey: PublicKey,
164 expiry_height: BlockHeight,
165 ) -> taproot::TaprootSpendInfo {
166 let combined_pk = musig::combine_keys([self.user_pubkey, server_pubkey]);
167 let server_sweeping_clause = self.server_sweeping_clause(expiry_height, server_pubkey);
168
169 taproot::TaprootBuilder::new()
170 .add_leaf(0, server_sweeping_clause.tapscript()).unwrap()
171 .finalize(&SECP, combined_pk).unwrap()
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
192pub struct ServerHtlcSendVtxoPolicy {
193 pub user_pubkey: PublicKey,
194 pub payment_hash: PaymentHash,
195 pub htlc_expiry: BlockHeight,
196}
197
198impl From<ServerHtlcSendVtxoPolicy> for VtxoPolicy {
199 fn from(policy: ServerHtlcSendVtxoPolicy) -> Self {
200 Self::ServerHtlcSend(policy)
201 }
202}
203
204impl ServerHtlcSendVtxoPolicy {
205 pub fn server_reveals_preimage_clause(
209 &self,
210 server_pubkey: PublicKey,
211 exit_delta: BlockDelta,
212 ) -> HashDelaySignClause {
213 HashDelaySignClause {
214 pubkey: server_pubkey,
215 payment_hash: self.payment_hash,
216 block_delta: exit_delta
217 }
218 }
219
220 pub fn user_claim_after_expiry_clause(
226 &self,
227 exit_delta: BlockDelta,
228 ) -> DelayedTimelockSignClause {
229 DelayedTimelockSignClause {
230 pubkey: self.user_pubkey,
231 timelock_height: self.htlc_expiry,
232 block_delta: 2 * exit_delta
233 }
234 }
235
236
237 pub fn clauses(&self, exit_delta: BlockDelta, server_pubkey: PublicKey) -> Vec<VtxoClause> {
238 vec![
239 self.server_reveals_preimage_clause(server_pubkey, exit_delta).into(),
240 self.user_claim_after_expiry_clause(exit_delta).into(),
241 ]
242 }
243
244 pub fn taproot(&self, server_pubkey: PublicKey, exit_delta: BlockDelta) -> taproot::TaprootSpendInfo {
245 let server_reveals_preimage_clause = self.server_reveals_preimage_clause(server_pubkey, exit_delta);
246 let user_claim_after_expiry_clause = self.user_claim_after_expiry_clause(exit_delta);
247
248 let combined_pk = musig::combine_keys([self.user_pubkey, server_pubkey]);
249 bitcoin::taproot::TaprootBuilder::new()
250 .add_leaf(1, server_reveals_preimage_clause.tapscript()).unwrap()
251 .add_leaf(1, user_claim_after_expiry_clause.tapscript()).unwrap()
252 .finalize(&SECP, combined_pk).unwrap()
253 }
254}
255
256
257#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
274pub struct ServerHtlcRecvVtxoPolicy {
275 pub user_pubkey: PublicKey,
276 pub payment_hash: PaymentHash,
277 pub htlc_expiry_delta: BlockDelta,
278 pub htlc_expiry: BlockHeight,
279}
280
281impl ServerHtlcRecvVtxoPolicy {
282 pub fn user_reveals_preimage_clause(&self, exit_delta: BlockDelta) -> HashDelaySignClause {
287 HashDelaySignClause {
288 pubkey: self.user_pubkey,
289 payment_hash: self.payment_hash,
290 block_delta: self.htlc_expiry_delta + exit_delta
291 }
292 }
293
294 pub fn server_claim_after_expiry_clause(
298 &self,
299 server_pubkey: PublicKey,
300 exit_delta: BlockDelta,
301 ) -> DelayedTimelockSignClause {
302 DelayedTimelockSignClause {
303 pubkey: server_pubkey,
304 timelock_height: self.htlc_expiry,
305 block_delta: exit_delta
306 }
307 }
308
309 pub fn clauses(&self, exit_delta: BlockDelta, server_pubkey: PublicKey) -> Vec<VtxoClause> {
310 vec![
311 self.user_reveals_preimage_clause(exit_delta).into(),
312 self.server_claim_after_expiry_clause(server_pubkey, exit_delta).into(),
313 ]
314 }
315
316 pub fn taproot(&self, server_pubkey: PublicKey, exit_delta: BlockDelta) -> taproot::TaprootSpendInfo {
317 let server_claim_after_expiry_clause = self.server_claim_after_expiry_clause(server_pubkey, exit_delta);
318 let user_reveals_preimage_clause = self.user_reveals_preimage_clause(exit_delta);
319
320 let combined_pk = musig::combine_keys([self.user_pubkey, server_pubkey]);
321 bitcoin::taproot::TaprootBuilder::new()
322 .add_leaf(1, server_claim_after_expiry_clause.tapscript()).unwrap()
323 .add_leaf(1, user_reveals_preimage_clause.tapscript()).unwrap()
324 .finalize(&SECP, combined_pk).unwrap()
325 }
326}
327
328impl From<ServerHtlcRecvVtxoPolicy> for VtxoPolicy {
329 fn from(policy: ServerHtlcRecvVtxoPolicy) -> Self {
330 Self::ServerHtlcRecv(policy)
331 }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
336pub enum VtxoPolicy {
337 Pubkey(PubkeyVtxoPolicy),
345 Checkpoint(CheckpointVtxoPolicy),
347 ServerHtlcSend(ServerHtlcSendVtxoPolicy),
349 ServerHtlcRecv(ServerHtlcRecvVtxoPolicy),
351}
352
353impl VtxoPolicy {
354 pub fn new_pubkey(user_pubkey: PublicKey) -> Self {
355 Self::Pubkey(PubkeyVtxoPolicy { user_pubkey })
356 }
357
358 pub fn new_checkpoint(user_pubkey: PublicKey) -> Self {
359 Self::Checkpoint(CheckpointVtxoPolicy { user_pubkey })
360 }
361
362 pub fn new_server_htlc_send(
363 user_pubkey: PublicKey,
364 payment_hash: PaymentHash,
365 htlc_expiry: BlockHeight,
366 ) -> Self {
367 Self::ServerHtlcSend(ServerHtlcSendVtxoPolicy { user_pubkey, payment_hash, htlc_expiry })
368 }
369
370 pub fn new_server_htlc_recv(
379 user_pubkey: PublicKey,
380 payment_hash: PaymentHash,
381 htlc_expiry: BlockHeight,
382 htlc_expiry_delta: BlockDelta,
383 ) -> Self {
384 Self::ServerHtlcRecv(ServerHtlcRecvVtxoPolicy {
385 user_pubkey, payment_hash, htlc_expiry, htlc_expiry_delta,
386 })
387 }
388
389 pub fn as_pubkey(&self) -> Option<&PubkeyVtxoPolicy> {
390 match self {
391 Self::Pubkey(v) => Some(v),
392 _ => None,
393 }
394 }
395
396 pub fn as_server_htlc_send(&self) -> Option<&ServerHtlcSendVtxoPolicy> {
397 match self {
398 Self::ServerHtlcSend(v) => Some(v),
399 _ => None,
400 }
401 }
402
403 pub fn as_server_htlc_recv(&self) -> Option<&ServerHtlcRecvVtxoPolicy> {
404 match self {
405 Self::ServerHtlcRecv(v) => Some(v),
406 _ => None,
407 }
408 }
409
410 pub fn policy_type(&self) -> VtxoPolicyKind {
412 match self {
413 Self::Pubkey { .. } => VtxoPolicyKind::Pubkey,
414 Self::Checkpoint { .. } => VtxoPolicyKind::Checkpoint,
415 Self::ServerHtlcSend { .. } => VtxoPolicyKind::ServerHtlcSend,
416 Self::ServerHtlcRecv { .. } => VtxoPolicyKind::ServerHtlcRecv,
417 }
418 }
419
420 pub fn is_arkoor_compatible(&self) -> bool {
422 match self {
423 Self::Pubkey { .. } => true,
424 Self::Checkpoint { .. } => true,
425 Self::ServerHtlcSend { .. } => false,
426 Self::ServerHtlcRecv { .. } => false,
427 }
428 }
429
430 pub fn arkoor_pubkey(&self) -> Option<PublicKey> {
434 match self {
435 Self::Pubkey(PubkeyVtxoPolicy { user_pubkey }) => Some(*user_pubkey),
436 Self::Checkpoint(CheckpointVtxoPolicy { user_pubkey }) => Some(*user_pubkey),
437 Self::ServerHtlcSend(ServerHtlcSendVtxoPolicy { user_pubkey, .. }) => Some(*user_pubkey),
438 Self::ServerHtlcRecv(ServerHtlcRecvVtxoPolicy { user_pubkey, .. }) => Some(*user_pubkey),
439 }
440 }
441
442 pub fn user_pubkey(&self) -> PublicKey {
444 match self {
445 Self::Pubkey(PubkeyVtxoPolicy { user_pubkey }) => *user_pubkey,
446 Self::Checkpoint(CheckpointVtxoPolicy { user_pubkey }) => *user_pubkey,
447 Self::ServerHtlcSend(ServerHtlcSendVtxoPolicy { user_pubkey, .. }) => *user_pubkey,
448 Self::ServerHtlcRecv(ServerHtlcRecvVtxoPolicy { user_pubkey, .. }) => *user_pubkey,
449 }
450 }
451
452 pub fn taproot(
453 &self,
454 server_pubkey: PublicKey,
455 exit_delta: BlockDelta,
456 expiry_height: BlockHeight,
457 ) -> taproot::TaprootSpendInfo {
458 match self {
459 Self::Pubkey(policy) => policy.taproot(server_pubkey, exit_delta),
460 Self::Checkpoint(policy) => policy.taproot(server_pubkey, expiry_height),
461 Self::ServerHtlcSend(policy) => policy.taproot(server_pubkey, exit_delta),
462 Self::ServerHtlcRecv(policy) => policy.taproot(server_pubkey, exit_delta),
463 }
464 }
465
466 pub(crate) fn script_pubkey(
467 &self,
468 server_pubkey: PublicKey,
469 exit_delta: BlockDelta,
470 expiry_height: BlockHeight,
471 ) -> ScriptBuf {
472 self.taproot(server_pubkey, exit_delta, expiry_height).script_pubkey()
473 }
474
475 pub(crate) fn txout(
476 &self,
477 amount: Amount,
478 server_pubkey: PublicKey,
479 exit_delta: BlockDelta,
480 expiry_height: BlockHeight,
481 ) -> TxOut {
482 TxOut {
483 value: amount,
484 script_pubkey: self.script_pubkey(server_pubkey, exit_delta, expiry_height),
485 }
486 }
487
488 pub fn clauses(
489 &self,
490 exit_delta: u16,
491 expiry_height: BlockHeight,
492 server_pubkey: PublicKey,
493 ) -> Vec<VtxoClause> {
494 match self {
495 Self::Pubkey(policy) => policy.clauses(exit_delta),
496 Self::Checkpoint(policy) => policy.clauses(expiry_height, server_pubkey),
497 Self::ServerHtlcSend(policy) => policy.clauses(exit_delta, server_pubkey),
498 Self::ServerHtlcRecv(policy) => policy.clauses(exit_delta, server_pubkey),
499 }
500 }
501}