bolt3

Lightning transaction and script formats, per BOLT #3 (docs.ppad.tech/bolt3).
git clone git://git.ppad.tech/bolt3.git
Log | Files | Refs | README | LICENSE

Tx.hs (21374B)


      1 {-# OPTIONS_HADDOCK hide #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE DeriveGeneric #-}
      4 
      5 -- |
      6 -- Module: Lightning.Protocol.BOLT3.Tx
      7 -- Copyright: (c) 2025 Jared Tobin
      8 -- License: MIT
      9 -- Maintainer: Jared Tobin <jared@ppad.tech>
     10 --
     11 -- Commitment, HTLC and closing transactions, per BOLT #3.
     12 
     13 module Lightning.Protocol.BOLT3.Tx (
     14     -- * Commitment transactions
     15     CommitmentContext(..)
     16   , CommitmentTx(..)
     17   , CommitmentOutput(..)
     18   , OutputType(..)
     19   , build_commitment_tx
     20 
     21     -- * HTLC transactions
     22   , HTLCContext(..)
     23   , HTLCTx(..)
     24   , build_htlc_tx
     25 
     26     -- * Closing transactions
     27   , ClosingTx(..)
     28   , ClosingOutput(..)
     29   , ClosingContext(..)
     30   , ClosingOutputs(..)
     31   , build_closing_tx
     32   , LegacyClosingContext(..)
     33   , build_legacy_closing_tx
     34 
     35     -- * Fees
     36   , commitment_fee
     37   , htlc_timeout_fee
     38   , htlc_success_fee
     39 
     40     -- * Trimming
     41   , htlc_trim_threshold
     42   , is_trimmed
     43 
     44     -- * Serialization
     45   , commitment_to_tx
     46   , htlc_to_tx
     47   , closing_to_tx
     48   , encode_commitment_tx
     49   , encode_htlc_tx
     50   , encode_closing_tx
     51   ) where
     52 
     53 import Bitcoin.Prim.Tx (OutPoint(..), TxId)
     54 import qualified Bitcoin.Prim.Tx as BT
     55 import Control.DeepSeq (NFData)
     56 import Data.Bits ((.&.), (.|.), shiftR)
     57 import qualified Data.ByteString as BS
     58 import Data.List (sortBy)
     59 import Data.List.NonEmpty (NonEmpty(..))
     60 import qualified Data.List.NonEmpty as NE
     61 import Data.Maybe (fromMaybe)
     62 import Data.Word (Word32, Word64)
     63 import GHC.Generics (Generic)
     64 import Lightning.Protocol.BOLT1 (Satoshi, MilliSatoshi)
     65 import qualified Lightning.Protocol.BOLT1 as BOLT1
     66 import Lightning.Protocol.BOLT3.Keys
     67 import Lightning.Protocol.BOLT3.Scripts
     68 import Lightning.Protocol.BOLT3.Types
     69 
     70 -- commitment transactions ----------------------------------------------------
     71 
     72 -- | What a commitment transaction output pays.
     73 data OutputType
     74   = OutputToLocal
     75   | OutputToRemote
     76   | OutputLocalAnchor
     77   | OutputRemoteAnchor
     78   | OutputHTLC !HTLC
     79     -- ^ an offered or received HTLC (per its 'htlc_direction')
     80   deriving (Eq, Show, Generic)
     81 
     82 instance NFData OutputType
     83 
     84 -- | A commitment transaction output.
     85 data CommitmentOutput = CommitmentOutput
     86   { co_value  :: {-# UNPACK #-} !Satoshi
     87   , co_script :: !Script     -- ^ scriptPubKey
     88   , co_type   :: !OutputType
     89   } deriving (Eq, Show, Generic)
     90 
     91 instance NFData CommitmentOutput
     92 
     93 -- | The parameters of a commitment transaction, from the point of view
     94 --   of its owner (the "local" party, whose outputs are delayed).
     95 data CommitmentContext = CommitmentContext
     96   { cc_funding_outpoint  :: {-# UNPACK #-} !OutPoint
     97   , cc_commitment_number :: {-# UNPACK #-} !CommitmentNumber
     98   , cc_local_payment_bp  :: !PaymentBasepoint
     99     -- ^ the owner's @payment_basepoint@
    100   , cc_remote_payment_bp :: !PaymentBasepoint
    101     -- ^ the other party's @payment_basepoint@
    102   , cc_to_self_delay     :: {-# UNPACK #-} !ToSelfDelay
    103     -- ^ the delay on the owner's outputs (set by the other party)
    104   , cc_dust_limit        :: {-# UNPACK #-} !DustLimit
    105     -- ^ the owner's @dust_limit_satoshis@
    106   , cc_feerate           :: {-# UNPACK #-} !FeeratePerKw
    107   , cc_format            :: !CommitmentFormat
    108   , cc_is_funder         :: !Bool
    109     -- ^ whether the owner opened the channel, and so pays the fee
    110   , cc_to_local_msat     :: {-# UNPACK #-} !MilliSatoshi
    111   , cc_to_remote_msat    :: {-# UNPACK #-} !MilliSatoshi
    112   , cc_htlcs             :: ![HTLC]
    113   , cc_keys              :: !CommitmentKeys
    114   } deriving (Eq, Show, Generic)
    115 
    116 instance NFData CommitmentContext
    117 
    118 -- | An unsigned commitment transaction.
    119 data CommitmentTx = CommitmentTx
    120   { ctx_version        :: {-# UNPACK #-} !Word32
    121   , ctx_locktime       :: {-# UNPACK #-} !Locktime
    122   , ctx_input_outpoint :: {-# UNPACK #-} !OutPoint
    123   , ctx_input_sequence :: {-# UNPACK #-} !Sequence
    124   , ctx_outputs        :: !(NonEmpty CommitmentOutput)
    125     -- ^ in BIP69+CLTV order
    126   , ctx_funding_script :: !Script
    127     -- ^ the funding output's witness script, which signatures commit
    128     --   to
    129   } deriving (Eq, Show, Generic)
    130 
    131 instance NFData CommitmentTx
    132 
    133 -- | Build a commitment transaction, per BOLT #3's "Commitment
    134 --   Transaction Construction":
    135 --
    136 --   * the locktime and input sequence encode the obscured commitment
    137 --     number (the payment basepoints are ordered by 'cc_is_funder');
    138 --   * HTLCs whose amount, less the HTLC transaction fee, is below the
    139 --     dust limit are trimmed;
    140 --   * the base fee (and under 'Anchors', both anchors) are taken from
    141 --     the funder's output, which is left at zero if it can't cover
    142 --     them;
    143 --   * @to_local@ and @to_remote@ outputs below the dust limit are
    144 --     omitted, and under 'Anchors' each party's anchor is added if its
    145 --     output, or any HTLC output, exists;
    146 --   * outputs are sorted by value, scriptPubKey and CLTV expiry.
    147 --
    148 --   Fails if the transaction would have no outputs.
    149 build_commitment_tx :: CommitmentContext -> Maybe CommitmentTx
    150 build_commitment_tx ctx = do
    151     outputs <- NE.nonEmpty . sort_outputs $
    152       to_local <> to_remote <> local_anchor <> remote_anchor <> htlcs
    153     pure CommitmentTx
    154       { ctx_version        = 2
    155       , ctx_locktime       =
    156           Locktime (0x20000000 .|. (fromIntegral obscured .&. 0xFFFFFF))
    157       , ctx_input_outpoint = cc_funding_outpoint ctx
    158       , ctx_input_sequence =
    159           Sequence
    160             (0x80000000 .|. (fromIntegral (obscured `shiftR` 24) .&. 0xFFFFFF))
    161       , ctx_outputs        = outputs
    162       , ctx_funding_script =
    163           funding_script (ck_local_funding keys) (ck_remote_funding keys)
    164       }
    165   where
    166     !keys = cc_keys ctx
    167     !fmt  = cc_format ctx
    168     !rate = cc_feerate ctx
    169     !dust = cc_dust_limit ctx
    170     DustLimit dust_sat = dust
    171 
    172     (!opener, !accepter)
    173       | cc_is_funder ctx = (cc_local_payment_bp ctx, cc_remote_payment_bp ctx)
    174       | otherwise        = (cc_remote_payment_bp ctx, cc_local_payment_bp ctx)
    175     !obscured =
    176       obscured_commitment_number opener accepter (cc_commitment_number ctx)
    177 
    178     untrimmed = filter (not . is_trimmed dust rate fmt) (cc_htlcs ctx)
    179 
    180     !fee = commitment_fee rate fmt (fromIntegral (length untrimmed))
    181     !deduction = case fmt of
    182       StaticRemotekey -> fee
    183       Anchors         ->
    184         fromMaybe BOLT1.max_satoshi (BOLT1.add_sat fee (sat 660))
    185     deduct s = fromMaybe (sat 0) (BOLT1.sub_sat s deduction)
    186 
    187     !local_sat  = BOLT1.msat_to_sat (cc_to_local_msat ctx)
    188     !remote_sat = BOLT1.msat_to_sat (cc_to_remote_msat ctx)
    189     (!to_local_sat, !to_remote_sat)
    190       | cc_is_funder ctx = (deduct local_sat, remote_sat)
    191       | otherwise        = (local_sat, deduct remote_sat)
    192 
    193     to_local =
    194       [ CommitmentOutput to_local_sat
    195           (to_p2wsh (to_local_script (ck_revocation_pubkey keys)
    196                        (cc_to_self_delay ctx) (ck_local_delayed keys)))
    197           OutputToLocal
    198       | to_local_sat >= dust_sat ]
    199 
    200     to_remote =
    201       [ CommitmentOutput to_remote_sat
    202           (to_remote_script_pubkey (ck_remote_payment keys) fmt)
    203           OutputToRemote
    204       | to_remote_sat >= dust_sat ]
    205 
    206     has_htlcs = not (null untrimmed)
    207     anchor pk ty = CommitmentOutput anchor_output_value
    208       (to_p2wsh (anchor_script pk)) ty
    209 
    210     local_anchor = case fmt of
    211       Anchors | not (null to_local) || has_htlcs ->
    212         [anchor (ck_local_funding keys) OutputLocalAnchor]
    213       _ -> []
    214 
    215     remote_anchor = case fmt of
    216       Anchors | not (null to_remote) || has_htlcs ->
    217         [anchor (ck_remote_funding keys) OutputRemoteAnchor]
    218       _ -> []
    219 
    220     htlcs = fmap htlc_output untrimmed
    221 
    222     htlc_output h =
    223       let !script = case htlc_direction h of
    224             HTLCOffered  -> offered_htlc_script
    225               (ck_revocation_pubkey keys) (ck_remote_htlc keys)
    226               (ck_local_htlc keys) (htlc_payment_hash h) fmt
    227             HTLCReceived -> received_htlc_script
    228               (ck_revocation_pubkey keys) (ck_remote_htlc keys)
    229               (ck_local_htlc keys) (htlc_payment_hash h)
    230               (htlc_cltv_expiry h) fmt
    231       in  CommitmentOutput (BOLT1.msat_to_sat (htlc_amount_msat h))
    232             (to_p2wsh script) (OutputHTLC h)
    233 
    234 -- BIP69+CLTV order: value, then scriptPubKey, then (for HTLCs) CLTV
    235 -- expiry.
    236 sort_outputs :: [CommitmentOutput] -> [CommitmentOutput]
    237 sort_outputs = sortBy cmp where
    238   cmp a b = compare (co_value a) (co_value b)
    239          <> compare (co_script a) (co_script b)
    240          <> compare (cltv (co_type a)) (cltv (co_type b))
    241   cltv t = case t of
    242     OutputHTLC h -> Just (htlc_cltv_expiry h)
    243     _            -> Nothing
    244 
    245 -- HTLC transactions ----------------------------------------------------------
    246 
    247 -- | The parameters of an HTLC-success or HTLC-timeout transaction,
    248 --   spending an HTLC output of the owner's commitment transaction.
    249 data HTLCContext = HTLCContext
    250   { hc_commitment_txid   :: !TxId
    251   , hc_output_index      :: {-# UNPACK #-} !Word32
    252   , hc_htlc              :: !HTLC
    253   , hc_to_self_delay     :: {-# UNPACK #-} !ToSelfDelay
    254   , hc_feerate           :: {-# UNPACK #-} !FeeratePerKw
    255   , hc_format            :: !CommitmentFormat
    256   , hc_revocation_pubkey :: !RevocationPubkey
    257   , hc_local_delayed     :: !LocalDelayedPubkey
    258   } deriving (Eq, Show, Generic)
    259 
    260 instance NFData HTLCContext
    261 
    262 -- | An unsigned HTLC-success or HTLC-timeout transaction.
    263 data HTLCTx = HTLCTx
    264   { htx_version        :: {-# UNPACK #-} !Word32
    265   , htx_locktime       :: {-# UNPACK #-} !Locktime
    266   , htx_input_outpoint :: {-# UNPACK #-} !OutPoint
    267   , htx_input_sequence :: {-# UNPACK #-} !Sequence
    268   , htx_output_value   :: {-# UNPACK #-} !Satoshi
    269   , htx_output_script  :: !Script
    270   } deriving (Eq, Show, Generic)
    271 
    272 instance NFData HTLCTx
    273 
    274 -- | Build the second-stage transaction for an HTLC output of the
    275 --   owner's commitment transaction: HTLC-timeout (locktime
    276 --   @cltv_expiry@) for an offered HTLC, and HTLC-success (locktime 0)
    277 --   for a received one.
    278 --
    279 --   The output pays the HTLC amount, less the HTLC transaction fee
    280 --   (zero under 'Anchors'), to the 'to_local_script' of the owner's
    281 --   keys. Fails if the fee exceeds the amount (such an HTLC is always
    282 --   trimmed).
    283 build_htlc_tx :: HTLCContext -> Maybe HTLCTx
    284 build_htlc_tx ctx = do
    285     value <- BOLT1.sub_sat (BOLT1.msat_to_sat (htlc_amount_msat h)) fee
    286     pure HTLCTx
    287       { htx_version        = 2
    288       , htx_locktime       = locktime
    289       , htx_input_outpoint =
    290           OutPoint (hc_commitment_txid ctx) (hc_output_index ctx)
    291       , htx_input_sequence = case fmt of
    292           StaticRemotekey -> Sequence 0
    293           Anchors         -> Sequence 1
    294       , htx_output_value   = value
    295       , htx_output_script  = to_p2wsh $ to_local_script
    296           (hc_revocation_pubkey ctx) (hc_to_self_delay ctx)
    297           (hc_local_delayed ctx)
    298       }
    299   where
    300     !h = hc_htlc ctx
    301     !fmt = hc_format ctx
    302     (!fee, !locktime) = case htlc_direction h of
    303       HTLCOffered  ->
    304         let CltvExpiry e = htlc_cltv_expiry h
    305         in  (htlc_timeout_fee (hc_feerate ctx) fmt, Locktime e)
    306       HTLCReceived ->
    307         (htlc_success_fee (hc_feerate ctx) fmt, Locktime 0)
    308 
    309 -- closing transactions -------------------------------------------------------
    310 
    311 -- | A closing transaction output.
    312 data ClosingOutput = ClosingOutput
    313   { clo_value  :: {-# UNPACK #-} !Satoshi
    314   , clo_script :: !Script     -- ^ scriptPubKey
    315   } deriving (Eq, Show, Generic)
    316 
    317 instance NFData ClosingOutput
    318 
    319 -- | An unsigned closing transaction.
    320 data ClosingTx = ClosingTx
    321   { cltx_version        :: {-# UNPACK #-} !Word32
    322   , cltx_locktime       :: {-# UNPACK #-} !Locktime
    323   , cltx_input_outpoint :: {-# UNPACK #-} !OutPoint
    324   , cltx_input_sequence :: {-# UNPACK #-} !Sequence
    325   , cltx_outputs        :: !(NonEmpty ClosingOutput)
    326     -- ^ in BIP69 order
    327   } deriving (Eq, Show, Generic)
    328 
    329 instance NFData ClosingTx
    330 
    331 -- | Which outputs an @option_simple_close@ closing transaction has,
    332 --   matching the signature fields of @closing_complete@.
    333 data ClosingOutputs
    334   = CloserOutputOnly
    335   | CloseeOutputOnly
    336   | CloserAndCloseeOutputs
    337   deriving (Eq, Show, Generic)
    338 
    339 instance NFData ClosingOutputs
    340 
    341 -- | The parameters of an @option_simple_close@ closing transaction,
    342 --   as given by a @closing_complete@ message.
    343 data ClosingContext = ClosingContext
    344   { clc_funding_outpoint :: {-# UNPACK #-} !OutPoint
    345   , clc_closer_msat      :: {-# UNPACK #-} !MilliSatoshi
    346     -- ^ the closer's final balance
    347   , clc_closee_msat      :: {-# UNPACK #-} !MilliSatoshi
    348     -- ^ the closee's final balance
    349   , clc_closer_script    :: !Script
    350   , clc_closee_script    :: !Script
    351   , clc_fee              :: {-# UNPACK #-} !Satoshi
    352   , clc_locktime         :: {-# UNPACK #-} !Locktime
    353   , clc_outputs          :: !ClosingOutputs
    354   } deriving (Eq, Show, Generic)
    355 
    356 instance NFData ClosingContext
    357 
    358 -- | Build an @option_simple_close@ closing transaction (BOLT #3's
    359 --   "Closing Transaction"), for @closing_complete@ and @closing_sig@.
    360 --
    361 --   The closer pays the fee: its output is its balance, rounded down
    362 --   to whole satoshis, less the fee. An output whose scriptPubKey
    363 --   starts with @OP_RETURN@ has amount 0. Only the chosen outputs are
    364 --   included, and none is trimmed. Fails if the fee exceeds the
    365 --   closer's balance.
    366 build_closing_tx :: ClosingContext -> Maybe ClosingTx
    367 build_closing_tx ctx = do
    368     closer_sat <-
    369       BOLT1.sub_sat (BOLT1.msat_to_sat (clc_closer_msat ctx)) (clc_fee ctx)
    370     let !closer = output closer_sat (clc_closer_script ctx)
    371         !closee = output (BOLT1.msat_to_sat (clc_closee_msat ctx))
    372                     (clc_closee_script ctx)
    373     pure ClosingTx
    374       { cltx_version        = 2
    375       , cltx_locktime       = clc_locktime ctx
    376       , cltx_input_outpoint = clc_funding_outpoint ctx
    377       , cltx_input_sequence = Sequence 0xFFFFFFFD
    378       , cltx_outputs        = case clc_outputs ctx of
    379           CloserOutputOnly       -> closer :| []
    380           CloseeOutputOnly       -> closee :| []
    381           CloserAndCloseeOutputs -> sort_closing (closer :| [closee])
    382       }
    383   where
    384     output amt spk@(Script s) = case BS.uncons s of
    385       Just (0x6a, _) -> ClosingOutput (sat 0) spk
    386       _              -> ClosingOutput amt spk
    387 
    388 -- | The parameters of a legacy closing transaction (for
    389 --   @closing_signed@), from the point of view of the node producing
    390 --   the signature (the "local" party).
    391 data LegacyClosingContext = LegacyClosingContext
    392   { lcc_funding_outpoint :: {-# UNPACK #-} !OutPoint
    393   , lcc_local_msat       :: {-# UNPACK #-} !MilliSatoshi
    394   , lcc_remote_msat      :: {-# UNPACK #-} !MilliSatoshi
    395   , lcc_local_script     :: !Script
    396   , lcc_remote_script    :: !Script
    397   , lcc_dust_limit       :: {-# UNPACK #-} !DustLimit
    398     -- ^ the signer's @dust_limit_satoshis@
    399   , lcc_fee              :: {-# UNPACK #-} !Satoshi
    400   , lcc_is_funder        :: !Bool
    401     -- ^ whether the signer funded the channel, and so pays the fee
    402   , lcc_omit_local       :: !Bool
    403     -- ^ whether the signer eliminates its own output
    404   } deriving (Eq, Show, Generic)
    405 
    406 instance NFData LegacyClosingContext
    407 
    408 -- | Build a legacy closing transaction (BOLT #3's "Legacy Closing
    409 --   Transaction"), for @closing_signed@.
    410 --
    411 --   Balances are rounded down to whole satoshis and the fee is taken
    412 --   from the funder's output. Outputs below the signer's dust limit
    413 --   are removed, as is the signer's own output if 'lcc_omit_local' is
    414 --   set. Fails if the fee exceeds the funder's balance, or if no
    415 --   output remains.
    416 build_legacy_closing_tx :: LegacyClosingContext -> Maybe ClosingTx
    417 build_legacy_closing_tx ctx = do
    418     let !local_sat  = BOLT1.msat_to_sat (lcc_local_msat ctx)
    419         !remote_sat = BOLT1.msat_to_sat (lcc_remote_msat ctx)
    420         DustLimit dust = lcc_dust_limit ctx
    421     (l, r) <-
    422       if   lcc_is_funder ctx
    423       then fmap (\x -> (x, remote_sat)) (BOLT1.sub_sat local_sat fee)
    424       else fmap (\x -> (local_sat, x)) (BOLT1.sub_sat remote_sat fee)
    425     outputs <- NE.nonEmpty $
    426          [ ClosingOutput l (lcc_local_script ctx)
    427          | not (lcc_omit_local ctx), l >= dust ]
    428       <> [ ClosingOutput r (lcc_remote_script ctx) | r >= dust ]
    429     pure ClosingTx
    430       { cltx_version        = 2
    431       , cltx_locktime       = Locktime 0
    432       , cltx_input_outpoint = lcc_funding_outpoint ctx
    433       , cltx_input_sequence = Sequence 0xFFFFFFFF
    434       , cltx_outputs        = sort_closing outputs
    435       }
    436   where
    437     !fee = lcc_fee ctx
    438 
    439 -- BIP69 order: value, then scriptPubKey.
    440 sort_closing :: NonEmpty ClosingOutput -> NonEmpty ClosingOutput
    441 sort_closing = NE.sortBy $ \a b ->
    442   compare (clo_value a) (clo_value b) <> compare (clo_script a) (clo_script b)
    443 
    444 -- fees -----------------------------------------------------------------------
    445 
    446 -- feerate * weight / 1000, saturating at 21 million BTC.
    447 weight_fee :: FeeratePerKw -> Integer -> Satoshi
    448 weight_fee (FeeratePerKw rate) weight =
    449   let !f = toInteger rate * weight `quot` 1000
    450   in  if   f > toInteger (BOLT1.un_satoshi BOLT1.max_satoshi)
    451       then BOLT1.max_satoshi
    452       else sat (fromInteger f)
    453 
    454 -- | The base fee of a commitment transaction with a given number of
    455 --   untrimmed HTLC outputs:
    456 --
    457 --   @feerate_per_kw * (724 + 172 * num_htlcs) / 1000@
    458 --
    459 --   with a base weight of 1124 rather than 724 under 'Anchors'.
    460 --
    461 --   >>> commitment_fee (FeeratePerKw 5000) StaticRemotekey 2
    462 --   Satoshi 5340
    463 commitment_fee :: FeeratePerKw -> CommitmentFormat -> Word64 -> Satoshi
    464 commitment_fee rate fmt n = weight_fee rate (base + 172 * toInteger n) where
    465   base = case fmt of
    466     StaticRemotekey -> 724
    467     Anchors         -> 1124
    468 
    469 -- | The fee of an HTLC-timeout transaction: @feerate_per_kw * 663 /
    470 --   1000@, or zero under 'Anchors'.
    471 --
    472 --   >>> htlc_timeout_fee (FeeratePerKw 5000) StaticRemotekey
    473 --   Satoshi 3315
    474 htlc_timeout_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi
    475 htlc_timeout_fee rate fmt = case fmt of
    476   StaticRemotekey -> weight_fee rate 663
    477   Anchors         -> sat 0
    478 
    479 -- | The fee of an HTLC-success transaction: @feerate_per_kw * 703 /
    480 --   1000@, or zero under 'Anchors'.
    481 --
    482 --   >>> htlc_success_fee (FeeratePerKw 5000) StaticRemotekey
    483 --   Satoshi 3515
    484 htlc_success_fee :: FeeratePerKw -> CommitmentFormat -> Satoshi
    485 htlc_success_fee rate fmt = case fmt of
    486   StaticRemotekey -> weight_fee rate 703
    487   Anchors         -> sat 0
    488 
    489 -- trimming -------------------------------------------------------------------
    490 
    491 -- | The amount below which an HTLC in a given direction is trimmed:
    492 --   the dust limit plus the fee of its HTLC transaction.
    493 --
    494 --   >>> let Just d = fmap DustLimit (BOLT1.satoshi 546)
    495 --   >>> htlc_trim_threshold d (FeeratePerKw 5000) StaticRemotekey HTLCOffered
    496 --   Satoshi 3861
    497 htlc_trim_threshold
    498   :: DustLimit
    499   -> FeeratePerKw
    500   -> CommitmentFormat
    501   -> HTLCDirection
    502   -> Satoshi
    503 htlc_trim_threshold (DustLimit dust) rate fmt dir =
    504   let !fee = case dir of
    505         HTLCOffered  -> htlc_timeout_fee rate fmt
    506         HTLCReceived -> htlc_success_fee rate fmt
    507   in  fromMaybe BOLT1.max_satoshi (BOLT1.add_sat dust fee)
    508 
    509 -- | Whether an HTLC is trimmed from a commitment transaction: whether
    510 --   its amount, rounded down to whole satoshis, is below
    511 --   'htlc_trim_threshold'.
    512 is_trimmed :: DustLimit -> FeeratePerKw -> CommitmentFormat -> HTLC -> Bool
    513 is_trimmed dust rate fmt h =
    514   BOLT1.msat_to_sat (htlc_amount_msat h)
    515     < htlc_trim_threshold dust rate fmt (htlc_direction h)
    516 
    517 -- serialization --------------------------------------------------------------
    518 
    519 unsigned_tx
    520   :: Word32 -> Locktime -> OutPoint -> Sequence -> NonEmpty BT.TxOut
    521   -> BT.Tx
    522 unsigned_tx version (Locktime lt) prevout (Sequence sq) outs = BT.Tx
    523   { BT.tx_version   = version
    524   , BT.tx_inputs    = BT.TxIn prevout BS.empty sq (BT.Witness []) :| []
    525   , BT.tx_outputs   = outs
    526   , BT.tx_locktime  = lt
    527   }
    528 
    529 txout :: Satoshi -> Script -> BT.TxOut
    530 txout v (Script s) = BT.TxOut (BOLT1.un_satoshi v) s
    531 {-# INLINE txout #-}
    532 
    533 -- | The unsigned commitment transaction as a ppad-tx 'BT.Tx' (with no
    534 --   witnesses), e.g. for computing its txid or sighashes.
    535 commitment_to_tx :: CommitmentTx -> BT.Tx
    536 commitment_to_tx c = unsigned_tx (ctx_version c) (ctx_locktime c)
    537   (ctx_input_outpoint c) (ctx_input_sequence c)
    538   (fmap (\o -> txout (co_value o) (co_script o)) (ctx_outputs c))
    539 
    540 -- | The unsigned HTLC transaction as a ppad-tx 'BT.Tx'.
    541 htlc_to_tx :: HTLCTx -> BT.Tx
    542 htlc_to_tx h = unsigned_tx (htx_version h) (htx_locktime h)
    543   (htx_input_outpoint h) (htx_input_sequence h)
    544   (txout (htx_output_value h) (htx_output_script h) :| [])
    545 
    546 -- | The unsigned closing transaction as a ppad-tx 'BT.Tx'.
    547 closing_to_tx :: ClosingTx -> BT.Tx
    548 closing_to_tx c = unsigned_tx (cltx_version c) (cltx_locktime c)
    549   (cltx_input_outpoint c) (cltx_input_sequence c)
    550   (fmap (\o -> txout (clo_value o) (clo_script o)) (cltx_outputs c))
    551 
    552 -- | Serialize an unsigned commitment transaction, without witness
    553 --   data. This is the form whose double-SHA256 is the txid; it is not
    554 --   what segwit signatures commit to (see ppad-tx's
    555 --   @Bitcoin.Prim.Tx.Sighash@). To broadcast, attach the witness to
    556 --   'commitment_to_tx' and serialize with ppad-tx.
    557 encode_commitment_tx :: CommitmentTx -> BS.ByteString
    558 encode_commitment_tx = BT.to_bytes_legacy . commitment_to_tx
    559 
    560 -- | Serialize an unsigned HTLC transaction, without witness data (cf.
    561 --   'encode_commitment_tx').
    562 encode_htlc_tx :: HTLCTx -> BS.ByteString
    563 encode_htlc_tx = BT.to_bytes_legacy . htlc_to_tx
    564 
    565 -- | Serialize an unsigned closing transaction, without witness data
    566 --   (cf. 'encode_commitment_tx').
    567 encode_closing_tx :: ClosingTx -> BS.ByteString
    568 encode_closing_tx = BT.to_bytes_legacy . closing_to_tx