lnp/channel/bolt/extensions/
htlc.rs

1// LNP/BP Core Library implementing LNPBP specifications & standards
2// Written in 2020-2022 by
3//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
4//
5// To the extent possible under law, the author(s) have dedicated all
6// copyright and related and neighboring rights to this software to
7// the public domain worldwide. This software is distributed without
8// any warranty.
9//
10// You should have received a copy of the MIT License
11// along with this software.
12// If not, see <https://opensource.org/licenses/MIT>.
13
14use std::collections::BTreeMap;
15
16use bitcoin::blockdata::opcodes::all::*;
17use bitcoin::blockdata::script;
18use bitcoin::secp256k1::PublicKey;
19use bitcoin::{OutPoint, Transaction, TxIn, TxOut};
20use bitcoin_scripts::hlc::{HashLock, HashPreimage};
21use bitcoin_scripts::{LockScript, PubkeyScript, WitnessScript};
22use lnp2p::bolt::{ChannelId, Messages};
23use p2p::bolt::ChannelType;
24use wallet::psbt;
25use wallet::psbt::{Psbt, PsbtVersion};
26
27use crate::channel::bolt::util::UpdateReq;
28use crate::channel::bolt::{BoltExt, ChannelState, Error, TxType};
29use crate::channel::tx_graph::TxGraph;
30use crate::{ChannelExtension, Extension};
31
32#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
33#[derive(StrictEncode, StrictDecode)]
34#[cfg_attr(
35    feature = "serde",
36    derive(Serialize, Deserialize),
37    serde(crate = "serde_crate")
38)]
39pub struct HtlcKnown {
40    pub amount: u64,
41    pub preimage: HashPreimage,
42    pub id: u64,
43    pub cltv_expiry: u32,
44}
45
46#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
47#[derive(StrictEncode, StrictDecode)]
48#[cfg_attr(
49    feature = "serde",
50    derive(Serialize, Deserialize),
51    serde(crate = "serde_crate")
52)]
53pub struct HtlcSecret {
54    pub amount: u64,
55    pub hashlock: HashLock,
56    pub id: u64,
57    pub cltv_expiry: u32,
58}
59
60#[derive(Getters, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
61#[derive(StrictEncode, StrictDecode)]
62pub struct Htlc {
63    /// Set if the feature `option_anchors_zero_fee_htlc_tx` was negotiated via
64    /// `channel_type`. Indicates that HTLC transactions will use zero fees and
65    /// will be pushed through an anchor transaction.
66    anchors_zero_fee_htlc_tx: bool,
67
68    // Sets of HTLC informations
69    offered_htlcs: BTreeMap<u64, HtlcSecret>,
70    received_htlcs: BTreeMap<u64, HtlcSecret>,
71    resolved_htlcs: BTreeMap<u64, HtlcKnown>,
72
73    // Commitment round specific information
74    to_self_delay: u16,
75    local_revocation_basepoint: PublicKey,
76    remote_revocation_basepoint: PublicKey,
77    local_basepoint: PublicKey,
78    remote_basepoint: PublicKey,
79    local_delayed_basepoint: PublicKey,
80
81    // Channel specific information
82    channel_id: ChannelId,
83
84    /// indicates the smallest value HTLC this node will accept.
85    htlc_minimum_msat: u64,
86    max_htlc_value_in_flight_msat: u64,
87    max_accepted_htlcs: u16,
88
89    next_recieved_htlc_id: u64,
90    next_offered_htlc_id: u64,
91}
92
93impl Default for Htlc {
94    fn default() -> Self {
95        Htlc {
96            anchors_zero_fee_htlc_tx: false,
97            offered_htlcs: empty!(),
98            received_htlcs: empty!(),
99            resolved_htlcs: empty!(),
100            to_self_delay: 0,
101            local_revocation_basepoint: dumb_pubkey!(),
102            remote_revocation_basepoint: dumb_pubkey!(),
103            local_basepoint: dumb_pubkey!(),
104            remote_basepoint: dumb_pubkey!(),
105            local_delayed_basepoint: dumb_pubkey!(),
106            channel_id: Default::default(),
107            htlc_minimum_msat: 0,
108            max_htlc_value_in_flight_msat: 0,
109            max_accepted_htlcs: 0,
110            next_recieved_htlc_id: 0,
111            next_offered_htlc_id: 0,
112        }
113    }
114}
115
116impl Htlc {
117    pub fn offer_htlc(
118        &mut self,
119        amount_msat: u64,
120        payment_hash: HashLock,
121        cltv_expiry: u32,
122    ) -> u64 {
123        let htlc_id = self.next_offered_htlc_id;
124        self.next_offered_htlc_id += 1;
125        self.offered_htlcs.insert(htlc_id, HtlcSecret {
126            amount: amount_msat,
127            hashlock: payment_hash,
128            id: htlc_id,
129            cltv_expiry,
130        });
131        htlc_id
132    }
133}
134
135impl Extension<BoltExt> for Htlc {
136    fn identity(&self) -> BoltExt {
137        BoltExt::Htlc
138    }
139
140    fn update_from_local(&mut self, _message: &()) -> Result<(), Error> {
141        // Nothing to do here so far
142        Ok(())
143    }
144
145    fn state_change(
146        &mut self,
147        request: &UpdateReq,
148        message: &mut Messages,
149    ) -> Result<(), Error> {
150        match (request, message) {
151            (
152                UpdateReq::PayBolt(_),
153                Messages::UpdateAddHtlc(update_add_htlc),
154            ) => {
155                let htlc_id = self.offer_htlc(
156                    update_add_htlc.amount_msat,
157                    update_add_htlc.payment_hash,
158                    update_add_htlc.cltv_expiry,
159                );
160                update_add_htlc.htlc_id = htlc_id;
161            }
162            (UpdateReq::PayBolt(_), _) => unreachable!(
163                "state change request must match provided LN P2P message"
164            ),
165        }
166        Ok(())
167    }
168
169    fn update_from_peer(&mut self, message: &Messages) -> Result<(), Error> {
170        match message {
171            Messages::OpenChannel(open_channel) => {
172                self.anchors_zero_fee_htlc_tx = open_channel
173                    .channel_type
174                    .map(ChannelType::has_anchors_zero_fee_htlc_tx)
175                    .unwrap_or_default();
176                self.htlc_minimum_msat = open_channel.htlc_minimum_msat;
177                self.max_accepted_htlcs = open_channel.max_accepted_htlcs;
178                self.max_htlc_value_in_flight_msat =
179                    open_channel.max_htlc_value_in_flight_msat;
180                self.remote_basepoint = open_channel.htlc_basepoint;
181                self.remote_revocation_basepoint =
182                    open_channel.revocation_basepoint;
183                self.local_delayed_basepoint =
184                    open_channel.delayed_payment_basepoint;
185                self.to_self_delay = open_channel.to_self_delay;
186            }
187            Messages::AcceptChannel(accept_channel) => {
188                self.anchors_zero_fee_htlc_tx = accept_channel
189                    .channel_type
190                    .map(ChannelType::has_anchors_zero_fee_htlc_tx)
191                    .unwrap_or_default();
192                self.htlc_minimum_msat = accept_channel.htlc_minimum_msat;
193                self.max_accepted_htlcs = accept_channel.max_accepted_htlcs;
194                self.max_htlc_value_in_flight_msat =
195                    accept_channel.max_htlc_value_in_flight_msat;
196                self.remote_basepoint = accept_channel.htlc_basepoint;
197                self.remote_revocation_basepoint =
198                    accept_channel.revocation_basepoint;
199                self.local_delayed_basepoint =
200                    accept_channel.delayed_payment_basepoint;
201                self.to_self_delay = accept_channel.to_self_delay;
202            }
203            Messages::UpdateAddHtlc(message) => {
204                // TODO: Filter messages by channel_id at channel level with
205                //       special API
206                if message.channel_id == self.channel_id {
207                    // Checks
208                    // 1. sending node should afford current fee rate after
209                    // adding this htlc to its local
210                    // commitment including anchor outputs
211                    // if opt in.
212                    if message.amount_msat == 0
213                        || message.amount_msat < self.htlc_minimum_msat
214                    {
215                        return Err(Error::Htlc(
216                            "amount_msat has to be greater than 0".to_string(),
217                        ));
218                    } else if self.received_htlcs.len()
219                        >= self.max_accepted_htlcs as usize
220                    {
221                        return Err(Error::Htlc(
222                            "max no. of HTLC limit exceeded".to_string(),
223                        ));
224                    } else if message.cltv_expiry > 500000000 {
225                        return Err(Error::Htlc(
226                            "cltv_expiry limit exceeded".to_string(),
227                        ));
228                    } else if message.amount_msat.leading_zeros() < 32 {
229                        return Err(Error::Htlc(
230                            "Leading zeros not satisfied for Bitcoin network"
231                                .to_string(),
232                        ));
233                    } else if message.htlc_id <= self.next_recieved_htlc_id {
234                        return Err(Error::Htlc(
235                            "HTLC id violation occurred".to_string(),
236                        )); // TODO handle reconnection
237                    } else {
238                        let htlc = HtlcSecret {
239                            amount: message.amount_msat,
240                            hashlock: message.payment_hash,
241                            id: message.htlc_id,
242                            cltv_expiry: message.cltv_expiry,
243                        };
244                        self.received_htlcs.insert(htlc.id, htlc);
245
246                        self.next_recieved_htlc_id += 1;
247                    }
248                } else {
249                    return Err(Error::Htlc(
250                        "Missmatched channel_id, bad remote node".to_string(),
251                    ));
252                }
253            }
254            Messages::UpdateFulfillHtlc(message) => {
255                if message.channel_id == self.channel_id {
256                    // Get the corresponding offered htlc
257                    let offered_htlc =
258                        self.received_htlcs.get(&message.htlc_id).ok_or_else(
259                            || Error::Htlc("HTLC id didn't match".to_string()),
260                        )?;
261
262                    // Check for correct hash preimage in the message
263                    if offered_htlc.hashlock
264                        == HashLock::from(message.payment_preimage)
265                    {
266                        self.offered_htlcs.remove(&message.htlc_id);
267                        let resolved_htlc = HtlcKnown {
268                            amount: offered_htlc.amount,
269                            preimage: message.payment_preimage,
270                            id: message.htlc_id,
271                            cltv_expiry: offered_htlc.cltv_expiry,
272                        };
273                        self.resolved_htlcs
274                            .insert(message.htlc_id, resolved_htlc);
275                    }
276                } else {
277                    return Err(Error::Htlc(
278                        "Missmatched channel_id, bad remote node".to_string(),
279                    ));
280                }
281            }
282            Messages::UpdateFailHtlc(message) => {
283                if message.channel_id == self.channel_id {
284                    self.offered_htlcs.remove(&message.htlc_id);
285
286                    // TODO the failure reason should be handled here
287                }
288            }
289            Messages::UpdateFailMalformedHtlc(_) => {}
290            Messages::CommitmentSigned(_) => {}
291            Messages::RevokeAndAck(_) => {}
292            Messages::ChannelReestablish(_) => {}
293            _ => {}
294        }
295        Ok(())
296    }
297
298    fn load_state(&mut self, state: &ChannelState) {
299        self.anchors_zero_fee_htlc_tx = state
300            .common_params
301            .channel_type
302            .has_anchors_zero_fee_htlc_tx();
303
304        self.offered_htlcs = state.offered_htlcs.clone();
305        self.received_htlcs = state.received_htlcs.clone();
306        self.resolved_htlcs = state.resolved_htlcs.clone();
307
308        self.to_self_delay = state.remote_params.to_self_delay;
309        self.local_revocation_basepoint =
310            state.local_keys.revocation_basepoint.key;
311        self.remote_revocation_basepoint =
312            state.remote_keys.revocation_basepoint;
313        self.local_basepoint = state.local_keys.payment_basepoint.key;
314        self.remote_basepoint = state.remote_keys.payment_basepoint;
315        self.local_delayed_basepoint =
316            state.local_keys.delayed_payment_basepoint.key;
317
318        self.channel_id = state.active_channel_id.as_slice32().into();
319
320        self.htlc_minimum_msat = state.remote_params.htlc_minimum_msat;
321        self.max_htlc_value_in_flight_msat =
322            state.remote_params.max_htlc_value_in_flight_msat;
323        self.max_accepted_htlcs = state.remote_params.max_accepted_htlcs;
324
325        self.next_recieved_htlc_id = state.last_recieved_htlc_id;
326        self.next_offered_htlc_id = state.last_offered_htlc_id;
327    }
328
329    fn store_state(&self, state: &mut ChannelState) {
330        state.offered_htlcs = self.offered_htlcs.clone();
331        state.received_htlcs = self.received_htlcs.clone();
332        state.resolved_htlcs = self.resolved_htlcs.clone();
333        state.last_recieved_htlc_id = self.next_recieved_htlc_id;
334        state.last_offered_htlc_id = self.next_offered_htlc_id;
335    }
336}
337
338impl ChannelExtension<BoltExt> for Htlc {
339    #[inline]
340    fn new() -> Box<dyn ChannelExtension<BoltExt>>
341    where
342        Self: Sized,
343    {
344        Box::new(Htlc::default())
345    }
346
347    fn build_graph(
348        &self,
349        tx_graph: &mut TxGraph,
350        _as_remote_node: bool,
351    ) -> Result<(), Error> {
352        // Process offered HTLCs
353        for (index, offered) in self.offered_htlcs.iter() {
354            let htlc_output = ScriptGenerators::ln_offered_htlc(
355                offered.amount,
356                self.remote_revocation_basepoint,
357                self.local_basepoint,
358                self.remote_basepoint,
359                offered.hashlock,
360            );
361            tx_graph.cmt_outs.push(htlc_output); // Should htlc outputs be inside graph.cmt?
362
363            let htlc_tx = Psbt::ln_htlc(
364                offered.amount,
365                // TODO: do a two-staged graph generation process
366                OutPoint::default(),
367                offered.cltv_expiry,
368                self.remote_revocation_basepoint,
369                self.local_delayed_basepoint,
370                self.to_self_delay,
371            );
372            // Last index of transaction in graph
373            let last_index = tx_graph.last_index(TxType::HtlcTimeout) + 1;
374            tx_graph.insert_tx(
375                TxType::HtlcTimeout,
376                last_index as u64 + index,
377                htlc_tx,
378            );
379        }
380
381        // Process received HTLCs
382        for (index, recieved) in self.received_htlcs.iter() {
383            let htlc_output = ScriptGenerators::ln_received_htlc(
384                recieved.amount,
385                self.remote_revocation_basepoint,
386                self.local_basepoint,
387                self.remote_basepoint,
388                recieved.cltv_expiry,
389                recieved.hashlock,
390            );
391            tx_graph.cmt_outs.push(htlc_output);
392
393            let htlc_tx = Psbt::ln_htlc(
394                recieved.amount,
395                // TODO: do a two-staged graph generation process
396                OutPoint::default(),
397                recieved.cltv_expiry,
398                self.remote_revocation_basepoint,
399                self.local_delayed_basepoint,
400                self.to_self_delay,
401            );
402            // Figure out the last index of transaction in graph
403            let last_index = tx_graph.last_index(TxType::HtlcSuccess) + 1;
404            tx_graph.insert_tx(
405                TxType::HtlcSuccess,
406                last_index as u64 + index,
407                htlc_tx,
408            );
409        }
410        Ok(())
411    }
412}
413
414pub trait ScriptGenerators {
415    fn ln_offered_htlc(
416        amount: u64,
417        revocationpubkey: PublicKey,
418        local_htlcpubkey: PublicKey,
419        remote_htlcpubkey: PublicKey,
420        payment_hash: HashLock,
421    ) -> Self;
422
423    fn ln_received_htlc(
424        amount: u64,
425        revocationpubkey: PublicKey,
426        local_htlcpubkey: PublicKey,
427        remote_htlcpubkey: PublicKey,
428        cltv_expiry: u32,
429        payment_hash: HashLock,
430    ) -> Self;
431
432    fn ln_htlc_output(
433        amount: u64,
434        revocationpubkey: PublicKey,
435        local_delayedpubkey: PublicKey,
436        to_self_delay: u16,
437    ) -> Self;
438}
439
440impl ScriptGenerators for LockScript {
441    fn ln_offered_htlc(
442        _: u64,
443        revocationpubkey: PublicKey,
444        local_htlcpubkey: PublicKey,
445        remote_htlcpubkey: PublicKey,
446        payment_hash: HashLock,
447    ) -> Self {
448        script::Builder::new()
449            .push_opcode(OP_DUP)
450            .push_opcode(OP_HASH160)
451            .push_slice(
452                &bitcoin::PublicKey::new(revocationpubkey).pubkey_hash(),
453            )
454            .push_opcode(OP_EQUAL)
455            .push_opcode(OP_IF)
456            .push_opcode(OP_CHECKSIG)
457            .push_opcode(OP_ELSE)
458            .push_key(&bitcoin::PublicKey::new(remote_htlcpubkey))
459            .push_opcode(OP_SWAP)
460            .push_opcode(OP_SIZE)
461            .push_int(32)
462            .push_opcode(OP_EQUAL)
463            .push_opcode(OP_NOTIF)
464            .push_opcode(OP_DROP)
465            .push_int(2)
466            .push_opcode(OP_SWAP)
467            .push_key(&bitcoin::PublicKey::new(local_htlcpubkey))
468            .push_int(2)
469            .push_opcode(OP_CHECKMULTISIG)
470            .push_opcode(OP_ELSE)
471            .push_opcode(OP_HASH160)
472            .push_slice(payment_hash.as_ref())
473            .push_opcode(OP_EQUALVERIFY)
474            .push_opcode(OP_CHECKSIG)
475            .push_opcode(OP_ENDIF)
476            .push_opcode(OP_ENDIF)
477            .into_script()
478            .into()
479    }
480
481    fn ln_received_htlc(
482        _: u64,
483        revocationpubkey: PublicKey,
484        local_htlcpubkey: PublicKey,
485        remote_htlcpubkey: PublicKey,
486        cltv_expiry: u32,
487        payment_hash: HashLock,
488    ) -> Self {
489        script::Builder::new()
490            .push_opcode(OP_DUP)
491            .push_opcode(OP_HASH160)
492            .push_slice(
493                &bitcoin::PublicKey::new(revocationpubkey).pubkey_hash(),
494            )
495            .push_opcode(OP_EQUAL)
496            .push_opcode(OP_IF)
497            .push_opcode(OP_CHECKSIG)
498            .push_opcode(OP_ELSE)
499            .push_key(&bitcoin::PublicKey::new(remote_htlcpubkey))
500            .push_opcode(OP_SWAP)
501            .push_opcode(OP_SIZE)
502            .push_int(32)
503            .push_opcode(OP_EQUAL)
504            .push_opcode(OP_IF)
505            .push_opcode(OP_HASH160)
506            .push_slice(payment_hash.as_ref())
507            .push_opcode(OP_EQUALVERIFY)
508            .push_int(2)
509            .push_opcode(OP_SWAP)
510            .push_key(&bitcoin::PublicKey::new(local_htlcpubkey))
511            .push_int(2)
512            .push_opcode(OP_CHECKMULTISIG)
513            .push_opcode(OP_ELSE)
514            .push_opcode(OP_DROP)
515            .push_int(cltv_expiry as i64)
516            .push_opcode(OP_CLTV)
517            .push_opcode(OP_DROP)
518            .push_opcode(OP_CHECKSIG)
519            .push_opcode(OP_ENDIF)
520            .push_opcode(OP_ENDIF)
521            .into_script()
522            .into()
523    }
524
525    fn ln_htlc_output(
526        _: u64,
527        revocationpubkey: PublicKey,
528        local_delayedpubkey: PublicKey,
529        to_self_delay: u16,
530    ) -> Self {
531        script::Builder::new()
532            .push_opcode(OP_IF)
533            .push_key(&bitcoin::PublicKey::new(revocationpubkey))
534            .push_opcode(OP_ELSE)
535            .push_int(to_self_delay as i64)
536            .push_opcode(OP_CSV)
537            .push_opcode(OP_DROP)
538            .push_key(&bitcoin::PublicKey::new(local_delayedpubkey))
539            .push_opcode(OP_ENDIF)
540            .push_opcode(OP_CHECKSIG)
541            .into_script()
542            .into()
543    }
544}
545
546impl ScriptGenerators for WitnessScript {
547    #[inline]
548    fn ln_offered_htlc(
549        amount: u64,
550        revocationpubkey: PublicKey,
551        local_htlcpubkey: PublicKey,
552        remote_htlcpubkey: PublicKey,
553        payment_hash: HashLock,
554    ) -> Self {
555        LockScript::ln_offered_htlc(
556            amount,
557            revocationpubkey,
558            local_htlcpubkey,
559            remote_htlcpubkey,
560            payment_hash,
561        )
562        .into()
563    }
564
565    #[inline]
566    fn ln_received_htlc(
567        amount: u64,
568        revocationpubkey: PublicKey,
569        local_htlcpubkey: PublicKey,
570        remote_htlcpubkey: PublicKey,
571        cltv_expiry: u32,
572        payment_hash: HashLock,
573    ) -> Self {
574        LockScript::ln_received_htlc(
575            amount,
576            revocationpubkey,
577            local_htlcpubkey,
578            remote_htlcpubkey,
579            cltv_expiry,
580            payment_hash,
581        )
582        .into()
583    }
584
585    #[inline]
586    fn ln_htlc_output(
587        amount: u64,
588        revocationpubkey: PublicKey,
589        local_delayedpubkey: PublicKey,
590        to_self_delay: u16,
591    ) -> Self {
592        LockScript::ln_htlc_output(
593            amount,
594            revocationpubkey,
595            local_delayedpubkey,
596            to_self_delay,
597        )
598        .into()
599    }
600}
601
602impl ScriptGenerators for PubkeyScript {
603    #[inline]
604    fn ln_offered_htlc(
605        amount: u64,
606        revocationpubkey: PublicKey,
607        local_htlcpubkey: PublicKey,
608        remote_htlcpubkey: PublicKey,
609        payment_hash: HashLock,
610    ) -> Self {
611        WitnessScript::ln_offered_htlc(
612            amount,
613            revocationpubkey,
614            local_htlcpubkey,
615            remote_htlcpubkey,
616            payment_hash,
617        )
618        .to_p2wsh()
619    }
620
621    #[inline]
622    fn ln_received_htlc(
623        amount: u64,
624        revocationpubkey: PublicKey,
625        local_htlcpubkey: PublicKey,
626        remote_htlcpubkey: PublicKey,
627        cltv_expiry: u32,
628        payment_hash: HashLock,
629    ) -> Self {
630        WitnessScript::ln_received_htlc(
631            amount,
632            revocationpubkey,
633            local_htlcpubkey,
634            remote_htlcpubkey,
635            cltv_expiry,
636            payment_hash,
637        )
638        .to_p2wsh()
639    }
640
641    #[inline]
642    fn ln_htlc_output(
643        amount: u64,
644        revocationpubkey: PublicKey,
645        local_delayedpubkey: PublicKey,
646        to_self_delay: u16,
647    ) -> Self {
648        WitnessScript::ln_htlc_output(
649            amount,
650            revocationpubkey,
651            local_delayedpubkey,
652            to_self_delay,
653        )
654        .to_p2wsh()
655    }
656}
657
658impl ScriptGenerators for TxOut {
659    #[inline]
660    fn ln_offered_htlc(
661        amount: u64,
662        revocationpubkey: PublicKey,
663        local_htlcpubkey: PublicKey,
664        remote_htlcpubkey: PublicKey,
665        payment_hash: HashLock,
666    ) -> Self {
667        TxOut {
668            value: amount,
669            script_pubkey: PubkeyScript::ln_offered_htlc(
670                amount,
671                revocationpubkey,
672                local_htlcpubkey,
673                remote_htlcpubkey,
674                payment_hash,
675            )
676            .into(),
677        }
678    }
679
680    #[inline]
681    fn ln_received_htlc(
682        amount: u64,
683        revocationpubkey: PublicKey,
684        local_htlcpubkey: PublicKey,
685        remote_htlcpubkey: PublicKey,
686        cltv_expiry: u32,
687        payment_hash: HashLock,
688    ) -> Self {
689        TxOut {
690            value: amount,
691            script_pubkey: PubkeyScript::ln_received_htlc(
692                amount,
693                revocationpubkey,
694                local_htlcpubkey,
695                remote_htlcpubkey,
696                cltv_expiry,
697                payment_hash,
698            )
699            .into(),
700        }
701    }
702
703    #[inline]
704    fn ln_htlc_output(
705        amount: u64,
706        revocationpubkey: PublicKey,
707        local_delayedpubkey: PublicKey,
708        to_self_delay: u16,
709    ) -> Self {
710        TxOut {
711            value: amount,
712            script_pubkey: PubkeyScript::ln_htlc_output(
713                amount,
714                revocationpubkey,
715                local_delayedpubkey,
716                to_self_delay,
717            )
718            .into(),
719        }
720    }
721}
722
723impl ScriptGenerators for psbt::Output {
724    #[inline]
725    fn ln_offered_htlc(
726        amount: u64,
727        revocationpubkey: PublicKey,
728        local_htlcpubkey: PublicKey,
729        remote_htlcpubkey: PublicKey,
730        payment_hash: HashLock,
731    ) -> Self {
732        let witness_script = WitnessScript::ln_offered_htlc(
733            amount,
734            revocationpubkey,
735            local_htlcpubkey,
736            remote_htlcpubkey,
737            payment_hash,
738        )
739        .into();
740        let txout = TxOut::ln_offered_htlc(
741            amount,
742            revocationpubkey,
743            local_htlcpubkey,
744            remote_htlcpubkey,
745            payment_hash,
746        );
747        let output = bitcoin::psbt::Output {
748            witness_script: Some(witness_script),
749            ..Default::default()
750        };
751        psbt::Output::with(0, output, txout)
752    }
753
754    #[inline]
755    fn ln_received_htlc(
756        amount: u64,
757        revocationpubkey: PublicKey,
758        local_htlcpubkey: PublicKey,
759        remote_htlcpubkey: PublicKey,
760        cltv_expiry: u32,
761        payment_hash: HashLock,
762    ) -> Self {
763        let witness_script = WitnessScript::ln_received_htlc(
764            amount,
765            revocationpubkey,
766            local_htlcpubkey,
767            remote_htlcpubkey,
768            cltv_expiry,
769            payment_hash,
770        )
771        .into();
772        let txout = TxOut::ln_received_htlc(
773            amount,
774            revocationpubkey,
775            local_htlcpubkey,
776            remote_htlcpubkey,
777            cltv_expiry,
778            payment_hash,
779        );
780        let output = bitcoin::psbt::Output {
781            witness_script: Some(witness_script),
782            ..Default::default()
783        };
784        psbt::Output::with(0, output, txout)
785    }
786
787    #[inline]
788    fn ln_htlc_output(
789        amount: u64,
790        revocationpubkey: PublicKey,
791        local_delayedpubkey: PublicKey,
792        to_self_delay: u16,
793    ) -> Self {
794        let witness_script = WitnessScript::ln_htlc_output(
795            amount,
796            revocationpubkey,
797            local_delayedpubkey,
798            to_self_delay,
799        )
800        .into();
801        let txout = TxOut::ln_htlc_output(
802            amount,
803            revocationpubkey,
804            local_delayedpubkey,
805            to_self_delay,
806        );
807        let output = bitcoin::psbt::Output {
808            witness_script: Some(witness_script),
809            ..Default::default()
810        };
811        psbt::Output::with(0, output, txout)
812    }
813}
814
815pub trait TxGenerators {
816    fn ln_htlc(
817        amount: u64,
818        outpoint: OutPoint,
819        cltv_expiry: u32,
820        revocationpubkey: PublicKey,
821        local_delayedpubkey: PublicKey,
822        to_self_delay: u16,
823    ) -> Self;
824}
825
826impl TxGenerators for Transaction {
827    /// NB: For HTLC Success transaction always set `cltv_expiry` parameter
828    ///     to zero!
829    fn ln_htlc(
830        amount: u64,
831        outpoint: OutPoint,
832        cltv_expiry: u32,
833        revocationpubkey: PublicKey,
834        local_delayedpubkey: PublicKey,
835        to_self_delay: u16,
836    ) -> Self {
837        let txout = TxOut::ln_htlc_output(
838            amount,
839            revocationpubkey,
840            local_delayedpubkey,
841            to_self_delay,
842        );
843        Transaction {
844            version: 2,
845            lock_time: bitcoin::PackedLockTime(cltv_expiry),
846            input: vec![TxIn {
847                previous_output: outpoint,
848                script_sig: none!(),
849                sequence: bitcoin::Sequence(0),
850                witness: empty!(),
851            }],
852            output: vec![txout],
853        }
854    }
855}
856
857impl TxGenerators for Psbt {
858    fn ln_htlc(
859        amount: u64,
860        outpoint: OutPoint,
861        cltv_expiry: u32,
862        revocationpubkey: PublicKey,
863        local_delayedpubkey: PublicKey,
864        to_self_delay: u16,
865    ) -> Self {
866        let output = psbt::Output::ln_htlc_output(
867            amount,
868            revocationpubkey,
869            local_delayedpubkey,
870            to_self_delay,
871        );
872
873        let mut psbt = Psbt::with(
874            Transaction::ln_htlc(
875                amount,
876                outpoint,
877                cltv_expiry,
878                revocationpubkey,
879                local_delayedpubkey,
880                to_self_delay,
881            ),
882            PsbtVersion::V0,
883        )
884        .expect("Tx has empty sigs so PSBT creation does not fail");
885        psbt.outputs[0] = output;
886        psbt
887    }
888}