bolt5

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

Spend.hs (14064B)


      1 {-# OPTIONS_HADDOCK hide #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 
      4 -- |
      5 -- Module: Lightning.Protocol.BOLT5.Spend
      6 -- Copyright: (c) 2025 Jared Tobin
      7 -- License: MIT
      8 -- Maintainer: Jared Tobin <jared@ppad.tech>
      9 --
     10 -- Spending transaction construction for BOLT #5 on-chain
     11 -- transaction handling.
     12 --
     13 -- All functions produce unsigned t'SpendingTx' values. The caller
     14 -- signs (using the sighash metadata provided) and assembles the
     15 -- witness with bolt3's witness functions.
     16 
     17 module Lightning.Protocol.BOLT5.Spend (
     18     -- * Local commitment spends
     19     spend_to_local
     20   , spend_htlc_tx
     21   , spend_htlc_output
     22 
     23     -- * Remote commitment spends
     24   , spend_remote_htlc_timeout
     25   , spend_remote_htlc_preimage
     26 
     27     -- * Revoked commitment spends
     28   , spend_revoked_to_local
     29   , spend_revoked_htlc
     30   , spend_revoked_htlc_output
     31 
     32     -- * Anchor spends
     33   , spend_anchor_owner
     34   , spend_anchor_anyone
     35   ) where
     36 
     37 import Bitcoin.Prim.Tx (OutPoint, Tx(..), TxIn(..), TxOut(..), Witness(..))
     38 import Bitcoin.Prim.Tx.Sighash (SighashType(..))
     39 import qualified Data.ByteString as BS
     40 import Data.List.NonEmpty (NonEmpty(..))
     41 import Data.Word (Word32, Word64)
     42 import Lightning.Protocol.BOLT1 (PaymentHash, Satoshi)
     43 import qualified Lightning.Protocol.BOLT1 as BOLT1
     44 import Lightning.Protocol.BOLT3
     45 import Lightning.Protocol.BOLT5.Types
     46 
     47 -- local commitment spends --------------------------------------------
     48 
     49 -- | Spend the to_local output of our local commitment tx.
     50 --
     51 --   The input's nSequence is the to_self_delay, so the transaction
     52 --   is valid only once the delay has passed. The caller signs with
     53 --   the local delayed privkey and uses bolt3's
     54 --   'to_local_witness_spend'.
     55 --
     56 --   Returns 'Nothing' if the fee would exceed the output value, or
     57 --   leave an output below the destination's dust threshold.
     58 spend_to_local
     59   :: OutPoint
     60   -- ^ Outpoint of the to_local output.
     61   -> Satoshi
     62   -- ^ Value of the to_local output.
     63   -> RevocationPubkey
     64   -> ToSelfDelay
     65   -> LocalDelayedPubkey
     66   -> Script
     67   -- ^ Destination scriptPubKey.
     68   -> FeeratePerKw
     69   -> Maybe SpendingTx
     70 spend_to_local !op !value !revpk delay@(ToSelfDelay d) !delayedpk
     71     !dest !feerate =
     72   sweep op value (to_local_script revpk delay delayedpk)
     73     to_local_penalty_input_weight (fromIntegral d) 0 dest feerate
     74 
     75 -- | Build the HTLC-timeout (for an offered HTLC) or HTLC-success
     76 --   (for a received HTLC) transaction spending an HTLC output of
     77 --   our local commitment, with bolt3's 'build_htlc_tx'.
     78 --
     79 --   The sighash type is that of the local signature, which is
     80 --   always @SIGHASH_ALL@. The remote's @htlc_signature@ uses bolt3's
     81 --   'remote_htlc_sighash' (@SIGHASH_SINGLE|SIGHASH_ANYONECANPAY@
     82 --   under 'Anchors'). The witness is bolt3's 'htlc_timeout_witness'
     83 --   or 'htlc_success_witness'.
     84 --
     85 --   Under 'Anchors' the transaction pays no fee; it must be
     86 --   combined with other inputs before broadcast.
     87 --
     88 --   Returns 'Nothing' if bolt3 can't build the transaction (the fee
     89 --   exceeds the HTLC amount).
     90 spend_htlc_tx
     91   :: HTLCContext
     92   -> CommitmentKeys
     93   -- ^ Keys for our local commitment (for the witness script).
     94   -> Maybe SpendingTx
     95 spend_htlc_tx !ctx !keys = do
     96   htx <- build_htlc_tx ctx
     97   let !h = hc_htlc ctx
     98   pure SpendingTx
     99     { stx_tx           = htlc_to_tx htx
    100     , stx_input_script = htlc_script keys (hc_format ctx) h
    101     , stx_input_value  = BOLT1.msat_to_sat (htlc_amount_msat h)
    102     , stx_sighash_type = SIGHASH_ALL
    103     }
    104 
    105 -- | Spend the output of an HTLC-timeout or HTLC-success
    106 --   transaction after the CSV delay.
    107 --
    108 --   The output uses the to_local script, so this is
    109 --   'spend_to_local'. The caller signs with the local delayed
    110 --   privkey and uses bolt3's 'to_local_witness_spend'.
    111 spend_htlc_output
    112   :: OutPoint
    113   -- ^ Outpoint of the second-stage output.
    114   -> Satoshi
    115   -- ^ Value of the second-stage output.
    116   -> RevocationPubkey
    117   -> ToSelfDelay
    118   -> LocalDelayedPubkey
    119   -> Script
    120   -- ^ Destination scriptPubKey.
    121   -> FeeratePerKw
    122   -> Maybe SpendingTx
    123 spend_htlc_output = spend_to_local
    124 
    125 -- remote commitment spends -------------------------------------------
    126 
    127 -- | Spend an HTLC we offered directly, after it times out, from the
    128 --   remote commitment (where it is a received HTLC).
    129 --
    130 --   The locktime is the HTLC's CLTV expiry. The caller signs with
    131 --   our HTLC privkey and uses bolt3's
    132 --   'received_htlc_witness_timeout'.
    133 --
    134 --   Returns 'Nothing' if the fee would exceed the output value, or
    135 --   leave an output below the destination's dust threshold.
    136 spend_remote_htlc_timeout
    137   :: OutPoint
    138   -- ^ Outpoint of the HTLC output.
    139   -> Satoshi
    140   -- ^ Value of the HTLC output.
    141   -> HTLC
    142   -- ^ The HTLC being spent.
    143   -> CommitmentKeys
    144   -- ^ Keys for the remote commitment.
    145   -> CommitmentFormat
    146   -> Script
    147   -- ^ Destination scriptPubKey.
    148   -> FeeratePerKw
    149   -> Maybe SpendingTx
    150 spend_remote_htlc_timeout !op !value !htlc !keys !fmt !dest !feerate =
    151   let expiry@(CltvExpiry e) = htlc_cltv_expiry htlc
    152       !script = received_htlc_script (ck_revocation_pubkey keys)
    153         (ck_remote_htlc keys) (ck_local_htlc keys)
    154         (htlc_payment_hash htlc) expiry fmt
    155   in  sweep op value script
    156         (htlc_output_type_weight fmt (HTLCReceivedOutput expiry))
    157         (remote_htlc_sequence fmt) e dest feerate
    158 
    159 -- | Spend an HTLC we received directly with the preimage, from the
    160 --   remote commitment (where it is an offered HTLC).
    161 --
    162 --   The caller signs with our HTLC privkey and uses bolt3's
    163 --   'offered_htlc_witness_preimage'.
    164 --
    165 --   Returns 'Nothing' if the fee would exceed the output value, or
    166 --   leave an output below the destination's dust threshold.
    167 spend_remote_htlc_preimage
    168   :: OutPoint
    169   -- ^ Outpoint of the HTLC output.
    170   -> Satoshi
    171   -- ^ Value of the HTLC output.
    172   -> HTLC
    173   -- ^ The HTLC being spent.
    174   -> CommitmentKeys
    175   -- ^ Keys for the remote commitment.
    176   -> CommitmentFormat
    177   -> Script
    178   -- ^ Destination scriptPubKey.
    179   -> FeeratePerKw
    180   -> Maybe SpendingTx
    181 spend_remote_htlc_preimage !op !value !htlc !keys !fmt !dest !feerate =
    182   let !expiry = htlc_cltv_expiry htlc
    183       !script = offered_htlc_script (ck_revocation_pubkey keys)
    184         (ck_remote_htlc keys) (ck_local_htlc keys)
    185         (htlc_payment_hash htlc) fmt
    186   in  sweep op value script
    187         (htlc_output_type_weight fmt (HTLCOfferedOutput expiry))
    188         (remote_htlc_sequence fmt) 0 dest feerate
    189 
    190 -- revoked commitment spends ------------------------------------------
    191 
    192 -- | Spend a revoked to_local output with the revocation key.
    193 --
    194 --   The caller signs with the revocation privkey and uses bolt3's
    195 --   'to_local_witness_revoke'.
    196 --
    197 --   Returns 'Nothing' if the fee would exceed the output value, or
    198 --   leave an output below the destination's dust threshold.
    199 spend_revoked_to_local
    200   :: OutPoint
    201   -- ^ Outpoint of the to_local output.
    202   -> Satoshi
    203   -- ^ Value of the to_local output.
    204   -> RevocationPubkey
    205   -> ToSelfDelay
    206   -> LocalDelayedPubkey
    207   -> Script
    208   -- ^ Destination scriptPubKey.
    209   -> FeeratePerKw
    210   -> Maybe SpendingTx
    211 spend_revoked_to_local !op !value !revpk !delay !delayedpk !dest
    212     !feerate =
    213   sweep op value (to_local_script revpk delay delayedpk)
    214     to_local_penalty_input_weight 0xFFFFFFFF 0 dest feerate
    215 
    216 -- | Spend a revoked HTLC output with the revocation key.
    217 --
    218 --   The caller signs with the revocation privkey and uses bolt3's
    219 --   'offered_htlc_witness_revoke' or 'received_htlc_witness_revoke',
    220 --   according to the output type.
    221 --
    222 --   Returns 'Nothing' if the fee would exceed the output value, or
    223 --   leave an output below the destination's dust threshold.
    224 spend_revoked_htlc
    225   :: OutPoint
    226   -- ^ Outpoint of the HTLC output.
    227   -> Satoshi
    228   -- ^ Value of the HTLC output.
    229   -> HTLCOutputType
    230   -- ^ Whether offered or received HTLC.
    231   -> RevocationPubkey
    232   -> CommitmentKeys
    233   -> CommitmentFormat
    234   -> PaymentHash
    235   -> Script
    236   -- ^ Destination scriptPubKey.
    237   -> FeeratePerKw
    238   -> Maybe SpendingTx
    239 spend_revoked_htlc !op !value !htype !revpk !keys !fmt !ph !dest
    240     !feerate =
    241   let !remote = ck_remote_htlc keys
    242       !local = ck_local_htlc keys
    243       !script = case htype of
    244         HTLCOfferedOutput _ ->
    245           offered_htlc_script revpk remote local ph fmt
    246         HTLCReceivedOutput expiry ->
    247           received_htlc_script revpk remote local ph expiry fmt
    248   in  sweep op value script (htlc_output_type_weight fmt htype)
    249         0xFFFFFFFF 0 dest feerate
    250 
    251 -- | Spend the output of a revoked HTLC-timeout or HTLC-success
    252 --   transaction with the revocation key.
    253 --
    254 --   The output uses the to_local script, so this is
    255 --   'spend_revoked_to_local'. The caller signs with the revocation
    256 --   privkey and uses bolt3's 'to_local_witness_revoke'.
    257 spend_revoked_htlc_output
    258   :: OutPoint
    259   -- ^ Outpoint of the second-stage output.
    260   -> Satoshi
    261   -- ^ Value of the second-stage output.
    262   -> RevocationPubkey
    263   -> ToSelfDelay
    264   -> LocalDelayedPubkey
    265   -> Script
    266   -- ^ Destination scriptPubKey.
    267   -> FeeratePerKw
    268   -> Maybe SpendingTx
    269 spend_revoked_htlc_output = spend_revoked_to_local
    270 
    271 -- anchor spends ------------------------------------------------------
    272 
    273 -- | Spend an anchor output as its owner (immediately).
    274 --
    275 --   The transaction pays no fee and has a single input: it must be
    276 --   combined with other inputs before broadcast. The caller signs
    277 --   with the funding privkey and uses bolt3's
    278 --   'anchor_witness_owner'.
    279 spend_anchor_owner
    280   :: OutPoint
    281   -- ^ Outpoint of the anchor output.
    282   -> Satoshi
    283   -- ^ Value of the anchor output (330 sats).
    284   -> FundingPubkey
    285   -> Script
    286   -- ^ Destination scriptPubKey.
    287   -> SpendingTx
    288 spend_anchor_owner !op !value !fundpk !dest =
    289   SpendingTx (mk_spending_tx op 0xFFFFFFFE dest value 0)
    290     (anchor_script fundpk) value SIGHASH_ALL
    291 
    292 -- | Spend an anchor output as anyone (16 blocks after the
    293 --   commitment confirms).
    294 --
    295 --   As 'spend_anchor_owner', the transaction pays no fee. The
    296 --   witness is bolt3's 'anchor_witness_anyone', which needs no
    297 --   signature.
    298 spend_anchor_anyone
    299   :: OutPoint
    300   -- ^ Outpoint of the anchor output.
    301   -> Satoshi
    302   -- ^ Value of the anchor output (330 sats).
    303   -> FundingPubkey
    304   -> Script
    305   -- ^ Destination scriptPubKey.
    306   -> SpendingTx
    307 spend_anchor_anyone !op !value !fundpk !dest =
    308   SpendingTx (mk_spending_tx op 16 dest value 0)
    309     (anchor_script fundpk) value SIGHASH_ALL
    310 
    311 -- internal helpers ---------------------------------------------------
    312 
    313 -- | A single-input, single-output sweep paying the input value, less
    314 --   the fee for the given input weight, to the destination.
    315 sweep
    316   :: OutPoint
    317   -> Satoshi      -- ^ input value
    318   -> Script       -- ^ input witness script
    319   -> Word64       -- ^ input weight
    320   -> Word32       -- ^ input nSequence
    321   -> Word32       -- ^ locktime
    322   -> Script       -- ^ destination scriptPubKey
    323   -> FeeratePerKw
    324   -> Maybe SpendingTx
    325 sweep !op !value !script !weight !sq !locktime !dest !feerate = do
    326   let !fee = spending_fee feerate (weight + penalty_tx_base_weight)
    327   out <- sweep_value value fee dest
    328   pure (SpendingTx (mk_spending_tx op sq dest out locktime) script
    329           value SIGHASH_ALL)
    330 
    331 -- | Value of a sweep's single output: the input value less the fee.
    332 --
    333 --   'Nothing' if the fee exceeds the input value, or if the output
    334 --   would be below the dust threshold of its script.
    335 sweep_value :: Satoshi -> Satoshi -> Script -> Maybe Satoshi
    336 sweep_value !value !fee (Script dest) = do
    337   out <- BOLT1.sub_sat value fee
    338   if   BOLT1.un_satoshi out < dust_threshold dest
    339   then Nothing
    340   else Just out
    341 
    342 -- | Dust threshold, in satoshis, for an output paying to a script.
    343 --
    344 --   As Bitcoin Core computes it for spendable outputs at the default
    345 --   dust relay feerate of 3 sat/vbyte: three times the size of the
    346 --   output plus that of an input spending it (67 vbytes for a
    347 --   witness program, 148 otherwise). This gives 546 sats for P2PKH,
    348 --   540 for P2SH, 294 for P2WPKH, and 330 for P2WSH and P2TR.
    349 dust_threshold :: BS.ByteString -> Word64
    350 dust_threshold s =
    351   let !len = fromIntegral (BS.length s) :: Word64
    352       !len_size
    353         | len < 0xfd = 1
    354         | len <= 0xffff = 3
    355         | otherwise = 5
    356       !spend_size = if is_witness_program s then 67 else 148
    357   in  3 * (8 + len_size + len + spend_size)
    358 
    359 -- | Whether a script is a segwit witness program: a version opcode
    360 --   (OP_0 or OP_1 to OP_16) followed by a single 2-40 byte push.
    361 is_witness_program :: BS.ByteString -> Bool
    362 is_witness_program s = case BS.unpack (BS.take 2 s) of
    363   [version, push_len] ->
    364        (version == 0x00 || (version >= 0x51 && version <= 0x60))
    365     && push_len >= 2 && push_len <= 40
    366     && fromIntegral push_len + 2 == BS.length s
    367   _ -> False
    368 
    369 -- | The nSequence of a direct HTLC spend from the remote commitment:
    370 --   1 under 'Anchors' (for the HTLC scripts' CSV), 0 otherwise.
    371 remote_htlc_sequence :: CommitmentFormat -> Word32
    372 remote_htlc_sequence fmt = case fmt of
    373   StaticRemotekey -> 0
    374   Anchors         -> 1
    375 
    376 -- | The witness script of an HTLC output of a commitment with the
    377 --   given keys.
    378 htlc_script :: CommitmentKeys -> CommitmentFormat -> HTLC -> Script
    379 htlc_script !keys !fmt !h =
    380   let !revpk = ck_revocation_pubkey keys
    381       !remote = ck_remote_htlc keys
    382       !local = ck_local_htlc keys
    383       !ph = htlc_payment_hash h
    384   in  case htlc_direction h of
    385         HTLCOffered  -> offered_htlc_script revpk remote local ph fmt
    386         HTLCReceived ->
    387           received_htlc_script revpk remote local ph
    388             (htlc_cltv_expiry h) fmt
    389 
    390 -- | Build a version 2, single-input, single-output transaction.
    391 mk_spending_tx
    392   :: OutPoint     -- ^ input outpoint
    393   -> Word32       -- ^ input nSequence
    394   -> Script       -- ^ output scriptPubKey
    395   -> Satoshi      -- ^ output value
    396   -> Word32       -- ^ locktime
    397   -> Tx
    398 mk_spending_tx !op !sq (Script dest) !value !locktime = Tx
    399   { tx_version   = 2
    400   , tx_inputs    = TxIn
    401       { txin_prevout    = op
    402       , txin_script_sig = BS.empty
    403       , txin_sequence   = sq
    404       , txin_witness    = Witness []
    405       } :| []
    406   , tx_outputs   = TxOut
    407       { txout_value         = BOLT1.un_satoshi value
    408       , txout_script_pubkey = dest
    409       } :| []
    410   , tx_locktime  = locktime
    411   }