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

Scripts.hs (18717B)


      1 {-# OPTIONS_HADDOCK prune #-}
      2 {-# LANGUAGE BangPatterns #-}
      3 {-# LANGUAGE OverloadedStrings #-}
      4 
      5 -- |
      6 -- Module: Lightning.Protocol.BOLT3.Scripts
      7 -- Copyright: (c) 2025 Jared Tobin
      8 -- License: MIT
      9 -- Maintainer: Jared Tobin <jared@ppad.tech>
     10 --
     11 -- Script templates for BOLT #3 transaction outputs.
     12 --
     13 -- Includes witness scripts for:
     14 --
     15 -- * Funding output (2-of-2 multisig)
     16 -- * to_local output (revocable with CSV delay)
     17 -- * to_remote output (P2WPKH or anchored)
     18 -- * Anchor outputs
     19 -- * Offered HTLC outputs
     20 -- * Received HTLC outputs
     21 -- * HTLC-timeout/success output (revocable with delay)
     22 
     23 module Lightning.Protocol.BOLT3.Scripts (
     24     -- * Funding output
     25     funding_script
     26   , funding_witness
     27 
     28     -- * to_local output
     29   , to_local_script
     30   , to_local_witness_spend
     31   , to_local_witness_revoke
     32 
     33     -- * to_remote output
     34   , to_remote_script
     35   , to_remote_witness
     36 
     37     -- * Anchor outputs
     38   , anchor_script
     39   , anchor_witness_owner
     40   , anchor_witness_anyone
     41 
     42     -- * Offered HTLC output
     43   , offered_htlc_script
     44   , offered_htlc_witness_preimage
     45   , offered_htlc_witness_revoke
     46 
     47     -- * Received HTLC output
     48   , received_htlc_script
     49   , received_htlc_witness_timeout
     50   , received_htlc_witness_revoke
     51 
     52     -- * HTLC-timeout/success output (same as to_local)
     53   , htlc_output_script
     54   , htlc_output_witness_spend
     55   , htlc_output_witness_revoke
     56 
     57     -- * P2WSH helpers
     58   , to_p2wsh
     59   , witness_script_hash
     60   ) where
     61 
     62 import Data.Bits ((.&.), shiftR)
     63 import Data.Word (Word8, Word16, Word32)
     64 import qualified Data.ByteString as BS
     65 import qualified Data.ByteString.Builder as BSB
     66 import qualified Data.ByteString.Lazy as BSL
     67 import qualified Crypto.Hash.SHA256 as SHA256
     68 import qualified Crypto.Hash.RIPEMD160 as RIPEMD160
     69 import Lightning.Protocol.BOLT3.Types
     70 
     71 -- opcodes ---------------------------------------------------------------------
     72 
     73 -- | OP_0 / OP_FALSE (0x00)
     74 op_0 :: Word8
     75 op_0 = 0x00
     76 
     77 -- | OP_PUSHDATA for 1-75 bytes just uses the length as opcode
     78 -- For data <=75 bytes, opcode is just the length
     79 
     80 -- | OP_IF (0x63)
     81 op_if :: Word8
     82 op_if = 0x63
     83 
     84 -- | OP_NOTIF (0x64)
     85 op_notif :: Word8
     86 op_notif = 0x64
     87 
     88 -- | OP_ELSE (0x67)
     89 op_else :: Word8
     90 op_else = 0x67
     91 
     92 -- | OP_ENDIF (0x68)
     93 op_endif :: Word8
     94 op_endif = 0x68
     95 
     96 -- | OP_DROP (0x75)
     97 op_drop :: Word8
     98 op_drop = 0x75
     99 
    100 -- | OP_DUP (0x76)
    101 op_dup :: Word8
    102 op_dup = 0x76
    103 
    104 -- | OP_SWAP (0x7c)
    105 op_swap :: Word8
    106 op_swap = 0x7c
    107 
    108 -- | OP_SIZE (0x82)
    109 op_size :: Word8
    110 op_size = 0x82
    111 
    112 -- | OP_EQUAL (0x87)
    113 op_equal :: Word8
    114 op_equal = 0x87
    115 
    116 -- | OP_EQUALVERIFY (0x88)
    117 op_equalverify :: Word8
    118 op_equalverify = 0x88
    119 
    120 -- | OP_IFDUP (0x73)
    121 op_ifdup :: Word8
    122 op_ifdup = 0x73
    123 
    124 -- | OP_HASH160 (0xa9)
    125 op_hash160 :: Word8
    126 op_hash160 = 0xa9
    127 
    128 -- | OP_CHECKSIG (0xac)
    129 op_checksig :: Word8
    130 op_checksig = 0xac
    131 
    132 -- | OP_CHECKSIGVERIFY (0xad)
    133 op_checksigverify :: Word8
    134 op_checksigverify = 0xad
    135 
    136 -- | OP_CHECKMULTISIG (0xae)
    137 op_checkmultisig :: Word8
    138 op_checkmultisig = 0xae
    139 
    140 -- | OP_CHECKLOCKTIMEVERIFY (0xb1)
    141 op_checklocktimeverify :: Word8
    142 op_checklocktimeverify = 0xb1
    143 
    144 -- | OP_CHECKSEQUENCEVERIFY (0xb2)
    145 op_checksequenceverify :: Word8
    146 op_checksequenceverify = 0xb2
    147 
    148 -- | OP_1 (0x51)
    149 op_1 :: Word8
    150 op_1 = 0x51
    151 
    152 -- | OP_2 (0x52)
    153 op_2 :: Word8
    154 op_2 = 0x52
    155 
    156 -- | OP_16 (0x60)
    157 op_16 :: Word8
    158 op_16 = 0x60
    159 
    160 -- helpers ---------------------------------------------------------------------
    161 
    162 -- | Push a bytestring onto the stack (handles length encoding).
    163 --
    164 -- For data <= 75 bytes, the length itself is the opcode.
    165 push_data :: BS.ByteString -> BSB.Builder
    166 push_data !bs
    167   | len <= 75 = BSB.word8 (fromIntegral len) <> BSB.byteString bs
    168   | len <= 255 = BSB.word8 0x4c <> BSB.word8 (fromIntegral len)
    169                  <> BSB.byteString bs
    170   | len <= 65535 = BSB.word8 0x4d <> BSB.word16LE (fromIntegral len)
    171                    <> BSB.byteString bs
    172   | otherwise = BSB.word8 0x4e <> BSB.word32LE (fromIntegral len)
    173                 <> BSB.byteString bs
    174   where
    175     !len = BS.length bs
    176 {-# INLINE push_data #-}
    177 
    178 -- | Encode a Word16 as minimal script number (for CSV delays).
    179 push_csv_delay :: Word16 -> BSB.Builder
    180 push_csv_delay !n
    181   | n == 0 = BSB.word8 op_0
    182   | n <= 16 = BSB.word8 (0x50 + fromIntegral n)
    183   | n <= 0x7f = push_data (BS.singleton (fromIntegral n))
    184   | n <= 0x7fff = push_data (BS.pack [lo, hi])
    185   | otherwise = push_data (BS.pack [lo, hi, 0x00])  -- need sign byte
    186   where
    187     !lo = fromIntegral (n .&. 0xff)
    188     !hi = fromIntegral ((n `shiftR` 8) .&. 0xff)
    189 {-# INLINE push_csv_delay #-}
    190 
    191 -- | Encode a Word32 as minimal script number (for CLTV).
    192 push_cltv :: Word32 -> BSB.Builder
    193 push_cltv !n
    194   | n == 0 = BSB.word8 op_0
    195   | n <= 16 = BSB.word8 (0x50 + fromIntegral n)
    196   | otherwise = push_data (encode_scriptnum n)
    197   where
    198     encode_scriptnum :: Word32 -> BS.ByteString
    199     encode_scriptnum 0 = BS.empty
    200     encode_scriptnum !v =
    201       let -- Build bytes little-endian (LSB first)
    202           go :: Word32 -> [Word8]
    203           go 0 = []
    204           go !x = fromIntegral (x .&. 0xff) : go (x `shiftR` 8)
    205           !bytes = go v
    206           -- If MSB has high bit set, need 0x00 suffix for positive numbers
    207           !result = case reverse bytes of
    208             [] -> bytes
    209             (msb:_) | msb .&. 0x80 /= 0 -> bytes ++ [0x00]
    210             _ -> bytes
    211       in BS.pack result
    212 {-# INLINE push_cltv #-}
    213 
    214 -- | Build script from builder.
    215 build_script :: BSB.Builder -> Script
    216 build_script = Script . BSL.toStrict . BSB.toLazyByteString
    217 {-# INLINE build_script #-}
    218 
    219 -- | HASH160 = RIPEMD160(SHA256(x))
    220 hash160 :: BS.ByteString -> BS.ByteString
    221 hash160 = RIPEMD160.hash . SHA256.hash
    222 {-# INLINE hash160 #-}
    223 
    224 -- P2WSH helpers ---------------------------------------------------------------
    225 
    226 -- | Compute SHA256 hash of a witness script.
    227 --
    228 -- >>> witness_script_hash (Script "some_script")
    229 -- <32-byte SHA256 hash>
    230 witness_script_hash :: Script -> BS.ByteString
    231 witness_script_hash (Script !s) = SHA256.hash s
    232 {-# INLINE witness_script_hash #-}
    233 
    234 -- | Convert a witness script to P2WSH scriptPubKey.
    235 --
    236 -- P2WSH format: OP_0 <32-byte-hash>
    237 --
    238 -- >>> to_p2wsh some_witness_script
    239 -- Script "\x00\x20<32-byte-hash>"
    240 to_p2wsh :: Script -> Script
    241 to_p2wsh !script =
    242   let !h = witness_script_hash script
    243   in build_script (BSB.word8 op_0 <> push_data h)
    244 {-# INLINE to_p2wsh #-}
    245 
    246 -- funding output --------------------------------------------------------------
    247 
    248 -- | Funding output witness script (2-of-2 multisig).
    249 --
    250 -- Script: @2 <pubkey1> <pubkey2> 2 OP_CHECKMULTISIG@
    251 --
    252 -- Where pubkey1 is lexicographically lesser.
    253 --
    254 -- >>> funding_script pk1 pk2
    255 -- Script "R!<pk_lesser>!<pk_greater>R\xae"
    256 funding_script :: FundingPubkey -> FundingPubkey -> Script
    257 funding_script (FundingPubkey (Pubkey !pk1)) (FundingPubkey (Pubkey !pk2)) =
    258   let (!lesser, !greater) = if pk1 <= pk2 then (pk1, pk2) else (pk2, pk1)
    259   in build_script $
    260        BSB.word8 op_2
    261        <> push_data lesser
    262        <> push_data greater
    263        <> BSB.word8 op_2
    264        <> BSB.word8 op_checkmultisig
    265 
    266 -- | Witness for spending funding output.
    267 --
    268 -- Witness: @0 <sig1> <sig2>@
    269 --
    270 -- Signatures ordered to match pubkey order in script.
    271 --
    272 -- >>> funding_witness sig1 sig2
    273 -- Witness ["", sig1, sig2]
    274 funding_witness :: BS.ByteString -> BS.ByteString -> Witness
    275 funding_witness !sig1 !sig2 = Witness [BS.empty, sig1, sig2]
    276 
    277 -- to_local output -------------------------------------------------------------
    278 
    279 -- | to_local witness script (revocable with CSV delay).
    280 --
    281 -- Script:
    282 --
    283 -- @
    284 -- OP_IF
    285 --     <revocationpubkey>
    286 -- OP_ELSE
    287 --     <to_self_delay>
    288 --     OP_CHECKSEQUENCEVERIFY
    289 --     OP_DROP
    290 --     <local_delayedpubkey>
    291 -- OP_ENDIF
    292 -- OP_CHECKSIG
    293 -- @
    294 --
    295 -- >>> to_local_script revpk delay localpk
    296 -- Script "c!<revpk>g<delay>\xb2u!<localpk>h\xac"
    297 to_local_script
    298   :: RevocationPubkey
    299   -> ToSelfDelay
    300   -> LocalDelayedPubkey
    301   -> Script
    302 to_local_script
    303   (RevocationPubkey (Pubkey !revpk))
    304   (ToSelfDelay !delay)
    305   (LocalDelayedPubkey (Pubkey !localpk)) =
    306     build_script $
    307       BSB.word8 op_if
    308       <> push_data revpk
    309       <> BSB.word8 op_else
    310       <> push_csv_delay delay
    311       <> BSB.word8 op_checksequenceverify
    312       <> BSB.word8 op_drop
    313       <> push_data localpk
    314       <> BSB.word8 op_endif
    315       <> BSB.word8 op_checksig
    316 
    317 -- | Witness for delayed spend of to_local output.
    318 --
    319 -- Input nSequence must be set to to_self_delay.
    320 --
    321 -- Witness: @<local_delayedsig> <>@
    322 --
    323 -- >>> to_local_witness_spend sig
    324 -- Witness [sig, ""]
    325 to_local_witness_spend :: BS.ByteString -> Witness
    326 to_local_witness_spend !sig = Witness [sig, BS.empty]
    327 
    328 -- | Witness for revocation spend of to_local output.
    329 --
    330 -- Witness: @<revocation_sig> 1@
    331 --
    332 -- >>> to_local_witness_revoke sig
    333 -- Witness [sig, "\x01"]
    334 to_local_witness_revoke :: BS.ByteString -> Witness
    335 to_local_witness_revoke !sig = Witness [sig, BS.singleton 0x01]
    336 
    337 -- to_remote output ------------------------------------------------------------
    338 
    339 -- | to_remote witness script.
    340 --
    341 -- With option_anchors:
    342 --
    343 -- @
    344 -- <remotepubkey> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY
    345 -- @
    346 --
    347 -- Without option_anchors: P2WPKH (just the pubkey hash).
    348 --
    349 -- >>> to_remote_script pk (ChannelFeatures True)
    350 -- Script "!<pk>\xadQ\xb2"
    351 to_remote_script :: RemotePubkey -> ChannelFeatures -> Script
    352 to_remote_script (RemotePubkey (Pubkey !pk)) !features
    353   | has_anchors features =
    354       -- Anchors: script with 1-block CSV
    355       build_script $
    356         push_data pk
    357         <> BSB.word8 op_checksigverify
    358         <> BSB.word8 op_1
    359         <> BSB.word8 op_checksequenceverify
    360   | otherwise =
    361       -- No anchors: P2WPKH (OP_0 <20-byte-hash>)
    362       let !h = hash160 pk
    363       in build_script (BSB.word8 op_0 <> push_data h)
    364 
    365 -- | Witness for spending to_remote output.
    366 --
    367 -- With option_anchors (P2WSH), input nSequence must be 1.
    368 -- Witness: @<remote_sig>@ (witness script appended by caller)
    369 --
    370 -- Without option_anchors (P2WPKH):
    371 -- Witness: @<remote_sig> <remotepubkey>@
    372 --
    373 -- >>> to_remote_witness sig pk (ChannelFeatures False)
    374 -- Witness [sig, pk]
    375 to_remote_witness :: BS.ByteString -> RemotePubkey -> ChannelFeatures -> Witness
    376 to_remote_witness !sig (RemotePubkey (Pubkey !pk)) !features
    377   | has_anchors features = Witness [sig]
    378   | otherwise = Witness [sig, pk]
    379 
    380 -- anchor outputs --------------------------------------------------------------
    381 
    382 -- | Anchor output witness script.
    383 --
    384 -- Script:
    385 --
    386 -- @
    387 -- <funding_pubkey> OP_CHECKSIG OP_IFDUP
    388 -- OP_NOTIF
    389 --     OP_16 OP_CHECKSEQUENCEVERIFY
    390 -- OP_ENDIF
    391 -- @
    392 --
    393 -- >>> anchor_script fundpk
    394 -- Script "!<fundpk>\xac\x73d`\xb2h"
    395 anchor_script :: FundingPubkey -> Script
    396 anchor_script (FundingPubkey (Pubkey !pk)) =
    397   build_script $
    398     push_data pk
    399     <> BSB.word8 op_checksig
    400     <> BSB.word8 op_ifdup
    401     <> BSB.word8 op_notif
    402     <> BSB.word8 op_16
    403     <> BSB.word8 op_checksequenceverify
    404     <> BSB.word8 op_endif
    405 
    406 -- | Witness for owner to spend anchor output.
    407 --
    408 -- Witness: @<sig>@
    409 --
    410 -- >>> anchor_witness_owner sig
    411 -- Witness [sig]
    412 anchor_witness_owner :: BS.ByteString -> Witness
    413 anchor_witness_owner !sig = Witness [sig]
    414 
    415 -- | Witness for anyone to sweep anchor output after 16 blocks.
    416 --
    417 -- Witness: @<>@
    418 --
    419 -- >>> anchor_witness_anyone
    420 -- Witness [""]
    421 anchor_witness_anyone :: Witness
    422 anchor_witness_anyone = Witness [BS.empty]
    423 
    424 -- offered HTLC output ---------------------------------------------------------
    425 
    426 -- | Offered HTLC witness script.
    427 --
    428 -- Without option_anchors:
    429 --
    430 -- @
    431 -- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
    432 -- OP_IF
    433 --     OP_CHECKSIG
    434 -- OP_ELSE
    435 --     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    436 --     OP_NOTIF
    437 --         OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    438 --     OP_ELSE
    439 --         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
    440 --         OP_CHECKSIG
    441 --     OP_ENDIF
    442 -- OP_ENDIF
    443 -- @
    444 --
    445 -- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before
    446 -- final OP_ENDIF.
    447 offered_htlc_script
    448   :: RevocationPubkey
    449   -> RemoteHtlcPubkey
    450   -> LocalHtlcPubkey
    451   -> PaymentHash
    452   -> ChannelFeatures
    453   -> Script
    454 offered_htlc_script
    455   (RevocationPubkey (Pubkey !revpk))
    456   (RemoteHtlcPubkey (Pubkey !remotepk))
    457   (LocalHtlcPubkey (Pubkey !localpk))
    458   (PaymentHash !ph)
    459   !features =
    460     let !revpk_hash = hash160 revpk
    461         !payment_hash160 = RIPEMD160.hash ph
    462         !csv_suffix = if has_anchors features
    463           then BSB.word8 op_1
    464                <> BSB.word8 op_checksequenceverify
    465                <> BSB.word8 op_drop
    466           else mempty
    467     in build_script $
    468          -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL
    469          BSB.word8 op_dup
    470          <> BSB.word8 op_hash160
    471          <> push_data revpk_hash
    472          <> BSB.word8 op_equal
    473          -- OP_IF OP_CHECKSIG
    474          <> BSB.word8 op_if
    475          <> BSB.word8 op_checksig
    476          -- OP_ELSE
    477          <> BSB.word8 op_else
    478          -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    479          <> push_data remotepk
    480          <> BSB.word8 op_swap
    481          <> BSB.word8 op_size
    482          <> push_data (BS.singleton 32)
    483          <> BSB.word8 op_equal
    484          -- OP_NOTIF
    485          <> BSB.word8 op_notif
    486          -- OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    487          <> BSB.word8 op_drop
    488          <> BSB.word8 op_2
    489          <> BSB.word8 op_swap
    490          <> push_data localpk
    491          <> BSB.word8 op_2
    492          <> BSB.word8 op_checkmultisig
    493          -- OP_ELSE
    494          <> BSB.word8 op_else
    495          -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY OP_CHECKSIG
    496          <> BSB.word8 op_hash160
    497          <> push_data payment_hash160
    498          <> BSB.word8 op_equalverify
    499          <> BSB.word8 op_checksig
    500          -- OP_ENDIF
    501          <> BSB.word8 op_endif
    502          -- CSV suffix for anchors
    503          <> csv_suffix
    504          -- OP_ENDIF
    505          <> BSB.word8 op_endif
    506 
    507 -- | Witness for remote node to claim offered HTLC with preimage.
    508 --
    509 -- With option_anchors, input nSequence must be 1.
    510 --
    511 -- Witness: @<remotehtlcsig> <payment_preimage>@
    512 --
    513 -- >>> offered_htlc_witness_preimage sig preimage
    514 -- Witness [sig, preimage]
    515 offered_htlc_witness_preimage
    516   :: BS.ByteString -> PaymentPreimage -> Witness
    517 offered_htlc_witness_preimage !sig (PaymentPreimage !preimage) =
    518   Witness [sig, preimage]
    519 
    520 -- | Witness for revocation spend of offered HTLC.
    521 --
    522 -- Witness: @<revocation_sig> <revocationpubkey>@
    523 --
    524 -- >>> offered_htlc_witness_revoke sig revpk
    525 -- Witness [sig, revpk]
    526 offered_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness
    527 offered_htlc_witness_revoke !sig (Pubkey !revpk) = Witness [sig, revpk]
    528 
    529 -- received HTLC output --------------------------------------------------------
    530 
    531 -- | Received HTLC witness script.
    532 --
    533 -- Without option_anchors:
    534 --
    535 -- @
    536 -- OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
    537 -- OP_IF
    538 --     OP_CHECKSIG
    539 -- OP_ELSE
    540 --     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    541 --     OP_IF
    542 --         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
    543 --         2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    544 --     OP_ELSE
    545 --         OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
    546 --         OP_CHECKSIG
    547 --     OP_ENDIF
    548 -- OP_ENDIF
    549 -- @
    550 --
    551 -- With option_anchors, adds @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ before
    552 -- final OP_ENDIF.
    553 received_htlc_script
    554   :: RevocationPubkey
    555   -> RemoteHtlcPubkey
    556   -> LocalHtlcPubkey
    557   -> PaymentHash
    558   -> CltvExpiry
    559   -> ChannelFeatures
    560   -> Script
    561 received_htlc_script
    562   (RevocationPubkey (Pubkey !revpk))
    563   (RemoteHtlcPubkey (Pubkey !remotepk))
    564   (LocalHtlcPubkey (Pubkey !localpk))
    565   (PaymentHash !ph)
    566   (CltvExpiry !expiry)
    567   !features =
    568     let !revpk_hash = hash160 revpk
    569         !payment_hash160 = RIPEMD160.hash ph
    570         !csv_suffix = if has_anchors features
    571           then BSB.word8 op_1
    572                <> BSB.word8 op_checksequenceverify
    573                <> BSB.word8 op_drop
    574           else mempty
    575     in build_script $
    576          -- OP_DUP OP_HASH160 <revpk_hash> OP_EQUAL
    577          BSB.word8 op_dup
    578          <> BSB.word8 op_hash160
    579          <> push_data revpk_hash
    580          <> BSB.word8 op_equal
    581          -- OP_IF OP_CHECKSIG
    582          <> BSB.word8 op_if
    583          <> BSB.word8 op_checksig
    584          -- OP_ELSE
    585          <> BSB.word8 op_else
    586          -- <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    587          <> push_data remotepk
    588          <> BSB.word8 op_swap
    589          <> BSB.word8 op_size
    590          <> push_data (BS.singleton 32)
    591          <> BSB.word8 op_equal
    592          -- OP_IF
    593          <> BSB.word8 op_if
    594          -- OP_HASH160 <payment_hash160> OP_EQUALVERIFY
    595          <> BSB.word8 op_hash160
    596          <> push_data payment_hash160
    597          <> BSB.word8 op_equalverify
    598          -- 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
    599          <> BSB.word8 op_2
    600          <> BSB.word8 op_swap
    601          <> push_data localpk
    602          <> BSB.word8 op_2
    603          <> BSB.word8 op_checkmultisig
    604          -- OP_ELSE
    605          <> BSB.word8 op_else
    606          -- OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG
    607          <> BSB.word8 op_drop
    608          <> push_cltv expiry
    609          <> BSB.word8 op_checklocktimeverify
    610          <> BSB.word8 op_drop
    611          <> BSB.word8 op_checksig
    612          -- OP_ENDIF
    613          <> BSB.word8 op_endif
    614          -- CSV suffix for anchors
    615          <> csv_suffix
    616          -- OP_ENDIF
    617          <> BSB.word8 op_endif
    618 
    619 -- | Witness for remote node to timeout received HTLC.
    620 --
    621 -- With option_anchors, input nSequence must be 1.
    622 --
    623 -- Witness: @<remotehtlcsig> <>@
    624 --
    625 -- >>> received_htlc_witness_timeout sig
    626 -- Witness [sig, ""]
    627 received_htlc_witness_timeout :: BS.ByteString -> Witness
    628 received_htlc_witness_timeout !sig = Witness [sig, BS.empty]
    629 
    630 -- | Witness for revocation spend of received HTLC.
    631 --
    632 -- Witness: @<revocation_sig> <revocationpubkey>@
    633 --
    634 -- >>> received_htlc_witness_revoke sig revpk
    635 -- Witness [sig, revpk]
    636 received_htlc_witness_revoke :: BS.ByteString -> Pubkey -> Witness
    637 received_htlc_witness_revoke !sig (Pubkey !revpk) = Witness [sig, revpk]
    638 
    639 -- HTLC-timeout/success output -------------------------------------------------
    640 
    641 -- | HTLC output witness script (same structure as to_local).
    642 --
    643 -- Used for HTLC-timeout and HTLC-success transaction outputs.
    644 --
    645 -- Script:
    646 --
    647 -- @
    648 -- OP_IF
    649 --     <revocationpubkey>
    650 -- OP_ELSE
    651 --     <to_self_delay>
    652 --     OP_CHECKSEQUENCEVERIFY
    653 --     OP_DROP
    654 --     <local_delayedpubkey>
    655 -- OP_ENDIF
    656 -- OP_CHECKSIG
    657 -- @
    658 htlc_output_script
    659   :: RevocationPubkey
    660   -> ToSelfDelay
    661   -> LocalDelayedPubkey
    662   -> Script
    663 htlc_output_script = to_local_script
    664 
    665 -- | Witness for delayed spend of HTLC output.
    666 --
    667 -- Input nSequence must be set to to_self_delay.
    668 --
    669 -- Witness: @<local_delayedsig> 0@
    670 htlc_output_witness_spend :: BS.ByteString -> Witness
    671 htlc_output_witness_spend = to_local_witness_spend
    672 
    673 -- | Witness for revocation spend of HTLC output.
    674 --
    675 -- Witness: @<revocationsig> 1@
    676 htlc_output_witness_revoke :: BS.ByteString -> Witness
    677 htlc_output_witness_revoke = to_local_witness_revoke