Scripts.hs (16039B)
1 {-# OPTIONS_HADDOCK hide #-} 2 {-# LANGUAGE BangPatterns #-} 3 4 -- | 5 -- Module: Lightning.Protocol.BOLT3.Scripts 6 -- Copyright: (c) 2025 Jared Tobin 7 -- License: MIT 8 -- Maintainer: Jared Tobin <jared@ppad.tech> 9 -- 10 -- Scripts and witnesses for BOLT #3 transaction outputs. 11 12 module Lightning.Protocol.BOLT3.Scripts ( 13 -- * Funding output 14 funding_script 15 , funding_witness 16 17 -- * to_local output 18 , to_local_script 19 , to_local_witness_spend 20 , to_local_witness_revoke 21 22 -- * to_remote output 23 , to_remote_witness_script 24 , to_remote_script_pubkey 25 , to_remote_witness 26 27 -- * Anchor outputs 28 , anchor_script 29 , anchor_witness_owner 30 , anchor_witness_anyone 31 32 -- * Offered HTLC outputs 33 , offered_htlc_script 34 , offered_htlc_witness_preimage 35 , offered_htlc_witness_revoke 36 37 -- * Received HTLC outputs 38 , received_htlc_script 39 , received_htlc_witness_timeout 40 , received_htlc_witness_revoke 41 42 -- * HTLC transaction inputs 43 , htlc_success_witness 44 , htlc_timeout_witness 45 , remote_htlc_sighash 46 47 -- * P2WSH 48 , to_p2wsh 49 ) where 50 51 import Bitcoin.Prim.Tx (Witness(..)) 52 import Bitcoin.Prim.Tx.Sighash (SighashType(..)) 53 import qualified Crypto.Hash.RIPEMD160 as RIPEMD160 54 import qualified Crypto.Hash.SHA256 as SHA256 55 import Data.Bits ((.&.), shiftR) 56 import qualified Data.ByteString as BS 57 import qualified Data.ByteString.Builder as BSB 58 import qualified Data.ByteString.Lazy as BSL 59 import Data.Word (Word8, Word16, Word32) 60 import Lightning.Protocol.BOLT1 (Point, PaymentPreimage) 61 import qualified Lightning.Protocol.BOLT1 as BOLT1 62 import Lightning.Protocol.BOLT3.Types 63 64 -- opcodes -------------------------------------------------------------------- 65 66 op_0, op_1, op_2, op_16 :: Word8 67 op_0 = 0x00 68 op_1 = 0x51 69 op_2 = 0x52 70 op_16 = 0x60 71 72 op_if, op_notif, op_else, op_endif, op_drop, op_dup, op_ifdup :: Word8 73 op_if = 0x63 74 op_notif = 0x64 75 op_else = 0x67 76 op_endif = 0x68 77 op_ifdup = 0x73 78 op_drop = 0x75 79 op_dup = 0x76 80 81 op_swap, op_size, op_equal, op_equalverify, op_hash160 :: Word8 82 op_swap = 0x7c 83 op_size = 0x82 84 op_equal = 0x87 85 op_equalverify = 0x88 86 op_hash160 = 0xa9 87 88 op_checksig, op_checksigverify, op_checkmultisig :: Word8 89 op_checksig = 0xac 90 op_checksigverify = 0xad 91 op_checkmultisig = 0xae 92 93 op_checklocktimeverify, op_checksequenceverify :: Word8 94 op_checklocktimeverify = 0xb1 95 op_checksequenceverify = 0xb2 96 97 -- helpers -------------------------------------------------------------------- 98 99 op :: Word8 -> BSB.Builder 100 op = BSB.word8 101 {-# INLINE op #-} 102 103 -- Push data of at most 75 bytes (all pushes here are keys, hashes or 104 -- short numbers). 105 push :: BS.ByteString -> BSB.Builder 106 push bs = BSB.word8 (fromIntegral (BS.length bs)) <> BSB.byteString bs 107 {-# INLINE push #-} 108 109 push_point :: Point -> BSB.Builder 110 push_point = push . BOLT1.un_point 111 {-# INLINE push_point #-} 112 113 -- Push a non-negative number, minimally encoded. 114 push_num :: Word32 -> BSB.Builder 115 push_num n 116 | n == 0 = op op_0 117 | n <= 16 = op (0x50 + fromIntegral n) 118 | otherwise = push (BS.pack (signed (le n))) 119 where 120 -- little-endian magnitude 121 le :: Word32 -> [Word8] 122 le !x 123 | x == 0 = [] 124 | otherwise = fromIntegral (x .&. 0xff) : le (x `shiftR` 8) 125 126 -- append a sign byte if the top bit is set 127 signed bs = case reverse bs of 128 (h : _) | h .&. 0x80 /= 0 -> bs <> [0x00] 129 _ -> bs 130 131 push_delay :: Word16 -> BSB.Builder 132 push_delay = push_num . fromIntegral 133 {-# INLINE push_delay #-} 134 135 script :: BSB.Builder -> Script 136 script = Script . BSL.toStrict . BSB.toLazyByteString 137 {-# INLINE script #-} 138 139 hash160 :: BS.ByteString -> BS.ByteString 140 hash160 = RIPEMD160.hash . SHA256.hash 141 {-# INLINE hash160 #-} 142 143 -- Append a witness script to a witness stack. 144 with_script :: [BS.ByteString] -> Script -> Witness 145 with_script items (Script s) = Witness (items <> [s]) 146 {-# INLINE with_script #-} 147 148 -- P2WSH ---------------------------------------------------------------------- 149 150 -- | The P2WSH scriptPubKey for a witness script: 151 -- @0 \<SHA256(script)\>@. 152 to_p2wsh :: Script -> Script 153 to_p2wsh (Script s) = script (op op_0 <> push (SHA256.hash s)) 154 155 -- funding output ------------------------------------------------------------- 156 157 -- | The funding output's witness script: 158 -- 159 -- @2 \<pubkey1\> \<pubkey2\> 2 OP_CHECKMULTISIG@ 160 -- 161 -- where @pubkey1@ is the lexicographically lesser of the two funding 162 -- pubkeys. The argument order doesn't matter. 163 funding_script :: FundingPubkey -> FundingPubkey -> Script 164 funding_script (FundingPubkey a) (FundingPubkey b) = 165 let (lo, hi) = if a <= b then (a, b) else (b, a) 166 in script $ 167 op op_2 <> push_point lo <> push_point hi 168 <> op op_2 <> op op_checkmultisig 169 170 -- | The witness spending the funding output, given each party's 171 -- funding pubkey and signature (DER-encoded, with sighash byte): 172 -- 173 -- @0 \<signature_for_pubkey1\> \<signature_for_pubkey2\> \<script\>@ 174 -- 175 -- The signatures are ordered to match the pubkeys in 176 -- 'funding_script', so the argument order doesn't matter. 177 funding_witness 178 :: FundingPubkey -> BS.ByteString 179 -> FundingPubkey -> BS.ByteString 180 -> Witness 181 funding_witness pa@(FundingPubkey a) sa pb@(FundingPubkey b) sb = 182 let (s1, s2) = if a <= b then (sa, sb) else (sb, sa) 183 in with_script [BS.empty, s1, s2] (funding_script pa pb) 184 185 -- to_local output ------------------------------------------------------------ 186 187 -- | The @to_local@ output's witness script: 188 -- 189 -- @ 190 -- OP_IF 191 -- \<revocationpubkey\> 192 -- OP_ELSE 193 -- \<to_self_delay\> OP_CHECKSEQUENCEVERIFY OP_DROP 194 -- \<local_delayedpubkey\> 195 -- OP_ENDIF 196 -- OP_CHECKSIG 197 -- @ 198 -- 199 -- HTLC-success and HTLC-timeout transaction outputs use the same 200 -- script. 201 to_local_script 202 :: RevocationPubkey 203 -> ToSelfDelay 204 -> LocalDelayedPubkey 205 -> Script 206 to_local_script 207 (RevocationPubkey rev) 208 (ToSelfDelay delay) 209 (LocalDelayedPubkey delayed) = script $ 210 op op_if 211 <> push_point rev 212 <> op op_else 213 <> push_delay delay 214 <> op op_checksequenceverify 215 <> op op_drop 216 <> push_point delayed 217 <> op op_endif 218 <> op op_checksig 219 220 -- | The witness for the owner's delayed spend of a @to_local@ (or 221 -- HTLC transaction) output, given its witness script: 222 -- 223 -- @\<local_delayedsig\> \<\> \<script\>@ 224 -- 225 -- The spending input's @nSequence@ must be @to_self_delay@. 226 to_local_witness_spend :: BS.ByteString -> Script -> Witness 227 to_local_witness_spend sig = with_script [sig, BS.empty] 228 229 -- | The witness for the revocation spend of a @to_local@ (or HTLC 230 -- transaction) output, given its witness script: 231 -- 232 -- @\<revocation_sig\> 1 \<script\>@ 233 to_local_witness_revoke :: BS.ByteString -> Script -> Witness 234 to_local_witness_revoke sig = with_script [sig, BS.singleton 0x01] 235 236 -- to_remote output ----------------------------------------------------------- 237 238 -- | The @to_remote@ output's witness script under 'Anchors': 239 -- 240 -- @\<remotepubkey\> OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY@ 241 -- 242 -- (Under 'StaticRemotekey' the output is P2WPKH, with no witness 243 -- script; see 'to_remote_script_pubkey'.) 244 to_remote_witness_script :: RemotePubkey -> Script 245 to_remote_witness_script (RemotePubkey pk) = script $ 246 push_point pk 247 <> op op_checksigverify 248 <> op op_1 249 <> op op_checksequenceverify 250 251 -- | The @to_remote@ output's scriptPubKey: P2WPKH to @remotepubkey@ 252 -- under 'StaticRemotekey', and P2WSH of 'to_remote_witness_script' 253 -- under 'Anchors'. 254 to_remote_script_pubkey :: RemotePubkey -> CommitmentFormat -> Script 255 to_remote_script_pubkey pk@(RemotePubkey p) fmt = case fmt of 256 StaticRemotekey -> script (op op_0 <> push (hash160 (BOLT1.un_point p))) 257 Anchors -> to_p2wsh (to_remote_witness_script pk) 258 259 -- | The witness spending a @to_remote@ output: @\<remote_sig\> 260 -- \<remotepubkey\>@ (P2WPKH) under 'StaticRemotekey', and 261 -- @\<remote_sig\> \<script\>@ under 'Anchors', where the spending 262 -- input's @nSequence@ must be 1. 263 to_remote_witness 264 :: BS.ByteString -> RemotePubkey -> CommitmentFormat -> Witness 265 to_remote_witness sig pk@(RemotePubkey p) fmt = case fmt of 266 StaticRemotekey -> Witness [sig, BOLT1.un_point p] 267 Anchors -> with_script [sig] (to_remote_witness_script pk) 268 269 -- anchor outputs ------------------------------------------------------------- 270 271 -- | An anchor output's witness script, for the funding pubkey of the 272 -- party it belongs to: 273 -- 274 -- @ 275 -- \<funding_pubkey\> OP_CHECKSIG OP_IFDUP 276 -- OP_NOTIF 277 -- OP_16 OP_CHECKSEQUENCEVERIFY 278 -- OP_ENDIF 279 -- @ 280 anchor_script :: FundingPubkey -> Script 281 anchor_script (FundingPubkey pk) = script $ 282 push_point pk 283 <> op op_checksig 284 <> op op_ifdup 285 <> op op_notif 286 <> op op_16 287 <> op op_checksequenceverify 288 <> op op_endif 289 290 -- | The witness for an anchor's owner to spend it: 291 -- @\<sig\> \<script\>@. 292 anchor_witness_owner :: BS.ByteString -> FundingPubkey -> Witness 293 anchor_witness_owner sig pk = with_script [sig] (anchor_script pk) 294 295 -- | The witness for anyone to sweep an anchor, 16 blocks after the 296 -- commitment transaction confirms: @\<\> \<script\>@. 297 anchor_witness_anyone :: FundingPubkey -> Witness 298 anchor_witness_anyone pk = with_script [BS.empty] (anchor_script pk) 299 300 -- offered HTLC outputs ------------------------------------------------------- 301 302 -- csv suffix for anchors HTLC scripts 303 htlc_csv :: CommitmentFormat -> BSB.Builder 304 htlc_csv fmt = case fmt of 305 StaticRemotekey -> mempty 306 Anchors -> op op_1 <> op op_checksequenceverify <> op op_drop 307 {-# INLINE htlc_csv #-} 308 309 -- | An offered HTLC output's witness script: 310 -- 311 -- @ 312 -- OP_DUP OP_HASH160 \<RIPEMD160(SHA256(revocationpubkey))\> OP_EQUAL 313 -- OP_IF 314 -- OP_CHECKSIG 315 -- OP_ELSE 316 -- \<remote_htlcpubkey\> OP_SWAP OP_SIZE 32 OP_EQUAL 317 -- OP_NOTIF 318 -- OP_DROP 2 OP_SWAP \<local_htlcpubkey\> 2 OP_CHECKMULTISIG 319 -- OP_ELSE 320 -- OP_HASH160 \<RIPEMD160(payment_hash)\> OP_EQUALVERIFY 321 -- OP_CHECKSIG 322 -- OP_ENDIF 323 -- OP_ENDIF 324 -- @ 325 -- 326 -- Under 'Anchors', @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ precedes the 327 -- final @OP_ENDIF@. 328 offered_htlc_script 329 :: RevocationPubkey 330 -> RemoteHtlcPubkey 331 -> LocalHtlcPubkey 332 -> BOLT1.PaymentHash 333 -> CommitmentFormat 334 -> Script 335 offered_htlc_script 336 (RevocationPubkey rev) 337 (RemoteHtlcPubkey remote) 338 (LocalHtlcPubkey local) 339 ph 340 fmt = script $ 341 op op_dup <> op op_hash160 342 <> push (hash160 (BOLT1.un_point rev)) 343 <> op op_equal 344 <> op op_if 345 <> op op_checksig 346 <> op op_else 347 <> push_point remote <> op op_swap <> op op_size 348 <> push (BS.singleton 32) <> op op_equal 349 <> op op_notif 350 <> op op_drop <> op op_2 <> op op_swap 351 <> push_point local <> op op_2 <> op op_checkmultisig 352 <> op op_else 353 <> op op_hash160 354 <> push (RIPEMD160.hash (BOLT1.un_payment_hash ph)) 355 <> op op_equalverify <> op op_checksig 356 <> op op_endif 357 <> htlc_csv fmt 358 <> op op_endif 359 360 -- | The witness for the remote party to claim an offered HTLC output 361 -- with the payment preimage, given its witness script: 362 -- 363 -- @\<remotehtlcsig\> \<payment_preimage\> \<script\>@ 364 -- 365 -- Under 'Anchors' the spending input's @nSequence@ must be 1. 366 offered_htlc_witness_preimage 367 :: BS.ByteString -> PaymentPreimage -> Script -> Witness 368 offered_htlc_witness_preimage sig pre = 369 with_script [sig, BOLT1.un_payment_preimage pre] 370 371 -- | The witness for the revocation spend of an offered HTLC output, 372 -- given its witness script: 373 -- 374 -- @\<revocation_sig\> \<revocationpubkey\> \<script\>@ 375 offered_htlc_witness_revoke 376 :: BS.ByteString -> RevocationPubkey -> Script -> Witness 377 offered_htlc_witness_revoke sig (RevocationPubkey rev) = 378 with_script [sig, BOLT1.un_point rev] 379 380 -- received HTLC outputs ------------------------------------------------------ 381 382 -- | A received HTLC output's witness script: 383 -- 384 -- @ 385 -- OP_DUP OP_HASH160 \<RIPEMD160(SHA256(revocationpubkey))\> OP_EQUAL 386 -- OP_IF 387 -- OP_CHECKSIG 388 -- OP_ELSE 389 -- \<remote_htlcpubkey\> OP_SWAP OP_SIZE 32 OP_EQUAL 390 -- OP_IF 391 -- OP_HASH160 \<RIPEMD160(payment_hash)\> OP_EQUALVERIFY 392 -- 2 OP_SWAP \<local_htlcpubkey\> 2 OP_CHECKMULTISIG 393 -- OP_ELSE 394 -- OP_DROP \<cltv_expiry\> OP_CHECKLOCKTIMEVERIFY OP_DROP 395 -- OP_CHECKSIG 396 -- OP_ENDIF 397 -- OP_ENDIF 398 -- @ 399 -- 400 -- Under 'Anchors', @1 OP_CHECKSEQUENCEVERIFY OP_DROP@ precedes the 401 -- final @OP_ENDIF@. 402 received_htlc_script 403 :: RevocationPubkey 404 -> RemoteHtlcPubkey 405 -> LocalHtlcPubkey 406 -> BOLT1.PaymentHash 407 -> CltvExpiry 408 -> CommitmentFormat 409 -> Script 410 received_htlc_script 411 (RevocationPubkey rev) 412 (RemoteHtlcPubkey remote) 413 (LocalHtlcPubkey local) 414 ph 415 (CltvExpiry expiry) 416 fmt = script $ 417 op op_dup <> op op_hash160 418 <> push (hash160 (BOLT1.un_point rev)) 419 <> op op_equal 420 <> op op_if 421 <> op op_checksig 422 <> op op_else 423 <> push_point remote <> op op_swap <> op op_size 424 <> push (BS.singleton 32) <> op op_equal 425 <> op op_if 426 <> op op_hash160 427 <> push (RIPEMD160.hash (BOLT1.un_payment_hash ph)) 428 <> op op_equalverify 429 <> op op_2 <> op op_swap <> push_point local 430 <> op op_2 <> op op_checkmultisig 431 <> op op_else 432 <> op op_drop <> push_num expiry 433 <> op op_checklocktimeverify <> op op_drop 434 <> op op_checksig 435 <> op op_endif 436 <> htlc_csv fmt 437 <> op op_endif 438 439 -- | The witness for the remote party to time out a received HTLC 440 -- output, given its witness script: 441 -- 442 -- @\<remotehtlcsig\> \<\> \<script\>@ 443 -- 444 -- Under 'Anchors' the spending input's @nSequence@ must be 1. 445 received_htlc_witness_timeout :: BS.ByteString -> Script -> Witness 446 received_htlc_witness_timeout sig = with_script [sig, BS.empty] 447 448 -- | The witness for the revocation spend of a received HTLC output, 449 -- given its witness script: 450 -- 451 -- @\<revocation_sig\> \<revocationpubkey\> \<script\>@ 452 received_htlc_witness_revoke 453 :: BS.ByteString -> RevocationPubkey -> Script -> Witness 454 received_htlc_witness_revoke sig (RevocationPubkey rev) = 455 with_script [sig, BOLT1.un_point rev] 456 457 -- HTLC transaction inputs ---------------------------------------------------- 458 459 -- | The witness for an HTLC-success transaction's input, given both 460 -- HTLC signatures, the payment preimage and the received HTLC 461 -- output's witness script: 462 -- 463 -- @0 \<remotehtlcsig\> \<localhtlcsig\> \<payment_preimage\> \<script\>@ 464 -- 465 -- See 'remote_htlc_sighash' for the signatures' sighash types. 466 htlc_success_witness 467 :: BS.ByteString -- ^ remotehtlcsig 468 -> BS.ByteString -- ^ localhtlcsig 469 -> PaymentPreimage 470 -> Script -- ^ the received HTLC output's witness script 471 -> Witness 472 htlc_success_witness remote local pre = 473 with_script [BS.empty, remote, local, BOLT1.un_payment_preimage pre] 474 475 -- | The witness for an HTLC-timeout transaction's input, given both 476 -- HTLC signatures and the offered HTLC output's witness script: 477 -- 478 -- @0 \<remotehtlcsig\> \<localhtlcsig\> \<\> \<script\>@ 479 -- 480 -- See 'remote_htlc_sighash' for the signatures' sighash types. 481 htlc_timeout_witness 482 :: BS.ByteString -- ^ remotehtlcsig 483 -> BS.ByteString -- ^ localhtlcsig 484 -> Script -- ^ the offered HTLC output's witness script 485 -> Witness 486 htlc_timeout_witness remote local = 487 with_script [BS.empty, remote, local, BS.empty] 488 489 -- | The sighash type of @remotehtlcsig@: the signature on an 490 -- HTLC-success or HTLC-timeout transaction that the commitment's 491 -- non-owner sends in @commitment_signed@. 492 -- 493 -- Under 'Anchors' it is @SIGHASH_SINGLE|SIGHASH_ANYONECANPAY@, so 494 -- that the owner can add inputs and outputs to pay the (otherwise 495 -- zero) fee; under 'StaticRemotekey' it is @SIGHASH_ALL@. The 496 -- owner's own signature, @localhtlcsig@, always uses @SIGHASH_ALL@. 497 -- 498 -- >>> remote_htlc_sighash Anchors 499 -- SIGHASH_SINGLE_ANYONECANPAY 500 remote_htlc_sighash :: CommitmentFormat -> SighashType 501 remote_htlc_sighash fmt = case fmt of 502 StaticRemotekey -> SIGHASH_ALL 503 Anchors -> SIGHASH_SINGLE_ANYONECANPAY