bolt5

On-chain transaction handling for Lightning (docs.ppad.tech/bolt5).
git clone git://git.ppad.tech/bolt5.git
Log | Files | Refs | README | LICENSE

Types.hs (10751B)


      1 {-# OPTIONS_HADDOCK hide #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE DeriveGeneric #-}
      4 
      5 -- |
      6 -- Module: Lightning.Protocol.BOLT5.Types
      7 -- Copyright: (c) 2025 Jared Tobin
      8 -- License: MIT
      9 -- Maintainer: Jared Tobin <jared@ppad.tech>
     10 --
     11 -- Types for BOLT #5 on-chain transaction handling.
     12 
     13 module Lightning.Protocol.BOLT5.Types (
     14     -- * Close identification
     15     CloseType(..)
     16 
     17     -- * Output classification
     18   , UnresolvedOutput(..)
     19   , OutputResolution(..)
     20 
     21     -- * HTLC output type
     22   , HTLCOutputType(..)
     23   , htlc_output_type
     24   , htlc_output_type_weight
     25 
     26     -- * Spending transactions
     27   , SpendingTx(..)
     28 
     29     -- * Revoked outputs
     30   , RevokedOutput(..)
     31   , RevokedOutputType(..)
     32   , revoked_output_weight
     33 
     34     -- * Weight constants (Appendix A)
     35   , to_local_penalty_witness_weight
     36   , offered_htlc_penalty_witness_weight
     37   , accepted_htlc_penalty_witness_weight
     38   , offered_htlc_penalty_witness_weight_anchors
     39   , accepted_htlc_penalty_witness_weight_anchors
     40   , to_local_penalty_input_weight
     41   , offered_htlc_penalty_input_weight
     42   , accepted_htlc_penalty_input_weight
     43   , offered_htlc_penalty_input_weight_anchors
     44   , accepted_htlc_penalty_input_weight_anchors
     45   , to_remote_input_weight
     46   , penalty_tx_base_weight
     47   , max_standard_weight
     48 
     49     -- * Fee calculation
     50   , spending_fee
     51   ) where
     52 
     53 import Bitcoin.Prim.Tx (OutPoint, Tx)
     54 import Bitcoin.Prim.Tx.Sighash (SighashType)
     55 import Control.DeepSeq (NFData(..))
     56 import Data.Word (Word64)
     57 import GHC.Generics (Generic)
     58 import Lightning.Protocol.BOLT1 (Satoshi)
     59 import qualified Lightning.Protocol.BOLT1 as BOLT1
     60 import Lightning.Protocol.BOLT3
     61 
     62 -- close identification -----------------------------------------------
     63 
     64 -- | What kind of close was detected on chain.
     65 --
     66 --   @identify_close@ produces only 'LocalCommitClose' and
     67 --   'RemoteCommitClose'; recognising mutual and revoked closes is
     68 --   left to the caller.
     69 data CloseType
     70   = MutualClose
     71     -- ^ Cooperative closure agreed by both parties.
     72   | LocalCommitClose
     73     -- ^ Our commitment transaction was broadcast.
     74   | RemoteCommitClose
     75     -- ^ The remote party's commitment transaction was broadcast.
     76   | RevokedCommitClose
     77     -- ^ A revoked (outdated) commitment transaction was broadcast.
     78   deriving (Eq, Show, Generic)
     79 
     80 instance NFData CloseType
     81 
     82 -- output classification ----------------------------------------------
     83 
     84 -- | An unresolved commitment transaction output.
     85 data UnresolvedOutput = UnresolvedOutput
     86   { uo_outpoint :: !OutPoint
     87   , uo_value    :: {-# UNPACK #-} !Satoshi
     88   , uo_type     :: !OutputResolution
     89   } deriving (Eq, Show, Generic)
     90 
     91 instance NFData UnresolvedOutput
     92 
     93 -- | How to resolve an output, per BOLT #5 rules.
     94 data OutputResolution
     95   = Resolved
     96     -- ^ Nothing to do (e.g. to_remote on our local commitment).
     97   | SpendToLocal
     98       !ToSelfDelay !RevocationPubkey !LocalDelayedPubkey
     99     -- ^ Spend to_local after the CSV delay.
    100   | SpendHTLCTimeout
    101       !HTLC !CommitmentKeys !CommitmentFormat
    102     -- ^ Spend via an HTLC-timeout transaction (our commitment, an
    103     --   HTLC we offered).
    104   | SpendHTLCSuccess
    105       !HTLC !CommitmentKeys !CommitmentFormat
    106     -- ^ Spend via an HTLC-success transaction (our commitment, an
    107     --   HTLC we received).
    108   | SpendHTLCTimeoutDirect !HTLC
    109     -- ^ Spend the HTLC directly after it times out (the remote
    110     --   commitment, an HTLC we offered).
    111   | SpendHTLCPreimageDirect !HTLC
    112     -- ^ Spend the HTLC directly with the preimage (the remote
    113     --   commitment, an HTLC we received).
    114   | Revoke !RevocationPubkey
    115     -- ^ Spend a revoked to_local output with the revocation key.
    116   | RevokeHTLC !RevocationPubkey !HTLCOutputType
    117     -- ^ Spend a revoked HTLC output with the revocation key.
    118   | AnchorSpend !FundingPubkey
    119     -- ^ Spend our anchor output.
    120   deriving (Eq, Show, Generic)
    121 
    122 instance NFData OutputResolution
    123 
    124 -- HTLC output type -------------------------------------------------
    125 
    126 -- | The kind of an HTLC output, from the commitment owner's point of
    127 --   view.
    128 --
    129 --   Unlike bolt3's 'OutputType', this can't describe a to_local,
    130 --   to_remote or anchor output.
    131 data HTLCOutputType
    132   = HTLCOfferedOutput {-# UNPACK #-} !CltvExpiry
    133     -- ^ Offered HTLC output with CLTV expiry.
    134   | HTLCReceivedOutput {-# UNPACK #-} !CltvExpiry
    135     -- ^ Received HTLC output with CLTV expiry.
    136   deriving (Eq, Show, Generic)
    137 
    138 instance NFData HTLCOutputType
    139 
    140 -- | The 'HTLCOutputType' of a bolt3 'OutputType', or 'Nothing' if
    141 --   it isn't an HTLC output.
    142 --
    143 --   >>> htlc_output_type OutputToLocal
    144 --   Nothing
    145 htlc_output_type :: OutputType -> Maybe HTLCOutputType
    146 htlc_output_type t = case t of
    147   OutputHTLC h -> Just $ case htlc_direction h of
    148     HTLCOffered  -> HTLCOfferedOutput (htlc_cltv_expiry h)
    149     HTLCReceived -> HTLCReceivedOutput (htlc_cltv_expiry h)
    150   OutputToLocal      -> Nothing
    151   OutputToRemote     -> Nothing
    152   OutputLocalAnchor  -> Nothing
    153   OutputRemoteAnchor -> Nothing
    154 {-# INLINE htlc_output_type #-}
    155 
    156 -- | Penalty input weight for an HTLC output.
    157 --
    158 --   >>> htlc_output_type_weight Anchors (HTLCOfferedOutput (CltvExpiry 500))
    159 --   410
    160 htlc_output_type_weight :: CommitmentFormat -> HTLCOutputType -> Word64
    161 htlc_output_type_weight !fmt !t = case (fmt, t) of
    162   (StaticRemotekey, HTLCOfferedOutput _)  ->
    163     offered_htlc_penalty_input_weight
    164   (StaticRemotekey, HTLCReceivedOutput _) ->
    165     accepted_htlc_penalty_input_weight
    166   (Anchors, HTLCOfferedOutput _)          ->
    167     offered_htlc_penalty_input_weight_anchors
    168   (Anchors, HTLCReceivedOutput _)         ->
    169     accepted_htlc_penalty_input_weight_anchors
    170 {-# INLINE htlc_output_type_weight #-}
    171 
    172 -- spending transactions ----------------------------------------------
    173 
    174 -- | An unsigned spending transaction, with what the caller needs to
    175 --   sign its (single) input.
    176 --
    177 --   The caller signs the input's BIP143 sighash (ppad-tx's
    178 --   @sighash_segwit@ over 'stx_tx', input 0, 'stx_input_script',
    179 --   'stx_input_value' and 'stx_sighash_type') and assembles the
    180 --   witness with bolt3's witness functions.
    181 data SpendingTx = SpendingTx
    182   { stx_tx           :: !Tx
    183     -- ^ The unsigned transaction.
    184   , stx_input_script :: !Script
    185     -- ^ Witness script of the output being spent.
    186   , stx_input_value  :: {-# UNPACK #-} !Satoshi
    187     -- ^ Value of the output being spent.
    188   , stx_sighash_type :: !SighashType
    189     -- ^ Sighash type of the local signature.
    190   } deriving (Eq, Show, Generic)
    191 
    192 instance NFData SpendingTx where
    193   rnf (SpendingTx tx s v h) = rnf tx `seq` rnf s `seq` rnf v `seq` h `seq` ()
    194 
    195 -- revoked outputs ----------------------------------------------------
    196 
    197 -- | A revoked output that can be swept with a revocation key.
    198 data RevokedOutput = RevokedOutput
    199   { ro_outpoint :: !OutPoint
    200   , ro_value    :: {-# UNPACK #-} !Satoshi
    201   , ro_type     :: !RevokedOutputType
    202   } deriving (Eq, Show, Generic)
    203 
    204 instance NFData RevokedOutput
    205 
    206 -- | What kind of revoked output is being swept.
    207 data RevokedOutputType
    208   = RevokedToLocal
    209     -- ^ Revoked to_local output.
    210   | RevokedHTLC !HTLCOutputType
    211     -- ^ Revoked HTLC output.
    212   deriving (Eq, Show, Generic)
    213 
    214 instance NFData RevokedOutputType
    215 
    216 -- | Penalty input weight for a revoked output.
    217 revoked_output_weight :: CommitmentFormat -> RevokedOutput -> Word64
    218 revoked_output_weight !fmt !ro = case ro_type ro of
    219   RevokedToLocal ->
    220     to_local_penalty_input_weight
    221   RevokedHTLC !htype ->
    222     htlc_output_type_weight fmt htype
    223 {-# INLINE revoked_output_weight #-}
    224 
    225 -- weight constants (BOLT #5 Appendix A) ------------------------------
    226 
    227 -- | Expected weight of the to_local penalty transaction witness
    228 --   (160 weight units).
    229 to_local_penalty_witness_weight :: Word64
    230 to_local_penalty_witness_weight = 160
    231 
    232 -- | Expected weight of the offered_htlc penalty transaction
    233 --   witness (243 weight units), for a 133-byte witness script
    234 --   (no option_anchors).
    235 offered_htlc_penalty_witness_weight :: Word64
    236 offered_htlc_penalty_witness_weight = 243
    237 
    238 -- | Expected weight of the accepted_htlc penalty transaction
    239 --   witness (249 weight units), for a 139-byte witness script
    240 --   (no option_anchors).
    241 accepted_htlc_penalty_witness_weight :: Word64
    242 accepted_htlc_penalty_witness_weight = 249
    243 
    244 -- | Expected weight of the offered_htlc penalty transaction
    245 --   witness with option_anchors (246 weight units), whose witness
    246 --   script is 136 bytes.
    247 offered_htlc_penalty_witness_weight_anchors :: Word64
    248 offered_htlc_penalty_witness_weight_anchors = 246
    249 
    250 -- | Expected weight of the accepted_htlc penalty transaction
    251 --   witness with option_anchors (252 weight units), whose witness
    252 --   script is 142 bytes.
    253 accepted_htlc_penalty_witness_weight_anchors :: Word64
    254 accepted_htlc_penalty_witness_weight_anchors = 252
    255 
    256 -- | Weight of a to_local penalty input (164 + 160 = 324).
    257 to_local_penalty_input_weight :: Word64
    258 to_local_penalty_input_weight = 324
    259 
    260 -- | Weight of an offered_htlc penalty input (164 + 243 = 407).
    261 offered_htlc_penalty_input_weight :: Word64
    262 offered_htlc_penalty_input_weight = 407
    263 
    264 -- | Weight of an accepted_htlc penalty input (164 + 249 = 413).
    265 accepted_htlc_penalty_input_weight :: Word64
    266 accepted_htlc_penalty_input_weight = 413
    267 
    268 -- | Weight of an offered_htlc penalty input with option_anchors
    269 --   (164 + 246 = 410).
    270 offered_htlc_penalty_input_weight_anchors :: Word64
    271 offered_htlc_penalty_input_weight_anchors = 410
    272 
    273 -- | Weight of an accepted_htlc penalty input with option_anchors
    274 --   (164 + 252 = 416).
    275 accepted_htlc_penalty_input_weight_anchors :: Word64
    276 accepted_htlc_penalty_input_weight_anchors = 416
    277 
    278 -- | Weight of a to_remote P2WPKH input (108 + 164 = 272).
    279 to_remote_input_weight :: Word64
    280 to_remote_input_weight = 272
    281 
    282 -- | Base weight of a penalty transaction (4 * 53 + 2 = 214).
    283 --
    284 --   Non-witness: version (4) + input count (1) + output count (1) +
    285 --   value (8) + script length (1) + P2WSH script (34) + locktime (4)
    286 --   = 53 bytes. Witness header: 2 bytes.
    287 --
    288 --   The spending transactions here use it as their base weight
    289 --   whatever their destination script.
    290 penalty_tx_base_weight :: Word64
    291 penalty_tx_base_weight = 214
    292 
    293 -- | Maximum standard transaction weight (400,000).
    294 max_standard_weight :: Word64
    295 max_standard_weight = 400000
    296 
    297 -- fee calculation ----------------------------------------------------
    298 
    299 -- | The fee for a transaction of the given weight:
    300 --
    301 --   @fee = feerate_per_kw * weight / 1000@
    302 --
    303 --   rounded down, and saturating at 21 million BTC.
    304 --
    305 --   >>> spending_fee (FeeratePerKw 253) 324
    306 --   Satoshi 81
    307 spending_fee :: FeeratePerKw -> Word64 -> Satoshi
    308 spending_fee (FeeratePerKw !rate) !weight =
    309   let !f = toInteger rate * toInteger weight `quot` 1000
    310       !m = BOLT1.un_satoshi BOLT1.max_satoshi
    311   in  case BOLT1.satoshi (fromInteger (min f (toInteger m))) of
    312         Just s  -> s
    313         Nothing -> BOLT1.max_satoshi
    314 {-# INLINE spending_fee #-}